iron_shapes/
multi_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//! Multi-polygons are a set of multiple polygons.
7
8use crate::CoordinateType;
9
10use crate::point::Point;
11use crate::polygon::Polygon;
12
13pub use crate::traits::{BoundingBox, DoubledOrientedArea, MapPointwise, WindingNumber};
14
15use crate::edge::Edge;
16use crate::prelude::Rect;
17use crate::traits::{TryBoundingBox, TryIntoBoundingBox};
18use std::iter::FromIterator;
19
20/// A `MultiPolygon` is a list of polygons. There is no restrictions on the polygons (they can be
21/// intersecting, empty, etc.).
22#[derive(Default, Clone, Debug, Hash)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24pub struct MultiPolygon<T> {
25    /// The list of polygons which defines the content of this multi polygon.
26    pub polygons: Vec<Polygon<T>>,
27}
28
29impl<T> MultiPolygon<T> {
30    /// Create an empty set of polygons.
31    pub fn new() -> Self {
32        Self { polygons: vec![] }
33    }
34
35    /// Create a `MultiPolygon` from a vector of `Polygon`s.
36    pub fn from_polygons(polygons: Vec<Polygon<T>>) -> Self {
37        MultiPolygon { polygons }
38    }
39
40    /// Return the number of polygons.
41    pub fn len(&self) -> usize {
42        self.polygons.len()
43    }
44
45    /// Check if polygon is empty.
46    pub fn is_empty(&self) -> bool {
47        self.polygons.is_empty()
48    }
49
50    /// Insert a polygon into the region.
51    pub fn insert(&mut self, polygon: Polygon<T>) {
52        self.polygons.push(polygon)
53    }
54}
55
56impl<T: Copy> MultiPolygon<T> {
57    /// Iterate over all edges of the polygons including holes.
58    pub fn all_edges_iter(&self) -> impl Iterator<Item = Edge<T>> + '_ {
59        self.polygons.iter().flat_map(|p| p.all_edges_iter())
60    }
61}
62
63impl<T> WindingNumber<T> for MultiPolygon<T>
64where
65    T: CoordinateType,
66{
67    fn winding_number(&self, point: Point<T>) -> isize {
68        self.polygons.iter().map(|p| p.winding_number(point)).sum()
69    }
70}
71
72impl<T> MapPointwise<T> for MultiPolygon<T>
73where
74    T: CoordinateType,
75{
76    fn transform<F: Fn(Point<T>) -> Point<T>>(&self, tf: F) -> Self {
77        MultiPolygon::from_polygons(self.polygons.iter().map(|p| p.transform(&tf)).collect())
78    }
79}
80
81impl<T, IP: Into<Polygon<T>>> From<IP> for MultiPolygon<T> {
82    fn from(x: IP) -> Self {
83        MultiPolygon::from_polygons(vec![x.into()])
84    }
85}
86
87impl<T> From<Vec<Polygon<T>>> for MultiPolygon<T> {
88    fn from(polygons: Vec<Polygon<T>>) -> Self {
89        MultiPolygon { polygons }
90    }
91}
92
93impl<T, IP: Into<Polygon<T>>> FromIterator<IP> for MultiPolygon<T> {
94    fn from_iter<I: IntoIterator<Item = IP>>(iter: I) -> Self {
95        MultiPolygon::from_polygons(iter.into_iter().map(|p| p.into()).collect())
96    }
97}
98
99impl<T> IntoIterator for MultiPolygon<T> {
100    type Item = Polygon<T>;
101    type IntoIter = ::std::vec::IntoIter<Polygon<T>>;
102
103    fn into_iter(self) -> Self::IntoIter {
104        self.polygons.into_iter()
105    }
106}
107
108impl<T: CoordinateType> TryBoundingBox<T> for MultiPolygon<T> {
109    fn try_bounding_box(&self) -> Option<Rect<T>> {
110        self.polygons.iter().try_into_bounding_box()
111    }
112}
113
114//impl<'a, T: CoordinateType> IntoIterator for &'a MultiPolygon<T> {
115//    type Item = &'a Polygon<T>;
116//    type IntoIter = ::std::vec::IntoIter<&'a Polygon<T>>;
117//
118//    fn into_iter(self) -> Self::IntoIter {
119//        let p = &self.polygons;
120//        p.into_iter()
121//    }
122//}