1#![allow(clippy::just_underscores_and_digits)]
11
12use super::{Angle, UnknownUnit};
13use crate::approxeq::ApproxEq;
14use crate::box2d::Box2D;
15use crate::num::{One, Zero};
16use crate::point::{point2, Point2D};
17use crate::rect::Rect;
18use crate::scale::Scale;
19use crate::transform3d::Transform3D;
20use crate::translation::Translation2D;
21use crate::trig::Trig;
22use crate::vector::{vec2, Vector2D};
23use crate::Rotation2D;
24use core::cmp::{Eq, PartialEq};
25use core::fmt;
26use core::hash::Hash;
27use core::marker::PhantomData;
28use core::ops::{Add, Div, Mul, Neg, Sub};
29
30#[cfg(feature = "bytemuck")]
31use bytemuck::{Pod, Zeroable};
32#[cfg(feature = "malloc_size_of")]
33use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
34#[cfg(feature = "mint")]
35use mint;
36use num_traits::{NumCast, Signed};
37#[cfg(feature = "serde")]
38use serde::{Deserialize, Serialize};
39
40#[repr(C)]
67#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
68#[cfg_attr(
69 feature = "serde",
70 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
71)]
72#[rustfmt::skip]
73pub struct Transform2D<T, Src, Dst> {
74 pub m11: T, pub m12: T,
75 pub m21: T, pub m22: T,
76 pub m31: T, pub m32: T,
77 #[doc(hidden)]
78 pub _unit: PhantomData<(Src, Dst)>,
79}
80
81#[cfg(feature = "arbitrary")]
82impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Transform2D<T, Src, Dst>
83where
84 T: arbitrary::Arbitrary<'a>,
85{
86 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
87 let (m11, m12, m21, m22, m31, m32) = arbitrary::Arbitrary::arbitrary(u)?;
88 Ok(Transform2D {
89 m11,
90 m12,
91 m21,
92 m22,
93 m31,
94 m32,
95 _unit: PhantomData,
96 })
97 }
98}
99
100#[cfg(feature = "bytemuck")]
101unsafe impl<T: Zeroable, Src, Dst> Zeroable for Transform2D<T, Src, Dst> {}
102
103#[cfg(feature = "bytemuck")]
104unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Transform2D<T, Src, Dst> {}
105
106#[cfg(feature = "malloc_size_of")]
107impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for Transform2D<T, Src, Dst> {
108 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
109 self.m11.size_of(ops)
110 + self.m12.size_of(ops)
111 + self.m21.size_of(ops)
112 + self.m22.size_of(ops)
113 + self.m31.size_of(ops)
114 + self.m32.size_of(ops)
115 }
116}
117
118impl<T: Copy, Src, Dst> Copy for Transform2D<T, Src, Dst> {}
119
120impl<T: Clone, Src, Dst> Clone for Transform2D<T, Src, Dst> {
121 fn clone(&self) -> Self {
122 Transform2D {
123 m11: self.m11.clone(),
124 m12: self.m12.clone(),
125 m21: self.m21.clone(),
126 m22: self.m22.clone(),
127 m31: self.m31.clone(),
128 m32: self.m32.clone(),
129 _unit: PhantomData,
130 }
131 }
132}
133
134impl<T, Src, Dst> Eq for Transform2D<T, Src, Dst> where T: Eq {}
135
136impl<T, Src, Dst> PartialEq for Transform2D<T, Src, Dst>
137where
138 T: PartialEq,
139{
140 fn eq(&self, other: &Self) -> bool {
141 self.m11 == other.m11
142 && self.m12 == other.m12
143 && self.m21 == other.m21
144 && self.m22 == other.m22
145 && self.m31 == other.m31
146 && self.m32 == other.m32
147 }
148}
149
150impl<T, Src, Dst> Hash for Transform2D<T, Src, Dst>
151where
152 T: Hash,
153{
154 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
155 self.m11.hash(h);
156 self.m12.hash(h);
157 self.m21.hash(h);
158 self.m22.hash(h);
159 self.m31.hash(h);
160 self.m32.hash(h);
161 }
162}
163
164impl<T, Src, Dst> Transform2D<T, Src, Dst> {
165 #[rustfmt::skip]
181 pub const fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
182 Transform2D {
183 m11, m12,
184 m21, m22,
185 m31, m32,
186 _unit: PhantomData,
187 }
188 }
189
190 #[inline]
195 pub fn approx_eq(&self, other: &Self) -> bool
196 where
197 T: ApproxEq<T>,
198 {
199 <Self as ApproxEq<T>>::approx_eq(self, other)
200 }
201
202 #[inline]
207 pub fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool
208 where
209 T: ApproxEq<T>,
210 {
211 <Self as ApproxEq<T>>::approx_eq_eps(self, other, eps)
212 }
213}
214
215impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
216 #[inline]
225 #[rustfmt::skip]
226 pub fn to_array(&self) -> [T; 6] {
227 [
228 self.m11, self.m12,
229 self.m21, self.m22,
230 self.m31, self.m32
231 ]
232 }
233
234 #[inline]
243 #[rustfmt::skip]
244 pub fn to_array_transposed(&self) -> [T; 6] {
245 [
246 self.m11, self.m21, self.m31,
247 self.m12, self.m22, self.m32
248 ]
249 }
250
251 #[inline]
254 pub fn to_arrays(&self) -> [[T; 2]; 3] {
255 [
256 [self.m11, self.m12],
257 [self.m21, self.m22],
258 [self.m31, self.m32],
259 ]
260 }
261
262 #[inline]
269 #[rustfmt::skip]
270 pub fn from_array(array: [T; 6]) -> Self {
271 Self::new(
272 array[0], array[1],
273 array[2], array[3],
274 array[4], array[5],
275 )
276 }
277
278 #[inline]
285 #[rustfmt::skip]
286 pub fn from_arrays(array: [[T; 2]; 3]) -> Self {
287 Self::new(
288 array[0][0], array[0][1],
289 array[1][0], array[1][1],
290 array[2][0], array[2][1],
291 )
292 }
293
294 #[inline]
296 #[rustfmt::skip]
297 pub fn to_untyped(&self) -> Transform2D<T, UnknownUnit, UnknownUnit> {
298 Transform2D::new(
299 self.m11, self.m12,
300 self.m21, self.m22,
301 self.m31, self.m32
302 )
303 }
304
305 #[inline]
307 #[rustfmt::skip]
308 pub fn from_untyped(p: &Transform2D<T, UnknownUnit, UnknownUnit>) -> Self {
309 Transform2D::new(
310 p.m11, p.m12,
311 p.m21, p.m22,
312 p.m31, p.m32
313 )
314 }
315
316 #[inline]
318 #[rustfmt::skip]
319 pub fn with_source<NewSrc>(&self) -> Transform2D<T, NewSrc, Dst> {
320 Transform2D::new(
321 self.m11, self.m12,
322 self.m21, self.m22,
323 self.m31, self.m32,
324 )
325 }
326
327 #[inline]
329 #[rustfmt::skip]
330 pub fn with_destination<NewDst>(&self) -> Transform2D<T, Src, NewDst> {
331 Transform2D::new(
332 self.m11, self.m12,
333 self.m21, self.m22,
334 self.m31, self.m32,
335 )
336 }
337
338 #[inline]
340 pub fn to_3d(&self) -> Transform3D<T, Src, Dst>
341 where
342 T: Zero + One,
343 {
344 Transform3D::new_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
345 }
346
347 #[inline]
350 pub fn is_scale_offset(&self) -> bool
351 where
352 T: Signed + One + PartialOrd + ApproxEq<T>,
353 {
354 self.is_scale_offset_eps(T::approx_epsilon())
355 }
356
357 #[inline]
360 pub fn is_scale_offset_eps(&self, epsilon: T) -> bool
361 where
362 T: Signed + One + PartialOrd,
363 {
364 (self.m12.abs() < epsilon) & (self.m21.abs() < epsilon)
365 }
366
367 #[inline]
373 pub fn to_scale_offset(&self) -> ScaleOffset2D<T, Src, Dst> {
374 ScaleOffset2D::new(self.m11, self.m22, self.m31, self.m32)
375 }
376}
377
378impl<T: NumCast + Copy, Src, Dst> Transform2D<T, Src, Dst> {
379 #[inline]
381 pub fn cast<NewT: NumCast>(&self) -> Transform2D<NewT, Src, Dst> {
382 self.try_cast().unwrap()
383 }
384
385 #[rustfmt::skip]
387 pub fn try_cast<NewT: NumCast>(&self) -> Option<Transform2D<NewT, Src, Dst>> {
388 match (NumCast::from(self.m11), NumCast::from(self.m12),
389 NumCast::from(self.m21), NumCast::from(self.m22),
390 NumCast::from(self.m31), NumCast::from(self.m32)) {
391 (Some(m11), Some(m12),
392 Some(m21), Some(m22),
393 Some(m31), Some(m32)) => {
394 Some(Transform2D::new(
395 m11, m12,
396 m21, m22,
397 m31, m32
398 ))
399 },
400 _ => None
401 }
402 }
403}
404
405impl<T, Src, Dst> Transform2D<T, Src, Dst>
406where
407 T: Zero + One,
408{
409 #[inline]
417 pub fn identity() -> Self {
418 Self::translation(T::zero(), T::zero())
419 }
420
421 fn is_identity(&self) -> bool
425 where
426 T: PartialEq,
427 {
428 *self == Self::identity()
429 }
430}
431
432impl<T, Src, Dst> Transform2D<T, Src, Dst>
434where
435 T: Copy + Add<Output = T> + Mul<Output = T>,
436{
437 #[must_use]
440 #[rustfmt::skip]
441 pub fn then<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> {
442 Transform2D::new(
443 self.m11 * mat.m11 + self.m12 * mat.m21,
444 self.m11 * mat.m12 + self.m12 * mat.m22,
445
446 self.m21 * mat.m11 + self.m22 * mat.m21,
447 self.m21 * mat.m12 + self.m22 * mat.m22,
448
449 self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
450 self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
451 )
452 }
453}
454
455impl<T, Src, Dst> Transform2D<T, Src, Dst>
457where
458 T: Zero + One,
459{
460 #[inline]
468 #[rustfmt::skip]
469 pub fn translation(x: T, y: T) -> Self {
470 let _0 = || T::zero();
471 let _1 = || T::one();
472
473 Self::new(
474 _1(), _0(),
475 _0(), _1(),
476 x, y,
477 )
478 }
479
480 #[inline]
482 #[must_use]
483 pub fn then_translate(&self, v: Vector2D<T, Dst>) -> Self
484 where
485 T: Copy + Add<Output = T> + Mul<Output = T>,
486 {
487 self.then(&Transform2D::translation(v.x, v.y))
488 }
489
490 #[inline]
492 #[must_use]
493 pub fn pre_translate(&self, v: Vector2D<T, Src>) -> Self
494 where
495 T: Copy + Add<Output = T> + Mul<Output = T>,
496 {
497 Transform2D::translation(v.x, v.y).then(self)
498 }
499}
500
501impl<T, Src, Dst> Transform2D<T, Src, Dst>
503where
504 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig,
505{
506 #[inline]
508 #[rustfmt::skip]
509 pub fn rotation(theta: Angle<T>) -> Self {
510 let _0 = Zero::zero();
511 let cos = theta.get().cos();
512 let sin = theta.get().sin();
513 Transform2D::new(
514 cos, sin,
515 _0 - sin, cos,
516 _0, _0
517 )
518 }
519
520 #[inline]
522 #[must_use]
523 pub fn then_rotate(&self, theta: Angle<T>) -> Self {
524 self.then(&Transform2D::rotation(theta))
525 }
526
527 #[inline]
529 #[must_use]
530 pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
531 Transform2D::rotation(theta).then(self)
532 }
533}
534
535impl<T, Src, Dst> Transform2D<T, Src, Dst> {
537 #[inline]
545 #[rustfmt::skip]
546 pub fn scale(x: T, y: T) -> Self
547 where
548 T: Zero,
549 {
550 let _0 = || Zero::zero();
551
552 Self::new(
553 x, _0(),
554 _0(), y,
555 _0(), _0(),
556 )
557 }
558
559 #[inline]
561 #[must_use]
562 pub fn then_scale(&self, x: T, y: T) -> Self
563 where
564 T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
565 {
566 self.then(&Transform2D::scale(x, y))
567 }
568
569 #[inline]
571 #[must_use]
572 #[rustfmt::skip]
573 pub fn pre_scale(&self, x: T, y: T) -> Self
574 where
575 T: Copy + Mul<Output = T>,
576 {
577 Transform2D::new(
578 self.m11 * x, self.m12 * x,
579 self.m21 * y, self.m22 * y,
580 self.m31, self.m32
581 )
582 }
583}
584
585impl<T, Src, Dst> Transform2D<T, Src, Dst>
587where
588 T: Copy + Add<Output = T> + Mul<Output = T>,
589{
590 #[inline]
592 #[must_use]
593 pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> {
594 Point2D::new(
595 point.x * self.m11 + point.y * self.m21 + self.m31,
596 point.x * self.m12 + point.y * self.m22 + self.m32,
597 )
598 }
599
600 #[inline]
602 #[must_use]
603 pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T, Dst> {
604 vec2(
605 vec.x * self.m11 + vec.y * self.m21,
606 vec.x * self.m12 + vec.y * self.m22,
607 )
608 }
609
610 #[inline]
613 #[must_use]
614 pub fn outer_transformed_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst>
615 where
616 T: Sub<Output = T> + Zero + PartialOrd,
617 {
618 let min = rect.min();
619 let max = rect.max();
620 Rect::from_points(&[
621 self.transform_point(min),
622 self.transform_point(max),
623 self.transform_point(point2(max.x, min.y)),
624 self.transform_point(point2(min.x, max.y)),
625 ])
626 }
627
628 #[inline]
631 #[must_use]
632 pub fn outer_transformed_box(&self, b: &Box2D<T, Src>) -> Box2D<T, Dst>
633 where
634 T: Sub<Output = T> + Zero + PartialOrd,
635 {
636 Box2D::from_points(&[
637 self.transform_point(b.min),
638 self.transform_point(b.max),
639 self.transform_point(point2(b.max.x, b.min.y)),
640 self.transform_point(point2(b.min.x, b.max.y)),
641 ])
642 }
643}
644
645impl<T, Src, Dst> Transform2D<T, Src, Dst>
646where
647 T: Copy + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + PartialEq + Zero + One,
648{
649 pub fn determinant(&self) -> T {
651 self.m11 * self.m22 - self.m12 * self.m21
652 }
653
654 #[inline]
656 pub fn is_invertible(&self) -> bool {
657 self.determinant() != Zero::zero()
658 }
659
660 #[must_use]
662 pub fn inverse(&self) -> Option<Transform2D<T, Dst, Src>> {
663 let det = self.determinant();
664
665 let _0: T = Zero::zero();
666 let _1: T = One::one();
667
668 if det == _0 {
669 return None;
670 }
671
672 let inv_det = _1 / det;
673 Some(Transform2D::new(
674 inv_det * self.m22,
675 inv_det * (_0 - self.m12),
676 inv_det * (_0 - self.m21),
677 inv_det * self.m11,
678 inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
679 inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
680 ))
681 }
682}
683
684impl<T, Src, Dst> Default for Transform2D<T, Src, Dst>
685where
686 T: Zero + One,
687{
688 fn default() -> Self {
690 Self::identity()
691 }
692}
693
694impl<T: ApproxEq<T>, Src, Dst> ApproxEq<T> for Transform2D<T, Src, Dst> {
695 #[inline]
696 fn approx_epsilon() -> T {
697 T::approx_epsilon()
698 }
699
700 fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
703 self.m11.approx_eq_eps(&other.m11, eps)
704 && self.m12.approx_eq_eps(&other.m12, eps)
705 && self.m21.approx_eq_eps(&other.m21, eps)
706 && self.m22.approx_eq_eps(&other.m22, eps)
707 && self.m31.approx_eq_eps(&other.m31, eps)
708 && self.m32.approx_eq_eps(&other.m32, eps)
709 }
710}
711
712impl<T, Src, Dst> fmt::Debug for Transform2D<T, Src, Dst>
713where
714 T: Copy + fmt::Debug + PartialEq + One + Zero,
715{
716 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
717 if self.is_identity() {
718 write!(f, "[I]")
719 } else {
720 self.to_array().fmt(f)
721 }
722 }
723}
724
725#[cfg(feature = "mint")]
726impl<T, Src, Dst> From<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> {
727 #[rustfmt::skip]
728 fn from(m: mint::RowMatrix3x2<T>) -> Self {
729 Transform2D {
730 m11: m.x.x, m12: m.x.y,
731 m21: m.y.x, m22: m.y.y,
732 m31: m.z.x, m32: m.z.y,
733 _unit: PhantomData,
734 }
735 }
736}
737#[cfg(feature = "mint")]
738impl<T, Src, Dst> From<Transform2D<T, Src, Dst>> for mint::RowMatrix3x2<T> {
739 fn from(t: Transform2D<T, Src, Dst>) -> Self {
740 mint::RowMatrix3x2 {
741 x: mint::Vector2 { x: t.m11, y: t.m12 },
742 y: mint::Vector2 { x: t.m21, y: t.m22 },
743 z: mint::Vector2 { x: t.m31, y: t.m32 },
744 }
745 }
746}
747
748impl<T, Src, Dst> From<Rotation2D<T, Src, Dst>> for Transform2D<T, Src, Dst>
749where
750 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig,
751{
752 fn from(r: Rotation2D<T, Src, Dst>) -> Self {
753 r.to_transform()
754 }
755}
756
757impl<T: Copy + Zero, Src, Dst> From<ScaleOffset2D<T, Src, Dst>> for Transform2D<T, Src, Dst> {
758 fn from(t: ScaleOffset2D<T, Src, Dst>) -> Self {
759 t.to_transform2d()
760 }
761}
762
763impl<T: Copy + Zero, Src, Dst> From<Scale<T, Src, Dst>> for Transform2D<T, Src, Dst> {
764 fn from(s: Scale<T, Src, Dst>) -> Self {
765 Transform2D::scale(s.0, s.0)
766 }
767}
768
769#[repr(C)]
770#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
771#[cfg_attr(
772 feature = "serde",
773 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
774)]
775pub struct ScaleOffset2D<T, Src, Dst> {
776 pub sx: T,
777 pub sy: T,
778 pub tx: T,
779 pub ty: T,
780 #[doc(hidden)]
781 pub _unit: PhantomData<(Src, Dst)>,
782}
783
784impl<T, Src, Dst> ScaleOffset2D<T, Src, Dst> {
785 #[inline]
787 pub fn identity() -> Self
788 where
789 T: Zero + One,
790 {
791 ScaleOffset2D {
792 sx: T::one(),
793 sy: T::one(),
794 tx: T::zero(),
795 ty: T::zero(),
796 _unit: PhantomData,
797 }
798 }
799
800 #[inline]
802 pub fn new(sx: T, sy: T, tx: T, ty: T) -> Self {
803 ScaleOffset2D {
804 sx,
805 sy,
806 tx,
807 ty,
808 _unit: PhantomData,
809 }
810 }
811
812 #[inline]
814 pub fn scale(sx: T, sy: T) -> Self
815 where
816 T: Zero,
817 {
818 ScaleOffset2D {
819 sx,
820 sy,
821 tx: T::zero(),
822 ty: T::zero(),
823 _unit: PhantomData,
824 }
825 }
826
827 #[inline]
829 pub fn offset(tx: T, ty: T) -> Self
830 where
831 T: One,
832 {
833 ScaleOffset2D {
834 sx: T::one(),
835 sy: T::one(),
836 tx,
837 ty,
838 _unit: PhantomData,
839 }
840 }
841
842 #[inline]
845 pub fn is_identity(&self) -> bool
846 where
847 T: One + Zero + ApproxEq<T>,
848 {
849 self.is_identity_eps(T::approx_epsilon())
850 }
851
852 #[inline]
854 pub fn is_identity_eps(&self, epsilon: T) -> bool
855 where
856 T: One + Zero + ApproxEq<T>,
857 {
858 self.sx.approx_eq_eps(&T::one(), &epsilon)
859 & self.sy.approx_eq_eps(&T::one(), &epsilon)
860 & self.tx.approx_eq_eps(&T::zero(), &epsilon)
861 & self.ty.approx_eq_eps(&T::zero(), &epsilon)
862 }
863}
864
865impl<T: Copy, Src, Dst> ScaleOffset2D<T, Src, Dst> {
866 #[inline]
868 pub fn transform_point(&self, p: Point2D<T, Src>) -> Point2D<T, Dst>
869 where
870 T: Add<Output = T> + Mul<Output = T>,
871 {
872 point2(p.x * self.sx + self.tx, p.y * self.sy + self.ty)
873 }
874
875 #[inline]
877 pub fn transform_vector(&self, v: Vector2D<T, Src>) -> Vector2D<T, Dst>
878 where
879 T: Mul<Output = T>,
880 {
881 vec2(v.x * self.sx, v.y * self.sy)
882 }
883
884 #[inline]
886 pub fn transform_box(&self, b: &Box2D<T, Src>) -> Box2D<T, Dst>
887 where
888 T: Zero + Add<Output = T> + Mul<Output = T> + PartialOrd,
889 {
890 let mut min = self.transform_point(b.min);
891 let mut max = self.transform_point(b.max);
892
893 if self.sx < T::zero() {
894 core::mem::swap(&mut min.x, &mut max.x);
895 }
896 if self.sy < T::zero() {
897 core::mem::swap(&mut min.y, &mut max.y);
898 }
899
900 Box2D { min, max }
901 }
902
903 #[inline]
905 pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T, Dst>
906 where
907 T: Zero + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + PartialOrd,
908 {
909 self.transform_box(&r.to_box2d()).to_rect()
910 }
911
912 #[inline]
917 pub fn then<NewDst>(
918 &self,
919 other: &ScaleOffset2D<T, Dst, NewDst>,
920 ) -> ScaleOffset2D<T, Src, NewDst>
921 where
922 T: Add<Output = T> + Mul<Output = T>,
923 {
924 ScaleOffset2D {
925 sx: self.sx * other.sx,
926 sy: self.sy * other.sy,
927 tx: self.tx * other.sx + other.tx,
928 ty: self.ty * other.sy + other.ty,
929 _unit: PhantomData,
930 }
931 }
932
933 #[inline]
935 #[must_use]
936 pub fn pre_translate(&self, v: Vector2D<T, Src>) -> Self
937 where
938 T: Add<Output = T> + Mul<Output = T>,
939 {
940 ScaleOffset2D {
941 sx: self.sx,
942 sy: self.sy,
943 tx: self.tx + v.x * self.sx,
944 ty: self.ty + v.y * self.sy,
945 _unit: PhantomData,
946 }
947 }
948
949 #[inline]
951 pub fn then_translate(&self, v: Vector2D<T, Dst>) -> Self
952 where
953 T: Add<Output = T> + Mul<Output = T>,
954 {
955 ScaleOffset2D {
956 sx: self.sx,
957 sy: self.sy,
958 tx: self.tx + v.x,
959 ty: self.ty + v.y,
960 _unit: PhantomData,
961 }
962 }
963
964 #[inline]
966 pub fn pre_scale(&self, sx: T, sy: T) -> Self
967 where
968 T: Add<Output = T> + Mul<Output = T>,
969 {
970 ScaleOffset2D {
971 sx: self.sx * sx,
972 sy: self.sy * sy,
973 tx: self.tx,
974 ty: self.ty,
975 _unit: PhantomData,
976 }
977 }
978
979 #[inline]
981 pub fn then_scale(&self, sx: T, sy: T) -> Self
982 where
983 T: Add<Output = T> + Mul<Output = T>,
984 {
985 ScaleOffset2D {
986 sx: self.sx * sx,
987 sy: self.sy * sy,
988 tx: self.tx * sx,
989 ty: self.ty * sy,
990 _unit: PhantomData,
991 }
992 }
993
994 #[inline]
996 pub fn is_invertible(&self) -> bool
997 where
998 T: Zero + PartialEq,
999 {
1000 !(self.sx == T::zero() || self.sy == T::zero())
1004 }
1005
1006 #[must_use]
1008 pub fn inverse(&self) -> Option<ScaleOffset2D<T, Dst, Src>>
1009 where
1010 T: Zero + One + PartialEq + Div<Output = T> + Mul<Output = T> + Neg<Output = T>,
1011 {
1012 if self.sx == T::zero() || self.sy == T::zero() {
1013 return None;
1014 }
1015
1016 let sx = T::one() / self.sx;
1017 let sy = T::one() / self.sy;
1018 let tx = -self.tx * sx;
1019 let ty = -self.ty * sy;
1020
1021 Some(ScaleOffset2D::new(sx, sy, tx, ty))
1022 }
1023
1024 #[inline]
1026 pub fn to_transform2d(&self) -> Transform2D<T, Src, Dst>
1027 where
1028 T: Zero,
1029 {
1030 Transform2D::new(self.sx, T::zero(), T::zero(), self.sy, self.tx, self.ty)
1031 }
1032
1033 #[inline]
1035 #[rustfmt::skip]
1036 pub fn to_transform3d(&self) -> Transform3D<T, Src, Dst>
1037 where
1038 T: Zero + One
1039 {
1040 Transform3D::new(
1041 self.sx, T::zero(), T::zero(), T::zero(),
1042 T::zero(), self.sy, T::zero(), T::zero(),
1043 T::zero(), T::zero(), T::one(), T::zero(),
1044 self.tx, self.ty, T::zero(), T::one(),
1045 )
1046 }
1047
1048 #[inline]
1053 pub fn to_array(&self) -> [T; 4] {
1054 [self.sx, self.sy, self.tx, self.ty]
1055 }
1056
1057 #[inline]
1059 pub fn with_source<NewSrc>(&self) -> ScaleOffset2D<T, NewSrc, Dst> {
1060 ScaleOffset2D::new(self.sx, self.sy, self.tx, self.ty)
1061 }
1062
1063 #[inline]
1065 pub fn with_destination<NewDst>(&self) -> ScaleOffset2D<T, Src, NewDst> {
1066 ScaleOffset2D::new(self.sx, self.sy, self.tx, self.ty)
1067 }
1068
1069 #[inline]
1071 pub fn to_untyped(&self) -> ScaleOffset2D<T, UnknownUnit, UnknownUnit> {
1072 ScaleOffset2D::new(self.sx, self.sy, self.tx, self.ty)
1073 }
1074
1075 #[inline]
1077 pub fn from_untyped(val: ScaleOffset2D<T, UnknownUnit, UnknownUnit>) -> Self {
1078 ScaleOffset2D::new(val.sx, val.sy, val.tx, val.ty)
1079 }
1080}
1081
1082impl<T: Copy, Src, Dst> Copy for ScaleOffset2D<T, Src, Dst> {}
1083
1084impl<T: Clone, Src, Dst> Clone for ScaleOffset2D<T, Src, Dst> {
1085 fn clone(&self) -> Self {
1086 ScaleOffset2D {
1087 sx: self.sx.clone(),
1088 sy: self.sy.clone(),
1089 tx: self.tx.clone(),
1090 ty: self.ty.clone(),
1091 _unit: PhantomData,
1092 }
1093 }
1094}
1095
1096impl<T, Src, Dst> Eq for ScaleOffset2D<T, Src, Dst> where T: Eq {}
1097
1098impl<T, Src, Dst> PartialEq for ScaleOffset2D<T, Src, Dst>
1099where
1100 T: PartialEq,
1101{
1102 fn eq(&self, other: &Self) -> bool {
1103 (self.sx == other.sx)
1104 & (self.sy == other.sy)
1105 & (self.tx == other.tx)
1106 & (self.ty == other.ty)
1107 }
1108}
1109
1110impl<T, Src, Dst> Hash for ScaleOffset2D<T, Src, Dst>
1111where
1112 T: Hash,
1113{
1114 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
1115 self.sx.hash(h);
1116 self.sy.hash(h);
1117 self.tx.hash(h);
1118 self.ty.hash(h);
1119 }
1120}
1121
1122impl<T, Src, Dst> Default for ScaleOffset2D<T, Src, Dst>
1123where
1124 T: Copy + Zero + One,
1125{
1126 fn default() -> Self {
1128 Self::identity()
1129 }
1130}
1131
1132impl<T: ApproxEq<T>, Src, Dst> ApproxEq<T> for ScaleOffset2D<T, Src, Dst> {
1133 #[inline]
1134 fn approx_epsilon() -> T {
1135 T::approx_epsilon()
1136 }
1137
1138 fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
1141 self.sx.approx_eq_eps(&other.sx, eps)
1142 && self.sy.approx_eq_eps(&other.sy, eps)
1143 && self.tx.approx_eq_eps(&other.tx, eps)
1144 && self.ty.approx_eq_eps(&other.ty, eps)
1145 }
1146}
1147
1148impl<T, Src, Dst> fmt::Debug for ScaleOffset2D<T, Src, Dst>
1149where
1150 T: Copy + fmt::Debug + PartialEq + One + Zero + ApproxEq<T>,
1151{
1152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1153 if self.is_identity() {
1154 write!(f, "[I]")
1155 } else {
1156 self.to_array().fmt(f)
1157 }
1158 }
1159}
1160
1161impl<T: Copy + Zero, Src, Dst> From<Scale<T, Src, Dst>> for ScaleOffset2D<T, Src, Dst> {
1162 fn from(s: Scale<T, Src, Dst>) -> Self {
1163 ScaleOffset2D::scale(s.0, s.0)
1164 }
1165}
1166
1167impl<T: Copy + One, Src, Dst> From<Translation2D<T, Src, Dst>> for ScaleOffset2D<T, Src, Dst> {
1168 fn from(t: Translation2D<T, Src, Dst>) -> Self {
1169 ScaleOffset2D::offset(t.x, t.y)
1170 }
1171}
1172
1173#[cfg(feature = "arbitrary")]
1174impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for ScaleOffset2D<T, Src, Dst>
1175where
1176 T: arbitrary::Arbitrary<'a>,
1177{
1178 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1179 let (sx, sy, tx, ty) = arbitrary::Arbitrary::arbitrary(u)?;
1180 Ok(ScaleOffset2D::new(sx, sy, tx, ty))
1181 }
1182}
1183
1184#[cfg(feature = "bytemuck")]
1185unsafe impl<T: Zeroable, Src, Dst> Zeroable for ScaleOffset2D<T, Src, Dst> {}
1186
1187#[cfg(feature = "bytemuck")]
1188unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for ScaleOffset2D<T, Src, Dst> {}
1189
1190#[cfg(feature = "malloc_size_of")]
1191impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for ScaleOffset2D<T, Src, Dst> {
1192 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
1193 self.sx.size_of(ops) + self.sy.size_of(ops) + self.tx.size_of(ops) + self.ty.size_of(ops)
1194 }
1195}
1196
1197#[cfg(test)]
1198mod test {
1199 use super::*;
1200 use crate::approxeq::ApproxEq;
1201 use crate::default;
1202 #[cfg(feature = "mint")]
1203 use mint;
1204 type ScaleOffset = crate::default::ScaleOffset2D<f32>;
1205 type IntScaleOffset = crate::default::ScaleOffset2D<i32>;
1206
1207 use core::f32::consts::FRAC_PI_2;
1208
1209 type Mat = default::Transform2D<f32>;
1210
1211 fn rad(v: f32) -> Angle<f32> {
1212 Angle::radians(v)
1213 }
1214
1215 #[test]
1216 pub fn test_translation() {
1217 let t1 = Mat::translation(1.0, 2.0);
1218 let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
1219 let t3 = Mat::identity().then_translate(vec2(1.0, 2.0));
1220 assert_eq!(t1, t2);
1221 assert_eq!(t1, t3);
1222
1223 assert_eq!(
1224 t1.transform_point(Point2D::new(1.0, 1.0)),
1225 Point2D::new(2.0, 3.0)
1226 );
1227
1228 assert_eq!(t1.then(&t1), Mat::translation(2.0, 4.0));
1229 }
1230
1231 #[test]
1232 pub fn test_rotation() {
1233 let r1 = Mat::rotation(rad(FRAC_PI_2));
1234 let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
1235 let r3 = Mat::identity().then_rotate(rad(FRAC_PI_2));
1236 assert_eq!(r1, r2);
1237 assert_eq!(r1, r3);
1238
1239 assert!(r1
1240 .transform_point(Point2D::new(1.0, 2.0))
1241 .approx_eq(&Point2D::new(-2.0, 1.0)));
1242
1243 assert!(r1.then(&r1).approx_eq(&Mat::rotation(rad(FRAC_PI_2 * 2.0))));
1244 }
1245
1246 #[test]
1247 pub fn test_scale() {
1248 let s1 = Mat::scale(2.0, 3.0);
1249 let s2 = Mat::identity().pre_scale(2.0, 3.0);
1250 let s3 = Mat::identity().then_scale(2.0, 3.0);
1251 assert_eq!(s1, s2);
1252 assert_eq!(s1, s3);
1253
1254 assert!(s1
1255 .transform_point(Point2D::new(2.0, 2.0))
1256 .approx_eq(&Point2D::new(4.0, 6.0)));
1257 }
1258
1259 #[test]
1260 pub fn test_pre_then_scale() {
1261 let m = Mat::rotation(rad(FRAC_PI_2)).then_translate(vec2(6.0, 7.0));
1262 let s = Mat::scale(2.0, 3.0);
1263 assert_eq!(m.then(&s), m.then_scale(2.0, 3.0));
1264 }
1265
1266 #[test]
1267 pub fn test_inverse_simple() {
1268 let m1 = Mat::identity();
1269 let m2 = m1.inverse().unwrap();
1270 assert!(m1.approx_eq(&m2));
1271 }
1272
1273 #[test]
1274 pub fn test_inverse_scale() {
1275 let m1 = Mat::scale(1.5, 0.3);
1276 let m2 = m1.inverse().unwrap();
1277 assert!(m1.then(&m2).approx_eq(&Mat::identity()));
1278 assert!(m2.then(&m1).approx_eq(&Mat::identity()));
1279 }
1280
1281 #[test]
1282 pub fn test_inverse_translate() {
1283 let m1 = Mat::translation(-132.0, 0.3);
1284 let m2 = m1.inverse().unwrap();
1285 assert!(m1.then(&m2).approx_eq(&Mat::identity()));
1286 assert!(m2.then(&m1).approx_eq(&Mat::identity()));
1287 }
1288
1289 #[test]
1290 fn test_inverse_none() {
1291 assert!(Mat::scale(2.0, 0.0).inverse().is_none());
1292 assert!(Mat::scale(2.0, 2.0).inverse().is_some());
1293 }
1294
1295 #[test]
1296 pub fn test_pre_post() {
1297 let m1 = default::Transform2D::identity()
1298 .then_scale(1.0, 2.0)
1299 .then_translate(vec2(1.0, 2.0));
1300 let m2 = default::Transform2D::identity()
1301 .pre_translate(vec2(1.0, 2.0))
1302 .pre_scale(1.0, 2.0);
1303 assert!(m1.approx_eq(&m2));
1304
1305 let r = Mat::rotation(rad(FRAC_PI_2));
1306 let t = Mat::translation(2.0, 3.0);
1307
1308 let a = Point2D::new(1.0, 1.0);
1309
1310 assert!(r
1311 .then(&t)
1312 .transform_point(a)
1313 .approx_eq(&Point2D::new(1.0, 4.0)));
1314 assert!(t
1315 .then(&r)
1316 .transform_point(a)
1317 .approx_eq(&Point2D::new(-4.0, 3.0)));
1318 assert!(t
1319 .then(&r)
1320 .transform_point(a)
1321 .approx_eq(&r.transform_point(t.transform_point(a))));
1322 }
1323
1324 #[test]
1325 fn test_size_of() {
1326 use core::mem::size_of;
1327 assert_eq!(size_of::<default::Transform2D<f32>>(), 6 * size_of::<f32>());
1328 assert_eq!(size_of::<default::Transform2D<f64>>(), 6 * size_of::<f64>());
1329 }
1330
1331 #[test]
1332 pub fn test_is_identity() {
1333 let m1 = default::Transform2D::identity();
1334 assert!(m1.is_identity());
1335 let m2 = m1.then_translate(vec2(0.1, 0.0));
1336 assert!(!m2.is_identity());
1337 }
1338
1339 #[test]
1340 pub fn test_transform_vector() {
1341 let m1 = Mat::translation(1.0, 1.0);
1343 let v1 = vec2(10.0, -10.0);
1344 assert_eq!(v1, m1.transform_vector(v1));
1345 }
1346
1347 #[cfg(feature = "mint")]
1348 #[test]
1349 pub fn test_mint() {
1350 let m1 = Mat::rotation(rad(FRAC_PI_2));
1351 let mm: mint::RowMatrix3x2<_> = m1.into();
1352 let m2 = Mat::from(mm);
1353
1354 assert_eq!(m1, m2);
1355 }
1356
1357 #[test]
1358 pub fn scale_offset_transform_point() {
1359 let t1 = IntScaleOffset::new(2, 3, 4, 5);
1360
1361 let p = point2(6, 7);
1362
1363 assert_eq!(
1364 t1.transform_point(p),
1365 t1.to_transform2d().transform_point(p),
1366 );
1367 }
1368
1369 #[test]
1370 pub fn scale_offset_transform_vector() {
1371 let t1 = IntScaleOffset::new(2, 3, 4, 5);
1372
1373 let v = vec2(6, 7);
1374
1375 assert_eq!(
1376 t1.transform_vector(v),
1377 t1.to_transform2d().transform_vector(v),
1378 );
1379 }
1380
1381 #[test]
1382 pub fn scale_offset_transform_box() {
1383 let transforms = [
1384 ScaleOffset::identity(),
1385 ScaleOffset::new(2.0, 3.0, 4.0, 5.0),
1386 ScaleOffset::new(-2.0, 3.0, 4.0, -5.0),
1387 ScaleOffset::new(2.0, -3.0, 4.0, -5.0),
1388 ScaleOffset::new(-2.0, -3.0, -4.0, 5.0),
1389 ];
1390
1391 let boxes = [
1392 default::Box2D {
1393 min: point2(0.0, 0.0),
1394 max: point2(1.0, 1.0),
1395 },
1396 Box2D {
1397 min: point2(-10.0, -10.0),
1398 max: point2(10.0, 10.0),
1399 },
1400 Box2D {
1401 min: point2(100.0, 130.0),
1402 max: point2(150.0, 160.0),
1403 },
1404 Box2D {
1405 min: point2(-100.0, -130.0),
1406 max: point2(-50.0, 160.0),
1407 },
1408 Box2D {
1409 min: point2(0.0, 0.0),
1410 max: point2(0.0, 0.0),
1411 },
1412 Box2D {
1413 min: point2(1.0, 0.0),
1414 max: point2(-1.0, 1.0),
1415 },
1416 Box2D {
1417 min: point2(1.0, 1.0),
1418 max: point2(1.0, -1.0),
1419 },
1420 Box2D {
1421 min: point2(1.0, 1.0),
1422 max: point2(-1.0, -1.0),
1423 },
1424 ];
1425
1426 for b in &boxes {
1427 for transform in &transforms {
1428 let b2 = transform.transform_box(b);
1429 assert_eq!(
1430 b2.is_empty(),
1431 b.is_empty(),
1432 "transform: {transform:?}, box: {b:?}, empty before: {:?} after {:?}",
1433 b.is_empty(),
1434 b2.is_empty()
1435 );
1436
1437 let mat = transform.to_transform2d();
1438 if !b.is_empty() {
1441 assert_eq!(b2, mat.outer_transformed_box(b));
1442 }
1443 }
1444 }
1445 }
1446
1447 #[test]
1448 pub fn scale_offset_then() {
1449 let t1 = IntScaleOffset::new(2, 3, 4, 5);
1450 let t2 = IntScaleOffset::new(6, 7, 8, 9);
1451
1452 let t3 = t1.then(&t2).to_transform2d();
1453 let t3_tx = t1.to_transform2d().then(&t2.to_transform2d());
1454
1455 assert_eq!(t3, t3_tx);
1456 }
1457
1458 #[test]
1459 pub fn scale_offset_pre_post() {
1460 let t1 = ScaleOffset::new(2.0, 3.0, 4.0, 5.0);
1461 let v = vec2(100.0, 200.0);
1462 assert!(t1
1463 .then_translate(v)
1464 .to_transform2d()
1465 .approx_eq(&t1.to_transform2d().then_translate(v)));
1466 assert!(t1
1467 .pre_translate(v)
1468 .to_transform2d()
1469 .approx_eq(&t1.to_transform2d().pre_translate(v)));
1470 let sx = 100.0;
1471 let sy = 120.0;
1472 assert!(t1
1473 .then_scale(sx, sy)
1474 .to_transform2d()
1475 .approx_eq(&t1.to_transform2d().then_scale(sx, sy)));
1476 assert!(t1
1477 .pre_scale(sx, sy)
1478 .to_transform2d()
1479 .approx_eq(&t1.to_transform2d().pre_scale(sx, sy)));
1480 }
1481
1482 #[test]
1483 pub fn scale_offset_inverse() {
1484 let t1 = ScaleOffset::new(2.0, 3.0, 4.0, 5.0);
1485
1486 assert_eq!(
1487 t1.inverse().unwrap().to_transform2d(),
1488 t1.to_transform2d().inverse().unwrap(),
1489 )
1490 }
1491
1492 #[test]
1493 pub fn scale_offset_identity() {
1494 assert!(ScaleOffset::identity().is_identity())
1495 }
1496}