Skip to main content

ferroui_base/rendering/composition/expressions/
expression_variant.rs

1//! The variant value type of expression animations.
2
3use std::fmt;
4use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub};
5
6use crate::media::Color;
7use crate::numerics::{Matrix3x2, Matrix4x4, Quaternion, Vector2, Vector3, Vector4};
8use crate::utilities::span_helpers::InvariantF64;
9use crate::{Matrix, RelativePoint, RelativeScalar, RelativeUnit, Vector, Vector3D};
10
11/// The type of the value held by an [`ExpressionVariant`].
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
13pub enum VariantType {
14    #[default]
15    Invalid,
16    Boolean,
17    Double,
18    Vector2,
19    Vector3,
20    Vector4,
21    Vector,
22    Vector3D,
23    FerroMatrix,
24    Matrix3x2,
25    Matrix4x4,
26    Quaternion,
27    Color,
28    RelativePoint,
29    RelativeScalar,
30    RelativeUnit,
31}
32
33/// A VARIANT type used in expression animations. Can represent multiple value types.
34///
35/// Every operation that is not defined for its operand types gives
36/// [`ExpressionVariant::Invalid`] rather than failing.
37#[derive(Clone, Copy, Debug, PartialEq, Default)]
38pub enum ExpressionVariant {
39    #[default]
40    Invalid,
41    Boolean(bool),
42    Double(f64),
43    Vector2(Vector2),
44    Vector3(Vector3),
45    Vector4(Vector4),
46    Vector(Vector),
47    Vector3D(Vector3D),
48    FerroMatrix(Matrix),
49    Matrix3x2(Matrix3x2),
50    Matrix4x4(Matrix4x4),
51    Quaternion(Quaternion),
52    Color(Color),
53    RelativePoint(RelativePoint),
54    RelativeScalar(RelativeScalar),
55    RelativeUnit(RelativeUnit),
56}
57
58use ExpressionVariant as V;
59
60impl ExpressionVariant {
61    /// The type of the held value.
62    pub fn variant_type(&self) -> VariantType {
63        match self {
64            V::Invalid => VariantType::Invalid,
65            V::Boolean(_) => VariantType::Boolean,
66            V::Double(_) => VariantType::Double,
67            V::Vector2(_) => VariantType::Vector2,
68            V::Vector3(_) => VariantType::Vector3,
69            V::Vector4(_) => VariantType::Vector4,
70            V::Vector(_) => VariantType::Vector,
71            V::Vector3D(_) => VariantType::Vector3D,
72            V::FerroMatrix(_) => VariantType::FerroMatrix,
73            V::Matrix3x2(_) => VariantType::Matrix3x2,
74            V::Matrix4x4(_) => VariantType::Matrix4x4,
75            V::Quaternion(_) => VariantType::Quaternion,
76            V::Color(_) => VariantType::Color,
77            V::RelativePoint(_) => VariantType::RelativePoint,
78            V::RelativeScalar(_) => VariantType::RelativeScalar,
79            V::RelativeUnit(_) => VariantType::RelativeUnit,
80        }
81    }
82
83    /// Gets a member (a component or a swizzle) of the held value. Member
84    /// names are case-sensitive; an unknown member gives `Invalid`.
85    pub fn get_property(&self, property: &str) -> ExpressionVariant {
86        match *self {
87            V::Vector2(v) => match property {
88                "X" => f(v.x),
89                "Y" => f(v.y),
90                _ => V::Invalid,
91            },
92
93            V::Vector(v) => match property {
94                "X" => v.x.into(),
95                "Y" => v.y.into(),
96                _ => V::Invalid,
97            },
98
99            V::Vector3(v) => match property {
100                "X" => f(v.x),
101                "Y" => f(v.y),
102                "Z" => f(v.z),
103                "XY" => Vector2::new(v.x, v.y).into(),
104                "YX" => Vector2::new(v.y, v.x).into(),
105                "XZ" => Vector2::new(v.x, v.z).into(),
106                "ZX" => Vector2::new(v.z, v.x).into(),
107                "YZ" => Vector2::new(v.y, v.z).into(),
108                "ZY" => Vector2::new(v.z, v.y).into(),
109                _ => V::Invalid,
110            },
111
112            V::Vector3D(v) => match property {
113                "X" => v.x.into(),
114                "Y" => v.y.into(),
115                "Z" => v.z.into(),
116                "XY" => Vector::new(v.x, v.y).into(),
117                "YX" => Vector::new(v.y, v.x).into(),
118                "XZ" => Vector::new(v.x, v.z).into(),
119                "ZX" => Vector::new(v.z, v.x).into(),
120                "YZ" => Vector::new(v.y, v.z).into(),
121                "ZY" => Vector::new(v.z, v.y).into(),
122                _ => V::Invalid,
123            },
124
125            V::Vector4(v) => match property {
126                "X" => f(v.x),
127                "Y" => f(v.y),
128                "Z" => f(v.z),
129                "W" => f(v.w),
130                _ => V::Invalid,
131            },
132
133            V::Matrix3x2(m) => match property {
134                "M11" => f(m.m11),
135                "M12" => f(m.m12),
136                "M21" => f(m.m21),
137                "M22" => f(m.m22),
138                "M31" => f(m.m31),
139                "M32" => f(m.m32),
140                _ => V::Invalid,
141            },
142
143            V::FerroMatrix(m) => match property {
144                "M11" => m.m11.into(),
145                "M12" => m.m12.into(),
146                "M13" => m.m13.into(),
147                "M21" => m.m21.into(),
148                "M22" => m.m22.into(),
149                "M23" => m.m23.into(),
150                "M31" => m.m31.into(),
151                "M32" => m.m32.into(),
152                "M33" => m.m33.into(),
153                _ => V::Invalid,
154            },
155
156            V::Matrix4x4(m) => match property {
157                "M11" => f(m.m11),
158                "M12" => f(m.m12),
159                "M13" => f(m.m13),
160                "M14" => f(m.m14),
161                "M21" => f(m.m21),
162                "M22" => f(m.m22),
163                "M23" => f(m.m23),
164                "M24" => f(m.m24),
165                "M31" => f(m.m31),
166                "M32" => f(m.m32),
167                "M33" => f(m.m33),
168                "M34" => f(m.m34),
169                "M41" => f(m.m41),
170                "M42" => f(m.m42),
171                "M43" => f(m.m43),
172                "M44" => f(m.m44),
173                _ => V::Invalid,
174            },
175
176            V::Quaternion(q) => match property {
177                "X" => f(q.x),
178                "Y" => f(q.y),
179                "Z" => f(q.z),
180                "W" => f(q.w),
181                _ => V::Invalid,
182            },
183
184            V::Color(c) => match property {
185                "A" => (c.a as f64).into(),
186                "R" => (c.r as f64).into(),
187                "G" => (c.g as f64).into(),
188                "B" => (c.b as f64).into(),
189                _ => V::Invalid,
190            },
191
192            // The point coordinates are narrowed to single precision here, as
193            // in the reference implementation.
194            V::RelativePoint(p) => match property {
195                "X" => f(p.point.x as f32),
196                "Y" => f(p.point.y as f32),
197                "Unit" => p.unit.into(),
198                _ => V::Invalid,
199            },
200
201            V::RelativeScalar(s) => match property {
202                "Scalar" => s.scalar.into(),
203                "Unit" => s.unit.into(),
204                _ => V::Invalid,
205            },
206
207            _ => V::Invalid,
208        }
209    }
210
211    /// The `==` operator of expressions: a `Boolean` for two values of the
212    /// same comparable type, `Invalid` otherwise.
213    pub fn equals_to(&self, right: ExpressionVariant) -> ExpressionVariant {
214        match (*self, right) {
215            (V::Double(l), V::Double(r)) => (l == r).into(),
216            (V::Vector2(l), V::Vector2(r)) => (l == r).into(),
217            (V::Vector(l), V::Vector(r)) => (l == r).into(),
218            (V::Vector3(l), V::Vector3(r)) => (l == r).into(),
219            (V::Vector3D(l), V::Vector3D(r)) => (l == r).into(),
220            (V::Vector4(l), V::Vector4(r)) => (l == r).into(),
221            (V::Boolean(l), V::Boolean(r)) => (l == r).into(),
222            (V::Matrix3x2(l), V::Matrix3x2(r)) => (l == r).into(),
223            (V::FerroMatrix(l), V::FerroMatrix(r)) => (l == r).into(),
224            (V::Matrix4x4(l), V::Matrix4x4(r)) => (l == r).into(),
225            (V::Quaternion(l), V::Quaternion(r)) => (l == r).into(),
226            (V::RelativePoint(l), V::RelativePoint(r)) => (l == r).into(),
227            (V::RelativeScalar(l), V::RelativeScalar(r)) => (l == r).into(),
228            (V::RelativeUnit(l), V::RelativeUnit(r)) => (l == r).into(),
229            _ => V::Invalid,
230        }
231    }
232
233    /// The `!=` operator of expressions.
234    pub fn not_equals_to(&self, right: ExpressionVariant) -> ExpressionVariant {
235        match self.equals_to(right) {
236            V::Boolean(r) => (!r).into(),
237            _ => V::Invalid,
238        }
239    }
240
241    /// The `<` operator of expressions.
242    pub fn less_than(&self, right: ExpressionVariant) -> ExpressionVariant {
243        match (*self, right) {
244            (V::Double(l), V::Double(r)) => (l < r).into(),
245            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => (l.scalar < r.scalar).into(),
246            _ => V::Invalid,
247        }
248    }
249
250    /// The `>` operator of expressions.
251    pub fn more_than(&self, right: ExpressionVariant) -> ExpressionVariant {
252        match (*self, right) {
253            (V::Double(l), V::Double(r)) => (l > r).into(),
254            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => (l.scalar > r.scalar).into(),
255            _ => V::Invalid,
256        }
257    }
258
259    /// The `&&` operator of expressions.
260    pub fn and(&self, right: ExpressionVariant) -> ExpressionVariant {
261        match (*self, right) {
262            (V::Boolean(l), V::Boolean(r)) => (l && r).into(),
263            _ => V::Invalid,
264        }
265    }
266
267    /// The `||` operator of expressions.
268    pub fn or(&self, right: ExpressionVariant) -> ExpressionVariant {
269        match (*self, right) {
270            (V::Boolean(l), V::Boolean(r)) => (l || r).into(),
271            _ => V::Invalid,
272        }
273    }
274
275    /// Converts the held value to `T`, or `None` when the variant does not
276    /// hold a value convertible to `T`.
277    #[inline]
278    pub fn try_cast<T: ExpressionVariantValue>(&self) -> Option<T> {
279        T::try_from_variant(self)
280    }
281
282    /// Creates a variant from a value.
283    #[inline]
284    pub fn create<T: ExpressionVariantValue>(v: T) -> ExpressionVariant {
285        v.into_variant()
286    }
287
288    /// Converts the held value to `T`, or gives the default value of `T`.
289    #[inline]
290    pub fn cast_or_default<T: ExpressionVariantValue>(&self) -> T {
291        self.try_cast::<T>().unwrap_or_default()
292    }
293}
294
295/// A single-precision member widened to the `Double` variant.
296#[inline]
297fn f(value: f32) -> ExpressionVariant {
298    V::Double(value as f64)
299}
300
301/// A value type an [`ExpressionVariant`] can be created from and cast to.
302pub trait ExpressionVariantValue: Copy + Default + 'static {
303    /// The variant type a foreign function parameter of this type is matched
304    /// against, or `None` when the type cannot be a foreign function parameter.
305    const FFI_VARIANT_TYPE: Option<VariantType>;
306
307    /// See [`ExpressionVariant::try_cast`].
308    fn try_from_variant(variant: &ExpressionVariant) -> Option<Self>;
309
310    /// See [`ExpressionVariant::create`].
311    fn into_variant(self) -> ExpressionVariant;
312}
313
314macro_rules! variant_value {
315    ($ty:ty, $variant:ident, $ffi:expr, |$v:ident| $cast:expr) => {
316        impl From<$ty> for ExpressionVariant {
317            #[inline]
318            fn from(value: $ty) -> ExpressionVariant {
319                ExpressionVariant::$variant(value)
320            }
321        }
322
323        impl ExpressionVariantValue for $ty {
324            const FFI_VARIANT_TYPE: Option<VariantType> = $ffi;
325
326            #[inline]
327            fn try_from_variant($v: &ExpressionVariant) -> Option<Self> {
328                $cast
329            }
330
331            #[inline]
332            fn into_variant(self) -> ExpressionVariant {
333                ExpressionVariant::$variant(self)
334            }
335        }
336    };
337    ($ty:ty, $variant:ident, $ffi:expr) => {
338        variant_value!($ty, $variant, $ffi, |v| match *v {
339            ExpressionVariant::$variant(value) => Some(value),
340            _ => None,
341        });
342    };
343}
344
345variant_value!(bool, Boolean, Some(VariantType::Boolean));
346variant_value!(f64, Double, Some(VariantType::Double));
347variant_value!(Vector2, Vector2, Some(VariantType::Vector2), |v| match *v {
348    V::Vector2(value) => Some(value),
349    V::Vector(value) => Some(value.into()),
350    _ => None,
351});
352variant_value!(Vector, Vector, Some(VariantType::Vector), |v| match *v {
353    V::Vector(value) => Some(value),
354    V::Vector2(value) => Some(value.into()),
355    _ => None,
356});
357variant_value!(Vector3, Vector3, Some(VariantType::Vector3), |v| match *v {
358    V::Vector3(value) => Some(value),
359    V::Vector3D(value) => Some(value.into()),
360    _ => None,
361});
362variant_value!(Vector3D, Vector3D, Some(VariantType::Vector3D), |v| match *v {
363    V::Vector3D(value) => Some(value),
364    V::Vector3(value) => Some(value.into()),
365    _ => None,
366});
367variant_value!(Vector4, Vector4, Some(VariantType::Vector4));
368variant_value!(Matrix3x2, Matrix3x2, Some(VariantType::Matrix3x2));
369// The 3x3 matrix is not in the foreign function type map of the reference
370// implementation.
371variant_value!(Matrix, FerroMatrix, None);
372variant_value!(Matrix4x4, Matrix4x4, Some(VariantType::Matrix4x4));
373variant_value!(Quaternion, Quaternion, Some(VariantType::Quaternion));
374variant_value!(Color, Color, Some(VariantType::Color));
375variant_value!(RelativePoint, RelativePoint, Some(VariantType::RelativePoint));
376variant_value!(RelativeScalar, RelativeScalar, Some(VariantType::RelativeScalar));
377variant_value!(RelativeUnit, RelativeUnit, Some(VariantType::RelativeUnit));
378
379/// A single-precision scalar is stored as a `Double`.
380impl From<f32> for ExpressionVariant {
381    #[inline]
382    fn from(value: f32) -> ExpressionVariant {
383        V::Double(value as f64)
384    }
385}
386
387impl ExpressionVariantValue for f32 {
388    const FFI_VARIANT_TYPE: Option<VariantType> = Some(VariantType::Double);
389
390    #[inline]
391    fn try_from_variant(variant: &ExpressionVariant) -> Option<Self> {
392        match *variant {
393            V::Double(value) => Some(value as f32),
394            _ => None,
395        }
396    }
397
398    #[inline]
399    fn into_variant(self) -> ExpressionVariant {
400        self.into()
401    }
402}
403
404impl Add for ExpressionVariant {
405    type Output = ExpressionVariant;
406
407    fn add(self, right: ExpressionVariant) -> ExpressionVariant {
408        match (self, right) {
409            (V::Double(l), V::Double(r)) => (l + r).into(),
410            (V::Vector2(l), V::Vector2(r)) => (l + r).into(),
411            (V::Vector(l), V::Vector(r)) => (l + r).into(),
412            (V::Vector3(l), V::Vector3(r)) => (l + r).into(),
413            (V::Vector3D(l), V::Vector3D(r)) => Vector3D::add(l, r).into(),
414            (V::Vector4(l), V::Vector4(r)) => (l + r).into(),
415            (V::Matrix3x2(l), V::Matrix3x2(r)) => (l + r).into(),
416            (V::Matrix4x4(l), V::Matrix4x4(r)) => (l + r).into(),
417            (V::Quaternion(l), V::Quaternion(r)) => (l + r).into(),
418            (V::RelativePoint(l), V::RelativePoint(r)) if l.unit == r.unit => {
419                RelativePoint::new(l.point.x + r.point.x, l.point.y + r.point.y, l.unit).into()
420            }
421            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => {
422                RelativeScalar::new(l.scalar + r.scalar, l.unit).into()
423            }
424            _ => V::Invalid,
425        }
426    }
427}
428
429impl Sub for ExpressionVariant {
430    type Output = ExpressionVariant;
431
432    fn sub(self, right: ExpressionVariant) -> ExpressionVariant {
433        match (self, right) {
434            (V::Double(l), V::Double(r)) => (l - r).into(),
435            (V::Vector2(l), V::Vector2(r)) => (l - r).into(),
436            (V::Vector(l), V::Vector(r)) => (l - r).into(),
437            (V::Vector3(l), V::Vector3(r)) => (l - r).into(),
438            (V::Vector3D(l), V::Vector3D(r)) => Vector3D::add(l, -r).into(),
439            (V::Vector4(l), V::Vector4(r)) => (l - r).into(),
440            (V::Matrix3x2(l), V::Matrix3x2(r)) => (l - r).into(),
441            (V::Matrix4x4(l), V::Matrix4x4(r)) => (l - r).into(),
442            (V::Quaternion(l), V::Quaternion(r)) => (l - r).into(),
443            (V::RelativePoint(l), V::RelativePoint(r)) if l.unit == r.unit => {
444                RelativePoint::new(l.point.x - r.point.x, l.point.y - r.point.y, l.unit).into()
445            }
446            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => {
447                RelativeScalar::new(l.scalar - r.scalar, l.unit).into()
448            }
449            _ => V::Invalid,
450        }
451    }
452}
453
454impl Neg for ExpressionVariant {
455    type Output = ExpressionVariant;
456
457    fn neg(self) -> ExpressionVariant {
458        match self {
459            V::Double(l) => (-l).into(),
460            V::Vector2(l) => (-l).into(),
461            V::Vector(l) => (-l).into(),
462            V::Vector3(l) => (-l).into(),
463            V::Vector3D(l) => (-l).into(),
464            V::Vector4(l) => (-l).into(),
465            V::Matrix3x2(l) => (-l).into(),
466            V::FerroMatrix(l) => (-l).into(),
467            V::Matrix4x4(l) => (-l).into(),
468            V::Quaternion(l) => (-l).into(),
469            V::RelativePoint(l) => RelativePoint::new(-l.point.x, -l.point.y, l.unit).into(),
470            V::RelativeScalar(l) => RelativeScalar::new(-l.scalar, l.unit).into(),
471            _ => V::Invalid,
472        }
473    }
474}
475
476impl Mul for ExpressionVariant {
477    type Output = ExpressionVariant;
478
479    fn mul(self, right: ExpressionVariant) -> ExpressionVariant {
480        match (self, right) {
481            (V::Double(l), V::Double(r)) => (l * r).into(),
482            (V::Vector2(l), V::Vector2(r)) => (l * r).into(),
483            (V::Vector(l), V::Vector(r)) => Vector::multiply(l, r).into(),
484            (V::Vector2(l), V::Double(r)) => (l * (r as f32)).into(),
485            (V::Vector(l), V::Double(r)) => (l * r).into(),
486            (V::Vector3(l), V::Vector3(r)) => (l * r).into(),
487            (V::Vector3D(l), V::Vector3D(r)) => Vector3D::multiply(l, r).into(),
488            (V::Vector3(l), V::Double(r)) => (l * (r as f32)).into(),
489            (V::Vector3D(l), V::Double(r)) => Vector3D::multiply_scalar(l, r).into(),
490            (V::Vector4(l), V::Vector4(r)) => (l * r).into(),
491            (V::Vector4(l), V::Double(r)) => (l * (r as f32)).into(),
492            (V::Matrix3x2(l), V::Matrix3x2(r)) => (l * r).into(),
493            (V::Matrix3x2(l), V::Double(r)) => (l * (r as f32)).into(),
494            (V::FerroMatrix(l), V::FerroMatrix(r)) => (l * r).into(),
495            (V::Matrix4x4(l), V::Matrix4x4(r)) => (l * r).into(),
496            (V::Matrix4x4(l), V::Double(r)) => (l * (r as f32)).into(),
497            (V::Quaternion(l), V::Quaternion(r)) => (l * r).into(),
498            (V::Quaternion(l), V::Double(r)) => (l * (r as f32)).into(),
499            (V::RelativePoint(l), V::Double(r)) => RelativePoint::new(l.point.x * r, l.point.y * r, l.unit).into(),
500            (V::Double(l), V::RelativePoint(r)) => RelativePoint::new(l * r.point.x, l * r.point.y, r.unit).into(),
501            (V::RelativeScalar(l), V::Double(r)) => RelativeScalar::new(l.scalar * r, l.unit).into(),
502            (V::Double(l), V::RelativeScalar(r)) => RelativeScalar::new(l * r.scalar, r.unit).into(),
503            (V::RelativePoint(l), V::RelativePoint(r)) if l.unit == r.unit => {
504                RelativePoint::new(l.point.x * r.point.x, l.point.y * r.point.y, l.unit).into()
505            }
506            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => {
507                RelativeScalar::new(l.scalar * r.scalar, l.unit).into()
508            }
509            _ => V::Invalid,
510        }
511    }
512}
513
514impl Div for ExpressionVariant {
515    type Output = ExpressionVariant;
516
517    fn div(self, right: ExpressionVariant) -> ExpressionVariant {
518        match (self, right) {
519            (V::Double(l), V::Double(r)) => (l / r).into(),
520            (V::Vector2(l), V::Vector2(r)) => (l / r).into(),
521            (V::Vector(l), V::Vector(r)) => Vector::divide(l, r).into(),
522            (V::Vector2(l), V::Double(r)) => (l / (r as f32)).into(),
523            (V::Vector(l), V::Double(r)) => (l / r).into(),
524            (V::Vector3(l), V::Vector3(r)) => (l / r).into(),
525            (V::Vector3D(l), V::Vector3D(r)) => Vector3D::divide(l, r).into(),
526            (V::Vector3(l), V::Double(r)) => (l / (r as f32)).into(),
527            (V::Vector3D(l), V::Double(r)) => Vector3D::divide_scalar(l, r).into(),
528            (V::Vector4(l), V::Vector4(r)) => (l / r).into(),
529            (V::Vector4(l), V::Double(r)) => (l / (r as f32)).into(),
530            (V::Quaternion(l), V::Quaternion(r)) => (l / r).into(),
531            (V::RelativePoint(l), V::Double(r)) => RelativePoint::new(l.point.x / r, l.point.y / r, l.unit).into(),
532            (V::RelativeScalar(l), V::Double(r)) => RelativeScalar::new(l.scalar / r, l.unit).into(),
533            (V::RelativePoint(l), V::RelativePoint(r)) if l.unit == r.unit => {
534                RelativePoint::new(l.point.x / r.point.x, l.point.y / r.point.y, l.unit).into()
535            }
536            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => {
537                RelativeScalar::new(l.scalar / r.scalar, l.unit).into()
538            }
539            _ => V::Invalid,
540        }
541    }
542}
543
544impl Not for ExpressionVariant {
545    type Output = ExpressionVariant;
546
547    fn not(self) -> ExpressionVariant {
548        match self {
549            V::Boolean(v) => (!v).into(),
550            _ => V::Invalid,
551        }
552    }
553}
554
555impl Rem for ExpressionVariant {
556    type Output = ExpressionVariant;
557
558    fn rem(self, right: ExpressionVariant) -> ExpressionVariant {
559        match (self, right) {
560            (V::Double(l), V::Double(r)) => (l % r).into(),
561            (V::RelativeScalar(l), V::RelativeScalar(r)) if l.unit == r.unit => {
562                RelativeScalar::new(l.scalar % r.scalar, l.unit).into()
563            }
564            _ => V::Invalid,
565        }
566    }
567}
568
569impl fmt::Display for ExpressionVariant {
570    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571        match self {
572            V::Boolean(v) => f.write_str(if *v { "True" } else { "False" }),
573            V::Double(v) => write!(f, "{}", InvariantF64(*v)),
574            V::Vector2(v) => v.fmt(f),
575            V::Vector(v) => v.fmt(f),
576            V::Vector3(v) => v.fmt(f),
577            V::Vector3D(v) => v.fmt(f),
578            V::Vector4(v) => v.fmt(f),
579            V::Quaternion(v) => v.fmt(f),
580            V::Matrix3x2(v) => v.fmt(f),
581            V::FerroMatrix(v) => v.fmt(f),
582            V::Matrix4x4(v) => v.fmt(f),
583            V::Color(v) => v.fmt(f),
584            V::RelativePoint(v) => v.fmt(f),
585            V::RelativeScalar(v) => v.fmt(f),
586            V::RelativeUnit(v) => fmt::Debug::fmt(v, f),
587            V::Invalid => f.write_str("Invalid"),
588        }
589    }
590}