Skip to main content

fast_mesh_ops/
stretch.rs

1//! Adaptive splitting: triangles a deformation stretched split on their own,
2//! and nothing else changes.
3//!
4//! [`capture_rest_sizes`] records each vertex's *rest size*, the longest edge
5//! on it, in the [`REST_SIZE_CHANNEL`] channel, before the deformation starts.
6//! [`split_stretched`] then splits an edge only when it has grown past
7//! [`StretchOptions::limit`] times the smaller rest size of its two endpoints.
8//!
9//! An edge's rest length is at most either endpoint's rest size, so an edge
10//! that did not stretch can never reach the limit, however long it is next to
11//! the rest of the mesh. A mesh with fine and coarse areas keeps both: each
12//! area is measured against itself, not against a mesh-wide mean.
13//!
14//! # What changes
15//!
16//! - Existing vertices keep their ids and positions, so anything that stores
17//!   vertex ids stays valid.
18//! - The triangles on a split edge are bisected. A triangle no split edge
19//!   touches keeps its slot and its corners: the only unstretched triangles
20//!   that change are neighbours of a stretched edge, to stay conforming.
21//! - Nothing collapses and nothing flips.
22//!
23//! The longest edge splits first, which keeps the bisected triangles from
24//! degrading into slivers.
25//!
26//! # Where new vertices go
27//!
28//! Existing vertices cannot move, which rules out Loop's edge rule: its
29//! `3/8 (a + b) + 1/8 (c + d)` relies on the vertex rule moving `a` and `b`
30//! too. Alone it pulls a new vertex below the midpoint on a convex surface and
31//! measures no better than a midpoint against the true normal. A new vertex
32//! takes the middle of the cubic Hermite curve between the edge's endpoints
33//! and their normals instead, which needs only the edge
34//! and is exact to second order on a smooth surface:
35//! `(a + b) / 2 - (((b - a) . na) na + ((a - b) . nb) nb) / 8`. An endpoint's
36//! normal averages only the triangles around it that face like the edge's own
37//! (within 45 degrees), so a flat face stays flat next to a hard edge. A
38//! gentler crease reads as a curve and rounds off: a flat face beside one bulges
39//! by up to about 5% of its edge.
40//!
41//! An exact midpoint is used on a boundary or non-manifold edge, on a sharp
42//! edge (owners more than 45 degrees apart), and wherever the curved point
43//! would fold a child triangle. The boundary rule is what keeps a UV seam
44//! closed: its two sides are separate vertices at one position, and both must
45//! compute the same new point.
46//!
47//! # Seams
48//!
49//! Two vertices at exactly one position take the larger of their rest sizes,
50//! so both sides of a seam use one limit and split together. Boundary edges
51//! also split before any interior edge and are exempt from
52//! [`StretchOptions::max_splits`], so a seam cannot open at the cap.
53//!
54//! # Cost
55//!
56//! One pass tests the vertices against the region and then touches only the
57//! edges around vertices inside it, using the mesh's vertex index
58//! ([`Mesh::ensure_incidence`]). It never copies or sorts the whole mesh.
59//! Without a cached index (inside a transaction that has already changed
60//! topology, for example) it builds a private one first, which costs a pass
61//! over every triangle.
62
63use std::cmp::Ordering;
64use std::collections::{BinaryHeap, HashMap};
65
66use fast_mesh_store::{
67    ChannelDesc, Incidence, Mesh, StoreError, Transaction, TriangleId, VertexId,
68};
69
70use crate::remesh::{
71    NewVert, Region, apex_of, blend_parents, carries, cross3, degenerate, dot3, f64v, midpoint,
72    runs_directed, sub3,
73};
74use crate::{OpsError, Result, allocation_allowed, filled, push, reserve};
75
76/// The source channel [`capture_rest_sizes`] writes: one value per vertex
77/// slot, interpolated for new vertices. Zero means no rest size is known.
78pub const REST_SIZE_CHANNEL: &str = "rest_edge_size";
79/// Default for [`StretchOptions::limit`].
80pub const DEFAULT_STRETCH_LIMIT: f32 = 4.0 / 3.0;
81/// Default for [`StretchOptions::max_splits`].
82pub const DEFAULT_MAX_SPLITS: u32 = 1 << 20;
83/// Cosine of the angle (45 degrees) past which two faces count as separated
84/// by a sharp edge.
85const SHARP_COS: f64 = std::f64::consts::FRAC_1_SQRT_2;
86
87/// Options for [`split_stretched`].
88#[derive(Debug, Clone, Copy, PartialEq)]
89pub struct StretchOptions {
90    /// Vertices inside this region have their edges checked. Edges with no
91    /// endpoint inside are left alone.
92    pub region: Region,
93    /// An edge splits past this multiple of the smaller rest size of its
94    /// endpoints. Must be finite and above 1: at 1 or below, an edge that did
95    /// not stretch could split.
96    pub limit: f32,
97    /// Most interior splits in one call; the longest go first and
98    /// [`StretchReport::remaining`] counts the rest, for a later call.
99    /// Boundary edges do not count, see the module docs.
100    pub max_splits: u32,
101    /// New interior vertices follow the surface's curvature. `false` puts
102    /// every new vertex at its edge's midpoint.
103    pub smooth: bool,
104}
105
106impl Default for StretchOptions {
107    fn default() -> Self {
108        StretchOptions {
109            region: Region::default(),
110            limit: DEFAULT_STRETCH_LIMIT,
111            max_splits: DEFAULT_MAX_SPLITS,
112            smooth: true,
113        }
114    }
115}
116
117/// Outcome of [`split_stretched`].
118#[derive(Debug, Clone, Default, PartialEq, Eq)]
119pub struct StretchReport {
120    /// Edge splits performed.
121    pub splits: usize,
122    /// Edges still past the limit because [`StretchOptions::max_splits`] ran
123    /// out.
124    pub remaining: usize,
125    /// `[new, a, b]` per split, in order: the new vertex and its edge's
126    /// endpoints, which may be new vertices from earlier in this list. For a
127    /// caller that holds per-vertex state of its own and has to carry it over.
128    pub vertices: Vec<[VertexId; 3]>,
129}
130
131/// Records every vertex's rest size for [`split_stretched`]: the longest edge
132/// on it, or on any vertex at exactly its position. Replaces an earlier
133/// capture, and builds the mesh's vertex index if needed.
134///
135/// Call it before the deformation starts, and keep it across strokes that
136/// should share one density: capturing again accepts what earlier strokes
137/// stretched as the new rest shape, which [`ensure_rest_sizes`] avoids. This is
138/// not an edit: no undo step, no journal entry, and the mesh's revision does not
139/// change. It costs one pass over the triangles and a sort of the vertices.
140///
141/// The sizes last as long as they are true: a topology edit other than
142/// [`split_stretched`] drops them (undoing that edit brings them back), and so
143/// does undoing or redoing an edit from before the capture.
144/// [`split_stretched`] then fails until they are taken again. Moving vertices
145/// does not drop them, whichever tool does it: that is the stretch they measure.
146///
147/// # Errors
148/// [`OpsError::Store`] when memory runs out; the mesh keeps any earlier
149/// capture unless the failure comes after it is replaced.
150pub fn capture_rest_sizes(mesh: &mut Mesh) -> Result<()> {
151    let positions = mesh.positions();
152    let mut sizes: Vec<f32> = filled(mesh.vertex_slots(), 0.0)?;
153    for t in 0..mesh.triangle_slots() as u32 {
154        let Some(c) = mesh.triangle(t) else { continue };
155        if degenerate(&c) {
156            continue;
157        }
158        for k in 0..3 {
159            let (a, b) = (c[k], c[(k + 1) % 3]);
160            let len = length(&positions[a as usize], &positions[b as usize]) as f32;
161            if len.is_finite() {
162                sizes[a as usize] = sizes[a as usize].max(len);
163                sizes[b as usize] = sizes[b as usize].max(len);
164            }
165        }
166    }
167    let mut order: Vec<u32> = Vec::new();
168    reserve(&mut order, sizes.len())?;
169    order.extend((0..sizes.len() as u32).filter(|&v| sizes[v as usize] > 0.0));
170    // `+ 0.0` folds -0 onto 0: seams are recognised by exact coincidence.
171    let key = |v: u32| positions[v as usize].map(|x| (x + 0.0).to_bits());
172    order.sort_unstable_by_key(|&v| key(v));
173    let mut start = 0;
174    while start < order.len() {
175        let group = key(order[start]);
176        let mut end = start + 1;
177        let mut size = sizes[order[start] as usize];
178        while end < order.len() && key(order[end]) == group {
179            size = size.max(sizes[order[end] as usize]);
180            end += 1;
181        }
182        for &v in &order[start..end] {
183            sizes[v as usize] = size;
184        }
185        start = end;
186    }
187    if mesh.channel(REST_SIZE_CHANNEL).is_some() {
188        mesh.remove_channel(REST_SIZE_CHANNEL)?;
189    }
190    mesh.add_channel_with_values(
191        ChannelDesc::source(REST_SIZE_CHANNEL, 1).bound_to_topology(),
192        &sizes,
193    )?;
194    mesh.ensure_incidence()?;
195    Ok(())
196}
197
198/// [`capture_rest_sizes`] unless the mesh holds valid rest sizes. Returns
199/// whether it captured.
200///
201/// # Errors
202/// As for [`capture_rest_sizes`].
203pub fn ensure_rest_sizes(mesh: &mut Mesh) -> Result<bool> {
204    if mesh.channel(REST_SIZE_CHANNEL).is_some() {
205        return Ok(false);
206    }
207    capture_rest_sizes(mesh)?;
208    Ok(true)
209}
210
211/// Splits the edges under `options.region` that have stretched past
212/// [`StretchOptions::limit`], until none is left or
213/// [`StretchOptions::max_splits`] runs out. See the [module docs](self) for
214/// what changes and where new vertices go. New vertices interpolate source
215/// channels, so UVs follow. The plan is computed first and committed through
216/// `tx` at the end: either every split lands or nothing does.
217///
218/// # Errors
219/// [`OpsError::InvalidInput`] for an invalid region or limit, or when the mesh
220/// holds no valid rest sizes: see [`capture_rest_sizes`].
221pub fn split_stretched(tx: &mut Transaction, options: &StretchOptions) -> Result<StretchReport> {
222    let region = &options.region;
223    if !region.radius.is_finite() || region.radius <= 0.0 {
224        return Err(OpsError::InvalidInput(
225            "stretch region radius must be positive and finite",
226        ));
227    }
228    if region.center.iter().any(|c| !c.is_finite()) {
229        return Err(OpsError::InvalidInput(
230            "stretch region center must be finite",
231        ));
232    }
233    if !options.limit.is_finite() || options.limit <= 1.0 {
234        return Err(OpsError::InvalidInput(
235            "stretch limit must be finite and above 1",
236        ));
237    }
238    let mesh = tx.mesh();
239    let Some(sizes) = mesh.channel(REST_SIZE_CHANNEL) else {
240        return Err(OpsError::InvalidInput(
241            "split_stretched needs capture_rest_sizes first",
242        ));
243    };
244    let rows = Rows::of(mesh)?;
245    let mut work = Work {
246        mesh,
247        rows: &rows,
248        slots: mesh.vertex_slots(),
249        sizes,
250        limit: f64::from(options.limit),
251        new_sizes: Vec::new(),
252        edges: Vec::new(),
253        added: Vec::new(),
254        tris: Vec::new(),
255        tri_of: HashMap::new(),
256        row_of: HashMap::new(),
257    };
258    let mut heap = work.initial_edges(&options.region)?;
259    let mut report = StretchReport::default();
260    let mut interior = 0;
261    while let Some(edge) = heap.peek().copied() {
262        if !edge.boundary && interior >= options.max_splits {
263            break;
264        }
265        heap.pop();
266        let owners = work.owners(edge.a, edge.b)?;
267        if owners.is_empty() {
268            continue;
269        }
270        let boundary = owners.len() != 2;
271        let mid = midpoint(&work.pos(edge.a), &work.pos(edge.b));
272        let pos = if options.smooth && !boundary {
273            let curved = work.curved_point(edge.a, edge.b, &owners);
274            if work.shortens(edge, curved) {
275                curved
276            } else {
277                mid
278            }
279        } else {
280            mid
281        };
282        // Too short to split at float precision: it stays as it is.
283        if !work.shortens(edge, pos) {
284            continue;
285        }
286        if !boundary {
287            interior += 1;
288        }
289        work.split(edge.a, edge.b, pos, &owners, boundary, &mut heap)?;
290        report.splits += 1;
291    }
292    report.remaining = heap.len();
293    if report.splits > 0 {
294        let slots = work.slots;
295        let edges = work.edges;
296        let plan = Plan {
297            slots,
298            tris: work.tris,
299            added: work.added,
300        };
301        // Before the first write, like the rest of the plan.
302        reserve(&mut report.vertices, edges.len())?;
303        let ids = tx.keeping_bound_channels(|tx| plan.commit(tx))?;
304        let resolve = |v: u32| {
305            if (v as usize) < slots {
306                v
307            } else {
308                ids[v as usize - slots]
309            }
310        };
311        for (&id, &(a, b)) in ids.iter().zip(&edges) {
312            report.vertices.push([id, resolve(a), resolve(b)]);
313        }
314    }
315    Ok(report)
316}
317
318/// An edge past its limit. Boundary edges order first, then longer ones; the
319/// endpoints break ties so the order never depends on hashing.
320#[derive(Clone, Copy, PartialEq)]
321struct Edge {
322    boundary: bool,
323    len: f64,
324    a: u32,
325    b: u32,
326}
327
328impl Eq for Edge {}
329
330impl Ord for Edge {
331    fn cmp(&self, other: &Self) -> Ordering {
332        self.boundary
333            .cmp(&other.boundary)
334            .then(self.len.total_cmp(&other.len))
335            .then(other.a.cmp(&self.a))
336            .then(other.b.cmp(&self.b))
337    }
338}
339
340impl PartialOrd for Edge {
341    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
342        Some(self.cmp(other))
343    }
344}
345
346/// The triangles of each vertex: the mesh's cached index, or a private one.
347enum Rows<'m> {
348    Cached(&'m Incidence),
349    Built { offsets: Vec<u32>, ids: Vec<u32> },
350}
351
352impl<'m> Rows<'m> {
353    fn of(mesh: &'m Mesh) -> Result<Self> {
354        if let Some(index) = mesh.incidence() {
355            return Ok(Rows::Cached(index));
356        }
357        let mut offsets: Vec<u32> = filled(mesh.vertex_slots() + 1, 0)?;
358        for t in 0..mesh.triangle_slots() as u32 {
359            if let Some(c) = mesh.triangle(t)
360                && !degenerate(&c)
361            {
362                for v in c {
363                    offsets[v as usize + 1] += 1;
364                }
365            }
366        }
367        for v in 1..offsets.len() {
368            offsets[v] += offsets[v - 1];
369        }
370        let mut ids: Vec<u32> = filled(offsets[offsets.len() - 1] as usize, 0)?;
371        let mut next: Vec<u32> = Vec::new();
372        reserve(&mut next, offsets.len())?;
373        next.extend_from_slice(&offsets);
374        for t in 0..mesh.triangle_slots() as u32 {
375            if let Some(c) = mesh.triangle(t)
376                && !degenerate(&c)
377            {
378                for v in c {
379                    ids[next[v as usize] as usize] = t;
380                    next[v as usize] += 1;
381                }
382            }
383        }
384        Ok(Rows::Built { offsets, ids })
385    }
386
387    fn row(&self, v: u32) -> &[TriangleId] {
388        match self {
389            Rows::Cached(index) => index.triangles(v),
390            Rows::Built { offsets, ids } => {
391                &ids[offsets[v as usize] as usize..offsets[v as usize + 1] as usize]
392            }
393        }
394    }
395}
396
397/// A triangle the plan has read or made: `id` is `None` for one a split made.
398struct WorkTri {
399    id: Option<TriangleId>,
400    corners: [u32; 3],
401}
402
403/// The plan's state, sparse over the mesh: vertices past `slots` are new, and
404/// only triangles around a split edge are ever loaded.
405///
406/// A vertex's row is loaded before anything it holds changes, and a split only
407/// changes the rows of its edge's endpoints and apexes, which it loads first.
408/// So a row not loaded yet still matches the mesh.
409struct Work<'a> {
410    mesh: &'a Mesh,
411    rows: &'a Rows<'a>,
412    slots: usize,
413    sizes: &'a [f32],
414    limit: f64,
415    new_sizes: Vec<f32>,
416    /// The edge each entry of `added` split.
417    edges: Vec<(u32, u32)>,
418    added: Vec<NewVert>,
419    tris: Vec<WorkTri>,
420    tri_of: HashMap<TriangleId, usize>,
421    row_of: HashMap<u32, Vec<usize>>,
422}
423
424impl Work<'_> {
425    fn pos(&self, v: u32) -> [f32; 3] {
426        let i = v as usize;
427        if i < self.slots {
428            self.mesh.positions()[i]
429        } else {
430            self.added[i - self.slots].pos
431        }
432    }
433
434    fn size(&self, v: u32) -> f32 {
435        let i = v as usize;
436        if i < self.slots {
437            self.sizes[i]
438        } else {
439            self.new_sizes[i - self.slots]
440        }
441    }
442
443    /// The length past which `(a, b)` splits; `None` when neither endpoint
444    /// has a rest size, so nothing says the edge stretched.
445    fn edge_limit(&self, a: u32, b: u32) -> Option<f64> {
446        let usable = |s: f32| s.is_finite() && s > 0.0;
447        let (sa, sb) = (self.size(a), self.size(b));
448        let size = match (usable(sa), usable(sb)) {
449            (true, true) => sa.min(sb),
450            (true, false) => sa,
451            (false, true) => sb,
452            (false, false) => return None,
453        };
454        Some(f64::from(size) * self.limit)
455    }
456
457    /// Whether a vertex at `pos` makes both halves of `edge` shorter than the
458    /// edge. It is what ends the pass: without it, a midpoint that rounds onto
459    /// an endpoint leaves a half as long as the edge, past its limit for ever.
460    fn shortens(&self, edge: Edge, pos: [f32; 3]) -> bool {
461        pos.iter().all(|x| x.is_finite())
462            && length(&self.pos(edge.a), &pos) < edge.len
463            && length(&pos, &self.pos(edge.b)) < edge.len
464    }
465
466    /// The edge as a heap entry when it is past its limit.
467    fn over(&self, a: u32, b: u32, boundary: bool) -> Option<Edge> {
468        let limit = self.edge_limit(a, b)?;
469        let len = length(&self.pos(a), &self.pos(b));
470        (len > limit).then_some(Edge {
471            boundary,
472            len,
473            a: a.min(b),
474            b: a.max(b),
475        })
476    }
477
478    /// Every edge past its limit with an endpoint in the region. The list is
479    /// deduplicated first, so an edge seen from both ends enters the heap once.
480    fn initial_edges(&mut self, region: &Region) -> Result<BinaryHeap<Edge>> {
481        let positions = self.mesh.positions();
482        let center = f64v(&region.center);
483        let radius = f64::from(region.radius);
484        let mut found: Vec<(u32, u32)> = Vec::new();
485        for (v, p) in positions.iter().enumerate() {
486            let v = v as u32;
487            // Squared: this runs for every vertex, and `hypot` is slow on wasm.
488            let d = sub3(f64v(p), center);
489            if dot3(d, d) >= radius * radius || !self.mesh.is_vertex_alive(v) {
490                continue;
491            }
492            for &t in self.rows.row(v) {
493                let Some(c) = self.mesh.triangle(t) else {
494                    continue;
495                };
496                if degenerate(&c) {
497                    continue;
498                }
499                for other in c {
500                    if other != v && self.over(v, other, false).is_some() {
501                        push(&mut found, (v.min(other), v.max(other)))?;
502                    }
503                }
504            }
505        }
506        found.sort_unstable();
507        found.dedup();
508        let mut heap = BinaryHeap::new();
509        allocation_allowed()?;
510        heap.try_reserve(found.len()).map_err(|_| oom())?;
511        for (a, b) in found {
512            let owners = self.owners(a, b)?;
513            if let Some(edge) = self.over(a, b, owners.len() != 2) {
514                heap.push(edge);
515            }
516        }
517        Ok(heap)
518    }
519
520    /// Makes sure `v`'s row is in `row_of`.
521    fn load(&mut self, v: u32) -> Result<()> {
522        if self.row_of.contains_key(&v) {
523            return Ok(());
524        }
525        let base = if (v as usize) < self.slots {
526            self.rows.row(v)
527        } else {
528            &[]
529        };
530        let mut row: Vec<usize> = Vec::new();
531        reserve(&mut row, base.len())?;
532        for &t in base {
533            let i = match self.tri_of.get(&t) {
534                Some(&i) => i,
535                None => {
536                    let Some(corners) = self.mesh.triangle(t) else {
537                        continue;
538                    };
539                    let i = self.tris.len();
540                    push(
541                        &mut self.tris,
542                        WorkTri {
543                            id: Some(t),
544                            corners,
545                        },
546                    )?;
547                    reserve_map(&mut self.tri_of)?;
548                    self.tri_of.insert(t, i);
549                    i
550                }
551            };
552            row.push(i);
553        }
554        reserve_map(&mut self.row_of)?;
555        self.row_of.insert(v, row);
556        Ok(())
557    }
558
559    /// The triangles on `(a, b)`, in the order of `a`'s row. Empty once the
560    /// edge has split. Degenerate triangles do not count, and keep the edge.
561    fn owners(&mut self, a: u32, b: u32) -> Result<Vec<usize>> {
562        self.load(a)?;
563        self.load(b)?;
564        let mut owners: Vec<usize> = Vec::new();
565        for &i in &self.row_of[&a] {
566            let c = &self.tris[i].corners;
567            if !degenerate(c) && carries(c, a, b) {
568                push(&mut owners, i)?;
569            }
570        }
571        Ok(owners)
572    }
573
574    fn normal(&self, corners: &[u32; 3]) -> [f64; 3] {
575        let [p0, p1, p2] = corners.map(|v| f64v(&self.pos(v)));
576        cross3(sub3(p1, p0), sub3(p2, p0))
577    }
578
579    /// The endpoint's normal over the triangles facing like `facing`, a unit
580    /// vector, weighted by area. `None` when they cancel out.
581    fn vertex_normal(&self, v: u32, facing: [f64; 3]) -> Option<[f64; 3]> {
582        let mut sum = [0.0; 3];
583        for &i in &self.row_of[&v] {
584            let c = &self.tris[i].corners;
585            if degenerate(c) {
586                continue;
587            }
588            let n = self.normal(c);
589            let Some(direction) = unit(n) else { continue };
590            if dot3(direction, facing) >= SHARP_COS {
591                sum = [sum[0] + n[0], sum[1] + n[1], sum[2] + n[2]];
592            }
593        }
594        unit(sum)
595    }
596
597    /// The new vertex of an interior edge: see the module docs. Falls back to
598    /// the midpoint on a sharp edge, and when a child would fold.
599    fn curved_point(&self, a: u32, b: u32, owners: &[usize]) -> [f32; 3] {
600        let (pa, pb) = (self.pos(a), self.pos(b));
601        let mid = midpoint(&pa, &pb);
602        let n0 = unit(self.normal(&self.tris[owners[0]].corners));
603        let n1 = unit(self.normal(&self.tris[owners[1]].corners));
604        let (Some(n0), Some(n1)) = (n0, n1) else {
605            return mid;
606        };
607        if dot3(n0, n1) < SHARP_COS {
608            return mid;
609        }
610        let Some(facing) = unit([n0[0] + n1[0], n0[1] + n1[1], n0[2] + n1[2]]) else {
611            return mid;
612        };
613        let (Some(na), Some(nb)) = (self.vertex_normal(a, facing), self.vertex_normal(b, facing))
614        else {
615            return mid;
616        };
617        let (fa, fb) = (f64v(&pa), f64v(&pb));
618        let wa = dot3(sub3(fb, fa), na);
619        let wb = dot3(sub3(fa, fb), nb);
620        let curved: [f64; 3] =
621            std::array::from_fn(|k| (fa[k] + fb[k]) / 2.0 - (wa * na[k] + wb * nb[k]) / 8.0);
622        let pos = curved.map(|x| x as f32);
623        if pos.iter().any(|x| !x.is_finite()) {
624            return mid;
625        }
626        let m = f64v(&pos);
627        for &i in owners {
628            let c = self.tris[i].corners;
629            let apex = f64v(&self.pos(apex_of(&c, a, b)));
630            let parent = self.normal(&c);
631            let (first, second) = if runs_directed(&c, a, b) {
632                ((fa, m), (m, fb))
633            } else {
634                ((fb, m), (m, fa))
635            };
636            let n1 = cross3(sub3(first.1, first.0), sub3(apex, first.0));
637            let n2 = cross3(sub3(second.1, second.0), sub3(apex, second.0));
638            if dot3(n1, parent) <= 0.0 || dot3(n2, parent) <= 0.0 {
639                return mid;
640            }
641        }
642        pos
643    }
644
645    /// Splits `(a, b)` at `pos`: each owner becomes two triangles that follow
646    /// its winding, and the edges this made join the heap if past their limit.
647    fn split(
648        &mut self,
649        a: u32,
650        b: u32,
651        pos: [f32; 3],
652        owners: &[usize],
653        boundary: bool,
654        heap: &mut BinaryHeap<Edge>,
655    ) -> Result<()> {
656        let m = (self.slots + self.added.len()) as u32;
657        let parents = blend_parents(a, b, self.slots, &self.added)?;
658        let size = self.size(a) / 2.0 + self.size(b) / 2.0;
659        push(&mut self.new_sizes, size)?;
660        push(&mut self.edges, (a, b))?;
661        push(&mut self.added, NewVert { pos, parents })?;
662        reserve_map(&mut self.row_of)?;
663        self.row_of.insert(m, Vec::new());
664        for &i in owners {
665            let corners = self.tris[i].corners;
666            let apex = apex_of(&corners, a, b);
667            self.load(apex)?;
668            let (first, second, moved) = if runs_directed(&corners, a, b) {
669                ([a, m, apex], [m, b, apex], b)
670            } else {
671                ([b, m, apex], [m, a, apex], a)
672            };
673            self.tris[i].corners = first;
674            let child = self.tris.len();
675            push(
676                &mut self.tris,
677                WorkTri {
678                    id: None,
679                    corners: second,
680                },
681            )?;
682            let row = self.row_of.get_mut(&moved).expect("endpoints are loaded");
683            row.retain(|&t| t != i);
684            push(row, child)?;
685            let row = self.row_of.get_mut(&apex).expect("apex is loaded");
686            push(row, child)?;
687            let row = self.row_of.get_mut(&m).expect("the new row is there");
688            push(row, i)?;
689            push(row, child)?;
690            if let Some(edge) = self.over(m, apex, false) {
691                reserve_heap(heap)?;
692                heap.push(edge);
693            }
694        }
695        for end in [a, b] {
696            if let Some(edge) = self.over(end, m, boundary) {
697                reserve_heap(heap)?;
698                heap.push(edge);
699            }
700        }
701        Ok(())
702    }
703}
704
705/// What a finished [`Work`] writes.
706struct Plan {
707    slots: usize,
708    tris: Vec<WorkTri>,
709    added: Vec<NewVert>,
710}
711
712impl Plan {
713    /// Everything is reserved before the first write, so an allocation failure
714    /// leaves an empty patch.
715    fn commit(self, tx: &mut Transaction) -> Result<Vec<VertexId>> {
716        let mut ids: Vec<VertexId> = Vec::new();
717        reserve(&mut ids, self.added.len())?;
718        let mut set_ids: Vec<TriangleId> = Vec::new();
719        reserve(&mut set_ids, self.tris.len())?;
720        let mut set_tris: Vec<[VertexId; 3]> = Vec::new();
721        reserve(&mut set_tris, self.tris.len())?;
722        let mut new_tris: Vec<[VertexId; 3]> = Vec::new();
723        reserve(&mut new_tris, self.tris.len())?;
724        for nv in &self.added {
725            ids.push(tx.add_vertex_with_parents(nv.pos, &nv.parents)?);
726        }
727        let slots = self.slots;
728        let resolve = |v: u32| {
729            if (v as usize) < slots {
730                v
731            } else {
732                ids[v as usize - slots]
733            }
734        };
735        let mesh = tx.mesh();
736        for tri in &self.tris {
737            let current = tri.corners.map(resolve);
738            match tri.id {
739                Some(id) => {
740                    if mesh.triangle(id) != Some(current) {
741                        set_ids.push(id);
742                        set_tris.push(current);
743                    }
744                }
745                None => new_tris.push(current),
746            }
747        }
748        tx.set_triangles(&set_ids, &set_tris)?;
749        tx.add_triangles(&new_tris)?;
750        Ok(ids)
751    }
752}
753
754fn oom() -> OpsError {
755    OpsError::Store(StoreError::OutOfMemory)
756}
757
758fn reserve_map<K: Eq + std::hash::Hash, V>(map: &mut HashMap<K, V>) -> Result<()> {
759    allocation_allowed()?;
760    map.try_reserve(1).map_err(|_| oom())
761}
762
763fn reserve_heap(heap: &mut BinaryHeap<Edge>) -> Result<()> {
764    allocation_allowed()?;
765    heap.try_reserve(1).map_err(|_| oom())
766}
767
768fn length(a: &[f32; 3], b: &[f32; 3]) -> f64 {
769    let d = sub3(f64v(a), f64v(b));
770    dot3(d, d).sqrt()
771}
772
773fn unit(v: [f64; 3]) -> Option<[f64; 3]> {
774    let len = dot3(v, v).sqrt();
775    (len.is_finite() && len > 0.0).then(|| v.map(|x| x / len))
776}
777
778#[cfg(test)]
779#[path = "stretch_tests.rs"]
780mod tests;