1use std::collections::HashMap;
7use crate::linalg::{Vec2, IVec3};
8use crate::types::{Halfedge, PolyVert, PolygonsIdx, SimplePolygonIdx, Polygons, Rect, K_PRECISION};
9
10#[path = "polygon_earclip.rs"]
11mod polygon_earclip;
12use polygon_earclip::EarClip;
13
14type IVec3Out = IVec3;
15
16const INVALID: usize = usize::MAX;
17const K_BEST: f64 = f64::NEG_INFINITY;
18
19pub fn ccw(p0: Vec2, p1: Vec2, p2: Vec2, tol: f64) -> i32 {
26 let v1 = p1 - p0;
27 let v2 = p2 - p0;
28 let area = v1.x * v2.y - v1.y * v2.x;
29 let base2 = (v1.x * v1.x + v1.y * v1.y).max(v2.x * v2.x + v2.y * v2.y);
30 if area * area * 4.0 <= base2 * tol * tol {
31 0
32 } else if area > 0.0 {
33 1
34 } else {
35 -1
36 }
37}
38
39#[inline]
40fn determinant2x2(a: Vec2, b: Vec2) -> f64 {
41 a.x * b.y - a.y * b.x
42}
43
44#[inline]
45fn safe_normalize_2d(v: Vec2) -> Vec2 {
46 let len = (v.x * v.x + v.y * v.y).sqrt();
47 if len == 0.0 || !len.is_finite() {
48 Vec2::new(0.0, 0.0)
49 } else {
50 Vec2::new(v.x / len, v.y / len)
51 }
52}
53
54#[inline]
55fn dot2d(a: Vec2, b: Vec2) -> f64 {
56 a.x * b.x + a.y * b.y
57}
58
59fn build_two_d_tree_impl(points: &mut [PolyVert], sort_x: bool) {
66 if sort_x {
67 points.sort_by(|a, b| a.pos.x.partial_cmp(&b.pos.x).unwrap_or(std::cmp::Ordering::Equal));
68 } else {
69 points.sort_by(|a, b| a.pos.y.partial_cmp(&b.pos.y).unwrap_or(std::cmp::Ordering::Equal));
70 }
71 if points.len() < 2 {
72 return;
73 }
74 let mid = points.len() / 2;
75 build_two_d_tree_impl(&mut points[..mid], !sort_x);
76 build_two_d_tree_impl(&mut points[mid + 1..], !sort_x);
77}
78
79fn build_two_d_tree(points: &mut Vec<PolyVert>) {
80 if points.len() <= 8 {
81 return;
82 }
83 build_two_d_tree_impl(points.as_mut_slice(), true);
84}
85
86fn query_two_d_tree<F: FnMut(PolyVert)>(points: &[PolyVert], r: Rect, mut f: F) {
88 if points.len() <= 8 {
89 for p in points {
90 if r.contains_point(p.pos) {
91 f(*p);
92 }
93 }
94 return;
95 }
96
97 let mut stack: Vec<(Rect, usize, usize, i32)> = Vec::with_capacity(64); let inf_rect = Rect {
102 min: Vec2::new(f64::NEG_INFINITY, f64::NEG_INFINITY),
103 max: Vec2::new(f64::INFINITY, f64::INFINITY),
104 };
105 stack.push((inf_rect, 0, points.len(), 0));
106
107 while let Some((current, start, len, level)) = stack.pop() {
108 if len <= 8 {
109 for p in &points[start..start + len] {
110 if r.contains_point(p.pos) {
111 f(*p);
112 }
113 }
114 continue;
115 }
116
117 let mid = len / 2;
118 let middle = points[start + mid];
119
120 let mut left = current;
121 let mut right = current;
122 if level % 2 == 0 {
123 left.max.x = middle.pos.x;
124 right.min.x = middle.pos.x;
125 } else {
126 left.max.y = middle.pos.y;
127 right.min.y = middle.pos.y;
128 }
129
130 if r.contains_point(middle.pos) {
131 f(middle);
132 }
133
134 if left.does_overlap(&r) {
135 stack.push((left, start, mid, level + 1));
136 }
137 if right.does_overlap(&r) {
138 stack.push((right, start + mid + 1, len - mid - 1, level + 1));
139 }
140 }
141}
142
143fn is_convex(polys: &PolygonsIdx, epsilon: f64) -> bool {
148 for poly in polys {
149 if poly.is_empty() {
150 continue;
151 }
152 let first_edge = poly[0].pos - poly[poly.len() - 1].pos;
153 let mut last_edge = safe_normalize_2d(first_edge);
154 for v in 0..poly.len() {
155 let edge = if v + 1 < poly.len() {
156 poly[v + 1].pos - poly[v].pos
157 } else {
158 first_edge
159 };
160 let det = determinant2x2(last_edge, edge);
161 if det <= 0.0 || (det.abs() < epsilon && dot2d(last_edge, edge) < 0.0) {
162 return false;
163 }
164 last_edge = safe_normalize_2d(edge);
165 }
166 }
167 true
168}
169
170fn triangulate_convex(polys: &PolygonsIdx) -> Vec<IVec3Out> {
175 let num_tri: usize = polys.iter().map(|p| p.len().saturating_sub(2)).sum();
176 let mut triangles = Vec::with_capacity(num_tri);
177 for poly in polys {
178 let mut i = 0usize;
179 let mut k = poly.len().saturating_sub(1);
180 let mut right = true;
181 while i + 1 < k {
182 let j = if right { i + 1 } else { k - 1 };
183 triangles.push(IVec3Out::new(poly[i].idx, poly[j].idx, poly[k].idx));
184 if right {
185 i = j;
186 } else {
187 k = j;
188 }
189 right = !right;
190 }
191 }
192 triangles
193}
194
195pub struct HalfedgeTriangulation {
213 pub halfedges: Vec<Halfedge>,
214 pub contour_end: usize,
215}
216
217impl HalfedgeTriangulation {
218 pub fn num_tri(&self) -> usize {
219 (self.halfedges.len() - self.contour_end) / 3
220 }
221}
222
223fn add_halfedge(
226 halfedges: &mut Vec<Halfedge>,
227 edge2halfedge: &mut HashMap<(i32, i32), Vec<i32>>,
228 start: i32,
229 end: i32,
230) {
231 let idx = halfedges.len() as i32;
232 let mut data = Halfedge {
233 start_vert: start,
234 end_vert: end,
235 paired_halfedge: -1,
236 prop_vert: -1,
237 };
238 if let Some(reverse) = edge2halfedge.get_mut(&(end, start)) {
239 if let Some(pair) = reverse.pop() {
240 data.paired_halfedge = pair;
241 halfedges[pair as usize].paired_halfedge = idx;
242 if reverse.is_empty() {
243 edge2halfedge.remove(&(end, start));
244 }
245 halfedges.push(data);
246 return;
247 }
248 }
249 edge2halfedge.entry((start, end)).or_default().push(idx);
250 halfedges.push(data);
251}
252
253pub fn triangulate_idx_halfedges(
260 polys: &PolygonsIdx,
261 epsilon: f64,
262 allow_convex: bool,
263) -> HalfedgeTriangulation {
264 let triangles = triangulate_idx(polys, epsilon, allow_convex);
265
266 let mut result = HalfedgeTriangulation {
267 halfedges: Vec::new(),
268 contour_end: 0,
269 };
270 let mut edge2halfedge: HashMap<(i32, i32), Vec<i32>> = HashMap::new();
271
272 for poly in polys {
275 for i in 0..poly.len() {
276 let start = poly[i].idx;
277 let end = poly[if i + 1 < poly.len() { i + 1 } else { 0 }].idx;
278 add_halfedge(&mut result.halfedges, &mut edge2halfedge, end, start);
279 }
280 }
281 result.contour_end = result.halfedges.len();
282
283 for tri in &triangles {
284 add_halfedge(&mut result.halfedges, &mut edge2halfedge, tri.x, tri.y);
285 add_halfedge(&mut result.halfedges, &mut edge2halfedge, tri.y, tri.z);
286 add_halfedge(&mut result.halfedges, &mut edge2halfedge, tri.z, tri.x);
287 }
288
289 #[cfg(debug_assertions)]
291 {
292 debug_assert!(
293 edge2halfedge.is_empty(),
294 "triangulation has unpaired halfedges"
295 );
296 for (i, he) in result.halfedges.iter().enumerate() {
297 let pair = he.paired_halfedge;
298 debug_assert!(
299 pair >= 0 && (pair as usize) < result.halfedges.len(),
300 "invalid paired halfedge"
301 );
302 let other = &result.halfedges[pair as usize];
303 debug_assert!(
304 other.paired_halfedge == i as i32,
305 "halfedge pair is not reciprocal"
306 );
307 debug_assert!(
308 he.start_vert == other.end_vert && he.end_vert == other.start_vert,
309 "halfedge pair endpoints do not match"
310 );
311 }
312 }
313
314 result
315}
316
317pub fn triangulate_idx(polys: &PolygonsIdx, epsilon: f64, allow_convex: bool) -> Vec<IVec3Out> {
323 if polys.is_empty() || polys.iter().all(|p| p.is_empty()) {
324 return Vec::new();
325 }
326 if allow_convex && is_convex(polys, epsilon) {
327 return triangulate_convex(polys);
328 }
329 let (triangles, _updated_eps) = EarClip::new(polys, epsilon).triangulate();
330 triangles
331}
332
333pub fn triangulate(polygons: &Polygons, epsilon: f64, allow_convex: bool) -> Vec<IVec3Out> {
335 let mut idx = 0i32;
336 let mut polygons_indexed: PolygonsIdx = Vec::new();
337 for poly in polygons {
338 let simple: SimplePolygonIdx = poly
339 .iter()
340 .map(|&pos| {
341 let v = PolyVert { pos, idx };
342 idx += 1;
343 v
344 })
345 .collect();
346 polygons_indexed.push(simple);
347 }
348 triangulate_idx(&polygons_indexed, epsilon, allow_convex)
349}
350
351#[cfg(test)]
352#[path = "polygon_tests.rs"]
353mod tests;