use crate::build::sweep::FillHandler;
use crate::segm::boolean::ShapeCountBoolean;
use crate::segm::segment::{
BOTH_BOTTOM, BOTH_TOP, CLIP_BOTH, CLIP_BOTTOM, CLIP_TOP, SUBJ_BOTH, SUBJ_BOTTOM, SUBJ_TOP, Segment,
SegmentFill,
};
use alloc::vec::Vec;
use core::ops::ControlFlow;
use i_float::int::point::IntPoint;
use i_key_sort::sort::two_keys::TwoKeysSort;
pub(crate) struct PointCoincidenceChecker {
subj_points: Vec<IntPoint>,
clip_points: Vec<IntPoint>,
}
impl PointCoincidenceChecker {
#[inline]
pub(crate) fn new(capacity: usize) -> Self {
Self {
subj_points: Vec::with_capacity(capacity * 2),
clip_points: Vec::with_capacity(capacity * 2),
}
}
#[inline]
pub(crate) fn add_segment(&mut self, segment: &Segment<ShapeCountBoolean>, fill: SegmentFill) {
let subj_interior = (fill & SUBJ_BOTH) == SUBJ_BOTH;
let clip_interior = (fill & CLIP_BOTH) == CLIP_BOTH;
if subj_interior || clip_interior || fill == 0 {
return;
}
let is_subj = fill & SUBJ_BOTH != 0;
let is_clip = fill & CLIP_BOTH != 0;
if is_subj && is_clip {
return;
}
if is_subj {
self.subj_points.push(segment.x_segment.a);
self.subj_points.push(segment.x_segment.b);
} else {
debug_assert!(is_clip);
self.clip_points.push(segment.x_segment.a);
self.clip_points.push(segment.x_segment.b);
}
}
#[inline]
pub(crate) fn has_coincidence(mut self) -> bool {
if self.subj_points.is_empty() || self.clip_points.is_empty() {
return false;
}
let (shorter, longer) = if self.subj_points.len() <= self.clip_points.len() {
(&mut self.subj_points, &self.clip_points)
} else {
(&mut self.clip_points, &self.subj_points)
};
shorter.sort_by_two_keys(false, |p| p.x, |p| p.y);
shorter.dedup();
longer.iter().any(|p| shorter.binary_search(p).is_ok())
}
}
pub(crate) struct IntersectsHandler {
point_checker: PointCoincidenceChecker,
}
impl IntersectsHandler {
pub(crate) fn new(capacity: usize) -> Self {
Self {
point_checker: PointCoincidenceChecker::new(capacity),
}
}
}
impl FillHandler<ShapeCountBoolean> for IntersectsHandler {
type Output = bool;
#[inline(always)]
fn handle(
&mut self,
_index: usize,
segment: &Segment<ShapeCountBoolean>,
fill: SegmentFill,
) -> ControlFlow<bool> {
let has_subj = (fill & SUBJ_BOTH) != 0;
let has_clip = (fill & CLIP_BOTH) != 0;
if has_subj && has_clip {
ControlFlow::Break(true)
} else {
self.point_checker.add_segment(segment, fill);
ControlFlow::Continue(())
}
}
#[inline(always)]
fn finalize(self) -> bool {
self.point_checker.has_coincidence()
}
}
pub(crate) struct InteriorsIntersectHandler;
impl FillHandler<ShapeCountBoolean> for InteriorsIntersectHandler {
type Output = bool;
#[inline(always)]
fn handle(
&mut self,
_index: usize,
_segment: &Segment<ShapeCountBoolean>,
fill: SegmentFill,
) -> ControlFlow<bool> {
if (fill & BOTH_TOP) == BOTH_TOP || (fill & BOTH_BOTTOM) == BOTH_BOTTOM {
ControlFlow::Break(true)
} else {
ControlFlow::Continue(())
}
}
#[inline(always)]
fn finalize(self) -> bool {
false
}
}
pub(crate) struct TouchesHandler {
has_boundary_contact: bool,
point_checker: PointCoincidenceChecker,
}
impl TouchesHandler {
pub(crate) fn new(capacity: usize) -> Self {
Self {
has_boundary_contact: false,
point_checker: PointCoincidenceChecker::new(capacity),
}
}
}
impl FillHandler<ShapeCountBoolean> for TouchesHandler {
type Output = bool;
#[inline(always)]
fn handle(
&mut self,
_index: usize,
segment: &Segment<ShapeCountBoolean>,
fill: SegmentFill,
) -> ControlFlow<bool> {
if (fill & BOTH_TOP) == BOTH_TOP || (fill & BOTH_BOTTOM) == BOTH_BOTTOM {
return ControlFlow::Break(false);
}
if (fill & SUBJ_BOTH) != 0 && (fill & CLIP_BOTH) != 0 {
self.has_boundary_contact = true;
}
self.point_checker.add_segment(segment, fill);
ControlFlow::Continue(())
}
#[inline(always)]
fn finalize(self) -> bool {
self.has_boundary_contact || self.point_checker.has_coincidence()
}
}
pub(crate) struct PointIntersectsHandler {
point_checker: PointCoincidenceChecker,
}
impl PointIntersectsHandler {
pub(crate) fn new(capacity: usize) -> Self {
Self {
point_checker: PointCoincidenceChecker::new(capacity),
}
}
}
impl FillHandler<ShapeCountBoolean> for PointIntersectsHandler {
type Output = bool;
#[inline(always)]
fn handle(
&mut self,
_index: usize,
segment: &Segment<ShapeCountBoolean>,
fill: SegmentFill,
) -> ControlFlow<bool> {
if (fill & BOTH_TOP) == BOTH_TOP || (fill & BOTH_BOTTOM) == BOTH_BOTTOM {
return ControlFlow::Break(false);
}
if (fill & SUBJ_BOTH) != 0 && (fill & CLIP_BOTH) != 0 {
return ControlFlow::Break(false);
}
self.point_checker.add_segment(segment, fill);
ControlFlow::Continue(())
}
#[inline(always)]
fn finalize(self) -> bool {
self.point_checker.has_coincidence()
}
}
pub(crate) struct WithinHandler {
subj_present: bool,
}
impl WithinHandler {
pub(crate) fn new() -> Self {
Self { subj_present: false }
}
}
impl FillHandler<ShapeCountBoolean> for WithinHandler {
type Output = bool;
#[inline(always)]
fn handle(
&mut self,
_index: usize,
_segment: &Segment<ShapeCountBoolean>,
fill: SegmentFill,
) -> ControlFlow<bool> {
let subj_top = (fill & SUBJ_TOP) != 0;
let subj_bot = (fill & SUBJ_BOTTOM) != 0;
let clip_top = (fill & CLIP_TOP) != 0;
let clip_bot = (fill & CLIP_BOTTOM) != 0;
if subj_top || subj_bot {
self.subj_present = true;
}
if (subj_top && !clip_top) || (subj_bot && !clip_bot) {
ControlFlow::Break(false)
} else {
ControlFlow::Continue(())
}
}
#[inline(always)]
fn finalize(self) -> bool {
self.subj_present
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geom::x_segment::XSegment;
fn make_segment(ax: i32, ay: i32, bx: i32, by: i32, subj: i32, clip: i32) -> Segment<ShapeCountBoolean> {
Segment {
x_segment: XSegment {
a: IntPoint::new(ax, ay),
b: IntPoint::new(bx, by),
},
count: ShapeCountBoolean { subj, clip },
}
}
#[test]
fn test_point_coincidence_no_points() {
let checker = PointCoincidenceChecker::new(10);
assert!(!checker.has_coincidence());
}
#[test]
fn test_point_coincidence_subj_only() {
let mut checker = PointCoincidenceChecker::new(10);
checker.add_segment(&make_segment(0, 0, 10, 0, 1, 0), SUBJ_TOP);
assert!(!checker.has_coincidence());
}
#[test]
fn test_point_coincidence_coincident_point() {
let mut checker = PointCoincidenceChecker::new(10);
checker.add_segment(&make_segment(0, 0, 10, 10, 1, 0), SUBJ_TOP);
checker.add_segment(&make_segment(10, 10, 20, 20, 0, 1), CLIP_TOP);
assert!(checker.has_coincidence());
}
#[test]
fn test_point_coincidence_no_coincidence() {
let mut checker = PointCoincidenceChecker::new(10);
checker.add_segment(&make_segment(0, 0, 5, 5, 1, 0), SUBJ_TOP);
checker.add_segment(&make_segment(10, 10, 20, 20, 0, 1), CLIP_TOP);
assert!(!checker.has_coincidence());
}
#[test]
fn test_point_coincidence_shared_segment_is_line_not_point() {
let mut checker = PointCoincidenceChecker::new(10);
checker.add_segment(&make_segment(0, 0, 10, 10, 1, 1), SUBJ_TOP | CLIP_BOTTOM);
assert!(!checker.has_coincidence());
}
#[test]
fn test_point_coincidence_dedup_works() {
let mut checker = PointCoincidenceChecker::new(10);
checker.add_segment(&make_segment(0, 0, 5, 5, 1, 0), SUBJ_TOP);
checker.add_segment(&make_segment(5, 5, 10, 10, 1, 0), SUBJ_TOP);
checker.add_segment(&make_segment(5, 5, 15, 15, 0, 1), CLIP_TOP);
assert!(checker.has_coincidence());
}
#[test]
fn test_intersects_handler_both_top() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = IntersectsHandler::new(10);
let fill = SUBJ_TOP | CLIP_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(true)));
}
#[test]
fn test_intersects_handler_both_bottom() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = IntersectsHandler::new(10);
let fill = SUBJ_BOTTOM | CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(true)));
}
#[test]
fn test_intersects_handler_boundary_contact() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = IntersectsHandler::new(10);
let fill = SUBJ_TOP | CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(true)));
}
#[test]
fn test_intersects_handler_no_intersection() {
let seg = make_segment(0, 0, 10, 0, 1, 0);
let mut handler = IntersectsHandler::new(10);
let fill = SUBJ_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
let seg = make_segment(0, 0, 10, 0, 0, 1);
let fill = CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
}
#[test]
fn test_intersects_handler_finalize_with_coincidence() {
let mut handler = IntersectsHandler::new(10);
let seg1 = make_segment(0, 0, 10, 10, 1, 0);
let seg2 = make_segment(10, 10, 20, 20, 0, 1);
let _ = handler.handle(0, &seg1, SUBJ_TOP);
let _ = handler.handle(1, &seg2, CLIP_TOP);
assert!(handler.finalize());
}
#[test]
fn test_interiors_intersect_handler_both_top() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = InteriorsIntersectHandler;
let fill = SUBJ_TOP | CLIP_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(true)));
}
#[test]
fn test_interiors_intersect_handler_both_bottom() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = InteriorsIntersectHandler;
let fill = SUBJ_BOTTOM | CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(true)));
}
#[test]
fn test_interiors_intersect_handler_boundary_only() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = InteriorsIntersectHandler;
let fill = SUBJ_TOP | CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
assert!(!handler.finalize());
}
#[test]
fn test_touches_handler_boundary_only() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = TouchesHandler::new(10);
let fill = SUBJ_TOP | CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
assert!(handler.finalize()); }
#[test]
fn test_touches_handler_interior_overlap() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = TouchesHandler::new(10);
let fill = SUBJ_TOP | CLIP_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(false))); }
#[test]
fn test_touches_handler_no_contact() {
let seg = make_segment(0, 0, 10, 0, 1, 0);
let mut handler = TouchesHandler::new(10);
let fill = SUBJ_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
assert!(!handler.finalize()); }
#[test]
fn test_touches_handler_point_coincidence() {
let mut handler = TouchesHandler::new(10);
let seg1 = make_segment(0, 0, 10, 10, 1, 0);
let seg2 = make_segment(10, 10, 20, 20, 0, 1);
let _ = handler.handle(0, &seg1, SUBJ_TOP);
let _ = handler.handle(1, &seg2, CLIP_TOP);
assert!(handler.finalize());
}
#[test]
fn test_within_handler_subject_inside_clip() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = WithinHandler::new();
let fill = SUBJ_TOP | CLIP_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
assert!(handler.finalize());
}
#[test]
fn test_within_handler_subject_outside_clip() {
let seg = make_segment(0, 0, 10, 0, 1, 0);
let mut handler = WithinHandler::new();
let fill = SUBJ_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(false)));
}
#[test]
fn test_within_handler_empty_subject() {
let handler = WithinHandler::new();
assert!(!handler.finalize());
}
#[test]
fn test_within_handler_clip_only() {
let seg = make_segment(0, 0, 10, 0, 0, 1);
let mut handler = WithinHandler::new();
let fill = CLIP_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Continue(())));
assert!(!handler.finalize());
}
#[test]
fn test_point_intersects_handler_point_only() {
let mut handler = PointIntersectsHandler::new(10);
let seg1 = make_segment(0, 0, 10, 10, 1, 0);
let seg2 = make_segment(10, 10, 20, 20, 0, 1);
let _ = handler.handle(0, &seg1, SUBJ_TOP);
let _ = handler.handle(1, &seg2, CLIP_TOP);
assert!(handler.finalize());
}
#[test]
fn test_point_intersects_handler_edge_contact() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = PointIntersectsHandler::new(10);
let fill = SUBJ_TOP | CLIP_BOTTOM;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(false)));
}
#[test]
fn test_point_intersects_handler_interior_overlap() {
let seg = make_segment(0, 0, 10, 0, 1, 1);
let mut handler = PointIntersectsHandler::new(10);
let fill = SUBJ_TOP | CLIP_TOP;
let result = handler.handle(0, &seg, fill);
assert!(matches!(result, ControlFlow::Break(false)));
}
#[test]
fn test_point_intersects_handler_no_contact() {
let seg1 = make_segment(0, 0, 5, 5, 1, 0);
let seg2 = make_segment(10, 10, 20, 20, 0, 1);
let mut handler = PointIntersectsHandler::new(10);
let _ = handler.handle(0, &seg1, SUBJ_TOP);
let _ = handler.handle(1, &seg2, CLIP_TOP);
assert!(!handler.finalize());
}
}