use super::*;
use crate::extractor::Extractor;
use crate::fields::{ReferenceField, Sphere};
use crate::{MeshBuffer, RuntimeShape3};
const N: u32 = 17;
fn grid() -> (Sphere<f64>, RuntimeShape3, [f64; 3], f64) {
let field = Sphere::<f64>::canonical();
let (lo, hi) = field.domain();
let h = (hi[0] - lo[0]) / f64::from(N - 1);
let shape = RuntimeShape3::new([N; 3]).expect("valid shape");
(field, shape, lo, h)
}
fn mesh_with<E: Extractor<f64>>(extractor: &mut E) -> MeshBuffer<f64> {
let (field, shape, lo, h) = grid();
let mut out = MeshBuffer::<f64>::new();
extractor
.extract_into(&field, &shape, lo, h, &mut out)
.expect("extraction");
out
}
fn report_of(mesh: &MeshBuffer<f64>) -> SealingReport {
let (field, shape, lo, h) = grid();
sealing(&field, &shape, lo, h, &mesh.positions, &mesh.indices)
}
#[test]
fn every_extractor_seals_a_sphere() {
crate::for_each_extractor!(f64, |name, extractor| {
let mesh = mesh_with(&mut extractor);
assert!(
!mesh.indices.is_empty(),
"{name} meshed a sphere to nothing"
);
let r = report_of(&mesh);
assert!(
r.agrees(),
"{name} does not seal a sphere:\n{r}\nunsealed {} spurious {} mixed {}",
r.unsealed_walls,
r.spurious_walls,
r.mixed_regions
);
});
}
#[test]
fn an_empty_mesh_leaves_every_wall_unsealed() {
let empty = MeshBuffer::<f64>::new();
let r = report_of(&empty);
assert!(r.field_walls > 0, "the fixture has no walls to miss");
assert_eq!(r.unsealed_walls, r.field_walls);
assert_eq!(r.mesh_walls, 0);
assert_eq!(r.spurious_walls, 0);
assert_eq!(r.mesh_regions, 1 + r.boundary_samples);
assert_eq!(r.mixed_regions, 1);
assert!(!r.agrees());
}
#[test]
fn a_displaced_mesh_disagrees_in_both_directions() {
let mut mesh = mesh_with(&mut crate::marching_cubes::MarchingCubes::<f64>::new());
for p in &mut mesh.positions {
p[0] += 0.3;
}
let r = report_of(&mesh);
assert!(
r.unsealed_walls > 0,
"a displaced sphere leaves the true boundary unsealed:\n{r}"
);
assert!(
r.spurious_walls > 0,
"a displaced sphere walls off open air:\n{r}"
);
assert!(!r.agrees());
}
#[test]
fn a_membrane_across_open_air_is_a_spurious_wall() {
let mut mesh = mesh_with(&mut crate::marching_cubes::MarchingCubes::<f64>::new());
let clean = report_of(&mesh);
assert!(clean.agrees(), "the fixture must start sealed:\n{clean}");
let base = mesh.positions.len() as u32;
for p in [
[-1.625, -1.85, -1.80],
[-1.625, -1.65, -1.80],
[-1.625, -1.75, -1.65],
] {
mesh.positions.push(p);
mesh.normals.push([1.0, 0.0, 0.0]);
}
mesh.indices.extend_from_slice(&[base, base + 1, base + 2]);
let r = report_of(&mesh);
assert_eq!(
r.spurious_walls, 1,
"one membrane should wall off exactly one probe:\n{r}"
);
assert_eq!(r.unsealed_walls, 0, "the sphere itself is untouched:\n{r}");
assert!(!r.agrees());
}
#[test]
fn the_merge_separates_the_primal_family_from_the_dual_one() {
let primal = report_of(&mesh_with(
&mut crate::marching_cubes::MarchingCubes::<f64>::new(),
));
let dual = report_of(&mesh_with(
&mut crate::dual_contouring::DualContouring::<f64>::new(),
));
assert_eq!(
primal.field_walls, dual.field_walls,
"same field, same walls"
);
assert!(
primal.merged_crossings > primal.field_walls,
"a primal method should merge more than one hit per wall, got {} for {} walls",
primal.merged_crossings,
primal.field_walls
);
assert!(
dual.merged_crossings * 5 < dual.field_walls,
"a dual method should barely merge at all, got {} for {} walls",
dual.merged_crossings,
dual.field_walls
);
}
#[test]
fn a_sample_on_the_surface_makes_its_probes_undecidable() {
let (field, _, lo, h) = grid();
let mut on_surface = alloc::vec::Vec::new();
for z in 0..N {
for y in 0..N {
for x in 0..N {
let p = [
lo[0] + h * f64::from(x),
lo[1] + h * f64::from(y),
lo[2] + h * f64::from(z),
];
#[allow(clippy::float_cmp, reason = "the exact tie is the subject")]
let on = field.sample(p) == 0.0;
if on {
on_surface.push(p);
}
}
}
}
assert_eq!(on_surface.len(), 6, "expected the six axis intercepts");
for p in &on_surface {
#[allow(clippy::float_cmp, reason = "an intercept's other two are exactly 0")]
let off_axis = p.iter().filter(|c| **c != 0.0).count();
assert_eq!(off_axis, 1, "{p:?} is not an axis intercept");
}
let r = report_of(&mesh_with(
&mut crate::marching_cubes::MarchingCubes::<f64>::new(),
));
assert_eq!(r.boundary_samples, 6);
assert_eq!(
r.degenerate_probes, 36,
"six interior samples, six edges each"
);
assert_eq!(
r.field_air_components, r.mesh_air_components,
"the exclusion has to be symmetric or it manufactures a difference"
);
}
#[test]
fn a_zero_area_triangle_is_excluded_before_it_can_be_judged() {
let mt = report_of(&mesh_with(
&mut crate::marching_tetrahedra::MarchingTetrahedra::<f64>::new(),
));
assert_eq!(mt.degenerate_triangles, 36);
assert_eq!(
mt.coplanar_probes, 0,
"the exclusion is what takes this to zero; 6624 without it"
);
let mc = report_of(&mesh_with(
&mut crate::marching_cubes::MarchingCubes::<f64>::new(),
));
assert_eq!(mc.degenerate_triangles, 0);
assert_eq!(mc.coplanar_probes, 0);
}
#[test]
fn sealing_is_deterministic() {
let mesh = mesh_with(&mut crate::surface_nets::SurfaceNets::<f64>::new());
assert_eq!(report_of(&mesh), report_of(&mesh));
}
#[test]
fn a_dual_leaves_the_domain_boundary_unsealed_and_only_there() {
use crate::fields::FbmTerrain;
const M: u32 = 17;
let field = FbmTerrain::<f64>::canonical();
let (flo, fhi) = field.domain();
let h = (fhi[0] - flo[0]) / f64::from(M - 1);
let shape = RuntimeShape3::new([M; 3]).expect("valid shape");
let mut duals = 0;
crate::for_each_extractor!(f64, |name, extractor| {
let mut out = MeshBuffer::<f64>::new();
extractor
.extract_into(&field, &shape, flo, h, &mut out)
.expect("extraction");
let r = sealing(&field, &shape, flo, h, &out.positions, &out.indices);
if name.starts_with("marching") {
assert_eq!(r.unsealed_walls, 0, "{name} left a wall open:\n{r}");
}
if name.ends_with("dual_contouring") || name == "surface_nets" {
duals += 1;
assert_eq!(r.unsealed_walls, 92, "{name}:\n{r}");
assert_eq!(
r.unsealed_on_domain_face, r.unsealed_walls,
"{name} left a hole away from the domain face, which is a \
different defect:\n{r}"
);
}
});
assert_eq!(duals, 3, "expected three dual entries in the registry");
}