geo/algorithm/
affine_ops.rs

1use num_traits::ToPrimitive;
2
3use crate::{Coord, CoordFloat, CoordNum, MapCoords, MapCoordsInPlace};
4use std::{fmt, ops::Mul, ops::Neg};
5
6/// Apply an [`AffineTransform`] like [`scale`](AffineTransform::scale),
7/// [`skew`](AffineTransform::skew), or [`rotate`](AffineTransform::rotate) to a
8/// [`Geometry`](crate::geometry::Geometry).
9///
10/// Multiple transformations can be composed in order to be efficiently applied in a single
11/// operation. See [`AffineTransform`] for more on how to build up a transformation.
12///
13/// If you are not composing operations, traits that leverage this same machinery exist which might
14/// be more readable. See: [`Scale`](crate::algorithm::Scale),
15/// [`Translate`](crate::algorithm::Translate), [`Rotate`](crate::algorithm::Rotate),
16/// and [`Skew`](crate::algorithm::Skew).
17///
18/// # Examples
19/// ## Build up transforms by beginning with a constructor, then chaining mutation operations
20/// ```
21/// use geo::{AffineOps, AffineTransform};
22/// use geo::{point, line_string, BoundingRect};
23/// use approx::assert_relative_eq;
24///
25/// let line_string = line_string![(x: 0.0, y: 0.0),(x: 1.0, y: 1.0)];
26///
27/// let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, point!(x: 0.0, y: 0.0));
28///
29/// let transformed_line_string = line_string.affine_transform(&transform);
30///
31/// assert_relative_eq!(
32///     transformed_line_string,
33///     line_string![(x: 2.0, y: 2.0),(x: 4.0, y: 4.0)]
34/// );
35/// ```
36pub trait AffineOps<T: CoordNum> {
37    /// Apply `transform` immutably, outputting a new geometry.
38    #[must_use]
39    fn affine_transform(&self, transform: &AffineTransform<T>) -> Self;
40
41    /// Apply `transform` to mutate `self`.
42    fn affine_transform_mut(&mut self, transform: &AffineTransform<T>);
43}
44
45impl<T: CoordNum, M: MapCoordsInPlace<T> + MapCoords<T, T, Output = Self>> AffineOps<T> for M {
46    fn affine_transform(&self, transform: &AffineTransform<T>) -> Self {
47        self.map_coords(|c| transform.apply(c))
48    }
49
50    fn affine_transform_mut(&mut self, transform: &AffineTransform<T>) {
51        self.map_coords_in_place(|c| transform.apply(c))
52    }
53}
54
55/// A general affine transformation matrix, and associated operations.
56///
57/// Note that affine ops are **already implemented** on most `geo-types` primitives, using this module.
58///
59/// Affine transforms using the same numeric type (e.g. [`CoordFloat`]) can be **composed**,
60/// and the result can be applied to geometries using e.g. [`MapCoords`]. This allows the
61/// efficient application of transforms: an arbitrary number of operations can be chained.
62/// These are then composed, producing a final transformation matrix which is applied to the geometry coordinates.
63///
64/// `AffineTransform` is a row-major matrix.
65/// 2D affine transforms require six matrix parameters:
66///
67/// `[a, b, xoff, d, e, yoff]`
68///
69/// these map onto the `AffineTransform` rows as follows:
70/// ```ignore
71/// [[a, b, xoff],
72/// [d, e, yoff],
73/// [0, 0, 1]]
74/// ```
75/// The equations for transforming coordinates `(x, y) -> (x', y')` are given as follows:
76///
77/// `x' = ax + by + xoff`
78///
79/// `y' = dx + ey + yoff`
80///
81/// # Usage
82///
83/// Two types of operation are provided: construction and mutation. **Construction** functions create a *new* transform
84/// and are denoted by the use of the **present tense**: `scale()`, `translate()`, `rotate()`, and `skew()`.
85///
86/// **Mutation** methods *add* a transform to the existing `AffineTransform`, and are denoted by the use of the past participle:
87/// `scaled()`, `translated()`, `rotated()`, and `skewed()`.
88///
89/// # Examples
90/// ## Build up transforms by beginning with a constructor, then chaining mutation operations
91/// ```
92/// use geo::{AffineOps, AffineTransform};
93/// use geo::{point, line_string, BoundingRect};
94/// use approx::assert_relative_eq;
95///
96/// let line_string = line_string![(x: 0.0, y: 0.0),(x: 1.0, y: 1.0)];
97///
98/// let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, point!(x: 0.0, y: 0.0));
99///
100/// let transformed_line_string = line_string.affine_transform(&transform);
101///
102/// assert_relative_eq!(
103///     transformed_line_string,
104///     line_string![(x: 2.0, y: 2.0),(x: 4.0, y: 4.0)]
105/// );
106/// ```
107///
108/// ## Create affine transform manually, and access elements using getter methods
109/// ```
110/// use geo::AffineTransform;
111///
112/// let transform = AffineTransform::new(10.0, 0.0, 400_000.0, 0.0, -10.0, 500_000.0);
113///
114/// let a: f64 = transform.a();
115/// let b: f64 = transform.b();
116/// let xoff: f64 = transform.xoff();
117/// let d: f64 = transform.d();
118/// let e: f64 = transform.e();
119/// let yoff: f64 = transform.yoff();
120/// assert_eq!(transform, AffineTransform::new(a, b, xoff, d, e, yoff))
121/// ```
122
123#[derive(Copy, Clone, PartialEq, Eq)]
124pub struct AffineTransform<T: CoordNum = f64>([[T; 3]; 3]);
125
126impl<T: CoordNum> Default for AffineTransform<T> {
127    fn default() -> Self {
128        // identity matrix
129        Self::identity()
130    }
131}
132
133impl<T: CoordNum> AffineTransform<T> {
134    /// Create a new affine transformation by composing two `AffineTransform`s.
135    ///
136    /// This is a **cumulative** operation; the new transform is *added* to the existing transform.
137    #[must_use]
138    pub fn compose(&self, other: &Self) -> Self {
139        // lol
140        Self([
141            [
142                (other.0[0][0] * self.0[0][0])
143                    + (other.0[0][1] * self.0[1][0])
144                    + (other.0[0][2] * self.0[2][0]),
145                (other.0[0][0] * self.0[0][1])
146                    + (other.0[0][1] * self.0[1][1])
147                    + (other.0[0][2] * self.0[2][1]),
148                (other.0[0][0] * self.0[0][2])
149                    + (other.0[0][1] * self.0[1][2])
150                    + (other.0[0][2] * self.0[2][2]),
151            ],
152            [
153                (other.0[1][0] * self.0[0][0])
154                    + (other.0[1][1] * self.0[1][0])
155                    + (other.0[1][2] * self.0[2][0]),
156                (other.0[1][0] * self.0[0][1])
157                    + (other.0[1][1] * self.0[1][1])
158                    + (other.0[1][2] * self.0[2][1]),
159                (other.0[1][0] * self.0[0][2])
160                    + (other.0[1][1] * self.0[1][2])
161                    + (other.0[1][2] * self.0[2][2]),
162            ],
163            [
164                // this section isn't technically necessary since the last row is invariant: [0, 0, 1]
165                (other.0[2][0] * self.0[0][0])
166                    + (other.0[2][1] * self.0[1][0])
167                    + (other.0[2][2] * self.0[2][0]),
168                (other.0[2][0] * self.0[0][1])
169                    + (other.0[2][1] * self.0[1][1])
170                    + (other.0[2][2] * self.0[2][1]),
171                (other.0[2][0] * self.0[0][2])
172                    + (other.0[2][1] * self.0[1][2])
173                    + (other.0[2][2] * self.0[2][2]),
174            ],
175        ])
176    }
177
178    /// Create a new affine transformation by composing an arbitrary number of `AffineTransform`s.
179    ///
180    /// This is a **cumulative** operation; the new transform is *added* to the existing transform.
181    /// ```
182    /// use geo::AffineTransform;
183    /// let mut transform = AffineTransform::identity();
184    ///
185    /// // create two transforms that cancel each other
186    /// let transform1 = AffineTransform::translate(1.0, 2.0);
187    /// let transform2 = AffineTransform::translate(-1.0, -2.0);
188    /// let transforms = vec![transform1, transform2];
189    ///
190    /// // apply them
191    /// let outcome = transform.compose_many(&transforms);
192    /// // we should be back to square one
193    /// assert!(outcome.is_identity());
194    /// ```
195    #[must_use]
196    pub fn compose_many(&self, transforms: &[Self]) -> Self {
197        self.compose(&transforms.iter().fold(
198            AffineTransform::default(),
199            |acc: AffineTransform<T>, transform| acc.compose(transform),
200        ))
201    }
202
203    /// Create the identity matrix
204    ///
205    /// The matrix is:
206    /// ```ignore
207    /// [[1, 0, 0],
208    /// [0, 1, 0],
209    /// [0, 0, 1]]
210    /// ```
211    pub fn identity() -> Self {
212        Self::new(
213            T::one(),
214            T::zero(),
215            T::zero(),
216            T::zero(),
217            T::one(),
218            T::zero(),
219        )
220    }
221
222    /// Whether the transformation is equivalent to the [identity matrix](Self::identity),
223    /// that is, whether it's application will be a a no-op.
224    ///
225    /// ```
226    /// use geo::AffineTransform;
227    /// let mut transform = AffineTransform::identity();
228    /// assert!(transform.is_identity());
229    ///
230    /// // mutate the transform a bit
231    /// transform = transform.translated(1.0, 2.0);
232    /// assert!(!transform.is_identity());
233    ///
234    /// // put it back
235    /// transform = transform.translated(-1.0, -2.0);
236    /// assert!(transform.is_identity());
237    /// ```
238    pub fn is_identity(&self) -> bool {
239        self == &Self::identity()
240    }
241
242    /// **Create** a new affine transform for scaling, scaled by factors along the `x` and `y` dimensions.
243    /// The point of origin is *usually* given as the 2D bounding box centre of the geometry, but
244    /// any coordinate may be specified.
245    /// Negative scale factors will mirror or reflect coordinates.
246    ///
247    /// The matrix is:
248    /// ```ignore
249    /// [[xfact, 0, xoff],
250    /// [0, yfact, yoff],
251    /// [0, 0, 1]]
252    ///
253    /// xoff = origin.x - (origin.x * xfact)
254    /// yoff = origin.y - (origin.y * yfact)
255    /// ```
256    pub fn scale(xfact: T, yfact: T, origin: impl Into<Coord<T>>) -> Self {
257        let (x0, y0) = origin.into().x_y();
258        let xoff = x0 - (x0 * xfact);
259        let yoff = y0 - (y0 * yfact);
260        Self::new(xfact, T::zero(), xoff, T::zero(), yfact, yoff)
261    }
262
263    /// **Add** an affine transform for scaling, scaled by factors along the `x` and `y` dimensions.
264    /// The point of origin is *usually* given as the 2D bounding box centre of the geometry, but
265    /// any coordinate may be specified.
266    /// Negative scale factors will mirror or reflect coordinates.
267    /// This is a **cumulative** operation; the new transform is *added* to the existing transform.
268    #[must_use]
269    pub fn scaled(mut self, xfact: T, yfact: T, origin: impl Into<Coord<T>>) -> Self {
270        self.0 = self.compose(&Self::scale(xfact, yfact, origin)).0;
271        self
272    }
273
274    /// **Create** an affine transform for translation, shifted by offsets along the `x` and `y` dimensions.
275    ///
276    /// The matrix is:
277    /// ```ignore
278    /// [[1, 0, xoff],
279    /// [0, 1, yoff],
280    /// [0, 0, 1]]
281    /// ```
282    pub fn translate(xoff: T, yoff: T) -> Self {
283        Self::new(T::one(), T::zero(), xoff, T::zero(), T::one(), yoff)
284    }
285
286    /// **Add** an affine transform for translation, shifted by offsets along the `x` and `y` dimensions
287    ///
288    /// This is a **cumulative** operation; the new transform is *added* to the existing transform.
289    #[must_use]
290    pub fn translated(mut self, xoff: T, yoff: T) -> Self {
291        self.0 = self.compose(&Self::translate(xoff, yoff)).0;
292        self
293    }
294
295    /// Apply the current transform to a coordinate
296    pub fn apply(&self, coord: Coord<T>) -> Coord<T> {
297        Coord {
298            x: (self.0[0][0] * coord.x + self.0[0][1] * coord.y + self.0[0][2]),
299            y: (self.0[1][0] * coord.x + self.0[1][1] * coord.y + self.0[1][2]),
300        }
301    }
302
303    /// Create a new custom transform matrix
304    ///
305    /// The argument order matches that of the affine transform matrix:
306    ///```ignore
307    /// [[a, b, xoff],
308    ///  [d, e, yoff],
309    ///  [0, 0, 1]] <-- not part of the input arguments
310    /// ```
311    pub fn new(a: T, b: T, xoff: T, d: T, e: T, yoff: T) -> Self {
312        Self([[a, b, xoff], [d, e, yoff], [T::zero(), T::zero(), T::one()]])
313    }
314
315    /// See [AffineTransform::new] for this value's role in the affine transformation.
316    pub fn a(&self) -> T {
317        self.0[0][0]
318    }
319    /// See [AffineTransform::new] for this value's role in the affine transformation.
320    pub fn b(&self) -> T {
321        self.0[0][1]
322    }
323    /// See [AffineTransform::new] for this value's role in the affine transformation.
324    pub fn xoff(&self) -> T {
325        self.0[0][2]
326    }
327    /// See [AffineTransform::new] for this value's role in the affine transformation.
328    pub fn d(&self) -> T {
329        self.0[1][0]
330    }
331    /// See [AffineTransform::new] for this value's role in the affine transformation.
332    pub fn e(&self) -> T {
333        self.0[1][1]
334    }
335    /// See [AffineTransform::new] for this value's role in the affine transformation.
336    pub fn yoff(&self) -> T {
337        self.0[1][2]
338    }
339}
340
341impl<T: CoordNum + Neg> AffineTransform<T> {
342    /// Return the inverse of a given transform. Composing a transform with its inverse yields
343    /// the [identity matrix](Self::identity)
344    #[must_use]
345    pub fn inverse(&self) -> Option<Self>
346    where
347        <T as Neg>::Output: Mul<T>,
348        <<T as Neg>::Output as Mul<T>>::Output: ToPrimitive,
349    {
350        let a = self.0[0][0];
351        let b = self.0[0][1];
352        let xoff = self.0[0][2];
353        let d = self.0[1][0];
354        let e = self.0[1][1];
355        let yoff = self.0[1][2];
356
357        let determinant = a * e - b * d;
358
359        if determinant == T::zero() {
360            return None; // The matrix is not invertible
361        }
362        let inv_det = T::one() / determinant;
363
364        // If conversion of either the b or d matrix value fails, bail out
365        Some(Self::new(
366            e * inv_det,
367            T::from(-b * inv_det)?,
368            (b * yoff - e * xoff) * inv_det,
369            T::from(-d * inv_det)?,
370            a * inv_det,
371            (d * xoff - a * yoff) * inv_det,
372        ))
373    }
374}
375
376impl<T: CoordNum> fmt::Debug for AffineTransform<T> {
377    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378        f.debug_struct("AffineTransform")
379            .field("a", &self.0[0][0])
380            .field("b", &self.0[0][1])
381            .field("xoff", &self.0[0][2])
382            .field("d", &self.0[1][0])
383            .field("e", &self.0[1][1])
384            .field("yoff", &self.0[1][2])
385            .finish()
386    }
387}
388
389impl<T: CoordNum> From<[T; 6]> for AffineTransform<T> {
390    fn from(arr: [T; 6]) -> Self {
391        Self::new(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5])
392    }
393}
394
395impl<T: CoordNum> From<(T, T, T, T, T, T)> for AffineTransform<T> {
396    fn from(tup: (T, T, T, T, T, T)) -> Self {
397        Self::new(tup.0, tup.1, tup.2, tup.3, tup.4, tup.5)
398    }
399}
400
401impl<U: CoordFloat> AffineTransform<U> {
402    /// **Create** an affine transform for rotation, using an arbitrary point as its centre.
403    ///
404    /// Note that this operation is only available for geometries with floating point coordinates.
405    ///
406    /// `angle` is given in **degrees**.
407    ///
408    /// The matrix (angle denoted as theta) is:
409    /// ```ignore
410    /// [[cos_theta, -sin_theta, xoff],
411    /// [sin_theta, cos_theta, yoff],
412    /// [0, 0, 1]]
413    ///
414    /// xoff = origin.x - (origin.x * cos(theta)) + (origin.y * sin(theta))
415    /// yoff = origin.y - (origin.x * sin(theta)) + (origin.y * cos(theta))
416    /// ```
417    pub fn rotate(degrees: U, origin: impl Into<Coord<U>>) -> Self {
418        let (sin_theta, cos_theta) = degrees.to_radians().sin_cos();
419        let (x0, y0) = origin.into().x_y();
420        let xoff = x0 - (x0 * cos_theta) + (y0 * sin_theta);
421        let yoff = y0 - (x0 * sin_theta) - (y0 * cos_theta);
422        Self::new(cos_theta, -sin_theta, xoff, sin_theta, cos_theta, yoff)
423    }
424
425    /// **Add** an affine transform for rotation, using an arbitrary point as its centre.
426    ///
427    /// Note that this operation is only available for geometries with floating point coordinates.
428    ///
429    /// `angle` is given in **degrees**.
430    ///
431    /// This is a **cumulative** operation; the new transform is *added* to the existing transform.
432    #[must_use]
433    pub fn rotated(mut self, angle: U, origin: impl Into<Coord<U>>) -> Self {
434        self.0 = self.compose(&Self::rotate(angle, origin)).0;
435        self
436    }
437
438    /// **Create** an affine transform for skewing.
439    ///
440    /// Note that this operation is only available for geometries with floating point coordinates.
441    ///
442    /// Geometries are sheared by angles along x (`xs`) and y (`ys`) dimensions.
443    /// The point of origin is *usually* given as the 2D bounding box centre of the geometry, but
444    /// any coordinate may be specified. Angles are given in **degrees**.
445    /// The matrix is:
446    /// ```ignore
447    /// [[1, tan(x), xoff],
448    /// [tan(y), 1, yoff],
449    /// [0, 0, 1]]
450    ///
451    /// xoff = -origin.y * tan(xs)
452    /// yoff = -origin.x * tan(ys)
453    /// ```
454    pub fn skew(xs: U, ys: U, origin: impl Into<Coord<U>>) -> Self {
455        let Coord { x: x0, y: y0 } = origin.into();
456        let mut tanx = xs.to_radians().tan();
457        let mut tany = ys.to_radians().tan();
458        // These checks are stolen from Shapely's implementation -- may not be necessary
459        if tanx.abs() < U::from::<f64>(2.5e-16).unwrap() {
460            tanx = U::zero();
461        }
462        if tany.abs() < U::from::<f64>(2.5e-16).unwrap() {
463            tany = U::zero();
464        }
465        let xoff = -y0 * tanx;
466        let yoff = -x0 * tany;
467        Self::new(U::one(), tanx, xoff, tany, U::one(), yoff)
468    }
469
470    /// **Add** an affine transform for skewing.
471    ///
472    /// Note that this operation is only available for geometries with floating point coordinates.
473    ///
474    /// Geometries are sheared by angles along x (`xs`) and y (`ys`) dimensions.
475    /// The point of origin is *usually* given as the 2D bounding box centre of the geometry, but
476    /// any coordinate may be specified. Angles are given in **degrees**.
477    ///
478    /// This is a **cumulative** operation; the new transform is *added* to the existing transform.
479    #[must_use]
480    pub fn skewed(mut self, xs: U, ys: U, origin: impl Into<Coord<U>>) -> Self {
481        self.0 = self.compose(&Self::skew(xs, ys, origin)).0;
482        self
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use approx::{AbsDiffEq, RelativeEq};
489
490    impl<T> RelativeEq for AffineTransform<T>
491    where
492        T: AbsDiffEq<Epsilon = T> + CoordNum + RelativeEq,
493    {
494        #[inline]
495        fn default_max_relative() -> Self::Epsilon {
496            T::default_max_relative()
497        }
498
499        /// Equality assertion within a relative limit.
500        ///
501        /// # Examples
502        ///
503        /// ```
504        /// use geo_types::AffineTransform;
505        /// use geo_types::point;
506        ///
507        /// let a = AffineTransform::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
508        /// let b = AffineTransform::new(1.01, 2.02, 3.03, 4.04, 5.05, 6.06);
509        ///
510        /// approx::assert_relative_eq!(a, b, max_relative=0.1)
511        /// approx::assert_relative_ne!(a, b, max_relative=0.055)
512        /// ```
513        #[inline]
514        fn relative_eq(
515            &self,
516            other: &Self,
517            epsilon: Self::Epsilon,
518            max_relative: Self::Epsilon,
519        ) -> bool {
520            let mut mp_zipper = self.0.iter().flatten().zip(other.0.iter().flatten());
521            mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative))
522        }
523    }
524
525    impl<T> AbsDiffEq for AffineTransform<T>
526    where
527        T: AbsDiffEq<Epsilon = T> + CoordNum,
528        T::Epsilon: Copy,
529    {
530        type Epsilon = T;
531
532        #[inline]
533        fn default_epsilon() -> Self::Epsilon {
534            T::default_epsilon()
535        }
536
537        /// Equality assertion with an absolute limit.
538        ///
539        /// # Examples
540        ///
541        /// ```
542        /// use geo_types::MultiPoint;
543        /// use geo_types::point;
544        ///
545        /// let a = AffineTransform::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
546        /// let b = AffineTransform::new(1.01, 2.02, 3.03, 4.04, 5.05, 6.06);
547        ///
548        /// approx::abs_diff_eq!(a, b, epsilon=0.1)
549        /// approx::abs_diff_ne!(a, b, epsilon=0.055)
550        /// ```
551        #[inline]
552        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
553            let mut mp_zipper = self.0.iter().flatten().zip(other.0.iter().flatten());
554            mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon))
555        }
556    }
557
558    use super::*;
559    use crate::{wkt, Point};
560
561    // given a matrix with the shape
562    // [[a, b, xoff],
563    // [d, e, yoff],
564    // [0, 0, 1]]
565    #[test]
566    fn matrix_multiply() {
567        let a = AffineTransform::new(1, 2, 5, 3, 4, 6);
568        let b = AffineTransform::new(7, 8, 11, 9, 10, 12);
569        let composed = a.compose(&b);
570        assert_eq!(composed.0[0][0], 31);
571        assert_eq!(composed.0[0][1], 46);
572        assert_eq!(composed.0[0][2], 94);
573        assert_eq!(composed.0[1][0], 39);
574        assert_eq!(composed.0[1][1], 58);
575        assert_eq!(composed.0[1][2], 117);
576    }
577    #[test]
578    fn test_transform_composition() {
579        let p0 = Point::new(0.0f64, 0.0);
580        // scale once
581        let mut scale_a = AffineTransform::default().scaled(2.0, 2.0, p0);
582        // rotate
583        scale_a = scale_a.rotated(45.0, p0);
584        // rotate back
585        scale_a = scale_a.rotated(-45.0, p0);
586        // scale up again, doubling
587        scale_a = scale_a.scaled(2.0, 2.0, p0);
588        // scaled once
589        let scale_b = AffineTransform::default().scaled(2.0, 2.0, p0);
590        // scaled once, but equal to 2 + 2
591        let scale_c = AffineTransform::default().scaled(4.0, 4.0, p0);
592        assert_ne!(&scale_a.0, &scale_b.0);
593        assert_relative_eq!(&scale_a, &scale_c);
594    }
595
596    #[test]
597    fn affine_transformed() {
598        let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, (0.0, 0.0));
599        let mut poly = wkt! { POLYGON((0.0 0.0,0.0 2.0,1.0 2.0)) };
600        poly.affine_transform_mut(&transform);
601
602        let expected = wkt! { POLYGON((2.0 2.0,2.0 6.0,4.0 6.0)) };
603        assert_eq!(expected, poly);
604    }
605    #[test]
606    fn affine_transformed_inverse() {
607        let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, (0.0, 0.0));
608        let tinv = transform.inverse().unwrap();
609        let identity = transform.compose(&tinv);
610        // test really only needs this, but let's be sure
611        assert!(identity.is_identity());
612
613        let mut poly = wkt! { POLYGON((0.0 0.0,0.0 2.0,1.0 2.0)) };
614        let expected = poly.clone();
615        poly.affine_transform_mut(&identity);
616        assert_eq!(expected, poly);
617    }
618    #[test]
619    fn test_affine_transform_getters() {
620        let transform = AffineTransform::new(10.0, 0.0, 400_000.0, 0.0, -10.0, 500_000.0);
621        assert_eq!(transform.a(), 10.0);
622        assert_eq!(transform.b(), 0.0);
623        assert_eq!(transform.xoff(), 400_000.0);
624        assert_eq!(transform.d(), 0.0);
625        assert_eq!(transform.e(), -10.0);
626        assert_eq!(transform.yoff(), 500_000.0);
627    }
628    #[test]
629    fn test_compose() {
630        let point = Point::new(1., 0.);
631
632        let translate = AffineTransform::translate(1., 0.);
633        let scale = AffineTransform::scale(4., 1., [0., 0.]);
634        let composed = translate.compose(&scale);
635
636        assert_eq!(point.affine_transform(&translate), Point::new(2., 0.));
637        assert_eq!(point.affine_transform(&scale), Point::new(4., 0.));
638        assert_eq!(
639            point.affine_transform(&translate).affine_transform(&scale),
640            Point::new(8., 0.)
641        );
642
643        assert_eq!(point.affine_transform(&composed), Point::new(8., 0.));
644    }
645}