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