use std::ops::{Add, Mul};
use num_traits::Float;
use crate::{
algebra::matrix::MatrixTag,
space::AffineTransform,
Matrix, Triangle, Vector,
};
impl<T, const N: usize, Tag> Mul<Triangle<T, N>> for Matrix<T, N, Tag>
where
T: Float + std::iter::Sum,
Tag: MatrixTag,
{
type Output = Triangle<T, N>;
fn mul(self, rhs: Triangle<T, N>) -> Self::Output {
let (a, b, c) = rhs.vertices();
Triangle::from((self * a, self * b, self * c))
}
}
impl<T, const N: usize, Tag> Mul<Triangle<T, N>> for AffineTransform<T, N, Tag>
where
T: Float + std::iter::Sum,
Tag: MatrixTag,
{
type Output = Triangle<T, N>;
fn mul(self, rhs: Triangle<T, N>) -> Self::Output {
let (a, b, c) = rhs.vertices();
Triangle::from((self * a, self * b, self * c))
}
}
impl<T, const N: usize> Add<Vector<T, N>> for Triangle<T, N>
where
T: Float + std::iter::Sum,
{
type Output = Triangle<T, N>;
fn add(self, rhs: Vector<T, N>) -> Self::Output {
let (a, b, c) = self.vertices();
Triangle::from((a + rhs, b + rhs, c + rhs))
}
}
#[cfg(test)]
mod tests {
use std::f64::consts::FRAC_PI_2;
use crate::algebra::matrix::Isometry;
use crate::space::AffineTransform;
use crate::{Angle, Matrix, Point, Triangle, Vector};
fn tri_2d_01() -> Triangle<f64, 2> {
Triangle::new([
Point::new([0.0, 0.0]),
Point::new([1.0, 0.0]),
Point::new([0.0, 1.0]),
])
}
#[test]
fn mul_matrix_identity_preserves_triangle_2d() {
let i = Matrix::<f64, 2, Isometry>::identity();
let tri = tri_2d_01();
let out = i * tri;
assert_eq!(out.a().coords_ref(), tri.a().coords_ref());
assert_eq!(out.b().coords_ref(), tri.b().coords_ref());
assert_eq!(out.c().coords_ref(), tri.c().coords_ref());
}
#[test]
fn mul_matrix_identity_preserves_triangle_3d() {
let i = Matrix::<f64, 3, Isometry>::identity();
let tri = Triangle::new([
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
]);
let out = i * tri;
assert_eq!(out.a().coords_ref(), tri.a().coords_ref());
assert_eq!(out.b().coords_ref(), tri.b().coords_ref());
assert_eq!(out.c().coords_ref(), tri.c().coords_ref());
}
#[test]
fn mul_matrix_rotation_2d_rotates_vertices_preserves_area() {
let angle = Angle::<f64>::from_radians(FRAC_PI_2);
let r = Matrix::<f64, 2, Isometry>::rotation_2d(angle);
let tri = tri_2d_01();
let out = r * tri;
assert!((out.a().coords_ref()[0] - 0.0).abs() < 1e-10);
assert!((out.a().coords_ref()[1] - 0.0).abs() < 1e-10);
assert!((out.b().coords_ref()[0] - 0.0).abs() < 1e-10);
assert!((out.b().coords_ref()[1] - 1.0).abs() < 1e-10);
assert!((out.c().coords_ref()[0] - (-1.0)).abs() < 1e-10);
assert!((out.c().coords_ref()[1] - 0.0).abs() < 1e-10);
assert!(
(out.area() - tri.area()).abs() < 1e-10,
"isometry must preserve area"
);
}
#[test]
fn add_vector_translates_all_vertices() {
let tri = tri_2d_01();
let v = Vector::new([5.0, 10.0]);
let out = tri + v;
assert_eq!(out.a().coords_ref(), &[5.0, 10.0]);
assert_eq!(out.b().coords_ref(), &[6.0, 10.0]);
assert_eq!(out.c().coords_ref(), &[5.0, 11.0]);
}
#[test]
fn add_vector_3d() {
let tri = Triangle::new([
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
Point::new([0.0, 0.0, 1.0]),
]);
let v = Vector::new([0.0, 0.0, 1.0]);
let out = tri + v;
assert_eq!(out.a().coords_ref(), &[1.0, 0.0, 1.0]);
assert_eq!(out.b().coords_ref(), &[0.0, 1.0, 1.0]);
assert_eq!(out.c().coords_ref(), &[0.0, 0.0, 2.0]);
}
#[test]
fn mul_affine_identity_plus_translation_moves_vertices() {
let tr = AffineTransform::new(
Matrix::<f64, 2, Isometry>::identity(),
Vector::new([10.0, 20.0]),
);
let tri = tri_2d_01();
let out = tr * tri;
assert_eq!(out.a().coords_ref(), &[10.0, 20.0]);
assert_eq!(out.b().coords_ref(), &[11.0, 20.0]);
assert_eq!(out.c().coords_ref(), &[10.0, 21.0]);
}
#[test]
fn mul_affine_rotation_and_translation_2d() {
let angle = Angle::<f64>::from_radians(FRAC_PI_2);
let linear = Matrix::<f64, 2, Isometry>::rotation_2d(angle);
let tr = AffineTransform::new(linear, Vector::new([1.0, 0.0]));
let tri = tri_2d_01();
let out = tr * tri;
assert!((out.a().coords_ref()[0] - 1.0).abs() < 1e-10);
assert!((out.a().coords_ref()[1] - 0.0).abs() < 1e-10);
assert!((out.b().coords_ref()[0] - 1.0).abs() < 1e-10);
assert!((out.b().coords_ref()[1] - 1.0).abs() < 1e-10);
assert!((out.c().coords_ref()[0] - 0.0).abs() < 1e-10);
assert!((out.c().coords_ref()[1] - 0.0).abs() < 1e-10);
}
}