1use std::fmt::Display;
9use std::ops::{Mul, MulAssign};
10
11use godot_ffi as sys;
12use sys::{ffi_methods, ExtVariantType, GodotFfi};
13
14use crate::builtin::math::{assert_ne_approx, ApproxEq, FloatExt, GlamConv, GlamType, XformInv};
15use crate::builtin::real_consts::PI;
16use crate::builtin::{real, RAffine2, RMat2, Rect2, Vector2};
17
18#[derive(Default, Copy, Clone, PartialEq, Debug)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54#[repr(C)]
55pub struct Transform2D {
56 pub a: Vector2,
60
61 pub b: Vector2,
65
66 pub origin: Vector2,
71}
72
73impl Transform2D {
74 pub const IDENTITY: Self = Self::from_basis_origin(Basis2D::IDENTITY, Vector2::ZERO);
80
81 pub const FLIP_X: Self = Self::from_basis_origin(Basis2D::FLIP_X, Vector2::ZERO);
85
86 pub const FLIP_Y: Self = Self::from_basis_origin(Basis2D::FLIP_Y, Vector2::ZERO);
90
91 const fn from_basis_origin(basis: Basis2D, origin: Vector2) -> Self {
92 let [a, b] = basis.cols;
93 Self { a, b, origin }
94 }
95
96 pub const fn from_cols(a: Vector2, b: Vector2, origin: Vector2) -> Self {
101 Self { a, b, origin }
102 }
103
104 pub fn from_angle(angle: real) -> Self {
106 Self::from_angle_origin(angle, Vector2::ZERO)
107 }
108
109 pub fn from_angle_origin(angle: real, origin: Vector2) -> Self {
114 Self::from_basis_origin(Basis2D::from_angle(angle), origin)
115 }
116
117 pub fn from_angle_scale_skew_origin(
122 angle: real,
123 scale: Vector2,
124 skew: real,
125 origin: Vector2,
126 ) -> Self {
127 Self::from_basis_origin(
130 Basis2D::from_cols(
131 Vector2::new(angle.cos(), angle.sin()),
132 Vector2::new(-(angle + skew).sin(), (angle + skew).cos()),
133 )
134 .scaled(scale),
135 origin,
136 )
137 }
138
139 #[doc(hidden)]
141 #[rustfmt::skip]
142 #[allow(clippy::too_many_arguments)]
143 pub const fn __internal_codegen(
144 ax: real, ay: real,
145 bx: real, by: real,
146 ox: real, oy: real,
147 ) -> Self {
148 Self::from_cols(
149 Vector2::new(ax, ay),
150 Vector2::new(bx, by),
151 Vector2::new(ox, oy),
152 )
153 }
154 fn basis<'a>(&'a self) -> &'a Basis2D {
157 unsafe { std::mem::transmute::<&'a Transform2D, &'a Basis2D>(self) }
161 }
162
163 #[allow(clippy::wrong_self_convention)]
165 fn to_basis(&self) -> Basis2D {
166 Basis2D::from_cols(self.a, self.b)
167 }
168
169 #[must_use]
174 pub fn affine_inverse(&self) -> Self {
175 self.glam(|aff| aff.inverse())
176 }
177
178 pub fn determinant(&self) -> real {
187 self.basis().determinant()
188 }
189
190 pub fn rotation(&self) -> real {
194 self.basis().rotation()
195 }
196
197 #[must_use]
201 pub fn scale(&self) -> Vector2 {
202 self.basis().scale()
203 }
204
205 #[must_use]
209 pub fn skew(&self) -> real {
210 self.basis().skew()
211 }
212
213 #[must_use]
218 pub fn interpolate_with(&self, other: &Self, weight: real) -> Self {
219 Self::from_angle_scale_skew_origin(
220 self.rotation().lerp_angle(other.rotation(), weight),
221 self.scale().lerp(other.scale(), weight),
222 self.skew().lerp_angle(other.skew(), weight),
223 self.origin.lerp(other.origin, weight),
224 )
225 }
226
227 pub fn is_finite(&self) -> bool {
232 self.a.is_finite() && self.b.is_finite() && self.origin.is_finite()
233 }
234
235 #[must_use]
240 pub fn orthonormalized(&self) -> Self {
241 Self::from_basis_origin(self.basis().orthonormalized(), self.origin)
242 }
243
244 #[must_use]
251 pub fn rotated(&self, angle: real) -> Self {
252 Self::from_angle(angle) * (*self)
253 }
254
255 #[must_use]
262 pub fn rotated_local(&self, angle: real) -> Self {
263 (*self) * Self::from_angle(angle)
264 }
265
266 #[must_use]
273 pub fn scaled(&self, scale: Vector2) -> Self {
274 let mut basis = self.to_basis();
275 basis.set_row_a(basis.row_a() * scale.x);
276 basis.set_row_b(basis.row_b() * scale.y);
277 Self::from_basis_origin(basis, self.origin * scale)
278 }
279
280 #[must_use]
287 pub fn scaled_local(&self, scale: Vector2) -> Self {
288 Self::from_basis_origin(self.basis().scaled(scale), self.origin)
289 }
290
291 #[must_use]
298 pub fn translated(&self, offset: Vector2) -> Self {
299 Self::from_cols(self.a, self.b, self.origin + offset)
300 }
301
302 #[must_use]
309 pub fn translated_local(&self, offset: Vector2) -> Self {
310 Self::from_cols(self.a, self.b, self.origin + (self.to_basis() * offset))
311 }
312
313 pub fn basis_xform(&self, v: Vector2) -> Vector2 {
318 self.to_basis() * v
319 }
320
321 pub fn basis_xform_inv(&self, v: Vector2) -> Vector2 {
326 self.basis().inverse() * v
327 }
328}
329
330impl Display for Transform2D {
331 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 let Transform2D { a, b, origin } = self;
340
341 write!(f, "[a: {a}, b: {b}, o: {origin}]")
342 }
343}
344
345impl Mul for Transform2D {
346 type Output = Self;
347
348 fn mul(self, rhs: Self) -> Self::Output {
349 self.glam2(&rhs, |a, b| a * b)
350 }
351}
352
353impl Mul<Vector2> for Transform2D {
354 type Output = Vector2;
355
356 fn mul(self, rhs: Vector2) -> Self::Output {
357 self.glam2(&rhs, |t, v| t.transform_point2(v))
358 }
359}
360
361impl XformInv<Vector2> for Transform2D {
362 fn xform_inv(&self, rhs: Vector2) -> Vector2 {
369 let v = rhs - self.origin;
370 self.basis_xform_inv(v)
371 }
372}
373
374impl Mul<real> for Transform2D {
375 type Output = Self;
376
377 fn mul(self, rhs: real) -> Self::Output {
378 Self::from_cols(self.a * rhs, self.b * rhs, self.origin * rhs)
379 }
380}
381
382impl Mul<Rect2> for Transform2D {
383 type Output = Rect2;
384
385 fn mul(self, rhs: Rect2) -> Self::Output {
388 let xa = self.a * rhs.position.x;
390 let xb = self.a * rhs.end().x;
391
392 let ya = self.b * rhs.position.y;
393 let yb = self.b * rhs.end().y;
394
395 let position = Vector2::coord_min(xa, xb) + Vector2::coord_min(ya, yb);
396 let end = Vector2::coord_max(xa, xb) + Vector2::coord_max(ya, yb);
397 Rect2::new(position + self.origin, end - position)
398 }
399}
400
401impl XformInv<Rect2> for Transform2D {
402 fn xform_inv(&self, rhs: Rect2) -> Rect2 {
408 let start = self.xform_inv(rhs.position);
414 let (mut min, mut max) = (start, start);
416
417 let vertices = [
418 Vector2::new(rhs.position.x, rhs.position.y + rhs.size.y),
419 rhs.end(),
420 Vector2::new(rhs.position.x + rhs.size.x, rhs.position.y),
421 ];
422
423 for v in vertices {
424 let transformed = self.xform_inv(v);
425 min = Vector2::coord_min(min, transformed);
426 max = Vector2::coord_max(max, transformed);
427 }
428
429 Rect2::new(min, max - min)
430 }
431}
432
433impl ApproxEq for Transform2D {
434 #[inline]
436 fn approx_eq(&self, other: &Self) -> bool {
437 Vector2::approx_eq(&self.a, &other.a)
438 && Vector2::approx_eq(&self.b, &other.b)
439 && Vector2::approx_eq(&self.origin, &other.origin)
440 }
441}
442
443impl GlamType for RAffine2 {
444 type Mapped = Transform2D;
445
446 fn to_front(&self) -> Self::Mapped {
447 Transform2D::from_basis_origin(self.matrix2.to_front(), self.translation.to_front())
448 }
449
450 fn from_front(mapped: &Self::Mapped) -> Self {
451 Self {
452 matrix2: mapped.basis().to_glam(),
453 translation: mapped.origin.to_glam(),
454 }
455 }
456}
457
458impl GlamConv for Transform2D {
459 type Glam = RAffine2;
460}
461
462unsafe impl GodotFfi for Transform2D {
465 const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::TRANSFORM2D);
466
467 ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
468}
469
470crate::meta::impl_godot_as_self!(Transform2D: ByValue);
471
472#[derive(Copy, Clone, PartialEq, Debug)]
480#[repr(C)]
481pub(crate) struct Basis2D {
482 cols: [Vector2; 2],
484}
485
486impl Basis2D {
487 pub(crate) const IDENTITY: Self = Self::from_diagonal(1.0, 1.0);
489
490 pub(crate) const FLIP_X: Self = Self::from_diagonal(-1.0, 1.0);
493
494 pub(crate) const FLIP_Y: Self = Self::from_diagonal(1.0, -1.0);
497
498 pub(crate) const fn from_diagonal(x: real, y: real) -> Self {
500 Self::from_cols(Vector2::new(x, 0.0), Vector2::new(0.0, y))
501 }
502
503 pub(crate) const fn from_cols(x: Vector2, y: Vector2) -> Self {
505 Self { cols: [x, y] }
506 }
507
508 pub(crate) fn from_angle(angle: real) -> Self {
510 RMat2::from_angle(angle).to_front()
511 }
512
513 #[must_use]
515 pub(crate) fn scale(&self) -> Vector2 {
516 let det_sign = self.determinant().signum();
517 Vector2::new(self.cols[0].length(), det_sign * self.cols[1].length())
518 }
519
520 #[must_use]
522 pub(crate) fn scaled(self, scale: Vector2) -> Self {
523 Self {
524 cols: [self.cols[0] * scale.x, self.cols[1] * scale.y],
525 }
526 }
527
528 pub fn determinant(&self) -> real {
530 self.glam(|mat| mat.determinant())
531 }
532
533 #[must_use]
535 pub fn inverse(self) -> Self {
536 self.glam(|mat| mat.inverse())
537 }
538
539 #[must_use]
541 pub(crate) fn orthonormalized(self) -> Self {
542 assert_ne_approx!(self.determinant(), 0.0, "Determinant should not be zero.");
543
544 let mut x = self.cols[0];
546 let mut y = self.cols[1];
547
548 x = x.normalized();
549 y = y - x * (x.dot(y));
550 y = y.normalized();
551
552 Self::from_cols(x, y)
553 }
554
555 pub(crate) fn rotation(&self) -> real {
557 real::atan2(self.cols[0].y, self.cols[0].x)
559 }
560
561 #[must_use]
563 pub(crate) fn skew(&self) -> real {
564 let det_sign = self.determinant().signum();
566 self.cols[0]
567 .normalized()
568 .dot(det_sign * self.cols[1].normalized())
569 .acos()
570 - PI * 0.5
571 }
572
573 pub(crate) fn set_row_a(&mut self, v: Vector2) {
574 self.cols[0].x = v.x;
575 self.cols[1].x = v.y;
576 }
577
578 pub(crate) fn row_a(&self) -> Vector2 {
579 Vector2::new(self.cols[0].x, self.cols[1].x)
580 }
581
582 pub(crate) fn set_row_b(&mut self, v: Vector2) {
583 self.cols[0].y = v.x;
584 self.cols[1].y = v.y;
585 }
586
587 pub(crate) fn row_b(&self) -> Vector2 {
588 Vector2::new(self.cols[0].y, self.cols[1].y)
589 }
590}
591
592impl Default for Basis2D {
593 fn default() -> Self {
594 Self::IDENTITY
595 }
596}
597
598impl Display for Basis2D {
599 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600 let [a, b] = self.cols;
601 write!(f, "[a: {a}, b: {b})]")
602 }
603}
604
605impl Mul for Basis2D {
606 type Output = Self;
607
608 fn mul(self, rhs: Self) -> Self::Output {
609 self.glam2(&rhs, |a, b| a * b)
610 }
611}
612
613impl Mul<real> for Basis2D {
614 type Output = Self;
615
616 fn mul(self, rhs: real) -> Self::Output {
617 (self.to_glam() * rhs).to_front()
618 }
619}
620
621impl MulAssign<real> for Basis2D {
622 fn mul_assign(&mut self, rhs: real) {
623 self.cols[0] *= rhs;
624 self.cols[1] *= rhs;
625 }
626}
627
628impl Mul<Vector2> for Basis2D {
629 type Output = Vector2;
630
631 fn mul(self, rhs: Vector2) -> Self::Output {
632 self.glam2(&rhs, |a, b| a * b)
633 }
634}
635
636impl GlamType for RMat2 {
637 type Mapped = Basis2D;
638
639 fn to_front(&self) -> Self::Mapped {
640 Basis2D {
641 cols: [self.col(0).to_front(), self.col(1).to_front()],
642 }
643 }
644
645 fn from_front(mapped: &Self::Mapped) -> Self {
646 Self::from_cols(mapped.cols[0].to_glam(), mapped.cols[1].to_glam())
647 }
648}
649
650impl GlamConv for Basis2D {
651 type Glam = RMat2;
652}
653
654#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
655mod test {
656 use super::*;
657 use crate::assert_eq_approx;
658
659 #[test]
660 fn transform2d_constructors_correct() {
661 let trans = Transform2D::from_angle(real!(115.0).to_radians());
662 assert_eq_approx!(trans.rotation(), real!(115.0).to_radians());
663
664 let trans =
665 Transform2D::from_angle_origin(real!(-80.0).to_radians(), Vector2::new(1.4, 9.8));
666 assert_eq_approx!(trans.rotation(), real!(-80.0).to_radians());
667 assert_eq_approx!(trans.origin, Vector2::new(1.4, 9.8));
668
669 let trans = Transform2D::from_angle_scale_skew_origin(
670 real!(170.0).to_radians(),
671 Vector2::new(3.6, 8.0),
672 real!(20.0).to_radians(),
673 Vector2::new(2.4, 6.8),
674 );
675 assert_eq_approx!(trans.rotation(), real!(170.0).to_radians());
676 assert_eq_approx!(trans.scale(), Vector2::new(3.6, 8.0));
677 assert_eq_approx!(trans.skew(), real!(20.0).to_radians());
678 assert_eq_approx!(trans.origin, Vector2::new(2.4, 6.8));
679 }
680
681 const DUMMY_TRANSFORM: Transform2D = Transform2D::from_basis_origin(
684 Basis2D::from_cols(Vector2::new(1.0, 2.0), Vector2::new(3.0, 4.0)),
685 Vector2::new(5.0, 6.0),
686 );
687
688 #[test]
689 fn translation() {
690 let offset = Vector2::new(1.0, 2.0);
691
692 assert_eq!(
694 Transform2D::IDENTITY.translated(offset),
695 Transform2D::IDENTITY.translated_local(offset)
696 );
697
698 let t = Transform2D::IDENTITY.translated(offset);
700 assert_eq!(DUMMY_TRANSFORM.translated(offset), t * DUMMY_TRANSFORM);
701 assert_eq!(
702 DUMMY_TRANSFORM.translated_local(offset),
703 DUMMY_TRANSFORM * t
704 );
705 }
706
707 #[test]
708 fn scaling() {
709 let scaling = Vector2::new(1.0, 2.0);
710
711 assert_eq!(
713 Transform2D::IDENTITY.scaled(scaling),
714 Transform2D::IDENTITY.scaled_local(scaling)
715 );
716
717 let s: Transform2D = Transform2D::IDENTITY.scaled(scaling);
719 assert_eq!(DUMMY_TRANSFORM.scaled(scaling), s * DUMMY_TRANSFORM);
720 assert_eq!(DUMMY_TRANSFORM.scaled_local(scaling), DUMMY_TRANSFORM * s);
721 }
722
723 #[test]
724 fn rotation() {
725 let phi = 1.0;
726
727 assert_eq!(
729 Transform2D::IDENTITY.rotated(phi),
730 Transform2D::IDENTITY.rotated_local(phi)
731 );
732
733 let r: Transform2D = Transform2D::IDENTITY.rotated(phi);
735 assert_eq!(DUMMY_TRANSFORM.rotated(phi), r * DUMMY_TRANSFORM);
736 assert_eq!(DUMMY_TRANSFORM.rotated_local(phi), DUMMY_TRANSFORM * r);
737 }
738
739 #[test]
740 fn interpolation() {
741 let rotate_scale_skew_pos: Transform2D = Transform2D::from_angle_scale_skew_origin(
742 real!(170.0).to_radians(),
743 Vector2::new(3.6, 8.0),
744 real!(20.0).to_radians(),
745 Vector2::new(2.4, 6.8),
746 );
747
748 let rotate_scale_skew_pos_halfway: Transform2D = Transform2D::from_angle_scale_skew_origin(
749 real!(85.0).to_radians(),
750 Vector2::new(2.3, 4.5),
751 real!(10.0).to_radians(),
752 Vector2::new(1.2, 3.4),
753 );
754
755 let interpolated: Transform2D =
756 Transform2D::IDENTITY.interpolate_with(&rotate_scale_skew_pos, 0.5);
757 assert_eq_approx!(interpolated.origin, rotate_scale_skew_pos_halfway.origin);
758 assert_eq_approx!(
759 interpolated.rotation(),
760 rotate_scale_skew_pos_halfway.rotation(),
761 );
762 assert_eq_approx!(interpolated.scale(), rotate_scale_skew_pos_halfway.scale());
763 assert_eq_approx!(interpolated.skew(), rotate_scale_skew_pos_halfway.skew());
764 assert_eq_approx!(interpolated, rotate_scale_skew_pos_halfway);
765
766 let interpolated = rotate_scale_skew_pos.interpolate_with(&Transform2D::IDENTITY, 0.5);
767 assert_eq_approx!(interpolated, rotate_scale_skew_pos_halfway);
768 }
769
770 #[test]
771 fn finite_number_checks() {
772 let x: Vector2 = Vector2::new(0.0, 1.0);
773 let infinite: Vector2 = Vector2::new(real::NAN, real::NAN);
774
775 assert!(
776 Transform2D::from_basis_origin(Basis2D::from_cols(x, x), x).is_finite(),
777 "let with: Transform2D all components finite should be finite",
778 );
779
780 assert!(
781 !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, x), x).is_finite(),
782 "let with: Transform2D one component infinite should not be finite.",
783 );
784 assert!(
785 !Transform2D::from_basis_origin(Basis2D::from_cols(x, infinite), x).is_finite(),
786 "let with: Transform2D one component infinite should not be finite.",
787 );
788 assert!(
789 !Transform2D::from_basis_origin(Basis2D::from_cols(x, x), infinite).is_finite(),
790 "let with: Transform2D one component infinite should not be finite.",
791 );
792
793 assert!(
794 !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, infinite), x).is_finite(),
795 "let with: Transform2D two components infinite should not be finite.",
796 );
797 assert!(
798 !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, x), infinite).is_finite(),
799 "let with: Transform2D two components infinite should not be finite.",
800 );
801 assert!(
802 !Transform2D::from_basis_origin(Basis2D::from_cols(x, infinite), infinite).is_finite(),
803 "let with: Transform2D two components infinite should not be finite.",
804 );
805
806 assert!(
807 !Transform2D::from_basis_origin(Basis2D::from_cols(infinite, infinite), infinite)
808 .is_finite(),
809 "let with: Transform2D three components infinite should not be finite.",
810 );
811 }
812
813 #[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
814 #[test]
815 fn serde_roundtrip() {
816 let transform = Transform2D::default();
817 let expected_json = "{\"a\":{\"x\":0.0,\"y\":0.0},\"b\":{\"x\":0.0,\"y\":0.0},\"origin\":{\"x\":0.0,\"y\":0.0}}";
818
819 crate::builtin::test_utils::roundtrip(&transform, expected_json);
820 }
821}