use alloc::vec;
use alloc::vec::Vec;
use super::{WeldReport, Welder, epsilon_for};
use crate::chunk::{ChunkId, ChunkLayout};
use crate::fields::{ReferenceField, Sphere};
use crate::marching_cubes::MarchingCubes;
use crate::validate::{ValidateConfig, check_determinism, validate_indexed};
use crate::{Error, MeshBuffer, Sdf};
fn points(positions: &[[f64; 3]]) -> MeshBuffer<f64> {
MeshBuffer::<f64> {
positions: positions.to_vec(),
normals: vec![[0.0, 0.0, 1.0]; positions.len()],
indices: Vec::new(),
}
}
#[test]
fn coincident_vertices_collapse_to_the_lowest_index() {
let mut mesh = points(&[[0.0; 3], [1.0, 0.0, 0.0], [0.0; 3], [0.0; 3]]);
mesh.indices = vec![0, 1, 2];
let report = Welder::<f64>::new()
.weld(&mut mesh, 1e-9)
.expect("valid epsilon");
assert_eq!(report.vertices_before, 4);
assert_eq!(report.vertices_after, 2);
assert_eq!(report.vertices_removed(), 2);
assert_eq!(
Welder::<f64>::new().remap().len(),
0,
"fresh welder is empty"
);
assert_eq!(report.triangles_collapsed, 1);
assert_eq!(mesh.triangle_count(), 0);
}
#[test]
fn the_representative_is_the_lowest_index_not_the_nearest() {
let mut mesh = points(&[[0.0; 3], [0.4e-9, 0.0, 0.0], [0.8e-9, 0.0, 0.0]]);
let mut welder = Welder::<f64>::new();
welder.weld(&mut mesh, 1e-9).expect("valid epsilon");
assert_eq!(welder.remap(), &[0, 0, 0]);
assert_eq!(mesh.positions, vec![[0.0; 3]]);
}
#[test]
fn a_chain_of_near_misses_does_not_merge_its_ends() {
let mut mesh = points(&[[0.0; 3], [0.9, 0.0, 0.0], [1.8, 0.0, 0.0]]);
let mut welder = Welder::<f64>::new();
let report = welder.weld(&mut mesh, 1.0).expect("valid epsilon");
assert_eq!(welder.remap(), &[0, 0, 1]);
assert_eq!(report.vertices_after, 2);
assert_eq!(mesh.positions, vec![[0.0; 3], [1.8, 0.0, 0.0]]);
}
#[test]
fn a_vertex_exactly_epsilon_away_welds_and_one_beyond_does_not() {
let mut inside = points(&[[0.0; 3], [1.0, 0.0, 0.0]]);
let report = Welder::<f64>::new()
.weld(&mut inside, 1.0)
.expect("valid epsilon");
assert_eq!(
report.vertices_after, 1,
"exactly epsilon is within epsilon"
);
let mut outside = points(&[[0.0; 3], [1.0 + 1e-12, 0.0, 0.0]]);
let report = Welder::<f64>::new()
.weld(&mut outside, 1.0)
.expect("valid epsilon");
assert!(report.is_noop(), "one ulp beyond epsilon must not weld");
}
#[test]
fn a_mesh_with_nothing_to_weld_is_left_alone() {
let mut mesh = points(&[[0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]);
mesh.indices = vec![0, 1, 2];
let before = mesh.clone();
let report = Welder::<f64>::new()
.weld(&mut mesh, 1e-9)
.expect("valid epsilon");
assert!(report.is_noop());
assert_eq!(mesh.positions, before.positions);
assert_eq!(mesh.normals, before.normals);
assert_eq!(mesh.indices, before.indices);
}
#[test]
fn an_empty_mesh_is_not_an_error() {
let mut mesh = MeshBuffer::<f64>::new();
let report = Welder::<f64>::new()
.weld(&mut mesh, 1e-9)
.expect("valid epsilon");
assert_eq!(report, WeldReport::default());
}
#[test]
fn a_meaningless_epsilon_is_rejected() {
for bad in [0.0f64, -1.0, f64::NAN, f64::INFINITY] {
let mut mesh = points(&[[0.0; 3], [0.0; 3]]);
let error = Welder::<f64>::new()
.weld(&mut mesh, bad)
.expect_err("epsilon must be finite and positive");
assert!(matches!(error, Error::InvalidWeldEpsilon { .. }), "{bad}");
assert_eq!(mesh.vertex_count(), 2, "a rejected weld changes nothing");
}
}
#[test]
fn a_non_finite_position_never_welds() {
let mut mesh = points(&[[f64::NAN, 0.0, 0.0], [f64::NAN, 0.0, 0.0], [0.0; 3]]);
let report = Welder::<f64>::new()
.weld(&mut mesh, 1.0)
.expect("valid epsilon");
assert_eq!(report.vertices_after, 3);
}
#[test]
fn the_remap_covers_every_input_vertex() {
let mut mesh = points(&[[0.0; 3], [5.0, 0.0, 0.0], [0.0; 3], [5.0, 0.0, 0.0]]);
let mut welder = Welder::<f64>::new();
welder.weld(&mut mesh, 1e-9).expect("valid epsilon");
assert_eq!(welder.remap(), &[0, 1, 0, 1]);
assert!(
welder
.remap()
.iter()
.all(|&o| (o as usize) < mesh.vertex_count())
);
}
fn mesh_chunk(
layout: &ChunkLayout<f64>,
field: &impl Sdf<Scalar = f64>,
chunk: ChunkId,
) -> MeshBuffer<f64> {
let shape = layout.sample_shape().expect("valid shape");
let mut out = MeshBuffer::<f64>::new();
MarchingCubes::<f64>::new()
.extract(
field,
&shape,
layout.sample_origin(chunk),
layout.cell_size(),
&mut out,
)
.expect("extraction");
out
}
#[test]
fn a_chunk_seam_welds_at_a_non_power_of_two_spacing() {
let h = 4.0 / 35.0;
let layout = ChunkLayout::<f64>::new(8, h, [-2.0; 3]).expect("valid layout");
let field = Sphere::<f64>::canonical();
let a = ChunkId::new([1, 1, 1]);
let b = a.neighbour(0, 1);
let mut joined = mesh_chunk(&layout, &field, a);
let second = mesh_chunk(&layout, &field, b);
assert!(
!joined.is_empty() && !second.is_empty(),
"both chunks must carry surface or this measures nothing"
);
joined
.append(&second)
.expect("the meshes fit the u32 index space");
let cfg = ValidateConfig::from_cell_size(h).expect("valid cell size");
let before = validate_indexed(&joined.positions, &joined.indices, &cfg);
let mut welder = Welder::<f64>::new();
let report = welder
.weld(&mut joined, epsilon_for(h))
.expect("valid epsilon");
let after = validate_indexed(&joined.positions, &joined.indices, &cfg);
std::println!(
"measured: A-013 seam at h = 4/35 -- {} vertices welded to {}, boundary edges {} -> {}, \
duplicate_vertices {} -> {}, chi {} -> {}",
report.vertices_before,
report.vertices_after,
before.boundary_edges,
after.boundary_edges,
before.duplicate_vertices,
after.duplicate_vertices,
before.euler_characteristic,
after.euler_characteristic,
);
assert!(
report.vertices_removed() > 0,
"the seam was not welded at all"
);
assert!(
after.boundary_edges < before.boundary_edges,
"welding did not close any boundary: {} -> {}",
before.boundary_edges,
after.boundary_edges
);
assert_eq!(after.duplicate_vertices, 0, "{after}");
assert_eq!(after.inconsistently_oriented_edges, 0, "{after}");
}
#[test]
fn a_chunk_seam_welds_at_a_power_of_two_spacing_too() {
let h = 0.125;
let layout = ChunkLayout::<f64>::new(8, h, [-2.0; 3]).expect("valid layout");
let field = Sphere::<f64>::canonical();
let a = ChunkId::new([1, 1, 1]);
let mut joined = mesh_chunk(&layout, &field, a);
let second = mesh_chunk(&layout, &field, a.neighbour(0, 1));
assert!(!joined.is_empty() && !second.is_empty());
joined
.append(&second)
.expect("the meshes fit the u32 index space");
let cfg = ValidateConfig::from_cell_size(h).expect("valid cell size");
let mut welder = Welder::<f64>::new();
let report = welder
.weld(&mut joined, epsilon_for(h))
.expect("valid epsilon");
let after = validate_indexed(&joined.positions, &joined.indices, &cfg);
assert!(report.vertices_removed() > 0);
assert_eq!(after.duplicate_vertices, 0, "{after}");
}
#[test]
fn welding_is_deterministic() {
let h = 4.0 / 35.0;
let layout = ChunkLayout::<f64>::new(8, h, [-2.0; 3]).expect("valid layout");
let field = Sphere::<f64>::canonical();
let a = ChunkId::new([1, 1, 1]);
let b = a.neighbour(0, 1);
let report = check_determinism(|out: &mut MeshBuffer<f64>| {
let first = mesh_chunk(&layout, &field, a);
let second = mesh_chunk(&layout, &field, b);
out.append(&first)
.expect("the meshes fit the u32 index space");
out.append(&second)
.expect("the meshes fit the u32 index space");
Welder::<f64>::new()
.weld(out, epsilon_for(h))
.expect("valid epsilon");
});
assert!(report.is_deterministic(), "{report}");
}
#[test]
fn a_reused_welder_gives_the_same_answer_as_a_fresh_one() {
let h = 4.0 / 35.0;
let layout = ChunkLayout::<f64>::new(8, h, [-2.0; 3]).expect("valid layout");
let field = Sphere::<f64>::canonical();
let eps = epsilon_for(h);
let build = || {
let a = ChunkId::new([1, 1, 1]);
let mut m = mesh_chunk(&layout, &field, a);
m.append(&mesh_chunk(&layout, &field, a.neighbour(0, 1)))
.expect("the meshes fit the u32 index space");
m
};
let mut reused = Welder::<f64>::new();
let mut other = build();
other
.append(&build())
.expect("the meshes fit the u32 index space");
reused.weld(&mut other, eps).expect("valid epsilon");
let mut from_reused = build();
let a = reused.weld(&mut from_reused, eps).expect("valid epsilon");
let mut from_fresh = build();
let b = Welder::<f64>::new()
.weld(&mut from_fresh, eps)
.expect("valid epsilon");
assert_eq!(a, b);
assert_eq!(from_reused.positions, from_fresh.positions);
assert_eq!(from_reused.indices, from_fresh.indices);
}
#[test]
fn the_validator_bounds_the_weld_rather_than_predicting_it() {
let h = 4.0 / 35.0;
let layout = ChunkLayout::<f64>::new(8, h, [-2.0; 3]).expect("valid layout");
let field = Sphere::<f64>::canonical();
let a = ChunkId::new([1, 1, 1]);
let mut joined = mesh_chunk(&layout, &field, a);
joined
.append(&mesh_chunk(&layout, &field, a.neighbour(0, 1)))
.expect("the meshes fit the u32 index space");
let cfg = ValidateConfig::from_cell_size(h).expect("valid cell size");
let counted = validate_indexed(&joined.positions, &joined.indices, &cfg).duplicate_vertices;
let removed = Welder::<f64>::new()
.weld(&mut joined, epsilon_for(h))
.expect("valid epsilon")
.vertices_removed();
std::println!("measured: seam -- validator counted {counted}, weld removed {removed}");
assert_eq!(counted as usize, removed, "no chains at a seam");
let mut chain = points(&[[0.0; 3], [0.9, 0.0, 0.0], [1.8, 0.0, 0.0]]);
let cfg = ValidateConfig::from_cell_size(1.0 / ValidateConfig::WELD_EPSILON_REL)
.expect("valid cell size");
let counted = validate_indexed(&chain.positions, &chain.indices, &cfg).duplicate_vertices;
let removed = Welder::<f64>::new()
.weld(&mut chain, 1.0)
.expect("valid epsilon")
.vertices_removed();
std::println!("measured: chain -- validator counted {counted}, weld removed {removed}");
assert_eq!(counted, 2, "the validator counts 1 and 2 as duplicates");
assert_eq!(removed, 1, "the weld only removes 1");
}
#[test]
fn a_whole_volume_mesh_welds_only_where_a_sample_sits_on_the_surface() {
let mut found: Vec<(&str, u32, usize, usize)> = Vec::new();
crate::for_each_reference_field!(f64, |name, field| {
for samples in [17u32, 25, 33] {
let (lo, hi) = field.domain();
let h = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = crate::RuntimeShape3::new([samples; 3]).expect("valid shape");
let mut mesh = MeshBuffer::<f64>::new();
MarchingCubes::<f64>::new()
.extract(&field, &shape, lo, h, &mut mesh)
.expect("extraction");
if mesh.is_empty() {
continue;
}
let report = Welder::<f64>::new()
.weld(&mut mesh, epsilon_for(h))
.expect("valid epsilon");
if !report.is_noop() {
found.push((
name,
samples,
report.vertices_removed(),
report.triangles_collapsed,
));
}
}
});
std::println!("measured: whole-volume welds (field, n, vertices, triangles) = {found:?}");
assert_eq!(
found,
vec![
("sphere", 25, 48, 96),
("gyroid", 17, 2, 4),
("gyroid", 25, 2, 4),
("gyroid", 33, 2, 4),
("fbm_terrain", 33, 1, 2),
("noise_cavity", 17, 30, 54),
("noise_cavity", 25, 136, 266),
("noise_cavity", 33, 35, 68),
]
);
}
#[test]
fn the_one_policy_is_one_number_however_it_is_reached() {
use crate::validate::ValidateConfig;
for h in [1.0, 0.5, 0.125, 0.1, 1.0 / 3.0, 0.0625, 7.5] {
let cfg = ValidateConfig::from_cell_size(h).expect("a valid spacing");
assert_eq!(
epsilon_for(h).to_bits(),
cfg.weld_epsilon().to_bits(),
"h = {h}: the welder and the validator disagree about coincidence"
);
}
assert_eq!(
epsilon_for(2.0f64).to_bits(),
(2.0 * epsilon_for(1.0f64)).to_bits()
);
assert_eq!(
epsilon_for(1.0f32).to_bits(),
(ValidateConfig::WELD_EPSILON_REL as f32).to_bits()
);
}
fn coincident_quad() -> MeshBuffer<f64> {
let mut mesh = MeshBuffer::<f64>::default();
for _ in 0..4 {
mesh.positions.push([1.0, 2.0, 3.0]);
mesh.normals.push([0.0, 1.0, 0.0]);
}
mesh.positions.push([9.0, 0.0, 0.0]);
mesh.normals.push([0.0, 1.0, 0.0]);
mesh.positions.push([0.0, 9.0, 0.0]);
mesh.normals.push([0.0, 1.0, 0.0]);
mesh.indices.extend_from_slice(&[0, 4, 5, 1, 5, 4]);
mesh
}
#[test]
fn an_empty_key_slice_is_the_unconditional_weld() {
let mut a = coincident_quad();
let mut b = coincident_quad();
let ra = Welder::default().weld(&mut a, 1e-6).expect("weld");
let rb = Welder::default()
.weld_split_by(&mut b, 1e-6, &[])
.expect("weld");
assert_eq!(ra.vertices_after, rb.vertices_after);
assert_eq!(a.positions, b.positions);
assert_eq!(a.indices, b.indices);
assert_eq!(ra.vertices_before - ra.vertices_after, 3);
}
#[test]
fn a_key_partitions_a_class_into_complete_subclasses() {
let mut mesh = coincident_quad();
let keys = [7u64, 7, 9, 9, 0, 1];
let report = Welder::default()
.weld_split_by(&mut mesh, 1e-6, &keys)
.expect("weld");
assert_eq!(report.vertices_before, 6);
assert_eq!(report.vertices_after, 4);
}
#[test]
fn distinct_keys_everywhere_refuse_every_merge() {
let mut mesh = coincident_quad();
let keys = [1u64, 2, 3, 4, 5, 6];
let report = Welder::default()
.weld_split_by(&mut mesh, 1e-6, &keys)
.expect("weld");
assert_eq!(
report.vertices_after, report.vertices_before,
"every vertex is its own class, so nothing may merge"
);
}
#[test]
fn one_key_everywhere_is_the_unconditional_weld() {
let mut split = coincident_quad();
let mut plain = coincident_quad();
let keys = [42u64; 6];
let a = Welder::default()
.weld_split_by(&mut split, 1e-6, &keys)
.expect("weld");
let b = Welder::default().weld(&mut plain, 1e-6).expect("weld");
assert_eq!(a.vertices_after, b.vertices_after);
assert_eq!(split.positions, plain.positions);
assert_eq!(split.indices, plain.indices);
}
#[test]
fn a_key_slice_of_the_wrong_length_is_refused() {
let mut mesh = coincident_quad();
let err = Welder::default()
.weld_split_by(&mut mesh, 1e-6, &[1u64, 2, 3])
.expect_err("three keys for six vertices");
assert!(matches!(
err,
crate::Error::WeldKeyLengthMismatch {
keys: 3,
vertices: 6
}
));
assert_eq!(mesh.positions.len(), 6);
}
#[test]
fn splitting_on_a_key_is_deterministic() {
let keys = [7u64, 7, 9, 9, 0, 1];
let mut first = coincident_quad();
let report = Welder::default()
.weld_split_by(&mut first, 1e-6, &keys)
.expect("weld");
for _ in 0..64 {
let mut again = coincident_quad();
let r = Welder::default()
.weld_split_by(&mut again, 1e-6, &keys)
.expect("weld");
assert_eq!(r.vertices_after, report.vertices_after);
assert_eq!(again.positions, first.positions);
assert_eq!(again.indices, first.indices);
}
}