1use std::collections::{BTreeMap, BTreeSet};
24
25use axiolid_contracts::{ExecutionOptions, GeomError, GeomResult, Sign};
26
27use crate::points::{inconsistent, ExactPoints, PointId};
28
29pub const CROSSING_SCRATCH_BYTES: usize = 512;
32
33pub const SPLIT_SCRATCH_BYTES: usize = 64;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct VertexId(pub usize);
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct PieceId(pub usize);
44
45#[derive(Debug, Clone)]
48pub struct PairBudget {
49 pub used: usize,
51 pub max: usize,
53 pub resource: &'static str,
55}
56
57impl PairBudget {
58 pub const fn new(max: usize, resource: &'static str) -> Self {
60 Self {
61 used: 0,
62 max,
63 resource,
64 }
65 }
66
67 pub fn count(&mut self) -> GeomResult<()> {
69 self.used += 1;
70 if self.used > self.max {
71 return Err(GeomError::BudgetExceeded {
72 resource: self.resource,
73 });
74 }
75 Ok(())
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct Segment {
83 pub ends: [PointId; 2],
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub struct Crossing {
90 pub segments: [usize; 2],
92 pub ends: [PointId; 4],
94}
95
96#[derive(Debug, Clone)]
98pub struct Vertex {
99 pub point: PointId,
101 pub inputs: Vec<PointId>,
103 pub crossing: Option<Crossing>,
105}
106
107#[derive(Debug, Clone)]
109pub struct Piece {
110 pub ends: [VertexId; 2],
112 pub segments: Vec<usize>,
114}
115
116#[derive(Debug, Clone)]
118pub struct Arrangement {
119 pub vertices: Vec<Vertex>,
121 pub pieces: Vec<Piece>,
123 pub paths: Vec<Vec<(PieceId, bool)>>,
127 pub crossings: usize,
129 incident: Vec<Vec<(PieceId, bool)>>,
132}
133
134#[derive(Clone, Copy)]
136enum Label {
137 Input,
138 Crossing([usize; 2], [PointId; 4]),
139}
140
141struct Cut {
142 ends: [usize; 2],
143 bounds: [f64; 4],
145 splits: Vec<usize>,
147 path: Vec<usize>,
149}
150
151struct Local {
153 exact: Vec<PointId>,
154 labels: Vec<Label>,
155 parent: Vec<usize>,
156}
157
158impl Local {
159 fn push(&mut self, exact: PointId, label: Label) -> usize {
160 self.exact.push(exact);
161 self.labels.push(label);
162 self.parent.push(self.parent.len());
163 self.parent.len() - 1
164 }
165
166 fn find(&mut self, mut point: usize) -> usize {
167 while self.parent[point] != point {
168 self.parent[point] = self.parent[self.parent[point]];
169 point = self.parent[point];
170 }
171 point
172 }
173
174 fn union(&mut self, left: usize, right: usize) {
176 let (left, right) = (self.find(left), self.find(right));
177 self.parent[left.max(right)] = left.min(right);
178 }
179}
180
181pub fn boxes_overlap(first: &[f64; 4], second: &[f64; 4]) -> bool {
183 first[0] <= second[2] && second[0] <= first[2] && first[1] <= second[3] && second[1] <= first[3]
184}
185
186pub fn union_box(boxes: &[[f64; 4]]) -> [f64; 4] {
188 boxes.iter().fold(
189 [
190 f64::INFINITY,
191 f64::INFINITY,
192 f64::NEG_INFINITY,
193 f64::NEG_INFINITY,
194 ],
195 |all, one| {
196 [
197 all[0].min(one[0]),
198 all[1].min(one[1]),
199 all[2].max(one[2]),
200 all[3].max(one[3]),
201 ]
202 },
203 )
204}
205
206pub(crate) fn sort_exact<T: Copy>(
208 items: &mut [T],
209 mut compare: impl FnMut(T, T) -> GeomResult<std::cmp::Ordering>,
210) -> GeomResult<()> {
211 let mut failure = None;
212 items.sort_by(|&left, &right| match compare(left, right) {
213 Ok(order) => order,
214 Err(error) => {
215 failure.get_or_insert(error);
216 std::cmp::Ordering::Equal
217 }
218 });
219 failure.map_or(Ok(()), Err)
220}
221
222pub fn arrange(
237 points: &mut ExactPoints,
238 segments: &[Segment],
239 budget: &mut PairBudget,
240 declared_bytes: usize,
241 options: &ExecutionOptions,
242) -> GeomResult<Arrangement> {
243 let mut inputs: Vec<PointId> = segments.iter().flat_map(|segment| segment.ends).collect();
245 inputs.sort_unstable();
246 inputs.dedup();
247 let mut local = Local {
248 exact: Vec::new(),
249 labels: Vec::new(),
250 parent: Vec::new(),
251 };
252 for &point in &inputs {
253 local.push(point, Label::Input);
254 }
255 let mut by_position: Vec<usize> = (0..inputs.len()).collect();
257 sort_exact(&mut by_position, |left, right| {
258 points.lexicographic(local.exact[left], local.exact[right])
259 })?;
260 for pair in by_position.windows(2) {
261 if points.same(local.exact[pair[0]], local.exact[pair[1]])? {
262 local.union(pair[0], pair[1]);
263 }
264 }
265
266 let local_of = |point: PointId| inputs.binary_search(&point).map_err(|_| inconsistent());
267 let mut cuts = Vec::with_capacity(segments.len());
268 for segment in segments {
269 let ends = [local_of(segment.ends[0])?, local_of(segment.ends[1])?];
270 let (low, high) = (
271 points.bounds(segment.ends[0]),
272 points.bounds(segment.ends[1]),
273 );
274 cuts.push(Cut {
275 ends,
276 bounds: [
277 low[0].min(high[0]),
278 low[1].min(high[1]),
279 low[2].max(high[2]),
280 low[3].max(high[3]),
281 ],
282 splits: Vec::new(),
283 path: Vec::new(),
284 });
285 }
286
287 cut_segments(
288 segments,
289 &mut cuts,
290 points,
291 &mut local,
292 (budget, declared_bytes),
293 options,
294 )?;
295 order_cuts(&mut cuts, points, &mut local, options)?;
296
297 let (vertices, vertex_of) = number_vertices(&mut local);
298 let crossings = vertices
299 .iter()
300 .filter(|vertex| vertex.inputs.is_empty())
301 .count();
302
303 let mut by_ends = BTreeMap::<(usize, usize), usize>::new();
305 let mut carriers = Vec::<BTreeSet<usize>>::new();
306 let mut on_segments = Vec::<(usize, usize, bool)>::new();
307 for (index, cut) in cuts.iter().enumerate() {
308 options.check_cancelled()?;
309 let mut previous: Option<usize> = None;
310 for &point in &cut.path {
311 let vertex = vertex_of[&local.find(point)];
312 if let Some(from) = previous {
313 if from != vertex {
314 let ends = (from.min(vertex), from.max(vertex));
315 let next = carriers.len();
316 let piece = *by_ends.entry(ends).or_insert(next);
317 if piece == next {
318 carriers.push(BTreeSet::new());
319 }
320 carriers[piece].insert(index);
321 on_segments.push((index, piece, from < vertex));
322 }
323 }
324 previous = Some(vertex);
325 }
326 }
327 let mut ends_of = vec![(0, 0); carriers.len()];
329 for (&ends, &piece) in &by_ends {
330 ends_of[piece] = ends;
331 }
332 let mut renumber = vec![0usize; carriers.len()];
333 let mut order: Vec<usize> = (0..carriers.len()).collect();
334 order.sort_by_key(|&piece| ends_of[piece]);
335 for (rank, &piece) in order.iter().enumerate() {
336 renumber[piece] = rank;
337 }
338 let pieces: Vec<Piece> = order
339 .iter()
340 .map(|&piece| Piece {
341 ends: [VertexId(ends_of[piece].0), VertexId(ends_of[piece].1)],
342 segments: carriers[piece].iter().copied().collect(),
343 })
344 .collect();
345 let mut paths = vec![Vec::new(); segments.len()];
346 for (segment, piece, forward) in on_segments {
347 paths[segment].push((PieceId(renumber[piece]), forward));
348 }
349 let mut incident = vec![Vec::new(); vertices.len()];
350 for (index, piece) in pieces.iter().enumerate() {
351 incident[piece.ends[0].0].push((PieceId(index), true));
352 incident[piece.ends[1].0].push((PieceId(index), false));
353 }
354 Ok(Arrangement {
355 vertices,
356 pieces,
357 paths,
358 crossings,
359 incident,
360 })
361}
362
363fn cut_segments(
366 segments: &[Segment],
367 cuts: &mut [Cut],
368 points: &mut ExactPoints,
369 local: &mut Local,
370 (budget, declared_bytes): (&mut PairBudget, usize),
371 options: &ExecutionOptions,
372) -> GeomResult<()> {
373 let mut order: Vec<usize> = (0..cuts.len()).collect();
374 order.sort_by(|&left, &right| {
375 cuts[left].bounds[0]
376 .total_cmp(&cuts[right].bounds[0])
377 .then(left.cmp(&right))
378 });
379 let boxes: Vec<[f64; 4]> = order.iter().map(|&index| cuts[index].bounds).collect();
381 let mut charged = 0usize;
382 for (rank, &first) in order.iter().enumerate() {
383 options.check_cancelled()?;
384 for (&second, held) in order[rank + 1..].iter().zip(&boxes[rank + 1..]) {
385 if held[0] > boxes[rank][2] {
386 break;
387 }
388 if !boxes_overlap(&boxes[rank], held) {
389 continue;
390 }
391 options.check_cancelled()?;
392 budget.count()?;
393 let (low, high) = (first.min(second), first.max(second));
394 let [a, b] = segments[low].ends;
395 let [c, d] = segments[high].ends;
396 if [a, b] == [c, d] || [a, b] == [d, c] {
398 continue;
399 }
400 if let Some((shared, near, far)) = shared_end([a, b], [c, d]) {
404 let (low_ends, high_ends) = (cuts[low].ends, cuts[high].ends);
405 let local = |end: PointId| {
406 let (own, cut) = if [a, b].contains(&end) {
407 ([a, b], low_ends)
408 } else {
409 ([c, d], high_ends)
410 };
411 cut[usize::from(own[1] == end)]
412 };
413 if points.orient(shared, near, far)? == Sign::Zero {
414 if points.strictly_between(shared, near, far)? {
416 let longer = if [a, b].contains(&far) { low } else { high };
417 charge(options, declared_bytes, &mut charged, SPLIT_SCRATCH_BYTES)?;
418 cuts[longer].splits.push(local(near));
419 } else if points.strictly_between(shared, far, near)? {
420 let longer = if [a, b].contains(&near) { low } else { high };
421 charge(options, declared_bytes, &mut charged, SPLIT_SCRATCH_BYTES)?;
422 cuts[longer].splits.push(local(far));
423 }
424 }
425 continue;
426 }
427 let (past_c, past_d) = (points.orient(a, b, c)?, points.orient(a, b, d)?);
430 if past_c == past_d && past_c != Sign::Zero {
431 continue;
432 }
433 let (past_a, past_b) = (points.orient(c, d, a)?, points.orient(c, d, b)?);
434 if past_a == past_b && past_a != Sign::Zero {
435 continue;
436 }
437 let around = [past_c, past_d, past_a, past_b];
438 let proper = around.iter().all(|&sign| sign != Sign::Zero)
439 && around[0] != around[1]
440 && around[2] != around[3];
441 if proper {
442 charge(
443 options,
444 declared_bytes,
445 &mut charged,
446 CROSSING_SCRATCH_BYTES,
447 )?;
448 let ends = [a, b, c, d];
449 let point = points.segment_crossing(ends)?;
450 let new = local.push(point, Label::Crossing([low, high], ends));
451 cuts[low].splits.push(new);
452 cuts[high].splits.push(new);
453 } else {
454 let (low_ends, high_ends) = (cuts[low].ends, cuts[high].ends);
455 if around[0] == Sign::Zero && points.strictly_between(a, c, b)? {
456 charge(options, declared_bytes, &mut charged, SPLIT_SCRATCH_BYTES)?;
457 cuts[low].splits.push(high_ends[0]);
458 }
459 if around[1] == Sign::Zero && points.strictly_between(a, d, b)? {
460 charge(options, declared_bytes, &mut charged, SPLIT_SCRATCH_BYTES)?;
461 cuts[low].splits.push(high_ends[1]);
462 }
463 if around[2] == Sign::Zero && points.strictly_between(c, a, d)? {
464 charge(options, declared_bytes, &mut charged, SPLIT_SCRATCH_BYTES)?;
465 cuts[high].splits.push(low_ends[0]);
466 }
467 if around[3] == Sign::Zero && points.strictly_between(c, b, d)? {
468 charge(options, declared_bytes, &mut charged, SPLIT_SCRATCH_BYTES)?;
469 cuts[high].splits.push(low_ends[1]);
470 }
471 }
472 }
473 }
474 Ok(())
475}
476
477fn shared_end(first: [PointId; 2], second: [PointId; 2]) -> Option<(PointId, PointId, PointId)> {
480 for (i, &end) in first.iter().enumerate() {
481 if let Some(j) = second.iter().position(|&other| other == end) {
482 return Some((end, first[1 - i], second[1 - j]));
483 }
484 }
485 None
486}
487
488pub(crate) fn charge(
491 options: &ExecutionOptions,
492 declared_bytes: usize,
493 charged: &mut usize,
494 cost: usize,
495) -> GeomResult<()> {
496 let Some(budget) = options.memory_budget_bytes() else {
497 return Ok(());
498 };
499 let needed = charged
500 .checked_add(cost)
501 .and_then(|total| declared_bytes.checked_add(total));
502 match needed {
503 Some(needed) if needed <= budget => {
504 *charged += cost;
505 Ok(())
506 }
507 _ => Err(GeomError::BudgetExceeded { resource: "memory" }),
508 }
509}
510
511fn order_cuts(
513 cuts: &mut [Cut],
514 points: &ExactPoints,
515 local: &mut Local,
516 options: &ExecutionOptions,
517) -> GeomResult<()> {
518 for cut in cuts {
519 options.check_cancelled()?;
520 let [first, last] = cut.ends.map(|end| local.exact[end]);
521 let axis = points.dominant(first, last)?;
522 let descending = points.compare(first, last, axis)? == std::cmp::Ordering::Greater;
523 let mut inner = std::mem::take(&mut cut.splits);
524 inner.sort_unstable();
525 inner.dedup();
526 sort_exact(&mut inner, |left, right| {
527 let order = points.compare(local.exact[left], local.exact[right], axis)?;
528 Ok(if descending { order.reverse() } else { order })
529 })?;
530 cut.path = std::iter::once(cut.ends[0])
531 .chain(inner)
532 .chain(std::iter::once(cut.ends[1]))
533 .collect();
534 for pair in cut.path.windows(2) {
535 if points.same(local.exact[pair[0]], local.exact[pair[1]])? {
536 local.union(pair[0], pair[1]);
537 }
538 }
539 }
540 Ok(())
541}
542
543fn number_vertices(local: &mut Local) -> (Vec<Vertex>, BTreeMap<usize, usize>) {
546 struct Group {
547 inputs: Vec<PointId>,
548 crossing: Option<(Crossing, usize)>,
550 any: usize,
552 }
553 let mut groups = BTreeMap::<usize, Group>::new();
554 for point in 0..local.parent.len() {
555 let root = local.find(point);
556 let group = groups.entry(root).or_insert(Group {
557 inputs: Vec::new(),
558 crossing: None,
559 any: point,
560 });
561 match local.labels[point] {
562 Label::Input => group.inputs.push(local.exact[point]),
563 Label::Crossing(segments, ends) => {
564 if group
565 .crossing
566 .is_none_or(|(known, _)| segments < known.segments)
567 {
568 group.crossing = Some((Crossing { segments, ends }, point));
569 }
570 }
571 }
572 }
573 let mut ordered: Vec<(usize, Group)> = groups.into_iter().collect();
574 for (_, group) in &mut ordered {
575 group.inputs.sort_unstable();
576 }
577 ordered.sort_by(
578 |(_, left), (_, right)| match (left.inputs.first(), right.inputs.first()) {
579 (Some(a), Some(b)) => a.cmp(b),
580 (Some(_), None) => std::cmp::Ordering::Less,
581 (None, Some(_)) => std::cmp::Ordering::Greater,
582 (None, None) => left
583 .crossing
584 .map(|c| c.0.segments)
585 .cmp(&right.crossing.map(|c| c.0.segments)),
586 },
587 );
588 let mut vertex_of = BTreeMap::new();
589 let mut vertices = Vec::with_capacity(ordered.len());
590 for (number, (root, group)) in ordered.into_iter().enumerate() {
591 vertex_of.insert(root, number);
592 let (crossing, point) = match (group.inputs.is_empty(), group.crossing) {
593 (true, Some((crossing, point))) => (Some(crossing), point),
594 _ => (None, group.any),
595 };
596 vertices.push(Vertex {
597 point: local.exact[point],
598 inputs: group.inputs,
599 crossing,
600 });
601 }
602 (vertices, vertex_of)
603}
604
605impl Arrangement {
606 pub fn fan(&self, points: &ExactPoints, vertex: VertexId) -> GeomResult<Vec<(PieceId, bool)>> {
614 let here = self.vertices[vertex.0].point;
615 let mut spokes = Vec::with_capacity(self.incident[vertex.0].len());
616 for &(piece, forward) in &self.incident[vertex.0] {
617 let ends = self.pieces[piece.0].ends;
618 let to = self.vertices[ends[usize::from(forward)].0].point;
619 let upper = points.compare(to, here, 1)? == std::cmp::Ordering::Greater
620 || (points.compare(to, here, 1)? == std::cmp::Ordering::Equal
621 && points.compare(to, here, 0)? == std::cmp::Ordering::Greater);
622 spokes.push((piece, forward, to, u8::from(!upper)));
623 }
624 sort_exact(&mut spokes, |left, right| {
625 if left.3 != right.3 {
626 return Ok(left.3.cmp(&right.3));
627 }
628 match points.orient(here, left.2, right.2)? {
629 Sign::Positive => Ok(std::cmp::Ordering::Less),
630 Sign::Negative => Ok(std::cmp::Ordering::Greater),
631 _ => Err(inconsistent()),
632 }
633 })?;
634 Ok(spokes
635 .into_iter()
636 .map(|(piece, forward, _, _)| (piece, forward))
637 .collect())
638 }
639}