1use 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
76pub const REST_SIZE_CHANNEL: &str = "rest_edge_size";
79pub const DEFAULT_STRETCH_LIMIT: f32 = 4.0 / 3.0;
81pub const DEFAULT_MAX_SPLITS: u32 = 1 << 20;
83const SHARP_COS: f64 = std::f64::consts::FRAC_1_SQRT_2;
86
87#[derive(Debug, Clone, Copy, PartialEq)]
89pub struct StretchOptions {
90 pub region: Region,
93 pub limit: f32,
97 pub max_splits: u32,
101 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#[derive(Debug, Clone, Default, PartialEq, Eq)]
119pub struct StretchReport {
120 pub splits: usize,
122 pub remaining: usize,
125 pub vertices: Vec<[VertexId; 3]>,
129}
130
131pub 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 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
198pub 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
211pub 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 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 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#[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
346enum 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
397struct WorkTri {
399 id: Option<TriangleId>,
400 corners: [u32; 3],
401}
402
403struct 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 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 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 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 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 fn initial_edges(&mut self, region: &Region) -> Result<BinaryHeap<Edge>> {
481 let positions = self.mesh.positions();
482 let center = f64v(®ion.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 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 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 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 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 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 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
705struct Plan {
707 slots: usize,
708 tris: Vec<WorkTri>,
709 added: Vec<NewVert>,
710}
711
712impl Plan {
713 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;