use sightloom_core::{
GeometryError, LineSegment, LineSide, Point, Polygon, crosses_segment, line_side,
};
fn point(x: f32, y: f32) -> Point {
Point::new(x, y).unwrap()
}
fn segment(start: (f32, f32), end: (f32, f32)) -> LineSegment {
LineSegment::new(point(start.0, start.1), point(end.0, end.1)).unwrap()
}
#[test]
fn line_segment_rejects_a_directionless_segment() {
let endpoint = point(1.0, 2.0);
assert_eq!(
LineSegment::new(endpoint, endpoint),
Err(GeometryError::DegenerateSegment)
);
}
#[test]
fn line_side_uses_an_exact_algebraic_orientation() {
let horizontal = segment((0.0, 0.0), (4.0, 0.0));
assert_eq!(line_side(horizontal, point(2.0, 1.0)), LineSide::Left);
assert_eq!(line_side(horizontal, point(2.0, -1.0)), LineSide::Right);
assert_eq!(line_side(horizontal, point(2.0, 0.0)), LineSide::On);
let large = segment((0.0, 0.0), (f32::MAX, f32::MAX * 0.5));
assert_eq!(
line_side(large, point(f32::MAX * 0.5, f32::MAX)),
LineSide::Left
);
}
#[test]
fn finite_segments_do_not_cross_only_on_their_extensions() {
assert!(crosses_segment(
segment((0.0, 0.0), (4.0, 0.0)),
segment((2.0, -1.0), (2.0, 1.0))
));
assert!(crosses_segment(
segment((0.0, 0.0), (2.0, 0.0)),
segment((2.0, 0.0), (2.0, 2.0))
));
assert!(crosses_segment(
segment((0.0, 0.0), (3.0, 0.0)),
segment((2.0, 0.0), (4.0, 0.0))
));
assert!(!crosses_segment(
segment((0.0, 0.0), (1.0, 0.0)),
segment((2.0, 0.0), (3.0, 0.0))
));
assert!(!crosses_segment(
segment((0.0, 0.0), (1.0, 0.0)),
segment((2.0, -1.0), (2.0, 1.0))
));
}
#[test]
fn polygon_rejects_fewer_than_three_supplied_points() {
let points = [point(0.0, 0.0), point(1.0, 0.0)];
assert_eq!(Polygon::new(&points), Err(GeometryError::TooFewPoints));
}
#[test]
fn polygon_membership_covers_inside_outside_and_boundary_points() {
let points = [
point(0.0, 0.0),
point(4.0, 0.0),
point(4.0, 4.0),
point(0.0, 4.0),
];
let polygon = Polygon::new(&points).unwrap();
assert!(polygon.contains(point(2.0, 2.0)));
assert!(!polygon.contains(point(5.0, 2.0)));
assert!(polygon.contains(point(0.0, 2.0)));
assert!(polygon.contains(point(0.0, 0.0)));
assert_eq!(polygon.points(), &points);
}
#[test]
fn polygon_uses_even_odd_membership_for_concave_and_self_intersecting_shapes() {
let concave = [
point(0.0, 0.0),
point(4.0, 0.0),
point(4.0, 4.0),
point(2.0, 2.0),
point(0.0, 4.0),
];
let concave = Polygon::new(&concave).unwrap();
assert!(concave.contains(point(3.0, 1.0)));
assert!(!concave.contains(point(2.0, 3.0)));
let bow_tie = [
point(0.0, 0.0),
point(4.0, 4.0),
point(0.0, 4.0),
point(4.0, 0.0),
];
let bow_tie = Polygon::new(&bow_tie).unwrap();
assert!(bow_tie.contains(point(2.0, 0.5)));
assert!(!bow_tie.contains(point(0.5, 2.0)));
assert!(bow_tie.contains(point(2.0, 2.0)));
}
#[test]
fn polygon_accepts_repeated_adjacent_and_closing_vertices() {
let points = [
point(0.0, 0.0),
point(4.0, 0.0),
point(4.0, 0.0),
point(4.0, 4.0),
point(0.0, 4.0),
point(0.0, 0.0),
];
assert!(Polygon::new(&points).unwrap().contains(point(2.0, 2.0)));
}
#[test]
fn translated_extreme_coordinates_keep_exact_geometry_signs() {
let max = f32::MAX;
let diagonal = segment((max, max), (0.0, 0.0));
assert_eq!(line_side(diagonal, point(1.0, 0.0)), LineSide::Left);
assert!(!crosses_segment(diagonal, segment((1.0, 0.0), (1.0, -1.0))));
let thin_triangle = [point(max, max), point(0.0, 0.0), point(0.0, -1.0)];
assert!(
!Polygon::new(&thin_triangle)
.unwrap()
.contains(point(1.0, 0.0))
);
}