use crate::CoordinateType;
use crate::vector::Vector;
use crate::point::Point;
use crate::matrix2d::*;
use crate::traits::{RotateOrtho, Mirror, Translate, Scale};
use crate::types::Angle;
use std::ops::Mul;
use crate::types::FloatType;
use crate::matrix3d::Matrix3d;
use num_traits::{Zero, Float};
#[derive(Clone, Hash, PartialEq, 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, 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(Clone, Default, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SimpleTransform<T: CoordinateType> {
pub mirror: bool,
pub rotation: Angle,
pub magnification: T,
pub displacement: Vector<T>,
}
impl<T: CoordinateType> SimpleTransform<T> {
pub fn new(mirror: bool, rotation: Angle, magnification: T, displacement: Vector<T>) -> Self {
SimpleTransform {
mirror,
rotation,
magnification,
displacement,
}
}
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 scale(factor: T) -> Self {
Self::new(
false, Angle::R0,
factor, Vector::zero(),
)
}
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 transform_distance(&self, d: T) -> T {
d * self.magnification
}
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 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);
}