1use num_traits::*;
3use ordered_float::OrderedFloat;
4use std::cmp::Ordering;
5use std::iter::Sum;
6use std::ops::Bound::*;
7use std::ops::*;
8
9use crate::data::{
10 DirectedEdge, HalfLineSoS, IHalfLineLineSegmentSoS::*, Point, PointLocation, TriangleView, Vector,
11};
12use crate::intersection::*;
13use crate::{Error, Orientation, PolygonScalar, TotalOrd};
14
15mod iter;
16pub use iter::*;
17
18mod convex;
19pub use convex::*;
20
21use super::Transform;
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct PositionId(usize);
25
26impl From<PositionId> for usize {
27 fn from(pid: PositionId) -> usize {
28 pid.0
29 }
30}
31
32#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct RingId(usize);
34
35impl From<RingId> for usize {
36 fn from(rid: RingId) -> usize {
37 rid.0
38 }
39}
40
41#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub struct PointId(usize);
43
44impl From<PointId> for usize {
45 fn from(pid: PointId) -> usize {
46 pid.0
47 }
48}
49
50impl std::fmt::Debug for PointId {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
52 f.write_fmt(format_args!("{}", self.0))
53 }
54}
55
56impl PointId {
57 pub const INVALID: PointId = PointId(usize::MAX);
58 pub fn usize(self) -> usize {
59 self.0
60 }
61}
62
63#[non_exhaustive]
64#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
65pub struct IndexEdge {
66 pub min: PointId,
67 pub max: PointId,
68}
69
70impl std::fmt::Debug for IndexEdge {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
72 f.debug_tuple("IndexEdge")
73 .field(&self.min)
74 .field(&self.max)
75 .finish()
76 }
77}
78
79impl IndexEdge {
81 pub fn new(a: PointId, b: PointId) -> IndexEdge {
82 IndexEdge {
83 min: std::cmp::min(a, b),
84 max: std::cmp::max(a, b),
85 }
86 }
87}
88
89impl From<DirectedIndexEdge> for IndexEdge {
90 fn from(directed: DirectedIndexEdge) -> IndexEdge {
91 IndexEdge::new(directed.src, directed.dst)
92 }
93}
94
95#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
96pub struct DirectedIndexEdge {
97 pub src: PointId,
98 pub dst: PointId,
99}
100
101#[derive(Debug, Clone, Hash)]
114pub struct Polygon<T> {
115 pub(crate) points: Vec<Point<T>>,
118 pub(crate) ring_index: Vec<RingId>,
120 pub(crate) position_index: Vec<PositionId>,
122 pub(crate) rings: Vec<Vec<PointId>>,
127}
128
129impl<T> Polygon<T> {
130 pub fn new_unchecked(vertices: Vec<Point<T, 2>>) -> Polygon<T>
132 where
133 T: PolygonScalar,
134 {
135 let len = vertices.len();
136 Polygon {
137 points: vertices,
138 ring_index: vec![RingId(0); len],
139 position_index: (0..len).map(PositionId).collect(),
140 rings: vec![(0..len).map(PointId).collect()],
141 }
142 }
143
144 pub fn new(points: Vec<Point<T, 2>>) -> Result<Polygon<T>, Error>
146 where
147 T: PolygonScalar,
148 {
149 if points.len() < 3 {
151 return Err(Error::InsufficientVertices);
152 }
153 let mut p = Self::new_unchecked(points);
154 p.ensure_ccw()?;
155 p.validate()?;
156 Ok(p)
157 }
158
159 pub fn validate(&self) -> Result<(), Error>
162 where
163 T: PolygonScalar,
164 {
165 let mut seen = std::collections::BTreeSet::new();
167 for pt in self.iter() {
168 if !seen.insert(pt) {
169 return Err(Error::DuplicatePoints);
170 }
171 }
172
173 self.validate_weakly()
174 }
175
176 pub fn validate_weakly(&self) -> Result<(), Error>
177 where
178 T: PolygonScalar,
179 {
180 assert!(!self.rings.is_empty());
183
184 if self.rings[0].len() < 3 {
186 return Err(Error::InsufficientVertices);
187 }
188 if self.orientation() != Orientation::CounterClockWise {
190 return Err(Error::ClockWiseViolation);
191 }
192 let edges: Vec<DirectedEdge<'_, T, 2>> = self.iter_boundary_edges().collect();
195 let mut isects = crate::algorithms::segment_intersections(&edges);
196 if isects.next().is_some() {
197 return Err(Error::SelfIntersections);
198 }
199 Ok(())
200 }
201
202 pub fn locate(&self, origin: &Point<T, 2>) -> PointLocation
203 where
204 T: PolygonScalar,
205 {
206 assert_eq!(
208 self.rings.len(),
209 1,
210 "FIXME: Polygon::locate should support polygons with holes."
211 );
212
213 let direction = Vector::unit_right();
214 let ray = HalfLineSoS::new_directed(origin, &direction);
215 let mut intersections = 0;
216 for edge in self.iter_boundary_edges() {
217 if edge.contains(origin) {
218 return PointLocation::OnBoundary;
219 }
220 if let Some(Crossing(lean)) = ray.intersect(edge) {
221 if !lean.is_cw() {
223 intersections += 1;
224 }
225 }
226 }
227 if intersections % 2 == 0 {
228 PointLocation::Outside
229 } else {
230 PointLocation::Inside
231 }
232 }
233
234 pub fn triangulate(
235 &self,
236 ) -> impl Iterator<Item = (Cursor<'_, T>, Cursor<'_, T>, Cursor<'_, T>)> + '_
237 where
238 T: PolygonScalar,
239 {
240 crate::algorithms::triangulation::earclip::earclip(self)
241 .map(move |(p1, p2, p3)| (self.cursor(p1), self.cursor(p2), self.cursor(p3)))
242 }
243
244 pub fn centroid(&self) -> Point<T>
250 where
251 T: PolygonScalar,
252 {
253 let xs: Vector<T, 2> = self
254 .iter_boundary_edges()
255 .map(|edge| {
256 let p = edge.src.as_vec();
257 let q = edge.dst.as_vec();
258 (p + q) * (p.0[0].clone() * q.0[1].clone() - q.0[0].clone() * p.0[1].clone())
259 })
260 .sum();
261 let three = T::from_constant(3);
262 Point::from(xs / (three * self.signed_area_2x()))
263 }
264
265 pub fn bounding_box(&self) -> (Point<T>, Point<T>)
266 where
267 T: PolygonScalar,
268 {
269 assert!(!self.rings[0].is_empty());
270 let mut min_x = self.points[self.rings[0][0].usize()].x_coord().clone();
271 let mut max_x = self.points[self.rings[0][0].usize()].x_coord().clone();
272 let mut min_y = self.points[self.rings[0][0].usize()].y_coord().clone();
273 let mut max_y = self.points[self.rings[0][0].usize()].y_coord().clone();
274 for pt in self.iter_boundary() {
275 if pt.point().x_coord() < &min_x {
276 min_x = pt.point().x_coord().clone();
277 }
278 if pt.point().x_coord() > &max_x {
279 max_x = pt.point().x_coord().clone();
280 }
281 if pt.point().y_coord() < &min_y {
282 min_y = pt.point().y_coord().clone();
283 }
284 if pt.point().y_coord() > &max_y {
285 max_y = pt.point().y_coord().clone();
286 }
287 }
288 (Point::new([min_x, min_y]), Point::new([max_x, max_y]))
289 }
290
291 pub fn signed_area<F>(&self) -> F
333 where
334 T: PolygonScalar + Into<F>,
335 F: NumOps<F, F> + Sum + FromPrimitive,
336 {
337 self.signed_area_2x::<F>() / F::from_usize(2).unwrap()
338 }
339
340 pub fn signed_area_2x<F>(&self) -> F
392 where
393 T: PolygonScalar + Into<F>,
394 F: NumOps<F, F> + Sum,
395 {
396 self
397 .iter_boundary_edges()
398 .map(|edge| {
399 let p = edge.src;
400 let q = edge.dst;
401 p.array[0].clone().into() * q.array[1].clone().into()
402 - q.array[0].clone().into() * p.array[1].clone().into()
403 })
404 .sum()
405 }
406
407 pub fn orientation(&self) -> Orientation
408 where
409 T: PolygonScalar,
410 {
411 match self.iter_boundary().min_by(|a, b| a.point().cmp(b.point())) {
412 None => Orientation::CoLinear,
413 Some(cursor) => cursor.orientation(),
414 }
415 }
416
417 pub fn point(&self, idx: PointId) -> &Point<T> {
421 &self.points[idx.0]
422 }
423
424 pub fn cursor(&self, idx: PointId) -> Cursor<'_, T> {
429 let ring_id = self.ring_index[idx.0];
430 let position_id = self.position_index[idx.0];
431 Cursor {
432 polygon: self,
433 position: Position {
434 ring_id,
435 position_id,
436 size: self.rings[ring_id.0].len(),
437 },
438 }
439 }
440
441 pub fn boundary_slice(&self) -> &[PointId] {
442 &self.rings[0]
443 }
444
445 pub fn equals(&self, other: &Self) -> bool
446 where
447 T: PolygonScalar,
448 {
449 if self.boundary_slice().len() != other.boundary_slice().len() {
450 return false;
451 }
452 for pos in self.iter_boundary() {
453 let iter = CursorIter {
454 cursor_head: pos,
455 cursor_tail: pos.prev(),
456 exhausted: false,
457 };
458 if iter == other.iter_boundary() {
459 return true;
460 }
461 }
462 false
463 }
464
465 pub fn iter_boundary(&self) -> CursorIter<'_, T> {
466 let root_cursor = Cursor {
467 polygon: self,
468 position: Position {
469 ring_id: RingId(0),
470 position_id: PositionId(0),
471 size: self.rings[0].len(),
472 },
473 };
474 CursorIter {
475 cursor_head: root_cursor,
476 cursor_tail: root_cursor.prev(),
477 exhausted: false,
478 }
479 }
480
481 pub fn iter_boundary_edges(&self) -> EdgeIter<'_, T> {
482 EdgeIter {
483 iter: self.iter_boundary(),
484 }
485 }
486
487 #[must_use]
488 pub fn map_points<F>(mut self, f: F) -> Polygon<T>
489 where
490 T: Clone,
491 F: Fn(Point<T>) -> Point<T>,
492 {
493 for pt in self.iter_mut() {
494 *pt = f(pt.clone())
495 }
496 self
497 }
498
499 pub fn iter(&self) -> Iter<'_, T> {
500 Iter {
501 iter: self.points.iter(),
502 }
503 }
504
505 pub fn iter_mut(&mut self) -> IterMut<'_, T> {
506 IterMut {
507 points: self.points.iter_mut(),
508 }
509 }
510
511 pub fn map<U, F>(self, f: F) -> Polygon<U>
512 where
513 T: Clone,
514 U: Clone,
515 F: Fn(T) -> U + Clone,
516 {
517 let pts = self.points.into_iter().map(|p| p.map(f.clone())).collect();
518 Polygon {
519 points: pts,
520 ring_index: self.ring_index,
521 position_index: self.position_index,
522 rings: self.rings,
523 }
524 }
525
526 pub fn cast<U>(self) -> Polygon<U>
527 where
528 T: Clone + Into<U>,
529 {
530 let pts = self.points.into_iter().map(|p| p.cast()).collect();
531 Polygon {
532 points: pts,
533 ring_index: self.ring_index,
534 position_index: self.position_index,
535 rings: self.rings,
536 }
537 }
538
539 pub fn float(self) -> Polygon<OrderedFloat<f64>>
540 where
541 T: Clone + Into<f64>,
542 {
543 self.map(|v| OrderedFloat(v.into()))
544 }
545
546 pub fn vertices_reverse(&mut self, mut pt1: Position, mut pt2: Position) {
550 loop {
551 self.swap_positions(pt1, pt2);
552 pt1.move_next();
553 if pt1 == pt2 {
554 break;
555 }
556 pt2.move_prev();
557 if pt1 == pt2 {
558 break;
559 }
560 }
561 }
562
563 pub fn vertices_join(&mut self, pt1: Position, mut pt2: Position) {
565 while pt2.prev() != pt1 {
566 let prev = pt2.prev();
567 self.swap_positions(prev, pt2);
568 pt2.position_id = prev.position_id;
569 }
570 }
571
572 pub fn direct(&self, edge: IndexEdge) -> DirectedIndexEdge {
574 let min_cursor = self.cursor(edge.min);
575 let max_cursor = self.cursor(edge.max);
576 if min_cursor.next() == max_cursor {
577 DirectedIndexEdge {
578 src: edge.min,
579 dst: edge.max,
580 }
581 } else if max_cursor.next() == min_cursor {
582 DirectedIndexEdge {
583 src: edge.max,
584 dst: edge.min,
585 }
586 } else {
587 panic!("Edge isn't part of polygon: {:?}", edge)
588 }
589 }
590
591 pub fn ensure_ccw(&mut self) -> Result<(), Error>
592 where
593 T: PolygonScalar,
594 {
595 match self.orientation() {
596 Orientation::CounterClockWise => Ok(()),
597 Orientation::CoLinear => Err(Error::CoLinearViolation),
598 Orientation::ClockWise => {
599 let root_position = Position {
600 ring_id: RingId(0),
601 position_id: PositionId(0),
602 size: self.rings[0].len(),
603 };
604 self.vertices_reverse(root_position, root_position.prev());
605 Ok(())
606 }
607 }
608 }
609
610 fn position_to_point_id(&self, position: Position) -> PointId {
611 self.rings[position.ring_id.0][position.position_id.0]
612 }
613
614 fn swap_positions(&mut self, pa: Position, pb: Position) {
615 assert_eq!(pa.ring_id, pb.ring_id);
616
617 let ring = &mut self.rings[pa.ring_id.0];
618
619 let pa_point_id: PointId = ring[pa.position_id.0];
620 let pb_point_id: PointId = ring[pb.position_id.0];
621 ring.swap(pa.position_id.0, pb.position_id.0);
622 self.position_index.swap(pa_point_id.0, pb_point_id.0);
623 }
624
625 pub fn is_monotone(&self, direction: &Vector<T, 2>) -> bool
626 where
627 T: PolygonScalar,
628 {
629 crate::algorithms::polygonization::monotone::is_monotone(self, direction)
630 }
631}
632
633impl Polygon<OrderedFloat<f64>> {
634 #[must_use]
635 pub fn normalize(&self) -> Polygon<OrderedFloat<f64>> {
637 let (min, max) = self.bounding_box();
638 let [min_x, min_y] = min.array;
639 let [max_x, max_y] = max.array;
640 let width = max_x - min_x;
641 let height = max_y - min_y;
642 let ratio = std::cmp::max(width, height);
643 let centroid = self.centroid();
644 let t = Transform::translate(-Vector::from(centroid));
645 let s = Transform::uniform_scale(ratio.recip());
646 s * t * self
647 }
648}
649
650#[derive(Debug)]
651pub struct Cursor<'a, T> {
652 polygon: &'a Polygon<T>,
653 pub(crate) position: Position,
654}
655
656impl<T: TotalOrd> TotalOrd for Cursor<'_, T> {
657 fn total_cmp(&self, other: &Self) -> Ordering {
658 self.deref().total_cmp(other.deref())
659 }
660}
661
662impl<T> Deref for Cursor<'_, T> {
663 type Target = Point<T>;
664 fn deref(&self) -> &Self::Target {
665 self.point()
666 }
667}
668
669impl<'a, T> PartialEq for Cursor<'a, T> {
670 fn eq(&self, other: &Cursor<'a, T>) -> bool {
671 self.position == other.position
673 }
674}
675
676impl<T> Clone for Cursor<'_, T> {
678 fn clone(&self) -> Self {
679 *self
680 }
681}
682impl<T> Copy for Cursor<'_, T> {}
683
684impl<'a, T> Cursor<'a, T> {
685 pub fn point_id(self) -> PointId {
686 self.polygon.position_to_point_id(self.position)
687 }
688
689 #[must_use]
690 pub fn prev(mut self) -> Cursor<'a, T> {
691 self.move_prev();
692 self
693 }
694
695 #[must_use]
696 pub fn next(mut self) -> Cursor<'a, T> {
697 self.move_next();
698 self
699 }
700
701 pub fn point(self: Cursor<'a, T>) -> &'a Point<T> {
702 &self.polygon.points[self.point_id().0]
703 }
704
705 pub fn move_next(&mut self) {
706 self.position.move_next()
707 }
708
709 pub fn move_prev(&mut self) {
710 self.position.move_prev()
711 }
712
713 pub fn is_ear(&self) -> bool
715 where
716 T: PolygonScalar,
717 {
718 let trig =
719 TriangleView::new_unchecked([self.prev().point(), self.point(), self.next().point()]);
720 if trig.orientation() == Orientation::CounterClockWise {
721 for pt in self.next().next().to(Excluded(self.prev())) {
722 if trig.locate(pt.point()) != PointLocation::Outside {
723 return false;
724 }
725 }
726 true
727 } else {
728 false
729 }
730 }
731
732 pub fn orientation(&self) -> Orientation
733 where
734 T: PolygonScalar,
735 {
736 let p1 = self.prev().point();
737 let p2 = self.point();
738 let p3 = self.next().point();
739 Point::orient(p1, p2, p3)
740 }
741
742 pub fn is_colinear(&self) -> bool
743 where
744 T: PolygonScalar,
745 {
746 self.orientation() == Orientation::CoLinear
747 }
748
749 pub fn to(self: Cursor<'a, T>, end: Bound<Cursor<'a, T>>) -> CursorIter<'a, T> {
750 match end {
752 Bound::Unbounded => CursorIter {
753 cursor_head: self,
754 cursor_tail: self.prev(),
755 exhausted: false,
756 },
757 Bound::Included(end) => CursorIter {
758 cursor_head: self,
759 cursor_tail: end,
760 exhausted: false,
761 },
762 Bound::Excluded(end) => CursorIter {
763 cursor_head: self,
764 cursor_tail: end.prev(),
765 exhausted: false,
766 },
767 }
768 }
769}
770
771#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
772pub struct Position {
773 ring_id: RingId, size: usize, position_id: PositionId, }
777
778impl Position {
779 #[must_use]
780 pub fn prev(mut self) -> Position {
781 self.move_prev();
782 self
783 }
784
785 #[must_use]
786 pub fn next(mut self) -> Position {
787 self.move_next();
788 self
789 }
790
791 pub fn move_next(&mut self) {
792 if self.position_id.0 == self.size - 1 {
793 self.position_id.0 = 0;
794 } else {
795 self.position_id.0 += 1;
796 }
797 }
798
799 pub fn move_prev(&mut self) {
800 if self.position_id.0 == 0 {
801 self.position_id.0 = self.size - 1;
802 } else {
803 self.position_id.0 -= 1;
804 }
805 }
806}
807
808#[cfg(test)]
809pub mod tests {
810 use super::*;
811
812 use crate::testing::*;
813 use ordered_float::NotNan;
814 use proptest::collection::vec;
815 use proptest::prelude::*;
816 use proptest::proptest as proptest_block;
817
818 proptest_block! {
819 #[test]
820 fn random_polygon(poly: Polygon<i8>) {
821 prop_assert_eq!(poly.validate().err(), None);
822 }
823
824 #[test]
825 fn fuzz_new_polygon(pts: Vec<Point<i8>>) {
826 Polygon::new(pts).ok();
828 }
829
830 #[test]
832 fn fuzz_polygon_traits(poly: Polygon<i8>) {
833 let _ = format!("{:?}", &poly);
834 let _ = poly.clone();
835 }
836
837 #[test]
838 fn fuzz_validate(pts: Vec<Point<i8>>) {
839 Polygon::new_unchecked(pts).validate().ok();
841 }
842
843 #[test]
844 fn fuzz_validate_weakly(pts: Vec<Point<i8>>) {
845 Polygon::new_unchecked(pts).validate_weakly().ok();
847 }
848
849 #[test]
850 fn fuzz_centroid(poly in polygon_nn()) {
851 poly.centroid();
852 }
853
854 #[test]
855 fn signed_area_non_negative_prop(poly in polygon_nn()) {
856 prop_assert!( poly.signed_area::<NotNan<f64>>() >= NotNan::zero())
857 }
858
859 #[test]
860 fn fuzz_orientation(pts in vec(any_nn(), 2..100)) {
861 let poly = Polygon::new_unchecked(pts);
862 poly.orientation();
863 }
864
865 #[test]
866 fn fuzz_ensure_ccw(pts in vec(any_nn(), 2..100)) {
867 let mut poly = Polygon::new_unchecked(pts);
868 poly.ensure_ccw().ok();
869 }
870
871 #[test]
872 fn locate_id_prop(poly: Polygon<i8>, origin: Point<i8>) {
873 prop_assert_eq!(
874 locate_by_triangulation(&poly, &origin),
875 poly.locate(&origin)
876 )
877 }
878
879 #[test]
880 fn equals_identity_prop(poly: Polygon<i8>, offset: usize) {
881 let points: Vec<Point<i8>> = poly.iter_boundary().map(|cursor| cursor.point()).cloned().collect();
882 let offset = offset % points.len();
883 let rotated_points: Vec<Point<i8>> = [&points[offset..], &points[0..offset]].concat();
884 prop_assert!(
885 poly.equals(&Polygon::new(rotated_points).unwrap())
886 )
887 }
888 }
889
890 #[test]
903 #[should_panic]
904 fn locate_feature_fixme() {
905 let mut poly: Polygon<i8> = Polygon::new(vec![
906 Point { array: [0, 0] },
907 Point { array: [-1, 127] },
908 Point { array: [-50, 48] },
909 ])
910 .expect("valid polygon");
911 poly.rings.push(vec![PointId(0), PointId(1), PointId(2)]);
913 let origin = Point { array: [79, 108] };
914 poly.locate(&origin);
915 }
916
917 #[test]
918 fn locate_unit_1() {
919 let poly: Polygon<i8> = Polygon::new(vec![
920 Point { array: [0, 0] },
921 Point { array: [-1, 127] },
922 Point { array: [-50, 48] },
923 ])
924 .expect("valid polygon");
925 let origin = Point { array: [79, 108] };
926
927 assert_eq!(poly.locate(&origin), PointLocation::Outside);
928 }
929
930 #[test]
931 fn locate_unit_2() {
932 let poly: Polygon<i8> = Polygon::new(vec![
933 Point { array: [-9, 83] },
934 Point { array: [-28, 88] },
935 Point { array: [-9, -75] },
936 Point { array: [-2, -6] },
937 Point { array: [-2, 11] },
938 ])
939 .expect("valid polygon");
940 let origin = Point { array: [-9, 0] };
941
942 assert_eq!(
943 locate_by_triangulation(&poly, &origin),
944 poly.locate(&origin)
945 );
946 }
947
948 fn locate_by_triangulation<T>(poly: &Polygon<T>, origin: &Point<T>) -> PointLocation
951 where
952 T: PolygonScalar,
953 {
954 for edge in poly.iter_boundary_edges() {
955 if edge.contains(origin) {
956 return PointLocation::OnBoundary;
957 }
958 }
959 for (a, b, c) in poly.triangulate() {
960 let trig = TriangleView::new([&a, &b, &c]).expect("valid triangle");
961 if trig.locate(origin) != PointLocation::Outside {
962 return PointLocation::Inside;
963 }
964 }
965 PointLocation::Outside
966 }
967}