1use std::collections::BTreeMap;
15
16use crate::impl_mesh::ManifoldImpl;
17use crate::linalg::{IVec3, Vec3};
18use crate::types::{Error, Halfedge};
19
20use super::exact::rational::R3;
21
22fn pos_key(v: Vec3) -> (u64, u64, u64) {
24 let norm = |x: f64| if x == 0.0 { 0.0f64 } else { x }.to_bits();
25 (norm(v.x), norm(v.y), norm(v.z))
26}
27
28fn is_degenerate(a: Vec3, b: Vec3, c: Vec3) -> bool {
30 R3::from_vec3(b)
31 .sub(&R3::from_vec3(a))
32 .cross(&R3::from_vec3(c).sub(&R3::from_vec3(a)))
33 .is_zero()
34}
35
36pub fn soupify(
50 imp: &mut ManifoldImpl,
51 tri_prop: &[IVec3],
52 tri_vert: &[IVec3],
53) -> Result<(), Error> {
54 let position_tris: &[IVec3] = if tri_vert.is_empty() { tri_prop } else { tri_vert };
55 debug_assert_eq!(position_tris.len(), tri_prop.len());
56
57 let mut weld: BTreeMap<(u64, u64, u64), i32> = BTreeMap::new();
60 let mut welded_id = vec![0i32; imp.vert_pos.len()];
61 for (i, &p) in imp.vert_pos.iter().enumerate() {
62 let id = *weld.entry(pos_key(p)).or_insert(i as i32);
63 welded_id[i] = id;
64 }
65
66 let mut keep: Vec<usize> = Vec::with_capacity(position_tris.len());
68 let mut balance: BTreeMap<(i32, i32), i64> = BTreeMap::new();
69 for (t, tv) in position_tris.iter().enumerate() {
70 let (a, b, c) = (tv.x as usize, tv.y as usize, tv.z as usize);
71 let (wa, wb, wc) = (welded_id[a], welded_id[b], welded_id[c]);
72 if wa == wb || wb == wc || wc == wa
73 || is_degenerate(imp.vert_pos[a], imp.vert_pos[b], imp.vert_pos[c])
74 {
75 continue; }
77 keep.push(t);
78 for (u, v) in [(wa, wb), (wb, wc), (wc, wa)] {
79 let key = (u.min(v), u.max(v));
80 *balance.entry(key).or_insert(0) += if u < v { 1 } else { -1 };
81 }
82 }
83 if keep.len() < 4 {
84 return Err(Error::NotClosed);
85 }
86 if balance.values().any(|&n| n != 0) {
87 return Err(Error::NotClosed);
88 }
89
90 let has_props = !tri_vert.is_empty();
94 let mut halfedges: Vec<Halfedge> = Vec::with_capacity(3 * keep.len());
95 let mut tri_ref = Vec::with_capacity(keep.len());
96 for (new_t, &old_t) in keep.iter().enumerate() {
97 let tv = position_tris[old_t];
98 let tp = tri_prop[old_t];
99 for i in 0..3 {
100 let j = (i + 1) % 3;
101 halfedges.push(Halfedge {
102 start_vert: tv[i],
103 end_vert: tv[j],
104 paired_halfedge: -1,
105 prop_vert: if has_props { tp[i] } else { tv[i] },
106 });
107 }
108 if old_t < imp.mesh_relation.tri_ref.len() {
109 tri_ref.push(imp.mesh_relation.tri_ref[old_t]);
110 }
111 let _ = new_t;
112 }
113 let mut open: BTreeMap<(i32, i32), Vec<usize>> = BTreeMap::new();
114 for (idx, he) in halfedges.iter().enumerate() {
115 let (u, v) = (welded_id[he.start_vert as usize], welded_id[he.end_vert as usize]);
116 open.entry((u.min(v), u.max(v))).or_default().push(idx);
117 }
118 for (_key, mut idxs) in open {
119 let mut fwd: Vec<usize> = Vec::new();
121 let mut bwd: Vec<usize> = Vec::new();
122 for idx in idxs.drain(..) {
123 let he = &halfedges[idx];
124 if welded_id[he.start_vert as usize] < welded_id[he.end_vert as usize] {
125 fwd.push(idx);
126 } else {
127 bwd.push(idx);
128 }
129 }
130 while let (Some(f), Some(b)) = (fwd.pop(), bwd.pop()) {
131 halfedges[f].paired_halfedge = b as i32;
132 halfedges[b].paired_halfedge = f as i32;
133 }
134 }
135 imp.halfedge = halfedges;
136 if tri_ref.len() == keep.len() {
137 imp.mesh_relation.tri_ref = tri_ref;
138 } else {
139 imp.mesh_relation.tri_ref.clear();
140 }
141
142 imp.face_normal = (0..imp.num_tri())
144 .map(|t| {
145 let a = imp.vert_pos[imp.halfedge[3 * t].start_vert as usize];
146 let b = imp.vert_pos[imp.halfedge[3 * t + 1].start_vert as usize];
147 let c = imp.vert_pos[imp.halfedge[3 * t + 2].start_vert as usize];
148 let n = crate::linalg::cross(b - a, c - a);
149 let len = crate::linalg::length(n);
150 if len > 0.0 { n / len } else { Vec3::new(0.0, 0.0, 0.0) }
151 })
152 .collect();
153 imp.vert_normal.clear();
154 imp.is_soup = true;
155 Ok(())
156}
157
158#[derive(Debug, Default)]
169pub struct SelfIntersectCache(std::sync::OnceLock<bool>);
170
171impl Clone for SelfIntersectCache {
172 fn clone(&self) -> Self {
173 let out = std::sync::OnceLock::new();
174 if let Some(&v) = self.0.get() {
175 let _ = out.set(v);
176 }
177 SelfIntersectCache(out)
178 }
179}
180
181impl SelfIntersectCache {
182 pub fn get(&self) -> Option<bool> {
184 self.0.get().copied()
185 }
186
187 pub fn set(&self, value: bool) {
190 let _ = self.0.set(value);
191 }
192}
193
194pub fn has_self_intersections(imp: &ManifoldImpl) -> bool {
205 has_self_intersections_with_token(imp, None)
206}
207
208pub fn has_self_intersections_with_token(
214 imp: &ManifoldImpl,
215 token: Option<&crate::cancel::CancelToken>,
216) -> bool {
217 if let Some(v) = imp.self_intersects.get() {
218 return v;
219 }
220 match compute_self_intersections(imp, token) {
221 Some(verdict) => {
222 imp.self_intersects.set(verdict);
223 verdict
224 }
225 None => true,
226 }
227}
228
229fn genuine_contact(
240 t1: [Vec3; 3],
241 t2: [Vec3; 3],
242 stats: &mut super::intersection_graph::SelfCutStats,
243) -> bool {
244 if t1.iter().all(|v| t2.contains(v)) {
245 return true;
246 }
247 super::intersection_graph::real_self_contact(t1, t2, stats).is_some()
248}
249
250fn compute_self_intersections(
260 imp: &ManifoldImpl,
261 token: Option<&crate::cancel::CancelToken>,
262) -> Option<bool> {
263 use super::intersection_graph::{is_degenerate as is_degenerate_tri, tri_box, SelfCutStats};
264 use crate::types::Box;
265
266 let tris = impl_to_tris(imp);
267 if tris.len() < 2 {
268 return Some(false);
269 }
270 if tris
276 .iter()
277 .flatten()
278 .any(|v| !v.x.is_finite() || !v.y.is_finite() || !v.z.is_finite())
279 {
280 return Some(true);
281 }
282
283 let boxes: Vec<Box> = tris.iter().map(tri_box).collect();
284 let live: Vec<bool> = tris.iter().map(|t| !is_degenerate_tri(t)).collect();
285
286 let mut leaf_tri: Vec<usize> = Vec::new();
289 let owned;
290 let collider = if imp.collider.num_leaves() == tris.len() {
291 &imp.collider
292 } else {
293 let mut order: Vec<usize> = (0..tris.len()).filter(|&i| live[i]).collect();
294 if order.len() < 2 {
295 return Some(false);
296 }
297 let scene = boxes
298 .iter()
299 .enumerate()
300 .filter(|(i, _)| live[*i])
301 .fold(Box::new(), |acc, (_, b)| acc.union_box(b));
302 order.sort_by_key(|&i| crate::sort::morton_code(boxes[i].center(), &scene));
303 owned = crate::collider::Collider::new(
304 order.iter().map(|&i| boxes[i]).collect(),
305 order
306 .iter()
307 .map(|&i| crate::sort::morton_code(boxes[i].center(), &scene))
308 .collect(),
309 );
310 leaf_tri = order;
311 &owned
312 };
313 let mapped = !leaf_tri.is_empty();
314
315 let mut stats = SelfCutStats::default();
316 let mut cands: Vec<usize> = Vec::new();
317 for i in 0..tris.len() {
318 if !live[i] {
319 continue;
320 }
321 if crate::cancel::is_cancelled(token) {
322 return None;
323 }
324 cands.clear();
325 collider.collisions_one(&boxes[i], i, |_, leaf| {
326 cands.push(if mapped { leaf_tri[leaf] } else { leaf });
327 });
328 cands.sort_unstable();
329 for &j in &cands {
330 if j <= i || !live[j] || !boxes[i].does_overlap_box(&boxes[j]) {
331 continue;
332 }
333 if genuine_contact(tris[i], tris[j], &mut stats) {
334 return Some(true);
335 }
336 }
337 }
338 Some(false)
339}
340
341#[cfg(test)]
342#[path = "soup_tests.rs"]
343mod tests;
344
345pub fn impl_to_corner_props(imp: &ManifoldImpl) -> Vec<f64> {
349 let np = imp.num_prop;
350 if np == 0 {
351 return Vec::new();
352 }
353 let mut out = Vec::with_capacity(3 * imp.num_tri() * np);
354 for t in 0..imp.num_tri() {
355 for i in 0..3 {
356 let pv = imp.halfedge[3 * t + i].prop_vert as usize;
357 out.extend_from_slice(&imp.properties[pv * np..(pv + 1) * np]);
358 }
359 }
360 out
361}
362
363pub fn impl_to_tris(imp: &ManifoldImpl) -> Vec<[Vec3; 3]> {
366 (0..imp.num_tri())
367 .map(|t| {
368 [
369 imp.vert_pos[imp.halfedge[3 * t].start_vert as usize],
370 imp.vert_pos[imp.halfedge[3 * t + 1].start_vert as usize],
371 imp.vert_pos[imp.halfedge[3 * t + 2].start_vert as usize],
372 ]
373 })
374 .collect()
375}