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 translation followed by a rotation 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 rotation followed by a translation 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.rotate_translate(other.rotation(), other.translation())
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    #[must_use]
93    pub fn matrix(&self) -> &[[NotNan<f32>; 3]; 3] {
94        &self.matrix
95    }
96
97    #[must_use]
98    pub fn decompose(&self) -> DTransformation {
99        let m = self.matrix();
100        let angle = m[1][0].atan2(m[0][0].into_inner());
101        let (tx, ty) = (m[0][2].into_inner(), m[1][2].into_inner());
102        DTransformation::new(angle, (tx, ty))
103    }
104}
105
106impl<T> From<T> for Transformation
107where
108    T: Borrow<DTransformation>,
109{
110    fn from(dt: T) -> Self {
111        let rot = dt.borrow().rotation();
112        let transl = dt.borrow().translation();
113        Self {
114            matrix: rot_transl_m(rot, transl),
115        }
116    }
117}
118
119impl Default for Transformation {
120    fn default() -> Self {
121        Self::empty()
122    }
123}
124
125const _0: NotNan<f32> = unsafe { NotNan::new_unchecked(0.0) };
126const _1: NotNan<f32> = unsafe { NotNan::new_unchecked(1.0) };
127
128const EMPTY_MATRIX: [[NotNan<f32>; 3]; 3] = [[_1, _0, _0], [_0, _1, _0], [_0, _0, _1]];
129
130fn rot_m(angle: f32) -> [[NotNan<f32>; 3]; 3] {
131    let (sin, cos) = angle.sin_cos();
132    let cos = NotNan::new(cos).expect("cos is NaN");
133    let sin = NotNan::new(sin).expect("sin is NaN");
134
135    [[cos, -sin, _0], [sin, cos, _0], [_0, _0, _1]]
136}
137
138fn transl_m((tx, ty): (f32, f32)) -> [[NotNan<f32>; 3]; 3] {
139    let h = NotNan::new(tx).expect("tx is NaN");
140    let k = NotNan::new(ty).expect("ty is NaN");
141
142    [[_1, _0, h], [_0, _1, k], [_0, _0, _1]]
143}
144
145//rotation followed by translation
146fn rot_transl_m(angle: f32, (tx, ty): (f32, f32)) -> [[NotNan<f32>; 3]; 3] {
147    let (sin, cos) = angle.sin_cos();
148    let cos = NotNan::new(cos).expect("cos is NaN");
149    let sin = NotNan::new(sin).expect("sin is NaN");
150    let h = NotNan::new(tx).expect("tx is NaN");
151    let k = NotNan::new(ty).expect("ty is NaN");
152
153    [[cos, -sin, h], [sin, cos, k], [_0, _0, _1]]
154}
155
156//translation followed by rotation
157fn transl_rot_m((tx, ty): (f32, f32), angle: f32) -> [[NotNan<f32>; 3]; 3] {
158    let (sin, cos) = angle.sin_cos();
159    let cos = NotNan::new(cos).expect("cos is NaN");
160    let sin = NotNan::new(sin).expect("sin is NaN");
161    let h = NotNan::new(tx).expect("tx is NaN");
162    let k = NotNan::new(ty).expect("ty is NaN");
163
164    [
165        [cos, -sin, h * cos - k * sin],
166        [sin, cos, h * sin + k * cos],
167        [_0, _0, _1],
168    ]
169}
170
171#[inline(always)]
172fn dot_prod<T>(l: &[[T; 3]; 3], r: &[[T; 3]; 3]) -> [[T; 3]; 3]
173where
174    T: Add<Output = T> + Mul<Output = T> + Copy + Default,
175{
176    [
177        [
178            l[0][0] * r[0][0] + l[0][1] * r[1][0] + l[0][2] * r[2][0],
179            l[0][0] * r[0][1] + l[0][1] * r[1][1] + l[0][2] * r[2][1],
180            l[0][0] * r[0][2] + l[0][1] * r[1][2] + l[0][2] * r[2][2],
181        ],
182        [
183            l[1][0] * r[0][0] + l[1][1] * r[1][0] + l[1][2] * r[2][0],
184            l[1][0] * r[0][1] + l[1][1] * r[1][1] + l[1][2] * r[2][1],
185            l[1][0] * r[0][2] + l[1][1] * r[1][2] + l[1][2] * r[2][2],
186        ],
187        [
188            l[2][0] * r[0][0] + l[2][1] * r[1][0] + l[2][2] * r[2][0],
189            l[2][0] * r[0][1] + l[2][1] * r[1][1] + l[2][2] * r[2][1],
190            l[2][0] * r[0][2] + l[2][1] * r[1][2] + l[2][2] * r[2][2],
191        ],
192    ]
193}
194
195#[inline(always)]
196fn inverse<T>(m: &[[T; 3]; 3]) -> [[T; 3]; 3]
197where
198    T: Add<Output = T> + Mul<Output = T> + Sub<Output = T> + Div<Output = T> + Copy,
199{
200    let det =
201        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]
202            - m[0][2] * m[1][1] * m[2][0]
203            - m[0][1] * m[1][0] * m[2][2]
204            - m[0][0] * m[1][2] * m[2][1];
205
206    [
207        [
208            (m[1][1] * m[2][2] - m[1][2] * m[2][1]) / det,
209            (m[0][2] * m[2][1] - m[0][1] * m[2][2]) / det,
210            (m[0][1] * m[1][2] - m[0][2] * m[1][1]) / det,
211        ],
212        [
213            (m[1][2] * m[2][0] - m[1][0] * m[2][2]) / det,
214            (m[0][0] * m[2][2] - m[0][2] * m[2][0]) / det,
215            (m[0][2] * m[1][0] - m[0][0] * m[1][2]) / det,
216        ],
217        [
218            (m[1][0] * m[2][1] - m[1][1] * m[2][0]) / det,
219            (m[0][1] * m[2][0] - m[0][0] * m[2][1]) / det,
220            (m[0][0] * m[1][1] - m[0][1] * m[1][0]) / det,
221        ],
222    ]
223}