use glam::DVec3;
use super::{BLANK_HALF_EXTENT, EPS_FEAS, MIN_TRIPLE_DET, VERTEX_DEDUP};
use crate::geometry::cuts::normals_coincide;
pub(super) struct SolidVertex {
pub(super) v: DVec3,
}
#[derive(Default)]
struct VertexAccumulator {
verts: Vec<SolidVertex>,
by_x: Vec<usize>,
}
impl VertexAccumulator {
fn insert_if_new(&mut self, v: DVec3) {
let verts = &self.verts;
let lo = self
.by_x
.partition_point(|&i| verts[i].v.x < v.x - VERTEX_DEDUP);
let hi = self
.by_x
.partition_point(|&i| verts[i].v.x <= v.x + VERTEX_DEDUP);
for &i in &self.by_x[lo..hi] {
if (self.verts[i].v - v).abs().max_element() < VERTEX_DEDUP {
return;
}
}
let idx = self.verts.len();
self.verts.push(SolidVertex { v });
let pos = self.by_x.partition_point(|&i| self.verts[i].v.x < v.x);
self.by_x.insert(pos, idx);
}
}
pub(super) fn dedup_planes(planes: &[(DVec3, f64)]) -> Vec<(DVec3, f64)> {
let mut out: Vec<(DVec3, f64)> = Vec::with_capacity(planes.len());
for &(n, m) in planes {
let dup = out
.iter()
.any(|&(n2, m2)| normals_coincide(n, n2) && (m - m2).abs() < 1e-9);
if !dup {
out.push((n, m));
}
}
out
}
fn flush_solid_batch(
batch: &crate::simd::TripleBatch,
lanes: &[[usize; 3]; crate::simd::TRIPLE_LANES],
soa: &crate::simd::PlanesSoA64,
det_floor: f64,
visit: &mut impl FnMut([usize; 3], f64, DVec3),
) -> Option<()> {
let sol = crate::simd::solve_triple_batch(batch);
#[allow(
clippy::needless_range_loop,
reason = "the lane index addresses several parallel SoA arrays (det, vx, vy, vz, lanes)"
)]
for lane in 0..batch.len {
let det_abs = sol.det[lane].abs();
if det_abs.is_nan() || det_abs < det_floor {
continue;
}
let v = DVec3::new(sol.vx[lane], sol.vy[lane], sol.vz[lane]);
if !v.is_finite() {
continue;
}
if v.abs().max_element() > BLANK_HALF_EXTENT + 1.0 {
continue;
}
if crate::simd::any_violation(soa, v, EPS_FEAS) {
continue;
}
if v.abs().max_element() > BLANK_HALF_EXTENT - 1.0 {
if det_abs >= MIN_TRIPLE_DET {
return None;
}
continue;
}
visit(lanes[lane], sol.det[lane], v);
}
Some(())
}
const PRUNE_MIN_SIN: f64 = 1e-3;
const SOLVE_RESIDUAL_NUMERATOR: f64 = 1e-11;
fn pair_misses_solid(all: &[(DVec3, f64)], a: usize, b: usize, det_floor: f64) -> bool {
let (na, ma) = all[a];
let (nb, mb) = all[b];
let dir = na.cross(nb);
let sin = dir.length();
if sin < PRUNE_MIN_SIN {
return false;
}
let origin = (nb.cross(dir) * ma + dir.cross(na) * mb) / (sin * sin);
let unit = dir / sin;
let margin = EPS_FEAS + 2.0 * SOLVE_RESIDUAL_NUMERATOR / det_floor / sin;
let (mut lo, mut hi) = (f64::NEG_INFINITY, f64::INFINITY);
for (c, &(nc, mc)) in all.iter().enumerate() {
if c == a || c == b {
continue;
}
let slope = nc.dot(unit);
let room = mc + margin - nc.dot(origin);
if slope > 0.0 {
hi = hi.min(room / slope);
} else if slope < 0.0 {
lo = lo.max(room / slope);
} else if room < 0.0 {
return true;
}
if lo > hi {
return true;
}
}
false
}
pub(in crate::geometry) fn for_each_feasible_triple(
planes: &[(DVec3, f64)],
det_floor: f64,
prune_pairs: bool,
mut visit: impl FnMut([usize; 3], f64, DVec3),
) -> Option<()> {
let mut all: Vec<(DVec3, f64)> = planes.to_vec();
for n in [
DVec3::X,
DVec3::NEG_X,
DVec3::Y,
DVec3::NEG_Y,
DVec3::Z,
DVec3::NEG_Z,
] {
all.push((n, BLANK_HALF_EXTENT));
}
let mut soa = crate::simd::PlanesSoA64::with_capacity(all.len());
for &(n, m) in &all {
soa.push(n, m, 0);
}
let p = all.len();
let mut batch = crate::simd::TripleBatch::default();
let mut lanes = [[0usize; 3]; crate::simd::TRIPLE_LANES];
for a in 0..p {
for b in (a + 1)..p {
if prune_pairs && pair_misses_solid(&all, a, b, det_floor) {
continue;
}
for c in (b + 1)..p {
let (pa, pb, pc) = (all[a], all[b], all[c]);
lanes[batch.len] = [a, b, c];
if batch.push((pa.0, pa.1), (pb.0, pb.1), (pc.0, pc.1)) {
flush_solid_batch(&batch, &lanes, &soa, det_floor, &mut visit)?;
batch = crate::simd::TripleBatch::default();
}
}
}
}
if batch.len > 0 {
flush_solid_batch(&batch, &lanes, &soa, det_floor, &mut visit)?;
}
Some(())
}
pub(super) fn feasible_vertices(planes: &[(DVec3, f64)]) -> Option<Vec<SolidVertex>> {
let mut acc = VertexAccumulator::default();
for_each_feasible_triple(planes, MIN_TRIPLE_DET, true, |_, _, v| {
acc.insert_if_new(v);
})?;
Some(acc.verts)
}
pub(super) fn dedup_origin_indices(
original: &[(DVec3, f64)],
deduped: &[(DVec3, f64)],
) -> Vec<usize> {
let same = |a: (DVec3, f64), b: (DVec3, f64)| {
a.0.x.to_bits() == b.0.x.to_bits()
&& a.0.y.to_bits() == b.0.y.to_bits()
&& a.0.z.to_bits() == b.0.z.to_bits()
&& a.1.to_bits() == b.1.to_bits()
};
let mut out = Vec::with_capacity(deduped.len());
let mut cursor = 0usize;
for &d in deduped {
while cursor < original.len() && !same(original[cursor], d) {
cursor += 1;
}
out.push(cursor.min(original.len().saturating_sub(1)));
cursor += 1;
}
out
}
pub(in crate::geometry) fn escaping_plane_indices(planes: &[(DVec3, f64)]) -> Vec<usize> {
let real_count = planes.len();
let mut all: Vec<(DVec3, f64)> = planes.to_vec();
for n in [
DVec3::X,
DVec3::NEG_X,
DVec3::Y,
DVec3::NEG_Y,
DVec3::Z,
DVec3::NEG_Z,
] {
all.push((n, BLANK_HALF_EXTENT));
}
let p = all.len();
let mut escaping: Vec<usize> = Vec::new();
for a in 0..p {
for b in (a + 1)..p {
for c in (b + 1)..p {
let (na, ma) = all[a];
let (nb, mb) = all[b];
let (nc, mc) = all[c];
let mat = glam::DMat3::from_cols(na, nb, nc).transpose();
let det = mat.determinant();
let det_abs = det.abs();
if det_abs.is_nan() || det_abs < MIN_TRIPLE_DET {
continue;
}
let v = mat.inverse() * DVec3::new(ma, mb, mc);
if !v.is_finite() {
continue;
}
if v.abs().max_element() > BLANK_HALF_EXTENT + 1.0 {
continue;
}
if all.iter().any(|&(n, m)| n.dot(v) - m > EPS_FEAS) {
continue;
}
if v.abs().max_element() > BLANK_HALF_EXTENT - 1.0 {
for idx in [a, b, c] {
if idx < real_count && !escaping.contains(&idx) {
escaping.push(idx);
}
}
}
}
}
}
escaping.sort_unstable();
escaping
}