glam_ext/f64_ext/
transform3a.rs

1#[cfg(feature = "approx")]
2use approx::{AbsDiffEq, RelativeEq, UlpsEq};
3use core::ops::{Mul, MulAssign};
4use glam::{DAffine3, DMat3, DMat4, DQuat, DVec3};
5
6use crate::macros::glam_assert;
7
8#[repr(C)]
9#[derive(Debug, Default, Clone, Copy, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
12pub struct DTransform3 {
13    pub translation: DVec3,
14    pub rotation: DQuat,
15    pub scale: DVec3,
16}
17
18impl DTransform3 {
19    /// The degenerate zero transform.
20    ///
21    /// This transforms any finite vector and point to zero.
22    /// The zero transform is non-invertible.
23    pub const ZERO: Self = Self {
24        translation: DVec3::ZERO,
25        rotation: DQuat::IDENTITY,
26        scale: DVec3::ONE,
27    };
28
29    /// The identity transform.
30    ///
31    /// Multiplying a vector with this returns the same vector.
32    pub const IDENTITY: Self = Self {
33        translation: DVec3::ZERO,
34        rotation: DQuat::IDENTITY,
35        scale: DVec3::ONE,
36    };
37
38    /// All NAN:s.
39    pub const NAN: Self = Self {
40        translation: DVec3::NAN,
41        rotation: DQuat::NAN,
42        scale: DVec3::NAN,
43    };
44
45    /// Creates a new transform.
46    #[inline]
47    #[must_use]
48    pub fn new(translation: DVec3, rotation: DQuat, scale: DVec3) -> Self {
49        Self {
50            translation,
51            rotation,
52            scale,
53        }
54    }
55
56    /// Creates an affine transform that changes scale.
57    /// Note that if any scale is zero the transform will be non-invertible.
58    #[inline]
59    #[must_use]
60    pub fn from_scale(scale: DVec3) -> Self {
61        Self {
62            translation: DVec3::ZERO,
63            rotation: DQuat::IDENTITY,
64            scale,
65        }
66    }
67
68    /// Creates a transform transform from the given `rotation` quaternion.
69    #[inline]
70    #[must_use]
71    pub fn from_quat(rotation: DQuat) -> Self {
72        Self {
73            translation: DVec3::ZERO,
74            rotation,
75            scale: DVec3::ONE,
76        }
77    }
78
79    /// Creates a transform transform containing a 3D rotation around a normalized
80    /// rotation `axis` of `angle` (in radians).
81    #[inline]
82    #[must_use]
83    pub fn from_axis_angle(axis: DVec3, angle: f64) -> Self {
84        Self {
85            translation: DVec3::ZERO,
86            rotation: DQuat::from_axis_angle(axis, angle),
87            scale: DVec3::ONE,
88        }
89    }
90
91    /// Creates a transform transform containing a 3D rotation around the x axis of
92    /// `angle` (in radians).
93    #[inline]
94    #[must_use]
95    pub fn from_rotation_x(angle: f64) -> Self {
96        Self {
97            translation: DVec3::ZERO,
98            rotation: DQuat::from_rotation_x(angle),
99            scale: DVec3::ONE,
100        }
101    }
102
103    /// Creates a transform transform containing a 3D rotation around the y axis of
104    /// `angle` (in radians).
105    #[inline]
106    #[must_use]
107    pub fn from_rotation_y(angle: f64) -> Self {
108        Self {
109            translation: DVec3::ZERO,
110            rotation: DQuat::from_rotation_y(angle),
111            scale: DVec3::ONE,
112        }
113    }
114
115    /// Creates a transform transform containing a 3D rotation around the z axis of
116    /// `angle` (in radians).
117    #[inline]
118    #[must_use]
119    pub fn from_rotation_z(angle: f64) -> Self {
120        Self {
121            translation: DVec3::ZERO,
122            rotation: DQuat::from_rotation_z(angle),
123            scale: DVec3::ONE,
124        }
125    }
126
127    /// Creates a transform transformation from the given 3D `translation`.
128    #[inline]
129    #[must_use]
130    pub fn from_translation(translation: DVec3) -> Self {
131        Self {
132            translation,
133            rotation: DQuat::IDENTITY,
134            scale: DVec3::ONE,
135        }
136    }
137
138    /// Creates a transform from the given 3D `rotation` and `translation`.
139    #[inline]
140    #[must_use]
141    pub fn from_rotation_translation(rotation: DQuat, translation: DVec3) -> Self {
142        Self {
143            translation,
144            rotation,
145            scale: DVec3::ONE,
146        }
147    }
148
149    /// Creates a transform from the given 3D `scale`, `rotation` and `translation`.
150    #[inline]
151    #[must_use]
152    pub fn from_scale_rotation_translation(scale: DVec3, rotation: DQuat, translation: DVec3) -> Self {
153        Self {
154            translation,
155            rotation,
156            scale,
157        }
158    }
159
160    /// Creates a transform from a 3x3 matrix (expressing scale and rotation)
161    ///
162    /// Note if the input matrix is non-uniform or shear, the result transform will be ill-defined.
163    #[inline]
164    #[must_use]
165    pub fn from_mat3(mat3: DMat3) -> Self {
166        Self::from_mat3_translation(mat3, DVec3::ZERO)
167    }
168
169    /// Creates a transform from a 3x3 matrix (expressing scale and rotation)
170    ///
171    /// Note if the input matrix is non-uniform or shear, the result transform will be ill-defined.
172    #[inline]
173    #[must_use]
174    pub fn from_mat3_translation(mat3: DMat3, translation: DVec3) -> Self {
175        use super::math;
176        let det = mat3.determinant();
177        glam_assert!(det != 0.0);
178
179        let scale = DVec3::new(
180            mat3.x_axis.length() * math::signum(det),
181            mat3.y_axis.length(),
182            mat3.z_axis.length(),
183        );
184
185        glam_assert!(scale.cmpne(DVec3::ZERO).all());
186
187        let inv_scale = scale.recip();
188
189        let rotation = DQuat::from_mat3(&DMat3::from_cols(
190            mat3.x_axis * inv_scale.x,
191            mat3.y_axis * inv_scale.y,
192            mat3.z_axis * inv_scale.z,
193        ));
194        Self {
195            translation,
196            rotation,
197            scale,
198        }
199    }
200
201    /// Creates a transform from a 4x4 matrix.
202    ///
203    /// Note if the input matrix is non-uniform or shear, the result transform will be ill-defined.
204    #[inline]
205    #[must_use]
206    pub fn from_mat4(mat4: DMat4) -> Self {
207        let translation = mat4.w_axis.truncate();
208        Self::from_mat3_translation(DMat3::from_mat4(mat4), translation)
209    }
210
211    /// Extracts `scale`, `rotation` and `translation` from `self`.
212    #[inline]
213    #[must_use]
214    pub fn to_scale_rotation_translation(&self) -> (DVec3, DQuat, DVec3) {
215        (self.scale, self.rotation, self.translation)
216    }
217
218    /// Transforms the given 3D points, applying scale, rotation and translation.
219    #[inline]
220    #[must_use]
221    pub fn transform_point3(&self, rhs: DVec3) -> DVec3 {
222        let scale: DVec3 = self.scale;
223        let translation: DVec3 = self.translation;
224        self.rotation * (rhs * scale) + translation
225    }
226
227    /// Transforms the given 3D vector, applying scale and rotation (but NOT translation).
228    #[inline]
229    #[must_use]
230    pub fn transform_vector3(&self, rhs: DVec3) -> DVec3 {
231        let scale: DVec3 = self.scale;
232        self.rotation * (rhs * scale)
233    }
234
235    /// Returns `true` if, and only if, all elements are finite.
236    ///
237    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
238    #[inline]
239    #[must_use]
240    pub fn is_finite(&self) -> bool {
241        self.translation.is_finite() && self.rotation.is_finite() && self.scale.is_finite()
242    }
243
244    /// Returns `true` if any elements are `NaN`.
245    #[inline]
246    #[must_use]
247    pub fn is_nan(&self) -> bool {
248        self.translation.is_nan() && self.rotation.is_nan() && self.scale.is_nan()
249    }
250
251    /// Returns true if the absolute difference of all elements between `self` and `rhs`
252    /// is less than or equal to `max_abs_diff`.
253    #[inline]
254    #[must_use]
255    pub fn abs_diff_eq(self, rhs: Self, max_abs_diff: f64) -> bool {
256        self.translation.abs_diff_eq(rhs.translation, max_abs_diff)
257            && self.rotation.abs_diff_eq(rhs.rotation, max_abs_diff)
258            && self.scale.abs_diff_eq(rhs.scale, max_abs_diff)
259    }
260
261    /// Return the inverse of this transform.
262    ///
263    /// Note that if the transform is not invertible the result will be invalid.
264    #[inline]
265    #[must_use]
266    pub fn inverse(&self) -> Self {
267        let rot = DMat3::from_quat(self.rotation);
268        let mat_inv = DMat3::from_cols(
269            rot.x_axis * self.scale.x,
270            rot.y_axis * self.scale.y,
271            rot.z_axis * self.scale.z,
272        )
273        .inverse();
274        let translation = -(mat_inv * self.translation);
275        DTransform3::from_mat3_translation(mat_inv, translation)
276    }
277}
278
279impl From<DTransform3> for DMat4 {
280    #[inline]
281    fn from(t: DTransform3) -> DMat4 {
282        let mat3 = DMat3::from_quat(t.rotation);
283        DMat4::from_cols(
284            (mat3.x_axis * t.scale.x).extend(0.0),
285            (mat3.y_axis * t.scale.y).extend(0.0),
286            (mat3.z_axis * t.scale.z).extend(0.0),
287            t.translation.extend(1.0),
288        )
289    }
290}
291
292impl From<DTransform3> for DAffine3 {
293    #[inline]
294    fn from(t: DTransform3) -> DAffine3 {
295        DAffine3::from_scale_rotation_translation(t.scale, t.rotation, t.translation)
296    }
297}
298
299impl Mul for DTransform3 {
300    type Output = DTransform3;
301
302    #[inline]
303    fn mul(self, rhs: Self) -> Self::Output {
304        let rot1 = DMat3::from_quat(self.rotation);
305        let mat1 = DMat3::from_cols(
306            rot1.x_axis * self.scale.x,
307            rot1.y_axis * self.scale.y,
308            rot1.z_axis * self.scale.z,
309        );
310        let rot2 = DMat3::from_quat(rhs.rotation);
311        let mat2 = DMat3::from_cols(
312            rot2.x_axis * rhs.scale.x,
313            rot2.y_axis * rhs.scale.y,
314            rot2.z_axis * rhs.scale.z,
315        );
316        let translation = self.rotation * (self.scale * rhs.translation) + self.translation;
317        DTransform3::from_mat3_translation(mat1 * mat2, translation)
318    }
319}
320
321impl MulAssign for DTransform3 {
322    #[inline]
323    fn mul_assign(&mut self, rhs: DTransform3) {
324        *self = self.mul(rhs);
325    }
326}
327
328impl Mul<DMat4> for DTransform3 {
329    type Output = DMat4;
330
331    #[inline]
332    fn mul(self, rhs: DMat4) -> Self::Output {
333        DMat4::from(self) * rhs
334    }
335}
336
337impl Mul<DTransform3> for DMat4 {
338    type Output = DMat4;
339
340    #[inline]
341    fn mul(self, rhs: DTransform3) -> Self::Output {
342        self * DMat4::from(rhs)
343    }
344}
345
346#[cfg(feature = "approx")]
347impl AbsDiffEq for DTransform3 {
348    type Epsilon = <f64 as AbsDiffEq>::Epsilon;
349
350    #[inline]
351    fn default_epsilon() -> Self::Epsilon {
352        f64::default_epsilon()
353    }
354
355    #[inline]
356    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
357        self.translation.abs_diff_eq(other.translation, epsilon)
358            && self.rotation.abs_diff_eq(other.rotation, epsilon)
359            && self.scale.abs_diff_eq(other.scale, epsilon)
360    }
361}
362
363#[cfg(feature = "approx")]
364impl RelativeEq for DTransform3 {
365    #[inline]
366    fn default_max_relative() -> Self::Epsilon {
367        f64::default_max_relative()
368    }
369
370    #[inline]
371    fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
372        self.translation.relative_eq(&other.translation, epsilon, max_relative)
373            && self.rotation.relative_eq(&other.rotation, epsilon, max_relative)
374            && self.scale.relative_eq(&other.scale, epsilon, max_relative)
375    }
376}
377
378#[cfg(feature = "approx")]
379impl UlpsEq for DTransform3 {
380    #[inline]
381    fn default_max_ulps() -> u32 {
382        f64::default_max_ulps()
383    }
384
385    #[inline]
386    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
387        self.translation.ulps_eq(&other.translation, epsilon, max_ulps)
388            && self.rotation.ulps_eq(&other.rotation, epsilon, max_ulps)
389            && self.scale.ulps_eq(&other.scale, epsilon, max_ulps)
390    }
391}
392
393#[cfg(test)]
394mod test {
395    use super::*;
396
397    #[test]
398    fn test_from_mat4() {
399        let scale = DVec3::new(0.5, 1.0, 2.0);
400        let rot = DQuat::from_rotation_y(-0.6);
401        let pos = DVec3::new(1.0, -2.0, 3.0);
402        let mat = DMat4::from_scale_rotation_translation(scale, rot, pos);
403        let tran = DTransform3::from_mat4(mat);
404        assert!(DVec3::abs_diff_eq(tran.scale, scale, 1e-6));
405        assert!(DQuat::abs_diff_eq(tran.rotation, rot, 1e-6));
406        assert!(DVec3::abs_diff_eq(tran.translation, pos, 1e-6));
407    }
408
409    #[test]
410    fn test_transform_point3() {
411        let scale = DVec3::new(1.0, 0.7, 0.5);
412        let rot = DQuat::from_rotation_x(0.41);
413        let pos = DVec3::new(1.1, 2.1, -3.1);
414        let mat = DMat4::from_scale_rotation_translation(scale, rot, pos);
415        let tran = DTransform3::from_mat4(mat);
416
417        let point = DVec3::new(5.0, -5.0, 5.0);
418        let p1 = mat.project_point3(point);
419        let p2 = tran.transform_point3(point);
420        assert!(DVec3::abs_diff_eq(p1, p2, 1e-6));
421    }
422
423    #[test]
424    fn test_transform_vec3() {
425        let scale = DVec3::new(2.0, 2.0, 0.35);
426        let rot = DQuat::from_rotation_z(-0.2);
427        let pos = DVec3::new(-1.5, 2.5, 4.5);
428        let mat = DMat4::from_scale_rotation_translation(scale, rot, pos);
429        let tran = DTransform3::from_mat4(mat);
430
431        let vec = DVec3::new(1.0, 0.0, 0.7);
432        let v1 = mat.transform_vector3(vec);
433        let v2 = tran.transform_vector3(vec);
434        assert!(DVec3::abs_diff_eq(v1, v2, 1e-6));
435    }
436
437    #[test]
438    fn test_inverse() {
439        let scale = DVec3::new(2.0, 1.7, 0.35);
440        let rot = DQuat::from_rotation_z(1.5) * DQuat::from_rotation_x(1.0);
441        let pos = DVec3::new(1.99, 0.77, -1.55);
442        let mat = DMat4::from_scale_rotation_translation(scale, rot, pos);
443        let mat_inv = mat.inverse();
444        let tran1 = DTransform3::from_mat4(mat).inverse();
445        let tran2 = DTransform3::from_mat4(mat_inv);
446        assert!(DTransform3::abs_diff_eq(tran1, tran2, 1e-6));
447    }
448
449    #[test]
450    fn test_mat4_from() {
451        let scale = DVec3::new(3.1, 0.7, 1.11);
452        let rot = DQuat::from_rotation_y(-2.0);
453        let pos = DVec3::new(3.0, 3.3, 3.33);
454        let mat = DMat4::from_scale_rotation_translation(scale, rot, pos);
455        let is = DTransform3::from_mat4(mat);
456        let mat2 = DMat4::from(is);
457        assert!(DMat4::abs_diff_eq(&mat, mat2, 1e-6));
458    }
459
460    #[test]
461    fn test_transform_mul() {
462        let scale1 = DVec3::new(1.1, 1.4, 1.7);
463        let rot1 = DQuat::from_rotation_x(0.77);
464        let pos1 = DVec3::new(5.5, -6.6, 3.3);
465        let mat1 = DMat4::from_scale_rotation_translation(scale1, rot1, pos1);
466        let tran1 = DTransform3::from_scale_rotation_translation(scale1, rot1, pos1);
467
468        let scale2 = DVec3::new(0.3, 1.0, 0.7);
469        let rot2 = DQuat::from_rotation_y(-0.44);
470        let pos2 = DVec3::new(-4.4, -2.2, -3.3);
471        let mat2 = DMat4::from_scale_rotation_translation(scale2, rot2, pos2);
472        let tran2 = DTransform3::from_scale_rotation_translation(scale2, rot2, pos2);
473
474        let mat = mat1 * mat2;
475        let tran = tran1 * tran2;
476        let tran_mat = DTransform3::from_mat4(mat);
477        assert!(DTransform3::abs_diff_eq(tran, tran_mat, 1e-6));
478    }
479}