use crate::{angle::Angle, Geonum};
pub trait Affine {
fn translate(&self, displacement: &Self) -> Self;
fn shear(&self, shear_angle: Angle) -> Self;
fn area_quadrilateral(p1: &Self, p2: &Self, p3: &Self, p4: &Self) -> f64;
}
#[cfg(feature = "affine")]
impl Affine for Geonum {
fn translate(&self, displacement: &Geonum) -> Geonum {
*self + *displacement }
fn shear(&self, shear_angle: Angle) -> Geonum {
Geonum {
mag: self.mag,
angle: self.angle + shear_angle, }
}
fn area_quadrilateral(p1: &Geonum, p2: &Geonum, p3: &Geonum, p4: &Geonum) -> f64 {
let edge1 = *p2 + p1.negate(); let edge2 = *p3 + p1.negate(); let triangle1_area = edge1.wedge(&edge2).mag / 2.0;
let edge3 = *p3 + p1.negate(); let edge4 = *p4 + p1.negate(); let triangle2_area = edge3.wedge(&edge4).mag / 2.0;
triangle1_area + triangle2_area
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Geonum;
const EPSILON: f64 = 1e-10;
#[test]
fn it_preserves_grade_after_translation() {
let point = Geonum::new(5.0, 1.0, 6.0); let displacement = Geonum::new(3.0, 1.0, 2.0);
let translated = point.translate(&displacement);
assert_eq!(translated.angle.grade(), point.angle.grade()); }
#[test]
fn it_reverses_translation_with_inverse_displacement() {
let point = Geonum::new(5.0, 1.0, 6.0); let displacement = Geonum::new(3.0, 1.0, 2.0); let inverse = displacement.negate();
let translated = point.translate(&displacement);
let back = translated.translate(&inverse);
assert!(point.mag_diff(&back) < EPSILON);
assert!((point.angle - back.angle).rem() < EPSILON);
}
#[test]
fn it_preserves_mag_and_transforms_angle_after_shear() {
let point = Geonum::new(5.0, 1.0, 3.0); let shear_angle = Angle::new(1.0, 6.0); let sheared = point.shear(shear_angle);
assert!(point.mag_diff(&sheared) < EPSILON); let expected_angle = point.angle + shear_angle;
assert!((sheared.angle - expected_angle).rem() < EPSILON);
assert_eq!(point.angle.grade(), 0); assert_eq!(sheared.angle.grade(), 1); }
#[test]
fn it_preserves_parallelism_after_shear() {
let dir1 = Geonum::new(2.0, 0.0, 1.0); let dir2 = Geonum::new(3.0, 0.0, 1.0);
let shear_angle = Angle::new(1.0, 4.0); let sheared1 = dir1.shear(shear_angle);
let sheared2 = dir2.shear(shear_angle);
assert!((sheared1.angle - sheared2.angle).rem() < EPSILON);
}
#[test]
fn it_returns_12_for_4x3_rectangle_area() {
let v1 = Geonum::new(0.0, 0.0, 1.0); let v2 = Geonum::new(4.0, 0.0, 1.0); let v3 = Geonum::new_with_angle(5.0, Angle::new_from_cartesian(4.0, 3.0)); let v4 = Geonum::new(3.0, 1.0, 2.0);
let area = Geonum::area_quadrilateral(&v1, &v2, &v3, &v4);
assert!((area - 12.0).abs() < EPSILON); }
}