Skip to main content

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            ///
207            /// Interpolates along the shortest arc: the result is `self` at `t == 0` and `other`
208            /// at `t == 1`, whichever branch of `angle` the two rotations fall on. Interpolating
209            /// their angles instead would take the long way around whenever the arc crosses the
210            /// `-pi`/`pi` cut.
211            #[inline]
212            pub fn slerp(&self, other: &Self, t: $Real) -> Self {
213                *self * Self::new(self.angle_between(other) * t)
214            }
215
216            /// Raises this rotation to a power.
217            ///
218            /// For example, `powf(2.0)` will return a rotation with double the angle.
219            #[inline]
220            pub fn powf(&self, n: $Real) -> Self {
221                Self::new(self.angle() * n)
222            }
223
224            /// Rotates `self` towards `rhs` up to `max_angle` (in radians).
225            ///
226            /// When `max_angle` is `0.0`, the result will be equal to `self`. When `max_angle` is
227            /// equal to `self.angle_between(rhs)`, the result will be equal to `rhs`. If
228            /// `max_angle` is negative, rotates towards the exact opposite of `rhs`.
229            /// Will not go past the target.
230            #[inline]
231            pub fn rotate_towards(&self, rhs: &Self, max_angle: $Real) -> Self {
232                let angle_between = self.angle_between(rhs);
233                if angle_between.abs() <= max_angle.abs() {
234                    *rhs
235                } else {
236                    self.slerp(rhs, max_angle / angle_between)
237                }
238            }
239
240            /// Returns the angle (in radians) between `self` and `rhs`.
241            #[inline]
242            pub fn angle_between(&self, rhs: &Self) -> $Real {
243                let diff = self.inverse() * *rhs;
244                diff.angle()
245            }
246
247            /// Computes the dot product of `self` and `rhs`.
248            ///
249            /// The dot product is equal to the cosine of the angle between two rotations.
250            #[inline]
251            pub fn dot(&self, rhs: Self) -> $Real {
252                self.re * rhs.re + self.im * rhs.im
253            }
254
255            /// Performs a linear interpolation between `self` and `rhs` based on
256            /// the value `s`.
257            ///
258            /// When `s` is `0.0`, the result will be equal to `self`. When `s` is
259            /// `1.0`, the result will be equal to `rhs`. For rotations, `slerp` is
260            /// usually preferred over `lerp`.
261            #[inline]
262            pub fn lerp(&self, rhs: Self, s: $Real) -> Self {
263                Self {
264                    re: self.re + (rhs.re - self.re) * s,
265                    im: self.im + (rhs.im - self.im) * s,
266                }
267            }
268
269            /// Normalizes `self` in place.
270            ///
271            /// Does nothing if `self` is already normalized or zero length.
272            #[inline]
273            pub fn normalize_mut(&mut self) {
274                *self = self.normalize();
275            }
276        }
277
278        // Rot2 * Rot2
279        impl Mul<$Rot2> for $Rot2 {
280            type Output = Self;
281
282            #[inline]
283            fn mul(self, rhs: Self) -> Self::Output {
284                // Complex multiplication: (a + bi)(c + di) = (ac - bd) + (ad + bc)i
285                Self {
286                    re: self.re * rhs.re - self.im * rhs.im,
287                    im: self.re * rhs.im + self.im * rhs.re,
288                }
289            }
290        }
291
292        // &Rot2 * Rot2
293        impl Mul<$Rot2> for &$Rot2 {
294            type Output = $Rot2;
295
296            #[inline]
297            fn mul(self, rhs: $Rot2) -> Self::Output {
298                *self * rhs
299            }
300        }
301
302        impl MulAssign<$Rot2> for $Rot2 {
303            #[inline]
304            fn mul_assign(&mut self, rhs: Self) {
305                *self = *self * rhs;
306            }
307        }
308
309        // Rot2 * Vec2
310        impl Mul<$Vec2> for $Rot2 {
311            type Output = $Vec2;
312
313            #[inline]
314            fn mul(self, rhs: $Vec2) -> Self::Output {
315                self.transform_vector(rhs)
316            }
317        }
318
319        impl Default for $Rot2 {
320            fn default() -> Self {
321                Self::identity()
322            }
323        }
324
325        #[cfg(feature = "approx")]
326        impl approx::AbsDiffEq for $Rot2 {
327            type Epsilon = $Real;
328
329            fn default_epsilon() -> Self::Epsilon {
330                <$Real>::EPSILON
331            }
332
333            fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
334                (self.re - other.re).abs() <= epsilon && (self.im - other.im).abs() <= epsilon
335            }
336        }
337
338        #[cfg(feature = "approx")]
339        impl approx::RelativeEq for $Rot2 {
340            fn default_max_relative() -> Self::Epsilon {
341                <$Real>::EPSILON
342            }
343
344            fn relative_eq(
345                &self,
346                other: &Self,
347                epsilon: Self::Epsilon,
348                max_relative: Self::Epsilon,
349            ) -> bool {
350                approx::AbsDiffEq::abs_diff_eq(self, other, epsilon.max(max_relative))
351            }
352        }
353
354        #[cfg(feature = "approx")]
355        impl approx::UlpsEq for $Rot2 {
356            fn default_max_ulps() -> u32 {
357                4
358            }
359
360            fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
361                approx::AbsDiffEq::abs_diff_eq(self, other, epsilon)
362                    || (self.re.to_bits().abs_diff(other.re.to_bits()) <= max_ulps as _
363                        && self.im.to_bits().abs_diff(other.im.to_bits()) <= max_ulps as _)
364            }
365        }
366    };
367}
368
369impl_rot2!(Rot2, f32, glam::Vec2, glam::Mat2);
370#[cfg(feature = "f64")]
371impl_rot2!(DRot2, f64, glam::DVec2, glam::DMat2);
372
373// f32 <-> f64 conversions
374#[cfg(feature = "f64")]
375impl From<Rot2> for DRot2 {
376    #[inline]
377    fn from(r: Rot2) -> Self {
378        Self {
379            re: r.re as f64,
380            im: r.im as f64,
381        }
382    }
383}
384
385#[cfg(feature = "f64")]
386impl From<DRot2> for Rot2 {
387    #[inline]
388    fn from(r: DRot2) -> Self {
389        Self {
390            re: r.re as f32,
391            im: r.im as f32,
392        }
393    }
394}
395
396// Nalgebra conversions
397#[cfg(feature = "nalgebra")]
398mod nalgebra_conv {
399    use super::*;
400
401    impl From<Rot2> for nalgebra::UnitComplex<f32> {
402        fn from(r: Rot2) -> Self {
403            nalgebra::UnitComplex::from_cos_sin_unchecked(r.re, r.im)
404        }
405    }
406
407    impl From<nalgebra::UnitComplex<f32>> for Rot2 {
408        fn from(r: nalgebra::UnitComplex<f32>) -> Self {
409            Self { re: r.re, im: r.im }
410        }
411    }
412
413    #[cfg(feature = "f64")]
414    impl From<DRot2> for nalgebra::UnitComplex<f64> {
415        fn from(r: DRot2) -> Self {
416            nalgebra::UnitComplex::from_cos_sin_unchecked(r.re, r.im)
417        }
418    }
419
420    #[cfg(feature = "f64")]
421    impl From<nalgebra::UnitComplex<f64>> for DRot2 {
422        fn from(r: nalgebra::UnitComplex<f64>) -> Self {
423            Self { re: r.re, im: r.im }
424        }
425    }
426}
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431    use approx::assert_relative_eq;
432    use core::f32::consts::PI;
433
434    #[test]
435    fn test_rot2_identity() {
436        let r = Rot2::IDENTITY;
437        assert_eq!(r.re, 1.0);
438        assert_eq!(r.im, 0.0);
439        assert!(r.is_normalized());
440    }
441
442    #[test]
443    fn test_rot2_new() {
444        let r = Rot2::new(PI / 2.0);
445        assert_relative_eq!(r.re, 0.0, epsilon = 1e-6);
446        assert_relative_eq!(r.im, 1.0, epsilon = 1e-6);
447        assert!(r.is_normalized());
448    }
449
450    #[test]
451    fn test_rot2_angle() {
452        let angle = 0.5;
453        let r = Rot2::new(angle);
454        assert_relative_eq!(r.angle(), angle, epsilon = 1e-6);
455        assert_relative_eq!(r.angle(), angle, epsilon = 1e-6);
456    }
457
458    #[test]
459    fn test_rot2_inverse() {
460        let r = Rot2::new(0.5);
461        let inv = r.inverse();
462        let identity = r * inv;
463        assert_relative_eq!(identity.re, 1.0, epsilon = 1e-6);
464        assert_relative_eq!(identity.im, 0.0, epsilon = 1e-6);
465    }
466
467    #[test]
468    fn test_rot2_transform_vector() {
469        let r = Rot2::new(PI / 2.0);
470        let v = glam::Vec2::new(1.0, 0.0);
471        let result = r.transform_vector(v);
472        assert_relative_eq!(result.x, 0.0, epsilon = 1e-6);
473        assert_relative_eq!(result.y, 1.0, epsilon = 1e-6);
474        // Operator should also work
475        let result2 = r * v;
476        assert_relative_eq!(result2.x, 0.0, epsilon = 1e-6);
477        assert_relative_eq!(result2.y, 1.0, epsilon = 1e-6);
478    }
479
480    #[test]
481    fn test_rot2_length() {
482        let r = Rot2::new(0.5);
483        assert_relative_eq!(r.length(), 1.0, epsilon = 1e-6);
484        assert_relative_eq!(r.length_squared(), 1.0, epsilon = 1e-6);
485    }
486
487    #[test]
488    fn test_rot2_from_rotation_arc() {
489        let from = glam::Vec2::new(1.0, 0.0);
490        let to = glam::Vec2::new(0.0, 1.0);
491        let r = Rot2::from_rotation_arc(from, to);
492        assert_relative_eq!(r.angle(), PI / 2.0, epsilon = 1e-6);
493    }
494
495    #[test]
496    fn test_rot2_to_mat2() {
497        let r = Rot2::new(PI / 4.0);
498        let m = r.to_mat();
499        let from_mat = Rot2::from_mat(m);
500        assert_relative_eq!(r.re, from_mat.re, epsilon = 1e-6);
501        assert_relative_eq!(r.im, from_mat.im, epsilon = 1e-6);
502    }
503
504    #[test]
505    fn test_rot2_angle_between() {
506        let r1 = Rot2::new(0.0);
507        let r2 = Rot2::new(PI / 2.0);
508        assert_relative_eq!(r1.angle_between(&r2), PI / 2.0, epsilon = 1e-6);
509    }
510
511    #[test]
512    fn test_rot2_slerp() {
513        let r1 = Rot2::new(0.2);
514        let r2 = Rot2::new(1.0);
515        assert_relative_eq!(r1.slerp(&r2, 0.0).angle(), 0.2, epsilon = 1e-6);
516        assert_relative_eq!(r1.slerp(&r2, 1.0).angle(), 1.0, epsilon = 1e-6);
517        assert_relative_eq!(r1.slerp(&r2, 0.5).angle(), 0.6, epsilon = 1e-6);
518        assert!(r1.slerp(&r2, 0.5).is_normalized());
519    }
520
521    #[test]
522    fn test_rot2_slerp_shortest_arc() {
523        // The interpolation from 0 to 3pi/2 goes backwards through -3pi/4, not forwards.
524        let r1 = Rot2::new(0.0);
525        let r2 = Rot2::new(3.0 * PI / 2.0);
526        assert_relative_eq!(r1.slerp(&r2, 0.5).angle(), -PI / 4.0, epsilon = 1e-6);
527    }
528
529    #[test]
530    fn test_rot2_slerp_across_branch_cut() {
531        // `angle` wraps to (-pi, pi], so these two are 0.02 rad apart, not 6.26.
532        let r1 = Rot2::new(3.13);
533        let r2 = Rot2::new(3.15);
534        assert_relative_eq!(r1.slerp(&r2, 0.5).angle(), 3.14, epsilon = 1e-6);
535        assert_relative_eq!(r1.slerp(&r2, 1.0).angle(), r2.angle(), epsilon = 1e-6);
536    }
537
538    #[cfg(feature = "f64")]
539    #[test]
540    fn test_drot2_new() {
541        let r = DRot2::new(core::f64::consts::PI / 2.0);
542        assert_relative_eq!(r.re, 0.0, epsilon = 1e-10);
543        assert_relative_eq!(r.im, 1.0, epsilon = 1e-10);
544        assert!(r.is_normalized());
545    }
546}