Skip to main content

ogeom_mesh/
editable.rs

1//! An indexed triangle mesh held for editing, with the topology queries
2//! edits need: which faces meet across each edge, the connected pieces,
3//! and the open rims.
4//!
5//! # Half-edges
6//!
7//! Side `k` of face `f` is half-edge `3 f + k`, running from the face's
8//! corner `k` to corner `k + 1`. Two half-edges are twins when they run one
9//! edge opposite ways and the faces are joined across it; a half-edge with
10//! no twin lies on a rim.
11//!
12//! # Points, and the vertices meeting at them
13//!
14//! Every vertex is a copy of a *point*: the welded vertex of the source it
15//! came from. Faces meet across an edge when their corners name the same
16//! two points, and are joined across it by this rule, read only from the
17//! faces on that edge:
18//!
19//! - two faces running it opposite ways are joined;
20//! - two running it the same way are not, and the edge is open on both;
21//! - three or more are taken in turn about the edge, and each one joined
22//!   to the next where the space between them lies behind both, as the
23//!   material of a closed body does. Two closed bodies sharing an edge each
24//!   close on their own; a fin is joined to neither side.
25//!
26//! The faces about a point, joined across the edges at it, make fans. A
27//! point one fan meets is one vertex; a point several fans meet, as at the
28//! centre of a bowtie or where two bodies touch, is split into one vertex
29//! per fan, each a copy of the point at its position, so that every vertex
30//! has one fan and every rim one way on. [`Editable::non_manifold`] reports
31//! the copies, and the ends of the edges the rule could not join cleanly.
32//!
33//! # What edits keep
34//!
35//! Face and vertex numbers are stable: an edit never renumbers what it
36//! leaves. A removed face keeps its number, unused; face `f` below the
37//! source's triangle count is source triangle `f`. After an edit, the fans
38//! about a point it touched, taken in the order of their lowest-numbered
39//! half-edge, each keep the lowest-numbered vertex their corners held that
40//! no fan before took, and a fan left without one takes a new copy of the
41//! point, numbered after every vertex there is. So a vertex split keeps its
42//! number on one side, fans joined again share the lowest number among
43//! them, and undoing an edit, flipping a face back, gives back the numbers
44//! it had. A vertex an edit leaves with no face is gone.
45//! [`Editable::to_triangulation`] is the compaction step: it numbers the
46//! vertices left in order, then the faces left in order.
47//!
48//! # Source vertices
49//!
50//! Every vertex and every copy of it remembers the source vertices it came
51//! from, through every edit: [`Editable::source_vertices`]. A vertex of
52//! the source has one, the vertex it was welded onto. A collapse, by
53//! [`Editable::simplify`] or by [`Editable::offset`] repairing a fold,
54//! leaves the vertex it keeps with the source vertices of both. A vertex
55//! an edit makes rather than carries, on an edge it cut or in a hole it
56//! filled, has none, and adds none to a vertex it is merged with.
57//! [`Editable::source_vertex`] names the source vertex where there is
58//! exactly one.
59//!
60//! A marking of the source's vertices selects the vertices all of whose
61//! source vertices it marks, and at least one:
62//! [`Editable::region_from_sources`] turns it into the per-vertex region
63//! that [`Editable::offset`] and [`Editable::smooth`] take. Provenance is
64//! only ever carried; nothing is matched by position.
65
66use ogeom_core::{FastMap, OgeomResult, ogeom_bail};
67use ogeom_math::{Aabb, Point, Vector};
68use ogeom_topo::Triangulation;
69
70pub mod cut;
71pub mod fill;
72pub mod offset;
73pub mod simplify;
74pub mod smooth;
75
76pub use cut::{Cap, CutReport, Side};
77pub use fill::{Fairing, Fill, FillReport, OPTIMAL_UP_TO};
78pub use offset::{OFFSET_EDGE_ANGLE, OFFSET_FARTHEST, OffsetReport};
79pub use simplify::SimplifyReport;
80pub use smooth::{PASSES, Smooth, SmoothReport};
81
82/// No half-edge, face or vertex.
83const NONE: u32 = u32::MAX;
84
85/// The half-edge after `h` round its face.
86const fn next(h: u32) -> u32 {
87    if h % 3 == 2 { h - 2 } else { h + 1 }
88}
89
90/// The half-edge before `h` round its face.
91const fn prev(h: u32) -> u32 {
92    if h.is_multiple_of(3) { h + 2 } else { h - 1 }
93}
94
95fn index(i: usize) -> u32 {
96    u32::try_from(i).unwrap_or(NONE)
97}
98
99/// An indexed triangle mesh held as half-edges, for editing.
100///
101/// Built by [`Editable::from`]; read back by
102/// [`Editable::to_triangulation`]. See the [module documentation](self)
103/// for how faces are joined, how a point several fans meet is split, and
104/// what numbers survive an edit. Edits take `&mut self` and run on one
105/// thread; queries take `&self`, and the mesh is `Send` and `Sync`.
106#[derive(Debug, Clone, PartialEq)]
107pub struct Editable {
108    positions: Vec<Point>,
109    /// The point each vertex is a copy of: the vertex itself, or for a
110    /// copy split off it, the vertex first given that point.
111    point: Vec<u32>,
112    /// The next copy of the same point, round a ring that holds them all.
113    copies: Vec<u32>,
114    /// The source vertex each vertex came from, or `NONE` for one an edit
115    /// made; for a vertex in `merged`, the lowest of its source vertices.
116    source: Vec<u32>,
117    /// The source vertices, more than one and in increasing order, of each
118    /// vertex a collapse left with several.
119    merged: FastMap<u32, Vec<u32>>,
120    /// How many vertices the source had.
121    source_count: usize,
122    /// A half-edge leaving each vertex: the one on a rim where there is
123    /// one, so turning from it sweeps the whole fan; `NONE` for a vertex
124    /// with no face.
125    out: Vec<u32>,
126    /// Whether each vertex was left with no face by an edit.
127    gone: Vec<bool>,
128    corners: Vec<[u32; 3]>,
129    /// Whether each face is still there.
130    live: Vec<bool>,
131    twin: Vec<u32>,
132    deflection_met: bool,
133}
134
135/// A connected piece of the mesh: faces joined across edges.
136#[derive(Debug, Clone, PartialEq)]
137pub struct Component {
138    /// Its faces, in increasing order.
139    pub faces: Vec<u32>,
140    /// The box of its faces' corners.
141    pub bounds: Aabb,
142    /// The sum of its faces' areas.
143    pub area: f64,
144}
145
146/// A closed loop of open edges.
147#[derive(Debug, Clone, PartialEq)]
148pub struct Rim {
149    /// Its half-edges, each starting where the one before ended, starting
150    /// from the lowest-numbered and running with the faces on the left, as
151    /// the faces wind.
152    pub edges: Vec<u32>,
153    /// The sum of the edges' lengths.
154    pub length: f64,
155    /// The greatest distance between two of its vertices.
156    pub span: f64,
157}
158
159/// A rim's name: its lowest-numbered half-edge, the first of its
160/// [`Rim::edges`]. An edit that leaves the rim's faces as they are leaves
161/// its name. One that adds faces on it, or removes or flips its faces, may
162/// give it another.
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
164pub struct RimId(pub u32);
165
166impl Rim {
167    /// The rim's name.
168    #[must_use]
169    pub fn id(&self) -> RimId {
170        RimId(self.edges[0])
171    }
172}
173
174/// Buffers reused from one point to the next while giving out vertices.
175#[derive(Default)]
176struct Scratch {
177    parent: Vec<usize>,
178    fan: Vec<usize>,
179    taken: Vec<u32>,
180}
181
182/// The points whose vertices an edit relinks: every point, or some.
183enum Scope {
184    All(usize),
185    Points(Vec<u32>),
186}
187
188impl Scope {
189    fn len(&self) -> usize {
190        match self {
191            Self::All(n) => *n,
192            Self::Points(p) => p.len(),
193        }
194    }
195
196    fn point(&self, slot: usize) -> u32 {
197        match self {
198            Self::All(_) => index(slot),
199            Self::Points(p) => p[slot],
200        }
201    }
202
203    fn slot(&self, point: u32) -> Option<usize> {
204        match self {
205            Self::All(n) => ((point as usize) < *n).then_some(point as usize),
206            Self::Points(p) => p.binary_search(&point).ok(),
207        }
208    }
209}
210
211impl Editable {
212    /// The mesh as half-edges, its vertices first welded within `weld`.
213    ///
214    /// With `weld` zero the mesh's own vertex numbers are the points, and a
215    /// manifold mesh reads back from [`Editable::to_triangulation`] with the
216    /// same vertices and the same triangles in the same order. With `weld`
217    /// positive, vertices within `weld` of each other become one: each
218    /// vertex in turn joins the lowest-numbered kept vertex within `weld`,
219    /// found through a grid of cells `weld` wide, or is kept itself at its
220    /// own position. A triangle naming one vertex twice, or two of whose
221    /// corners became one, is removed, its face number unused.
222    ///
223    /// Normals and surface parameters are not carried: an edit leaves
224    /// neither true.
225    ///
226    /// # Errors
227    ///
228    /// [`OgeomError::Numeric`](ogeom_core::OgeomError::Numeric) if `weld` is
229    /// negative or not finite, or a position is not finite;
230    /// [`OgeomError::Range`](ogeom_core::OgeomError::Range) if a triangle
231    /// names a vertex the mesh does not have, or the mesh has more
232    /// half-edges and vertices than `u32` numbers.
233    pub fn from(mesh: &Triangulation, weld: f64) -> OgeomResult<Self> {
234        if !weld.is_finite() || weld < 0.0 {
235            ogeom_bail!(Numeric, "a weld distance of {weld} is not a distance");
236        }
237        if let Some(k) = mesh.positions.iter().position(|p| !p.is_finite()) {
238            ogeom_bail!(Numeric, "mesh vertex {k} is not finite");
239        }
240        let count = mesh.positions.len();
241        if let Some(&k) = mesh
242            .triangles
243            .iter()
244            .flatten()
245            .find(|&&k| k as usize >= count)
246        {
247            ogeom_bail!(Range, "a triangle names vertex {k} of {count}");
248        }
249        // Every split copies a corner, so the vertices never outnumber
250        // the positions and the corners together.
251        if count.saturating_add(mesh.triangles.len().saturating_mul(3)) >= NONE as usize {
252            ogeom_bail!(
253                Range,
254                "a mesh of {count} vertices and {} triangles is past u32 numbering",
255                mesh.triangles.len()
256            );
257        }
258
259        let (positions, remap, source) = if weld > 0.0 {
260            weld_points(&mesh.positions, weld)
261        } else {
262            let all: Vec<u32> = (0..count).map(index).collect();
263            (mesh.positions.clone(), all.clone(), all)
264        };
265        let n = positions.len();
266        let corners: Vec<[u32; 3]> = mesh
267            .triangles
268            .iter()
269            .map(|t| t.map(|v| remap[v as usize]))
270            .collect();
271        let live = corners
272            .iter()
273            .map(|&[a, b, c]| a != b && b != c && c != a)
274            .collect();
275        let mut mesh = Self {
276            positions,
277            point: (0..n).map(index).collect(),
278            copies: (0..n).map(index).collect(),
279            source,
280            merged: FastMap::default(),
281            source_count: count,
282            out: vec![NONE; n],
283            gone: vec![false; n],
284            twin: vec![NONE; corners.len() * 3],
285            corners,
286            live,
287            deflection_met: mesh.deflection_met,
288        };
289        let faces: Vec<u32> = (0..mesh.corners.len())
290            .filter(|&f| mesh.live[f])
291            .map(index)
292            .collect();
293        mesh.relink(&Scope::All(n), &faces);
294        Ok(mesh)
295    }
296
297    /// The mesh's vertices and faces, those an edit left, renumbered in
298    /// order. A vertex no face of the source used is kept.
299    ///
300    /// A vertex no edit touched keeps its position; a copy split off a
301    /// point stands at the point's position, so two vertices of the result
302    /// may coincide.
303    #[must_use]
304    pub fn to_triangulation(&self) -> Triangulation {
305        let mut renumber = vec![NONE; self.positions.len()];
306        let mut positions = Vec::with_capacity(self.positions.len());
307        for (v, &p) in self.positions.iter().enumerate() {
308            if !self.gone[v] {
309                renumber[v] = index(positions.len());
310                positions.push(p);
311            }
312        }
313        let triangles = self
314            .corners
315            .iter()
316            .zip(&self.live)
317            .filter(|&(_, &live)| live)
318            .map(|(t, _)| t.map(|v| renumber[v as usize]))
319            .collect();
320        Triangulation {
321            positions,
322            triangles,
323            deflection_met: self.deflection_met,
324            ..Triangulation::new()
325        }
326    }
327
328    /// How many vertex numbers there are, including those of vertices
329    /// gone.
330    #[must_use]
331    pub fn vertex_count(&self) -> usize {
332        self.positions.len()
333    }
334
335    /// How many face numbers there are, including those of faces removed.
336    #[must_use]
337    pub fn face_count(&self) -> usize {
338        self.corners.len()
339    }
340
341    /// The position of vertex `v`, if it is there.
342    #[must_use]
343    pub fn position(&self, v: u32) -> Option<Point> {
344        let k = v as usize;
345        (k < self.positions.len() && !self.gone[k]).then(|| self.positions[k])
346    }
347
348    /// The corners of face `f`, counter-clockwise about its outward
349    /// normal, if it is there.
350    #[must_use]
351    pub fn face(&self, f: u32) -> Option<[u32; 3]> {
352        let k = f as usize;
353        (k < self.corners.len() && self.live[k]).then(|| self.corners[k])
354    }
355
356    /// The vertices half-edge `h` runs from and to, if its face is there.
357    #[must_use]
358    pub fn edge(&self, h: u32) -> Option<[u32; 2]> {
359        self.face(h / 3)
360            .is_some()
361            .then(|| [self.tail(h), self.head(h)])
362    }
363
364    /// The source vertex vertex `v` came from, where it came from exactly
365    /// one: the one it was welded onto, for every copy split off it and
366    /// through every edit. `None` for a vertex an edit made, one a
367    /// collapse left with several source vertices, or a number past the
368    /// last.
369    #[must_use]
370    pub fn source_vertex(&self, v: u32) -> Option<u32> {
371        match self.source_vertices(v) {
372            &[s] => Some(s),
373            _ => None,
374        }
375    }
376
377    /// The source vertices vertex `v` came from, in increasing order: one
378    /// for a vertex carried from the source, every one merged into it for
379    /// a vertex a collapse kept, and none for a vertex an edit made or a
380    /// number past the last. See the [module documentation](self).
381    #[must_use]
382    pub fn source_vertices(&self, v: u32) -> &[u32] {
383        let k = v as usize;
384        if k >= self.source.len() || self.source[k] == NONE {
385            return &[];
386        }
387        self.merged
388            .get(&v)
389            .map_or_else(|| core::slice::from_ref(&self.source[k]), Vec::as_slice)
390    }
391
392    /// How many vertices the source had: the length of a marking of them.
393    /// [`Editable::simplify`] keeps the count of the mesh first built.
394    #[must_use]
395    pub fn source_count(&self) -> usize {
396        self.source_count
397    }
398
399    /// The region, one entry per vertex number, that a marking of the
400    /// source's vertices selects: the vertices that have at least one
401    /// source vertex, and every one of them marked. A vertex an edit made,
402    /// with none, and a vertex gone, are not selected. The result is what
403    /// [`Editable::offset`] and [`Smooth::region`] take.
404    ///
405    /// # Errors
406    ///
407    /// [`OgeomError::Range`](ogeom_core::OgeomError::Range) if `marked`
408    /// does not have one entry per source vertex, as
409    /// [`Editable::source_count`] counts them.
410    pub fn region_from_sources(&self, marked: &[bool]) -> OgeomResult<Vec<bool>> {
411        if marked.len() != self.source_count {
412            ogeom_bail!(
413                Range,
414                "a marking of {} entries for {} source vertices",
415                marked.len(),
416                self.source_count
417            );
418        }
419        Ok((0..self.positions.len())
420            .map(|k| {
421                let sources = self.source_vertices(index(k));
422                !self.gone[k] && !sources.is_empty() && sources.iter().all(|&s| marked[s as usize])
423            })
424            .collect())
425    }
426
427    /// Give vertex `to` the source vertices of vertex `from`.
428    fn copy_sources(&mut self, to: u32, from: u32) {
429        self.source[to as usize] = self.source[from as usize];
430        match self.merged.get(&from).cloned() {
431            Some(set) => self.merged.insert(to, set),
432            None => self.merged.remove(&to),
433        };
434    }
435
436    /// Give vertex `to` the source vertices of vertex `from` as well as
437    /// its own.
438    fn absorb_sources(&mut self, to: u32, from: u32) {
439        let mut set: Vec<u32> = self.source_vertices(to).to_vec();
440        set.extend_from_slice(self.source_vertices(from));
441        set.sort_unstable();
442        set.dedup();
443        self.set_sources(to, set);
444    }
445
446    /// Set vertex `v`'s source vertices, sorted and once each.
447    fn set_sources(&mut self, v: u32, set: Vec<u32>) {
448        self.source[v as usize] = set.first().copied().unwrap_or(NONE);
449        if set.len() > 1 {
450            self.merged.insert(v, set);
451        } else {
452            self.merged.remove(&v);
453        }
454    }
455
456    /// Whether there is a face and every edge of every face is joined to
457    /// another face.
458    #[must_use]
459    pub fn is_closed(&self) -> bool {
460        let mut any = false;
461        for (f, &live) in self.live.iter().enumerate() {
462            if live {
463                any = true;
464                if self.twin[3 * f..3 * f + 3].contains(&NONE) {
465                    return false;
466                }
467            }
468        }
469        any
470    }
471
472    /// The vertices where the faces as they stand are not a manifold, in
473    /// increasing order: each copy of a point several fans meet, and both
474    /// ends of each edge that more than two faces use, or that two faces
475    /// run the same way.
476    #[must_use]
477    pub fn non_manifold(&self) -> Vec<u32> {
478        let mut flagged = vec![false; self.positions.len()];
479        for v in 0..self.positions.len() {
480            if self.out[v] == NONE || flagged[v] || self.copies[v] as usize == v {
481                continue;
482            }
483            let ring = self.ring(index(v));
484            let with_faces: Vec<u32> = ring
485                .into_iter()
486                .filter(|&w| self.out[w as usize] != NONE)
487                .collect();
488            if with_faces.len() > 1 {
489                for w in with_faces {
490                    flagged[w as usize] = true;
491                }
492            }
493        }
494        let mut edges: Vec<(u64, u32)> = Vec::new();
495        for (f, &live) in self.live.iter().enumerate() {
496            if live {
497                for h in index(3 * f)..index(3 * f + 3) {
498                    edges.push((self.edge_key(h), h));
499                }
500            }
501        }
502        edges.sort_unstable();
503        for run in edges.chunk_by(|a, b| a.0 == b.0) {
504            let clean =
505                run.len() == 1 || (run.len() == 2 && self.twin[run[0].1 as usize] == run[1].1);
506            if !clean {
507                for &(_, h) in run {
508                    flagged[self.tail(h) as usize] = true;
509                    flagged[self.head(h) as usize] = true;
510                }
511            }
512        }
513        (0..self.positions.len())
514            .filter(|&v| flagged[v])
515            .map(index)
516            .collect()
517    }
518
519    /// The connected pieces, faces joined across edges, in the order of
520    /// their lowest-numbered faces.
521    #[must_use]
522    pub fn components(&self) -> Vec<Component> {
523        let mut seen = vec![false; self.corners.len()];
524        let mut pieces = Vec::new();
525        let mut stack = Vec::new();
526        for seed in 0..self.corners.len() {
527            if !self.live[seed] || seen[seed] {
528                continue;
529            }
530            seen[seed] = true;
531            stack.push(index(seed));
532            let mut faces = Vec::new();
533            let (mut lo, mut hi) = (
534                Point::new(f64::MAX, f64::MAX, f64::MAX),
535                Point::new(f64::MIN, f64::MIN, f64::MIN),
536            );
537            let mut area = 0.0;
538            while let Some(f) = stack.pop() {
539                faces.push(f);
540                let [a, b, c] = self.corners[f as usize].map(|v| self.positions[v as usize]);
541                lo = lo.min(a).min(b).min(c);
542                hi = hi.max(a).max(b).max(c);
543                area += (b - a).cross(c - a).magnitude() * 0.5;
544                for h in 3 * f..3 * f + 3 {
545                    let g = self.twin[h as usize];
546                    if g != NONE && !seen[(g / 3) as usize] {
547                        seen[(g / 3) as usize] = true;
548                        stack.push(g / 3);
549                    }
550                }
551            }
552            faces.sort_unstable();
553            pieces.push(Component {
554                faces,
555                bounds: Aabb::of_corners(lo, hi),
556                area,
557            });
558        }
559        pieces
560    }
561
562    /// The rims: the loops of half-edges with no twin, in the order of
563    /// their lowest-numbered half-edges.
564    #[must_use]
565    pub fn rims(&self) -> Vec<Rim> {
566        let mut seen = vec![false; self.twin.len()];
567        let mut rims = Vec::new();
568        for start in 0..self.twin.len() {
569            if self.twin[start] != NONE || seen[start] || !self.live[start / 3] {
570                continue;
571            }
572            let mut edges = Vec::new();
573            let mut points = Vec::new();
574            let mut length = 0.0;
575            let mut h = index(start);
576            loop {
577                seen[h as usize] = true;
578                edges.push(h);
579                let (a, b) = (self.tail(h), self.head(h));
580                let p = self.positions[a as usize];
581                points.push(p);
582                length += p.distance(self.positions[b as usize]);
583                h = self.rim_after(h);
584                if h as usize == start {
585                    break;
586                }
587            }
588            rims.push(Rim {
589                edges,
590                length,
591                span: diameter(&points),
592            });
593        }
594        rims
595    }
596
597    /// Remove faces, and split each vertex whose faces left fall into
598    /// several fans into one vertex per fan, so what is left stays a
599    /// manifold. Faces not there are passed over.
600    pub fn remove_faces(&mut self, faces: &[u32]) {
601        let removed = self.listed(faces);
602        if removed.is_empty() {
603            return;
604        }
605        let (scope, around) = self.around(&removed);
606        for &f in &removed {
607            self.live[f as usize] = false;
608            for h in 3 * f..3 * f + 3 {
609                self.twin[h as usize] = NONE;
610            }
611        }
612        let kept: Vec<u32> = around
613            .into_iter()
614            .filter(|f| removed.binary_search(f).is_err())
615            .collect();
616        self.relink(&scope, &kept);
617    }
618
619    /// Reverse the winding of faces, and join or part the faces about
620    /// them again by the same rule as [`Editable::from`]: an edge two
621    /// faces now run the same way opens, and one they now run opposite
622    /// ways closes. Each face is flipped once however often it is listed;
623    /// faces not there are passed over.
624    pub fn flip_faces(&mut self, faces: &[u32]) {
625        let flipped = self.listed(faces);
626        if flipped.is_empty() {
627            return;
628        }
629        let (scope, around) = self.around(&flipped);
630        for &f in &flipped {
631            self.corners[f as usize].swap(1, 2);
632        }
633        self.relink(&scope, &around);
634    }
635
636    /// The faces listed that are there, sorted and once each.
637    fn listed(&self, faces: &[u32]) -> Vec<u32> {
638        let mut listed: Vec<u32> = faces
639            .iter()
640            .copied()
641            .filter(|&f| (f as usize) < self.corners.len() && self.live[f as usize])
642            .collect();
643        listed.sort_unstable();
644        listed.dedup();
645        listed
646    }
647
648    /// The points at the corners of `faces`, and every face about any of
649    /// them, sorted.
650    fn around(&self, faces: &[u32]) -> (Scope, Vec<u32>) {
651        let mut points: Vec<u32> = faces
652            .iter()
653            .flat_map(|&f| self.corners[f as usize])
654            .map(|v| self.point[v as usize])
655            .collect();
656        points.sort_unstable();
657        points.dedup();
658        let around = self.about(&points);
659        (Scope::Points(points), around)
660    }
661
662    /// Every face with a corner at one of `points`, sorted.
663    fn about(&self, points: &[u32]) -> Vec<u32> {
664        let mut around = Vec::new();
665        for &p in points {
666            for v in self.ring(p) {
667                let start = self.out[v as usize];
668                if start == NONE {
669                    continue;
670                }
671                let mut h = start;
672                loop {
673                    around.push(h / 3);
674                    h = self.twin[prev(h) as usize];
675                    if h == NONE || h == start {
676                        break;
677                    }
678                }
679            }
680        }
681        around.sort_unstable();
682        around.dedup();
683        around
684    }
685
686    /// The copies of the point `p`, starting from `p`.
687    fn ring(&self, p: u32) -> Vec<u32> {
688        let mut ring = vec![p];
689        let mut v = self.copies[p as usize];
690        while v != p {
691            ring.push(v);
692            v = self.copies[v as usize];
693        }
694        ring
695    }
696
697    fn tail(&self, h: u32) -> u32 {
698        self.corners[(h / 3) as usize][(h % 3) as usize]
699    }
700
701    fn head(&self, h: u32) -> u32 {
702        self.tail(next(h))
703    }
704
705    /// The edge `h` lies on, as its two points, smaller first.
706    fn edge_key(&self, h: u32) -> u64 {
707        let (a, b) = (
708            self.point[self.tail(h) as usize],
709            self.point[self.head(h) as usize],
710        );
711        (u64::from(a.min(b)) << 32) | u64::from(a.max(b))
712    }
713
714    /// The rim half-edge leaving the vertex rim half-edge `h` ends at:
715    /// turning about that vertex through its fan, from `h`'s face to the
716    /// fan's other side.
717    fn rim_after(&self, h: u32) -> u32 {
718        let mut g = next(h);
719        while self.twin[g as usize] != NONE {
720            g = next(self.twin[g as usize]);
721        }
722        g
723    }
724
725    /// Join the half-edges on every edge at a point in `scope`, then give
726    /// each fan about such a point its vertex. `faces` must be every live
727    /// face with a corner at one of those points, sorted.
728    fn relink(&mut self, scope: &Scope, faces: &[u32]) {
729        // The half-edges leaving each point in scope, in increasing order:
730        // one per corner there.
731        let mut start = vec![0_usize; scope.len() + 1];
732        for &f in faces {
733            for v in self.corners[f as usize] {
734                if let Some(s) = scope.slot(self.point[v as usize]) {
735                    start[s + 1] += 1;
736                }
737            }
738        }
739        for s in 0..scope.len() {
740            start[s + 1] += start[s];
741        }
742        let mut fill = start.clone();
743        let mut leaving = vec![0_u32; start[scope.len()]];
744        for &f in faces {
745            for (k, v) in self.corners[f as usize].into_iter().enumerate() {
746                if let Some(s) = scope.slot(self.point[v as usize]) {
747                    leaving[fill[s]] = 3 * f + index(k);
748                    fill[s] += 1;
749                }
750            }
751        }
752
753        let mut on_edges: Vec<(u32, u32)> = Vec::new();
754        for s in 0..scope.len() {
755            let p = scope.point(s);
756            on_edges.clear();
757            for &h in &leaving[start[s]..start[s + 1]] {
758                let into = prev(h);
759                on_edges.push((self.point[self.head(h) as usize], h));
760                on_edges.push((self.point[self.tail(into) as usize], into));
761            }
762            on_edges.sort_unstable();
763            for run in on_edges.chunk_by(|a, b| a.0 == b.0) {
764                let other = run[0].0;
765                // An edge with both ends in scope is joined from its
766                // smaller end.
767                if other < p && scope.slot(other).is_some() {
768                    continue;
769                }
770                self.join(run, p.min(other));
771            }
772        }
773
774        let mut scratch = Scratch::default();
775        for s in 0..scope.len() {
776            let fan = &leaving[start[s]..start[s + 1]];
777            self.give_vertices(scope.point(s), fan, &mut scratch);
778        }
779    }
780
781    /// Join the half-edges of one edge, each the second of a pair in `run`,
782    /// `low` the smaller of the edge's two points, by the rule in the
783    /// module documentation.
784    fn join(&mut self, run: &[(u32, u32)], low: u32) {
785        for &(_, h) in run {
786            self.twin[h as usize] = NONE;
787        }
788        let rising = |me: &Self, h: u32| me.point[me.tail(h) as usize] == low;
789        match *run {
790            [_] => {}
791            [(_, a), (_, b)] => {
792                if rising(self, a) != rising(self, b) {
793                    self.twin[a as usize] = b;
794                    self.twin[b as usize] = a;
795                }
796            }
797            _ => {
798                // About the edge's axis, from its low end to its high end,
799                // each face is a half-plane at an angle. A face running the
800                // edge from low to high faces the way the angle grows, so
801                // its material lies the way it falls; one running it from
802                // high to low has its material the way the angle grows.
803                let h0 = run[0].1;
804                let (from, to) = if rising(self, h0) {
805                    (self.tail(h0), self.head(h0))
806                } else {
807                    (self.head(h0), self.tail(h0))
808                };
809                let from = self.positions[from as usize];
810                let axis = self.positions[to as usize] - from;
811                let length = axis.magnitude();
812                let axis = if length > 0.0 { axis / length } else { axis };
813                let across: Vec<Vector> = run
814                    .iter()
815                    .map(|&(_, h)| {
816                        let w = self.positions[self.tail(prev(h)) as usize] - from;
817                        w - axis * w.dot(axis)
818                    })
819                    .collect();
820                let mut turn: Vec<(f64, u32, bool)> = match across
821                    .iter()
822                    .find(|w| w.magnitude() > 0.0)
823                {
824                    Some(&first) => {
825                        let e1 = first / first.magnitude();
826                        let e2 = axis.cross(e1);
827                        run.iter()
828                            .zip(&across)
829                            .map(|(&(_, h), w)| (w.dot(e2).atan2(w.dot(e1)), h, rising(self, h)))
830                            .collect()
831                    }
832                    None => run
833                        .iter()
834                        .map(|&(_, h)| (0.0, h, rising(self, h)))
835                        .collect(),
836                };
837                turn.sort_by(|a, b| a.0.total_cmp(&b.0).then(a.1.cmp(&b.1)));
838                let n = turn.len();
839                for i in 0..n {
840                    let (a, b) = (turn[i], turn[(i + 1) % n]);
841                    if !a.2 && b.2 {
842                        self.twin[a.1 as usize] = b.1;
843                        self.twin[b.1 as usize] = a.1;
844                    }
845                }
846            }
847        }
848    }
849
850    /// Group the half-edges `leaving` point `p`, in increasing order, into
851    /// fans joined across the edges at `p`, and give each fan a vertex: the
852    /// lowest-numbered one its corners held that no earlier fan took, or a
853    /// new copy of the point. Copies of `p` no fan took, and that had
854    /// faces, are gone.
855    fn give_vertices(&mut self, p: u32, leaving: &[u32], scratch: &mut Scratch) {
856        fn root(parent: &mut [usize], mut i: usize) -> usize {
857            while parent[i] != i {
858                parent[i] = parent[parent[i]];
859                i = parent[i];
860            }
861            i
862        }
863        let Scratch { parent, fan, taken } = scratch;
864        parent.clear();
865        parent.extend(0..leaving.len());
866        for (i, &h) in leaving.iter().enumerate() {
867            let g = self.twin[h as usize];
868            if g == NONE {
869                continue;
870            }
871            if let Ok(j) = leaving.binary_search(&next(g)) {
872                let (a, b) = (root(parent, i), root(parent, j));
873                parent[a.max(b)] = a.min(b);
874            }
875        }
876        // Rooted always at the smaller, a fan's root is its first
877        // half-edge, so the fans are numbered in the order of their first.
878        fan.clear();
879        let mut fans = 0;
880        for i in 0..leaving.len() {
881            let r = root(parent, i);
882            if r == i {
883                fan.push(fans);
884                fans += 1;
885            } else {
886                let k = fan[r];
887                fan.push(k);
888            }
889        }
890
891        taken.clear();
892        for k in 0..fans {
893            let members = || {
894                leaving
895                    .iter()
896                    .zip(fan.iter())
897                    .filter(move |&(_, &f)| f == k)
898                    .map(|(&h, _)| h)
899            };
900            let held = members()
901                .map(|h| self.tail(h))
902                .filter(|v| !taken.contains(v))
903                .min();
904            let first = members().next().unwrap_or(NONE);
905            let v = held.unwrap_or_else(|| self.copy_of(p, self.tail(first)));
906            taken.push(v);
907            let mut out = NONE;
908            for h in members() {
909                self.corners[(h / 3) as usize][(h % 3) as usize] = v;
910                if out == NONE && self.twin[h as usize] == NONE {
911                    out = h;
912                }
913            }
914            self.out[v as usize] = if out == NONE { first } else { out };
915            self.gone[v as usize] = false;
916        }
917        let mut v = p;
918        loop {
919            let k = v as usize;
920            if !taken.contains(&v) && self.out[k] != NONE {
921                self.out[k] = NONE;
922                self.gone[k] = true;
923            }
924            v = self.copies[k];
925            if v == p {
926                break;
927            }
928        }
929    }
930
931    /// A new vertex copying point `p`, standing where vertex `like` does
932    /// and remembering its source.
933    fn copy_of(&mut self, p: u32, like: u32) -> u32 {
934        let v = index(self.positions.len());
935        self.positions.push(self.positions[like as usize]);
936        self.point.push(p);
937        self.source.push(NONE);
938        self.copy_sources(v, like);
939        self.out.push(NONE);
940        self.gone.push(false);
941        self.copies.push(self.copies[p as usize]);
942        self.copies[p as usize] = v;
943        v
944    }
945}
946
947/// Weld points within `distance`: each in turn joins the lowest-numbered
948/// kept point within `distance`, looked for in the cells `distance` wide
949/// about it, or is kept. Returns the kept points, the kept point each
950/// point became, and the point each kept one was.
951fn weld_points(positions: &[Point], distance: f64) -> (Vec<Point>, Vec<u32>, Vec<u32>) {
952    // A cast saturates, so a coordinate past `i64`'s cells shares the last
953    // one and is still compared by distance.
954    #[allow(clippy::cast_possible_truncation, reason = "saturating")]
955    let cell = |p: Point| {
956        (
957            (p.x / distance).floor() as i64,
958            (p.y / distance).floor() as i64,
959            (p.z / distance).floor() as i64,
960        )
961    };
962    let mut grid: FastMap<(i64, i64, i64), Vec<u32>> =
963        FastMap::with_capacity_and_hasher(positions.len(), Default::default());
964    let mut kept: Vec<Point> = Vec::with_capacity(positions.len());
965    let mut founders = Vec::with_capacity(positions.len());
966    let mut remap = Vec::with_capacity(positions.len());
967    for (i, p) in positions.iter().enumerate() {
968        let (x, y, z) = cell(*p);
969        let mut found = NONE;
970        for dx in -1..=1_i64 {
971            for dy in -1..=1_i64 {
972                for dz in -1..=1_i64 {
973                    let key = (
974                        x.saturating_add(dx),
975                        y.saturating_add(dy),
976                        z.saturating_add(dz),
977                    );
978                    if let Some(list) = grid.get(&key)
979                        && let Some(&k) = list
980                            .iter()
981                            .find(|&&k| kept[k as usize].distance(*p) <= distance)
982                    {
983                        found = found.min(k);
984                    }
985                }
986            }
987        }
988        if found == NONE {
989            found = index(kept.len());
990            kept.push(*p);
991            founders.push(index(i));
992            grid.entry((x, y, z)).or_default().push(found);
993        }
994        remap.push(found);
995    }
996    (kept, remap, founders)
997}
998
999/// The greatest distance between two of `points`, by branch and bound over
1000/// a tree of boxes: a pair of boxes is opened only while the farthest their
1001/// corners stand apart exceeds the best pair found, which, the rounded
1002/// differences of coordinates growing with the coordinates, bounds every
1003/// pair of points within them as computed.
1004fn diameter(points: &[Point]) -> f64 {
1005    struct Node {
1006        lo: Point,
1007        hi: Point,
1008        range: (usize, usize),
1009        children: Option<(usize, usize)>,
1010    }
1011    fn build(points: &mut [Point], at: usize, nodes: &mut Vec<Node>) -> usize {
1012        let lo = points.iter().fold(points[0], |a, &p| a.min(p));
1013        let hi = points.iter().fold(points[0], |a, &p| a.max(p));
1014        let k = nodes.len();
1015        nodes.push(Node {
1016            lo,
1017            hi,
1018            range: (at, at + points.len()),
1019            children: None,
1020        });
1021        // Leaves of a few points, compared pairwise.
1022        if points.len() > 8 {
1023            let size = hi - lo;
1024            let axis = if size.x >= size.y && size.x >= size.z {
1025                0
1026            } else if size.y >= size.z {
1027                1
1028            } else {
1029                2
1030            };
1031            let half = points.len() / 2;
1032            let along = |p: &Point| [p.x, p.y, p.z][axis];
1033            points.select_nth_unstable_by(half, |a, b| along(a).total_cmp(&along(b)));
1034            let (left, right) = points.split_at_mut(half);
1035            let a = build(left, at, nodes);
1036            let b = build(right, at + half, nodes);
1037            nodes[k].children = Some((a, b));
1038        }
1039        k
1040    }
1041    fn reach(a: &Node, b: &Node) -> f64 {
1042        let span =
1043            |lo1: f64, hi1: f64, lo2: f64, hi2: f64| (hi1 - lo2).abs().max((hi2 - lo1).abs());
1044        Vector::new(
1045            span(a.lo.x, a.hi.x, b.lo.x, b.hi.x),
1046            span(a.lo.y, a.hi.y, b.lo.y, b.hi.y),
1047            span(a.lo.z, a.hi.z, b.lo.z, b.hi.z),
1048        )
1049        .magnitude()
1050    }
1051
1052    if points.len() < 2 {
1053        return 0.0;
1054    }
1055    let mut points = points.to_vec();
1056    let mut nodes = Vec::new();
1057    build(&mut points, 0, &mut nodes);
1058    // A good first pair: the point farthest from the first, and the point
1059    // farthest from that.
1060    let farthest = |from: Point| {
1061        points
1062            .iter()
1063            .copied()
1064            .max_by(|a, b| a.distance(from).total_cmp(&b.distance(from)))
1065            .unwrap_or(from)
1066    };
1067    let a = farthest(points[0]);
1068    let mut best = a.distance(farthest(a));
1069    let mut stack = vec![(0_usize, 0_usize)];
1070    while let Some((i, j)) = stack.pop() {
1071        let (a, b) = (&nodes[i], &nodes[j]);
1072        if reach(a, b) <= best {
1073            continue;
1074        }
1075        match (a.children, b.children) {
1076            (None, None) => {
1077                for p in &points[a.range.0..a.range.1] {
1078                    for q in &points[b.range.0..b.range.1] {
1079                        best = best.max(p.distance(*q));
1080                    }
1081                }
1082            }
1083            (Some((l, r)), None) => stack.extend([(l, j), (r, j)]),
1084            (None, Some((l, r))) => stack.extend([(i, l), (i, r)]),
1085            (Some((l, r)), Some(_)) if i == j => {
1086                stack.extend([(l, l), (l, r), (r, r)]);
1087            }
1088            (Some((l, r)), Some((m, s))) => {
1089                if a.range.1 - a.range.0 >= b.range.1 - b.range.0 {
1090                    stack.extend([(l, j), (r, j)]);
1091                } else {
1092                    stack.extend([(i, m), (i, s)]);
1093                }
1094            }
1095        }
1096    }
1097    best
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102    #![allow(
1103        clippy::unwrap_used,
1104        clippy::cast_possible_truncation,
1105        reason = "test code; the f32 casts are the rounding under test"
1106    )]
1107    use super::*;
1108    use core::f64::consts::{SQRT_2, TAU};
1109    use proptest::prelude::*;
1110
1111    pub(super) fn mesh(positions: Vec<Point>, triangles: Vec<[u32; 3]>) -> Triangulation {
1112        Triangulation {
1113            positions,
1114            triangles,
1115            ..Triangulation::new()
1116        }
1117    }
1118
1119    /// A torus about `z`, `rings` by `sides` quads of two triangles each,
1120    /// wound outward.
1121    pub(super) fn torus(rings: u32, sides: u32) -> Triangulation {
1122        let mut positions = Vec::new();
1123        for i in 0..rings {
1124            let u = f64::from(i) / f64::from(rings) * TAU;
1125            for j in 0..sides {
1126                let v = f64::from(j) / f64::from(sides) * TAU;
1127                let r = 3.0 + v.cos();
1128                positions.push(Point::new(r * u.cos(), r * u.sin(), v.sin()));
1129            }
1130        }
1131        let at = |i: u32, j: u32| (i % rings) * sides + (j % sides);
1132        let mut triangles = Vec::new();
1133        for i in 0..rings {
1134            for j in 0..sides {
1135                let (a, b, c, d) = (at(i, j), at(i + 1, j), at(i + 1, j + 1), at(i, j + 1));
1136                triangles.push([a, b, c]);
1137                triangles.push([a, c, d]);
1138            }
1139        }
1140        mesh(positions, triangles)
1141    }
1142
1143    /// A square of `n` by `n` unit cells in `z = 0`, facing `+z`; cell
1144    /// `(i, j)` is faces `2 (n j + i)` and the one after.
1145    pub(super) fn grid(n: u32) -> Triangulation {
1146        let mut positions = Vec::new();
1147        for j in 0..=n {
1148            for i in 0..=n {
1149                positions.push(Point::new(f64::from(i), f64::from(j), 0.0));
1150            }
1151        }
1152        let at = |i: u32, j: u32| j * (n + 1) + i;
1153        let mut triangles = Vec::new();
1154        for j in 0..n {
1155            for i in 0..n {
1156                triangles.push([at(i, j), at(i + 1, j), at(i + 1, j + 1)]);
1157                triangles.push([at(i, j), at(i + 1, j + 1), at(i, j + 1)]);
1158            }
1159        }
1160        mesh(positions, triangles)
1161    }
1162
1163    /// The tetrahedron on four points, each face wound away from the
1164    /// corner opposite it, its corners numbered from `first`.
1165    fn tetrahedron(points: [Point; 4], first: u32) -> Vec<[u32; 3]> {
1166        [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]
1167            .into_iter()
1168            .map(|[a, b, c]| {
1169                let opposite = 6 - a - b - c;
1170                let [pa, pb, pc] = [a, b, c].map(|k| points[k]);
1171                let normal = (pb - pa).cross(pc - pa);
1172                let t = if normal.dot(points[opposite] - pa) < 0.0 {
1173                    [a, b, c]
1174                } else {
1175                    [a, c, b]
1176                };
1177                t.map(|k| first + index(k))
1178            })
1179            .collect()
1180    }
1181
1182    /// A cube of side `n h` as six separate grids of `n` by `n` cells, each
1183    /// with its own vertices and wound outward.
1184    pub(super) fn cube_of_grids(n: u32, h: f64) -> Triangulation {
1185        let side = f64::from(n) * h;
1186        let mut out = Triangulation::new();
1187        // Each face as an origin and two in-plane steps whose cross product
1188        // points out of the cube.
1189        let faces = [
1190            ([0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]),
1191            ([0.0, 0.0, side], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]),
1192            ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]),
1193            ([0.0, side, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]),
1194            ([0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0]),
1195            ([side, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]),
1196        ];
1197        for (o, u, v) in faces {
1198            let mut g = grid(n);
1199            for p in &mut g.positions {
1200                let (a, b) = (p.x * h, p.y * h);
1201                *p = Point::new(
1202                    o[0] + a * u[0] + b * v[0],
1203                    o[1] + a * u[1] + b * v[1],
1204                    o[2] + a * u[2] + b * v[2],
1205                );
1206            }
1207            out.append(&g);
1208        }
1209        out
1210    }
1211
1212    /// Each triangle given its own three vertices.
1213    fn soup(m: &Triangulation) -> Triangulation {
1214        let mut out = Triangulation::new();
1215        for t in &m.triangles {
1216            let first = index(out.positions.len());
1217            out.positions.extend(t.map(|v| m.positions[v as usize]));
1218            out.triangles.push([first, first + 1, first + 2]);
1219        }
1220        out
1221    }
1222
1223    /// The half-edge structure's invariants: twins run one edge opposite
1224    /// ways between live faces, every vertex with faces has exactly one fan
1225    /// and its `out` sweeps it from the rim, and copies share a ring.
1226    pub(super) fn check(e: &Editable) {
1227        let mut at: Vec<Vec<u32>> = vec![Vec::new(); e.positions.len()];
1228        for f in 0..e.corners.len() {
1229            if !e.live[f] {
1230                continue;
1231            }
1232            for h in index(3 * f)..index(3 * f + 3) {
1233                let v = e.tail(h);
1234                assert!(!e.gone[v as usize], "face {f} names gone vertex {v}");
1235                at[v as usize].push(h);
1236                let g = e.twin[h as usize];
1237                if g != NONE {
1238                    assert!(e.live[(g / 3) as usize]);
1239                    assert_eq!(e.twin[g as usize], h);
1240                    assert_eq!((e.tail(g), e.head(g)), (e.head(h), e.tail(h)));
1241                }
1242            }
1243        }
1244        for (v, leaving) in at.iter().enumerate() {
1245            let start = e.out[v];
1246            if leaving.is_empty() {
1247                assert_eq!(start, NONE, "vertex {v} has no face");
1248                continue;
1249            }
1250            if leaving.iter().any(|&h| e.twin[h as usize] == NONE) {
1251                assert_eq!(
1252                    e.twin[start as usize], NONE,
1253                    "vertex {v} starts off its rim"
1254                );
1255            }
1256            let mut swept = Vec::new();
1257            let mut h = start;
1258            loop {
1259                swept.push(h);
1260                h = e.twin[prev(h) as usize];
1261                if h == NONE || h == start {
1262                    break;
1263                }
1264            }
1265            swept.sort_unstable();
1266            assert_eq!(&swept, leaving, "vertex {v} has more than one fan");
1267            assert!(e.ring(e.point[v]).contains(&index(v)));
1268        }
1269    }
1270
1271    fn reads_back(e: &Editable, m: &Triangulation) {
1272        let back = e.to_triangulation();
1273        assert_eq!(back.positions, m.positions);
1274        assert_eq!(back.triangles, m.triangles);
1275    }
1276
1277    #[test]
1278    fn a_manifold_mesh_reads_back_unchanged() {
1279        let closed = torus(12, 8);
1280        let e = Editable::from(&closed, 0.0).unwrap();
1281        check(&e);
1282        reads_back(&e, &closed);
1283        assert!(e.is_closed());
1284        assert!(e.rims().is_empty());
1285        assert!(e.non_manifold().is_empty());
1286        assert_eq!(e.components().len(), 1);
1287        assert!((0..e.vertex_count()).all(|v| e.source_vertex(index(v)) == Some(index(v))));
1288
1289        let open = grid(5);
1290        let e = Editable::from(&open, 0.0).unwrap();
1291        check(&e);
1292        reads_back(&e, &open);
1293        assert!(!e.is_closed());
1294        assert!(e.non_manifold().is_empty());
1295        let rims = e.rims();
1296        assert_eq!(rims.len(), 1);
1297        assert_eq!(rims[0].edges.len(), 20);
1298        // Unit edges and half-unit triangles sum without rounding.
1299        assert_eq!(rims[0].length, 20.0);
1300        assert_eq!(rims[0].span, 50.0_f64.sqrt());
1301        let pieces = e.components();
1302        assert_eq!(pieces.len(), 1);
1303        assert_eq!(pieces[0].area, 25.0);
1304    }
1305
1306    #[test]
1307    fn three_islands_are_three_components_with_their_bounds() {
1308        let mut positions = Vec::new();
1309        let mut triangles = Vec::new();
1310        let corners = [[0.0, 0.0, 0.0], [5.0, 1.0, -2.0], [-4.0, 3.0, 7.0]];
1311        let sizes = [1.0, 2.0, 0.5];
1312        for (o, s) in corners.into_iter().zip(sizes) {
1313            let o = Point::new(o[0], o[1], o[2]);
1314            let points = [
1315                o,
1316                o + Vector::new(s, 0.0, 0.0),
1317                o + Vector::new(0.0, s, 0.0),
1318                o + Vector::new(0.0, 0.0, s),
1319            ];
1320            triangles.extend(tetrahedron(points, index(positions.len())));
1321            positions.extend(points);
1322        }
1323        let m = mesh(positions, triangles);
1324        let e = Editable::from(&m, 0.0).unwrap();
1325        check(&e);
1326        assert!(e.is_closed());
1327        let pieces = e.components();
1328        assert_eq!(pieces.len(), 3);
1329        for (k, (piece, (o, s))) in pieces
1330            .iter()
1331            .zip(corners.into_iter().zip(sizes))
1332            .enumerate()
1333        {
1334            let k = index(k);
1335            assert_eq!(piece.faces, vec![4 * k, 4 * k + 1, 4 * k + 2, 4 * k + 3]);
1336            let o = Point::new(o[0], o[1], o[2]);
1337            assert_eq!(piece.bounds, Aabb::of_corners(o, o + Vector::new(s, s, s)));
1338            // Three right triangles and an equilateral one of side s√2,
1339            // to the rounding of four cross products.
1340            let area = 1.5 * s * s + 3.0_f64.sqrt() / 2.0 * s * s;
1341            assert!((piece.area - area).abs() < 1e-14 * area);
1342        }
1343    }
1344
1345    #[test]
1346    fn removing_a_patch_leaves_one_rim_round_it() {
1347        let (n, h) = (6, 0.5);
1348        let side = f64::from(n) * h;
1349        let cube = cube_of_grids(n, h);
1350        let mut e = Editable::from(&cube, 1e-9).unwrap();
1351        check(&e);
1352        assert!(e.is_closed());
1353        assert_eq!(e.vertex_count(), 6 * 36 + 2);
1354        assert!(e.non_manifold().is_empty());
1355        // The top's middle two by two cells.
1356        let patch: Vec<u32> = (0..e.face_count())
1357            .map(index)
1358            .filter(|&f| {
1359                let ps = e.face(f).unwrap().map(|v| e.position(v).unwrap());
1360                let c = (ps[0] + (ps[1] - ps[0]) / 3.0) + (ps[2] - ps[0]) / 3.0;
1361                ps.iter().all(|p| p.z == side)
1362                    && (c.x - side / 2.0).abs() < h
1363                    && (c.y - side / 2.0).abs() < h
1364            })
1365            .collect();
1366        assert_eq!(patch.len(), 8);
1367        e.remove_faces(&patch);
1368        check(&e);
1369        assert!(!e.is_closed());
1370        assert!(e.non_manifold().is_empty());
1371        let rims = e.rims();
1372        assert_eq!(rims.len(), 1);
1373        assert_eq!(rims[0].edges.len(), 8);
1374        // Every coordinate is a multiple of a half: no rounding anywhere.
1375        assert_eq!(rims[0].length, 8.0 * h);
1376        assert_eq!(rims[0].span, 2.0_f64.sqrt());
1377        // The rim runs with the faces left on its left: round the hole
1378        // clockwise seen from outside, above it.
1379        let [a, b] = e.edge(rims[0].edges[0]).unwrap();
1380        let [_, c] = e.edge(rims[0].edges[1]).unwrap();
1381        let [a, b, c] = [a, b, c].map(|v| e.position(v).unwrap());
1382        assert!((b - a).cross(c - b).z <= 0.0);
1383        let pieces = e.components();
1384        assert_eq!(pieces.len(), 1);
1385        assert_eq!(pieces[0].area, 6.0 * side * side - 4.0 * h * h);
1386        // The patch's middle vertex is gone with its faces.
1387        let back = e.to_triangulation();
1388        assert_eq!(back.positions.len(), 6 * 36 + 2 - 1);
1389        assert_eq!(back.triangles.len(), cube.triangles.len() - 8);
1390    }
1391
1392    #[test]
1393    fn a_bowtie_is_split_into_a_vertex_per_fan() {
1394        let positions = vec![
1395            Point::new(0.0, 0.0, 0.0),
1396            Point::new(1.0, 0.0, 0.0),
1397            Point::new(1.0, 1.0, 0.0),
1398            Point::new(-1.0, 0.0, 0.0),
1399            Point::new(-1.0, -1.0, 0.0),
1400        ];
1401        let m = mesh(positions.clone(), vec![[0, 1, 2], [0, 3, 4]]);
1402        let e = Editable::from(&m, 0.0).unwrap();
1403        check(&e);
1404        assert_eq!(e.vertex_count(), 6);
1405        assert_eq!(e.non_manifold(), vec![0, 5]);
1406        assert_eq!(e.source_vertex(5), Some(0));
1407        assert_eq!(e.components().len(), 2);
1408        let rims = e.rims();
1409        assert_eq!(rims.len(), 2);
1410        assert_eq!(rims[0].length, 1.0 + SQRT_2 + 1.0);
1411        let back = e.to_triangulation();
1412        assert_eq!(back.triangles, vec![[0, 1, 2], [5, 3, 4]]);
1413        assert_eq!(back.positions[5], positions[0]);
1414        assert_eq!(&back.positions[..5], &positions[..]);
1415    }
1416
1417    #[test]
1418    fn removing_faces_splits_the_pinch_they_leave() {
1419        // A two by two grid less two opposite cells meets itself only at
1420        // the middle vertex.
1421        let m = grid(2);
1422        let mut e = Editable::from(&m, 0.0).unwrap();
1423        e.remove_faces(&[0, 1, 6, 7]);
1424        check(&e);
1425        assert_eq!(e.non_manifold(), vec![4, 9]);
1426        assert_eq!(e.source_vertex(9), Some(4));
1427        assert_eq!(e.components().len(), 2);
1428        let rims = e.rims();
1429        assert_eq!(rims.len(), 2);
1430        assert!(rims.iter().all(|r| r.length == 4.0));
1431        // Putting a cell back is not possible, but flipping both cells
1432        // over leaves the pinch as it was.
1433        e.flip_faces(&[2, 3, 4, 5]);
1434        check(&e);
1435        assert_eq!(e.non_manifold(), vec![4, 9]);
1436    }
1437
1438    #[test]
1439    fn an_edge_of_three_faces_joins_the_sheet_and_leaves_the_fin() {
1440        // A sheet across the `x` axis and a fin standing on it.
1441        let m = mesh(
1442            vec![
1443                Point::new(0.0, 0.0, 0.0),
1444                Point::new(1.0, 0.0, 0.0),
1445                Point::new(0.5, 1.0, 0.0),
1446                Point::new(0.5, -1.0, 0.0),
1447                Point::new(0.5, 0.0, 1.0),
1448            ],
1449            vec![[0, 1, 2], [1, 0, 3], [0, 1, 4]],
1450        );
1451        let e = Editable::from(&m, 0.0).unwrap();
1452        check(&e);
1453        assert_eq!(e.twin[0], 3);
1454        assert_eq!(e.face(2), Some([5, 6, 4]));
1455        assert_eq!(e.non_manifold(), vec![0, 1, 5, 6]);
1456        assert_eq!(e.source_vertex(5), Some(0));
1457        assert_eq!(e.source_vertex(6), Some(1));
1458        let pieces = e.components();
1459        assert_eq!(pieces.len(), 2);
1460        assert_eq!(pieces[1].faces, vec![2]);
1461    }
1462
1463    #[test]
1464    fn two_bodies_sharing_an_edge_each_close() {
1465        let points = [
1466            Point::new(0.0, 0.0, 0.0),
1467            Point::new(1.0, 0.0, 0.0),
1468            Point::new(0.5, 1.0, 0.0),
1469            Point::new(0.5, 0.5, 1.0),
1470            Point::new(0.5, -1.0, 0.0),
1471            Point::new(0.5, -0.5, -1.0),
1472        ];
1473        let first = tetrahedron([points[0], points[1], points[2], points[3]], 0);
1474        let mut second = tetrahedron([points[0], points[1], points[4], points[5]], 0);
1475        for t in &mut second {
1476            *t = t.map(|v| if v >= 2 { v + 2 } else { v });
1477        }
1478        let m = mesh(points.to_vec(), [first, second].concat());
1479        let e = Editable::from(&m, 0.0).unwrap();
1480        check(&e);
1481        assert!(e.is_closed());
1482        assert_eq!(e.components().len(), 2);
1483        assert_eq!(e.non_manifold(), vec![0, 1, 6, 7]);
1484    }
1485
1486    #[test]
1487    fn flipping_faces_reopens_and_recloses_edges() {
1488        let closed = torus(10, 6);
1489        let mut e = Editable::from(&closed, 0.0).unwrap();
1490        let volume = closed.volume();
1491
1492        let all: Vec<u32> = (0..e.face_count()).map(index).collect();
1493        e.flip_faces(&all);
1494        check(&e);
1495        assert!(e.is_closed());
1496        assert!(e.non_manifold().is_empty());
1497        let flipped = e.to_triangulation();
1498        // The same tetrahedra summed with their signs turned.
1499        assert_eq!(flipped.volume(), -volume);
1500        e.flip_faces(&all);
1501        reads_back(&e, &closed);
1502
1503        // One face turned against its neighbours parts from all three.
1504        e.flip_faces(&[17, 17]);
1505        check(&e);
1506        assert!(!e.is_closed());
1507        let rims = e.rims();
1508        assert_eq!(rims.len(), 2);
1509        // The same three edges, summed from different starts.
1510        assert!((rims[0].length - rims[1].length).abs() <= 4.0 * f64::EPSILON * rims[0].length);
1511        assert_eq!(e.components().len(), 2);
1512        assert!(!e.non_manifold().is_empty());
1513        assert!(
1514            (0..e.vertex_count())
1515                .filter_map(|v| e.position(index(v)).map(|p| (v, p)))
1516                .all(|(v, p)| closed.positions[e.source_vertex(index(v)).unwrap() as usize] == p)
1517        );
1518        e.flip_faces(&[17]);
1519        check(&e);
1520        assert!(e.is_closed());
1521        assert!(e.non_manifold().is_empty());
1522        reads_back(&e, &closed);
1523    }
1524
1525    #[test]
1526    fn sources_survive_welding_removal_and_flips() {
1527        let pieces = soup(&grid(4));
1528        let mut e = Editable::from(&pieces, 1e-9).unwrap();
1529        check(&e);
1530        assert_eq!(e.vertex_count(), 25);
1531        // Each vertex is the first copy of its position the soup lists.
1532        for v in 0..25 {
1533            let s = e.source_vertex(v).unwrap() as usize;
1534            let p = e.position(v).unwrap();
1535            assert_eq!(pieces.positions[s], p);
1536            assert_eq!(pieces.positions.iter().position(|&q| q == p), Some(s));
1537        }
1538        e.remove_faces(&[0, 1, 10, 11, 12, 13, 22, 23]);
1539        e.flip_faces(&[2, 3, 30, 31]);
1540        check(&e);
1541        assert!(e.vertex_count() > 25);
1542        for v in 0..e.vertex_count() {
1543            if let Some(p) = e.position(index(v)) {
1544                let s = e.source_vertex(index(v)).unwrap();
1545                assert_eq!(pieces.positions[s as usize], p);
1546            }
1547        }
1548        assert_eq!(e.source_vertex(index(e.vertex_count())), None);
1549    }
1550
1551    #[test]
1552    fn single_precision_copies_weld_within_their_quantum() {
1553        // A cube read as a soup of `f32` triangles, each copy of a corner
1554        // after the first moved by one unit in the last place.
1555        let cube = cube_of_grids(1, 100.0);
1556        let mut pieces = soup(&cube);
1557        let mut seen: Vec<Point> = Vec::new();
1558        for p in &mut pieces.positions {
1559            let single = |x: f64| f64::from(x as f32);
1560            let up = |x: f64| f64::from(f32::from_bits((x as f32).to_bits() + 1));
1561            let q = Point::new(single(p.x), single(p.y), single(p.z));
1562            *p = if seen.contains(&q) {
1563                Point::new(up(q.x), q.y, up(q.z))
1564            } else {
1565                seen.push(q);
1566                q
1567            };
1568        }
1569        // Units in the last place at 100 are 2^-17.
1570        let quantum = 2.0_f64.powi(-17);
1571        let e = Editable::from(&pieces, 2.0 * quantum).unwrap();
1572        check(&e);
1573        assert!(e.is_closed());
1574        assert_eq!(e.to_triangulation().positions.len(), 8);
1575        for v in 0..8 {
1576            let s = e.source_vertex(v).unwrap() as usize;
1577            assert_eq!(e.position(v), Some(seen[v as usize]));
1578            assert_eq!(pieces.positions[s], seen[v as usize]);
1579        }
1580        // Closer than the copies stand, they stay apart.
1581        let apart = Editable::from(&pieces, quantum / 2.0).unwrap();
1582        assert!(!apart.is_closed());
1583    }
1584
1585    #[test]
1586    fn a_weld_tie_goes_to_the_lowest_kept_point() {
1587        // The third point is within the distance of both kept ones, and
1588        // the grid holds the later one in the first cell it looks at.
1589        let points = [1.5, 0.0, 0.75].map(|x| Point::new(x, 0.0, 0.0));
1590        let (kept, remap, founders) = weld_points(&points, 1.0);
1591        assert_eq!(kept.len(), 2);
1592        assert_eq!(remap, vec![0, 1, 0]);
1593        assert_eq!(founders, vec![0, 1]);
1594    }
1595
1596    #[test]
1597    fn bad_input_is_refused() {
1598        let refusal = |m: &Triangulation, weld: f64| Editable::from(m, weld).unwrap_err();
1599        let m = mesh(vec![Point::new(0.0, 0.0, 0.0); 3], vec![[0, 1, 3]]);
1600        let e = refusal(&m, 0.0);
1601        assert!(matches!(e, ogeom_core::OgeomError::Range(_)));
1602        assert!(e.to_string().contains("names vertex 3 of 3"), "{e}");
1603        let m = grid(1);
1604        for weld in [-1.0, f64::NAN] {
1605            let e = refusal(&m, weld);
1606            assert!(matches!(e, ogeom_core::OgeomError::Numeric(_)));
1607            assert!(e.to_string().contains("weld distance"), "{e}");
1608        }
1609        let mut m = grid(1);
1610        m.positions[2].x = f64::NAN;
1611        let e = refusal(&m, 0.0);
1612        assert!(e.to_string().contains("vertex 2 is not finite"), "{e}");
1613    }
1614
1615    #[test]
1616    fn an_editable_mesh_crosses_threads() {
1617        fn send_sync<T: Send + Sync>() {}
1618        send_sync::<Editable>();
1619    }
1620
1621    #[test]
1622    fn the_span_is_the_farthest_pair() {
1623        let points: Vec<Point> = (0..200)
1624            .map(|k| {
1625                let t = f64::from(k) * 0.37;
1626                Point::new(t.sin() * 3.0, (t * 1.7).cos() * 2.0, (t * 0.3).sin())
1627            })
1628            .collect();
1629        let brute = points
1630            .iter()
1631            .flat_map(|p| points.iter().map(move |q| p.distance(*q)))
1632            .fold(0.0, f64::max);
1633        assert_eq!(diameter(&points), brute);
1634    }
1635
1636    proptest! {
1637        #![proptest_config(ProptestConfig::with_cases(64))]
1638
1639        /// After any removals and flips the structure is sound, and reads
1640        /// the same as one built afresh from what it writes.
1641        #[test]
1642        fn edits_keep_a_sound_manifold(
1643            edits in prop::collection::vec(
1644                (any::<bool>(), prop::collection::vec(0_u32..60, 0..8)),
1645                1..6,
1646            )
1647        ) {
1648            let mut e = Editable::from(&torus(6, 5), 0.0).unwrap();
1649            for (remove, faces) in &edits {
1650                if *remove {
1651                    e.remove_faces(faces);
1652                } else {
1653                    e.flip_faces(faces);
1654                }
1655                check(&e);
1656            }
1657            let written = e.to_triangulation();
1658            let again = Editable::from(&written, 0.0).unwrap();
1659            prop_assert_eq!(again.to_triangulation(), written);
1660            prop_assert_eq!(again.is_closed(), e.is_closed());
1661            prop_assert_eq!(again.components().len(), e.components().len());
1662            let lengths = |m: &Editable| {
1663                let mut l: Vec<usize> = m.rims().iter().map(|r| r.edges.len()).collect();
1664                l.sort_unstable();
1665                l
1666            };
1667            prop_assert_eq!(lengths(&again), lengths(&e));
1668        }
1669    }
1670}