use std::cmp::Ordering;
use hyperreal::Real;
use crate::classify::{compare_reals_for_split_ordering, is_zero};
use crate::{
Classification, Contour2, ContourIntersection, ContourIntersectionSet, ContourOperand,
CurvePolicy, Point2, UncertaintyReason,
};
#[derive(Clone, Debug, PartialEq)]
pub struct SegmentSplitPoint {
pub segment_index: usize,
pub param: Real,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SegmentSplitMarker {
pub segment_index: usize,
pub param: Real,
pub point: Point2,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ContourSplitMarkers {
segment_markers: Vec<Vec<SegmentSplitMarker>>,
}
impl ContourSplitMarkers {
pub const fn new(segment_markers: Vec<Vec<SegmentSplitMarker>>) -> Self {
Self { segment_markers }
}
pub fn with_contour_endpoints(contour: &Contour2) -> Self {
let mut segment_markers = Vec::with_capacity(contour.len());
for (segment_index, segment) in contour.segments().iter().enumerate() {
segment_markers.push(vec![
SegmentSplitMarker {
segment_index,
param: Real::zero(),
point: segment.start().clone(),
},
SegmentSplitMarker {
segment_index,
param: Real::one(),
point: segment.end().clone(),
},
]);
}
Self { segment_markers }
}
pub fn from_intersections(
contour: &Contour2,
intersections: &ContourIntersectionSet,
operand: ContourOperand,
policy: &CurvePolicy,
) -> Classification<Self> {
let mut markers = Self::with_contour_endpoints(contour);
match markers.merge_intersections(intersections, operand, policy) {
Classification::Decided(()) => Classification::Decided(markers),
Classification::Uncertain(reason) => Classification::Uncertain(reason),
}
}
pub fn from_self_intersections(
contour: &Contour2,
intersections: &ContourIntersectionSet,
policy: &CurvePolicy,
) -> Classification<Self> {
let mut markers = Self::with_contour_endpoints(contour);
match markers.merge_self_intersections(intersections, policy) {
Classification::Decided(()) => Classification::Decided(markers),
Classification::Uncertain(reason) => Classification::Uncertain(reason),
}
}
pub fn segments(&self) -> &[Vec<SegmentSplitMarker>] {
&self.segment_markers
}
pub fn markers_for_segment(&self, segment_index: usize) -> Option<&[SegmentSplitMarker]> {
self.segment_markers
.get(segment_index)
.map(std::vec::Vec::as_slice)
}
pub fn segment_count(&self) -> usize {
self.segment_markers.len()
}
pub fn is_empty(&self) -> bool {
self.segment_markers.is_empty()
}
pub fn merge_intersections(
&mut self,
intersections: &ContourIntersectionSet,
operand: ContourOperand,
policy: &CurvePolicy,
) -> Classification<()> {
for event in intersections.events() {
match self.merge_event(event, operand, policy) {
Ok(()) => {}
Err(reason) => return Classification::Uncertain(reason),
}
}
Classification::Decided(())
}
pub fn merge_self_intersections(
&mut self,
intersections: &ContourIntersectionSet,
policy: &CurvePolicy,
) -> Classification<()> {
for event in intersections.events() {
match self.merge_event(event, ContourOperand::First, policy) {
Ok(()) => {}
Err(reason) => return Classification::Uncertain(reason),
}
match self.merge_event(event, ContourOperand::Second, policy) {
Ok(()) => {}
Err(reason) => return Classification::Uncertain(reason),
}
}
Classification::Decided(())
}
pub fn split_markers(&self) -> Vec<SegmentSplitMarker> {
let mut split_markers = Vec::new();
for markers in &self.segment_markers {
for marker in markers {
split_markers.push(marker.clone());
}
}
split_markers
}
fn merge_event(
&mut self,
event: &ContourIntersection,
operand: ContourOperand,
policy: &CurvePolicy,
) -> Result<(), UncertaintyReason> {
match event {
ContourIntersection::Point(point) => {
let (segment_index, param) = match operand {
ContourOperand::First => (point.a_segment_index, point.a_param.clone()),
ContourOperand::Second => (point.b_segment_index, point.b_param.clone()),
};
self.insert_marker(
SegmentSplitMarker {
segment_index,
param,
point: point.point.clone(),
},
policy,
)
}
ContourIntersection::Overlap(overlap) => {
let (segment_index, start_param, end_param) = match operand {
ContourOperand::First => (
overlap.a_segment_index,
overlap.a_range.start().clone(),
overlap.a_range.end().clone(),
),
ContourOperand::Second => (
overlap.b_segment_index,
overlap.b_range.start().clone(),
overlap.b_range.end().clone(),
),
};
self.insert_marker(
SegmentSplitMarker {
segment_index,
param: start_param,
point: overlap.segment.start().clone(),
},
policy,
)?;
self.insert_marker(
SegmentSplitMarker {
segment_index,
param: end_param,
point: overlap.segment.end().clone(),
},
policy,
)
}
ContourIntersection::Uncertain(uncertain) => Err(uncertain.reason),
}
}
fn insert_marker(
&mut self,
marker: SegmentSplitMarker,
policy: &CurvePolicy,
) -> Result<(), UncertaintyReason> {
let Some(markers) = self.segment_markers.get_mut(marker.segment_index) else {
return Err(UncertaintyReason::Unsupported);
};
insert_unique_sorted_marker(markers, marker, policy)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ContourSplitMap {
segment_splits: Vec<Vec<Real>>,
}
impl ContourSplitMap {
pub const fn new(segment_splits: Vec<Vec<Real>>) -> Self {
Self { segment_splits }
}
pub fn with_segment_count(segment_count: usize) -> Self {
let mut segment_splits = Vec::with_capacity(segment_count);
for _ in 0..segment_count {
segment_splits.push(vec![Real::zero(), Real::one()]);
}
Self { segment_splits }
}
pub fn from_intersections(
segment_count: usize,
intersections: &ContourIntersectionSet,
operand: ContourOperand,
policy: &CurvePolicy,
) -> Classification<Self> {
let mut map = Self::with_segment_count(segment_count);
match map.merge_intersections(intersections, operand, policy) {
Classification::Decided(()) => Classification::Decided(map),
Classification::Uncertain(reason) => Classification::Uncertain(reason),
}
}
pub fn segments(&self) -> &[Vec<Real>] {
&self.segment_splits
}
pub fn params_for_segment(&self, segment_index: usize) -> Option<&[Real]> {
self.segment_splits
.get(segment_index)
.map(std::vec::Vec::as_slice)
}
pub fn segment_count(&self) -> usize {
self.segment_splits.len()
}
pub fn is_empty(&self) -> bool {
self.segment_splits.is_empty()
}
pub fn merge_intersections(
&mut self,
intersections: &ContourIntersectionSet,
operand: ContourOperand,
policy: &CurvePolicy,
) -> Classification<()> {
for event in intersections.events() {
match self.merge_event(event, operand, policy) {
Ok(()) => {}
Err(reason) => return Classification::Uncertain(reason),
}
}
Classification::Decided(())
}
pub fn split_points(&self) -> Vec<SegmentSplitPoint> {
let mut split_points = Vec::new();
for (segment_index, params) in self.segment_splits.iter().enumerate() {
for param in params {
split_points.push(SegmentSplitPoint {
segment_index,
param: param.clone(),
});
}
}
split_points
}
fn merge_event(
&mut self,
event: &ContourIntersection,
operand: ContourOperand,
policy: &CurvePolicy,
) -> Result<(), UncertaintyReason> {
match event {
ContourIntersection::Point(point) => {
let (segment_index, param) = match operand {
ContourOperand::First => (point.a_segment_index, point.a_param.clone()),
ContourOperand::Second => (point.b_segment_index, point.b_param.clone()),
};
self.insert_param(segment_index, param, policy)
}
ContourIntersection::Overlap(overlap) => {
let (segment_index, start, end) = match operand {
ContourOperand::First => (
overlap.a_segment_index,
overlap.a_range.start().clone(),
overlap.a_range.end().clone(),
),
ContourOperand::Second => (
overlap.b_segment_index,
overlap.b_range.start().clone(),
overlap.b_range.end().clone(),
),
};
self.insert_param(segment_index, start, policy)?;
self.insert_param(segment_index, end, policy)
}
ContourIntersection::Uncertain(uncertain) => Err(uncertain.reason),
}
}
fn insert_param(
&mut self,
segment_index: usize,
param: Real,
policy: &CurvePolicy,
) -> Result<(), UncertaintyReason> {
let Some(params) = self.segment_splits.get_mut(segment_index) else {
return Err(UncertaintyReason::Unsupported);
};
insert_unique_sorted(params, param, policy)
}
}
fn insert_unique_sorted(
params: &mut Vec<Real>,
param: Real,
policy: &CurvePolicy,
) -> Result<(), UncertaintyReason> {
for index in 0..params.len() {
match compare_reals_for_split_ordering(¶m, ¶ms[index], policy) {
Some(Ordering::Equal) => return Ok(()),
Some(Ordering::Less) => {
params.insert(index, param);
return Ok(());
}
Some(Ordering::Greater) => {}
None => return Err(UncertaintyReason::Ordering),
}
}
params.push(param);
Ok(())
}
fn insert_unique_sorted_marker(
markers: &mut Vec<SegmentSplitMarker>,
marker: SegmentSplitMarker,
policy: &CurvePolicy,
) -> Result<(), UncertaintyReason> {
for index in 0..markers.len() {
match compare_reals_for_split_ordering(&marker.param, &markers[index].param, policy) {
Some(Ordering::Equal) => {
let distance = marker.point.distance_squared(&markers[index].point);
return match is_zero(&distance, policy) {
Some(true) => Ok(()),
Some(false) => Err(UncertaintyReason::Unsupported),
None => Err(UncertaintyReason::RealSign),
};
}
Some(Ordering::Less) => {
markers.insert(index, marker);
return Ok(());
}
Some(Ordering::Greater) => {}
None => return Err(UncertaintyReason::Ordering),
}
}
markers.push(marker);
Ok(())
}