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>(self, builder: &mut facet::Partial<'facet>, i: usize) {
394        match self {
395            Value::Float(v) => builder.set_nth_field(i, v),
396            Value::Vec2(v) => builder.set_nth_field(i, v),
397            Value::Vec3(v) => builder.set_nth_field(i, v),
398            Value::Vec4(v) => builder.set_nth_field(i, v),
399            Value::Axis(v) => builder.set_nth_field(i, v),
400            Value::Plane(v) => builder.set_nth_field(i, v),
401            Value::Tree(v) => builder.set_nth_field(i, v),
402            Value::VecTree(v) => builder.set_nth_field(i, v),
403        }
404        .unwrap();
405    }
406}
407
408macro_rules! try_from_type {
409    ($ty:ty, $name:ident) => {
410        impl<'a> TryFrom<&'a Value> for &'a $ty {
411            type Error = $crate::types::Error;
412            fn try_from(v: &'a Value) -> Result<&'a $ty, Self::Error> {
413                if let Value::$name(f) = v {
414                    Ok(f)
415                } else {
416                    Err(Self::Error::WrongType {
417                        expected: Type::$name,
418                        actual: v.discriminant(),
419                    })
420                }
421            }
422        }
423    };
424    ($ty:ident) => {
425        try_from_type!($ty, $ty);
426    };
427}
428
429try_from_type!(f64, Float);
430try_from_type!(Vec2);
431try_from_type!(Vec3);
432try_from_type!(Vec4);
433try_from_type!(Tree);
434try_from_type!(Plane);
435try_from_type!(Axis);
436try_from_type!(Vec<Tree>, VecTree);
437
438impl std::fmt::Display for Type {
439    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
440        let s = match self {
441            Type::Float => "f64",
442            Type::Vec2 => "Vec2",
443            Type::Vec3 => "Vec3",
444            Type::Vec4 => "Vec4",
445            Type::Axis => "Axis",
446            Type::Plane => "Plane",
447            Type::Tree => "Tree",
448            Type::VecTree => "Vec<Tree>",
449        };
450        write!(f, "{s}")
451    }
452}
453
454/// Convert from a Facet type id to a tag
455impl TryFrom<facet::ConstTypeId> for Type {
456    type Error = facet::ConstTypeId;
457    fn try_from(t: facet::ConstTypeId) -> Result<Self, Self::Error> {
458        if t == ConstTypeId::of::<f64>() {
459            Ok(Self::Float)
460        } else if t == ConstTypeId::of::<Vec2>() {
461            Ok(Self::Vec2)
462        } else if t == ConstTypeId::of::<Vec3>() {
463            Ok(Self::Vec3)
464        } else if t == ConstTypeId::of::<Vec4>() {
465            Ok(Self::Vec4)
466        } else if t == ConstTypeId::of::<Axis>() {
467            Ok(Self::Axis)
468        } else if t == ConstTypeId::of::<Plane>() {
469            Ok(Self::Plane)
470        } else if t == ConstTypeId::of::<Tree>() {
471            Ok(Self::Tree)
472        } else if t == ConstTypeId::of::<Vec<Tree>>() {
473            Ok(Self::VecTree)
474        } else {
475            Err(t)
476        }
477    }
478}
479
480impl Type {
481    /// Executes a default builder function for the given type
482    ///
483    /// # Safety
484    /// `f` must be a builder for the type associated with this tag
485    pub unsafe fn build_from_default_fn(
486        &self,
487        f: unsafe fn(facet::PtrUninit) -> facet::PtrMut,
488    ) -> Value {
489        unsafe {
490            match self {
491                Type::Float => Value::Float(eval_default_fn(f)),
492                Type::Vec2 => Value::Vec2(eval_default_fn(f)),
493                Type::Vec3 => Value::Vec3(eval_default_fn(f)),
494                Type::Vec4 => Value::Vec4(eval_default_fn(f)),
495                Type::Axis => Value::Axis(eval_default_fn(f)),
496                Type::Plane => Value::Plane(eval_default_fn(f)),
497                Type::Tree => Value::Tree(eval_default_fn(f)),
498                Type::VecTree => Value::VecTree(eval_default_fn(f)),
499            }
500        }
501    }
502}
503
504/// Evaluates a default builder function, returning a value
505///
506/// # Safety
507/// `f` must be a builder for type `T`
508pub unsafe fn eval_default_fn<T>(
509    f: unsafe fn(facet::PtrUninit) -> facet::PtrMut,
510) -> T {
511    let mut v = std::mem::MaybeUninit::<T>::uninit();
512    let ptr = facet::PtrUninit::new(&mut v);
513    // SAFETY: `f` must be a builder for type `T`
514    unsafe { f(ptr) };
515    // SAFETY: `v` is initialized by `f`
516    unsafe { v.assume_init() }
517}