use crate::bbox::{Aabb2, aabb_decided_misses_point, aabbs_decided_disjoint, decided_segment_aabb};
use crate::facts::{CurveStringFacts, RegionFacts};
use crate::{
BooleanBoundaryLoopSet, BooleanOp, CircularArc2, CircularArc2Facts, Classification, Contour2,
ContourIntersectionSet, ContourPointLocation, CurvePolicy, CurveResult, CurveString2,
CurveStringIntersection, FillRule, LineSeg2, LineSeg2Facts, LineSide, Point2, Region2,
RegionContourIntersection, RegionContourKey, RegionContourRole, RegionIntersectionSet,
RegionPointLocation, RegionSide, RegionView2, Segment2, SegmentIntersection, UncertaintyReason,
};
#[derive(Clone, Debug, PartialEq)]
pub struct PreparedLineSeg2<'a> {
line: &'a LineSeg2,
facts: LineSeg2Facts,
#[cfg(feature = "predicates")]
predicate_start: hyperlimit::Point2,
#[cfg(feature = "predicates")]
predicate_end: hyperlimit::Point2,
#[cfg(feature = "predicates")]
predicate_facts: hyperlimit::PreparedPredicateFacts,
}
impl<'a> PreparedLineSeg2<'a> {
pub fn from_line_segment(line: &'a LineSeg2) -> Self {
let facts = line.structural_facts();
#[cfg(feature = "predicates")]
{
let predicate_start = predicate_point(line.start());
let predicate_end = predicate_point(line.end());
let predicate_facts =
hyperlimit::PreparedLine2::new(&predicate_start, &predicate_end).facts();
Self {
line,
facts,
predicate_start,
predicate_end,
predicate_facts,
}
}
#[cfg(not(feature = "predicates"))]
{
Self { line, facts }
}
}
pub const fn line_segment(&self) -> &'a LineSeg2 {
self.line
}
pub const fn facts(&self) -> &LineSeg2Facts {
&self.facts
}
pub fn classify_point(&self, point: &Point2, policy: &CurvePolicy) -> Classification<LineSide> {
#[cfg(feature = "predicates")]
if !matches!(policy.numeric_mode, crate::NumericMode::EdgePreview) {
let query = predicate_point(point);
return classify_prepared_line(
&self.predicate_start,
&self.predicate_end,
self.predicate_facts,
&query,
policy,
);
}
self.line.classify_point(point, policy)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PreparedCircularArc2<'a> {
arc: &'a CircularArc2,
facts: CircularArc2Facts,
#[cfg(feature = "predicates")]
predicate_center: hyperlimit::Point2,
#[cfg(feature = "predicates")]
predicate_start: hyperlimit::Point2,
#[cfg(feature = "predicates")]
predicate_end: hyperlimit::Point2,
#[cfg(feature = "predicates")]
center_start_facts: hyperlimit::PreparedPredicateFacts,
#[cfg(feature = "predicates")]
center_end_facts: hyperlimit::PreparedPredicateFacts,
}
impl<'a> PreparedCircularArc2<'a> {
pub fn from_circular_arc(arc: &'a CircularArc2) -> Self {
let facts = arc.structural_facts();
#[cfg(feature = "predicates")]
{
let predicate_center = predicate_point(arc.center());
let predicate_start = predicate_point(arc.start());
let predicate_end = predicate_point(arc.end());
let center_start_facts =
hyperlimit::PreparedLine2::new(&predicate_center, &predicate_start).facts();
let center_end_facts =
hyperlimit::PreparedLine2::new(&predicate_center, &predicate_end).facts();
Self {
arc,
facts,
predicate_center,
predicate_start,
predicate_end,
center_start_facts,
center_end_facts,
}
}
#[cfg(not(feature = "predicates"))]
{
Self { arc, facts }
}
}
pub const fn circular_arc(&self) -> &'a CircularArc2 {
self.arc
}
pub const fn facts(&self) -> &CircularArc2Facts {
&self.facts
}
pub fn contains_sweep_point(
&self,
point: &Point2,
policy: &CurvePolicy,
) -> Classification<bool> {
#[cfg(feature = "predicates")]
if !matches!(policy.numeric_mode, crate::NumericMode::EdgePreview) {
let query = predicate_point(point);
let start_side = classify_prepared_line(
&self.predicate_center,
&self.predicate_start,
self.center_start_facts,
&query,
policy,
);
let end_side = classify_prepared_line(
&self.predicate_center,
&self.predicate_end,
self.center_end_facts,
&query,
policy,
);
let (Classification::Decided(start_side), Classification::Decided(end_side)) =
(start_side, end_side)
else {
return Classification::Uncertain(UncertaintyReason::Predicate);
};
let contains = if self.arc.is_clockwise() {
matches!(start_side, LineSide::Right | LineSide::On)
&& matches!(end_side, LineSide::Left | LineSide::On)
} else {
matches!(start_side, LineSide::Left | LineSide::On)
&& matches!(end_side, LineSide::Right | LineSide::On)
};
return Classification::Decided(contains);
}
self.arc.contains_sweep_point(point, policy)
}
pub fn contains_point(&self, point: &Point2, policy: &CurvePolicy) -> Classification<bool> {
let radius_delta = point.distance_squared(self.arc.center()) - self.arc.radius_squared();
match crate::classify::is_zero(&radius_delta, policy) {
Some(false) => Classification::Decided(false),
Some(true) => self.contains_sweep_point(point, policy),
None => Classification::Uncertain(UncertaintyReason::RealSign),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum PreparedSegment2<'a> {
Line(PreparedLineSeg2<'a>),
Arc(PreparedCircularArc2<'a>),
}
impl<'a> PreparedSegment2<'a> {
pub fn from_segment(segment: &'a Segment2) -> Self {
match segment {
Segment2::Line(line) => Self::Line(PreparedLineSeg2::from_line_segment(line)),
Segment2::Arc(arc) => Self::Arc(PreparedCircularArc2::from_circular_arc(arc)),
}
}
pub const fn is_line(&self) -> bool {
matches!(self, Self::Line(_))
}
pub const fn is_arc(&self) -> bool {
matches!(self, Self::Arc(_))
}
pub fn intersect_prepared_segment(
&self,
other: &PreparedSegment2<'a>,
policy: &CurvePolicy,
) -> CurveResult<SegmentIntersection> {
match (self, other) {
(Self::Line(first), Self::Line(second)) => first
.line_segment()
.intersect_line(second.line_segment(), policy)
.map(SegmentIntersection::LineLine),
(Self::Line(line), Self::Arc(arc)) => Ok(SegmentIntersection::LineArc {
order: crate::LineArcOrder::LineThenArc,
result: line
.line_segment()
.intersect_arc(arc.circular_arc(), policy)?,
}),
(Self::Arc(arc), Self::Line(line)) => Ok(SegmentIntersection::LineArc {
order: crate::LineArcOrder::ArcThenLine,
result: line
.line_segment()
.intersect_arc(arc.circular_arc(), policy)?,
}),
(Self::Arc(first), Self::Arc(second)) => first
.circular_arc()
.intersect_arc(second.circular_arc(), policy)
.map(SegmentIntersection::ArcArc),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PreparedCurveStringView2<'a> {
curve: &'a CurveString2,
prepared_segments: Vec<PreparedSegment2<'a>>,
segment_boxes: Vec<Option<Aabb2>>,
curve_box: Option<Aabb2>,
facts: CurveStringFacts,
}
impl<'a> PreparedCurveStringView2<'a> {
pub fn from_curve_string(curve: &'a CurveString2, policy: &CurvePolicy) -> Self {
let segment_boxes = decided_segment_boxes(curve.segments(), policy);
let curve_box = union_all_decided_boxes(segment_boxes.iter().map(Option::as_ref), policy);
let facts = crate::facts::curve_string_facts(
curve,
segment_boxes.iter().filter(|bbox| bbox.is_some()).count(),
curve_box.is_some(),
);
let prepared_segments = prepared_segments(curve.segments());
Self {
curve,
prepared_segments,
segment_boxes,
curve_box,
facts,
}
}
pub const fn curve_string(&self) -> &'a CurveString2 {
self.curve
}
pub const fn curve_box(&self) -> Option<&Aabb2> {
self.curve_box.as_ref()
}
pub fn segment_boxes(&self) -> &[Option<Aabb2>] {
&self.segment_boxes
}
pub fn prepared_segments(&self) -> &[PreparedSegment2<'a>] {
&self.prepared_segments
}
pub const fn facts(&self) -> &CurveStringFacts {
&self.facts
}
pub fn intersect_prepared_curve_string(
&self,
other: &PreparedCurveStringView2<'_>,
policy: &CurvePolicy,
) -> CurveResult<Vec<CurveStringIntersection>> {
intersect_prepared_segment_pairs_with_cached_aabbs(
&self.prepared_segments,
&other.prepared_segments,
self.segment_boxes(),
other.segment_boxes(),
policy,
)
}
pub fn intersect_curve_string(
&self,
other: &CurveString2,
policy: &CurvePolicy,
) -> CurveResult<Vec<CurveStringIntersection>> {
let other = PreparedCurveStringView2::from_curve_string(other, policy);
self.intersect_prepared_curve_string(&other, policy)
}
pub fn has_self_contacts(&self, policy: &CurvePolicy) -> CurveResult<Classification<bool>> {
crate::self_intersect::segments_have_self_contacts_with_cached_aabbs(
self.curve.segments(),
&self.segment_boxes,
false,
policy,
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PreparedContourView2<'a> {
contour: &'a Contour2,
prepared_segments: Vec<PreparedSegment2<'a>>,
segment_boxes: Vec<Option<Aabb2>>,
contour_box: Option<Aabb2>,
facts: CurveStringFacts,
}
impl<'a> PreparedContourView2<'a> {
pub fn from_contour(contour: &'a Contour2, policy: &CurvePolicy) -> Self {
let segment_boxes = decided_segment_boxes(contour.segments(), policy);
let contour_box = union_all_decided_boxes(segment_boxes.iter().map(Option::as_ref), policy);
let facts = crate::facts::contour_facts(
contour,
segment_boxes.iter().filter(|bbox| bbox.is_some()).count(),
contour_box.is_some(),
);
let prepared_segments = prepared_segments(contour.segments());
Self {
contour,
prepared_segments,
segment_boxes,
contour_box,
facts,
}
}
pub const fn contour(&self) -> &'a Contour2 {
self.contour
}
pub const fn contour_box(&self) -> Option<&Aabb2> {
self.contour_box.as_ref()
}
pub fn segment_boxes(&self) -> &[Option<Aabb2>] {
&self.segment_boxes
}
pub fn prepared_segments(&self) -> &[PreparedSegment2<'a>] {
&self.prepared_segments
}
pub const fn facts(&self) -> &CurveStringFacts {
&self.facts
}
pub fn intersect_prepared_contour(
&self,
other: &PreparedContourView2<'_>,
policy: &CurvePolicy,
) -> CurveResult<ContourIntersectionSet> {
crate::events::intersect_contours_with_cached_aabbs(
self.contour,
other.contour,
self.contour_box(),
other.contour_box(),
&self.segment_boxes,
&other.segment_boxes,
policy,
)
}
pub fn intersect_contour(
&self,
other: &Contour2,
policy: &CurvePolicy,
) -> CurveResult<ContourIntersectionSet> {
let other = PreparedContourView2::from_contour(other, policy);
self.intersect_prepared_contour(&other, policy)
}
pub fn intersect_self(&self, policy: &CurvePolicy) -> CurveResult<ContourIntersectionSet> {
crate::events::intersect_contour_self_with_cached_aabbs(
self.contour,
&self.segment_boxes,
policy,
)
}
pub fn classify_point(
&self,
point: &Point2,
policy: &CurvePolicy,
) -> Classification<ContourPointLocation> {
crate::contour::classify_contour_point_with_cached_aabbs(
self.contour,
point,
self.contour_box(),
&self.segment_boxes,
policy,
)
}
pub fn point_on_boundary(&self, point: &Point2, policy: &CurvePolicy) -> Classification<bool> {
crate::contour::point_on_contour_boundary_with_cached_aabbs(
self.contour,
point,
self.contour_box(),
&self.segment_boxes,
policy,
)
}
pub fn winding_number(&self, point: &Point2, policy: &CurvePolicy) -> Classification<i32> {
crate::contour::contour_winding_number_with_cached_aabbs(
self.contour,
point,
self.contour_box(),
&self.segment_boxes,
policy,
)
}
pub fn has_self_contacts(&self, policy: &CurvePolicy) -> CurveResult<Classification<bool>> {
crate::self_intersect::segments_have_self_contacts_with_cached_aabbs(
self.contour.segments(),
&self.segment_boxes,
true,
policy,
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PreparedRegionView2<'a> {
material_contours: Vec<&'a Contour2>,
hole_contours: Vec<&'a Contour2>,
material_prepared_contours: Vec<PreparedContourView2<'a>>,
hole_prepared_contours: Vec<PreparedContourView2<'a>>,
region_box: Option<Aabb2>,
facts: RegionFacts,
}
impl<'a> PreparedRegionView2<'a> {
pub fn from_region(region: &'a Region2, policy: &CurvePolicy) -> Self {
Self::from_region_view(®ion.as_view(), policy)
}
pub fn from_region_view(region: &RegionView2<'a>, policy: &CurvePolicy) -> Self {
let material_contours = region.material_contours().to_vec();
let hole_contours = region.hole_contours().to_vec();
let material_prepared_contours = prepared_contours(&material_contours, policy);
let hole_prepared_contours = prepared_contours(&hole_contours, policy);
let region_box = union_all_decided_boxes(
material_prepared_contours
.iter()
.chain(hole_prepared_contours.iter())
.map(PreparedContourView2::contour_box),
policy,
);
let facts = crate::facts::region_view_facts(region, region_box.is_some());
Self {
material_contours,
hole_contours,
material_prepared_contours,
hole_prepared_contours,
region_box,
facts,
}
}
pub const fn region_box(&self) -> Option<&Aabb2> {
self.region_box.as_ref()
}
pub fn material_contours(&self) -> &[&'a Contour2] {
&self.material_contours
}
pub fn hole_contours(&self) -> &[&'a Contour2] {
&self.hole_contours
}
pub fn as_region_view(&self) -> RegionView2<'a> {
RegionView2::from_contours(
self.material_contours.iter().copied(),
self.hole_contours.iter().copied(),
)
}
pub fn prepared_material_contours(&self) -> &[PreparedContourView2<'a>] {
&self.material_prepared_contours
}
pub fn prepared_hole_contours(&self) -> &[PreparedContourView2<'a>] {
&self.hole_prepared_contours
}
pub const fn facts(&self) -> &RegionFacts {
&self.facts
}
pub fn classify_point(
&self,
point: &Point2,
policy: &CurvePolicy,
) -> Classification<RegionPointLocation> {
let depth = match self.signed_depth(point, policy) {
Classification::Decided(depth) => depth,
Classification::Uncertain(UncertaintyReason::Boundary) => {
return Classification::Decided(RegionPointLocation::Boundary);
}
Classification::Uncertain(reason) => return Classification::Uncertain(reason),
};
Classification::Decided(if depth > 0 {
RegionPointLocation::Inside
} else {
RegionPointLocation::Outside
})
}
pub fn signed_depth(&self, point: &Point2, policy: &CurvePolicy) -> Classification<i32> {
if self
.region_box
.as_ref()
.is_some_and(|bbox| aabb_decided_misses_point(bbox, point, policy))
{
return Classification::Decided(0);
}
let mut depth = 0;
match accumulate_depth(
&mut depth,
&self.material_prepared_contours,
point,
1,
policy,
) {
Classification::Decided(()) => {}
Classification::Uncertain(reason) => return Classification::Uncertain(reason),
}
match accumulate_depth(&mut depth, &self.hole_prepared_contours, point, -1, policy) {
Classification::Decided(()) => Classification::Decided(depth),
Classification::Uncertain(reason) => Classification::Uncertain(reason),
}
}
pub fn intersect_prepared_region(
&self,
other: &PreparedRegionView2<'_>,
policy: &CurvePolicy,
) -> CurveResult<RegionIntersectionSet> {
let mut pairs = Vec::new();
collect_prepared_role_pairs(
&mut pairs,
&self.material_prepared_contours,
RegionContourRole::Material,
&other.material_prepared_contours,
RegionContourRole::Material,
policy,
)?;
collect_prepared_role_pairs(
&mut pairs,
&self.material_prepared_contours,
RegionContourRole::Material,
&other.hole_prepared_contours,
RegionContourRole::Hole,
policy,
)?;
collect_prepared_role_pairs(
&mut pairs,
&self.hole_prepared_contours,
RegionContourRole::Hole,
&other.material_prepared_contours,
RegionContourRole::Material,
policy,
)?;
collect_prepared_role_pairs(
&mut pairs,
&self.hole_prepared_contours,
RegionContourRole::Hole,
&other.hole_prepared_contours,
RegionContourRole::Hole,
policy,
)?;
RegionIntersectionSet::new(pairs)
}
pub fn intersect_region(
&self,
other: &RegionView2<'_>,
policy: &CurvePolicy,
) -> CurveResult<RegionIntersectionSet> {
let other = PreparedRegionView2::from_region_view(other, policy);
self.intersect_prepared_region(&other, policy)
}
pub fn boolean_boundary_loops(
&self,
other: &PreparedRegionView2<'_>,
op: BooleanOp,
policy: &CurvePolicy,
) -> CurveResult<Classification<BooleanBoundaryLoopSet>> {
crate::prepared_boolean::boolean_boundary_loops_between_prepared(self, other, op, policy)
}
pub fn boolean_boundary_loops_against_region(
&self,
other: &RegionView2<'_>,
op: BooleanOp,
policy: &CurvePolicy,
) -> CurveResult<Classification<BooleanBoundaryLoopSet>> {
let other = PreparedRegionView2::from_region_view(other, policy);
self.boolean_boundary_loops(&other, op, policy)
}
pub fn boolean_boundary_contours(
&self,
other: &PreparedRegionView2<'_>,
op: BooleanOp,
fill_rule: FillRule,
policy: &CurvePolicy,
) -> CurveResult<Classification<Vec<Contour2>>> {
crate::prepared_boolean::boolean_boundary_contours_between_prepared(
self, other, op, fill_rule, policy,
)
}
pub fn boolean_boundary_contours_against_region(
&self,
other: &RegionView2<'_>,
op: BooleanOp,
fill_rule: FillRule,
policy: &CurvePolicy,
) -> CurveResult<Classification<Vec<Contour2>>> {
let other = PreparedRegionView2::from_region_view(other, policy);
self.boolean_boundary_contours(&other, op, fill_rule, policy)
}
pub fn boolean_region(
&self,
other: &PreparedRegionView2<'_>,
op: BooleanOp,
fill_rule: FillRule,
policy: &CurvePolicy,
) -> CurveResult<Classification<Region2>> {
crate::prepared_boolean::boolean_region_between_prepared(self, other, op, fill_rule, policy)
}
pub fn boolean_region_against_region(
&self,
other: &RegionView2<'_>,
op: BooleanOp,
fill_rule: FillRule,
policy: &CurvePolicy,
) -> CurveResult<Classification<Region2>> {
let other = PreparedRegionView2::from_region_view(other, policy);
self.boolean_region(&other, op, fill_rule, policy)
}
}
impl CurveString2 {
pub fn prepare_topology_queries(&self, policy: &CurvePolicy) -> PreparedCurveStringView2<'_> {
PreparedCurveStringView2::from_curve_string(self, policy)
}
}
impl Contour2 {
pub fn prepare_topology_queries(&self, policy: &CurvePolicy) -> PreparedContourView2<'_> {
PreparedContourView2::from_contour(self, policy)
}
}
impl Region2 {
pub fn prepare_point_classifier(&self, policy: &CurvePolicy) -> PreparedRegionView2<'_> {
PreparedRegionView2::from_region(self, policy)
}
pub fn prepare_topology_queries(&self, policy: &CurvePolicy) -> PreparedRegionView2<'_> {
PreparedRegionView2::from_region(self, policy)
}
}
impl<'a> RegionView2<'a> {
pub fn prepare_point_classifier(&self, policy: &CurvePolicy) -> PreparedRegionView2<'a> {
PreparedRegionView2::from_region_view(self, policy)
}
pub fn prepare_topology_queries(&self, policy: &CurvePolicy) -> PreparedRegionView2<'a> {
PreparedRegionView2::from_region_view(self, policy)
}
pub fn intersect_prepared_region(
&self,
other: &PreparedRegionView2<'_>,
policy: &CurvePolicy,
) -> CurveResult<RegionIntersectionSet> {
let this = PreparedRegionView2::from_region_view(self, policy);
this.intersect_prepared_region(other, policy)
}
pub fn boolean_boundary_loops_against_prepared_region(
&self,
other: &PreparedRegionView2<'_>,
op: BooleanOp,
policy: &CurvePolicy,
) -> CurveResult<Classification<BooleanBoundaryLoopSet>> {
let this = PreparedRegionView2::from_region_view(self, policy);
this.boolean_boundary_loops(other, op, policy)
}
pub fn boolean_boundary_contours_against_prepared_region(
&self,
other: &PreparedRegionView2<'_>,
op: BooleanOp,
fill_rule: FillRule,
policy: &CurvePolicy,
) -> CurveResult<Classification<Vec<Contour2>>> {
let this = PreparedRegionView2::from_region_view(self, policy);
this.boolean_boundary_contours(other, op, fill_rule, policy)
}
pub fn boolean_region_against_prepared_region(
&self,
other: &PreparedRegionView2<'_>,
op: BooleanOp,
fill_rule: FillRule,
policy: &CurvePolicy,
) -> CurveResult<Classification<Region2>> {
let this = PreparedRegionView2::from_region_view(self, policy);
this.boolean_region(other, op, fill_rule, policy)
}
}
fn prepared_contours<'a>(
contours: &[&'a Contour2],
policy: &CurvePolicy,
) -> Vec<PreparedContourView2<'a>>
where
{
contours
.iter()
.map(|contour| PreparedContourView2::from_contour(contour, policy))
.collect()
}
fn decided_segment_boxes(segments: &[crate::Segment2], policy: &CurvePolicy) -> Vec<Option<Aabb2>> {
segments
.iter()
.map(|segment| decided_segment_aabb(segment, policy))
.collect()
}
fn prepared_segments(segments: &[Segment2]) -> Vec<PreparedSegment2<'_>> {
segments
.iter()
.map(PreparedSegment2::from_segment)
.collect()
}
fn intersect_prepared_segment_pairs_with_cached_aabbs(
first_prepared_segments: &[PreparedSegment2<'_>],
second_prepared_segments: &[PreparedSegment2<'_>],
first_segment_boxes: &[Option<Aabb2>],
second_segment_boxes: &[Option<Aabb2>],
policy: &CurvePolicy,
) -> CurveResult<Vec<CurveStringIntersection>> {
let mut intersections = Vec::new();
for (a_segment_index, a_segment) in first_prepared_segments.iter().enumerate() {
for (b_segment_index, b_segment) in second_prepared_segments.iter().enumerate() {
if let (Some(Some(a_box)), Some(Some(b_box))) = (
first_segment_boxes.get(a_segment_index),
second_segment_boxes.get(b_segment_index),
) && aabbs_decided_disjoint(a_box, b_box, policy)
{
continue;
}
let relation = match (a_segment, b_segment) {
(PreparedSegment2::Line(_), PreparedSegment2::Line(_))
| (PreparedSegment2::Line(_), PreparedSegment2::Arc(_))
| (PreparedSegment2::Arc(_), PreparedSegment2::Line(_))
| (PreparedSegment2::Arc(_), PreparedSegment2::Arc(_)) => {
a_segment.intersect_prepared_segment(b_segment, policy)?
}
};
if !relation.is_none() {
intersections.push(CurveStringIntersection {
a_segment_index,
b_segment_index,
relation,
});
}
}
}
Ok(intersections)
}
#[cfg(feature = "predicates")]
fn predicate_point(point: &Point2) -> hyperlimit::Point2 {
hyperlimit::Point2::new(point.x().clone(), point.y().clone())
}
#[cfg(feature = "predicates")]
fn classify_prepared_line(
from: &hyperlimit::Point2,
to: &hyperlimit::Point2,
facts: hyperlimit::PreparedPredicateFacts,
point: &hyperlimit::Point2,
policy: &CurvePolicy,
) -> Classification<LineSide> {
let prepared = hyperlimit::PreparedLine2::from_facts(from, to, facts);
match prepared.classify_point_with_policy(point, policy.predicate_policy) {
hyperlimit::PredicateOutcome::Decided { value, .. } => {
Classification::Decided(line_side_from_hyperlimit(value))
}
hyperlimit::PredicateOutcome::Unknown { .. } => {
Classification::Uncertain(UncertaintyReason::Predicate)
}
}
}
#[cfg(feature = "predicates")]
const fn line_side_from_hyperlimit(side: hyperlimit::LineSide) -> LineSide {
match side {
hyperlimit::LineSide::Left => LineSide::Left,
hyperlimit::LineSide::Right => LineSide::Right,
hyperlimit::LineSide::On => LineSide::On,
}
}
fn union_all_decided_boxes<'a, I>(boxes: I, policy: &CurvePolicy) -> Option<Aabb2>
where
I: IntoIterator<Item = Option<&'a Aabb2>>,
{
let mut boxes = boxes.into_iter();
let first = boxes.next()??.clone();
let mut merged = first;
for bbox in boxes {
let bbox = bbox?;
let Classification::Decided(next) = merged.union(bbox, policy) else {
return None;
};
merged = next;
}
Some(merged)
}
fn accumulate_depth(
depth: &mut i32,
contours: &[PreparedContourView2<'_>],
point: &Point2,
sign: i32,
policy: &CurvePolicy,
) -> Classification<()> {
for contour in contours {
if contour
.contour_box()
.is_some_and(|bbox| aabb_decided_misses_point(bbox, point, policy))
{
continue;
}
match contour.classify_point(point, policy) {
Classification::Decided(ContourPointLocation::Inside) => *depth += sign,
Classification::Decided(ContourPointLocation::Outside) => {}
Classification::Decided(ContourPointLocation::Boundary) => {
return Classification::Uncertain(UncertaintyReason::Boundary);
}
Classification::Uncertain(reason) => return Classification::Uncertain(reason),
}
}
Classification::Decided(())
}
fn collect_prepared_role_pairs(
pairs: &mut Vec<RegionContourIntersection>,
first_contours: &[PreparedContourView2<'_>],
first_role: RegionContourRole,
second_contours: &[PreparedContourView2<'_>],
second_role: RegionContourRole,
policy: &CurvePolicy,
) -> CurveResult<()> {
for (first_index, first_contour) in first_contours.iter().enumerate() {
for (second_index, second_contour) in second_contours.iter().enumerate() {
let intersections = first_contour.intersect_prepared_contour(second_contour, policy)?;
if intersections.is_empty() {
continue;
}
pairs.push(RegionContourIntersection {
first: RegionContourKey::new(RegionSide::First, first_role, first_index),
second: RegionContourKey::new(RegionSide::Second, second_role, second_index),
intersections,
});
}
}
Ok(())
}