use axiolid_guarantees::Sign;
use crate::arc::ArcRing;
use crate::OverlayError;
use super::boxes::BoxTree;
use super::edge::Bounds;
use super::edge::{crossings, Edge};
use super::point::{same_point, sign, Pred, XPoint};
use super::{assemble, crossing, edges_of, monotone, sample, tangent_of, Carrier, Mono, Piece};
pub(crate) struct RawEdge {
pub(crate) from: usize,
pub(crate) to: usize,
pub(crate) bulge: f64,
pub(crate) inside: Vec<usize>,
pub(crate) sources: Vec<(usize, usize, bool)>,
}
impl RawEdge {
pub(crate) fn sides(&self, count: usize, left: bool) -> Vec<bool> {
let mut out = vec![false; count];
for &ring in &self.inside {
out[ring] = true;
}
for &(ring, _, same) in &self.sources {
out[ring] = same == left;
}
out
}
}
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::rounded).collect()
}
pub(crate) fn straight_on(&self, arriving: (usize, bool), leaving: (usize, bool)) -> bool {
let (a, b) = (&self.pieces[arriving.0], &self.pieces[leaving.0]);
if a.arc.is_some() || b.arc.is_some() {
return false;
}
let travel = |piece: &Piece, reversed: bool| {
if reversed {
piece.clone().reversed().tangent
} else {
piece.tangent.clone()
}
};
let (u, v) = (travel(a, arriving.1), travel(b, leaving.1));
let at = if leaving.1 { &b.to } else { &b.from };
let ask = |cross| {
sign(Pred::Tangents {
at,
u: &u,
v: &v,
cross,
})
};
ask(true) == Sign::Zero && ask(false) == Sign::Positive
}
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)>;
struct Winder {
bounds: Bounds,
parts: Vec<Mono>,
tree: Option<BoxTree>,
}
const SCAN: usize = 32;
impl Winder {
fn new(own: &[Edge]) -> Self {
let mut parts = Vec::new();
let mut boxes = Vec::new();
for edge in own {
for part in monotone(edge) {
parts.push(part);
boxes.push(edge.bounds);
}
}
let bounds = Bounds::hull(own.iter().map(|e| e.bounds)).expect("a ring has edges");
let tree = (parts.len() > SCAN).then(|| BoxTree::new(boxes));
Self {
bounds,
parts,
tree,
}
}
fn winding(&self, s: &XPoint) -> i64 {
match &self.tree {
None => self.parts.iter().map(|part| crossing(part, s)).sum(),
Some(tree) => {
let (sx, sy) = s.enclosures();
tree.query(|b| b.may_hold((sx.0, f64::INFINITY), sy))
.into_iter()
.map(|index| crossing(&self.parts[index], s))
.sum()
}
}
}
}
pub(crate) fn build(rings: &[ArcRing]) -> Raw {
let edges: Vec<Vec<Edge>> = rings.iter().map(edges_of).collect();
let flat: Vec<(usize, usize)> = edges
.iter()
.enumerate()
.flat_map(|(ring, own)| (0..own.len()).map(move |edge| (ring, edge)))
.collect();
let edge_tree = BoxTree::new(
flat.iter()
.map(|&(ring, edge)| edges[ring][edge].bounds)
.collect(),
);
let windings: Vec<Winder> = edges.iter().map(|own| Winder::new(own)).collect();
let ring_tree = BoxTree::new(windings.iter().map(|w| w.bounds).collect());
let mut cuts: Vec<Vec<XPoint>> = flat
.iter()
.map(|&(ring, edge)| vec![edges[ring][edge].p0.clone(), edges[ring][edge].p1.clone()])
.collect();
let add = |stops: &mut Vec<XPoint>, x: &XPoint| {
if !stops.iter().any(|y| same_point(y, x)) {
stops.push(x.clone());
}
};
for (index, &(ring, own)) in flat.iter().enumerate() {
let edge = &edges[ring][own];
for near in edge_tree.query(|b| b.overlaps(&edge.bounds)) {
let (other, their) = flat[near];
if near <= index || other == ring {
continue;
}
for x in crossings(edge, &edges[other][their]) {
add(&mut cuts[index], &x);
add(&mut cuts[near], &x);
}
}
}
let mut pieces = Vec::new();
let mut out = Vec::new();
for ((ring, _), (edge, mut stops)) in flat.iter().copied().zip(edges.iter().flatten().zip(cuts))
{
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,
};
let (sx, sy) = piece.sample.enclosures();
let mut carriers: Vec<(usize, usize, Option<bool>)> = Vec::new();
let mut kept_earlier = false;
for index in edge_tree.query(|b| b.may_hold(sx, sy)) {
let (other, their) = flat[index];
if carriers.iter().any(|c| c.0 == other) {
continue;
}
let theirs = &edges[other][their];
let twin = edge.same_segment(theirs);
if twin.is_some() || theirs.contains(&piece.sample) {
if other < ring {
kept_earlier = true;
break;
}
carriers.push((other, their, twin));
}
}
if kept_earlier {
continue;
}
let mut sources = Vec::new();
carriers.sort_unstable();
for &(other, index, twin) in &carriers {
let same = twin.unwrap_or_else(|| {
sign(Pred::Tangents {
at: &piece.sample,
u: &piece.tangent,
v: &tangent_of(&edges[other][index]),
cross: false,
}) == Sign::Positive
});
sources.push((other, index, same));
}
let mut inside: Vec<usize> = ring_tree
.query(|b| b.may_hold(sx, sy))
.into_iter()
.filter(|other| !carriers.iter().any(|c| c.0 == *other))
.filter(|&other| windings[other].winding(&piece.sample) != 0)
.collect();
inside.sort_unstable();
out.push(RawEdge {
from: 0,
to: 0,
bulge: piece.bulge(),
inside,
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,
}
}