use glam::DVec3;
use super::{BLANK_HALF_EXTENT, EPS_FEAS, MIN_TRIPLE_DET, VERTEX_DEDUP};
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)| n.dot(n2) > 1.0 - 1e-12 && (m - m2).abs() < 1e-9);
if !dup {
out.push((n, m));
}
}
out
}
fn flush_solid_batch(
batch: &crate::simd::TripleBatch,
soa: &crate::simd::PlanesSoA64,
acc: &mut VertexAccumulator,
) -> Option<()> {
let sol = crate::simd::solve_triple_batch(batch);
for lane in 0..batch.len {
if sol.det[lane].abs() < MIN_TRIPLE_DET {
continue;
}
let v = DVec3::new(sol.vx[lane], sol.vy[lane], sol.vz[lane]);
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 {
return None;
}
acc.insert_if_new(v);
}
Some(())
}
pub(super) fn feasible_vertices(planes: &[(DVec3, f64)]) -> Option<Vec<SolidVertex>> {
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 acc = VertexAccumulator::default();
let mut batch = crate::simd::TripleBatch::default();
for a in 0..p {
for b in (a + 1)..p {
for c in (b + 1)..p {
let (pa, pb, pc) = (all[a], all[b], all[c]);
if batch.push((pa.0, pa.1), (pb.0, pb.1), (pc.0, pc.1)) {
flush_solid_batch(&batch, &soa, &mut acc)?;
batch = crate::simd::TripleBatch::default();
}
}
}
}
if batch.len > 0 {
flush_solid_batch(&batch, &soa, &mut acc)?;
}
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(super) 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();
if det.abs() < MIN_TRIPLE_DET {
continue;
}
let v = mat.inverse() * DVec3::new(ma, mb, mc);
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
}