use glam::DVec3;
use super::{
BLANK_HALF_EXTENT, EPS_FEAS, MIN_TRIPLE_DET, SolidMesh, SolidStatus, VERTEX_DEDUP,
build_solid_mesh, measure_solid,
mesh::face_area,
vertices::{SolidVertex, dedup_planes, feasible_vertices, for_each_feasible_triple},
};
use crate::geometry::{cuts::StandardGemCuts, plane::GpuFacetPlane};
mod concave;
mod dedup;
mod measure;
mod mesh;
mod proportions;
fn flush_solid_batch_linear_reference(
batch: &crate::simd::TripleBatch,
soa: &crate::simd::PlanesSoA64,
verts: &mut Vec<SolidVertex>,
) -> 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;
}
let dup = verts
.iter()
.any(|s| (s.v - v).abs().max_element() < VERTEX_DEDUP);
if !dup {
verts.push(SolidVertex { v });
}
}
Some(())
}
fn feasible_vertices_linear_reference(planes: &[(DVec3, f64)]) -> Option<Vec<DVec3>> {
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 verts: Vec<SolidVertex> = Vec::new();
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_linear_reference(&batch, &soa, &mut verts)?;
batch = crate::simd::TripleBatch::default();
}
}
}
}
if batch.len > 0 {
flush_solid_batch_linear_reference(&batch, &soa, &mut verts)?;
}
Some(verts.into_iter().map(|s| s.v).collect())
}
fn assert_dedup_matches_reference(planes: &[(DVec3, f64)], label: &str) {
let deduped = dedup_planes(planes);
let fast = feasible_vertices(&deduped).map(|v| v.into_iter().map(|s| s.v).collect::<Vec<_>>());
let reference = feasible_vertices_linear_reference(&deduped);
match (fast, reference) {
(None, None) => {}
(Some(f), Some(r)) => {
assert_eq!(
f.len(),
r.len(),
"{label}: vertex count differs (indexed {} vs linear-scan reference {})",
f.len(),
r.len()
);
for (i, (fv, rv)) in f.iter().zip(r.iter()).enumerate() {
assert_eq!(
(fv.x.to_bits(), fv.y.to_bits(), fv.z.to_bits()),
(rv.x.to_bits(), rv.y.to_bits(), rv.z.to_bits()),
"{label}: vertex {i} differs (indexed {fv:?} vs reference {rv:?})"
);
}
}
(f, r) => panic!(
"{label}: indexed and reference dedup disagree on boundedness (indexed {:?}, reference {:?})",
f.is_some(),
r.is_some()
),
}
}
fn planes_from_asc_schedule(text: &str) -> Vec<(DVec3, f64)> {
let schedule = indicatrix_formats::asc::parse_asc(text).expect("fixture schedule parses");
let mut tiers = crate::geometry::meet_solver::meet_tier_inputs_from_asc(&schedule);
for j in [0usize, 1, 2] {
if let Some(t) = schedule.tiers.get(j) {
tiers[j].constraint =
crate::geometry::meet_solver::MeetConstraint::ScaleReference(t.mast);
}
}
let normals =
crate::geometry::meet_solver::tier_instance_normals(schedule.gear_teeth_abs(), &tiers);
let solved = crate::geometry::meet_solver::solve_meet_points(schedule.gear_teeth_abs(), &tiers);
normals
.iter()
.zip(solved.iter().map(|s| s.mast))
.flat_map(|(ns, m)| ns.iter().map(move |&n| (n, m)))
.collect()
}
fn mesh_divergence_volume(mesh: &SolidMesh) -> f64 {
let mut acc = 0.0f64;
for tri in mesh.indices.as_chunks::<3>().0 {
let v0 = mesh.positions[tri[0] as usize];
let v1 = mesh.positions[tri[1] as usize];
let v2 = mesh.positions[tri[2] as usize];
acc += v0.dot(v1.cross(v2));
}
acc / 6.0
}
fn assert_watertight(mesh: &SolidMesh, label: &str) {
type PosKey = (u64, u64, u64);
let key = |i: u32| -> PosKey {
let p = mesh.positions[i as usize];
(p.x.to_bits(), p.y.to_bits(), p.z.to_bits())
};
let mut edges: Vec<(PosKey, PosKey)> = Vec::new();
for tri in mesh.indices.as_chunks::<3>().0 {
for &(a, b) in &[(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])] {
let (ka, kb) = (key(a), key(b));
edges.push(if ka <= kb { (ka, kb) } else { (kb, ka) });
}
}
for e in &edges {
let count = edges.iter().filter(|other| *other == e).count();
assert_eq!(
count, 2,
"{label}: edge {e:?} is shared by {count} triangles, not 2 (not watertight)"
);
}
}
fn verts_for(planes: &[(DVec3, f64)]) -> Vec<SolidVertex> {
feasible_vertices(&dedup_planes(planes)).expect("fixture must be bounded")
}
fn assert_mesh_matches_measure_solid(planes: &[(DVec3, f64)], label: &str) {
let status = build_solid_mesh(planes);
let SolidStatus::Closed(mesh) = status else {
panic!("{label}: expected a closed mesh, got {status:?}");
};
assert_watertight(&mesh, label);
let verts = verts_for(planes);
for &(facet_idx, ref ring) in &mesh.rings {
let (normal, offset) = planes[facet_idx];
let want_area = face_area(normal, offset, &verts);
let centroid = ring.iter().copied().sum::<DVec3>() / ring.len() as f64;
let mut got_area = 0.0f64;
for i in 0..ring.len() {
let a = ring[i] - centroid;
let b = ring[(i + 1) % ring.len()] - centroid;
got_area = 0.5f64.mul_add(a.cross(b).length(), got_area);
}
assert!(
(got_area - want_area).abs() < 1e-9,
"{label}: facet {facet_idx} triangle-area sum {got_area} != face_area {want_area}"
);
}
let want_volume = measure_solid(planes).expect("must measure").volume;
let got_volume = mesh_divergence_volume(&mesh);
assert!(
(got_volume - want_volume).abs() < 1e-6,
"{label}: mesh divergence volume {got_volume} != measure_solid volume {want_volume}"
);
}
fn halfspaces(planes: Vec<GpuFacetPlane>) -> Vec<(DVec3, f64)> {
planes
.into_iter()
.map(GpuFacetPlane::to_halfspace_f64)
.collect()
}
#[test]
fn a_duplicated_plane_leaves_the_round_brilliant_volume_unchanged() {
let base = halfspaces(StandardGemCuts::standard_round_brilliant());
let want = measure_solid(&base)
.expect("the brilliant must measure")
.volume;
for (i, &plane) in base.iter().enumerate() {
let mut doubled = base.clone();
doubled.push(plane);
let got = measure_solid(&doubled)
.unwrap_or_else(|| panic!("plane {i} duplicated: must still measure"))
.volume;
assert!(
(got - want).abs() <= 1e-12 * want.abs(),
"plane {i} duplicated: volume {got} differs from {want}"
);
}
}
type TripleRecord = ([usize; 3], u64, [u64; 3]);
fn walk_triples(
planes: &[(DVec3, f64)],
det_floor: f64,
prune_pairs: bool,
) -> (bool, Vec<TripleRecord>) {
let mut visited = Vec::new();
let completed = for_each_feasible_triple(planes, det_floor, prune_pairs, |t, det, v| {
visited.push((
t,
det.to_bits(),
[v.x.to_bits(), v.y.to_bits(), v.z.to_bits()],
));
})
.is_some();
(completed, visited)
}
#[test]
fn pair_prune_visits_the_same_triples_as_the_exhaustive_walk() {
for (label, planes) in [
(
"round brilliant",
StandardGemCuts::standard_round_brilliant(),
),
("emerald", StandardGemCuts::emerald_cut()),
] {
let planes = halfspaces(planes);
for det_floor in [MIN_TRIPLE_DET, 1e-8] {
let exhaustive = walk_triples(&planes, det_floor, false);
let pruned = walk_triples(&planes, det_floor, true);
assert!(exhaustive.0, "{label}: the cut must be bounded");
assert!(
!exhaustive.1.is_empty(),
"{label}: the walk must find vertices"
);
assert_eq!(
exhaustive, pruned,
"{label}, det floor {det_floor}: pruned walk diverged from the exhaustive one"
);
}
}
}