use crate::point::Point; static NEAR_ZERO: f64 = 0.000001; use std::ops::Add; use std::ops::AddAssign; use std::ops::Mul; use std::ops::MulAssign; use std::ops::Sub; use std::ops::Div; use std::ops::DivAssign; //use std::ops::BitXor impl Add for Pt { type Output = Self; fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y, } } } impl AddAssign for Pt { fn add_assign(&mut self, other: Self) { self.x += other.x; self.y += other.y; } } impl Sub for Pt { type Output = Self; fn sub(self, other: Self) -> Self { Self { x: self.x - other.x, y: self.y - other.y, } } } impl MulAssign for Pt { fn mul_assign(&mut self, rhs: f64) { self.x *= rhs; self.y *= rhs; } } impl Mul for Pt { type Output = Self; fn mul(self, rhs: f64) -> Self { Pt { x: self.x * rhs, y: self.y * rhs, } } } impl Div for Pt { type Output = Self; fn div(self, rhs: f64) -> Self { Pt { x: self.x / rhs, y: self.y / rhs, } } } impl DivAssign for Pt { fn div_assign(&mut self, rhs: f64) { self.x /= rhs; self.y /= rhs; } } #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub struct Pt { pub x: f64, pub y: f64, } impl From for (f64, f64) { fn from(pt: Pt) -> Self { (pt.x, pt.y) } } impl From<(f64, f64)> for Pt { fn from((x, y): (f64, f64)) -> Self { Pt { x, y } } } impl From<&(f64, f64)> for Pt { fn from((x, y): &(f64, f64)) -> Self { Pt { x: *x, y: *y } } } impl Pt { pub fn new(x: f64, y: f64) -> Self { Self { x, y } } pub fn cross(&self, other: &Pt) -> f64 { self.x * other.x + self.y * other.y } pub fn mean(pts: &[Pt]) -> Pt { let mut mean = Pt{x: 0., y: 0.}; for pt in pts { mean += *pt; } mean / (pts.len() as f64) } } #[derive(Debug, Clone, Copy)] pub struct CartesianEquation { pub a: f64, pub b: f64, pub c: f64, } #[allow(dead_code)] impl CartesianEquation { pub fn new_from_tuple(((x0, y0), (x1, y1)): ((f64, f64), (f64, f64))) -> Self { let p0 = Pt { x: x0, y: y0 }; let p1 = Pt { x: x1, y: y1 }; CartesianEquation::new_from_pt(&p0, &p1) } pub fn new_from_pt(p0: &Pt, p1: &Pt) -> Self { let a = p0.y - p1.y; let b = p1.x - p0.x; let c = -(a * p0.x + b * p0.y); Self { a, b, c } } pub fn from_parametric_eq(pt: &Pt, dir: &Pt) -> Self { let p0 = pt.clone(); let p1 = Pt { x: pt.x + dir.x, y: pt.y + dir.y, }; CartesianEquation::new_from_pt(&p0, &p1) } pub fn new(a: f64, b: f64, c: f64) -> Self { Self { a, b, c } } pub fn is_similar(&self, other: &Self) -> bool { if self.b.abs() > NEAR_ZERO { let y0 = -self.a / self.b; let y1 = -other.a / other.b; return (y0 - y1).abs() <= NEAR_ZERO; } else { let x0 = -self.b / self.a; let x1 = -other.b / other.a; return (x0 - x1).abs() <= NEAR_ZERO; } } pub fn intersection(&self, other: &Self) -> Option { let (a0, b0, c0) = (self.a, self.b, self.c); let (a1, b1, c1) = (other.a, other.b, other.c); if self.is_similar(other) { return None; } let x = (b0 * (c0 - c1) - c0 * (b0 - b1)) / (b0 * (a1 - a0) + a0 * (b0 - b1)); let y = (a0 * (c0 - c1) - c0 * (a0 - a1)) / (a0 * (b1 - b0) + b0 * (a0 - a1)); Some(Pt { x, y }) } } pub struct EqAffine { pub a: f64, pub b: f64, pub dst: f64, } impl EqAffine { pub fn new(a0: f64, b0: f64, a1: f64, b1: f64) -> Self { let dx = a1 - a0; let dy = b1 - b0; let dst = (dx * dx + dy * dy).sqrt(); let a = dy / dx; let b = b0 - a * a0; EqAffine { a, b, dst } } pub fn get_val(&self, var: f64) -> f64 { self.a * var + self.b } pub fn get_val_dst(&self, var: f64) -> f64 { self.dst * self.get_val(var) } } //#[derive(Serialize,Deserialize,Debug,Clone,Copy)] #[derive(Debug, Clone, Copy)] pub struct Keystone { pub p0: Pt, pub p1: Pt, pub p2: Pt, pub p3: Pt, pub fx: Option, pub fy: Option, pub factor: f64, pub translate: f64, } #[allow(dead_code)] impl Keystone { pub fn new( pt0: (f64, f64), pt1: (f64, f64), pt2: (f64, f64), pt3: (f64, f64), factor: f64, translate: f64, ) -> Self { let eqx1 = CartesianEquation::new_from_pt(&Pt::from(pt0), &Pt::from(pt1)); let eqx2 = CartesianEquation::new_from_pt(&Pt::from(pt3), &Pt::from(pt2)); let eqy1 = CartesianEquation::new_from_pt(&Pt::from(pt0), &Pt::from(pt3)); let eqy2 = CartesianEquation::new_from_pt(&Pt::from(pt1), &Pt::from(pt2)); let fx = eqx1.intersection(&eqx2); let fy = eqy1.intersection(&eqy2); Keystone { p0: Pt::from(pt0), p1: Pt::from(pt1), p2: Pt::from(pt2), p3: Pt::from(pt3), fx, fy, factor, translate, } } pub fn transform(&self, pt: &Point) -> Point { let x = (pt.x as f64 + self.translate) / self.factor; let y = (pt.y as f64 + self.translate) / self.factor; let ptx: Pt = Pt { x: self.p0.x + x * (self.p1.x - self.p0.x), y: self.p0.y + x * (self.p1.y - self.p0.y), }; let pty: Pt = Pt { x: self.p0.x + y * (self.p3.x - self.p0.x), y: self.p0.y + y * (self.p3.y - self.p0.y), }; let eqx = if self.fx.is_some() { CartesianEquation::new_from_pt(&(self.fx.unwrap()), &pty) } else { let dir = Pt { x: self.p1.x - self.p0.x, y: self.p1.y - self.p0.y, }; CartesianEquation::from_parametric_eq(&pty, &dir) }; let eqy = if self.fy.is_some() { CartesianEquation::new_from_pt(&(self.fy.unwrap()), &ptx) } else { let dir = Pt { x: self.p3.x - self.p0.x, y: self.p3.y - self.p0.y, }; CartesianEquation::from_parametric_eq(&ptx, &dir) }; let intersection = eqx.intersection(&eqy).unwrap(); Point { x: intersection.x as f32, y: intersection.y as f32, ..*pt } } } //impl Transformers for Keystone { // fn apply(&self, point_list: &[Point]) -> Vec { // point_list.iter() // .map(| pt | { // self.transform(pt) // }).collect() // } //} #[cfg(test)] mod test_keystone_correction { use super::*; #[test] fn simple_y_focal() { let keystone = Keystone::new((0.1, 0.0), (0.9, 0.0), (1.0, 1.0), (0.0, 1.0), 1.0, 0.); let must_be = Pt::new(0.5, -4.0); let x_are_similar = (must_be.x - keystone.fy.unwrap().x).abs() < NEAR_ZERO; let y_are_similar = (must_be.y - keystone.fy.unwrap().y).abs() < NEAR_ZERO; assert!(x_are_similar); assert!(y_are_similar); assert_eq!(None, keystone.fx); } #[test] fn simple_x_focal() { let keystone = Keystone::new((0.0, 0.0), (1.0, 0.1), (1.0, 0.9), (0.0, 1.0), 1., 0.); let must_be = Pt::new(5., 0.5); let x_are_similar = (must_be.x - keystone.fx.unwrap().x).abs() < NEAR_ZERO; let y_are_similar = (must_be.y - keystone.fx.unwrap().y).abs() < NEAR_ZERO; println!("keystone:{:?}", keystone); assert!(x_are_similar); assert!(y_are_similar); assert_eq!(None, keystone.fy); } #[test] fn zero_correction() { // on change rien let keystone = Keystone::new((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), 1., 0.); let pt = Point::from((0.34, 0.2, 0)); let res = keystone.transform(&pt); assert!(((pt.x - res.x).abs() as f64) < NEAR_ZERO); assert!(((pt.y - res.y).abs() as f64) < NEAR_ZERO); } #[test] fn x_correction_1() { let keystone = Keystone::new((0.1, 0.0), (0.9, 0.0), (1.0, 1.0), (0.0, 1.0), 1.0, 0.); let pt = Point::from((0.1, 0., 0)); let res = keystone.transform(&pt); let must_be = Point::from((0.18, 0., 0)); assert!(((must_be.x - res.x).abs() as f64) < NEAR_ZERO); assert!(((must_be.y - res.y).abs() as f64) < NEAR_ZERO); } #[test] fn x_correction_2() { let keystone = Keystone::new((0.0, 0.0), (0.9, 0.0), (1.0, 1.0), (0.0, 1.0), 1.0, 0.); let pt = Point::from((0.1, 0.1, 0)); let res = keystone.transform(&pt); let must_be = Point::from((0.091, 0.1, 0)); assert!(((must_be.x - res.x).abs() as f64) < NEAR_ZERO); assert!(((must_be.y - res.y).abs() as f64) < NEAR_ZERO); } #[test] fn y_correction_1() { let keystone = Keystone::new((0.0, 0.0), (1.0, 0.1), (1.0, 0.9), (0.0, 1.0), 1.0, 0.); let pt = Point::from((0., 0.1, 0)); let res = keystone.transform(&pt); let must_be = Point::from((0., 0.1, 0)); assert!(((must_be.x - res.x).abs() as f64) < NEAR_ZERO); assert!(((must_be.y - res.y).abs() as f64) < NEAR_ZERO); } #[test] fn y_correction_2() { let keystone = Keystone::new((0.0, 0.0), (1.0, 0.0), (1.0, 0.9), (0.0, 1.0), 1.0, 0.); let pt = Point::from((0.1, 0.1, 0)); let res = keystone.transform(&pt); let must_be = Point::from((0.1, 0.099, 0)); assert!(((must_be.x - res.x).abs() as f64) < NEAR_ZERO); assert!(((must_be.y - res.y).abs() as f64) < NEAR_ZERO); } } #[cfg(test)] mod test_cartesian_equation { use super::*; #[test] fn similar_eq_1() { let p0a = Pt::new(0.1, 0.0); let p1a = Pt::new(0.9, 0.0); let eq0 = CartesianEquation::new_from_pt(&p0a, &p1a); let p0b = Pt::new(0.0, 1.0); let p1b = Pt::new(1.0, 1.0); let eq1 = CartesianEquation::new_from_pt(&p0b, &p1b); assert!(eq0.is_similar(&eq1)); } #[test] fn similar_eq_2() { let p0a = Pt::new(0.0, 0.0); let p1a = Pt::new(0.0, 1.0); let eq0 = CartesianEquation::new_from_pt(&p0a, &p1a); let p0b = Pt::new(1.0, 0.1); let p1b = Pt::new(1.0, 0.9); let eq1 = CartesianEquation::new_from_pt(&p0b, &p1b); assert!(eq0.is_similar(&eq1)); } #[test] fn paralle_value() { let (p0, p1) = (Pt { x: 0., y: 0. }, Pt { x: 1., y: 4. }); let (p2, p3) = (Pt { x: 1., y: 3. }, Pt { x: 0., y: -1. }); let eq0 = CartesianEquation::new_from_pt(&p0, &p1); let eq1 = CartesianEquation::new_from_pt(&p2, &p3); let res = eq0.intersection(&eq1); assert_eq!(res, None); } #[test] fn test_intersection() { let d0 = Pt { x: 1., y: 1. }; let p0 = Pt { x: 0., y: 0. }; let d1 = Pt { x: -1., y: 1. }; let p1 = Pt { x: 4., y: 0. }; let must_be = Pt { x: 2., y: 2. }; let eq0 = CartesianEquation::from_parametric_eq(&p0, &d0); let eq1 = CartesianEquation::from_parametric_eq(&p1, &d1); let intersect = eq0.intersection(&eq1).unwrap(); assert_eq!(must_be, intersect); } }