1use std::cell::Cell;
20use std::cmp::Ordering;
21
22use axiolid_contracts::{GeomError, GeomResult, Sign};
23use axiolid_core::Point2;
24use axiolid_exact::{Arith, Dyadic, Interval};
25use axiolid_predicates::orient2d;
26
27use crate::ratio::round_ratio;
28
29type Homogeneous<T> = [T; 3];
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct PointId(pub usize);
35
36#[derive(Debug, Clone)]
37struct ExactPoint {
38 exact: Homogeneous<Dyadic>,
39 enclosure: Homogeneous<Interval>,
40 bounds: [f64; 4],
42 flat: Option<Point2>,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum Location {
49 Inside,
51 Outside,
53 Boundary,
55}
56
57#[derive(Debug, Clone, Copy)]
59pub struct RingBox {
60 bounds: [f64; 4],
63 core: [f64; 4],
66}
67
68impl RingBox {
69 pub fn possibly_within(&self, other: &Self) -> bool {
72 self.core[0] >= other.bounds[0]
73 && self.core[1] >= other.bounds[1]
74 && self.core[2] <= other.bounds[2]
75 && self.core[3] <= other.bounds[3]
76 }
77}
78
79#[derive(Debug, Clone)]
86pub struct ExactPoints {
87 frame_sign: Sign,
88 points: Vec<ExactPoint>,
89 escalations: Cell<usize>,
90}
91
92impl Default for ExactPoints {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98fn not_finite() -> GeomError {
99 GeomError::Degenerate("an exact point is not finite".into())
100}
101
102pub(crate) fn undecided() -> GeomError {
103 GeomError::Degenerate("an exact sign could not be decided".into())
104}
105
106pub(crate) fn inconsistent() -> GeomError {
107 GeomError::Degenerate("the exact arrangement state is inconsistent".into())
108}
109
110fn exact(value: f64) -> GeomResult<Dyadic> {
111 Dyadic::try_from_f64(value).ok_or_else(not_finite)
112}
113
114fn minor<T: Arith>(p: &Homogeneous<T>, q: &Homogeneous<T>, i: usize, j: usize) -> T {
116 p[i].mul(&q[j]).sub(&q[i].mul(&p[j]))
117}
118
119fn cross<T: Arith>(p: &Homogeneous<T>, q: &Homogeneous<T>) -> Homogeneous<T> {
121 [minor(p, q, 1, 2), minor(p, q, 2, 0), minor(p, q, 0, 1)]
122}
123
124fn determinant<T: Arith>(a: &Homogeneous<T>, b: &Homogeneous<T>, c: &Homogeneous<T>) -> T {
127 a[0].mul(&minor(b, c, 1, 2))
128 .sub(&a[1].mul(&minor(b, c, 0, 2)))
129 .add(&a[2].mul(&minor(b, c, 0, 1)))
130}
131
132fn difference<T: Arith>(a: &Homogeneous<T>, b: &Homogeneous<T>, axis: usize) -> T {
134 a[axis].mul(&b[2]).sub(&b[axis].mul(&a[2]))
135}
136
137pub(crate) fn ordering(sign: Sign) -> Ordering {
138 match sign {
139 Sign::Positive => Ordering::Greater,
140 Sign::Negative => Ordering::Less,
141 _ => Ordering::Equal,
142 }
143}
144
145fn coordinate(point: Point2, axis: usize) -> f64 {
146 if axis == 0 {
147 point.x
148 } else {
149 point.y
150 }
151}
152
153fn sign_times(left: Sign, right: Sign) -> Sign {
155 if left == Sign::Zero || right == Sign::Zero {
156 Sign::Zero
157 } else if left == right {
158 Sign::Positive
159 } else {
160 Sign::Negative
161 }
162}
163
164fn exact_quotient(numerator: &Dyadic, weight: &Dyadic, hint: Option<f64>) -> Option<f64> {
167 let candidate = match hint {
168 Some(hint) => hint,
169 None if numerator.bits() <= 53 && weight.bits() <= 53 => {
173 numerator.to_f64() / weight.to_f64()
174 }
175 None => return None,
176 };
177 let exact_candidate = Dyadic::try_from_f64(candidate)?;
178 (exact_candidate.mul(weight) == *numerator).then_some(candidate)
179}
180
181impl ExactPoints {
182 pub fn new() -> Self {
184 Self::with_frame_sign(Sign::Positive)
185 }
186
187 pub fn with_frame_sign(frame_sign: Sign) -> Self {
190 Self {
191 frame_sign,
192 points: Vec::new(),
193 escalations: Cell::new(0),
194 }
195 }
196
197 pub fn frame_sign(&self) -> Sign {
199 self.frame_sign
200 }
201
202 pub fn len(&self) -> usize {
204 self.points.len()
205 }
206
207 pub fn is_empty(&self) -> bool {
209 self.points.is_empty()
210 }
211
212 pub fn push_flat(&mut self, flat: Point2) -> GeomResult<PointId> {
214 let point = [exact(flat.x)?, exact(flat.y)?, exact(1.0)?];
215 let enclosure = [
216 Interval::point(flat.x),
217 Interval::point(flat.y),
218 Interval::point(1.0),
219 ];
220 self.points.push(ExactPoint {
221 exact: point,
222 enclosure,
223 bounds: [flat.x, flat.y, flat.x, flat.y],
224 flat: Some(flat),
225 });
226 Ok(PointId(self.points.len() - 1))
227 }
228
229 pub fn push(&mut self, homogeneous: [Dyadic; 3]) -> GeomResult<PointId> {
233 self.push_hinted(homogeneous, [None, None])
234 }
235
236 pub fn push_hinted(
240 &mut self,
241 homogeneous: [Dyadic; 3],
242 hints: [Option<f64>; 2],
243 ) -> GeomResult<PointId> {
244 let mut point = homogeneous;
245 match point[2].sign() {
246 Some(Sign::Positive) => {}
247 Some(Sign::Negative) => point = point.map(|value| value.neg()),
248 _ => return Err(inconsistent()),
249 }
250 if let (Some(x), Some(y)) = (
251 exact_quotient(&point[0], &point[2], hints[0]),
252 exact_quotient(&point[1], &point[2], hints[1]),
253 ) {
254 return self.push_flat(Point2::new(x, y));
255 }
256 let enclosure = [
257 point[0].enclosure(),
258 point[1].enclosure(),
259 point[2].enclosure(),
260 ];
261 let (x, y) = (
262 enclosure[0].quotient(enclosure[2]),
263 enclosure[1].quotient(enclosure[2]),
264 );
265 self.points.push(ExactPoint {
266 exact: point,
267 enclosure,
268 bounds: [x.lo(), y.lo(), x.hi(), y.hi()],
269 flat: None,
270 });
271 Ok(PointId(self.points.len() - 1))
272 }
273
274 pub fn flat(&self, point: PointId) -> Option<Point2> {
276 self.points[point.0].flat
277 }
278
279 pub fn exact(&self, point: PointId) -> &[Dyadic; 3] {
281 &self.points[point.0].exact
282 }
283
284 pub fn segment_crossing(&mut self, ends: [PointId; 4]) -> GeomResult<PointId> {
292 let [a, b, c, d] = ends.map(|end| &self.points[end.0]);
293 let point = cross(&cross(&a.exact, &b.exact), &cross(&c.exact, &d.exact));
294 let fixed = |axis: usize| -> Option<f64> {
296 [(a, b), (c, d)].into_iter().find_map(|(from, to)| {
297 let (from, to) = (coordinate(from.flat?, axis), coordinate(to.flat?, axis));
298 (from == to).then_some(from)
299 })
300 };
301 let hints = [fixed(0), fixed(1)];
302 self.push_hinted(point, hints)
303 }
304
305 pub fn segment_parameter(&self, ends: [PointId; 4]) -> GeomResult<f64> {
309 let [a, b, c, d] = ends.map(|end| &self.points[end.0].exact);
310 let difference = |to: &Homogeneous<Dyadic>, from: &Homogeneous<Dyadic>| {
311 [to[0].sub(&from[0]), to[1].sub(&from[1])]
312 };
313 let turn = |p: [Dyadic; 2], q: [Dyadic; 2]| p[0].mul(&q[1]).sub(&p[1].mul(&q[0]));
314 let along = difference(d, c);
315 let numerator = turn(difference(c, a), along.clone());
316 let denominator = turn(difference(b, a), along);
317 if denominator.sign() == Some(Sign::Zero) {
318 return Err(inconsistent());
319 }
320 let (m_n, e_n) = numerator.approx_parts();
321 let (m_d, e_d) = denominator.approx_parts();
322 let shift = (e_n - e_d).clamp(-1100, 1100) as i32;
323 let t = m_n / m_d * 2f64.powi(shift / 2) * 2f64.powi(shift - shift / 2);
324 if t.is_finite() {
325 Ok(t)
326 } else {
327 Err(not_finite())
328 }
329 }
330
331 pub fn orient(&self, a: PointId, b: PointId, c: PointId) -> GeomResult<Sign> {
334 let (a, b, c) = (&self.points[a.0], &self.points[b.0], &self.points[c.0]);
335 if let (Some(fa), Some(fb), Some(fc)) = (a.flat, b.flat, c.flat) {
336 let sign = orient2d(fa, fb, fc).sign().ok_or_else(undecided)?;
337 return Ok(sign_times(sign, self.frame_sign));
338 }
339 let sign = match determinant(&a.enclosure, &b.enclosure, &c.enclosure).sign() {
340 Some(sign) => sign,
341 None => {
342 self.escalations.set(self.escalations.get() + 1);
343 determinant(&a.exact, &b.exact, &c.exact)
344 .sign()
345 .ok_or_else(undecided)?
346 }
347 };
348 Ok(sign_times(sign, self.frame_sign))
349 }
350
351 pub fn compare(&self, a: PointId, b: PointId, axis: usize) -> GeomResult<Ordering> {
354 let (a, b) = (&self.points[a.0], &self.points[b.0]);
355 if let (Some(fa), Some(fb)) = (a.flat, b.flat) {
356 return coordinate(fa, axis)
357 .partial_cmp(&coordinate(fb, axis))
358 .ok_or_else(not_finite);
359 }
360 let sign = match difference(&a.enclosure, &b.enclosure, axis).sign() {
361 Some(sign) => sign,
362 None => {
363 self.escalations.set(self.escalations.get() + 1);
364 difference(&a.exact, &b.exact, axis)
365 .sign()
366 .ok_or_else(undecided)?
367 }
368 };
369 Ok(ordering(sign))
370 }
371
372 pub fn same(&self, a: PointId, b: PointId) -> GeomResult<bool> {
374 Ok(a == b
375 || (self.compare(a, b, 0)? == Ordering::Equal
376 && self.compare(a, b, 1)? == Ordering::Equal))
377 }
378
379 pub fn lexicographic(&self, a: PointId, b: PointId) -> GeomResult<Ordering> {
381 match self.compare(a, b, 0)? {
382 Ordering::Equal => self.compare(a, b, 1),
383 order => Ok(order),
384 }
385 }
386
387 pub fn bounds(&self, point: PointId) -> [f64; 4] {
389 self.points[point.0].bounds
390 }
391
392 pub fn dominant(&self, a: PointId, b: PointId) -> GeomResult<usize> {
394 Ok(if self.compare(a, b, 0)? == Ordering::Equal {
395 1
396 } else {
397 0
398 })
399 }
400
401 pub fn ordered(&self, a: PointId, b: PointId, axis: usize) -> GeomResult<(PointId, PointId)> {
403 Ok(if self.compare(a, b, axis)? == Ordering::Greater {
404 (b, a)
405 } else {
406 (a, b)
407 })
408 }
409
410 pub fn strictly_between(
412 &self,
413 before: PointId,
414 at: PointId,
415 after: PointId,
416 ) -> GeomResult<bool> {
417 let axis = self.dominant(before, after)?;
418 let (low, high) = self.ordered(before, after, axis)?;
419 Ok(self.compare(low, at, axis)? == Ordering::Less
420 && self.compare(at, high, axis)? == Ordering::Less)
421 }
422
423 pub fn within_segment(&self, point: PointId, start: PointId, end: PointId) -> GeomResult<bool> {
425 let axis = self.dominant(start, end)?;
426 let (low, high) = self.ordered(start, end, axis)?;
427 Ok(self.compare(low, point, axis)? != Ordering::Greater
428 && self.compare(point, high, axis)? != Ordering::Greater)
429 }
430
431 pub fn locate(&self, point: PointId, ring: &[PointId]) -> GeomResult<Location> {
439 let mut winding = 0i32;
440 for index in 0..ring.len() {
441 let (a, b) = (ring[index], ring[(index + 1) % ring.len()]);
442 let side = self.orient(a, b, point)?;
443 if side == Sign::Zero && self.within_segment(point, a, b)? {
444 return Ok(Location::Boundary);
445 }
446 if self.compare(a, point, 1)? != Ordering::Greater {
447 if self.compare(b, point, 1)? == Ordering::Greater && side == Sign::Positive {
448 winding += 1;
449 }
450 } else if self.compare(b, point, 1)? != Ordering::Greater && side == Sign::Negative {
451 winding -= 1;
452 }
453 }
454 Ok(if winding == 0 {
455 Location::Outside
456 } else {
457 Location::Inside
458 })
459 }
460
461 pub fn ring_box(&self, ring: &[PointId]) -> RingBox {
463 let mut bounds = [
464 f64::INFINITY,
465 f64::INFINITY,
466 f64::NEG_INFINITY,
467 f64::NEG_INFINITY,
468 ];
469 let mut core = bounds;
470 for &node in ring {
471 let [x_lo, y_lo, x_hi, y_hi] = self.bounds(node);
472 bounds = [
473 bounds[0].min(x_lo),
474 bounds[1].min(y_lo),
475 bounds[2].max(x_hi),
476 bounds[3].max(y_hi),
477 ];
478 core = [
479 core[0].min(x_hi),
480 core[1].min(y_hi),
481 core[2].max(x_lo),
482 core[3].max(y_lo),
483 ];
484 }
485 RingBox { bounds, core }
486 }
487
488 pub fn round(&self, point: PointId) -> GeomResult<Point2> {
492 let point = &self.points[point.0];
493 if let Some(flat) = point.flat {
494 return Ok(flat);
495 }
496 let [x, y, w] = &point.exact;
497 match (round_ratio(x, w), round_ratio(y, w)) {
498 (Some(x), Some(y)) => Ok(Point2::new(x, y)),
499 _ => Err(not_finite()),
500 }
501 }
502
503 pub fn escalations(&self) -> usize {
506 self.escalations.get()
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513
514 fn flats(points: &mut ExactPoints, corners: &[(f64, f64)]) -> Vec<PointId> {
515 corners
516 .iter()
517 .map(|&(x, y)| points.push_flat(Point2::new(x, y)).expect("point"))
518 .collect()
519 }
520
521 #[test]
522 fn the_diagonals_of_the_unit_square_meet_at_its_centre_on_the_fast_path() {
523 let mut points = ExactPoints::new();
524 let at = flats(
525 &mut points,
526 &[(0.0, 0.0), (1.0, 1.0), (1.0, 0.0), (0.0, 1.0), (0.5, 0.5)],
527 );
528 let crossing = points
529 .segment_crossing([at[0], at[1], at[2], at[3]])
530 .expect("crossing");
531 assert_eq!(points.flat(crossing), Some(Point2::new(0.5, 0.5)));
532 assert!(points.same(crossing, at[4]).expect("same"));
533 }
534
535 #[test]
536 fn a_segment_crossing_does_not_depend_on_the_direction_of_its_segments() {
537 let mut points = ExactPoints::new();
538 let at = flats(
539 &mut points,
540 &[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0), (0.0, 1.0)],
541 );
542 let forward = points
543 .segment_crossing([at[0], at[1], at[2], at[3]])
544 .expect("crossing");
545 let swapped = points
546 .segment_crossing([at[2], at[3], at[0], at[1]])
547 .expect("crossing");
548 let reversed = points
549 .segment_crossing([at[1], at[0], at[3], at[2]])
550 .expect("crossing");
551 assert_eq!(points.flat(forward), None);
552 let exact = points.exact(forward);
553 assert_eq!(exact[0].add(&exact[0]), exact[1]);
555 assert_eq!(exact[2].sign(), Some(Sign::Positive));
556 for other in [swapped, reversed] {
557 assert!(points.same(forward, other).expect("same"));
558 assert_eq!(points.exact(forward), points.exact(other));
559 }
560 }
561
562 #[test]
563 fn crossings_on_one_segment_are_collinear_in_either_frame_sense() {
564 for (frame_sign, turn) in [
565 (Sign::Positive, Sign::Positive),
566 (Sign::Negative, Sign::Negative),
567 ] {
568 let mut points = ExactPoints::with_frame_sign(frame_sign);
569 let mut corners = vec![(0.1, 0.1), (3.7, 2.9), (2.0, 0.0)];
572 for x in [0.7, 1.3, 2.9] {
573 corners.extend([(x, -1.0), (x + 0.3, 4.0)]);
574 }
575 let at = flats(&mut points, &corners);
576 let crossing: Vec<PointId> = (0..3)
577 .map(|k| {
578 points
579 .segment_crossing([at[0], at[1], at[3 + 2 * k], at[4 + 2 * k]])
580 .expect("crossing")
581 })
582 .collect();
583 assert!(crossing.iter().all(|&p| points.flat(p).is_none()));
584 assert_eq!(
585 points
586 .orient(crossing[0], crossing[1], crossing[2])
587 .expect("orient"),
588 Sign::Zero
589 );
590 let side = points
593 .orient(crossing[0], crossing[1], at[2])
594 .expect("orient");
595 assert_eq!(side, sign_times(Sign::Negative, turn));
596 }
597 }
598
599 #[test]
600 fn a_mirrored_frame_reverses_the_orientation() {
601 let corners = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)];
602 let mut counter = ExactPoints::new();
603 let mut mirrored = ExactPoints::with_frame_sign(Sign::Negative);
604 let (a, b) = (
605 flats(&mut counter, &corners),
606 flats(&mut mirrored, &corners),
607 );
608 assert_eq!(counter.orient(a[0], a[1], a[2]).unwrap(), Sign::Positive);
609 assert_eq!(mirrored.orient(b[0], b[1], b[2]).unwrap(), Sign::Negative);
610 }
611
612 #[test]
613 fn the_parameter_of_a_segment_crossing_is_its_position_along_the_first_segment() {
614 let mut points = ExactPoints::new();
615 let at = flats(
616 &mut points,
617 &[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0), (0.0, 1.0)],
618 );
619 let t = points
620 .segment_parameter([at[0], at[1], at[2], at[3]])
621 .expect("parameter");
622 assert!((t - 2.0 / 3.0).abs() < 1e-15, "{t}");
623 let back = points
624 .segment_parameter([at[2], at[3], at[0], at[1]])
625 .expect("parameter");
626 assert!((back - 2.0 / 3.0).abs() < 1e-15, "{back}");
627 assert!(points
628 .segment_parameter([at[0], at[1], at[0], at[1]])
629 .is_err());
630 }
631
632 #[test]
633 fn parallel_segments_have_no_crossing() {
634 let mut points = ExactPoints::new();
635 let at = flats(
636 &mut points,
637 &[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (1.0, 1.0)],
638 );
639 assert!(points
640 .segment_crossing([at[0], at[1], at[2], at[3]])
641 .is_err());
642 }
643
644 #[test]
645 fn a_homogeneous_point_with_a_binary64_position_takes_the_fast_path() {
646 let mut points = ExactPoints::new();
647 let three = Dyadic::try_from_f64(3.0).unwrap();
648 let (x, y) = (
649 Dyadic::try_from_f64(1.5).unwrap(),
650 Dyadic::try_from_f64(0.75).unwrap(),
651 );
652 let half = points
654 .push([
655 x.mul(&Dyadic::try_from_f64(1.0).unwrap()),
656 y.mul(&Dyadic::try_from_f64(1.0).unwrap()),
657 three.clone(),
658 ])
659 .expect("point");
660 let negated = points.push([x.neg(), y.neg(), three.neg()]).expect("point");
661 assert_eq!(points.flat(half), Some(Point2::new(0.5, 0.25)));
662 assert!(points.same(half, negated).expect("same"));
663 assert!(points.push([x, y, Dyadic::zero()]).is_err());
664 }
665
666 #[test]
667 fn a_crossing_rounds_once_to_the_nearest_binary64() {
668 let mut points = ExactPoints::new();
669 let at = flats(
670 &mut points,
671 &[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0), (0.0, 1.0)],
672 );
673 let crossing = points
674 .segment_crossing([at[0], at[1], at[2], at[3]])
675 .expect("crossing");
676 let rounded = points.round(crossing).expect("rounded");
677 assert_eq!(rounded.x.to_bits(), (1.0f64 / 3.0).to_bits());
678 assert_eq!(rounded.y.to_bits(), (2.0f64 / 3.0).to_bits());
679 assert_eq!(points.round(at[1]).unwrap(), Point2::new(0.5, 1.0));
680 }
681
682 #[test]
683 fn locate_tells_inside_outside_and_boundary() {
684 let mut points = ExactPoints::new();
685 let at = flats(
686 &mut points,
687 &[
688 (0.0, 0.0),
689 (2.0, 0.0),
690 (2.0, 2.0),
691 (0.0, 2.0),
692 (1.0, 1.0),
693 (3.0, 1.0),
694 (1.0, 0.0),
695 ],
696 );
697 let ring = &at[..4];
698 assert_eq!(points.locate(at[4], ring).unwrap(), Location::Inside);
699 assert_eq!(points.locate(at[5], ring).unwrap(), Location::Outside);
700 assert_eq!(points.locate(at[6], ring).unwrap(), Location::Boundary);
701 assert!(points
702 .ring_box(ring)
703 .possibly_within(&points.ring_box(ring)));
704 }
705
706 #[test]
707 fn escalations_count_the_calls_the_interval_filter_could_not_decide() {
708 let mut points = ExactPoints::new();
709 let at = flats(
710 &mut points,
711 &[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0), (0.0, 1.0)],
712 );
713 let crossing = points
714 .segment_crossing([at[0], at[1], at[2], at[3]])
715 .expect("crossing");
716 assert_eq!(points.escalations(), 0);
717 assert_eq!(
720 points.orient(at[0], crossing, at[1]).expect("orient"),
721 Sign::Zero
722 );
723 assert!(points.escalations() >= 1);
724 }
725}