use super::signed_distance_field;
use crate::{RuntimeShape3, Sdf, Shape3};
fn sample_grid<F: Sdf<Scalar = f64>>(
field: &F,
shape: &RuntimeShape3,
origin: [f64; 3],
h: f64,
) -> alloc::vec::Vec<f64> {
let size = shape.size();
let mut out = alloc::vec::Vec::with_capacity(shape.element_count());
for z in 0..size[2] {
for y in 0..size[1] {
for x in 0..size[0] {
out.push(field.sample([
origin[0] + h * f64::from(x),
origin[1] + h * f64::from(y),
origin[2] + h * f64::from(z),
]));
}
}
}
out
}
#[test]
fn agrees_with_brute_force_exactly() {
let field = crate::fields::Sphere::<f64>::canonical();
let shape = RuntimeShape3::new([11, 9, 13]).expect("valid shape");
let size = shape.size();
let h = 0.25_f64;
let origin = [-1.25, -1.0, -1.5];
let samples = sample_grid(&field, &shape, origin, h);
let got = signed_distance_field(&samples, &shape, h).expect("transform");
let index = |x: u32, y: u32, z: u32| ((z * size[1] + y) * size[0] + x) as usize;
let mut checked = 0usize;
for z in 0..size[2] {
for y in 0..size[1] {
for x in 0..size[0] {
let i = index(x, y, z);
let inside = samples[i] < 0.0;
let mut best = f64::INFINITY;
for bz in 0..size[2] {
for by in 0..size[1] {
for bx in 0..size[0] {
let j = index(bx, by, bz);
if (samples[j] < 0.0) == inside {
continue;
}
let d = f64::from(x as i64 as i32 - bx as i64 as i32).powi(2)
+ f64::from(y as i64 as i32 - by as i64 as i32).powi(2)
+ f64::from(z as i64 as i32 - bz as i64 as i32).powi(2);
best = best.min(d);
}
}
}
let want = if inside {
-best.sqrt() * h
} else {
best.sqrt() * h
};
assert!(
(got[i] - want).abs() < 1e-12,
"at ({x},{y},{z}): transform {} but brute force {want}",
got[i]
);
checked += 1;
}
}
}
assert_eq!(checked, shape.element_count());
std::println!("measured: {checked} samples agree with brute force exactly");
}
#[test]
fn matches_the_analytic_sphere_within_one_spacing() {
let field = crate::fields::Sphere::<f64>::canonical();
let shape = RuntimeShape3::new([41; 3]).expect("valid shape");
let h = 0.1_f64;
let origin = [-2.0; 3];
let samples = sample_grid(&field, &shape, origin, h);
let got = signed_distance_field(&samples, &shape, h).expect("transform");
let size = shape.size();
let mut worst = 0.0f64;
let mut worst_at = [0u32; 3];
for z in 0..size[2] {
for y in 0..size[1] {
for x in 0..size[0] {
let p = [
origin[0] + h * f64::from(x),
origin[1] + h * f64::from(y),
origin[2] + h * f64::from(z),
];
let truth = field.sample(p);
let i = ((z * size[1] + y) * size[0] + x) as usize;
let err = (got[i] - truth).abs();
if err > worst {
worst = err;
worst_at = [x, y, z];
}
}
}
}
let cells = worst / h;
assert!(
cells <= 1.0 + 1e-9,
"worst disagreement {cells:.4} cells exceeds one spacing at {worst_at:?}"
);
std::println!(
"measured: worst |transform − analytic| = {worst:.5} = {cells:.4} cells, \
which is the sampling limit rather than an algorithmic error"
);
}
#[test]
fn a_grid_too_small_to_hold_a_cell_is_refused() {
let shape = RuntimeShape3::new([1, 4, 4]).expect("valid shape");
let samples = alloc::vec![1.0f64; shape.element_count()];
assert!(matches!(
signed_distance_field(&samples, &shape, 0.1),
Err(crate::Error::GridTooSmall { .. })
));
}
#[test]
fn a_mismatched_sample_count_is_refused() {
let shape = RuntimeShape3::new([4; 3]).expect("valid shape");
let samples = alloc::vec![1.0f64; 7];
assert!(signed_distance_field(&samples, &shape, 0.1).is_err());
}
#[test]
fn sweeping_and_the_exact_transform_trade_places_with_distance() {
let field = crate::fields::Sphere::<f64>::canonical();
let shape = RuntimeShape3::new([41; 3]).expect("valid shape");
let h = 0.1_f64;
let origin = [-2.0; 3];
let samples = sample_grid(&field, &shape, origin, h);
let exact = signed_distance_field(&samples, &shape, h).expect("transform");
let swept = super::signed_distance_field_swept(&samples, &shape, h).expect("sweep");
let size = shape.size();
let mut near = (0.0f64, 0.0f64);
let mut far_band = (0.0f64, 0.0f64);
for z in 0..size[2] {
for y in 0..size[1] {
for x in 0..size[0] {
let p = [
origin[0] + h * f64::from(x),
origin[1] + h * f64::from(y),
origin[2] + h * f64::from(z),
];
let truth = field.sample(p);
let i = ((z * size[1] + y) * size[0] + x) as usize;
let (ee, se) = ((exact[i] - truth).abs(), (swept[i] - truth).abs());
if truth.abs() <= 2.0 * h {
near = (near.0.max(ee), near.1.max(se));
} else if truth.abs() >= 8.0 * h {
far_band = (far_band.0.max(ee), far_band.1.max(se));
}
}
}
}
std::println!(
"measured: within 2 cells of the surface — exact {:.5}, swept {:.5}\n\
measured: beyond 8 cells — exact {:.5}, swept {:.5}",
near.0,
near.1,
far_band.0,
far_band.1
);
assert!(
near.1 * 2.0 < near.0,
"sweeping was expected to win near the surface by a wide margin: \
exact {:.5}, swept {:.5}",
near.0,
near.1
);
assert!(
far_band.1 <= far_band.0 + 1e-9,
"sweeping lost far from the surface after all: exact {:.5}, swept {:.5} — \
the accumulation concern is real and M-252 needs revisiting",
far_band.0,
far_band.1
);
}
#[test]
fn sweeping_refuses_what_the_transform_refuses() {
let shape = RuntimeShape3::new([1, 4, 4]).expect("valid shape");
let samples = alloc::vec![1.0f64; shape.element_count()];
assert!(super::signed_distance_field_swept(&samples, &shape, 0.1).is_err());
let shape = RuntimeShape3::new([4; 3]).expect("valid shape");
assert!(super::signed_distance_field_swept(&alloc::vec![1.0f64; 7], &shape, 0.1).is_err());
}
#[test]
fn marching_agrees_with_sweeping_and_neither_is_the_other() {
let field = crate::fields::Sphere::<f64>::canonical();
let shape = RuntimeShape3::new([33; 3]).expect("valid shape");
let h = 0.125_f64;
let origin = [-2.0; 3];
let samples = sample_grid(&field, &shape, origin, h);
let swept = super::signed_distance_field_swept(&samples, &shape, h).expect("sweep");
let marched = super::signed_distance_field_marched(&samples, &shape, h).expect("march");
let size = shape.size();
let mut worst_gap = 0.0f64;
let mut swept_err = 0.0f64;
let mut marched_err = 0.0f64;
let mut differ = 0usize;
for z in 0..size[2] {
for y in 0..size[1] {
for x in 0..size[0] {
let p = [
origin[0] + h * f64::from(x),
origin[1] + h * f64::from(y),
origin[2] + h * f64::from(z),
];
let truth = field.sample(p);
let i = ((z * size[1] + y) * size[0] + x) as usize;
worst_gap = worst_gap.max((swept[i] - marched[i]).abs());
swept_err = swept_err.max((swept[i] - truth).abs());
marched_err = marched_err.max((marched[i] - truth).abs());
if (swept[i] - marched[i]).abs() > 1e-12 {
differ += 1;
}
}
}
}
std::println!(
"measured: swept vs marched — worst gap {worst_gap:.5}, {differ} samples differ; \
error against analytic: swept {swept_err:.5}, marched {marched_err:.5}"
);
assert!(
worst_gap < 4.0 * h,
"the two orderings disagree by {worst_gap:.5}, which is more than an \
ordering can explain"
);
assert!(
marched_err < 2.0 * h,
"marching's error {marched_err:.5} is too large"
);
assert!(
swept_err < 2.0 * h,
"sweeping's error {swept_err:.5} is too large"
);
}
#[test]
fn marching_refuses_what_the_others_refuse() {
let shape = RuntimeShape3::new([1, 4, 4]).expect("valid shape");
let samples = alloc::vec![1.0f64; shape.element_count()];
assert!(super::signed_distance_field_marched(&samples, &shape, 0.1).is_err());
let shape = RuntimeShape3::new([4; 3]).expect("valid shape");
assert!(super::signed_distance_field_marched(&alloc::vec![1.0f64; 7], &shape, 0.1).is_err());
}
#[test]
fn reinitialisation_does_not_move_the_zero_set() {
let field = crate::fields::Sphere::<f64>::canonical();
let shape = RuntimeShape3::new([33; 3]).expect("valid shape");
let h = 0.125_f64;
let origin = [-2.0; 3];
let size = shape.size();
let mut values = sample_grid(&field, &shape, origin, h);
let crossings = |v: &[f64]| {
let mut out = alloc::vec::Vec::new();
for z in 0..size[2] {
for y in 0..size[1] {
for x in 0..size[0] - 1 {
let i = ((z * size[1] + y) * size[0] + x) as usize;
let j = i + 1;
if (v[i] < 0.0) != (v[j] < 0.0) {
out.push((i, v[i] / (v[i] - v[j])));
}
}
}
}
out
};
let first = crossings(&values);
assert!(first.len() > 300, "only {} crossings to track", first.len());
let mut touched = 0usize;
for _ in 0..20 {
let (next, visited) =
super::reinitialise_narrow_band(&values, &shape, h, 3).expect("reinit");
values = next;
touched = visited;
}
let last = crossings(&values);
assert_eq!(
first.len(),
last.len(),
"reinitialisation created or destroyed a crossing, which is worse than \
moving one"
);
let mut worst = 0.0f64;
for ((ia, ta), (ib, tb)) in first.iter().zip(&last) {
assert_eq!(ia, ib, "a crossing moved to a different edge");
worst = worst.max((ta - tb).abs());
}
let total = shape.element_count();
let share = 100.0 * touched as f64 / total as f64;
std::println!(
"measured: 20 reinitialisations moved the zero set by at most {worst:.6} of a cell; \
the band solve finalised {touched} of {total} samples ({share:.1}%)"
);
assert!(
share < 25.0,
"the band solve touched {share:.1}% of the grid — that is a volume cost, \
not a surface-area one"
);
#[allow(clippy::float_cmp)]
{
assert!(
worst == 0.0,
"the zero set moved by {worst:e} of a cell over 20 reinitialisations"
);
}
}
#[test]
fn reinitialisation_refuses_what_the_others_refuse() {
let shape = RuntimeShape3::new([1, 4, 4]).expect("valid shape");
let v = alloc::vec![1.0f64; shape.element_count()];
assert!(super::reinitialise_narrow_band(&v, &shape, 0.1, 3).is_err());
}