1use array_init::{array_init, try_array_init};
2use float::FloatCore;
3use num_bigint::BigInt;
4use num_rational::BigRational;
5use num_traits::*;
6use ordered_float::OrderedFloat;
7use ordered_float::{FloatIsNan, NotNan};
8use rand::distributions::{Distribution, Standard};
9use rand::Rng;
10use std::cmp::Ordering;
11use std::convert::TryFrom;
12use std::fmt::Debug;
13use std::iter::Sum;
14use std::ops::Deref;
15use std::ops::Index;
16
17use super::{Direction, Vector};
18use crate::{Orientation, PolygonScalar, TotalOrd};
19
20#[allow(clippy::derived_hash_with_manual_eq)]
21#[derive(Debug, Clone, Copy, Hash)]
22#[repr(transparent)] pub struct Point<T, const N: usize = 2> {
24 pub array: [T; N],
25}
26
27impl<T: TotalOrd, const N: usize> TotalOrd for Point<T, N> {
28 fn total_cmp(&self, other: &Self) -> Ordering {
29 for i in 0..N {
30 match self.array[i].total_cmp(&other.array[i]) {
31 Ordering::Equal => continue,
32 other => return other,
33 }
34 }
35 Ordering::Equal
36 }
37}
38
39impl<T: TotalOrd, const N: usize> PartialEq for Point<T, N> {
40 fn eq(&self, other: &Self) -> bool {
41 self.total_cmp(other).is_eq()
42 }
43}
44impl<T: TotalOrd, const N: usize> Eq for Point<T, N> {}
45
46impl<T: TotalOrd, const N: usize> PartialOrd for Point<T, N> {
47 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48 Some(std::cmp::Ord::cmp(self, other))
49 }
50}
51
52impl<T: TotalOrd, const N: usize> Ord for Point<T, N> {
53 fn cmp(&self, other: &Self) -> Ordering {
54 self.total_cmp(other)
55 }
56}
57
58#[derive(Debug, Clone, Copy)]
59pub struct PointSoS<'a, T, const N: usize = 2> {
60 pub index: u32,
61 pub point: &'a Point<T, N>,
62}
63
64impl<T: TotalOrd, const N: usize> TotalOrd for PointSoS<'_, T, N> {
65 fn total_cmp(&self, other: &Self) -> Ordering {
66 (self.index, self.point).total_cmp(&(other.index, other.point))
67 }
68}
69
70impl<T: TotalOrd, const N: usize> PartialEq for PointSoS<'_, T, N> {
71 fn eq(&self, other: &Self) -> bool {
72 self.total_cmp(other).is_eq()
73 }
74}
75impl<T: TotalOrd, const N: usize> Eq for PointSoS<'_, T, N> {}
76
77impl<T: TotalOrd, const N: usize> PartialOrd for PointSoS<'_, T, N> {
78 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
79 Some(std::cmp::Ord::cmp(self, other))
80 }
81}
82
83impl<T: TotalOrd, const N: usize> Ord for PointSoS<'_, T, N> {
84 fn cmp(&self, other: &Self) -> Ordering {
85 self.total_cmp(other)
86 }
87}
88
89impl<T, const N: usize> Distribution<Point<T, N>> for Standard
91where
92 Standard: Distribution<T>,
93{
94 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point<T, N> {
96 Point {
97 array: array_init(|_| rng.gen()),
98 }
99 }
100}
101
102impl<T, const N: usize> Point<T, N> {
104 pub const fn new(array: [T; N]) -> Point<T, N> {
105 Point { array }
106 }
107
108 pub fn new_nn(array: [T; N]) -> Point<NotNan<T>, N>
112 where
113 T: FloatCore,
114 {
115 Point::new(array_init(|i| NotNan::new(array[i]).unwrap()))
116 }
117
118 pub fn as_vec(&self) -> &Vector<T, N> {
119 self.into()
120 }
121
122 pub fn squared_euclidean_distance<F>(&self, rhs: &Point<T, N>) -> F
125 where
126 T: Clone + Into<F>,
127 F: NumOps + Clone + Sum,
128 {
129 self
130 .array
131 .iter()
132 .zip(rhs.array.iter())
133 .map(|(a, b)| {
134 let diff = a.clone().into() - b.clone().into();
135 diff.clone() * diff
136 })
137 .sum()
138 }
139
140 pub fn zero() -> Self
142 where
143 T: Sum,
144 {
145 Point {
146 array: array_init(|_| std::iter::empty().sum()),
147 }
148 }
149
150 pub fn map<U, F>(&self, f: F) -> Point<U, N>
151 where
152 T: Clone,
153 F: Fn(T) -> U,
154 {
155 Point {
156 array: array_init(|i| f(self.array[i].clone())),
157 }
158 }
159
160 pub fn cast<U>(&self) -> Point<U, N>
161 where
162 T: Clone + Into<U>,
163 {
164 Point {
165 array: array_init(|i| self.array[i].clone().into()),
166 }
167 }
168
169 pub fn to_float(&self) -> Point<OrderedFloat<f64>, N>
170 where
171 T: Clone + Into<f64>,
172 {
173 self.map(|v| OrderedFloat(v.into()))
174 }
175}
176
177impl<T, const N: usize> Index<usize> for Point<T, N> {
178 type Output = T;
179 fn index(&self, key: usize) -> &T {
180 self.array.index(key)
181 }
182}
183
184impl<const N: usize> TryFrom<Point<f64, N>> for Point<NotNan<f64>, N> {
185 type Error = FloatIsNan;
186 fn try_from(point: Point<f64, N>) -> Result<Point<NotNan<f64>, N>, FloatIsNan> {
187 Ok(Point {
188 array: try_array_init(|i| NotNan::try_from(point.array[i]))?,
189 })
190 }
191}
192
193impl<T> From<(T, T)> for Point<T, 2> {
194 fn from(point: (T, T)) -> Point<T, 2> {
195 Point {
196 array: [point.0, point.1],
197 }
198 }
199}
200
201impl From<Point<i64, 2>> for Point<BigInt, 2> {
202 fn from(point: Point<i64, 2>) -> Point<BigInt, 2> {
203 Point {
204 array: [point.array[0].into(), point.array[1].into()],
205 }
206 }
207}
208
209impl<const N: usize> From<&Point<BigRational, N>> for Point<f64, N> {
210 fn from(point: &Point<BigRational, N>) -> Point<f64, N> {
211 Point {
212 array: array_init(|i| point.array[i].to_f64().unwrap()),
213 }
214 }
215}
216
217impl<const N: usize> From<Point<BigRational, N>> for Point<f64, N> {
218 fn from(point: Point<BigRational, N>) -> Point<f64, N> {
219 Point {
220 array: array_init(|i| point.array[i].to_f64().unwrap()),
221 }
222 }
223}
224
225impl<const N: usize> From<&Point<f64, N>> for Point<BigRational, N> {
226 fn from(point: &Point<f64, N>) -> Point<BigRational, N> {
227 Point {
228 array: array_init(|i| BigRational::from_f64(point.array[i]).unwrap()),
229 }
230 }
231}
232
233impl<const N: usize> From<Point<f64, N>> for Point<BigRational, N> {
234 fn from(point: Point<f64, N>) -> Point<BigRational, N> {
235 Point {
236 array: array_init(|i| BigRational::from_f64(point.array[i]).unwrap()),
237 }
238 }
239}
240
241impl<T, const N: usize> From<Vector<T, N>> for Point<T, N> {
242 fn from(vector: Vector<T, N>) -> Point<T, N> {
243 Point { array: vector.0 }
244 }
245}
246
247impl<T: PolygonScalar> Point<T> {
249 pub fn cmp_distance_to(&self, p: &Point<T, 2>, q: &Point<T, 2>) -> Ordering {
250 T::cmp_dist(self, p, q)
251 }
252
253 pub fn orient(p1: &Point<T, 2>, p2: &Point<T, 2>, p3: &Point<T, 2>) -> Orientation {
275 Orientation::new(p1, p2, p3)
276 }
277
278 pub fn orient_along_direction(
279 p1: &Point<T, 2>,
280 direction: Direction<'_, T, 2>,
281 p2: &Point<T, 2>,
282 ) -> Orientation {
283 match direction {
284 Direction::Vector(v) => Orientation::along_vector(p1, v, p2),
285 Direction::Through(p) => Orientation::new(p1, p, p2),
286 }
287 }
288
289 pub fn orient_along_vector(
290 p1: &Point<T, 2>,
291 vector: &Vector<T, 2>,
292 p2: &Point<T, 2>,
293 ) -> Orientation {
294 Orientation::along_vector(p1, vector, p2)
295 }
296
297 pub fn orient_along_perp_vector(
298 p1: &Point<T, 2>,
299 vector: &Vector<T, 2>,
300 p2: &Point<T, 2>,
301 ) -> Orientation {
302 Orientation::along_perp_vector(p1, vector, p2)
303 }
304
305 pub fn all_colinear(pts: &[Point<T>]) -> bool {
306 if pts.len() < 3 {
307 return true;
308 }
309 pts
310 .iter()
311 .all(|pt| Point::orient(&pts[0], &pts[1], pt).is_colinear())
312 }
313
314 pub fn ccw_cmp_around(&self, p: &Point<T, 2>, q: &Point<T, 2>) -> Ordering {
316 self.ccw_cmp_around_with(&Vector([T::from_constant(1), T::from_constant(0)]), p, q)
317 }
318
319 pub fn ccw_cmp_around_with(
320 &self,
321 z: &Vector<T, 2>,
322 p: &Point<T, 2>,
323 q: &Point<T, 2>,
324 ) -> Ordering {
325 Orientation::ccw_cmp_around_with(z, self, p, q)
326 }
327}
328
329impl<T> Point<T, 1> {
331 pub fn x_coord(&self) -> &T {
332 &self.array[0]
333 }
334}
335impl<T> Point<T, 2> {
336 pub fn x_coord(&self) -> &T {
337 &self.array[0]
338 }
339 pub fn y_coord(&self) -> &T {
340 &self.array[1]
341 }
342}
343impl<T> Point<T, 3> {
344 pub fn x_coord(&self) -> &T {
345 &self.array[0]
346 }
347 pub fn y_coord(&self) -> &T {
348 &self.array[1]
349 }
350 pub fn z_coord(&self) -> &T {
351 &self.array[2]
352 }
353}
354
355impl<T, const N: usize> Deref for Point<T, N> {
356 type Target = [T; N];
357 fn deref(&self) -> &[T; N] {
358 &self.array
359 }
360}
361
362mod add;
363mod sub;
364
365#[cfg(test)]
366#[cfg(not(tarpaulin_include))]
367pub mod tests {
368 use super::*;
369 use crate::testing::*;
370 use crate::Orientation::*;
371
372 use proptest::prelude::*;
373 use test_strategy::proptest;
374
375 #[proptest]
376 fn cmp_dist_i8_fuzz(pt1: Point<i8, 2>, pt2: Point<i8, 2>, pt3: Point<i8, 2>) {
377 let pt1_big: Point<BigInt, 2> = pt1.cast();
378 let pt2_big: Point<BigInt, 2> = pt2.cast();
379 let pt3_big: Point<BigInt, 2> = pt3.cast();
380 prop_assert_eq!(
381 pt1.cmp_distance_to(&pt2, &pt3),
382 pt1_big.cmp_distance_to(&pt2_big, &pt3_big)
383 );
384 }
385
386 #[proptest]
387 fn squared_euclidean_distance_fuzz(
388 #[strategy(any_nn::<2>())] pt1: Point<NotNan<f64>, 2>,
389 #[strategy(any_nn::<2>())] pt2: Point<NotNan<f64>, 2>,
390 ) {
391 let _out: f64 = pt1.squared_euclidean_distance(&pt2);
392 }
393
394 #[proptest]
395 fn squared_euclidean_distance_i8_fuzz(pt1: Point<i8, 2>, pt2: Point<i8, 2>) {
396 let _out: i64 = pt1.squared_euclidean_distance(&pt2);
397 }
398
399 #[proptest]
400 fn cmp_around_fuzz_nn(
401 #[strategy(any_nn::<2>())] pt1: Point<NotNan<f64>, 2>,
402 #[strategy(any_nn::<2>())] pt2: Point<NotNan<f64>, 2>,
403 #[strategy(any_nn::<2>())] pt3: Point<NotNan<f64>, 2>,
404 ) {
405 let _ = pt1.ccw_cmp_around(&pt2, &pt3);
406 }
407
408 #[proptest]
409 fn cmp_around_fuzz_i8(pt1: Point<i8, 2>, pt2: Point<i8, 2>, pt3: Point<i8, 2>) {
410 let _ = pt1.ccw_cmp_around(&pt2, &pt3);
411 }
412
413 #[proptest]
414 fn bigint_colinear(
415 #[strategy(any_r::<2>())] pt1: Point<BigInt, 2>,
416 #[strategy(any_r::<2>())] pt2: Point<BigInt, 2>,
417 ) {
418 let diff = &pt2 - &pt1;
419 let pt3 = &pt2 + &diff;
420 prop_assert!(Point::orient(&pt1, &pt2, &pt3).is_colinear())
421 }
422
423 #[proptest]
424 fn bigint_not_colinear(
425 #[strategy(any_r::<2>())] pt1: Point<BigInt, 2>,
426 #[strategy(any_r::<2>())] pt2: Point<BigInt, 2>,
427 ) {
428 let diff = &pt2 - &pt1;
429 let pt3 = &pt2 + &diff + &Vector([BigInt::from(1), BigInt::from(1)]);
430 prop_assert!(!Point::orient(&pt1, &pt2, &pt3).is_colinear())
431 }
432
433 #[proptest]
436 fn orient_bigint_i8_prop(pt1: Point<i8, 2>, pt2: Point<i8, 2>, pt3: Point<i8, 2>) {
437 let pt1_big: Point<BigInt, 2> = pt1.cast();
438 let pt2_big: Point<BigInt, 2> = pt2.cast();
439 let pt3_big: Point<BigInt, 2> = pt3.cast();
440
441 prop_assert_eq!(
442 Point::orient(&pt1, &pt2, &pt3),
443 Point::orient(&pt1_big, &pt2_big, &pt3_big)
444 )
445 }
446
447 #[proptest]
450 fn orient_f64_i8_prop(pt1: Point<i8, 2>, pt2: Point<i8, 2>, pt3: Point<i8, 2>) {
451 let pt1_big: Point<OrderedFloat<f64>, 2> = pt1.to_float();
452 let pt2_big: Point<OrderedFloat<f64>, 2> = pt2.to_float();
453 let pt3_big: Point<OrderedFloat<f64>, 2> = pt3.to_float();
454
455 prop_assert_eq!(
456 Point::orient(&pt1, &pt2, &pt3),
457 Point::orient(&pt1_big, &pt2_big, &pt3_big)
458 )
459 }
460
461 #[proptest]
462 fn orientation_reverse(pt1: Point<i64, 2>, pt2: Point<i64, 2>, pt3: Point<i64, 2>) {
463 let abc = Point::orient(&pt1, &pt2, &pt3);
464 let cba = Point::orient(&pt3, &pt2, &pt1);
465 prop_assert_eq!(abc, cba.reverse())
466 }
467
468 #[test]
469 fn test_turns() {
470 assert_eq!(
471 Point::orient(
472 &Point::new([0, 0]),
473 &Point::new([1, 1]),
474 &Point::new([2, 2])
475 ),
476 CoLinear
477 );
478 assert_eq!(
479 Orientation::new(
480 &Point::new_nn([0.0, 0.0]),
481 &Point::new_nn([1.0, 1.0]),
482 &Point::new_nn([2.0, 2.0])
483 ),
484 CoLinear
485 );
486
487 assert_eq!(
488 Point::orient(
489 &Point::new([0, 0]),
490 &Point::new([0, 1]),
491 &Point::new([2, 2])
492 ),
493 ClockWise
494 );
495 assert_eq!(
496 Point::orient(
497 &Point::new_nn([0.0, 0.0]),
498 &Point::new_nn([0.0, 1.0]),
499 &Point::new_nn([2.0, 2.0])
500 ),
501 ClockWise
502 );
503
504 assert_eq!(
505 Point::orient(
506 &Point::new([0, 0]),
507 &Point::new([0, 1]),
508 &Point::new([-2, 2])
509 ),
510 CounterClockWise
511 );
512 assert_eq!(
513 Point::orient(
514 &Point::new([0, 0]),
515 &Point::new([0, 0]),
516 &Point::new([0, 0])
517 ),
518 CoLinear
519 );
520 }
521
522 #[test]
523 fn unit_1() {
524 assert_eq!(
525 Point::orient(
526 &Point::new([0, 0]),
527 &Point::new([1, 0]),
528 &Point::new([1, 0])
529 ),
530 CoLinear
531 );
532 assert_eq!(
533 Point::orient(
534 &Point::new([0, 0]),
535 &Point::new([1, 0]),
536 &Point::new([2, 0])
537 ),
538 CoLinear
539 );
540 assert_eq!(
541 Point::orient(
542 &Point::new([1, 0]),
543 &Point::new([2, 0]),
544 &Point::new([0, 0])
545 ),
546 CoLinear
547 );
548 assert_eq!(
549 Point::orient(
550 &Point::new([1, 0]),
551 &Point::new([2, 0]),
552 &Point::new([1, 0])
553 ),
554 CoLinear
555 );
556 }
557
558 #[test]
559 fn unit_2() {
560 assert_eq!(
561 Point::orient(
562 &Point::new([1, 0]),
563 &Point::new([0, 6]),
564 &Point::new([0, 8])
565 ),
566 ClockWise
567 );
568 }
569
570 #[test]
571 fn unit_3() {
572 assert_eq!(
573 Point::orient(
574 &Point::new([-12_i8, -126]),
575 &Point::new([-12, -126]),
576 &Point::new([0, -126])
577 ),
578 CoLinear
579 );
580 }
581}