godot-core 0.5.1

Internal crate used by godot-rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
/*
 * Copyright (c) godot-rust; Bromeon and contributors.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

use std::fmt::Display;
use std::ops::{Mul, MulAssign};

use godot_ffi as sys;
use sys::{ExtVariantType, GodotFfi, ffi_methods};

use crate::builtin::math::{ApproxEq, FloatExt, GlamConv, GlamType, XformInv, assert_ne_approx};
use crate::builtin::real_consts::PI;
use crate::builtin::{RAffine2, RMat2, Rect2, Vector2, real};

/// Affine 2D transform (2x3 matrix).
///
/// Represents transformations such as translation, rotation, or scaling.
///
/// Expressed as a 2x3 matrix, this transform consists of a two column vectors
/// `a` and `b` representing the basis of the transform, as well as the origin:
/// ```text
/// [ a.x  b.x  origin.x ]
/// [ a.y  b.y  origin.y ]
/// ```
///
/// # All matrix types
///
/// | Dimension | Orthogonal basis | Affine transform        | Projective transform |
/// |-----------|------------------|-------------------------|----------------------|
/// | 2D        |                  | **`Transform2D`** (2x3) |                      |
/// | 3D        | [`Basis`] (3x3)  | [`Transform3D`] (3x4)   | [`Projection`] (4x4) |
///
/// [`Basis`]: crate::builtin::Basis
/// [`Transform3D`]: crate::builtin::Transform3D
/// [`Projection`]: crate::builtin::Projection
///
/// # Transform operations
///
/// | Operation                      | Transform2D                      | Notes                               |
/// |--------------------------------|----------------------------------|-------------------------------------|
/// | Apply                          | `transform * v`                  | Supports [`Rect2`] and [`Vector2`]. |
/// | Apply inverse                  | `transform.xform_inv(v)`         | Supports [`Rect2`] and [`Vector2`]. |
/// | Apply, no translate            | `transform.basis_xform(v)`       | Supports [`Vector2`].               |
/// | Apply inverse, no translate    | `transform.basis_xform_inv(v)`   | Supports [`Vector2`].               |
///
/// # Godot docs
///
/// [`Transform2D` (stable)](https://docs.godotengine.org/en/stable/classes/class_transform2d.html)
#[derive(Default, Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Transform2D {
    /// The first basis vector.
    ///
    /// _Godot equivalent: `Transform2D.x`_, see [`Basis`][crate::builtin::Basis] for why it's changed
    pub a: Vector2,

    /// The second basis vector.
    ///
    /// _Godot equivalent: `Transform2D.y`_, see [`Basis`][crate::builtin::Basis] for why it's changed
    pub b: Vector2,

    /// The origin of the transform. The coordinate space defined by this transform
    /// starts at this point.
    ///
    /// _Godot equivalent: `Transform2D.origin`_
    pub origin: Vector2,
}

impl Transform2D {
    /// The identity transform, with no translation, rotation or scaling
    /// applied. When applied to other data structures, `IDENTITY` performs no
    /// transformation.
    ///
    /// _Godot equivalent: `Transform2D.IDENTITY`_
    pub const IDENTITY: Self = Self::from_basis_origin(Basis2D::IDENTITY, Vector2::ZERO);

    /// The `Transform2D` that will flip something along its X axis.
    ///
    /// _Godot equivalent: `Transform2D.FLIP_X`_
    pub const FLIP_X: Self = Self::from_basis_origin(Basis2D::FLIP_X, Vector2::ZERO);

    /// The `Transform2D` that will flip something along its Y axis.
    ///
    /// _Godot equivalent: `Transform2D.FLIP_Y`_
    pub const FLIP_Y: Self = Self::from_basis_origin(Basis2D::FLIP_Y, Vector2::ZERO);

    const fn from_basis_origin(basis: Basis2D, origin: Vector2) -> Self {
        let [a, b] = basis.cols;
        Self { a, b, origin }
    }

    /// Create a new `Transform2D` with the given column vectors.
    ///
    /// _Godot equivalent: `Transform2D(Vector2 x_axis, Vector2 y_axis, Vector2 origin)`_, see [`Basis`][crate::builtin::Basis] for why it's
    /// changed
    pub const fn from_cols(a: Vector2, b: Vector2, origin: Vector2) -> Self {
        Self { a, b, origin }
    }

    /// Create a new `Transform2D` which will rotate by the given angle.
    pub fn from_angle(angle: real) -> Self {
        Self::from_angle_origin(angle, Vector2::ZERO)
    }

    /// Create a new `Transform2D` which will rotate by `angle` and translate
    /// by `origin`.
    ///
    /// _Godot equivalent: `Transform2D(float rotation, Vector2 position)`_
    pub fn from_angle_origin(angle: real, origin: Vector2) -> Self {
        Self::from_basis_origin(Basis2D::from_angle(angle), origin)
    }

    /// Create a new `Transform2D` which will rotate by `angle`, scale by
    /// `scale`, skew by `skew` and translate by `origin`.
    ///
    /// _Godot equivalent: `Transform2D(float rotation, Vector2 scale, float skew, Vector2 position)`_
    pub fn from_angle_scale_skew_origin(
        angle: real,
        scale: Vector2,
        skew: real,
        origin: Vector2,
    ) -> Self {
        // Translated from Godot's implementation

        Self::from_basis_origin(
            Basis2D::from_cols(
                Vector2::new(angle.cos(), angle.sin()),
                Vector2::new(-(angle + skew).sin(), (angle + skew).cos()),
            )
            .scaled(scale),
            origin,
        )
    }

    /// Unstable, used to simplify codegen. Too many parameters for public API and easy to have off-by-one, `from_cols()` is preferred.
    #[doc(hidden)]
    #[rustfmt::skip]
    #[allow(clippy::too_many_arguments)]
    pub const fn __internal_codegen(
       ax: real, ay: real,
       bx: real, by: real,
       ox: real, oy: real,
    ) -> Self {
        Self::from_cols(
            Vector2::new(ax, ay),
            Vector2::new(bx, by),
            Vector2::new(ox, oy),
        )
    }
    /// Create a reference to the first two columns of the transform
    /// interpreted as a [`Basis2D`].
    fn basis<'a>(&'a self) -> &'a Basis2D {
        // SAFETY: Both `Basis2D` and `Transform2D` are `repr(C)`, and the
        // layout of `Basis2D` is a prefix of `Transform2D`

        unsafe { std::mem::transmute::<&'a Transform2D, &'a Basis2D>(self) }
    }

    /// Create a [`Basis2D`] from the first two columns of the transform.
    #[allow(clippy::wrong_self_convention)]
    fn to_basis(&self) -> Basis2D {
        Basis2D::from_cols(self.a, self.b)
    }

    /// Returns the inverse of the transform, under the assumption that the
    /// transformation is composed of rotation, scaling and translation.
    ///
    /// _Godot equivalent: `Transform2D.affine_inverse()`_
    #[must_use]
    pub fn affine_inverse(&self) -> Self {
        self.glam(|aff| aff.inverse())
    }

    /// Returns the determinant of the basis matrix.
    ///
    /// If the basis is uniformly scaled, then its determinant equals the square of the scale factor.
    ///
    /// A negative determinant means the basis was flipped, so one part of the scale is negative.
    /// A zero determinant means the basis isn't invertible, and is usually considered invalid.
    ///
    /// _Godot equivalent: `Transform2D.determinant()`_
    pub fn determinant(&self) -> real {
        self.basis().determinant()
    }

    /// Returns the transform's rotation (in radians).
    ///
    /// _Godot equivalent: `Transform2D.get_rotation()`_
    pub fn rotation(&self) -> real {
        self.basis().rotation()
    }

    /// Returns the transform's scale.
    ///
    /// _Godot equivalent: `Transform2D.get_scale()`_
    #[must_use]
    pub fn scale(&self) -> Vector2 {
        self.basis().scale()
    }

    /// Returns the transform's skew (in radians).
    ///
    /// _Godot equivalent: `Transform2D.get_skew()`_
    #[must_use]
    pub fn skew(&self) -> real {
        self.basis().skew()
    }

    /// Returns a transform interpolated between this transform and another by
    /// a given `weight` (on the range of 0.0 to 1.0).
    ///
    /// _Godot equivalent: `Transform2D.interpolate_with()`_
    #[must_use]
    pub fn interpolate_with(&self, other: &Self, weight: real) -> Self {
        Self::from_angle_scale_skew_origin(
            self.rotation().lerp_angle(other.rotation(), weight),
            self.scale().lerp(other.scale(), weight),
            self.skew().lerp_angle(other.skew(), weight),
            self.origin.lerp(other.origin, weight),
        )
    }

    /// Returns `true` if this transform is finite, by calling
    /// [`Vector2::is_finite()`] on each component.
    ///
    /// _Godot equivalent: `Transform2D.is_finite()`_
    pub fn is_finite(&self) -> bool {
        self.a.is_finite() && self.b.is_finite() && self.origin.is_finite()
    }

    /// Returns the transform with the basis orthogonal (90 degrees), and
    /// normalized axis vectors (scale of 1 or -1).
    ///
    /// _Godot equivalent: `Transform2D.orthonormalized()`_
    #[must_use]
    pub fn orthonormalized(&self) -> Self {
        Self::from_basis_origin(self.basis().orthonormalized(), self.origin)
    }

    /// Returns a copy of the transform rotated by the given `angle` (in radians).
    /// This method is an optimized version of multiplying the given transform `X`
    /// with a corresponding rotation transform `R` from the left, i.e., `R * X`.
    /// This can be seen as transforming with respect to the global/parent frame.
    ///
    /// _Godot equivalent: `Transform2D.rotated()`_
    #[must_use]
    pub fn rotated(&self, angle: real) -> Self {
        Self::from_angle(angle) * (*self)
    }

    /// Returns a copy of the transform rotated by the given `angle` (in radians).
    /// This method is an optimized version of multiplying the given transform `X`
    /// with a corresponding rotation transform `R` from the right, i.e., `X * R`.
    /// This can be seen as transforming with respect to the local frame.
    ///
    /// _Godot equivalent: `Transform2D.rotated_local()`_
    #[must_use]
    pub fn rotated_local(&self, angle: real) -> Self {
        (*self) * Self::from_angle(angle)
    }

    /// Returns a copy of the transform scaled by the given scale factor.
    /// This method is an optimized version of multiplying the given transform `X`
    /// with a corresponding scaling transform `S` from the left, i.e., `S * X`.
    /// This can be seen as transforming with respect to the global/parent frame.
    ///
    /// _Godot equivalent: `Transform2D.scaled()`_
    #[must_use]
    pub fn scaled(&self, scale: Vector2) -> Self {
        let mut basis = self.to_basis();
        basis.set_row_a(basis.row_a() * scale.x);
        basis.set_row_b(basis.row_b() * scale.y);
        Self::from_basis_origin(basis, self.origin * scale)
    }

    /// Returns a copy of the transform scaled by the given scale factor.
    /// This method is an optimized version of multiplying the given transform `X`
    /// with a corresponding scaling transform `S` from the right, i.e., `X * S`.
    /// This can be seen as transforming with respect to the local frame.
    ///
    /// _Godot equivalent: `Transform2D.scaled_local()`_
    #[must_use]
    pub fn scaled_local(&self, scale: Vector2) -> Self {
        Self::from_basis_origin(self.basis().scaled(scale), self.origin)
    }

    /// Returns a copy of the transform translated by the given offset.
    /// This method is an optimized version of multiplying the given transform `X`
    /// with a corresponding translation transform `T` from the left, i.e., `T * X`.
    /// This can be seen as transforming with respect to the global/parent frame.
    ///
    /// _Godot equivalent: `Transform2D.translated()`_
    #[must_use]
    pub fn translated(&self, offset: Vector2) -> Self {
        Self::from_cols(self.a, self.b, self.origin + offset)
    }

    /// Returns a copy of the transform translated by the given offset.
    /// This method is an optimized version of multiplying the given transform `X`
    /// with a corresponding translation transform `T` from the right, i.e., `X * T`.
    /// This can be seen as transforming with respect to the local frame.
    ///
    /// _Godot equivalent: `Transform2D.translated()`_
    #[must_use]
    pub fn translated_local(&self, offset: Vector2) -> Self {
        Self::from_cols(self.a, self.b, self.origin + (self.to_basis() * offset))
    }

    /// Returns a vector transformed (multiplied) by the basis matrix.
    /// This method does not account for translation (the origin vector).
    ///
    /// _Godot equivalent: `Transform2D.basis_xform()`_
    pub fn basis_xform(&self, v: Vector2) -> Vector2 {
        self.to_basis() * v
    }

    /// Returns a vector transformed (multiplied) by the inverse basis matrix.
    /// This method does not account for translation (the origin vector).
    ///
    /// _Godot equivalent: `Transform2D.basis_xform_inv()`_
    pub fn basis_xform_inv(&self, v: Vector2) -> Vector2 {
        self.basis().inverse() * v
    }
}

impl Display for Transform2D {
    /// Formats the value with the given formatter.  [Read more](https://doc.rust-lang.org/1.79.0/core/fmt/trait.Display.html#tymethod.fmt)
    ///
    /// The output is similar to Godot's, but calls the columns a/b instead of X/Y.  See [`Basis`][crate::builtin::Basis] for why.
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // Godot output:
        // [X: (1, 2), Y: (3, 4), O: (5, 6)]
        // Where X,Y,O are the columns

        let Transform2D { a, b, origin } = self;

        write!(f, "[a: {a}, b: {b}, o: {origin}]")
    }
}

impl Mul for Transform2D {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        self.glam2(&rhs, |a, b| a * b)
    }
}

impl Mul<Vector2> for Transform2D {
    type Output = Vector2;

    fn mul(self, rhs: Vector2) -> Self::Output {
        self.glam2(&rhs, |t, v| t.transform_point2(v))
    }
}

impl XformInv<Vector2> for Transform2D {
    /// Inversely transforms (multiplies) the given [`Vector2`] by this transformation matrix,
    /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not).
    ///
    /// For transforming by inverse of an affine transformation (e.g. with scaling) `transform.affine_inverse() * vector` can be used instead. See: [`Transform2D::affine_inverse()`].
    ///
    /// _Godot equivalent: `vector * transform` or `transform.inverse() * vector`_
    fn xform_inv(&self, rhs: Vector2) -> Vector2 {
        let v = rhs - self.origin;
        self.basis_xform_inv(v)
    }
}

impl Mul<real> for Transform2D {
    type Output = Self;

    fn mul(self, rhs: real) -> Self::Output {
        Self::from_cols(self.a * rhs, self.b * rhs, self.origin * rhs)
    }
}

impl Mul<Rect2> for Transform2D {
    type Output = Rect2;

    /// Transforms each coordinate in `rhs.position` and `rhs.end()` individually by this transform, then
    /// creates a `Rect2` containing all of them.
    fn mul(self, rhs: Rect2) -> Self::Output {
        // https://web.archive.org/web/20220317024830/https://dev.theomader.com/transform-bounding-boxes/
        let xa = self.a * rhs.position.x;
        let xb = self.a * rhs.end().x;

        let ya = self.b * rhs.position.y;
        let yb = self.b * rhs.end().y;

        let position = Vector2::coord_min(xa, xb) + Vector2::coord_min(ya, yb);
        let end = Vector2::coord_max(xa, xb) + Vector2::coord_max(ya, yb);
        Rect2::new(position + self.origin, end - position)
    }
}

impl XformInv<Rect2> for Transform2D {
    /// Inversely transforms (multiplies) the given [`Rect2`] by this [`Transform2D`] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not).
    ///
    /// For transforming by inverse of an affine transformation (e.g. with scaling) `transform.affine_inverse() * vector` can be used instead. See: [`Transform2D::affine_inverse()`].
    ///
    /// _Godot equivalent: `rect2 * transform` or `transform.inverse() * rect2`_
    fn xform_inv(&self, rhs: Rect2) -> Rect2 {
        // https://web.archive.org/web/20220317024830/https://dev.theomader.com/transform-bounding-boxes/
        // Same as Godot's `Transform2D::xform_inv` but omits unnecessary `Rect2::expand_to`.
        // There is probably some more clever way to do that.

        // Use the first point initialize our min/max.
        let start = self.xform_inv(rhs.position);
        // `min` is position of our aabb, `max` is the farthest vertex.
        let (mut min, mut max) = (start, start);

        let vertices = [
            Vector2::new(rhs.position.x, rhs.position.y + rhs.size.y),
            rhs.end(),
            Vector2::new(rhs.position.x + rhs.size.x, rhs.position.y),
        ];

        for v in vertices {
            let transformed = self.xform_inv(v);
            min = Vector2::coord_min(min, transformed);
            max = Vector2::coord_max(max, transformed);
        }

        Rect2::new(min, max - min)
    }
}

impl ApproxEq for Transform2D {
    /// Returns if the two transforms are approximately equal, by comparing each component separately.
    #[inline]
    fn approx_eq(&self, other: &Self) -> bool {
        Vector2::approx_eq(&self.a, &other.a)
            && Vector2::approx_eq(&self.b, &other.b)
            && Vector2::approx_eq(&self.origin, &other.origin)
    }
}

impl GlamType for RAffine2 {
    type Mapped = Transform2D;

    fn to_front(&self) -> Self::Mapped {
        Transform2D::from_basis_origin(self.matrix2.to_front(), self.translation.to_front())
    }

    fn from_front(mapped: &Self::Mapped) -> Self {
        Self {
            matrix2: mapped.basis().to_glam(),
            translation: mapped.origin.to_glam(),
        }
    }
}

impl GlamConv for Transform2D {
    type Glam = RAffine2;
}

// SAFETY:
// This type is represented as `Self` in Godot, so `*mut Self` is sound.
unsafe impl GodotFfi for Transform2D {
    const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::TRANSFORM2D);

    ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}

crate::meta::impl_godot_as_self!(Transform2D: ByValue);

/// A 2x2 matrix, typically used as an orthogonal basis for [`Transform2D`].
///
/// Indexing into a `Basis2D` is done in a column-major order, meaning that
/// `basis[0]` is the first basis-vector.
///
/// This has no direct equivalent in Godot, but is the same as the `x` and `y`
/// vectors from a `Transform2D`.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub(crate) struct Basis2D {
    /// The columns of the matrix.
    cols: [Vector2; 2],
}

impl Basis2D {
    /// The identity basis, with no rotation or scaling applied.
    pub(crate) const IDENTITY: Self = Self::from_diagonal(1.0, 1.0);

    /// The basis that will flip something along the X axis when used in a
    /// transformation.
    pub(crate) const FLIP_X: Self = Self::from_diagonal(-1.0, 1.0);

    /// The basis that will flip something along the X axis when used in a
    /// transformation.
    pub(crate) const FLIP_Y: Self = Self::from_diagonal(1.0, -1.0);

    /// Create a diagonal matrix from the given values.
    pub(crate) const fn from_diagonal(x: real, y: real) -> Self {
        Self::from_cols(Vector2::new(x, 0.0), Vector2::new(0.0, y))
    }

    /// Create a new basis from 2 basis vectors.
    pub(crate) const fn from_cols(x: Vector2, y: Vector2) -> Self {
        Self { cols: [x, y] }
    }

    /// Create a `Basis2D` from an angle.
    pub(crate) fn from_angle(angle: real) -> Self {
        RMat2::from_angle(angle).to_front()
    }

    /// Returns the scale of the matrix.
    #[must_use]
    pub(crate) fn scale(&self) -> Vector2 {
        let det_sign = self.determinant().signum();
        Vector2::new(self.cols[0].length(), det_sign * self.cols[1].length())
    }

    /// Introduces an additional scaling.
    #[must_use]
    pub(crate) fn scaled(self, scale: Vector2) -> Self {
        Self {
            cols: [self.cols[0] * scale.x, self.cols[1] * scale.y],
        }
    }

    /// Returns the determinant of the matrix.
    pub fn determinant(&self) -> real {
        self.glam(|mat| mat.determinant())
    }

    /// Returns the inverse of the matrix.
    #[must_use]
    pub fn inverse(self) -> Self {
        self.glam(|mat| mat.inverse())
    }

    /// Returns the orthonormalized version of the basis.
    #[must_use]
    pub(crate) fn orthonormalized(self) -> Self {
        assert_ne_approx!(self.determinant(), 0.0, "Determinant should not be zero.");

        // Gram-Schmidt Process
        let mut x = self.cols[0];
        let mut y = self.cols[1];

        x = x.normalized();
        y = y - x * (x.dot(y));
        y = y.normalized();

        Self::from_cols(x, y)
    }

    /// Returns the rotation of the matrix
    pub(crate) fn rotation(&self) -> real {
        // Translated from Godot
        real::atan2(self.cols[0].y, self.cols[0].x)
    }

    /// Returns the skew of the matrix
    #[must_use]
    pub(crate) fn skew(&self) -> real {
        // Translated from Godot
        let det_sign = self.determinant().signum();
        self.cols[0]
            .normalized()
            .dot(det_sign * self.cols[1].normalized())
            .acos()
            - PI * 0.5
    }

    pub(crate) fn set_row_a(&mut self, v: Vector2) {
        self.cols[0].x = v.x;
        self.cols[1].x = v.y;
    }

    pub(crate) fn row_a(&self) -> Vector2 {
        Vector2::new(self.cols[0].x, self.cols[1].x)
    }

    pub(crate) fn set_row_b(&mut self, v: Vector2) {
        self.cols[0].y = v.x;
        self.cols[1].y = v.y;
    }

    pub(crate) fn row_b(&self) -> Vector2 {
        Vector2::new(self.cols[0].y, self.cols[1].y)
    }
}

impl Default for Basis2D {
    fn default() -> Self {
        Self::IDENTITY
    }
}

impl Display for Basis2D {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let [a, b] = self.cols;
        write!(f, "[a: {a}, b: {b})]")
    }
}

impl Mul for Basis2D {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        self.glam2(&rhs, |a, b| a * b)
    }
}

impl Mul<real> for Basis2D {
    type Output = Self;

    fn mul(self, rhs: real) -> Self::Output {
        (self.to_glam() * rhs).to_front()
    }
}

impl MulAssign<real> for Basis2D {
    fn mul_assign(&mut self, rhs: real) {
        self.cols[0] *= rhs;
        self.cols[1] *= rhs;
    }
}

impl Mul<Vector2> for Basis2D {
    type Output = Vector2;

    fn mul(self, rhs: Vector2) -> Self::Output {
        self.glam2(&rhs, |a, b| a * b)
    }
}

impl GlamType for RMat2 {
    type Mapped = Basis2D;

    fn to_front(&self) -> Self::Mapped {
        Basis2D {
            cols: [self.col(0).to_front(), self.col(1).to_front()],
        }
    }

    fn from_front(mapped: &Self::Mapped) -> Self {
        Self::from_cols(mapped.cols[0].to_glam(), mapped.cols[1].to_glam())
    }
}

impl GlamConv for Basis2D {
    type Glam = RMat2;
}

#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
mod test {
    use super::*;
    use crate::assert_eq_approx;

    #[test]
    fn transform2d_constructors_correct() {
        let trans = Transform2D::from_angle(real!(115.0).to_radians());
        assert_eq_approx!(trans.rotation(), real!(115.0).to_radians());

        let trans =
            Transform2D::from_angle_origin(real!(-80.0).to_radians(), Vector2::new(1.4, 9.8));
        assert_eq_approx!(trans.rotation(), real!(-80.0).to_radians());
        assert_eq_approx!(trans.origin, Vector2::new(1.4, 9.8));

        let trans = Transform2D::from_angle_scale_skew_origin(
            real!(170.0).to_radians(),
            Vector2::new(3.6, 8.0),
            real!(20.0).to_radians(),
            Vector2::new(2.4, 6.8),
        );
        assert_eq_approx!(trans.rotation(), real!(170.0).to_radians());
        assert_eq_approx!(trans.scale(), Vector2::new(3.6, 8.0));
        assert_eq_approx!(trans.skew(), real!(20.0).to_radians());
        assert_eq_approx!(trans.origin, Vector2::new(2.4, 6.8));
    }

    // Tests translated from Godot.

    const DUMMY_TRANSFORM: Transform2D = Transform2D::from_basis_origin(
        Basis2D::from_cols(Vector2::new(1.0, 2.0), Vector2::new(3.0, 4.0)),
        Vector2::new(5.0, 6.0),
    );

    #[test]
    fn translation() {
        let offset = Vector2::new(1.0, 2.0);

        // Both versions should give the same result applied to identity.
        assert_eq!(
            Transform2D::IDENTITY.translated(offset),
            Transform2D::IDENTITY.translated_local(offset)
        );

        // Check both versions against left and right multiplications.
        let t = Transform2D::IDENTITY.translated(offset);
        assert_eq!(DUMMY_TRANSFORM.translated(offset), t * DUMMY_TRANSFORM);
        assert_eq!(
            DUMMY_TRANSFORM.translated_local(offset),
            DUMMY_TRANSFORM * t
        );
    }

    #[test]
    fn scaling() {
        let scaling = Vector2::new(1.0, 2.0);

        // Both versions should give the same result applied to identity.
        assert_eq!(
            Transform2D::IDENTITY.scaled(scaling),
            Transform2D::IDENTITY.scaled_local(scaling)
        );

        // Check both versions against left and right multiplications.
        let s: Transform2D = Transform2D::IDENTITY.scaled(scaling);
        assert_eq!(DUMMY_TRANSFORM.scaled(scaling), s * DUMMY_TRANSFORM);
        assert_eq!(DUMMY_TRANSFORM.scaled_local(scaling), DUMMY_TRANSFORM * s);
    }

    #[test]
    fn rotation() {
        let phi = 1.0;

        // Both versions should give the same result applied to identity.
        assert_eq!(
            Transform2D::IDENTITY.rotated(phi),
            Transform2D::IDENTITY.rotated_local(phi)
        );

        // Check both versions against left and right multiplications.
        let r: Transform2D = Transform2D::IDENTITY.rotated(phi);
        assert_eq!(DUMMY_TRANSFORM.rotated(phi), r * DUMMY_TRANSFORM);
        assert_eq!(DUMMY_TRANSFORM.rotated_local(phi), DUMMY_TRANSFORM * r);
    }

    #[test]
    fn interpolation() {
        let rotate_scale_skew_pos: Transform2D = Transform2D::from_angle_scale_skew_origin(
            real!(170.0).to_radians(),
            Vector2::new(3.6, 8.0),
            real!(20.0).to_radians(),
            Vector2::new(2.4, 6.8),
        );

        let rotate_scale_skew_pos_halfway: Transform2D = Transform2D::from_angle_scale_skew_origin(
            real!(85.0).to_radians(),
            Vector2::new(2.3, 4.5),
            real!(10.0).to_radians(),
            Vector2::new(1.2, 3.4),
        );

        let interpolated: Transform2D =
            Transform2D::IDENTITY.interpolate_with(&rotate_scale_skew_pos, 0.5);
        assert_eq_approx!(interpolated.origin, rotate_scale_skew_pos_halfway.origin);
        assert_eq_approx!(
            interpolated.rotation(),
            rotate_scale_skew_pos_halfway.rotation(),
        );
        assert_eq_approx!(interpolated.scale(), rotate_scale_skew_pos_halfway.scale());
        assert_eq_approx!(interpolated.skew(), rotate_scale_skew_pos_halfway.skew());
        assert_eq_approx!(interpolated, rotate_scale_skew_pos_halfway);

        let interpolated = rotate_scale_skew_pos.interpolate_with(&Transform2D::IDENTITY, 0.5);
        assert_eq_approx!(interpolated, rotate_scale_skew_pos_halfway);
    }

    #[test]
    fn finite_number_checks() {
        let x: Vector2 = Vector2::new(0.0, 1.0);
        let infinite: Vector2 = Vector2::new(real::NAN, real::NAN);

        assert!(
            Transform2D::from_basis_origin(Basis2D::from_cols(x, x), x).is_finite(),
            "let with: Transform2D all components finite should be finite",
        );

        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, x), x).is_finite(),
            "let with: Transform2D one component infinite should not be finite.",
        );
        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(x, infinite), x).is_finite(),
            "let with: Transform2D one component infinite should not be finite.",
        );
        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(x, x), infinite).is_finite(),
            "let with: Transform2D one component infinite should not be finite.",
        );

        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, infinite), x).is_finite(),
            "let with: Transform2D two components infinite should not be finite.",
        );
        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, x), infinite).is_finite(),
            "let with: Transform2D two components infinite should not be finite.",
        );
        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(x, infinite), infinite).is_finite(),
            "let with: Transform2D two components infinite should not be finite.",
        );

        assert!(
            !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, infinite), infinite)
                .is_finite(),
            "let with: Transform2D three components infinite should not be finite.",
        );
    }

    #[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
    #[test]
    fn serde_roundtrip() {
        let transform = Transform2D::default();
        let expected_json = "{\"a\":{\"x\":0.0,\"y\":0.0},\"b\":{\"x\":0.0,\"y\":0.0},\"origin\":{\"x\":0.0,\"y\":0.0}}";

        crate::builtin::test_utils::roundtrip(&transform, expected_json);
    }
}