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