axiolid_triangulate/build.rs
1// SPDX-License-Identifier: MPL-2.0
2
3//! Incremental constrained Delaunay construction.
4//!
5//! Points are inserted one at a time: located by a walk, split one into
6//! three (or, on an edge, two into four), then legalised by Lawson flips.
7//! Each split and flip re-links only the twin pointers of the triangles it
8//! rewrites and of their outside neighbours,
9//! so a step costs O(1) beyond the walk; recomputing the whole twin array
10//! after each one, as 0.3.2 did, made n points cost O(n^2) or worse
11//! (#268). Debug builds compare the result with a full recomputation
12//! after every step (see the `adjacency` module). The walk starts next
13//! to a grid hint (see the `hint` module), so it crosses a few triangles
14//! rather than O(sqrt n).
15//!
16//! # The vertex at infinity
17//!
18//! The build starts from one triangle of input points and closes the
19//! convex hull with a single symbolic vertex at infinity, the standard
20//! construction of Shewchuk's Triangle and CGAL's `Triangulation_2`:
21//! every hull edge carries a *ghost* triangle with the infinite vertex on
22//! its outer side, so the mesh is a closed surface and every halfedge has
23//! a twin. The infinite vertex has no coordinates; the predicates decide
24//! it symbolically:
25//!
26//! - a point lies in a ghost triangle when it lies strictly on the outer
27//! side of the ghost's hull edge: the ghost's "circumcircle" is that
28//! open half-plane;
29//! - the infinite vertex lies in no finite triangle's circumcircle, so a
30//! hull edge never flips;
31//! - an edge to the infinite vertex flips when the new point sees the hull
32//! edge beyond it strictly, which wraps the hull round a point inserted
33//! outside it; on that edge's line it does not, so collinear hull
34//! points stay hull vertices.
35//!
36//! Removing the ghosts leaves exactly the Delaunay triangulation of the
37//! input, its convex hull covered. The finite super triangle this replaced
38//! (32 times the input's span) lost thin triangles along the hull whose
39//! circumcircles reached its corners, without a word (#299): 3,977
40//! instead of 3,979 triangles for 2,000 random points.
41//!
42//! When the Delaunay triangulation is unique (no four input points
43//! cocircular), the output is the same set of triangles as before for
44//! every input the super triangle got right; their order in the list
45//! differs, because the build starts from a different first triangle.
46//! Where four or more points are cocircular the Delaunay triangulation is
47//! not unique, and the two builds may choose differently.
48//!
49//! # Coincident points
50//!
51//! A point at exactly the position of one already inserted is merged into
52//! it (#300): the walk ends in a triangle with that vertex as a corner,
53//! and the point is recorded instead of split in. Splitting it in, as the
54//! build did, left zero-area and overlapping triangles that used one
55//! directed edge twice. The merge is reported through
56//! [`Triangulation::merged_points`] (input index to kept vertex), so
57//! nothing is lost. The kept vertex is the lowest input index at that
58//! position: points are inserted in input order, and the first triangle
59//! takes the first index at each of its positions. Constraints are carried
60//! over to the kept vertices; one whose two endpoints coincide asks for an
61//! edge from a vertex to itself and is refused as
62//! [`TriangulationError::CoincidentConstraint`].
63
64use axiolid_core::Point2;
65use axiolid_guarantees::Sign;
66use axiolid_predicates::orient2d;
67
68use crate::adjacency::{debug_check, link, note_update};
69use crate::domain::{check_ring_sizes, check_rings, Ring};
70use crate::hint::CellHints;
71use crate::mesh::{MergedPoint, Triangulation, TriangulationError, NO_HALFEDGE};
72use crate::recover::{recover_constraints, Splits};
73use crate::{decided, in_circumcircle, turns_left, Constraint};
74
75// ---------------------------------------------------------------------------
76// Construction
77// ---------------------------------------------------------------------------
78
79/// Build a constrained Delaunay triangulation of `points`.
80///
81/// Every edge in `constraints` appears in the output as an edge of some
82/// triangle. Away from constraints the result satisfies the Delaunay
83/// empty-circumcircle property, decided exactly.
84///
85/// # Errors
86///
87/// Returns [`TriangulationError`] when the input has fewer than three points,
88/// a point with a non-finite coordinate, is entirely collinear, references a vertex that does not exist, has a
89/// constraint between two coincident points, or contains constraints that
90/// cross one another.
91///
92/// Exactly coincident points are merged into the lowest input index at
93/// their position and reported by [`Triangulation::merged_points`]; the
94/// triangles and [`Triangulation::constraints`] use the kept indices.
95pub fn triangulate(
96 points: &[Point2],
97 constraints: &[Constraint],
98) -> Result<Triangulation, TriangulationError> {
99 build(points, constraints, None).map(|built| built.tri)
100}
101
102/// A built triangulation with what the domain filter needs from the build.
103pub(crate) struct Built {
104 pub(crate) tri: Triangulation,
105 /// The constraints that were split, and where.
106 pub(crate) splits: Splits,
107 /// The vertex each input point is: itself, or the coincident point it
108 /// was merged into.
109 pub(crate) kept: Vec<u32>,
110}
111
112/// The shared build of [`triangulate`] and `triangulate_domain`.
113///
114/// `rings` is `None` for the plain hull triangulation. The checks run in a
115/// fixed order, so the same bad input always reports the same error: too
116/// few points, non-finite points, constraint range, ring range and size,
117/// empty ring list, collinear points, coincident constraints, ring shape, then recovery.
118pub(crate) fn build(
119 points: &[Point2],
120 constraints: &[Constraint],
121 rings: Option<&[Ring]>,
122) -> Result<Built, TriangulationError> {
123 if points.len() < 3 {
124 return Err(TriangulationError::TooFewPoints);
125 }
126 if let Some(index) = points
127 .iter()
128 .position(|p| !(p.x.is_finite() && p.y.is_finite()))
129 {
130 return Err(TriangulationError::NonFinitePoint {
131 index: u32::try_from(index).unwrap_or(u32::MAX),
132 });
133 }
134 let count = u32::try_from(points.len()).unwrap_or(u32::MAX);
135 for c in constraints {
136 if c.a >= count || c.b >= count {
137 let index = if c.a >= count { c.a } else { c.b };
138 return Err(TriangulationError::ConstraintOutOfRange { index });
139 }
140 }
141 if let Some(rings) = rings {
142 check_ring_sizes(rings, count)?;
143 }
144 if !has_non_collinear_triple(points) {
145 return Err(TriangulationError::AllPointsCollinear);
146 }
147
148 let mut state = Builder::new(points);
149 state.insert_all(first_triangle(points))?;
150 let mut constraints = state.carry_over(constraints)?;
151 if let Some(rings) = rings {
152 constraints.extend(check_rings(points, &state.kept, rings)?);
153 }
154 let kept = state.kept.clone();
155 let mut tri = state.finish(points, &constraints);
156 let splits = recover_constraints(&mut tri)?;
157 Ok(Built { tri, splits, kept })
158}
159
160/// Whether the input contains three points that are not collinear.
161///
162/// Checked with the exact predicate rather than by an area threshold: a
163/// tolerance here would reject a legitimately thin but non-degenerate input.
164fn has_non_collinear_triple(points: &[Point2]) -> bool {
165 let a = points[0];
166 // Find the first point distinct from `a`, then the first that is not on
167 // the line through the two. Scanning is linear and runs once.
168 let Some(b) = points.iter().copied().find(|p| *p != a) else {
169 return false;
170 };
171 points
172 .iter()
173 .any(|&c| turns_left(a, b, c) || turns_left(b, a, c))
174}
175
176/// The first three points that span a triangle, counter-clockwise.
177///
178/// The first point, the first point distinct from it, and the first point
179/// off the line through the two; the caller has checked that one exists.
180/// For points in general position that is `(0, 1, 2)`, so the insertion
181/// order is the input order, as it was with the super triangle.
182fn first_triangle(points: &[Point2]) -> [u32; 3] {
183 let a = 0;
184 let b = points.iter().position(|p| *p != points[0]).unwrap_or(1);
185 let (c, sign) = points
186 .iter()
187 .enumerate()
188 .find_map(|(k, &q)| match decided(orient2d(points[a], points[b], q)) {
189 Sign::Zero => None,
190 sign => Some((k, sign)),
191 })
192 .unwrap_or((2, Sign::Positive));
193 if sign == Sign::Positive {
194 [a as u32, b as u32, c as u32]
195 } else {
196 [a as u32, c as u32, b as u32]
197 }
198}
199
200/// Incremental Delaunay construction closed by a vertex at infinity.
201struct Builder {
202 /// The input points. The infinite vertex has no entry, so reading its
203 /// position by mistake panics instead of computing with a stand-in.
204 points: Vec<Point2>,
205 triangles: Vec<u32>,
206 halfedges: Vec<u32>,
207 /// The infinite vertex: one past the last input point.
208 infinite: u32,
209 /// The vertex each input point is: itself, or the coincident point it
210 /// was merged into.
211 kept: Vec<u32>,
212 /// A triangle at each vertex, kept current by every split and flip,
213 /// so a walk can start next to a hinted vertex.
214 corner: Vec<u32>,
215 hints: CellHints,
216}
217
218impl Builder {
219 fn new(points: &[Point2]) -> Self {
220 let (min, max) = bounds(points);
221 let n = points.len();
222 Self {
223 points: points.to_vec(),
224 triangles: Vec::new(),
225 halfedges: Vec::new(),
226 infinite: n as u32,
227 kept: (0..n as u32).collect(),
228 corner: vec![NO_HALFEDGE; n + 1],
229 hints: CellHints::new(min, max, n),
230 }
231 }
232
233 /// Start from the triangle `first` and its three ghosts, then insert
234 /// every other point in input order.
235 fn insert_all(&mut self, first: [u32; 3]) -> Result<(), TriangulationError> {
236 let [a, b, c] = first;
237 let g = self.infinite;
238 // Slot 0 is the finite triangle; slots 1, 2 and 3 are the ghosts
239 // across its edges a -> b, b -> c and c -> a.
240 self.triangles = vec![a, b, c, b, a, g, c, b, g, a, c, g];
241 self.halfedges = vec![NO_HALFEDGE; 12];
242 let h = &mut self.halfedges;
243 link(h, 0, 3); // a -> b | b -> a
244 link(h, 1, 6); // b -> c | c -> b
245 link(h, 2, 9); // c -> a | a -> c
246 link(h, 4, 11); // a -> g | g -> a
247 link(h, 7, 5); // b -> g | g -> b
248 link(h, 10, 8); // c -> g | g -> c
249 for v in first {
250 self.corner[v as usize] = 0;
251 self.hints.set(self.points[v as usize], v);
252 }
253 self.corner[g as usize] = 1;
254 note_update();
255 debug_check(&self.triangles, &self.halfedges);
256 for v in 0..self.infinite {
257 if !first.contains(&v) {
258 self.insert_point(v)?;
259 }
260 }
261 Ok(())
262 }
263
264 /// The slot of the infinite vertex in triangle `t`, if it is a ghost.
265 fn ghost_slot(&self, t: usize) -> Option<usize> {
266 (0..3).find(|&i| self.triangles[3 * t + i] == self.infinite)
267 }
268
269 /// Insert one vertex, splitting its containing triangle and restoring the
270 /// Delaunay property by flipping.
271 fn insert_point(&mut self, v: u32) -> Result<(), TriangulationError> {
272 // A vertex that cannot be located is a corrupted adjacency, not a
273 // benign skip. Dropping it silently produced a triangulation missing
274 // an input point -- the caller would get a valid-looking mesh with a
275 // hole where their vertex should be.
276 let p = self.points[v as usize];
277 let Some(t) = self.locate(p) else {
278 return Err(TriangulationError::VertexUnplaceable { index: v });
279 };
280 let (a, b, c) = (
281 self.triangles[3 * t],
282 self.triangles[3 * t + 1],
283 self.triangles[3 * t + 2],
284 );
285
286 // At a vertex already inserted: merge, and report it (#300). A
287 // point at a vertex lies in the closure of exactly the triangles
288 // around it, so the triangle found has that vertex as a corner. A
289 // ghost holds only points strictly outside the hull, and its
290 // infinite corner has no position to compare.
291 if self.ghost_slot(t).is_none() {
292 if let Some(&at) = [a, b, c].iter().find(|&&u| self.points[u as usize] == p) {
293 self.kept[v as usize] = at;
294 return Ok(());
295 }
296 }
297 self.hints.set(p, v);
298
299 // Outside the hull: the ghost's hull edge is visible from `p`. The
300 // split gives one finite triangle and two ghosts, and the flips
301 // wrap the hull round `p`.
302 if self.ghost_slot(t).is_some() {
303 let (t1, t2) = self.split_triangle(t, (a, b, c), v);
304 self.legalize(3 * t);
305 self.legalize(3 * t1);
306 self.legalize(3 * t2);
307 return Ok(());
308 }
309
310 // On an edge: split the edge, and the triangles either side of it,
311 // rather than the one triangle into three -- one of the three would
312 // have no area, and flips over a triangle with no area never settle.
313 for i in 0..3 {
314 let (u, w) = (
315 self.triangles[3 * t + i],
316 self.triangles[3 * t + (i + 1) % 3],
317 );
318 if decided(orient2d(
319 self.points[u as usize],
320 self.points[w as usize],
321 p,
322 )) != Sign::Zero
323 {
324 continue;
325 }
326 if self.halfedges[3 * t + i] == NO_HALFEDGE {
327 return Err(TriangulationError::VertexUnplaceable { index: v });
328 }
329 let [t, ot, t1, t2] = self.split_edge(t, i, v);
330 for tri in [t, ot, t1, t2] {
331 self.legalize(3 * tri);
332 }
333 return Ok(());
334 }
335
336 let (t1, t2) = self.split_triangle(t, (a, b, c), v);
337 self.legalize(3 * t);
338 self.legalize(3 * t1);
339 self.legalize(3 * t2);
340 Ok(())
341 }
342
343 /// Split triangle `t` = `(a, b, c)` into three around `v`, keeping the
344 /// twin pointers current, and return the two appended triangles.
345 ///
346 /// Slot `t` is reused for `(a, b, v)`, and `(b, c, v)` and `(c, a, v)`
347 /// are appended, so existing neighbour indices into `t` stay valid
348 /// where they can. Where they cannot is the trap: the old neighbours across
349 /// `b -> c` and `c -> a` still point at halfedges `3t + 1` and `3t + 2`,
350 /// which now run `b -> v` and `v -> a`. Those two neighbours are
351 /// re-pointed at the appended triangles here; missing it corrupts the
352 /// mesh in a way that only shows several insertions later, as a walk
353 /// that leaves it.
354 fn split_triangle(&mut self, t: usize, (a, b, c): (u32, u32, u32), v: u32) -> (usize, usize) {
355 let across_bc = self.halfedges[3 * t + 1];
356 let across_ca = self.halfedges[3 * t + 2];
357 let t1 = self.triangles.len() / 3;
358 let t2 = t1 + 1;
359 self.triangles[3 * t] = a;
360 self.triangles[3 * t + 1] = b;
361 self.triangles[3 * t + 2] = v;
362 self.triangles.extend_from_slice(&[b, c, v]);
363 self.triangles.extend_from_slice(&[c, a, v]);
364 self.halfedges.extend_from_slice(&[NO_HALFEDGE; 6]);
365
366 // `a -> b` keeps its twin in slot `3t`.
367 let h = &mut self.halfedges;
368 link(h, 3 * t1, across_bc);
369 link(h, 3 * t2, across_ca);
370 link(h, 3 * t + 1, (3 * t1 + 2) as u32); // b -> v | v -> b
371 link(h, 3 * t1 + 1, (3 * t2 + 2) as u32); // c -> v | v -> c
372 link(h, 3 * t2 + 1, (3 * t + 2) as u32); // a -> v | v -> a
373 self.corner[c as usize] = t1 as u32;
374 self.corner[v as usize] = t as u32;
375 note_update();
376 debug_check(&self.triangles, &self.halfedges);
377 (t1, t2)
378 }
379
380 /// Split edge `i` of triangle `t`, and the triangles either side of it,
381 /// at `v`, keeping the twin pointers current. Returns the four
382 /// triangles: the two reused slots, then the two appended.
383 ///
384 /// `(u, w, x)` and `(w, u, y)` become `(x, u, v)`, `(y, w, v)`,
385 /// `(w, x, v)` and `(u, y, v)`. Every outside edge moves to a new slot,
386 /// so all four outside neighbours are re-pointed.
387 fn split_edge(&mut self, t: usize, i: usize, v: u32) -> [usize; 4] {
388 let twin = self.halfedges[3 * t + i] as usize;
389 let u = self.triangles[3 * t + i];
390 let w = self.triangles[3 * t + (i + 1) % 3];
391 let x = self.triangles[3 * t + (i + 2) % 3];
392 let ot = twin / 3;
393 let oi = twin % 3;
394 let y = self.triangles[3 * ot + (oi + 2) % 3];
395 let across_wx = self.halfedges[3 * t + (i + 1) % 3];
396 let across_xu = self.halfedges[3 * t + (i + 2) % 3];
397 let across_uy = self.halfedges[3 * ot + (oi + 1) % 3];
398 let across_yw = self.halfedges[3 * ot + (oi + 2) % 3];
399
400 let t1 = self.triangles.len() / 3;
401 let t2 = t1 + 1;
402 self.triangles[3 * t] = x;
403 self.triangles[3 * t + 1] = u;
404 self.triangles[3 * t + 2] = v;
405 self.triangles[3 * ot] = y;
406 self.triangles[3 * ot + 1] = w;
407 self.triangles[3 * ot + 2] = v;
408 self.triangles.extend_from_slice(&[w, x, v]);
409 self.triangles.extend_from_slice(&[u, y, v]);
410 self.halfedges.extend_from_slice(&[NO_HALFEDGE; 6]);
411
412 let h = &mut self.halfedges;
413 link(h, 3 * t, across_xu);
414 link(h, 3 * ot, across_yw);
415 link(h, 3 * t1, across_wx);
416 link(h, 3 * t2, across_uy);
417 link(h, 3 * t + 1, (3 * t2 + 2) as u32); // u -> v | v -> u
418 link(h, 3 * t + 2, (3 * t1 + 1) as u32); // v -> x | x -> v
419 link(h, 3 * ot + 1, (3 * t1 + 2) as u32); // w -> v | v -> w
420 link(h, 3 * ot + 2, (3 * t2 + 1) as u32); // v -> y | y -> v
421 for (vertex, tri) in [(u, t), (w, ot), (v, t)] {
422 self.corner[vertex as usize] = tri as u32;
423 }
424 note_update();
425 debug_check(&self.triangles, &self.halfedges);
426 [t, ot, t1, t2]
427 }
428
429 /// Find a triangle containing `p`.
430 ///
431 /// The walk starts next to the last vertex inserted in `p`'s grid cell
432 /// (see the `hint` module). A point strictly inside the triangle found
433 /// has no other; a point on an edge or vertex, or a hinted walk that
434 /// fails, is located again by the walk from the last triangle, the one
435 /// the build always made, so the choice between neighbouring triangles,
436 /// and with it the output, is unchanged.
437 fn locate(&self, p: Point2) -> Option<usize> {
438 let last = self.triangles.len() / 3 - 1;
439 let start = self.hints.get(p).map_or(last, |v| {
440 let t = self.corner[v as usize] as usize;
441 // A stale entry would still be a valid start, only a slower
442 // one; holding it exact keeps the bookkeeping honest.
443 debug_assert!(
444 self.triangles[3 * t..3 * t + 3].contains(&v),
445 "vertex {v} is not a corner of its recorded triangle {t}"
446 );
447 t
448 });
449 if start != last {
450 if let Some(t) = self.walk(start, p) {
451 if self.strictly_inside(t, p) {
452 return Some(t);
453 }
454 }
455 }
456 self.walk(last, p)
457 }
458
459 /// Whether `p` lies strictly inside finite triangle `t`, off its edges.
460 ///
461 /// Never for a ghost: a point outside the hull can see several hull
462 /// edges, so the ghost a walk ends in need not be the only one.
463 fn strictly_inside(&self, t: usize, p: Point2) -> bool {
464 self.ghost_slot(t).is_none()
465 && (0..3).all(|i| {
466 let u = self.points[self.triangles[3 * t + i] as usize];
467 let w = self.points[self.triangles[3 * t + (i + 1) % 3] as usize];
468 decided(orient2d(u, w, p)) == Sign::Positive
469 })
470 }
471
472 /// Walk from triangle `start` to a finite triangle whose closure holds
473 /// `p`, or to a ghost whose hull edge `p` sees strictly.
474 ///
475 /// A straight walk needs no auxiliary structure. The ghosts close the
476 /// mesh, so no halfedge lacks a twin; `None` means a corrupted mesh, or
477 /// a walk that did not settle within one step per triangle.
478 fn walk(&self, start: usize, p: Point2) -> Option<usize> {
479 let mut t = start;
480 for _ in 0..self.triangles.len() {
481 #[cfg(test)]
482 crate::hint::tests::STEPS.with(|c| c.set(c.get() + 1));
483 if let Some(k) = self.ghost_slot(t) {
484 match self.ghost_step(t, k, p) {
485 Some(next) => t = next,
486 None => return Some(t),
487 }
488 continue;
489 }
490 let (a, b, c) = (
491 self.points[self.triangles[3 * t] as usize],
492 self.points[self.triangles[3 * t + 1] as usize],
493 self.points[self.triangles[3 * t + 2] as usize],
494 );
495 // Step across the first edge that `p` lies strictly outside of.
496 // For a counter-clockwise triangle, "outside edge (u, w)" means
497 // p is strictly to the RIGHT of u->w. Testing `turns_left(w, u, p)`
498 // instead also fires when p is exactly ON the edge, which sends
499 // the walk across a boundary it should have stopped at.
500 let mut moved = false;
501 for (i, (u, w)) in [(a, b), (b, c), (c, a)].into_iter().enumerate() {
502 let right_of = decided(orient2d(u, w, p)) == Sign::Negative;
503 if right_of {
504 let twin = self.halfedges[3 * t + i];
505 if twin == NO_HALFEDGE {
506 return None;
507 }
508 t = twin as usize / 3;
509 moved = true;
510 break;
511 }
512 }
513 if !moved {
514 return Some(t);
515 }
516 }
517 None
518 }
519
520 /// The next triangle of a walk standing in ghost `t`, whose infinite
521 /// vertex is in slot `k`, or `None` when `p` lies in it.
522 ///
523 /// The ghost of hull edge `a -> b` holds the points strictly on the
524 /// edge's outer side. Any other point steps across `a -> b` into the
525 /// hull, and the walk goes on from there: one on the edge stops in
526 /// the finite triangle beside it, and one on the edge's line beyond an
527 /// end lies strictly outside another hull edge, which the walk then
528 /// crosses into that edge's ghost. A walk only stands in a ghost it did
529 /// not enter that way at its start (a hint or the last triangle), since
530 /// it enters a ghost only across a hull edge `p` lies strictly outside.
531 fn ghost_step(&self, t: usize, k: usize, p: Point2) -> Option<usize> {
532 let edge = 3 * t + (k + 1) % 3;
533 let a = self.points[self.triangles[edge] as usize];
534 let b = self.points[self.triangles[3 * t + (k + 2) % 3] as usize];
535 if decided(orient2d(a, b, p)) == Sign::Positive {
536 None
537 } else {
538 Some(self.halfedges[edge] as usize / 3)
539 }
540 }
541
542 /// Whether the edge `p0 -> p1` of the triangle `(p0, p1, apex)` must
543 /// flip, with `other` the vertex across it.
544 ///
545 /// A finite quadrilateral flips when `other` lies strictly inside the
546 /// circumcircle and the quadrilateral is strictly convex. With the
547 /// infinite vertex: nothing lies beyond the hull, so a hull edge never
548 /// flips (`other` infinite); an edge to the infinite vertex flips when
549 /// `other` lies strictly on the outer side of the ghost's hull edge,
550 /// which also makes the finite triangle the flip creates
551 /// counter-clockwise. On that edge's line it does not flip, and the
552 /// collinear vertex stays on the hull.
553 fn must_flip(&self, p0: u32, p1: u32, apex: u32, other: u32) -> bool {
554 let g = self.infinite;
555 let q = |k: u32| self.points[k as usize];
556 if other == g || apex == g {
557 return false;
558 }
559 if p1 == g {
560 // (p0, ∞, apex) is the ghost of hull edge apex -> p0.
561 return turns_left(q(apex), q(p0), q(other));
562 }
563 if p0 == g {
564 // (∞, p1, apex) is the ghost of hull edge p1 -> apex.
565 return turns_left(q(p1), q(apex), q(other));
566 }
567 in_circumcircle(q(p0), q(p1), q(apex), q(other))
568 // Only a strictly convex quadrilateral flips without inverting.
569 && turns_left(q(apex), q(p0), q(other))
570 && turns_left(q(other), q(p1), q(apex))
571 }
572
573 /// Restore the Delaunay property across `edge`, whose triangle's third
574 /// corner is the vertex just inserted, and propagate to the edges
575 /// across from it.
576 fn legalize(&mut self, edge: usize) {
577 let mut stack = vec![edge];
578 // Lawson's flips after one insertion are few; the bound only keeps
579 // a corrupted mesh from spinning. Anything left is repaired after
580 // constraint recovery.
581 let mut budget = 4 * self.triangles.len() + 64;
582 while let Some(e) = stack.pop() {
583 if budget == 0 {
584 break;
585 }
586 budget -= 1;
587 let twin = self.halfedges[e];
588 if twin == NO_HALFEDGE {
589 continue;
590 }
591 let t = e / 3;
592 let ti = e % 3;
593 let o = twin as usize;
594 let ot = o / 3;
595 let oi = o % 3;
596
597 let p0 = self.triangles[3 * t + ti];
598 let p1 = self.triangles[3 * t + (ti + 1) % 3];
599 let apex = self.triangles[3 * t + (ti + 2) % 3];
600 let other = self.triangles[3 * ot + (oi + 2) % 3];
601
602 if !self.must_flip(p0, p1, apex, other) {
603 continue;
604 }
605
606 // Flip the shared edge to the other diagonal: (p0, other, apex)
607 // and (p1, apex, other). Slot `ti` of `t` now runs p0 -> other,
608 // whose outside twin was across `ot`'s p0 -> other; slot `oi` of
609 // `ot` runs p1 -> apex, whose twin was across `t`'s p1 -> apex.
610 // The new diagonal pairs the two `+ 1` slots, and the remaining
611 // edges keep both their slot and their twin.
612 let across_p0_other = self.halfedges[3 * ot + (oi + 1) % 3];
613 let across_p1_apex = self.halfedges[3 * t + (ti + 1) % 3];
614 self.triangles[3 * t + (ti + 1) % 3] = other;
615 self.triangles[3 * ot + (oi + 1) % 3] = apex;
616 let h = &mut self.halfedges;
617 link(h, 3 * t + ti, across_p0_other);
618 link(h, 3 * ot + oi, across_p1_apex);
619 link(h, 3 * t + (ti + 1) % 3, (3 * ot + (oi + 1) % 3) as u32);
620 // p1 left `t` and p0 left `ot`.
621 self.corner[p0 as usize] = t as u32;
622 self.corner[p1 as usize] = ot as u32;
623 note_update();
624 debug_check(&self.triangles, &self.halfedges);
625
626 // The two edges now across from the inserted vertex: p0 -> other
627 // and other -> p1. (Pushing the new diagonal instead, as this
628 // did, checked an edge that is Delaunay by construction and left
629 // these unchecked; thin quads then flipped back and forth.)
630 stack.push(3 * t + ti);
631 stack.push(3 * ot + (oi + 2) % 3);
632 }
633 }
634
635 /// The constraints between kept vertices, or the first one whose two
636 /// endpoints were merged into one vertex.
637 fn carry_over(
638 &self,
639 constraints: &[Constraint],
640 ) -> Result<Vec<Constraint>, TriangulationError> {
641 constraints
642 .iter()
643 .map(|c| {
644 let (a, b) = (self.kept[c.a as usize], self.kept[c.b as usize]);
645 if a == b && c.a != c.b {
646 Err(TriangulationError::CoincidentConstraint { a: c.a, b: c.b })
647 } else {
648 Ok(Constraint::new(a, b))
649 }
650 })
651 .collect()
652 }
653
654 /// Drop the ghosts; the finite triangles cover the convex hull.
655 fn finish(self, original: &[Point2], constraints: &[Constraint]) -> Triangulation {
656 let g = self.infinite;
657 let mut triangles = Vec::with_capacity(self.triangles.len());
658 for t in self.triangles.as_chunks::<3>().0 {
659 if !t.contains(&g) {
660 triangles.extend_from_slice(t);
661 }
662 }
663 let mut sorted = constraints.to_vec();
664 sorted.sort_unstable();
665 sorted.dedup();
666 let mut out = Triangulation {
667 points: original.to_vec(),
668 triangles,
669 halfedges: Vec::new(),
670 constraints: sorted,
671 outside: Vec::new(),
672 domain: None,
673 merged: (0..self.kept.len() as u32)
674 .filter(|&v| self.kept[v as usize] != v)
675 .map(|v| MergedPoint {
676 input: v,
677 kept: self.kept[v as usize],
678 })
679 .collect(),
680 };
681 out.rebuild_halfedges();
682 out
683 }
684}
685
686/// Axis-aligned bounds of a point set.
687fn bounds(points: &[Point2]) -> (Point2, Point2) {
688 let mut min = points[0];
689 let mut max = points[0];
690 for p in &points[1..] {
691 min.x = min.x.min(p.x);
692 min.y = min.y.min(p.y);
693 max.x = max.x.max(p.x);
694 max.y = max.y.max(p.y);
695 }
696 (min, max)
697}