jagua_rs/geometry/
transformation.rs

1use std::borrow::Borrow;
2use std::ops::{Add, Div, Mul, Sub};
3
4use ordered_float::NotNan;
5
6use crate::geometry::DTransformation;
7
8#[derive(Clone, Debug)]
9///The matrix form of [`DTransformation`].
10///[read more](https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/geometry/geo-tran.html)
11pub struct Transformation {
12    matrix: [[NotNan<f32>; 3]; 3],
13}
14
15impl Transformation {
16    /// Creates a transformation with no effect.
17    pub const fn empty() -> Self {
18        Self {
19            matrix: EMPTY_MATRIX,
20        }
21    }
22
23    pub fn from_translation((tx, ty): (f32, f32)) -> Self {
24        Self {
25            matrix: transl_m((tx, ty)),
26        }
27    }
28
29    pub fn from_rotation(angle: f32) -> Self {
30        Self {
31            matrix: rot_m(angle),
32        }
33    }
34
35    /// Applies a rotation to `self`.
36    pub fn rotate(mut self, angle: f32) -> Self {
37        self.matrix = dot_prod(&rot_m(angle), &self.matrix);
38        self
39    }
40
41    /// Applies a translation to `self`.
42    pub fn translate(mut self, (tx, ty): (f32, f32)) -> Self {
43        self.matrix = dot_prod(&transl_m((tx, ty)), &self.matrix);
44        self
45    }
46
47    /// Applies a translation followed by a rotation to `self`.
48    pub fn rotate_translate(mut self, angle: f32, (tx, ty): (f32, f32)) -> Self {
49        self.matrix = dot_prod(&rot_transl_m(angle, (tx, ty)), &self.matrix);
50        self
51    }
52
53    /// Applies a rotation followed by a translation to `self`.
54    pub fn translate_rotate(mut self, (tx, ty): (f32, f32), angle: f32) -> Self {
55        self.matrix = dot_prod(&transl_rot_m((tx, ty), angle), &self.matrix);
56        self
57    }
58
59    /// Applies `other` to `self`.
60    pub fn transform(mut self, other: &Self) -> Self {
61        self.matrix = dot_prod(&other.matrix, &self.matrix);
62        self
63    }
64
65    pub fn transform_from_decomposed(self, other: &DTransformation) -> Self {
66        self.rotate_translate(other.rotation(), other.translation())
67    }
68
69    /// Generates the transformation that undoes the effect of `self`.
70    pub fn inverse(mut self) -> Self {
71        self.matrix = inverse(&self.matrix);
72        self
73    }
74
75    pub fn is_empty(&self) -> bool {
76        self.matrix == EMPTY_MATRIX
77    }
78
79    pub fn matrix(&self) -> &[[NotNan<f32>; 3]; 3] {
80        &self.matrix
81    }
82
83    pub fn decompose(&self) -> DTransformation {
84        let m = self.matrix();
85        let angle = m[1][0].atan2(m[0][0].into_inner());
86        let (tx, ty) = (m[0][2].into_inner(), m[1][2].into_inner());
87        DTransformation::new(angle, (tx, ty))
88    }
89}
90
91impl<T> From<T> for Transformation
92where
93    T: Borrow<DTransformation>,
94{
95    fn from(dt: T) -> Self {
96        let rot = dt.borrow().rotation();
97        let transl = dt.borrow().translation();
98        Self {
99            matrix: rot_transl_m(rot, transl),
100        }
101    }
102}
103
104impl Default for Transformation {
105    fn default() -> Self {
106        Self::empty()
107    }
108}
109
110const _0: NotNan<f32> = unsafe { NotNan::new_unchecked(0.0) };
111const _1: NotNan<f32> = unsafe { NotNan::new_unchecked(1.0) };
112
113const EMPTY_MATRIX: [[NotNan<f32>; 3]; 3] = [[_1, _0, _0], [_0, _1, _0], [_0, _0, _1]];
114
115fn rot_m(angle: f32) -> [[NotNan<f32>; 3]; 3] {
116    let (sin, cos) = angle.sin_cos();
117    let cos = NotNan::new(cos).expect("cos is NaN");
118    let sin = NotNan::new(sin).expect("sin is NaN");
119
120    [[cos, -sin, _0], [sin, cos, _0], [_0, _0, _1]]
121}
122
123fn transl_m((tx, ty): (f32, f32)) -> [[NotNan<f32>; 3]; 3] {
124    let h = NotNan::new(tx).expect("tx is NaN");
125    let k = NotNan::new(ty).expect("ty is NaN");
126
127    [[_1, _0, h], [_0, _1, k], [_0, _0, _1]]
128}
129
130//rotation followed by translation
131fn rot_transl_m(angle: f32, (tx, ty): (f32, f32)) -> [[NotNan<f32>; 3]; 3] {
132    let (sin, cos) = angle.sin_cos();
133    let cos = NotNan::new(cos).expect("cos is NaN");
134    let sin = NotNan::new(sin).expect("sin is NaN");
135    let h = NotNan::new(tx).expect("tx is NaN");
136    let k = NotNan::new(ty).expect("ty is NaN");
137
138    [[cos, -sin, h], [sin, cos, k], [_0, _0, _1]]
139}
140
141//translation followed by rotation
142fn transl_rot_m((tx, ty): (f32, f32), angle: f32) -> [[NotNan<f32>; 3]; 3] {
143    let (sin, cos) = angle.sin_cos();
144    let cos = NotNan::new(cos).expect("cos is NaN");
145    let sin = NotNan::new(sin).expect("sin is NaN");
146    let h = NotNan::new(tx).expect("tx is NaN");
147    let k = NotNan::new(ty).expect("ty is NaN");
148
149    [
150        [cos, -sin, h * cos - k * sin],
151        [sin, cos, h * sin + k * cos],
152        [_0, _0, _1],
153    ]
154}
155
156#[inline(always)]
157fn dot_prod<T>(l: &[[T; 3]; 3], r: &[[T; 3]; 3]) -> [[T; 3]; 3]
158where
159    T: Add<Output = T> + Mul<Output = T> + Copy + Default,
160{
161    [
162        [
163            l[0][0] * r[0][0] + l[0][1] * r[1][0] + l[0][2] * r[2][0],
164            l[0][0] * r[0][1] + l[0][1] * r[1][1] + l[0][2] * r[2][1],
165            l[0][0] * r[0][2] + l[0][1] * r[1][2] + l[0][2] * r[2][2],
166        ],
167        [
168            l[1][0] * r[0][0] + l[1][1] * r[1][0] + l[1][2] * r[2][0],
169            l[1][0] * r[0][1] + l[1][1] * r[1][1] + l[1][2] * r[2][1],
170            l[1][0] * r[0][2] + l[1][1] * r[1][2] + l[1][2] * r[2][2],
171        ],
172        [
173            l[2][0] * r[0][0] + l[2][1] * r[1][0] + l[2][2] * r[2][0],
174            l[2][0] * r[0][1] + l[2][1] * r[1][1] + l[2][2] * r[2][1],
175            l[2][0] * r[0][2] + l[2][1] * r[1][2] + l[2][2] * r[2][2],
176        ],
177    ]
178}
179
180#[inline(always)]
181fn inverse<T>(m: &[[T; 3]; 3]) -> [[T; 3]; 3]
182where
183    T: Add<Output = T> + Mul<Output = T> + Sub<Output = T> + Div<Output = T> + Copy,
184{
185    let det =
186        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]
187            - m[0][2] * m[1][1] * m[2][0]
188            - m[0][1] * m[1][0] * m[2][2]
189            - m[0][0] * m[1][2] * m[2][1];
190
191    [
192        [
193            (m[1][1] * m[2][2] - m[1][2] * m[2][1]) / det,
194            (m[0][2] * m[2][1] - m[0][1] * m[2][2]) / det,
195            (m[0][1] * m[1][2] - m[0][2] * m[1][1]) / det,
196        ],
197        [
198            (m[1][2] * m[2][0] - m[1][0] * m[2][2]) / det,
199            (m[0][0] * m[2][2] - m[0][2] * m[2][0]) / det,
200            (m[0][2] * m[1][0] - m[0][0] * m[1][2]) / det,
201        ],
202        [
203            (m[1][0] * m[2][1] - m[1][1] * m[2][0]) / det,
204            (m[0][1] * m[2][0] - m[0][0] * m[2][1]) / det,
205            (m[0][0] * m[1][1] - m[0][1] * m[1][0]) / det,
206        ],
207    ]
208}