physdes/
polygon.rs

1use super::{Point, Vector2};
2
3use num_traits::{Num, Zero};
4
5/// The `Polygon` struct represents a polygon with a generic type `T` and contains an origin point and a
6/// vector of 2D vectors.
7///
8/// Properties:
9///
10/// * `origin`: The origin property represents the starting point or the reference point of the polygon.
11/// It is of type Point<T>, where T is the generic type parameter of the Polygon struct.
12/// * `vecs`: vecs is a vector that stores the vectors representing the sides of the polygon. Each
13/// vector represents the direction and magnitude of a side of the polygon.
14pub struct Polygon<T> {
15    pub origin: Point<T>,
16    pub vecs: Vec<Vector2<T>>,
17}
18
19impl<T: Clone + Num + Copy> Polygon<T> {
20    /// The `new` function constructs a new `Polygon` object by calculating the vectors between each
21    /// coordinate and the origin.
22    ///
23    /// Arguments:
24    ///
25    /// * `coords`: An array slice of Point<T> objects, representing the coordinates of the polygon. The
26    /// first element of the slice is considered the origin of the polygon, and the remaining elements
27    /// are treated as vectors relative to the origin.
28    ///
29    /// Returns:
30    ///
31    /// The `new` function returns a new instance of the `Polygon` object.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use physdes::point::Point;
37    /// use physdes::polygon::Polygon;
38    /// use physdes::vector2::Vector2;
39    ///
40    /// let p1 = Point::new(1, 1);
41    /// let p2 = Point::new(2, 2);
42    /// let p3 = Point::new(3, 3);
43    /// let p4 = Point::new(4, 4);
44    /// let p5 = Point::new(5, 5);
45    /// let poly = Polygon::new(&[p1, p2, p3, p4, p5]);
46    /// assert_eq!(poly.origin, Point::new(1, 1));
47    /// assert_eq!(poly.vecs.len(), 4);
48    /// assert_eq!(poly.vecs[0], Vector2::new(1, 1));
49    /// ```
50    pub fn new(coords: &[Point<T>]) -> Self {
51        let origin = coords[0];
52        let mut vecs = vec![];
53        for pt in coords.iter().skip(1) {
54            vecs.push(pt - origin);
55        }
56        Polygon { origin, vecs }
57    }
58
59    /// The `signed_area_x2` function calculates the signed area multiplied by 2 of a polygon.
60    ///
61    /// Returns:
62    ///
63    /// The function `signed_area_x2` returns the signed area multiplied by 2.
64    /// Signed area x 2
65    ///
66    /// # Panics
67    ///
68    /// Panics if n < 2
69    ///
70    /// Returns:
71    ///
72    /// The `new` function returns a new instance of the `Polygon` object.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// use physdes::point::Point;
78    /// use physdes::polygon::Polygon;
79    /// use physdes::vector2::Vector2;
80    ///
81    /// let p1 = Point::new(1, 1);
82    /// let p2 = Point::new(2, 2);
83    /// let p3 = Point::new(3, 3);
84    /// let p4 = Point::new(4, 4);
85    /// let p5 = Point::new(5, 5);
86    /// let poly = Polygon::new(&[p1, p2, p3, p4, p5]);
87    /// assert_eq!(poly.signed_area_x2(), 0);
88    /// ```
89    pub fn signed_area_x2(&self) -> T {
90        let vs = &self.vecs;
91        let n = vs.len();
92        assert!(n >= 2);
93        let mut res = vs[0].x_ * vs[1].y_ - vs[n - 1].x_ * vs[n - 2].y_;
94        for i in 1..n - 1 {
95            res = res + vs[i].x_ * (vs[i + 1].y_ - vs[i - 1].y_);
96        }
97        res
98    }
99
100    /// The function `lb` returns a `Point<T>`.
101    ///
102    /// Returns:
103    ///
104    /// a value of type `Point<T>`.
105    pub fn lb(&self) -> Point<T> {
106        unimplemented!()
107    }
108
109    /// The function `ub` returns a `Point<T>`.
110    ///
111    /// Returns:
112    ///
113    /// a value of type `Point<T>`.
114    pub fn ub(&self) -> Point<T> {
115        unimplemented!()
116    }
117}
118
119impl<T: Clone + Num + Ord + Copy> Polygon<T> {
120    /// The function `create_mono_polygon` takes a set of points and a function, and returns a sorted
121    /// list of points that form a monotonic polygon.
122    ///
123    /// Arguments:
124    ///
125    /// * `pointset`: pointset is a slice of Point<T> objects, representing a set of points in a 2D
126    /// space.
127    /// * `f`: The parameter `f` is a closure that takes a reference to a `Point<T>` and returns a tuple
128    /// `(T, T)`. It is used to determine the ordering of the points in the polygon.
129    ///
130    /// Returns:
131    ///
132    /// The function `create_mono_polygon` returns a `Vec<Point<T>>`, which is a vector of points.
133    /// Create a x-mono Polygon object
134    pub fn create_mono_polygon<F>(pointset: &[Point<T>], f: F) -> Vec<Point<T>>
135    where
136        F: Fn(&&Point<T>) -> (T, T),
137    {
138        let max_pt = pointset.iter().max_by_key(&f).unwrap();
139        let min_pt = pointset.iter().min_by_key(&f).unwrap();
140        let d = max_pt - min_pt;
141        let (mut lst1, mut lst2): (Vec<Point<T>>, Vec<Point<T>>) = pointset
142            .iter()
143            .partition(|&a| d.cross(&(a - min_pt)) <= Zero::zero());
144        lst1.sort_by_key(|a| f(&a));
145        lst2.sort_by_key(|a| f(&a));
146        lst2.reverse();
147        lst1.append(&mut lst2);
148        lst1
149    }
150
151    /// The function `create_xmono_polygon` creates a monotone polygon object using a given point set,
152    /// with the x-coordinate as the primary sorting criterion.
153    ///
154    /// Arguments:
155    ///
156    /// * `pointset`: A slice of Point objects, where each Point object has xcoord and ycoord
157    /// properties.
158    ///
159    /// Returns:
160    ///
161    /// The function `create_xmono_polygon` returns a vector of `Point<T>`.
162    #[inline]
163    pub fn create_xmono_polygon(pointset: &[Point<T>]) -> Vec<Point<T>> {
164        Self::create_mono_polygon(pointset, |a| (a.xcoord, a.ycoord))
165    }
166
167    /// The function creates a y-monotone polygon object using a given point set.
168    ///
169    /// Arguments:
170    ///
171    /// * `pointset`: A slice of Point objects, where each Point object has two fields: ycoord and
172    /// xcoord.
173    ///
174    /// Returns:
175    ///
176    /// The function `create_ymono_polygon` returns a vector of `Point<T>` objects.
177    #[inline]
178    pub fn create_ymono_polygon(pointset: &[Point<T>]) -> Vec<Point<T>> {
179        Self::create_mono_polygon(pointset, |a| (a.ycoord, a.xcoord))
180    }
181
182    /// The function `point_in_polygon` determines if a given point is within a polygon.
183    ///
184    /// The code below is from Wm. Randolph Franklin <wrf@ecse.rpi.edu>
185    /// (see URL below) with some minor modifications for integer. It returns
186    /// true for strictly interior points, false for strictly exterior, and ub
187    /// for points on the boundary.  The boundary behavior is complex but
188    /// determined; in particular, for a partition of a region into polygons,
189    /// each Point is "in" exactly one Polygon.
190    /// (See p.243 of [O'Rourke (C)] for a discussion of boundary behavior.)
191    ///
192    /// See <http://www.faqs.org/faqs/graphics/algorithms-faq/> Subject 2.03
193    ///
194    /// Arguments:
195    ///
196    /// * `pointset`: A slice of points representing the vertices of the polygon. Each point has x and y
197    /// coordinates.
198    /// * `q`: The parameter `q` represents the point that we want to determine if it is within the
199    /// polygon or not.
200    ///
201    /// Returns:
202    ///
203    /// The function `point_in_polygon` returns a boolean value. It returns `true` if the given point
204    /// `q` is strictly inside the polygon defined by the `pointset` array, `false` if the point is
205    /// strictly outside the polygon, and `ub` (undefined behavior) if the point lies on the boundary of
206    /// the polygon.
207    pub fn point_in_polygon(pointset: &[Point<T>], q: &Point<T>) -> bool {
208        let n = pointset.len();
209        let mut p0 = &pointset[n - 1];
210        let mut c = false;
211        for p1 in pointset.iter() {
212            if (p1.ycoord <= q.ycoord && q.ycoord < p0.ycoord)
213                || (p0.ycoord <= q.ycoord && q.ycoord < p1.ycoord)
214            {
215                let d = (q - p0).cross(&(p1 - p0));
216                if p1.ycoord > p0.ycoord {
217                    if d < Zero::zero() {
218                        c = !c;
219                    }
220                } else {
221                    // v1.ycoord < v0.ycoord
222                    if d > Zero::zero() {
223                        c = !c;
224                    }
225                }
226            }
227            p0 = p1;
228        }
229        c
230    }
231}
232
233#[cfg(test)]
234mod test {
235    #![allow(non_upper_case_globals)]
236
237    use super::*;
238
239    #[test]
240    fn test_ymono_polygon() {
241        let coords = [
242            (-2, 2),
243            (0, -1),
244            (-5, 1),
245            (-2, 4),
246            (0, -4),
247            (-4, 3),
248            (-6, -2),
249            (5, 1),
250            (2, 2),
251            (3, -3),
252            (-3, -3),
253            (3, 3),
254            (-3, -4),
255            (1, 4),
256        ];
257        let mut pointset = vec![];
258        for (x, y) in coords.iter() {
259            pointset.push(Point::<i32>::new(*x, *y));
260        }
261        let pointset = Polygon::<i32>::create_ymono_polygon(&pointset);
262        for p in pointset.iter() {
263            print!("({}, {}) ", p.xcoord, p.ycoord);
264        }
265        let poly = Polygon::<i32>::new(&pointset);
266        assert_eq!(poly.signed_area_x2(), 102);
267    }
268
269    #[test]
270    fn test_xmono_polygon() {
271        let coords = [
272            (-2, 2),
273            (0, -1),
274            (-5, 1),
275            (-2, 4),
276            (0, -4),
277            (-4, 3),
278            (-6, -2),
279            (5, 1),
280            (2, 2),
281            (3, -3),
282            (-3, -3),
283            (3, 3),
284            (-3, -4),
285            (1, 4),
286        ];
287        let mut pointset = vec![];
288        for (x, y) in coords.iter() {
289            pointset.push(Point::<i32>::new(*x, *y));
290        }
291        let pointset = Polygon::<i32>::create_xmono_polygon(&pointset);
292        for p in pointset.iter() {
293            print!("({}, {}) ", p.xcoord, p.ycoord);
294        }
295        let poly = Polygon::<i32>::new(&pointset);
296        assert_eq!(poly.signed_area_x2(), 111);
297    }
298}