|
| 1 | +use num_traits::ToPrimitive; |
| 2 | + |
1 | 3 | use crate::{Coord, CoordFloat, CoordNum, MapCoords, MapCoordsInPlace};
|
2 |
| -use std::fmt; |
| 4 | +use std::{fmt, ops::Mul, ops::Neg}; |
3 | 5 |
|
4 | 6 | /// Apply an [`AffineTransform`] like [`scale`](AffineTransform::scale),
|
5 | 7 | /// [`skew`](AffineTransform::skew), or [`rotate`](AffineTransform::rotate) to a
|
@@ -277,6 +279,41 @@ impl<T: CoordNum> AffineTransform<T> {
|
277 | 279 | }
|
278 | 280 | }
|
279 | 281 |
|
| 282 | +impl<T: CoordNum + Neg> AffineTransform<T> { |
| 283 | + /// Return the inverse of a given transform. Composing a transform with its inverse yields |
| 284 | + /// the [identity matrix](Self::identity) |
| 285 | + #[must_use] |
| 286 | + pub fn inverse(&self) -> Option<Self> |
| 287 | + where |
| 288 | + <T as Neg>::Output: Mul<T>, |
| 289 | + <<T as Neg>::Output as Mul<T>>::Output: ToPrimitive, |
| 290 | + { |
| 291 | + let a = self.0[0][0]; |
| 292 | + let b = self.0[0][1]; |
| 293 | + let xoff = self.0[0][2]; |
| 294 | + let d = self.0[1][0]; |
| 295 | + let e = self.0[1][1]; |
| 296 | + let yoff = self.0[1][2]; |
| 297 | + |
| 298 | + let determinant = a * e - b * d; |
| 299 | + |
| 300 | + if determinant == T::zero() { |
| 301 | + return None; // The matrix is not invertible |
| 302 | + } |
| 303 | + let inv_det = T::one() / determinant; |
| 304 | + |
| 305 | + // If conversion of either the b or d matrix value fails, bail out |
| 306 | + Some(Self::new( |
| 307 | + e * inv_det, |
| 308 | + T::from(-b * inv_det)?, |
| 309 | + (b * yoff - e * xoff) * inv_det, |
| 310 | + T::from(-d * inv_det)?, |
| 311 | + a * inv_det, |
| 312 | + (d * xoff - a * yoff) * inv_det, |
| 313 | + )) |
| 314 | + } |
| 315 | +} |
| 316 | + |
280 | 317 | impl<T: CoordNum> fmt::Debug for AffineTransform<T> {
|
281 | 318 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
282 | 319 | f.debug_struct("AffineTransform")
|
@@ -436,4 +473,17 @@ mod tests {
|
436 | 473 | let expected = polygon![(x: 1.0, y: 1.0), (x: 1.0, y: 5.0), (x: 3.0, y: 5.0)];
|
437 | 474 | assert_eq!(expected, poly);
|
438 | 475 | }
|
| 476 | + #[test] |
| 477 | + fn affine_transformed_inverse() { |
| 478 | + let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, (0.0, 0.0)); |
| 479 | + let tinv = transform.inverse().unwrap(); |
| 480 | + let identity = transform.compose(&tinv); |
| 481 | + // test really only needs this, but let's be sure |
| 482 | + assert!(identity.is_identity()); |
| 483 | + |
| 484 | + let mut poly = polygon![(x: 0.0, y: 0.0), (x: 0.0, y: 2.0), (x: 1.0, y: 2.0)]; |
| 485 | + let expected = poly.clone(); |
| 486 | + poly.affine_transform_mut(&identity); |
| 487 | + assert_eq!(expected, poly); |
| 488 | + } |
439 | 489 | }
|
0 commit comments