use crate::zzbase::ZZNum;
use crate::zzsigned::ZSigned;
use num_traits::Zero;
pub fn dot<ZZ: ZZNum>(p1: &ZZ, p2: &ZZ) -> ZZ::Real {
let (p1x, p1y) = p1.re_im();
let (p2x, p2y) = p2.re_im();
(p1x * p2x) + (p1y * p2y)
}
pub fn wedge<ZZ: ZZNum>(p1: &ZZ, p2: &ZZ) -> ZZ::Real {
let (p1x, p1y) = p1.re_im();
let (p2x, p2y) = p2.re_im();
(p1x * p2y) - (p1y * p2x)
}
pub fn norm<ZZ: ZZNum>(p: &ZZ) -> ZZ::Real {
dot::<ZZ>(&p, &p)
}
pub fn angle_between<ZZ: ZZNum>(p: &ZZ, (a, b): (&ZZ, &ZZ)) -> bool {
!wedge(a, p).is_negative() && !wedge(p, b).is_negative()
}
pub fn is_between<ZZ: ZZNum>(p: &ZZ, (a, b): (&ZZ, &ZZ)) -> bool {
let v = *a - *p;
let w = *p - *b;
wedge(&v, &w).is_zero() && dot(&v, &w).is_positive()
}
pub fn is_ccw<ZZ: ZZNum>(p: &ZZ, (a, b): (&ZZ, &ZZ)) -> bool {
wedge(&(*a - *p), &(*b - *p)).is_positive()
}
fn line_through<ZZ: ZZNum>(p1: &ZZ, p2: &ZZ) -> (ZZ::Real, ZZ::Real, ZZ::Real) {
((*p1 - *p2).im(), (*p2 - *p1).re(), wedge::<ZZ>(&p1, &p2))
}
fn is_colinear<ZZ: ZZNum>(p: &ZZ, (a, b, c): &(ZZ::Real, ZZ::Real, ZZ::Real)) -> bool {
(*a * p.re() + *b * p.im() + *c).is_zero()
}
pub fn intersect<ZZ: ZZNum>(&(a, b): &(ZZ, ZZ), &(c, d): &(ZZ, ZZ)) -> bool {
if a == c || a == d || b == c || b == d {
return false;
}
let l_ab = line_through(&a, &b);
if is_colinear(&c, &l_ab) && is_colinear(&d, &l_ab) {
is_between(&c, (&a, &b)) || is_between(&d, (&a, &b))
} else {
is_ccw(&a, (&c, &d)) != is_ccw(&b, (&c, &d)) && is_ccw(&a, (&b, &c)) != is_ccw(&a, (&b, &d))
}
}
pub fn point_in_rect<ZZ: ZZNum>(p: &ZZ, (pos_min, pos_max): (&ZZ, &ZZ), strict: bool) -> bool {
let (px, py) = p.re_im();
let (x_min, y_min) = pos_min.re_im();
let (x_max, y_max) = pos_max.re_im();
let x_in_open_bound = if strict {
(px - x_min).is_positive() && (x_max - px).is_positive()
} else {
!(px - x_min).is_negative() && !(x_max - px).is_negative()
};
let y_in_open_bound = if strict {
(py - y_min).is_positive() && (y_max - py).is_positive()
} else {
!(py - y_min).is_negative() && !(y_max - py).is_negative()
};
x_in_open_bound && y_in_open_bound
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::OneImag;
use crate::zz::{Z12, ZZ12};
use crate::zzbase::ZZBase;
use num_traits::{One, Zero};
type ZZi = ZZ12;
#[test]
fn test_dot() {
let mut p = ZZi::zero();
for i in 0..ZZi::turn() {
p = p + ZZi::unit(i).scale(i as i64);
}
let q: ZZi = norm(&p).into();
for i in 1..ZZi::turn() {
let pi = p * ZZi::unit(i);
let qi: ZZi = norm(&pi).into();
assert_eq!(qi, q);
}
}
#[test]
fn test_colinear() {
if !ZZi::has_qturn() {
return;
}
let a: ZZi = ZZi::zero();
let b: ZZi = ZZi::one();
let c: ZZi = ZZi::from(2);
let d: ZZi = ZZi::from(3);
let e: ZZi = ZZi::one_i();
let f: ZZi = b + e;
let l_ab = line_through(&a, &b);
let l_ac = line_through(&a, &c);
let l_af = line_through(&a, &f);
assert!(is_colinear(&b, &l_ac));
assert!(is_colinear(&d, &l_ac));
assert!(is_colinear(&c, &l_ab));
assert!(is_colinear(&d, &l_ab));
assert!(!is_colinear(&e, &l_ab));
assert!(!is_colinear(&f, &l_ab));
assert!(!(is_colinear(&a, &l_ab) && is_colinear(&e, &l_ab)));
assert!(!is_colinear(&b, &l_af));
assert!(!is_colinear(&d, &l_af));
}
#[test]
fn test_dot_extra() {
let p1 = ZZ12::one();
let p2 = ZZ12::from(2);
let p3 = ZZ12::from(3);
let pi = ZZ12::unit(3); let p60 = ZZ12::unit(2);
let pm60 = ZZ12::unit(-2);
assert_eq!(dot(&p1, &pi), Z12::zero());
assert_eq!(dot(&0.into(), &pi), Z12::zero());
assert_eq!(dot(&p2, &p3), Z12::from(6));
let d1 = ZZ12::from(dot(&p60, &pi).pow(2)).complex64();
assert_eq!(d1.re, 0.75);
assert_eq!(d1.im, 0.0);
let d2 = ZZ12::from(dot(&pm60, &pi)).complex64();
assert!(d2.re < 0.0);
assert_eq!(d2.im, 0.0);
assert_eq!(ZZ12::from(dot(&pm60, &pi).pow(2)).complex64().re, 0.75);
}
#[test]
fn test_is_between() {
let a: ZZi = ZZi::zero();
let b: ZZi = ZZi::one();
let c: ZZi = ZZi::from(2);
let e: ZZi = ZZi::unit(ZZi::hturn() / 2);
let f: ZZi = b + e;
let g: ZZi = ZZi::unit(1) + ZZi::unit(-1) - ZZi::one();
assert!(is_between(&b, (&a, &c)));
assert!(is_between(&g, (&a, &b)));
assert!(!is_between(&b, (&a, &b)));
assert!(!is_between(&a, (&a, &b)));
assert!(!is_between(&c, (&a, &b)));
assert!(!is_between(&f, (&a, &b)));
}
#[test]
fn test_intersect() {
let a: ZZ12 = ZZ12::zero();
let b: ZZ12 = ZZ12::one();
let c: ZZ12 = ZZ12::from(2);
let d: ZZ12 = ZZ12::from(3);
let e: ZZ12 = ZZ12::one_i();
let f: ZZ12 = b + e;
assert!(!intersect(&(a, b), &(c, d))); assert!(!intersect(&(d, c), &(a, b))); assert!(!intersect(&(a, b), &(b, c))); assert!(intersect(&(a, c), &(b, d))); assert!(intersect(&(a, d), &(b, c)));
assert!(!intersect(&(a, b), &(e, f)));
assert!(!intersect(&(a, e), &(b, f)));
assert!(!intersect(&(a, e), &(b, c))); assert!(!intersect(&(a, f), &(b, c)));
assert!(!intersect(&(a, e), &(a, b))); assert!(!intersect(&(a, f), &(a, b))); assert!(!intersect(&(a, e), &(e, e - b)));
assert!(intersect(&(b, f), &(a, c))); assert!(intersect(&(b, e), &(a, c)));
assert!(intersect(&(a, f), &(b, e))); assert!(intersect(&(a, f), &(c, e))); assert!(intersect(&(a, f), &(d, e))); }
#[test]
fn test_point_in_rect() {
use crate::traits::Ccw;
let min: ZZ12 = 0.into();
let max: ZZ12 = (2, 2).into();
assert!(point_in_rect(&(1, 1).into(), (&min, &max), true));
assert!(point_in_rect(&ZZ12::ccw(), (&min, &max), true));
assert!(!point_in_rect(&min, (&min, &max), true));
assert!(!point_in_rect(&max, (&min, &max), true));
assert!(point_in_rect(&min, (&min, &max), false));
assert!(point_in_rect(&max, (&min, &max), false));
assert!(!point_in_rect(&(3, 1).into(), (&min, &max), true));
}
}