egml_core/model/geometry/primitives/
linear_ring.rs1use crate::error::Error;
2use crate::error::Error::NotEnoughElements;
3
4use crate::Error::{ContainsDuplicateElements, ContainsEqualStartAndLastElement};
5use crate::model::base::AbstractGml;
6use crate::model::geometry::{DirectPosition, Triangle, TriangulatedSurface};
7use crate::operations::geometry::Geometry;
8use crate::operations::surface::Surface;
9use crate::operations::triangulate::Triangulate;
10use nalgebra::Isometry3;
11use rayon::prelude::*;
12
13const MINIMUM_NUMBER_OF_POINTS: usize = 3;
14
15#[derive(Debug, Clone, PartialEq)]
16pub struct LinearRing {
17 pub abstract_gml: AbstractGml,
18 points: Vec<DirectPosition>,
19}
20
21impl LinearRing {
22 pub fn new(abstract_gml: AbstractGml, points: Vec<DirectPosition>) -> Result<Self, Error> {
23 let duplicates_count = points.windows(2).filter(|x| x[0] == x[1]).count();
24 if duplicates_count >= 1 {
25 return Err(ContainsDuplicateElements);
26 }
27 if points.len() < MINIMUM_NUMBER_OF_POINTS {
28 return Err(NotEnoughElements(
29 "Linear ring must at least have three unique points",
30 ));
31 }
32 if points.first().expect("") == points.last().expect("") {
33 return Err(ContainsEqualStartAndLastElement);
34 }
35
36 Ok(Self {
37 abstract_gml,
38 points,
39 })
40 }
41
42 pub fn set_points(&mut self, val: Vec<DirectPosition>) -> Result<(), Error> {
43 let duplicates_count = val.windows(2).filter(|x| x[0] == x[1]).count();
44 if duplicates_count >= 1 {
45 return Err(ContainsDuplicateElements);
46 }
47 if val.len() < MINIMUM_NUMBER_OF_POINTS {
48 return Err(NotEnoughElements(
49 "Linear ring must at least have three unique points",
50 ));
51 }
52 self.points = val;
53 Ok(())
54 }
55}
56
57impl Geometry for LinearRing {
58 fn points(&self) -> Vec<&DirectPosition> {
59 self.points.iter().collect()
60 }
61
62 fn apply_transform(&mut self, m: &Isometry3<f64>) {
63 self.points.iter_mut().for_each(|p| {
64 p.apply_transform(m);
65 });
66 }
68}
69
70impl Surface for LinearRing {
71 fn outer_boundary_points(&self) -> Vec<&DirectPosition> {
72 self.points.iter().collect()
73 }
74}
75
76impl Triangulate for LinearRing {
77 fn triangulate(&self) -> Result<TriangulatedSurface, Error> {
78 let vertices_3d = self.points.iter().map(|p| p.coords()).collect::<Vec<_>>();
79 let mut vertices_2d_buf = Vec::new();
80 earcut::utils3d::project3d_to_2d(&vertices_3d, vertices_3d.len(), &mut vertices_2d_buf);
81
82 let mut triangle_indices: Vec<usize> = vec![];
83 let mut earcut = earcut::Earcut::new();
84 earcut.earcut(vertices_2d_buf.iter().copied(), &[], &mut triangle_indices);
85
86 let triangles: Vec<Triangle> = triangle_indices
87 .chunks(3)
88 .map(|x| {
89 let vertex_a = self.points[x[0]];
90 let vertex_b = self.points[x[1]];
91 let vertex_c = self.points[x[2]];
92 Triangle::new(vertex_a, vertex_b, vertex_c).expect("should work")
93 })
94 .collect::<Vec<_>>();
95
96 let triangulated_surface = TriangulatedSurface::new(triangles)?;
97 Ok(triangulated_surface)
98 }
99}
100
101#[cfg(test)]
102mod test {
103 use super::*;
104 use crate::model::base::Id;
105
106 #[test]
107 fn triangulate() {
108 let abstract_gml = AbstractGml::new(Id::try_from("test_id").expect("must work"));
109 let linear_ring = LinearRing::new(
110 abstract_gml,
111 vec![
112 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
113 DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
114 DirectPosition::new(1.0, 1.0, 0.0).unwrap(),
115 DirectPosition::new(0.0, 1.0, 0.0).unwrap(),
116 ],
117 )
118 .unwrap();
119
120 let result = &linear_ring.triangulate().unwrap();
121
122 assert_eq!(result.number_of_patches(), 2);
123 assert!(result.patches()[0].area() > 0.0);
124 assert!(result.patches()[1].area() > 0.0);
125 }
126}