1#![cfg_attr(feature = "cargo-clippy", allow(just_underscores_and_digits))]
11
12use super::{UnknownUnit, Angle};
13#[cfg(feature = "mint")]
14use mint;
15use crate::num::{One, Zero};
16use crate::point::{Point2D, point2};
17use crate::vector::{Vector2D, vec2};
18use crate::rect::Rect;
19use crate::box2d::Box2D;
20use crate::transform3d::Transform3D;
21use core::ops::{Add, Mul, Div, Sub};
22use core::marker::PhantomData;
23use core::cmp::{Eq, PartialEq};
24use core::hash::{Hash};
25use crate::approxeq::ApproxEq;
26use crate::trig::Trig;
27use core::fmt;
28use num_traits::NumCast;
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31
32#[repr(C)]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60#[cfg_attr(
61 feature = "serde",
62 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
63)]
64pub struct Transform2D<T, Src, Dst> {
65 pub m11: T, pub m12: T,
66 pub m21: T, pub m22: T,
67 pub m31: T, pub m32: T,
68 #[doc(hidden)]
69 pub _unit: PhantomData<(Src, Dst)>,
70}
71
72impl<T: Copy, Src, Dst> Copy for Transform2D<T, Src, Dst> {}
73
74impl<T: Clone, Src, Dst> Clone for Transform2D<T, Src, Dst> {
75 fn clone(&self) -> Self {
76 Transform2D {
77 m11: self.m11.clone(),
78 m12: self.m12.clone(),
79 m21: self.m21.clone(),
80 m22: self.m22.clone(),
81 m31: self.m31.clone(),
82 m32: self.m32.clone(),
83 _unit: PhantomData,
84 }
85 }
86}
87
88impl<T, Src, Dst> Eq for Transform2D<T, Src, Dst> where T: Eq {}
89
90impl<T, Src, Dst> PartialEq for Transform2D<T, Src, Dst>
91 where T: PartialEq
92{
93 fn eq(&self, other: &Self) -> bool {
94 self.m11 == other.m11 &&
95 self.m12 == other.m12 &&
96 self.m21 == other.m21 &&
97 self.m22 == other.m22 &&
98 self.m31 == other.m31 &&
99 self.m32 == other.m32
100 }
101}
102
103impl<T, Src, Dst> Hash for Transform2D<T, Src, Dst>
104 where T: Hash
105{
106 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
107 self.m11.hash(h);
108 self.m12.hash(h);
109 self.m21.hash(h);
110 self.m22.hash(h);
111 self.m31.hash(h);
112 self.m32.hash(h);
113 }
114}
115
116
117impl<T, Src, Dst> Transform2D<T, Src, Dst> {
118 pub const fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
134 Transform2D {
135 m11, m12,
136 m21, m22,
137 m31, m32,
138 _unit: PhantomData,
139 }
140 }
141
142 #[inline]
149 pub fn approx_eq(&self, other: &Self) -> bool
150 where T : ApproxEq<T> {
151 <Self as ApproxEq<T>>::approx_eq(&self, &other)
152 }
153
154 #[inline]
161 pub fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool
162 where T : ApproxEq<T> {
163 <Self as ApproxEq<T>>::approx_eq_eps(&self, &other, &eps)
164 }
165}
166
167impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
168 #[inline]
177 pub fn to_array(&self) -> [T; 6] {
178 [
179 self.m11, self.m12,
180 self.m21, self.m22,
181 self.m31, self.m32
182 ]
183 }
184
185 #[inline]
194 pub fn to_array_transposed(&self) -> [T; 6] {
195 [
196 self.m11, self.m21, self.m31,
197 self.m12, self.m22, self.m32
198 ]
199 }
200
201 #[inline]
204 pub fn to_arrays(&self) -> [[T; 2]; 3] {
205 [
206 [self.m11, self.m12],
207 [self.m21, self.m22],
208 [self.m31, self.m32],
209 ]
210 }
211
212 #[inline]
219 pub fn from_array(array: [T; 6]) -> Self {
220 Self::new(
221 array[0], array[1],
222 array[2], array[3],
223 array[4], array[5],
224 )
225 }
226
227 #[inline]
234 pub fn from_arrays(array: [[T; 2]; 3]) -> Self {
235 Self::new(
236 array[0][0], array[0][1],
237 array[1][0], array[1][1],
238 array[2][0], array[2][1],
239 )
240 }
241
242 #[inline]
244 pub fn to_untyped(&self) -> Transform2D<T, UnknownUnit, UnknownUnit> {
245 Transform2D::new(
246 self.m11, self.m12,
247 self.m21, self.m22,
248 self.m31, self.m32
249 )
250 }
251
252 #[inline]
254 pub fn from_untyped(p: &Transform2D<T, UnknownUnit, UnknownUnit>) -> Self {
255 Transform2D::new(
256 p.m11, p.m12,
257 p.m21, p.m22,
258 p.m31, p.m32
259 )
260 }
261
262 #[inline]
264 pub fn with_source<NewSrc>(&self) -> Transform2D<T, NewSrc, Dst> {
265 Transform2D::new(
266 self.m11, self.m12,
267 self.m21, self.m22,
268 self.m31, self.m32,
269 )
270 }
271
272 #[inline]
274 pub fn with_destination<NewDst>(&self) -> Transform2D<T, Src, NewDst> {
275 Transform2D::new(
276 self.m11, self.m12,
277 self.m21, self.m22,
278 self.m31, self.m32,
279 )
280 }
281
282 pub fn to_3d(&self) -> Transform3D<T, Src, Dst>
284 where
285 T: Zero + One,
286 {
287 Transform3D::new_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
288 }
289}
290
291impl<T: NumCast + Copy, Src, Dst> Transform2D<T, Src, Dst> {
292 #[inline]
294 pub fn cast<NewT: NumCast>(&self) -> Transform2D<NewT, Src, Dst> {
295 self.try_cast().unwrap()
296 }
297
298 pub fn try_cast<NewT: NumCast>(&self) -> Option<Transform2D<NewT, Src, Dst>> {
300 match (NumCast::from(self.m11), NumCast::from(self.m12),
301 NumCast::from(self.m21), NumCast::from(self.m22),
302 NumCast::from(self.m31), NumCast::from(self.m32)) {
303 (Some(m11), Some(m12),
304 Some(m21), Some(m22),
305 Some(m31), Some(m32)) => {
306 Some(Transform2D::new(
307 m11, m12,
308 m21, m22,
309 m31, m32
310 ))
311 },
312 _ => None
313 }
314 }
315}
316
317impl<T, Src, Dst> Transform2D<T, Src, Dst>
318where
319 T: Zero + One,
320{
321 #[inline]
329 pub fn identity() -> Self {
330 Self::translation(T::zero(), T::zero())
331 }
332
333 fn is_identity(&self) -> bool
337 where
338 T: PartialEq,
339 {
340 *self == Self::identity()
341 }
342}
343
344
345impl<T, Src, Dst> Transform2D<T, Src, Dst>
347where
348 T: Copy + Add<Output = T> + Mul<Output = T>,
349{
350 #[must_use]
353 pub fn then<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> {
354 Transform2D::new(
355 self.m11 * mat.m11 + self.m12 * mat.m21,
356 self.m11 * mat.m12 + self.m12 * mat.m22,
357
358 self.m21 * mat.m11 + self.m22 * mat.m21,
359 self.m21 * mat.m12 + self.m22 * mat.m22,
360
361 self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
362 self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
363 )
364 }
365}
366
367impl<T, Src, Dst> Transform2D<T, Src, Dst>
369where
370 T: Zero + One,
371{
372 #[inline]
380 pub fn translation(x: T, y: T) -> Self {
381 let _0 = || T::zero();
382 let _1 = || T::one();
383
384 Self::new(
385 _1(), _0(),
386 _0(), _1(),
387 x, y,
388 )
389 }
390
391 #[inline]
393 #[must_use]
394 pub fn then_translate(&self, v: Vector2D<T, Dst>) -> Self
395 where
396 T: Copy + Add<Output = T> + Mul<Output = T>,
397 {
398 self.then(&Transform2D::translation(v.x, v.y))
399 }
400
401 #[inline]
403 #[must_use]
404 pub fn pre_translate(&self, v: Vector2D<T, Src>) -> Self
405 where
406 T: Copy + Add<Output = T> + Mul<Output = T>,
407 {
408 Transform2D::translation(v.x, v.y).then(self)
409 }
410}
411
412impl<T, Src, Dst> Transform2D<T, Src, Dst>
414where
415 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig,
416{
417 #[inline]
419 pub fn rotation(theta: Angle<T>) -> Self {
420 let _0 = Zero::zero();
421 let cos = theta.get().cos();
422 let sin = theta.get().sin();
423 Transform2D::new(
424 cos, sin,
425 _0 - sin, cos,
426 _0, _0
427 )
428 }
429
430 #[inline]
432 #[must_use]
433 pub fn then_rotate(&self, theta: Angle<T>) -> Self {
434 self.then(&Transform2D::rotation(theta))
435 }
436
437 #[inline]
439 #[must_use]
440 pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
441 Transform2D::rotation(theta).then(self)
442 }
443}
444
445impl<T, Src, Dst> Transform2D<T, Src, Dst> {
447 #[inline]
455 pub fn scale(x: T, y: T) -> Self
456 where
457 T: Zero,
458 {
459 let _0 = || Zero::zero();
460
461 Self::new(
462 x, _0(),
463 _0(), y,
464 _0(), _0(),
465 )
466 }
467
468 #[inline]
470 #[must_use]
471 pub fn then_scale(&self, x: T, y: T) -> Self
472 where
473 T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
474 {
475 self.then(&Transform2D::scale(x, y))
476 }
477
478 #[inline]
480 #[must_use]
481 pub fn pre_scale(&self, x: T, y: T) -> Self
482 where
483 T: Copy + Mul<Output = T>,
484 {
485 Transform2D::new(
486 self.m11 * x, self.m12 * x,
487 self.m21 * y, self.m22 * y,
488 self.m31, self.m32
489 )
490 }
491}
492
493impl<T, Src, Dst> Transform2D<T, Src, Dst>
495where
496 T: Copy + Add<Output = T> + Mul<Output = T>,
497{
498 #[inline]
500 #[must_use]
501 pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> {
502 Point2D::new(
503 point.x * self.m11 + point.y * self.m21 + self.m31,
504 point.x * self.m12 + point.y * self.m22 + self.m32
505 )
506 }
507
508 #[inline]
510 #[must_use]
511 pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T, Dst> {
512 vec2(vec.x * self.m11 + vec.y * self.m21,
513 vec.x * self.m12 + vec.y * self.m22)
514 }
515
516 #[inline]
519 #[must_use]
520 pub fn outer_transformed_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst>
521 where
522 T: Sub<Output = T> + Zero + PartialOrd,
523 {
524 let min = rect.min();
525 let max = rect.max();
526 Rect::from_points(&[
527 self.transform_point(min),
528 self.transform_point(max),
529 self.transform_point(point2(max.x, min.y)),
530 self.transform_point(point2(min.x, max.y)),
531 ])
532 }
533
534
535 #[inline]
538 #[must_use]
539 pub fn outer_transformed_box(&self, b: &Box2D<T, Src>) -> Box2D<T, Dst>
540 where
541 T: Sub<Output = T> + Zero + PartialOrd,
542 {
543 Box2D::from_points(&[
544 self.transform_point(b.min),
545 self.transform_point(b.max),
546 self.transform_point(point2(b.max.x, b.min.y)),
547 self.transform_point(point2(b.min.x, b.max.y)),
548 ])
549 }
550}
551
552
553impl<T, Src, Dst> Transform2D<T, Src, Dst>
554where
555 T: Copy + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + PartialEq + Zero + One,
556{
557 pub fn determinant(&self) -> T {
559 self.m11 * self.m22 - self.m12 * self.m21
560 }
561
562 #[inline]
564 pub fn is_invertible(&self) -> bool {
565 self.determinant() != Zero::zero()
566 }
567
568 #[must_use]
570 pub fn inverse(&self) -> Option<Transform2D<T, Dst, Src>> {
571 let det = self.determinant();
572
573 let _0: T = Zero::zero();
574 let _1: T = One::one();
575
576 if det == _0 {
577 return None;
578 }
579
580 let inv_det = _1 / det;
581 Some(Transform2D::new(
582 inv_det * self.m22,
583 inv_det * (_0 - self.m12),
584 inv_det * (_0 - self.m21),
585 inv_det * self.m11,
586 inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
587 inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
588 ))
589 }
590}
591
592impl <T, Src, Dst> Default for Transform2D<T, Src, Dst>
593 where T: Zero + One
594{
595 fn default() -> Self {
597 Self::identity()
598 }
599}
600
601impl<T: ApproxEq<T>, Src, Dst> ApproxEq<T> for Transform2D<T, Src, Dst> {
602 #[inline]
603 fn approx_epsilon() -> T { T::approx_epsilon() }
604
605 fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
608 self.m11.approx_eq_eps(&other.m11, eps) && self.m12.approx_eq_eps(&other.m12, eps) &&
609 self.m21.approx_eq_eps(&other.m21, eps) && self.m22.approx_eq_eps(&other.m22, eps) &&
610 self.m31.approx_eq_eps(&other.m31, eps) && self.m32.approx_eq_eps(&other.m32, eps)
611 }
612}
613
614impl<T, Src, Dst> fmt::Debug for Transform2D<T, Src, Dst>
615where T: Copy + fmt::Debug +
616 PartialEq +
617 One + Zero {
618 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
619 if self.is_identity() {
620 write!(f, "[I]")
621 } else {
622 self.to_array().fmt(f)
623 }
624 }
625}
626
627#[cfg(feature = "mint")]
628impl<T, Src, Dst> From<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> {
629 fn from(m: mint::RowMatrix3x2<T>) -> Self {
630 Transform2D {
631 m11: m.x.x, m12: m.x.y,
632 m21: m.y.x, m22: m.y.y,
633 m31: m.z.x, m32: m.z.y,
634 _unit: PhantomData,
635 }
636 }
637}
638#[cfg(feature = "mint")]
639impl<T, Src, Dst> Into<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> {
640 fn into(self) -> mint::RowMatrix3x2<T> {
641 mint::RowMatrix3x2 {
642 x: mint::Vector2 { x: self.m11, y: self.m12 },
643 y: mint::Vector2 { x: self.m21, y: self.m22 },
644 z: mint::Vector2 { x: self.m31, y: self.m32 },
645 }
646 }
647}
648
649
650#[cfg(test)]
651mod test {
652 use super::*;
653 use crate::default;
654 use crate::approxeq::ApproxEq;
655 #[cfg(feature = "mint")]
656 use mint;
657
658 use core::f32::consts::FRAC_PI_2;
659
660 type Mat = default::Transform2D<f32>;
661
662 fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
663
664 #[test]
665 pub fn test_translation() {
666 let t1 = Mat::translation(1.0, 2.0);
667 let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
668 let t3 = Mat::identity().then_translate(vec2(1.0, 2.0));
669 assert_eq!(t1, t2);
670 assert_eq!(t1, t3);
671
672 assert_eq!(t1.transform_point(Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
673
674 assert_eq!(t1.then(&t1), Mat::translation(2.0, 4.0));
675 }
676
677 #[test]
678 pub fn test_rotation() {
679 let r1 = Mat::rotation(rad(FRAC_PI_2));
680 let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
681 let r3 = Mat::identity().then_rotate(rad(FRAC_PI_2));
682 assert_eq!(r1, r2);
683 assert_eq!(r1, r3);
684
685 assert!(r1.transform_point(Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(-2.0, 1.0)));
686
687 assert!(r1.then(&r1).approx_eq(&Mat::rotation(rad(FRAC_PI_2*2.0))));
688 }
689
690 #[test]
691 pub fn test_scale() {
692 let s1 = Mat::scale(2.0, 3.0);
693 let s2 = Mat::identity().pre_scale(2.0, 3.0);
694 let s3 = Mat::identity().then_scale(2.0, 3.0);
695 assert_eq!(s1, s2);
696 assert_eq!(s1, s3);
697
698 assert!(s1.transform_point(Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
699 }
700
701
702 #[test]
703 pub fn test_pre_then_scale() {
704 let m = Mat::rotation(rad(FRAC_PI_2)).then_translate(vec2(6.0, 7.0));
705 let s = Mat::scale(2.0, 3.0);
706 assert_eq!(m.then(&s), m.then_scale(2.0, 3.0));
707 }
708
709 #[test]
710 pub fn test_inverse_simple() {
711 let m1 = Mat::identity();
712 let m2 = m1.inverse().unwrap();
713 assert!(m1.approx_eq(&m2));
714 }
715
716 #[test]
717 pub fn test_inverse_scale() {
718 let m1 = Mat::scale(1.5, 0.3);
719 let m2 = m1.inverse().unwrap();
720 assert!(m1.then(&m2).approx_eq(&Mat::identity()));
721 assert!(m2.then(&m1).approx_eq(&Mat::identity()));
722 }
723
724 #[test]
725 pub fn test_inverse_translate() {
726 let m1 = Mat::translation(-132.0, 0.3);
727 let m2 = m1.inverse().unwrap();
728 assert!(m1.then(&m2).approx_eq(&Mat::identity()));
729 assert!(m2.then(&m1).approx_eq(&Mat::identity()));
730 }
731
732 #[test]
733 fn test_inverse_none() {
734 assert!(Mat::scale(2.0, 0.0).inverse().is_none());
735 assert!(Mat::scale(2.0, 2.0).inverse().is_some());
736 }
737
738 #[test]
739 pub fn test_pre_post() {
740 let m1 = default::Transform2D::identity().then_scale(1.0, 2.0).then_translate(vec2(1.0, 2.0));
741 let m2 = default::Transform2D::identity().pre_translate(vec2(1.0, 2.0)).pre_scale(1.0, 2.0);
742 assert!(m1.approx_eq(&m2));
743
744 let r = Mat::rotation(rad(FRAC_PI_2));
745 let t = Mat::translation(2.0, 3.0);
746
747 let a = Point2D::new(1.0, 1.0);
748
749 assert!(r.then(&t).transform_point(a).approx_eq(&Point2D::new(1.0, 4.0)));
750 assert!(t.then(&r).transform_point(a).approx_eq(&Point2D::new(-4.0, 3.0)));
751 assert!(t.then(&r).transform_point(a).approx_eq(&r.transform_point(t.transform_point(a))));
752 }
753
754 #[test]
755 fn test_size_of() {
756 use core::mem::size_of;
757 assert_eq!(size_of::<default::Transform2D<f32>>(), 6*size_of::<f32>());
758 assert_eq!(size_of::<default::Transform2D<f64>>(), 6*size_of::<f64>());
759 }
760
761 #[test]
762 pub fn test_is_identity() {
763 let m1 = default::Transform2D::identity();
764 assert!(m1.is_identity());
765 let m2 = m1.then_translate(vec2(0.1, 0.0));
766 assert!(!m2.is_identity());
767 }
768
769 #[test]
770 pub fn test_transform_vector() {
771 let m1 = Mat::translation(1.0, 1.0);
773 let v1 = vec2(10.0, -10.0);
774 assert_eq!(v1, m1.transform_vector(v1));
775 }
776
777 #[cfg(feature = "mint")]
778 #[test]
779 pub fn test_mint() {
780 let m1 = Mat::rotation(rad(FRAC_PI_2));
781 let mm: mint::RowMatrix3x2<_> = m1.into();
782 let m2 = Mat::from(mm);
783
784 assert_eq!(m1, m2);
785 }
786}