emath/
rot2.rs

1use super::Vec2;
2
3// {s,c} represents the rotation matrix:
4//
5// | c -s |
6// | s  c |
7//
8// `vec2(c,s)` represents where the X axis will end up after rotation.
9//
10/// Represents a rotation in the 2D plane.
11///
12/// A rotation of 𝞃/4 = 90° rotates the X axis to the Y axis.
13///
14/// Normally a [`Rot2`] is normalized (unit-length).
15/// If not, it will also scale vectors.
16#[repr(C)]
17#[derive(Clone, Copy, PartialEq)]
18#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
19#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
20pub struct Rot2 {
21    /// `angle.sin()`
22    s: f32,
23
24    /// `angle.cos()`
25    c: f32,
26}
27
28/// Identity rotation
29impl Default for Rot2 {
30    /// Identity rotation
31    #[inline]
32    fn default() -> Self {
33        Self { s: 0.0, c: 1.0 }
34    }
35}
36
37impl Rot2 {
38    /// The identity rotation: nothing rotates
39    pub const IDENTITY: Self = Self { s: 0.0, c: 1.0 };
40
41    /// Angle is clockwise in radians.
42    /// A 𝞃/4 = 90° rotation means rotating the X axis to the Y axis.
43    #[inline]
44    pub fn from_angle(angle: f32) -> Self {
45        let (s, c) = angle.sin_cos();
46        Self { s, c }
47    }
48
49    #[inline]
50    pub fn angle(self) -> f32 {
51        self.s.atan2(self.c)
52    }
53
54    /// The factor by which vectors will be scaled.
55    #[inline]
56    pub fn length(self) -> f32 {
57        self.c.hypot(self.s)
58    }
59
60    #[inline]
61    pub fn length_squared(self) -> f32 {
62        self.c.powi(2) + self.s.powi(2)
63    }
64
65    #[inline]
66    pub fn is_finite(self) -> bool {
67        self.c.is_finite() && self.s.is_finite()
68    }
69
70    #[must_use]
71    #[inline]
72    pub fn inverse(self) -> Self {
73        Self {
74            s: -self.s,
75            c: self.c,
76        } / self.length_squared()
77    }
78
79    #[must_use]
80    #[inline]
81    pub fn normalized(self) -> Self {
82        let l = self.length();
83        let ret = Self {
84            c: self.c / l,
85            s: self.s / l,
86        };
87        debug_assert!(ret.is_finite());
88        ret
89    }
90}
91
92impl std::fmt::Debug for Rot2 {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        if let Some(precision) = f.precision() {
95            write!(
96                f,
97                "Rot2 {{ angle: {:.2$}°, length: {} }}",
98                self.angle().to_degrees(),
99                self.length(),
100                precision
101            )
102        } else {
103            write!(
104                f,
105                "Rot2 {{ angle: {:.1}°, length: {} }}",
106                self.angle().to_degrees(),
107                self.length(),
108            )
109        }
110    }
111}
112
113impl std::ops::Mul<Self> for Rot2 {
114    type Output = Self;
115
116    #[inline]
117    fn mul(self, r: Self) -> Self {
118        /*
119        |lc -ls| * |rc -rs|
120        |ls  lc|   |rs  rc|
121        */
122        Self {
123            c: self.c * r.c - self.s * r.s,
124            s: self.s * r.c + self.c * r.s,
125        }
126    }
127}
128
129/// Rotates (and maybe scales) the vector.
130impl std::ops::Mul<Vec2> for Rot2 {
131    type Output = Vec2;
132
133    #[inline]
134    fn mul(self, v: Vec2) -> Vec2 {
135        Vec2 {
136            x: self.c * v.x - self.s * v.y,
137            y: self.s * v.x + self.c * v.y,
138        }
139    }
140}
141
142/// Scales the rotor.
143impl std::ops::Mul<Rot2> for f32 {
144    type Output = Rot2;
145
146    #[inline]
147    fn mul(self, r: Rot2) -> Rot2 {
148        Rot2 {
149            c: self * r.c,
150            s: self * r.s,
151        }
152    }
153}
154
155/// Scales the rotor.
156impl std::ops::Mul<f32> for Rot2 {
157    type Output = Self;
158
159    #[inline]
160    fn mul(self, r: f32) -> Self {
161        Self {
162            c: self.c * r,
163            s: self.s * r,
164        }
165    }
166}
167
168/// Scales the rotor.
169impl std::ops::Div<f32> for Rot2 {
170    type Output = Self;
171
172    #[inline]
173    fn div(self, r: f32) -> Self {
174        Self {
175            c: self.c / r,
176            s: self.s / r,
177        }
178    }
179}
180
181#[cfg(test)]
182mod test {
183    use super::Rot2;
184    use crate::vec2;
185
186    #[test]
187    fn test_rotation2() {
188        {
189            let angle = std::f32::consts::TAU / 6.0;
190            let rot = Rot2::from_angle(angle);
191            assert!((rot.angle() - angle).abs() < 1e-5);
192            assert!((rot * rot.inverse()).angle().abs() < 1e-5);
193            assert!((rot.inverse() * rot).angle().abs() < 1e-5);
194        }
195
196        {
197            let angle = std::f32::consts::TAU / 4.0;
198            let rot = Rot2::from_angle(angle);
199            assert!(((rot * vec2(1.0, 0.0)) - vec2(0.0, 1.0)).length() < 1e-5);
200        }
201
202        {
203            // Test rotation and scaling
204            let angle = std::f32::consts::TAU / 4.0;
205            let rot = 3.0 * Rot2::from_angle(angle);
206            let rotated = rot * vec2(1.0, 0.0);
207            let expected = vec2(0.0, 3.0);
208            assert!(
209                (rotated - expected).length() < 1e-5,
210                "Expected {rotated:?} to equal {expected:?}. rot: {rot:?}",
211            );
212
213            let undone = rot.inverse() * rot;
214            assert!(undone.angle().abs() < 1e-5);
215            assert!((undone.length() - 1.0).abs() < 1e-5,);
216        }
217    }
218}