1use crate::model::geometry::primitives::{
2 AbstractSurface, AsAbstractSurface, AsAbstractSurfaceMut, RingProperty, TriangulatedSurface,
3};
4use crate::model::geometry::{DirectPosition, Envelope};
5use crate::util::plane::Plane;
6use crate::util::triangulate::triangulate;
7use crate::{Error, impl_abstract_surface_traits};
8use nalgebra::{Isometry3, Vector3};
9use rayon::prelude::*;
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct Polygon {
13 pub(crate) abstract_surface: AbstractSurface,
14 exterior: Option<RingProperty>,
15 interior: Vec<RingProperty>,
16}
17
18impl Polygon {
19 pub fn new(
20 exterior: Option<RingProperty>,
21 interior: impl IntoIterator<Item = RingProperty>,
22 ) -> Result<Self, Error> {
23 Ok(Self {
24 abstract_surface: AbstractSurface::default(),
25 exterior,
26 interior: interior.into_iter().collect(),
27 })
28 }
29
30 pub fn exterior(&self) -> Option<&RingProperty> {
31 self.exterior.as_ref()
32 }
33
34 pub fn set_exterior(&mut self, exterior: Option<RingProperty>) {
35 self.exterior = exterior;
36 }
37
38 pub fn interior(&self) -> &[RingProperty] {
39 &self.interior
40 }
41
42 pub fn set_interior(&mut self, interior: Vec<RingProperty>) {
43 self.interior = interior;
44 }
45
46 pub fn push_interior(&mut self, ring: RingProperty) {
47 self.interior.push(ring);
48 }
49
50 pub fn extend_interiors(&mut self, rings: impl IntoIterator<Item = RingProperty>) {
51 self.interior.extend(rings);
52 }
53}
54
55impl Polygon {
56 pub fn compute_envelope(&self) -> Option<Envelope> {
57 if let Some(exterior) = &self.exterior
58 && let Some(object) = exterior.object.as_ref()
59 && let e = object.compute_envelope()
60 {
61 return Some(e);
62 }
63
64 let envelopes = self
65 .interior
66 .iter()
67 .filter_map(|x| x.object.as_ref())
68 .map(|x| x.compute_envelope())
69 .collect::<Vec<_>>();
70
71 Envelope::from_envelopes(&envelopes)
72 }
73
74 fn normal(&self) -> Vector3<f64> {
77 let mut enclosed_boundary_points = self
78 .exterior()
79 .expect("should be there")
80 .object
81 .as_ref()
82 .expect("should be there")
83 .points()
84 .to_vec();
85 let first = enclosed_boundary_points
86 .first()
87 .copied()
88 .expect("should be there");
89 enclosed_boundary_points.push(first);
90
91 let mut normal = Vector3::new(0.0, 0.0, 0.0);
92 for current_point_pair in enclosed_boundary_points.windows(2) {
93 let current_first_point: Vector3<f64> = current_point_pair[0].into();
94 let current_second_point: Vector3<f64> = current_point_pair[1].into();
95
96 normal += (current_first_point - current_second_point)
97 .cross(&(current_first_point + current_second_point));
98 }
99
100 normal.normalize()
101 }
102
103 pub fn plane_equation(&self) -> Plane {
104 let envelope = self.compute_envelope().expect("should have envelope");
105 Plane::new(*envelope.lower_corner(), self.normal())
106 }
107
108 pub fn area_3d(&self) -> Result<f64, Error> {
116 let exterior_ring = self.exterior.as_ref().ok_or(Error::MissingExteriorRing)?;
117 let exterior = exterior_ring
118 .object
119 .as_ref()
120 .ok_or_else(|| Error::UnresolvedRingReference {
121 href: exterior_ring.href.clone(),
122 })?
123 .area_3d();
124
125 let holes = self
126 .interior
127 .iter()
128 .map(|r| {
129 r.object
130 .as_ref()
131 .ok_or_else(|| Error::UnresolvedRingReference {
132 href: r.href.clone(),
133 })
134 .map(|ring| ring.area_3d())
135 })
136 .collect::<Result<Vec<f64>, Error>>()?
137 .into_iter()
138 .sum::<f64>();
139
140 Ok(exterior - holes)
141 }
142
143 pub fn triangulate(&self) -> Result<TriangulatedSurface, Error> {
144 triangulate(self.exterior.clone(), self.interior.to_vec())
145 }
146
147 pub fn points(&self) -> Vec<&DirectPosition> {
148 let mut all_points = Vec::new();
149 if let Some(exterior) = &self.exterior
150 && let Some(object) = exterior.object.as_ref()
151 {
152 all_points.extend(object.points());
153 }
154
155 for ring in &self.interior {
156 if let Some(object) = ring.object.as_ref() {
157 all_points.extend(object.points());
158 }
159 }
160
161 all_points
162 }
163
164 pub fn apply_transform(&mut self, m: &Isometry3<f64>) {
165 if let Some(exterior) = &mut self.exterior
166 && let Some(object) = exterior.object.as_mut()
167 {
168 object.apply_transform(m);
169 }
170
171 self.interior.par_iter_mut().for_each(|p| {
172 if let Some(object) = p.object.as_mut() {
173 object.apply_transform(m);
174 }
175 });
176 }
177}
178
179impl AsAbstractSurface for Polygon {
180 fn abstract_surface(&self) -> &AbstractSurface {
181 &self.abstract_surface
182 }
183}
184
185impl AsAbstractSurfaceMut for Polygon {
186 fn abstract_surface_mut(&mut self) -> &mut AbstractSurface {
187 &mut self.abstract_surface
188 }
189}
190
191impl_abstract_surface_traits!(Polygon);
192
193#[cfg(test)]
194mod test {
195 use super::*;
196 use crate::model::geometry::DirectPosition;
197 use crate::model::geometry::primitives::{AsSurface, LinearRing, RingKind};
198 use nalgebra::Vector3;
199
200 #[test]
201 fn area_3d_unit_square() {
202 let ring = LinearRing::new([
203 DirectPosition::new(0.0, 0.0, 1.0).unwrap(),
204 DirectPosition::new(1.0, 0.0, 1.0).unwrap(),
205 DirectPosition::new(1.0, 1.0, 1.0).unwrap(),
206 DirectPosition::new(0.0, 1.0, 1.0).unwrap(),
207 ])
208 .unwrap();
209 let polygon =
210 Polygon::new(Some(RingProperty::new(RingKind::LinearRing(ring))), []).unwrap();
211 assert!((polygon.area_3d().expect("has exterior ring") - 1.0).abs() < 1e-10);
212 }
213
214 #[test]
215 fn area_3d_with_hole() {
216 let exterior = LinearRing::new([
218 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
219 DirectPosition::new(4.0, 0.0, 0.0).unwrap(),
220 DirectPosition::new(4.0, 4.0, 0.0).unwrap(),
221 DirectPosition::new(0.0, 4.0, 0.0).unwrap(),
222 ])
223 .unwrap();
224 let hole = LinearRing::new([
225 DirectPosition::new(1.0, 1.0, 0.0).unwrap(),
226 DirectPosition::new(2.0, 1.0, 0.0).unwrap(),
227 DirectPosition::new(2.0, 2.0, 0.0).unwrap(),
228 DirectPosition::new(1.0, 2.0, 0.0).unwrap(),
229 ])
230 .unwrap();
231 let polygon = Polygon::new(
232 Some(RingProperty::new(RingKind::LinearRing(exterior))),
233 vec![RingProperty::new(RingKind::LinearRing(hole))],
234 )
235 .unwrap();
236 assert!((polygon.area_3d().expect("has exterior ring") - 15.0).abs() < 1e-10);
237 }
238
239 #[test]
240 fn area_3d_no_exterior_ring() {
241 let polygon = Polygon::new(None, []).unwrap();
242 assert_eq!(polygon.area_3d(), Err(Error::MissingExteriorRing));
243 }
244
245 #[test]
246 fn area_3d_unresolved_exterior_ring() {
247 let exterior = RingProperty::new_href("urn:example:ring-1");
248 let polygon = Polygon::new(Some(exterior), []).unwrap();
249 assert_eq!(
250 polygon.area_3d(),
251 Err(Error::UnresolvedRingReference {
252 href: Some("urn:example:ring-1".to_string())
253 })
254 );
255 }
256
257 #[test]
258 fn area_3d_unresolved_interior_ring() {
259 let exterior = LinearRing::new([
260 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
261 DirectPosition::new(4.0, 0.0, 0.0).unwrap(),
262 DirectPosition::new(4.0, 4.0, 0.0).unwrap(),
263 DirectPosition::new(0.0, 4.0, 0.0).unwrap(),
264 ])
265 .unwrap();
266 let hole = RingProperty::new_href("urn:example:hole-1");
267 let polygon = Polygon::new(
268 Some(RingProperty::new(RingKind::LinearRing(exterior))),
269 vec![hole],
270 )
271 .unwrap();
272 assert_eq!(
273 polygon.area_3d(),
274 Err(Error::UnresolvedRingReference {
275 href: Some("urn:example:hole-1".to_string())
276 })
277 );
278 }
279
280 #[test]
281 fn basic_normal_vector() {
282 let point_a = DirectPosition::new(0.0, 0.0, 1.0).unwrap();
283 let point_b = DirectPosition::new(1.0, 0.0, 1.0).unwrap();
284 let point_c = DirectPosition::new(1.0, 1.0, 1.0).unwrap();
285 let point_d = DirectPosition::new(0.0, 1.0, 1.0).unwrap();
286 let linear_ring = LinearRing::new([point_a, point_b, point_c, point_d]).unwrap();
287 let linear_ring = RingProperty::new(RingKind::LinearRing(linear_ring));
288 let polygon = Polygon::new(Some(linear_ring), []).unwrap();
289 let normal = polygon.normal();
290
291 assert_eq!(normal, Vector3::new(0.0, 0.0, 1.0));
292 }
293
294 #[test]
295 fn basic_plane_equation() {
296 let point_a = DirectPosition::new(0.0, 0.0, 1.0).unwrap();
297 let point_b = DirectPosition::new(1.0, 0.0, 1.0).unwrap();
298 let point_c = DirectPosition::new(1.0, 1.0, 1.0).unwrap();
299 let point_d = DirectPosition::new(0.0, 1.0, 1.0).unwrap();
300 let linear_ring = LinearRing::new([point_a, point_b, point_c, point_d]).unwrap();
301 let linear_ring = RingProperty::new(RingKind::LinearRing(linear_ring));
302 let polygon = Polygon::new(Some(linear_ring), []).unwrap();
303 let plane_equation = polygon.plane_equation();
304
305 assert_eq!(
306 plane_equation.point,
307 DirectPosition::new(0.0, 0.0, 1.0).unwrap()
308 );
309 assert_eq!(plane_equation.normal(), Vector3::new(0.0, 0.0, 1.0));
310 }
311
312 #[test]
313 fn test_polygon_triangulation() {
314 let linear_ring_exterior = LinearRing::new([
315 DirectPosition::new(0.0, 0.0, 0.0).expect("should work"),
316 DirectPosition::new(1.0, 0.0, 0.0).expect("should work"),
317 DirectPosition::new(1.0, 1.0, 2.0).expect("should work"),
318 DirectPosition::new(0.0, 1.0, 2.0).expect("should work"),
319 ])
320 .expect("should work");
321 let linear_ring_exterior = RingProperty::new(RingKind::LinearRing(linear_ring_exterior));
322
323 let polygon = Polygon::new(Some(linear_ring_exterior), vec![]).expect("should work");
324 let triangulated_surface = polygon.triangulate().expect("should work");
325 assert_eq!(triangulated_surface.patches_len(), 2);
326 }
327
328 #[test]
329 fn test_polygon_with_interior_triangulation() {
330 let linear_ring_exterior = LinearRing::new([
331 DirectPosition::new(0.0, 0.0, 0.0).expect("should work"),
332 DirectPosition::new(1.0, 0.0, 0.0).expect("should work"),
333 DirectPosition::new(1.0, 1.0, 2.0).expect("should work"),
334 DirectPosition::new(0.0, 1.0, 2.0).expect("should work"),
335 DirectPosition::new(0.0, 1.0, 3.0).expect("should work"),
336 DirectPosition::new(0.0, 1.0, 5.0).expect("should work"),
337 ])
338 .expect("should work");
339 let linear_ring_exterior = RingProperty::new(RingKind::LinearRing(linear_ring_exterior));
340
341 let linear_ring_interior = LinearRing::new([
342 DirectPosition::new(0.5, 0.0, 0.0).expect("should work"),
343 DirectPosition::new(1.0, 0.0, 0.0).expect("should work"),
344 DirectPosition::new(1.0, 1.0, 2.0).expect("should work"),
345 DirectPosition::new(0.5, 1.0, 2.0).expect("should work"),
346 ])
347 .expect("should work");
348 let linear_ring_interior = RingProperty::new(RingKind::LinearRing(linear_ring_interior));
349
350 let polygon = Polygon::new(
351 Some(linear_ring_exterior),
352 vec![linear_ring_interior.clone(), linear_ring_interior.clone()],
353 )
354 .expect("should work");
355 let triangulated_surface = polygon.triangulate().expect("should work");
356 }
358}