1use std::cmp::Ordering;
60use std::collections::BTreeMap;
61use std::sync::{Arc, OnceLock};
62
63use axiolid_core::Point2;
64use axiolid_exact::{certify, Arith, Dyadic, Interval, SignExpr};
65use axiolid_guarantees::Sign;
66
67use crate::exact_arc::{orient_doubles, round_ratio};
68
69#[non_exhaustive]
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum SegmentSweepError {
73 NonFinite {
75 segment: usize,
77 },
78}
79
80impl core::fmt::Display for SegmentSweepError {
81 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82 match self {
83 Self::NonFinite { segment } => {
84 write!(f, "segment {segment} has a non-finite coordinate")
85 }
86 }
87 }
88}
89
90impl std::error::Error for SegmentSweepError {}
91
92#[derive(Debug, Clone)]
99pub struct ExactPoint2 {
100 repr: Repr,
101}
102
103#[derive(Debug, Clone)]
104enum Repr {
105 Input(Point2),
106 Rational(Arc<Rational>),
107}
108
109#[derive(Debug)]
110struct Rational {
111 x: Dyadic,
112 y: Dyadic,
113 w: Dyadic,
114 bx: Interval,
116 by: Interval,
117 rounded: OnceLock<Point2>,
118}
119
120impl ExactPoint2 {
121 #[must_use]
123 pub fn from_homogeneous(x: Dyadic, y: Dyadic, w: Dyadic) -> Option<Self> {
124 let flip = match w.sign()? {
125 Sign::Positive => false,
126 Sign::Negative => true,
127 _ => return None,
128 };
129 let (x, y, w) = if flip {
130 (x.neg(), y.neg(), w.neg())
131 } else {
132 (x, y, w)
133 };
134 let we = w.enclosure();
135 Some(Self {
136 repr: Repr::Rational(Arc::new(Rational {
137 bx: x.enclosure().quotient(we),
138 by: y.enclosure().quotient(we),
139 x,
140 y,
141 w,
142 rounded: OnceLock::new(),
143 })),
144 })
145 }
146
147 fn input(point: Point2) -> Self {
148 Self {
149 repr: Repr::Input(point),
150 }
151 }
152
153 #[must_use]
158 pub fn as_input(&self) -> Option<Point2> {
159 match &self.repr {
160 Repr::Input(p) => Some(*p),
161 Repr::Rational(_) => None,
162 }
163 }
164
165 #[must_use]
167 pub fn homogeneous(&self) -> (Dyadic, Dyadic, Dyadic) {
168 match &self.repr {
169 Repr::Input(p) => (dy(p.x), dy(p.y), dy(1.0)),
170 Repr::Rational(r) => (r.x.clone(), r.y.clone(), r.w.clone()),
171 }
172 }
173
174 #[must_use]
180 pub fn rounded(&self) -> Point2 {
181 match &self.repr {
182 Repr::Input(p) => *p,
183 Repr::Rational(r) => *r.rounded.get_or_init(|| {
184 let guess = |n: &Dyadic| {
185 let (mn, en) = n.approx_parts();
186 let (mw, ew) = r.w.approx_parts();
187 scale2(mn / mw, en - ew)
188 };
189 Point2::new(
190 round_ratio(&r.x, &r.w, guess(&r.x)),
191 round_ratio(&r.y, &r.w, guess(&r.y)),
192 )
193 }),
194 }
195 }
196
197 fn bound(&self, axis: Axis) -> Interval {
198 match (&self.repr, axis) {
199 (Repr::Input(p), Axis::X) => Interval::point(p.x),
200 (Repr::Input(p), Axis::Y) => Interval::point(p.y),
201 (Repr::Rational(r), Axis::X) => r.bx,
202 (Repr::Rational(r), Axis::Y) => r.by,
203 }
204 }
205
206 fn exact(&self, axis: Axis) -> (Dyadic, Dyadic) {
207 match (&self.repr, axis) {
208 (Repr::Input(p), Axis::X) => (dy(p.x), dy(1.0)),
209 (Repr::Input(p), Axis::Y) => (dy(p.y), dy(1.0)),
210 (Repr::Rational(r), Axis::X) => (r.x.clone(), r.w.clone()),
211 (Repr::Rational(r), Axis::Y) => (r.y.clone(), r.w.clone()),
212 }
213 }
214}
215
216impl PartialEq for ExactPoint2 {
217 fn eq(&self, other: &Self) -> bool {
218 self.cmp(other) == Ordering::Equal
219 }
220}
221
222impl Eq for ExactPoint2 {}
223
224impl PartialOrd for ExactPoint2 {
225 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
226 Some(self.cmp(other))
227 }
228}
229
230impl Ord for ExactPoint2 {
231 fn cmp(&self, other: &Self) -> Ordering {
232 if let (Repr::Input(a), Repr::Input(b)) = (&self.repr, &other.repr) {
233 return cmp_f64(a.x, b.x).then_with(|| cmp_f64(a.y, b.y));
236 }
237 cmp_axis(self, other, Axis::X).then_with(|| cmp_axis(self, other, Axis::Y))
238 }
239}
240
241#[non_exhaustive]
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
245pub enum SegmentLocation {
246 Start,
248 End,
250 Interior,
252 Degenerate,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
258pub struct Incidence {
259 pub segment: usize,
261 pub location: SegmentLocation,
263}
264
265#[non_exhaustive]
267#[derive(Debug, Clone, PartialEq)]
268pub struct IntersectionPoint {
269 pub point: ExactPoint2,
271 pub rounded: Point2,
273 pub incidences: Vec<Incidence>,
275}
276
277#[non_exhaustive]
279#[derive(Debug, Clone, PartialEq)]
280pub struct SegmentOverlap {
281 pub from: Point2,
284 pub to: Point2,
286 pub segments: Vec<usize>,
289}
290
291#[non_exhaustive]
293#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
294pub struct SweepEvidence {
295 pub segments: usize,
297 pub events: usize,
299 pub crossings: usize,
301}
302
303#[non_exhaustive]
305#[derive(Debug, Clone, PartialEq)]
306pub struct SegmentIntersections {
307 pub points: Vec<IntersectionPoint>,
309 pub overlaps: Vec<SegmentOverlap>,
312 pub evidence: SweepEvidence,
314}
315
316pub fn segment_intersections(
336 segments: &[[Point2; 2]],
337) -> Result<SegmentIntersections, SegmentSweepError> {
338 let segs = prepare(segments)?;
339 Ok(Sweep::new(segs).run())
340}
341
342#[derive(Debug, Clone, Copy)]
345enum Axis {
346 X,
347 Y,
348}
349
350fn dy(value: f64) -> Dyadic {
351 Dyadic::from_f64(value)
352}
353
354fn to_i8(sign: Sign) -> i8 {
355 match sign {
356 Sign::Positive => 1,
357 Sign::Negative => -1,
358 _ => 0,
359 }
360}
361
362fn exact_sign(value: &Dyadic) -> i8 {
363 to_i8(value.sign().expect("dyadic signs are always decided"))
364}
365
366fn cmp_f64(a: f64, b: f64) -> Ordering {
367 a.partial_cmp(&b)
368 .expect("coordinates were validated finite")
369}
370
371fn scale2(mut x: f64, mut n: i64) -> f64 {
373 while n > 1000 && x.is_finite() && x != 0.0 {
374 x *= 2f64.powi(1000);
375 n -= 1000;
376 }
377 while n < -1000 && x != 0.0 {
378 x *= 2f64.powi(-1000);
379 n += 1000;
380 }
381 x * 2f64.powi(n as i32)
382}
383
384fn cmp_axis(p: &ExactPoint2, q: &ExactPoint2, axis: Axis) -> Ordering {
387 let (pb, qb) = (p.bound(axis), q.bound(axis));
388 if pb.hi() < qb.lo() {
389 return Ordering::Less;
390 }
391 if qb.hi() < pb.lo() {
392 return Ordering::Greater;
393 }
394 let (pn, pw) = p.exact(axis);
395 let (qn, qw) = q.exact(axis);
396 exact_sign(&pn.mul(&qw).sub(&qn.mul(&pw))).cmp(&0)
397}
398
399fn orient_point(a: Point2, b: Point2, p: &ExactPoint2) -> i8 {
401 match &p.repr {
402 Repr::Input(c) => {
403 if *c == a || *c == b {
404 return 0;
405 }
406 to_i8(orient_doubles(a, b, *c))
407 }
408 Repr::Rational(r) => {
409 let (ax, ay) = (Interval::point(a.x), Interval::point(a.y));
410 let bax = Interval::point(b.x).sub(&ax);
411 let bay = Interval::point(b.y).sub(&ay);
412 let value = bax.mul(&r.by.sub(&ay)).sub(&bay.mul(&r.bx.sub(&ax)));
413 if let Some(sign) = value.sign() {
414 return to_i8(sign);
415 }
416 let (ax, ay) = (dy(a.x), dy(a.y));
418 let bax = dy(b.x).sub(&ax);
419 let bay = dy(b.y).sub(&ay);
420 let value = bax
421 .mul(&r.y.sub(&ay.mul(&r.w)))
422 .sub(&bay.mul(&r.x.sub(&ax.mul(&r.w))));
423 exact_sign(&value)
424 }
425 }
426}
427
428struct CrossDirections([Point2; 4]);
430
431impl SignExpr for CrossDirections {
432 fn sign_in<T: Arith>(&self) -> Option<Sign> {
433 let [u0, u1, v0, v1] = self.0;
434 let ux = T::from_f64(u1.x).sub(&T::from_f64(u0.x));
435 let uy = T::from_f64(u1.y).sub(&T::from_f64(u0.y));
436 let vx = T::from_f64(v1.x).sub(&T::from_f64(v0.x));
437 let vy = T::from_f64(v1.y).sub(&T::from_f64(v0.y));
438 ux.mul(&vy).sub(&uy.mul(&vx)).sign()
439 }
440}
441
442#[derive(Debug, Clone, Copy)]
446struct Seg {
447 lo: Point2,
448 hi: Point2,
449 reversed: bool,
451 degenerate: bool,
452}
453
454impl Seg {
455 fn lo_location(&self) -> SegmentLocation {
456 if self.degenerate {
457 SegmentLocation::Degenerate
458 } else if self.reversed {
459 SegmentLocation::End
460 } else {
461 SegmentLocation::Start
462 }
463 }
464
465 fn hi_location(&self) -> SegmentLocation {
466 if self.reversed {
467 SegmentLocation::Start
468 } else {
469 SegmentLocation::End
470 }
471 }
472}
473
474fn prepare(segments: &[[Point2; 2]]) -> Result<Vec<Seg>, SegmentSweepError> {
475 segments
476 .iter()
477 .enumerate()
478 .map(|(index, [a, b])| {
479 if ![a.x, a.y, b.x, b.y].iter().all(|v| v.is_finite()) {
480 return Err(SegmentSweepError::NonFinite { segment: index });
481 }
482 let reversed = cmp_f64(a.x, b.x).then_with(|| cmp_f64(a.y, b.y)) == Ordering::Greater;
483 let (lo, hi) = if reversed { (*b, *a) } else { (*a, *b) };
484 Ok(Seg {
485 lo,
486 hi,
487 reversed,
488 degenerate: lo == hi,
489 })
490 })
491 .collect()
492}
493
494fn cross_sign(s: &Seg, t: &Seg) -> i8 {
497 let expr = CrossDirections([s.lo, s.hi, t.lo, t.hi]);
498 to_i8(certify(&expr).expect("inputs were validated finite"))
499}
500
501fn boxes_meet(s: &Seg, t: &Seg) -> bool {
502 let (sy0, sy1) = (s.lo.y.min(s.hi.y), s.lo.y.max(s.hi.y));
503 let (ty0, ty1) = (t.lo.y.min(t.hi.y), t.lo.y.max(t.hi.y));
504 s.lo.x <= t.hi.x && t.lo.x <= s.hi.x && sy0 <= ty1 && ty0 <= sy1
505}
506
507fn proper_crossing(s: &Seg, t: &Seg) -> bool {
509 if !boxes_meet(s, t) {
510 return false;
511 }
512 let o = |a: Point2, b: Point2, c: Point2| to_i8(orient_doubles(a, b, c));
513 let (o1, o2) = (o(s.lo, s.hi, t.lo), o(s.lo, s.hi, t.hi));
514 if o1 == 0 || o2 == 0 || o1 == o2 {
515 return false;
516 }
517 let (o3, o4) = (o(t.lo, t.hi, s.lo), o(t.lo, t.hi, s.hi));
518 o3 != 0 && o4 != 0 && o3 != o4
519}
520
521fn crossing_point(s: &Seg, t: &Seg) -> ExactPoint2 {
524 line_crossing(s.lo, s.hi, t.lo, t.hi).expect("proper crossings are not parallel")
525}
526
527fn line_crossing(a: Point2, b: Point2, c: Point2, d: Point2) -> Option<ExactPoint2> {
530 let (ax, ay) = (dy(a.x), dy(a.y));
531 let (ux, uy) = (dy(b.x).sub(&ax), dy(b.y).sub(&ay));
532 let (cx, cy) = (dy(c.x), dy(c.y));
533 let (vx, vy) = (dy(d.x).sub(&cx), dy(d.y).sub(&cy));
534 let den = ux.mul(&vy).sub(&uy.mul(&vx));
535 let (acx, acy) = (cx.sub(&ax), cy.sub(&ay));
536 let num = acx.mul(&vy).sub(&acy.mul(&vx));
537 let x = ax.mul(&den).add(&num.mul(&ux));
538 let y = ay.mul(&den).add(&num.mul(&uy));
539 ExactPoint2::from_homogeneous(x, y, den)
540}
541
542pub(crate) fn rounded_crossing(a: Point2, b: Point2, c: Point2, d: Point2) -> Option<Point2> {
546 line_crossing(a, b, c, d).map(|p| p.rounded())
547}
548
549const NIL: u32 = u32::MAX;
552
553#[derive(Debug, Clone, Copy)]
554struct Node {
555 left: u32,
556 right: u32,
557 parent: u32,
558 priority: u64,
559}
560
561struct Status {
568 nodes: Vec<Node>,
569 root: u32,
570 state: u64,
571}
572
573impl Status {
574 fn new(count: usize) -> Self {
575 let empty = Node {
576 left: NIL,
577 right: NIL,
578 parent: NIL,
579 priority: 0,
580 };
581 Self {
582 nodes: vec![empty; count],
583 root: NIL,
584 state: 0x9E37_79B9_7F4A_7C15,
585 }
586 }
587
588 fn random(&mut self) -> u64 {
589 self.state ^= self.state >> 12;
591 self.state ^= self.state << 25;
592 self.state ^= self.state >> 27;
593 self.state.wrapping_mul(0x2545_F491_4F6C_DD1D)
594 }
595
596 fn node(&self, id: u32) -> &Node {
597 &self.nodes[id as usize]
598 }
599
600 fn node_mut(&mut self, id: u32) -> &mut Node {
601 &mut self.nodes[id as usize]
602 }
603
604 fn first_where(&self, mut pred: impl FnMut(u32) -> bool) -> u32 {
607 let (mut current, mut found) = (self.root, NIL);
608 while current != NIL {
609 if pred(current) {
610 found = current;
611 current = self.node(current).left;
612 } else {
613 current = self.node(current).right;
614 }
615 }
616 found
617 }
618
619 fn min_from(&self, mut id: u32) -> u32 {
620 while self.node(id).left != NIL {
621 id = self.node(id).left;
622 }
623 id
624 }
625
626 fn max_from(&self, mut id: u32) -> u32 {
627 while self.node(id).right != NIL {
628 id = self.node(id).right;
629 }
630 id
631 }
632
633 fn last(&self) -> u32 {
634 if self.root == NIL {
635 NIL
636 } else {
637 self.max_from(self.root)
638 }
639 }
640
641 fn next(&self, mut id: u32) -> u32 {
642 if self.node(id).right != NIL {
643 return self.min_from(self.node(id).right);
644 }
645 loop {
646 let parent = self.node(id).parent;
647 if parent == NIL || self.node(parent).left == id {
648 return parent;
649 }
650 id = parent;
651 }
652 }
653
654 fn prev(&self, mut id: u32) -> u32 {
655 if self.node(id).left != NIL {
656 return self.max_from(self.node(id).left);
657 }
658 loop {
659 let parent = self.node(id).parent;
660 if parent == NIL || self.node(parent).right == id {
661 return parent;
662 }
663 id = parent;
664 }
665 }
666
667 fn relink(&mut self, parent: u32, old: u32, new: u32) {
669 if parent == NIL {
670 self.root = new;
671 } else if self.node(parent).left == old {
672 self.node_mut(parent).left = new;
673 } else {
674 self.node_mut(parent).right = new;
675 }
676 if new != NIL {
677 self.node_mut(new).parent = parent;
678 }
679 }
680
681 fn rotate_up(&mut self, id: u32) {
683 let parent = self.node(id).parent;
684 let grand = self.node(parent).parent;
685 if self.node(parent).left == id {
686 let middle = self.node(id).right;
687 self.node_mut(parent).left = middle;
688 if middle != NIL {
689 self.node_mut(middle).parent = parent;
690 }
691 self.node_mut(id).right = parent;
692 } else {
693 let middle = self.node(id).left;
694 self.node_mut(parent).right = middle;
695 if middle != NIL {
696 self.node_mut(middle).parent = parent;
697 }
698 self.node_mut(id).left = parent;
699 }
700 self.node_mut(parent).parent = id;
701 self.relink(grand, parent, id);
702 }
703
704 fn insert_before(&mut self, id: u32, before: u32) {
707 let priority = self.random();
708 *self.node_mut(id) = Node {
709 left: NIL,
710 right: NIL,
711 parent: NIL,
712 priority,
713 };
714 if self.root == NIL {
715 self.root = id;
716 return;
717 }
718 if before == NIL {
719 let last = self.max_from(self.root);
720 self.node_mut(last).right = id;
721 self.node_mut(id).parent = last;
722 } else if self.node(before).left == NIL {
723 self.node_mut(before).left = id;
724 self.node_mut(id).parent = before;
725 } else {
726 let last = self.max_from(self.node(before).left);
727 self.node_mut(last).right = id;
728 self.node_mut(id).parent = last;
729 }
730 while self.node(id).parent != NIL
731 && self.node(self.node(id).parent).priority < self.node(id).priority
732 {
733 self.rotate_up(id);
734 }
735 }
736
737 fn remove(&mut self, id: u32) {
738 loop {
739 let Node { left, right, .. } = *self.node(id);
740 if left == NIL || right == NIL {
741 let child = if left == NIL { right } else { left };
742 let parent = self.node(id).parent;
743 self.relink(parent, id, child);
744 return;
745 }
746 let up = if self.node(left).priority > self.node(right).priority {
747 left
748 } else {
749 right
750 };
751 self.rotate_up(up);
752 }
753 }
754}
755
756struct Piece {
759 from: ExactPoint2,
760 to: Option<ExactPoint2>,
761 segments: Vec<usize>,
762}
763
764struct Sweep {
765 segs: Vec<Seg>,
766 queue: BTreeMap<ExactPoint2, Vec<u32>>,
768 status: Status,
769 open: Vec<Option<usize>>,
771 pieces: Vec<Piece>,
772 closed_here: Vec<usize>,
774 points: Vec<IntersectionPoint>,
775 evidence: SweepEvidence,
776}
777
778impl Sweep {
779 #[allow(clippy::mutable_key_type)]
782 fn new(segs: Vec<Seg>) -> Self {
783 let mut queue: BTreeMap<ExactPoint2, Vec<u32>> = BTreeMap::new();
784 for (index, seg) in segs.iter().enumerate() {
785 queue
786 .entry(ExactPoint2::input(seg.lo))
787 .or_default()
788 .push(index as u32);
789 if !seg.degenerate {
790 queue.entry(ExactPoint2::input(seg.hi)).or_default();
791 }
792 }
793 let count = segs.len();
794 Self {
795 status: Status::new(count),
796 open: vec![None; count],
797 pieces: Vec::new(),
798 closed_here: Vec::new(),
799 points: Vec::new(),
800 evidence: SweepEvidence {
801 segments: count,
802 ..SweepEvidence::default()
803 },
804 queue,
805 segs,
806 }
807 }
808
809 fn run(mut self) -> SegmentIntersections {
810 while let Some((p, starts)) = self.queue.pop_first() {
811 self.evidence.events += 1;
812 self.event(&p, &starts);
813 }
814 let mut overlaps: Vec<SegmentOverlap> = self
815 .pieces
816 .into_iter()
817 .map(|piece| {
818 let to = piece.to.expect("every overlap ends at an event");
819 SegmentOverlap {
820 from: endpoint(&piece.from),
821 to: endpoint(&to),
822 segments: piece.segments,
823 }
824 })
825 .collect();
826 overlaps
827 .sort_by(|a, b| cmp_f64(a.from.x, b.from.x).then_with(|| cmp_f64(a.from.y, b.from.y)));
828 SegmentIntersections {
829 points: self.points,
830 overlaps,
831 evidence: self.evidence,
832 }
833 }
834
835 fn event(&mut self, p: &ExactPoint2, starts: &[u32]) {
836 let segs = &self.segs;
839 let side = |id: u32| {
840 let seg = &segs[id as usize];
841 orient_point(seg.lo, seg.hi, p)
842 };
843 let first = self.status.first_where(|id| side(id) <= 0);
844 let mut block = Vec::new();
845 let mut above = first;
846 while above != NIL && side(above) == 0 {
847 block.push(above);
848 above = self.status.next(above);
849 }
850 let below = if first == NIL {
851 self.status.last()
852 } else {
853 self.status.prev(first)
854 };
855
856 let at = p.as_input();
860 let ended: Vec<bool> = block
861 .iter()
862 .map(|&id| at.is_some_and(|q| segs[id as usize].hi == q))
863 .collect();
864
865 self.report(p, starts, &block, &ended);
866 self.close_overlaps(p, &block);
867
868 for &id in &block {
869 self.status.remove(id);
870 }
871 let mut through: Vec<u32> = starts
872 .iter()
873 .copied()
874 .filter(|&id| !self.segs[id as usize].degenerate)
875 .chain(
876 block
877 .iter()
878 .zip(&ended)
879 .filter(|(_, &ended)| !ended)
880 .map(|(&id, _)| id),
881 )
882 .collect();
883 let segs = &self.segs;
884 through.sort_by(
885 |&s, &t| match cross_sign(&segs[s as usize], &segs[t as usize]) {
886 1 => Ordering::Less,
887 -1 => Ordering::Greater,
888 _ => s.cmp(&t),
889 },
890 );
891 for &id in &through {
892 self.status.insert_before(id, above);
893 }
894 self.open_overlaps(p, &through);
895
896 match (through.first(), through.last()) {
897 (Some(&low), Some(&high)) => {
898 self.check(below, low, p);
899 self.check(high, above, p);
900 }
901 _ => self.check(below, above, p),
902 }
903 }
904
905 fn report(&mut self, p: &ExactPoint2, starts: &[u32], block: &[u32], ended: &[bool]) {
906 if starts.len() + block.len() < 2 {
907 return;
908 }
909 let mut incidences = Vec::with_capacity(starts.len() + block.len());
910 for &id in starts {
911 incidences.push(Incidence {
912 segment: id as usize,
913 location: self.segs[id as usize].lo_location(),
914 });
915 }
916 for (&id, &ends) in block.iter().zip(ended) {
917 let location = if ends {
918 self.segs[id as usize].hi_location()
919 } else {
920 SegmentLocation::Interior
921 };
922 incidences.push(Incidence {
923 segment: id as usize,
924 location,
925 });
926 }
927 incidences.sort_unstable();
931 self.points.push(IntersectionPoint {
932 rounded: p.rounded(),
933 point: p.clone(),
934 incidences,
935 });
936 }
937
938 fn close_overlaps(&mut self, p: &ExactPoint2, block: &[u32]) {
939 self.closed_here.clear();
940 for &id in block {
941 if let Some(index) = self.open[id as usize].take() {
942 let piece = &mut self.pieces[index];
943 if piece.to.is_none() {
944 piece.to = Some(p.clone());
945 self.closed_here.push(index);
946 }
947 }
948 }
949 }
950
951 fn open_overlaps(&mut self, p: &ExactPoint2, through: &[u32]) {
955 let mut start = 0;
956 while start < through.len() {
957 let head = &self.segs[through[start] as usize];
958 let mut end = start + 1;
959 while end < through.len() && cross_sign(head, &self.segs[through[end] as usize]) == 0 {
960 end += 1;
961 }
962 if end - start >= 2 {
963 let mut members: Vec<usize> =
964 through[start..end].iter().map(|&id| id as usize).collect();
965 members.sort_unstable();
966 let resumed = self
967 .closed_here
968 .iter()
969 .copied()
970 .find(|&index| self.pieces[index].segments == members);
971 let index = if let Some(index) = resumed {
972 self.pieces[index].to = None;
973 index
974 } else {
975 self.pieces.push(Piece {
976 from: p.clone(),
977 to: None,
978 segments: members.clone(),
979 });
980 self.pieces.len() - 1
981 };
982 for &member in &members {
983 self.open[member] = Some(index);
984 }
985 }
986 start = end;
987 }
988 }
989
990 fn check(&mut self, low: u32, high: u32, p: &ExactPoint2) {
992 if low == NIL || high == NIL {
993 return;
994 }
995 let (s, t) = (&self.segs[low as usize], &self.segs[high as usize]);
996 if !proper_crossing(s, t) {
997 return;
998 }
999 let q = crossing_point(s, t);
1000 if q > *p {
1001 if !self.queue.contains_key(&q) {
1002 self.evidence.crossings += 1;
1003 }
1004 self.queue.entry(q).or_default();
1005 }
1006 }
1007}
1008
1009fn endpoint(point: &ExactPoint2) -> Point2 {
1012 debug_assert!(point.as_input().is_some(), "overlap ends are input points");
1013 point.as_input().unwrap_or_else(|| point.rounded())
1014}