egml_core/model/geometry/primitives/
polygon.rs1use crate::Error;
2use crate::model::base::Gml;
3use crate::model::geometry::{DirectPosition, Envelope, LinearRing, Triangle, TriangulatedSurface};
4use crate::operations::geometry::Geometry;
5use crate::operations::surface::Surface;
6use crate::operations::triangulate::Triangulate;
7use nalgebra::Isometry3;
8use rayon::prelude::*;
9
10#[derive(Debug, Clone, PartialEq)]
11pub struct Polygon {
12 pub gml: Gml,
13 pub exterior: LinearRing,
14 pub interior: Vec<LinearRing>,
15}
16
17impl Polygon {
18 pub fn new(gml: Gml, exterior: LinearRing, interior: Vec<LinearRing>) -> Result<Self, Error> {
19 Ok(Self {
20 gml,
21 exterior,
22 interior,
23 })
24 }
25
26 pub fn get_envelope(&self) -> Envelope {
27 self.exterior.envelope()
28 }
29}
30
31impl Geometry for Polygon {
32 fn points(&self) -> Vec<&DirectPosition> {
33 let mut all_points = Vec::new();
34 all_points.extend(self.exterior.points());
35
36 for ring in &self.interior {
37 all_points.extend(ring.points().iter());
38 }
39
40 all_points
41 }
42
43 fn apply_transform(&mut self, m: &Isometry3<f64>) {
44 self.exterior.apply_transform(m);
45
46 self.interior.par_iter_mut().for_each(|p| {
47 p.apply_transform(m);
48 });
49 }
50}
51
52impl Surface for Polygon {
53 fn outer_boundary_points(&self) -> Vec<&DirectPosition> {
54 self.exterior.outer_boundary_points()
55 }
56}
57
58impl Triangulate for Polygon {
59 fn triangulate(&self) -> Result<TriangulatedSurface, Error> {
60 let mut exterior_buf = Vec::new();
61 let mut all_direct_positions: Vec<&DirectPosition> = self.exterior.points();
62 all_direct_positions.extend(self.interior.iter().flat_map(|x| x.points()));
63
64 let linear_ring_lengths: Vec<usize> = {
65 let mut vec = vec![self.exterior.points().len()];
66 vec.extend(self.interior.iter().map(|x| x.points().len()));
67 vec
68 };
69 let hole_indices: Vec<usize> = linear_ring_lengths
70 .iter()
71 .scan(0, |sum, e| {
72 *sum += e;
73 Some(*sum)
74 })
75 .take(linear_ring_lengths.len() - 1)
76 .collect();
77
78 let vertices = all_direct_positions
79 .iter()
80 .map(|p| p.coords())
81 .collect::<Vec<_>>();
82 earcut::utils3d::project3d_to_2d(&vertices, vertices.len(), &mut exterior_buf);
83
84 let mut triangle_indices: Vec<usize> = vec![];
85 let mut earcut = earcut::Earcut::new();
86 earcut.earcut(
87 exterior_buf.iter().copied(),
88 &hole_indices,
89 &mut triangle_indices,
90 );
91
92 let triangles: Vec<Triangle> = triangle_indices
93 .chunks(3)
94 .map(|x| {
95 let vertex_a = all_direct_positions[x[0]];
96 let vertex_b = all_direct_positions[x[1]];
97 let vertex_c = all_direct_positions[x[2]];
98 Triangle::new(*vertex_a, *vertex_b, *vertex_c).expect("should work")
99 })
100 .collect::<Vec<_>>();
101
102 let triangulated_surface = TriangulatedSurface::new(triangles)?;
103 Ok(triangulated_surface)
104 }
105}
106
107#[cfg(test)]
108mod test {
109 use super::*;
110
111 #[test]
112 fn test_polygon_triangulation() {
113 let gml = Gml::new("exterior_linear_ring_id".to_string().try_into().unwrap());
114 let linear_ring_exterior = LinearRing::new(
115 gml,
116 vec![
117 DirectPosition::new(0.0, 0.0, 0.0).expect("should work"),
118 DirectPosition::new(1.0, 0.0, 0.0).expect("should work"),
119 DirectPosition::new(1.0, 1.0, 2.0).expect("should work"),
120 DirectPosition::new(0.0, 1.0, 2.0).expect("should work"),
121 ],
122 )
123 .expect("should work");
124
125 let gml = Gml::new("polygon_id".to_string().try_into().unwrap());
126 let polygon = Polygon::new(gml, linear_ring_exterior, vec![]).expect("should work");
127 let triangulated_surface = polygon.triangulate().expect("should work");
128 assert_eq!(triangulated_surface.number_of_patches(), 2);
129 }
130
131 #[test]
132 fn test_polygon_with_interior_triangulation() {
133 let gml = Gml::new("exterior_linear_ring_id".to_string().try_into().unwrap());
134 let linear_ring_exterior = LinearRing::new(
135 gml,
136 vec![
137 DirectPosition::new(0.0, 0.0, 0.0).expect("should work"),
138 DirectPosition::new(1.0, 0.0, 0.0).expect("should work"),
139 DirectPosition::new(1.0, 1.0, 2.0).expect("should work"),
140 DirectPosition::new(0.0, 1.0, 2.0).expect("should work"),
141 DirectPosition::new(0.0, 1.0, 3.0).expect("should work"),
142 DirectPosition::new(0.0, 1.0, 5.0).expect("should work"),
143 ],
144 )
145 .expect("should work");
146
147 let gml = Gml::new("interior_linear_ring_id".to_string().try_into().unwrap());
148 let linear_ring_interior = LinearRing::new(
149 gml,
150 vec![
151 DirectPosition::new(0.5, 0.0, 0.0).expect("should work"),
152 DirectPosition::new(1.0, 0.0, 0.0).expect("should work"),
153 DirectPosition::new(1.0, 1.0, 2.0).expect("should work"),
154 DirectPosition::new(0.5, 1.0, 2.0).expect("should work"),
155 ],
156 )
157 .expect("should work");
158
159 let gml = Gml::new("polygon_id".to_string().try_into().unwrap());
160 let polygon = Polygon::new(
161 gml,
162 linear_ring_exterior,
163 vec![linear_ring_interior.clone(), linear_ring_interior.clone()],
164 )
165 .expect("should work");
166 let triangulated_surface = polygon.triangulate().expect("should work");
167 }
169}