graphic design tool
git clone https://git.lucas.co/cce-designer.git
src/remesh.rs (25K)
1 //! Incremental isotropic remeshing.
2 //!
3 //! The load-bearing half of Phase 3. Without topology that keeps primitives
4 //! proportional to surface area, every growth simulation degenerates within a
5 //! few dozen frames: `develop` pushes points apart, the triangles between them
6 //! stretch, and an attribute diffused across a stretched mesh is being
7 //! averaged over distances that no longer mean what they meant.
8 //!
9 //! The algorithm is Botsch and Kobbelt's, four passes over the mesh repeated a
10 //! few times:
11 //!
12 //! 1. **Split** every edge longer than 4/3 of the target length.
13 //! 2. **Collapse** every edge shorter than 4/5 of it.
14 //! 3. **Flip** edges that would bring their four points closer to valence 6.
15 //! 4. **Relax** each point toward the centroid of its neighbours, with the
16 //! normal component removed so the pass smooths the triangulation without
17 //! moving the surface.
18 //!
19 //! The 4/3 and 4/5 are the paper's, and they are not arbitrary: a window
20 //! narrower than that lets a split produce two edges short enough for the next
21 //! collapse to undo, and the mesh oscillates instead of converging.
22 //!
23 //! ## What it does with the simulation's data
24 //!
25 //! This is the node that Phase 2's contract was built for, so it is careful
26 //! about identity and attributes:
27 //!
28 //! - A **split** allocates a new point and interpolates every attribute from
29 //! the two endpoints. A place that did not exist gets values consistent with
30 //! its neighbourhood rather than zeros.
31 //! - A **collapse** keeps one endpoint — its identity and its values — rather
32 //! than averaging into a new point. The surviving point is one the solver
33 //! has been writing to, and a remesh should cost the simulation as little
34 //! memory as it can.
35 //! - A **flip** and a **relax** change no attributes at all.
36 //!
37 //! The paper's fifth pass is here too: after relaxing, every point is pulled
38 //! back onto the surface the remesh started from. Tangential relaxation alone
39 //! lets a surface creep — each point slides a little, and over a few dozen
40 //! iterations a sphere quietly shrinks — so the projection is what makes it
41 //! safe to run a remesh every frame of a solve, which is the whole point.
42
43 use crate::detail::{AttribData, AttribValue, Detail, PointId};
44 use glam::Vec3;
45 use std::collections::HashMap;
46
47 /// How the four passes are tuned. Defaults are the paper's.
48 #[derive(Clone, Copy, Debug)]
49 pub struct Settings {
50 /// The edge length the mesh is steered toward.
51 pub target: f32,
52 /// How many times the four passes run.
53 pub iterations: usize,
54 /// Strength of the tangential relaxation, 0 to 1.
55 pub relax: f32,
56 pub split: bool,
57 pub collapse: bool,
58 pub flip: bool,
59 /// Pull relaxed points back onto the input surface.
60 pub project: bool,
61 }
62
63 impl Default for Settings {
64 fn default() -> Self {
65 Self {
66 target: 0.1,
67 iterations: 3,
68 relax: 0.5,
69 split: true,
70 collapse: true,
71 flip: true,
72 project: true,
73 }
74 }
75 }
76
77 /// A triangle mesh in a form that can be edited in place.
78 ///
79 /// [`Detail`]'s CSR storage is compact and good for reading, which is what
80 /// every other operator does to it. Remeshing is the one thing that rewires
81 /// topology per edge, so it converts in, edits, and converts back rather than
82 /// making every other operator pay for an edit-friendly layout.
83 ///
84 /// Points and triangles are tombstoned rather than removed during a pass:
85 /// compacting mid-pass would invalidate every index the pass is holding.
86 struct Mesh {
87 pos: Vec<Vec3>,
88 ids: Vec<PointId>,
89 /// Per point, its attribute values as loose components, in `attr_names`
90 /// order — the form the split interpolation works in.
91 attrs: Vec<Vec<f32>>,
92 attr_names: Vec<String>,
93 attr_types: Vec<crate::detail::AttribType>,
94 groups: Vec<(String, Vec<bool>)>,
95 tris: Vec<[u32; 3]>,
96 dead_point: Vec<bool>,
97 dead_tri: Vec<bool>,
98 /// Point to the triangles that have referenced it. Maintained as
99 /// triangles are added and rewired, and read through a filter that drops
100 /// dead entries and ones the point has since been rewired out of — so a
101 /// stale entry is harmless and nothing has to be removed eagerly.
102 ///
103 /// Without this every adjacency question is a scan of the whole mesh, and
104 /// the flip pass alone asks four per edge.
105 p2t: Vec<Vec<usize>>,
106 next_id: PointId,
107 }
108
109 impl Mesh {
110 fn from_detail(d: &Detail) -> Mesh {
111 let attr_names: Vec<String> = d.points().names().iter().map(|s| s.to_string()).collect();
112 let attr_types: Vec<crate::detail::AttribType> = attr_names
113 .iter()
114 .filter_map(|n| d.points().get(n).map(|a| a.ty()))
115 .collect();
116 let attrs: Vec<Vec<f32>> = (0..d.num_points())
117 .map(|p| {
118 attr_names
119 .iter()
120 .flat_map(|n| match d.points().value(n, p) {
121 Some(AttribValue::Float(x)) => vec![x],
122 Some(AttribValue::Float2(x)) => x.to_vec(),
123 Some(AttribValue::Float3(x)) => x.to_vec(),
124 Some(AttribValue::Float4(x)) => x.to_vec(),
125 Some(AttribValue::Int(x)) => vec![x as f32],
126 None => vec![],
127 })
128 .collect()
129 })
130 .collect();
131 let groups: Vec<(String, Vec<bool>)> = d
132 .points()
133 .group_names()
134 .iter()
135 .map(|g| {
136 (
137 g.to_string(),
138 (0..d.num_points()).map(|p| d.points().in_group(g, p)).collect(),
139 )
140 })
141 .collect();
142
143 // Only triangles are remeshed. A polygon fans on the way in, which is
144 // what the renderer does with it anyway.
145 let mut tris = Vec::new();
146 for prim in 0..d.num_prims() {
147 let pts = d.prim_points(prim);
148 for i in 1..pts.len().saturating_sub(1) {
149 tris.push([pts[0], pts[i], pts[i + 1]]);
150 }
151 }
152
153 let n = d.num_points();
154 let max_id = d.ids().iter().copied().max().map(|m| m + 1).unwrap_or(0);
155 let mut p2t: Vec<Vec<usize>> = vec![Vec::new(); n];
156 for (t, tri) in tris.iter().enumerate() {
157 for &q in tri {
158 p2t[q as usize].push(t);
159 }
160 }
161 Mesh {
162 pos: (0..n).map(|p| d.pos(p)).collect(),
163 ids: d.ids().to_vec(),
164 attrs,
165 attr_names,
166 attr_types,
167 groups,
168 dead_tri: vec![false; tris.len()],
169 tris,
170 dead_point: vec![false; n],
171 p2t,
172 next_id: max_id,
173 }
174 }
175
176 /// Add a triangle, keeping the incidence in step.
177 fn add_tri(&mut self, tri: [u32; 3]) {
178 let t = self.tris.len();
179 self.tris.push(tri);
180 self.dead_tri.push(false);
181 for &q in &tri {
182 self.p2t[q as usize].push(t);
183 }
184 }
185
186 /// Point `from` to `to` in triangle `t`, keeping the incidence in step.
187 fn rewire(&mut self, t: usize, from: u32, to: u32) {
188 for slot in self.tris[t].iter_mut() {
189 if *slot == from {
190 *slot = to;
191 }
192 }
193 self.p2t[to as usize].push(t);
194 }
195
196 /// Live triangles using both endpoints of an edge.
197 fn tris_on_edge(&self, a: u32, b: u32) -> Vec<usize> {
198 let mut out: Vec<usize> = self
199 .p2t
200 .get(a as usize)
201 .map(|ts| {
202 ts.iter()
203 .copied()
204 .filter(|&t| {
205 !self.dead_tri[t] && self.tris[t].contains(&a) && self.tris[t].contains(&b)
206 })
207 .collect()
208 })
209 .unwrap_or_default();
210 out.sort_unstable();
211 out.dedup();
212 out
213 }
214
215 fn into_detail(mut self) -> Detail {
216 self.dead_tri.resize(self.tris.len(), false);
217 // Drop points nothing references any more, as well as the ones
218 // collapse tombstoned: a split-then-collapse can strand a point that
219 // was never itself collapsed.
220 let mut used = vec![false; self.pos.len()];
221 for (t, tri) in self.tris.iter().enumerate() {
222 if self.dead_tri[t] {
223 continue;
224 }
225 for &p in tri {
226 used[p as usize] = true;
227 }
228 }
229
230 let mut d = Detail::new();
231 let mut remap = vec![u32::MAX; self.pos.len()];
232 let mut kept: Vec<usize> = Vec::new();
233 for p in 0..self.pos.len() {
234 if self.dead_point[p] || !used[p] {
235 continue;
236 }
237 remap[p] = d.add_point(self.pos[p]);
238 kept.push(p);
239 }
240 // Identities are restored rather than re-allocated: a point that
241 // survived a remesh is the same point, and the solver has been writing
242 // to it.
243 // `kept` and the points just added are the same list, so this cannot
244 // fail. Asserted rather than discarded because the failure mode is a
245 // silently renumbered mesh, which a simulation would experience as
246 // every point forgetting itself at once.
247 d.set_ids(kept.iter().map(|&p| self.ids[p]).collect(), self.next_id)
248 .expect("one identity per surviving point");
249
250 for (t, tri) in self.tris.iter().enumerate() {
251 if self.dead_tri[t] {
252 continue;
253 }
254 let mapped = [remap[tri[0] as usize], remap[tri[1] as usize], remap[tri[2] as usize]];
255 if mapped.iter().any(|&m| m == u32::MAX) || mapped[0] == mapped[1] || mapped[1] == mapped[2] || mapped[0] == mapped[2] {
256 continue;
257 }
258 d.add_prim(&mapped);
259 }
260
261 let mut offset = 0usize;
262 for (i, name) in self.attr_names.iter().enumerate() {
263 let ty = self.attr_types[i];
264 let k = ty.components();
265 let mut data = AttribData::zeroed(ty, kept.len());
266 for (new, &old) in kept.iter().enumerate() {
267 let row = &self.attrs[old];
268 let comps: Vec<f32> = (0..k).map(|c| row.get(offset + c).copied().unwrap_or(0.0)).collect();
269 let _ = data.set(new, components(ty, &comps));
270 }
271 let _ = d.points_mut().insert(name, data);
272 offset += k;
273 }
274 for (name, members) in &self.groups {
275 d.points_mut().create_group(name);
276 for (new, &old) in kept.iter().enumerate() {
277 if members.get(old).copied().unwrap_or(false) {
278 d.points_mut().add_to_group(name, new);
279 }
280 }
281 }
282 d
283 }
284
285 /// A point halfway along an edge, with every attribute interpolated.
286 fn split_point(&mut self, a: u32, b: u32) -> u32 {
287 let (a, b) = (a as usize, b as usize);
288 let pos = (self.pos[a] + self.pos[b]) * 0.5;
289 let attrs: Vec<f32> = self.attrs[a]
290 .iter()
291 .zip(self.attrs[b].iter())
292 .map(|(x, y)| (x + y) * 0.5)
293 .collect();
294 self.pos.push(pos);
295 self.attrs.push(attrs);
296 self.ids.push(self.next_id);
297 self.next_id += 1;
298 self.dead_point.push(false);
299 self.p2t.push(Vec::new());
300 // A new point joins a group only where BOTH its parents were in it: a
301 // point that is half in a selection is not in it, and the alternative
302 // grows every group along its own boundary every time the mesh is
303 // remeshed.
304 for (_, members) in self.groups.iter_mut() {
305 let inherits = members.get(a).copied().unwrap_or(false)
306 && members.get(b).copied().unwrap_or(false);
307 members.push(inherits);
308 }
309 (self.pos.len() - 1) as u32
310 }
311
312 /// Live triangles touching a point.
313 fn tris_of(&self, p: u32) -> Vec<usize> {
314 let mut out: Vec<usize> = self
315 .p2t
316 .get(p as usize)
317 .map(|ts| {
318 ts.iter()
319 .copied()
320 .filter(|&t| !self.dead_tri[t] && self.tris[t].contains(&p))
321 .collect()
322 })
323 .unwrap_or_default();
324 out.sort_unstable();
325 out.dedup();
326 out
327 }
328
329 /// Unique live edges, each as `[low, high]`, with the triangles on them.
330 fn edges(&self) -> Vec<([u32; 2], Vec<usize>)> {
331 let mut map: HashMap<[u32; 2], Vec<usize>> = HashMap::new();
332 for (t, tri) in self.tris.iter().enumerate() {
333 if self.dead_tri[t] {
334 continue;
335 }
336 for i in 0..3 {
337 let (a, b) = (tri[i], tri[(i + 1) % 3]);
338 map.entry([a.min(b), a.max(b)]).or_default().push(t);
339 }
340 }
341 let mut out: Vec<([u32; 2], Vec<usize>)> = map.into_iter().collect();
342 // Sorted, because HashMap order would make the result depend on the
343 // hasher's seed and a remesh has to be reproducible.
344 out.sort_unstable_by_key(|(e, _)| *e);
345 out
346 }
347
348 fn len_of(&self, e: [u32; 2]) -> f32 {
349 (self.pos[e[1] as usize] - self.pos[e[0] as usize]).length()
350 }
351 }
352
353 fn components(ty: crate::detail::AttribType, c: &[f32]) -> AttribValue {
354 let at = |i: usize| c.get(i).copied().unwrap_or(0.0);
355 match ty {
356 crate::detail::AttribType::Float => AttribValue::Float(at(0)),
357 crate::detail::AttribType::Float2 => AttribValue::Float2([at(0), at(1)]),
358 crate::detail::AttribType::Float3 => AttribValue::Float3([at(0), at(1), at(2)]),
359 crate::detail::AttribType::Float4 => AttribValue::Float4([at(0), at(1), at(2), at(3)]),
360 crate::detail::AttribType::Int => AttribValue::Int(at(0).round() as i32),
361 }
362 }
363
364 /// Split every edge longer than 4/3 of the target.
365 fn split_pass(m: &mut Mesh, target: f32) -> usize {
366 let long = target * 4.0 / 3.0;
367 let mut done = 0;
368 // The edge LIST is a snapshot — the pass decides up front which edges it
369 // will consider, so a split cannot cascade within one pass. The TRIANGLES
370 // are looked up at the moment of the split: an earlier split in the same
371 // pass has already replaced the faces this edge sits on, and acting on
372 // the snapshot's stale indices is what tears the surface open.
373 for (e, _) in m.edges() {
374 if m.dead_point[e[0] as usize] || m.dead_point[e[1] as usize] || m.len_of(e) <= long {
375 continue;
376 }
377 let tris = m.tris_on_edge(e[0], e[1]);
378 if tris.is_empty() {
379 continue;
380 }
381 let mid = m.split_point(e[0], e[1]);
382 for t in tris {
383 if m.dead_tri[t] {
384 continue;
385 }
386 let tri = m.tris[t];
387 // The corner opposite the split edge; the triangle becomes two,
388 // each keeping the original winding.
389 let Some(i) = (0..3).find(|&i| !e.contains(&tri[i])) else { continue };
390 let (opp, x, y) = (tri[i], tri[(i + 1) % 3], tri[(i + 2) % 3]);
391 m.dead_tri[t] = true;
392 m.add_tri([opp, x, mid]);
393 m.add_tri([opp, mid, y]);
394 }
395 done += 1;
396 }
397 done
398 }
399
400 /// Collapse every edge shorter than 4/5 of the target.
401 ///
402 /// The survivor keeps its identity and values; the other endpoint is
403 /// tombstoned and every triangle referencing it is rewired. Collapses that
404 /// would leave a neighbour edge too long are refused, which is what stops the
405 /// pass from undoing the splits that just ran.
406 fn collapse_pass(m: &mut Mesh, target: f32) -> usize {
407 let short = target * 4.0 / 5.0;
408 let long = target * 4.0 / 3.0;
409 let mut done = 0;
410 for (e, _) in m.edges() {
411 let (a, b) = (e[0], e[1]);
412 if m.dead_point[a as usize] || m.dead_point[b as usize] || m.len_of(e) >= short {
413 continue;
414 }
415 // Would the survivor end up with an edge that the next split pass
416 // would just cut again? Then leave it: two passes undoing each other
417 // is how a remesh oscillates instead of converging.
418 let keep = m.pos[a as usize];
419 let too_long = m.tris_of(b).iter().any(|&t| {
420 m.tris[t]
421 .iter()
422 .any(|&q| q != b && q != a && (m.pos[q as usize] - keep).length() > long)
423 });
424 if too_long {
425 continue;
426 }
427 // Refuse a collapse that would flip a triangle over: if any triangle
428 // keeping both points would end up facing the other way, the surface
429 // would self-intersect where it used to be flat.
430 let folds = m.tris_of(b).iter().any(|&t| {
431 let tri = m.tris[t];
432 if tri.contains(&a) {
433 return false;
434 }
435 let before = face_normal(m, tri);
436 let after_tri = tri.map(|q| if q == b { a } else { q });
437 let after = face_normal(m, after_tri);
438 before.dot(after) <= 0.0
439 });
440 if folds {
441 continue;
442 }
443
444 m.dead_point[b as usize] = true;
445 for t in m.tris_of(b) {
446 if m.dead_tri[t] {
447 continue;
448 }
449 if m.tris[t].contains(&a) {
450 // The two triangles along the collapsed edge fold to nothing.
451 m.dead_tri[t] = true;
452 continue;
453 }
454 m.rewire(t, b, a);
455 }
456 done += 1;
457 }
458 done
459 }
460
461 fn face_normal(m: &Mesh, tri: [u32; 3]) -> Vec3 {
462 let (a, b, c) = (
463 m.pos[tri[0] as usize],
464 m.pos[tri[1] as usize],
465 m.pos[tri[2] as usize],
466 );
467 (b - a).cross(c - a)
468 }
469
470 /// Flip edges whose two triangles would be better shaped the other way.
471 ///
472 /// "Better" is total deviation from valence 6, which is the valence a regular
473 /// triangulation of a plane has — the measure the paper uses, and the one that
474 /// drives a mesh toward equilateral triangles.
475 fn flip_pass(m: &mut Mesh) -> usize {
476 let mut done = 0;
477 for (e, _) in m.edges() {
478 // Looked up now rather than taken from the snapshot, for the same
479 // reason the split pass does: an earlier flip has rewired faces.
480 let tris = m.tris_on_edge(e[0], e[1]);
481 if tris.len() != 2 {
482 continue; // a boundary edge has nothing to flip into
483 }
484 let (t0, t1) = (tris[0], tris[1]);
485 let Some(&o0) = m.tris[t0].iter().find(|q| !e.contains(q)) else { continue };
486 let Some(&o1) = m.tris[t1].iter().find(|q| !e.contains(q)) else { continue };
487 if o0 == o1 {
488 continue;
489 }
490
491 let val = |p: u32| m.tris_of(p).len() as i32;
492 let dev = |v: i32| (v - 6).abs();
493 let before = dev(val(e[0])) + dev(val(e[1])) + dev(val(o0)) + dev(val(o1));
494 // The flip moves one triangle off each endpoint and onto each opposite
495 // corner.
496 let after = dev(val(e[0]) - 1) + dev(val(e[1]) - 1) + dev(val(o0) + 1) + dev(val(o1) + 1);
497 if after >= before {
498 continue;
499 }
500 // Refuse a flip that would fold either new triangle against the
501 // surface it came from.
502 let n0 = face_normal(m, m.tris[t0]);
503 let (new0, new1) = ([o0, e[0], o1], [o1, e[1], o0]);
504 if face_normal(m, new0).dot(n0) <= 0.0 || face_normal(m, new1).dot(n0) <= 0.0 {
505 continue;
506 }
507 // Rewiring both triangles wholesale, so the incidence is rebuilt for
508 // the corners that changed.
509 m.tris[t0] = new0;
510 m.tris[t1] = new1;
511 for &q in new0.iter().chain(new1.iter()) {
512 m.p2t[q as usize].push(t0);
513 m.p2t[q as usize].push(t1);
514 }
515 done += 1;
516 }
517 done
518 }
519
520 /// Move each point toward the centroid of its neighbours, with the normal
521 /// component removed.
522 ///
523 /// Removing the normal component is what makes this a retriangulation rather
524 /// than a smooth: the points slide within the surface to even out the
525 /// triangles, and the shape they describe is left where it was.
526 fn relax_pass(m: &mut Mesh, amount: f32) {
527 if amount <= 0.0 {
528 return;
529 }
530 let mut nbrs: Vec<Vec<u32>> = vec![Vec::new(); m.pos.len()];
531 for (t, tri) in m.tris.iter().enumerate() {
532 if m.dead_tri[t] {
533 continue;
534 }
535 for i in 0..3 {
536 let (a, b) = (tri[i], tri[(i + 1) % 3]);
537 if !nbrs[a as usize].contains(&b) {
538 nbrs[a as usize].push(b);
539 }
540 if !nbrs[b as usize].contains(&a) {
541 nbrs[b as usize].push(a);
542 }
543 }
544 }
545 let mut normals: Vec<Vec3> = vec![Vec3::ZERO; m.pos.len()];
546 for (t, tri) in m.tris.iter().enumerate() {
547 if m.dead_tri[t] {
548 continue;
549 }
550 let n = face_normal(m, *tri);
551 for &p in tri {
552 normals[p as usize] += n;
553 }
554 }
555
556 let before = m.pos.clone();
557 for p in 0..m.pos.len() {
558 if m.dead_point[p] || nbrs[p].is_empty() {
559 continue;
560 }
561 let centroid: Vec3 =
562 nbrs[p].iter().map(|&q| before[q as usize]).sum::<Vec3>() / nbrs[p].len() as f32;
563 let mut delta = (centroid - before[p]) * amount;
564 let n = normals[p].normalize_or_zero();
565 if n != Vec3::ZERO {
566 delta -= n * delta.dot(n);
567 }
568 m.pos[p] = before[p] + delta;
569 }
570 }
571
572 /// Pull every point back onto the surface the remesh started from.
573 ///
574 /// Relaxation slides points within the surface, but "within" is only true to
575 /// first order: on anything curved the slide leaves the surface slightly, and
576 /// the error compounds. Without this a sphere remeshed for fifty iterations is
577 /// visibly smaller than the one it started as.
578 fn project_pass(m: &mut Mesh, rest: &crate::spatial::TriGrid) {
579 if rest.is_empty() {
580 return;
581 }
582 for p in 0..m.pos.len() {
583 if m.dead_point[p] {
584 continue;
585 }
586 if let Some(hit) = rest.closest(m.pos[p]) {
587 m.pos[p] = hit.point;
588 }
589 }
590 }
591
592 /// Subdivide every triangle into four, `depth` times.
593 ///
594 /// Distinct from remeshing, and deliberately so: this makes a predictable,
595 /// uniform refinement of the mesh it is given — every edge gets a midpoint,
596 /// every triangle becomes four, and the shape does not move. Remesh steers
597 /// toward a length and rearranges topology to get there; Subdivide multiplies
598 /// what is already there.
599 ///
600 /// It does NOT smooth, which the Houdini SOP of this name does. A subdivision
601 /// that also moved points would be two operations wearing one name, and the
602 /// smoothing one is already available as Remesh's relaxation.
603 ///
604 /// Attributes interpolate onto the midpoints, the same way a remesh split
605 /// does, so a field defined on a coarse mesh survives being refined.
606 pub fn subdivide(input: &Detail, depth: usize) -> Detail {
607 if input.num_prims() == 0 || depth == 0 {
608 return input.clone();
609 }
610 let mut m = Mesh::from_detail(input);
611 // Capped because this is exponential: each level is four times the
612 // triangles, so six levels is four thousand times the input and anything
613 // past that is a hang rather than a render.
614 for _ in 0..depth.min(6) {
615 let mut mids: HashMap<[u32; 2], u32> = HashMap::new();
616 for (e, _) in m.edges() {
617 let mid = m.split_point(e[0], e[1]);
618 mids.insert(e, mid);
619 }
620 let key = |a: u32, b: u32| [a.min(b), a.max(b)];
621 // Snapshotted, because the loop adds triangles as it goes and the new
622 // ones are already subdivided.
623 let count = m.tris.len();
624 for t in 0..count {
625 if m.dead_tri[t] {
626 continue;
627 }
628 let tri = m.tris[t];
629 let (Some(&ab), Some(&bc), Some(&ca)) = (
630 mids.get(&key(tri[0], tri[1])),
631 mids.get(&key(tri[1], tri[2])),
632 mids.get(&key(tri[2], tri[0])),
633 ) else {
634 continue;
635 };
636 m.dead_tri[t] = true;
637 // Three corner triangles and the middle one, each keeping the
638 // original winding.
639 m.add_tri([tri[0], ab, ca]);
640 m.add_tri([ab, tri[1], bc]);
641 m.add_tri([ca, bc, tri[2]]);
642 m.add_tri([ab, bc, ca]);
643 }
644 }
645 m.into_detail()
646 }
647
648 /// Remesh toward `settings.target` edge length.
649 pub fn remesh(input: &Detail, settings: Settings) -> Detail {
650 if input.num_prims() == 0 || settings.target <= 0.0 {
651 return input.clone();
652 }
653 // Built once from the INPUT and reused by every iteration: projecting onto
654 // the previous iteration's surface would chase the creep rather than
655 // correct it, since each iteration's drift would become the next one's
656 // idea of where the surface is.
657 let rest = settings.project.then(|| crate::spatial::TriGrid::build(input));
658 let mut m = Mesh::from_detail(input);
659 for _ in 0..settings.iterations.min(20) {
660 if settings.split {
661 split_pass(&mut m, settings.target);
662 }
663 if settings.collapse {
664 collapse_pass(&mut m, settings.target);
665 }
666 if settings.flip {
667 flip_pass(&mut m);
668 }
669 relax_pass(&mut m, settings.relax.clamp(0.0, 1.0));
670 if let Some(rest) = &rest {
671 project_pass(&mut m, rest);
672 }
673 }
674 m.into_detail()
675 }