use axiolid_core::Tolerance;
use crate::arc::{arc_ring_area, validate_arc_ring, ArcRing};
use crate::exact_arc;
use crate::{OverlayError, OverlayOperation};
#[derive(Debug, Clone, PartialEq)]
pub struct ArcPolygon {
pub outer: ArcRing,
pub holes: Vec<ArcRing>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ArcOverlayEvidence {
pub regions: usize,
pub holes: usize,
pub arc_edges: usize,
pub line_edges: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ArcOverlayResult {
pub regions: Vec<ArcPolygon>,
pub evidence: ArcOverlayEvidence,
}
fn count_edges(ring: &ArcRing) -> (usize, usize) {
let arcs = ring
.vertices
.iter()
.filter(|vertex| vertex.bulge != 0.0)
.count();
(arcs, ring.vertices.len() - arcs)
}
fn oriented(ring: ArcRing, want_positive: bool) -> ArcRing {
if (arc_ring_area(&ring) > 0.0) == want_positive {
ring
} else {
crate::arc::reverse_arc_ring(&ring)
}
}
pub(crate) fn presented(mut ring: ArcRing, tolerance: Tolerance) -> Option<ArcRing> {
loop {
let count = ring.vertices.len();
if count < 2 {
return None;
}
let short = (0..count).find(|&i| {
let (a, b) = (ring.vertices[i].point, ring.vertices[(i + 1) % count].point);
(b - a).length() <= tolerance.linear()
});
let Some(i) = short else {
break;
};
let j = (i + 1) % count;
ring.vertices[i].bulge = ring.vertices[j].bulge;
ring.vertices.remove(j);
}
match validate_arc_ring(&ring, tolerance) {
Err(OverlayError::RingTooShort | OverlayError::ZeroArea) => None,
_ => Some(ring),
}
}
pub fn arc_overlay(
subject: &ArcRing,
clip: &ArcRing,
operation: OverlayOperation,
tolerance: Tolerance,
) -> Result<ArcOverlayResult, OverlayError> {
validate_arc_ring(subject, tolerance)?;
validate_arc_ring(clip, tolerance)?;
let subject = oriented(subject.clone(), true);
let clip = oriented(clip.clone(), true);
let regions: Vec<ArcPolygon> = exact_arc::boolean(&subject, &clip, operation)?
.into_iter()
.filter_map(|(outer, holes)| {
Some(ArcPolygon {
outer: oriented(presented(outer, tolerance)?, true),
holes: holes
.into_iter()
.filter_map(|h| presented(h, tolerance))
.map(|h| oriented(h, false))
.collect(),
})
})
.collect();
let mut arc_edges = 0;
let mut line_edges = 0;
let mut holes = 0;
for region in ®ions {
holes += region.holes.len();
for ring in std::iter::once(®ion.outer).chain(®ion.holes) {
let (arcs, lines) = count_edges(ring);
arc_edges += arcs;
line_edges += lines;
}
}
Ok(ArcOverlayResult {
evidence: ArcOverlayEvidence {
regions: regions.len(),
holes,
arc_edges,
line_edges,
},
regions,
})
}