Skip to main content

jagua_rs/geometry/
transformation.rs

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)]
11///The matrix form of [`DTransformation`].
12///[read more](https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/geometry/geo-tran.html)
13pub struct Transformation {
14    matrix: [[NotNan<f32>; 3]; 3],
15}
16
17impl Transformation {
18    /// Creates a transformation with no effect.
19    #[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    /// Applies a rotation to `self`.
41    #[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    /// Applies a translation to `self`.
48    #[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    /// Applies a rotation followed by a translation to `self`.
55    #[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    /// Applies a translation followed by a rotation to `self`.
62    #[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    /// Applies `other` to `self`.
69    #[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    /// Generates the transformation that undoes the effect of `self`.
81    #[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    /// Whether this transformation reverses orientation.
93    #[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
154//rotation followed by translation
155fn 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
165//translation followed by rotation
166fn 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}