use num_traits::ToPrimitive;
use crate::{Coord, CoordFloat, CoordNum, MapCoords, MapCoordsInPlace};
use std::{fmt, ops::Mul, ops::Neg};
pub trait AffineOps<T: CoordNum> {
#[must_use]
fn affine_transform(&self, transform: &AffineTransform<T>) -> Self;
fn affine_transform_mut(&mut self, transform: &AffineTransform<T>);
}
impl<T: CoordNum, M: MapCoordsInPlace<T> + MapCoords<T, T, Output = Self>> AffineOps<T> for M {
fn affine_transform(&self, transform: &AffineTransform<T>) -> Self {
self.map_coords(|c| transform.apply(c))
}
fn affine_transform_mut(&mut self, transform: &AffineTransform<T>) {
self.map_coords_in_place(|c| transform.apply(c))
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct AffineTransform<T: CoordNum = f64>([[T; 3]; 3]);
impl<T: CoordNum> Default for AffineTransform<T> {
fn default() -> Self {
Self::identity()
}
}
impl<T: CoordNum> AffineTransform<T> {
#[must_use]
pub fn compose(&self, other: &Self) -> Self {
Self([
[
(other.0[0][0] * self.0[0][0])
+ (other.0[0][1] * self.0[1][0])
+ (other.0[0][2] * self.0[2][0]),
(other.0[0][0] * self.0[0][1])
+ (other.0[0][1] * self.0[1][1])
+ (other.0[0][2] * self.0[2][1]),
(other.0[0][0] * self.0[0][2])
+ (other.0[0][1] * self.0[1][2])
+ (other.0[0][2] * self.0[2][2]),
],
[
(other.0[1][0] * self.0[0][0])
+ (other.0[1][1] * self.0[1][0])
+ (other.0[1][2] * self.0[2][0]),
(other.0[1][0] * self.0[0][1])
+ (other.0[1][1] * self.0[1][1])
+ (other.0[1][2] * self.0[2][1]),
(other.0[1][0] * self.0[0][2])
+ (other.0[1][1] * self.0[1][2])
+ (other.0[1][2] * self.0[2][2]),
],
[
(other.0[2][0] * self.0[0][0])
+ (other.0[2][1] * self.0[1][0])
+ (other.0[2][2] * self.0[2][0]),
(other.0[2][0] * self.0[0][1])
+ (other.0[2][1] * self.0[1][1])
+ (other.0[2][2] * self.0[2][1]),
(other.0[2][0] * self.0[0][2])
+ (other.0[2][1] * self.0[1][2])
+ (other.0[2][2] * self.0[2][2]),
],
])
}
#[must_use]
pub fn compose_many(&self, transforms: &[Self]) -> Self {
self.compose(&transforms.iter().fold(
AffineTransform::default(),
|acc: AffineTransform<T>, transform| acc.compose(transform),
))
}
pub fn identity() -> Self {
Self::new(
T::one(),
T::zero(),
T::zero(),
T::zero(),
T::one(),
T::zero(),
)
}
pub fn is_identity(&self) -> bool {
self == &Self::identity()
}
pub fn scale(xfact: T, yfact: T, origin: impl Into<Coord<T>>) -> Self {
let (x0, y0) = origin.into().x_y();
let xoff = x0 - (x0 * xfact);
let yoff = y0 - (y0 * yfact);
Self::new(xfact, T::zero(), xoff, T::zero(), yfact, yoff)
}
#[must_use]
pub fn scaled(mut self, xfact: T, yfact: T, origin: impl Into<Coord<T>>) -> Self {
self.0 = self.compose(&Self::scale(xfact, yfact, origin)).0;
self
}
pub fn translate(xoff: T, yoff: T) -> Self {
Self::new(T::one(), T::zero(), xoff, T::zero(), T::one(), yoff)
}
#[must_use]
pub fn translated(mut self, xoff: T, yoff: T) -> Self {
self.0 = self.compose(&Self::translate(xoff, yoff)).0;
self
}
pub fn apply(&self, coord: Coord<T>) -> Coord<T> {
Coord {
x: (self.0[0][0] * coord.x + self.0[0][1] * coord.y + self.0[0][2]),
y: (self.0[1][0] * coord.x + self.0[1][1] * coord.y + self.0[1][2]),
}
}
pub fn new(a: T, b: T, xoff: T, d: T, e: T, yoff: T) -> Self {
Self([[a, b, xoff], [d, e, yoff], [T::zero(), T::zero(), T::one()]])
}
pub fn a(&self) -> T {
self.0[0][0]
}
pub fn b(&self) -> T {
self.0[0][1]
}
pub fn xoff(&self) -> T {
self.0[0][2]
}
pub fn d(&self) -> T {
self.0[1][0]
}
pub fn e(&self) -> T {
self.0[1][1]
}
pub fn yoff(&self) -> T {
self.0[1][2]
}
}
impl<T: CoordNum + Neg> AffineTransform<T> {
#[must_use]
pub fn inverse(&self) -> Option<Self>
where
<T as Neg>::Output: Mul<T>,
<<T as Neg>::Output as Mul<T>>::Output: ToPrimitive,
{
let a = self.0[0][0];
let b = self.0[0][1];
let xoff = self.0[0][2];
let d = self.0[1][0];
let e = self.0[1][1];
let yoff = self.0[1][2];
let determinant = a * e - b * d;
if determinant == T::zero() {
return None; }
let inv_det = T::one() / determinant;
Some(Self::new(
e * inv_det,
T::from(-b * inv_det)?,
(b * yoff - e * xoff) * inv_det,
T::from(-d * inv_det)?,
a * inv_det,
(d * xoff - a * yoff) * inv_det,
))
}
}
impl<T: CoordNum> fmt::Debug for AffineTransform<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AffineTransform")
.field("a", &self.0[0][0])
.field("b", &self.0[0][1])
.field("xoff", &self.0[0][2])
.field("d", &self.0[1][0])
.field("e", &self.0[1][1])
.field("yoff", &self.0[1][2])
.finish()
}
}
impl<T: CoordNum> From<[T; 6]> for AffineTransform<T> {
fn from(arr: [T; 6]) -> Self {
Self::new(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5])
}
}
impl<T: CoordNum> From<(T, T, T, T, T, T)> for AffineTransform<T> {
fn from(tup: (T, T, T, T, T, T)) -> Self {
Self::new(tup.0, tup.1, tup.2, tup.3, tup.4, tup.5)
}
}
impl<U: CoordFloat> AffineTransform<U> {
pub fn rotate(degrees: U, origin: impl Into<Coord<U>>) -> Self {
let (sin_theta, cos_theta) = degrees.to_radians().sin_cos();
let (x0, y0) = origin.into().x_y();
let xoff = x0 - (x0 * cos_theta) + (y0 * sin_theta);
let yoff = y0 - (x0 * sin_theta) - (y0 * cos_theta);
Self::new(cos_theta, -sin_theta, xoff, sin_theta, cos_theta, yoff)
}
#[must_use]
pub fn rotated(mut self, angle: U, origin: impl Into<Coord<U>>) -> Self {
self.0 = self.compose(&Self::rotate(angle, origin)).0;
self
}
pub fn skew(xs: U, ys: U, origin: impl Into<Coord<U>>) -> Self {
let Coord { x: x0, y: y0 } = origin.into();
let mut tanx = xs.to_radians().tan();
let mut tany = ys.to_radians().tan();
if tanx.abs() < U::from::<f64>(2.5e-16).unwrap() {
tanx = U::zero();
}
if tany.abs() < U::from::<f64>(2.5e-16).unwrap() {
tany = U::zero();
}
let xoff = -y0 * tanx;
let yoff = -x0 * tany;
Self::new(U::one(), tanx, xoff, tany, U::one(), yoff)
}
#[must_use]
pub fn skewed(mut self, xs: U, ys: U, origin: impl Into<Coord<U>>) -> Self {
self.0 = self.compose(&Self::skew(xs, ys, origin)).0;
self
}
}
#[cfg(test)]
mod tests {
use approx::{AbsDiffEq, RelativeEq};
impl<T> RelativeEq for AffineTransform<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum + RelativeEq,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
let mut mp_zipper = self.0.iter().flatten().zip(other.0.iter().flatten());
mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative))
}
}
impl<T> AbsDiffEq for AffineTransform<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum,
T::Epsilon: Copy,
{
type Epsilon = T;
#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
let mut mp_zipper = self.0.iter().flatten().zip(other.0.iter().flatten());
mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon))
}
}
use super::*;
use crate::{Point, wkt};
#[test]
fn matrix_multiply() {
let a = AffineTransform::new(1, 2, 5, 3, 4, 6);
let b = AffineTransform::new(7, 8, 11, 9, 10, 12);
let composed = a.compose(&b);
assert_eq!(composed.0[0][0], 31);
assert_eq!(composed.0[0][1], 46);
assert_eq!(composed.0[0][2], 94);
assert_eq!(composed.0[1][0], 39);
assert_eq!(composed.0[1][1], 58);
assert_eq!(composed.0[1][2], 117);
}
#[test]
fn test_transform_composition() {
let p0 = Point::new(0.0f64, 0.0);
let mut scale_a = AffineTransform::default().scaled(2.0, 2.0, p0);
scale_a = scale_a.rotated(45.0, p0);
scale_a = scale_a.rotated(-45.0, p0);
scale_a = scale_a.scaled(2.0, 2.0, p0);
let scale_b = AffineTransform::default().scaled(2.0, 2.0, p0);
let scale_c = AffineTransform::default().scaled(4.0, 4.0, p0);
assert_ne!(&scale_a.0, &scale_b.0);
assert_relative_eq!(&scale_a, &scale_c);
}
#[test]
fn affine_transformed() {
let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, (0.0, 0.0));
let mut poly = wkt! { POLYGON((0.0 0.0,0.0 2.0,1.0 2.0)) };
poly.affine_transform_mut(&transform);
let expected = wkt! { POLYGON((2.0 2.0,2.0 6.0,4.0 6.0)) };
assert_eq!(expected, poly);
}
#[test]
fn affine_transformed_inverse() {
let transform = AffineTransform::translate(1.0, 1.0).scaled(2.0, 2.0, (0.0, 0.0));
let tinv = transform.inverse().unwrap();
let identity = transform.compose(&tinv);
assert!(identity.is_identity());
let mut poly = wkt! { POLYGON((0.0 0.0,0.0 2.0,1.0 2.0)) };
let expected = poly.clone();
poly.affine_transform_mut(&identity);
assert_eq!(expected, poly);
}
#[test]
fn test_affine_transform_getters() {
let transform = AffineTransform::new(10.0, 0.0, 400_000.0, 0.0, -10.0, 500_000.0);
assert_eq!(transform.a(), 10.0);
assert_eq!(transform.b(), 0.0);
assert_eq!(transform.xoff(), 400_000.0);
assert_eq!(transform.d(), 0.0);
assert_eq!(transform.e(), -10.0);
assert_eq!(transform.yoff(), 500_000.0);
}
#[test]
fn test_compose() {
let point = Point::new(1., 0.);
let translate = AffineTransform::translate(1., 0.);
let scale = AffineTransform::scale(4., 1., [0., 0.]);
let composed = translate.compose(&scale);
assert_eq!(point.affine_transform(&translate), Point::new(2., 0.));
assert_eq!(point.affine_transform(&scale), Point::new(4., 0.));
assert_eq!(
point.affine_transform(&translate).affine_transform(&scale),
Point::new(8., 0.)
);
assert_eq!(point.affine_transform(&composed), Point::new(8., 0.));
}
}