#![forbid(unsafe_code)]
use std::collections::HashMap;
use std::f64::consts::TAU;
use delaunay::core::vertex::{Vertex, VertexBuilder};
use delaunay::geometry::kernel::RobustKernel;
use delaunay::geometry::point::Point;
use delaunay::geometry::traits::coordinate::Coordinate;
use delaunay::prelude::triangulation::repair::{DelaunayRepairError, TopologyGuarantee};
use delaunay::topology::characteristics::euler::{count_simplices, euler_characteristic};
use delaunay::topology::traits::topological_space::{GlobalTopology, ToroidalConstructionMode};
use delaunay::triangulation::builder::{DelaunayTriangulationBuilder, ExplicitConstructionError};
use delaunay::triangulation::delaunay::{
ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationConstructionError,
InsertionOrderStrategy,
};
use delaunay::vertex;
#[test]
fn test_builder_euclidean_matches_new_2d() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let dt_new = DelaunayTriangulation::new(&vertices).expect("new() should succeed");
let dt_builder = DelaunayTriangulationBuilder::new(&vertices)
.build::<()>()
.expect("builder should succeed");
assert_eq!(dt_new.number_of_vertices(), dt_builder.number_of_vertices());
assert_eq!(dt_new.number_of_cells(), dt_builder.number_of_cells());
assert_eq!(dt_new.dim(), dt_builder.dim());
}
#[test]
fn test_builder_euclidean_3d() {
let vertices = vec![
vertex!([0.0_f64, 0.0, 0.0]),
vertex!([1.0, 0.0, 0.0]),
vertex!([0.0, 1.0, 0.0]),
vertex!([0.0, 0.0, 1.0]),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.build::<()>()
.expect("3D build should succeed");
assert_eq!(dt.number_of_vertices(), 4);
assert_eq!(dt.dim(), 3);
assert!(dt.validate().is_ok(), "Level 1-4 validation should pass");
}
#[test]
fn test_builder_topology_guarantee_propagated() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.topology_guarantee(TopologyGuarantee::Pseudomanifold)
.build::<()>()
.expect("build should succeed");
assert_eq!(dt.topology_guarantee(), TopologyGuarantee::Pseudomanifold);
}
#[test]
fn test_builder_custom_construction_options() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let opts = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input);
let dt = DelaunayTriangulationBuilder::new(&vertices)
.construction_options(opts)
.build::<()>()
.expect("build should succeed");
assert_eq!(dt.number_of_vertices(), 3);
assert!(dt.validate().is_ok());
}
#[test]
fn test_builder_convenience() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let dt = DelaunayTriangulation::builder(&vertices)
.build::<()>()
.expect("builder() convenience method should succeed");
assert_eq!(dt.number_of_vertices(), 3);
assert!(dt.validate().is_ok());
}
#[test]
fn test_builder_toroidal_convenience() {
let vertices = vec![
vertex!([0.2_f64, 0.3]),
vertex!([1.8, 0.1]), vertex!([0.5, 0.7]),
vertex!([-0.4, 0.9]), ];
let dt = DelaunayTriangulation::builder(&vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("toroidal builder should succeed");
assert_eq!(dt.number_of_vertices(), 4);
assert!(dt.as_triangulation().validate().is_ok());
}
#[test]
fn test_builder_toroidal_canonicalizes_coordinates() {
let canonical_vertices = vec![
vertex!([0.2_f64, 0.3]),
vertex!([0.8, 0.1]),
vertex!([0.5, 0.7]),
vertex!([0.6, 0.9]),
];
let shifted_vertices = vec![
vertex!([2.2_f64, 3.3]), vertex!([-0.2, 1.1]), vertex!([1.5, 0.7]), vertex!([-0.4, 2.9]), ];
let dt_canonical = DelaunayTriangulationBuilder::new(&canonical_vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("canonical build should succeed");
let dt_shifted = DelaunayTriangulationBuilder::new(&shifted_vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("shifted build should succeed");
assert_eq!(
dt_canonical.number_of_vertices(),
dt_shifted.number_of_vertices(),
"Both inputs should produce the same number of vertices after canonicalization"
);
assert_eq!(
dt_canonical.number_of_cells(),
dt_shifted.number_of_cells(),
"Both inputs should produce the same number of cells after canonicalization"
);
}
#[test]
fn test_builder_toroidal_validates_2d() {
let vertices = vec![
vertex!([0.2_f64, 0.3]),
vertex!([0.8, 0.1]),
vertex!([0.5, 0.7]),
vertex!([0.1, 0.9]),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("toroidal build should succeed");
assert!(
dt.as_triangulation().validate().is_ok(),
"Levels 1-3 validation should pass for toroidally-built triangulation"
);
}
#[test]
fn test_builder_toroidal_delaunay_property_valid_2d() {
let vertices = vec![
vertex!([0.2_f64, 0.3]),
vertex!([0.8, 0.1]),
vertex!([0.5, 0.7]),
vertex!([0.1, 0.9]),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("toroidal build should succeed");
assert!(
dt.validate().is_ok(),
"Full Levels 1-4 validation should pass for toroidally-built triangulation"
);
}
#[test]
fn test_builder_toroidal_2d_euler_dimension() {
let vertices = vec![
vertex!([0.2_f64, 0.3]),
vertex!([0.8, 0.1]),
vertex!([0.5, 0.7]),
vertex!([0.1, 0.9]),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("build should succeed");
assert_eq!(
dt.dim(),
2,
"Four 2D points should produce a 2-dimensional triangulation"
);
}
#[test]
fn test_builder_toroidal_matches_euclidean_on_canonical_input() {
let vertices = vec![
vertex!([0.1_f64, 0.2]),
vertex!([0.8, 0.3]),
vertex!([0.4, 0.9]),
];
let dt_euclidean = DelaunayTriangulationBuilder::new(&vertices)
.build::<()>()
.expect("euclidean build should succeed");
let dt_toroidal = DelaunayTriangulationBuilder::new(&vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("toroidal build should succeed");
assert_eq!(
dt_euclidean.number_of_vertices(),
dt_toroidal.number_of_vertices()
);
assert_eq!(
dt_euclidean.number_of_cells(),
dt_toroidal.number_of_cells()
);
}
#[test]
fn test_builder_toroidal_larger_point_set_2d() {
let vertices = vec![
vertex!([0.1_f64, 0.2]),
vertex!([0.6, 0.1]),
vertex!([0.9, 0.4]),
vertex!([0.7, 0.8]),
vertex!([0.3, 0.7]),
vertex!([0.48, 0.52]),
vertex!([0.15, 0.85]),
vertex!([0.8, 0.65]),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal([1.0, 1.0])
.build::<()>()
.expect("larger toroidal build should succeed");
assert_eq!(dt.number_of_vertices(), 8);
assert!(dt.validate().is_ok(), "Full validation should pass");
}
#[test]
fn test_builder_toroidal_robust_kernel_3d() {
let vertices = vec![
vertex!([0.2_f64, 0.3, 0.4]),
vertex!([0.8, 0.1, 0.2]),
vertex!([0.5, 0.7, 0.6]),
vertex!([0.1, 0.9, 0.3]),
vertex!([0.6, 0.4, 0.8]),
];
let kernel = RobustKernel::new();
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal([1.0, 1.0, 1.0])
.build_with_kernel::<_, ()>(&kernel)
.expect("toroidal robust kernel 3D build should succeed");
assert_eq!(dt.number_of_vertices(), 5);
assert!(dt.validate().is_ok());
}
fn toroidal_periodic_vertices<const D: usize>() -> Vec<Vertex<f64, (), D>> {
assert!((2..=5).contains(&D));
let multipliers = [
0.618_033_988_749_894_8,
0.414_213_562_373_095_03,
0.732_050_807_568_877_2,
0.236_067_977_499_789_8,
0.324_717_957_244_746,
];
(0..(2 * D + 3))
.map(|index| {
let mut coords = [0.0_f64; D];
let index_f64 = f64::from(u32::try_from(index).expect("test index fits in u32"));
for axis in 0..D {
let axis_f64 = f64::from(u32::try_from(axis).expect("test axis fits in u32"));
let stride = 0.037_f64.mul_add(axis_f64 + 1.0, multipliers[axis]);
let phase = (index_f64 + 1.0) * stride;
coords[axis] = 0.9_f64.mul_add(phase.fract(), 0.05);
}
vertex!(coords)
})
.collect()
}
fn build_toroidal_periodic_triangulation<const D: usize>()
-> DelaunayTriangulation<RobustKernel<f64>, (), (), D> {
let vertices = toroidal_periodic_vertices::<D>();
let expected_vertices = vertices.len();
let kernel = RobustKernel::new();
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal_periodic([1.0_f64; D])
.build_with_kernel::<_, ()>(&kernel)
.expect("periodic build should succeed");
assert_eq!(dt.number_of_vertices(), expected_vertices);
assert!(
dt.global_topology().is_toroidal(),
"global_topology should be Toroidal"
);
assert!(
dt.global_topology().is_periodic(),
"global_topology should use periodic image-point construction"
);
dt
}
#[test]
fn test_builder_toroidal_periodic_chi_zero_2d() {
let dt = build_toroidal_periodic_triangulation::<2>();
assert!(
dt.tds().is_valid().is_ok(),
"TDS structural validity should pass for periodic triangulation"
);
let counts = count_simplices(dt.tds()).unwrap();
let chi = euler_characteristic(&counts);
assert_eq!(
chi, 0,
"Euler characteristic of periodic 2D triangulation must be 0 (torus)"
);
}
macro_rules! gen_toroidal_periodic_validation_test {
($dim:literal, $label:ident, $run_level4:expr $(, #[$attr:meta])?) => {
pastey::paste! {
#[test]
$(#[$attr])?
fn [<test_builder_toroidal_periodic_validate_ $label _ $dim d>]() {
let dt = build_toroidal_periodic_triangulation::<$dim>();
let topology_result = dt.as_triangulation().validate();
assert!(
topology_result.is_ok(),
"PLManifold Levels 1-3 validate() should pass for toroidal_periodic: {:?}",
topology_result.err()
);
if $run_level4 {
let level4_result = dt.validate();
assert!(
level4_result.is_ok(),
"Level 1-4 validate() should pass for toroidal_periodic: {:?}",
level4_result.err()
);
}
}
}
};
}
gen_toroidal_periodic_validation_test!(2, levels_1_to_4, true);
#[test]
#[ignore = "Slow (>60s): periodic 3D Level 4 scans image-point quotient cells"]
fn test_builder_periodic_topology_level4_smoke_3d() {
let vertices = vec![
vertex!([0.2_f64, 0.3, 0.4]),
vertex!([0.8, 0.1, 0.2]),
vertex!([0.5, 0.7, 0.6]),
vertex!([0.1, 0.9, 0.3]),
vertex!([0.6, 0.4, 0.8]),
vertex!([0.3, 0.5, 0.9]),
vertex!([0.9, 0.2, 0.6]),
];
let kernel = RobustKernel::new();
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal_periodic([1.0_f64; 3])
.build_with_kernel::<_, ()>(&kernel)
.expect("compact periodic 3D build should succeed");
assert_eq!(dt.number_of_vertices(), vertices.len());
assert!(
dt.tds().is_valid().is_ok(),
"TDS structural validity should pass for compact periodic 3D"
);
assert!(
dt.global_topology().is_periodic(),
"global_topology should use periodic image-point construction"
);
assert!(
dt.cells().all(|(_, cell)| {
cell.periodic_vertex_offsets()
.is_some_and(|offsets| offsets.len() == cell.number_of_vertices())
}),
"periodic image-point construction should populate per-cell periodic offsets"
);
match dt.is_delaunay_via_flips() {
Ok(()) => {}
Err(DelaunayRepairError::PostconditionFailed { message }) => {
assert!(
!message.contains("predicate failed in strict mode")
&& !message.contains("periodic offset")
&& !message.contains("cannot align periodic vertex"),
"periodic Level 4 should evaluate lifted predicates with populated offsets: {message}"
);
}
Err(err) => panic!("periodic Level 4 validation returned an unexpected error: {err:?}"),
}
}
gen_toroidal_periodic_validation_test!(
3,
levels_1_to_4,
true,
#[ignore = "Slow (>60s): periodic 3D expands to 3^D image points; run with --ignored"]
);
gen_toroidal_periodic_validation_test!(
4,
levels_1_to_3,
false,
#[ignore = "Slow: periodic 4D expands to 3^D image points; run with --ignored"]
);
gen_toroidal_periodic_validation_test!(
5,
levels_1_to_3,
false,
#[ignore = "Slow: periodic 5D expands to 3^D image points; run with --ignored"]
);
#[test]
fn test_explicit_toroidal_heawood_torus_validates() {
let vertices: Vec<_> = (0..7)
.map(|i| {
let angle = TAU * f64::from(i) / 7.0;
vertex!([angle.cos(), angle.sin()])
})
.collect();
let mut cells: Vec<Vec<usize>> = Vec::with_capacity(14);
for i in 0..7 {
cells.push(vec![i, (i + 1) % 7, (i + 3) % 7]);
cells.push(vec![i, (i + 2) % 7, (i + 3) % 7]);
}
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.global_topology(GlobalTopology::Toroidal {
domain: [2.0, 2.0],
mode: ToroidalConstructionMode::Explicit,
})
.build::<()>()
.expect("explicit toroidal torus build should succeed");
assert_eq!(dt.number_of_vertices(), 7);
assert_eq!(dt.number_of_cells(), 14);
assert!(
dt.global_topology().is_toroidal(),
"global_topology should be Toroidal"
);
assert!(
dt.tds().is_valid().is_ok(),
"TDS structural validity (Level 1-2) should pass"
);
let counts = count_simplices(dt.tds()).unwrap();
let chi = euler_characteristic(&counts);
assert_eq!(chi, 0, "Euler characteristic of explicit torus must be 0");
}
#[test]
fn test_explicit_toroidal_torus_euler_mismatch_without_override() {
let vertices: Vec<_> = (0..7)
.map(|i| {
let angle = TAU * f64::from(i) / 7.0;
vertex!([angle.cos(), angle.sin()])
})
.collect();
let mut cells: Vec<Vec<usize>> = Vec::with_capacity(14);
for i in 0..7 {
cells.push(vec![i, (i + 1) % 7, (i + 3) % 7]);
cells.push(vec![i, (i + 2) % 7, (i + 3) % 7]);
}
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect_err("explicit torus without Toroidal metadata should fail Euler validation");
let msg = err.to_string();
assert!(
msg.contains("Euler characteristic") || msg.contains("topology validation failed"),
"Error should mention topology/Euler failure: {msg}"
);
}
#[test]
#[ignore = "Slow (>60s): periodic 3D expands to 3^D image points; run with --ignored"]
fn test_builder_toroidal_periodic_3d_success() {
let vertices = vec![
vertex!([0.1_f64, 0.2, 0.3]),
vertex!([0.4, 0.7, 0.1]),
vertex!([0.7, 0.3, 0.8]),
vertex!([0.2, 0.9, 0.5]),
vertex!([0.8, 0.6, 0.2]),
vertex!([0.5, 0.1, 0.7]),
vertex!([0.3, 0.5, 0.9]),
vertex!([0.6, 0.8, 0.4]),
vertex!([0.9, 0.2, 0.6]),
];
let n = vertices.len();
let kernel = RobustKernel::new();
let dt = DelaunayTriangulationBuilder::new(&vertices)
.toroidal_periodic([1.0_f64, 1.0, 1.0])
.build_with_kernel::<_, ()>(&kernel)
.expect("periodic 3D build should succeed");
assert_eq!(dt.number_of_vertices(), n);
assert!(
dt.tds().is_valid().is_ok(),
"TDS structural validity should pass for 3D periodic triangulation"
);
}
#[test]
fn test_builder_from_vertices_f32() {
let vertices: Vec<Vertex<f32, (), 2>> = vec![
VertexBuilder::default()
.point(Point::new([0.0_f32, 0.0]))
.build()
.unwrap(),
VertexBuilder::default()
.point(Point::new([1.0_f32, 0.0]))
.build()
.unwrap(),
VertexBuilder::default()
.point(Point::new([0.0_f32, 1.0]))
.build()
.unwrap(),
];
let dt = DelaunayTriangulationBuilder::from_vertices(&vertices)
.build::<()>()
.expect("f32 from_vertices build should succeed");
assert_eq!(dt.number_of_vertices(), 3);
assert_eq!(dt.number_of_cells(), 1);
}
#[test]
fn test_explicit_2d_two_triangle_quad() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([1.0, 1.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 2], vec![0, 2, 3]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("explicit 2D build should succeed");
assert_eq!(dt.number_of_vertices(), 4);
assert_eq!(dt.number_of_cells(), 2);
assert!(
dt.tds().is_valid().is_ok(),
"TDS should be structurally valid"
);
let mut neighbor_count = 0;
for (_, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for n in neighbors {
if n.is_some() {
neighbor_count += 1;
}
}
}
}
assert!(
neighbor_count >= 2,
"Shared facet should produce neighbor pointers"
);
}
#[test]
fn test_explicit_normalizes_incoherent_cell_order() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([1.0, 1.0]),
vertex!([0.0, 1.0]),
];
let mut cells = vec![vec![0, 1, 2], vec![0, 2, 3]];
cells[1].swap(0, 1);
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("explicit build should normalize incoherent cell ordering");
assert!(
dt.tds().is_valid().is_ok(),
"builder should canonicalize incoherent cell orderings into a valid TDS"
);
}
#[test]
fn test_explicit_3d_two_tetrahedra() {
let vertices = vec![
vertex!([0.0_f64, 0.0, 0.0]),
vertex!([1.0, 0.0, 0.0]),
vertex!([0.0, 1.0, 0.0]),
vertex!([0.0, 0.0, 1.0]),
vertex!([1.0, 1.0, 1.0]),
];
let cells = vec![vec![0, 1, 2, 3], vec![0, 1, 2, 4]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("explicit 3D build should succeed");
assert_eq!(dt.number_of_vertices(), 5);
assert_eq!(dt.number_of_cells(), 2);
assert!(
dt.tds().is_valid().is_ok(),
"TDS should be structurally valid"
);
}
#[test]
fn test_explicit_round_trip_3d() {
let vertices = vec![
vertex!([0.0_f64, 0.0, 0.0]),
vertex!([1.0, 0.0, 0.0]),
vertex!([0.0, 1.0, 0.0]),
vertex!([0.0, 0.0, 1.0]),
vertex!([1.0, 1.0, 1.0]),
];
let dt_original = DelaunayTriangulation::new(&vertices).expect("Delaunay build should succeed");
let original_vertex_count = dt_original.number_of_vertices();
let original_cell_count = dt_original.number_of_cells();
let tds = dt_original.tds();
let vertex_keys: Vec<_> = tds.vertex_keys().collect();
let key_to_index: HashMap<_, _> = vertex_keys
.iter()
.enumerate()
.map(|(idx, &vk)| (vk, idx))
.collect();
let extracted_vertices: Vec<_> = vertex_keys
.iter()
.map(|&vk| *tds.get_vertex_by_key(vk).unwrap())
.collect();
let mut cell_specs: Vec<Vec<usize>> = Vec::new();
for (_, cell) in tds.cells() {
let spec: Vec<usize> = cell.vertices().iter().map(|vk| key_to_index[vk]).collect();
cell_specs.push(spec);
}
let dt_reconstructed =
DelaunayTriangulationBuilder::from_vertices_and_cells(&extracted_vertices, &cell_specs)
.build::<()>()
.expect("explicit 3D reconstruction should succeed");
assert_eq!(dt_reconstructed.number_of_vertices(), original_vertex_count);
assert_eq!(dt_reconstructed.number_of_cells(), original_cell_count);
assert!(
dt_reconstructed.tds().is_valid().is_ok(),
"Reconstructed 3D TDS should be structurally valid"
);
}
#[test]
fn test_explicit_round_trip_2d() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
vertex!([1.0, 1.0]),
];
let dt_original = DelaunayTriangulation::new(&vertices).expect("Delaunay build should succeed");
let original_vertex_count = dt_original.number_of_vertices();
let original_cell_count = dt_original.number_of_cells();
let tds = dt_original.tds();
let vertex_keys: Vec<_> = tds.vertex_keys().collect();
let key_to_index: HashMap<_, _> = vertex_keys
.iter()
.enumerate()
.map(|(idx, &vk)| (vk, idx))
.collect();
let extracted_vertices: Vec<_> = vertex_keys
.iter()
.map(|&vk| *tds.get_vertex_by_key(vk).unwrap())
.collect();
let mut cell_specs: Vec<Vec<usize>> = Vec::new();
for (_, cell) in tds.cells() {
let spec: Vec<usize> = cell.vertices().iter().map(|vk| key_to_index[vk]).collect();
cell_specs.push(spec);
}
let dt_reconstructed =
DelaunayTriangulationBuilder::from_vertices_and_cells(&extracted_vertices, &cell_specs)
.build::<()>()
.expect("explicit reconstruction should succeed");
assert_eq!(dt_reconstructed.number_of_vertices(), original_vertex_count);
assert_eq!(dt_reconstructed.number_of_cells(), original_cell_count);
assert!(
dt_reconstructed.tds().is_valid().is_ok(),
"Reconstructed TDS should be structurally valid"
);
}
#[test]
fn test_explicit_error_empty_cells() {
let vertices = vec![vertex!([0.0_f64, 0.0]), vertex!([1.0, 0.0])];
let cells: Vec<Vec<usize>> = vec![];
let result =
DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells).build::<()>();
assert!(result.is_err(), "Empty cells should produce an error");
}
#[test]
fn test_explicit_error_wrong_arity() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1]];
let result =
DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells).build::<()>();
assert!(result.is_err(), "Wrong arity should produce an error");
}
#[test]
fn test_explicit_error_index_out_of_bounds() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 99]];
let result =
DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells).build::<()>();
assert!(
result.is_err(),
"Out-of-bounds index should produce an error"
);
}
#[test]
fn test_explicit_error_duplicate_vertex_in_cell() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 1]];
let result =
DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells).build::<()>();
assert!(result.is_err(), "Duplicate vertex should produce an error");
}
#[test]
fn test_explicit_error_toroidal_incompatible() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 2]];
let result = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.toroidal([1.0, 1.0])
.build::<()>();
assert!(
result.is_err(),
"Toroidal + explicit cells should produce an error"
);
}
#[test]
fn test_explicit_2d_single_triangle() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 2]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("single triangle should succeed");
assert_eq!(dt.number_of_vertices(), 3);
assert_eq!(dt.number_of_cells(), 1);
assert!(dt.tds().is_valid().is_ok());
}
#[test]
fn test_explicit_3d_single_tetrahedron() {
let vertices = vec![
vertex!([0.0_f64, 0.0, 0.0]),
vertex!([1.0, 0.0, 0.0]),
vertex!([0.0, 1.0, 0.0]),
vertex!([0.0, 0.0, 1.0]),
];
let cells = vec![vec![0, 1, 2, 3]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("single tetrahedron should succeed");
assert_eq!(dt.number_of_vertices(), 4);
assert_eq!(dt.number_of_cells(), 1);
assert!(dt.tds().is_valid().is_ok());
}
#[test]
fn test_explicit_non_delaunay_mesh() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([4.0, 0.0]),
vertex!([4.0, 2.0]),
vertex!([1.0, 2.0]),
];
let cells = vec![vec![0, 1, 2], vec![0, 2, 3]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("non-Delaunay mesh should build successfully (Levels 1-3)");
assert_eq!(dt.number_of_vertices(), 4);
assert_eq!(dt.number_of_cells(), 2);
assert!(
dt.tds().is_valid().is_ok(),
"TDS structural validity (Levels 1-3) should pass"
);
assert!(
dt.is_valid().is_err(),
"Delaunay property (Level 4) should fail for non-Delaunay connectivity"
);
}
#[test]
fn test_explicit_topology_guarantee_propagated() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 2]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.topology_guarantee(TopologyGuarantee::Pseudomanifold)
.build::<()>()
.expect("build should succeed");
assert_eq!(dt.topology_guarantee(), TopologyGuarantee::Pseudomanifold);
}
#[test]
fn test_explicit_preserves_vertex_data() {
let vertices: Vec<Vertex<f64, i32, 2>> = vec![
VertexBuilder::default()
.point(Point::new([0.0, 0.0]))
.data(10_i32)
.build()
.unwrap(),
VertexBuilder::default()
.point(Point::new([1.0, 0.0]))
.data(20_i32)
.build()
.unwrap(),
VertexBuilder::default()
.point(Point::new([0.0, 1.0]))
.data(30_i32)
.build()
.unwrap(),
];
let cells = vec![vec![0, 1, 2]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("explicit build with vertex data should succeed");
let mut data: Vec<i32> = dt
.vertices()
.filter_map(|(_, v)| v.data().copied())
.collect();
data.sort_unstable();
assert_eq!(
data,
vec![10, 20, 30],
"Vertex data must survive explicit construction"
);
}
#[test]
fn test_explicit_validate_delaunay_mesh() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.5, 0.866_025_403_784_438_6]),
];
let cells = vec![vec![0, 1, 2]];
let dt = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.expect("build should succeed");
assert!(
dt.validate().is_ok(),
"Full Levels 1-4 validation should pass for Delaunay-compatible explicit mesh"
);
}
#[test]
fn test_explicit_unreferenced_vertices_rejected() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
vertex!([5.0, 5.0]), ];
let cells = vec![vec![0, 1, 2]];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::ValidationFailed { .. }
)
),
"Unreferenced vertices should produce ValidationFailed, got: {err}"
);
}
#[test]
fn test_explicit_error_variant_empty_cells() {
let vertices = vec![vertex!([0.0_f64, 0.0])];
let cells: Vec<Vec<usize>> = vec![];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::EmptyCells
)
),
"Expected ExplicitConstruction(EmptyCells), got: {err}"
);
}
#[test]
fn test_explicit_error_variant_wrong_arity() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1]];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::InvalidCellArity {
cell_index: 0,
actual: 2,
expected: 3
}
)
),
"Expected InvalidCellArity, got: {err}"
);
}
#[test]
fn test_explicit_error_variant_non_manifold_facet() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
vertex!([1.0, 1.0]),
vertex!([0.5, -1.0]),
];
let cells = vec![vec![0, 1, 2], vec![0, 1, 3], vec![0, 1, 4]];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::ValidationFailed { .. }
)
),
"Expected ExplicitConstruction(ValidationFailed), got: {err}"
);
}
#[test]
fn test_explicit_error_variant_duplicate_vertex_in_cell() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 1]];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::DuplicateVertexInCell { cell_index: 0 }
)
),
"Expected DuplicateVertexInCell, got: {err}"
);
}
#[test]
fn test_explicit_error_variant_incompatible_topology() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 2]];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.toroidal([1.0, 1.0])
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::IncompatibleTopology
)
),
"Expected IncompatibleTopology, got: {err}"
);
}
#[test]
fn test_explicit_error_variant_index_out_of_bounds() {
let vertices = vec![
vertex!([0.0_f64, 0.0]),
vertex!([1.0, 0.0]),
vertex!([0.0, 1.0]),
];
let cells = vec![vec![0, 1, 99]];
let err = DelaunayTriangulationBuilder::from_vertices_and_cells(&vertices, &cells)
.build::<()>()
.unwrap_err();
assert!(
matches!(
err,
DelaunayTriangulationConstructionError::ExplicitConstruction(
ExplicitConstructionError::IndexOutOfBounds {
cell_index: 0,
vertex_index: 99,
bound: 3,
}
)
),
"Expected IndexOutOfBounds, got: {err}"
);
}