use super::{Segment, Vec2};
use crate::model::Point2;
use crate::numeric::Tolerance;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Input {
pub seg: Segment,
pub operand: u8,
pub source: usize,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Piece {
pub seg: Segment,
pub v0: usize,
pub v1: usize,
pub operand: u8,
pub source: usize,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Arrangement {
pub pieces: Vec<Piece>,
pub vertices: Vec<Point2>,
pub groups: Vec<Vec<usize>>,
}
pub(crate) struct Dsu {
parent: Vec<usize>,
}
impl Dsu {
pub(crate) fn new(n: usize) -> Self {
Self { parent: (0..n).collect() }
}
pub(crate) fn find(&mut self, mut i: usize) -> usize {
while self.parent[i] != i {
self.parent[i] = self.parent[self.parent[i]];
i = self.parent[i];
}
i
}
pub(crate) fn union(&mut self, a: usize, b: usize) {
let (ra, rb) = (self.find(a), self.find(b));
if ra != rb {
let (lo, hi) = if ra < rb { (ra, rb) } else { (rb, ra) };
self.parent[hi] = lo;
}
}
}
pub fn arrange(inputs: &[Input], tol: &Tolerance) -> Arrangement {
let n = inputs.len();
let mut points: Vec<Point2> = Vec::with_capacity(2 * n);
for i in inputs {
points.push(i.seg.start);
points.push(i.seg.end);
}
let mut splits: Vec<Vec<(f64, usize)>> = vec![Vec::new(); n];
let boxes: Vec<(Point2, Point2)> = inputs.iter().map(|s| s.seg.bounds()).collect();
let mut order: Vec<usize> = (0..n).collect();
order.sort_by(|&a, &b| boxes[a].0.x.total_cmp(&boxes[b].0.x).then(a.cmp(&b)));
let eps = tol.distance_eps;
let mut active: Vec<usize> = Vec::new();
for &i in &order {
let (lo, hi) = boxes[i];
active.retain(|&j| boxes[j].1.x >= lo.x - eps);
for &j in &active {
let (jlo, jhi) = boxes[j];
if jlo.y > hi.y + eps || lo.y > jhi.y + eps {
continue;
}
let (a, b) = if i < j { (i, j) } else { (j, i) };
let hits = inputs[a].seg.intersect(&inputs[b].seg, tol);
for h in hits {
let idx = points.len();
points.push(h.point);
splits[a].push((h.ta, idx));
splits[b].push((h.tb, idx));
}
}
active.push(i);
}
let m = points.len();
let mut dsu = Dsu::new(m);
let mut by_x: Vec<usize> = (0..m).collect();
by_x.sort_by(|&a, &b| points[a].x.total_cmp(&points[b].x).then(a.cmp(&b)));
for (k, &i) in by_x.iter().enumerate() {
for &j in &by_x[k + 1..] {
if points[j].x - points[i].x > tol.point_eps {
break;
}
if points[i].distance(points[j]) <= tol.point_eps {
dsu.union(i, j);
}
}
}
let mut vid_of_root: Vec<Option<usize>> = vec![None; m];
let mut vertices: Vec<Point2> = Vec::new();
let mut vid: Vec<usize> = vec![0; m];
for i in 0..m {
let r = dsu.find(i);
let v = match vid_of_root[r] {
Some(v) => v,
None => {
vertices.push(points[r]);
vid_of_root[r] = Some(vertices.len() - 1);
vertices.len() - 1
}
};
vid[i] = v;
}
let mut pieces: Vec<Piece> = Vec::new();
for (i, input) in inputs.iter().enumerate() {
let mut cuts = std::mem::take(&mut splits[i]);
cuts.push((0.0, 2 * i));
cuts.push((1.0, 2 * i + 1));
cuts.sort_by(|a, b| a.0.total_cmp(&b.0).then(a.1.cmp(&b.1)));
let mut prev: Option<usize> = None;
for &(_, pidx) in &cuts {
let v = vid[pidx];
if let Some(pv) = prev {
if pv != v {
let seg = input.seg.sub_segment(vertices[pv], vertices[v]);
if seg.start.distance(seg.end) > tol.point_eps {
pieces.push(Piece { seg, v0: pv, v1: v, operand: input.operand, source: i });
}
}
}
prev = Some(v);
}
}
let mut keyed: Vec<(usize, usize, usize)> = pieces.iter().enumerate().map(|(k, p)| (p.v0.min(p.v1), p.v0.max(p.v1), k)).collect();
keyed.sort();
let mut groups: Vec<Vec<usize>> = Vec::new();
let mut start = 0;
while start < keyed.len() {
let mut end = start + 1;
while end < keyed.len() && keyed[end].0 == keyed[start].0 && keyed[end].1 == keyed[start].1 {
end += 1;
}
let members: Vec<usize> = keyed[start..end].iter().map(|k| k.2).collect();
let mut taken = vec![false; members.len()];
for a in 0..members.len() {
if taken[a] {
continue;
}
let mut g = vec![members[a]];
taken[a] = true;
let ma = pieces[members[a]].seg.midpoint();
for b in a + 1..members.len() {
if !taken[b] && ma.distance(pieces[members[b]].seg.midpoint()) <= tol.point_eps {
g.push(members[b]);
taken[b] = true;
}
}
groups.push(g);
}
start = end;
}
groups.sort();
Arrangement { pieces, vertices, groups }
}