1use axiolid_core::Point2;
69
70use crate::build::build;
71use crate::mesh::{Triangulation, TriangulationError, NO_HALFEDGE};
72use crate::recover::Splits;
73use crate::{collinear, turns_left, Constraint};
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum RingKind {
78 Outer,
81 Hole,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Hash)]
90pub struct Ring {
91 pub vertices: Vec<u32>,
93 pub kind: RingKind,
95}
96
97impl Ring {
98 #[must_use]
100 pub fn outer(vertices: impl Into<Vec<u32>>) -> Self {
101 Self {
102 vertices: vertices.into(),
103 kind: RingKind::Outer,
104 }
105 }
106
107 #[must_use]
109 pub fn hole(vertices: impl Into<Vec<u32>>) -> Self {
110 Self {
111 vertices: vertices.into(),
112 kind: RingKind::Hole,
113 }
114 }
115}
116
117pub fn triangulate_domain(
135 points: &[Point2],
136 constraints: &[Constraint],
137 rings: &[Ring],
138) -> Result<Triangulation, TriangulationError> {
139 let built = build(points, constraints, Some(rings))?;
140 let mut tri = built.tri;
141 classify(&mut tri, &built.kept, &built.splits, rings)?;
142 tri.domain = Some(rings.to_vec());
143 Ok(tri)
144}
145
146pub(crate) fn rebuild(
150 points: &[Point2],
151 constraints: &[Constraint],
152 domain: Option<&[Ring]>,
153) -> Result<Triangulation, TriangulationError> {
154 match domain {
155 None => crate::triangulate(points, constraints),
156 Some(rings) => triangulate_domain(points, constraints, rings),
157 }
158}
159
160pub(crate) fn check_ring_sizes(rings: &[Ring], count: u32) -> Result<(), TriangulationError> {
162 for (r, ring) in rings.iter().enumerate() {
163 let r = r as u32;
164 if let Some(&index) = ring.vertices.iter().find(|&&v| v >= count) {
165 return Err(TriangulationError::RingOutOfRange { ring: r, index });
166 }
167 if ring.vertices.len() < 3 {
168 return Err(TriangulationError::TooFewRingVertices { ring: r });
169 }
170 }
171 if rings.is_empty() {
172 return Err(TriangulationError::EmptyDomain);
173 }
174 Ok(())
175}
176
177fn kept_vertices(ring: &Ring, kept: &[u32]) -> Vec<u32> {
179 ring.vertices.iter().map(|&v| kept[v as usize]).collect()
180}
181
182fn lex(p: Point2, q: Point2) -> core::cmp::Ordering {
184 (p.x, p.y)
185 .partial_cmp(&(q.x, q.y))
186 .unwrap_or(core::cmp::Ordering::Equal)
187}
188
189pub(crate) fn check_rings(
195 points: &[Point2],
196 kept: &[u32],
197 rings: &[Ring],
198) -> Result<Vec<Constraint>, TriangulationError> {
199 let at = |k: u32| points[k as usize];
200 let mut stamp = vec![u32::MAX; points.len()];
201 let mut edges = Vec::new();
202 for (r, ring) in rings.iter().enumerate() {
203 let r = r as u32;
204 let seq = kept_vertices(ring, kept);
205 for (i, &k) in seq.iter().enumerate() {
206 if stamp[k as usize] == r {
207 return Err(TriangulationError::RepeatedRingVertex {
208 ring: r,
209 index: ring.vertices[i],
210 });
211 }
212 stamp[k as usize] = r;
213 }
214 let m = seq.len();
215 for i in 0..m {
216 let (prev, v, next) = (seq[(i + m - 1) % m], seq[i], seq[(i + 1) % m]);
217 let folds = collinear(at(prev), at(v), at(next))
218 && lex(at(prev), at(v)) == lex(at(next), at(v));
219 if folds {
220 return Err(TriangulationError::FoldedRing {
221 ring: r,
222 index: ring.vertices[i],
223 });
224 }
225 edges.push(Constraint::new(v, next));
226 }
227 }
228 Ok(edges)
229}
230
231fn is_ccw(points: &[Point2], seq: &[u32]) -> bool {
236 let m = seq.len();
237 let at = |i: usize| points[seq[i % m] as usize];
238 let lowest = (0..m)
239 .min_by(|&i, &j| lex(at(i), at(j)))
240 .unwrap_or_default();
241 turns_left(at(lowest + m - 1), at(lowest), at(lowest + 1))
242}
243
244struct Pieces(Vec<((u32, u32), i32, u32)>);
246
247impl Pieces {
248 fn along(&self, u: u32, v: u32) -> (i32, u32) {
251 let key = (u.min(v), u.max(v));
252 match self.0.binary_search_by_key(&key, |e| e.0) {
253 Ok(i) if u < v => (self.0[i].1, self.0[i].2),
254 Ok(i) => (-self.0[i].1, self.0[i].2),
255 Err(_) => (0, 0),
256 }
257 }
258}
259
260fn ring_pieces(points: &[Point2], kept: &[u32], splits: &Splits, rings: &[Ring]) -> Pieces {
263 let mut raw: Vec<((u32, u32), i32, u32)> = Vec::new();
264 for (r, ring) in rings.iter().enumerate() {
265 let seq = kept_vertices(ring, kept);
266 let outer = ring.kind == RingKind::Outer;
267 let sign = if is_ccw(points, &seq) == outer { 1 } else { -1 };
268 let m = seq.len();
269 for i in 0..m {
270 let (u, v) = (seq[i], seq[(i + 1) % m]);
271 let key = Constraint::new(u, v);
272 let mut chain: Vec<u32> = splits
273 .chain(key)
274 .map_or_else(|| vec![key.a, key.b], <[u32]>::to_vec);
275 if u != key.a {
276 chain.reverse();
277 }
278 for w in chain.windows(2) {
279 let (p, q) = (w[0], w[1]);
280 if p < q {
281 raw.push(((p, q), sign, r as u32));
282 } else {
283 raw.push(((q, p), -sign, r as u32));
284 }
285 }
286 }
287 }
288 raw.sort_unstable();
289 let mut net: Vec<((u32, u32), i32, u32)> = Vec::new();
290 for (key, delta, ring) in raw {
291 match net.last_mut() {
292 Some(last) if last.0 == key => {
293 last.1 += delta;
294 last.2 = last.2.min(ring);
295 }
296 _ => net.push((key, delta, ring)),
297 }
298 }
299 Pieces(net)
300}
301
302fn classify(
304 tri: &mut Triangulation,
305 kept: &[u32],
306 splits: &Splits,
307 rings: &[Ring],
308) -> Result<(), TriangulationError> {
309 let pieces = ring_pieces(&tri.points, kept, splits, rings);
310 let winding = flood(tri, &pieces)?;
311
312 let mut triangles = Vec::new();
313 for (t, corners) in tri.triangles.as_chunks::<3>().0.iter().enumerate() {
314 if winding[t] == 1 {
315 triangles.extend_from_slice(corners);
316 }
317 }
318 if triangles.is_empty() {
319 return Err(TriangulationError::EmptyDomain);
320 }
321 tri.triangles = triangles;
322 tri.rebuild_halfedges();
323
324 let mut edges: Vec<(u32, u32)> = tri
325 .triangles
326 .as_chunks::<3>()
327 .0
328 .iter()
329 .flat_map(|c| (0..3).map(move |i| (c[i].min(c[(i + 1) % 3]), c[i].max(c[(i + 1) % 3]))))
330 .collect();
331 edges.sort_unstable();
332 edges.dedup();
333 tri.constraints
334 .retain(|c| edges.binary_search(&(c.a, c.b)).is_ok());
335
336 let mut used = vec![false; tri.points.len()];
337 for &v in &tri.triangles {
338 used[v as usize] = true;
339 }
340 let mut merged = vec![false; tri.points.len()];
341 for m in &tri.merged {
342 merged[m.input as usize] = true;
343 }
344 tri.outside = (0..tri.points.len() as u32)
345 .filter(|&v| !used[v as usize] && !merged[v as usize])
346 .collect();
347 Ok(())
348}
349
350fn flood(tri: &Triangulation, pieces: &Pieces) -> Result<Vec<i32>, TriangulationError> {
352 use std::collections::VecDeque;
353
354 let edge = |t: usize, i: usize| (tri.triangles[3 * t + i], tri.triangles[3 * t + (i + 1) % 3]);
355 let seed = (0..tri.triangle_count())
356 .find_map(|t| {
357 (0..3)
358 .find(|&i| tri.halfedges[3 * t + i] == NO_HALFEDGE)
359 .map(|i| (t, i))
360 })
361 .ok_or(TriangulationError::EmptyDomain)?;
362
363 let (u, v) = edge(seed.0, seed.1);
366 let (net, ring) = pieces.along(u, v);
367 check_winding(net, ring)?;
368 let mut winding = vec![i32::MIN; tri.triangle_count()];
369 winding[seed.0] = net;
370 let mut queue = VecDeque::from([seed.0]);
371 while let Some(t) = queue.pop_front() {
372 for i in 0..3 {
373 let twin = tri.halfedges[3 * t + i];
374 let (u, v) = edge(t, i);
375 let (net, ring) = pieces.along(u, v);
376 if twin == NO_HALFEDGE {
377 debug_assert_eq!(winding[t], net, "hull edge disagrees with the flood");
378 continue;
379 }
380 let next = twin as usize / 3;
381 let w = winding[t] - net;
382 if winding[next] == i32::MIN {
383 check_winding(w, ring)?;
384 winding[next] = w;
385 queue.push_back(next);
386 } else {
387 debug_assert_eq!(winding[next], w, "the winding is path dependent");
388 }
389 }
390 }
391 Ok(winding)
392}
393
394fn check_winding(winding: i32, ring: u32) -> Result<(), TriangulationError> {
396 if (0..=1).contains(&winding) {
397 Ok(())
398 } else {
399 Err(TriangulationError::MisnestedRing { ring, winding })
400 }
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406 use crate::TriangulationError as E;
407
408 fn p(x: f64, y: f64) -> Point2 {
409 Point2::new(x, y)
410 }
411
412 fn square() -> Vec<Point2> {
413 vec![p(0.0, 0.0), p(4.0, 0.0), p(4.0, 4.0), p(0.0, 4.0)]
414 }
415
416 fn refused(points: &[Point2], rings: &[Ring]) -> E {
417 triangulate_domain(points, &[], rings).unwrap_err()
418 }
419
420 #[test]
421 fn a_ring_out_of_range_is_refused() {
422 assert_eq!(
423 refused(&square(), &[Ring::outer([0, 1, 9])]),
424 E::RingOutOfRange { ring: 0, index: 9 }
425 );
426 }
427
428 #[test]
429 fn a_short_ring_is_refused() {
430 assert_eq!(
431 refused(&square(), &[Ring::outer([0, 1])]),
432 E::TooFewRingVertices { ring: 0 }
433 );
434 }
435
436 #[test]
437 fn closing_a_ring_explicitly_is_a_repeat() {
438 assert_eq!(
439 refused(&square(), &[Ring::outer([0, 1, 2, 3, 0])]),
440 E::RepeatedRingVertex { ring: 0, index: 0 }
441 );
442 }
443
444 #[test]
445 fn a_coincident_index_is_a_repeat() {
446 let mut pts = square();
447 pts.push(p(0.0, 0.0));
448 assert_eq!(
449 refused(&pts, &[Ring::outer([0, 1, 2, 3, 4])]),
450 E::RepeatedRingVertex { ring: 0, index: 4 }
451 );
452 }
453
454 #[test]
455 fn three_collinear_vertices_fold() {
456 let pts = vec![p(0.0, 0.0), p(1.0, 0.0), p(2.0, 0.0), p(0.0, 3.0)];
457 assert_eq!(
458 refused(&pts, &[Ring::outer([0, 1, 2])]),
459 E::FoldedRing { ring: 0, index: 0 }
460 );
461 }
462
463 #[test]
464 fn a_spike_folds() {
465 let pts = vec![
468 p(0.0, 0.0),
469 p(4.0, 0.0),
470 p(4.0, 4.0),
471 p(0.0, 4.0),
472 p(6.0, 0.0),
473 p(5.0, 0.0),
474 ];
475 assert_eq!(
476 refused(&pts, &[Ring::outer([0, 1, 4, 5, 2, 3])]),
477 E::FoldedRing { ring: 0, index: 4 }
478 );
479 }
480
481 #[test]
482 fn a_straight_vertex_is_accepted() {
483 let mut pts = square();
484 pts.push(p(2.0, 0.0));
485 let tri = triangulate_domain(&pts, &[], &[Ring::outer([0, 4, 1, 2, 3])]).unwrap();
486 assert_eq!(tri.triangle_count(), 3);
487 assert!(tri.outside_points().is_empty());
488 }
489
490 #[test]
491 fn orientation_is_read_at_a_reflex_first_vertex() {
492 let pts = vec![
495 p(2.0, 1.0),
496 p(4.0, 0.0),
497 p(4.0, 4.0),
498 p(0.0, 4.0),
499 p(0.0, 0.0),
500 ];
501 assert!(is_ccw(&pts, &[0, 1, 2, 3, 4]));
502 assert!(!is_ccw(&pts, &[4, 3, 2, 1, 0]));
503 let a = triangulate_domain(&pts, &[], &[Ring::outer([0, 1, 2, 3, 4])]).unwrap();
504 let b = triangulate_domain(&pts, &[], &[Ring::outer([4, 3, 2, 1, 0])]).unwrap();
505 assert_eq!(a.triangle_count(), 3);
506 assert_eq!(b.triangle_count(), 3);
507 }
508
509 #[test]
510 fn an_empty_ring_list_is_an_empty_domain() {
511 assert_eq!(refused(&square(), &[]), E::EmptyDomain);
512 }
513}