#![allow(dead_code)] use std::ops::{Add, Mul}; use crate::point::Point; use crate::sealed::Sealed; use crate::wide4::Float4; /// A forward affine transform. /// /// Use this for working with transforms that still need to be /// manipulated or composed with other transforms, or for storing /// transforms more compactly. /// /// Note: slightly counter-intuitively, even though this can perform /// forward (but not inverse) transforms on points and vectors, it is /// capable of *inverse* (but not forward) transforms on surface normals. /// This is because forward transforms on surface normals require the /// inverse transform matrix. /// /// Convert to an [`XformFull`] for a larger-format type capable of /// efficiently performing both forward and inverse transforms on all /// types, but which is effectively "frozen" in terms of further /// manipulation of the transform itself. #[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] pub struct Xform { /// Rotation/scale/shear matrix. pub m: [Float4; 3], /// Translation. pub t: Float4, } impl Xform { /// Creates a new affine transform with the specified values: /// /// ```text /// a d g j /// b e h k /// c f i l /// ``` /// /// Where j, k, and l are the xyz translation component. #[inline] #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] pub fn new( a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32, h: f32, i: f32, j: f32, k: f32, l: f32, ) -> Self { Self { m: [ Float4::new(a, b, c, 0.0), Float4::new(d, e, f, 0.0), Float4::new(g, h, i, 0.0), ], t: Float4::new(j, k, l, 0.0), } } /// Creates a new identity transform. #[inline] pub fn identity() -> Self { Self { m: [ Float4::new(1.0, 0.0, 0.0, 0.0), Float4::new(0.0, 1.0, 0.0, 0.0), Float4::new(0.0, 0.0, 1.0, 0.0), ], t: Float4::splat(0.0), } } #[inline] pub fn from_location(loc: Point) -> Xform { Self { m: [ Float4::new(1.0, 0.0, 0.0, 0.0), Float4::new(0.0, 1.0, 0.0, 0.0), Float4::new(0.0, 0.0, 1.0, 0.0), ], t: loc.0, } } /// Returns whether the matrices are approximately equal to each other. /// Each corresponding element in the matrices cannot have a relative /// error exceeding epsilon. pub(crate) fn aprx_eq(&self, other: Xform, max_ulps: u32) -> bool { let mut eq = true; eq &= Float4::aprx_eq(self.m[0], other.m[0], max_ulps); eq &= Float4::aprx_eq(self.m[1], other.m[1], max_ulps); eq &= Float4::aprx_eq(self.m[2], other.m[2], max_ulps); eq &= Float4::aprx_eq(self.t, other.t, max_ulps); eq } /// Compute the "full" version of the transform. #[inline] pub fn to_full(&self) -> Option { if let Some(inv_m) = Float4::invert_3x3(&self.m) { Some(XformFull { fwd: *self, inv_m: inv_m, }) } else { None } } /// Faster but less precise version of `to_full()`. #[inline] pub fn to_full_fast(&self) -> Option { if let Some(inv_m) = Float4::invert_3x3_fast(&self.m) { Some(XformFull { fwd: *self, inv_m: inv_m, }) } else { None } } /// Composes two transforms together. /// /// The resulting transform is the same as doing `self` and then /// `rhs` in sequence. #[inline] pub fn compose(&self, rhs: &Self) -> Self { let (m, t) = Float4::affine_mul_affine(&self.m, self.t, &rhs.m, rhs.t); Self { m: m, t: t } } /// Composes two transforms together. /// /// Faster but less precise version. #[inline] pub fn compose_fast(&self, rhs: &Self) -> Self { let (m, t) = Float4::affine_mul_affine_fast(&self.m, self.t, &rhs.m, rhs.t); Self { m: m, t: t } } } impl Default for Xform { fn default() -> Self { Self::identity() } } /// Multiply a matrix by a f32 impl Mul for Xform { type Output = Self; #[inline] fn mul(self, rhs: f32) -> Self { Self { m: [self.m[0] * rhs, self.m[1] * rhs, self.m[2] * rhs], t: self.t * rhs, } } } /// Add two matrices together impl Add for Xform { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { Self { m: [ self.m[0] + rhs.m[0], self.m[1] + rhs.m[1], self.m[2] + rhs.m[2], ], t: self.t + rhs.t, } } } impl AsXform for Xform { #[inline(always)] fn as_xform(&self) -> &Xform { self } } impl Sealed for Xform {} //------------------------------------------------------------- /// A combined forward/inverse affine transform. /// /// Unlike [`Xform`], this can perform both forward and inverse /// transforms on all types. However, it also takes up more space and /// is effectively "frozen" in terms of further manipulation. Prefer /// [`Xform`] when manipulating or composing transforms, and also /// when storing transforms if space is a consideration. /// /// Note: only the 3x3 part of the transform is stored inverted. This /// is because it's both trivial and more numerically stable to reuse /// the forward translation vector to do inverse transforms, as /// `(point - fwd.t) * inv_m`. #[derive(Debug, Copy, Clone)] #[repr(C)] pub struct XformFull { /// Forward transform. pub fwd: Xform, /// Inverse rotation/scale/shear matrix. pub inv_m: [Float4; 3], } impl XformFull { pub fn identity() -> Self { Self { fwd: Xform { m: [ Float4::new(1.0, 0.0, 0.0, 0.0), Float4::new(0.0, 1.0, 0.0, 0.0), Float4::new(0.0, 0.0, 1.0, 0.0), ], t: Float4::splat(0.0), }, inv_m: [ Float4::new(1.0, 0.0, 0.0, 0.0), Float4::new(0.0, 1.0, 0.0, 0.0), Float4::new(0.0, 0.0, 1.0, 0.0), ], } } } impl AsXform for XformFull { #[inline(always)] fn as_xform(&self) -> &Xform { &self.fwd } } impl Sealed for XformFull {} //------------------------------------------------------------- pub trait AsXform: Sealed { fn as_xform(&self) -> &Xform; } //------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; #[test] fn equality() { let a = Xform::identity(); let b = Xform::identity(); let c = Xform::new(1.1, 0.0, 0.0, 0.0, 0.0, 1.1, 0.0, 0.0, 0.0, 0.0, 1.1, 0.0); assert_eq!(a, b); assert!(a != c); } #[test] fn approximate_equality() { let a = Xform::identity(); let b = Xform::new( 1.000001, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0, ); let c = Xform::new( 1.000003, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0, ); assert!(a.aprx_eq(b, 10)); assert!(!a.aprx_eq(b, 6)); assert!(a.aprx_eq(c, 27)); assert!(!a.aprx_eq(c, 23)); } #[test] fn compose() { let a = Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0); let b = Xform::new( 1.0, 2.0, 3.0, 5.0, 6.0, 7.0, 9.0, 10.0, 11.0, 13.0, 14.0, 15.0, ); let c = Xform::new( 97.0, 110.0, 123.0, 50.0, 60.0, 70.0, 136.0, 156.0, 176.0, 162.5, 185.0, 207.5, ); assert_eq!(a.compose(&b), c); assert_eq!(a.compose_fast(&b), c); } }