use crate::CoordinateType;
use crate::matrix2d::*;
use crate::point::Point;
use crate::traits::{Mirror, RotateOrtho, Scale, Translate, TryCastCoord};
use crate::types::Angle;
use crate::vector::Vector;
use crate::matrix3d::Matrix3d;
use crate::types::FloatType;
use num_traits::{Float, NumCast, One, Zero};
use std::ops::Mul;
pub trait Transformation {
type SourceCoord;
type DestinationCoord;
fn transform_point(&self, p: Point<Self::SourceCoord>) -> Point<Self::DestinationCoord>;
}
pub trait AffineTransform: Transformation {
fn eigen_vectors(&self) -> (Vector<Self::SourceCoord>, Vector<Self::SourceCoord>);
}
pub trait SimilarityTransform: AffineTransform {
fn transform_distance(&self, distance: Self::SourceCoord) -> Self::DestinationCoord;
}
pub trait SimilarityTransform90:
SimilarityTransform<SourceCoord = Self::Coord, DestinationCoord = Self::Coord>
{
type Coord;
}
pub trait IsometricTransform: SimilarityTransform {
}
pub trait IsometricTransform90: IsometricTransform + SimilarityTransform90 {
fn rotation(&self) -> Angle;
}
pub trait DisplacementTransform: IsometricTransform90 {
fn displacement(&self) -> Vector<Self::SourceCoord>;
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Matrix2dTransform<T: CoordinateType> {
matrix: Matrix2d<T>,
}
impl<T: CoordinateType> Matrix2dTransform<T> {
pub fn new(matrix: Matrix2d<T>) -> Self {
Matrix2dTransform { matrix }
}
pub fn new_rotation90(angle: Angle) -> Self {
let zero = T::zero();
let one = T::one();
let minus_one = zero - one;
let matrix = match angle {
Angle::R0 => Matrix2d::new(one, zero, zero, one),
Angle::R90 => Matrix2d::new(zero, minus_one, one, zero),
Angle::R180 => Matrix2d::new(minus_one, zero, zero, minus_one),
Angle::R270 => Matrix2d::new(zero, one, minus_one, zero),
};
Matrix2dTransform::new(matrix)
}
pub fn new_scaling(factor: T) -> Self {
let zero = T::zero();
Matrix2dTransform::new(Matrix2d::new(factor, zero, zero, factor))
}
pub fn new_mirror_x() -> Self {
let zero = T::zero();
let one = T::one();
let minus_one = zero - one;
Matrix2dTransform::new(Matrix2d::new(minus_one, zero, zero, one))
}
pub fn new_mirror_y() -> Self {
let zero = T::zero();
let one = T::one();
let minus_one = zero - one;
Matrix2dTransform::new(Matrix2d::new(one, zero, zero, minus_one))
}
pub fn transform_point(&self, p: Point<T>) -> Point<T> {
self.matrix.mul_column_vector(p.into()).into()
}
pub fn to_matrix2d(&self) -> Matrix2d<T> {
self.matrix.clone()
}
pub fn try_invert(&self) -> Option<Self> {
self.matrix.try_inverse().map(|inv| Self { matrix: inv })
}
}
#[test]
fn test_matrix_transform_rotations() {
let p = Point::new(1, 0);
assert_eq!(
Matrix2dTransform::new_rotation90(Angle::R0).transform_point(p),
p
);
assert_eq!(
Matrix2dTransform::new_rotation90(Angle::R90).transform_point(p),
Point::new(0, 1)
);
assert_eq!(
Matrix2dTransform::new_rotation90(Angle::R180).transform_point(p),
Point::new(-1, 0)
);
assert_eq!(
Matrix2dTransform::new_rotation90(Angle::R270).transform_point(p),
Point::new(0, -1)
);
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Rot90Transform {
angle: Angle,
}
impl Rot90Transform {
pub fn new(angle: Angle) -> Self {
Rot90Transform { angle }
}
pub fn is_unitary(&self) -> bool {
true
}
pub fn transform_point<T: CoordinateType>(&self, p: Point<T>) -> Point<T> {
p.rotate_ortho(self.angle)
}
pub fn magnification<T: CoordinateType>(&self) -> T {
T::one()
}
pub fn try_magnification<T: CoordinateType>(&self) -> Option<T> {
Some(self.magnification())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SimpleTransform<T> {
pub mirror: bool,
pub rotation: Angle,
pub magnification: T,
pub displacement: Vector<T>,
}
impl<T: CoordinateType> Default for SimpleTransform<T> {
fn default() -> Self {
SimpleTransform::identity()
}
}
impl<T> SimpleTransform<T> {
pub fn new(mirror: bool, rotation: Angle, magnification: T, displacement: Vector<T>) -> Self {
SimpleTransform {
mirror,
rotation,
magnification,
displacement,
}
}
}
impl<T: Zero + One> SimpleTransform<T> {
pub fn identity() -> Self {
Self::translate(Vector::zero())
}
pub fn translate<V: Into<Vector<T>>>(v: V) -> Self {
let t = v.into();
Self::new(false, Angle::R0, T::one(), t)
}
pub fn rotate90(angle: Angle) -> Self {
Self::new(false, angle, T::one(), Vector::zero())
}
pub fn rotate_ccw90() -> Self {
Self::rotate90(Angle::R90)
}
pub fn rotate_cw90() -> Self {
Self::rotate90(Angle::R270)
}
pub fn mirror_x() -> Self {
Self::new(true, Angle::R0, T::one(), Vector::zero())
}
pub fn mirror_y() -> Self {
Self::new(true, Angle::R180, T::one(), Vector::zero())
}
pub fn scale(factor: T) -> Self {
Self::new(false, Angle::R0, factor, Vector::zero())
}
}
impl<T> SimpleTransform<T>
where
T: Copy + Mul<Output = T>,
{
pub fn transform_distance(&self, d: T) -> T {
d * self.magnification
}
}
impl<T: CoordinateType> SimpleTransform<T> {
pub fn rotate90_around(angle: Angle, rotation_center: Point<T>) -> Self {
Self::translate(Point::zero() - rotation_center)
.then(&Self::rotate90(angle))
.then(&Self::translate(rotation_center))
}
pub fn transform_point(&self, p: Point<T>) -> Point<T> {
if self.mirror { p.mirror_x() } else { p }
.rotate_ortho(self.rotation)
.scale(self.magnification)
.translate(self.displacement)
}
pub fn inverse_transform_point(&self, p: Point<T>) -> Point<T> {
let p = p
.translate(Vector::zero() - self.displacement)
.scale(T::one() / self.magnification)
.rotate_ortho(-self.rotation);
if self.mirror {
p.mirror_x()
} else {
p
}
}
pub fn to_matrix_transform(&self) -> Matrix3dTransform<T> {
if self.mirror {
Matrix3dTransform::mirror_x()
} else {
Matrix3dTransform::identity()
}
.then_rotate90(self.rotation)
.then_scale(self.magnification)
.then_translate(self.displacement)
}
pub fn then(&self, t: &Self) -> Self {
let d = t.transform_point(self.displacement.into());
let r = if t.mirror {
-self.rotation
} else {
self.rotation
};
Self {
mirror: self.mirror ^ t.mirror,
rotation: r + t.rotation,
magnification: self.magnification * t.magnification,
displacement: d.v(),
}
}
}
#[test]
fn test_simple_transform_combine() {
let t1 = SimpleTransform::new(false, Angle::R90, 1, (1, 2).into());
let t2 = SimpleTransform::new(true, Angle::R90, 1, (3, 4).into());
let p = Point::new(10, 11);
assert_eq!(
t2.transform_point(t1.transform_point(p)),
t1.then(&t2).transform_point(p)
);
assert_eq!(
t1.transform_point(t2.transform_point(p)),
t2.then(&t1).transform_point(p)
);
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ComplexTransform<T: CoordinateType> {
mirror: bool,
rotation: FloatType,
magnification: FloatType,
displacement: Vector<T>,
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Matrix3dTransform<T: CoordinateType> {
pub m11: T,
pub m12: T,
pub m21: T,
pub m22: T,
pub m31: T,
pub m32: T,
}
impl<T: CoordinateType> Matrix3dTransform<T> {
pub fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
Matrix3dTransform {
m11,
m12,
m21,
m22,
m31,
m32,
}
}
pub fn identity() -> Self {
Self::translate(Vector::zero())
}
pub fn translate<V: Into<Vector<T>>>(v: V) -> Self {
let t = v.into();
Self::new(T::one(), T::zero(), T::zero(), T::one(), t.x, t.y)
}
pub fn rotate90(angle: Angle) -> Self {
let zero = T::zero();
let one = T::one();
let minus_one = zero - one;
let (a, b, c, d) = match angle {
Angle::R0 => (one, zero, zero, one),
Angle::R90 => (zero, one, minus_one, zero),
Angle::R180 => (minus_one, zero, zero, minus_one),
Angle::R270 => (zero, minus_one, one, zero),
};
Self::new(a, b, c, d, T::zero(), T::zero())
}
pub fn scale(factor: T) -> Self {
let zero = T::zero();
Self::new(factor, zero, zero, factor, zero, zero)
}
pub fn mirror_x() -> Self {
let zero = T::zero();
let one = T::one();
let minus_one = zero - one;
Self::new(minus_one, zero, zero, one, zero, zero)
}
pub fn mirror_y() -> Self {
let zero = T::zero();
let one = T::one();
let minus_one = zero - one;
Self::new(one, zero, zero, minus_one, zero, zero)
}
pub fn transform_point(&self, p: Point<T>) -> Point<T> {
Point::new(
p.x * self.m11 + p.y * self.m21 + self.m31,
p.x * self.m12 + p.y * self.m22 + self.m32,
)
}
pub fn to_matrix3d(&self) -> Matrix3d<T> {
Matrix3d::new([
[self.m11, self.m12, T::zero()],
[self.m21, self.m22, T::zero()],
[self.m31, self.m32, T::zero()],
])
}
pub fn to_matrix2d(&self) -> Matrix2d<T> {
Matrix2d::new(self.m11, self.m12, self.m21, self.m22)
}
pub fn determinant(&self) -> T {
self.m11 * self.m22 - self.m12 * self.m21
}
pub fn try_invert(&self) -> Option<Self> {
let a = self;
let det = a.determinant();
if !det.is_zero() {
Some(Self::new(
a.m22 / det,
T::zero() - a.m12 / det,
T::zero() - a.m21 / det,
a.m11 / det,
(a.m21 * a.m32 - a.m22 * a.m31) / det,
(a.m12 * a.m31 - a.m11 * a.m32) / det,
))
} else {
None
}
}
pub fn then(&self, t: &Self) -> Self {
Self::new(
self.m11 * t.m11 + self.m12 * t.m21,
self.m11 * t.m12 + self.m12 * t.m22,
self.m21 * t.m11 + self.m22 * t.m21,
self.m21 * t.m12 + self.m22 * t.m22,
self.m31 * t.m11 + self.m32 * t.m21 + t.m31,
self.m31 * t.m12 + self.m32 * t.m22 + t.m32,
)
}
pub fn then_scale(&self, factor: T) -> Self {
self.then(&Self::scale(factor))
}
pub fn then_translate<V: Into<Vector<T>>>(&self, v: V) -> Self {
self.then(&Self::translate(v))
}
pub fn then_rotate90(&self, angle: Angle) -> Self {
self.then(&Self::rotate90(angle))
}
pub fn then_mirror_x(&self) -> Self {
self.then(&Self::mirror_x())
}
pub fn then_mirror_y(&self) -> Self {
self.then(&Self::mirror_y())
}
pub fn get_translation(&self) -> Vector<T> {
Vector::new(self.m31, self.m32)
}
}
impl<T: CoordinateType> Mul for Matrix3dTransform<T> {
type Output = Matrix3dTransform<T>;
fn mul(self, rhs: Self) -> Self::Output {
self.then(&rhs)
}
}
#[test]
fn test_identity() {
let p = Point::new(1, 2);
let tf = Matrix3dTransform::identity();
assert_eq!(tf.transform_point(p), p);
}
#[test]
fn test_translate() {
let p = Point::new(1, 2);
let tf = Matrix3dTransform::translate(Vector::new(10, 100));
assert_eq!(tf.transform_point(p), Point::new(11, 102));
assert_eq!(tf.get_translation(), Vector::new(10, 100));
}
#[test]
fn test_rotate90() {
let p = Point::new(1, 2);
let tf = Matrix3dTransform::rotate90(Angle::R0);
assert_eq!(tf.transform_point(p), Point::new(1, 2));
let tf = Matrix3dTransform::rotate90(Angle::R90);
assert_eq!(tf.transform_point(p), Point::new(-2, 1));
let tf = Matrix3dTransform::rotate90(Angle::R180);
assert_eq!(tf.transform_point(p), Point::new(-1, -2));
let tf = Matrix3dTransform::rotate90(Angle::R270);
assert_eq!(tf.transform_point(p), Point::new(2, -1));
}
#[test]
fn test_scale() {
let p = Point::new(1, 2);
let tf = Matrix3dTransform::scale(2);
assert_eq!(tf.transform_point(p), Point::new(2, 4));
}
impl<T: CoordinateType + Float> Matrix3dTransform<T> {
pub fn rotation(phi: T) -> Self {
let zero = T::zero();
let cos = phi.cos();
let sin = phi.sin();
Self::new(cos, sin, zero - sin, cos, T::zero(), T::zero())
}
pub fn then_rotate(&self, phi: T) -> Self {
self.then(&Self::rotation(phi))
}
}
#[test]
fn test_rotate() {
let p = Point::new(1.0, 0.0);
let pi = std::f64::consts::PI;
let tf = Matrix3dTransform::rotation(pi);
assert!((tf.transform_point(p) - Point::new(-1.0, 0.0)).norm2_squared() < 1e-6);
let tf = Matrix3dTransform::rotation(pi * 0.5);
assert!((tf.transform_point(p) - Point::new(0.0, 1.0)).norm2_squared() < 1e-6);
}
#[test]
fn test_then() {
let tf1 = Matrix3dTransform::translate((1, 2));
let id: Matrix3dTransform<i32> = Matrix3dTransform::identity();
assert_eq!(tf1.then(&id), tf1);
let tf1_tf1 = Matrix3dTransform::translate((2, 4));
assert_eq!(tf1.then(&tf1), tf1_tf1);
let tf3 = Matrix3dTransform::rotate90(Angle::R90);
assert_eq!(tf3.then(&tf3).then(&tf3).then(&tf3), id);
}
#[test]
fn test_invert() {
let id: Matrix3dTransform<i32> = Matrix3dTransform::identity();
assert_eq!(id.try_invert(), Some(id));
let tf1 = Matrix3dTransform::translate((1, 2));
let tf1_inv = tf1.try_invert().unwrap();
assert_eq!(tf1.then(&tf1_inv), Matrix3dTransform::identity());
let tf2 = Matrix3dTransform::translate((1, 2))
.then_rotate90(Angle::R90)
.then_mirror_x();
assert!(tf2.to_matrix2d().is_unitary());
let tf2_inv = tf2.try_invert().unwrap();
assert_eq!(tf2.then(&tf2_inv), Matrix3dTransform::identity());
assert_eq!(tf2.try_invert().unwrap().try_invert(), Some(tf2));
}
#[test]
fn test_invert_float() {
let tf = Matrix3dTransform::rotation(1.234)
.then_scale(12345.6)
.then_translate((1.2, 34.5));
let tf_inv = tf.try_invert().unwrap();
let p = Point::new(42.42, -1.0);
let p2 = tf_inv.transform_point(tf.transform_point(p));
assert!((p - p2).norm2_squared() < 1e-6); }
impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst>
for SimpleTransform<T>
{
type Output = SimpleTransform<Dst>;
fn try_cast(&self) -> Option<Self::Output> {
match (self.displacement.try_cast(), Dst::from(self.magnification)) {
(Some(displacement), Some(magnification)) => Some(Self::Output {
mirror: self.mirror,
displacement,
magnification,
rotation: self.rotation,
}),
_ => None,
}
}
}