jagua_rs/geometry/
d_transformation.rs

1use std::borrow::Borrow;
2use std::f32::consts::PI;
3use std::fmt::Display;
4
5use crate::geometry::Transformation;
6use ordered_float::NotNan;
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, Default)]
9/// [Proper rigid transformation](https://en.wikipedia.org/wiki/Rigid_transformation),
10/// decomposed into a rotation followed by a translation.
11pub struct DTransformation {
12    /// The rotation in radians
13    pub rotation: NotNan<f32>,
14    /// The translation in the x and y-axis
15    pub translation: (NotNan<f32>, NotNan<f32>),
16}
17
18impl DTransformation {
19    #[must_use]
20    pub fn new(rotation: f32, translation: (f32, f32)) -> Self {
21        Self {
22            rotation: NotNan::new(rotation).expect("rotation is NaN"),
23            translation: (
24                NotNan::new(translation.0).expect("translation.0 is NaN"),
25                NotNan::new(translation.1).expect("translation.1 is NaN"),
26            ),
27        }
28    }
29
30    #[must_use]
31    pub const fn empty() -> Self {
32        const _0: NotNan<f32> = unsafe { NotNan::new_unchecked(0.0) };
33        Self {
34            rotation: _0,
35            translation: (_0, _0),
36        }
37    }
38
39    #[must_use]
40    pub fn rotation(&self) -> f32 {
41        self.rotation.into()
42    }
43
44    #[must_use]
45    pub fn translation(&self) -> (f32, f32) {
46        (self.translation.0.into(), self.translation.1.into())
47    }
48
49    #[must_use]
50    pub fn compose(&self) -> Transformation {
51        self.into()
52    }
53}
54
55impl<T> From<T> for DTransformation
56where
57    T: Borrow<Transformation>,
58{
59    fn from(t: T) -> Self {
60        t.borrow().decompose()
61    }
62}
63
64impl Display for DTransformation {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(
67            f,
68            "r: {:.3}°, t: ({:.3}, {:.3})",
69            self.rotation.to_degrees(),
70            self.translation.0.into_inner(),
71            self.translation.1.into_inner()
72        )
73    }
74}
75
76/// Normalizes a rotation angle to the range [0, 2π).
77#[must_use]
78pub fn normalize_rotation(r: f32) -> f32 {
79    let normalized = r % (2.0 * PI);
80    if normalized < 0.0 {
81        normalized + 2.0 * PI
82    } else {
83        normalized
84    }
85}