1#![allow(clippy::inline_always)]
2
3use std::borrow::Borrow;
4use std::ops::{Add, Div, Mul, Sub};
5
6use ordered_float::NotNan;
7
8use crate::geometry::DTransformation;
9
10#[derive(Clone, Debug)]
11pub struct Transformation {
14 matrix: [[NotNan<f32>; 3]; 3],
15}
16
17impl Transformation {
18 #[must_use]
20 pub const fn empty() -> Self {
21 Self {
22 matrix: EMPTY_MATRIX,
23 }
24 }
25
26 #[must_use]
27 pub fn from_translation((tx, ty): (f32, f32)) -> Self {
28 Self {
29 matrix: transl_m((tx, ty)),
30 }
31 }
32
33 #[must_use]
34 pub fn from_rotation(angle: f32) -> Self {
35 Self {
36 matrix: rot_m(angle),
37 }
38 }
39
40 #[must_use]
42 pub fn rotate(mut self, angle: f32) -> Self {
43 self.matrix = dot_prod(&rot_m(angle), &self.matrix);
44 self
45 }
46
47 #[must_use]
49 pub fn translate(mut self, (tx, ty): (f32, f32)) -> Self {
50 self.matrix = dot_prod(&transl_m((tx, ty)), &self.matrix);
51 self
52 }
53
54 #[must_use]
56 pub fn rotate_translate(mut self, angle: f32, (tx, ty): (f32, f32)) -> Self {
57 self.matrix = dot_prod(&rot_transl_m(angle, (tx, ty)), &self.matrix);
58 self
59 }
60
61 #[must_use]
63 pub fn translate_rotate(mut self, (tx, ty): (f32, f32), angle: f32) -> Self {
64 self.matrix = dot_prod(&transl_rot_m((tx, ty), angle), &self.matrix);
65 self
66 }
67
68 #[must_use]
70 pub fn transform(mut self, other: &Self) -> Self {
71 self.matrix = dot_prod(&other.matrix, &self.matrix);
72 self
73 }
74
75 #[must_use]
76 pub fn transform_from_decomposed(self, other: &DTransformation) -> Self {
77 self.transform(&other.compose())
78 }
79
80 #[must_use]
82 pub fn inverse(mut self) -> Self {
83 self.matrix = inverse(&self.matrix);
84 self
85 }
86
87 #[must_use]
88 pub fn is_empty(&self) -> bool {
89 self.matrix == EMPTY_MATRIX
90 }
91
92 #[must_use]
94 pub fn is_reflected(&self) -> bool {
95 let m = &self.matrix;
96 m[0][0] * m[1][1] < m[0][1] * m[1][0]
97 }
98
99 #[must_use]
100 pub fn matrix(&self) -> &[[NotNan<f32>; 3]; 3] {
101 &self.matrix
102 }
103
104 #[must_use]
105 pub fn decompose(&self) -> DTransformation {
106 let m = self.matrix();
107 let angle = m[1][0].atan2(m[0][0].into_inner());
108 let (tx, ty) = (m[0][2].into_inner(), m[1][2].into_inner());
109 DTransformation::new(angle, (tx, ty)).with_reflection(self.is_reflected())
110 }
111}
112
113impl<T> From<T> for Transformation
114where
115 T: Borrow<DTransformation>,
116{
117 fn from(dt: T) -> Self {
118 let dt = dt.borrow();
119 let mut matrix = rot_transl_m(dt.rotation(), dt.translation());
120 if dt.reflected {
121 matrix[0][1] = -matrix[0][1];
122 matrix[1][1] = -matrix[1][1];
123 }
124 Self { matrix }
125 }
126}
127
128impl Default for Transformation {
129 fn default() -> Self {
130 Self::empty()
131 }
132}
133
134const _0: NotNan<f32> = unsafe { NotNan::new_unchecked(0.0) };
135const _1: NotNan<f32> = unsafe { NotNan::new_unchecked(1.0) };
136
137const EMPTY_MATRIX: [[NotNan<f32>; 3]; 3] = [[_1, _0, _0], [_0, _1, _0], [_0, _0, _1]];
138
139fn rot_m(angle: f32) -> [[NotNan<f32>; 3]; 3] {
140 let (sin, cos) = angle.sin_cos();
141 let cos = NotNan::new(cos).expect("cos is NaN");
142 let sin = NotNan::new(sin).expect("sin is NaN");
143
144 [[cos, -sin, _0], [sin, cos, _0], [_0, _0, _1]]
145}
146
147fn transl_m((tx, ty): (f32, f32)) -> [[NotNan<f32>; 3]; 3] {
148 let h = NotNan::new(tx).expect("tx is NaN");
149 let k = NotNan::new(ty).expect("ty is NaN");
150
151 [[_1, _0, h], [_0, _1, k], [_0, _0, _1]]
152}
153
154fn rot_transl_m(angle: f32, (tx, ty): (f32, f32)) -> [[NotNan<f32>; 3]; 3] {
156 let (sin, cos) = angle.sin_cos();
157 let cos = NotNan::new(cos).expect("cos is NaN");
158 let sin = NotNan::new(sin).expect("sin is NaN");
159 let h = NotNan::new(tx).expect("tx is NaN");
160 let k = NotNan::new(ty).expect("ty is NaN");
161
162 [[cos, -sin, h], [sin, cos, k], [_0, _0, _1]]
163}
164
165fn transl_rot_m((tx, ty): (f32, f32), angle: f32) -> [[NotNan<f32>; 3]; 3] {
167 let (sin, cos) = angle.sin_cos();
168 let cos = NotNan::new(cos).expect("cos is NaN");
169 let sin = NotNan::new(sin).expect("sin is NaN");
170 let h = NotNan::new(tx).expect("tx is NaN");
171 let k = NotNan::new(ty).expect("ty is NaN");
172
173 [
174 [cos, -sin, h * cos - k * sin],
175 [sin, cos, h * sin + k * cos],
176 [_0, _0, _1],
177 ]
178}
179
180#[inline(always)]
181fn dot_prod<T>(l: &[[T; 3]; 3], r: &[[T; 3]; 3]) -> [[T; 3]; 3]
182where
183 T: Add<Output = T> + Mul<Output = T> + Copy + Default,
184{
185 [
186 [
187 l[0][0] * r[0][0] + l[0][1] * r[1][0] + l[0][2] * r[2][0],
188 l[0][0] * r[0][1] + l[0][1] * r[1][1] + l[0][2] * r[2][1],
189 l[0][0] * r[0][2] + l[0][1] * r[1][2] + l[0][2] * r[2][2],
190 ],
191 [
192 l[1][0] * r[0][0] + l[1][1] * r[1][0] + l[1][2] * r[2][0],
193 l[1][0] * r[0][1] + l[1][1] * r[1][1] + l[1][2] * r[2][1],
194 l[1][0] * r[0][2] + l[1][1] * r[1][2] + l[1][2] * r[2][2],
195 ],
196 [
197 l[2][0] * r[0][0] + l[2][1] * r[1][0] + l[2][2] * r[2][0],
198 l[2][0] * r[0][1] + l[2][1] * r[1][1] + l[2][2] * r[2][1],
199 l[2][0] * r[0][2] + l[2][1] * r[1][2] + l[2][2] * r[2][2],
200 ],
201 ]
202}
203
204#[inline(always)]
205fn inverse<T>(m: &[[T; 3]; 3]) -> [[T; 3]; 3]
206where
207 T: Add<Output = T> + Mul<Output = T> + Sub<Output = T> + Div<Output = T> + Copy,
208{
209 let det =
210 m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1]
211 - m[0][2] * m[1][1] * m[2][0]
212 - m[0][1] * m[1][0] * m[2][2]
213 - m[0][0] * m[1][2] * m[2][1];
214
215 [
216 [
217 (m[1][1] * m[2][2] - m[1][2] * m[2][1]) / det,
218 (m[0][2] * m[2][1] - m[0][1] * m[2][2]) / det,
219 (m[0][1] * m[1][2] - m[0][2] * m[1][1]) / det,
220 ],
221 [
222 (m[1][2] * m[2][0] - m[1][0] * m[2][2]) / det,
223 (m[0][0] * m[2][2] - m[0][2] * m[2][0]) / det,
224 (m[0][2] * m[1][0] - m[0][0] * m[1][2]) / det,
225 ],
226 [
227 (m[1][0] * m[2][1] - m[1][1] * m[2][0]) / det,
228 (m[0][1] * m[2][0] - m[0][0] * m[2][1]) / det,
229 (m[0][0] * m[1][1] - m[0][1] * m[1][0]) / det,
230 ],
231 ]
232}