use super::{DegenerateReason, IntersectionSolid, intersection_solid};
use crate::kernel::arrangement::Tri;
use crate::kernel::mesh_bridge::tris_to_mesh;
use crate::mesh::Mesh;
use crate::router::voids::geom::mesh_signed_volume;
use crate::world_frame_fixture::{
WorldFrameCase, normal_projected_noise_bound, placed_box_mesh,
};
const OVERLAP_M: f64 = 0.005;
fn overlapping_pair(case: WorldFrameCase) -> (Mesh, Mesh) {
let a = placed_box_mesh(case, [0.0, 0.0, 0.0], [1.0, 1.0, 0.3]);
let b = placed_box_mesh(case, [0.0, 0.0, 0.3 - OVERLAP_M], [1.0, 1.0, 0.6]);
(a, b)
}
#[test]
fn the_overlap_is_provably_above_the_z_noise_bound_in_every_case() {
for case in crate::world_frame_fixture::WORLD_FRAME_CASES {
let (a, b) = overlapping_pair(case);
let bound = normal_projected_noise_bound([0.0, 0.0, 1.0], &[&a, &b]);
assert!(
OVERLAP_M > 10_000.0 * bound,
"corpus premise broken for {case:?}: overlap {OVERLAP_M} vs z-noise bound {bound}"
);
}
}
#[test]
fn counter_case_a_5mm_overlap_at_the_origin_is_a_solid() {
let (a, b) = overlapping_pair(WorldFrameCase::AtOrigin);
let solid = intersection_solid(&a, &b);
let volume = solid
.volume_m3()
.unwrap_or_else(|| panic!("expected a Solid at the origin, got {solid:?}"));
let expected = 1.0 * 1.0 * OVERLAP_M;
assert!(
(volume - expected).abs() < 1e-4,
"volume {volume} vs expected {expected}"
);
}
#[test]
fn counter_case_a_sub_band_overlap_at_the_origin_stays_withheld() {
let a = placed_box_mesh(WorldFrameCase::AtOrigin, [0.0, 0.0, 0.0], [1.0, 1.0, 0.3]);
let b = placed_box_mesh(
WorldFrameCase::AtOrigin,
[0.0, 0.0, 0.3 - 0.0002],
[1.0, 1.0, 0.6],
);
assert!(
matches!(
intersection_solid(&a, &b),
IntersectionSolid::Degenerate(DegenerateReason::BelowKernelResolution { .. })
),
"a 0.2 mm overlap sits inside the kernel's near band and must stay withheld"
);
}
#[test]
fn a_5mm_overlap_10km_out_in_x_must_still_be_a_solid() {
let (a, b) = overlapping_pair(WorldFrameCase::FarBaked);
let solid = intersection_solid(&a, &b);
let volume = solid.volume_m3().unwrap_or_else(|| {
panic!(
"the SAME genuine 5 mm overlap that is a Solid at the origin must be a \
Solid 10 km out in X (offset axis X, contact normal Z); got {solid:?}"
)
});
let expected = 1.0 * 1.0 * OVERLAP_M;
assert!(
(volume - expected).abs() < 1e-4,
"the gate returned a Solid 10 km out, but its far-placement volume {volume} \
does not match the expected {expected}"
);
}
#[test]
fn a_rotated_overlap_9km_out_reports_the_same_volume_as_at_the_origin() {
let sphere_far = far_sphere();
let lo: [f64; 3] = std::array::from_fn(|k| FAR_SITE_M[k] - 0.5);
let hi = [FAR_SITE_M[0] + 0.5, FAR_SITE_M[1] + 0.5, FAR_SITE_M[2] + 0.05];
let box_far = placed_box_mesh(WorldFrameCase::AtOrigin, lo, hi);
let sphere_near = translated_exactly(&sphere_far, FAR_SITE_M);
let box_near = translated_exactly(&box_far, FAR_SITE_M);
let read = |a: &Mesh, b: &Mesh| {
let solid = intersection_solid(a, b);
solid
.volume_m3()
.unwrap_or_else(|| panic!("a thick sphere/box overlap must be a Solid, got {solid:?}"))
};
let v_near = read(&sphere_near, &box_near);
let v_far = read(&sphere_far, &box_far);
let sphere = SPHERE_VOLUME_M3;
assert!(
v_near > 0.5 * sphere && v_near < sphere,
"near-origin control reads {v_near}, outside ({}, {sphere})",
0.5 * sphere
);
assert!(
((v_far - v_near) / v_near).abs() < 1e-7,
"the same overlap 9 km out reports {v_far} against {v_near} at the origin \
(relative {:e})",
(v_far - v_near) / v_near
);
}
const FAR_SITE_M: [f64; 3] = [9000.375, 5000.25, 300.125];
const SPHERE_R_M: f64 = 0.3;
const SPHERE_VOLUME_M3: f64 = 4.0 / 3.0 * std::f64::consts::PI * SPHERE_R_M * SPHERE_R_M * SPHERE_R_M;
fn far_sphere() -> Mesh {
let (centre, r, stacks, slices) = (FAR_SITE_M, SPHERE_R_M, 20usize, 25usize);
let point = |i: usize, j: usize| -> [f64; 3] {
let phi = std::f64::consts::PI * i as f64 / stacks as f64;
let theta = 2.0 * std::f64::consts::PI * j as f64 / slices as f64;
let p = [r * phi.sin() * theta.cos(), r * phi.sin() * theta.sin(), r * phi.cos()];
rotate_and_place(p, centre)
};
let mut tris: Vec<Tri> = Vec::new();
for i in 0..stacks {
for j in 0..slices {
let (p00, p01) = (point(i, j), point(i, j + 1));
let (p10, p11) = (point(i + 1, j), point(i + 1, j + 1));
if i + 1 < stacks {
tris.push([p00, p10, p11]);
}
if i > 0 {
tris.push([p00, p11, p01]);
}
}
}
tris_to_mesh(&tris)
}
fn rotate_and_place(p: [f64; 3], centre: [f64; 3]) -> [f64; 3] {
let (ax, ay, az) = (1.0 / 14f64.sqrt(), 2.0 / 14f64.sqrt(), 3.0 / 14f64.sqrt());
let (s, c) = 0.7f64.sin_cos();
let t = 1.0 - c;
let rot = [
[t * ax * ax + c, t * ax * ay - s * az, t * ax * az + s * ay],
[t * ax * ay + s * az, t * ay * ay + c, t * ay * az - s * ax],
[t * ax * az - s * ay, t * ay * az + s * ax, t * az * az + c],
];
std::array::from_fn(|k| rot[k][0] * p[0] + rot[k][1] * p[1] + rot[k][2] * p[2] + centre[k])
}
fn translated_exactly(mesh: &Mesh, offset: [f64; 3]) -> Mesh {
let mut out = mesh.clone();
for chunk in out.positions.chunks_exact_mut(3) {
for k in 0..3 {
chunk[k] = (f64::from(chunk[k]) - offset[k]) as f32;
}
}
out
}
fn crack_one_vertex(mesh: &mut Mesh) {
let i = mesh.indices[7 * 3] as usize * 3;
mesh.positions[i] = f32::from_bits(mesh.positions[i].to_bits() + 1);
}
#[test]
fn mesh_signed_volume_reads_the_same_volume_9km_out_as_at_the_origin() {
let closed_far = far_sphere();
let mut cracked_far = closed_far.clone();
crack_one_vertex(&mut cracked_far);
let analytic = SPHERE_VOLUME_M3;
for (label, far, tol) in [
("closed", &closed_far, 1e-6),
("cracked", &cracked_far, 1e-3),
] {
let near = translated_exactly(far, FAR_SITE_M);
let v_near = mesh_signed_volume(&near);
let v_far = mesh_signed_volume(far);
assert!(
(v_near - analytic).abs() < 0.03 * analytic,
"{label}: near-origin control must read the sphere's volume: {v_near} vs {analytic}"
);
assert!(
((v_far - v_near) / v_near).abs() < tol,
"{label}: the same mesh 9 km out read {v_far} against {v_near} at the origin \
(relative {:e}): the reading depends on where the model sits",
(v_far - v_near) / v_near
);
}
}