1use core::ops::Mul;
8use core::{fmt::Debug, marker::PhantomData};
9
10use approx::{AbsDiffEq, RelativeEq, UlpsEq};
11use bytemuck::{Pod, Zeroable, cast};
12
13use crate::peel;
14use crate::{
15 Angle, Matrix3, Matrix4, Point2, Point3, Transparent, Unit, Vector2, Vector3,
16 bindings::prelude::*, scalar::FloatScalar,
17};
18
19#[repr(transparent)]
24#[cfg_attr(feature = "facet", derive(facet_derive::Facet))]
25pub struct Transform2<Src: Unit, Dst: Unit> {
26 pub matrix: Matrix3<Src::Scalar>,
28 _marker: PhantomData<Dst>,
29}
30
31#[repr(transparent)]
36#[cfg_attr(feature = "facet", derive(facet_derive::Facet))]
37pub struct Transform3<Src: Unit, Dst: Unit> {
38 pub matrix: Matrix4<Src::Scalar>,
40 _marker: PhantomData<Dst>,
41}
42
43pub trait TransformMap<T> {
45 type Output;
47
48 #[must_use]
50 fn map(&self, value: T) -> Self::Output;
51}
52
53impl<Src: Unit, Dst: Unit> Clone for Transform2<Src, Dst> {
54 fn clone(&self) -> Self {
55 *self
56 }
57}
58
59impl<Src: Unit, Dst: Unit> Clone for Transform3<Src, Dst> {
60 fn clone(&self) -> Self {
61 *self
62 }
63}
64
65impl<Src: Unit, Dst: Unit> Copy for Transform2<Src, Dst> {}
66impl<Src: Unit, Dst: Unit> Copy for Transform3<Src, Dst> {}
67
68impl<Src, Dst> Default for Transform2<Src, Dst>
69where
70 Src: Unit,
71 Src::Scalar: FloatScalar,
72 Dst: Unit,
73{
74 #[inline]
75 fn default() -> Self {
76 Self {
77 matrix: Matrix3::default(),
78 _marker: PhantomData,
79 }
80 }
81}
82
83impl<Src, Dst> Default for Transform3<Src, Dst>
84where
85 Src: Unit,
86 Src::Scalar: FloatScalar,
87 Dst: Unit,
88{
89 #[inline]
90 fn default() -> Self {
91 Self {
92 matrix: Matrix4::default(),
93 _marker: PhantomData,
94 }
95 }
96}
97
98impl<Src, Dst> Debug for Transform2<Src, Dst>
99where
100 Src: Unit,
101 Src::Scalar: FloatScalar,
102 Dst: Unit,
103{
104 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105 f.debug_struct("Transform2")
106 .field("matrix", &self.matrix)
107 .finish()
108 }
109}
110
111impl<Src, Dst> Debug for Transform3<Src, Dst>
112where
113 Src: Unit,
114 Src::Scalar: FloatScalar,
115 Dst: Unit,
116{
117 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118 f.debug_struct("Transform3")
119 .field("matrix", &self.matrix)
120 .finish()
121 }
122}
123
124impl<Src, Dst> PartialEq for Transform2<Src, Dst>
125where
126 Src: Unit,
127 Dst: Unit,
128{
129 fn eq(&self, other: &Self) -> bool {
130 self.matrix == other.matrix
131 }
132}
133
134impl<Src, Dst> AbsDiffEq for Transform2<Src, Dst>
135where
136 Src: Unit,
137 Src::Scalar: FloatScalar,
138 Dst: Unit<Scalar = Src::Scalar>,
139{
140 type Epsilon = <Matrix3<Src::Scalar> as AbsDiffEq>::Epsilon;
141
142 fn default_epsilon() -> Self::Epsilon {
143 <Matrix3<Src::Scalar> as AbsDiffEq>::default_epsilon()
144 }
145
146 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
147 self.matrix.abs_diff_eq(&other.matrix, epsilon)
148 }
149}
150
151impl<Src, Dst> RelativeEq for Transform2<Src, Dst>
152where
153 Src: Unit,
154 Src::Scalar: FloatScalar,
155 Dst: Unit<Scalar = Src::Scalar>,
156{
157 fn default_max_relative() -> Self::Epsilon {
158 <Matrix3<Src::Scalar> as RelativeEq>::default_max_relative()
159 }
160
161 fn relative_eq(
162 &self,
163 other: &Self,
164 epsilon: Self::Epsilon,
165 max_relative: Self::Epsilon,
166 ) -> bool {
167 self.matrix
168 .relative_eq(&other.matrix, epsilon, max_relative)
169 }
170}
171
172impl<Src, Dst> UlpsEq for Transform2<Src, Dst>
173where
174 Src: Unit,
175 Src::Scalar: FloatScalar,
176 Dst: Unit<Scalar = Src::Scalar>,
177{
178 fn default_max_ulps() -> u32 {
179 <Matrix3<Src::Scalar> as UlpsEq>::default_max_ulps()
180 }
181
182 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
183 self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps)
184 }
185}
186
187impl<Src: Unit, Dst: Unit> PartialEq for Transform3<Src, Dst> {
188 fn eq(&self, other: &Self) -> bool {
189 self.matrix == other.matrix
190 }
191}
192
193impl<Src, Dst> AbsDiffEq for Transform3<Src, Dst>
194where
195 Src: Unit,
196 Src::Scalar: FloatScalar,
197 Dst: Unit,
198{
199 type Epsilon = <Matrix4<Src::Scalar> as AbsDiffEq>::Epsilon;
200
201 fn default_epsilon() -> Self::Epsilon {
202 <Matrix4<Src::Scalar> as AbsDiffEq>::default_epsilon()
203 }
204
205 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
206 self.matrix.abs_diff_eq(&other.matrix, epsilon)
207 }
208}
209
210impl<Src, Dst> RelativeEq for Transform3<Src, Dst>
211where
212 Src: Unit,
213 Src::Scalar: FloatScalar,
214 Dst: Unit,
215{
216 fn default_max_relative() -> Self::Epsilon {
217 <Matrix4<Src::Scalar> as RelativeEq>::default_max_relative()
218 }
219
220 fn relative_eq(
221 &self,
222 other: &Self,
223 epsilon: Self::Epsilon,
224 max_relative: Self::Epsilon,
225 ) -> bool {
226 self.matrix
227 .relative_eq(&other.matrix, epsilon, max_relative)
228 }
229}
230
231impl<Src, Dst> UlpsEq for Transform3<Src, Dst>
232where
233 Src: Unit,
234 Src::Scalar: FloatScalar,
235 Dst: Unit,
236{
237 fn default_max_ulps() -> u32 {
238 <Matrix4<Src::Scalar> as UlpsEq>::default_max_ulps()
239 }
240
241 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
242 self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps)
243 }
244}
245
246unsafe impl<Src: Unit, Dst: Unit> Zeroable for Transform2<Src, Dst> {}
249unsafe impl<Src: Unit, Dst: Unit> Pod for Transform2<Src, Dst> {}
250unsafe impl<Src: Unit, Dst: Unit> Zeroable for Transform3<Src, Dst> {}
251unsafe impl<Src: Unit, Dst: Unit> Pod for Transform3<Src, Dst> {}
252impl<Src: Unit, Dst: Unit> Transparent for Transform2<Src, Dst> {
253 type Wrapped = Matrix3<Src::Scalar>;
254}
255impl<Src: Unit, Dst: Unit> Transparent for Transform3<Src, Dst> {
256 type Wrapped = Matrix4<Src::Scalar>;
257}
258
259impl<Src, Dst> Transform2<Src, Dst>
260where
261 Src: Unit,
262 Src::Scalar: FloatScalar,
263 Dst: Unit<Scalar = Src::Scalar>,
264{
265 pub const IDENTITY: Self = Self {
267 matrix: Matrix3::IDENTITY,
268 _marker: PhantomData,
269 };
270
271 #[inline]
273 #[must_use]
274 pub const fn from_matrix_unchecked(matrix: Matrix3<Src::Scalar>) -> Self {
275 Transform2 {
276 matrix,
277 _marker: PhantomData,
278 }
279 }
280
281 #[inline]
283 #[must_use]
284 pub fn from_matrix(matrix: Matrix3<Src::Scalar>) -> Option<Self> {
285 if matrix.is_invertible() {
286 Some(Self::from_matrix_unchecked(matrix))
287 } else {
288 None
289 }
290 }
291
292 #[inline]
311 #[must_use]
312 pub fn from_angle(angle: Angle<Src::Scalar>) -> Self {
313 Self::from_matrix_unchecked(Matrix3::from_angle(Angle {
314 radians: angle.radians,
315 }))
316 }
317
318 #[inline]
337 #[must_use]
338 pub fn from_scale(scale: Vector2<Src>) -> Self {
339 Self::from_matrix_unchecked(Matrix3::from_scale(scale.to_untyped()))
340 }
341
342 #[inline]
361 #[must_use]
362 pub fn from_translation(translation: Vector2<Src>) -> Self {
363 Self::from_matrix_unchecked(Matrix3::from_translation(translation.to_untyped()))
364 }
365
366 #[inline]
387 #[must_use]
388 pub fn from_scale_angle_translation(
389 scale: Vector2<Src>,
390 angle: Angle<Src::Scalar>,
391 translation: Vector2<Src>,
392 ) -> Self {
393 Self::from_matrix_unchecked(Matrix3::from_scale_angle_translation(
394 scale.to_untyped(),
395 angle,
396 translation.to_untyped(),
397 ))
398 }
399}
400
401impl<Src, Dst> TransformMap<Point2<Src>> for Transform2<Src, Dst>
402where
403 Src: Unit,
404 Src::Scalar: FloatScalar,
405 Dst: Unit<Scalar = Src::Scalar>,
406{
407 type Output = Point2<Dst>;
408
409 #[must_use]
410 fn map(&self, value: Point2<Src>) -> Self::Output {
411 Point2::from_untyped(self.matrix.transform_point(value.to_untyped()))
412 }
413}
414
415impl<Src, Dst> TransformMap<Vector2<Src>> for Transform2<Src, Dst>
416where
417 Src: Unit,
418 Src::Scalar: FloatScalar,
419 Dst: Unit<Scalar = Src::Scalar>,
420{
421 type Output = Vector2<Dst>;
422
423 #[must_use]
424 fn map(&self, value: Vector2<Src>) -> Self::Output {
425 Vector2::from_untyped(self.matrix.transform_vector(value.to_untyped()))
426 }
427}
428
429impl<Src, Dst> Transform2<Src, Dst>
430where
431 Src: Unit,
432 Src::Scalar: FloatScalar,
433 Dst: Unit<Scalar = Src::Scalar>,
434{
435 #[inline]
445 #[must_use]
446 pub fn then<Dst2>(self, other: Transform2<Dst, Dst2>) -> Transform2<Src, Dst2>
447 where
448 Dst2: Unit<Scalar = Dst::Scalar>,
449 {
450 Transform2::from_matrix_unchecked(other.matrix * self.matrix)
451 }
452
453 #[inline]
457 #[must_use]
458 pub fn then_rotate(self, angle: Angle<Src::Scalar>) -> Self {
459 self.then(Transform2::from_angle(angle))
460 }
461
462 #[inline]
476 #[must_use]
477 pub fn then_scale(self, scale: Vector2<Dst>) -> Self {
478 self.then(Transform2::from_scale(scale))
479 }
480
481 #[inline]
485 #[must_use]
486 pub fn then_translate(self, translation: Vector2<Dst>) -> Self {
487 self.then(Transform2::from_translation(translation))
488 }
489
490 #[inline]
494 #[must_use]
495 pub fn inverse(&self) -> Transform2<Dst, Src> {
496 Transform2::from_matrix_unchecked(self.matrix.inverse())
497 }
498}
499
500impl<Src, Dst, Dst2> Mul<Transform2<Dst, Dst2>> for Transform2<Src, Dst>
501where
502 Src: Unit,
503 Src::Scalar: FloatScalar,
504 Dst: Unit<Scalar = Src::Scalar>,
505 Dst2: Unit<Scalar = Src::Scalar>,
506{
507 type Output = Transform2<Src, Dst2>;
508
509 #[inline]
510 #[must_use]
511 fn mul(self, rhs: Transform2<Dst, Dst2>) -> Self::Output {
512 Transform2::from_matrix_unchecked(rhs.matrix * self.matrix)
513 }
514}
515
516impl<Src, Dst, Dst2> Mul<Transform3<Dst, Dst2>> for Transform3<Src, Dst>
517where
518 Src: Unit,
519 Src::Scalar: FloatScalar,
520 Dst: Unit<Scalar = Src::Scalar>,
521 Dst2: Unit<Scalar = Src::Scalar>,
522{
523 type Output = Transform3<Src, Dst2>;
524
525 #[inline]
526 #[must_use]
527 fn mul(self, rhs: Transform3<Dst, Dst2>) -> Self::Output {
528 Transform3::from_matrix_unchecked(rhs.matrix * self.matrix)
529 }
530}
531
532impl<Src, Dst> Transform3<Src, Dst>
533where
534 Src: Unit,
535 Src::Scalar: FloatScalar,
536 Dst: Unit<Scalar = Src::Scalar>,
537{
538 pub const IDENTITY: Self = Self {
540 matrix: Matrix4::IDENTITY,
541 _marker: PhantomData,
542 };
543
544 #[inline]
546 #[must_use]
547 pub const fn from_matrix_unchecked(matrix: Matrix4<Src::Scalar>) -> Self {
548 Transform3 {
549 matrix,
550 _marker: PhantomData,
551 }
552 }
553
554 #[inline]
556 #[must_use]
557 pub fn from_matrix(matrix: Matrix4<Src::Scalar>) -> Option<Self> {
558 if matrix.is_invertible() {
559 Some(Self::from_matrix_unchecked(matrix))
560 } else {
561 None
562 }
563 }
564
565 #[inline]
575 #[must_use]
576 pub fn then<Dst2: Unit<Scalar = Dst::Scalar>>(
577 self,
578 other: Transform3<Dst, Dst2>,
579 ) -> Transform3<Src, Dst2> {
580 Transform3::from_matrix_unchecked(other.matrix * self.matrix)
581 }
582
583 #[inline]
587 #[must_use]
588 pub fn then_rotate(self, axis: Vector3<Dst>, angle: Angle<Src::Scalar>) -> Self {
589 self.then(Transform3::from_axis_angle(axis, angle))
590 }
591
592 #[inline]
606 #[must_use]
607 pub fn then_scale(self, scale: Vector3<Dst>) -> Self {
608 self.then(Transform3::from_scale(scale))
609 }
610
611 #[inline]
615 #[must_use]
616 pub fn then_translate(self, translation: Vector3<Dst>) -> Self {
617 self.then(Transform3::from_translation(translation))
618 }
619
620 #[inline]
639 #[must_use]
640 pub fn from_axis_angle(axis: Vector3<Src>, angle: Angle<Src::Scalar>) -> Self {
641 Self::from_matrix_unchecked(Matrix4::from_axis_angle(
642 axis.to_untyped(),
643 Angle {
644 radians: angle.radians,
645 },
646 ))
647 }
648
649 #[inline]
668 #[must_use]
669 pub fn from_scale(scale: Vector3<Src>) -> Self {
670 Self::from_matrix_unchecked(Matrix4::from_scale(scale.to_untyped()))
671 }
672
673 #[inline]
692 #[must_use]
693 pub fn from_translation(translation: Vector3<Src>) -> Self {
694 Self::from_matrix_unchecked(Matrix4::from_translation(translation.to_untyped()))
695 }
696
697 #[inline]
724 #[must_use]
725 pub fn from_scale_rotation_translation(
726 scale: Vector3<Src>,
727 axis: Vector3<Src>,
728 angle: Angle<Src::Scalar>,
729 translation: Vector3<Src>,
730 ) -> Self {
731 let rotation = <Src::Scalar as FloatScalar>::Quat::from_axis_angle(peel(axis), peel(angle));
732 Self::from_matrix_unchecked(Matrix4::from_scale_rotation_translation(
733 scale.to_untyped(),
734 rotation,
735 translation.to_untyped(),
736 ))
737 }
738
739 #[inline]
744 #[must_use]
745 pub fn map_vector(&self, vector: Vector3<Src>) -> Vector3<Dst> {
746 cast(self.matrix.transform_vector3(vector.to_untyped()))
747 }
748
749 #[inline]
754 #[must_use]
755 pub fn map_point(&self, point: Point3<Src>) -> Point3<Dst> {
756 cast(self.matrix.project_point3(point.to_untyped()))
757 }
758
759 #[inline]
763 #[must_use]
764 pub fn inverse(&self) -> Transform3<Dst, Src> {
765 Transform3::from_matrix_unchecked(self.matrix.inverse())
766 }
767}
768
769impl<Src, Dst> TransformMap<Point3<Src>> for Transform3<Src, Dst>
770where
771 Src: Unit,
772 Src::Scalar: FloatScalar,
773 Dst: Unit<Scalar = Src::Scalar>,
774{
775 type Output = Point3<Dst>;
776
777 #[must_use]
778 fn map(&self, value: Point3<Src>) -> Self::Output {
779 self.map_point(value)
780 }
781}
782
783impl<Src, Dst> TransformMap<Vector3<Src>> for Transform3<Src, Dst>
784where
785 Src: Unit,
786 Src::Scalar: FloatScalar,
787 Dst: Unit<Scalar = Src::Scalar>,
788{
789 type Output = Vector3<Dst>;
790
791 #[must_use]
792 fn map(&self, value: Vector3<Src>) -> Self::Output {
793 self.map_vector(value)
794 }
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800 use approx::*;
801
802 struct TestSrc;
803 impl Unit for TestSrc {
804 type Scalar = f32;
805 }
806
807 struct TestDst;
808 impl Unit for TestDst {
809 type Scalar = f32;
810 }
811
812 struct TestDst2;
813 impl Unit for TestDst2 {
814 type Scalar = f32;
815 }
816
817 macro_rules! check_2d_and_3d {
818 { $($test:tt)* } => {{
819 {
820 type Transform = Transform2<TestSrc, TestDst>;
821 type Mat = Matrix3<f32>;
822 type TransformInverse = Transform2<TestDst, TestSrc>;
823 type VectorSrc = Vector2<TestSrc>;
824 type VectorDst = Vector2<TestDst>;
825 type PointSrc = Point2<TestSrc>;
826 type PointDst = Point2<TestDst>;
827 let _ = core::mem::size_of::<(Transform, Mat, TransformInverse, VectorSrc, VectorDst, PointSrc, PointDst)>();
828 $($test)*
829 }
830 {
831 type Transform = Transform3<TestSrc, TestDst>;
832 type Mat = Matrix4<f32>;
833 type TransformInverse = Transform3<TestDst, TestSrc>;
834 type VectorSrc = Vector3<TestSrc>;
835 type VectorDst = Vector3<TestDst>;
836 type PointSrc = Point3<TestSrc>;
837 type PointDst = Point3<TestDst>;
838 let _ = core::mem::size_of::<(Transform, Mat, TransformInverse, VectorSrc, VectorDst, PointSrc, PointDst)>();
839 $($test)*
840 }
841 }};
842 }
843
844 #[test]
845 fn basic() {
846 check_2d_and_3d! {
847 let a = Transform::IDENTITY;
848 let b = a;
849 assert_eq!(a, b);
850 assert_abs_diff_eq!(a, b);
851 assert_relative_eq!(a, b);
852 assert_ulps_eq!(a, b);
853
854 let c = a.then_scale(VectorDst::splat(2.0));
855 assert_ne!(a, c);
856 assert_abs_diff_ne!(a, c);
857 assert_relative_ne!(a, c);
858 assert_ulps_ne!(a, c);
859 };
860
861 check_2d_and_3d! {
862 let a = Transform::IDENTITY;
863 assert_eq!(a, Transform::default());
864 };
865 }
866
867 #[test]
868 fn from_matrix() {
869 check_2d_and_3d! {
870 assert!(Transform::from_matrix(Mat::ZERO).is_none());
871 assert_eq!(Transform::from_matrix(Mat::IDENTITY), Some(Transform::IDENTITY));
872 };
873 }
874
875 #[test]
876 fn inverse() {
877 check_2d_and_3d! {
878 assert!(Transform::from_matrix(Mat::zeroed()).is_none());
879
880 let transform = Transform::from_translation(VectorSrc::splat(1.0));
881 let point = PointSrc::splat(2.0);
882 let point_dst: PointDst = transform.map(point);
883 assert_abs_diff_eq!(point_dst, PointDst::splat(3.0));
884
885 let inverse: TransformInverse = transform.inverse();
886 let point_src = inverse.map(point_dst);
887 assert_abs_diff_eq!(point_src, point);
888 };
889 }
890
891 #[test]
892 fn concatenation() {
893 {
894 let a = Transform2::<TestSrc, TestDst>::from_scale((2.0, 2.0).into());
895 let b = Transform2::<TestDst, TestDst2>::from_translation((1.0, 1.0).into());
896 let c: Transform2<TestSrc, TestDst2> = a * b;
897 assert_eq!(
898 c,
899 Transform2::<TestSrc, TestDst2>::from_scale((2.0, 2.0).into())
900 .then_translate((1.0, 1.0).into())
901 );
902 }
903
904 {
905 let a = Transform3::<TestSrc, TestDst>::from_scale((2.0, 2.0, 2.0).into());
906 let b = Transform3::<TestDst, TestDst2>::from_translation((1.0, 1.0, 1.0).into());
907 let c: Transform3<TestSrc, TestDst2> = a * b;
908 assert_eq!(
909 c,
910 Transform3::<TestSrc, TestDst2>::from_scale((2.0, 2.0, 2.0).into())
911 .then_translate((1.0, 1.0, 1.0).into())
912 );
913 }
914 }
915
916 #[test]
917 fn gaslight_coverage() {
918 fn clone_me<T: Clone>(v: &T) -> T {
919 v.clone()
920 }
921 _ = clone_me(&Transform2::<f32, f32>::IDENTITY);
922 _ = clone_me(&Transform3::<f32, f32>::IDENTITY);
923 }
924}