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