1#![deny(clippy::cast_precision_loss)]
3#![deny(clippy::lossy_float_literal)]
4#![doc(test(no_crate_inject))]
5#![doc(html_playground_url = "https://rgeometry.org/playground.html")]
6#![doc(test(no_crate_inject))]
7use num_traits::*;
52use std::cmp::Ordering;
53use std::iter::Sum;
54use std::ops::*;
55
56pub mod algorithms;
57pub mod data;
58mod intersection;
59mod matrix;
60mod orientation;
61mod transformation;
62mod utils;
63
64pub use orientation::{Orientation, SoS};
65
66pub use intersection::Intersects;
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum Error {
70 InsufficientVertices,
71 SelfIntersections,
72 DuplicatePoints,
73 ConvexViolation,
75 ClockWiseViolation,
76 CoLinearViolation,
77}
78
79impl std::fmt::Display for Error {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
81 match self {
82 Error::InsufficientVertices => write!(f, "Insufficient vertices"),
83 Error::SelfIntersections => write!(f, "Self intersections"),
84 Error::DuplicatePoints => write!(f, "Duplicate points"),
85 Error::ConvexViolation => write!(f, "Convex violation"),
86 Error::ClockWiseViolation => write!(f, "Clockwise violation"),
87 Error::CoLinearViolation => write!(
88 f,
89 "Two or more points are colinear and no valid solution exists"
90 ),
91 }
92 }
93}
94
95pub trait TotalOrd {
96 fn total_cmp(&self, other: &Self) -> Ordering;
97
98 fn total_min(self, other: Self) -> Self
99 where
100 Self: Sized,
101 {
102 std::cmp::min_by(self, other, TotalOrd::total_cmp)
103 }
104
105 fn total_max(self, other: Self) -> Self
106 where
107 Self: Sized,
108 {
109 std::cmp::max_by(self, other, TotalOrd::total_cmp)
110 }
111}
112
113impl TotalOrd for u32 {
114 fn total_cmp(&self, other: &Self) -> Ordering {
115 self.cmp(other)
116 }
117}
118
119impl<A: TotalOrd> TotalOrd for &A {
120 fn total_cmp(&self, other: &Self) -> Ordering {
121 (*self).total_cmp(*other)
122 }
123}
124
125impl<A: TotalOrd, B: TotalOrd> TotalOrd for (A, B) {
126 fn total_cmp(&self, other: &Self) -> Ordering {
127 self
128 .0
129 .total_cmp(&other.0)
130 .then_with(|| self.1.total_cmp(&other.1))
131 }
132}
133
134pub trait PolygonScalar:
136 std::fmt::Debug
137 + Neg<Output = Self>
138 + NumAssignOps
139 + NumOps<Self, Self>
140 + TotalOrd
141 + PartialOrd
142 + Sum
143 + Clone
144{
145 fn from_constant(val: i8) -> Self;
146 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
147 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
148 fn cmp_vector_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
149 fn cmp_perp_vector_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
150
151 fn cmp_edge_normal_slope(
159 edge_start: &[Self; 2],
160 edge_end: &[Self; 2],
161 p: &[Self; 2],
162 q: &[Self; 2],
163 ) -> std::cmp::Ordering;
164
165 fn cmp_perp_edge_normal_slope(
170 edge_start: &[Self; 2],
171 edge_end: &[Self; 2],
172 p: &[Self; 2],
173 q: &[Self; 2],
174 ) -> std::cmp::Ordering;
175
176 fn cmp_edge_normals_cross(
185 ref_edge_start: &[Self; 2],
186 ref_edge_end: &[Self; 2],
187 edge_start: &[Self; 2],
188 edge_end: &[Self; 2],
189 ) -> std::cmp::Ordering;
190
191 fn cmp_edge_normals_dot(
200 ref_edge_start: &[Self; 2],
201 ref_edge_end: &[Self; 2],
202 edge_start: &[Self; 2],
203 edge_end: &[Self; 2],
204 ) -> std::cmp::Ordering;
205
206 fn cmp_edge_normal_cross_direction(
211 edge_start: &[Self; 2],
212 edge_end: &[Self; 2],
213 direction: &[Self; 2],
214 ) -> std::cmp::Ordering;
215
216 fn approx_intersection_point(
217 p1: [Self; 2],
218 p2: [Self; 2],
219 q1: [Self; 2],
220 q2: [Self; 2],
221 ) -> Option<[Self; 2]> {
222 approx_intersection_point_basic(Self::from_constant(0), p1, p2, q1, q2)
223 }
224 fn incircle(a: &[Self; 2], b: &[Self; 2], c: &[Self; 2], d: &[Self; 2]) -> Ordering;
225}
226
227fn approx_intersection_point_basic<T: Clone + NumOps<T, T> + PartialEq + std::fmt::Debug>(
228 zero: T,
229 p1: [T; 2],
230 p2: [T; 2],
231 q1: [T; 2],
232 q2: [T; 2],
233) -> Option<[T; 2]> {
234 let [x1, y1] = p1;
235 let [x2, y2] = p2;
236 let [x3, y3] = q1;
237 let [x4, y4] = q2;
238 let denom: T = (x1.clone() - x2.clone()) * (y3.clone() - y4.clone())
239 - (y1.clone() - y2.clone()) * (x3.clone() - x4.clone());
240 if denom == zero {
241 return None;
242 }
243 let part_a = x1.clone() * y2.clone() - y1.clone() * x2.clone();
244 let part_b = x3.clone() * y4.clone() - y3.clone() * x4.clone();
245 let x_num =
246 part_a.clone() * (x3.clone() - x4.clone()) - (x1.clone() - x2.clone()) * part_b.clone();
247 let y_num = part_a * (y3.clone() - y4.clone()) - (y1.clone() - y2.clone()) * part_b;
248 Some([x_num / denom.clone(), y_num / denom])
249}
250
251fn incircle_exact<T>(a: &[T; 2], b: &[T; 2], c: &[T; 2], d: &[T; 2]) -> Ordering
252where
253 T: Clone + NumOps<T, T> + Zero + Ord,
254{
255 let ax = a[0].clone() - d[0].clone();
256 let ay = a[1].clone() - d[1].clone();
257 let bx = b[0].clone() - d[0].clone();
258 let by = b[1].clone() - d[1].clone();
259 let cx = c[0].clone() - d[0].clone();
260 let cy = c[1].clone() - d[1].clone();
261
262 let a_len = ax.clone() * ax.clone() + ay.clone() * ay.clone();
263 let b_len = bx.clone() * bx.clone() + by.clone() * by.clone();
264 let c_len = cx.clone() * cx.clone() + cy.clone() * cy.clone();
265
266 let det = det3_generic(&ax, &ay, &a_len, &bx, &by, &b_len, &cx, &cy, &c_len);
267 det.cmp(&T::zero())
268}
269
270#[allow(clippy::too_many_arguments)]
271fn det3_generic<T>(a1: &T, a2: &T, a3: &T, b1: &T, b2: &T, b3: &T, c1: &T, c2: &T, c3: &T) -> T
272where
273 T: Clone + NumOps<T, T>,
274{
275 let m1 = b2.clone() * c3.clone() - b3.clone() * c2.clone();
276 let m2 = b1.clone() * c3.clone() - b3.clone() * c1.clone();
277 let m3 = b1.clone() * c2.clone() - b2.clone() * c1.clone();
278 a1.clone() * m1 - a2.clone() * m2 + a3.clone() * m3
279}
280
281macro_rules! fixed_precision {
282 ( $ty:ty, $uty:ty, $long:ty, $ulong: ty, $apfp_mod:path ) => {
283 impl TotalOrd for $ty {
284 fn total_cmp(&self, other: &Self) -> Ordering {
285 self.cmp(other)
286 }
287 }
288
289 impl PolygonScalar for $ty {
290 fn from_constant(val: i8) -> Self {
291 val as $ty
292 }
293 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
294 use $apfp_mod as apfp_mod;
295 apfp_mod::cmp_dist(
296 &apfp_mod::Coord::new(p[0], p[1]),
297 &apfp_mod::Coord::new(q[0], q[1]),
298 &apfp_mod::Coord::new(r[0], r[1]),
299 )
300 }
301
302 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
303 use apfp::geometry::Orientation;
304 use $apfp_mod as apfp_mod;
305 let orient = apfp_mod::orient2d(
306 &apfp_mod::Coord::new(p[0], p[1]),
307 &apfp_mod::Coord::new(q[0], q[1]),
308 &apfp_mod::Coord::new(r[0], r[1]),
309 );
310 match orient {
311 Orientation::CounterClockwise => Ordering::Greater,
312 Orientation::Clockwise => Ordering::Less,
313 Orientation::CoLinear => Ordering::Equal,
314 }
315 }
316
317 fn cmp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
318 use apfp::geometry::Orientation;
319 use $apfp_mod as apfp_mod;
320 let orient = apfp_mod::orient2d_vec(
321 &apfp_mod::Coord::new(p[0], p[1]),
322 &apfp_mod::Coord::new(vector[0], vector[1]),
323 &apfp_mod::Coord::new(q[0], q[1]),
324 );
325 match orient {
326 Orientation::CounterClockwise => Ordering::Greater,
327 Orientation::Clockwise => Ordering::Less,
328 Orientation::CoLinear => Ordering::Equal,
329 }
330 }
331
332 fn cmp_perp_vector_slope(
333 vector: &[Self; 2],
334 p: &[Self; 2],
335 q: &[Self; 2],
336 ) -> std::cmp::Ordering {
337 use apfp::geometry::Orientation;
338 use $apfp_mod as apfp_mod;
339 let orient = apfp_mod::orient2d_normal(
340 &apfp_mod::Coord::new(p[0], p[1]),
341 &apfp_mod::Coord::new(vector[0], vector[1]),
342 &apfp_mod::Coord::new(q[0], q[1]),
343 );
344 match orient {
345 Orientation::CounterClockwise => Ordering::Greater,
346 Orientation::Clockwise => Ordering::Less,
347 Orientation::CoLinear => Ordering::Equal,
348 }
349 }
350
351 fn cmp_edge_normal_slope(
352 edge_start: &[Self; 2],
353 edge_end: &[Self; 2],
354 p: &[Self; 2],
355 q: &[Self; 2],
356 ) -> std::cmp::Ordering {
357 let e0 = edge_start[0];
363 let e1 = edge_start[1];
364 let f0 = edge_end[0];
365 let f1 = edge_end[1];
366 let p0 = p[0];
367 let p1 = p[1];
368 let q0 = q[0];
369 let q1 = q[1];
370 let sign = apfp::int_signum!((p0 - q0) * (e0 - f0) - (p1 - q1) * (f1 - e1));
371 match sign {
372 1 => Ordering::Greater,
373 -1 => Ordering::Less,
374 0 => Ordering::Equal,
375 _ => unreachable!(),
376 }
377 }
378
379 fn cmp_perp_edge_normal_slope(
380 edge_start: &[Self; 2],
381 edge_end: &[Self; 2],
382 p: &[Self; 2],
383 q: &[Self; 2],
384 ) -> std::cmp::Ordering {
385 let e0 = edge_start[0];
391 let e1 = edge_start[1];
392 let f0 = edge_end[0];
393 let f1 = edge_end[1];
394 let p0 = p[0];
395 let p1 = p[1];
396 let q0 = q[0];
397 let q1 = q[1];
398 let sign = apfp::int_signum!((p0 - q0) * (f1 - e1) + (p1 - q1) * (e0 - f0));
399 match sign {
400 1 => Ordering::Greater,
401 -1 => Ordering::Less,
402 0 => Ordering::Equal,
403 _ => unreachable!(),
404 }
405 }
406
407 fn cmp_edge_normals_cross(
408 ref_edge_start: &[Self; 2],
409 ref_edge_end: &[Self; 2],
410 edge_start: &[Self; 2],
411 edge_end: &[Self; 2],
412 ) -> std::cmp::Ordering {
413 let re0 = ref_edge_start[0];
419 let re1 = ref_edge_start[1];
420 let rf0 = ref_edge_end[0];
421 let rf1 = ref_edge_end[1];
422 let e0 = edge_start[0];
423 let e1 = edge_start[1];
424 let f0 = edge_end[0];
425 let f1 = edge_end[1];
426 let sign = apfp::int_signum!((rf1 - re1) * (e0 - f0) - (re0 - rf0) * (f1 - e1));
427 match sign {
428 1 => Ordering::Greater,
429 -1 => Ordering::Less,
430 0 => Ordering::Equal,
431 _ => unreachable!(),
432 }
433 }
434
435 fn cmp_edge_normals_dot(
436 ref_edge_start: &[Self; 2],
437 ref_edge_end: &[Self; 2],
438 edge_start: &[Self; 2],
439 edge_end: &[Self; 2],
440 ) -> std::cmp::Ordering {
441 let re0 = ref_edge_start[0];
447 let re1 = ref_edge_start[1];
448 let rf0 = ref_edge_end[0];
449 let rf1 = ref_edge_end[1];
450 let e0 = edge_start[0];
451 let e1 = edge_start[1];
452 let f0 = edge_end[0];
453 let f1 = edge_end[1];
454 let sign = apfp::int_signum!((rf0 - re0) * (f0 - e0) + (rf1 - re1) * (f1 - e1));
455 match sign {
456 1 => Ordering::Greater,
457 -1 => Ordering::Less,
458 0 => Ordering::Equal,
459 _ => unreachable!(),
460 }
461 }
462
463 fn cmp_edge_normal_cross_direction(
464 edge_start: &[Self; 2],
465 edge_end: &[Self; 2],
466 direction: &[Self; 2],
467 ) -> std::cmp::Ordering {
468 let e0 = edge_start[0];
473 let e1 = edge_start[1];
474 let f0 = edge_end[0];
475 let f1 = edge_end[1];
476 let d0 = direction[0];
477 let d1 = direction[1];
478 let sign = apfp::int_signum!((f1 - e1) * d1 + (f0 - e0) * d0);
479 match sign {
480 1 => Ordering::Greater,
481 -1 => Ordering::Less,
482 0 => Ordering::Equal,
483 _ => unreachable!(),
484 }
485 }
486 fn approx_intersection_point(
487 p1: [Self; 2],
488 p2: [Self; 2],
489 q1: [Self; 2],
490 q2: [Self; 2],
491 ) -> Option<[Self; 2]> {
492 let long_p1: [$long; 2] = [p1[0] as $long, p1[1] as $long];
493 let long_p2: [$long; 2] = [p2[0] as $long, p2[1] as $long];
494 let long_q1: [$long; 2] = [q1[0] as $long, q1[1] as $long];
495 let long_q2: [$long; 2] = [q2[0] as $long, q2[1] as $long];
496 let long_result = approx_intersection_point_basic(0, long_p1, long_p2, long_q1, long_q2);
497 long_result.and_then(|[x, y]| Some([Self::try_from(x).ok()?, Self::try_from(y).ok()?]))
498 }
499 fn incircle(a: &[Self; 2], b: &[Self; 2], c: &[Self; 2], d: &[Self; 2]) -> Ordering {
500 use apfp::geometry::Orientation;
501 use $apfp_mod as apfp_mod;
502 let result = apfp_mod::incircle(
503 &apfp_mod::Coord::new(a[0], a[1]),
504 &apfp_mod::Coord::new(b[0], b[1]),
505 &apfp_mod::Coord::new(c[0], c[1]),
506 &apfp_mod::Coord::new(d[0], d[1]),
507 );
508 match result {
509 Orientation::CounterClockwise => Ordering::Greater,
510 Orientation::Clockwise => Ordering::Less,
511 Orientation::CoLinear => Ordering::Equal,
512 }
513 }
514 }
515 };
516}
517
518macro_rules! arbitrary_precision {
519 ( $( $ty:ty ),* ) => {
520 $(
521 impl TotalOrd for $ty {
522 fn total_cmp(&self, other: &Self) -> Ordering {
523 self.cmp(other)
524 }
525 }
526
527 impl PolygonScalar for $ty {
528 fn from_constant(val: i8) -> Self {
529 <$ty>::from_i8(val).unwrap()
530 }
531 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
532 let pq_x = &p[0] - &q[0];
533 let pq_y = &p[1] - &q[1];
534 let pq_dist_squared: Self = &pq_x*&pq_x + &pq_y*&pq_y;
535 let pr_x = &p[0] - &r[0];
536 let pr_y = &p[1] - &r[1];
537 let pr_dist_squared: Self = &pr_x*&pr_x + &pr_y*&pr_y;
538 pq_dist_squared.cmp(&pr_dist_squared)
539 }
540
541 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
542 let slope1 = (&r[1] - &q[1]) * (&q[0] - &p[0]);
543 let slope2 = (&q[1] - &p[1]) * (&r[0] - &q[0]);
544 slope1.cmp(&slope2)
545 }
546 fn cmp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
547 PolygonScalar::cmp_slope(
548 p,
549 &[&p[0] + &vector[0], &p[1] + &vector[1]],
550 q
551 )
552 }
553 fn cmp_perp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
554 PolygonScalar::cmp_slope(
555 p,
556 &[&p[0] - &vector[1], &p[1] + &vector[0]],
557 q
558 )
559 }
560 fn cmp_edge_normal_slope(
561 edge_start: &[Self; 2],
562 edge_end: &[Self; 2],
563 p: &[Self; 2],
564 q: &[Self; 2],
565 ) -> std::cmp::Ordering {
566 let edge_normal = [
568 &edge_end[1] - &edge_start[1],
569 &edge_start[0] - &edge_end[0],
570 ];
571 PolygonScalar::cmp_vector_slope(&edge_normal, p, q)
572 }
573 fn cmp_perp_edge_normal_slope(
574 edge_start: &[Self; 2],
575 edge_end: &[Self; 2],
576 p: &[Self; 2],
577 q: &[Self; 2],
578 ) -> std::cmp::Ordering {
579 let edge_normal = [
581 &edge_end[1] - &edge_start[1],
582 &edge_start[0] - &edge_end[0],
583 ];
584 PolygonScalar::cmp_perp_vector_slope(&edge_normal, p, q)
585 }
586 fn cmp_edge_normals_cross(
587 ref_edge_start: &[Self; 2],
588 ref_edge_end: &[Self; 2],
589 edge_start: &[Self; 2],
590 edge_end: &[Self; 2],
591 ) -> std::cmp::Ordering {
592 let ref_nx = &ref_edge_end[1] - &ref_edge_start[1];
594 let ref_ny = &ref_edge_start[0] - &ref_edge_end[0];
595 let edge_nx = &edge_end[1] - &edge_start[1];
596 let edge_ny = &edge_start[0] - &edge_end[0];
597 let cross = &ref_nx * &edge_ny - &ref_ny * &edge_nx;
598 let zero = Self::from_constant(0);
599 cross.cmp(&zero)
600 }
601 fn cmp_edge_normals_dot(
602 ref_edge_start: &[Self; 2],
603 ref_edge_end: &[Self; 2],
604 edge_start: &[Self; 2],
605 edge_end: &[Self; 2],
606 ) -> std::cmp::Ordering {
607 let dot = (&ref_edge_end[0] - &ref_edge_start[0]) * (&edge_end[0] - &edge_start[0])
609 + (&ref_edge_end[1] - &ref_edge_start[1]) * (&edge_end[1] - &edge_start[1]);
610 let zero = Self::from_constant(0);
611 dot.cmp(&zero)
612 }
613 fn cmp_edge_normal_cross_direction(
614 edge_start: &[Self; 2],
615 edge_end: &[Self; 2],
616 direction: &[Self; 2],
617 ) -> std::cmp::Ordering {
618 let dot = (&edge_end[0] - &edge_start[0]) * &direction[0]
620 + (&edge_end[1] - &edge_start[1]) * &direction[1];
621 let zero = Self::from_constant(0);
622 dot.cmp(&zero)
623 }
624 fn incircle(
625 a: &[Self; 2],
626 b: &[Self; 2],
627 c: &[Self; 2],
628 d: &[Self; 2],
629 ) -> Ordering {
630 incircle_exact(a, b, c, d)
631 }
632 })*
633 };
634}
635
636macro_rules! floating_precision {
637 ( $( $ty:ty ),* ) => {
638 $(
639 impl TotalOrd for $ty {
640 fn total_cmp(&self, other: &Self) -> Ordering {
641 if *self == 0.0 && *other == 0.0 {
646 Ordering::Equal
647 } else {
648 <$ty>::total_cmp(self, other)
649 }
650 }
651 }
652
653 impl PolygonScalar for $ty {
654 fn from_constant(val: i8) -> Self {
655 <$ty>::from_i8(val).unwrap()
656 }
657 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
660 PolygonScalar::cmp_dist(
661 &[float_to_rational(p[0]), float_to_rational(p[1])],
662 &[float_to_rational(q[0]), float_to_rational(q[1])],
663 &[float_to_rational(r[0]), float_to_rational(r[1])],
664 )
665 }
666
667 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
670 use apfp::geometry::{Orientation, f64::Coord};
671 let orient = apfp::geometry::f64::orient2d(
672 &Coord::new(p[0] as f64, p[1] as f64),
673 &Coord::new(q[0] as f64, q[1] as f64),
674 &Coord::new(r[0] as f64, r[1] as f64),
675 );
676 match orient {
677 Orientation::CounterClockwise => Ordering::Greater,
678 Orientation::Clockwise => Ordering::Less,
679 Orientation::CoLinear => Ordering::Equal,
680 }
681 }
682 fn cmp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
684 use apfp::geometry::{Orientation, f64::Coord};
685 let orient = apfp::geometry::f64::orient2d_vec(
686 &Coord::new(p[0] as f64, p[1] as f64),
687 &Coord::new(vector[0] as f64, vector[1] as f64),
688 &Coord::new(q[0] as f64, q[1] as f64),
689 );
690 match orient {
691 Orientation::CounterClockwise => Ordering::Greater,
692 Orientation::Clockwise => Ordering::Less,
693 Orientation::CoLinear => Ordering::Equal,
694 }
695 }
696 fn cmp_perp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
698 use apfp::geometry::{Orientation, f64::Coord};
699 let orient = apfp::geometry::f64::orient2d_normal(
700 &Coord::new(p[0] as f64, p[1] as f64),
701 &Coord::new(vector[0] as f64, vector[1] as f64),
702 &Coord::new(q[0] as f64, q[1] as f64),
703 );
704 match orient {
705 Orientation::CounterClockwise => Ordering::Greater,
706 Orientation::Clockwise => Ordering::Less,
707 Orientation::CoLinear => Ordering::Equal,
708 }
709 }
710 fn cmp_edge_normal_slope(
711 edge_start: &[Self; 2],
712 edge_end: &[Self; 2],
713 p: &[Self; 2],
714 q: &[Self; 2],
715 ) -> std::cmp::Ordering {
716 let px = p[0] as f64;
724 let py = p[1] as f64;
725 let qx = q[0] as f64;
726 let qy = q[1] as f64;
727 let es0 = edge_start[0] as f64;
728 let es1 = edge_start[1] as f64;
729 let ee0 = edge_end[0] as f64;
730 let ee1 = edge_end[1] as f64;
731 match apfp::apfp_signum!(
735 (px - qx) * (es0 - ee0) - (py - qy) * (ee1 - es1)
736 ) {
737 1 => Ordering::Greater,
738 -1 => Ordering::Less,
739 _ => Ordering::Equal,
740 }
741 }
742 fn cmp_perp_edge_normal_slope(
743 edge_start: &[Self; 2],
744 edge_end: &[Self; 2],
745 p: &[Self; 2],
746 q: &[Self; 2],
747 ) -> std::cmp::Ordering {
748 let px = p[0] as f64;
755 let py = p[1] as f64;
756 let qx = q[0] as f64;
757 let qy = q[1] as f64;
758 let es0 = edge_start[0] as f64;
759 let es1 = edge_start[1] as f64;
760 let ee0 = edge_end[0] as f64;
761 let ee1 = edge_end[1] as f64;
762 match apfp::apfp_signum!(
764 (px - qx) * (ee1 - es1) + (py - qy) * (es0 - ee0)
765 ) {
766 1 => Ordering::Greater,
767 -1 => Ordering::Less,
768 _ => Ordering::Equal,
769 }
770 }
771 fn cmp_edge_normals_cross(
772 ref_edge_start: &[Self; 2],
773 ref_edge_end: &[Self; 2],
774 edge_start: &[Self; 2],
775 edge_end: &[Self; 2],
776 ) -> std::cmp::Ordering {
777 let ref_sx = ref_edge_start[0] as f64;
780 let ref_sy = ref_edge_start[1] as f64;
781 let ref_ex = ref_edge_end[0] as f64;
782 let ref_ey = ref_edge_end[1] as f64;
783 let edge_sx = edge_start[0] as f64;
784 let edge_sy = edge_start[1] as f64;
785 let edge_ex = edge_end[0] as f64;
786 let edge_ey = edge_end[1] as f64;
787 match apfp::apfp_signum!(
788 (ref_ey - ref_sy) * (edge_sx - edge_ex) - (ref_sx - ref_ex) * (edge_ey - edge_sy)
789 ) {
790 1 => Ordering::Greater,
791 -1 => Ordering::Less,
792 _ => Ordering::Equal,
793 }
794 }
795 fn cmp_edge_normals_dot(
796 ref_edge_start: &[Self; 2],
797 ref_edge_end: &[Self; 2],
798 edge_start: &[Self; 2],
799 edge_end: &[Self; 2],
800 ) -> std::cmp::Ordering {
801 let ref_sx = ref_edge_start[0] as f64;
804 let ref_sy = ref_edge_start[1] as f64;
805 let ref_ex = ref_edge_end[0] as f64;
806 let ref_ey = ref_edge_end[1] as f64;
807 let edge_sx = edge_start[0] as f64;
808 let edge_sy = edge_start[1] as f64;
809 let edge_ex = edge_end[0] as f64;
810 let edge_ey = edge_end[1] as f64;
811 match apfp::apfp_signum!(
812 (ref_ex - ref_sx) * (edge_ex - edge_sx) + (ref_ey - ref_sy) * (edge_ey - edge_sy)
813 ) {
814 1 => Ordering::Greater,
815 -1 => Ordering::Less,
816 _ => Ordering::Equal,
817 }
818 }
819 fn cmp_edge_normal_cross_direction(
820 edge_start: &[Self; 2],
821 edge_end: &[Self; 2],
822 direction: &[Self; 2],
823 ) -> std::cmp::Ordering {
824 let edge_sx = edge_start[0] as f64;
827 let edge_sy = edge_start[1] as f64;
828 let edge_ex = edge_end[0] as f64;
829 let edge_ey = edge_end[1] as f64;
830 let dir_x = direction[0] as f64;
831 let dir_y = direction[1] as f64;
832 match apfp::apfp_signum!(
833 (edge_ex - edge_sx) * dir_x + (edge_ey - edge_sy) * dir_y
834 ) {
835 1 => Ordering::Greater,
836 -1 => Ordering::Less,
837 _ => Ordering::Equal,
838 }
839 }
840 fn incircle(
841 a: &[Self; 2],
842 b: &[Self; 2],
843 c: &[Self; 2],
844 d: &[Self; 2],
845 ) -> Ordering {
846 use apfp::geometry::{Orientation, f64::Coord};
847 let result = apfp::geometry::f64::incircle(
848 &Coord::new(a[0] as f64, a[1] as f64),
849 &Coord::new(b[0] as f64, b[1] as f64),
850 &Coord::new(c[0] as f64, c[1] as f64),
851 &Coord::new(d[0] as f64, d[1] as f64),
852 );
853 match result {
854 Orientation::CounterClockwise => Ordering::Greater,
855 Orientation::Clockwise => Ordering::Less,
856 Orientation::CoLinear => Ordering::Equal,
857 }
858 }
859 })*
860 };
861}
862
863fixed_precision!(i8, u8, i16, u16, apfp::geometry::i8);
864fixed_precision!(i16, u16, i32, u32, apfp::geometry::i16);
865fixed_precision!(i32, u32, i64, u64, apfp::geometry::i32);
866fixed_precision!(i64, u64, i128, u128, apfp::geometry::i64);
867
868impl TotalOrd for isize {
871 fn total_cmp(&self, other: &Self) -> Ordering {
872 self.cmp(other)
873 }
874}
875
876impl PolygonScalar for isize {
877 fn from_constant(val: i8) -> Self {
878 val as isize
879 }
880 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
881 use apfp::geometry::i64 as apfp_mod;
882 apfp_mod::cmp_dist(
883 &apfp_mod::Coord::new(p[0] as i64, p[1] as i64),
884 &apfp_mod::Coord::new(q[0] as i64, q[1] as i64),
885 &apfp_mod::Coord::new(r[0] as i64, r[1] as i64),
886 )
887 }
888
889 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
890 use apfp::geometry::{Orientation, i64 as apfp_mod};
891 let orient = apfp_mod::orient2d(
892 &apfp_mod::Coord::new(p[0] as i64, p[1] as i64),
893 &apfp_mod::Coord::new(q[0] as i64, q[1] as i64),
894 &apfp_mod::Coord::new(r[0] as i64, r[1] as i64),
895 );
896 match orient {
897 Orientation::CounterClockwise => Ordering::Greater,
898 Orientation::Clockwise => Ordering::Less,
899 Orientation::CoLinear => Ordering::Equal,
900 }
901 }
902
903 fn cmp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
904 use apfp::geometry::{Orientation, i64 as apfp_mod};
905 let orient = apfp_mod::orient2d_vec(
906 &apfp_mod::Coord::new(p[0] as i64, p[1] as i64),
907 &apfp_mod::Coord::new(vector[0] as i64, vector[1] as i64),
908 &apfp_mod::Coord::new(q[0] as i64, q[1] as i64),
909 );
910 match orient {
911 Orientation::CounterClockwise => Ordering::Greater,
912 Orientation::Clockwise => Ordering::Less,
913 Orientation::CoLinear => Ordering::Equal,
914 }
915 }
916
917 fn cmp_perp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
918 use apfp::geometry::{Orientation, i64 as apfp_mod};
919 let orient = apfp_mod::orient2d_normal(
920 &apfp_mod::Coord::new(p[0] as i64, p[1] as i64),
921 &apfp_mod::Coord::new(vector[0] as i64, vector[1] as i64),
922 &apfp_mod::Coord::new(q[0] as i64, q[1] as i64),
923 );
924 match orient {
925 Orientation::CounterClockwise => Ordering::Greater,
926 Orientation::Clockwise => Ordering::Less,
927 Orientation::CoLinear => Ordering::Equal,
928 }
929 }
930
931 fn cmp_edge_normal_slope(
932 edge_start: &[Self; 2],
933 edge_end: &[Self; 2],
934 p: &[Self; 2],
935 q: &[Self; 2],
936 ) -> std::cmp::Ordering {
937 let (e0, e1) = (edge_start[0] as i64, edge_start[1] as i64);
939 let (f0, f1) = (edge_end[0] as i64, edge_end[1] as i64);
940 let (p0, p1) = (p[0] as i64, p[1] as i64);
941 let (q0, q1) = (q[0] as i64, q[1] as i64);
942 let sign = apfp::int_signum!((p0 - q0) * (e0 - f0) - (p1 - q1) * (f1 - e1));
943 match sign {
944 1 => Ordering::Greater,
945 -1 => Ordering::Less,
946 0 => Ordering::Equal,
947 _ => unreachable!(),
948 }
949 }
950
951 fn cmp_perp_edge_normal_slope(
952 edge_start: &[Self; 2],
953 edge_end: &[Self; 2],
954 p: &[Self; 2],
955 q: &[Self; 2],
956 ) -> std::cmp::Ordering {
957 let (e0, e1) = (edge_start[0] as i64, edge_start[1] as i64);
959 let (f0, f1) = (edge_end[0] as i64, edge_end[1] as i64);
960 let (p0, p1) = (p[0] as i64, p[1] as i64);
961 let (q0, q1) = (q[0] as i64, q[1] as i64);
962 let sign = apfp::int_signum!((p0 - q0) * (f1 - e1) + (p1 - q1) * (e0 - f0));
963 match sign {
964 1 => Ordering::Greater,
965 -1 => Ordering::Less,
966 0 => Ordering::Equal,
967 _ => unreachable!(),
968 }
969 }
970
971 fn cmp_edge_normals_cross(
972 ref_edge_start: &[Self; 2],
973 ref_edge_end: &[Self; 2],
974 edge_start: &[Self; 2],
975 edge_end: &[Self; 2],
976 ) -> std::cmp::Ordering {
977 let (re0, re1) = (ref_edge_start[0] as i64, ref_edge_start[1] as i64);
978 let (rf0, rf1) = (ref_edge_end[0] as i64, ref_edge_end[1] as i64);
979 let (e0, e1) = (edge_start[0] as i64, edge_start[1] as i64);
980 let (f0, f1) = (edge_end[0] as i64, edge_end[1] as i64);
981 let sign = apfp::int_signum!((rf1 - re1) * (e0 - f0) - (re0 - rf0) * (f1 - e1));
982 match sign {
983 1 => Ordering::Greater,
984 -1 => Ordering::Less,
985 0 => Ordering::Equal,
986 _ => unreachable!(),
987 }
988 }
989
990 fn cmp_edge_normals_dot(
991 ref_edge_start: &[Self; 2],
992 ref_edge_end: &[Self; 2],
993 edge_start: &[Self; 2],
994 edge_end: &[Self; 2],
995 ) -> std::cmp::Ordering {
996 let (re0, re1) = (ref_edge_start[0] as i64, ref_edge_start[1] as i64);
997 let (rf0, rf1) = (ref_edge_end[0] as i64, ref_edge_end[1] as i64);
998 let (e0, e1) = (edge_start[0] as i64, edge_start[1] as i64);
999 let (f0, f1) = (edge_end[0] as i64, edge_end[1] as i64);
1000 let sign = apfp::int_signum!((rf0 - re0) * (f0 - e0) + (rf1 - re1) * (f1 - e1));
1001 match sign {
1002 1 => Ordering::Greater,
1003 -1 => Ordering::Less,
1004 0 => Ordering::Equal,
1005 _ => unreachable!(),
1006 }
1007 }
1008
1009 fn cmp_edge_normal_cross_direction(
1010 edge_start: &[Self; 2],
1011 edge_end: &[Self; 2],
1012 direction: &[Self; 2],
1013 ) -> std::cmp::Ordering {
1014 let (e0, e1) = (edge_start[0] as i64, edge_start[1] as i64);
1015 let (f0, f1) = (edge_end[0] as i64, edge_end[1] as i64);
1016 let (d0, d1) = (direction[0] as i64, direction[1] as i64);
1017 let sign = apfp::int_signum!((f1 - e1) * d1 + (f0 - e0) * d0);
1018 match sign {
1019 1 => Ordering::Greater,
1020 -1 => Ordering::Less,
1021 0 => Ordering::Equal,
1022 _ => unreachable!(),
1023 }
1024 }
1025 fn approx_intersection_point(
1026 p1: [Self; 2],
1027 p2: [Self; 2],
1028 q1: [Self; 2],
1029 q2: [Self; 2],
1030 ) -> Option<[Self; 2]> {
1031 let long_p1: [i128; 2] = [p1[0] as i128, p1[1] as i128];
1032 let long_p2: [i128; 2] = [p2[0] as i128, p2[1] as i128];
1033 let long_q1: [i128; 2] = [q1[0] as i128, q1[1] as i128];
1034 let long_q2: [i128; 2] = [q2[0] as i128, q2[1] as i128];
1035 let long_result = approx_intersection_point_basic(0, long_p1, long_p2, long_q1, long_q2);
1036 long_result.and_then(|[x, y]| Some([Self::try_from(x).ok()?, Self::try_from(y).ok()?]))
1037 }
1038 fn incircle(a: &[Self; 2], b: &[Self; 2], c: &[Self; 2], d: &[Self; 2]) -> Ordering {
1039 use apfp::geometry::{Orientation, i64 as apfp_mod};
1040 let result = apfp_mod::incircle(
1041 &apfp_mod::Coord::new(a[0] as i64, a[1] as i64),
1042 &apfp_mod::Coord::new(b[0] as i64, b[1] as i64),
1043 &apfp_mod::Coord::new(c[0] as i64, c[1] as i64),
1044 &apfp_mod::Coord::new(d[0] as i64, d[1] as i64),
1045 );
1046 match result {
1047 Orientation::CounterClockwise => Ordering::Greater,
1048 Orientation::Clockwise => Ordering::Less,
1049 Orientation::CoLinear => Ordering::Equal,
1050 }
1051 }
1052}
1053arbitrary_precision!(num_bigint::BigInt);
1054arbitrary_precision!(num_rational::BigRational);
1055floating_precision!(f32);
1056floating_precision!(f64);
1057
1058#[cfg(feature = "rug")]
1059impl TotalOrd for rug::Integer {
1060 fn total_cmp(&self, other: &Self) -> Ordering {
1061 self.cmp(other)
1062 }
1063}
1064
1065#[cfg(feature = "rug")]
1066impl PolygonScalar for rug::Integer {
1067 fn from_constant(val: i8) -> Self {
1068 rug::Integer::from(val)
1069 }
1070 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
1071 let [qx, qy] = q.clone();
1072 let [px, py] = p.clone();
1073 let pq_x = px - qx;
1074 let pq_y = py - qy;
1075 let pq_dist_squared: Self = pq_x.square() + pq_y.square();
1076 let [rx, ry] = r.clone();
1077 let [px, py] = p.clone();
1078 let pr_x = px - rx;
1079 let pr_y = py - ry;
1080 let pr_dist_squared: Self = pr_x.square() + pr_y.square();
1081 pq_dist_squared.cmp(&pr_dist_squared)
1082 }
1083
1084 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
1085 let [qx, qy] = q.clone();
1086 let [rx, ry] = r.clone();
1087 let ry_qy = ry - &q[1];
1088 let qx_px = qx - &p[0];
1089 let slope1 = ry_qy * qx_px;
1090 let qy_py = qy - &p[1];
1091 let rx_qx = rx - &q[0];
1092 let slope2 = qy_py * rx_qx;
1093 slope1.cmp(&slope2)
1094 }
1095 fn cmp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
1096 let new_x = rug::Integer::from(&p[0] + &vector[0]);
1097 let new_y = rug::Integer::from(&p[1] + &vector[1]);
1098 PolygonScalar::cmp_slope(p, &[new_x, new_y], q)
1099 }
1100 fn cmp_perp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
1101 let new_x = rug::Integer::from(&p[0] - &vector[1]);
1104 let new_y = rug::Integer::from(&p[1] + &vector[0]);
1105 PolygonScalar::cmp_slope(p, &[new_x, new_y], q)
1106 }
1107 fn cmp_edge_normal_slope(
1108 edge_start: &[Self; 2],
1109 edge_end: &[Self; 2],
1110 p: &[Self; 2],
1111 q: &[Self; 2],
1112 ) -> std::cmp::Ordering {
1113 let edge_normal = [
1115 rug::Integer::from(&edge_end[1] - &edge_start[1]),
1116 rug::Integer::from(&edge_start[0] - &edge_end[0]),
1117 ];
1118 PolygonScalar::cmp_vector_slope(&edge_normal, p, q)
1119 }
1120 fn cmp_perp_edge_normal_slope(
1121 edge_start: &[Self; 2],
1122 edge_end: &[Self; 2],
1123 p: &[Self; 2],
1124 q: &[Self; 2],
1125 ) -> std::cmp::Ordering {
1126 let edge_normal = [
1128 rug::Integer::from(&edge_end[1] - &edge_start[1]),
1129 rug::Integer::from(&edge_start[0] - &edge_end[0]),
1130 ];
1131 PolygonScalar::cmp_perp_vector_slope(&edge_normal, p, q)
1132 }
1133 fn cmp_edge_normals_cross(
1134 ref_edge_start: &[Self; 2],
1135 ref_edge_end: &[Self; 2],
1136 edge_start: &[Self; 2],
1137 edge_end: &[Self; 2],
1138 ) -> std::cmp::Ordering {
1139 let ref_nx = rug::Integer::from(&ref_edge_end[1] - &ref_edge_start[1]);
1141 let ref_ny = rug::Integer::from(&ref_edge_start[0] - &ref_edge_end[0]);
1142 let edge_nx = rug::Integer::from(&edge_end[1] - &edge_start[1]);
1143 let edge_ny = rug::Integer::from(&edge_start[0] - &edge_end[0]);
1144 let cross = ref_nx * edge_ny - ref_ny * edge_nx;
1145 cross.cmp(&rug::Integer::new())
1146 }
1147 fn cmp_edge_normals_dot(
1148 ref_edge_start: &[Self; 2],
1149 ref_edge_end: &[Self; 2],
1150 edge_start: &[Self; 2],
1151 edge_end: &[Self; 2],
1152 ) -> std::cmp::Ordering {
1153 let dot = rug::Integer::from(&ref_edge_end[0] - &ref_edge_start[0])
1155 * rug::Integer::from(&edge_end[0] - &edge_start[0])
1156 + rug::Integer::from(&ref_edge_end[1] - &ref_edge_start[1])
1157 * rug::Integer::from(&edge_end[1] - &edge_start[1]);
1158 dot.cmp(&rug::Integer::new())
1159 }
1160 fn cmp_edge_normal_cross_direction(
1161 edge_start: &[Self; 2],
1162 edge_end: &[Self; 2],
1163 direction: &[Self; 2],
1164 ) -> std::cmp::Ordering {
1165 let dot = rug::Integer::from(&edge_end[0] - &edge_start[0]) * &direction[0]
1167 + rug::Integer::from(&edge_end[1] - &edge_start[1]) * &direction[1];
1168 dot.cmp(&rug::Integer::new())
1169 }
1170 fn incircle(a: &[Self; 2], b: &[Self; 2], c: &[Self; 2], d: &[Self; 2]) -> Ordering {
1171 let mut ax = a[0].clone();
1172 ax -= &d[0];
1173 let mut ay = a[1].clone();
1174 ay -= &d[1];
1175 let mut bx = b[0].clone();
1176 bx -= &d[0];
1177 let mut by = b[1].clone();
1178 by -= &d[1];
1179 let mut cx = c[0].clone();
1180 cx -= &d[0];
1181 let mut cy = c[1].clone();
1182 cy -= &d[1];
1183
1184 let a_len = ax.clone() * ax.clone() + ay.clone() * ay.clone();
1185 let b_len = bx.clone() * bx.clone() + by.clone() * by.clone();
1186 let c_len = cx.clone() * cx.clone() + cy.clone() * cy.clone();
1187
1188 let det = det3_generic(&ax, &ay, &a_len, &bx, &by, &b_len, &cx, &cy, &c_len);
1189 det.cmp(&rug::Integer::new())
1190 }
1191}
1192
1193fn float_to_rational(f: impl num::traits::float::FloatCore) -> num::BigRational {
1194 num::BigRational::from_float(f).expect("cannot convert NaN or infinite to exact precision number")
1195}
1196
1197#[cfg(test)]
1198pub mod testing;
1199
1200#[cfg(test)]
1201mod floating_robustness_tests {
1202 use super::*;
1203
1204 fn to_big(f: f64) -> num_rational::BigRational {
1206 num_rational::BigRational::from_float(f).unwrap()
1207 }
1208
1209 #[test]
1225 fn cmp_edge_normals_cross_f64_robustness() {
1226 let big = 1e15f64;
1243
1244 let ref_start = [0.0f64, 0.0];
1245 let ref_end = [big, big + 1.0];
1246 let edge_start = [0.0f64, 0.0];
1247 let edge_end = [big + 2.0, big + 3.0];
1248
1249 let ref_start_big = [to_big(ref_start[0]), to_big(ref_start[1])];
1251 let ref_end_big = [to_big(ref_end[0]), to_big(ref_end[1])];
1252 let edge_start_big = [to_big(edge_start[0]), to_big(edge_start[1])];
1253 let edge_end_big = [to_big(edge_end[0]), to_big(edge_end[1])];
1254 let expected = num_rational::BigRational::cmp_edge_normals_cross(
1255 &ref_start_big,
1256 &ref_end_big,
1257 &edge_start_big,
1258 &edge_end_big,
1259 );
1260
1261 assert_eq!(
1263 expected,
1264 Ordering::Less,
1265 "BigRational should give Less (cross = -2)"
1266 );
1267
1268 let result = f64::cmp_edge_normals_cross(&ref_start, &ref_end, &edge_start, &edge_end);
1270 assert_eq!(
1271 result, expected,
1272 "f64 cmp_edge_normals_cross gave {:?} but BigRational gave {:?}. \
1273 This demonstrates precision loss: true cross is -2, but naive f64 gives 0.",
1274 result, expected
1275 );
1276 }
1277
1278 #[test]
1286 fn cmp_edge_normals_dot_f64_robustness() {
1287 let big = 1e15f64;
1288
1289 let ref_start = [0.0f64, 0.0];
1290 let ref_end = [big, 1.0];
1291 let edge_start = [0.0f64, 0.0];
1292 let edge_end = [-1.0, big + 0.5];
1293
1294 let ref_start_big = [to_big(ref_start[0]), to_big(ref_start[1])];
1295 let ref_end_big = [to_big(ref_end[0]), to_big(ref_end[1])];
1296 let edge_start_big = [to_big(edge_start[0]), to_big(edge_start[1])];
1297 let edge_end_big = [to_big(edge_end[0]), to_big(edge_end[1])];
1298 let expected = num_rational::BigRational::cmp_edge_normals_dot(
1299 &ref_start_big,
1300 &ref_end_big,
1301 &edge_start_big,
1302 &edge_end_big,
1303 );
1304
1305 assert_eq!(expected, Ordering::Greater);
1306
1307 let result = f64::cmp_edge_normals_dot(&ref_start, &ref_end, &edge_start, &edge_end);
1308 assert_eq!(result, expected);
1309 }
1310
1311 #[test]
1315 fn cmp_edge_normal_cross_direction_f64_robustness() {
1316 let big = 1e15f64;
1321
1322 let edge_start = [0.0f64, 0.0];
1323 let edge_end = [big, 1.0];
1324 let direction = [-1.0f64, big + 0.5];
1325
1326 let edge_start_big = [to_big(edge_start[0]), to_big(edge_start[1])];
1327 let edge_end_big = [to_big(edge_end[0]), to_big(edge_end[1])];
1328 let direction_big = [to_big(direction[0]), to_big(direction[1])];
1329 let expected = num_rational::BigRational::cmp_edge_normal_cross_direction(
1330 &edge_start_big,
1331 &edge_end_big,
1332 &direction_big,
1333 );
1334
1335 assert_eq!(
1336 expected,
1337 Ordering::Greater,
1338 "BigRational should give Greater (result = 0.5)"
1339 );
1340
1341 let result = f64::cmp_edge_normal_cross_direction(&edge_start, &edge_end, &direction);
1342 assert_eq!(
1343 result, expected,
1344 "f64 cmp_edge_normal_cross_direction gave {:?} but BigRational gave {:?}",
1345 result, expected
1346 );
1347 }
1348
1349 #[test]
1353 fn cmp_edge_normal_slope_f64_mismatch() {
1354 let edge_start = [0.1f64, 0.2];
1358 let edge_end = [0.5f64, 0.1];
1359 let p = [1125899906842624.0f64, 4503599627370496.0];
1360 let q = [0.0f64, 0.0];
1361
1362 let edge_start_big = [to_big(edge_start[0]), to_big(edge_start[1])];
1363 let edge_end_big = [to_big(edge_end[0]), to_big(edge_end[1])];
1364 let p_big = [to_big(p[0]), to_big(p[1])];
1365 let q_big = [to_big(q[0]), to_big(q[1])];
1366
1367 let expected = num_rational::BigRational::cmp_edge_normal_slope(
1368 &edge_start_big,
1369 &edge_end_big,
1370 &p_big,
1371 &q_big,
1372 );
1373 assert_eq!(
1374 expected,
1375 Ordering::Greater,
1376 "BigRational should give Greater for this configuration"
1377 );
1378
1379 let result = f64::cmp_edge_normal_slope(&edge_start, &edge_end, &p, &q);
1380 assert_eq!(
1381 result, expected,
1382 "f64 cmp_edge_normal_slope gave {:?} but BigRational gave {:?}",
1383 result, expected
1384 );
1385 }
1386
1387 #[test]
1391 fn cmp_perp_edge_normal_slope_f64_mismatch() {
1392 let edge_start = [0.1f64, 0.1];
1393 let edge_end = [0.2f64, 0.5];
1394 let p = [1125899906842624.0f64, 4503599627370496.0];
1395 let q = [0.0f64, 0.0];
1396
1397 let edge_start_big = [to_big(edge_start[0]), to_big(edge_start[1])];
1398 let edge_end_big = [to_big(edge_end[0]), to_big(edge_end[1])];
1399 let p_big = [to_big(p[0]), to_big(p[1])];
1400 let q_big = [to_big(q[0]), to_big(q[1])];
1401
1402 let expected = num_rational::BigRational::cmp_perp_edge_normal_slope(
1403 &edge_start_big,
1404 &edge_end_big,
1405 &p_big,
1406 &q_big,
1407 );
1408 assert_eq!(
1409 expected,
1410 Ordering::Less,
1411 "BigRational should give Less for this configuration"
1412 );
1413
1414 let result = f64::cmp_perp_edge_normal_slope(&edge_start, &edge_end, &p, &q);
1415 assert_eq!(
1416 result, expected,
1417 "f64 cmp_perp_edge_normal_slope gave {:?} but BigRational gave {:?}",
1418 result, expected
1419 );
1420 }
1421}