use super::super::{GeometryRouter, NORMALIZE_EPSILON};
use crate::{Mesh, Vector3};
impl GeometryRouter {
pub(in super::super) fn remove_internal_membrane(
opening_mesh: &Mesh,
axis_dir: Vector3<f64>,
) -> Mesh {
let tri_count = opening_mesh.indices.len() / 3;
if tri_count < 4 {
return opening_mesh.clone();
}
let p = |i: usize| -> [f64; 3] {
[
opening_mesh.positions[i * 3] as f64,
opening_mesh.positions[i * 3 + 1] as f64,
opening_mesh.positions[i * 3 + 2] as f64,
]
};
let mut d = axis_dir;
if d.norm() < NORMALIZE_EPSILON {
let (mut lo, mut hi) = ([f64::INFINITY; 3], [f64::NEG_INFINITY; 3]);
for c in opening_mesh.positions.chunks_exact(3) {
for a in 0..3 {
lo[a] = lo[a].min(c[a] as f64);
hi[a] = hi[a].max(c[a] as f64);
}
}
let ext = [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]];
let la = (0..3).max_by(|&i, &j| ext[i].total_cmp(&ext[j])).unwrap();
d = Vector3::new(
if la == 0 { 1.0 } else { 0.0 },
if la == 1 { 1.0 } else { 0.0 },
if la == 2 { 1.0 } else { 0.0 },
);
}
d /= d.norm();
let (mut smin, mut smax) = (f64::INFINITY, f64::NEG_INFINITY);
for c in opening_mesh.positions.chunks_exact(3) {
let s = c[0] as f64 * d.x + c[1] as f64 * d.y + c[2] as f64 * d.z;
smin = smin.min(s);
smax = smax.max(s);
}
let span = (smax - smin).abs();
if span < NORMALIZE_EPSILON {
return opening_mesh.clone();
}
let cell = (span * 0.005).max(5.0e-4);
let bucket = |s: f64| (s / cell).round() as i64;
let (min_b, max_b) = (bucket(smin), bucket(smax));
let helper = if d.x.abs() < 0.9 {
Vector3::new(1.0, 0.0, 0.0)
} else {
Vector3::new(0.0, 1.0, 0.0)
};
let u = d.cross(&helper).normalize();
let v = d.cross(&u);
let bbox_union = |bb: &mut Option<[f64; 4]>, lu: f64, lv: f64| match bb {
None => *bb = Some([lu, lu, lv, lv]),
Some(b) => {
b[0] = b[0].min(lu);
b[1] = b[1].max(lu);
b[2] = b[2].min(lv);
b[3] = b[3].max(lv);
}
};
let mut buckets: std::collections::HashMap<i64, [Option<[f64; 4]>; 2]> =
std::collections::HashMap::new();
let mut cap_tris: Vec<(i64, f64, f64)> = Vec::with_capacity(tri_count);
for t in 0..tri_count {
let (i0, i1, i2) = (
opening_mesh.indices[t * 3] as usize,
opening_mesh.indices[t * 3 + 1] as usize,
opening_mesh.indices[t * 3 + 2] as usize,
);
let (a, b, c) = (p(i0), p(i1), p(i2));
let e1 = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
let e2 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
let n = [
e1[1] * e2[2] - e1[2] * e2[1],
e1[2] * e2[0] - e1[0] * e2[2],
e1[0] * e2[1] - e1[1] * e2[0],
];
let nl = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
if nl < 1e-12 {
cap_tris.push((i64::MIN, 0.0, 0.0));
continue;
}
let align = (n[0] * d.x + n[1] * d.y + n[2] * d.z) / nl;
if align.abs() <= 0.9 {
cap_tris.push((i64::MIN, 0.0, 0.0)); continue;
}
let cx = (a[0] + b[0] + c[0]) / 3.0;
let cy = (a[1] + b[1] + c[1]) / 3.0;
let cz = (a[2] + b[2] + c[2]) / 3.0;
let cs = cx * d.x + cy * d.y + cz * d.z;
let lu = cx * u.x + cy * u.y + cz * u.z;
let lv = cx * v.x + cy * v.y + cz * v.z;
let bk = bucket(cs);
cap_tris.push((bk, lu, lv));
if bk != min_b && bk != max_b {
let e = buckets.entry(bk).or_insert([None, None]);
bbox_union(&mut e[(align > 0.0) as usize], lu, lv);
}
}
let mut membrane_region: std::collections::HashMap<i64, [f64; 4]> =
std::collections::HashMap::new();
for (&bk, dirs) in &buckets {
if let (Some(neg), Some(pos)) = (dirs[0], dirs[1]) {
let iu0 = neg[0].max(pos[0]);
let iu1 = neg[1].min(pos[1]);
let iv0 = neg[2].max(pos[2]);
let iv1 = neg[3].min(pos[3]);
if iu1 - iu0 > 1.0e-3 && iv1 - iv0 > 1.0e-3 {
membrane_region.insert(bk, [iu0, iu1, iv0, iv1]);
}
}
}
if membrane_region.is_empty() {
return opening_mesh.clone();
}
let pad = 1.0e-4;
let mut out = opening_mesh.clone();
out.indices.clear();
for t in 0..tri_count {
let (bk, lu, lv) = cap_tris[t];
if let Some(r) = membrane_region.get(&bk) {
if lu >= r[0] - pad && lu <= r[1] + pad && lv >= r[2] - pad && lv <= r[3] + pad
{
continue; }
}
out.indices.push(opening_mesh.indices[t * 3]);
out.indices.push(opening_mesh.indices[t * 3 + 1]);
out.indices.push(opening_mesh.indices[t * 3 + 2]);
}
out
}
}