glam_ext/f64_ext/
transform3a.rs

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