use super::{BLANK_HALF_EXTENT, EPS_FEAS, MIN_TRIPLE_DET};
use glam::DVec3;
struct PlaneRec {
n: DVec3,
m: f64,
owner: usize,
instance: usize,
}
pub(super) struct CachedCandidate {
pub(super) v: DVec3,
pub(super) violated: Option<usize>,
key: [(usize, usize); 3],
}
pub(super) struct Phase1Cache {
soa: crate::simd::PlanesSoA64,
real_planes: Vec<PlaneRec>,
pub(super) candidates: Vec<CachedCandidate>,
}
impl Phase1Cache {
pub(super) fn new() -> Self {
let mut soa = crate::simd::PlanesSoA64::with_capacity(6);
for n in [
DVec3::X,
DVec3::NEG_X,
DVec3::Y,
DVec3::NEG_Y,
DVec3::Z,
DVec3::NEG_Z,
] {
soa.push(n, BLANK_HALF_EXTENT, crate::simd::BLANK_OWNER);
}
Self {
soa,
real_planes: Vec::new(),
candidates: Vec::new(),
}
}
pub(super) fn add_tier(&mut self, owner: usize, normals_j: &[DVec3], mast: f64) {
let new_planes: Vec<PlaneRec> = normals_j
.iter()
.enumerate()
.map(|(instance, &n)| PlaneRec {
n,
m: mast,
owner,
instance,
})
.collect();
revalidate(&mut self.candidates, &new_planes, owner);
for p in &new_planes {
self.soa.push(p.n, p.m, owner as u32);
}
let mut new_candidates: Vec<CachedCandidate> = Vec::new();
let mut batch = crate::simd::TripleBatch::default();
let mut key_meta = [[(0usize, 0usize); 3]; crate::simd::TRIPLE_LANES];
let k = new_planes.len();
for ja in 0..k {
for jb in (ja + 1)..k {
for jc in (jb + 1)..k {
push_triple(
&mut batch,
&mut key_meta,
&new_planes[ja],
&new_planes[jb],
&new_planes[jc],
&self.soa,
&mut new_candidates,
);
}
}
}
for ja in 0..k {
for jb in (ja + 1)..k {
for o in &self.real_planes {
push_triple(
&mut batch,
&mut key_meta,
&new_planes[ja],
&new_planes[jb],
o,
&self.soa,
&mut new_candidates,
);
}
}
}
let old_len = self.real_planes.len();
for new_plane in &new_planes {
for ia in 0..old_len {
for ib in (ia + 1)..old_len {
push_triple(
&mut batch,
&mut key_meta,
new_plane,
&self.real_planes[ia],
&self.real_planes[ib],
&self.soa,
&mut new_candidates,
);
}
}
}
if batch.len > 0 {
flush_keyed(&batch, &self.soa, &key_meta, &mut new_candidates);
}
new_candidates.sort_by_key(|cc| cc.key);
self.candidates = merge_sorted(std::mem::take(&mut self.candidates), new_candidates);
self.real_planes.extend(new_planes);
}
}
fn revalidate(existing: &mut Vec<CachedCandidate>, new_planes: &[PlaneRec], owner: usize) {
if new_planes.is_empty() {
return;
}
let mut mini = crate::simd::PlanesSoA64::with_capacity(new_planes.len());
for p in new_planes {
mini.push(p.n, p.m, owner as u32);
}
existing.retain_mut(
|cc| match crate::simd::classify_feasibility(&mini, cc.v, EPS_FEAS) {
crate::simd::Feasibility::Dead => {
debug_assert!(
false,
"single-owner mini-scan against one tier's planes cannot classify Dead"
);
false
}
crate::simd::Feasibility::Ok(None) => true,
crate::simd::Feasibility::Ok(Some(_)) => match cc.violated {
None => {
cc.violated = Some(owner);
true
}
Some(prior) => prior == owner,
},
},
);
}
fn push_triple(
batch: &mut crate::simd::TripleBatch,
key_meta: &mut [[(usize, usize); 3]; crate::simd::TRIPLE_LANES],
p0: &PlaneRec,
p1: &PlaneRec,
p2: &PlaneRec,
soa: &crate::simd::PlanesSoA64,
out: &mut Vec<CachedCandidate>,
) {
let mut trio = [p0, p1, p2];
trio.sort_by_key(|p| (p.owner, p.instance));
let [a, b, c] = trio;
key_meta[batch.len] = [
(a.owner, a.instance),
(b.owner, b.instance),
(c.owner, c.instance),
];
if batch.push((a.n, a.m), (b.n, b.m), (c.n, c.m)) {
flush_keyed(batch, soa, key_meta, out);
*batch = crate::simd::TripleBatch::default();
}
}
fn flush_keyed(
batch: &crate::simd::TripleBatch,
soa: &crate::simd::PlanesSoA64,
key_meta: &[[(usize, usize); 3]; crate::simd::TRIPLE_LANES],
out: &mut Vec<CachedCandidate>,
) {
let sol = crate::simd::solve_triple_batch(batch);
for (lane, key) in key_meta.iter().enumerate().take(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.x.abs() > BLANK_HALF_EXTENT + 1.0
|| v.y.abs() > BLANK_HALF_EXTENT + 1.0
|| v.z.abs() > BLANK_HALF_EXTENT + 1.0
{
continue;
}
match crate::simd::classify_feasibility(soa, v, EPS_FEAS) {
crate::simd::Feasibility::Dead => {}
crate::simd::Feasibility::Ok(violated) => {
out.push(CachedCandidate {
v,
violated: violated.map(|o| o as usize),
key: *key,
});
}
}
}
}
fn merge_sorted(a: Vec<CachedCandidate>, b: Vec<CachedCandidate>) -> Vec<CachedCandidate> {
let mut out = Vec::with_capacity(a.len() + b.len());
let mut a = a.into_iter();
let mut b = b.into_iter();
let mut next_a = a.next();
let mut next_b = b.next();
loop {
match (next_a.take(), next_b.take()) {
(Some(x), Some(y)) => {
if x.key <= y.key {
next_b = Some(y);
out.push(x);
next_a = a.next();
} else {
next_a = Some(x);
out.push(y);
next_b = b.next();
}
}
(Some(x), None) => {
out.push(x);
out.extend(a);
break;
}
(None, Some(y)) => {
out.push(y);
out.extend(b);
break;
}
(None, None) => break,
}
}
out
}