Skip to main content

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}