iron_shapes/
transform.rs

1// Copyright (c) 2018-2020 Thomas Kramer.
2// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Transforms are used to describe the location, rotation, scaling and mirroring
7//! of geometric shapes.
8
9use crate::CoordinateType;
10
11use crate::matrix2d::*;
12use crate::point::Point;
13use crate::traits::{Mirror, RotateOrtho, Scale, Translate, TryCastCoord};
14use crate::types::Angle;
15use crate::vector::Vector;
16
17use crate::matrix3d::Matrix3d;
18use crate::types::FloatType;
19use num_traits::{Float, NumCast, One, Zero};
20use std::ops::Mul;
21
22/// General geometric transformation.
23pub trait Transformation {
24    /// Coordinate type of input points.
25    type SourceCoord;
26    /// Coordinate type of output points.
27    type DestinationCoord;
28
29    /// Apply the transform to a point.
30    fn transform_point(&self, p: Point<Self::SourceCoord>) -> Point<Self::DestinationCoord>;
31}
32
33/// Geometric transformation which preserves parallelism.
34/// Adds 'shear' to the [`SimilarityTransform`].
35pub trait AffineTransform: Transformation {
36    /// Get the both eigen-vectors which characterize the 'shear'.
37    /// Eigen-vectors are the vectors whose direction is not changed when applying the transformation.
38    fn eigen_vectors(&self) -> (Vector<Self::SourceCoord>, Vector<Self::SourceCoord>);
39
40    // fn eigen_values(&self) -> (); // TODO
41}
42
43/// Geometric transformation which preserves angles and ratios of distances.
44/// Adds resizing to the [`IsometricTransform`].
45pub trait SimilarityTransform: AffineTransform {
46    /// Transform a distance.
47    fn transform_distance(&self, distance: Self::SourceCoord) -> Self::DestinationCoord;
48}
49
50/// Geometric transformation which preserves angles and ratios of distances.
51/// Adds resizing by integer numbers to the [`IsometricTransform90`].
52pub trait SimilarityTransform90:
53    SimilarityTransform<SourceCoord = Self::Coord, DestinationCoord = Self::Coord>
54{
55    /// Type or source and destination coordinates.
56    type Coord;
57    // TODO
58}
59
60/// Geometric transformation which preserves angles and distances (e.g. euclidean transform).
61pub trait IsometricTransform: SimilarityTransform {
62    // TODO
63}
64
65/// Geometric transformation which preserves angles and distances (e.g. euclidean transform) but
66/// allows only rotations by a multiple of 90 degrees.
67pub trait IsometricTransform90: IsometricTransform + SimilarityTransform90 {
68    // TODO
69
70    /// Return the rotation by a multiple of 90 degrees.
71    fn rotation(&self) -> Angle;
72}
73
74/// Geometric transformation which preserves oriented angles and distances (i.e. translation).
75pub trait DisplacementTransform: IsometricTransform90 {
76    /// Get the displacement vector.
77    fn displacement(&self) -> Vector<Self::SourceCoord>;
78}
79
80// // Blanket implementation.
81// impl<Tf: DisplacementTransform> IsometricRTransform for Tf {
82//     fn rotation(&self) -> Angle {
83//         Angle::R0
84//     }
85// }
86//
87// /// A geometric displacement transformation.
88// pub struct Displacement<T> {
89//     disp: Vector<T>
90// }
91//
92// impl<T: Copy> DisplacementTransform<Coord=T> for Displacement<T> {
93//     fn displacement(&self) -> Vector<Self::SourceCoord> {
94//         *self.disp
95//     }
96// }
97
98/// Description of a transformation in the euclidean plane by a 2x2 matrix `A`.
99/// Transforming a point `p` is computed by the matrix product `A*p`.
100#[derive(Clone, Hash, PartialEq, Eq, Debug)]
101#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
102pub struct Matrix2dTransform<T: CoordinateType> {
103    matrix: Matrix2d<T>,
104}
105
106impl<T: CoordinateType> Matrix2dTransform<T> {
107    /// Create a new transform based on a matrix.
108    pub fn new(matrix: Matrix2d<T>) -> Self {
109        Matrix2dTransform { matrix }
110    }
111
112    /// Create a rotation by an integer multiple of 90 degrees.
113    pub fn new_rotation90(angle: Angle) -> Self {
114        let zero = T::zero();
115        let one = T::one();
116        let minus_one = zero - one;
117
118        let matrix = match angle {
119            Angle::R0 => Matrix2d::new(one, zero, zero, one),
120            Angle::R90 => Matrix2d::new(zero, minus_one, one, zero),
121            Angle::R180 => Matrix2d::new(minus_one, zero, zero, minus_one),
122            Angle::R270 => Matrix2d::new(zero, one, minus_one, zero),
123        };
124
125        Matrix2dTransform::new(matrix)
126    }
127
128    /// Create a scaling by a factor.
129    pub fn new_scaling(factor: T) -> Self {
130        let zero = T::zero();
131        Matrix2dTransform::new(Matrix2d::new(factor, zero, zero, factor))
132    }
133
134    /// Mirror at the x-axis.
135    pub fn new_mirror_x() -> Self {
136        let zero = T::zero();
137        let one = T::one();
138        let minus_one = zero - one;
139        Matrix2dTransform::new(Matrix2d::new(minus_one, zero, zero, one))
140    }
141
142    /// Mirror at the y-axis.
143    pub fn new_mirror_y() -> Self {
144        let zero = T::zero();
145        let one = T::one();
146        let minus_one = zero - one;
147        Matrix2dTransform::new(Matrix2d::new(one, zero, zero, minus_one))
148    }
149
150    // pub fn is_unitary(&self) -> bool {
151    //     // TODO
152    //     true
153    // }
154
155    /// Apply the transformation to a single point.
156    pub fn transform_point(&self, p: Point<T>) -> Point<T> {
157        self.matrix.mul_column_vector(p.into()).into()
158    }
159
160    /// Return the matrix describing this transformation.
161    pub fn to_matrix2d(&self) -> Matrix2d<T> {
162        self.matrix.clone()
163    }
164
165    /// Get the inverse transformation.
166    pub fn try_invert(&self) -> Option<Self> {
167        self.matrix.try_inverse().map(|inv| Self { matrix: inv })
168    }
169}
170
171#[test]
172fn test_matrix_transform_rotations() {
173    let p = Point::new(1, 0);
174
175    assert_eq!(
176        Matrix2dTransform::new_rotation90(Angle::R0).transform_point(p),
177        p
178    );
179    assert_eq!(
180        Matrix2dTransform::new_rotation90(Angle::R90).transform_point(p),
181        Point::new(0, 1)
182    );
183    assert_eq!(
184        Matrix2dTransform::new_rotation90(Angle::R180).transform_point(p),
185        Point::new(-1, 0)
186    );
187    assert_eq!(
188        Matrix2dTransform::new_rotation90(Angle::R270).transform_point(p),
189        Point::new(0, -1)
190    );
191}
192
193/// Transformation that consists only of a rotation by a multiple of 90 degrees
194/// around the origin `(0, 0)`.
195#[derive(Clone, Hash, PartialEq, Eq, Debug)]
196#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
197pub struct Rot90Transform {
198    angle: Angle,
199}
200
201impl Rot90Transform {
202    /// Create a new rotation transformation.
203    pub fn new(angle: Angle) -> Self {
204        Rot90Transform { angle }
205    }
206    /// This transformation is always unitary. Returns always `true`.
207    pub fn is_unitary(&self) -> bool {
208        true
209    }
210    /// Apply the transformation to a single point.
211    pub fn transform_point<T: CoordinateType>(&self, p: Point<T>) -> Point<T> {
212        p.rotate_ortho(self.angle)
213    }
214
215    /// Get the magnification of this transformation. Always `1`.
216    pub fn magnification<T: CoordinateType>(&self) -> T {
217        T::one()
218    }
219
220    /// Get the magnification of this transformation. Always `Some(1)`.
221    pub fn try_magnification<T: CoordinateType>(&self) -> Option<T> {
222        Some(self.magnification())
223    }
224}
225
226/// Describes a geometric transformation that consists of a optional mirroring along the x-axis
227/// followed by a rotation by a multiple of 90 degrees
228/// followed by a displacement.
229#[derive(Copy, Clone, PartialEq, Eq, Debug)]
230#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
231pub struct SimpleTransform<T> {
232    /// Mirror on the x-axis?
233    pub mirror: bool,
234    /// Rotation by a multiple of 90 degrees.
235    pub rotation: Angle,
236    /// Enlargement.
237    pub magnification: T,
238    /// Translation.
239    pub displacement: Vector<T>,
240}
241
242impl<T: CoordinateType> Default for SimpleTransform<T> {
243    fn default() -> Self {
244        SimpleTransform::identity()
245    }
246}
247
248impl<T> SimpleTransform<T> {
249    /// Create a new transformation.
250    pub fn new(mirror: bool, rotation: Angle, magnification: T, displacement: Vector<T>) -> Self {
251        SimpleTransform {
252            mirror,
253            rotation,
254            magnification,
255            displacement,
256        }
257    }
258}
259
260impl<T: Zero + One> SimpleTransform<T> {
261    /// Get the identity transform.
262    pub fn identity() -> Self {
263        Self::translate(Vector::zero())
264    }
265
266    /// Create a translation by a vector.
267    pub fn translate<V: Into<Vector<T>>>(v: V) -> Self {
268        let t = v.into();
269        Self::new(false, Angle::R0, T::one(), t)
270    }
271
272    /// Create a rotation by an integer multiple of 90 degrees.
273    /// Rotation center is (0, 0).
274    pub fn rotate90(angle: Angle) -> Self {
275        Self::new(false, angle, T::one(), Vector::zero())
276    }
277
278    /// Rotate 90 degrees counter-clock wise.
279    pub fn rotate_ccw90() -> Self {
280        Self::rotate90(Angle::R90)
281    }
282
283    /// Rotate 90 degrees counter-clock wise.
284    pub fn rotate_cw90() -> Self {
285        Self::rotate90(Angle::R270)
286    }
287
288    /// Create a transformation that mirrors at the x-axis.
289    pub fn mirror_x() -> Self {
290        Self::new(true, Angle::R0, T::one(), Vector::zero())
291    }
292
293    /// Create a transformation that mirrors at the y-axis.
294    pub fn mirror_y() -> Self {
295        Self::new(true, Angle::R180, T::one(), Vector::zero())
296    }
297
298    /// Create a scaling by a factor.
299    pub fn scale(factor: T) -> Self {
300        Self::new(false, Angle::R0, factor, Vector::zero())
301    }
302}
303
304impl<T> SimpleTransform<T>
305where
306    T: Copy + Mul<Output = T>,
307{
308    /// Transform a distance.
309    pub fn transform_distance(&self, d: T) -> T {
310        d * self.magnification
311    }
312}
313
314impl<T: CoordinateType> SimpleTransform<T> {
315    /// Create a rotation arount `rotation_center` by an integer multiple of 90 degrees.
316    pub fn rotate90_around(angle: Angle, rotation_center: Point<T>) -> Self {
317        Self::translate(Point::zero() - rotation_center)
318            .then(&Self::rotate90(angle))
319            .then(&Self::translate(rotation_center))
320    }
321
322    /// Transform a single point.
323    pub fn transform_point(&self, p: Point<T>) -> Point<T> {
324        if self.mirror { p.mirror_x() } else { p }
325            .rotate_ortho(self.rotation)
326            .scale(self.magnification)
327            .translate(self.displacement)
328    }
329
330    /// Inverse-transform a single point.
331    pub fn inverse_transform_point(&self, p: Point<T>) -> Point<T> {
332        let p = p
333            .translate(Vector::zero() - self.displacement)
334            .scale(T::one() / self.magnification)
335            .rotate_ortho(-self.rotation);
336
337        if self.mirror {
338            p.mirror_x()
339        } else {
340            p
341        }
342    }
343
344    /// Convert to a matrix transformation.
345    pub fn to_matrix_transform(&self) -> Matrix3dTransform<T> {
346        if self.mirror {
347            Matrix3dTransform::mirror_x()
348        } else {
349            Matrix3dTransform::identity()
350        }
351        .then_rotate90(self.rotation)
352        .then_scale(self.magnification)
353        .then_translate(self.displacement)
354    }
355
356    /// Return a new transformation that is equal to applying
357    /// first `self` then `t`.
358    pub fn then(&self, t: &Self) -> Self {
359        let d = t.transform_point(self.displacement.into());
360        let r = if t.mirror {
361            -self.rotation
362        } else {
363            self.rotation
364        };
365        Self {
366            mirror: self.mirror ^ t.mirror,
367            rotation: r + t.rotation,
368            magnification: self.magnification * t.magnification,
369            displacement: d.v(),
370        }
371    }
372}
373
374#[test]
375fn test_simple_transform_combine() {
376    let t1 = SimpleTransform::new(false, Angle::R90, 1, (1, 2).into());
377    let t2 = SimpleTransform::new(true, Angle::R90, 1, (3, 4).into());
378
379    let p = Point::new(10, 11);
380    assert_eq!(
381        t2.transform_point(t1.transform_point(p)),
382        t1.then(&t2).transform_point(p)
383    );
384    assert_eq!(
385        t1.transform_point(t2.transform_point(p)),
386        t2.then(&t1).transform_point(p)
387    );
388}
389
390/// Transformation described by a mirroring at the `x` axis,
391/// then a rotation around the origin, then a scaling, then a translation.
392/// This transformation allows rotations by arbitrary angles.
393#[derive(Clone, PartialEq, Debug)]
394#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
395pub struct ComplexTransform<T: CoordinateType> {
396    /// Mirror at `x`-axis?
397    mirror: bool,
398    /// Rotation by an arbitrary angle (radians).
399    rotation: FloatType,
400    /// Scaling.
401    magnification: FloatType,
402    /// Translation.
403    displacement: Vector<T>,
404}
405
406/// Affine transformation represented as a 3x3 matrix like:
407/// ```txt
408/// m11 m12 0
409/// m21 m22 0
410/// m31 m32 1
411/// ```
412#[derive(Clone, Hash, PartialEq, Eq, Debug)]
413#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
414pub struct Matrix3dTransform<T: CoordinateType> {
415    /// m11
416    pub m11: T,
417    /// m12
418    pub m12: T,
419    /// m21
420    pub m21: T,
421    /// m22
422    pub m22: T,
423    /// m31. Used to express the `x` component of the translation.
424    pub m31: T,
425    /// m32. Used to express the `y` component of the translation.
426    pub m32: T,
427}
428
429impl<T: CoordinateType> Matrix3dTransform<T> {
430    /// Create a new transform based on the matrix elements.
431    pub fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
432        Matrix3dTransform {
433            m11,
434            m12,
435            m21,
436            m22,
437            m31,
438            m32,
439        }
440    }
441
442    /// Get the identity transform.
443    pub fn identity() -> Self {
444        Self::translate(Vector::zero())
445    }
446
447    /// Create a translation by a vector.
448    pub fn translate<V: Into<Vector<T>>>(v: V) -> Self {
449        let t = v.into();
450        Self::new(T::one(), T::zero(), T::zero(), T::one(), t.x, t.y)
451    }
452
453    /// Create a rotation by an integer multiple of 90 degrees.
454    pub fn rotate90(angle: Angle) -> Self {
455        let zero = T::zero();
456        let one = T::one();
457        let minus_one = zero - one;
458
459        let (a, b, c, d) = match angle {
460            Angle::R0 => (one, zero, zero, one),
461            Angle::R90 => (zero, one, minus_one, zero),
462            Angle::R180 => (minus_one, zero, zero, minus_one),
463            Angle::R270 => (zero, minus_one, one, zero),
464        };
465
466        Self::new(a, b, c, d, T::zero(), T::zero())
467    }
468
469    /// Create a scaling by a factor.
470    pub fn scale(factor: T) -> Self {
471        let zero = T::zero();
472        Self::new(factor, zero, zero, factor, zero, zero)
473    }
474
475    /// Mirror at the x-axis.
476    pub fn mirror_x() -> Self {
477        let zero = T::zero();
478        let one = T::one();
479        let minus_one = zero - one;
480        Self::new(minus_one, zero, zero, one, zero, zero)
481    }
482
483    /// Mirror at the y-axis.
484    pub fn mirror_y() -> Self {
485        let zero = T::zero();
486        let one = T::one();
487        let minus_one = zero - one;
488        Self::new(one, zero, zero, minus_one, zero, zero)
489    }
490
491    /// Apply the transformation to a single point.
492    pub fn transform_point(&self, p: Point<T>) -> Point<T> {
493        Point::new(
494            p.x * self.m11 + p.y * self.m21 + self.m31,
495            p.x * self.m12 + p.y * self.m22 + self.m32,
496        )
497    }
498
499    /// Return the 3x3 matrix describing this transformation.
500    pub fn to_matrix3d(&self) -> Matrix3d<T> {
501        Matrix3d::new([
502            [self.m11, self.m12, T::zero()],
503            [self.m21, self.m22, T::zero()],
504            [self.m31, self.m32, T::zero()],
505        ])
506    }
507
508    /// Return the 2x2 matrix that describes this transformation
509    /// without any translation.
510    pub fn to_matrix2d(&self) -> Matrix2d<T> {
511        Matrix2d::new(self.m11, self.m12, self.m21, self.m22)
512    }
513
514    /// Compute the determinant of the 3x3 matrix that describes this transformation.
515    pub fn determinant(&self) -> T {
516        /*
517        ```python
518        import sympy as sp
519        m = sp.Matrix([[sp.Symbol(f"self.m{i}{j}") for j in range(1,4)] for i in range(1,4)])
520        m[0,2] = 0
521        m[1,2] = 0
522        m[2,2] = 1
523        print(m.det())
524        ```
525         */
526        self.m11 * self.m22 - self.m12 * self.m21
527    }
528
529    /// Get the inverse transformation if it exists.
530    pub fn try_invert(&self) -> Option<Self> {
531        /*
532        ```python
533        import sympy as sp
534        m = sp.Matrix([[sp.Symbol(f"a.m{i}{j}") for j in range(1,4)] for i in range(1,4)])
535        m[0,2] = 0
536        m[1,2] = 0
537        m[2,2] = 1
538        det = sp.Symbol('det')
539
540        # Compute inverse multiplied with determinant.
541        inv_det = m.inv() * m.det()
542        inv = inv_det / det
543        # Print inverse with factored-out determinant.
544        print(repr(inv).replace('[', '').replace(']', ''))
545        ```
546         */
547        let a = self;
548
549        // Compute determinant.
550        let det = a.determinant();
551        if !det.is_zero() {
552            Some(Self::new(
553                a.m22 / det,
554                T::zero() - a.m12 / det,
555                T::zero() - a.m21 / det,
556                a.m11 / det,
557                (a.m21 * a.m32 - a.m22 * a.m31) / det,
558                (a.m12 * a.m31 - a.m11 * a.m32) / det,
559            ))
560        } else {
561            None
562        }
563    }
564
565    /// Return a new transformation that is equal to applying
566    /// first `self` then `t`.
567    pub fn then(&self, t: &Self) -> Self {
568        Self::new(
569            self.m11 * t.m11 + self.m12 * t.m21,
570            self.m11 * t.m12 + self.m12 * t.m22,
571            self.m21 * t.m11 + self.m22 * t.m21,
572            self.m21 * t.m12 + self.m22 * t.m22,
573            self.m31 * t.m11 + self.m32 * t.m21 + t.m31,
574            self.m31 * t.m12 + self.m32 * t.m22 + t.m32,
575        )
576    }
577
578    /// Create a new transformation with an additional scaling.
579    pub fn then_scale(&self, factor: T) -> Self {
580        self.then(&Self::scale(factor))
581    }
582
583    /// Create a new transformation with an additional translation.
584    pub fn then_translate<V: Into<Vector<T>>>(&self, v: V) -> Self {
585        self.then(&Self::translate(v))
586    }
587
588    /// Create a new transformation with an additional rotation by a multiple of 90 degrees.
589    pub fn then_rotate90(&self, angle: Angle) -> Self {
590        self.then(&Self::rotate90(angle))
591    }
592
593    /// Create a new transformation with an additional mirroring at the x-axis.
594    pub fn then_mirror_x(&self) -> Self {
595        self.then(&Self::mirror_x())
596    }
597
598    /// Create a new transformation with an additional mirroring at the y-axis.
599    pub fn then_mirror_y(&self) -> Self {
600        self.then(&Self::mirror_y())
601    }
602
603    /// Get the translation part of this affine transformation.
604    pub fn get_translation(&self) -> Vector<T> {
605        Vector::new(self.m31, self.m32)
606    }
607}
608
609impl<T: CoordinateType> Mul for Matrix3dTransform<T> {
610    type Output = Matrix3dTransform<T>;
611
612    /// Shortcut for `self.then(&rhs)`.
613    fn mul(self, rhs: Self) -> Self::Output {
614        self.then(&rhs)
615    }
616}
617
618#[test]
619fn test_identity() {
620    let p = Point::new(1, 2);
621    let tf = Matrix3dTransform::identity();
622    assert_eq!(tf.transform_point(p), p);
623}
624
625#[test]
626fn test_translate() {
627    let p = Point::new(1, 2);
628    let tf = Matrix3dTransform::translate(Vector::new(10, 100));
629    assert_eq!(tf.transform_point(p), Point::new(11, 102));
630    assert_eq!(tf.get_translation(), Vector::new(10, 100));
631}
632
633#[test]
634fn test_rotate90() {
635    let p = Point::new(1, 2);
636    let tf = Matrix3dTransform::rotate90(Angle::R0);
637    assert_eq!(tf.transform_point(p), Point::new(1, 2));
638    let tf = Matrix3dTransform::rotate90(Angle::R90);
639    assert_eq!(tf.transform_point(p), Point::new(-2, 1));
640    let tf = Matrix3dTransform::rotate90(Angle::R180);
641    assert_eq!(tf.transform_point(p), Point::new(-1, -2));
642    let tf = Matrix3dTransform::rotate90(Angle::R270);
643    assert_eq!(tf.transform_point(p), Point::new(2, -1));
644}
645
646#[test]
647fn test_scale() {
648    let p = Point::new(1, 2);
649    let tf = Matrix3dTransform::scale(2);
650    assert_eq!(tf.transform_point(p), Point::new(2, 4));
651}
652
653impl<T: CoordinateType + Float> Matrix3dTransform<T> {
654    /// Create a rotation by an arbitrary angle (in radians).
655    pub fn rotation(phi: T) -> Self {
656        let zero = T::zero();
657        let cos = phi.cos();
658        let sin = phi.sin();
659        Self::new(cos, sin, zero - sin, cos, T::zero(), T::zero())
660    }
661
662    /// Create a new transformation with an additional rotation.
663    pub fn then_rotate(&self, phi: T) -> Self {
664        self.then(&Self::rotation(phi))
665    }
666}
667
668#[test]
669fn test_rotate() {
670    let p = Point::new(1.0, 0.0);
671    let pi = std::f64::consts::PI;
672    let tf = Matrix3dTransform::rotation(pi);
673    assert!((tf.transform_point(p) - Point::new(-1.0, 0.0)).norm2_squared() < 1e-6);
674    let tf = Matrix3dTransform::rotation(pi * 0.5);
675    assert!((tf.transform_point(p) - Point::new(0.0, 1.0)).norm2_squared() < 1e-6);
676}
677
678#[test]
679fn test_then() {
680    let tf1 = Matrix3dTransform::translate((1, 2));
681    let id: Matrix3dTransform<i32> = Matrix3dTransform::identity();
682    assert_eq!(tf1.then(&id), tf1);
683
684    let tf1_tf1 = Matrix3dTransform::translate((2, 4));
685    assert_eq!(tf1.then(&tf1), tf1_tf1);
686
687    let tf3 = Matrix3dTransform::rotate90(Angle::R90);
688    assert_eq!(tf3.then(&tf3).then(&tf3).then(&tf3), id);
689}
690
691#[test]
692fn test_invert() {
693    let id: Matrix3dTransform<i32> = Matrix3dTransform::identity();
694    assert_eq!(id.try_invert(), Some(id));
695
696    let tf1 = Matrix3dTransform::translate((1, 2));
697    let tf1_inv = tf1.try_invert().unwrap();
698    assert_eq!(tf1.then(&tf1_inv), Matrix3dTransform::identity());
699
700    let tf2 = Matrix3dTransform::translate((1, 2))
701        .then_rotate90(Angle::R90)
702        .then_mirror_x();
703    assert!(tf2.to_matrix2d().is_unitary());
704
705    let tf2_inv = tf2.try_invert().unwrap();
706    assert_eq!(tf2.then(&tf2_inv), Matrix3dTransform::identity());
707
708    assert_eq!(tf2.try_invert().unwrap().try_invert(), Some(tf2));
709}
710
711#[test]
712fn test_invert_float() {
713    let tf = Matrix3dTransform::rotation(1.234)
714        .then_scale(12345.6)
715        .then_translate((1.2, 34.5));
716    let tf_inv = tf.try_invert().unwrap();
717    let p = Point::new(42.42, -1.0);
718    let p2 = tf_inv.transform_point(tf.transform_point(p));
719    assert!((p - p2).norm2_squared() < 1e-6); // Test for approximate equality.
720}
721
722impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst>
723    for SimpleTransform<T>
724{
725    type Output = SimpleTransform<Dst>;
726
727    fn try_cast(&self) -> Option<Self::Output> {
728        match (self.displacement.try_cast(), Dst::from(self.magnification)) {
729            (Some(displacement), Some(magnification)) => Some(Self::Output {
730                mirror: self.mirror,
731                displacement,
732                magnification,
733                rotation: self.rotation,
734            }),
735            _ => None,
736        }
737    }
738}