kasane_logic/geometry/shapes/polygon/
geometry_relation.rs1use crate::{Coordinate, Ecef, IntoCoordinates, IntoLines, IntoTriangles, Line, Polygon, Triangle};
2
3impl IntoCoordinates for Polygon {
4 fn into_coordinates(self) -> impl Iterator<Item = Coordinate> {
5 self.vertices.into_iter()
6 }
7
8 fn iter_coordinates(&self) -> impl Iterator<Item = Coordinate> {
9 self.vertices.clone().into_iter()
10 }
11}
12
13impl IntoLines for Polygon {
14 fn into_lines(self) -> impl Iterator<Item = Line> {
15 let triangles: Vec<Triangle> = self.into_triangles().collect();
16 triangles
17 .into_iter()
18 .flat_map(|triangle| triangle.into_lines())
19 }
20
21 fn iter_lines(&self) -> impl Iterator<Item = Line> {
22 let triangles: Vec<Triangle> = self.iter_triangles().collect();
23 triangles
24 .into_iter()
25 .flat_map(|triangle| triangle.into_lines())
26 }
27}
28
29impl IntoTriangles for Polygon {
30 fn into_triangles(self) -> impl Iterator<Item = Triangle> {
31 self.iter_triangles().collect::<Vec<_>>().into_iter()
32 }
33
34 fn iter_triangles(&self) -> impl Iterator<Item = Triangle> {
35 let n = self.vertices.len();
36 if n < 3 {
37 return Vec::<Triangle>::new().into_iter();
38 }
39
40 if n == 3 {
41 return vec![Triangle::new([
42 self.vertices[0],
43 self.vertices[1],
44 self.vertices[2],
45 ])]
46 .into_iter();
47 }
48
49 let ecef_points: Vec<Ecef> = self.vertices.iter().map(|&c| c.into()).collect();
51
52 let (u_axis, v_axis) = get_projection_axes(&ecef_points);
54
55 let points_2d: Vec<(f64, f64)> = ecef_points
57 .iter()
58 .map(|p| p.project_2d(u_axis, v_axis))
59 .collect();
60
61 let area = signed_area(&points_2d);
63 let area_sign = if area > 0.0 { 1.0 } else { -1.0 };
64
65 let mut indices: Vec<usize> = (0..n).collect();
67 let mut result = Vec::with_capacity(n - 2);
68 let mut count = 0;
69 let max_iters = n * n;
70
71 while indices.len() > 3 && count < max_iters {
72 let mut ear_found = false;
73 let len = indices.len();
74
75 for i in 0..len {
76 let prev_idx = indices[(i + len - 1) % len];
77 let curr_idx = indices[i];
78 let next_idx = indices[(i + 1) % len];
79
80 if is_ear(
81 prev_idx, curr_idx, next_idx, &indices, &points_2d, area_sign,
82 ) {
83 result.push(Triangle::new([
84 self.vertices[prev_idx],
85 self.vertices[curr_idx],
86 self.vertices[next_idx],
87 ]));
88 indices.remove(i);
89 ear_found = true;
90 break;
91 }
92 }
93 if !ear_found {
94 break;
95 }
96 count += 1;
97 }
98
99 if indices.len() == 3 {
100 result.push(Triangle::new([
101 self.vertices[indices[0]],
102 self.vertices[indices[1]],
103 self.vertices[indices[2]],
104 ]));
105 }
106
107 result.into_iter()
108 }
109}
110
111fn get_projection_axes(pts: &[Ecef]) -> (usize, usize) {
113 let mut nx = 0.0;
114 let mut ny = 0.0;
115 let mut nz = 0.0;
116
117 let len = pts.len();
118
119 for i in 0..len {
120 let curr = pts[i];
121 let next = pts[(i + 1) % len];
122
123 nx += (curr.y() - next.y()) * (curr.z() + next.z());
124 ny += (curr.z() - next.z()) * (curr.x() + next.x());
125 nz += (curr.x() - next.x()) * (curr.y() + next.y());
126 }
127
128 let ax = nx.abs();
129 let ay = ny.abs();
130 let az = nz.abs();
131
132 if ax >= ay && ax >= az {
133 (1, 2)
134 } else if ay >= ax && ay >= az {
135 (0, 2)
136 } else {
137 (0, 1)
138 }
139}
140
141fn signed_area(pts: &[(f64, f64)]) -> f64 {
142 let mut area = 0.0;
143 for i in 0..pts.len() {
144 let j = (i + 1) % pts.len();
145 area += pts[i].0 * pts[j].1;
146 area -= pts[j].0 * pts[i].1;
147 }
148 area / 2.0
149}
150
151fn is_ear(
152 p: usize,
153 c: usize,
154 n: usize,
155 indices: &[usize],
156 pts: &[(f64, f64)],
157 area_sign: f64,
158) -> bool {
159 let a = pts[p];
160 let b = pts[c];
161 let c_pt = pts[n];
162
163 let cross = (b.0 - a.0) * (c_pt.1 - b.1) - (b.1 - a.1) * (c_pt.0 - b.0);
165
166 if cross * area_sign <= -1e-10 {
168 return false;
169 }
170
171 for &idx in indices {
173 if idx == p || idx == c || idx == n {
174 continue;
175 }
176 if is_point_in_triangle(pts[idx], a, b, c_pt) {
177 return false;
178 }
179 }
180 true
181}
182
183fn is_point_in_triangle(p: (f64, f64), a: (f64, f64), b: (f64, f64), c: (f64, f64)) -> bool {
184 let area2 = 0.5 * (-b.1 * c.0 + a.1 * (-b.0 + c.0) + a.0 * (b.1 - c.1) + b.0 * c.1);
185 if area2.abs() < 1e-12 {
186 return false;
187 }
188 let s = 1.0 / (2.0 * area2) * (a.1 * c.0 - a.0 * c.1 + (c.1 - a.1) * p.0 + (a.0 - c.0) * p.1);
189 let t = 1.0 / (2.0 * area2) * (a.0 * b.1 - a.1 * b.0 + (a.1 - b.1) * p.0 + (b.0 - a.0) * p.1);
190 s > 0.0 && t > 0.0 && (1.0 - s - t) > 0.0
191}