physdes/
polygon.rs

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