use axiolid_guarantees::Sign;
use crate::arc::ArcRing;
use crate::OverlayError;
use super::edge::{crossings, Edge};
use super::point::{same_point, sign, Pred, XPoint};
use super::{assemble, edges_of, monotone, sample, tangent_of, winding, Carrier, Mono, Piece};
pub(crate) struct RawEdge {
pub(crate) from: usize,
pub(crate) to: usize,
pub(crate) bulge: f64,
pub(crate) left: Vec<bool>,
pub(crate) right: Vec<bool>,
pub(crate) sources: Vec<(usize, usize, bool)>,
}
pub(crate) struct Raw {
pieces: Vec<Piece>,
vertices: Vec<XPoint>,
pub(crate) edges: Vec<RawEdge>,
}
impl Raw {
pub(crate) fn vertex_positions(&self) -> Vec<axiolid_core::Point2> {
self.vertices.iter().map(XPoint::approx).collect()
}
pub(crate) fn regions(
&self,
keep: &[Option<bool>],
) -> Result<Vec<(RingUses, Vec<RingUses>)>, OverlayError> {
let kept: Vec<Piece> = self
.pieces
.iter()
.zip(keep)
.filter_map(|(piece, keep)| {
keep.map(|reversed| {
if reversed {
piece.clone().reversed()
} else {
piece.clone()
}
})
})
.collect();
let uses = |ring: &[Piece]| ring.iter().map(|p| (p.tag, p.flipped)).collect();
Ok(assemble(kept)?
.into_iter()
.map(|(outer, holes)| (uses(&outer), holes.iter().map(|h| uses(h)).collect()))
.collect())
}
}
pub(crate) type RingUses = Vec<(usize, bool)>;
pub(crate) fn build(rings: &[ArcRing]) -> Raw {
let count = rings.len();
let edges: Vec<Vec<Edge>> = rings.iter().map(edges_of).collect();
let parts: Vec<Vec<Mono>> = edges
.iter()
.map(|own| own.iter().flat_map(monotone).collect())
.collect();
let ring_bounds: Vec<Option<crate::exact_arc::edge::Bounds>> = edges
.iter()
.map(|own| crate::exact_arc::edge::Bounds::hull(own.iter().map(|e| e.bounds)))
.collect();
let near = |ring: usize, x: &XPoint| {
let (sx, sy) = x.enclosures();
ring_bounds[ring]
.as_ref()
.is_some_and(|b| b.may_hold(sx, sy))
};
let carrier = |ring: usize, x: &XPoint| -> Option<(usize, &Edge)> {
if !near(ring, x) {
return None;
}
let (sx, sy) = x.enclosures();
edges[ring]
.iter()
.enumerate()
.filter(|(_, edge)| edge.bounds.may_hold(sx, sy))
.find(|(_, edge)| edge.contains(x))
};
let mut pieces = Vec::new();
let mut out = Vec::new();
for (ring, own) in edges.iter().enumerate() {
for edge in own {
let mut stops = vec![edge.p0.clone(), edge.p1.clone()];
for (other, theirs) in edges.iter().enumerate() {
if other == ring
|| !ring_bounds[other]
.as_ref()
.is_some_and(|b| b.overlaps(&edge.bounds))
{
continue;
}
for their in theirs.iter().filter(|t| t.bounds.overlaps(&edge.bounds)) {
for x in crossings(edge, their) {
if !stops.iter().any(|y| same_point(y, &x)) {
stops.push(x);
}
}
}
}
stops.sort_by(|a, b| match edge.order(a, b) {
Sign::Negative => std::cmp::Ordering::Less,
Sign::Positive => std::cmp::Ordering::Greater,
_ => std::cmp::Ordering::Equal,
});
let split = stops.len() > 2;
let arc = match &edge.carrier {
Carrier::Segment => None,
Carrier::Arc { circle, turn, .. } => Some((circle.clone(), *turn)),
};
let whole = match &edge.carrier {
Carrier::Arc { bulge, .. } if !split => Some(bulge.to_f64()),
_ => None,
};
for w in stops.windows(2) {
let piece = Piece {
sample: sample(edge, &w[0], &w[1]),
from: w[0].clone(),
to: w[1].clone(),
tangent: tangent_of(edge),
arc: arc.clone(),
whole_bulge: whole,
tag: pieces.len(),
flipped: false,
};
if (0..ring).any(|earlier| carrier(earlier, &piece.sample).is_some()) {
continue;
}
let mut left = vec![false; count];
let mut right = vec![false; count];
let mut sources = Vec::new();
for other in 0..count {
if let Some((index, their)) = carrier(other, &piece.sample) {
let same = sign(Pred::Tangents {
at: &piece.sample,
u: &piece.tangent,
v: &tangent_of(their),
cross: false,
}) == Sign::Positive;
if same {
left[other] = true;
} else {
right[other] = true;
}
sources.push((other, index, same));
} else if near(other, &piece.sample)
&& winding(&piece.sample, &parts[other]) != 0
{
left[other] = true;
right[other] = true;
}
}
out.push(RawEdge {
from: 0,
to: 0,
bulge: piece.bulge(),
left,
right,
sources,
});
pieces.push(piece);
}
}
}
let mut vertices: Vec<XPoint> = Vec::new();
let mut by_lo: Vec<(f64, f64, usize)> = Vec::new();
let mut reach = 0.0f64;
let mut intern = |x: &XPoint| -> usize {
let ((lo, hi), _) = x.enclosures();
let floor = lo - reach;
let first = by_lo.partition_point(|entry| entry.0 < floor);
for &(_, _, index) in by_lo[first..].iter().take_while(|entry| entry.0 <= hi) {
if same_point(&vertices[index], x) {
return index;
}
}
let index = vertices.len();
vertices.push(x.clone());
let width = hi - lo;
reach = if width.is_nan() {
f64::INFINITY
} else {
reach.max(width)
};
let at = by_lo.partition_point(|entry| entry.0 < lo);
by_lo.insert(at, (lo, hi, index));
index
};
for (piece, edge) in pieces.iter().zip(out.iter_mut()) {
edge.from = intern(&piece.from);
edge.to = intern(&piece.to);
}
Raw {
pieces,
vertices,
edges: out,
}
}