fey_math/
affine2.rs

1use crate::{Angle, Float, Mat2, Quad, Rect, Vec2, impl_affine};
2
3pub type Affine2F = Affine2<f32>;
4
5/// A 2D affine matrix (translation, rotation, scaling and shear).
6#[repr(C)]
7#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
8pub struct Affine2<T> {
9    pub matrix: Mat2<T>,
10    pub translation: Vec2<T>,
11}
12
13/// Create an [`Affine2`].
14#[inline]
15pub const fn affine2<T>(matrix: Mat2<T>, translation: Vec2<T>) -> Affine2<T> {
16    Affine2 {
17        matrix,
18        translation,
19    }
20}
21
22impl_affine!(
23    NAME = Affine2
24    SHORT = affine2
25    MUL_FN = mul_affine2
26    MAT_TY = Mat2
27    VEC_TY = Vec2
28);
29
30impl<T: Float> Affine2<T> {
31    /// Create a rotation matrix.
32    #[inline]
33    pub fn rotation(angle: impl Angle<T>) -> Self {
34        affine2(Mat2::rotation(angle), Vec2::ZERO)
35    }
36
37    /// Create a translation-rotation-scaling matrix.
38    #[inline]
39    pub fn trs(translation: Vec2<T>, rotation: impl Angle<T>, scale: Vec2<T>) -> Self {
40        // TODO: simplify this?
41        Self::translation(translation) * Self::rotation(rotation) * Self::scale(scale)
42    }
43
44    // /// Decompose the matrix into a transform.
45    // #[inline]
46    // pub fn decompose(self) -> Transform2D<T> {
47    //     transform2d(
48    //         self.translation,
49    //         Radians(T::atan2(-self.matrix.y_axis.x, self.matrix.y_axis.y)),
50    //         vec2(
51    //             self.matrix.x_axis.len() * T::signum(self.matrix.determinant()),
52    //             self.matrix.y_axis.len(),
53    //         ),
54    //     )
55    // }
56
57    /// Transforms a 2D vector.
58    #[inline]
59    pub fn transform_vec2(&self, rhs: Vec2<T>) -> Vec2<T> {
60        self.matrix.transform_vec2(rhs)
61    }
62
63    /// Transforms a 2D point.
64    #[inline]
65    pub fn transform_pos2(&self, rhs: Vec2<T>) -> Vec2<T> {
66        self.matrix.transform_vec2(rhs) + self.translation
67    }
68
69    // /// Transform the circle by the matrix, generating a polygon of `N` points.
70    // /// This is useful for generating a polygonal representation of an ellipse.
71    // #[inline]
72    // pub fn transform_circ(&self, rhs: Circle<T>, seg_len: T) -> Polygon<T> {
73    //     let mut poly = Polygon::new();
74    //     rhs.hull_points(seg_len, Radians::ZERO, |p| {
75    //         poly.push(self.transform_pos2(p));
76    //     });
77    //     poly
78    // }
79    //
80    // /// Transform the circle by the matrix, but have it remain a circle. A true
81    // /// transformation would have the circle become an ellipse, but ellipses are
82    // /// not supported currently.
83    // #[inline]
84    // pub fn transform_circ_retain(&self, rhs: Circle<T>) -> Circle<T> {
85    //     let quad = rhs.bounds().corners().map(|p| self.transform_pos2(p));
86    //     let dist_ab = (quad[0] - quad[1]).sqr_len();
87    //     let dist_bc = (quad[1] - quad[2]).sqr_len();
88    //     let sum = quad[0] + quad[1] + quad[2] + quad[3];
89    //     Circle {
90    //         center: sum / T::FOUR,
91    //         radius: T::min(dist_ab, dist_bc) / T::TWO,
92    //     }
93    // }
94    //
95    // /// Transforms a rectangle.
96    // #[inline]
97    // pub fn transform_tri(&self, mut rhs: Triangle<T>) -> Triangle<T> {
98    //     for p in &mut rhs.0 {
99    //         *p = self.transform_pos2(*p);
100    //     }
101    //     rhs
102    // }
103    //
104    /// Transforms a rectangle.
105    #[inline]
106    pub fn transform_rect(&self, rhs: Rect<T>) -> Quad<T> {
107        self.transform_quad(Quad::from_rect(rhs))
108    }
109    //
110    // /// Transforms a rectangle, but have it remain a rectangle.
111    // #[inline]
112    // pub fn transform_rect_retain(&self, rhs: Rect<T>) -> Rect<T> {
113    //     self.transform_rect(rhs).bounds()
114    // }
115    //
116    /// Transforms a quad.
117    #[inline]
118    pub fn transform_quad(&self, mut rhs: Quad<T>) -> Quad<T> {
119        for p in &mut rhs.0 {
120            *p = self.transform_pos2(*p);
121        }
122        rhs
123    }
124    //
125    // /// Transforms a polygon.
126    // #[inline]
127    // pub fn transform_poly(&self, rhs: &mut Polygon<T>) {
128    //     rhs.transform_in_place(self);
129    //     rhs
130    // }
131    //
132    // /// Transforms a dynamic shape.
133    // #[inline]
134    // pub fn transform_dyn(&self, rhs: DynShape<T>, seg_len: T) -> DynShape<T> {
135    //     match rhs {
136    //         DynShape::Circle(sh) => DynShape::Polygon(self.transform_circ(sh, seg_len)),
137    //         DynShape::Triangle(sh) => DynShape::Triangle(self.transform_tri(sh)),
138    //         DynShape::Rect(sh) => DynShape::Quad(self.transform_rect(sh)),
139    //         DynShape::Quad(sh) => DynShape::Quad(self.transform_quad(sh)),
140    //         DynShape::Polygon(sh) => DynShape::Polygon(self.transform_poly(sh)),
141    //     }
142    // }
143    //
144    // /// Transforms a dynamic shape without changing the shape's variant. This
145    // /// means that for circles and rectangles, the `transform_circ_retain()` and
146    // /// `transform_rect_retain()` variations will be used.
147    // #[inline]
148    // pub fn transform_dyn_retain(&self, rhs: DynShape<T>) -> DynShape<T> {
149    //     match rhs {
150    //         DynShape::Circle(sh) => DynShape::Circle(self.transform_circ_retain(sh)),
151    //         DynShape::Triangle(sh) => DynShape::Triangle(self.transform_tri(sh)),
152    //         DynShape::Rect(sh) => DynShape::Rect(self.transform_rect_retain(sh)),
153    //         DynShape::Quad(sh) => DynShape::Quad(self.transform_quad(sh)),
154    //         DynShape::Polygon(sh) => DynShape::Polygon(self.transform_poly(sh)),
155    //     }
156    // }
157    //
158    // /// Transforms a line.
159    // #[inline]
160    // pub fn transform_line(&self, rhs: Line<T>) -> Line<T> {
161    //     line(self.transform_pos2(rhs.start), self.transform_pos2(rhs.end))
162    // }
163
164    /// Try to invert the matrix.
165    #[inline]
166    pub fn inverse(&self) -> Option<Self> {
167        let matrix = self.matrix.inverse()?;
168        let translation = -matrix.transform_vec2(self.translation);
169        Some(affine2(matrix, translation))
170    }
171
172    /// Multiply by another matrix.
173    #[inline]
174    pub fn mul_affine2(&self, rhs: &Self) -> Self {
175        affine2(
176            self.matrix * rhs.matrix,
177            self.matrix.transform_vec2(rhs.translation) + self.translation,
178        )
179    }
180}