euclid/
rotation.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::approxeq::ApproxEq;
11use crate::num::{One, Zero};
12use crate::trig::Trig;
13use crate::{point2, point3, vec3, Angle, Point2D, Point3D, Vector2D, Vector3D};
14use crate::{Transform2D, Transform3D, UnknownUnit};
15
16use core::cmp::{Eq, PartialEq};
17use core::fmt;
18use core::hash::Hash;
19use core::marker::PhantomData;
20use core::ops::{Add, Mul, Neg, Sub};
21
22#[cfg(feature = "bytemuck")]
23use bytemuck::{Pod, Zeroable};
24#[cfg(feature = "malloc_size_of")]
25use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
26use num_traits::real::Real;
27use num_traits::NumCast;
28#[cfg(feature = "serde")]
29use serde::{Deserialize, Serialize};
30
31/// A transform that can represent rotations in 2d, represented as an angle in radians.
32#[repr(C)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(
35    feature = "serde",
36    serde(bound(
37        serialize = "T: serde::Serialize",
38        deserialize = "T: serde::Deserialize<'de>"
39    ))
40)]
41pub struct Rotation2D<T, Src, Dst> {
42    /// Angle in radians
43    pub angle: T,
44    #[doc(hidden)]
45    pub _unit: PhantomData<(Src, Dst)>,
46}
47
48impl<T: Copy, Src, Dst> Copy for Rotation2D<T, Src, Dst> {}
49
50impl<T: Clone, Src, Dst> Clone for Rotation2D<T, Src, Dst> {
51    fn clone(&self) -> Self {
52        Rotation2D {
53            angle: self.angle.clone(),
54            _unit: PhantomData,
55        }
56    }
57}
58
59impl<T, Src, Dst> Eq for Rotation2D<T, Src, Dst> where T: Eq {}
60
61impl<T, Src, Dst> PartialEq for Rotation2D<T, Src, Dst>
62where
63    T: PartialEq,
64{
65    fn eq(&self, other: &Self) -> bool {
66        self.angle == other.angle
67    }
68}
69
70impl<T, Src, Dst> Hash for Rotation2D<T, Src, Dst>
71where
72    T: Hash,
73{
74    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
75        self.angle.hash(h);
76    }
77}
78
79#[cfg(feature = "arbitrary")]
80impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Rotation2D<T, Src, Dst>
81where
82    T: arbitrary::Arbitrary<'a>,
83{
84    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
85        Ok(Rotation2D::new(arbitrary::Arbitrary::arbitrary(u)?))
86    }
87}
88
89#[cfg(feature = "bytemuck")]
90unsafe impl<T: Zeroable, Src, Dst> Zeroable for Rotation2D<T, Src, Dst> {}
91
92#[cfg(feature = "bytemuck")]
93unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Rotation2D<T, Src, Dst> {}
94
95#[cfg(feature = "malloc_size_of")]
96impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for Rotation2D<T, Src, Dst> {
97    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
98        self.angle.size_of(ops)
99    }
100}
101
102impl<T, Src, Dst> Rotation2D<T, Src, Dst> {
103    /// Creates a rotation from an angle in radians.
104    #[inline]
105    pub fn new(angle: Angle<T>) -> Self {
106        Rotation2D {
107            angle: angle.radians,
108            _unit: PhantomData,
109        }
110    }
111
112    /// Creates a rotation from an angle in radians.
113    pub fn radians(angle: T) -> Self {
114        Self::new(Angle::radians(angle))
115    }
116
117    /// Creates the identity rotation.
118    #[inline]
119    pub fn identity() -> Self
120    where
121        T: Zero,
122    {
123        Self::radians(T::zero())
124    }
125}
126
127impl<T: Copy, Src, Dst> Rotation2D<T, Src, Dst> {
128    /// Cast the unit, preserving the numeric value.
129    ///
130    /// # Example
131    ///
132    /// ```rust
133    /// # use euclid::Rotation2D;
134    /// enum Local {}
135    /// enum World {}
136    ///
137    /// enum Local2 {}
138    /// enum World2 {}
139    ///
140    /// let to_world: Rotation2D<_, Local, World> = Rotation2D::radians(42);
141    ///
142    /// assert_eq!(to_world.angle, to_world.cast_unit::<Local2, World2>().angle);
143    /// ```
144    #[inline]
145    pub fn cast_unit<Src2, Dst2>(&self) -> Rotation2D<T, Src2, Dst2> {
146        Rotation2D {
147            angle: self.angle,
148            _unit: PhantomData,
149        }
150    }
151
152    /// Drop the units, preserving only the numeric value.
153    ///
154    /// # Example
155    ///
156    /// ```rust
157    /// # use euclid::Rotation2D;
158    /// enum Local {}
159    /// enum World {}
160    ///
161    /// let to_world: Rotation2D<_, Local, World> = Rotation2D::radians(42);
162    ///
163    /// assert_eq!(to_world.angle, to_world.to_untyped().angle);
164    /// ```
165    #[inline]
166    pub fn to_untyped(&self) -> Rotation2D<T, UnknownUnit, UnknownUnit> {
167        self.cast_unit()
168    }
169
170    /// Tag a unitless value with units.
171    ///
172    /// # Example
173    ///
174    /// ```rust
175    /// # use euclid::Rotation2D;
176    /// use euclid::UnknownUnit;
177    /// enum Local {}
178    /// enum World {}
179    ///
180    /// let rot: Rotation2D<_, UnknownUnit, UnknownUnit> = Rotation2D::radians(42);
181    ///
182    /// assert_eq!(rot.angle, Rotation2D::<_, Local, World>::from_untyped(&rot).angle);
183    /// ```
184    #[inline]
185    pub fn from_untyped(r: &Rotation2D<T, UnknownUnit, UnknownUnit>) -> Self {
186        r.cast_unit()
187    }
188}
189
190impl<T, Src, Dst> Rotation2D<T, Src, Dst>
191where
192    T: Copy,
193{
194    /// Returns self.angle as a strongly typed `Angle<T>`.
195    pub fn get_angle(&self) -> Angle<T> {
196        Angle::radians(self.angle)
197    }
198}
199
200impl<T: Real, Src, Dst> Rotation2D<T, Src, Dst> {
201    /// Creates a 3d rotation (around the z axis) from this 2d rotation.
202    #[inline]
203    pub fn to_3d(&self) -> Rotation3D<T, Src, Dst> {
204        Rotation3D::around_z(self.get_angle())
205    }
206
207    /// Returns the inverse of this rotation.
208    #[inline]
209    pub fn inverse(&self) -> Rotation2D<T, Dst, Src> {
210        Rotation2D::radians(-self.angle)
211    }
212
213    /// Returns a rotation representing the other rotation followed by this rotation.
214    #[inline]
215    pub fn then<NewSrc>(&self, other: &Rotation2D<T, NewSrc, Src>) -> Rotation2D<T, NewSrc, Dst> {
216        Rotation2D::radians(self.angle + other.angle)
217    }
218
219    /// Returns the given 2d point transformed by this rotation.
220    ///
221    /// The input point must be use the unit Src, and the returned point has the unit Dst.
222    #[inline]
223    pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> {
224        let (sin, cos) = Real::sin_cos(self.angle);
225        point2(point.x * cos - point.y * sin, point.y * cos + point.x * sin)
226    }
227
228    /// Returns the given 2d vector transformed by this rotation.
229    ///
230    /// The input point must be use the unit Src, and the returned point has the unit Dst.
231    #[inline]
232    pub fn transform_vector(&self, vector: Vector2D<T, Src>) -> Vector2D<T, Dst> {
233        self.transform_point(vector.to_point()).to_vector()
234    }
235}
236
237impl<T, Src, Dst> Rotation2D<T, Src, Dst>
238where
239    T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig,
240{
241    /// Returns the matrix representation of this rotation.
242    #[inline]
243    pub fn to_transform(&self) -> Transform2D<T, Src, Dst> {
244        Transform2D::rotation(self.get_angle())
245    }
246}
247
248impl<T: fmt::Debug, Src, Dst> fmt::Debug for Rotation2D<T, Src, Dst> {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        write!(f, "Rotation({:?} rad)", self.angle)
251    }
252}
253
254impl<T, Src, Dst> ApproxEq<T> for Rotation2D<T, Src, Dst>
255where
256    T: Copy + Neg<Output = T> + ApproxEq<T>,
257{
258    fn approx_epsilon() -> T {
259        T::approx_epsilon()
260    }
261
262    fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
263        self.angle.approx_eq_eps(&other.angle, eps)
264    }
265}
266
267/// A transform that can represent rotations in 3d, represented as a quaternion.
268///
269/// Most methods expect the quaternion to be normalized.
270/// When in doubt, use [`unit_quaternion`] instead of [`quaternion`] to create
271/// a rotation as the former will ensure that its result is normalized.
272///
273/// Some people use the `x, y, z, w` (or `w, x, y, z`) notations. The equivalence is
274/// as follows: `x -> i`, `y -> j`, `z -> k`, `w -> r`.
275/// The memory layout of this type corresponds to the `x, y, z, w` notation
276///
277/// [`quaternion`]: Self::quaternion
278/// [`unit_quaternion`]: Self::unit_quaternion
279#[repr(C)]
280#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
281#[cfg_attr(
282    feature = "serde",
283    serde(bound(
284        serialize = "T: serde::Serialize",
285        deserialize = "T: serde::Deserialize<'de>"
286    ))
287)]
288pub struct Rotation3D<T, Src, Dst> {
289    /// Component multiplied by the imaginary number `i`.
290    pub i: T,
291    /// Component multiplied by the imaginary number `j`.
292    pub j: T,
293    /// Component multiplied by the imaginary number `k`.
294    pub k: T,
295    /// The real part.
296    pub r: T,
297    #[doc(hidden)]
298    pub _unit: PhantomData<(Src, Dst)>,
299}
300
301impl<T: Copy, Src, Dst> Copy for Rotation3D<T, Src, Dst> {}
302
303impl<T: Clone, Src, Dst> Clone for Rotation3D<T, Src, Dst> {
304    fn clone(&self) -> Self {
305        Rotation3D {
306            i: self.i.clone(),
307            j: self.j.clone(),
308            k: self.k.clone(),
309            r: self.r.clone(),
310            _unit: PhantomData,
311        }
312    }
313}
314
315impl<T, Src, Dst> Eq for Rotation3D<T, Src, Dst> where T: Eq {}
316
317impl<T, Src, Dst> PartialEq for Rotation3D<T, Src, Dst>
318where
319    T: PartialEq,
320{
321    fn eq(&self, other: &Self) -> bool {
322        self.i == other.i && self.j == other.j && self.k == other.k && self.r == other.r
323    }
324}
325
326impl<T, Src, Dst> Hash for Rotation3D<T, Src, Dst>
327where
328    T: Hash,
329{
330    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
331        self.i.hash(h);
332        self.j.hash(h);
333        self.k.hash(h);
334        self.r.hash(h);
335    }
336}
337
338/// Note: the quaternions produced by this implementation are not normalized
339/// (nor even necessarily finite). That is, this is not appropriate to use to
340/// choose an actual “arbitrary rotation”, at least not without postprocessing.
341#[cfg(feature = "arbitrary")]
342impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Rotation3D<T, Src, Dst>
343where
344    T: arbitrary::Arbitrary<'a>,
345{
346    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
347        let (i, j, k, r) = arbitrary::Arbitrary::arbitrary(u)?;
348        Ok(Rotation3D::quaternion(i, j, k, r))
349    }
350}
351
352#[cfg(feature = "bytemuck")]
353unsafe impl<T: Zeroable, Src, Dst> Zeroable for Rotation3D<T, Src, Dst> {}
354
355#[cfg(feature = "bytemuck")]
356unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Rotation3D<T, Src, Dst> {}
357
358#[cfg(feature = "malloc_size_of")]
359impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for Rotation3D<T, Src, Dst> {
360    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
361        self.i.size_of(ops) + self.j.size_of(ops) + self.k.size_of(ops) + self.r.size_of(ops)
362    }
363}
364
365impl<T, Src, Dst> Rotation3D<T, Src, Dst> {
366    /// Creates a rotation around from a quaternion representation.
367    ///
368    /// The parameters are a, b, c and r compose the quaternion `a*i + b*j + c*k + r`
369    /// where `a`, `b` and `c` describe the vector part and the last parameter `r` is
370    /// the real part.
371    ///
372    /// The resulting quaternion is not necessarily normalized. See [`unit_quaternion`].
373    ///
374    /// [`unit_quaternion`]: Self::unit_quaternion
375    #[inline]
376    pub fn quaternion(a: T, b: T, c: T, r: T) -> Self {
377        Rotation3D {
378            i: a,
379            j: b,
380            k: c,
381            r,
382            _unit: PhantomData,
383        }
384    }
385
386    /// Creates the identity rotation.
387    #[inline]
388    pub fn identity() -> Self
389    where
390        T: Zero + One,
391    {
392        Self::quaternion(T::zero(), T::zero(), T::zero(), T::one())
393    }
394}
395
396impl<T, Src, Dst> Rotation3D<T, Src, Dst>
397where
398    T: Copy,
399{
400    /// Returns the vector part (i, j, k) of this quaternion.
401    #[inline]
402    pub fn vector_part(&self) -> Vector3D<T, UnknownUnit> {
403        vec3(self.i, self.j, self.k)
404    }
405
406    /// Cast the unit, preserving the numeric value.
407    ///
408    /// # Example
409    ///
410    /// ```rust
411    /// # use euclid::Rotation3D;
412    /// enum Local {}
413    /// enum World {}
414    ///
415    /// enum Local2 {}
416    /// enum World2 {}
417    ///
418    /// let to_world: Rotation3D<_, Local, World> = Rotation3D::quaternion(1, 2, 3, 4);
419    ///
420    /// assert_eq!(to_world.i, to_world.cast_unit::<Local2, World2>().i);
421    /// assert_eq!(to_world.j, to_world.cast_unit::<Local2, World2>().j);
422    /// assert_eq!(to_world.k, to_world.cast_unit::<Local2, World2>().k);
423    /// assert_eq!(to_world.r, to_world.cast_unit::<Local2, World2>().r);
424    /// ```
425    #[inline]
426    pub fn cast_unit<Src2, Dst2>(&self) -> Rotation3D<T, Src2, Dst2> {
427        Rotation3D {
428            i: self.i,
429            j: self.j,
430            k: self.k,
431            r: self.r,
432            _unit: PhantomData,
433        }
434    }
435
436    /// Drop the units, preserving only the numeric value.
437    ///
438    /// # Example
439    ///
440    /// ```rust
441    /// # use euclid::Rotation3D;
442    /// enum Local {}
443    /// enum World {}
444    ///
445    /// let to_world: Rotation3D<_, Local, World> = Rotation3D::quaternion(1, 2, 3, 4);
446    ///
447    /// assert_eq!(to_world.i, to_world.to_untyped().i);
448    /// assert_eq!(to_world.j, to_world.to_untyped().j);
449    /// assert_eq!(to_world.k, to_world.to_untyped().k);
450    /// assert_eq!(to_world.r, to_world.to_untyped().r);
451    /// ```
452    #[inline]
453    pub fn to_untyped(&self) -> Rotation3D<T, UnknownUnit, UnknownUnit> {
454        self.cast_unit()
455    }
456
457    /// Tag a unitless value with units.
458    ///
459    /// # Example
460    ///
461    /// ```rust
462    /// # use euclid::Rotation3D;
463    /// use euclid::UnknownUnit;
464    /// enum Local {}
465    /// enum World {}
466    ///
467    /// let rot: Rotation3D<_, UnknownUnit, UnknownUnit> = Rotation3D::quaternion(1, 2, 3, 4);
468    ///
469    /// assert_eq!(rot.i, Rotation3D::<_, Local, World>::from_untyped(&rot).i);
470    /// assert_eq!(rot.j, Rotation3D::<_, Local, World>::from_untyped(&rot).j);
471    /// assert_eq!(rot.k, Rotation3D::<_, Local, World>::from_untyped(&rot).k);
472    /// assert_eq!(rot.r, Rotation3D::<_, Local, World>::from_untyped(&rot).r);
473    /// ```
474    #[inline]
475    pub fn from_untyped(r: &Rotation3D<T, UnknownUnit, UnknownUnit>) -> Self {
476        r.cast_unit()
477    }
478}
479
480impl<T, Src, Dst> Rotation3D<T, Src, Dst>
481where
482    T: Real,
483{
484    /// Creates a rotation around from a quaternion representation and normalizes it.
485    ///
486    /// The parameters are a, b, c and r compose the quaternion `a*i + b*j + c*k + r`
487    /// before normalization, where `a`, `b` and `c` describe the vector part and the
488    /// last parameter `r` is the real part.
489    #[inline]
490    pub fn unit_quaternion(i: T, j: T, k: T, r: T) -> Self {
491        Self::quaternion(i, j, k, r).normalize()
492    }
493
494    /// Creates a rotation around a given axis.
495    pub fn around_axis(axis: Vector3D<T, Src>, angle: Angle<T>) -> Self {
496        let axis = axis.normalize();
497        let two = T::one() + T::one();
498        let (sin, cos) = Angle::sin_cos(angle / two);
499        Self::quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos)
500    }
501
502    /// Creates a rotation around the x axis.
503    pub fn around_x(angle: Angle<T>) -> Self {
504        let zero = Zero::zero();
505        let two = T::one() + T::one();
506        let (sin, cos) = Angle::sin_cos(angle / two);
507        Self::quaternion(sin, zero, zero, cos)
508    }
509
510    /// Creates a rotation around the y axis.
511    pub fn around_y(angle: Angle<T>) -> Self {
512        let zero = Zero::zero();
513        let two = T::one() + T::one();
514        let (sin, cos) = Angle::sin_cos(angle / two);
515        Self::quaternion(zero, sin, zero, cos)
516    }
517
518    /// Creates a rotation around the z axis.
519    pub fn around_z(angle: Angle<T>) -> Self {
520        let zero = Zero::zero();
521        let two = T::one() + T::one();
522        let (sin, cos) = Angle::sin_cos(angle / two);
523        Self::quaternion(zero, zero, sin, cos)
524    }
525
526    /// Creates a rotation from Euler angles.
527    ///
528    /// The rotations are applied in roll then pitch then yaw order.
529    ///
530    ///  - Roll (also called bank) is a rotation around the x axis.
531    ///  - Pitch (also called bearing) is a rotation around the y axis.
532    ///  - Yaw (also called heading) is a rotation around the z axis.
533    pub fn euler(roll: Angle<T>, pitch: Angle<T>, yaw: Angle<T>) -> Self {
534        let half = T::one() / (T::one() + T::one());
535
536        let (sy, cy) = Real::sin_cos(half * yaw.get());
537        let (sp, cp) = Real::sin_cos(half * pitch.get());
538        let (sr, cr) = Real::sin_cos(half * roll.get());
539
540        Self::quaternion(
541            cy * sr * cp - sy * cr * sp,
542            cy * cr * sp + sy * sr * cp,
543            sy * cr * cp - cy * sr * sp,
544            cy * cr * cp + sy * sr * sp,
545        )
546    }
547
548    /// Returns the inverse of this rotation.
549    #[inline]
550    pub fn inverse(&self) -> Rotation3D<T, Dst, Src> {
551        Rotation3D::quaternion(-self.i, -self.j, -self.k, self.r)
552    }
553
554    /// Computes the norm of this quaternion.
555    #[inline]
556    pub fn norm(&self) -> T {
557        self.square_norm().sqrt()
558    }
559
560    /// Computes the squared norm of this quaternion.
561    #[inline]
562    pub fn square_norm(&self) -> T {
563        self.i * self.i + self.j * self.j + self.k * self.k + self.r * self.r
564    }
565
566    /// Returns a [unit quaternion] from this one.
567    ///
568    /// [unit quaternion]: https://en.wikipedia.org/wiki/Quaternion#Unit_quaternion
569    #[inline]
570    pub fn normalize(&self) -> Self {
571        self.mul(T::one() / self.norm())
572    }
573
574    /// Returns `true` if [norm] of this quaternion is (approximately) one.
575    ///
576    /// [norm]: Self::norm
577    #[inline]
578    pub fn is_normalized(&self) -> bool
579    where
580        T: ApproxEq<T>,
581    {
582        let eps = NumCast::from(1.0e-5).unwrap();
583        self.square_norm().approx_eq_eps(&T::one(), &eps)
584    }
585
586    /// Spherical linear interpolation between this rotation and another rotation.
587    ///
588    /// `t` is expected to be between zero and one.
589    pub fn slerp(&self, other: &Self, t: T) -> Self
590    where
591        T: ApproxEq<T>,
592    {
593        debug_assert!(self.is_normalized());
594        debug_assert!(other.is_normalized());
595
596        let r1 = *self;
597        let mut r2 = *other;
598
599        let mut dot = r1.i * r2.i + r1.j * r2.j + r1.k * r2.k + r1.r * r2.r;
600
601        let one = T::one();
602
603        if dot.approx_eq(&T::one()) {
604            // If the inputs are too close, linearly interpolate to avoid precision issues.
605            return r1.lerp(&r2, t);
606        }
607
608        // If the dot product is negative, the quaternions
609        // have opposite handed-ness and slerp won't take
610        // the shorter path. Fix by reversing one quaternion.
611        if dot < T::zero() {
612            r2 = r2.mul(-T::one());
613            dot = -dot;
614        }
615
616        // For robustness, stay within the domain of acos.
617        dot = Real::min(dot, one);
618
619        // Angle between r1 and the result.
620        let theta = Real::acos(dot) * t;
621
622        // r1 and r3 form an orthonormal basis.
623        let r3 = r2.sub(r1.mul(dot)).normalize();
624        let (sin, cos) = Real::sin_cos(theta);
625        r1.mul(cos).add(r3.mul(sin))
626    }
627
628    /// Basic Linear interpolation between this rotation and another rotation.
629    #[inline]
630    pub fn lerp(&self, other: &Self, t: T) -> Self {
631        let one_t = T::one() - t;
632        self.mul(one_t).add(other.mul(t)).normalize()
633    }
634
635    /// Returns the given 3d point transformed by this rotation.
636    ///
637    /// The input point must be use the unit Src, and the returned point has the unit Dst.
638    pub fn transform_point3d(&self, point: Point3D<T, Src>) -> Point3D<T, Dst>
639    where
640        T: ApproxEq<T>,
641    {
642        debug_assert!(self.is_normalized());
643
644        let two = T::one() + T::one();
645        let cross = self.vector_part().cross(point.to_vector().to_untyped()) * two;
646
647        point3(
648            point.x + self.r * cross.x + self.j * cross.z - self.k * cross.y,
649            point.y + self.r * cross.y + self.k * cross.x - self.i * cross.z,
650            point.z + self.r * cross.z + self.i * cross.y - self.j * cross.x,
651        )
652    }
653
654    /// Returns the given 2d point transformed by this rotation then projected on the xy plane.
655    ///
656    /// The input point must be use the unit Src, and the returned point has the unit Dst.
657    #[inline]
658    pub fn transform_point2d(&self, point: Point2D<T, Src>) -> Point2D<T, Dst>
659    where
660        T: ApproxEq<T>,
661    {
662        self.transform_point3d(point.to_3d()).xy()
663    }
664
665    /// Returns the given 3d vector transformed by this rotation.
666    ///
667    /// The input vector must be use the unit Src, and the returned point has the unit Dst.
668    #[inline]
669    pub fn transform_vector3d(&self, vector: Vector3D<T, Src>) -> Vector3D<T, Dst>
670    where
671        T: ApproxEq<T>,
672    {
673        self.transform_point3d(vector.to_point()).to_vector()
674    }
675
676    /// Returns the given 2d vector transformed by this rotation then projected on the xy plane.
677    ///
678    /// The input vector must be use the unit Src, and the returned point has the unit Dst.
679    #[inline]
680    pub fn transform_vector2d(&self, vector: Vector2D<T, Src>) -> Vector2D<T, Dst>
681    where
682        T: ApproxEq<T>,
683    {
684        self.transform_vector3d(vector.to_3d()).xy()
685    }
686
687    /// Returns the matrix representation of this rotation.
688    #[inline]
689    #[rustfmt::skip]
690    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
691    where
692        T: ApproxEq<T>,
693    {
694        debug_assert!(self.is_normalized());
695
696        let i2 = self.i + self.i;
697        let j2 = self.j + self.j;
698        let k2 = self.k + self.k;
699        let ii = self.i * i2;
700        let ij = self.i * j2;
701        let ik = self.i * k2;
702        let jj = self.j * j2;
703        let jk = self.j * k2;
704        let kk = self.k * k2;
705        let ri = self.r * i2;
706        let rj = self.r * j2;
707        let rk = self.r * k2;
708
709        let one = T::one();
710        let zero = T::zero();
711
712        let m11 = one - (jj + kk);
713        let m12 = ij + rk;
714        let m13 = ik - rj;
715
716        let m21 = ij - rk;
717        let m22 = one - (ii + kk);
718        let m23 = jk + ri;
719
720        let m31 = ik + rj;
721        let m32 = jk - ri;
722        let m33 = one - (ii + jj);
723
724        Transform3D::new(
725            m11, m12, m13, zero,
726            m21, m22, m23, zero,
727            m31, m32, m33, zero,
728            zero, zero, zero, one,
729        )
730    }
731
732    /// Returns a rotation representing this rotation followed by the other rotation.
733    #[inline]
734    pub fn then<NewDst>(&self, other: &Rotation3D<T, Dst, NewDst>) -> Rotation3D<T, Src, NewDst>
735    where
736        T: ApproxEq<T>,
737    {
738        debug_assert!(self.is_normalized());
739        Rotation3D::quaternion(
740            other.i * self.r + other.r * self.i + other.j * self.k - other.k * self.j,
741            other.j * self.r + other.r * self.j + other.k * self.i - other.i * self.k,
742            other.k * self.r + other.r * self.k + other.i * self.j - other.j * self.i,
743            other.r * self.r - other.i * self.i - other.j * self.j - other.k * self.k,
744        )
745    }
746
747    // add, sub and mul are used internally for intermediate computation but aren't public
748    // because they don't carry real semantic meanings (I think?).
749
750    #[inline]
751    fn add(&self, other: Self) -> Self {
752        Self::quaternion(
753            self.i + other.i,
754            self.j + other.j,
755            self.k + other.k,
756            self.r + other.r,
757        )
758    }
759
760    #[inline]
761    fn sub(&self, other: Self) -> Self {
762        Self::quaternion(
763            self.i - other.i,
764            self.j - other.j,
765            self.k - other.k,
766            self.r - other.r,
767        )
768    }
769
770    #[inline]
771    fn mul(&self, factor: T) -> Self {
772        Self::quaternion(
773            self.i * factor,
774            self.j * factor,
775            self.k * factor,
776            self.r * factor,
777        )
778    }
779}
780
781impl<T: fmt::Debug, Src, Dst> fmt::Debug for Rotation3D<T, Src, Dst> {
782    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
783        write!(
784            f,
785            "Quat({:?}*i + {:?}*j + {:?}*k + {:?})",
786            self.i, self.j, self.k, self.r
787        )
788    }
789}
790
791impl<T, Src, Dst> ApproxEq<T> for Rotation3D<T, Src, Dst>
792where
793    T: Copy + Neg<Output = T> + ApproxEq<T>,
794{
795    fn approx_epsilon() -> T {
796        T::approx_epsilon()
797    }
798
799    fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
800        (self.i.approx_eq_eps(&other.i, eps)
801            && self.j.approx_eq_eps(&other.j, eps)
802            && self.k.approx_eq_eps(&other.k, eps)
803            && self.r.approx_eq_eps(&other.r, eps))
804            || (self.i.approx_eq_eps(&-other.i, eps)
805                && self.j.approx_eq_eps(&-other.j, eps)
806                && self.k.approx_eq_eps(&-other.k, eps)
807                && self.r.approx_eq_eps(&-other.r, eps))
808    }
809}
810
811impl<T, Src, Dst> From<Rotation3D<T, Src, Dst>> for Transform3D<T, Src, Dst>
812where
813    T: Real + ApproxEq<T>,
814{
815    fn from(r: Rotation3D<T, Src, Dst>) -> Self {
816        r.to_transform()
817    }
818}
819
820#[test]
821fn simple_rotation_2d() {
822    use crate::default::Rotation2D;
823    use core::f32::consts::{FRAC_PI_2, PI};
824
825    let ri = Rotation2D::identity();
826    let r90 = Rotation2D::radians(FRAC_PI_2);
827    let rm90 = Rotation2D::radians(-FRAC_PI_2);
828    let r180 = Rotation2D::radians(PI);
829
830    assert!(ri
831        .transform_point(point2(1.0, 2.0))
832        .approx_eq(&point2(1.0, 2.0)));
833    assert!(r90
834        .transform_point(point2(1.0, 2.0))
835        .approx_eq(&point2(-2.0, 1.0)));
836    assert!(rm90
837        .transform_point(point2(1.0, 2.0))
838        .approx_eq(&point2(2.0, -1.0)));
839    assert!(r180
840        .transform_point(point2(1.0, 2.0))
841        .approx_eq(&point2(-1.0, -2.0)));
842
843    assert!(r90
844        .inverse()
845        .inverse()
846        .transform_point(point2(1.0, 2.0))
847        .approx_eq(&r90.transform_point(point2(1.0, 2.0))));
848}
849
850#[test]
851fn simple_rotation_3d_in_2d() {
852    use crate::default::Rotation3D;
853    use core::f32::consts::{FRAC_PI_2, PI};
854
855    let ri = Rotation3D::identity();
856    let r90 = Rotation3D::around_z(Angle::radians(FRAC_PI_2));
857    let rm90 = Rotation3D::around_z(Angle::radians(-FRAC_PI_2));
858    let r180 = Rotation3D::around_z(Angle::radians(PI));
859
860    assert!(ri
861        .transform_point2d(point2(1.0, 2.0))
862        .approx_eq(&point2(1.0, 2.0)));
863    assert!(r90
864        .transform_point2d(point2(1.0, 2.0))
865        .approx_eq(&point2(-2.0, 1.0)));
866    assert!(rm90
867        .transform_point2d(point2(1.0, 2.0))
868        .approx_eq(&point2(2.0, -1.0)));
869    assert!(r180
870        .transform_point2d(point2(1.0, 2.0))
871        .approx_eq(&point2(-1.0, -2.0)));
872
873    assert!(r90
874        .inverse()
875        .inverse()
876        .transform_point2d(point2(1.0, 2.0))
877        .approx_eq(&r90.transform_point2d(point2(1.0, 2.0))));
878}
879
880#[test]
881fn pre_post() {
882    use crate::default::Rotation3D;
883    use core::f32::consts::FRAC_PI_2;
884
885    let r1 = Rotation3D::around_x(Angle::radians(FRAC_PI_2));
886    let r2 = Rotation3D::around_y(Angle::radians(FRAC_PI_2));
887    let r3 = Rotation3D::around_z(Angle::radians(FRAC_PI_2));
888
889    let t1 = r1.to_transform();
890    let t2 = r2.to_transform();
891    let t3 = r3.to_transform();
892
893    let p = point3(1.0, 2.0, 3.0);
894
895    // Check that the order of transformations is correct (corresponds to what
896    // we do in Transform3D).
897    let p1 = r1.then(&r2).then(&r3).transform_point3d(p);
898    let p2 = t1.then(&t2).then(&t3).transform_point3d(p);
899
900    assert!(p1.approx_eq(&p2.unwrap()));
901
902    // Check that changing the order indeed matters.
903    let p3 = t3.then(&t1).then(&t2).transform_point3d(p);
904    assert!(!p1.approx_eq(&p3.unwrap()));
905}
906
907#[test]
908fn to_transform3d() {
909    use crate::default::Rotation3D;
910
911    use core::f32::consts::{FRAC_PI_2, PI};
912    let rotations = [
913        Rotation3D::identity(),
914        Rotation3D::around_x(Angle::radians(FRAC_PI_2)),
915        Rotation3D::around_x(Angle::radians(-FRAC_PI_2)),
916        Rotation3D::around_x(Angle::radians(PI)),
917        Rotation3D::around_y(Angle::radians(FRAC_PI_2)),
918        Rotation3D::around_y(Angle::radians(-FRAC_PI_2)),
919        Rotation3D::around_y(Angle::radians(PI)),
920        Rotation3D::around_z(Angle::radians(FRAC_PI_2)),
921        Rotation3D::around_z(Angle::radians(-FRAC_PI_2)),
922        Rotation3D::around_z(Angle::radians(PI)),
923    ];
924
925    let points = [
926        point3(0.0, 0.0, 0.0),
927        point3(1.0, 2.0, 3.0),
928        point3(-5.0, 3.0, -1.0),
929        point3(-0.5, -1.0, 1.5),
930    ];
931
932    for rotation in &rotations {
933        for &point in &points {
934            let p1 = rotation.transform_point3d(point);
935            let p2 = rotation.to_transform().transform_point3d(point);
936            assert!(p1.approx_eq(&p2.unwrap()));
937        }
938    }
939}
940
941#[test]
942fn slerp() {
943    use crate::default::Rotation3D;
944
945    let q1 = Rotation3D::quaternion(1.0, 0.0, 0.0, 0.0);
946    let q2 = Rotation3D::quaternion(0.0, 1.0, 0.0, 0.0);
947    let q3 = Rotation3D::quaternion(0.0, 0.0, -1.0, 0.0);
948
949    // The values below can be obtained with a python program:
950    // import numpy
951    // import quaternion
952    // q1 = numpy.quaternion(1, 0, 0, 0)
953    // q2 = numpy.quaternion(0, 1, 0, 0)
954    // quaternion.slerp_evaluate(q1, q2, 0.2)
955
956    assert!(q1.slerp(&q2, 0.0).approx_eq(&q1));
957    assert!(q1.slerp(&q2, 0.2).approx_eq(&Rotation3D::quaternion(
958        0.951056516295154,
959        0.309016994374947,
960        0.0,
961        0.0
962    )));
963    assert!(q1.slerp(&q2, 0.4).approx_eq(&Rotation3D::quaternion(
964        0.809016994374947,
965        0.587785252292473,
966        0.0,
967        0.0
968    )));
969    assert!(q1.slerp(&q2, 0.6).approx_eq(&Rotation3D::quaternion(
970        0.587785252292473,
971        0.809016994374947,
972        0.0,
973        0.0
974    )));
975    assert!(q1.slerp(&q2, 0.8).approx_eq(&Rotation3D::quaternion(
976        0.309016994374947,
977        0.951056516295154,
978        0.0,
979        0.0
980    )));
981    assert!(q1.slerp(&q2, 1.0).approx_eq(&q2));
982
983    assert!(q1.slerp(&q3, 0.0).approx_eq(&q1));
984    assert!(q1.slerp(&q3, 0.2).approx_eq(&Rotation3D::quaternion(
985        0.951056516295154,
986        0.0,
987        -0.309016994374947,
988        0.0
989    )));
990    assert!(q1.slerp(&q3, 0.4).approx_eq(&Rotation3D::quaternion(
991        0.809016994374947,
992        0.0,
993        -0.587785252292473,
994        0.0
995    )));
996    assert!(q1.slerp(&q3, 0.6).approx_eq(&Rotation3D::quaternion(
997        0.587785252292473,
998        0.0,
999        -0.809016994374947,
1000        0.0
1001    )));
1002    assert!(q1.slerp(&q3, 0.8).approx_eq(&Rotation3D::quaternion(
1003        0.309016994374947,
1004        0.0,
1005        -0.951056516295154,
1006        0.0
1007    )));
1008    assert!(q1.slerp(&q3, 1.0).approx_eq(&q3));
1009}
1010
1011#[test]
1012fn around_axis() {
1013    use crate::default::Rotation3D;
1014    use core::f32::consts::{FRAC_PI_2, PI};
1015
1016    // Two sort of trivial cases:
1017    let r1 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Angle::radians(PI));
1018    let r2 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Angle::radians(FRAC_PI_2));
1019    assert!(r1
1020        .transform_point3d(point3(1.0, 2.0, 0.0))
1021        .approx_eq(&point3(2.0, 1.0, 0.0)));
1022    assert!(r2
1023        .transform_point3d(point3(1.0, 0.0, 0.0))
1024        .approx_eq(&point3(0.5, 0.5, -0.5.sqrt())));
1025
1026    // A more arbitrary test (made up with numpy):
1027    let r3 = Rotation3D::around_axis(vec3(0.5, 1.0, 2.0), Angle::radians(2.291288));
1028    assert!(r3
1029        .transform_point3d(point3(1.0, 0.0, 0.0))
1030        .approx_eq(&point3(-0.58071821, 0.81401868, -0.01182979)));
1031}
1032
1033#[test]
1034fn from_euler() {
1035    use crate::default::Rotation3D;
1036    use core::f32::consts::FRAC_PI_2;
1037
1038    // First test simple separate yaw pitch and roll rotations, because it is easy to come
1039    // up with the corresponding quaternion.
1040    // Since several quaternions can represent the same transformation we compare the result
1041    // of transforming a point rather than the values of each quaternions.
1042    let p = point3(1.0, 2.0, 3.0);
1043
1044    let angle = Angle::radians(FRAC_PI_2);
1045    let zero = Angle::radians(0.0);
1046
1047    // roll
1048    let roll_re = Rotation3D::euler(angle, zero, zero);
1049    let roll_rq = Rotation3D::around_x(angle);
1050    let roll_pe = roll_re.transform_point3d(p);
1051    let roll_pq = roll_rq.transform_point3d(p);
1052
1053    // pitch
1054    let pitch_re = Rotation3D::euler(zero, angle, zero);
1055    let pitch_rq = Rotation3D::around_y(angle);
1056    let pitch_pe = pitch_re.transform_point3d(p);
1057    let pitch_pq = pitch_rq.transform_point3d(p);
1058
1059    // yaw
1060    let yaw_re = Rotation3D::euler(zero, zero, angle);
1061    let yaw_rq = Rotation3D::around_z(angle);
1062    let yaw_pe = yaw_re.transform_point3d(p);
1063    let yaw_pq = yaw_rq.transform_point3d(p);
1064
1065    assert!(roll_pe.approx_eq(&roll_pq));
1066    assert!(pitch_pe.approx_eq(&pitch_pq));
1067    assert!(yaw_pe.approx_eq(&yaw_pq));
1068
1069    // Now check that the yaw pitch and roll transformations when combined are applied in
1070    // the proper order: roll -> pitch -> yaw.
1071    let ypr_e = Rotation3D::euler(angle, angle, angle);
1072    let ypr_q = roll_rq.then(&pitch_rq).then(&yaw_rq);
1073    let ypr_pe = ypr_e.transform_point3d(p);
1074    let ypr_pq = ypr_q.transform_point3d(p);
1075
1076    assert!(ypr_pe.approx_eq(&ypr_pq));
1077}