glamx/
rot2.rs

1//! 2D rotation types represented as unit complex numbers.
2
3use core::ops::{Mul, MulAssign};
4use simba::scalar::{ComplexField, RealField};
5
6/// Macro to generate a 2D rotation type for a specific scalar type.
7macro_rules! impl_rot2 {
8    ($Rot2:ident, $Real:ty, $Vec2:ty, $Mat2:ty $(, #[$attr:meta])*) => {
9        #[doc = concat!("A 2D rotation represented as a unit complex number (", stringify!($Real), " precision).")]
10        ///
11        /// The rotation is stored as `re + im * i` where `re = cos(angle)` and `im = sin(angle)`.
12        #[derive(Copy, Clone, Debug, PartialEq)]
13        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14        #[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
15        #[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
16        #[repr(C)]
17        $(#[$attr])*
18        pub struct $Rot2 {
19            /// Real part (cosine of the angle).
20            pub re: $Real,
21            /// Imaginary part (sine of the angle).
22            pub im: $Real,
23        }
24
25        impl $Rot2 {
26            /// The identity rotation (no rotation).
27            pub const IDENTITY: Self = Self { re: 1.0, im: 0.0 };
28
29            /// Creates a new unit complex from cosine and sine values.
30            ///
31            /// The caller must ensure that `re*re + im*im = 1`.
32            #[inline]
33            pub const fn from_cos_sin_unchecked(re: $Real, im: $Real) -> Self {
34                Self { re, im }
35            }
36
37            /// Creates a rotation from a rotation matrix (without normalization).
38            #[inline]
39            pub fn from_matrix_unchecked(mat: $Mat2) -> Self {
40                Self {
41                    re: mat.x_axis.x,
42                    im: mat.x_axis.y,
43                }
44            }
45
46            /// Creates the identity rotation.
47            #[inline]
48            pub fn identity() -> Self {
49                Self::IDENTITY
50            }
51
52            /// Creates a rotation from an angle in radians.
53            #[inline]
54            pub fn new(angle: $Real) -> Self {
55                let (im, re) = <$Real as ComplexField>::sin_cos(angle);
56                Self { re, im }
57            }
58
59            /// Creates a rotation from an angle in radians.
60            #[inline]
61            pub fn from_angle(angle: $Real) -> Self {
62                Self::new(angle)
63            }
64
65            /// Returns the rotation angle in radians.
66            #[inline]
67            pub fn angle(&self) -> $Real {
68                RealField::atan2(self.im, self.re)
69            }
70
71            /// Returns the cosine of the rotation angle (the real part).
72            #[inline]
73            pub fn cos(&self) -> $Real {
74                self.re
75            }
76
77            /// Returns the sine of the rotation angle (the imaginary part).
78            #[inline]
79            pub fn sin(&self) -> $Real {
80                self.im
81            }
82
83            /// Returns the inverse rotation.
84            #[inline]
85            pub fn inverse(self) -> Self {
86                Self {
87                    re: self.re,
88                    im: -self.im,
89                }
90            }
91
92            /// Computes the length (magnitude) of `self`.
93            ///
94            /// For a valid unit rotation, this should always be `1.0`.
95            #[inline]
96            pub fn length(&self) -> $Real {
97                <$Real as ComplexField>::sqrt(self.re * self.re + self.im * self.im)
98            }
99
100            /// Computes the squared length of `self`.
101            ///
102            /// This is faster than `length()` as it avoids a square root.
103            /// For a valid unit rotation, this should always be `1.0`.
104            #[inline]
105            pub fn length_squared(&self) -> $Real {
106                self.re * self.re + self.im * self.im
107            }
108
109            /// Computes `1.0 / length()`.
110            #[inline]
111            pub fn length_recip(&self) -> $Real {
112                1.0 / self.length()
113            }
114
115            /// Returns `self` normalized to length `1.0`.
116            ///
117            /// For valid unit rotations this should be a no-op.
118            #[inline]
119            pub fn normalize(&self) -> Self {
120                let len = self.length();
121                if len > <$Real>::EPSILON {
122                    Self {
123                        re: self.re / len,
124                        im: self.im / len,
125                    }
126                } else {
127                    Self::IDENTITY
128                }
129            }
130
131            /// Returns `true` if, and only if, all elements are finite.
132            #[inline]
133            pub fn is_finite(&self) -> bool {
134                self.re.is_finite() && self.im.is_finite()
135            }
136
137            /// Returns `true` if any element is `NaN`.
138            #[inline]
139            pub fn is_nan(&self) -> bool {
140                self.re.is_nan() || self.im.is_nan()
141            }
142
143            /// Returns whether `self` is of length `1.0` or not.
144            ///
145            /// Uses a precision threshold of approximately `1e-4`.
146            #[inline]
147            pub fn is_normalized(&self) -> bool {
148                (self.length_squared() - 1.0).abs() < 2e-4
149            }
150
151            /// Rotates a 2D vector by this rotation.
152            #[inline]
153            pub fn transform_vector(&self, v: $Vec2) -> $Vec2 {
154                <$Vec2>::new(self.re * v.x - self.im * v.y, self.im * v.x + self.re * v.y)
155            }
156
157            /// Transforms a 2D vector by the inverse of this rotation.
158            #[inline]
159            pub fn inverse_transform_vector(&self, v: $Vec2) -> $Vec2 {
160                <$Vec2>::new(
161                    self.re * v.x + self.im * v.y,
162                    -self.im * v.x + self.re * v.y,
163                )
164            }
165
166            /// Returns the rotation matrix equivalent to this rotation.
167            #[inline]
168            pub fn to_mat(&self) -> $Mat2 {
169                <$Mat2>::from_cols(
170                    <$Vec2>::new(self.re, self.im),
171                    <$Vec2>::new(-self.im, self.re),
172                )
173            }
174
175            /// Creates a rotation from a 2x2 rotation matrix.
176            ///
177            /// The matrix should be a valid rotation matrix (orthogonal with determinant 1).
178            #[inline]
179            pub fn from_mat(mat: $Mat2) -> Self {
180                Self::from_mat_unchecked(mat).normalize()
181            }
182
183            /// Creates a rotation from a 2x2 matrix without normalization.
184            ///
185            /// Alias for `from_matrix_unchecked`.
186            #[inline]
187            pub fn from_mat_unchecked(mat: $Mat2) -> Self {
188                Self::from_matrix_unchecked(mat)
189            }
190
191            /// Gets the minimal rotation for transforming `from` to `to`.
192            ///
193            /// Both vectors must be normalized. The rotation is in the plane
194            /// spanned by the two vectors.
195            #[inline]
196            pub fn from_rotation_arc(from: $Vec2, to: $Vec2) -> Self {
197                // For unit vectors, the rotation from a to b has:
198                // re = a . b = cos(angle)
199                // im = a x b = sin(angle) (2D cross product is scalar)
200                let re = from.dot(to);
201                let im = from.x * to.y - from.y * to.x;
202                Self { re, im }
203            }
204
205            /// Spherical linear interpolation between two rotations.
206            #[inline]
207            pub fn slerp(&self, other: &Self, t: $Real) -> Self {
208                let angle_diff = other.angle() - self.angle();
209                Self::new(self.angle() + t * angle_diff)
210            }
211
212            /// Raises this rotation to a power.
213            ///
214            /// For example, `powf(2.0)` will return a rotation with double the angle.
215            #[inline]
216            pub fn powf(&self, n: $Real) -> Self {
217                Self::new(self.angle() * n)
218            }
219
220            /// Rotates `self` towards `rhs` up to `max_angle` (in radians).
221            ///
222            /// When `max_angle` is `0.0`, the result will be equal to `self`. When `max_angle` is
223            /// equal to `self.angle_between(rhs)`, the result will be equal to `rhs`. If
224            /// `max_angle` is negative, rotates towards the exact opposite of `rhs`.
225            /// Will not go past the target.
226            #[inline]
227            pub fn rotate_towards(&self, rhs: &Self, max_angle: $Real) -> Self {
228                let angle_between = self.angle_between(rhs);
229                if angle_between.abs() <= max_angle.abs() {
230                    *rhs
231                } else {
232                    self.slerp(rhs, max_angle / angle_between)
233                }
234            }
235
236            /// Returns the angle (in radians) between `self` and `rhs`.
237            #[inline]
238            pub fn angle_between(&self, rhs: &Self) -> $Real {
239                let diff = self.inverse() * *rhs;
240                diff.angle()
241            }
242
243            /// Computes the dot product of `self` and `rhs`.
244            ///
245            /// The dot product is equal to the cosine of the angle between two rotations.
246            #[inline]
247            pub fn dot(&self, rhs: Self) -> $Real {
248                self.re * rhs.re + self.im * rhs.im
249            }
250
251            /// Performs a linear interpolation between `self` and `rhs` based on
252            /// the value `s`.
253            ///
254            /// When `s` is `0.0`, the result will be equal to `self`. When `s` is
255            /// `1.0`, the result will be equal to `rhs`. For rotations, `slerp` is
256            /// usually preferred over `lerp`.
257            #[inline]
258            pub fn lerp(&self, rhs: Self, s: $Real) -> Self {
259                Self {
260                    re: self.re + (rhs.re - self.re) * s,
261                    im: self.im + (rhs.im - self.im) * s,
262                }
263            }
264
265            /// Normalizes `self` in place.
266            ///
267            /// Does nothing if `self` is already normalized or zero length.
268            #[inline]
269            pub fn normalize_mut(&mut self) {
270                *self = self.normalize();
271            }
272        }
273
274        // Rot2 * Rot2
275        impl Mul<$Rot2> for $Rot2 {
276            type Output = Self;
277
278            #[inline]
279            fn mul(self, rhs: Self) -> Self::Output {
280                // Complex multiplication: (a + bi)(c + di) = (ac - bd) + (ad + bc)i
281                Self {
282                    re: self.re * rhs.re - self.im * rhs.im,
283                    im: self.re * rhs.im + self.im * rhs.re,
284                }
285            }
286        }
287
288        // &Rot2 * Rot2
289        impl Mul<$Rot2> for &$Rot2 {
290            type Output = $Rot2;
291
292            #[inline]
293            fn mul(self, rhs: $Rot2) -> Self::Output {
294                *self * rhs
295            }
296        }
297
298        impl MulAssign<$Rot2> for $Rot2 {
299            #[inline]
300            fn mul_assign(&mut self, rhs: Self) {
301                *self = *self * rhs;
302            }
303        }
304
305        // Rot2 * Vec2
306        impl Mul<$Vec2> for $Rot2 {
307            type Output = $Vec2;
308
309            #[inline]
310            fn mul(self, rhs: $Vec2) -> Self::Output {
311                self.transform_vector(rhs)
312            }
313        }
314
315        impl Default for $Rot2 {
316            fn default() -> Self {
317                Self::identity()
318            }
319        }
320
321        #[cfg(feature = "approx")]
322        impl approx::AbsDiffEq for $Rot2 {
323            type Epsilon = $Real;
324
325            fn default_epsilon() -> Self::Epsilon {
326                <$Real>::EPSILON
327            }
328
329            fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
330                (self.re - other.re).abs() <= epsilon && (self.im - other.im).abs() <= epsilon
331            }
332        }
333
334        #[cfg(feature = "approx")]
335        impl approx::RelativeEq for $Rot2 {
336            fn default_max_relative() -> Self::Epsilon {
337                <$Real>::EPSILON
338            }
339
340            fn relative_eq(
341                &self,
342                other: &Self,
343                epsilon: Self::Epsilon,
344                max_relative: Self::Epsilon,
345            ) -> bool {
346                approx::AbsDiffEq::abs_diff_eq(self, other, epsilon.max(max_relative))
347            }
348        }
349
350        #[cfg(feature = "approx")]
351        impl approx::UlpsEq for $Rot2 {
352            fn default_max_ulps() -> u32 {
353                4
354            }
355
356            fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
357                approx::AbsDiffEq::abs_diff_eq(self, other, epsilon)
358                    || (self.re.to_bits().abs_diff(other.re.to_bits()) <= max_ulps as _
359                        && self.im.to_bits().abs_diff(other.im.to_bits()) <= max_ulps as _)
360            }
361        }
362    };
363}
364
365impl_rot2!(Rot2, f32, glam::Vec2, glam::Mat2);
366impl_rot2!(DRot2, f64, glam::DVec2, glam::DMat2);
367
368// f32 <-> f64 conversions
369impl From<Rot2> for DRot2 {
370    #[inline]
371    fn from(r: Rot2) -> Self {
372        Self {
373            re: r.re as f64,
374            im: r.im as f64,
375        }
376    }
377}
378
379impl From<DRot2> for Rot2 {
380    #[inline]
381    fn from(r: DRot2) -> Self {
382        Self {
383            re: r.re as f32,
384            im: r.im as f32,
385        }
386    }
387}
388
389// Nalgebra conversions
390#[cfg(feature = "nalgebra")]
391mod nalgebra_conv {
392    use super::*;
393
394    impl From<Rot2> for nalgebra::UnitComplex<f32> {
395        fn from(r: Rot2) -> Self {
396            nalgebra::UnitComplex::from_cos_sin_unchecked(r.re, r.im)
397        }
398    }
399
400    impl From<nalgebra::UnitComplex<f32>> for Rot2 {
401        fn from(r: nalgebra::UnitComplex<f32>) -> Self {
402            Self { re: r.re, im: r.im }
403        }
404    }
405
406    impl From<DRot2> for nalgebra::UnitComplex<f64> {
407        fn from(r: DRot2) -> Self {
408            nalgebra::UnitComplex::from_cos_sin_unchecked(r.re, r.im)
409        }
410    }
411
412    impl From<nalgebra::UnitComplex<f64>> for DRot2 {
413        fn from(r: nalgebra::UnitComplex<f64>) -> Self {
414            Self { re: r.re, im: r.im }
415        }
416    }
417}
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422    use approx::assert_relative_eq;
423    use core::f32::consts::PI;
424
425    #[test]
426    fn test_rot2_identity() {
427        let r = Rot2::IDENTITY;
428        assert_eq!(r.re, 1.0);
429        assert_eq!(r.im, 0.0);
430        assert!(r.is_normalized());
431    }
432
433    #[test]
434    fn test_rot2_new() {
435        let r = Rot2::new(PI / 2.0);
436        assert_relative_eq!(r.re, 0.0, epsilon = 1e-6);
437        assert_relative_eq!(r.im, 1.0, epsilon = 1e-6);
438        assert!(r.is_normalized());
439    }
440
441    #[test]
442    fn test_rot2_angle() {
443        let angle = 0.5;
444        let r = Rot2::new(angle);
445        assert_relative_eq!(r.angle(), angle, epsilon = 1e-6);
446        assert_relative_eq!(r.angle(), angle, epsilon = 1e-6);
447    }
448
449    #[test]
450    fn test_rot2_inverse() {
451        let r = Rot2::new(0.5);
452        let inv = r.inverse();
453        let identity = r * inv;
454        assert_relative_eq!(identity.re, 1.0, epsilon = 1e-6);
455        assert_relative_eq!(identity.im, 0.0, epsilon = 1e-6);
456    }
457
458    #[test]
459    fn test_rot2_transform_vector() {
460        let r = Rot2::new(PI / 2.0);
461        let v = glam::Vec2::new(1.0, 0.0);
462        let result = r.transform_vector(v);
463        assert_relative_eq!(result.x, 0.0, epsilon = 1e-6);
464        assert_relative_eq!(result.y, 1.0, epsilon = 1e-6);
465        // Operator should also work
466        let result2 = r * v;
467        assert_relative_eq!(result2.x, 0.0, epsilon = 1e-6);
468        assert_relative_eq!(result2.y, 1.0, epsilon = 1e-6);
469    }
470
471    #[test]
472    fn test_rot2_length() {
473        let r = Rot2::new(0.5);
474        assert_relative_eq!(r.length(), 1.0, epsilon = 1e-6);
475        assert_relative_eq!(r.length_squared(), 1.0, epsilon = 1e-6);
476    }
477
478    #[test]
479    fn test_rot2_from_rotation_arc() {
480        let from = glam::Vec2::new(1.0, 0.0);
481        let to = glam::Vec2::new(0.0, 1.0);
482        let r = Rot2::from_rotation_arc(from, to);
483        assert_relative_eq!(r.angle(), PI / 2.0, epsilon = 1e-6);
484    }
485
486    #[test]
487    fn test_rot2_to_mat2() {
488        let r = Rot2::new(PI / 4.0);
489        let m = r.to_mat();
490        let from_mat = Rot2::from_mat(m);
491        assert_relative_eq!(r.re, from_mat.re, epsilon = 1e-6);
492        assert_relative_eq!(r.im, from_mat.im, epsilon = 1e-6);
493    }
494
495    #[test]
496    fn test_rot2_angle_between() {
497        let r1 = Rot2::new(0.0);
498        let r2 = Rot2::new(PI / 2.0);
499        assert_relative_eq!(r1.angle_between(&r2), PI / 2.0, epsilon = 1e-6);
500    }
501
502    #[test]
503    fn test_drot2_new() {
504        let r = DRot2::new(core::f64::consts::PI / 2.0);
505        assert_relative_eq!(r.re, 0.0, epsilon = 1e-10);
506        assert_relative_eq!(r.im, 1.0, epsilon = 1e-10);
507        assert!(r.is_normalized());
508    }
509}