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}