godot_core/builtin/
basis.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use std::cmp::Ordering;
9use std::fmt::Display;
10use std::ops::{Mul, MulAssign};
11
12use godot_ffi as sys;
13use sys::{ffi_methods, ExtVariantType, GodotFfi};
14
15use crate::builtin::math::{ApproxEq, FloatExt, GlamConv, GlamType, XformInv};
16use crate::builtin::real_consts::FRAC_PI_2;
17use crate::builtin::{real, EulerOrder, Quaternion, RMat3, RQuat, RVec2, RVec3, Vector3};
18
19/// A 3x3 matrix, typically used as an orthogonal basis for [`Transform3D`](crate::builtin::Transform3D).
20///
21/// Indexing into a `Basis` is done in row-major order. So `mat[1]` would return the first *row* and not
22/// the first *column*/basis vector. This means that indexing into the matrix happens in the same order
23/// it usually does in math, except that we index starting at 0.
24///
25/// The basis vectors are the columns of the matrix, whereas the [`rows`](Self::rows) field represents
26/// the row vectors.
27///
28/// Note that the names of the column vectors here are `a`, `b`, and `c`, which differs from Godot's convention of `x`, `y`, and `z`.  This is
29/// because columns are the basis vectors of the transform, while rows represent the X/Y/Z coordinates of each vector. Although basis vectors are
30/// the _transformed_ unit vectors of X/Y/Z axes, they have no direct relation to those axes in the _transformed_ coordinate system. Thus, an
31/// independent notion of a, b, c does not suggest such a relation.  Furthermore, there are sometimes expressions such as `x.x`, `x.y`, `y.x`
32/// etc. They are typically hard to read and error-prone to write. Having `a.x`, `a.y`, `b.x` makes things more understandable.
33///
34/// # All matrix types
35///
36/// | Dimension | Orthogonal basis   | Affine transform      | Projective transform |
37/// |-----------|--------------------|-----------------------|----------------------|
38/// | 2D        |                    | [`Transform2D`] (2x3) |                      |
39/// | 3D        | **`Basis`** (3x3)  | [`Transform3D`] (3x4) | [`Projection`] (4x4) |
40///
41/// [`Transform2D`]: crate::builtin::Transform2D
42/// [`Transform3D`]: crate::builtin::Transform3D
43/// [`Projection`]: crate::builtin::Projection
44///
45/// # Godot docs
46///
47/// [`Basis` (stable)](https://docs.godotengine.org/en/stable/classes/class_basis.html)
48#[derive(Copy, Clone, PartialEq, Debug)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[repr(C)]
51pub struct Basis {
52    /// The rows of the matrix. These are *not* the basis vectors.  
53    ///
54    /// To access the basis vectors see [`col_a()`](Self::col_a), [`set_col_a()`](Self::set_col_a),
55    /// [`col_b()`](Self::col_b), [`set_col_b()`](Self::set_col_b), [`col_c`](Self::col_c()),
56    /// [`set_col_c()`](Self::set_col_c).
57    pub rows: [Vector3; 3],
58}
59
60impl Basis {
61    /// The identity basis, with no rotation or scaling applied.
62    ///
63    /// _Godot equivalent: `Basis.IDENTITY`_
64    pub const IDENTITY: Self = Self::from_diagonal(1.0, 1.0, 1.0);
65
66    /// The basis that will flip something along the X axis when used in a transformation.
67    ///
68    /// _Godot equivalent: `Basis.FLIP_X`_
69    pub const FLIP_X: Self = Self::from_diagonal(-1.0, 1.0, 1.0);
70
71    /// The basis that will flip something along the Y axis when used in a transformation.
72    ///
73    /// _Godot equivalent: `Basis.FLIP_Y`_
74    pub const FLIP_Y: Self = Self::from_diagonal(1.0, -1.0, 1.0);
75
76    /// The basis that will flip something along the Z axis when used in a transformation.
77    ///
78    /// _Godot equivalent: `Basis.FLIP_Z`_
79    pub const FLIP_Z: Self = Self::from_diagonal(1.0, 1.0, -1.0);
80
81    /// Create a new basis from 3 row vectors. These are *not* basis vectors.
82    pub const fn from_rows(x: Vector3, y: Vector3, z: Vector3) -> Self {
83        Self { rows: [x, y, z] }
84    }
85
86    /// Create a new basis from 3 column vectors.
87    pub const fn from_cols(a: Vector3, b: Vector3, c: Vector3) -> Self {
88        Self::from_rows_array(&[a.x, b.x, c.x, a.y, b.y, c.y, a.z, b.z, c.z])
89    }
90
91    /// Create a `Basis` from an axis and angle.
92    ///
93    /// _Godot equivalent: `Basis(Vector3 axis, float angle)`_
94    pub fn from_axis_angle(axis: Vector3, angle: real) -> Self {
95        RMat3::from_axis_angle(axis.to_glam(), angle).to_front()
96    }
97
98    /// Create a diagonal matrix from the given values.
99    pub const fn from_diagonal(x: real, y: real, z: real) -> Self {
100        Self {
101            rows: [
102                Vector3::new(x, 0.0, 0.0),
103                Vector3::new(0.0, y, 0.0),
104                Vector3::new(0.0, 0.0, z),
105            ],
106        }
107    }
108
109    /// Create a diagonal matrix from the given values.
110    ///
111    /// _Godot equivalent: `Basis.from_scale(Vector3 scale)`
112    pub const fn from_scale(scale: Vector3) -> Self {
113        Self::from_diagonal(scale.x, scale.y, scale.z)
114    }
115
116    const fn from_rows_array(rows: &[real; 9]) -> Self {
117        let [ax, bx, cx, ay, by, cy, az, bz, cz] = rows;
118        Self::from_rows(
119            Vector3::new(*ax, *bx, *cx),
120            Vector3::new(*ay, *by, *cy),
121            Vector3::new(*az, *bz, *cz),
122        )
123    }
124
125    /// Create a `Basis` from a `Quaternion`.
126    ///
127    /// _Godot equivalent: `Basis(Quaternion from)`_
128    pub fn from_quaternion(quat: Quaternion) -> Self {
129        RMat3::from_quat(quat.to_glam()).to_front()
130    }
131
132    /// Create a `Basis` from three angles `a`, `b`, and `c` interpreted
133    /// as Euler angles according to the given `EulerOrder`.
134    ///
135    /// _Godot equivalent: `Basis.from_euler(Vector3 euler, int order)`_
136    pub fn from_euler(order: EulerOrder, angles: Vector3) -> Self {
137        // Translated from "Basis::from_euler" in
138        // https://github.com/godotengine/godot/blob/master/core/math/basis.cpp
139
140        // We can't use glam to do these conversions since glam uses intrinsic rotations
141        // whereas godot uses extrinsic rotations.
142        // see https://github.com/bitshifter/glam-rs/issues/337
143        let Vector3 { x: a, y: b, z: c } = angles;
144        let xmat =
145            Basis::from_rows_array(&[1.0, 0.0, 0.0, 0.0, a.cos(), -a.sin(), 0.0, a.sin(), a.cos()]);
146        let ymat =
147            Basis::from_rows_array(&[b.cos(), 0.0, b.sin(), 0.0, 1.0, 0.0, -b.sin(), 0.0, b.cos()]);
148        let zmat =
149            Basis::from_rows_array(&[c.cos(), -c.sin(), 0.0, c.sin(), c.cos(), 0.0, 0.0, 0.0, 1.0]);
150
151        match order {
152            EulerOrder::XYZ => xmat * ymat * zmat,
153            EulerOrder::XZY => xmat * zmat * ymat,
154            EulerOrder::YXZ => ymat * xmat * zmat,
155            EulerOrder::YZX => ymat * zmat * xmat,
156            EulerOrder::ZXY => zmat * xmat * ymat,
157            EulerOrder::ZYX => zmat * ymat * xmat,
158        }
159    }
160
161    /// If `use_model_front` is true, the +Z axis (asset front) is treated as forward (implies +X is left)
162    /// and points toward the target position. By default, the -Z axis (camera forward) is treated as forward
163    /// (implies +X is right).
164    ///
165    /// _Godot equivalent: `Basis.looking_at()`_
166    pub fn looking_at(target: Vector3, up: Vector3, use_model_front: bool) -> Self {
167        super::inner::InnerBasis::looking_at(target, up, use_model_front)
168    }
169
170    /// Creates a `[Vector3; 3]` with the columns of the `Basis`.
171    pub fn to_cols(&self) -> [Vector3; 3] {
172        self.transposed().rows
173    }
174
175    const fn to_rows_array(self) -> [real; 9] {
176        let [Vector3 {
177            x: ax,
178            y: bx,
179            z: cx,
180        }, Vector3 {
181            x: ay,
182            y: by,
183            z: cy,
184        }, Vector3 {
185            x: az,
186            y: bz,
187            z: cz,
188        }] = self.rows;
189        [ax, bx, cx, ay, by, cy, az, bz, cz]
190    }
191
192    /// Creates a [`Quaternion`] representing the same rotation as this basis.
193    ///
194    /// _Godot equivalent: `Basis.get_rotation_quaternion()`_
195    #[doc(alias = "get_rotation_quaternion")]
196    pub fn get_quaternion(&self) -> Quaternion {
197        RQuat::from_mat3(&self.orthonormalized().to_glam()).to_front()
198    }
199
200    /// Returns the scale of the matrix.
201    ///
202    /// _Godot equivalent: `Basis.get_scale()`_
203    #[must_use]
204    pub fn get_scale(&self) -> Vector3 {
205        let det = self.determinant();
206        let det_sign = if det < 0.0 { -1.0 } else { 1.0 };
207
208        Vector3::new(
209            self.col_a().length(),
210            self.col_b().length(),
211            self.col_c().length(),
212        ) * det_sign
213    }
214
215    /// Returns the rotation of the matrix in euler angles, with the order `YXZ`.
216    ///
217    /// See [`get_euler_with()`](Self::get_euler_with) for custom angle orders.
218    pub fn get_euler(&self) -> Vector3 {
219        self.get_euler_with(EulerOrder::YXZ)
220    }
221
222    /// Returns the rotation of the matrix in euler angles.
223    ///
224    /// The order of the angles are given by `order`. To use the default order `YXZ`, see [`get_euler()`](Self::get_euler).
225    ///
226    /// _Godot equivalent: `Basis.get_euler()`_
227    pub fn get_euler_with(&self, order: EulerOrder) -> Vector3 {
228        use glam::swizzles::Vec3Swizzles as _;
229
230        let col_a = self.col_a().to_glam();
231        let col_b = self.col_b().to_glam();
232        let col_c = self.col_c().to_glam();
233
234        let row_a = self.rows[0].to_glam();
235        let row_b = self.rows[1].to_glam();
236        let row_c = self.rows[2].to_glam();
237
238        let major = match order {
239            EulerOrder::XYZ => self.rows[0].z,
240            EulerOrder::XZY => self.rows[0].y,
241            EulerOrder::YXZ => self.rows[1].z,
242            EulerOrder::YZX => self.rows[1].x,
243            EulerOrder::ZXY => self.rows[2].y,
244            EulerOrder::ZYX => self.rows[2].x,
245        };
246
247        // Return the simplest forms for pure rotations
248        if let Some(pure_rotation) = match order {
249            EulerOrder::XYZ => self
250                .to_euler_pure_rotation(major, 1, row_a.zx())
251                .map(RVec3::yxz),
252            EulerOrder::YXZ => {
253                self.to_euler_pure_rotation(major, 0, RVec2::new(-major, self.rows[1].y))
254            }
255            _ => None,
256        } {
257            return pure_rotation.to_front();
258        }
259
260        match order {
261            EulerOrder::XYZ => {
262                -Self::to_euler_inner(major, col_c.yz(), row_a.yx(), col_b.zy()).yxz()
263            }
264            EulerOrder::XZY => {
265                Self::to_euler_inner(major, col_b.zy(), row_a.zx(), col_c.yz()).yzx()
266            }
267            EulerOrder::YXZ => {
268                let mut vec = Self::to_euler_inner(major, col_c.xz(), row_b.xy(), row_a.yx());
269                if Self::is_between_neg1_1(major).is_lt() {
270                    vec.y = -vec.y;
271                }
272                vec
273            }
274            EulerOrder::YZX => {
275                -Self::to_euler_inner(major, row_b.zy(), col_a.zx(), row_c.yz()).yzx()
276            }
277            EulerOrder::ZXY => {
278                -Self::to_euler_inner(major, row_c.xz(), col_b.xy(), row_a.zx()).xyz()
279            }
280            EulerOrder::ZYX => {
281                Self::to_euler_inner(major, col_a.yx(), row_c.yz(), col_b.xy()).zxy()
282            }
283        }
284        .to_front()
285    }
286
287    fn is_between_neg1_1(f: real) -> Ordering {
288        if f >= (1.0 - real::CMP_EPSILON) {
289            Ordering::Greater
290        } else if f <= -(1.0 - real::CMP_EPSILON) {
291            Ordering::Less
292        } else {
293            Ordering::Equal
294        }
295    }
296
297    /// Check if the element at `basis_{i,i}` is 1, and all the other values in
298    /// that row and column are 0.
299    fn is_identity_index(&self, index: usize) -> bool {
300        let row = self.rows[index];
301        let col = self.transposed().rows[index];
302        if row != col {
303            return false;
304        }
305        match index {
306            0 => row == Vector3::RIGHT,
307            1 => row == Vector3::UP,
308            2 => row == Vector3::BACK,
309            _ => panic!("Unknown Index {index}"),
310        }
311    }
312
313    #[allow(clippy::wrong_self_convention)]
314    fn to_euler_pure_rotation(
315        &self,
316        major: real,
317        index: usize,
318        rotation_vec: RVec2,
319    ) -> Option<RVec3> {
320        if Self::is_between_neg1_1(major).is_ne() {
321            return None;
322        }
323
324        if !self.is_identity_index(index) {
325            return None;
326        }
327
328        Some(RVec3::new(
329            real::atan2(rotation_vec.x, rotation_vec.y),
330            0.0,
331            0.0,
332        ))
333    }
334
335    #[allow(clippy::wrong_self_convention)]
336    fn to_euler_inner(major: real, pair0: RVec2, pair1: RVec2, pair2: RVec2) -> RVec3 {
337        match Self::is_between_neg1_1(major) {
338            // It's -1
339            Ordering::Less => RVec3::new(FRAC_PI_2, -real::atan2(pair2.x, pair2.y), 0.0),
340            // Is it a pure rotation?
341            Ordering::Equal => RVec3::new(
342                real::asin(-major),
343                real::atan2(pair0.x, pair0.y),
344                real::atan2(pair1.x, pair1.y),
345            ),
346            // It's 1
347            Ordering::Greater => RVec3::new(-FRAC_PI_2, -real::atan2(pair2.x, pair2.y), 0.0),
348        }
349    }
350
351    /// Returns the determinant of the matrix.
352    ///
353    /// _Godot equivalent: `Basis.determinant()`_
354    pub fn determinant(&self) -> real {
355        self.to_glam().determinant()
356    }
357
358    /// Introduce an additional scaling specified by the given 3D scaling factor.
359    ///
360    /// _Godot equivalent: `Basis.scaled()`_
361    #[must_use]
362    pub fn scaled(&self, scale: Vector3) -> Self {
363        Self::from_diagonal(scale.x, scale.y, scale.z) * (*self)
364    }
365
366    /// Returns the inverse of the matrix.
367    ///
368    /// _Godot equivalent: `Basis.inverse()`_
369    #[must_use]
370    pub fn inverse(&self) -> Basis {
371        self.glam(|mat| mat.inverse())
372    }
373
374    /// Returns the transposed version of the matrix.
375    ///
376    /// _Godot equivalent: `Basis.transposed()`_
377    #[must_use]
378    pub fn transposed(&self) -> Self {
379        Self::from_cols(self.rows[0], self.rows[1], self.rows[2])
380    }
381
382    /// ⚠️ Returns the orthonormalized version of the matrix (useful to call from
383    /// time to time to avoid rounding error for orthogonal matrices). This
384    /// performs a Gram-Schmidt orthonormalization on the basis of the matrix.
385    ///
386    /// # Panics
387    ///
388    /// If the determinant of the matrix is 0.
389    ///
390    /// _Godot equivalent: `Basis.orthonormalized()`_
391    #[must_use]
392    pub fn orthonormalized(&self) -> Self {
393        assert!(
394            !self.determinant().is_zero_approx(),
395            "Determinant should not be zero."
396        );
397
398        // Gram-Schmidt Process
399        let mut x = self.col_a();
400        let mut y = self.col_b();
401        let mut z = self.col_c();
402
403        x = x.normalized();
404        y = y - x * x.dot(y);
405        y = y.normalized();
406        z = z - x * x.dot(z) - y * y.dot(z);
407        z = z.normalized();
408
409        Self::from_cols(x, y, z)
410    }
411
412    /// Introduce an additional rotation around the given `axis` by `angle`
413    /// (in radians). The axis must be a normalized vector.
414    ///
415    /// _Godot equivalent: `Basis.rotated()`_
416    #[must_use]
417    pub fn rotated(&self, axis: Vector3, angle: real) -> Self {
418        Self::from_axis_angle(axis, angle) * (*self)
419    }
420
421    /// Assuming that the matrix is a proper rotation matrix, slerp performs
422    /// a spherical-linear interpolation with another rotation matrix.
423    ///
424    /// _Godot equivalent: `Basis.slerp()`_
425    #[must_use]
426    pub fn slerp(&self, other: &Self, weight: real) -> Self {
427        let from = self.get_quaternion();
428        let to = other.get_quaternion();
429
430        let mut result = Self::from_quaternion(from.slerp(to, weight));
431
432        for i in 0..3 {
433            result.rows[i] *= self.rows[i].length().lerp(other.rows[i].length(), weight);
434        }
435
436        result
437    }
438
439    /// Transposed dot product with the X axis (column) of the matrix.
440    ///
441    /// _Godot equivalent: `Basis.tdotx()`_
442    #[must_use]
443    pub fn tdotx(&self, with: Vector3) -> real {
444        self.col_a().dot(with)
445    }
446
447    /// Transposed dot product with the Y axis (column) of the matrix.
448    ///
449    /// _Godot equivalent: `Basis.tdoty()`_
450    #[must_use]
451    pub fn tdoty(&self, with: Vector3) -> real {
452        self.col_b().dot(with)
453    }
454
455    /// Transposed dot product with the Z axis (column) of the matrix.
456    ///
457    /// _Godot equivalent: `Basis.tdotz()`_
458    #[must_use]
459    pub fn tdotz(&self, with: Vector3) -> real {
460        self.col_c().dot(with)
461    }
462
463    /// Returns `true` if this basis is finite. Meaning each element of the
464    /// matrix is not `NaN`, positive infinity, or negative infinity.
465    ///
466    /// _Godot equivalent: `Basis.is_finite()`_
467    pub fn is_finite(&self) -> bool {
468        self.rows[0].is_finite() && self.rows[1].is_finite() && self.rows[2].is_finite()
469    }
470
471    /// Returns the first column of the matrix,
472    ///
473    /// _Godot equivalent: `Basis.x`_, see [`Basis`] for why it's changed
474    #[doc(alias = "x")]
475    #[must_use]
476    pub fn col_a(&self) -> Vector3 {
477        Vector3::new(self.rows[0].x, self.rows[1].x, self.rows[2].x)
478    }
479
480    /// Set the values of the first column of the matrix.
481    pub fn set_col_a(&mut self, col: Vector3) {
482        self.rows[0].x = col.x;
483        self.rows[1].x = col.y;
484        self.rows[2].x = col.z;
485    }
486
487    /// Returns the second column of the matrix,
488    ///
489    /// _Godot equivalent: `Basis.y`_, see [`Basis`] for why it's changed
490    #[doc(alias = "y")]
491    #[must_use]
492    pub fn col_b(&self) -> Vector3 {
493        Vector3::new(self.rows[0].y, self.rows[1].y, self.rows[2].y)
494    }
495
496    /// Set the values of the second column of the matrix.
497    pub fn set_col_b(&mut self, col: Vector3) {
498        self.rows[0].y = col.x;
499        self.rows[1].y = col.y;
500        self.rows[2].y = col.z;
501    }
502
503    /// Returns the third column of the matrix,
504    ///
505    /// _Godot equivalent: `Basis.z`_, see [`Basis`] for why it's changed
506    #[doc(alias = "z")]
507    #[must_use]
508    pub fn col_c(&self) -> Vector3 {
509        Vector3::new(self.rows[0].z, self.rows[1].z, self.rows[2].z)
510    }
511
512    /// Set the values of the third column of the matrix.
513    pub fn set_col_c(&mut self, col: Vector3) {
514        self.rows[0].z = col.x;
515        self.rows[1].z = col.y;
516        self.rows[2].z = col.z;
517    }
518}
519
520impl Display for Basis {
521    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
522        // Godot output:
523        // [X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1)]
524        // Where X,Y,Z are the columns
525        let [a, b, c] = self.to_cols();
526
527        write!(f, "[a: {a}, b: {b}, c: {c}]")
528    }
529}
530
531impl ApproxEq for Basis {
532    /// Returns if this basis and `other` are approximately equal, by calling `is_equal_approx` on each row.
533    fn approx_eq(&self, other: &Self) -> bool {
534        Vector3::approx_eq(&self.rows[0], &other.rows[0])
535            && Vector3::approx_eq(&self.rows[1], &other.rows[1])
536            && Vector3::approx_eq(&self.rows[2], &other.rows[2])
537    }
538}
539
540impl GlamConv for Basis {
541    type Glam = RMat3;
542}
543
544impl GlamType for RMat3 {
545    type Mapped = Basis;
546
547    fn to_front(&self) -> Self::Mapped {
548        Basis::from_rows_array(&self.to_cols_array()).transposed()
549    }
550
551    fn from_front(mapped: &Self::Mapped) -> Self {
552        Self::from_cols_array(&mapped.to_rows_array()).transpose()
553    }
554}
555
556#[cfg(not(feature = "double-precision"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "double-precision"))))]
557impl GlamType for glam::Mat3A {
558    type Mapped = Basis;
559
560    fn to_front(&self) -> Self::Mapped {
561        Basis::from_rows_array(&self.to_cols_array()).transposed()
562    }
563
564    fn from_front(mapped: &Self::Mapped) -> Self {
565        Self::from_cols_array(&mapped.to_rows_array()).transpose()
566    }
567}
568
569impl Default for Basis {
570    fn default() -> Self {
571        Self::IDENTITY
572    }
573}
574
575impl Mul for Basis {
576    type Output = Self;
577
578    fn mul(self, rhs: Self) -> Self::Output {
579        self.glam2(&rhs, |a, b| a * b)
580    }
581}
582
583impl Mul<real> for Basis {
584    type Output = Self;
585
586    fn mul(mut self, rhs: real) -> Self::Output {
587        self *= rhs;
588        self
589    }
590}
591
592impl MulAssign<real> for Basis {
593    fn mul_assign(&mut self, rhs: real) {
594        self.rows[0] *= rhs;
595        self.rows[1] *= rhs;
596        self.rows[2] *= rhs;
597    }
598}
599
600impl Mul<Vector3> for Basis {
601    type Output = Vector3;
602
603    fn mul(self, rhs: Vector3) -> Self::Output {
604        self.glam2(&rhs, |a, b| a * b)
605    }
606}
607
608impl XformInv<Vector3> for Basis {
609    /// Inversely transforms given [`Vector3`] by this basis,
610    /// under the assumption that the basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not).
611    ///
612    /// Since given basis is assumed to be orthonormal (i.e. it is both orthogonal – with all axis perpendicular to each other – and all the axis are normalized),
613    /// `basis.transposed()` (matrix flipped over its diagonal) is equal to `basis.inverse()`
614    /// (i.e. `basis * basis.transposed() == basis * basis.inverse() == Basis::Identity`),
615    /// thus `basis.xform_inv(vector)` is equivalent both to `basis.transposed() * vector` and `basis.inverse() * vector`.
616    ///
617    /// See [`Basis::transposed()`] and [Godot docs (stable) for `Basis`](https://docs.godotengine.org/en/stable/classes/class_basis.html)
618    /// with following [Matrices and Transform tutorial](https://docs.godotengine.org/en/stable/tutorials/math/matrices_and_transforms.html).
619    ///
620    /// For transforming by inverse of a non-orthonormal basis (e.g. with scaling) `basis.inverse() * vector` can be used instead. See [`Basis::inverse()`].
621    ///
622    /// _Godot equivalent: `vector * basis`_
623    fn xform_inv(&self, rhs: Vector3) -> Vector3 {
624        Vector3::new(
625            self.col_a().dot(rhs),
626            self.col_b().dot(rhs),
627            self.col_c().dot(rhs),
628        )
629    }
630}
631
632// SAFETY:
633// This type is represented as `Self` in Godot, so `*mut Self` is sound.
634unsafe impl GodotFfi for Basis {
635    const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::BASIS);
636
637    ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
638}
639
640crate::meta::impl_godot_as_self!(Basis: ByValue);
641
642#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
643mod test {
644    use super::*;
645    use crate::assert_eq_approx;
646    use crate::builtin::real_consts::{FRAC_PI_2, PI};
647
648    fn deg_to_rad(rotation: Vector3) -> Vector3 {
649        Vector3::new(
650            rotation.x.to_radians(),
651            rotation.y.to_radians(),
652            rotation.z.to_radians(),
653        )
654    }
655
656    // Translated from Godot
657    fn test_rotation(deg_original_euler: Vector3, rot_order: EulerOrder) {
658        // This test:
659        // 1. Converts the rotation vector from deg to rad.
660        // 2. Converts euler to basis.
661        // 3. Converts the above basis back into euler.
662        // 4. Converts the above euler into basis again.
663        // 5. Compares the basis obtained in step 2 with the basis of step 4
664        //
665        // The conversion "basis to euler", done in the step 3, may be different from
666        // the original euler, even if the final rotation are the same.
667        // This happens because there are more ways to represents the same rotation,
668        // both valid, using eulers.
669        // For this reason is necessary to convert that euler back to basis and finally
670        // compares it.
671        //
672        // In this way we can assert that both functions: basis to euler / euler to basis
673        // are correct.
674
675        // Euler to rotation
676        let original_euler: Vector3 = deg_to_rad(deg_original_euler);
677        let to_rotation: Basis = Basis::from_euler(rot_order, original_euler);
678
679        // Euler from rotation
680        let euler_from_rotation: Vector3 = to_rotation.get_euler_with(rot_order);
681        let rotation_from_computed_euler: Basis = Basis::from_euler(rot_order, euler_from_rotation);
682
683        let res: Basis = to_rotation.inverse() * rotation_from_computed_euler;
684        assert!(
685            (res.col_a() - Vector3::RIGHT).length() <= 0.1,
686            "Fail due to X {} with {deg_original_euler} using {rot_order:?}",
687            res.col_a()
688        );
689        assert!(
690            (res.col_b() - Vector3::UP).length() <= 0.1,
691            "Fail due to Y {} with {deg_original_euler} using {rot_order:?}",
692            res.col_b()
693        );
694        assert!(
695            (res.col_c() - Vector3::BACK).length() <= 0.1,
696            "Fail due to Z {} with {deg_original_euler} using {rot_order:?}",
697            res.col_c()
698        );
699
700        // Double check `to_rotation` decomposing with XYZ rotation order.
701        let euler_xyz_from_rotation: Vector3 = to_rotation.get_euler_with(EulerOrder::XYZ);
702        let rotation_from_xyz_computed_euler: Basis =
703            Basis::from_euler(EulerOrder::XYZ, euler_xyz_from_rotation);
704
705        let res = to_rotation.inverse() * rotation_from_xyz_computed_euler;
706
707        assert!(
708        (res.col_a() - Vector3::new(1.0, 0.0, 0.0)).length() <= 0.1,
709        "Double check with XYZ rot order failed, due to X {} with {deg_original_euler} using {rot_order:?}",
710        res.col_a(),
711    );
712        assert!(
713        (res.col_b() - Vector3::new(0.0, 1.0, 0.0)).length() <= 0.1,
714        "Double check with XYZ rot order failed, due to Y {} with {deg_original_euler} using {rot_order:?}",
715        res.col_b(),
716    );
717        assert!(
718        (res.col_c() - Vector3::new(0.0, 0.0, 1.0)).length() <= 0.1,
719        "Double check with XYZ rot order failed, due to Z {} with {deg_original_euler} using {rot_order:?}",
720        res.col_c(),
721    );
722    }
723
724    #[test]
725    fn consts_behavior_correct() {
726        let v = Vector3::new(1.0, 2.0, 3.0);
727
728        assert_eq_approx!(Basis::IDENTITY * v, v);
729        assert_eq_approx!(Basis::FLIP_X * v, Vector3::new(-v.x, v.y, v.z),);
730        assert_eq_approx!(Basis::FLIP_Y * v, Vector3::new(v.x, -v.y, v.z),);
731        assert_eq_approx!(Basis::FLIP_Z * v, Vector3::new(v.x, v.y, -v.z),);
732    }
733
734    #[test]
735    fn basic_rotation_correct() {
736        assert_eq_approx!(
737            Basis::from_axis_angle(Vector3::FORWARD, 0.0) * Vector3::RIGHT,
738            Vector3::RIGHT,
739        );
740        assert_eq_approx!(
741            Basis::from_axis_angle(Vector3::FORWARD, FRAC_PI_2) * Vector3::RIGHT,
742            Vector3::DOWN,
743        );
744        assert_eq_approx!(
745            Basis::from_axis_angle(Vector3::FORWARD, PI) * Vector3::RIGHT,
746            Vector3::LEFT,
747        );
748        assert_eq_approx!(
749            Basis::from_axis_angle(Vector3::FORWARD, PI + FRAC_PI_2) * Vector3::RIGHT,
750            Vector3::UP,
751        );
752    }
753
754    // Translated from Godot
755    #[test]
756    fn basis_euler_conversions() {
757        let euler_order_to_test: Vec<EulerOrder> = vec![
758            EulerOrder::XYZ,
759            EulerOrder::XZY,
760            EulerOrder::YZX,
761            EulerOrder::YXZ,
762            EulerOrder::ZXY,
763            EulerOrder::ZYX,
764        ];
765
766        let vectors_to_test: Vec<Vector3> = vec![
767            Vector3::new(0.0, 0.0, 0.0),
768            Vector3::new(0.5, 0.5, 0.5),
769            Vector3::new(-0.5, -0.5, -0.5),
770            Vector3::new(40.0, 40.0, 40.0),
771            Vector3::new(-40.0, -40.0, -40.0),
772            Vector3::new(0.0, 0.0, -90.0),
773            Vector3::new(0.0, -90.0, 0.0),
774            Vector3::new(-90.0, 0.0, 0.0),
775            Vector3::new(0.0, 0.0, 90.0),
776            Vector3::new(0.0, 90.0, 0.0),
777            Vector3::new(90.0, 0.0, 0.0),
778            Vector3::new(0.0, 0.0, -30.0),
779            Vector3::new(0.0, -30.0, 0.0),
780            Vector3::new(-30.0, 0.0, 0.0),
781            Vector3::new(0.0, 0.0, 30.0),
782            Vector3::new(0.0, 30.0, 0.0),
783            Vector3::new(30.0, 0.0, 0.0),
784            Vector3::new(0.5, 50.0, 20.0),
785            Vector3::new(-0.5, -50.0, -20.0),
786            Vector3::new(0.5, 0.0, 90.0),
787            Vector3::new(0.5, 0.0, -90.0),
788            Vector3::new(360.0, 360.0, 360.0),
789            Vector3::new(-360.0, -360.0, -360.0),
790            Vector3::new(-90.0, 60.0, -90.0),
791            Vector3::new(90.0, 60.0, -90.0),
792            Vector3::new(90.0, -60.0, -90.0),
793            Vector3::new(-90.0, -60.0, -90.0),
794            Vector3::new(-90.0, 60.0, 90.0),
795            Vector3::new(90.0, 60.0, 90.0),
796            Vector3::new(90.0, -60.0, 90.0),
797            Vector3::new(-90.0, -60.0, 90.0),
798            Vector3::new(60.0, 90.0, -40.0),
799            Vector3::new(60.0, -90.0, -40.0),
800            Vector3::new(-60.0, -90.0, -40.0),
801            Vector3::new(-60.0, 90.0, 40.0),
802            Vector3::new(60.0, 90.0, 40.0),
803            Vector3::new(60.0, -90.0, 40.0),
804            Vector3::new(-60.0, -90.0, 40.0),
805            Vector3::new(-90.0, 90.0, -90.0),
806            Vector3::new(90.0, 90.0, -90.0),
807            Vector3::new(90.0, -90.0, -90.0),
808            Vector3::new(-90.0, -90.0, -90.0),
809            Vector3::new(-90.0, 90.0, 90.0),
810            Vector3::new(90.0, 90.0, 90.0),
811            Vector3::new(90.0, -90.0, 90.0),
812            Vector3::new(20.0, 150.0, 30.0),
813            Vector3::new(20.0, -150.0, 30.0),
814            Vector3::new(-120.0, -150.0, 30.0),
815            Vector3::new(-120.0, -150.0, -130.0),
816            Vector3::new(120.0, -150.0, -130.0),
817            Vector3::new(120.0, 150.0, -130.0),
818            Vector3::new(120.0, 150.0, 130.0),
819        ];
820
821        for order in euler_order_to_test.iter() {
822            for vector in vectors_to_test.iter() {
823                test_rotation(*vector, *order);
824            }
825        }
826    }
827
828    // Translated from Godot
829    #[test]
830    fn basis_finite_number_test() {
831        let x: Vector3 = Vector3::new(0.0, 1.0, 2.0);
832        let infinite: Vector3 = Vector3::new(real::NAN, real::NAN, real::NAN);
833
834        assert!(
835            Basis::from_cols(x, x, x).is_finite(),
836            "Basis with all components finite should be finite"
837        );
838
839        assert!(
840            !Basis::from_cols(infinite, x, x).is_finite(),
841            "Basis with one component infinite should not be finite."
842        );
843        assert!(
844            !Basis::from_cols(x, infinite, x).is_finite(),
845            "Basis with one component infinite should not be finite."
846        );
847        assert!(
848            !Basis::from_cols(x, x, infinite).is_finite(),
849            "Basis with one component infinite should not be finite."
850        );
851
852        assert!(
853            !Basis::from_cols(infinite, infinite, x).is_finite(),
854            "Basis with two components infinite should not be finite."
855        );
856        assert!(
857            !Basis::from_cols(infinite, x, infinite).is_finite(),
858            "Basis with two components infinite should not be finite."
859        );
860        assert!(
861            !Basis::from_cols(x, infinite, infinite).is_finite(),
862            "Basis with two components infinite should not be finite."
863        );
864
865        assert!(
866            !Basis::from_cols(infinite, infinite, infinite).is_finite(),
867            "Basis with three components infinite should not be finite."
868        );
869    }
870
871    #[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
872    #[test]
873    fn serde_roundtrip() {
874        let basis = Basis::IDENTITY;
875        let expected_json = "{\"rows\":[{\"x\":1.0,\"y\":0.0,\"z\":0.0},{\"x\":0.0,\"y\":1.0,\"z\":0.0},{\"x\":0.0,\"y\":0.0,\"z\":1.0}]}";
876
877        crate::builtin::test_utils::roundtrip(&basis, expected_json);
878    }
879}