egml_core/model/geometry/primitives/
polygon.rs

1use 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        // assert_eq!(triangulated_surface.number_of_patches(), 2);
168    }
169}