Skip to main content

fidget_shapes/
types.rs

1//! Types used in shape construction
2//!
3//! This module includes both GLSL-style `Vec` types and higher-level
4//! representations of modeling concepts (e.g. [`Axis`]).
5//!
6//! We use dedicated types (instead of `nalgebra` types) because we must derive
7//! `Facet` on them, so are limited by the orphan rule.
8use facet::{ConstTypeId, Facet};
9use strum::IntoDiscriminant;
10
11use fidget_core::context::Tree;
12
13/// Error type for axis construction
14#[derive(thiserror::Error, Debug)]
15pub enum AxisError {
16    /// Vector is too short to convert to an axis
17    #[error("vector is too short to convert to an axis (length: {0})")]
18    TooShort(f64),
19
20    /// Vector is too long to convert to an axis
21    #[error("vector is too long to convert to an axis (length: {0})")]
22    TooLong(f64),
23
24    /// Could not normalize vector due to an invalid length
25    #[error("could not normalize vector due to an invalid length")]
26    BadLength,
27}
28
29/// Error type for conversions
30#[derive(thiserror::Error, Debug)]
31#[error("wrong type; expected {expected}, got {actual}")]
32pub struct WrongType {
33    /// Expected type
34    expected: Type,
35    /// Actual type
36    actual: Type,
37}
38
39/// 2D position
40#[derive(Copy, Clone, Debug, PartialEq, Facet)]
41#[allow(missing_docs)]
42pub struct Vec2 {
43    pub x: f64,
44    pub y: f64,
45}
46
47impl From<nalgebra::Vector2<f64>> for Vec2 {
48    fn from(value: nalgebra::Vector2<f64>) -> Self {
49        Self {
50            x: value.x,
51            y: value.y,
52        }
53    }
54}
55
56impl From<Vec2> for nalgebra::Vector2<f64> {
57    fn from(value: Vec2) -> Self {
58        Self::new(value.x, value.y)
59    }
60}
61
62impl From<f64> for Vec2 {
63    fn from(value: f64) -> Self {
64        Self { x: value, y: value }
65    }
66}
67
68impl Vec2 {
69    /// Builds a new `Vec2` from `x, y` coordinates
70    pub fn new(x: f64, y: f64) -> Self {
71        Self { x, y }
72    }
73    /// Returns the L2-norm
74    pub fn norm(&self) -> f64 {
75        (self.x.powi(2) + self.y.powi(2)).sqrt()
76    }
77    fn combine<F: Fn(f64, f64) -> f64>(self, rhs: Self, f: F) -> Self {
78        Self {
79            x: f(self.x, rhs.x),
80            y: f(self.y, rhs.y),
81        }
82    }
83    fn map<F: Fn(f64) -> f64>(self, f: F) -> Self {
84        Self {
85            x: f(self.x),
86            y: f(self.y),
87        }
88    }
89}
90
91////////////////////////////////////////////////////////////////////////////////
92
93/// 3D position
94#[derive(Copy, Clone, Debug, PartialEq, Facet)]
95#[allow(missing_docs)]
96pub struct Vec3 {
97    pub x: f64,
98    pub y: f64,
99    pub z: f64,
100}
101
102impl From<nalgebra::Vector3<f64>> for Vec3 {
103    fn from(value: nalgebra::Vector3<f64>) -> Self {
104        Self {
105            x: value.x,
106            y: value.y,
107            z: value.z,
108        }
109    }
110}
111
112impl From<Vec3> for nalgebra::Vector3<f64> {
113    fn from(value: Vec3) -> Self {
114        Self::new(value.x, value.y, value.z)
115    }
116}
117
118impl From<f64> for Vec3 {
119    fn from(value: f64) -> Self {
120        Self {
121            x: value,
122            y: value,
123            z: value,
124        }
125    }
126}
127
128impl Vec3 {
129    /// Builds a new `Vec3` from `x, y, z` coordinates
130    pub fn new(x: f64, y: f64, z: f64) -> Self {
131        Self { x, y, z }
132    }
133    /// Returns the L2-norm
134    pub fn norm(&self) -> f64 {
135        (self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).sqrt()
136    }
137    fn combine<F: Fn(f64, f64) -> f64>(self, rhs: Self, f: F) -> Self {
138        Self {
139            x: f(self.x, rhs.x),
140            y: f(self.y, rhs.y),
141            z: f(self.z, rhs.z),
142        }
143    }
144    fn map<F: Fn(f64) -> f64>(self, f: F) -> Self {
145        Self {
146            x: f(self.x),
147            y: f(self.y),
148            z: f(self.z),
149        }
150    }
151}
152
153////////////////////////////////////////////////////////////////////////////////
154
155/// 4D position (`xyzw`)
156#[derive(Copy, Clone, Debug, PartialEq, Facet)]
157#[allow(missing_docs)]
158pub struct Vec4 {
159    pub x: f64,
160    pub y: f64,
161    pub z: f64,
162    pub w: f64,
163}
164
165impl From<nalgebra::Vector4<f64>> for Vec4 {
166    fn from(value: nalgebra::Vector4<f64>) -> Self {
167        Self {
168            x: value.x,
169            y: value.y,
170            z: value.z,
171            w: value.w,
172        }
173    }
174}
175
176impl From<Vec4> for nalgebra::Vector4<f64> {
177    fn from(value: Vec4) -> Self {
178        Self::new(value.x, value.y, value.z, value.w)
179    }
180}
181
182impl From<f64> for Vec4 {
183    fn from(value: f64) -> Self {
184        Self {
185            x: value,
186            y: value,
187            z: value,
188            w: value,
189        }
190    }
191}
192
193impl Vec4 {
194    fn combine<F: Fn(f64, f64) -> f64>(self, rhs: Self, f: F) -> Self {
195        Self {
196            x: f(self.x, rhs.x),
197            y: f(self.y, rhs.y),
198            z: f(self.z, rhs.z),
199            w: f(self.w, rhs.w),
200        }
201    }
202    fn map<F: Fn(f64) -> f64>(self, f: F) -> Self {
203        Self {
204            x: f(self.x),
205            y: f(self.y),
206            z: f(self.z),
207            w: f(self.w),
208        }
209    }
210}
211
212////////////////////////////////////////////////////////////////////////////////
213
214macro_rules! impl_binary {
215    ($ty:ident, $op:ident, $base_fn:ident) => {
216        impl std::ops::$op<$ty> for $ty {
217            type Output = $ty;
218
219            fn $base_fn(self, rhs: $ty) -> Self {
220                self.combine(rhs, |a, b| a.$base_fn(b))
221            }
222        }
223        impl std::ops::$op<$ty> for f64 {
224            type Output = $ty;
225            fn $base_fn(self, rhs: $ty) -> $ty {
226                $ty::from(self).$base_fn(rhs)
227            }
228        }
229        impl std::ops::$op<f64> for $ty {
230            type Output = $ty;
231            fn $base_fn(self, rhs: f64) -> $ty {
232                self.$base_fn($ty::from(rhs))
233            }
234        }
235    };
236    ($ty:ident, $base_fn:ident, $f:expr) => {
237        pub fn $base_fn<R>(self, rhs: R) -> Self
238        where
239            $ty: From<R>,
240        {
241            self.combine($ty::from(rhs), $f)
242        }
243    };
244    ($ty:ident, $base_fn:ident) => {
245        impl_binary!($ty, $base_fn, |a, b| a.$base_fn(b));
246    };
247}
248
249macro_rules! impl_unary {
250    ($ty:ident, $op:ident, $base_fn:ident) => {
251        impl std::ops::$op for $ty {
252            type Output = $ty;
253            fn $base_fn(self) -> $ty {
254                self.map(std::ops::$op::$base_fn)
255            }
256        }
257    };
258    ($ty:ident, $base_fn:ident, $f:expr) => {
259        pub fn $base_fn(self) -> Self {
260            self.map($f)
261        }
262    };
263    ($ty:ident, $base_fn:ident) => {
264        impl_unary!($ty, $base_fn, |a| a.$base_fn());
265    };
266}
267
268macro_rules! impl_all {
269    ($ty:ident) => {
270        impl_binary!($ty, Add, add);
271        impl_binary!($ty, Mul, mul);
272        impl_binary!($ty, Sub, sub);
273        impl_binary!($ty, Div, div);
274        impl_unary!($ty, Neg, neg);
275
276        #[allow(missing_docs)]
277        impl $ty {
278            impl_binary!($ty, min);
279            impl_binary!($ty, max);
280            impl_unary!($ty, sqrt);
281            impl_unary!($ty, abs);
282        }
283    };
284}
285
286impl_all!(Vec2);
287impl_all!(Vec3);
288impl_all!(Vec4);
289
290////////////////////////////////////////////////////////////////////////////////
291
292/// Normalized 3D axis (of length 1)
293#[derive(Copy, Clone, Debug, PartialEq, Facet)]
294pub struct Axis(Vec3);
295
296impl TryFrom<Vec3> for Axis {
297    type Error = AxisError;
298    fn try_from(value: Vec3) -> Result<Self, Self::Error> {
299        let norm = value.norm();
300        if norm.is_nan() {
301            Err(AxisError::BadLength)
302        } else if norm < 1e-8 {
303            Err(AxisError::TooShort(norm))
304        } else if norm > 1e8 {
305            Err(AxisError::TooLong(norm))
306        } else {
307            Ok(Self(value / norm))
308        }
309    }
310}
311
312impl Axis {
313    /// Returns the axis vector
314    pub fn vec(&self) -> &Vec3 {
315        &self.0
316    }
317    /// The X axis
318    pub const X: Self = Axis(Vec3 {
319        x: 1.0,
320        y: 0.0,
321        z: 0.0,
322    });
323    /// The Y axis
324    pub const Y: Self = Axis(Vec3 {
325        x: 0.0,
326        y: 1.0,
327        z: 0.0,
328    });
329    /// The Z axis
330    pub const Z: Self = Axis(Vec3 {
331        x: 0.0,
332        y: 0.0,
333        z: 1.0,
334    });
335}
336
337/// Unoriented plane in 3D space, specified as an axis + offset
338#[derive(Copy, Clone, Debug, PartialEq, Facet)]
339pub struct Plane {
340    /// Axis orthogonal to the plane
341    pub axis: Axis,
342    /// Offset relative to the origin
343    pub offset: f64,
344}
345
346impl Plane {
347    /// The XY plane
348    pub const XY: Self = Plane {
349        axis: Axis::Y,
350        offset: 0.0,
351    };
352    /// The YZ plane
353    pub const YZ: Self = Plane {
354        axis: Axis::X,
355        offset: 0.0,
356    };
357    /// The ZX plane
358    pub const ZX: Self = Plane {
359        axis: Axis::Y,
360        offset: 0.0,
361    };
362}
363
364impl From<Plane> for Tree {
365    fn from(v: Plane) -> Self {
366        let (x, y, z) = Tree::axes();
367        let a = v.axis.vec();
368        x * a.x + y * a.y + z * a.z - v.offset
369    }
370}
371
372////////////////////////////////////////////////////////////////////////////////
373
374/// Enumeration representing all types that can be used in shapes
375#[derive(Debug, strum::EnumDiscriminants)]
376#[strum_discriminants(name(Type), derive(enum_map::Enum), allow(missing_docs))]
377#[allow(missing_docs)]
378pub enum Value {
379    Float(f64),
380    Vec2(Vec2),
381    Vec3(Vec3),
382    Vec4(Vec4),
383    Axis(Axis),
384    Plane(Plane),
385    Tree(Tree),
386    VecTree(Vec<Tree>),
387}
388
389impl Value {
390    /// Puts the type into an in-progress builder at a particular index
391    ///
392    /// # Panics
393    /// If the currently-selected builder field does not match our type
394    pub fn put<'facet>(
395        self,
396        builder: facet::Partial<'facet>,
397        i: usize,
398    ) -> facet::Partial<'facet> {
399        match self {
400            Value::Float(v) => builder.set_nth_field(i, v),
401            Value::Vec2(v) => builder.set_nth_field(i, v),
402            Value::Vec3(v) => builder.set_nth_field(i, v),
403            Value::Vec4(v) => builder.set_nth_field(i, v),
404            Value::Axis(v) => builder.set_nth_field(i, v),
405            Value::Plane(v) => builder.set_nth_field(i, v),
406            Value::Tree(v) => builder.set_nth_field(i, v),
407            Value::VecTree(v) => builder.set_nth_field(i, v),
408        }
409        .unwrap()
410    }
411}
412
413macro_rules! try_from_type {
414    ($ty:ty, $name:ident) => {
415        impl<'a> TryFrom<&'a Value> for &'a $ty {
416            type Error = $crate::types::WrongType;
417            fn try_from(v: &'a Value) -> Result<&'a $ty, Self::Error> {
418                if let Value::$name(f) = v {
419                    Ok(f)
420                } else {
421                    Err(Self::Error {
422                        expected: Type::$name,
423                        actual: v.discriminant(),
424                    })
425                }
426            }
427        }
428    };
429    ($ty:ident) => {
430        try_from_type!($ty, $ty);
431    };
432}
433
434try_from_type!(f64, Float);
435try_from_type!(Vec2);
436try_from_type!(Vec3);
437try_from_type!(Vec4);
438try_from_type!(Tree);
439try_from_type!(Plane);
440try_from_type!(Axis);
441try_from_type!(Vec<Tree>, VecTree);
442
443impl std::fmt::Display for Type {
444    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445        let s = match self {
446            Type::Float => "f64",
447            Type::Vec2 => "Vec2",
448            Type::Vec3 => "Vec3",
449            Type::Vec4 => "Vec4",
450            Type::Axis => "Axis",
451            Type::Plane => "Plane",
452            Type::Tree => "Tree",
453            Type::VecTree => "Vec<Tree>",
454        };
455        write!(f, "{s}")
456    }
457}
458
459/// Convert from a Facet type id to a tag
460impl TryFrom<facet::ConstTypeId> for Type {
461    type Error = facet::ConstTypeId;
462    fn try_from(t: facet::ConstTypeId) -> Result<Self, Self::Error> {
463        if t == ConstTypeId::of::<f64>() {
464            Ok(Self::Float)
465        } else if t == ConstTypeId::of::<Vec2>() {
466            Ok(Self::Vec2)
467        } else if t == ConstTypeId::of::<Vec3>() {
468            Ok(Self::Vec3)
469        } else if t == ConstTypeId::of::<Vec4>() {
470            Ok(Self::Vec4)
471        } else if t == ConstTypeId::of::<Axis>() {
472            Ok(Self::Axis)
473        } else if t == ConstTypeId::of::<Plane>() {
474            Ok(Self::Plane)
475        } else if t == ConstTypeId::of::<Tree>() {
476            Ok(Self::Tree)
477        } else if t == ConstTypeId::of::<Vec<Tree>>() {
478            Ok(Self::VecTree)
479        } else {
480            Err(t)
481        }
482    }
483}
484
485impl Type {
486    /// Executes a default builder function for the given type
487    ///
488    /// # Safety
489    /// `f` must be a builder for the type associated with this tag
490    pub unsafe fn build_from_default_fn(
491        &self,
492        f: facet::DefaultSource,
493    ) -> Value {
494        match f {
495            facet::DefaultSource::Custom(f) => unsafe {
496                match self {
497                    Type::Float => Value::Float(eval_default_fn(f)),
498                    Type::Vec2 => Value::Vec2(eval_default_fn(f)),
499                    Type::Vec3 => Value::Vec3(eval_default_fn(f)),
500                    Type::Vec4 => Value::Vec4(eval_default_fn(f)),
501                    Type::Axis => Value::Axis(eval_default_fn(f)),
502                    Type::Plane => Value::Plane(eval_default_fn(f)),
503                    Type::Tree => Value::Tree(eval_default_fn(f)),
504                    Type::VecTree => Value::VecTree(eval_default_fn(f)),
505                }
506            },
507            facet::DefaultSource::FromTrait => {
508                // Tested in a unit test elsewhere
509                panic!("must have default builder")
510            }
511        }
512    }
513}
514
515/// Evaluates a default builder function, returning a value
516///
517/// # Safety
518/// `f` must be a builder for type `T`
519pub unsafe fn eval_default_fn<T>(
520    f: unsafe fn(facet::PtrUninit) -> facet::PtrMut,
521) -> T {
522    let mut v = std::mem::MaybeUninit::<T>::uninit();
523    let ptr = facet::PtrUninit::new((&mut v) as *mut _);
524    // SAFETY: `f` must be a builder for type `T`
525    unsafe { f(ptr) };
526    // SAFETY: `v` is initialized by `f`
527    unsafe { v.assume_init() }
528}