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}