iron_shapes/simple_polygon.rs
1// Copyright (c) 2018-2020 Thomas Kramer.
2// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! This module contains data types and functions for basic polygons without holes.
7
8use crate::CoordinateType;
9
10use crate::edge::Edge;
11use crate::point::Point;
12use crate::rect::Rect;
13
14pub use crate::traits::{DoubledOrientedArea, MapPointwise, TryBoundingBox, WindingNumber};
15
16use crate::types::*;
17
18use crate::traits::TryCastCoord;
19use num_traits::{Num, NumCast};
20use std::cmp::{Ord, PartialEq};
21use std::iter::FromIterator;
22use std::slice::Iter;
23
24/// A `SimplePolygon` is a polygon defined by vertices. It does not contain holes but can be
25/// self-intersecting.
26///
27/// TODO: Implement `Deref` for accessing the vertices.
28#[derive(Clone, Debug, Hash, Eq, PartialEq)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30pub struct SimplePolygon<T> {
31 /// Vertices of the polygon.
32 points: Vec<Point<T>>,
33 /// Set to `true` only if the vertices are normalized.
34 normalized: bool,
35}
36
37/// Shorthand notation for creating a simple polygon.
38/// # Example
39/// ```
40/// # #[macro_use]
41/// # extern crate iron_shapes;
42/// # fn main() {
43/// use iron_shapes::prelude::*;
44/// let p = simple_polygon!((0, 0), (1, 0), (1, 1));
45/// assert_eq!(p, SimplePolygon::from(vec![(0, 0), (1, 0), (1, 1)]));
46/// # }
47/// ```
48#[macro_export]
49macro_rules! simple_polygon {
50 ($($x:expr),*) => {SimplePolygon::new((vec![$($x.into()),*]))}
51}
52
53impl<T> SimplePolygon<T> {
54 /// Create a new polygon from a list of points.
55 /// The points are taken as they are, without reordering
56 /// or simplification.
57 pub fn new_raw(points: Vec<Point<T>>) -> Self {
58 Self {
59 points,
60 normalized: false,
61 }
62 }
63
64 /// Create empty polygon without any vertices.
65 pub fn empty() -> Self {
66 SimplePolygon {
67 points: Vec::new(),
68 normalized: true,
69 }
70 }
71
72 /// Get the number of vertices.
73 pub fn len(&self) -> usize {
74 self.points.len()
75 }
76
77 /// Check if polygon has no vertices.
78 pub fn is_empty(&self) -> bool {
79 self.points.is_empty()
80 }
81
82 /// Shortcut for `self.points.iter()`.
83 pub fn iter(&self) -> Iter<Point<T>> {
84 self.points.iter()
85 }
86
87 /// Access the inner list of points.
88 pub fn points(&self) -> &[Point<T>] {
89 &self.points
90 }
91}
92
93impl<T: PartialOrd> SimplePolygon<T> {
94 /// Create a new polygon from a list of points.
95 /// The polygon is normalized by rotating the points.
96 pub fn new(points: Vec<Point<T>>) -> Self {
97 Self::new_raw(points).normalized()
98 }
99
100 /// Mutably access the inner list of points.
101 /// If the polygon was normalized, then the modified list of points will be normalized again.
102 pub fn with_points_mut<R>(&mut self, f: impl FnOnce(&mut Vec<Point<T>>) -> R) -> R {
103 let normalize = self.normalized;
104 self.normalized = false;
105 let r = f(&mut self.points);
106 if normalize {
107 self.normalize()
108 }
109 r
110 }
111}
112
113impl<T: PartialOrd> SimplePolygon<T> {
114 /// Rotate the vertices to get the lexicographically smallest polygon.
115 /// Does not change the orientation.
116 pub fn normalize(&mut self) {
117 if !self.normalized {
118 let rotated = |rot: usize| self.points.iter().cycle().skip(rot).take(self.points.len());
119 let best_rot = (0..self.points.len()).skip(1).fold(0, |best_rot, rot| {
120 if rotated(rot).lt(rotated(best_rot)) {
121 rot
122 } else {
123 best_rot
124 }
125 });
126 self.points.rotate_left(best_rot);
127 }
128 self.normalized = true;
129 }
130
131 /// Rotate the vertices to get the lexicographically smallest polygon.
132 /// Does not change the orientation.
133 pub fn normalized(mut self) -> Self {
134 self.normalize();
135 self
136 }
137}
138
139impl<T: PartialEq> SimplePolygon<T> {
140 /// Check if polygons can be made equal by rotating their vertices.
141 pub fn normalized_eq(&self, other: &Self) -> bool {
142 if self.len() != other.len() {
143 false
144 } else {
145 (0..self.points.len()).any(|rot| {
146 let rotated = self.points.iter().cycle().skip(rot).take(self.points.len());
147 rotated.eq(other.points.iter())
148 })
149 }
150 }
151}
152
153impl<T: Copy> SimplePolygon<T> {
154 /// Create a new simple polygon from a rectangle.
155 pub fn from_rect(rect: &Rect<T>) -> Self {
156 Self {
157 points: vec![
158 rect.lower_left(),
159 rect.lower_right(),
160 rect.upper_right(),
161 rect.upper_left(),
162 ],
163 normalized: true,
164 }
165 }
166}
167
168#[test]
169fn test_from_rect() {
170 let r = Rect::new((1, 2), (3, 4));
171 assert_eq!(
172 SimplePolygon::from_rect(&r),
173 SimplePolygon::from_rect(&r).normalized()
174 );
175}
176
177impl<T> SimplePolygon<T> {
178 /// Get index of previous vertex.
179 fn prev(&self, i: usize) -> usize {
180 match i {
181 0 => self.points.len() - 1,
182 x => x - 1,
183 }
184 }
185
186 /// Get index of next vertex.
187 fn next(&self, i: usize) -> usize {
188 match i {
189 _ if i == self.points.len() - 1 => 0,
190 x => x + 1,
191 }
192 }
193}
194
195impl<T: Copy> SimplePolygon<T> {
196 /// Get an iterator over the polygon points.
197 /// Point 0 is appended to the end to close the cycle.
198 fn iter_cycle(&self) -> impl Iterator<Item = &Point<T>> {
199 self.points.iter().cycle().take(self.points.len() + 1)
200 }
201
202 /// Get all exterior edges of the polygon.
203 /// # Examples
204 ///
205 /// ```
206 /// use iron_shapes::simple_polygon::SimplePolygon;
207 /// use iron_shapes::edge::Edge;
208 /// let coords = vec![(0, 0), (1, 0)];
209 ///
210 /// let poly = SimplePolygon::from(coords);
211 ///
212 /// assert_eq!(poly.edges(), vec![Edge::new((0, 0), (1, 0)), Edge::new((1, 0), (0, 0))]);
213 ///
214 /// ```
215 pub fn edges(&self) -> Vec<Edge<T>> {
216 self.edges_iter().collect()
217 }
218
219 /// Iterate over all edges.
220 pub fn edges_iter(&self) -> impl Iterator<Item = Edge<T>> + '_ {
221 self.iter()
222 .zip(self.iter_cycle().skip(1))
223 .map(|(a, b)| Edge::new(a, b))
224 }
225}
226
227impl<T: CoordinateType> SimplePolygon<T> {
228 /// Normalize the points of the polygon such that they are arranged counter-clock-wise.
229 ///
230 /// After normalizing, `SimplePolygon::area_doubled_oriented()` will return a semi-positive value.
231 ///
232 /// For self-intersecting polygons, the orientation is not clearly defined. For example an `8` shape
233 /// has not orientation.
234 /// Here, the oriented area is used to define the orientation.
235 pub fn normalize_orientation<Area>(&mut self)
236 where
237 Area: Num + PartialOrd + From<T>,
238 {
239 if self.orientation::<Area>() != Orientation::CounterClockWise {
240 self.points.reverse();
241 }
242 }
243
244 /// Call `normalize_orientation()` while taking ownership and returning the result.
245 pub fn normalized_orientation<Area>(mut self) -> Self
246 where
247 Area: Num + PartialOrd + From<T>,
248 {
249 self.normalize_orientation::<Area>();
250 self
251 }
252
253 /// Get the orientation of the polygon.
254 /// The orientation is defined by the oriented area. A polygon with a positive area
255 /// is oriented counter-clock-wise, otherwise it is oriented clock-wise.
256 ///
257 /// # Examples
258 ///
259 /// ```
260 /// use iron_shapes::simple_polygon::SimplePolygon;
261 /// use iron_shapes::point::Point;
262 /// use iron_shapes::types::Orientation;
263 /// let coords = vec![(0, 0), (3, 0), (3, 1)];
264 ///
265 /// let poly = SimplePolygon::from(coords);
266 ///
267 /// assert_eq!(poly.orientation::<i64>(), Orientation::CounterClockWise);
268 ///
269 /// ```
270 pub fn orientation<Area>(&self) -> Orientation
271 where
272 Area: Num + From<T> + PartialOrd,
273 {
274 // Find the orientation based the polygon area.
275 let area2: Area = self.area_doubled_oriented();
276
277 if area2 > Area::zero() {
278 Orientation::CounterClockWise
279 } else if area2 < Area::zero() {
280 Orientation::ClockWise
281 } else {
282 debug_assert!(area2 == Area::zero());
283 Orientation::Straight
284 }
285 }
286
287 /// Get the convex hull of the polygon.
288 ///
289 /// Implements Andrew's Monotone Chain algorithm.
290 /// See: <http://geomalgorithms.com/a10-_hull-1.html>
291 pub fn convex_hull(&self) -> SimplePolygon<T>
292 where
293 T: Ord,
294 {
295 crate::algorithms::convex_hull::convex_hull(self.points.clone())
296 }
297
298 /// Test if all edges are parallel to the x or y axis.
299 pub fn is_rectilinear(&self) -> bool {
300 self.edges_iter().all(|e| e.is_rectilinear())
301 }
302
303 /// Get the vertex with lowest x-coordinate. Prefer lower y-coordinates to break ties.
304 ///
305 /// # Examples
306 ///
307 /// ```
308 /// use iron_shapes::simple_polygon::SimplePolygon;
309 /// use iron_shapes::point::Point;
310 /// let coords = vec![(0, 0), (1, 0), (-1, 2), (-1, 1)];
311 ///
312 /// let poly = SimplePolygon::from(coords);
313 ///
314 /// assert_eq!(poly.lower_left_vertex(), Point::new(-1, 1));
315 ///
316 /// ```
317 pub fn lower_left_vertex(&self) -> Point<T> {
318 debug_assert!(!self.points.is_empty());
319
320 self.lower_left_vertex_with_index().1
321 }
322
323 /// Get the vertex with lowest x-coordinate and its index.
324 /// Prefer lower y-coordinates to break ties.
325 fn lower_left_vertex_with_index(&self) -> (usize, Point<T>) {
326 debug_assert!(!self.points.is_empty());
327
328 // Find minimum.
329 let min = self
330 .points
331 .iter()
332 .enumerate()
333 .min_by(|(_, &p1), (_, &p2)| p1.partial_cmp(&p2).unwrap());
334 let (idx, point) = min.unwrap();
335
336 (idx, *point)
337 }
338}
339
340impl<T> WindingNumber<T> for SimplePolygon<T>
341where
342 T: CoordinateType,
343{
344 /// Calculate the winding number of the polygon around this point.
345 ///
346 /// TODO: Define how point on edges and vertices is handled.
347 ///
348 /// See: <http://geomalgorithms.com/a03-_inclusion.html>
349 fn winding_number(&self, point: Point<T>) -> isize {
350 let edges = self.edges();
351 let mut winding_number = 0isize;
352
353 // Edge Crossing Rules
354 //
355 // 1. an upward edge includes its starting endpoint, and excludes its final endpoint;
356 // 2. a downward edge excludes its starting endpoint, and includes its final endpoint;
357 // 3. horizontal edges are excluded
358 // 4. the edge-ray intersection point must be strictly right of the point P.
359
360 for e in edges {
361 if e.start.y <= point.y {
362 // Crosses upward?
363 if e.end.y > point.y {
364 // Crosses really upward?
365 // Yes, crosses upward.
366 if e.side_of(point) == Side::Left {
367 winding_number += 1;
368 }
369 }
370 } else if e.end.y <= point.y {
371 // Crosses downward?
372 // Yes, crosses downward.
373 // `e.start.y > point.y` needs not to be checked anymore.
374 if e.side_of(point) == Side::Right {
375 winding_number -= 1;
376 }
377 }
378 }
379
380 winding_number
381 }
382}
383
384/// Create a polygon from a type that is convertible into an iterator of values convertible to `Point`s.
385impl<I, T, P> From<I> for SimplePolygon<T>
386where
387 T: Copy + PartialOrd,
388 I: IntoIterator<Item = P>,
389 Point<T>: From<P>,
390{
391 fn from(iter: I) -> Self {
392 let points: Vec<Point<T>> = iter.into_iter().map(|x| x.into()).collect();
393
394 SimplePolygon::new(points)
395 }
396}
397
398// impl<T: CoordinateType> From<&Rect<T>> for SimplePolygon<T> {
399// fn from(rect: &Rect<T>) -> Self {
400// Self::new(
401// vec![rect.lower_left(), rect.lower_right(),
402// rect.upper_right(), rect.upper_left()]
403// )
404// }
405// }
406
407//
408// /// Create a polygon from a `Vec` of values convertible to `Point`s.
409// impl<'a, T, P> From<&'a Vec<P>> for SimplePolygon<T>
410// where T: CoordinateType,
411// Point<T>: From<&'a P>
412// {
413// fn from(vec: &'a Vec<P>) -> Self {
414// let points: Vec<Point<T>> = vec.into_iter().map(
415// |x| x.into()
416// ).collect();
417//
418// SimplePolygon { points }
419// }
420// }
421//
422// /// Create a polygon from a `Vec` of values convertible to `Point`s.
423// impl<T, P> From<Vec<P>> for SimplePolygon<T>
424// where T: CoordinateType,
425// Point<T>: From<P>
426// {
427// fn from(vec: Vec<P>) -> Self {
428// let points: Vec<Point<T>> = vec.into_iter().map(
429// |x| x.into()
430// ).collect();
431//
432// SimplePolygon { points }
433// }
434// }
435
436/// Create a polygon from a iterator of values convertible to `Point`s.
437impl<T, P> FromIterator<P> for SimplePolygon<T>
438where
439 T: Copy,
440 P: Into<Point<T>>,
441{
442 fn from_iter<I>(iter: I) -> Self
443 where
444 I: IntoIterator<Item = P>,
445 {
446 let points: Vec<Point<T>> = iter.into_iter().map(|x| x.into()).collect();
447
448 assert!(
449 points.len() >= 2,
450 "A polygon needs to have at least two points."
451 );
452
453 Self::new_raw(points)
454 }
455}
456
457impl<'a, T> IntoIterator for &'a SimplePolygon<T> {
458 type Item = &'a Point<T>;
459
460 type IntoIter = std::slice::Iter<'a, Point<T>>;
461
462 fn into_iter(self) -> Self::IntoIter {
463 self.points.iter()
464 }
465}
466// impl<T> IntoIterator for SimplePolygon<T> {
467// type Item = Point<T>;
468
469// type IntoIter = std::vec::IntoIter<Point<T>>;
470
471// fn into_iter(self) -> Self::IntoIter {
472// self.points.into_iter()
473// }
474// }
475
476impl<T> TryBoundingBox<T> for SimplePolygon<T>
477where
478 T: Copy + PartialOrd,
479{
480 fn try_bounding_box(&self) -> Option<Rect<T>> {
481 if !self.is_empty() {
482 let mut x_min = self.points[0].x;
483 let mut x_max = x_min;
484 let mut y_min = self.points[0].y;
485 let mut y_max = y_min;
486
487 for p in self.iter().skip(1) {
488 if p.x < x_min {
489 x_min = p.x;
490 }
491 if p.x > x_max {
492 x_max = p.x;
493 }
494 if p.y < y_min {
495 y_min = p.y;
496 }
497 if p.y > y_max {
498 y_max = p.y;
499 }
500 }
501
502 Some(Rect::new((x_min, y_min), (x_max, y_max)))
503 } else {
504 None
505 }
506 }
507}
508
509impl<T> MapPointwise<T> for SimplePolygon<T>
510where
511 T: CoordinateType,
512{
513 fn transform<F: Fn(Point<T>) -> Point<T>>(&self, tf: F) -> Self {
514 let points = self.points.iter().map(|&p| tf(p)).collect();
515
516 let mut new = SimplePolygon::new_raw(points);
517
518 // Make sure the polygon is oriented the same way as before.
519 // TODO: Could be done more efficiently if the magnification/mirroring of the transformation is known.
520 if new.orientation::<T>() != self.orientation::<T>() {
521 new.points.reverse()
522 }
523
524 new.normalized()
525 }
526}
527
528impl<A, T> DoubledOrientedArea<A> for SimplePolygon<T>
529where
530 T: CoordinateType,
531 A: Num + From<T>,
532{
533 /// Calculates the doubled oriented area.
534 ///
535 /// Using doubled area allows to compute in the integers because the area
536 /// of a polygon with integer coordinates is either integer or half-integer.
537 ///
538 /// The area will be positive if the vertices are listed counter-clockwise,
539 /// negative otherwise.
540 ///
541 /// Complexity: O(n)
542 ///
543 /// # Examples
544 ///
545 /// ```
546 /// use iron_shapes::traits::DoubledOrientedArea;
547 /// use iron_shapes::simple_polygon::SimplePolygon;
548 /// let coords = vec![(0, 0), (3, 0), (3, 1)];
549 ///
550 /// let poly = SimplePolygon::from(coords);
551 ///
552 /// let area: i64 = poly.area_doubled_oriented();
553 /// assert_eq!(area, 3);
554 ///
555 /// ```
556 fn area_doubled_oriented(&self) -> A {
557 let mut sum = A::zero();
558 let ps = &self.points;
559 for i in 0..ps.len() {
560 let dy = ps[self.next(i)].y - ps[self.prev(i)].y;
561 let x = ps[i].x;
562 sum = sum + A::from(x) * A::from(dy);
563 }
564 sum
565 }
566}
567
568impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst>
569 for SimplePolygon<T>
570{
571 type Output = SimplePolygon<Dst>;
572
573 fn try_cast(&self) -> Option<Self::Output> {
574 let new_points: Option<Vec<_>> = self.points.iter().map(|p| p.try_cast()).collect();
575
576 new_points.map(|p| SimplePolygon::new(p))
577 }
578}
579
580/// Two simple polygons should be the same even if points are shifted cyclical.
581#[test]
582fn test_normalized_eq() {
583 let p1 = simple_polygon!((0, 0), (0, 1), (1, 1), (1, 0));
584 let p2 = simple_polygon!((0, 0), (0, 1), (1, 1), (1, 0));
585 assert!(p1.normalized_eq(&p2));
586
587 let p2 = simple_polygon!((0, 1), (1, 1), (1, 0), (0, 0));
588 assert!(p1.normalized_eq(&p2));
589}
590
591#[test]
592fn test_normalize() {
593 let p1 = simple_polygon!((0, 0), (0, 1), (1, 1), (1, 0));
594 let p2 = simple_polygon!((0, 1), (1, 1), (1, 0), (0, 0));
595
596 assert_eq!(p1.normalized(), p2.normalized());
597}
598
599/// Simple sanity check for computation of bounding box.
600#[test]
601fn test_bounding_box() {
602 let p = simple_polygon!((0, 0), (0, 1), (1, 1));
603 assert_eq!(p.try_bounding_box(), Some(Rect::new((0, 0), (1, 1))));
604}