use std::collections::BTreeMap;
use crate::impl_mesh::ManifoldImpl;
use crate::linalg::{IVec3, Vec3};
use crate::types::{Error, Halfedge};
use super::exact::rational::R3;
fn pos_key(v: Vec3) -> (u64, u64, u64) {
let norm = |x: f64| if x == 0.0 { 0.0f64 } else { x }.to_bits();
(norm(v.x), norm(v.y), norm(v.z))
}
fn is_degenerate(a: Vec3, b: Vec3, c: Vec3) -> bool {
R3::from_vec3(b)
.sub(&R3::from_vec3(a))
.cross(&R3::from_vec3(c).sub(&R3::from_vec3(a)))
.is_zero()
}
pub fn soupify(
imp: &mut ManifoldImpl,
tri_prop: &[IVec3],
tri_vert: &[IVec3],
) -> Result<(), Error> {
let position_tris: &[IVec3] = if tri_vert.is_empty() { tri_prop } else { tri_vert };
debug_assert_eq!(position_tris.len(), tri_prop.len());
let mut weld: BTreeMap<(u64, u64, u64), i32> = BTreeMap::new();
let mut welded_id = vec![0i32; imp.vert_pos.len()];
for (i, &p) in imp.vert_pos.iter().enumerate() {
let id = *weld.entry(pos_key(p)).or_insert(i as i32);
welded_id[i] = id;
}
let mut keep: Vec<usize> = Vec::with_capacity(position_tris.len());
let mut balance: BTreeMap<(i32, i32), i64> = BTreeMap::new();
for (t, tv) in position_tris.iter().enumerate() {
let (a, b, c) = (tv.x as usize, tv.y as usize, tv.z as usize);
let (wa, wb, wc) = (welded_id[a], welded_id[b], welded_id[c]);
if wa == wb || wb == wc || wc == wa
|| is_degenerate(imp.vert_pos[a], imp.vert_pos[b], imp.vert_pos[c])
{
continue; }
keep.push(t);
for (u, v) in [(wa, wb), (wb, wc), (wc, wa)] {
let key = (u.min(v), u.max(v));
*balance.entry(key).or_insert(0) += if u < v { 1 } else { -1 };
}
}
if keep.len() < 4 {
return Err(Error::NotClosed);
}
if balance.values().any(|&n| n != 0) {
return Err(Error::NotClosed);
}
let has_props = !tri_vert.is_empty();
let mut halfedges: Vec<Halfedge> = Vec::with_capacity(3 * keep.len());
let mut tri_ref = Vec::with_capacity(keep.len());
for (new_t, &old_t) in keep.iter().enumerate() {
let tv = position_tris[old_t];
let tp = tri_prop[old_t];
for i in 0..3 {
let j = (i + 1) % 3;
halfedges.push(Halfedge {
start_vert: tv[i],
end_vert: tv[j],
paired_halfedge: -1,
prop_vert: if has_props { tp[i] } else { tv[i] },
});
}
if old_t < imp.mesh_relation.tri_ref.len() {
tri_ref.push(imp.mesh_relation.tri_ref[old_t]);
}
let _ = new_t;
}
let mut open: BTreeMap<(i32, i32), Vec<usize>> = BTreeMap::new();
for (idx, he) in halfedges.iter().enumerate() {
let (u, v) = (welded_id[he.start_vert as usize], welded_id[he.end_vert as usize]);
open.entry((u.min(v), u.max(v))).or_default().push(idx);
}
for (_key, mut idxs) in open {
let mut fwd: Vec<usize> = Vec::new();
let mut bwd: Vec<usize> = Vec::new();
for idx in idxs.drain(..) {
let he = &halfedges[idx];
if welded_id[he.start_vert as usize] < welded_id[he.end_vert as usize] {
fwd.push(idx);
} else {
bwd.push(idx);
}
}
while let (Some(f), Some(b)) = (fwd.pop(), bwd.pop()) {
halfedges[f].paired_halfedge = b as i32;
halfedges[b].paired_halfedge = f as i32;
}
}
imp.halfedge = halfedges;
if tri_ref.len() == keep.len() {
imp.mesh_relation.tri_ref = tri_ref;
} else {
imp.mesh_relation.tri_ref.clear();
}
imp.face_normal = (0..imp.num_tri())
.map(|t| {
let a = imp.vert_pos[imp.halfedge[3 * t].start_vert as usize];
let b = imp.vert_pos[imp.halfedge[3 * t + 1].start_vert as usize];
let c = imp.vert_pos[imp.halfedge[3 * t + 2].start_vert as usize];
let n = crate::linalg::cross(b - a, c - a);
let len = crate::linalg::length(n);
if len > 0.0 { n / len } else { Vec3::new(0.0, 0.0, 0.0) }
})
.collect();
imp.vert_normal.clear();
imp.is_soup = true;
Ok(())
}
#[derive(Debug, Default)]
pub struct SelfIntersectCache(std::sync::OnceLock<bool>);
impl Clone for SelfIntersectCache {
fn clone(&self) -> Self {
let out = std::sync::OnceLock::new();
if let Some(&v) = self.0.get() {
let _ = out.set(v);
}
SelfIntersectCache(out)
}
}
impl SelfIntersectCache {
pub fn get(&self) -> Option<bool> {
self.0.get().copied()
}
pub fn set(&self, value: bool) {
let _ = self.0.set(value);
}
}
pub fn has_self_intersections(imp: &ManifoldImpl) -> bool {
has_self_intersections_with_token(imp, None)
}
pub fn has_self_intersections_with_token(
imp: &ManifoldImpl,
token: Option<&crate::cancel::CancelToken>,
) -> bool {
if let Some(v) = imp.self_intersects.get() {
return v;
}
match compute_self_intersections(imp, token) {
Some(verdict) => {
imp.self_intersects.set(verdict);
verdict
}
None => true,
}
}
fn genuine_contact(
t1: [Vec3; 3],
t2: [Vec3; 3],
stats: &mut super::intersection_graph::SelfCutStats,
) -> bool {
if t1.iter().all(|v| t2.contains(v)) {
return true;
}
super::intersection_graph::real_self_contact(t1, t2, stats).is_some()
}
fn compute_self_intersections(
imp: &ManifoldImpl,
token: Option<&crate::cancel::CancelToken>,
) -> Option<bool> {
use super::intersection_graph::{is_degenerate as is_degenerate_tri, tri_box, SelfCutStats};
use crate::types::Box;
let tris = impl_to_tris(imp);
if tris.len() < 2 {
return Some(false);
}
if tris
.iter()
.flatten()
.any(|v| !v.x.is_finite() || !v.y.is_finite() || !v.z.is_finite())
{
return Some(true);
}
let boxes: Vec<Box> = tris.iter().map(tri_box).collect();
let live: Vec<bool> = tris.iter().map(|t| !is_degenerate_tri(t)).collect();
let mut leaf_tri: Vec<usize> = Vec::new();
let owned;
let collider = if imp.collider.num_leaves() == tris.len() {
&imp.collider
} else {
let mut order: Vec<usize> = (0..tris.len()).filter(|&i| live[i]).collect();
if order.len() < 2 {
return Some(false);
}
let scene = boxes
.iter()
.enumerate()
.filter(|(i, _)| live[*i])
.fold(Box::new(), |acc, (_, b)| acc.union_box(b));
order.sort_by_key(|&i| crate::sort::morton_code(boxes[i].center(), &scene));
owned = crate::collider::Collider::new(
order.iter().map(|&i| boxes[i]).collect(),
order
.iter()
.map(|&i| crate::sort::morton_code(boxes[i].center(), &scene))
.collect(),
);
leaf_tri = order;
&owned
};
let mapped = !leaf_tri.is_empty();
let mut stats = SelfCutStats::default();
let mut cands: Vec<usize> = Vec::new();
for i in 0..tris.len() {
if !live[i] {
continue;
}
if crate::cancel::is_cancelled(token) {
return None;
}
cands.clear();
collider.collisions_one(&boxes[i], i, |_, leaf| {
cands.push(if mapped { leaf_tri[leaf] } else { leaf });
});
cands.sort_unstable();
for &j in &cands {
if j <= i || !live[j] || !boxes[i].does_overlap_box(&boxes[j]) {
continue;
}
if genuine_contact(tris[i], tris[j], &mut stats) {
return Some(true);
}
}
}
Some(false)
}
#[cfg(test)]
#[path = "soup_tests.rs"]
mod tests;
pub fn impl_to_corner_props(imp: &ManifoldImpl) -> Vec<f64> {
let np = imp.num_prop;
if np == 0 {
return Vec::new();
}
let mut out = Vec::with_capacity(3 * imp.num_tri() * np);
for t in 0..imp.num_tri() {
for i in 0..3 {
let pv = imp.halfedge[3 * t + i].prop_vert as usize;
out.extend_from_slice(&imp.properties[pv * np..(pv + 1) * np]);
}
}
out
}
pub fn impl_to_tris(imp: &ManifoldImpl) -> Vec<[Vec3; 3]> {
(0..imp.num_tri())
.map(|t| {
[
imp.vert_pos[imp.halfedge[3 * t].start_vert as usize],
imp.vert_pos[imp.halfedge[3 * t + 1].start_vert as usize],
imp.vert_pos[imp.halfedge[3 * t + 2].start_vert as usize],
]
})
.collect()
}