1use crate::polygon::PolygonType;
22use crate::wgs84::Wgs84;
23use crate::wgs84_polygon::Wgs84Polygon;
24
25#[derive(Debug, Clone, Copy)]
27struct PartitionVertex {
28 is_active: bool,
29 is_convex: bool,
30 is_ear: bool,
31 p: Wgs84,
32 angle: f64,
33 previous: usize,
34 next: usize,
35}
36
37#[derive(Debug, Default, Clone, Copy)]
39pub struct PolygonTriangulation;
40
41impl PolygonTriangulation {
42 pub fn new() -> Self {
44 PolygonTriangulation
45 }
46
47 pub fn triangulate_by_ear_clipping(&self, polygon: &Wgs84Polygon) -> Wgs84Polygon {
54 let mut result = Wgs84Polygon::with_type(PolygonType::TriangleList);
55
56 if polygon.polygon_type() != PolygonType::SimplePolygon || polygon.vertices().len() < 3 {
57 return Wgs84Polygon::with_type(PolygonType::Unknown);
58 }
59
60 let num_vertices = polygon.vertices().len();
61
62 if num_vertices == 3 {
64 result.add_vertices(polygon.vertices());
65 return result;
66 }
67
68 let mut vertices: Vec<PartitionVertex> = (0..num_vertices)
69 .map(|i| PartitionVertex {
70 is_active: true,
71 is_convex: false,
72 is_ear: false,
73 p: polygon.get(i),
74 angle: 0.0,
75 next: if i == num_vertices - 1 { 0 } else { i + 1 },
76 previous: if i == 0 { num_vertices - 1 } else { i - 1 },
77 })
78 .collect();
79
80 for i in 0..num_vertices {
81 Self::update_vertex(i, &mut vertices, num_vertices);
82 }
83
84 let mut ear = 0usize;
85 for i in 0..(num_vertices - 3) {
86 let mut ear_found = false;
87
88 for j in 0..num_vertices {
90 if !vertices[j].is_active || !vertices[j].is_ear {
91 continue;
92 }
93 if !ear_found {
94 ear_found = true;
95 ear = j;
96 } else if vertices[j].angle > vertices[ear].angle {
97 ear = j;
98 }
99 }
100
101 if !ear_found {
102 return Wgs84Polygon::with_type(PolygonType::Unknown);
103 }
104
105 let ear_prev = vertices[ear].previous;
106 let ear_next = vertices[ear].next;
107 result.add_vertex(vertices[ear_prev].p);
108 result.add_vertex(vertices[ear].p);
109 result.add_vertex(vertices[ear_next].p);
110
111 vertices[ear].is_active = false;
112 vertices[ear_prev].next = ear_next;
113 vertices[ear_next].previous = ear_prev;
114
115 if i == num_vertices - 4 {
116 break;
117 }
118
119 Self::update_vertex(ear_prev, &mut vertices, num_vertices);
120 Self::update_vertex(ear_next, &mut vertices, num_vertices);
121 }
122
123 for i in 0..num_vertices {
124 if vertices[i].is_active {
125 let prev = vertices[i].previous;
126 let next = vertices[i].next;
127 result.add_vertex(vertices[prev].p);
128 result.add_vertex(vertices[i].p);
129 result.add_vertex(vertices[next].p);
130 break;
131 }
132 }
133
134 result
135 }
136
137 fn normalize(p: Wgs84) -> Wgs84 {
143 let n = (p.lon * p.lon + p.lat * p.lat).sqrt();
144 if n != 0.0 {
145 Wgs84::new(p.lon / n, p.lat / n)
146 } else {
147 Wgs84::new(0.0, 0.0)
148 }
149 }
150
151 fn is_convex(p1: Wgs84, p2: Wgs84, p3: Wgs84) -> bool {
153 (p3.lat - p1.lat) * (p2.lon - p1.lon) - (p3.lon - p1.lon) * (p2.lat - p1.lat) > 0.0
154 }
155
156 fn is_inside(p1: Wgs84, p2: Wgs84, p3: Wgs84, p: Wgs84) -> bool {
158 !Self::is_convex(p1, p, p2) && !Self::is_convex(p2, p, p3) && !Self::is_convex(p3, p, p1)
159 }
160
161 fn update_vertex(v_idx: usize, vertices: &mut [PartitionVertex], num_vertices: usize) {
163 let prev_idx = vertices[v_idx].previous;
164 let next_idx = vertices[v_idx].next;
165 let v_p = vertices[v_idx].p;
166 let v1_p = vertices[prev_idx].p;
167 let v3_p = vertices[next_idx].p;
168
169 let is_convex = Self::is_convex(v1_p, v_p, v3_p);
170
171 let vec1 = Self::normalize(v1_p - v_p);
174 let vec3 = Self::normalize(v3_p - v_p);
175
176 let angle = vec1.lon * vec3.lon + vec1.lat * vec3.lat;
177
178 let mut is_ear = false;
179 if is_convex {
180 is_ear = true;
181 for vert in vertices.iter().take(num_vertices) {
182 let ip = vert.p;
183 if ip.lon == v_p.lon && ip.lat == v_p.lat {
184 continue;
185 }
186 if ip.lon == v1_p.lon && ip.lat == v1_p.lat {
187 continue;
188 }
189 if ip.lon == v3_p.lon && ip.lat == v3_p.lat {
190 continue;
191 }
192 if Self::is_inside(v1_p, v_p, v3_p, ip) {
193 is_ear = false;
194 break;
195 }
196 }
197 }
198
199 let v = &mut vertices[v_idx];
200 v.is_convex = is_convex;
201 v.angle = angle;
202 v.is_ear = is_ear;
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 fn pts(coords: &[(f64, f64)]) -> Vec<Wgs84> {
211 coords.iter().map(|&(x, y)| Wgs84::new(x, y)).collect()
212 }
213
214 #[test]
215 fn triangle_passthrough() {
216 let t = PolygonTriangulation::new();
217 let poly = Wgs84Polygon::from_vertices(pts(&[(0.0, 0.0), (4.0, 0.0), (0.0, 4.0)]));
218 let out = t.triangulate_by_ear_clipping(&poly);
219 assert_eq!(out.polygon_type(), PolygonType::TriangleList);
220 assert_eq!(out.vertices().len(), 3);
221 }
222
223 #[test]
224 fn convex_quad() {
225 let t = PolygonTriangulation;
226 let poly =
227 Wgs84Polygon::from_vertices(pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]));
228 let out = t.triangulate_by_ear_clipping(&poly);
229 assert_eq!(out.polygon_type(), PolygonType::TriangleList);
230 assert_eq!(out.vertices().len(), 6);
232 }
233
234 #[test]
235 fn concave_with_reflex_vertex() {
236 let t = PolygonTriangulation::new();
237 let poly = Wgs84Polygon::from_vertices(pts(&[
239 (0.0, 0.0),
240 (4.0, 0.0),
241 (2.0, 2.0),
242 (4.0, 4.0),
243 (0.0, 4.0),
244 ]));
245 let out = t.triangulate_by_ear_clipping(&poly);
246 assert_eq!(out.polygon_type(), PolygonType::TriangleList);
247 assert_eq!(out.vertices().len(), 9);
249 }
250
251 #[test]
252 fn too_few_vertices_is_unknown() {
253 let t = PolygonTriangulation::new();
254 let poly = Wgs84Polygon::from_vertices(pts(&[(0.0, 0.0), (1.0, 0.0)]));
255 let out = t.triangulate_by_ear_clipping(&poly);
256 assert_eq!(out.polygon_type(), PolygonType::Unknown);
257 assert!(out.is_empty());
258 }
259
260 #[test]
261 fn wrong_type_is_unknown() {
262 let t = PolygonTriangulation::new();
263 let poly = Wgs84Polygon::with_type_and_vertices(
264 PolygonType::TriangleList,
265 pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]),
266 );
267 let out = t.triangulate_by_ear_clipping(&poly);
268 assert_eq!(out.polygon_type(), PolygonType::Unknown);
269 }
270
271 #[test]
272 fn regular_pentagon_uses_angle_tiebreak() {
273 let t = PolygonTriangulation::new();
276 let mut coords = Vec::new();
277 for i in 0..5 {
278 let a = 2.0 * std::f64::consts::PI * (i as f64) / 5.0;
279 coords.push((a.cos(), a.sin()));
280 }
281 let poly = Wgs84Polygon::from_vertices(pts(&coords));
282 let out = t.triangulate_by_ear_clipping(&poly);
283 assert_eq!(out.polygon_type(), PolygonType::TriangleList);
284 assert_eq!(out.vertices().len(), 9);
286 }
287
288 #[test]
289 fn clockwise_polygon_finds_no_ear() {
290 let t = PolygonTriangulation::new();
293 let poly =
294 Wgs84Polygon::from_vertices(pts(&[(0.0, 0.0), (0.0, 4.0), (4.0, 4.0), (4.0, 0.0)]));
295 let out = t.triangulate_by_ear_clipping(&poly);
296 assert_eq!(out.polygon_type(), PolygonType::Unknown);
297 assert!(out.is_empty());
298 }
299
300 #[test]
301 fn coincident_vertex_zero_length_edge() {
302 let t = PolygonTriangulation::new();
305 let poly =
306 Wgs84Polygon::from_vertices(pts(&[(0.0, 0.0), (0.0, 0.0), (4.0, 0.0), (0.0, 4.0)]));
307 let out = t.triangulate_by_ear_clipping(&poly);
308 assert_eq!(out.polygon_type(), PolygonType::TriangleList);
309 assert_eq!(out.vertices().len(), 6);
310 }
311}