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