Skip to main content

arris_math/
isometry.rs

1//! Rigid motions.
2
3use nalgebra::{Isometry3, Translation3, UnitQuaternion};
4
5use crate::{Frame, Point3, UnitVec3, Vec3};
6
7/// A rigid motion: a rotation followed by a translation, `p ↦ R p + t`.
8/// Lengths and angles are preserved, so a `Frame` moved by one is still a
9/// frame and geometry moved by one is the same geometry in another place
10/// (`docs/DATA-MODEL.md` §Conventions: a transform is a frame change).
11///
12/// ```
13/// use arris_math::{Isometry, Point3, Vec3, nalgebra::UnitQuaternion};
14/// use core::f64::consts::FRAC_PI_2;
15///
16/// let quarter_turn = UnitQuaternion::from_axis_angle(&Vec3::z_axis(), FRAC_PI_2);
17/// let m = Isometry::new(quarter_turn, Vec3::new(1.0, 0.0, 0.0));
18/// let p = m.apply(Point3::new(1.0, 0.0, 0.0));
19/// assert!((p - Point3::new(1.0, 1.0, 0.0)).norm() < 1e-15);
20/// let back = m.inverse().apply(p);
21/// assert!((back - Point3::new(1.0, 0.0, 0.0)).norm() < 1e-15);
22/// ```
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct Isometry {
25    inner: Isometry3<f64>,
26}
27
28impl Isometry {
29    /// `p ↦ rotation · p + translation`.
30    pub fn new(rotation: UnitQuaternion<f64>, translation: Vec3) -> Self {
31        Isometry {
32            inner: Isometry3::from_parts(Translation3::from(translation), rotation),
33        }
34    }
35
36    /// The motion that moves nothing.
37    pub fn identity() -> Self {
38        Isometry {
39            inner: Isometry3::identity(),
40        }
41    }
42
43    /// A pure translation by `v`.
44    pub fn from_translation(v: Vec3) -> Self {
45        Self::new(UnitQuaternion::identity(), v)
46    }
47
48    /// A pure rotation about the origin.
49    pub fn from_rotation(rotation: UnitQuaternion<f64>) -> Self {
50        Self::new(rotation, Vec3::zeros())
51    }
52
53    /// The rotation part.
54    pub fn rotation(&self) -> UnitQuaternion<f64> {
55        self.inner.rotation
56    }
57
58    /// The translation part: where the origin goes.
59    pub fn translation(&self) -> Vec3 {
60        self.inner.translation.vector
61    }
62
63    /// `R p + t`.
64    pub fn apply(&self, p: Point3) -> Point3 {
65        self.inner.transform_point(&p)
66    }
67
68    /// `R v`: a vector is a difference of points, so the translation does
69    /// not act on it.
70    pub fn apply_vec(&self, v: Vec3) -> Vec3 {
71        self.inner.transform_vector(&v)
72    }
73
74    /// `R u`, re-normalised so the result is unit to rounding.
75    pub fn apply_unit(&self, u: UnitVec3) -> UnitVec3 {
76        UnitVec3::new_normalize(self.inner.transform_vector(&u))
77    }
78
79    /// The frame moved by this motion; the same as
80    /// [`Frame::transformed`].
81    pub fn apply_frame(&self, frame: &Frame) -> Frame {
82        frame.transformed(self)
83    }
84
85    /// The motion that is `self` first, then `next`:
86    /// `a.then(&b).apply(p) == b.apply(a.apply(p))` to rounding.
87    pub fn then(&self, next: &Isometry) -> Isometry {
88        Isometry {
89            inner: next.inner * self.inner,
90        }
91    }
92
93    /// The motion that undoes this one: `m.inverse().apply(m.apply(p)) ==
94    /// p` to rounding.
95    pub fn inverse(&self) -> Isometry {
96        Isometry {
97            inner: self.inner.inverse(),
98        }
99    }
100}
101
102impl Default for Isometry {
103    fn default() -> Self {
104        Self::identity()
105    }
106}