use std::cmp::Ordering;
use super::linalg::{dot_sign, is_between, is_ccw, wedge_sign};
use super::traits::{HasZZ4, IntersectUnitSegments, IsRing, OneImag};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PointLocation {
Inside,
Outside,
Boundary,
}
pub fn point_in_polygon<ZZ: IsRing>(q: &ZZ, polygon: &[ZZ]) -> PointLocation {
let n = polygon.len();
assert!(n >= 3, "point_in_polygon: need at least 3 vertices");
for v in polygon {
if q == v {
return PointLocation::Boundary;
}
}
for i in 0..n {
let a = &polygon[i];
let b = &polygon[(i + 1) % n];
let edge = *b - *a;
let to_q = *q - *a;
if wedge_sign(&edge, &to_q) == 0 && is_between(q, (a, b)) {
return PointLocation::Boundary;
}
}
let mut wn: i32 = 0;
for i in 0..n {
let a = &polygon[i];
let b = &polygon[(i + 1) % n];
let da = (*a - *q).im_sign();
let db = (*b - *q).im_sign();
if da <= 0 && db > 0 {
let edge = *b - *a;
let to_q = *q - *a;
if wedge_sign(&edge, &to_q) > 0 {
wn += 1;
}
} else if da > 0 && db <= 0 {
let edge = *b - *a;
let to_q = *q - *a;
if wedge_sign(&edge, &to_q) < 0 {
wn -= 1;
}
}
}
if wn != 0 {
PointLocation::Inside
} else {
PointLocation::Outside
}
}
pub fn cmp_xy<ZZ: IsRing>(a: &ZZ, b: &ZZ) -> Ordering {
let d = *a - *b;
match d.re_sign().cmp(&0) {
Ordering::Equal => d.im_sign().cmp(&0),
ord => ord,
}
}
pub fn convex_hull<ZZ: IsRing>(points: &[ZZ]) -> Vec<usize> {
assert!(!points.is_empty(), "convex_hull: empty input");
let n = points.len();
if n <= 2 {
return (0..n).collect();
}
let mut idx: Vec<usize> = (0..n).collect();
idx.sort_by(|&i, &j| cmp_xy(&points[i], &points[j]));
let mut hull: Vec<usize> = Vec::with_capacity(n);
for &pi in &idx {
while hull.len() >= 2 {
let a = hull[hull.len() - 2];
let b = hull[hull.len() - 1];
if wedge_sign(&(points[b] - points[a]), &(points[pi] - points[b])) >= 0 {
hull.pop();
} else {
break;
}
}
hull.push(pi);
}
let lower_len = hull.len() + 1;
for &pi in idx.iter().rev() {
while hull.len() >= lower_len {
let a = hull[hull.len() - 2];
let b = hull[hull.len() - 1];
if wedge_sign(&(points[b] - points[a]), &(points[pi] - points[b])) >= 0 {
hull.pop();
} else {
break;
}
}
hull.push(pi);
}
hull.pop();
hull
}
pub fn hull_labels<ZZ: IsRing>(points: &[ZZ]) -> Vec<bool> {
let hull = convex_hull(points);
let mut labels = vec![false; points.len()];
for idx in hull {
labels[idx] = true;
}
labels
}
pub fn is_colinear<ZZ: IsRing>(p: &ZZ, a: &ZZ, b: &ZZ) -> bool {
wedge_sign(&(*b - *a), &(*p - *a)) == 0
}
pub fn lines_parallel<ZZ: IsRing>((a, b): (&ZZ, &ZZ), (c, d): (&ZZ, &ZZ)) -> bool {
wedge_sign(&(*b - *a), &(*d - *c)) == 0
}
pub fn lines_perp<ZZ: IsRing>((a, b): (&ZZ, &ZZ), (c, d): (&ZZ, &ZZ)) -> bool {
dot_sign(&(*b - *a), &(*d - *c)) == 0
}
pub fn intersect<ZZ: IsRing + PartialEq>(&(a, b): &(ZZ, ZZ), &(c, d): &(ZZ, ZZ)) -> bool {
if a == c || a == d || b == c || b == d {
return false;
}
let c_on_ab = is_colinear(&c, &a, &b);
let d_on_ab = is_colinear(&d, &a, &b);
if c_on_ab && d_on_ab {
return is_between(&c, (&a, &b)) || is_between(&d, (&a, &b));
}
if c_on_ab && is_between(&c, (&a, &b)) {
return true;
}
if d_on_ab && is_between(&d, (&a, &b)) {
return true;
}
if is_colinear(&a, &c, &d) && is_between(&a, (&c, &d)) {
return true;
}
if is_colinear(&b, &c, &d) && is_between(&b, (&c, &d)) {
return true;
}
is_ccw(&a, (&c, &d)) != is_ccw(&b, (&c, &d)) && is_ccw(&a, (&b, &c)) != is_ccw(&a, (&b, &d))
}
#[inline]
pub fn intersect_unit_segments<ZZ: IntersectUnitSegments>(s1: &(ZZ, ZZ), s2: &(ZZ, ZZ)) -> bool {
ZZ::intersect_unit_segments(s1, s2)
}
pub fn rect_signs<ZZ: IsRing>(p: &ZZ, pos_min: &ZZ, pos_max: &ZZ) -> [i8; 4] {
let dlo = *p - *pos_min;
let dhi = *pos_max - *p;
[dlo.re_sign(), dlo.im_sign(), dhi.re_sign(), dhi.im_sign()]
}
pub fn point_in_rect<ZZ: IsRing>(p: &ZZ, (pos_min, pos_max): &(ZZ, ZZ), strict: bool) -> bool {
let signs = rect_signs(p, pos_min, pos_max);
if strict {
signs.iter().all(|&s| s > 0)
} else {
signs.iter().all(|&s| s >= 0)
}
}
pub fn point_mod_rect<ZZ: HasZZ4>(p: &ZZ, anchor: &ZZ, (width, height): (i64, i64)) -> ZZ {
let w_step: ZZ = ZZ::from(width);
let h_step: ZZ = ZZ::one_i() * ZZ::from(height);
let upper: ZZ = *anchor + w_step + h_step;
let mut ret = *p;
let mut was_less = false;
while (ret - *anchor).re_sign() < 0 {
was_less = true;
ret = ret + w_step;
}
if !was_less {
while (upper - ret).re_sign() < 0 {
ret = ret - w_step;
}
}
let mut was_less = false;
while (ret - *anchor).im_sign() < 0 {
was_less = true;
ret = ret + h_step;
}
if !was_less {
while (upper - ret).im_sign() < 0 {
ret = ret - h_step;
}
}
ret
}
pub fn point_in_int_bbox<ZZ: IsRing>(
p: &ZZ,
(x_min, y_min): (i64, i64),
(x_max, y_max): (i64, i64),
) -> bool {
let (cx, cy) = p.cell_floor();
x_min <= cx && cx < x_max && y_min <= cy && cy < y_max
}
#[cfg(test)]
mod tests {
use num_traits::{One, Zero};
use super::super::rings::ZZ12;
use super::super::traits::{Ccw, OneImag, Units};
use super::*;
type ZZi = ZZ12;
#[test]
fn test_intersect_zz12_t_touch_endpoint_on_segment() {
let omega = ZZ12::ccw();
let v1 = -omega;
let v2 = -ZZ12::one() - omega + omega * omega;
let v5 = -ZZ12::from(2);
let v6 = v5 + omega;
assert_eq!(
wedge_sign(&(v6 - v5), &(v2 - v5)),
0,
"V_2 should be colinear with V_5-V_6"
);
assert!(
is_between(&v2, (&v5, &v6)),
"V_2 should be strictly interior to V_5-V_6"
);
assert_ne!(v1, v5);
assert_ne!(v1, v6);
assert_ne!(v2, v5);
assert_ne!(v2, v6);
assert!(
intersect(&(v1, v2), &(v5, v6)),
"generic intersect missed T-touch (V_2 lies on V_5-V_6 interior)"
);
assert!(
intersect_unit_segments(&(v1, v2), &(v5, v6)),
"intersect_unit_segments missed T-touch (V_2 lies on V_5-V_6 interior)"
);
}
#[test]
fn test_cmp_xy() {
let a: ZZi = ZZi::zero();
let b: ZZi = ZZi::one();
let e: ZZi = ZZi::one_i();
assert_eq!(cmp_xy(&a, &a), Ordering::Equal);
assert_eq!(cmp_xy(&a, &b), Ordering::Less);
assert_eq!(cmp_xy(&b, &a), Ordering::Greater);
assert_eq!(cmp_xy(&a, &e), Ordering::Less);
assert_eq!(cmp_xy(&e, &b), Ordering::Less);
}
#[test]
fn test_convex_hull_triangle() {
let pts: Vec<ZZi> = vec![ZZi::zero(), ZZi::one(), ZZi::one_i()];
let hull = convex_hull(&pts);
assert_eq!(hull.len(), 3);
}
#[test]
fn test_convex_hull_square() {
let pts: Vec<ZZi> = vec![
ZZi::zero(),
ZZi::one(),
ZZi::one() + ZZi::one_i(),
ZZi::one_i(),
];
let hull = convex_hull(&pts);
assert_eq!(hull.len(), 4);
}
#[test]
fn test_convex_hull_colinear_excluded() {
let pts: Vec<ZZi> = vec![
ZZi::zero(), ZZi::one(), ZZi::from(2), ZZi::from(2) + ZZi::one_i(), ZZi::from(2) + ZZi::from(2) * ZZi::one_i(), ZZi::one() + ZZi::from(2) * ZZi::one_i(), ZZi::from(2) * ZZi::one_i(), ZZi::one_i(), ];
let hull = convex_hull(&pts);
assert_eq!(hull.len(), 4);
let hull_set: std::collections::HashSet<usize> = hull.into_iter().collect();
assert!(hull_set.contains(&0));
assert!(hull_set.contains(&2));
assert!(hull_set.contains(&4));
assert!(hull_set.contains(&6));
assert!(!hull_set.contains(&1));
assert!(!hull_set.contains(&3));
assert!(!hull_set.contains(&5));
assert!(!hull_set.contains(&7));
}
use crate::geom::snake::Snake;
use crate::geom::tiles;
fn snake_hull_labels<T: IsRing>(snake: &Snake<T>) -> Vec<bool> {
assert!(snake.is_closed());
let n = snake.len();
hull_labels(&snake.representative()[..n])
}
fn validate_hull_labels<T: IsRing>(snake: &Snake<T>, labels: &[bool]) {
let n = snake.len();
assert_eq!(labels.len(), n);
let hull_indices: Vec<usize> = (0..n).filter(|&i| labels[i]).collect();
assert!(!hull_indices.is_empty(), "hull must be non-empty");
let pts = &snake.representative()[..n];
let h = &hull_indices;
for k in 0..h.len() {
let a = h[k];
let b = h[(k + 1) % h.len()];
let c = h[(k + 2) % h.len()];
let w = wedge_sign(&(pts[b] - pts[a]), &(pts[c] - pts[b]));
assert!(
w > 0,
"hull not strictly convex at hull vertices [{a},{b},{c}], wedge={w}"
);
}
for i in 0..n {
if labels[i] {
continue;
}
let v = pts[i];
for k in 0..h.len() {
let a = pts[h[k]];
let b = pts[h[(k + 1) % h.len()]];
let w = wedge_sign(&(b - a), &(v - a));
assert!(
w >= 0,
"non-hull vertex {i} is outside hull edge [{}..{}], wedge={w}",
h[k],
h[(k + 1) % h.len()]
);
}
}
}
#[test]
fn test_hull_labels_hexagon_all_on_hull() {
let hex: Snake<ZZ12> = tiles::hexagon();
let labels = snake_hull_labels(&hex);
assert_eq!(labels.len(), 6);
assert!(
labels.iter().all(|&b| b),
"all hex vertices should be on hull"
);
validate_hull_labels(&hex, &labels);
}
#[test]
fn test_hull_labels_triangle_all_on_hull() {
let tri: Snake<ZZ12> = tiles::triangle();
let labels = snake_hull_labels(&tri);
assert_eq!(labels.len(), 3);
assert!(labels.iter().all(|&b| b));
validate_hull_labels(&tri, &labels);
}
#[test]
fn test_hull_labels_square_all_on_hull() {
let sq: Snake<ZZ12> = tiles::square();
let labels = snake_hull_labels(&sq);
assert_eq!(labels.len(), 4);
assert!(labels.iter().all(|&b| b));
validate_hull_labels(&sq, &labels);
}
#[test]
fn test_hull_labels_rect_colinear_intermediates() {
let rect: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 0, 3, 0, 3, 0]).unwrap();
let labels = snake_hull_labels(&rect);
assert_eq!(labels.len(), 8);
assert!(labels[0], "(0,0) should be on hull");
assert!(!labels[1], "(1,0) colinear intermediate");
assert!(labels[2], "(2,0) should be on hull");
assert!(!labels[3], "(2,1) colinear intermediate");
assert!(labels[4], "(2,2) should be on hull");
assert!(!labels[5], "(1,2) colinear intermediate");
assert!(labels[6], "(0,2) should be on hull");
assert!(!labels[7], "(0,1) colinear intermediate");
validate_hull_labels(&rect, &labels);
}
#[test]
fn test_hull_labels_l_shape() {
let l: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 3, -3, 3, 3, 0]).unwrap();
assert!(l.is_closed());
let labels = snake_hull_labels(&l);
assert_eq!(labels.len(), 8);
assert!(labels[0], "(0,0) on hull");
assert!(!labels[1], "(1,0) colinear on bottom edge");
assert!(labels[2], "(2,0) on hull");
assert!(labels[3], "(2,1) on hull");
assert!(!labels[4], "(1,1) concave corner");
assert!(labels[5], "(1,2) on hull");
assert!(labels[6], "(0,2) on hull");
assert!(!labels[7], "(0,1) colinear on left edge");
validate_hull_labels(&l, &labels);
}
#[test]
fn test_hull_labels_longer_rect_multiple_colinear() {
let rect: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 0, 3, 0, 3, 0, 0, 3, 0]).unwrap();
assert!(rect.is_closed());
let labels = snake_hull_labels(&rect);
assert_eq!(labels.len(), 10);
assert!(labels[0]);
assert!(!labels[1], "(1,0) colinear");
assert!(!labels[2], "(2,0) colinear");
assert!(labels[3], "(3,0) corner");
assert!(!labels[4], "(3,1) colinear");
assert!(labels[5], "(3,2) corner");
assert!(!labels[6], "(2,2) colinear");
assert!(!labels[7], "(1,2) colinear");
assert!(labels[8], "(0,2) corner");
assert!(!labels[9], "(0,1) colinear");
validate_hull_labels(&rect, &labels);
}
#[test]
fn test_hull_labels_spectre() {
let spec: Snake<ZZ12> = tiles::spectre();
let labels = snake_hull_labels(&spec);
assert_eq!(labels.len(), 14);
let hull_count = labels.iter().filter(|&&b| b).count();
assert!(
hull_count < 14,
"spectre is non-convex, some vertices must be non-hull"
);
assert!(hull_count >= 4, "hull should have at least 4 vertices");
validate_hull_labels(&spec, &labels);
}
#[test]
fn test_hull_labels_cyclic_invariant() {
use crate::geom::rat::Rat;
let hex: Snake<ZZ12> = tiles::hexagon();
let labels_base = snake_hull_labels(&hex);
let rat: Rat<ZZ12> = Rat::from_unchecked(&hex);
for shift in 0..6 {
let shifted = rat.cycled(shift);
let shifted_snake: Snake<ZZ12> = Snake::from_slice_unsafe(shifted.seq());
let labels_shifted = snake_hull_labels(&shifted_snake);
let hull_count_base = labels_base.iter().filter(|&&b| b).count();
let hull_count_shifted = labels_shifted.iter().filter(|&&b| b).count();
assert_eq!(
hull_count_base, hull_count_shifted,
"hull vertex count changed at shift {shift}"
);
validate_hull_labels(&shifted_snake, &labels_shifted);
}
}
#[test]
fn test_hull_labels_cross_ring() {
use crate::cyclotomic::ZZ24;
use crate::geom::tiles as tiles24;
let hex12: Snake<ZZ12> = tiles::hexagon();
let hex24: Snake<ZZ24> = tiles24::hexagon();
let labels12 = snake_hull_labels(&hex12);
let labels24 = snake_hull_labels(&hex24);
assert_eq!(
labels12, labels24,
"same shape should give same hull labels"
);
validate_hull_labels(&hex12, &labels12);
validate_hull_labels(&hex24, &labels24);
}
#[test]
fn test_hull_labels_u_shape() {
let u: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 0, 3, 0, 3, 3, -3, -3, 3, 3, 0]).unwrap();
assert!(u.is_closed());
let labels = snake_hull_labels(&u);
assert_eq!(labels.len(), 12);
assert!(labels[0], "(0,0) BL corner");
assert!(!labels[1], "(1,0) colinear bottom");
assert!(!labels[2], "(2,0) colinear bottom");
assert!(labels[3], "(3,0) BR corner");
assert!(!labels[4], "(3,1) colinear right");
assert!(labels[5], "(3,2) TR corner");
assert!(!labels[6], "(2,2) colinear top");
assert!(!labels[7], "(2,1) concave inner");
assert!(!labels[8], "(1,1) concave inner");
assert!(!labels[9], "(1,2) colinear top");
assert!(labels[10], "(0,2) TL corner");
assert!(!labels[11], "(0,1) colinear left");
let hull_count = labels.iter().filter(|&&b| b).count();
assert_eq!(hull_count, 4, "U-shape hull should have exactly 4 vertices");
validate_hull_labels(&u, &labels);
}
#[test]
fn test_hull_labels_u_shape_cyclic_invariant() {
use crate::geom::rat::Rat;
let u: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 0, 3, 0, 3, 3, -3, -3, 3, 3, 0]).unwrap();
let labels_base = snake_hull_labels(&u);
let hull_count_base = labels_base.iter().filter(|&&b| b).count();
assert_eq!(hull_count_base, 4);
let rat: Rat<ZZ12> = Rat::from_unchecked(&u);
for shift in 0..12 {
let shifted = rat.cycled(shift);
let shifted_snake: Snake<ZZ12> = Snake::from_slice_unsafe(shifted.seq());
let labels_shifted = snake_hull_labels(&shifted_snake);
let hull_count_shifted = labels_shifted.iter().filter(|&&b| b).count();
assert_eq!(
hull_count_base, hull_count_shifted,
"hull vertex count changed at shift {shift}"
);
validate_hull_labels(&shifted_snake, &labels_shifted);
}
}
fn snake_points<T: IsRing>(snake: &Snake<T>) -> Vec<T> {
let n = snake.len();
snake.representative()[..n].to_vec()
}
#[test]
fn test_pip_rect_interior() {
let rect: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 0, 3, 0, 3, 0]).unwrap();
let pts = snake_points(&rect);
assert_eq!(
point_in_polygon(&(ZZ12::one() + ZZ12::one_i()), &pts),
PointLocation::Inside,
"(1,1) should be Inside the 2x2 rect"
);
}
#[test]
fn test_pip_rect_exterior() {
let rect: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 0, 3, 0, 3, 0]).unwrap();
let pts = snake_points(&rect);
let far: ZZ12 = ZZ12::from(5) + ZZ12::from(5) * ZZ12::one_i();
assert_eq!(
point_in_polygon(&far, &pts),
PointLocation::Outside,
"(5,5) should be Outside"
);
let below: ZZ12 = -ZZ12::one_i();
assert_eq!(
point_in_polygon(&below, &pts),
PointLocation::Outside,
"(0,-1) should be Outside"
);
}
#[test]
fn test_pip_square_boundary_vertex() {
let sq: Snake<ZZ12> = tiles::square();
let pts = snake_points(&sq);
for i in 0..pts.len() {
assert_eq!(
point_in_polygon(&pts[i], &pts),
PointLocation::Boundary,
"square vertex {i} should be Boundary"
);
}
}
#[test]
fn test_pip_hex_boundary_vertex() {
let hex: Snake<ZZ12> = tiles::hexagon();
let pts = snake_points(&hex);
for i in 0..pts.len() {
assert_eq!(
point_in_polygon(&pts[i], &pts),
PointLocation::Boundary,
"hex vertex {i} should be Boundary"
);
}
}
#[test]
fn test_pip_triangle_boundary_vertex() {
let tri: Snake<ZZ12> = tiles::triangle();
let pts = snake_points(&tri);
for i in 0..pts.len() {
assert_eq!(
point_in_polygon(&pts[i], &pts),
PointLocation::Boundary,
"triangle vertex {i} should be Boundary"
);
}
}
#[test]
fn test_pip_rect_colinear_vertex_boundary() {
let rect: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 0, 3, 0, 3, 0]).unwrap();
let pts = snake_points(&rect);
assert_eq!(
point_in_polygon(&ZZ12::one(), &pts),
PointLocation::Boundary,
"(1,0) is a polygon vertex, must be Boundary"
);
}
#[test]
fn test_pip_u_shape_notch_vertices_are_boundary() {
let u: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 0, 3, 0, 3, 3, -3, -3, 3, 3, 0]).unwrap();
let pts = snake_points(&u);
for i in 0..pts.len() {
assert_eq!(
point_in_polygon(&pts[i], &pts),
PointLocation::Boundary,
"U vertex {i} should be Boundary"
);
}
}
#[test]
fn test_pip_u_shape_exterior() {
let u: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 0, 3, 0, 3, 3, -3, -3, 3, 3, 0]).unwrap();
let pts = snake_points(&u);
let far: ZZ12 = ZZ12::from(5) + ZZ12::from(5) * ZZ12::one_i();
assert_eq!(
point_in_polygon(&far, &pts),
PointLocation::Outside,
"(5,5) should be Outside the U"
);
}
#[test]
fn test_pip_l_shape_vertices_boundary() {
let l: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 3, -3, 3, 3, 0]).unwrap();
let pts = snake_points(&l);
for i in 0..pts.len() {
assert_eq!(
point_in_polygon(&pts[i], &pts),
PointLocation::Boundary,
"L vertex {i} should be Boundary"
);
}
}
#[test]
fn test_pip_cyclic_invariant() {
use crate::geom::rat::Rat;
let rect: Snake<ZZ12> = Snake::try_from(&[0i8, 0, 3, 0, 3, 0, 3, 0]).unwrap();
let rat: Rat<ZZ12> = Rat::from_unchecked(&rect);
for shift in 0..8 {
let shifted = rat.cycled(shift);
let shifted_snake: Snake<ZZ12> = Snake::from_slice_unsafe(shifted.seq());
let shifted_pts = snake_points(&shifted_snake);
for j in 0..shifted_pts.len() {
assert_eq!(
point_in_polygon(&shifted_pts[j], &shifted_pts),
PointLocation::Boundary,
"shift {shift}, vertex {j}: must be Boundary"
);
}
}
}
fn get_test_points() -> (ZZi, ZZi, ZZi, ZZi, ZZi, ZZi) {
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;
(a, b, c, d, e, f)
}
#[test]
fn test_colinear_parallel_perp() {
let (a, b, c, d, e, f) = get_test_points();
assert!(is_colinear(&b, &a, &c));
assert!(is_colinear(&d, &a, &c));
assert!(is_colinear(&c, &a, &b));
assert!(is_colinear(&d, &a, &b));
assert!(!is_colinear(&e, &a, &b));
assert!(!is_colinear(&f, &a, &b));
assert!(!(is_colinear(&a, &a, &b) && is_colinear(&e, &a, &b)));
assert!(!is_colinear(&b, &a, &f));
assert!(!is_colinear(&d, &a, &f));
assert!(lines_parallel::<ZZi>((&a, &b), (&a, &c)));
assert!(!lines_parallel::<ZZi>((&a, &b), (&a, &f)));
assert!(lines_perp::<ZZi>((&a, &b), (&a, &e)));
assert!(!lines_perp::<ZZi>((&a, &b), (&a, &f)));
}
#[test]
fn test_intersect() {
let (a, b, c, d, e, f) = get_test_points();
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_intersect_unit_segments_matches_intersect() {
let zero: ZZi = ZZi::zero();
let one: ZZi = ZZi::one();
let mi_one: ZZi = -one;
let one_i: ZZi = ZZi::one_i();
let mi_i: ZZi = -one_i;
let one_plus_i: ZZi = one + one_i;
let centers = [zero, one, mi_one, one_i, mi_i, one_plus_i];
let mut unit_segs: Vec<(ZZi, ZZi)> = Vec::new();
for &c in ¢ers {
for k in 0..12i8 {
let u = <ZZi as Units>::unit(k);
unit_segs.push((c, c + u));
}
}
for s1 in &unit_segs {
for s2 in &unit_segs {
let general = intersect(s1, s2);
let specialized = intersect_unit_segments(s1, s2);
assert_eq!(
general, specialized,
"mismatch for s1={s1:?}, s2={s2:?}: general={general} specialized={specialized}"
);
}
}
}
#[test]
fn test_point_in_rect() {
let rect @ (min, max): (ZZ12, ZZ12) = (0.into(), (2, 2).into());
assert!(point_in_rect(&(1, 1).into(), &rect, true));
assert!(point_in_rect(&ZZ12::ccw(), &rect, true));
assert!(!point_in_rect(&min, &rect, true));
assert!(!point_in_rect(&max, &rect, true));
assert!(point_in_rect(&min, &rect, false));
assert!(point_in_rect(&max, &rect, false));
assert!(!point_in_rect(&(3, 1).into(), &rect, true));
}
}