use super::DualContouring;
use crate::fields::{BoxExact, ReferenceField, Sphere};
use crate::surface_nets::SurfaceNets;
use crate::validate::{ValidateConfig, check_determinism, validate_indexed};
use crate::{MeshBuffer, RuntimeShape3, Sdf};
fn mesh_dc<F: Sdf<Scalar = f64> + ReferenceField>(
field: &F,
samples: u32,
) -> (MeshBuffer<f64>, f64) {
mesh_dc_with(field, samples, crate::dual_contouring::Clamp::ToCell)
}
fn mesh_dc_with<F: Sdf<Scalar = f64> + ReferenceField>(
field: &F,
samples: u32,
clamp: crate::dual_contouring::Clamp,
) -> (MeshBuffer<f64>, f64) {
let (lo, hi) = field.domain();
let cell_size = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = RuntimeShape3::new([samples; 3]).expect("valid shape");
let mut dc = DualContouring::<f64>::new();
dc.set_clamp(clamp);
let mut out = MeshBuffer::<f64>::new();
dc.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction");
(out, cell_size)
}
fn mesh_sn<F: Sdf<Scalar = f64> + ReferenceField>(
field: &F,
samples: u32,
) -> (MeshBuffer<f64>, f64) {
let (lo, hi) = field.domain();
let cell_size = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = RuntimeShape3::new([samples; 3]).expect("valid shape");
let mut sn = SurfaceNets::<f64>::new();
let mut out = MeshBuffer::<f64>::new();
sn.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction");
(out, cell_size)
}
fn nearest_vertex(mesh: &MeshBuffer<f64>, target: [f64; 3]) -> f64 {
mesh.positions
.iter()
.map(|p| {
((p[0] - target[0]).powi(2) + (p[1] - target[1]).powi(2) + (p[2] - target[2]).powi(2))
.sqrt()
})
.fold(f64::INFINITY, f64::min)
}
#[test]
fn topology_is_identical_to_surface_nets() {
for samples in [17u32, 27, 33] {
let field = BoxExact::<f64>::canonical();
let (dc, _) = mesh_dc_with(&field, samples, crate::dual_contouring::Clamp::None);
let (sn, _) = mesh_sn(&field, samples);
assert_eq!(
dc.vertex_count(),
sn.vertex_count(),
"{samples}^3: vertex counts differ"
);
assert_eq!(
dc.indices, sn.indices,
"{samples}^3: connectivity differs — the shared engine is not shared"
);
let moved = dc
.positions
.iter()
.zip(&sn.positions)
.filter(|(a, b)| (0..3).any(|k| a[k].to_bits() != b[k].to_bits()))
.count();
assert!(moved > 0, "{samples}^3: no vertex moved at all");
assert!(
moved < dc.vertex_count(),
"{samples}^3: every vertex moved, so the flat-region identity is broken"
);
}
}
#[test]
fn dual_contouring_moves_only_the_feature_vertices() {
let field = BoxExact::<f64>::canonical();
let samples = 27;
let (dc, cell_size) = mesh_dc_with(&field, samples, crate::dual_contouring::Clamp::None);
let (sn, _) = mesh_sn(&field, samples);
const ROUNDING: f64 = 1e-12;
const FEATURE: f64 = 0.1;
let mut flat = 0usize;
let mut feature = 0usize;
let mut between = 0usize;
for (a, b) in dc.positions.iter().zip(&sn.positions) {
let d = ((a[0] - b[0]).powi(2) + (a[1] - b[1]).powi(2) + (a[2] - b[2]).powi(2)).sqrt()
/ cell_size;
if d <= ROUNDING {
flat += 1;
} else if d >= FEATURE {
feature += 1;
} else {
between += 1;
}
}
std::println!(
"measured: box_exact at {samples}^3 -> {flat} vertices agree with surface nets to rounding, {feature} moved by >= {FEATURE} cells, {between} in between"
);
assert_eq!(
between, 0,
"the split must be clean: {between} vertices landed between rounding and a feature"
);
assert!(feature > 0, "no vertex was moved by the solve");
assert!(
flat > 4 * feature,
"a box is mostly flat: {flat} flat vs {feature} feature"
);
}
#[test]
fn the_corner_is_sharper_than_surface_nets() {
let field = BoxExact::<f64>::canonical();
let samples = 27;
let (dc, cell_size) = mesh_dc(&field, samples);
let (sn, _) = mesh_sn(&field, samples);
let corner = [1.0, 1.0, 1.0];
let dc_gap = nearest_vertex(&dc, corner);
let sn_gap = nearest_vertex(&sn, corner);
std::println!(
"measured: box_exact at {samples}^3 (h = {cell_size:.4}, not grid-aligned) -> nearest vertex to (1,1,1): dual contouring {dc_gap:.4} ({:.2} cells), surface nets {sn_gap:.4} ({:.2} cells)",
dc_gap / cell_size,
sn_gap / cell_size
);
assert!(
dc_gap < sn_gap,
"dual contouring must reach the corner Surface Nets rounds: {dc_gap} vs {sn_gap}"
);
assert!(
dc_gap < 0.2 * cell_size,
"expected the corner within 0.2 cells, got {:.3} cells",
dc_gap / cell_size
);
}
#[test]
fn every_reference_field_meshes_soundly() {
crate::for_each_reference_field!(f64, |name, field| {
for samples in [17u32, 27, 33] {
let (mesh, cell_size) = mesh_dc(&field, samples);
let cfg = ValidateConfig::from_cell_size(cell_size).expect("valid spacing");
let report = validate_indexed(&mesh.positions, &mesh.indices, &cfg);
assert!(
!report.has_structural_errors(),
"{name} at {samples}^3: malformed\n{report}"
);
assert_eq!(
report.inconsistently_oriented_edges, 0,
"{name} at {samples}^3: winding\n{report}"
);
if field.closed_in_domain() {
assert_eq!(
report.boundary_edges, 0,
"{name} at {samples}^3: the surface left the grid\n{report}"
);
}
std::println!(
"measured: dual contouring {name:16} at {samples}^3 -> {} tris, chi {}, {} non-manifold edges",
mesh.triangle_count(),
report.euler_characteristic,
report.non_manifold_edges
);
}
});
}
#[test]
fn extraction_is_deterministic() {
let field = Sphere::<f64>::canonical();
let (lo, hi) = field.domain();
let samples = 25u32;
let cell_size = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = RuntimeShape3::new([samples; 3]).expect("valid shape");
let report = check_determinism(|out: &mut MeshBuffer<f64>| {
let mut dc = DualContouring::<f64>::new();
dc.extract(&field, &shape, lo, cell_size, out)
.expect("extraction");
});
assert!(report.is_deterministic(), "{report}");
}
#[test]
fn edge_cases_are_handled_rather_than_panicking() {
let field = Sphere::<f64>::canonical();
let mut dc = DualContouring::<f64>::new();
let mut out = MeshBuffer::<f64>::new();
let flat = RuntimeShape3::new([1, 9, 9]).expect("valid shape");
assert!(dc.extract(&field, &flat, [-2.0; 3], 0.5, &mut out).is_err());
let away = RuntimeShape3::new([5; 3]).expect("valid shape");
out.reset();
dc.extract(&field, &away, [50.0; 3], 0.1, &mut out)
.expect("extraction");
assert_eq!(out.triangle_count(), 0);
}
#[test]
fn the_clamp_measured_on_every_reference_field() {
use crate::dual_contouring::Clamp;
use crate::validate::self_intersections;
let samples = 33u32;
std::println!(
"measured: A-009 cell clamp at {samples}^3 -- lambda = intersecting pairs per 1,000 triangles"
);
std::println!(
" {:16} {:>8} {:>12} {:>12} {:>14} {:>9}",
"field",
"tris",
"lambda off",
"lambda on",
"outside/verts",
"max cells"
);
crate::for_each_reference_field!(f64, |name, field| {
let (lo, hi) = field.domain();
let cell_size = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = RuntimeShape3::new([samples; 3]).expect("valid shape");
let measure = |clamp: Clamp| {
let mut dc = DualContouring::<f64>::new();
dc.set_clamp(clamp);
let mut out = MeshBuffer::<f64>::new();
dc.extract(&field, &shape, lo, cell_size, &mut out)
.expect("extraction");
let si = self_intersections(&out.positions, &out.indices, cell_size)
.expect("self-intersection scan");
(out, si)
};
let (unclamped, si_off) = measure(Clamp::None);
let (clamped, si_on) = measure(Clamp::ToCell);
let displacement: alloc::vec::Vec<f64> = unclamped
.positions
.iter()
.zip(&clamped.positions)
.map(|(a, b)| {
((a[0] - b[0]).powi(2) + (a[1] - b[1]).powi(2) + (a[2] - b[2]).powi(2)).sqrt()
/ cell_size
})
.collect();
let outside = displacement.iter().filter(|d| **d > 1e-3).count();
let worst = displacement.iter().copied().fold(0.0f64, f64::max);
std::println!(
" {name:16} {:>8} {:>12.3} {:>12.3} {:>7}/{:<6} {:>9.2}",
clamped.triangle_count(),
si_off.per_thousand_triangles(),
si_on.per_thousand_triangles(),
outside,
clamped.vertex_count(),
worst
);
assert!(
si_on.per_thousand_triangles() <= si_off.per_thousand_triangles(),
"{name}: the clamp increased lambda, {} -> {}",
si_off.per_thousand_triangles(),
si_on.per_thousand_triangles()
);
assert_eq!(
unclamped.indices, clamped.indices,
"{name}: the clamp changed connectivity"
);
});
}
#[test]
fn the_clamp_cost_in_sharpness_is_measured() {
use crate::dual_contouring::Clamp;
let field = BoxExact::<f64>::canonical();
let samples = 27u32;
let (lo, hi) = field.domain();
let cell_size = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = RuntimeShape3::new([samples; 3]).expect("valid shape");
let gap = |clamp: Clamp| {
let mut dc = DualContouring::<f64>::new();
dc.set_clamp(clamp);
let mut out = MeshBuffer::<f64>::new();
dc.extract(&field, &shape, lo, cell_size, &mut out)
.expect("extraction");
nearest_vertex(&out, [1.0, 1.0, 1.0]) / cell_size
};
let off = gap(Clamp::None);
let on = gap(Clamp::ToCell);
std::println!(
"measured: A-009 sharpness cost on box_exact at {samples}^3 -> corner gap {off:.4} cells unclamped, {on:.4} cells clamped"
);
assert!(
on < 0.2,
"the clamp must not surrender the corner: {on:.4} cells"
);
}