use crate::build::sweep::{FillHandler, SweepRunner};
use crate::core::fill_rule::FillRule;
use crate::core::overlay::ShapeType;
use crate::core::predicate::{
InteriorsIntersectHandler, IntersectsHandler, PointIntersectsHandler, TouchesHandler, WithinHandler,
};
use crate::core::solver::Solver;
use crate::segm::boolean::ShapeCountBoolean;
use crate::segm::build::BuildSegments;
use crate::segm::segment::Segment;
use crate::split::solver::SplitSolver;
use alloc::vec::Vec;
use i_float::int::point::IntPoint;
use i_shape::int::shape::{IntContour, IntShape};
pub struct PredicateOverlay {
pub solver: Solver,
pub fill_rule: FillRule,
pub(crate) segments: Vec<Segment<ShapeCountBoolean>>,
pub(crate) split_solver: SplitSolver,
sweep_runner: SweepRunner<ShapeCountBoolean>,
}
impl PredicateOverlay {
#[inline]
pub fn new(capacity: usize) -> Self {
Self {
solver: Default::default(),
fill_rule: FillRule::EvenOdd,
segments: Vec::with_capacity(capacity),
split_solver: SplitSolver::new(),
sweep_runner: SweepRunner::new(),
}
}
fn evaluate<T: Default, H: FillHandler<ShapeCountBoolean, Output = T>>(&mut self, handler: H) -> T {
if self.segments.is_empty() {
return T::default();
}
self.split_solver.split_segments(&mut self.segments, &self.solver);
if self.segments.is_empty() {
return T::default();
}
self.sweep_runner
.run_with_fill_rule(self.fill_rule, &self.solver, &self.segments, handler)
}
#[inline]
pub fn intersects(&mut self) -> bool {
let capacity = self.segments.len();
self.evaluate(IntersectsHandler::new(capacity))
}
#[inline]
pub fn interiors_intersect(&mut self) -> bool {
self.evaluate(InteriorsIntersectHandler)
}
#[inline]
pub fn touches(&mut self) -> bool {
let capacity = self.segments.len();
self.evaluate(TouchesHandler::new(capacity))
}
#[inline]
pub fn point_intersects(&mut self) -> bool {
let capacity = self.segments.len();
self.evaluate(PointIntersectsHandler::new(capacity))
}
#[inline]
pub fn within(&mut self) -> bool {
self.evaluate(WithinHandler::new())
}
#[inline]
pub fn add_path_iter<I: Iterator<Item = IntPoint>>(&mut self, iter: I, shape_type: ShapeType) {
self.segments.append_path_iter(iter, shape_type, false);
}
#[inline]
pub fn add_contour(&mut self, contour: &[IntPoint], shape_type: ShapeType) {
self.segments
.append_path_iter(contour.iter().copied(), shape_type, false);
}
#[inline]
pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) {
for contour in contours.iter() {
self.add_contour(contour, shape_type);
}
}
#[inline]
pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) {
self.add_contours(shape, shape_type);
}
#[inline]
pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) {
for shape in shapes.iter() {
self.add_contours(shape, shape_type);
}
}
#[inline]
pub fn clear(&mut self) {
self.segments.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn square(x: i32, y: i32, size: i32) -> Vec<IntPoint> {
vec![
IntPoint::new(x, y),
IntPoint::new(x, y + size),
IntPoint::new(x + size, y + size),
IntPoint::new(x + size, y),
]
}
#[test]
fn test_add_contour_intersects() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(5, 5, 10), ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_add_contour_disjoint() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(20, 20, 10), ShapeType::Clip);
assert!(!overlay.intersects());
}
#[test]
fn test_add_contour_touches() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 0, 10), ShapeType::Clip);
assert!(overlay.touches());
overlay.clear();
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 0, 10), ShapeType::Clip);
assert!(!overlay.interiors_intersect());
}
#[test]
fn test_add_contour_within() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(5, 5, 10), ShapeType::Subject);
overlay.add_contour(&square(0, 0, 20), ShapeType::Clip);
assert!(overlay.within());
}
#[test]
fn test_add_contours() {
let mut overlay = PredicateOverlay::new(16);
let contours = vec![square(0, 0, 5), square(10, 10, 5)];
overlay.add_contours(&contours, ShapeType::Subject);
overlay.add_contour(&square(2, 2, 3), ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_add_shape() {
let mut overlay = PredicateOverlay::new(16);
let shape = vec![square(0, 0, 10)];
overlay.add_shape(&shape, ShapeType::Subject);
overlay.add_contour(&square(5, 5, 10), ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_add_shapes() {
let mut overlay = PredicateOverlay::new(16);
let shapes = vec![vec![square(0, 0, 5)], vec![square(20, 20, 5)]];
overlay.add_shapes(&shapes, ShapeType::Subject);
overlay.add_contour(&square(2, 2, 3), ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_add_path_iter() {
let mut overlay = PredicateOverlay::new(16);
let points = square(0, 0, 10);
overlay.add_path_iter(points.into_iter(), ShapeType::Subject);
overlay.add_contour(&square(5, 5, 10), ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_point_touch_intersects() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 10, 10), ShapeType::Clip);
assert!(overlay.intersects(), "point-to-point should intersect");
}
#[test]
fn test_point_touch_touches() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 10, 10), ShapeType::Clip);
assert!(overlay.touches(), "point-to-point should touch");
}
#[test]
fn test_point_touch_no_interior_intersect() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 10, 10), ShapeType::Clip);
assert!(
!overlay.interiors_intersect(),
"point touch has no interior intersection"
);
}
#[test]
fn test_intersects_edge_through_interior() {
let shape_a = vec![
IntPoint::new(0, 1),
IntPoint::new(0, 0),
IntPoint::new(3, 0),
IntPoint::new(3, 8),
];
let shape_b = vec![
IntPoint::new(0, 3),
IntPoint::new(1, 3),
IntPoint::new(1, 4),
IntPoint::new(0, 4),
];
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&shape_a, ShapeType::Subject);
overlay.add_contour(&shape_b, ShapeType::Clip);
assert!(
overlay.intersects(),
"Edge (0,1)->(3,8) passes through box interior at (1, 3.33); should intersect"
);
}
#[test]
fn test_segment_end_to_start_touch() {
let subj = vec![IntPoint::new(0, 0), IntPoint::new(10, 0), IntPoint::new(5, 10)];
let clip = vec![IntPoint::new(10, 0), IntPoint::new(20, 0), IntPoint::new(15, 10)];
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&subj, ShapeType::Subject);
overlay.add_contour(&clip, ShapeType::Clip);
assert!(
overlay.intersects(),
"segment b touching segment a should intersect"
);
overlay.clear();
overlay.add_contour(&subj, ShapeType::Subject);
overlay.add_contour(&clip, ShapeType::Clip);
assert!(overlay.touches(), "segment b touching segment a should touch");
overlay.clear();
overlay.add_contour(&subj, ShapeType::Subject);
overlay.add_contour(&clip, ShapeType::Clip);
assert!(
!overlay.interiors_intersect(),
"segment b touching segment a should not have interior intersection"
);
}
fn doughnut(
outer_x: i32,
outer_y: i32,
outer_size: i32,
hole_x: i32,
hole_y: i32,
hole_size: i32,
) -> Vec<Vec<IntPoint>> {
vec![
vec![
IntPoint::new(outer_x, outer_y),
IntPoint::new(outer_x, outer_y + outer_size),
IntPoint::new(outer_x + outer_size, outer_y + outer_size),
IntPoint::new(outer_x + outer_size, outer_y),
],
vec![
IntPoint::new(hole_x, hole_y),
IntPoint::new(hole_x + hole_size, hole_y),
IntPoint::new(hole_x + hole_size, hole_y + hole_size),
IntPoint::new(hole_x, hole_y + hole_size),
],
]
}
fn diamond(cx: i32, cy: i32, radius: i32) -> Vec<IntPoint> {
vec![
IntPoint::new(cx, cy - radius), IntPoint::new(cx + radius, cy), IntPoint::new(cx, cy + radius), IntPoint::new(cx - radius, cy), ]
}
#[test]
fn test_doughnut_with_diamond_touching_hole_intersects() {
let mut overlay = PredicateOverlay::new(32);
let doughnut_shape = doughnut(0, 0, 30, 10, 10, 10);
overlay.add_shape(&doughnut_shape, ShapeType::Subject);
overlay.add_contour(&diamond(15, 15, 5), ShapeType::Clip);
assert!(
overlay.intersects(),
"diamond touching hole boundary should intersect"
);
}
#[test]
fn test_doughnut_with_diamond_touching_hole_touches() {
let mut overlay = PredicateOverlay::new(32);
let doughnut_shape = doughnut(0, 0, 30, 10, 10, 10);
overlay.add_shape(&doughnut_shape, ShapeType::Subject);
overlay.add_contour(&diamond(15, 15, 5), ShapeType::Clip);
assert!(overlay.touches(), "diamond touching hole boundary should touch");
}
#[test]
fn test_doughnut_with_diamond_touching_hole_no_interior_intersect() {
let mut overlay = PredicateOverlay::new(32);
let doughnut_shape = doughnut(0, 0, 30, 10, 10, 10);
overlay.add_shape(&doughnut_shape, ShapeType::Subject);
overlay.add_contour(&diamond(15, 15, 5), ShapeType::Clip);
assert!(
!overlay.interiors_intersect(),
"diamond touching hole boundary should not have interior intersection"
);
}
#[test]
fn test_doughnut_with_diamond_inside_hole_disjoint() {
let mut overlay = PredicateOverlay::new(32);
let doughnut_shape = doughnut(0, 0, 30, 10, 10, 10);
overlay.add_shape(&doughnut_shape, ShapeType::Subject);
overlay.add_contour(&diamond(15, 15, 2), ShapeType::Clip);
assert!(!overlay.intersects(), "diamond inside hole should not intersect");
assert!(!overlay.touches(), "diamond inside hole should not touch");
}
#[test]
fn test_doughnut_with_diamond_touching_single_corner() {
let diamond_touching_corner = vec![
IntPoint::new(10, 10), IntPoint::new(12, 10), IntPoint::new(12, 12), IntPoint::new(10, 12), ];
let mut overlay = PredicateOverlay::new(32);
overlay.add_shape(&doughnut(0, 0, 30, 10, 10, 10), ShapeType::Subject);
overlay.add_contour(&diamond_touching_corner, ShapeType::Clip);
assert!(
overlay.intersects(),
"diamond touching hole corner should intersect"
);
assert!(overlay.touches(), "diamond touching hole corner should touch");
assert!(
!overlay.interiors_intersect(),
"diamond touching hole corner should not have interior intersection"
);
}
#[test]
fn test_outer_diamond_touching_doughnut_corner() {
let diamond_outside = vec![
IntPoint::new(0, 0), IntPoint::new(-5, 3), IntPoint::new(-5, -3), ];
let mut overlay = PredicateOverlay::new(32);
overlay.add_shape(&doughnut(0, 0, 30, 10, 10, 10), ShapeType::Subject);
overlay.add_contour(&diamond_outside, ShapeType::Clip);
assert!(
overlay.intersects(),
"triangle touching outer corner should intersect"
);
assert!(overlay.touches(), "triangle touching outer corner should touch");
assert!(
!overlay.interiors_intersect(),
"triangle touching outer corner should not have interior intersection"
);
}
#[test]
fn test_point_intersects_corner_to_corner() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 10, 10), ShapeType::Clip);
assert!(
overlay.point_intersects(),
"corner-to-corner should be point-only intersection"
);
}
#[test]
fn test_point_intersects_edge_sharing() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 0, 10), ShapeType::Clip);
assert!(overlay.touches());
overlay.clear();
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(10, 0, 10), ShapeType::Clip);
assert!(
!overlay.point_intersects(),
"edge sharing is not point-only intersection"
);
}
#[test]
fn test_point_intersects_overlapping() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(5, 5, 10), ShapeType::Clip);
assert!(
!overlay.point_intersects(),
"overlapping shapes are not point-only intersection"
);
}
#[test]
fn test_point_intersects_disjoint() {
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&square(0, 0, 10), ShapeType::Subject);
overlay.add_contour(&square(20, 20, 10), ShapeType::Clip);
assert!(
!overlay.point_intersects(),
"disjoint shapes have no point intersection"
);
}
#[test]
fn test_point_intersects_triangle_vertex() {
let tri1 = vec![IntPoint::new(0, 0), IntPoint::new(10, 0), IntPoint::new(5, 10)];
let tri2 = vec![IntPoint::new(10, 0), IntPoint::new(20, 0), IntPoint::new(15, 10)];
let mut overlay = PredicateOverlay::new(16);
overlay.add_contour(&tri1, ShapeType::Subject);
overlay.add_contour(&tri2, ShapeType::Clip);
assert!(
overlay.point_intersects(),
"triangles touching at vertex should be point-only intersection"
);
}
}