glamour/
transform.rs

1//! Strongly typed 2D and 3D transforms.
2//!
3//! # Chaining transforms
4//!
5//!
6
7use core::ops::Mul;
8use core::{fmt::Debug, marker::PhantomData};
9
10use approx::{AbsDiffEq, RelativeEq, UlpsEq};
11use bytemuck::{Pod, Zeroable, cast};
12
13use crate::peel;
14use crate::{
15    Angle, Matrix3, Matrix4, Point2, Point3, Transparent, Unit, Vector2, Vector3,
16    bindings::prelude::*, scalar::FloatScalar,
17};
18
19/// 2D transform represented as a 3x3 column-major matrix.
20///
21/// This is a strongly typed wrapper around a [`Matrix3`], where that matrix
22/// describes how to map between units.
23#[repr(transparent)]
24#[cfg_attr(feature = "facet", derive(facet_derive::Facet))]
25pub struct Transform2<Src: Unit, Dst: Unit> {
26    /// Underlying matrix.
27    pub matrix: Matrix3<Src::Scalar>,
28    _marker: PhantomData<Dst>,
29}
30
31/// 3D transform represented as a 4x4 column-major matrix.
32///
33/// This is a strongly typed wrapper around a [`Matrix4`], where that matrix
34/// describes how to map between units.
35#[repr(transparent)]
36#[cfg_attr(feature = "facet", derive(facet_derive::Facet))]
37pub struct Transform3<Src: Unit, Dst: Unit> {
38    /// Underlying matrix.
39    pub matrix: Matrix4<Src::Scalar>,
40    _marker: PhantomData<Dst>,
41}
42
43/// The mapping operation from one unit to another through a transform.
44pub trait TransformMap<T> {
45    /// Result type of the transformation.
46    type Output;
47
48    /// Map `T` to `Self::Output`.
49    #[must_use]
50    fn map(&self, value: T) -> Self::Output;
51}
52
53impl<Src: Unit, Dst: Unit> Clone for Transform2<Src, Dst> {
54    fn clone(&self) -> Self {
55        *self
56    }
57}
58
59impl<Src: Unit, Dst: Unit> Clone for Transform3<Src, Dst> {
60    fn clone(&self) -> Self {
61        *self
62    }
63}
64
65impl<Src: Unit, Dst: Unit> Copy for Transform2<Src, Dst> {}
66impl<Src: Unit, Dst: Unit> Copy for Transform3<Src, Dst> {}
67
68impl<Src, Dst> Default for Transform2<Src, Dst>
69where
70    Src: Unit,
71    Src::Scalar: FloatScalar,
72    Dst: Unit,
73{
74    #[inline]
75    fn default() -> Self {
76        Self {
77            matrix: Matrix3::default(),
78            _marker: PhantomData,
79        }
80    }
81}
82
83impl<Src, Dst> Default for Transform3<Src, Dst>
84where
85    Src: Unit,
86    Src::Scalar: FloatScalar,
87    Dst: Unit,
88{
89    #[inline]
90    fn default() -> Self {
91        Self {
92            matrix: Matrix4::default(),
93            _marker: PhantomData,
94        }
95    }
96}
97
98impl<Src, Dst> Debug for Transform2<Src, Dst>
99where
100    Src: Unit,
101    Src::Scalar: FloatScalar,
102    Dst: Unit,
103{
104    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105        f.debug_struct("Transform2")
106            .field("matrix", &self.matrix)
107            .finish()
108    }
109}
110
111impl<Src, Dst> Debug for Transform3<Src, Dst>
112where
113    Src: Unit,
114    Src::Scalar: FloatScalar,
115    Dst: Unit,
116{
117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        f.debug_struct("Transform3")
119            .field("matrix", &self.matrix)
120            .finish()
121    }
122}
123
124impl<Src, Dst> PartialEq for Transform2<Src, Dst>
125where
126    Src: Unit,
127    Dst: Unit,
128{
129    fn eq(&self, other: &Self) -> bool {
130        self.matrix == other.matrix
131    }
132}
133
134impl<Src, Dst> AbsDiffEq for Transform2<Src, Dst>
135where
136    Src: Unit,
137    Src::Scalar: FloatScalar,
138    Dst: Unit<Scalar = Src::Scalar>,
139{
140    type Epsilon = <Matrix3<Src::Scalar> as AbsDiffEq>::Epsilon;
141
142    fn default_epsilon() -> Self::Epsilon {
143        <Matrix3<Src::Scalar> as AbsDiffEq>::default_epsilon()
144    }
145
146    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
147        self.matrix.abs_diff_eq(&other.matrix, epsilon)
148    }
149}
150
151impl<Src, Dst> RelativeEq for Transform2<Src, Dst>
152where
153    Src: Unit,
154    Src::Scalar: FloatScalar,
155    Dst: Unit<Scalar = Src::Scalar>,
156{
157    fn default_max_relative() -> Self::Epsilon {
158        <Matrix3<Src::Scalar> as RelativeEq>::default_max_relative()
159    }
160
161    fn relative_eq(
162        &self,
163        other: &Self,
164        epsilon: Self::Epsilon,
165        max_relative: Self::Epsilon,
166    ) -> bool {
167        self.matrix
168            .relative_eq(&other.matrix, epsilon, max_relative)
169    }
170}
171
172impl<Src, Dst> UlpsEq for Transform2<Src, Dst>
173where
174    Src: Unit,
175    Src::Scalar: FloatScalar,
176    Dst: Unit<Scalar = Src::Scalar>,
177{
178    fn default_max_ulps() -> u32 {
179        <Matrix3<Src::Scalar> as UlpsEq>::default_max_ulps()
180    }
181
182    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
183        self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps)
184    }
185}
186
187impl<Src: Unit, Dst: Unit> PartialEq for Transform3<Src, Dst> {
188    fn eq(&self, other: &Self) -> bool {
189        self.matrix == other.matrix
190    }
191}
192
193impl<Src, Dst> AbsDiffEq for Transform3<Src, Dst>
194where
195    Src: Unit,
196    Src::Scalar: FloatScalar,
197    Dst: Unit,
198{
199    type Epsilon = <Matrix4<Src::Scalar> as AbsDiffEq>::Epsilon;
200
201    fn default_epsilon() -> Self::Epsilon {
202        <Matrix4<Src::Scalar> as AbsDiffEq>::default_epsilon()
203    }
204
205    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
206        self.matrix.abs_diff_eq(&other.matrix, epsilon)
207    }
208}
209
210impl<Src, Dst> RelativeEq for Transform3<Src, Dst>
211where
212    Src: Unit,
213    Src::Scalar: FloatScalar,
214    Dst: Unit,
215{
216    fn default_max_relative() -> Self::Epsilon {
217        <Matrix4<Src::Scalar> as RelativeEq>::default_max_relative()
218    }
219
220    fn relative_eq(
221        &self,
222        other: &Self,
223        epsilon: Self::Epsilon,
224        max_relative: Self::Epsilon,
225    ) -> bool {
226        self.matrix
227            .relative_eq(&other.matrix, epsilon, max_relative)
228    }
229}
230
231impl<Src, Dst> UlpsEq for Transform3<Src, Dst>
232where
233    Src: Unit,
234    Src::Scalar: FloatScalar,
235    Dst: Unit,
236{
237    fn default_max_ulps() -> u32 {
238        <Matrix4<Src::Scalar> as UlpsEq>::default_max_ulps()
239    }
240
241    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
242        self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps)
243    }
244}
245
246// SAFETY: These impls are safe because all members are required to be Pod by
247// trait bounds in Scalar.
248unsafe impl<Src: Unit, Dst: Unit> Zeroable for Transform2<Src, Dst> {}
249unsafe impl<Src: Unit, Dst: Unit> Pod for Transform2<Src, Dst> {}
250unsafe impl<Src: Unit, Dst: Unit> Zeroable for Transform3<Src, Dst> {}
251unsafe impl<Src: Unit, Dst: Unit> Pod for Transform3<Src, Dst> {}
252impl<Src: Unit, Dst: Unit> Transparent for Transform2<Src, Dst> {
253    type Wrapped = Matrix3<Src::Scalar>;
254}
255impl<Src: Unit, Dst: Unit> Transparent for Transform3<Src, Dst> {
256    type Wrapped = Matrix4<Src::Scalar>;
257}
258
259impl<Src, Dst> Transform2<Src, Dst>
260where
261    Src: Unit,
262    Src::Scalar: FloatScalar,
263    Dst: Unit<Scalar = Src::Scalar>,
264{
265    /// Identity matrix.
266    pub const IDENTITY: Self = Self {
267        matrix: Matrix3::IDENTITY,
268        _marker: PhantomData,
269    };
270
271    /// Create from matrix.
272    #[inline]
273    #[must_use]
274    pub const fn from_matrix_unchecked(matrix: Matrix3<Src::Scalar>) -> Self {
275        Transform2 {
276            matrix,
277            _marker: PhantomData,
278        }
279    }
280
281    /// Create from matrix, checking if the matrix is invertible.
282    #[inline]
283    #[must_use]
284    pub fn from_matrix(matrix: Matrix3<Src::Scalar>) -> Option<Self> {
285        if matrix.is_invertible() {
286            Some(Self::from_matrix_unchecked(matrix))
287        } else {
288            None
289        }
290    }
291
292    /// Create rotation transform.
293    ///
294    /// #### Example
295    /// ```rust
296    /// # use glamour::prelude::*;
297    /// # use approx::*;
298    /// struct A;
299    /// impl Unit for A { type Scalar = f64; }
300    /// struct B;
301    /// impl Unit for B { type Scalar = f64; }
302    ///
303    /// type Transform = Transform2<A, B>;
304    ///
305    /// let translate = Transform::from_angle(Angle::FRAG_PI_2);
306    /// let a: Vector2<A> = Vector2 { x: 10.0, y: 20.0 };
307    /// let b: Vector2<B> = translate.map(a);
308    /// assert_abs_diff_eq!(b, vec2!(-20.0, 10.0), epsilon = 0.000001);
309    /// ```
310    #[inline]
311    #[must_use]
312    pub fn from_angle(angle: Angle<Src::Scalar>) -> Self {
313        Self::from_matrix_unchecked(Matrix3::from_angle(Angle {
314            radians: angle.radians,
315        }))
316    }
317
318    /// Create scaling transform.
319    ///
320    /// #### Example
321    /// ```rust
322    /// # use glamour::prelude::*;
323    /// # use approx::*;
324    /// struct A;
325    /// impl Unit for A { type Scalar = f32; }
326    /// struct B;
327    /// impl Unit for B { type Scalar = f32; }
328    ///
329    /// type Transform = Transform2<A, B>;
330    ///
331    /// let translate = Transform::from_scale(Vector2 { x: 2.0, y: 3.0 });
332    /// let a: Vector2<A> = Vector2 { x: 10.0, y: 20.0 };
333    /// let b: Vector2<B> = translate.map(a);
334    /// assert_abs_diff_eq!(b, vec2!(20.0, 60.0));
335    /// ```
336    #[inline]
337    #[must_use]
338    pub fn from_scale(scale: Vector2<Src>) -> Self {
339        Self::from_matrix_unchecked(Matrix3::from_scale(scale.to_untyped()))
340    }
341
342    /// Create translation transform.
343    ///
344    /// #### Example
345    /// ```rust
346    /// # use glamour::prelude::*;
347    /// # use approx::*;
348    /// struct A;
349    /// impl Unit for A { type Scalar = f32; }
350    /// struct B;
351    /// impl Unit for B { type Scalar = f32; }
352    ///
353    /// type Transform = Transform2<A, B>;
354    ///
355    /// let translate = Transform::from_translation(Vector2 { x: 10.0, y: 20.0 });
356    /// let a: Point2<A> = Point2 { x: 1.0, y: 2.0 };
357    /// let b: Point2<B> = translate.map(a);
358    /// assert_abs_diff_eq!(b, point!(11.0, 22.0));
359    /// ```
360    #[inline]
361    #[must_use]
362    pub fn from_translation(translation: Vector2<Src>) -> Self {
363        Self::from_matrix_unchecked(Matrix3::from_translation(translation.to_untyped()))
364    }
365
366    /// Create scaling, rotation, and translation matrix.
367    ///
368    /// #### Example
369    /// ```rust
370    /// # use glamour::prelude::*;
371    /// # use approx::*;
372    ///
373    /// type Transform = Transform2<f32, f32>;
374    ///
375    /// let a = Transform::from_scale_angle_translation(
376    ///     (2.0, 3.0).into(),
377    ///     Angle::from_degrees(90.0),
378    ///     (10.0, 20.0).into(),
379    /// );
380    ///
381    /// let b = Transform::from_scale((2.0, 3.0).into())
382    ///     .then_rotate(Angle::from_degrees(90.0))
383    ///     .then_translate((10.0, 20.0).into());
384    ///
385    /// assert_abs_diff_eq!(a, b);
386    #[inline]
387    #[must_use]
388    pub fn from_scale_angle_translation(
389        scale: Vector2<Src>,
390        angle: Angle<Src::Scalar>,
391        translation: Vector2<Src>,
392    ) -> Self {
393        Self::from_matrix_unchecked(Matrix3::from_scale_angle_translation(
394            scale.to_untyped(),
395            angle,
396            translation.to_untyped(),
397        ))
398    }
399}
400
401impl<Src, Dst> TransformMap<Point2<Src>> for Transform2<Src, Dst>
402where
403    Src: Unit,
404    Src::Scalar: FloatScalar,
405    Dst: Unit<Scalar = Src::Scalar>,
406{
407    type Output = Point2<Dst>;
408
409    #[must_use]
410    fn map(&self, value: Point2<Src>) -> Self::Output {
411        Point2::from_untyped(self.matrix.transform_point(value.to_untyped()))
412    }
413}
414
415impl<Src, Dst> TransformMap<Vector2<Src>> for Transform2<Src, Dst>
416where
417    Src: Unit,
418    Src::Scalar: FloatScalar,
419    Dst: Unit<Scalar = Src::Scalar>,
420{
421    type Output = Vector2<Dst>;
422
423    #[must_use]
424    fn map(&self, value: Vector2<Src>) -> Self::Output {
425        Vector2::from_untyped(self.matrix.transform_vector(value.to_untyped()))
426    }
427}
428
429impl<Src, Dst> Transform2<Src, Dst>
430where
431    Src: Unit,
432    Src::Scalar: FloatScalar,
433    Dst: Unit<Scalar = Src::Scalar>,
434{
435    /// Perform matrix multiplication such that `other`'s transformation applies
436    /// after `self`.
437    ///
438    /// This may change the target coordinate space - that is, the resulting
439    /// transform takes points and vectors from `Src` to `Dst2`.
440    ///
441    /// This operation is equivalent to `other.matrix * self.matrix` - that is,
442    /// the multiplication order is the reverse of the order of the effects of
443    /// the transformation.
444    #[inline]
445    #[must_use]
446    pub fn then<Dst2>(self, other: Transform2<Dst, Dst2>) -> Transform2<Src, Dst2>
447    where
448        Dst2: Unit<Scalar = Dst::Scalar>,
449    {
450        Transform2::from_matrix_unchecked(other.matrix * self.matrix)
451    }
452
453    /// Shorthand for `.then(Transform2::from_angle(angle))`.
454    ///
455    /// This does not change the target coordinate space.
456    #[inline]
457    #[must_use]
458    pub fn then_rotate(self, angle: Angle<Src::Scalar>) -> Self {
459        self.then(Transform2::from_angle(angle))
460    }
461
462    /// Shorthand for `.then(Transform2::from_scale(scale))`.
463    ///
464    /// This does not change the target coordinate space.
465    ///
466    /// #### Example
467    ///
468    /// ```rust
469    /// # use glamour::prelude::*;
470    /// let a = Transform2::<f32, f32>::IDENTITY
471    ///     .then_scale((2.0, 3.0).into());
472    /// let b = Transform2::<f32, f32>::from_scale((2.0, 3.0).into());
473    /// assert_eq!(a, b);
474    /// ```
475    #[inline]
476    #[must_use]
477    pub fn then_scale(self, scale: Vector2<Dst>) -> Self {
478        self.then(Transform2::from_scale(scale))
479    }
480
481    /// Shorthand for `.then(Transform2::from_scale(scale))`.
482    ///
483    /// This does not change the target coordinate space.
484    #[inline]
485    #[must_use]
486    pub fn then_translate(self, translation: Vector2<Dst>) -> Self {
487        self.then(Transform2::from_translation(translation))
488    }
489
490    /// Invert the matrix.
491    ///
492    /// See [`glam::Mat3::inverse()`] and [`glam::DMat3::inverse()`].
493    #[inline]
494    #[must_use]
495    pub fn inverse(&self) -> Transform2<Dst, Src> {
496        Transform2::from_matrix_unchecked(self.matrix.inverse())
497    }
498}
499
500impl<Src, Dst, Dst2> Mul<Transform2<Dst, Dst2>> for Transform2<Src, Dst>
501where
502    Src: Unit,
503    Src::Scalar: FloatScalar,
504    Dst: Unit<Scalar = Src::Scalar>,
505    Dst2: Unit<Scalar = Src::Scalar>,
506{
507    type Output = Transform2<Src, Dst2>;
508
509    #[inline]
510    #[must_use]
511    fn mul(self, rhs: Transform2<Dst, Dst2>) -> Self::Output {
512        Transform2::from_matrix_unchecked(rhs.matrix * self.matrix)
513    }
514}
515
516impl<Src, Dst, Dst2> Mul<Transform3<Dst, Dst2>> for Transform3<Src, Dst>
517where
518    Src: Unit,
519    Src::Scalar: FloatScalar,
520    Dst: Unit<Scalar = Src::Scalar>,
521    Dst2: Unit<Scalar = Src::Scalar>,
522{
523    type Output = Transform3<Src, Dst2>;
524
525    #[inline]
526    #[must_use]
527    fn mul(self, rhs: Transform3<Dst, Dst2>) -> Self::Output {
528        Transform3::from_matrix_unchecked(rhs.matrix * self.matrix)
529    }
530}
531
532impl<Src, Dst> Transform3<Src, Dst>
533where
534    Src: Unit,
535    Src::Scalar: FloatScalar,
536    Dst: Unit<Scalar = Src::Scalar>,
537{
538    /// Identity matrix.
539    pub const IDENTITY: Self = Self {
540        matrix: Matrix4::IDENTITY,
541        _marker: PhantomData,
542    };
543
544    /// Create from matrix.
545    #[inline]
546    #[must_use]
547    pub const fn from_matrix_unchecked(matrix: Matrix4<Src::Scalar>) -> Self {
548        Transform3 {
549            matrix,
550            _marker: PhantomData,
551        }
552    }
553
554    /// Create from matrix, checking if the matrix is invertible.
555    #[inline]
556    #[must_use]
557    pub fn from_matrix(matrix: Matrix4<Src::Scalar>) -> Option<Self> {
558        if matrix.is_invertible() {
559            Some(Self::from_matrix_unchecked(matrix))
560        } else {
561            None
562        }
563    }
564
565    /// Perform matrix multiplication such that `other`'s transformation applies
566    /// after `self`.
567    ///
568    /// This may change the target coordinate space - that is, the resulting
569    /// transform takes points and vectors from `Src` to `Dst2`.
570    ///
571    /// This operation is equivalent to `other.matrix * self.matrix` - that is,
572    /// the multiplication order is the reverse of the order of the effects of
573    /// the transformation.
574    #[inline]
575    #[must_use]
576    pub fn then<Dst2: Unit<Scalar = Dst::Scalar>>(
577        self,
578        other: Transform3<Dst, Dst2>,
579    ) -> Transform3<Src, Dst2> {
580        Transform3::from_matrix_unchecked(other.matrix * self.matrix)
581    }
582
583    /// Shorthand for `.then(Transform3::from_angle(angle))`.
584    ///
585    /// This does not change the target coordinate space.
586    #[inline]
587    #[must_use]
588    pub fn then_rotate(self, axis: Vector3<Dst>, angle: Angle<Src::Scalar>) -> Self {
589        self.then(Transform3::from_axis_angle(axis, angle))
590    }
591
592    /// Shorthand for `.then(Transform3::from_scale(scale))`.
593    ///
594    /// This does not change the target coordinate space.
595    ///
596    /// #### Example
597    ///
598    /// ```rust
599    /// # use glamour::prelude::*;
600    /// let a = Transform3::<f32, f32>::IDENTITY
601    ///     .then_scale((2.0, 3.0, 4.0).into());
602    /// let b = Transform3::<f32, f32>::from_scale((2.0, 3.0, 4.0).into());
603    /// assert_eq!(a, b);
604    /// ```
605    #[inline]
606    #[must_use]
607    pub fn then_scale(self, scale: Vector3<Dst>) -> Self {
608        self.then(Transform3::from_scale(scale))
609    }
610
611    /// Shorthand for `.then(Transform3::from_scale(scale))`.
612    ///
613    /// This does not change the target coordinate space.
614    #[inline]
615    #[must_use]
616    pub fn then_translate(self, translation: Vector3<Dst>) -> Self {
617        self.then(Transform3::from_translation(translation))
618    }
619
620    /// Create rotation transform.
621    ///
622    /// #### Example
623    /// ```rust
624    /// # use glamour::prelude::*;
625    /// # use approx::*;
626    /// struct A;
627    /// impl Unit for A { type Scalar = f64; }
628    /// struct B;
629    /// impl Unit for B { type Scalar = f64; }
630    ///
631    /// type Transform = Transform3<A, B>;
632    ///
633    /// let translate = Transform::from_axis_angle(Vector3::Z, Angle::FRAG_PI_2);
634    /// let a: Vector3<A> = Vector3 { x: 10.0, y: 20.0, z: 30.0 };
635    /// let b: Vector3<B> = translate.map(a);
636    /// assert_abs_diff_eq!(b, vec3!(-20.0, 10.0, 30.0), epsilon = 0.000001);
637    /// ```
638    #[inline]
639    #[must_use]
640    pub fn from_axis_angle(axis: Vector3<Src>, angle: Angle<Src::Scalar>) -> Self {
641        Self::from_matrix_unchecked(Matrix4::from_axis_angle(
642            axis.to_untyped(),
643            Angle {
644                radians: angle.radians,
645            },
646        ))
647    }
648
649    /// Create scaling transform.
650    ///
651    /// #### Example
652    /// ```rust
653    /// # use glamour::{Unit, prelude::*};
654    /// # use approx::*;
655    /// struct A;
656    /// impl Unit for A { type Scalar = f32; }
657    /// struct B;
658    /// impl Unit for B { type Scalar = f32; }
659    ///
660    /// type Transform = Transform3<A, B>;
661    ///
662    /// let scale = Transform::from_scale(Vector3 { x: 2.0, y: 3.0, z: 1.0 });
663    /// let a: Vector3<A> = Vector3 { x: 10.0, y: 20.0, z: 30.0 };
664    /// let b: Vector3<B> = scale.map(a);
665    /// assert_abs_diff_eq!(b, vec3!(20.0, 60.0, 30.0));
666    /// ```
667    #[inline]
668    #[must_use]
669    pub fn from_scale(scale: Vector3<Src>) -> Self {
670        Self::from_matrix_unchecked(Matrix4::from_scale(scale.to_untyped()))
671    }
672
673    /// Create translation transform.
674    ///
675    /// #### Example
676    /// ```rust
677    /// # use glamour::{Unit, prelude::*};
678    /// # use approx::*;
679    /// struct A;
680    /// impl Unit for A { type Scalar = f32; }
681    /// struct B;
682    /// impl Unit for B { type Scalar = f32; }
683    ///
684    /// type Transform = Transform3<A, B>;
685    ///
686    /// let translate = Transform::from_translation(Vector3 { x: 10.0, y: 20.0, z: 30.0 });
687    /// let a: Point3<A> = Point3 { x: 1.0, y: 2.0, z: 30.0 };
688    /// let b: Point3<B> = translate.map(a);
689    /// assert_abs_diff_eq!(b, point!(11.0, 22.0, 60.0));
690    /// ```
691    #[inline]
692    #[must_use]
693    pub fn from_translation(translation: Vector3<Src>) -> Self {
694        Self::from_matrix_unchecked(Matrix4::from_translation(translation.to_untyped()))
695    }
696
697    /// Create scaling, rotation, and translation matrix.
698    ///
699    /// Note: This internally converts `axis` and `angle` to a quaternion and
700    /// calls [`glam::Mat4::from_scale_rotation_translation()`].
701    ///
702    /// #### Example
703    /// ```rust
704    /// # use glamour::{Unit, prelude::*};
705    /// # use approx::*;
706    ///
707    /// type Transform = Transform3<f32, f32>;
708    ///
709    /// let a = Transform::from_scale_rotation_translation(
710    ///     (2.0, 3.0, 1.0).into(),
711    ///     (0.0, 0.0, 1.0).into(),
712    ///     Angle::from_degrees(90.0),
713    ///     (10.0, 20.0, 30.0).into(),
714    /// );
715    ///
716    /// // While this is equivalent, it introduces a significant amount of
717    /// // floating point imprecision.
718    /// let b = Transform::from_scale((2.0, 3.0, 1.0).into())
719    ///     .then_rotate((0.0, 0.0, 1.0).into(), Angle::from_degrees(90.0))
720    ///     .then_translate((10.0, 20.0, 30.0).into());
721    ///
722    /// assert_abs_diff_eq!(a, b, epsilon = 0.001);
723    #[inline]
724    #[must_use]
725    pub fn from_scale_rotation_translation(
726        scale: Vector3<Src>,
727        axis: Vector3<Src>,
728        angle: Angle<Src::Scalar>,
729        translation: Vector3<Src>,
730    ) -> Self {
731        let rotation = <Src::Scalar as FloatScalar>::Quat::from_axis_angle(peel(axis), peel(angle));
732        Self::from_matrix_unchecked(Matrix4::from_scale_rotation_translation(
733            scale.to_untyped(),
734            rotation,
735            translation.to_untyped(),
736        ))
737    }
738
739    /// Map vector from `Src` to `Dst`.
740    ///
741    /// See [`glam::Mat4::transform_vector3()`] and
742    /// [`glam::DMat4::transform_vector3()`].
743    #[inline]
744    #[must_use]
745    pub fn map_vector(&self, vector: Vector3<Src>) -> Vector3<Dst> {
746        cast(self.matrix.transform_vector3(vector.to_untyped()))
747    }
748
749    /// Map point from `Src` to `Dst`, including perspective correction.
750    ///
751    /// See [`glam::Mat4::project_point3()`] and
752    /// [`glam::DMat4::project_point3()`].
753    #[inline]
754    #[must_use]
755    pub fn map_point(&self, point: Point3<Src>) -> Point3<Dst> {
756        cast(self.matrix.project_point3(point.to_untyped()))
757    }
758
759    /// Invert the matrix.
760    ///
761    /// See [`glam::Mat4::inverse()`] and [`glam::DMat4::inverse()`].
762    #[inline]
763    #[must_use]
764    pub fn inverse(&self) -> Transform3<Dst, Src> {
765        Transform3::from_matrix_unchecked(self.matrix.inverse())
766    }
767}
768
769impl<Src, Dst> TransformMap<Point3<Src>> for Transform3<Src, Dst>
770where
771    Src: Unit,
772    Src::Scalar: FloatScalar,
773    Dst: Unit<Scalar = Src::Scalar>,
774{
775    type Output = Point3<Dst>;
776
777    #[must_use]
778    fn map(&self, value: Point3<Src>) -> Self::Output {
779        self.map_point(value)
780    }
781}
782
783impl<Src, Dst> TransformMap<Vector3<Src>> for Transform3<Src, Dst>
784where
785    Src: Unit,
786    Src::Scalar: FloatScalar,
787    Dst: Unit<Scalar = Src::Scalar>,
788{
789    type Output = Vector3<Dst>;
790
791    #[must_use]
792    fn map(&self, value: Vector3<Src>) -> Self::Output {
793        self.map_vector(value)
794    }
795}
796
797#[cfg(test)]
798mod tests {
799    use super::*;
800    use approx::*;
801
802    struct TestSrc;
803    impl Unit for TestSrc {
804        type Scalar = f32;
805    }
806
807    struct TestDst;
808    impl Unit for TestDst {
809        type Scalar = f32;
810    }
811
812    struct TestDst2;
813    impl Unit for TestDst2 {
814        type Scalar = f32;
815    }
816
817    macro_rules! check_2d_and_3d {
818        { $($test:tt)* } => {{
819            {
820                type Transform = Transform2<TestSrc, TestDst>;
821                type Mat = Matrix3<f32>;
822                type TransformInverse = Transform2<TestDst, TestSrc>;
823                type VectorSrc = Vector2<TestSrc>;
824                type VectorDst = Vector2<TestDst>;
825                type PointSrc = Point2<TestSrc>;
826                type PointDst = Point2<TestDst>;
827                let _ = core::mem::size_of::<(Transform, Mat, TransformInverse, VectorSrc, VectorDst, PointSrc, PointDst)>();
828                $($test)*
829            }
830            {
831                type Transform = Transform3<TestSrc, TestDst>;
832                type Mat = Matrix4<f32>;
833                type TransformInverse = Transform3<TestDst, TestSrc>;
834                type VectorSrc = Vector3<TestSrc>;
835                type VectorDst = Vector3<TestDst>;
836                type PointSrc = Point3<TestSrc>;
837                type PointDst = Point3<TestDst>;
838                let _ = core::mem::size_of::<(Transform, Mat, TransformInverse, VectorSrc, VectorDst, PointSrc, PointDst)>();
839                $($test)*
840            }
841        }};
842    }
843
844    #[test]
845    fn basic() {
846        check_2d_and_3d! {
847            let a = Transform::IDENTITY;
848            let b = a;
849            assert_eq!(a, b);
850            assert_abs_diff_eq!(a, b);
851            assert_relative_eq!(a, b);
852            assert_ulps_eq!(a, b);
853
854            let c = a.then_scale(VectorDst::splat(2.0));
855            assert_ne!(a, c);
856            assert_abs_diff_ne!(a, c);
857            assert_relative_ne!(a, c);
858            assert_ulps_ne!(a, c);
859        };
860
861        check_2d_and_3d! {
862            let a = Transform::IDENTITY;
863            assert_eq!(a, Transform::default());
864        };
865    }
866
867    #[test]
868    fn from_matrix() {
869        check_2d_and_3d! {
870            assert!(Transform::from_matrix(Mat::ZERO).is_none());
871            assert_eq!(Transform::from_matrix(Mat::IDENTITY), Some(Transform::IDENTITY));
872        };
873    }
874
875    #[test]
876    fn inverse() {
877        check_2d_and_3d! {
878            assert!(Transform::from_matrix(Mat::zeroed()).is_none());
879
880            let transform = Transform::from_translation(VectorSrc::splat(1.0));
881            let point = PointSrc::splat(2.0);
882            let point_dst: PointDst = transform.map(point);
883            assert_abs_diff_eq!(point_dst, PointDst::splat(3.0));
884
885            let inverse: TransformInverse = transform.inverse();
886            let point_src = inverse.map(point_dst);
887            assert_abs_diff_eq!(point_src, point);
888        };
889    }
890
891    #[test]
892    fn concatenation() {
893        {
894            let a = Transform2::<TestSrc, TestDst>::from_scale((2.0, 2.0).into());
895            let b = Transform2::<TestDst, TestDst2>::from_translation((1.0, 1.0).into());
896            let c: Transform2<TestSrc, TestDst2> = a * b;
897            assert_eq!(
898                c,
899                Transform2::<TestSrc, TestDst2>::from_scale((2.0, 2.0).into())
900                    .then_translate((1.0, 1.0).into())
901            );
902        }
903
904        {
905            let a = Transform3::<TestSrc, TestDst>::from_scale((2.0, 2.0, 2.0).into());
906            let b = Transform3::<TestDst, TestDst2>::from_translation((1.0, 1.0, 1.0).into());
907            let c: Transform3<TestSrc, TestDst2> = a * b;
908            assert_eq!(
909                c,
910                Transform3::<TestSrc, TestDst2>::from_scale((2.0, 2.0, 2.0).into())
911                    .then_translate((1.0, 1.0, 1.0).into())
912            );
913        }
914    }
915
916    #[test]
917    fn gaslight_coverage() {
918        fn clone_me<T: Clone>(v: &T) -> T {
919            v.clone()
920        }
921        _ = clone_me(&Transform2::<f32, f32>::IDENTITY);
922        _ = clone_me(&Transform3::<f32, f32>::IDENTITY);
923    }
924}