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}