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}