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::*;
8use std::cmp::Ordering;
9use std::iter::Sum;
10use std::ops::*;
11
12pub mod algorithms;
13pub mod data;
14mod intersection;
15mod matrix;
16mod orientation;
17mod transformation;
18mod utils;
19
20pub use orientation::{Orientation, SoS};
21
22pub use intersection::Intersects;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum Error {
26 InsufficientVertices,
27 SelfIntersections,
28 DuplicatePoints,
29 ConvexViolation,
31 ClockWiseViolation,
32 CoLinearViolation,
33}
34
35impl std::fmt::Display for Error {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
37 match self {
38 Error::InsufficientVertices => write!(f, "Insufficient vertices"),
39 Error::SelfIntersections => write!(f, "Self intersections"),
40 Error::DuplicatePoints => write!(f, "Duplicate points"),
41 Error::ConvexViolation => write!(f, "Convex violation"),
42 Error::ClockWiseViolation => write!(f, "Clockwise violation"),
43 Error::CoLinearViolation => write!(
44 f,
45 "Two or more points are colinear and no valid solution exists"
46 ),
47 }
48 }
49}
50
51pub trait TotalOrd {
52 fn total_cmp(&self, other: &Self) -> Ordering;
53
54 fn total_min(self, other: Self) -> Self
55 where
56 Self: Sized,
57 {
58 std::cmp::min_by(self, other, TotalOrd::total_cmp)
59 }
60
61 fn total_max(self, other: Self) -> Self
62 where
63 Self: Sized,
64 {
65 std::cmp::max_by(self, other, TotalOrd::total_cmp)
66 }
67}
68
69impl TotalOrd for u32 {
70 fn total_cmp(&self, other: &Self) -> Ordering {
71 self.cmp(other)
72 }
73}
74
75impl<A: TotalOrd> TotalOrd for &A {
76 fn total_cmp(&self, other: &Self) -> Ordering {
77 (*self).total_cmp(*other)
78 }
79}
80
81impl<A: TotalOrd, B: TotalOrd> TotalOrd for (A, B) {
82 fn total_cmp(&self, other: &Self) -> Ordering {
83 self
84 .0
85 .total_cmp(&other.0)
86 .then_with(|| self.1.total_cmp(&other.1))
87 }
88}
89
90pub trait PolygonScalar:
92 std::fmt::Debug
93 + Neg<Output = Self>
94 + NumAssignOps
95 + NumOps<Self, Self>
96 + TotalOrd
97 + PartialOrd
98 + Sum
99 + Clone
100{
101 fn from_constant(val: i8) -> Self;
102 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
103 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
104 fn cmp_vector_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
105 fn cmp_perp_vector_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering;
106}
107
108macro_rules! fixed_precision {
109 ( $ty:ty, $uty:ty, $long:ty, $ulong: ty ) => {
110 impl TotalOrd for $ty {
111 fn total_cmp(&self, other: &Self) -> Ordering {
112 self.cmp(other)
113 }
114 }
115
116 impl PolygonScalar for $ty {
117 fn from_constant(val: i8) -> Self {
118 val as $ty
119 }
120 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
121 fn diff(a: $ty, b: $ty) -> $ulong {
122 if b > a {
123 b.wrapping_sub(a) as $uty as $ulong
124 } else {
125 a.wrapping_sub(b) as $uty as $ulong
126 }
127 }
128 let pq_x = diff(p[0], q[0]);
129 let pq_y = diff(p[1], q[1]);
130 let (pq_dist_squared, pq_overflow) = (pq_x * pq_x).overflowing_add(pq_y * pq_y);
131 let pr_x = diff(p[0], r[0]);
132 let pr_y = diff(p[1], r[1]);
133 let (pr_dist_squared, pr_overflow) = (pr_x * pr_x).overflowing_add(pr_y * pr_y);
134 match (pq_overflow, pr_overflow) {
135 (true, false) => Ordering::Greater,
136 (false, true) => Ordering::Less,
137 _ => pq_dist_squared.cmp(&pr_dist_squared),
138 }
139 }
140
141 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
142 fn diff(a: $ty, b: $ty) -> ($ulong, bool) {
148 if b > a {
149 (b.wrapping_sub(a) as $uty as $ulong, true)
150 } else {
151 (a.wrapping_sub(b) as $uty as $ulong, false)
152 }
153 }
154 let (ux, ux_neg) = diff(q[0], p[0]);
155 let (vy, vy_neg) = diff(r[1], p[1]);
156 let ux_vy_neg = ux_neg.bitxor(vy_neg) && ux != 0 && vy != 0;
157 let (uy, uy_neg) = diff(q[1], p[1]);
158 let (vx, vx_neg) = diff(r[0], p[0]);
159 let uy_vx_neg = uy_neg.bitxor(vx_neg) && uy != 0 && vx != 0;
160 match (ux_vy_neg, uy_vx_neg) {
161 (true, false) => Ordering::Less,
162 (false, true) => Ordering::Greater,
163 (true, true) => (uy * vx).cmp(&(ux * vy)),
164 (false, false) => (ux * vy).cmp(&(uy * vx)),
165 }
166 }
167
168 fn cmp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
169 fn diff(a: $ty, b: $ty) -> ($ulong, bool) {
175 if b > a {
176 (b.wrapping_sub(a) as $uty as $ulong, true)
177 } else {
178 (a.wrapping_sub(b) as $uty as $ulong, false)
179 }
180 }
181 let (ux, ux_neg) = (vector[0].unsigned_abs() as $ulong, vector[0] < 0);
182 let (vy, vy_neg) = diff(q[1], p[1]);
183 let ux_vy_neg = ux_neg.bitxor(vy_neg) && ux != 0 && vy != 0;
188 let (uy, uy_neg) = (vector[1].unsigned_abs() as $ulong, vector[1] < 0);
189 let (vx, vx_neg) = diff(q[0], p[0]);
190 let uy_vx_neg = uy_neg.bitxor(vx_neg) && uy != 0 && vx != 0;
191 match (ux_vy_neg, uy_vx_neg) {
192 (true, false) => Ordering::Less,
193 (false, true) => Ordering::Greater,
194 (true, true) => (uy * vx).cmp(&(ux * vy)),
195 (false, false) => (ux * vy).cmp(&(uy * vx)),
196 }
197 }
198
199 fn cmp_perp_vector_slope(
200 vector: &[Self; 2],
201 p: &[Self; 2],
202 q: &[Self; 2],
203 ) -> std::cmp::Ordering {
204 fn diff(a: $ty, b: $ty) -> ($ulong, bool) {
210 if b > a {
211 (b.wrapping_sub(a) as $uty as $ulong, true)
212 } else {
213 (a.wrapping_sub(b) as $uty as $ulong, false)
214 }
215 }
216 let (ux, ux_neg) = (vector[1].unsigned_abs() as $ulong, vector[1] > 0);
217 let (vy, vy_neg) = diff(q[1], p[1]);
218 let ux_vy_neg = ux_neg.bitxor(vy_neg) && ux != 0 && vy != 0;
223 let (uy, uy_neg) = (vector[0].unsigned_abs() as $ulong, vector[0] < 0);
224 let (vx, vx_neg) = diff(q[0], p[0]);
225 let uy_vx_neg = uy_neg.bitxor(vx_neg) && uy != 0 && vx != 0;
226 match (ux_vy_neg, uy_vx_neg) {
227 (true, false) => Ordering::Less,
228 (false, true) => Ordering::Greater,
229 (true, true) => (uy * vx).cmp(&(ux * vy)),
230 (false, false) => (ux * vy).cmp(&(uy * vx)),
231 }
232 }
233 }
234 };
235}
236
237macro_rules! arbitrary_precision {
238 ( $( $ty:ty ),* ) => {
239 $(
240 impl TotalOrd for $ty {
241 fn total_cmp(&self, other: &Self) -> Ordering {
242 self.cmp(other)
243 }
244 }
245
246 impl PolygonScalar for $ty {
247 fn from_constant(val: i8) -> Self {
248 <$ty>::from_i8(val).unwrap()
249 }
250 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
251 let pq_x = &p[0] - &q[0];
252 let pq_y = &p[1] - &q[1];
253 let pq_dist_squared: Self = &pq_x*&pq_x + &pq_y*&pq_y;
254 let pr_x = &p[0] - &r[0];
255 let pr_y = &p[1] - &r[1];
256 let pr_dist_squared: Self = &pr_x*&pr_x + &pr_y*&pr_y;
257 pq_dist_squared.cmp(&pr_dist_squared)
258 }
259
260 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
261 let slope1 = (&r[1] - &q[1]) * (&q[0] - &p[0]);
262 let slope2 = (&q[1] - &p[1]) * (&r[0] - &q[0]);
263 slope1.cmp(&slope2)
264 }
265 fn cmp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
266 PolygonScalar::cmp_slope(
267 p,
268 &[&p[0] + &vector[0], &p[1] + &vector[1]],
269 q
270 )
271 }
272 fn cmp_perp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
273 PolygonScalar::cmp_slope(
274 p,
275 &[&p[0] - &vector[1], &p[1] + &vector[0]],
276 q
277 )
278 }
279 })*
280 };
281}
282
283macro_rules! wrapped_floating_precision {
284 ( $( $ty:ty ),* ) => {
285 $(
286 impl TotalOrd for $ty {
287 fn total_cmp(&self, other: &Self) -> Ordering {
288 self.cmp(other)
289 }
290 }
291
292 impl PolygonScalar for $ty {
293 fn from_constant(val: i8) -> Self {
294 <$ty>::from_i8(val).unwrap()
295 }
296 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
299 PolygonScalar::cmp_dist(
300 &[float_to_rational(p[0].into_inner()), float_to_rational(p[1].into_inner())],
301 &[float_to_rational(q[0].into_inner()), float_to_rational(q[1].into_inner())],
302 &[float_to_rational(r[0].into_inner()), float_to_rational(r[1].into_inner())],
303 )
304 }
305
306 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
310 let orient = geometry_predicates::predicates::orient2d(
311 [p[0].into_inner() as f64, p[1].into_inner() as f64],
312 [q[0].into_inner() as f64, q[1].into_inner() as f64],
313 [r[0].into_inner() as f64, r[1].into_inner() as f64],
314 );
315 if orient > 0.0 {
316 Ordering::Greater
317 } else if orient < 0.0 {
318 Ordering::Less
319 } else {
320 Ordering::Equal
321 }
322 }
323 fn cmp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
326 PolygonScalar::cmp_vector_slope(
327 &[float_to_rational(vector[0].into_inner()), float_to_rational(vector[1].into_inner())],
328 &[float_to_rational(p[0].into_inner()), float_to_rational(p[1].into_inner())],
329 &[float_to_rational(q[0].into_inner()), float_to_rational(q[1].into_inner())],
330 )
331 }
332 fn cmp_perp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
335 PolygonScalar::cmp_perp_vector_slope(
336 &[float_to_rational(vector[0].into_inner()), float_to_rational(vector[1].into_inner())],
337 &[float_to_rational(p[0].into_inner()), float_to_rational(p[1].into_inner())],
338 &[float_to_rational(q[0].into_inner()), float_to_rational(q[1].into_inner())],
339 )
340 }
341 })*
342 };
343}
344
345macro_rules! floating_precision {
346 ( $( $ty:ty ),* ) => {
347 $(
348 impl TotalOrd for $ty {
349 fn total_cmp(&self, other: &Self) -> Ordering {
350 <$ty>::total_cmp(self, other)
351 }
352 }
353
354 impl PolygonScalar for $ty {
355 fn from_constant(val: i8) -> Self {
356 <$ty>::from_i8(val).unwrap()
357 }
358 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
361 PolygonScalar::cmp_dist(
362 &[float_to_rational(p[0]), float_to_rational(p[1])],
363 &[float_to_rational(q[0]), float_to_rational(q[1])],
364 &[float_to_rational(r[0]), float_to_rational(r[1])],
365 )
366 }
367
368 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
372 let orient = geometry_predicates::predicates::orient2d(
373 [p[0] as f64, p[1] as f64],
374 [q[0] as f64, q[1] as f64],
375 [r[0] as f64, r[1] as f64],
376 );
377 if orient > 0.0 {
378 Ordering::Greater
379 } else if orient < 0.0 {
380 Ordering::Less
381 } else {
382 Ordering::Equal
383 }
384 }
385 fn cmp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
388 PolygonScalar::cmp_vector_slope(
389 &[float_to_rational(vector[0]), float_to_rational(vector[1])],
390 &[float_to_rational(p[0]), float_to_rational(p[1])],
391 &[float_to_rational(q[0]), float_to_rational(q[1])],
392 )
393 }
394 fn cmp_perp_vector_slope(vector: &[Self;2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
397 PolygonScalar::cmp_perp_vector_slope(
398 &[float_to_rational(vector[0]), float_to_rational(vector[1])],
399 &[float_to_rational(p[0]), float_to_rational(p[1])],
400 &[float_to_rational(q[0]), float_to_rational(q[1])],
401 )
402 }
403 })*
404 };
405}
406
407fixed_precision!(i8, u8, i16, u16);
408fixed_precision!(i16, u16, i32, u32);
409fixed_precision!(i32, u32, i64, u64);
410fixed_precision!(i64, u64, i128, u128);
411fixed_precision!(isize, usize, i128, u128);
412arbitrary_precision!(num_bigint::BigInt);
413arbitrary_precision!(num_rational::BigRational);
414wrapped_floating_precision!(ordered_float::OrderedFloat<f32>);
415wrapped_floating_precision!(ordered_float::OrderedFloat<f64>);
416wrapped_floating_precision!(ordered_float::NotNan<f32>);
417wrapped_floating_precision!(ordered_float::NotNan<f64>);
418floating_precision!(f32);
419floating_precision!(f64);
420
421#[cfg(feature = "rug")]
422impl TotalOrd for rug::Integer {
423 fn total_cmp(&self, other: &Self) -> Ordering {
424 self.cmp(other)
425 }
426}
427
428#[cfg(feature = "rug")]
429impl PolygonScalar for rug::Integer {
430 fn from_constant(val: i8) -> Self {
431 rug::Integer::from(val)
432 }
433 fn cmp_dist(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
434 let [qx, qy] = q.clone();
435 let [px, py] = p.clone();
436 let pq_x = px - qx;
437 let pq_y = py - qy;
438 let pq_dist_squared: Self = pq_x.square() + pq_y.square();
439 let [rx, ry] = r.clone();
440 let [px, py] = p.clone();
441 let pr_x = px - rx;
442 let pr_y = py - ry;
443 let pr_dist_squared: Self = pr_x.square() + pr_y.square();
444 pq_dist_squared.cmp(&pr_dist_squared)
445 }
446
447 fn cmp_slope(p: &[Self; 2], q: &[Self; 2], r: &[Self; 2]) -> std::cmp::Ordering {
448 let [qx, qy] = q.clone();
449 let [rx, ry] = r.clone();
450 let ry_qy = ry - &q[1];
451 let qx_px = qx - &p[0];
452 let slope1 = ry_qy * qx_px;
453 let qy_py = qy - &p[1];
454 let rx_qx = rx - &q[0];
455 let slope2 = qy_py * rx_qx;
456 slope1.cmp(&slope2)
457 }
458 fn cmp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
459 let new_x = rug::Integer::from(&p[0] + &vector[0]);
460 let new_y = rug::Integer::from(&p[1] + &vector[1]);
461 PolygonScalar::cmp_slope(p, &[new_x, new_y], q)
462 }
463 fn cmp_perp_vector_slope(vector: &[Self; 2], p: &[Self; 2], q: &[Self; 2]) -> std::cmp::Ordering {
464 let new_x = rug::Integer::from(&p[0] + &vector[1]);
465 let new_y = rug::Integer::from(&p[1] + &vector[0]);
466 PolygonScalar::cmp_slope(p, &[new_x, new_y], q)
467 }
468}
469
470fn float_to_rational(f: impl num::traits::float::FloatCore) -> num::BigRational {
471 num::BigRational::from_float(f).expect("cannot convert NaN or infinite to exact precision number")
472}
473
474#[cfg(test)]
475pub mod testing;