#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use brepkit_operations::boolean::{BooleanOp, boolean};
use brepkit_operations::tessellate::tessellate_solid_with_tolerance;
use brepkit_topology::Topology;
use brepkit_topology::explorer::solid_faces;
use brepkit_topology::solid::SolidId;
fn fixture(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/data")
.join(name)
}
fn load(name: &str, topo: &mut Topology) -> SolidId {
brepkit_io::arena_io::deserialize_solid(&std::fs::read(fixture(name)).unwrap(), topo).unwrap()
}
fn mesh_health(topo: &Topology, solid: SolidId) -> (usize, usize) {
type Q = (i64, i64, i64);
let mesh = tessellate_solid_with_tolerance(topo, solid, 0.02, 6.0_f64.to_radians()).unwrap();
let q = |v: f64| (v * 1.0e4).round() as i64;
let mut occ: HashMap<(Q, Q), usize> = HashMap::new();
for t in mesh.indices.chunks(3) {
for k in 0..3 {
let a = t[k] as usize;
let b = t[(k + 1) % 3] as usize;
let vs = &mesh.positions;
let pa = (q(vs[a].x()), q(vs[a].y()), q(vs[a].z()));
let pb = (q(vs[b].x()), q(vs[b].y()), q(vs[b].z()));
let key = if pa <= pb { (pa, pb) } else { (pb, pa) };
*occ.entry(key).or_default() += 1;
}
}
let bnd = occ.values().filter(|&&c| c == 1).count();
let nm = occ.values().filter(|&&c| c > 2).count();
(bnd, nm)
}
#[test]
fn completing_the_fourth_socket_keeps_the_junction_disc() {
let mut topo = Topology::new();
let mut current = load("snapclip_junction_slab.bin", &mut topo);
for i in 0..4 {
let pocket = load(&format!("snapclip_junction_pocket{i}.bin"), &mut topo);
current = boolean(&mut topo, BooleanOp::Cut, current, pocket).unwrap();
let faces = solid_faces(&topo, current).unwrap().len();
assert!(
faces < 200,
"pocket cut {i}: face count {faces} signals a mesh fallback \
(analytic chain stays under ~130 faces)"
);
let (bnd, nm) = mesh_health(&topo, current);
assert_eq!(
(bnd, nm),
(0, 0),
"pocket cut {i}: must stay watertight and manifold; got bnd={bnd} nm={nm} ({faces} faces)"
);
}
}