diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a30ffc5..9b89b03f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Validate toroidal domains at parse boundaries [#437](https://github.com/acgetchell/delaunay/pull/437) [#450](https://github.com/acgetchell/delaunay/pull/450) - Add vertex construction macro [#469](https://github.com/acgetchell/delaunay/pull/469) - Add embedded triangulation validation layer [#449](https://github.com/acgetchell/delaunay/pull/449) [#481](https://github.com/acgetchell/delaunay/pull/481) +- Add Pachner feasibility checks [#489](https://github.com/acgetchell/delaunay/pull/489) - Box nested FlipError payloads [#406](https://github.com/acgetchell/delaunay/pull/406) [#435](https://github.com/acgetchell/delaunay/pull/435) - Adopt la-stack 0.4.3 API [#424](https://github.com/acgetchell/delaunay/pull/424) [#438](https://github.com/acgetchell/delaunay/pull/438) - Require refined generator and ordering parameters [#439](https://github.com/acgetchell/delaunay/pull/439) @@ -29,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Merged Pull Requests +- Add Pachner feasibility checks [#489](https://github.com/acgetchell/delaunay/pull/489) +- Make targeted PL repair failure-atomic [#488](https://github.com/acgetchell/delaunay/pull/488) - Preserve positive orientation after vertex removal [#487](https://github.com/acgetchell/delaunay/pull/487) - Preserve exact 2D layered strip vertices [#486](https://github.com/acgetchell/delaunay/pull/486) - Bump actions/checkout from 6.0.3 to 7.0.0 [#485](https://github.com/acgetchell/delaunay/pull/485) @@ -135,6 +138,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refresh the validation guide, invariants, API-design, and prelude docs for the five-level stack. +- [**breaking**] Add Pachner feasibility checks [#489](https://github.com/acgetchell/delaunay/pull/489) + [`be6728c`](https://github.com/acgetchell/delaunay/commit/be6728c4a84c988226a7bf4973ef1d382ff7e63b) + + - Add immutable flip and Pachner feasibility reports for dry-run workflows. + - Route Pachner feasibility through the shared bistellar preflight used by mutating flips. + - Export Pachner feasibility from the focused Pachner prelude. ### Changed @@ -342,6 +351,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Canonicalize coherent orientation per simplex-neighbor component so disconnected repair states do not keep a negative geometric sign. - Surface post-repair orientation failures through the existing typed invariant errors. - Avoid redundant full-TDS canonicalization passes on the successful normalization path. +- Make targeted PL repair failure-atomic [#488](https://github.com/acgetchell/delaunay/pull/488) + [`abd466f`](https://github.com/acgetchell/delaunay/commit/abd466f38da370b58610e7b34cb91aca800abb65) + + - Extend PL-manifold repair from facet over-sharing to bounded boundary-ridge, ridge-link, and vertex-link repair stages with typed diagnostics. + - Run delaunayize through the Delaunay rollback transaction so failed topology repair, failed Delaunay repair, fallback snapshot failures, and rebuild + failures restore the pre-call triangulation. + + - Expose PlManifoldRepairStage through the crate root and delaunayize prelude, and add benchmark fixtures for targeted topology repair. + - Align local and CI tooling on Python 3.14, uv 0.11.26, and the reviewed Rust/Python tool pins. ### Maintenance diff --git a/Cargo.toml b/Cargo.toml index a80ce64e..736f98ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,11 +89,6 @@ name = "allocation_hot_paths" path = "benches/allocation_hot_paths.rs" harness = false -[[bench]] -name = "boundary_uuid_iter" -path = "benches/boundary_uuid_iter.rs" -harness = false - [[bench]] name = "ci_performance_suite" path = "benches/ci_performance_suite.rs" @@ -109,6 +104,11 @@ name = "cold_path_predicates" path = "benches/cold_path_predicates.rs" harness = false +[[bench]] +name = "delaunay_repair" +path = "benches/delaunay_repair.rs" +harness = false + [[bench]] name = "profiling_suite" path = "benches/profiling_suite.rs" @@ -131,8 +131,8 @@ path = "benches/delete_vertex.rs" harness = false [[bench]] -name = "edge_key_queries" -path = "benches/edge_key_queries.rs" +name = "locate" +path = "benches/locate.rs" harness = false [[bench]] diff --git a/README.md b/README.md index 15e3b0e3..54b6fa98 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; assert_eq!(dt.dim(), 3); assert_eq!(dt.number_of_vertices(), 4); diff --git a/benches/PERFORMANCE_RESULTS.md b/benches/PERFORMANCE_RESULTS.md index 1fd4d549..0954697d 100644 --- a/benches/PERFORMANCE_RESULTS.md +++ b/benches/PERFORMANCE_RESULTS.md @@ -57,7 +57,7 @@ boundary traversal, and explicit bistellar flip roundtrips. #### Construction -Public API: `DelaunayTriangulation::try_new_with_options` +Public API: `DelaunayTriangulation::builder(...).construction_options(...).build()` | Benchmark ID | Dimension | Input | Variant | Mean | 95% CI | |--------------|-----------|-------|---------|------|--------| diff --git a/benches/README.md b/benches/README.md index e82c1152..69ce3ca1 100644 --- a/benches/README.md +++ b/benches/README.md @@ -9,15 +9,15 @@ predicates fast across 2D-5D. | Benchmark | Purpose | Scale | Typical Runtime | Used By | |-----------|---------|-------|-----------------|---------| | `allocation_hot_paths.rs` | Allocation contracts for public hot paths | Calibrated 2D-5D canary fixtures | ~1-2 min | Manual allocation checks | -| `boundary_uuid_iter.rs` | Focused boundary-facet and UUID iterator microbenchmarks | 3D small fixed schedules | <1 min | Manual micro-optimization checks | | `ci_performance_suite.rs` | Public workflow regression contract | Calibrated 2D-5D canaries | ~5-10 min | CI, baselines, `just perf-no-regressions` | | `circumsphere_containment.rs` | Compare circumsphere predicate methods | 2D-5D fixed, 3D random, edge cases | ~5 min | Predicate tuning, summaries | | `cold_path_predicates.rs` | Track hot/cold predicate paths | 2D-5D hot queries, near-boundary cases | ~2-5 min | Predicate optimization work | +| `delaunay_repair.rs` | Flip-based Delaunay repair on prepared Levels 1-4 fixtures | 2D-5D repair-convergent fixtures | ~2-5 min | Repair tuning | | `pachner_stress.rs` | Unified Pachner move API stress | Accepted 4D k=1/k=2/k=3 forward/inverse moves | <1 min | Monte-Carlo move workflow tuning | | `pl_manifold_repair.rs` | Over-shared facet and targeted topology repair | 2D/3D synthetic repair fixtures | <1 min | PL-manifold repair tuning | | `profiling_suite.rs` | Large-scale construction, memory, query, validation profiling | 2D/3D 10k, 4D 3k, 5D 1k | ~2-3 hr | Manual/monthly | | `delete_vertex.rs` | Vertex deletion and rollback cost | 2D-5D fixed cases | ~1-5 min | Vertex deletion | -| `edge_key_queries.rs` | Public `EdgeKey` construction microbenchmarks | 2D-5D fixed live-edge cases | <1 min | Query API tuning | +| `locate.rs` | Point-location facet-walk latency (no-hint vs exact-hint) | 2D-5D fixed cases | ~1-3 min | Locate/walk tuning | | `tds_clone.rs` | `Tds::clone()` snapshot cost | Deterministic 2D-5D triangulations | ~1-3 min | Rollback design baselines | | `topology_guarantee_construction.rs` | Cost of topology guarantee modes | 2D-5D construction cases | ~5-15 min | Manual topology policy work | @@ -39,14 +39,14 @@ predicates fast across 2D-5D. | Durable latest-version benchmark baseline | GitHub Release asset `delaunay-vX.Y.Z-criterion-baseline.tar.gz` | | Smoke-test benchmark harnesses | `just bench-smoke` | | Allocation hot-path contracts | `cargo bench --profile perf --bench allocation_hot_paths --features count-allocations -- --noplot` | -| Boundary/UUID microbenchmarks | `cargo bench --profile perf --bench boundary_uuid_iter -- --noplot` | | Predicate comparison | `cargo bench --profile perf --bench circumsphere_containment -- --noplot` | | Predicate cold-path work | `cargo bench --profile perf --bench cold_path_predicates -- --noplot` | +| Flip-based Delaunay repair | `cargo bench --profile perf --bench delaunay_repair -- --noplot` | | Unified Pachner move stress | `cargo bench --profile perf --bench pachner_stress -- --noplot` | | PL-manifold repair path | `cargo bench --profile perf --features bench --bench pl_manifold_repair -- --noplot` | | Large-scale scaling suite | `cargo bench --profile perf --bench profiling_suite -- --noplot` | | Vertex deletion mutation baseline | `cargo bench --profile perf --bench delete_vertex -- --noplot` | -| Edge-key query construction | `cargo bench --profile perf --bench edge_key_queries -- --noplot` | +| Point-location facet walk | `cargo bench --profile perf --bench locate -- --noplot` | | One-dimension acceptance/profiling run | `just debug-large-scale-{2,3,4,5}d [n] [repair_every]` | | Deep profiling | `cargo bench --profile perf --bench profiling_suite --features count-allocations` | @@ -141,13 +141,13 @@ cargo bench --profile perf --bench ci_performance_suite `ci_performance_suite.rs` is the stable public workflow contract. It covers: -- construction via `DelaunayTriangulation::try_new_with_options` +- construction via `DelaunayTriangulation::builder(...).construction_options(...).build()` - adversarial construction - convex hull extraction - boundary facet traversal - validation Levels 1-4 - incremental vertex insertion into prepared triangulations -- explicit 4D bistellar flip roundtrips +- explicit 2D-5D bistellar flip roundtrips The current calibrated fixture sizes are: diff --git a/benches/allocation_hot_paths.rs b/benches/allocation_hot_paths.rs index edc5236c..d5f8ad09 100644 --- a/benches/allocation_hot_paths.rs +++ b/benches/allocation_hot_paths.rs @@ -207,7 +207,10 @@ mod allocation_contracts { attempts, base_seed: Some(seed), }); - let dt = BenchTriangulation::::try_new_with_options(&vertices, options).or_abort(); + let dt: BenchTriangulation = DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + .or_abort(); let simplex_key = representative_simplex_key(&dt).or_abort(); let removal_keys = dt.tds().simplex_keys().take(32).collect(); let facet_vertices = first_facet_vertices(&dt, simplex_key).or_abort(); diff --git a/benches/boundary_uuid_iter.rs b/benches/boundary_uuid_iter.rs deleted file mode 100644 index 63049ca3..00000000 --- a/benches/boundary_uuid_iter.rs +++ /dev/null @@ -1,144 +0,0 @@ -#![forbid(unsafe_code)] - -//! Focused microbenchmarks for boundary-facet traversal and simplex UUID iteration. -//! -//! These cases used to live as `#[cfg(feature = "bench")]` unit tests. Keeping -//! them in a Criterion harness avoids creating a third test bucket while -//! preserving the quick performance probes. - -use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; -use delaunay::prelude::construction::{DelaunayTriangulation, vertex}; -use delaunay::prelude::generators::generate_random_points_in_range_seeded; -use delaunay::prelude::geometry::{AdaptiveKernel, CoordinateRange}; -use delaunay::prelude::query::FacetIncidenceAnalysis; -use delaunay::try_vertices_from_points; -use uuid::Uuid; - -use std::collections::HashSet; -use std::hint::black_box; - -/// Shared benchmark setup error helpers. -#[path = "common/bench_utils.rs"] -pub mod bench_utils; -use bench_utils::{OrAbort, OrAbortWithContext}; - -const BOUNDARY_COUNTS_3D: &[usize] = &[20, 40, 60, 80]; - -fn benchmark_bounds() -> CoordinateRange { - CoordinateRange::try_new(-100.0_f64, 100.0).or_abort() -} - -fn boundary_triangulation_3d( - requested_vertices: usize, -) -> DelaunayTriangulation, (), (), 3> { - let points = generate_random_points_in_range_seeded::<3>( - requested_vertices, - benchmark_bounds(), - 0xB0DA_FACE_0000_0000 ^ requested_vertices as u64, - ); - let points = points.or_abort(); - let vertices = try_vertices_from_points(&points).or_abort(); - DelaunayTriangulation::try_new(&vertices).or_abort() -} - -fn bench_boundary_facets_micro(c: &mut Criterion) { - let mut group = c.benchmark_group("boundary_facets_micro"); - - for &requested_vertices in BOUNDARY_COUNTS_3D { - let dt = boundary_triangulation_3d(requested_vertices); - let boundary_count = dt - .boundary_facets() - .or_abort() - .try_fold(0_usize, |count, facet| facet.map(|_| count + 1)) - .or_abort(); - group.throughput(Throughput::Elements( - u64::try_from(boundary_count).or_abort(), - )); - - group.bench_with_input( - BenchmarkId::new("boundary_facets_count_3d", requested_vertices), - &dt, - |b, dt| { - b.iter(|| { - black_box( - dt.boundary_facets() - .or_abort() - .try_fold(0_usize, |count, facet| facet.map(|_| count + 1)) - .or_abort(), - ); - }); - }, - ); - - let boundary_facets = dt - .boundary_facets() - .or_abort() - .collect::, _>>() - .or_abort(); - group.bench_with_input( - BenchmarkId::new("is_one_sided_facet_3d", requested_vertices), - &(&dt, boundary_facets), - |b, (dt, facets)| { - b.iter(|| { - let confirmed = facets - .iter() - .filter(|facet| dt.tds().is_one_sided_facet(facet).or_abort()) - .count(); - black_box(confirmed); - }); - }, - ); - } - - group.finish(); -} - -fn uuid_iter_source() -> DelaunayTriangulation, (), (), 3> { - let vertices = vec![ - vertex![0.0, 0.0, 0.0].or_abort(), - vertex![1.0, 0.0, 0.0].or_abort(), - vertex![0.0, 1.0, 0.0].or_abort(), - vertex![0.0, 0.0, 1.0].or_abort(), - ]; - DelaunayTriangulation::try_new(&vertices).or_abort() -} - -fn bench_vertex_uuid_iter(c: &mut Criterion) { - let mut group = c.benchmark_group("vertex_uuid_iter"); - let dt = uuid_iter_source(); - let (_simplex_key, simplex) = dt - .simplices() - .next() - .or_abort("simplex should exist for UUID iterator benchmark"); - - group.throughput(Throughput::Elements( - u64::try_from(simplex.vertices().len()).or_abort(), - )); - - group.bench_function("by_value", |b| { - b.iter(|| { - let unique_uuids = simplex - .vertex_uuid_iter(dt.tds()) - .collect::, _>>(); - let unique_uuids = unique_uuids.or_abort(); - black_box(unique_uuids); - }); - }); - - group.bench_function("simulated_by_reference", |b| { - b.iter(|| { - let uuid_values: Vec = simplex - .vertices() - .iter() - .map(|&vkey| dt.tds().vertex(vkey).or_abort("vertex should exist").uuid()) - .collect(); - let uuid_refs: Vec<&Uuid> = uuid_values.iter().collect(); - black_box(uuid_refs); - }); - }); - - group.finish(); -} - -criterion_group!(benches, bench_boundary_facets_micro, bench_vertex_uuid_iter); -criterion_main!(benches); diff --git a/benches/ci_performance_suite.rs b/benches/ci_performance_suite.rs index 6a0578d1..ebb17b6a 100644 --- a/benches/ci_performance_suite.rs +++ b/benches/ci_performance_suite.rs @@ -190,7 +190,7 @@ fn api_benchmark_entries() -> Vec { vec![ ApiBenchmarkEntry { group: "construction", - public_api: "DelaunayTriangulation::try_new_with_options", + public_api: "DelaunayTriangulation::builder(...).construction_options(...).build", dimensions: "2,3,4,5", benchmark_ids: construction_benchmark_ids(), note: "construct_from_calibrated_seeded_and_adversarial_inputs", @@ -377,7 +377,10 @@ fn prepare_dt(dim_seed: u64, count: usize) -> BenchTriangulation base_seed: Some(seed), }); - BenchTriangulation::::try_new_with_options(&vertices, options).or_abort() + DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + .or_abort() } fn prepare_adv_dt(dim_seed: u64, count: usize) -> BenchTriangulation { @@ -388,7 +391,10 @@ fn prepare_adv_dt(dim_seed: u64, count: usize) -> BenchTriangula base_seed: Some(seed), }); - BenchTriangulation::::try_new_with_options(&vertices, options).or_abort() + DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + .or_abort() } fn prepare_inserts( @@ -430,7 +436,11 @@ fn find_seed_vertices( base_seed: Some(candidate_seed), }); - if DelaunayTriangulation::<_, (), (), D>::try_new_with_options(&vertices, options).is_ok() { + if DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + .is_ok() + { return Some((candidate_seed, points, vertices)); } } @@ -450,7 +460,9 @@ fn stable_adv_points( base_seed: Some(seed), }); - BenchTriangulation::::try_new_with_options(&vertices, options) + DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() .is_ok() .then_some((seed, points, vertices)) } @@ -811,7 +823,10 @@ fn emit_construction_metric( vertices: &[Vertex<(), D>], options: ConstructionOptions, ) { - let dt = BenchTriangulation::::try_new_with_options(vertices, options).or_abort(); + let dt: BenchTriangulation = DelaunayTriangulation::builder(vertices) + .construction_options(options) + .build() + .or_abort(); println!( "api_benchmark_metric benchmark_id={benchmark_id} vertices={} simplices={}", vertices.len(), @@ -953,17 +968,17 @@ macro_rules! benchmark_tds_new_dimension { emit_construction_metric::<$dim>(&bench_id, &vertices, options); b.iter(|| { - match DelaunayTriangulation::<_, (), (), $dim>::try_new_with_options( - &vertices, - options, - ) { + match DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + { Ok(dt) => { black_box(dt); } Err(err) => { let error = format!("{err:?}"); abort_benchmark(format_args!( - "DelaunayTriangulation::try_new_with_options failed for {}D: {error}; dim={}; count={}; seed={}; bounds={:?}; sample_points={sample_points:?}", + "DelaunayTriangulation builder construction failed for {}D: {error}; dim={}; count={}; seed={}; bounds={:?}; sample_points={sample_points:?}", $dim, $dim, count, @@ -992,17 +1007,17 @@ macro_rules! benchmark_tds_new_dimension { emit_construction_metric::<$dim>(&adv_bench_id, &vertices, options); b.iter(|| { - match DelaunayTriangulation::<_, (), (), $dim>::try_new_with_options( - &vertices, - options, - ) { + match DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + { Ok(dt) => { black_box(dt); } Err(err) => { let error = format!("{err:?}"); abort_benchmark(format_args!( - "adversarial DelaunayTriangulation::try_new_with_options failed for {}D: {error}; dim={}; count={}; seed={}; sample_points={sample_points:?}", + "adversarial DelaunayTriangulation builder construction failed for {}D: {error}; dim={}; count={}; seed={}; sample_points={sample_points:?}", $dim, $dim, count, diff --git a/benches/common/flip_workflows.rs b/benches/common/flip_workflows.rs index a446fd92..bbac7c87 100644 --- a/benches/common/flip_workflows.rs +++ b/benches/common/flip_workflows.rs @@ -12,8 +12,9 @@ use delaunay::flips::{ TriangleHandleError, }; use delaunay::prelude::construction::{ - ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationConstructionError, - InsertionOrderStrategy, TopologyGuarantee, Vertex, vertex, + ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationBuilder, + DelaunayTriangulationConstructionError, InsertionOrderStrategy, TopologyGuarantee, Vertex, + vertex, }; use delaunay::prelude::geometry::{CoordinateConversionError, Point, RobustKernel, simplex_volume}; use delaunay::prelude::query::{JaccardComputationError, format_jaccard_report}; @@ -490,16 +491,14 @@ pub fn build_flip_dt( let options = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); - DelaunayTriangulation::try_with_topology_guarantee_and_options( - &RobustKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - options, - ) - .map_err(|source| FlipWorkflowError::Construction { - dimension: D, - source: Box::new(source), - }) + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .construction_options(options) + .build_with_kernel(&RobustKernel::new()) + .map_err(|source| FlipWorkflowError::Construction { + dimension: D, + source: Box::new(source), + }) } /// Captures vertex identity and simplex incidence for exact roundtrip checks. diff --git a/benches/delaunay_repair.rs b/benches/delaunay_repair.rs new file mode 100644 index 00000000..6e9adfa9 --- /dev/null +++ b/benches/delaunay_repair.rs @@ -0,0 +1,235 @@ +#![forbid(unsafe_code)] + +//! Benchmark: flip-based Delaunay repair (`repair_delaunay_with_flips_advanced`). +//! +//! Fixtures are built with +//! [`ConstructionOptions::without_final_delaunay_enforcement`], which returns a +//! valid Levels 1-4 triangulation without running batch or final Level 5 +//! repair, so well-conditioned random fixtures typically still carry Delaunay +//! violations. The benchmark then times the public repair entry point on a +//! clone of each prepared fixture. +//! +//! Setup verifies on a throwaway clone that repair converges for the chosen +//! seed, so the measured closure never times a repair failure chain. Case +//! labels record whether the prepared fixture was `violating` or already +//! `delaunay`, so baseline comparisons notice when a fixture changes meaning. +//! Exactly-cospherical adversarial fixtures are deliberately excluded: strict +//! flip repair can legitimately fail to converge on them, which is a +//! correctness scenario rather than a stable performance contract. +//! +//! Intended for **manual** runs (not part of the CI performance suite). +//! +//! Run with: +//! ```bash +//! cargo bench --profile perf --bench delaunay_repair +//! ``` + +use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; +use delaunay::prelude::construction::{ + ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationBuilder, Vertex, +}; +use delaunay::prelude::generators::generate_random_points_in_range_seeded; +use delaunay::prelude::geometry::{ + AdaptiveKernel, CoordinateRange, ExactPredicates, Kernel, Point, +}; +use delaunay::prelude::repair::DelaunayRepairHeuristicConfig; +use delaunay::try_vertices_from_points; +use std::hint::black_box; +use std::process; +use std::time::Duration; + +/// Shared benchmark setup error helpers. +#[path = "common/bench_utils.rs"] +pub mod bench_utils; +use bench_utils::{OrAbort, abort_benchmark}; + +const SEED_SALT: u64 = 0x9E37_79B9_7F4A_7C15; +const SEED_SEARCH_ATTEMPTS: usize = 16; +const SAMPLE_SIZE: usize = 10; +const WARM_UP_TIME: Duration = Duration::from_millis(500); +const MEASUREMENT_TIME: Duration = Duration::from_secs(2); + +type BenchTriangulation = DelaunayTriangulation, (), (), D>; + +struct RepairSource { + vertex_count: usize, + simplex_count: usize, + violating: bool, + triangulation: BenchTriangulation, +} + +/// Construct a finite point or abort benchmark setup. +fn finite_point(coords: [f64; D]) -> Point { + Point::try_new(coords).unwrap_or_else(|_| process::abort()) +} + +/// Coordinate range used for raw generated points. +fn point_bounds() -> CoordinateRange { + CoordinateRange::try_new(0.0_f64, 1.0).or_abort() +} + +/// Derive a deterministic, dimension-specific seed for one benchmark case. +fn seed_for_case(requested_vertices: usize, seed_base: u64) -> u64 { + let vertices = u64::try_from(requested_vertices).or_abort(); + let dimension = u64::try_from(D).or_abort(); + seed_base ^ vertices.wrapping_mul(SEED_SALT) ^ dimension.rotate_left(32) +} + +/// Generate the minimal full-dimensional simplex points. +fn simplex_points() -> Vec> { + let mut points = Vec::with_capacity(D + 1); + points.push(finite_point([0.0; D])); + + for axis in 0..D { + let mut coords = [0.0; D]; + coords[axis] = 1.0; + points.push(finite_point(coords)); + } + + points +} + +/// Generate a reproducible fixture vertex set. +fn generate_vertices(requested_vertices: usize, seed: u64) -> Vec> { + let generated_count = requested_vertices.saturating_sub(D + 1); + let mut points = simplex_points::(); + points.extend( + generate_random_points_in_range_seeded::(generated_count, point_bounds(), seed) + .or_abort(), + ); + try_vertices_from_points(&points).or_abort() +} + +/// Build one repair fixture whose repair is verified to converge. +/// +/// Seeds are searched until construction succeeds and a throwaway clone +/// completes `repair_delaunay_with_flips_advanced`, so the measured closure +/// never times a repair failure chain. +fn build_source(requested_vertices: usize, seed_base: u64) -> RepairSource +where + AdaptiveKernel: ExactPredicates + Kernel, +{ + let options = ConstructionOptions::default().without_final_delaunay_enforcement(); + + for attempt in 0..SEED_SEARCH_ATTEMPTS { + let attempt_seed = u64::try_from(attempt).or_abort(); + let seed = seed_for_case::(requested_vertices, seed_base) + ^ attempt_seed.wrapping_mul(SEED_SALT.rotate_left(17)); + let vertices = generate_vertices::(requested_vertices, seed); + let Ok(triangulation) = DelaunayTriangulationBuilder::new(&vertices) + .construction_options(options) + .build() + else { + continue; + }; + + let mut probe = triangulation.clone(); + if probe + .repair_delaunay_with_flips_advanced(DelaunayRepairHeuristicConfig::default()) + .is_err() + { + continue; + } + + let violating = triangulation.is_delaunay_via_flips().is_err(); + return RepairSource { + vertex_count: triangulation.number_of_vertices(), + simplex_count: triangulation.number_of_simplices(), + violating, + triangulation, + }; + } + + abort_benchmark(format!( + "no repair-convergent {D}D fixture built for {requested_vertices} vertices \ + after {SEED_SEARCH_ATTEMPTS} seeds" + )) +} + +/// Report benchmark throughput in total stored vertices plus simplices. +fn triangulation_element_count(source: &RepairSource) -> u64 { + let total_elements = source.vertex_count + source.simplex_count; + u64::try_from(total_elements).or_abort() +} + +/// Register the repair cases for one dimension and input-size schedule. +fn bench_repair_dimension( + c: &mut Criterion, + dim_label: &str, + counts: &[usize], + seed_base: u64, +) where + AdaptiveKernel: ExactPredicates + Kernel, +{ + let mut group = c.benchmark_group(format!("delaunay_repair/{dim_label}")); + group.sample_size(SAMPLE_SIZE); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for &requested_vertices in counts { + let source = build_source::(requested_vertices, seed_base); + let delaunay_state = if source.violating { + "violating" + } else { + "delaunay" + }; + group.throughput(Throughput::Elements(triangulation_element_count(&source))); + + group.bench_with_input( + BenchmarkId::new( + "repair_advanced", + format!( + "{delaunay_state}_vertices_{}_simplices_{}", + source.vertex_count, source.simplex_count + ), + ), + &source, + |b, source| { + b.iter_batched( + || source.triangulation.clone(), + |mut triangulation| { + black_box( + triangulation + .repair_delaunay_with_flips_advanced( + DelaunayRepairHeuristicConfig::default(), + ) + .or_abort(), + ); + }, + BatchSize::SmallInput, + ); + }, + ); + } + + group.finish(); +} + +/// Benchmark 2D flip-based Delaunay repair. +fn bench_delaunay_repair_2d(c: &mut Criterion) { + bench_repair_dimension::<2>(c, "2d", &[500, 2_000], 0x2EFA_0000_0000_0002); +} + +/// Benchmark 3D flip-based Delaunay repair. +fn bench_delaunay_repair_3d(c: &mut Criterion) { + bench_repair_dimension::<3>(c, "3d", &[150, 500], 0x2EFA_0000_0000_0003); +} + +/// Benchmark 4D flip-based Delaunay repair. +fn bench_delaunay_repair_4d(c: &mut Criterion) { + bench_repair_dimension::<4>(c, "4d", &[50, 100], 0x2EFA_0000_0000_0004); +} + +/// Benchmark 5D flip-based Delaunay repair. +fn bench_delaunay_repair_5d(c: &mut Criterion) { + bench_repair_dimension::<5>(c, "5d", &[25, 40], 0x2EFA_0000_0000_0005); +} + +criterion_group!( + benches, + bench_delaunay_repair_2d, + bench_delaunay_repair_3d, + bench_delaunay_repair_4d, + bench_delaunay_repair_5d +); +criterion_main!(benches); diff --git a/benches/delete_vertex.rs b/benches/delete_vertex.rs index b5dd0596..0c257a0d 100644 --- a/benches/delete_vertex.rs +++ b/benches/delete_vertex.rs @@ -333,7 +333,7 @@ fn build_success_source( ^ attempt_seed.wrapping_mul(SEED_SALT.rotate_left(17)); let fixture_kind = fixture_kind_for_attempt(preferred_kind, attempt); let vertices = generate_vertices::(requested_vertices, seed, fixture_kind); - let Ok(triangulation) = DelaunayTriangulation::try_new(&vertices) else { + let Ok(triangulation) = DelaunayTriangulation::builder(&vertices).build() else { continue; }; let Some(vertex_key) = successful_deletion_vertex(&triangulation) else { @@ -358,7 +358,8 @@ fn build_success_source( /// Build the source triangulation for invalid-deletion rollback measurements. fn build_rollback_source() -> DeletionSource { let vertices = simplex_vertices::(); - let triangulation: BenchTriangulation = DelaunayTriangulation::try_new(&vertices).or_abort(); + let triangulation: BenchTriangulation = + DelaunayTriangulation::builder(&vertices).build().or_abort(); let vertex_key = triangulation .vertices() .next() diff --git a/benches/edge_key_queries.rs b/benches/edge_key_queries.rs deleted file mode 100644 index fc591880..00000000 --- a/benches/edge_key_queries.rs +++ /dev/null @@ -1,168 +0,0 @@ -#![forbid(unsafe_code)] - -//! Focused microbenchmarks for public edge-key query construction. -//! -//! Run with: -//! -//! ```bash -//! cargo bench --profile perf --bench edge_key_queries -- --noplot -//! ``` - -#[path = "common/bench_utils.rs"] -mod bench_utils; - -use criterion::{ - BenchmarkGroup, BenchmarkId, Criterion, criterion_group, criterion_main, measurement::WallTime, -}; -use delaunay::prelude::construction::{DelaunayTriangulation, DelaunayTriangulationBuilder}; -use delaunay::prelude::generators::generate_random_points_in_range_seeded; -use delaunay::prelude::geometry::{AdaptiveKernel, CoordinateRange}; -use delaunay::prelude::tds::{EdgeKey, VertexKey}; -use delaunay::try_vertices_from_points; -use std::{collections::BTreeSet, hint::black_box, time::Duration}; - -use bench_utils::{OrAbort, OrAbortWithContext, abort_benchmark}; - -const EDGE_PAIR_COUNT: usize = 128; -const COUNT_2D: usize = 1_000; -const COUNT_3D: usize = 350; -const COUNT_4D: usize = 80; -const COUNT_5D: usize = 35; -const SEED_2D: u64 = 9_321; -const SEED_3D: u64 = 9_322; -const SEED_4D: u64 = 9_323; -const SEED_5D: u64 = 9_324; -const SAMPLE_SIZE: usize = 32; - -type BenchTriangulation = DelaunayTriangulation, (), (), D>; - -/// Holds a prebuilt triangulation and live edge endpoints selected outside the measured path. -struct EdgeKeyFixture { - dt: BenchTriangulation, - endpoint_pairs: Vec<(VertexKey, VertexKey)>, - vertex_count: usize, - simplex_count: usize, -} - -/// Returns a deterministic coordinate range for benchmark point clouds. -fn benchmark_bounds() -> CoordinateRange { - CoordinateRange::try_new(-100.0_f64, 100.0).or_abort() -} - -/// Builds a deterministic triangulation and live edge endpoint pairs for one dimension. -fn prepare_fixture(count: usize, seed: u64) -> EdgeKeyFixture { - let points = - generate_random_points_in_range_seeded::(count, benchmark_bounds(), seed).or_abort(); - let vertices = try_vertices_from_points(&points).or_abort(); - let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() - .or_abort(); - let endpoint_pairs = late_simplex_edge_pairs(&dt); - if endpoint_pairs.len() < EDGE_PAIR_COUNT { - abort_benchmark(format_args!( - "{D}D EdgeKey benchmark found {} unique edge pairs, expected at least {EDGE_PAIR_COUNT}", - endpoint_pairs.len() - )); - } - let _first_pair = endpoint_pairs.first().or_abort(format_args!( - "{D}D EdgeKey benchmark should contain at least one endpoint pair" - )); - - EdgeKeyFixture { - vertex_count: dt.number_of_vertices(), - simplex_count: dt.number_of_simplices(), - dt, - endpoint_pairs, - } -} - -/// Selects unique live edges from the end of simplex storage to exercise lookup cost. -fn late_simplex_edge_pairs( - dt: &BenchTriangulation, -) -> Vec<(VertexKey, VertexKey)> { - let simplex_vertices: Vec<_> = dt - .simplices() - .map(|(_simplex_key, simplex)| simplex.vertices().to_vec()) - .collect(); - let mut pairs = BTreeSet::new(); - - for vertices in simplex_vertices.iter().rev() { - for i in 0..vertices.len() { - for j in (i + 1)..vertices.len() { - let first = vertices[i]; - let second = vertices[j]; - pairs.insert(canonical_pair(first, second)); - if pairs.len() >= EDGE_PAIR_COUNT { - return pairs.into_iter().collect(); - } - } - } - } - - pairs.into_iter().collect() -} - -/// Canonicalizes an endpoint pair without constructing the `EdgeKey` being benchmarked. -fn canonical_pair(first: VertexKey, second: VertexKey) -> (VertexKey, VertexKey) { - if first <= second { - (first, second) - } else { - (second, first) - } -} - -/// Measures successful public `EdgeKey::try_new` construction for one dimension. -fn bench_edge_key_try_new( - group: &mut BenchmarkGroup<'_, WallTime>, - fixture: &EdgeKeyFixture, -) { - let tds = fixture.dt.tds(); - let endpoint_pairs = &fixture.endpoint_pairs; - - group.bench_function( - BenchmarkId::new( - format!("edge_key_try_new_{D}d"), - format!( - "vertices_{}_simplices_{}_edges_{}", - fixture.vertex_count, - fixture.simplex_count, - endpoint_pairs.len() - ), - ), - |b| { - b.iter(|| { - let mut edge_count = 0_usize; - for &(first, second) in endpoint_pairs { - let edge = - EdgeKey::try_new(tds, black_box(first), black_box(second)).or_abort(); - black_box(edge); - edge_count += 1; - } - assert_eq!(edge_count, endpoint_pairs.len()); - black_box(edge_count); - }); - }, - ); -} - -/// Runs edge-key construction benchmarks across supported practical dimensions. -fn edge_key_queries(c: &mut Criterion) { - let mut group = c.benchmark_group("edge_key_queries"); - group.sample_size(SAMPLE_SIZE); - group.warm_up_time(Duration::from_secs(1)); - group.measurement_time(Duration::from_secs(2)); - - let fixture_2d = prepare_fixture::<2>(COUNT_2D, SEED_2D); - let fixture_3d = prepare_fixture::<3>(COUNT_3D, SEED_3D); - let fixture_4d = prepare_fixture::<4>(COUNT_4D, SEED_4D); - let fixture_5d = prepare_fixture::<5>(COUNT_5D, SEED_5D); - - bench_edge_key_try_new(&mut group, &fixture_2d); - bench_edge_key_try_new(&mut group, &fixture_3d); - bench_edge_key_try_new(&mut group, &fixture_4d); - bench_edge_key_try_new(&mut group, &fixture_5d); - group.finish(); -} - -criterion_group!(benches, edge_key_queries); -criterion_main!(benches); diff --git a/benches/locate.rs b/benches/locate.rs new file mode 100644 index 00000000..46a03a02 --- /dev/null +++ b/benches/locate.rs @@ -0,0 +1,229 @@ +#![forbid(unsafe_code)] + +//! Benchmark: point-location (`locate`) facet-walk latency. +//! +//! This benchmark isolates locate cost from insertion cost by building +//! deterministic triangulations once, precomputing interior query batches in +//! setup, and timing only the `locate` calls. Two cases are tracked per +//! dimension and fixture size: +//! +//! - `no_hint`: every query starts from the triangulation's first simplex, +//! measuring full facet-walk cost. +//! - `exact_hint`: every query passes its own containing simplex as the hint, +//! measuring the hint fast path used by locality-aware callers such as +//! incremental insertion. +//! +//! Intended for **manual** runs (not part of the CI performance suite). +//! +//! Run with: +//! ```bash +//! cargo bench --profile perf --bench locate +//! ``` + +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; +use delaunay::prelude::algorithms::{LocateResult, locate}; +use delaunay::prelude::construction::{ + DelaunayTriangulation, DelaunayTriangulationBuilder, Vertex, +}; +use delaunay::prelude::generators::generate_random_points_in_range_seeded; +use delaunay::prelude::geometry::{AdaptiveKernel, CoordinateRange, Point}; +use delaunay::prelude::tds::SimplexKey; +use delaunay::try_vertices_from_points; +use std::hint::black_box; +use std::time::Duration; + +/// Shared benchmark setup error helpers. +#[path = "common/bench_utils.rs"] +pub mod bench_utils; +use bench_utils::{OrAbort, abort_benchmark}; + +const SEED_SALT: u64 = 0x9E37_79B9_7F4A_7C15; +const SEED_SEARCH_ATTEMPTS: usize = 16; +const QUERY_COUNT: usize = 64; +const QUERY_CANDIDATE_FACTOR: usize = 8; +const SAMPLE_SIZE: usize = 10; +const WARM_UP_TIME: Duration = Duration::from_millis(500); +const MEASUREMENT_TIME: Duration = Duration::from_secs(2); + +type BenchTriangulation = DelaunayTriangulation, (), (), D>; + +struct LocateSource { + vertex_count: usize, + simplex_count: usize, + triangulation: BenchTriangulation, + /// Interior queries paired with their containing simplex for the hint fast path. + hinted_queries: Vec<(Point, SimplexKey)>, +} + +/// Derive a deterministic, dimension-specific seed for one benchmark case. +fn seed_for_case(requested_vertices: usize, seed_base: u64) -> u64 { + let vertices = u64::try_from(requested_vertices).or_abort(); + let dimension = u64::try_from(D).or_abort(); + seed_base ^ vertices.wrapping_mul(SEED_SALT) ^ dimension.rotate_left(32) +} + +/// Coordinate range used for triangulation points. +fn point_bounds() -> CoordinateRange { + CoordinateRange::try_new(0.0_f64, 1.0).or_abort() +} + +/// Coordinate range used for query candidates, interior to the point cloud. +fn query_bounds() -> CoordinateRange { + CoordinateRange::try_new(0.25_f64, 0.75).or_abort() +} + +/// Build one deterministic triangulation plus an inside-the-hull query batch. +fn build_source(requested_vertices: usize, seed_base: u64) -> LocateSource { + let kernel = AdaptiveKernel::::new(); + + for attempt in 0..SEED_SEARCH_ATTEMPTS { + let attempt_seed = u64::try_from(attempt).or_abort(); + let seed = seed_for_case::(requested_vertices, seed_base) + ^ attempt_seed.wrapping_mul(SEED_SALT.rotate_left(17)); + let points = + generate_random_points_in_range_seeded::(requested_vertices, point_bounds(), seed) + .or_abort(); + let vertices: Vec> = try_vertices_from_points(&points).or_abort(); + let Ok(triangulation) = DelaunayTriangulationBuilder::new(&vertices).build() else { + continue; + }; + + let candidates = generate_random_points_in_range_seeded::( + QUERY_COUNT * QUERY_CANDIDATE_FACTOR, + query_bounds(), + seed ^ SEED_SALT, + ) + .or_abort(); + + let mut hinted_queries = Vec::with_capacity(QUERY_COUNT); + for query in candidates { + if hinted_queries.len() == QUERY_COUNT { + break; + } + let located = locate(triangulation.tds(), &kernel, &query, None).or_abort(); + if let LocateResult::InsideSimplex(simplex_key) = located { + hinted_queries.push((query, simplex_key)); + } + } + if hinted_queries.len() < QUERY_COUNT { + continue; + } + + return LocateSource { + vertex_count: triangulation.number_of_vertices(), + simplex_count: triangulation.number_of_simplices(), + triangulation, + hinted_queries, + }; + } + + abort_benchmark(format!( + "no {D}D locate fixture with {QUERY_COUNT} interior queries found for \ + {requested_vertices} vertices after {SEED_SEARCH_ATTEMPTS} seeds" + )) +} + +/// Register the no-hint and exact-hint locate cases for one dimension. +fn bench_locate_dimension( + c: &mut Criterion, + dim_label: &str, + counts: &[usize], + seed_base: u64, +) { + let kernel = AdaptiveKernel::::new(); + let sources: Vec> = counts + .iter() + .map(|&requested_vertices| build_source::(requested_vertices, seed_base)) + .collect(); + let queries_per_iteration = u64::try_from(QUERY_COUNT).or_abort(); + + { + let mut group = c.benchmark_group(format!("locate/no_hint/{dim_label}")); + group.sample_size(SAMPLE_SIZE); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + for source in &sources { + group.throughput(Throughput::Elements(queries_per_iteration)); + group.bench_with_input( + BenchmarkId::new( + "locate", + format!( + "vertices_{}_simplices_{}", + source.vertex_count, source.simplex_count + ), + ), + source, + |b, source| { + b.iter(|| { + for (query, _) in &source.hinted_queries { + black_box( + locate(source.triangulation.tds(), &kernel, query, None).or_abort(), + ); + } + }); + }, + ); + } + group.finish(); + } + + { + let mut group = c.benchmark_group(format!("locate/exact_hint/{dim_label}")); + group.sample_size(SAMPLE_SIZE); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + for source in &sources { + group.throughput(Throughput::Elements(queries_per_iteration)); + group.bench_with_input( + BenchmarkId::new( + "locate", + format!( + "vertices_{}_simplices_{}", + source.vertex_count, source.simplex_count + ), + ), + source, + |b, source| { + b.iter(|| { + for (query, hint) in &source.hinted_queries { + black_box( + locate(source.triangulation.tds(), &kernel, query, Some(*hint)) + .or_abort(), + ); + } + }); + }, + ); + } + group.finish(); + } +} + +/// Benchmark 2D point location. +fn bench_locate_2d(c: &mut Criterion) { + bench_locate_dimension::<2>(c, "2d", &[100, 500, 2_000], 0x10CA_0000_0000_0002); +} + +/// Benchmark 3D point location. +fn bench_locate_3d(c: &mut Criterion) { + bench_locate_dimension::<3>(c, "3d", &[50, 150, 500], 0x10CA_0000_0000_0003); +} + +/// Benchmark 4D point location. +fn bench_locate_4d(c: &mut Criterion) { + bench_locate_dimension::<4>(c, "4d", &[50, 100], 0x10CA_0000_0000_0004); +} + +/// Benchmark 5D point location. +fn bench_locate_5d(c: &mut Criterion) { + bench_locate_dimension::<5>(c, "5d", &[25, 40], 0x10CA_0000_0000_0005); +} + +criterion_group!( + benches, + bench_locate_2d, + bench_locate_3d, + bench_locate_4d, + bench_locate_5d +); +criterion_main!(benches); diff --git a/benches/pachner_stress.rs b/benches/pachner_stress.rs index 6bf8117e..4cf5796f 100644 --- a/benches/pachner_stress.rs +++ b/benches/pachner_stress.rs @@ -198,6 +198,10 @@ fn clone_batch(base_dt: &FlipTriangulation<4>) -> Vec> { } /// Registers one stress case that repeats the same raw Pachner request. +/// +/// `bench_pachner_move` measures `attempt_pachner_move`, so each sample now +/// includes `propose_pachner` feasibility work and `attempt_on` revalidation +/// before mutation, not only the raw flip mutation cost. fn bench_pachner_move( group: &mut BenchmarkGroup<'_, WallTime>, name: &'static str, diff --git a/benches/profiling_suite.rs b/benches/profiling_suite.rs index 56893083..d82ebb94 100644 --- a/benches/profiling_suite.rs +++ b/benches/profiling_suite.rs @@ -296,7 +296,10 @@ fn construct_triangulation( vertices: &[Vertex<(), D>], seed: u64, ) -> DelaunayTriangulation, (), (), D> { - DelaunayTriangulation::try_new_with_options(vertices, construction_options(seed)).or_abort() + DelaunayTriangulation::builder(vertices) + .construction_options(construction_options(seed)) + .build() + .or_abort() } /// Converts generated point fixtures into benchmark vertices without attaching data. @@ -835,7 +838,7 @@ fn bench_memory_usage( let alloc_info = measure(|| { let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .or_abort(); black_box(dt); }); @@ -924,7 +927,7 @@ fn benchmark_query_latency(c: &mut Criterion) { // Setup: Create triangulation and query points let points = gen_points::<3>(count, PointDistribution::Random, DEFAULT_SEED); let vertices = benchmark_vertices_from_generated_points(&points); - let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices).build::<()>() else { + let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices).build() else { // Construction hit a geometric degeneracy; skip this benchmark entry b.iter(|| {}); return; @@ -1030,7 +1033,7 @@ macro_rules! benchmark_validation_components_dimension { builder }; - match builder.build::<()>() { + match builder.build() { Ok(dt) => Some(dt), Err(err) => { last_error = Some(format!("{err}")); @@ -1142,9 +1145,7 @@ fn bench_bottlenecks(c: &mut Criterion) { let points = gen_points::<3>(count, PointDistribution::Random, DEFAULT_SEED); let vertices = benchmark_vertices_from_generated_points(&points); - DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() - .ok() + DelaunayTriangulationBuilder::new(&vertices).build().ok() }, |dt| { if let Some(dt) = dt { @@ -1174,9 +1175,7 @@ fn bench_bottlenecks(c: &mut Criterion) { let points = gen_points::<3>(count, PointDistribution::Random, DEFAULT_SEED); let vertices = benchmark_vertices_from_generated_points(&points); - DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() - .ok() + DelaunayTriangulationBuilder::new(&vertices).build().ok() }, |dt| { if let Some(dt) = dt { diff --git a/benches/tds_clone.rs b/benches/tds_clone.rs index 77626fba..1aa5e77d 100644 --- a/benches/tds_clone.rs +++ b/benches/tds_clone.rs @@ -64,7 +64,8 @@ fn generate_vertices(requested_vertices: usize, seed: u64) -> Ve fn build_clone_source(requested_vertices: usize, seed_base: u64) -> CloneSource { let seed = seed_for_case::(requested_vertices, seed_base); let vertices = generate_vertices::(requested_vertices, seed); - let triangulation: BenchTriangulation = DelaunayTriangulation::try_new(&vertices).or_abort(); + let triangulation: BenchTriangulation = + DelaunayTriangulation::builder(&vertices).build().or_abort(); let tds = triangulation.tds().clone(); CloneSource { diff --git a/docs/api_design.md b/docs/api_design.md index efb6975e..4328f801 100644 --- a/docs/api_design.md +++ b/docs/api_design.md @@ -79,7 +79,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Incremental insertion (maintains Delaunay property) let new_vertex = vertex![0.5, 0.5, 0.5]?; @@ -116,7 +116,7 @@ fn main() -> DelaunayResult<()> { .try_canonicalized_toroidal([1.0, 1.0]) ? // Wrap input coordinates before Euclidean construction. .topology_guarantee(TopologyGuarantee::PLManifoldStrict) - .build::<()>()?; + .build()?; dt.set_validation_policy(ValidationPolicy::Always); @@ -203,7 +203,7 @@ fn main() -> Result<(), ExampleError> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // k=1 move: Insert a vertex into a simplex (splits simplex into D+1 simplices) let Some((simplex_key, _)) = dt.simplices().next() else { @@ -381,7 +381,7 @@ fn main() -> Result<(), ExampleError> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // 2. Add vertices using Builder API (maintains Delaunay) dt.insert_vertex(vertex![0.5, 0.5, 0.5]?)?; @@ -597,7 +597,7 @@ fn main() -> Result<(), ExampleError> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default())?; assert!(outcome.topology_repair.succeeded); diff --git a/docs/architecture/project_structure.md b/docs/architecture/project_structure.md index ebc06292..7e7da98c 100644 --- a/docs/architecture/project_structure.md +++ b/docs/architecture/project_structure.md @@ -23,11 +23,12 @@ delaunay/ ├── benches/ │ ├── common/ │ ├── allocation_hot_paths.rs -│ ├── boundary_uuid_iter.rs │ ├── ci_performance_suite.rs │ ├── circumsphere_containment.rs │ ├── cold_path_predicates.rs +│ ├── delaunay_repair.rs │ ├── delete_vertex.rs +│ ├── locate.rs │ ├── pachner_stress.rs │ ├── pl_manifold_repair.rs │ ├── profiling_suite.rs diff --git a/docs/dev/rust.md b/docs/dev/rust.md index 795e8105..3495b3ce 100644 --- a/docs/dev/rust.md +++ b/docs/dev/rust.md @@ -435,11 +435,22 @@ being parsed: Current migration targets for API-normalization work: -- Public `DelaunayTriangulation::try_new*` methods are the default fallible - constructors for default-kernel triangulations, and public - `DelaunayTriangulation::try_with_*` methods are the fallible custom-kernel - constructors. Infallible empty constructors remain `empty` and - `with_empty_*` because they accept no user geometry or topology. +- Public Delaunay construction examples should teach + `DelaunayTriangulationBuilder::new(&vertices).build()?` and its fluent + option setters/terminal variants as the canonical default-kernel workflow, + with `DelaunayTriangulation::builder(&vertices)` acceptable only as a terse + builder alias in tests and benchmarks. Do not add local helpers whose whole + purpose is hiding `DelaunayTriangulation::builder(...).build()` or the + equivalent `DelaunayTriangulationBuilder::new(...).build()` chain; such + helpers mask API friction instead of testing the canonical fluent workflow. + The legacy `DelaunayTriangulation::try_new*` and `try_with_*` wrappers are not + public API and should not exist, even as hidden compatibility shims. Use the builder + terminals (`build`, `build_with_statistics`, `build_with_kernel`, and + `build_with_kernel_and_statistics`) at call sites so options, topology + expectations, statistics, and kernels remain visible in domain order. + Shared implementation hooks should stay crate-private, named as builder + backends, and unreachable from downstream callers. Infallible empty constructors + remain `empty` and `with_empty_*` because they accept no user geometry or topology. - `DelaunayTriangulationBuilder::try_from_vertices_and_simplices*` validates explicit simplex specs before storing them in a private proof-bearing wrapper. Full TDS/topology/Delaunay validation still happens at `build`, where the diff --git a/docs/diagnostics.md b/docs/diagnostics.md index 19c22dce..f63c19f3 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -78,7 +78,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; assert!(dt.validate().is_ok()); let report = dt.validation_report(); @@ -155,7 +155,7 @@ fn main() -> Result<(), DiagnosticsExampleError> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; let report = delaunay_violation_report(dt.tds(), None)?; assert!(report.is_valid()); diff --git a/docs/mesh_export.md b/docs/mesh_export.md index 252ee51d..01ad2295 100644 --- a/docs/mesh_export.md +++ b/docs/mesh_export.md @@ -47,7 +47,7 @@ fn main() -> Result<(), ExampleError> { vertex![1.0, 0.0]?, vertex![0.0, 1.0]?, ]; - let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; let export = triangulation.to_mesh_export()?; let json = serde_json::to_string_pretty(&export)?; let decoded: MeshExport<2> = serde_json::from_str(&json)?; diff --git a/docs/numerical_robustness_guide.md b/docs/numerical_robustness_guide.md index b501a792..ac1ecdef 100644 --- a/docs/numerical_robustness_guide.md +++ b/docs/numerical_robustness_guide.md @@ -117,13 +117,13 @@ support through D ≤ 6, but the public exact repair contract is tied to exact insphere support and the currently tested triangulation envelope, so `ExactPredicates` stops at D ≤ 5. -`DelaunayTriangulationBuilder::new(&vertices).build::<()>()` and +`DelaunayTriangulationBuilder::new(&vertices).build()` and `DelaunayTriangulation::empty()` use `AdaptiveKernel`. To opt into a different kernel, use the explicit-kernel constructors: ```rust use delaunay::prelude::geometry::RobustKernel; -use delaunay::prelude::construction::{DelaunayTriangulation, vertex}; +use delaunay::prelude::construction::{DelaunayTriangulation, DelaunayTriangulationBuilder, vertex}; let kernel = RobustKernel::::new(); @@ -135,7 +135,8 @@ let vertices = vec![ ]; let dt: DelaunayTriangulation, (), (), 3> = - DelaunayTriangulation::try_with_kernel(&kernel, &vertices)?; + DelaunayTriangulationBuilder::new(&vertices) + .build_with_kernel(&kernel)?; assert!(dt.is_valid_delaunay().is_ok()); ``` @@ -268,8 +269,9 @@ the triangulation interior. ### Layer 1: Hilbert-sort preprocessing dedup (batch construction) When vertices are inserted via batch construction -(`DelaunayTriangulationBuilder::new(&vertices).build::<()>()`, -`DelaunayTriangulation::try_with_kernel()`, etc.) using the default +(`DelaunayTriangulationBuilder::new(&vertices).build()`, +`DelaunayTriangulationBuilder::new(&vertices).build_with_kernel(&kernel)`, +etc.) using the default `InsertionOrderStrategy::Hilbert`, the Hilbert ordering pass quantizes each coordinate to a fixed-width integer grid before computing the space-filling curve index. After sorting, vertices that map to the same @@ -369,7 +371,7 @@ and per-insertion checks handle any remaining cases. ## Practical recommendations -- Start with the default `AdaptiveKernel` (`DelaunayTriangulationBuilder::new(&vertices).build::<()>()` / +- Start with the default `AdaptiveKernel` (`DelaunayTriangulationBuilder::new(&vertices).build()` / `DelaunayTriangulation::empty()`). This handles near-degenerate configurations correctly out of the box. - If you need explicit `BOUNDARY`/`DEGENERATE` signals (e.g. to detect and handle cospherical diff --git a/docs/topology.md b/docs/topology.md index 5fd4c14d..05430eb8 100644 --- a/docs/topology.md +++ b/docs/topology.md @@ -244,7 +244,7 @@ let vertices = vec![ let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0])? // Canonicalized toroidal construction - .build::<()>()?; + .build()?; ``` Canonicalized toroidal construction wraps coordinates into the fundamental diff --git a/docs/validation.md b/docs/validation.md index 7602d051..a43fdb3f 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -142,7 +142,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Default PL-manifold mode: caller-owned full validation checkpoints. assert_eq!(dt.validation_policy(), ValidationPolicy::ExplicitOnly); @@ -198,7 +198,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); // Optional: relax topology checks for speed (weaker guarantees). @@ -358,7 +358,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Quick structural check (Level 2) assert!(dt.tds().is_valid().is_ok()); @@ -445,7 +445,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 0.0, 1.0]?, vertex![0.25, 0.25, 0.25]?, // Interior point ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Thorough topology validation (includes Levels 1–2 TDS checks) match dt.as_triangulation().validate() { @@ -521,7 +521,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Faithful embedding validation (Levels 1-4) match dt.as_triangulation().validate_embedding() { @@ -597,7 +597,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Delaunay property validation (Level 5) match dt.is_valid_delaunay() { diff --git a/docs/workflows.md b/docs/workflows.md index da5a1b18..ea58e0b5 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -28,7 +28,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // Optional verification (see docs/validation.md for when to use each): assert!(dt.as_triangulation().validate_embedding().is_ok()); // Levels 1-4 (faithful embedding) @@ -132,7 +132,7 @@ fn main() -> Result<(), RepairExampleError> { vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; let _stats = dt.repair_delaunay_with_flips()?; Ok(()) @@ -185,7 +185,7 @@ fn main() -> DelaunayResult<()> { vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; match dt.repair_delaunay_with_flips() { Ok(_stats) => {} @@ -242,7 +242,7 @@ fn main() -> Result<(), RepairExampleError> { vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; let outcome = dt .repair_delaunay_with_flips_advanced(DelaunayRepairHeuristicConfig::default())?; @@ -277,7 +277,7 @@ fn main() -> DelaunayResult<()> { let mut dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) ? // input coordinate canonicalization - .build::<()>()?; + .build()?; // Subsequent insertions are standard Euclidean insertions; canonicalize // additional points at the call site if they come from the same domain. @@ -318,7 +318,7 @@ fn main() -> DelaunayResult<()> { vertex![1.0, 0.0; data = 20]?, vertex![0.0, 1.0; data = 30]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).simplex_data_type::().build()?; // Read vertex data for (_key, vertex) in dt.vertices() { @@ -433,7 +433,7 @@ fn main() -> Result<(), DeletionExampleError> { vertex![0.2, 0.2, 0.2]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; let Some((vertex_key, _)) = dt.vertices().next() else { return Ok(()); }; @@ -496,7 +496,7 @@ fn main() -> Result<(), FlipExampleError> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; // k=1: split a simplex by inserting a vertex. let Some((simplex_key, _)) = dt.simplices().next() else { diff --git a/examples/delaunayize_repair.rs b/examples/delaunayize_repair.rs index 0db51cb7..9c103de2 100644 --- a/examples/delaunayize_repair.rs +++ b/examples/delaunayize_repair.rs @@ -20,7 +20,9 @@ //! cargo run --example delaunayize_repair //! ``` -use delaunay::prelude::construction::{DelaunayTriangulationConstructionError, vertex}; +use delaunay::prelude::construction::{ + DelaunayTriangulationBuilder, DelaunayTriangulationConstructionError, vertex, +}; use delaunay::prelude::delaunayize::*; use delaunay::prelude::geometry::CoordinateConversionError; use delaunay::prelude::pachner::{FacetError, FacetHandle, FlipError, PachnerMove, PachnerMoves}; @@ -84,7 +86,8 @@ fn already_delaunay_3d() -> Result<(), DelaunayizeRepairExampleError> { vertex![0.0, 0.0, 1.0]?, vertex![0.5, 0.5, 0.5]?, ]; - let mut dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::try_new(&vertices)?; + let mut dt: DelaunayTriangulation<_, (), (), 3> = + DelaunayTriangulationBuilder::new(&vertices).build()?; println!( " Built 3D triangulation: {} vertices, {} simplices", @@ -116,7 +119,8 @@ fn already_delaunay_4d() -> Result<(), DelaunayizeRepairExampleError> { vertex![0.0, 0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 0.0, 1.0]?, ]; - let mut dt: DelaunayTriangulation<_, (), (), 4> = DelaunayTriangulation::try_new(&vertices)?; + let mut dt: DelaunayTriangulation<_, (), (), 4> = + DelaunayTriangulationBuilder::new(&vertices).build()?; println!( " Built 4D triangulation: {} vertices, {} simplices", @@ -152,7 +156,8 @@ fn flip_then_repair_2d() -> Result<(), DelaunayizeRepairExampleError> { vertex![1.0, 1.0]?, vertex![3.0, 1.0]?, ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::try_new(&vertices)?; + let mut dt: DelaunayTriangulation<_, (), (), 2> = + DelaunayTriangulationBuilder::new(&vertices).build()?; println!( " Initial: {} vertices, {} simplices", @@ -232,7 +237,8 @@ fn custom_config_2d() -> Result<(), DelaunayizeRepairExampleError> { vertex![1.0, 1.0]?, vertex![0.5, 0.5]?, ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::try_new(&vertices)?; + let mut dt: DelaunayTriangulation<_, (), (), 2> = + DelaunayTriangulationBuilder::new(&vertices).build()?; let config = DelaunayizeConfig { topology_max_iterations: 10, diff --git a/examples/diagnostics.rs b/examples/diagnostics.rs index 5afc7fe0..0f0f5cf1 100644 --- a/examples/diagnostics.rs +++ b/examples/diagnostics.rs @@ -12,7 +12,8 @@ use delaunay::prelude::DelaunayValidationError; #[cfg(feature = "diagnostics")] use delaunay::prelude::construction::{ - DelaunayTriangulation, DelaunayTriangulationConstructionError, vertex, + ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationBuilder, + DelaunayTriangulationConstructionError, vertex, }; #[cfg(feature = "diagnostics")] use delaunay::prelude::diagnostics::{ @@ -21,9 +22,7 @@ use delaunay::prelude::diagnostics::{ #[cfg(feature = "diagnostics")] use delaunay::prelude::geometry::{AdaptiveKernel, CoordinateConversionError}; #[cfg(feature = "diagnostics")] -use delaunay::prelude::pachner::{FacetError, FacetHandle, PachnerMove, PachnerMoves}; -#[cfg(feature = "diagnostics")] -use delaunay::prelude::validation::DelaunayTriangulationValidationError; +use delaunay::prelude::tds::InvariantError; #[cfg(feature = "diagnostics")] #[derive(Debug, thiserror::Error)] enum DiagnosticsExampleError { @@ -34,9 +33,7 @@ enum DiagnosticsExampleError { #[error(transparent)] CoordinateConversion(#[from] CoordinateConversionError), #[error(transparent)] - Facet(#[from] FacetError), - #[error("expected at least one public k=2 flip to produce a Delaunay violation")] - NoDelaunayViolatingFlip, + Invariant(#[from] InvariantError), } #[cfg(feature = "diagnostics")] @@ -77,7 +74,8 @@ fn report_valid_triangulation() -> Result<(), DiagnosticsExampleError> { vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::try_new(&vertices)?; + let dt: DelaunayTriangulation<_, (), (), 3> = + DelaunayTriangulationBuilder::new(&vertices).build()?; let report = delaunay_violation_report(dt.tds(), None)?; @@ -90,13 +88,13 @@ fn report_valid_triangulation() -> Result<(), DiagnosticsExampleError> { Ok(()) } -/// Applies a public topology edit that breaks Delaunayness, then reports the violation. +/// Imports valid explicit connectivity that is not Delaunay, then reports the violation. #[cfg(feature = "diagnostics")] fn report_non_delaunay_triangulation() -> Result<(), DiagnosticsExampleError> { let dt = build_non_delaunay_triangulation_2d()?; let report = delaunay_violation_report(dt.tds(), None)?; - println!("Non-Delaunay 2D triangulation after an explicit k=2 flip:"); + println!("Explicit non-Delaunay 2D triangulation:"); println!(" vertices: {}", report.number_of_vertices); println!(" simplices: {}", report.number_of_simplices); println!( @@ -115,48 +113,23 @@ fn report_non_delaunay_triangulation() -> Result<(), DiagnosticsExampleError> { Ok(()) } -/// Builds a valid 2D triangulation and returns a clone after a k=2 flip creates a violation. +/// Builds a valid Levels 1-4 triangulation whose prescribed diagonal violates Delaunayness. #[cfg(feature = "diagnostics")] fn build_non_delaunay_triangulation_2d() -> Result, (), (), 2>, DiagnosticsExampleError> { let vertices = vec![ vertex![0.0, 0.0]?, vertex![4.0, 0.0]?, - vertex![4.0, 4.0]?, - vertex![0.0, 4.0]?, - vertex![2.0, 2.0]?, - vertex![1.0, 1.0]?, - vertex![3.0, 1.0]?, + vertex![4.0, 2.0]?, + vertex![1.0, 2.0]?, ]; - let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::try_new(&vertices)?; - - for (simplex_key, simplex) in dt.simplices() { - if let Some(neighbors) = simplex.neighbors() { - for (facet_index, neighbor) in neighbors.enumerate() { - if neighbor.is_none() { - continue; - } - - let Ok(facet_index) = u8::try_from(facet_index) else { - continue; - }; - let facet = FacetHandle::try_new(dt.tds(), simplex_key, facet_index)?; - let mut trial = dt.clone(); - let Ok(proposal) = trial.propose_pachner(PachnerMove::K2 { facet }) else { - continue; - }; - if proposal.attempt_on(&mut trial).is_ok() - && trial.as_triangulation().validate().is_ok() - && matches!( - trial.is_valid_delaunay(), - Err(DelaunayTriangulationValidationError::VerificationFailed { .. }) - ) - { - return Ok(trial); - } - } - } - } - - Err(DiagnosticsExampleError::NoDelaunayViolatingFlip) + let simplices = vec![vec![0, 1, 2], vec![0, 2, 3]]; + let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) + .map_err(DelaunayTriangulationConstructionError::from)? + .construction_options(ConstructionOptions::default().without_final_delaunay_enforcement()) + .build()?; + + dt.as_triangulation().validate()?; + assert!(dt.is_valid_delaunay().is_err()); + Ok(dt) } diff --git a/examples/into_from_conversions.rs b/examples/into_from_conversions.rs index 833069ea..a53b755e 100644 --- a/examples/into_from_conversions.rs +++ b/examples/into_from_conversions.rs @@ -19,9 +19,8 @@ //! //! Run this example with: `cargo run --example into_from_conversions` -use delaunay::prelude::construction::vertex; -use delaunay::prelude::geometry::CoordinateConversionError; -use delaunay::prelude::query::*; +use delaunay::prelude::construction::{Vertex, vertex}; +use delaunay::prelude::geometry::{Coordinate, CoordinateConversionError, Point}; /// Demonstrates Into/From trait conversions from vertices and points to coordinate arrays. /// diff --git a/examples/numerical_robustness.rs b/examples/numerical_robustness.rs index 786635a3..90833ac0 100644 --- a/examples/numerical_robustness.rs +++ b/examples/numerical_robustness.rs @@ -8,7 +8,8 @@ //! default adaptive construction path on a small point set. use delaunay::prelude::construction::{ - DelaunayTriangulation, DelaunayTriangulationConstructionError, vertex, + DelaunayTriangulation, DelaunayTriangulationBuilder, DelaunayTriangulationConstructionError, + vertex, }; use delaunay::prelude::geometry::{ AdaptiveKernel, CircumcenterError, CoordinateConversionError, CoordinateValidationError, @@ -107,7 +108,7 @@ fn build_with_adaptive_kernel() -> Result<(), NumericalRobustnessExampleError> { ]; let dt: DelaunayTriangulation, (), (), 3> = - DelaunayTriangulation::try_new(&vertices)?; + DelaunayTriangulationBuilder::new(&vertices).build()?; dt.validate()?; println!( diff --git a/examples/topology_editing.rs b/examples/topology_editing.rs index 830e70e4..38545766 100644 --- a/examples/topology_editing.rs +++ b/examples/topology_editing.rs @@ -28,25 +28,19 @@ use delaunay::prelude::construction::{ vertex, }; use delaunay::prelude::geometry::{ - CircumcenterError, Coordinate, CoordinateConversionError, Point, circumcenter, hypot, + AdaptiveKernel, CircumcenterError, Coordinate, CoordinateConversionError, Point, circumcenter, + hypot, }; use delaunay::prelude::insertion::InsertionError; use delaunay::prelude::pachner::{ - EdgeKey, EdgeKeyError, FacetError, FacetHandle, FlipError, PachnerMove, PachnerMoveResult, - PachnerMoves, RidgeHandle, VertexKey, + EdgeKey, EdgeKeyError, FacetError, FacetHandle, FlipError, PachnerMove, PachnerMoves, + RidgeHandle, TriangleHandle, TriangleHandleError, Vertex, VertexKey, }; -use delaunay::prelude::tds::TdsError; +use delaunay::prelude::tds::{InvariantError, TdsError}; use delaunay::prelude::validation::DelaunayTriangulationValidationError; type ExampleResult = Result; - -/// Parses and commits one Pachner request on the same topology owner. -fn attempt_pachner_move( - dt: &mut impl PachnerMoves, - pachner_move: PachnerMove<(), D>, -) -> Result, FlipError> { - dt.propose_pachner(pachner_move)?.attempt_on(dt) -} +type Dt3 = DelaunayTriangulation, (), (), 3>; #[derive(Debug, thiserror::Error)] enum TopologyEditingExampleError { @@ -61,10 +55,14 @@ enum TopologyEditingExampleError { #[error(transparent)] Edge(#[from] EdgeKeyError), #[error(transparent)] + Triangle(#[from] TriangleHandleError), + #[error(transparent)] Facet(#[from] FacetError), #[error(transparent)] Tds(#[from] TdsError), #[error(transparent)] + Invariant(#[from] InvariantError), + #[error(transparent)] Circumcenter(#[from] CircumcenterError), #[error(transparent)] CoordinateConversion(#[from] CoordinateConversionError), @@ -79,6 +77,16 @@ enum TopologyEditingExampleError { }, #[error("{demo} has no interior facet")] NoInteriorFacet { demo: &'static str }, + #[error("{demo} has no roundtrip-capable k=2 facet")] + NoRoundtripK2Facet { demo: &'static str }, + #[error("{demo} has no flippable k=3 ridge")] + NoFlippableRidge { demo: &'static str }, + #[error("{demo} expected {expected} inserted-face vertices, got {actual}")] + UnexpectedInsertedFaceVertices { + demo: &'static str, + expected: usize, + actual: usize, + }, } fn main() -> ExampleResult { @@ -124,7 +132,7 @@ fn builder_api_2d() -> ExampleResult { // Build initial triangulation let vertices = vec![vertex![0.0, 0.0]?, vertex![4.0, 0.0]?, vertex![2.0, 3.0]?]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; println!("Initial triangle:"); print_stats_2d(&dt); @@ -158,7 +166,7 @@ fn pachner_2d_k1() -> ExampleResult { let vertices = vec![vertex![0.0, 0.0]?, vertex![3.0, 0.0]?, vertex![1.5, 2.5]?]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; println!("Initial triangle:"); print_stats_2d(&dt); @@ -258,7 +266,7 @@ fn pachner_2d_k2() -> ExampleResult { vertex![0.0, 2.0]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; println!("Initial square (2 triangles):"); print_stats_2d(&dt); @@ -333,7 +341,7 @@ fn builder_api_3d() -> ExampleResult { vertex![1.0, 0.5, 1.5]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; println!("Initial tetrahedron:"); print_stats_3d(&dt); @@ -371,7 +379,7 @@ fn pachner_3d_k1() -> ExampleResult { vertex![1.0, 0.5, 1.5]?, ]; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; println!("Initial tetrahedron:"); print_stats_3d(&dt); @@ -461,59 +469,34 @@ fn pachner_3d_k2() -> ExampleResult { println!("Note: k=2 flips in 3D replace 2 tetrahedra with 3 (and vice versa)"); println!(" This demonstrates the inverse flip operation available in 3D+\n"); - // Build a simple regular tetrahedron and add one interior point - let vertices = vec![ - vertex![0.0, 0.0, 0.0]?, - vertex![2.0, 0.0, 0.0]?, - vertex![1.0, 1.8, 0.0]?, - vertex![1.0, 0.6, 1.6]?, - vertex![1.0, 0.6, 0.4]?, // Interior point - ]; + let mut dt = build_stable_pachner_dt_3d()?; - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + println!("Stable 3D fixture:"); + print_stats_3d(&dt); - println!("Triangulation with interior point:"); + let facet = find_roundtrip_k2_facet_3d(&dt)?.ok_or( + TopologyEditingExampleError::NoRoundtripK2Facet { + demo: "3D k=2 demo", + }, + )?; + let flip_info = dt + .propose_pachner(PachnerMove::K2 { facet })? + .attempt_on(&mut dt)?; + println!("\nApplied k=2 flip:"); print_stats_3d(&dt); + println!(" Removed: {} simplices", flip_info.removed_simplices.len()); + println!(" Inserted: {} simplices", flip_info.new_simplices.len()); - // Find an interior facet to flip - if let Some(facet) = find_interior_facet(&dt)? { - match attempt_pachner_move(&mut dt, PachnerMove::K2 { facet }) { - Ok(flip_info) => { - println!("\nApplied k=2 flip:"); - print_stats_3d(&dt); - println!(" Removed: {} simplices", flip_info.removed_simplices.len()); - println!(" Inserted: {} simplices", flip_info.new_simplices.len()); - - // Try inverse - println!("\nApplying k=2 inverse:"); - let edge = EdgeKey::try_new( - dt.tds(), - flip_info.inserted_face_vertices[0], - flip_info.inserted_face_vertices[1], - )?; - - match attempt_pachner_move(&mut dt, PachnerMove::K2Inverse { edge }) { - Ok(_) => { - println!("After k=2 inverse:"); - print_stats_3d(&dt); - println!("\n✓ k=2 flip roundtrip successful in 3D"); - } - Err(e) => { - println!("⚠️ k=2 inverse failed: {e}"); - println!( - " (This demonstrates that not all configurations support inverse flips)" - ); - } - } - } - Err(e) => { - println!("⚠️ k=2 flip not possible: {e}"); - println!(" (This demonstrates geometric constraints on flip operations)"); - } - } - } else { - println!("⚠️ No interior facet found for k=2 flip demo"); - } + println!("\nApplying k=2 inverse:"); + let edge = inserted_edge_3d(&dt, &flip_info.inserted_face_vertices, "3D k=2 demo")?; + let inverse_info = dt + .propose_pachner(PachnerMove::K2Inverse { edge })? + .attempt_on(&mut dt)?; + assert!(!inverse_info.removed_simplices.is_empty()); + dt.validate()?; + println!("After k=2 inverse:"); + print_stats_3d(&dt); + println!("\n✓ k=2 flip roundtrip successful in 3D"); Ok(()) } @@ -524,47 +507,34 @@ fn pachner_3d_k3() -> ExampleResult { println!("-----------------------------------------\n"); println!("Note: k=3 flips are only available in 3D and higher dimensions\n"); - // k=3 flips are more complex and have strict geometric requirements - // For this demo, we'll show that the API is available but the geometric - // conditions may not always be satisfied - println!("k=3 flips replace 3 tetrahedra around a ridge (edge) with"); println!("2 tetrahedra around a triangle (and vice versa).\n"); - println!("This operation has strict geometric requirements:"); + println!("This operation has strict geometric requirements, so the example"); + println!("searches a stable fixture for an accepted ridge before mutating:"); println!(" - The ridge must be surrounded by exactly 3 tetrahedra"); println!(" - The resulting configuration must not be degenerate"); println!(" - The flip must preserve manifold topology\n"); - println!("Building a simple 3D triangulation..."); - let vertices = vec![ - vertex![0.0, 0.0, 0.0]?, - vertex![2.0, 0.0, 0.0]?, - vertex![1.0, 1.7, 0.0]?, - vertex![1.0, 0.6, 1.4]?, - ]; - - let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + println!("Building a stable 3D fixture..."); + let mut dt = build_stable_pachner_dt_3d()?; print_stats_3d(&dt); - // Try to find and flip a ridge - if let Some(ridge) = find_flippable_ridge_3d(&dt)? { - match attempt_pachner_move(&mut dt, PachnerMove::K3 { ridge }) { - Ok(flip_info) => { - println!("\n✓ k=3 flip succeeded:"); - print_stats_3d(&dt); - println!(" Removed: {} simplices", flip_info.removed_simplices.len()); - println!(" Inserted: {} simplices", flip_info.new_simplices.len()); - } - Err(e) => { - println!("\n⚠️ k=3 flip not applicable: {e}"); - println!(" (Geometric constraints not satisfied for this configuration)"); - } - } - } else { - println!("\n⚠️ No ridges found in this simple triangulation"); - } + let ridge = + find_flippable_ridge_3d(&dt)?.ok_or(TopologyEditingExampleError::NoFlippableRidge { + demo: "3D k=3 demo", + })?; + let flip_info = dt + .propose_pachner(PachnerMove::K3 { ridge })? + .attempt_on(&mut dt)?; + dt.as_triangulation().validate()?; + let triangle = inserted_triangle(&flip_info.inserted_face_vertices, "3D k=3 demo")?; + println!("\n✓ k=3 flip succeeded:"); + print_stats_3d(&dt); + println!(" Removed: {} simplices", flip_info.removed_simplices.len()); + println!(" Inserted: {} simplices", flip_info.new_simplices.len()); + println!(" Inverse candidate triangle: {triangle:?}"); println!("\n✓ k=3 move API demonstrated (Pachner Move API - 3D+ only)"); Ok(()) } @@ -589,6 +559,27 @@ fn print_stats_3d(dt: &DelaunayTriangulation) { ); } +/// Builds the stable 3D local-edit fixture used by the successful k=2/k=3 demos. +fn build_stable_pachner_dt_3d() -> ExampleResult { + let vertices = stable_pachner_vertices_3d()?; + Ok(DelaunayTriangulationBuilder::new(&vertices).build()?) +} + +/// Returns the stable 3D point set used to find accepted public Pachner moves. +fn stable_pachner_vertices_3d() -> Result>, CoordinateConversionError> { + Ok(vec![ + vertex![0.0, 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![0.20, 0.20, 0.20]?, + vertex![0.75, 0.15, 0.30]?, + vertex![0.20, 0.70, 0.35]?, + vertex![0.30, 0.25, 0.80]?, + vertex![0.65, 0.60, 0.55]?, + ]) +} + fn find_interior_facet( dt: &DelaunayTriangulation, ) -> ExampleResult> { @@ -612,26 +603,101 @@ fn find_interior_facet( Ok(None) } -fn find_flippable_ridge_3d( - dt: &DelaunayTriangulation, -) -> ExampleResult> { - // Try to find any ridge (edge in 3D shared by multiple tetrahedra) +/// Finds a 3D k=2 facet whose forward move and public inverse both succeed. +fn find_roundtrip_k2_facet_3d(dt: &Dt3) -> ExampleResult> { for (simplex_key, simplex) in dt.simplices() { - let vertex_count = simplex.number_of_vertices(); - // Try each pair of vertices (each defines a ridge) - for i in 0..vertex_count.saturating_sub(1) { - let Ok(omit_a) = u8::try_from(i) else { + let Some(neighbors) = simplex.neighbors() else { + continue; + }; + for (facet_idx, neighbor) in neighbors.enumerate() { + if neighbor.is_none() { + continue; + } + let Ok(facet_idx) = u8::try_from(facet_idx) else { + continue; + }; + let facet = FacetHandle::try_new(dt.tds(), simplex_key, facet_idx)?; + if dt.propose_pachner(PachnerMove::K2 { facet }).is_err() { + continue; + } + let mut trial = dt.clone(); + let Ok(proposal) = trial.propose_pachner(PachnerMove::K2 { facet }) else { + continue; + }; + let Ok(info) = proposal.attempt_on(&mut trial) else { + continue; + }; + let Ok(edge) = inserted_edge_3d(&trial, &info.inserted_face_vertices, "3D k=2 demo") + else { continue; }; - let Ok(omit_b) = u8::try_from(i + 1) else { + let Ok(inverse) = trial.propose_pachner(PachnerMove::K2Inverse { edge }) else { continue; }; - let ridge = RidgeHandle::try_new(dt.tds(), simplex_key, omit_a, omit_b)?; + if inverse.attempt_on(&mut trial).is_ok() && trial.validate().is_ok() { + return Ok(Some(facet)); + } + } + } + Ok(None) +} - // Just return the first one we find - // (In practice, you'd want to check if it's actually flippable) - return Ok(Some(ridge)); +/// Finds a 3D k=3 ridge whose forward move succeeds and preserves topology. +fn find_flippable_ridge_3d(dt: &Dt3) -> ExampleResult> { + for (simplex_key, simplex) in dt.simplices() { + let vertex_count = simplex.number_of_vertices(); + for i in 0..vertex_count { + for j in (i + 1)..vertex_count { + let Ok(omit_a) = u8::try_from(i) else { + continue; + }; + let Ok(omit_b) = u8::try_from(j) else { + continue; + }; + let ridge = RidgeHandle::try_new(dt.tds(), simplex_key, omit_a, omit_b)?; + let mut trial = dt.clone(); + let Ok(proposal) = trial.propose_pachner(PachnerMove::K3 { ridge }) else { + continue; + }; + if proposal.attempt_on(&mut trial).is_ok() + && trial.as_triangulation().validate().is_ok() + { + return Ok(Some(ridge)); + } + } } } Ok(None) } + +/// Parses the inserted face of a k=2 move into the edge expected by its inverse. +fn inserted_edge_3d( + dt: &Dt3, + vertices: &[VertexKey], + demo: &'static str, +) -> ExampleResult { + let [a, b] = vertices else { + return Err( + TopologyEditingExampleError::UnexpectedInsertedFaceVertices { + demo, + expected: 2, + actual: vertices.len(), + }, + ); + }; + Ok(EdgeKey::try_new(dt.tds(), *a, *b)?) +} + +/// Parses the inserted face of a k=3 move into its inverse triangle candidate. +fn inserted_triangle(vertices: &[VertexKey], demo: &'static str) -> ExampleResult { + let [a, b, c] = vertices else { + return Err( + TopologyEditingExampleError::UnexpectedInsertedFaceVertices { + demo, + expected: 3, + actual: vertices.len(), + }, + ); + }; + Ok(TriangleHandle::try_new(*a, *b, *c)?) +} diff --git a/examples/triangulation_and_hull.rs b/examples/triangulation_and_hull.rs index 10bce762..336b072a 100644 --- a/examples/triangulation_and_hull.rs +++ b/examples/triangulation_and_hull.rs @@ -15,7 +15,8 @@ use std::num::NonZeroUsize; use std::time::Instant; use delaunay::prelude::construction::{ - ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationConstructionError, RetryPolicy, + ConstructionOptions, DelaunayTriangulation, DelaunayTriangulationBuilder, + DelaunayTriangulationConstructionError, RetryPolicy, }; use delaunay::prelude::generators::{ RandomPointGenerationError, generate_random_points_in_range_seeded, @@ -88,8 +89,9 @@ fn run_case( println!("{label} Delaunay triangulation ({point_count} seeded points)"); let start = Instant::now(); - let dt: WorkflowTriangulation = - DelaunayTriangulation::try_new_with_options(&vertices, options)?; + let dt: WorkflowTriangulation = DelaunayTriangulationBuilder::new(&vertices) + .construction_options(options) + .build()?; println!(" construction: {:?}", start.elapsed()); println!(" vertices: {}", dt.number_of_vertices()); println!(" simplices: {}", dt.number_of_simplices()); diff --git a/pyproject.toml b/pyproject.toml index c7ce7e92..247c4995 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -230,7 +230,7 @@ dev = [ "semgrep==1.168.0", "shellcheck-py==0.11.0.1", "shfmt-py==4.0.0", - "ty==0.0.55", + "ty==0.0.56", "yamllint==1.38.0", ] notebooks = [ diff --git a/scripts/archive_changelog.py b/scripts/archive_changelog.py index 350afbee..d887d8eb 100644 --- a/scripts/archive_changelog.py +++ b/scripts/archive_changelog.py @@ -17,8 +17,6 @@ archive-changelog --archive-dir docs/archive/changelog """ -from __future__ import annotations - import argparse import logging import os diff --git a/scripts/ci/filter_codacy_sarif.py b/scripts/ci/filter_codacy_sarif.py index e21effaa..ff3d4538 100644 --- a/scripts/ci/filter_codacy_sarif.py +++ b/scripts/ci/filter_codacy_sarif.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 """Filter and split Codacy SARIF before GitHub Code Scanning upload.""" -from __future__ import annotations - import argparse import copy import json diff --git a/scripts/notebook_check.py b/scripts/notebook_check.py index 7c2482a0..cf4a589a 100644 --- a/scripts/notebook_check.py +++ b/scripts/notebook_check.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 """Inspect, lint, and optionally execute Jupyter notebooks.""" -from __future__ import annotations - import argparse import ast import json diff --git a/scripts/postprocess_changelog.py b/scripts/postprocess_changelog.py index 126d10ed..cded20f4 100644 --- a/scripts/postprocess_changelog.py +++ b/scripts/postprocess_changelog.py @@ -16,8 +16,6 @@ postprocess-changelog path/to/CHANGELOG.md """ -from __future__ import annotations - import argparse import re import sys diff --git a/scripts/semgrep_fixture_config.py b/scripts/semgrep_fixture_config.py index d985120f..c5bc3f37 100644 --- a/scripts/semgrep_fixture_config.py +++ b/scripts/semgrep_fixture_config.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 """Generate per-fixture Semgrep configs from fixture annotations.""" -from __future__ import annotations - import argparse import re import sys diff --git a/scripts/tag_release.py b/scripts/tag_release.py index 5bc3af07..0ed0dc71 100644 --- a/scripts/tag_release.py +++ b/scripts/tag_release.py @@ -12,8 +12,6 @@ Ported from the la-stack project's tag_release.py. """ -from __future__ import annotations - import argparse import logging import re diff --git a/scripts/tests/conftest.py b/scripts/tests/conftest.py index c5dcc098..fbe558f1 100644 --- a/scripts/tests/conftest.py +++ b/scripts/tests/conftest.py @@ -4,8 +4,6 @@ Provides common testing utilities that can be reused across multiple test files. """ -from __future__ import annotations - import os import subprocess import sys diff --git a/scripts/tests/test_archive_changelog.py b/scripts/tests/test_archive_changelog.py index 58fabd89..3d7e4056 100644 --- a/scripts/tests/test_archive_changelog.py +++ b/scripts/tests/test_archive_changelog.py @@ -1,7 +1,5 @@ """Tests for archive_changelog.py — parsing, grouping, split/archive, and idempotency.""" -from __future__ import annotations - import logging from typing import TYPE_CHECKING diff --git a/scripts/tests/test_filter_codacy_sarif.py b/scripts/tests/test_filter_codacy_sarif.py index 4bacc144..05a6212a 100644 --- a/scripts/tests/test_filter_codacy_sarif.py +++ b/scripts/tests/test_filter_codacy_sarif.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 """Tests for Codacy SARIF filtering before GitHub Code Scanning upload.""" -from __future__ import annotations - import json from typing import TYPE_CHECKING diff --git a/scripts/tests/test_notebook_check.py b/scripts/tests/test_notebook_check.py index 296024e2..dcecdd42 100644 --- a/scripts/tests/test_notebook_check.py +++ b/scripts/tests/test_notebook_check.py @@ -1,7 +1,5 @@ """Tests for notebook_check.py notebook linting diagnostics.""" -from __future__ import annotations - import json import os import subprocess diff --git a/scripts/tests/test_postprocess_changelog.py b/scripts/tests/test_postprocess_changelog.py index db1f3cc5..cd4237c8 100644 --- a/scripts/tests/test_postprocess_changelog.py +++ b/scripts/tests/test_postprocess_changelog.py @@ -1,7 +1,5 @@ """Tests for postprocess_changelog.py — trailing blanks, reflow, code blocks, summaries.""" -from __future__ import annotations - from typing import TYPE_CHECKING from postprocess_changelog import ( diff --git a/scripts/tests/test_readme_citation_mirror.py b/scripts/tests/test_readme_citation_mirror.py index 536cafe1..059f530f 100644 --- a/scripts/tests/test_readme_citation_mirror.py +++ b/scripts/tests/test_readme_citation_mirror.py @@ -1,7 +1,5 @@ """Tests for README and CITATION.cff documentation coupling.""" -from __future__ import annotations - import re from pathlib import Path diff --git a/scripts/tests/test_semgrep_fixture_config.py b/scripts/tests/test_semgrep_fixture_config.py index 3cf22e8e..1b479a63 100644 --- a/scripts/tests/test_semgrep_fixture_config.py +++ b/scripts/tests/test_semgrep_fixture_config.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 """Tests for per-fixture Semgrep config generation.""" -from __future__ import annotations - from typing import TYPE_CHECKING from semgrep_fixture_config import annotated_rule_ids, main, write_fixture_config diff --git a/semgrep.yaml b/semgrep.yaml index 6e74ceff..fed018a1 100644 --- a/semgrep.yaml +++ b/semgrep.yaml @@ -568,17 +568,19 @@ rules: languages: - generic severity: WARNING - message: "Use try_new*/try_with_* for fallible triangulation construction and ConvexHull::try_from_triangulation for fallible hull snapshots." + message: "Use the fluent Delaunay builder for fallible triangulation construction and ConvexHull::try_from_triangulation for fallible hull snapshots." metadata: category: correctness tracking_issue: "https://github.com/acgetchell/delaunay/issues/459" rationale: >- Batch Delaunay construction and hull snapshot construction parse raw invariant-bearing topology and geometry. Public names must expose that - fallibility with `try_new*`, `try_with_*`, or `try_from_*`. Empty + fallibility with the fluent builder terminal or `try_from_*`. Empty triangulation constructors remain infallible because they accept no - caller geometry or topology, and builder `new` remains infallible - because validation is explicitly deferred to `build`. + caller geometry or topology, builder `new` remains infallible because + validation is explicitly deferred to `build`, and builder implementation + hooks stay crate-private with backend names instead of public constructor + spelling. paths: include: - "/src/**/*.rs" @@ -594,6 +596,95 @@ rules: \bDelaunayTriangulation\b\s*(?:::\s*<[^;\n]+>\s*)?::\s*(?:new|new_with_(?:construction_statistics|options(?:_and_construction_statistics)?|topology_guarantee)|with_(?:kernel|topology_guarantee(?:_and_options)?|options_and_statistics))\s*\( - pattern-regex: '\bConvexHull\b\s*(?:::\s*<[^;\n]+>\s*)?::\s*from_triangulation\s*\(' + - id: delaunay.rust.no-legacy-delaunay-try-new-constructors + languages: + - generic + severity: WARNING + message: >- + Use DelaunayTriangulationBuilder::new(...).build() or + DelaunayTriangulation::builder(...).build(), not legacy + DelaunayTriangulation::try_new* or try_with_* wrappers. + metadata: + category: correctness + tracking_issue: "https://github.com/acgetchell/delaunay/issues/459" + rationale: >- + The fluent builder is the canonical Delaunay construction API. Keeping + `DelaunayTriangulation::try_new*` or `try_with_*` call sites or compatibility shims + recreates a parallel constructor family and hides the option/statistics/ + topology/kernel stages that should remain visible in domain order. + paths: + include: + - "/src/**/*.rs" + - "/tests/**/*.rs" + - "/benches/**/*.rs" + - "/examples/**/*.rs" + - "/docs/**/*.md" + - "/README.md" + - "/tests/semgrep/src/project_rules/rust_style.rs" + exclude: + - "/docs/archive/**" + pattern-regex: >- + \b(?:[A-Za-z_][A-Za-z0-9_]*DelaunayTriangulation|DelaunayTriangulation)\b\s*(?:::\s*<[^;\n]+>\s*)?::\s*try_(?:new(?:_with_(?:construction_statistics|options(?:_and_construction_statistics)?|topology_guarantee))?|with_(?:kernel|topology_guarantee(?:_and_options)?|options_and_statistics))\s*\( + + - id: delaunay.rust.no-legacy-delaunay-try-new-constructor-definitions + languages: + - rust + severity: WARNING + message: >- + Do not define legacy DelaunayTriangulation::try_new* or try_with_* wrappers; + keep Delaunay construction on the fluent builder terminals. + metadata: + category: correctness + tracking_issue: "https://github.com/acgetchell/delaunay/issues/459" + rationale: >- + Hidden compatibility shims recreate the same parallel constructor family + as public call sites. Builder backends should be crate-private and named + by their implementation role, not by legacy public constructor spelling. + paths: + include: + - "/src/delaunay/**/*.rs" + - "/tests/semgrep/src/project_rules/rust_style.rs" + exclude: + - "/docs/archive/**" + patterns: + - pattern-either: + - pattern-inside: | + impl DelaunayTriangulation { + ... + } + - pattern-inside: | + impl DelaunayTriangulation + where + K: Kernel, + U: DataType, + V: DataType, + { + ... + } + - pattern-inside: | + impl DelaunayTriangulation, (), (), D> { + ... + } + - pattern-either: + - pattern: pub fn try_new(...) -> $RET { ... } + - pattern: pub fn try_new_with_construction_statistics(...) -> $RET { ... } + - pattern: pub fn try_new_with_options(...) -> $RET { ... } + - pattern: pub fn try_new_with_options_and_construction_statistics(...) -> $RET { ... } + - pattern: pub fn try_new_with_topology_guarantee(...) -> $RET { ... } + - pattern: pub fn try_with_kernel(...) -> $RET { ... } + - pattern: pub fn try_with_topology_guarantee(...) -> $RET { ... } + - pattern: pub fn try_with_topology_guarantee_and_options(...) -> $RET { ... } + - pattern: pub fn try_with_options_and_statistics(...) -> $RET { ... } + - pattern: pub(crate) fn try_new(...) -> $RET { ... } + - pattern: pub(crate) fn try_new_with_construction_statistics(...) -> $RET { ... } + - pattern: pub(crate) fn try_new_with_options(...) -> $RET { ... } + - pattern: pub(crate) fn try_new_with_options_and_construction_statistics(...) -> $RET { ... } + - pattern: pub(crate) fn try_new_with_topology_guarantee(...) -> $RET { ... } + - pattern: pub(crate) fn try_with_kernel(...) -> $RET { ... } + - pattern: pub(crate) fn try_with_topology_guarantee(...) -> $RET { ... } + - pattern: pub(crate) fn try_with_topology_guarantee_and_options(...) -> $RET { ... } + - pattern: pub(crate) fn try_with_options_and_statistics(...) -> $RET { ... } + - id: delaunay.rust.no-infallible-fallible-constructor-definitions languages: - generic diff --git a/src/core/adjacency.rs b/src/core/adjacency.rs index 2c4244a0..ae5000c6 100644 --- a/src/core/adjacency.rs +++ b/src/core/adjacency.rs @@ -152,7 +152,7 @@ pub struct SimplexNeighborIndex<'tds> { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let adjacency = dt.as_triangulation().adjacency()?; /// /// assert_eq!(adjacency.number_of_edges(), 6); @@ -404,7 +404,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let incidence = tri.incidence().unwrap(); let edge_index = tri.build_edge_index().unwrap(); diff --git a/src/core/algorithms/flips.rs b/src/core/algorithms/flips.rs index 75820fb9..1cd052a5 100644 --- a/src/core/algorithms/flips.rs +++ b/src/core/algorithms/flips.rs @@ -4344,7 +4344,7 @@ pub struct FlipInfo { /// ]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -4547,7 +4547,7 @@ impl TryFrom<[VertexKey; 3]> for TriangleHandle { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -7434,7 +7434,7 @@ where /// ]; /// /// let dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let kernel = AdaptiveKernel::::new(); /// /// // Fast O(N) verification @@ -7489,7 +7489,7 @@ where /// ]; /// /// let dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Topology-aware O(N) verification /// assert!(verify_delaunay_for_triangulation(dt.as_triangulation()).is_ok()); @@ -16662,7 +16662,7 @@ mod tests { vertex!([1.0, 0.2]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds(); let local_simplex = tds.simplex_keys().next().unwrap(); let outcome = RepairAttemptOutcome { @@ -16694,7 +16694,7 @@ mod tests { vertex!([1.0, 0.2]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let before = snapshot_topology(&tds); let kernel = AdaptiveKernel::::new(); @@ -16725,7 +16725,7 @@ mod tests { vertex!([1.0, 0.2]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let kernel = AdaptiveKernel::::new(); diff --git a/src/core/algorithms/incremental_insertion.rs b/src/core/algorithms/incremental_insertion.rs index 19287c49..550f9c30 100644 --- a/src/core/algorithms/incremental_insertion.rs +++ b/src/core/algorithms/incremental_insertion.rs @@ -2144,7 +2144,7 @@ impl InsertionError { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// let Some(vkey) = tds.vertex_keys().next() else { return Ok(()); }; /// let boundary_facets: Vec = Vec::new(); @@ -3124,7 +3124,7 @@ where /// delaunay::vertex![1.0, 1.1]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 2> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// /// let Some((simplex_key, facet_idx, neighbor_key)) = tds @@ -3366,7 +3366,7 @@ where /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 2> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// /// let _changed_slots = repair_neighbor_pointers(&mut tds)?; @@ -4895,7 +4895,7 @@ mod tests { fn []() { // Create initial simplex let vertices = $initial_vertices; - let mut dt = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); // Insert new vertex @@ -5007,7 +5007,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let invalid_vkey = VertexKey::from(KeyData::from_ffi(u64::MAX)); @@ -5033,7 +5033,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let new_vkey = tds @@ -5061,7 +5061,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let new_vkey = tds @@ -5093,7 +5093,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let mut invalid_simplices = SimplexKeyBuffer::new(); @@ -5353,7 +5353,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let new_vkey = tds @@ -5373,7 +5373,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); // Insert a new vertex (apex) @@ -5459,7 +5459,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let simplex_key = tds.simplex_keys().next().unwrap(); @@ -6346,7 +6346,7 @@ mod tests { #[test] fn []() { let vertices = $initial_vertices; - let mut dt = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); // Verify all neighbor pointers are initially valid @@ -6434,7 +6434,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), vertex!([1.0, 1.1]).unwrap(), // break cocircular symmetry ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); // Remove all neighbor pointers. @@ -6471,7 +6471,7 @@ mod tests { #[test] fn []() { let vertices = $initial_vertices; - let mut dt = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let (simplex_key, facet_idx, neighbor_key, _) = first_neighbor_pair(tds).expect("test triangulation should have adjacent simplices"); @@ -6619,7 +6619,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), vertex!([1.0, 1.1]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let (simplex_key, facet_idx, neighbor_key, _) = first_neighbor_pair(tds).expect("test triangulation should have adjacent simplices"); @@ -6707,7 +6707,7 @@ mod tests { vertex!([1.0, 1.1]).unwrap(), vertex!([0.5, 0.35]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let (simplex_key, facet_idx, _neighbor_key, _) = first_neighbor_pair(tds).expect("test triangulation should have adjacent simplices"); @@ -6732,7 +6732,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let kernel = FastKernel::::new(); @@ -6753,7 +6753,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds_mut(); let kernel = FastKernel::::new(); @@ -6824,7 +6824,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let point = Point::try_new([0.5, 0.0]).expect("finite point coordinates"); // on boundary edge let facet = find_boundary_edge_split_facet(dt.tds(), &point).unwrap(); @@ -6838,7 +6838,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let point = Point::try_new([0.0, 0.0]).expect("finite point coordinates"); let err = find_boundary_edge_split_facet(dt.tds(), &point).unwrap_err(); @@ -6862,7 +6862,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let point = Point::try_new([2.0, 0.0]).expect("finite point coordinates"); // collinear with an edge line, outside segment let facet = find_boundary_edge_split_facet(dt.tds(), &point).unwrap(); @@ -6876,7 +6876,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let point = Point::try_new([f64::from_bits(1.0_f64.to_bits() + 1), 0.0]) .expect("finite point coordinates"); @@ -6891,7 +6891,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let point = Point::try_new([0.2, 0.2]).expect("finite point coordinates"); // inside simplex @@ -6906,7 +6906,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::<_, (), (), 2>::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let point = Point::try_new([3.0, 3.0]).expect("finite point coordinates"); // clearly outside diff --git a/src/core/algorithms/locate.rs b/src/core/algorithms/locate.rs index fe59b677..f678cc7b 100644 --- a/src/core/algorithms/locate.rs +++ b/src/core/algorithms/locate.rs @@ -776,7 +776,7 @@ pub(crate) struct LocateTrace { /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let kernel = FastKernel::::new(); /// /// // Point inside the 4-simplex @@ -822,7 +822,7 @@ pub(crate) struct LocateTrace { /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let kernel = RobustKernel::::default(); /// /// // Get a simplex to use as hint (spatially close to query point) @@ -885,7 +885,7 @@ where /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 2> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let kernel = FastKernel::::new(); /// /// let query_point = Point::try_from([0.3, 0.3])?; @@ -1256,7 +1256,7 @@ const fn conflict_simplex_points_error( /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let kernel = FastKernel::::new(); /// // Point inside the 4-simplex @@ -1675,7 +1675,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let kernel = FastKernel::::new(); /// let query_point = Point::try_from([0.2, 0.2, 0.2, 0.2])?; @@ -2175,7 +2175,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds(); let simplex_key = tds.simplex_keys().next().unwrap(); let simplex = tds.simplex(simplex_key).unwrap(); @@ -2348,7 +2348,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Get the single simplex @@ -2426,7 +2426,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Point inside the triangle @@ -2449,7 +2449,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Point inside the tetrahedron @@ -2471,7 +2471,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Point far outside the triangle @@ -2489,7 +2489,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Point far outside the tetrahedron @@ -2507,7 +2507,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Get a valid simplex as hint @@ -2525,7 +2525,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = RobustKernel::::default(); let point = Point::try_new([0.3, 0.3]).expect("finite point coordinates"); @@ -2545,7 +2545,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let simplex_key = dt.tds().simplex_keys().next().unwrap(); @@ -2583,7 +2583,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let simplex_key = dt.tds().simplex_keys().next().unwrap(); @@ -2604,7 +2604,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let simplex_key = dt.tds().simplex_keys().next().unwrap(); @@ -2634,7 +2634,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = RobustKernel::::default(); // Point very close to an edge but still inside @@ -2654,7 +2654,7 @@ mod tests { let vertices: Vec<_> = vec![ $(vertex!($coords).unwrap()),+ ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let point = Point::try_new($inside_point).expect("finite point coordinates"); @@ -2718,7 +2718,7 @@ mod tests { let vertices: Vec<_> = vec![ $(vertex!($coords).unwrap()),+ ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); @@ -2788,7 +2788,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); @@ -2812,7 +2812,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Create invalid simplex key @@ -2835,7 +2835,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = RobustKernel::::default(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); @@ -2857,7 +2857,7 @@ mod tests { let vertices: Vec<_> = vec![ $(vertex!($coords).unwrap()),+ ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); let mut conflict_simplices = SimplexKeyBuffer::new(); @@ -2931,7 +2931,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let conflict_simplices = SimplexKeyBuffer::new(); // Empty @@ -2951,7 +2951,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let point = Point::try_new([0.25, 0.25]).expect("finite point coordinates"); @@ -2973,7 +2973,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let expected_simplex = dt.tds().simplex_keys().next().unwrap(); @@ -3108,7 +3108,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let point = Point::try_new([0.2, 0.2]).expect("finite point coordinates"); let hint = dt.tds().simplex_keys().next().unwrap(); @@ -3140,7 +3140,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let invalid = SimplexKey::from(KeyData::from_ffi(424_242)); let mut conflict_simplices = SimplexKeyBuffer::new(); @@ -3480,7 +3480,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let point = Point::try_new([0.25_f64, 0.25_f64]).expect("finite point coordinates"); @@ -3506,7 +3506,7 @@ mod tests { let vertices: Vec<_> = vec![ $(vertex!($coords).unwrap()),+ ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); @@ -3596,7 +3596,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); // Point inside the circumcircle — the single simplex should be in conflict. @@ -3620,7 +3620,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); @@ -3658,7 +3658,7 @@ mod tests { vertex!([4.0, 3.0]).unwrap(), vertex!([0.0, 3.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let kernel = FastKernel::::new(); let start_simplex = dt.tds().simplex_keys().next().unwrap(); diff --git a/src/core/algorithms/pl_manifold_repair.rs b/src/core/algorithms/pl_manifold_repair.rs index aff53ae1..b8aed671 100644 --- a/src/core/algorithms/pl_manifold_repair.rs +++ b/src/core/algorithms/pl_manifold_repair.rs @@ -1192,7 +1192,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let config = PlManifoldRepairConfig::default(); @@ -1217,7 +1217,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); // Zero iterations — should succeed because already PL-manifold. @@ -1243,7 +1243,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let config = PlManifoldRepairConfig::default(); @@ -1266,7 +1266,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let stats = repair_facet_oversharing(&mut tds, &PlManifoldRepairConfig::default()).unwrap(); @@ -1297,7 +1297,7 @@ mod tests { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); assert!( tds.number_of_simplices() > 1, @@ -1903,7 +1903,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = dt.tds(); let simplex_key = tds.simplex_keys().next().unwrap(); @@ -1969,7 +1969,7 @@ mod tests { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let config = PlManifoldRepairConfig::default(); diff --git a/src/core/collections/key_maps.rs b/src/core/collections/key_maps.rs index 60c78770..e3fa368a 100644 --- a/src/core/collections/key_maps.rs +++ b/src/core/collections/key_maps.rs @@ -63,7 +63,7 @@ pub type VertexUuidSet = FastHashSet; /// ]; /// let dt: DelaunayTriangulation<_, _, _, 3> = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// println!("Topology guarantee: {:?}", dt.topology_guarantee()); /// let tds = dt.tds(); /// @@ -119,7 +119,7 @@ pub type UuidToVertexKeyMap = FastHashMap; /// ]; /// let dt: DelaunayTriangulation<_, _, _, 3> = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); /// let tds = dt.tds(); /// diff --git a/src/core/collections/secondary_maps.rs b/src/core/collections/secondary_maps.rs index 56324b9d..74d1dcef 100644 --- a/src/core/collections/secondary_maps.rs +++ b/src/core/collections/secondary_maps.rs @@ -48,7 +48,7 @@ use slotmap::SparseSecondaryMap; /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, _, _, 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// use delaunay::prelude::collections::SimplexSecondaryMap; @@ -99,7 +99,7 @@ pub type SimplexSecondaryMap = SparseSecondaryMap; /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, _, _, 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// use delaunay::prelude::collections::VertexSecondaryMap; diff --git a/src/core/edge.rs b/src/core/edge.rs index 4aac0753..f0eded77 100644 --- a/src/core/edge.rs +++ b/src/core/edge.rs @@ -112,7 +112,7 @@ pub enum EdgeKeyError { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -165,7 +165,7 @@ impl EdgeKey { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -317,7 +317,7 @@ impl EdgeKey { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -360,7 +360,7 @@ impl EdgeKey { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -403,7 +403,7 @@ impl EdgeKey { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -457,7 +457,7 @@ impl EdgeKey { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -520,7 +520,7 @@ impl<'tds, U, V, const D: usize> EdgeView<'tds, U, V, D> { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -650,7 +650,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); let simplex = dt.simplices().next().unwrap().1; let vertices = simplex.vertices(); @@ -725,7 +725,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); let keys: Vec = dt.tds().vertex_keys().collect(); let missing_edge = keys.iter().enumerate().find_map(|(i, &a)| { diff --git a/src/core/embedding.rs b/src/core/embedding.rs index dba7d059..e219d248 100644 --- a/src/core/embedding.rs +++ b/src/core/embedding.rs @@ -464,7 +464,7 @@ impl TriangulationEmbeddingValidationReport { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// std::assert_matches!( /// dt.as_triangulation().embedding_report(), @@ -892,7 +892,7 @@ impl Triangulation { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.as_triangulation().is_valid_embedding().is_ok()); /// # Ok(()) @@ -931,7 +931,7 @@ impl Triangulation { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// std::assert_matches!(dt.as_triangulation().embedding_diagnostic(), Ok(None)); /// # Ok(()) @@ -970,7 +970,7 @@ impl Triangulation { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// std::assert_matches!( /// dt.as_triangulation().embedding_report(), @@ -1063,7 +1063,7 @@ impl Triangulation { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.as_triangulation().validate_embedding().is_ok()); /// # Ok(()) @@ -1994,7 +1994,7 @@ mod tests { test_vertex([0.25, 0.25, 0.25]), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); assert!(dt.as_triangulation().validate_embedding().is_ok()); diff --git a/src/core/facet.rs b/src/core/facet.rs index 98579cf6..ff9aa8f5 100644 --- a/src/core/facet.rs +++ b/src/core/facet.rs @@ -65,7 +65,7 @@ //! ]; //! //! // Create a 3D triangulation -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! let Some((simplex_key, _)) = dt.simplices().next() else { //! return Ok(()); //! }; @@ -343,7 +343,7 @@ pub enum FacetError { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -393,7 +393,7 @@ impl FacetHandle { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -462,7 +462,7 @@ impl FacetHandle { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -499,7 +499,7 @@ impl FacetHandle { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -546,7 +546,7 @@ impl FacetHandle { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -704,7 +704,7 @@ impl<'tds, U, V, const D: usize> FacetView<'tds, U, V, D> { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -769,7 +769,7 @@ impl<'tds, U, V, const D: usize> FacetView<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); @@ -873,7 +873,7 @@ impl<'tds, U, V, const D: usize> FacetView<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// if let Some((simplex_key, _)) = dt.simplices().next() { /// let facet = FacetView::try_new(dt.tds(), simplex_key, 0)?; @@ -919,7 +919,7 @@ impl<'tds, U, V, const D: usize> FacetView<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -966,7 +966,7 @@ impl<'tds, U, V, const D: usize> FacetView<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1016,7 +1016,7 @@ impl<'tds, U, V, const D: usize> FacetView<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1509,7 +1509,7 @@ impl Eq for FacetView<'_, U, V, D> {} /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1631,7 +1631,7 @@ impl FusedIterator for SimplexFacetsIter<'_, U, V, D> {} /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let count = dt.tds().facets() /// .try_fold(0_usize, |count, facet| facet.map(|_| count + 1))?; @@ -1714,7 +1714,7 @@ impl<'tds, U, V, const D: usize> AllFacetsIter<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let mut iter = dt.tds().facets(); /// assert!(iter.next().transpose()?.is_some()); @@ -1771,7 +1771,7 @@ impl Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1823,7 +1823,7 @@ impl Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let facet_count = dt /// .tds() /// .facets() @@ -1919,7 +1919,7 @@ impl<'tds, U, V, const D: usize> Iterator for AllFacetsIter<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let count = dt.boundary_facets()? /// .try_fold(0_usize, |count, facet| facet.map(|_| count + 1))?; /// assert_eq!(count, 4); @@ -1969,7 +1969,7 @@ impl<'tds, U, V, const D: usize> BoundaryFacetsIter<'tds, U, V, D> { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut iter = dt.boundary_facets()?; /// assert!(iter.next().transpose()?.is_some()); /// # Ok(()) @@ -2041,7 +2041,7 @@ impl FusedIterator for BoundaryFacetsIter<'_, U, V, D> {} /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let one_sided_count = dt /// .tds() @@ -2231,6 +2231,7 @@ pub fn facet_key_from_vertices(vertices: &[VertexKey]) -> u64 { #[cfg(test)] mod tests { use super::*; + use crate::builder::DelaunayTriangulationBuilder; use crate::construction::{ ConstructionOptions, InitialSimplexStrategy, InsertionOrderStrategy, }; @@ -2296,7 +2297,7 @@ mod tests { fn test_facet_error_handling() { // Create a 1D triangulation (2 vertices forming an edge) let vertices = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Test invalid facet index (should be 0 or 1 for 1D, facet_index >= 2 is invalid) @@ -2315,7 +2316,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet view for facet 0 (excludes vertex 0) @@ -2332,7 +2333,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let handle = FacetHandle::try_new(dt.tds(), simplex_key, 1).unwrap(); @@ -2362,7 +2363,9 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.5, 1.0]).unwrap(), ]; - let dt_2d = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt_2d = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let simplex_key_2d = dt_2d.simplices().next().unwrap().0; let result_2d = FacetView::try_new(dt_2d.tds(), simplex_key_2d, 0); @@ -2373,7 +2376,9 @@ mod tests { // Test 1D case: Create an edge (1D simplex with 2 vertices) let vertices_1d = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; - let dt_1d = DelaunayTriangulation::try_new(&vertices_1d).unwrap(); + let dt_1d = DelaunayTriangulation::builder(&vertices_1d) + .build() + .unwrap(); let simplex_key_1d = dt_1d.simplices().next().unwrap().0; let result_1d = FacetView::try_new(dt_1d.tds(), simplex_key_1d, 0); @@ -2392,7 +2397,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Test invalid facet index (3D simplex has vertices 0-3, facet index 4 is invalid) @@ -2408,7 +2413,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet view for facet 0 (excludes vertex 0) @@ -2429,7 +2434,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet views with same facet index (should be equal) @@ -2450,7 +2455,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -2480,7 +2485,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -2509,13 +2514,11 @@ mod tests { .with_insertion_order(InsertionOrderStrategy::Input) .with_initial_simplex_strategy(InitialSimplexStrategy::First); let dt: DelaunayTriangulation, i32, (), 3> = - DelaunayTriangulation::try_with_topology_guarantee_and_options( - &AdaptiveKernel::new(), - &vertices, - TopologyGuarantee::DEFAULT, - options, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::DEFAULT) + .construction_options(options) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet view for facet 0 (excludes vertex 0) @@ -2559,7 +2562,7 @@ mod tests { fn $test_name() { // Test basic facet view creation let vertices = $vertices; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet view for facet 0 (excludes vertex 0) @@ -2575,7 +2578,7 @@ mod tests { fn [<$test_name _key_consistency>]() { // Test FacetKey computation consistency let vertices = $vertices; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create same facet twice @@ -2595,7 +2598,7 @@ mod tests { fn [<$test_name _equality>]() { // Test facet equality comparison let vertices = $vertices; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet1 = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -2610,7 +2613,7 @@ mod tests { fn [<$test_name _all_facets>]() { // Test iterating through all facets of a simplex let vertices = $vertices; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // D+1 dimensional simplex should have D+1 facets (one opposite each vertex) @@ -2668,7 +2671,10 @@ mod tests { let options = ConstructionOptions::default() .with_insertion_order(InsertionOrderStrategy::Input) .with_initial_simplex_strategy(InitialSimplexStrategy::First); - let dt = DelaunayTriangulation::try_new_with_options(&vertices, options).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet view for facet 0 (excludes vertex 0) @@ -3036,7 +3042,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet views for different facets @@ -3063,7 +3069,7 @@ mod tests { fn facet_vertices_empty_simplex() { // Test edge case of minimal simplex (1D edge with 2 vertices) let vertices = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet with vertex 0 as opposite - should have only vertex 1 in facet @@ -3084,7 +3090,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create facet view for facet 2 (excludes vertex 2) @@ -3104,7 +3110,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet1 = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -3131,7 +3137,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Create two facet views that reference the same facet @@ -3204,7 +3210,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Test valid facet creation @@ -3226,7 +3232,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet_view = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -3258,7 +3264,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet_view = FacetView::try_new(dt.tds(), simplex_key, 1).unwrap(); @@ -3283,7 +3289,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet_view = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -3302,7 +3308,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet_views = dt.tds().try_simplex_facets(simplex_key).unwrap(); @@ -3331,7 +3337,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet_view1 = FacetView::try_new(dt.tds(), simplex_key, 0).unwrap(); @@ -3354,7 +3360,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let facet_view = FacetView::try_new(dt.tds(), simplex_key, 1).unwrap(); diff --git a/src/core/facet_incidence.rs b/src/core/facet_incidence.rs index 4651ec6b..6e7d18aa 100644 --- a/src/core/facet_incidence.rs +++ b/src/core/facet_incidence.rs @@ -105,7 +105,7 @@ impl FacetIncidenceAnalysis for Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // High-level API returns `QueryError` if the underlying TDS is corrupted. /// let high_level_count = dt @@ -184,7 +184,7 @@ impl FacetIncidenceAnalysis for Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Get one-sided facets using the TDS incidence API. /// let Some(first_facet) = dt.tds().one_sided_facets()?.next().transpose()? else { @@ -249,7 +249,7 @@ impl FacetIncidenceAnalysis for Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Build the facet index once for multiple queries /// let facet_to_simplices = dt.tds().build_facet_to_simplices_index()?; @@ -313,7 +313,7 @@ impl FacetIncidenceAnalysis for Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // A single Euclidean tetrahedron has 4 one-sided facets. /// assert_eq!(dt.tds().number_of_one_sided_facets()?, 4); @@ -404,7 +404,7 @@ mod tests { Point::try_new([0.5, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!( dt.number_of_simplices(), @@ -446,7 +446,7 @@ mod tests { Point::try_new([0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!( dt.number_of_simplices(), @@ -489,7 +489,7 @@ mod tests { Point::try_new([0.0, 0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!( dt.number_of_simplices(), @@ -560,7 +560,7 @@ mod tests { Point::try_new([0.0, 0.0, 0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!( dt.number_of_simplices(), @@ -619,7 +619,7 @@ mod tests { Point::try_new([0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Test one_sided_facets() normal path. let one_sided_count = dt @@ -652,7 +652,7 @@ mod tests { Point::try_new([0.5, 0.5, 0.5]).expect("finite point coordinates"), // Interior point ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // After robust cleanup and facet-sharing filtering, we may end up with a single simplex assert!( @@ -744,7 +744,7 @@ mod tests { Point::try_new([0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Build facet index let facet_to_simplices = dt @@ -798,7 +798,7 @@ mod tests { Point::try_new([0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let facet = dt.boundary_facets().unwrap().next().unwrap().unwrap(); let facet_key = facet.key(); @@ -842,9 +842,9 @@ mod tests { ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let foreign_dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let foreign_facet = foreign_dt .boundary_facets() .unwrap() @@ -870,9 +870,9 @@ mod tests { ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let foreign_dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let facet = dt.boundary_facets().unwrap().next().unwrap().unwrap(); let foreign_index = foreign_dt.tds().build_facet_to_simplices_index().unwrap(); @@ -922,7 +922,7 @@ mod tests { ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let (simplex_key, _) = dt.tri.tds.simplices().next().unwrap(); let first_vertex = dt.tri.tds.simplex(simplex_key).unwrap().vertices()[0]; @@ -956,7 +956,7 @@ mod tests { Point::try_new([0.0, 0.0, 1.0]).expect("finite point coordinates"), ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Test both methods return consistent results let one_sided_facets_count = dt diff --git a/src/core/insertion.rs b/src/core/insertion.rs index 53191dae..1185ea5a 100644 --- a/src/core/insertion.rs +++ b/src/core/insertion.rs @@ -4413,7 +4413,7 @@ mod tests { .map(|c| vertex!([c[0] * scale, c[1] * scale, c[2] * scale]).unwrap()) .collect(); let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); (dt.number_of_vertices(), dt.number_of_simplices()) } diff --git a/src/core/query.rs b/src/core/query.rs index 8c11a671..446e3833 100644 --- a/src/core/query.rs +++ b/src/core/query.rs @@ -50,7 +50,7 @@ use std::marker::PhantomData; /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let boundary_count = dt /// .as_triangulation() @@ -124,7 +124,7 @@ impl Triangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tri = dt.as_triangulation(); /// /// // Iterate over simplices @@ -163,7 +163,7 @@ impl Triangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tri = dt.as_triangulation(); /// /// // Iterate over vertices @@ -201,7 +201,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.as_triangulation().number_of_vertices(), 4); /// # Ok(()) /// # } @@ -234,7 +234,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.as_triangulation().number_of_simplices(), 1); // Single tetrahedron /// # Ok(()) /// # } @@ -272,7 +272,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.as_triangulation().dim(), 3); /// # Ok(()) /// # } @@ -319,7 +319,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Iterate over all facets /// let facet_count = dt @@ -369,7 +369,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let boundary_count = dt /// .as_triangulation() @@ -440,7 +440,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tri = dt.as_triangulation(); /// /// let edges: std::collections::HashSet<_> = tri.edges().collect(); @@ -476,7 +476,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 2> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tri = dt.as_triangulation(); /// /// assert_eq!(tri.number_of_edges(), 3); @@ -584,7 +584,7 @@ impl Triangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let adjacency = dt.as_triangulation().adjacency()?; /// /// assert_eq!(adjacency.number_of_edges(), 6); @@ -816,7 +816,7 @@ impl Triangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tri = dt.as_triangulation(); /// let Some((_simplex_key, simplex)) = tri.simplices().next() else { /// return Ok(()); @@ -874,7 +874,7 @@ impl Triangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tri = dt.as_triangulation(); /// let Some((_simplex_key, simplex)) = tri.simplices().next() else { /// return Ok(()); @@ -1237,7 +1237,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); assert_eq!(tri.number_of_simplices(), 1); @@ -1347,7 +1347,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); assert_eq!(tri.number_of_simplices(), 2); @@ -1416,7 +1416,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let incidence = tri.incidence().unwrap(); let edge_index = tri.build_edge_index().unwrap(); @@ -1462,7 +1462,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let vertex_key = tri @@ -1804,7 +1804,7 @@ mod tests { vertex!([1.0, 1.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let edge_count = tri.number_of_edges(); diff --git a/src/core/repair.rs b/src/core/repair.rs index 75d1f51b..5db09ca8 100644 --- a/src/core/repair.rs +++ b/src/core/repair.rs @@ -1058,7 +1058,7 @@ where /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let simplex_keys: Vec<_> = dt.simplices().map(|(ck, _)| ck).collect(); /// let issues = dt @@ -1341,7 +1341,7 @@ where /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 2> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Empty issues map => nothing to remove. /// let mut tri = dt.as_triangulation().clone(); @@ -1611,7 +1611,7 @@ mod tests { v }; - let mut dt = DelaunayTriangulation::try_new(&vertices) + let mut dt = DelaunayTriangulation::builder(&vertices).build() .expect("Failed to create triangulation"); // Find and remove the interior vertex @@ -1905,7 +1905,7 @@ mod tests { vertex![1.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let simplex_keys: Vec<_> = tri.simplices().map(|(ck, _)| ck).collect(); @@ -2002,7 +2002,7 @@ mod tests { vertex![0.5, 0.5].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let vertex_key = dt .vertices() .find(|(_, vertex)| { @@ -2036,7 +2036,7 @@ mod tests { vertex![0.0, 0.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tri = dt.as_triangulation().clone(); let missing_vertex = VertexKey::from(KeyData::from_ffi(0xBAD)); let vertex_count = tri.tds.number_of_vertices(); @@ -2062,7 +2062,7 @@ mod tests { vertex![0.0, 0.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tri = dt.as_triangulation().clone(); let isolated_vertex = tri .tds @@ -2268,7 +2268,7 @@ mod tests { vertex![0.5, 0.5].unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let initial_simplices = dt.number_of_simplices(); let vertex_key = dt @@ -2296,7 +2296,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let initial_vertices = dt.number_of_vertices(); let initial_simplices = dt.number_of_simplices(); @@ -2359,7 +2359,7 @@ mod tests { vertex![1.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tri = dt.as_triangulation().clone(); // Add a duplicate simplex with the same vertices as an existing simplex. diff --git a/src/core/simplex.rs b/src/core/simplex.rs index f8fec8ba..82b45c57 100644 --- a/src/core/simplex.rs +++ b/src/core/simplex.rs @@ -37,7 +37,7 @@ //! //! // Create a 3D triangulation with simplices //! let dt: DelaunayTriangulation<_, (), (), 3> = -//! DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! DelaunayTriangulationBuilder::new(&vertices).build()?; //! let Some((simplex_key, simplex)) = dt.simplices().next() else { //! return Ok(()); //! }; @@ -348,7 +348,7 @@ impl NeighborSlot { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Get first simplex and iterate over vertex keys /// let Some((simplex_key, simplex)) = dt.simplices().next() else { @@ -711,7 +711,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -759,7 +759,7 @@ impl Simplex { /// delaunay::vertex![0.0, 1.0]?, /// delaunay::vertex![1.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut simplices_iter = dt.simplices().map(|(_, simplex)| simplex); /// let Some(simplex1) = simplices_iter.next() else { return Ok(()); }; /// let Some(simplex2) = simplices_iter.next() else { return Ok(()); }; @@ -803,7 +803,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -851,7 +851,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -939,7 +939,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -997,7 +997,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1228,7 +1228,7 @@ impl Simplex { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1278,7 +1278,7 @@ impl Simplex { /// delaunay::vertex![0.0, 0.0, 0.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1318,7 +1318,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).simplex_data_type::().build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1391,7 +1391,7 @@ impl Simplex { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1460,7 +1460,7 @@ impl Simplex { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1518,7 +1518,7 @@ impl Simplex { /// delaunay::vertex![0.0, 0.0, 0.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1582,9 +1582,9 @@ impl Simplex { /// delaunay::vertex![1.0, 1.0, 2.0]?, /// ]; /// let dt1: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices1).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices1).build()?; /// let dt2: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices2).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices2).build()?; /// let Some((_, simplex1)) = dt1.tds().simplices().next() else { /// return Ok(()); /// }; @@ -1697,7 +1697,7 @@ impl Simplex { /// delaunay::vertex![0.0, 0.0, 0.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1857,9 +1857,9 @@ impl Simplex { /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt1: DelaunayTriangulation<_, _, _, 2> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let dt2: DelaunayTriangulation<_, _, _, 2> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds1 = dt1.tds(); /// let tds2 = dt2.tds(); /// @@ -1905,9 +1905,9 @@ impl Simplex { /// delaunay::vertex![0.0, 2.0]?, // Different coordinate /// ]; /// let dt1: DelaunayTriangulation<_, _, _, 2> = - /// DelaunayTriangulationBuilder::new(&vertices1).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices1).build()?; /// let dt2: DelaunayTriangulation<_, _, _, 2> = - /// DelaunayTriangulationBuilder::new(&vertices2).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices2).build()?; /// let tds1 = dt1.tds(); /// let tds2 = dt2.tds(); /// @@ -2108,7 +2108,7 @@ mod tests { fn $test_name() { // Test basic simplex creation let vertices = $vertices; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); assert_simplex_properties(simplex, $dim + 1, $dim); } @@ -2119,7 +2119,10 @@ mod tests { // Test simplex with data - need generic constructor for non-() simplex data let vertices = $vertices; let dt: DelaunayTriangulation, (), i32, $dim> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -2133,7 +2136,10 @@ mod tests { // Test serialization with Some data - use generic constructor let vertices = $vertices; let dt: DelaunayTriangulation, (), i32, $dim> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); let mut simplex = simplex.clone(); simplex.data = Some(99); @@ -2146,7 +2152,7 @@ mod tests { // Test serialization with None data - use simple constructor let vertices = $vertices; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); let serialized = serde_json::to_string(&simplex).unwrap(); @@ -2161,9 +2167,9 @@ mod tests { // Test UUID uniqueness by creating two separate triangulations let vertices1 = $vertices; let vertices2 = $vertices; - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); let (_, simplex1) = dt1.simplices().next().unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let (_, simplex2) = dt2.simplices().next().unwrap(); assert_ne!(simplex1.uuid(), simplex2.uuid()); assert!(!simplex1.uuid().is_nil()); @@ -2276,7 +2282,7 @@ mod tests { ]; // Create DT to get a simplex with TDS context. - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); assert_eq!(simplex.number_of_vertices(), 4); @@ -2326,8 +2332,8 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt1 = DelaunayTriangulation::try_new(&vertices).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex1 = dt1.simplices().next().unwrap().1; let simplex2 = dt2.simplices().next().unwrap().1; @@ -2344,8 +2350,8 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt1 = DelaunayTriangulation::try_new(&vertices).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex1 = dt1.simplices().next().unwrap().1; let simplex2 = dt2.simplices().next().unwrap().1; @@ -2365,7 +2371,10 @@ mod tests { // Build DT with integer simplex data type let dt: DelaunayTriangulation, (), i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -2415,9 +2424,11 @@ mod tests { vertex!([0.0, 0.0, 1.0]; data = 4).unwrap(), // Need 4 vertices for 3D simplex ]; - // Create DT with vertex data - simple constructor works since simplex data is () + // Create DT with vertex data and default simplex data. let dt: DelaunayTriangulation, i32, (), 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); @@ -2453,7 +2464,7 @@ mod tests { vertex!([1.0, 0.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 0.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex1) = dt.simplices().next().unwrap(); let simplex2 = simplex1.clone(); @@ -2477,7 +2488,9 @@ mod tests { vertex!([0.0, 0.0, 0.0]).unwrap(), vertex!([1.0, 1.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&all_vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&all_vertices) + .build() + .unwrap(); let simplices: Vec<_> = dt.simplices().map(|(_, simplex)| simplex).collect(); if simplices.len() >= 2 { @@ -2502,7 +2515,7 @@ mod tests { vertex!([1.0, 0.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 0.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex1) = dt.simplices().next().unwrap(); let simplex2 = simplex1.clone(); @@ -2528,7 +2541,10 @@ mod tests { vertex!([1.0, 1.0, 1.0]; data = 2).unwrap(), ]; let dt: DelaunayTriangulation, i32, i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex1 = simplex_ref.clone(); simplex1.data = Some(42); @@ -2545,7 +2561,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex1) = dt.simplices().next().unwrap(); let simplex2 = simplex1.clone(); @@ -2566,13 +2582,17 @@ mod tests { #[test] fn simplex_number_of_vertices() { let vertices_2d = create_test_vertices_2d(); - let dt_2d = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt_2d = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let simplex_key_2d = dt_2d.tds().simplex_keys().next().unwrap(); let triangle = dt_2d.tds().simplex(simplex_key_2d).unwrap(); assert_eq!(triangle.number_of_vertices(), 3); let vertices_3d = create_test_vertices_3d(); - let dt_3d = DelaunayTriangulation::try_new(&vertices_3d).unwrap(); + let dt_3d = DelaunayTriangulation::builder(&vertices_3d) + .build() + .unwrap(); let simplex_key_3d = dt_3d.tds().simplex_keys().next().unwrap(); let tetrahedron = dt_3d.tds().simplex(simplex_key_3d).unwrap(); assert_eq!(tetrahedron.number_of_vertices(), 4); @@ -2587,7 +2607,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), vertex!([1.0, 1.1]).unwrap(), // break cocircular symmetry ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplices: Vec<_> = dt.simplices().map(|(_, simplex)| simplex).collect(); assert!( @@ -2663,7 +2683,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), vertex!([0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplices: Vec<_> = dt.simplices().map(|(_, simplex)| simplex).collect(); assert!( @@ -2705,7 +2725,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut malformed_neighbor = simplex_ref.clone(); @@ -2717,13 +2737,17 @@ mod tests { #[test] fn simplex_dim() { let vertices_2d = create_test_vertices_2d(); - let dt_2d = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt_2d = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let simplex_key_2d = dt_2d.tds().simplex_keys().next().unwrap(); let triangle = dt_2d.tds().simplex(simplex_key_2d).unwrap(); assert_eq!(triangle.dim(), 2); let vertices_3d = create_test_vertices_3d(); - let dt_3d = DelaunayTriangulation::try_new(&vertices_3d).unwrap(); + let dt_3d = DelaunayTriangulation::builder(&vertices_3d) + .build() + .unwrap(); let simplex_key_3d = dt_3d.tds().simplex_keys().next().unwrap(); let tetrahedron = dt_3d.tds().simplex(simplex_key_3d).unwrap(); assert_eq!(tetrahedron.dim(), 3); @@ -2739,7 +2763,9 @@ mod tests { // Create DT to get VertexKeys - use generic constructor for vertex data let vertices = vec![vertex1, vertex2, vertex3, vertex4]; let dt: DelaunayTriangulation, i32, (), 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -2763,7 +2789,10 @@ mod tests { vertex!([1.0, 1.0, 1.0]; data = 2).unwrap(), ]; let tds1: DelaunayTriangulation, i32, i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices1).unwrap(); + DelaunayTriangulationBuilder::new(&vertices1) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = tds1.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); simplex.data = Some(42); @@ -2775,7 +2804,10 @@ mod tests { vertex!([0.0, 0.0, 0.0]; data = 0).unwrap(), ]; let tds2: DelaunayTriangulation, i32, i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices2).unwrap(); + DelaunayTriangulationBuilder::new(&vertices2) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex2_ref) = tds2.simplices().next().unwrap(); let mut simplex2 = simplex2_ref.clone(); simplex2.data = Some(43); @@ -2798,7 +2830,9 @@ mod tests { let vertices = vec![vertex1, vertex2, vertex3, vertex4]; let dt: DelaunayTriangulation, i32, (), 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -2830,7 +2864,7 @@ mod tests { // Tested through TDS, which is the user-facing API. let vertices = vec![vertex!([0.0, 0.0, 0.0]).unwrap()]; - let result = DelaunayTriangulation::try_new(&vertices); + let result = DelaunayTriangulation::builder(&vertices).build(); assert!(result.is_err()); let error = result.unwrap_err(); @@ -2846,7 +2880,9 @@ mod tests { let vertices = vec![vertex1, vertex2, vertex3]; let dt: DelaunayTriangulation, i32, (), 2> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -2883,7 +2919,9 @@ mod tests { ]; let dt: DelaunayTriangulation, i32, (), 4> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -2935,7 +2973,7 @@ mod tests { #[test] fn simplex_1d() { let vertices = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); assert_simplex_properties(simplex, 2, 1); } @@ -2945,7 +2983,7 @@ mod tests { // Test that creating a 3D triangulation with insufficient vertices fails validation // Tested through TDS, which is the user-facing API. let vertices = vec![vertex!([0.0, 0.0, 0.0]).unwrap()]; - let result = DelaunayTriangulation::try_new(&vertices); + let result = DelaunayTriangulation::builder(&vertices).build(); assert!(result.is_err()); let error_msg = result.unwrap_err().to_string(); @@ -2962,7 +3000,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); // Note: neighbors may be set by TDS construction, this tests simplex structure @@ -2977,7 +3015,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex) = dt.simplices().next().unwrap(); assert!(simplex.data.is_none()); @@ -2992,7 +3030,10 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation, (), i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3017,7 +3058,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([1.0, 1.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Iterate simplices from DT (clone into owned values for the hashmap). let mut simplices_iter = dt.simplices().map(|(_, simplex)| simplex.clone()); @@ -3047,7 +3088,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let simplex = simplex_ref.clone(); let uuid = simplex.uuid(); @@ -3065,7 +3106,7 @@ mod tests { #[test] fn simplex_debug_format() { - // Use a simple non-degenerate 3D tetrahedron so `DelaunayTriangulation::try_new` can construct + // Use a simple non-degenerate 3D tetrahedron so `DelaunayTriangulationBuilder::build` can construct // a valid simplex for debug-format testing. let vertices = vec![ vertex!([0.0, 0.0, 0.0]).unwrap(), @@ -3074,7 +3115,10 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation, (), i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3098,7 +3142,7 @@ mod tests { #[test] fn simplex_to_and_from_json() { // Test serialization through TDS context. - // Use a non-degenerate 3D tetrahedron so `DelaunayTriangulation::try_new` can construct a + // Use a non-degenerate 3D tetrahedron so `DelaunayTriangulationBuilder::build` can construct a // valid initial simplex. let vertices = vec![ vertex!([0.0, 0.0, 0.0]).unwrap(), @@ -3106,7 +3150,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Serialize the entire DT (includes simplices with proper context) let serialized = serde_json::to_string(&dt).unwrap(); @@ -3217,7 +3261,10 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation, (), i32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex_with_data = simplex_ref.clone(); simplex_with_data.data = Some(42); @@ -3235,7 +3282,7 @@ mod tests { ); // Test 3: Serialization with None data omits field (optimization) - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_none) = dt.simplices().next().unwrap(); let serialized_none = serde_json::to_string(&simplex_none).unwrap(); assert!( @@ -3275,7 +3322,9 @@ mod tests { vertex!([-1.0, -2.0, -1.0]).unwrap(), vertex!([-1.0, -1.0, -2.0]).unwrap(), ]; - let dt_neg = DelaunayTriangulation::try_new(&negative_vertices).unwrap(); + let dt_neg = DelaunayTriangulation::builder(&negative_vertices) + .build() + .unwrap(); let (_, simplex_neg) = dt_neg.tds().simplices().next().unwrap(); assert_eq!(simplex_neg.number_of_vertices(), 4); assert_eq!(simplex_neg.dim(), 3); @@ -3287,7 +3336,9 @@ mod tests { vertex!([1e6, 2e6, 1e6]).unwrap(), vertex!([1e6, 1e6, 2e6]).unwrap(), ]; - let dt_large = DelaunayTriangulation::try_new(&large_vertices).unwrap(); + let dt_large = DelaunayTriangulation::builder(&large_vertices) + .build() + .unwrap(); let (_, simplex_large) = dt_large.tds().simplices().next().unwrap(); assert_eq!(simplex_large.number_of_vertices(), 4); assert_eq!(simplex_large.dim(), 3); @@ -3299,7 +3350,9 @@ mod tests { vertex!([0.0, 1e-3, 0.0]).unwrap(), vertex!([0.0, 0.0, 1e-3]).unwrap(), ]; - let dt_small = DelaunayTriangulation::try_new(&small_vertices).unwrap(); + let dt_small = DelaunayTriangulation::builder(&small_vertices) + .build() + .unwrap(); let (_, simplex_small) = dt_small.tds().simplices().next().unwrap(); assert_eq!(simplex_small.number_of_vertices(), 4); assert_eq!(simplex_small.dim(), 3); @@ -3312,7 +3365,7 @@ mod tests { let vertex3 = vertex!([0.0, 1.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -3338,7 +3391,7 @@ mod tests { let vertex_outside: Vertex<(), 3> = vertex!([2.0, 2.0, 2.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3, vertex4]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -3358,7 +3411,9 @@ mod tests { let vertices = vec![vertex1, vertex2, vertex3, vertex4]; let dt: DelaunayTriangulation, i32, (), 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -3392,7 +3447,7 @@ mod tests { let vertex3 = vertex!([0.0, 1.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -3426,7 +3481,7 @@ mod tests { let vertex4 = vertex!([0.0, 0.0, 1.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3, vertex4]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -3454,7 +3509,7 @@ mod tests { let vertex4 = vertex!([0.0, 0.0, 1.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3, vertex4]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Test that we can get facet views for all facets @@ -3503,7 +3558,7 @@ mod tests { let vertex4 = vertex!([1.0, 1.0, 1.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3, vertex4]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Get all facet views @@ -3554,7 +3609,7 @@ mod tests { let vertex6 = vertex!([0.0, 0.0, 0.0, 0.0, 1.0]).unwrap(); let vertices = vec![vertex1, vertex2, vertex3, vertex4, vertex5, vertex6]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let simplex = &dt.tds().simplex(simplex_key).unwrap(); @@ -3579,7 +3634,10 @@ mod tests { let vertices = vec![vertex1, vertex2, vertex3, vertex4]; let mut dt: DelaunayTriangulation, i32, u32, 3> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Set the simplex data to a known value @@ -3642,7 +3700,9 @@ mod tests { vertex!([1.0, 0.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 0.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices_3d).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices_3d) + .build() + .unwrap(); let (_, simplex_3d) = dt.simplices().next().unwrap(); assert!( simplex_3d.is_valid().is_ok(), @@ -3655,7 +3715,9 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex_2d = simplex_ref.clone(); assert!( @@ -3691,7 +3753,7 @@ mod tests { vertex!([1.0, 0.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 0.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut invalid_uuid_simplex = simplex_ref.clone(); invalid_uuid_simplex.uuid = uuid::Uuid::nil(); @@ -3708,7 +3770,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.0, 1.0, 0.0]).unwrap(), ]; - let result = DelaunayTriangulation::try_new(&insufficient_vertices); + let result = DelaunayTriangulation::builder(&insufficient_vertices).build(); assert!( result.is_err(), "TDS should fail with insufficient vertices" @@ -3720,7 +3782,9 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex_wrong_neighbors = simplex_ref.clone(); let err = simplex_wrong_neighbors @@ -3763,7 +3827,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let vkeys: Vec<_> = dt.tds().vertices().map(|(k, _)| k).collect(); // Too few vertices for a 3D simplex (D+1 = 4) @@ -3793,7 +3857,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); // Insufficient vertices (wrong vertex buffer length) @@ -3820,7 +3884,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (simplex_key, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3844,7 +3908,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3868,7 +3932,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (simplex_key, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3902,7 +3966,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3922,7 +3986,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (simplex_key, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3958,7 +4022,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let (_, simplex_ref) = dt.simplices().next().unwrap(); let mut simplex = simplex_ref.clone(); @@ -3976,7 +4040,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.tds().simplex_keys().next().unwrap(); // Grab a stable key we can duplicate to inflate the vertex buffer. @@ -4076,9 +4140,13 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt1 = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let (_, simplex_2d) = dt1.tds().simplices().next().unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let (_, simplex_2d_copy) = dt2.tds().simplices().next().unwrap(); // Test equality for 2D simplices @@ -4097,7 +4165,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.tds().simplex_keys().next().unwrap(); @@ -4164,7 +4232,8 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); let key = dt.simplices().next().unwrap().0; diff --git a/src/core/tds/equality.rs b/src/core/tds/equality.rs index cc4143d3..08eed84a 100644 --- a/src/core/tds/equality.rs +++ b/src/core/tds/equality.rs @@ -249,9 +249,9 @@ mod tests { let verts_a = standard_vertices::<$dim>(); let verts_b = standard_vertices_with_extra::<$dim>(); let dt_a: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&verts_a).unwrap(); + DelaunayTriangulation::builder(&verts_a).build().unwrap(); let dt_b: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&verts_b).unwrap(); + DelaunayTriangulation::builder(&verts_b).build().unwrap(); assert_ne!(dt_a.tds(), dt_b.tds()); } @@ -260,7 +260,7 @@ mod tests { fn []() { let verts = standard_vertices::<$dim>(); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&verts).unwrap(); + DelaunayTriangulation::builder(&verts).build().unwrap(); let valid = dt.tds().clone(); let mut corrupted = dt.tds().clone(); let dangling_vertex = VertexKey::from(KeyData::from_ffi(9999)); @@ -273,7 +273,7 @@ mod tests { fn []() { let verts = standard_vertices::<$dim>(); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&verts).unwrap(); + DelaunayTriangulation::builder(&verts).build().unwrap(); let valid = dt.tds().clone(); let mut corrupted = dt.tds().clone(); corrupted.set_first_simplex_periodic_offsets_storage_only_for_test(Some( @@ -287,7 +287,7 @@ mod tests { fn []() { let verts = standard_vertices::<$dim>(); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&verts).unwrap(); + DelaunayTriangulation::builder(&verts).build().unwrap(); let valid = dt.tds().clone(); let mut corrupted = dt.tds().clone(); let vertex_key = corrupted.vertices().next().map(|(key, _)| key).unwrap(); @@ -317,9 +317,9 @@ mod tests { vertex!([0.0, 2.0]).unwrap(), ]; let dt_a: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&verts_a).unwrap(); + DelaunayTriangulation::builder(&verts_a).build().unwrap(); let dt_b: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&verts_b).unwrap(); + DelaunayTriangulation::builder(&verts_b).build().unwrap(); assert_ne!(dt_a.tds(), dt_b.tds()); } } diff --git a/src/core/tds/errors.rs b/src/core/tds/errors.rs index 082b2872..e5894133 100644 --- a/src/core/tds/errors.rs +++ b/src/core/tds/errors.rs @@ -178,7 +178,7 @@ pub enum GeometricError { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_vertices(), 4); /// assert!(dt.is_valid_delaunay().is_ok()); /// # Ok(()) diff --git a/src/core/tds/keys.rs b/src/core/tds/keys.rs index 578b77f8..5570318c 100644 --- a/src/core/tds/keys.rs +++ b/src/core/tds/keys.rs @@ -33,7 +33,7 @@ new_key_type! { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some(key) = dt.tds().vertex_keys().next() else { /// return Ok(()); /// }; @@ -75,7 +75,7 @@ new_key_type! { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some(key) = dt.tds().simplex_keys().next() else { /// return Ok(()); /// }; diff --git a/src/core/tds/mutation.rs b/src/core/tds/mutation.rs index 339a9ed8..44201475 100644 --- a/src/core/tds/mutation.rs +++ b/src/core/tds/mutation.rs @@ -669,7 +669,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0; data = 20]?, /// delaunay::vertex![0.0, 1.0; data = 30]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// let Some(key) = tds.vertex_keys().next() else { /// return Ok(()); @@ -753,7 +753,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).simplex_data_type::().build()?; /// let mut tds = dt.tds().clone(); /// let Some(key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -793,6 +793,21 @@ impl Tds { Ok(previous) } + /// Fills every live simplex with data computed from the current simplex view. + /// + /// This does not alter topology, UUID mappings, neighbor links, or generation: + /// simplex payloads are orthogonal to the TDS structural invariants. + #[inline] + pub(crate) fn fill_simplex_data(&mut self, mut data_for: F) + where + F: FnMut(SimplexKey, &Simplex) -> V, + { + for (simplex_key, simplex) in &mut self.simplices { + let data = data_for(simplex_key, simplex); + simplex.data = Some(data); + } + } + /// Removes multiple simplices by their keys in a batch operation. /// /// This method performs a **local** topology update: @@ -855,7 +870,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -1342,7 +1357,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some((simplex_key, _)) = tds.simplices().next() else { /// return Ok(()); @@ -1703,7 +1718,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -1823,7 +1838,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let mut tds = dt.tds().clone(); /// tds.assign_incident_simplices()?; /// let all_assigned = tds.vertices().all(|(_, v)| v.incident_simplex().is_some()); @@ -2221,7 +2236,9 @@ mod tests { #[test] fn test_add_vertex_duplicate_coordinates_rejected() { let initial_vertices = initial_simplex_vertices_3d(); - let mut dt = DelaunayTriangulation::try_new(&initial_vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&initial_vertices) + .build() + .unwrap(); let vertex = vertex!([1.0, 2.0, 3.0]).unwrap(); let duplicate = vertex!([1.0, 2.0, 3.0]).unwrap(); @@ -2239,7 +2256,9 @@ mod tests { #[test] fn test_add_vertex_duplicate_uuid_rejected() { let initial_vertices = initial_simplex_vertices_3d(); - let mut dt = DelaunayTriangulation::try_new(&initial_vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&initial_vertices) + .build() + .unwrap(); let vertex1 = vertex!([1.0, 2.0, 3.0]).unwrap(); let uuid1 = vertex1.uuid(); @@ -2264,7 +2283,9 @@ mod tests { #[test] fn test_add_vertex_increases_counts_and_leaves_tds_valid() { let initial_vertices = initial_simplex_vertices_3d(); - let mut dt = DelaunayTriangulation::try_new(&initial_vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&initial_vertices) + .build() + .unwrap(); let initial_simplex_count = dt.number_of_simplices(); let new_vertex = vertex!([0.5, 0.5, 0.5]).unwrap(); @@ -2284,7 +2305,9 @@ mod tests { #[test] fn test_add_vertex_is_accessible_by_uuid_and_coordinates() { let initial_vertices = initial_simplex_vertices_3d(); - let mut dt = DelaunayTriangulation::try_new(&initial_vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&initial_vertices) + .build() + .unwrap(); let vertex = vertex!([1.0, 2.0, 3.0]).unwrap(); let uuid = vertex.uuid(); @@ -2329,7 +2352,7 @@ mod tests { vertex!([0.5, 1.0]).unwrap(), vertex!([1.5, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Verify initial state let initial_vertices = dt.number_of_vertices(); @@ -2398,7 +2421,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); // Use a key that was never added let nonexistent_key = VertexKey::from(KeyData::from_ffi(u64::MAX)); @@ -2434,7 +2457,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let vertex_key = dt.vertices().next().unwrap().0; @@ -2467,7 +2490,9 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt_2d: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); let vertex_key = dt_2d.vertices().next().unwrap().0; let simplices_removed = dt_2d.delete_vertex(vertex_key).unwrap(); assert!(simplices_removed > 0); @@ -2484,7 +2509,9 @@ mod tests { vertex!([0.2, 0.2, 0.2]).unwrap(), ]; let mut dt_3d: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices_3d).unwrap(); + DelaunayTriangulation::builder(&vertices_3d) + .build() + .unwrap(); let vertex_key = dt_3d .vertices() .find(|(_, vertex)| { @@ -2512,7 +2539,9 @@ mod tests { vertex!([0.2, 0.2, 0.2, 0.2]).unwrap(), ]; let mut dt_4d: DelaunayTriangulation<_, (), (), 4> = - DelaunayTriangulation::try_new(&vertices_4d).unwrap(); + DelaunayTriangulation::builder(&vertices_4d) + .build() + .unwrap(); let vertex_key = dt_4d .vertices() .find(|(_, vertex)| { @@ -2546,7 +2575,7 @@ mod tests { vertex!([0.2, 0.2, 0.2]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Find the interior vertex by coordinates (order-independent) let interior_coords = [0.2, 0.2, 0.2]; @@ -3377,7 +3406,7 @@ mod tests { #[test] fn test_remove_duplicate_simplices_noop_when_no_duplicates() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut tds = dt.tds().clone(); let generation_before = tds.generation(); @@ -3448,7 +3477,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); assert!(tds.number_of_simplices() > 1); @@ -3778,7 +3807,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); assert!(tds.number_of_simplices() > 1); @@ -3845,7 +3874,7 @@ mod tests { #[test] fn test_normalize_coherent_orientation_handles_single_simplex() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut tds = dt.tds().clone(); assert_eq!(tds.number_of_simplices(), 1); @@ -3862,7 +3891,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); assert!(tds.number_of_simplices() > 1); @@ -3993,7 +4022,7 @@ mod tests { vertex!([0.0, 1.0]; data = 30).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); let mut tds = dt.tds().clone(); let key = tds.vertex_keys().next().unwrap(); @@ -4011,7 +4040,7 @@ mod tests { vertex!([1.0, 0.0]).unwrap(), vertex!([0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let key = tds.vertex_keys().next().unwrap(); @@ -4037,7 +4066,8 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); let mut tds = dt.tds().clone(); let key = tds.simplex_keys().next().unwrap(); @@ -4055,7 +4085,8 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); let mut tds = dt.tds().clone(); let key = tds.simplex_keys().next().unwrap(); @@ -4082,7 +4113,7 @@ mod tests { vertex!([0.0, 1.0]; data = 3).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); // Mutate every vertex's data through the DT wrapper. @@ -4110,7 +4141,8 @@ mod tests { vertex!([1.5, 0.5]).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); assert!(dt.number_of_simplices() > 1); @@ -4138,7 +4170,7 @@ mod tests { vertex!([0.0, 1.0]; data = 30).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); let key = dt.vertices().next().unwrap().0; @@ -4161,7 +4193,7 @@ mod tests { vertex!([0.0, 1.0]; data = 30).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); let live_key = dt.vertices().next().unwrap().0; let stale = VertexKey::from(KeyData::from_ffi(0xFEED)); @@ -4186,7 +4218,8 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); let key = dt.simplices().next().unwrap().0; @@ -4209,7 +4242,8 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); let live_key = dt.simplices().next().unwrap().0; let stale = SimplexKey::from(KeyData::from_ffi(0xFEED)); @@ -4234,7 +4268,7 @@ mod tests { vertex!([0.0, 1.0]; data = 0).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); // Insert a new vertex so the locate hint is populated. diff --git a/src/core/tds/snapshot.rs b/src/core/tds/snapshot.rs index 89e9d5ba..48420a56 100644 --- a/src/core/tds/snapshot.rs +++ b/src/core/tds/snapshot.rs @@ -1279,7 +1279,7 @@ mod tests { /// constructs the JSON text needed to exercise duplicate-key deserialization. fn snapshot_json_with_duplicate_relationship_key(field: &str) -> String { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -1370,7 +1370,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let original = dt.tds().clone(); let json = serde_json::to_value(&original).expect("serialize TDS to JSON value"); @@ -1474,7 +1474,7 @@ mod tests { #[test] fn test_tds_snapshot_deserialize_rejects_unknown_simplex_record_fields() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut json = serde_json::to_value(dt.tds()).expect("serialize TDS to JSON value"); let simplex_record = json @@ -1578,7 +1578,7 @@ mod tests { #[test] fn test_tds_snapshot_deserialize_rejects_unknown_top_level_fields() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut json = serde_json::to_value(dt.tds()).expect("serialize TDS to JSON value"); json.as_object_mut() .expect("serialized TDS should be an object") @@ -1596,7 +1596,7 @@ mod tests { #[test] fn test_tds_snapshot_deserialize_rejects_missing_top_level_neighbor_map() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut json = serde_json::to_value(dt.tds()).expect("serialize TDS to JSON value"); json.as_object_mut() .expect("serialized TDS should be an object") @@ -1616,7 +1616,7 @@ mod tests { #[test] fn test_tds_snapshot_serde_round_trip_preserves_tds_structure() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let original = dt.tds().clone(); let json = serde_json::to_string(&original).expect("serialize failed"); @@ -1644,7 +1644,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let original = dt.tds().clone(); assert!(original.number_of_simplices() > 1); @@ -1666,7 +1666,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let original = dt.tds().clone(); let json = serde_json::to_string(&original).unwrap(); @@ -1759,7 +1759,7 @@ mod tests { #[test] fn test_tds_snapshot_deserialize_rejects_duplicate_vertex_uuids() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let original = dt.tds().clone(); let mut json = serde_json::to_value(&original).expect("serialize TDS to JSON value"); @@ -1796,7 +1796,7 @@ mod tests { #[test] fn test_tds_snapshot_deserialize_rejects_extra_simplex_vertex_uuid_mapping() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let original = dt.tds().clone(); let mut json = serde_json::to_value(&original).expect("serialize TDS to JSON value"); let (_, simplex) = original @@ -1827,7 +1827,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_unknown_relationship_map_simplex_uuids() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let unknown_neighbor_simplex_uuid = Uuid::new_v4(); @@ -1865,7 +1865,7 @@ mod tests { #[test] fn test_tds_snapshot_deserialize_rejects_invalid_simplex_vertex_uuid_mappings() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let original = dt.tds().clone(); let json = serde_json::to_value(&original).expect("serialize TDS to JSON value"); let (_, simplex) = original @@ -1946,7 +1946,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let original = dt.tds().clone(); assert!(original.number_of_simplices() > 1); let mut json = serde_json::to_value(&original).expect("serialize TDS to JSON value"); @@ -2081,7 +2081,7 @@ mod tests { #[test] fn test_tds_snapshot_rejects_missing_runtime_neighbor_slots() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut tds = dt.tds().clone(); let simplex_uuid = tds .simplices() @@ -2104,7 +2104,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_duplicate_vertex_uuid() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let duplicate_uuid = snapshot @@ -2134,7 +2134,7 @@ mod tests { #[test] fn test_tds_snapshot_hydration_relies_on_validated_vertex_uuid_uniqueness() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()) .parse() .expect("raw snapshot should parse into a validated snapshot"); @@ -2162,7 +2162,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_missing_simplex_vertex_mapping() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -2186,7 +2186,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_invalid_simplex_vertex_uuid_slot_count() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -2217,7 +2217,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_missing_simplex_neighbor_mapping() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -2241,7 +2241,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_dangling_vertex_uuid_reference() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -2277,7 +2277,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = *snapshot .simplex_neighbors @@ -2336,7 +2336,7 @@ mod tests { #[test] fn test_tds_snapshot_error_preserves_duplicate_simplex_uuid() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -2369,7 +2369,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let (simplex_uuid, neighbors) = snapshot .simplex_neighbors @@ -2399,7 +2399,7 @@ mod tests { #[test] fn test_tds_snapshot_rejects_wrong_neighbor_slot_count() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut snapshot = raw_snapshot_from_tds(dt.tds()); let simplex_uuid = snapshot .simplices @@ -2427,7 +2427,7 @@ mod tests { #[test] fn test_tds_snapshot_rejects_dangling_runtime_neighbor_key() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let mut tds = dt.tds().clone(); let simplex_key = tds .simplices() diff --git a/src/core/tds/storage.rs b/src/core/tds/storage.rs index d04e33d1..bc5a3f06 100644 --- a/src/core/tds/storage.rs +++ b/src/core/tds/storage.rs @@ -150,7 +150,7 @@ //! vertex![0.0, 1.0, 0.0]?, //! vertex![0.0, 0.0, 1.0]?, //! ]; -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! // Level 2: structural only (fast) //! assert!(dt.tds().is_valid().is_ok()); @@ -218,7 +218,7 @@ //! ]; //! //! // Create Delaunay triangulation -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! // Query triangulation properties //! assert_eq!(dt.number_of_vertices(), 4); @@ -264,7 +264,7 @@ //! delaunay::vertex![0.0, 0.0, 1.0]?, //! ]; //! -//! let mut dt = DelaunayTriangulationBuilder::new(&initial_vertices).build::<()>()?; +//! let mut dt = DelaunayTriangulationBuilder::new(&initial_vertices).build()?; //! //! // Add a new vertex //! let new_vertex = vertex![0.2, 0.2, 0.2]?; @@ -312,7 +312,7 @@ //! delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, // Unit vector along fourth dimension //! ]; //! -//! let dt_4d = DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; +//! let dt_4d = DelaunayTriangulationBuilder::new(&vertices_4d).build()?; //! assert_eq!(dt_4d.dim(), 4); //! assert_eq!(dt_4d.number_of_vertices(), 5); //! assert_eq!(dt_4d.number_of_simplices(), 1); @@ -487,7 +487,7 @@ pub trait TopologyOwner { /// ]; /// /// // Create a new triangulation -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Check the number of simplices and vertices /// assert_eq!(dt.number_of_simplices(), 1); @@ -888,7 +888,7 @@ impl Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// for (simplex_key, simplex) in dt.simplices() { /// println!("Simplex {:?} has {} vertices", simplex_key, simplex.number_of_vertices()); @@ -943,7 +943,7 @@ impl Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// for (_, simplex) in dt.simplices() { /// println!("Simplex has {} vertices", simplex.number_of_vertices()); @@ -990,7 +990,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.5, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// for (vertex_key, vertex) in dt.vertices() { /// println!("Vertex {:?} at {:?}", vertex_key, vertex.point()); @@ -1031,7 +1031,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let keys: Vec<_> = dt.tds().vertex_keys().collect(); /// assert_eq!(keys.len(), 3); /// # Ok(()) @@ -1070,7 +1070,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let keys: Vec<_> = dt.tds().simplex_keys().collect(); /// assert_eq!(keys.len(), 1); /// # Ok(()) @@ -1116,7 +1116,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -1163,7 +1163,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -1251,7 +1251,7 @@ impl Tds { /// ]; /// /// let vertices = delaunay::try_vertices_from_points(&points)?; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_vertices(), 4); /// # Ok(()) /// # } @@ -1348,7 +1348,7 @@ impl Tds { /// Point::try_from([0.5, 1.0])?, /// ]; /// let vertices_2d = delaunay::try_vertices_from_points(&points_2d)?; - /// let dt_2d = DelaunayTriangulationBuilder::new(&vertices_2d).build::<()>()?; + /// let dt_2d = DelaunayTriangulationBuilder::new(&vertices_2d).build()?; /// assert_eq!(dt_2d.dim(), 2); /// /// // 4D triangulation with 5 vertices (minimum for 4D simplex) @@ -1360,7 +1360,7 @@ impl Tds { /// Point::try_from([0.0, 0.0, 0.0, 1.0])?, /// ]; /// let vertices_4d = delaunay::try_vertices_from_points(&points_4d)?; - /// let dt_4d = DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// let dt_4d = DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// assert_eq!(dt_4d.dim(), 4); /// # Ok(()) /// # } @@ -1442,7 +1442,7 @@ impl Tds { /// ]; /// /// let vertices = delaunay::try_vertices_from_points(&points)?; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_simplices(), 1); // Simplices are automatically created via triangulation /// # Ok(()) /// # } @@ -1473,7 +1473,7 @@ impl Tds { /// ]; /// /// let vertices = delaunay::try_vertices_from_points(&points)?; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_simplices(), 1); // One tetrahedron for 4 points in 3D /// # Ok(()) /// # } @@ -1531,7 +1531,7 @@ impl Tds { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert!(dt.tds().is_connected()); /// /// let empty = dt.tds().number_of_simplices() == 0 || dt.tds().is_connected(); @@ -1723,7 +1723,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -1801,7 +1801,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get the first simplex and its UUID @@ -1878,7 +1878,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get the first vertex and its UUID @@ -1955,7 +1955,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get the first simplex key and expected UUID @@ -1997,7 +1997,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get the first simplex's UUID @@ -2064,7 +2064,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get the first vertex key and expected UUID @@ -2106,7 +2106,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get the first vertex's UUID @@ -2192,7 +2192,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some(vertex_key) = tds.vertex_keys().next() else { /// return Ok(()); @@ -2256,7 +2256,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some(simplex_key) = tds.simplex_keys().next() else { /// return Ok(()); @@ -2304,7 +2304,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let Some(vertex_key) = tds.vertex_keys().next() else { /// return Ok(()); diff --git a/src/core/tds/validation.rs b/src/core/tds/validation.rs index 7d471f5c..ef2abfff 100644 --- a/src/core/tds/validation.rs +++ b/src/core/tds/validation.rs @@ -70,7 +70,7 @@ impl Tds { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// let facet_index = tds.build_facet_to_simplices_index()?; /// assert!(!facet_index.is_empty()); @@ -576,7 +576,7 @@ impl Tds { /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 2> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.tds().is_coherently_oriented()); /// # Ok(()) @@ -999,7 +999,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 4> = - /// DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// /// // Level 2: TDS structural validation /// assert!(dt.tds().is_valid().is_ok()); @@ -1090,7 +1090,7 @@ impl Tds { /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 4> = - /// DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// /// // Levels 1–2: elements + TDS structure /// assert!(dt.tds().validate().is_ok()); @@ -3627,7 +3627,7 @@ mod tests { #[test] fn test_tds_is_valid_passes_for_valid_simplex() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); assert!(dt.tds().is_valid().is_ok()); assert!(dt.tds().validate().is_ok()); } @@ -3635,7 +3635,7 @@ mod tests { #[test] fn test_build_facet_to_simplices_map_basic() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let tds = dt.tds(); let facet_map = tds.build_facet_to_simplices_map().unwrap(); @@ -3746,7 +3746,7 @@ mod tests { #[test] fn test_validate_simplex_coordinate_uniqueness_passes_for_distinct_coords() { let verts = initial_simplex_vertices_3d(); - let dt = DelaunayTriangulation::try_new(&verts).unwrap(); + let dt = DelaunayTriangulation::builder(&verts).build().unwrap(); let tds = dt.tds(); assert!(tds.validate_simplex_coordinate_uniqueness().is_ok()); } diff --git a/src/core/traits/facet_cache.rs b/src/core/traits/facet_cache.rs index e2ab9529..ec700114 100644 --- a/src/core/traits/facet_cache.rs +++ b/src/core/traits/facet_cache.rs @@ -315,7 +315,9 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - DelaunayTriangulation::try_new(&vertices).expect("Failed to create test triangulation") + DelaunayTriangulation::builder(&vertices) + .build() + .expect("Failed to create test triangulation") } #[test] diff --git a/src/core/triangulation.rs b/src/core/triangulation.rs index d83aaaca..fffd6ad5 100644 --- a/src/core/triangulation.rs +++ b/src/core/triangulation.rs @@ -161,7 +161,7 @@ where /// delaunay::vertex![1.0, 0.0; data = 20]?, /// delaunay::vertex![0.0, 1.0; data = 30]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let key = dt.vertices().next().ok_or(ExampleError::MissingVertex)?.0; /// let prev = dt.set_vertex_data(key, Some(99))?; /// assert!(prev.is_some()); @@ -221,7 +221,7 @@ where /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).simplex_data_type::().build()?; /// let key = dt.simplices().next().ok_or(ExampleError::MissingSimplex)?.0; /// let prev = dt.set_simplex_data(key, Some(42))?; /// assert_eq!(prev, None); diff --git a/src/core/util/facet_keys.rs b/src/core/util/facet_keys.rs index 97c3c66e..2d4c7f4b 100644 --- a/src/core/util/facet_keys.rs +++ b/src/core/util/facet_keys.rs @@ -51,7 +51,7 @@ use thiserror::Error; /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get facet vertex keys from a simplex - no need to materialize Vertex objects @@ -474,7 +474,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = &dt.as_triangulation().tds; // Test 1: Basic functionality - successful key derivation @@ -614,7 +614,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = &dt.as_triangulation().tds; let simplex_key = tds.simplex_keys().next().unwrap(); assert!(verify_facet_index_consistency(tds, simplex_key, simplex_key, 0).unwrap()); diff --git a/src/core/util/facet_utils.rs b/src/core/util/facet_utils.rs index 0e1c1638..c2cf6061 100644 --- a/src/core/util/facet_utils.rs +++ b/src/core/util/facet_utils.rs @@ -350,8 +350,8 @@ mod tests { let mut vertices2 = shared_vertices; vertices2.push(vertex_b); - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; @@ -441,8 +441,8 @@ mod tests { let mut vertices2 = shared_edge; vertices2.push(vertex_d); - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; @@ -490,8 +490,8 @@ mod tests { // Edge 2: shared_vertex to vertex_right let vertices2 = vec![shared_vertex, vertex_right]; - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; @@ -542,7 +542,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = &dt.as_triangulation().tds; let simplex_key = tds.simplex_keys().next().unwrap(); @@ -582,7 +582,7 @@ mod tests { vertex!([1.0, 1.0, 2.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = &dt.as_triangulation().tds; let simplex_key = tds.simplex_keys().next().unwrap(); @@ -628,8 +628,8 @@ mod tests { vertex!([10.0, 10.0, 11.0]).unwrap(), ]; - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; @@ -660,8 +660,8 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt1 = DelaunayTriangulation::try_new(&vertices).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; @@ -724,8 +724,8 @@ mod tests { let mut vertices2 = shared_tetrahedron; vertices2.push(vertex_f); - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; @@ -785,8 +785,8 @@ mod tests { let mut vertices2 = shared_4d_simplex; vertices2.push(vertex_h); - let dt1 = DelaunayTriangulation::try_new(&vertices1).unwrap(); - let dt2 = DelaunayTriangulation::try_new(&vertices2).unwrap(); + let dt1 = DelaunayTriangulation::builder(&vertices1).build().unwrap(); + let dt2 = DelaunayTriangulation::builder(&vertices2).build().unwrap(); let tds1 = &dt1.as_triangulation().tds; let tds2 = &dt2.as_triangulation().tds; diff --git a/src/core/util/jaccard.rs b/src/core/util/jaccard.rs index 55b383ae..8d409c4e 100644 --- a/src/core/util/jaccard.rs +++ b/src/core/util/jaccard.rs @@ -218,7 +218,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// let coord_set = extract_vertex_coordinate_set(tds); @@ -284,7 +284,7 @@ const fn canonical_edge(u: u128, v: u128) -> (u128, u128) { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// let edge_set = extract_edge_set(tds)?; @@ -367,7 +367,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// let facet_set = extract_facet_identifier_set(tds)?; @@ -447,7 +447,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// /// let facet_set = extract_hull_facet_set(&hull, dt.as_triangulation())?; @@ -710,7 +710,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = &dt.as_triangulation().tds; // Sub-test: Vertex coordinate extraction @@ -738,7 +738,7 @@ mod tests { assert_eq!(facet_set.len(), 4, "Tetrahedron should have 4 facets"); // Sub-test: Hull facet extraction - let dt_hull = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt_hull = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt_hull.as_triangulation(); let hull = ConvexHull::try_from_triangulation(tri).unwrap(); let hull_facet_set = extract_hull_facet_set(&hull, tri).unwrap(); @@ -753,7 +753,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.as_triangulation().tds.simplex_keys().next().unwrap(); let invalid_vkey = VertexKey::from(KeyData::from_ffi(u64::MAX)); @@ -778,7 +778,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.as_triangulation().tds.simplex_keys().next().unwrap(); let invalid_vkey = VertexKey::from(KeyData::from_ffi(u64::MAX)); @@ -817,7 +817,7 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let hull = ConvexHull::try_from_triangulation(tri).unwrap(); diff --git a/src/core/validation.rs b/src/core/validation.rs index 0ca54c42..7361ace1 100644 --- a/src/core/validation.rs +++ b/src/core/validation.rs @@ -182,7 +182,7 @@ pub(crate) fn insertion_error_to_invariant_error( /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let result: Result<(), InvariantError> = dt.as_triangulation().validate(); /// assert!(result.is_ok()); @@ -570,7 +570,7 @@ impl Default for ValidationPolicy { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let mut dt: DelaunayTriangulation<_, (), (), 3> = -/// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); /// /// // Optional: relax topology checks for speed (weaker guarantees). @@ -1210,7 +1210,7 @@ where /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 4> = - /// DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// /// // Level 3: topology validation (manifold-with-boundary + Euler characteristic) /// assert!(dt.as_triangulation().is_valid_topology().is_ok()); @@ -1245,7 +1245,7 @@ where /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.as_triangulation().topology_diagnostic().is_none()); /// # Ok(()) @@ -1284,7 +1284,7 @@ where /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.as_triangulation().topology_report().is_ok()); /// # Ok(()) @@ -1384,7 +1384,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert!(dt.as_triangulation().validate_at_completion().is_ok()); /// # Ok(()) /// # } @@ -1465,7 +1465,7 @@ where /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 4> = - /// DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// /// // Levels 1–3: elements + TDS structure + topology /// assert!(dt.as_triangulation().validate().is_ok()); @@ -1515,7 +1515,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.as_triangulation().validation_report().is_ok()); /// # Ok(()) @@ -2975,7 +2975,7 @@ mod tests { test_vertex([0.5, 0.5, 0.5]), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert!(dt.as_triangulation().validation_report().is_ok()); } @@ -3023,7 +3023,7 @@ mod tests { let expected_vertices = vertices.len(); assert_eq!(expected_vertices, $dim + 1); - let dt = DelaunayTriangulation::try_new(&vertices) + let dt = DelaunayTriangulation::builder(&vertices).build() .expect("simplex construction should succeed"); let tri = dt.as_triangulation(); @@ -3308,7 +3308,7 @@ mod tests { test_vertex([0.0, 1.0, 0.0]), test_vertex([0.0, 0.0, 1.0]), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); assert!(tri.tds.validate().is_ok()); @@ -3804,7 +3804,7 @@ mod tests { test_vertex([0.0, 0.0, 1.0]), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let suspicion = SuspicionFlags { repair_loop_entered: true, diff --git a/src/core/vertex.rs b/src/core/vertex.rs index 729929cd..f49cf0b3 100644 --- a/src/core/vertex.rs +++ b/src/core/vertex.rs @@ -294,7 +294,7 @@ impl Vertex { /// delaunay::prelude::Vertex::<(), _>::try_new([1.0, 0.0])?, /// delaunay::prelude::Vertex::<(), _>::try_new([0.0, 1.0])?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_, vertex)) = dt.vertices().next() else { /// return Ok(()); /// }; diff --git a/src/delaunay/builder.rs b/src/delaunay/builder.rs index f025f6b9..b104af40 100644 --- a/src/delaunay/builder.rs +++ b/src/delaunay/builder.rs @@ -1,4 +1,5 @@ -//! Fluent builder for [`DelaunayTriangulation`] with optional toroidal topology. +//! Fluent builder for [`DelaunayTriangulation`] with optional toroidal topology +//! and typed simplex storage. //! //! [`DelaunayTriangulationBuilder`] unifies the existing family of `DelaunayTriangulation` //! constructors under a single, composable API and adds first-class support for @@ -12,6 +13,8 @@ //! | Custom `ConstructionOptions` or `TopologyGuarantee` | [`DelaunayTriangulationBuilder`] | //! | Periodic toroidal quotient (χ = 0) | [`DelaunayTriangulationBuilder`] with [`.try_toroidal()`](DelaunayTriangulationBuilder::try_toroidal) | //! | Canonicalized toroidal points | [`DelaunayTriangulationBuilder`] with [`.try_canonicalized_toroidal()`](DelaunayTriangulationBuilder::try_canonicalized_toroidal) | +//! | Euler/topology metadata expectation only | [`DelaunayTriangulationBuilder`] with [`.global_topology(...)`](DelaunayTriangulationBuilder::global_topology) | +//! | Persisted simplex payloads | [`DelaunayTriangulationBuilder`] with [`.simplex_data_type::()`](DelaunayTriangulationBuilder::simplex_data_type), then [`DelaunayTriangulation::fill_simplex_data`] | //! | Custom kernel (`RobustKernel`, etc.) | [`DelaunayTriangulationBuilder::build_with_kernel`] | //! //! # Canonicalized vs periodic toroidal construction @@ -47,7 +50,7 @@ //! ]; //! //! let dt = DelaunayTriangulationBuilder::new(&vertices) -//! .build::<()>()?; +//! .build()?; //! //! assert_eq!(dt.number_of_vertices(), 3); //! # Ok(()) @@ -71,7 +74,7 @@ //! let dt = DelaunayTriangulationBuilder::new(&vertices) //! .try_canonicalized_toroidal([1.0, 1.0]) //! ? -//! .build::<()>()?; +//! .build()?; //! //! assert_eq!(dt.number_of_vertices(), 4); //! # Ok(()) @@ -102,7 +105,7 @@ //! let dt = DelaunayTriangulationBuilder::new(&vertices) //! .try_toroidal([1.0, 1.0]) //! ? -//! .build_with_kernel::<_, ()>(&kernel)?; +//! .build_with_kernel(&kernel)?; //! //! assert_eq!(dt.number_of_vertices(), 7); //! // Every vertex has a valid incident simplex (no boundary). @@ -114,8 +117,9 @@ #![forbid(unsafe_code)] use crate::construction::{ - ConstructionOptions, DelaunayConstructionFailure, DelaunayTriangulationConstructionError, - InitialSimplexStrategy, RetryPolicy, + ConstructionOptions, ConstructionStatistics, DelaunayConstructionFailure, + DelaunayTriangulationConstructionError, DelaunayTriangulationConstructionErrorWithStatistics, + InitialSimplexStrategy, RetryPolicy, duration_nanos_saturating, }; use crate::core::algorithms::incremental_insertion::InsertionError; use crate::core::collections::{ @@ -152,7 +156,9 @@ use num_traits::ToPrimitive; use rand::SeedableRng; use rand::rngs::StdRng; use rand::seq::SliceRandom; +use std::marker::PhantomData; use std::num::NonZeroUsize; +use std::time::Instant; use thiserror::Error; const TWO_POW_52_I64: i64 = 4_503_599_627_370_496; // 2^52 const TWO_POW_52_F64: f64 = 4_503_599_627_370_496.0; // 2^52 @@ -391,10 +397,12 @@ pub enum ExplicitConstructionError { expected: usize, }, /// A simplex contains duplicate vertex indices. - #[error("Simplex {simplex_index}: contains duplicate vertex indices")] + #[error("Simplex {simplex_index}: contains duplicate vertex index {vertex_index}")] DuplicateVertexInSimplex { /// The index of the simplex in the input slice. simplex_index: usize, + /// The duplicated vertex index. + vertex_index: usize, }, /// No simplices were provided. #[error("No simplices provided for explicit construction")] @@ -549,6 +557,7 @@ fn validate_explicit_simplex_specs( if vi == vj { return Err(ExplicitConstructionError::DuplicateVertexInSimplex { simplex_index: simplex_idx, + vertex_index: vi, }); } } @@ -569,10 +578,13 @@ fn validate_explicit_simplex_specs( /// - `'v` — Lifetime of the borrowed vertex slice. /// - `U` — Vertex data type (inferred from the vertex slice). /// - `D` — Spatial dimension (inferred from the vertex slice). +/// - `V` — Simplex data type selected before construction; defaults to `()`. /// -/// The simplex data type `V` and kernel `K` are deferred to the -/// [`build`](Self::build) / [`build_with_kernel`](Self::build_with_kernel) -/// call, keeping the builder type signature concise. +/// The simplex data type `V` defaults to `()` and can be inferred from the +/// caller's expected return type. The builder never computes or validates +/// payload values; attach persisted simplex data afterward with follow-on +/// methods on [`DelaunayTriangulation`], keeping construction focused on the +/// Levels 1–5 Delaunay validity boundary. /// /// # Examples /// @@ -592,13 +604,13 @@ fn validate_explicit_simplex_specs( /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::Pseudomanifold) /// .construction_options(ConstructionOptions::default()) -/// .build::<()>()?; +/// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 4); /// # Ok(()) /// # } /// ``` -pub struct DelaunayTriangulationBuilder<'v, U, const D: usize> { +pub struct DelaunayTriangulationBuilder<'v, U, const D: usize, V = ()> { vertices: &'v [Vertex], /// Topology mode for construction. /// @@ -618,30 +630,84 @@ pub struct DelaunayTriangulationBuilder<'v, U, const D: usize> { /// Euclidean paths default to [`GlobalTopology::Euclidean`], while periodic /// image-point construction derives closed toroidal metadata from its domain. requested_global_topology: Option>, + _simplex_data: PhantomData, } +/// Topology mode requested by the public builder chain. +/// +/// This stores only proof-carrying topology values, so fallible parsing stays at +/// raw-input setters and the build terminals never need to revalidate periods. +#[derive(Clone, Copy)] enum BuilderTopology { + /// Ordinary Euclidean batch construction. Euclidean, + /// Canonicalize input coordinates into a toroidal fundamental domain, then + /// use Euclidean batch construction. Canonicalized(ToroidalDomain), + /// Construct a toroidal quotient through periodic image points. PeriodicImagePoint(ToroidalDomain), } +/// Non-periodic topology mode accepted by batch-construction backends. +/// +/// The public statistics and non-statistics builder terminals share this type so +/// both paths apply the same topology rejection and canonicalization preamble +/// before choosing the terminal-specific construction backend. +#[derive(Clone, Copy)] +enum BatchBuilderTopology { + /// Use the original vertex slice as-is. + Euclidean, + /// Canonicalize vertices before Euclidean batch construction. + Canonicalized(ToroidalDomain), +} + +/// Prepared vertex storage for non-periodic batch construction paths. +/// +/// Euclidean construction borrows the caller's slice, while canonicalized +/// toroidal construction owns a transformed vertex buffer. This lets public +/// builder terminals pass one stable slice shape to their backend without +/// duplicating canonicalization behavior. +enum PreparedBatchVertices<'a, U, const D: usize> { + /// Original caller-provided vertices. + Borrowed(&'a [Vertex]), + /// Canonicalized vertices with UUIDs and payloads preserved. + Owned(Vec>), +} + +impl PreparedBatchVertices<'_, U, D> { + /// Returns the vertices to pass to a batch-construction backend. + fn as_slice(&self) -> &[Vertex] { + match self { + Self::Borrowed(vertices) => vertices, + Self::Owned(vertices) => vertices, + } + } +} + // ============================================================================= // BUILDER IMPL — f64 coordinate storage, any vertex data U // -// U is inferred from the vertex slice. +// U is inferred from the vertex slice. V defaults to () and can be inferred +// from the expected DelaunayTriangulation return type when callers need typed +// persisted simplex payloads. // // let vertices = vec![Vertex::<(), _>::try_new([0.0, 0.0])?, ...]; -// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>(); +// let dt = DelaunayTriangulationBuilder::new(&vertices).build(); // // let typed: [Vertex; 3] = [Vertex::<_, _>::try_new_with_data([0.0, 0.0], 1)?, ...]; -// let dt = DelaunayTriangulationBuilder::new(&typed).build::<()>(); +// let dt = DelaunayTriangulationBuilder::new(&typed).build(); // // ============================================================================= impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// Creates a builder for `f64` vertices with any user data type `U`. /// + /// This is intentionally infallible: it creates an inert construction + /// request and does not parse the point set into a triangulation. The + /// fallible invariant boundary is [`build`](Self::build) or + /// [`build_with_kernel`](Self::build_with_kernel), which validates the + /// constructed triangulation before returning it. + /// /// `U` is inferred from the vertex slice — no explicit type annotations needed /// for either `U = ()` (the common case) or typed vertex data. /// @@ -655,7 +721,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// # fn main() -> DelaunayResult<()> { /// // No vertex data (U = () inferred) /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let _dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let _dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Typed vertex data (U = i32 inferred) /// let typed: [Vertex; 3] = [ @@ -663,7 +729,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// delaunay::vertex![1.0, 0.0; data = 2]?, /// delaunay::vertex![0.0, 1.0; data = 3]?, /// ]; - /// let _dt = DelaunayTriangulationBuilder::new(&typed).build::<()>()?; + /// let _dt = DelaunayTriangulationBuilder::new(&typed).build()?; /// # Ok(()) /// # } /// ``` @@ -676,6 +742,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { construction_options: ConstructionOptions::default(), explicit_simplices: None, requested_global_topology: None, + _simplex_data: PhantomData, } } @@ -716,7 +783,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// /// let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) /// .map_err(DelaunayTriangulationConstructionError::from)? - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 4); /// assert_eq!(dt.number_of_simplices(), 2); @@ -781,7 +848,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// /// let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices_generic(&vertices, &simplices) /// .map_err(DelaunayTriangulationConstructionError::from)? - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 3); /// assert_eq!(dt.number_of_simplices(), 1); @@ -798,6 +865,62 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { builder.explicit_simplices = Some(explicit_simplices); Ok(builder) } +} + +impl<'v, U, V, const D: usize> DelaunayTriangulationBuilder<'v, U, D, V> { + /// Selects the simplex payload type before topology storage is allocated. + /// + /// This method does not compute or validate payload values. It only chooses + /// the persisted simplex storage type so later calls such as + /// [`DelaunayTriangulation::fill_simplex_data`] can assign values without + /// rebuilding topology or remapping simplex keys. + /// + /// # Examples + /// + /// ```rust + /// use delaunay::prelude::construction::{ + /// DelaunayResult, DelaunayTriangulationBuilder, + /// }; + /// + /// # fn main() -> DelaunayResult<()> { + /// let vertices = vec![ + /// delaunay::vertex![0.0, 0.0]?, + /// delaunay::vertex![1.0, 0.0]?, + /// delaunay::vertex![0.0, 1.0]?, + /// ]; + /// + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) + /// .simplex_data_type::() + /// .build()?; + /// dt.fill_simplex_data(|_, simplex| simplex.number_of_vertices()); + /// + /// for (_, simplex) in dt.simplices() { + /// assert_eq!(simplex.data(), Some(&3)); + /// } + /// # Ok(()) + /// # } + /// ``` + #[must_use] + pub const fn simplex_data_type(self) -> DelaunayTriangulationBuilder<'v, U, D, W> { + let Self { + vertices, + topology, + topology_guarantee, + construction_options, + explicit_simplices, + requested_global_topology, + _simplex_data: _, + } = self; + DelaunayTriangulationBuilder { + vertices, + topology, + topology_guarantee, + construction_options, + explicit_simplices, + requested_global_topology, + _simplex_data: PhantomData, + } + } /// Enables periodic toroidal topology via the image-point method. /// @@ -834,7 +957,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .try_toroidal([1.0, 1.0]) /// ? - /// .build_with_kernel::<_, ()>(&kernel)?; + /// .build_with_kernel(&kernel)?; /// /// assert_eq!(dt.number_of_vertices(), 7); /// assert!(dt.tds().is_valid().is_ok()); @@ -879,7 +1002,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// let kernel = RobustKernel::new(); /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .toroidal(domain) - /// .build_with_kernel::<_, ()>(&kernel)?; + /// .build_with_kernel(&kernel)?; /// /// assert_eq!(dt.number_of_vertices(), 7); /// # Ok(()) @@ -921,7 +1044,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .try_canonicalized_toroidal([1.0, 1.0]) /// ? - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 4); /// # Ok(()) @@ -963,7 +1086,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// let domain = ToroidalDomain::<2>::unit(); /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .canonicalized_toroidal(domain) - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 4); /// # Ok(()) @@ -998,7 +1121,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::Pseudomanifold) - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.topology_guarantee(), TopologyGuarantee::Pseudomanifold); /// # Ok(()) @@ -1057,7 +1180,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// let result = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) /// .map_err(DelaunayTriangulationConstructionError::from)? /// .global_topology(topology) - /// .build::<()>(); + /// .build(); /// /// assert!(result.is_err()); /// # Ok(()) @@ -1098,7 +1221,7 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { /// /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .construction_options(opts) - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 3); /// # Ok(()) @@ -1111,9 +1234,10 @@ impl<'v, U, const D: usize> DelaunayTriangulationBuilder<'v, U, D> { } } -impl DelaunayTriangulationBuilder<'_, U, D> +impl DelaunayTriangulationBuilder<'_, U, D, V> where U: DataType, + V: DataType, { // ------------------------------------------------------------------------- // Internal helpers @@ -1254,6 +1378,35 @@ where Ok(out) } + /// Prepares Euclidean and canonicalized-toroidal vertices for batch construction. + /// + /// This centralizes topology metadata rejection, topology-model validation, + /// and canonicalization so the statistics and non-statistics build terminals + /// cannot drift apart. + fn prepare_batch_vertices( + topology: BatchBuilderTopology, + global_topology: GlobalTopology, + vertices: &[Vertex], + ) -> Result, DelaunayTriangulationConstructionError> { + match topology { + BatchBuilderTopology::Euclidean => { + Self::reject_euclidean_non_euclidean_topology(global_topology)?; + Ok(PreparedBatchVertices::Borrowed(vertices)) + } + BatchBuilderTopology::Canonicalized(domain) => { + Self::reject_canonicalized_non_euclidean_topology(global_topology)?; + let topology = GlobalTopology::Toroidal { + domain, + mode: ToroidalConstructionMode::Canonicalized, + }; + let topology_model = topology.model(); + Self::validate_topology_model(&topology_model)?; + let canonical = Self::canonicalize_vertices(vertices, &topology_model)?; + Ok(PreparedBatchVertices::Owned(canonical)) + } + } + } + // ------------------------------------------------------------------------- // Build methods // ------------------------------------------------------------------------- @@ -1290,25 +1443,91 @@ where /// ]; /// /// let dt = DelaunayTriangulationBuilder::new(&vertices) - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(dt.number_of_vertices(), 4); /// assert!(dt.validate().is_ok()); /// # Ok(()) /// # } /// ``` - pub fn build( + pub fn build( self, ) -> Result< DelaunayTriangulation, U, V, D>, DelaunayTriangulationConstructionError, - > - where - V: DataType, - { + > { self.build_with_kernel(&AdaptiveKernel::new()) } + /// Builds the triangulation and returns aggregate construction statistics. + /// + /// This is the fluent builder terminal for callers that want the configured + /// workflow and skipped-input observability in one chain. Euclidean and + /// canonicalized-toroidal construction return statistics from the same + /// batch backend used by [`Self::build_with_kernel`]. Explicit + /// connectivity and true periodic quotient construction bypass the batch + /// insertion statistics collector; on those successful paths the returned + /// statistics record the final vertex count in + /// [`ConstructionStatistics::inserted`] and total construction timing in + /// [`crate::diagnostics::ConstructionTelemetry::construction_total_nanos`], + /// while per-insertion telemetry stays at its default values. + /// + /// # Errors + /// + /// Returns [`DelaunayTriangulationConstructionErrorWithStatistics`] if + /// construction fails. The error carries partial statistics when the selected + /// construction path collects them; otherwise it carries default counters + /// plus total construction timing when that timing was observed. + /// + /// # Examples + /// + /// ```rust + /// use delaunay::prelude::construction::{ + /// DelaunayTriangulationBuilder, DelaunayTriangulationConstructionErrorWithStatistics, + /// }; + /// + /// # #[derive(Debug, thiserror::Error)] + /// # enum ExampleError { + /// # #[error(transparent)] + /// # Source(#[from] DelaunayTriangulationConstructionErrorWithStatistics), + /// # #[error(transparent)] + /// # Coordinate(#[from] delaunay::prelude::geometry::CoordinateConversionError), + /// # } + /// # fn main() -> Result<(), ExampleError> { + /// let vertices = vec![ + /// delaunay::vertex![0.0, 0.0, 0.0]?, + /// delaunay::vertex![1.0, 0.0, 0.0]?, + /// delaunay::vertex![0.0, 1.0, 0.0]?, + /// delaunay::vertex![0.0, 0.0, 1.0]?, + /// ]; + /// + /// let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + /// .build_with_statistics()?; + /// + /// assert_eq!(dt.number_of_vertices(), stats.inserted); + /// # Ok(()) + /// # } + /// ``` + #[expect( + clippy::result_large_err, + reason = "Public API intentionally returns by-value construction statistics" + )] + #[expect( + clippy::type_complexity, + reason = "Public API returns the constructed triangulation together with construction statistics" + )] + pub fn build_with_statistics( + self, + ) -> Result< + ( + DelaunayTriangulation, U, V, D>, + ConstructionStatistics, + ), + DelaunayTriangulationConstructionErrorWithStatistics, + > { + self.build_with_kernel_and_statistics(&AdaptiveKernel::new()) + } + /// Builds the triangulation using a caller-supplied kernel. /// /// [`build()`](Self::build) already defaults to [`AdaptiveKernel`], so this method is @@ -1346,19 +1565,18 @@ where /// /// let kernel = RobustKernel::new(); /// let dt = DelaunayTriangulationBuilder::new(&vertices) - /// .build_with_kernel::<_, ()>(&kernel)?; + /// .build_with_kernel(&kernel)?; /// /// assert_eq!(dt.number_of_vertices(), 4); /// # Ok(()) /// # } /// ``` - pub fn build_with_kernel( + pub fn build_with_kernel( self, kernel: &K, ) -> Result, DelaunayTriangulationConstructionError> where K: Kernel, - V: DataType, { // Explicit-simplices path: bypass Delaunay insertion entirely. if let Some(simplices) = self.explicit_simplices { @@ -1378,38 +1596,9 @@ where ); } - match self.topology { - BuilderTopology::Euclidean => { - Self::reject_euclidean_non_euclidean_topology(self.global_topology_or_default())?; - // Euclidean path: delegate directly. - let dt = DelaunayTriangulation::try_with_topology_guarantee_and_options( - kernel, - self.vertices, - self.topology_guarantee, - self.construction_options, - )?; - Ok(dt) - } - BuilderTopology::Canonicalized(domain) => { - Self::reject_canonicalized_non_euclidean_topology( - self.global_topology_or_default(), - )?; - let topology = GlobalTopology::Toroidal { - domain, - mode: ToroidalConstructionMode::Canonicalized, - }; - let topology_model = topology.model(); - Self::validate_topology_model(&topology_model)?; - // Canonicalized toroidal construction: canonicalize then delegate. - let canonical = Self::canonicalize_vertices(self.vertices, &topology_model)?; - let dt = DelaunayTriangulation::try_with_topology_guarantee_and_options( - kernel, - &canonical, - self.topology_guarantee, - self.construction_options, - )?; - Ok(dt) - } + let batch_topology = match self.topology { + BuilderTopology::Euclidean => BatchBuilderTopology::Euclidean, + BuilderTopology::Canonicalized(domain) => BatchBuilderTopology::Canonicalized(domain), BuilderTopology::PeriodicImagePoint(domain) => { let topology = Self::periodic_image_global_topology(domain); Self::reject_periodic_conflicting_global_topology( @@ -1461,11 +1650,182 @@ where source: e, } })?; - Ok(dt) + return Ok(dt); + } + }; + + let prepared = Self::prepare_batch_vertices( + batch_topology, + self.global_topology_or_default(), + self.vertices, + )?; + let dt = DelaunayTriangulation::build_with_kernel_options( + kernel, + prepared.as_slice(), + self.topology_guarantee, + self.construction_options, + )?; + Ok(dt) + } + + /// Builds the triangulation with a caller-supplied kernel and returns statistics. + /// + /// This is the statistics-returning counterpart of + /// [`build_with_kernel`](Self::build_with_kernel). Euclidean and + /// canonicalized-toroidal construction return batch insertion statistics. + /// Explicit connectivity and true periodic quotient construction bypass the + /// batch insertion statistics collector; on those successful paths the + /// returned statistics record the final vertex count in + /// [`ConstructionStatistics::inserted`] and total construction timing in + /// [`crate::diagnostics::ConstructionTelemetry::construction_total_nanos`], + /// while per-insertion telemetry stays at its default values. + /// + /// # Errors + /// + /// Returns [`DelaunayTriangulationConstructionErrorWithStatistics`] if + /// construction fails. The error carries partial statistics when the selected + /// construction path collects them; otherwise it carries default counters + /// plus total construction timing when that timing was observed. + /// + /// # Examples + /// + /// ```rust + /// use delaunay::prelude::construction::{ + /// DelaunayTriangulationBuilder, DelaunayTriangulationConstructionErrorWithStatistics, + /// }; + /// use delaunay::prelude::geometry::RobustKernel; + /// + /// # #[derive(Debug, thiserror::Error)] + /// # enum ExampleError { + /// # #[error(transparent)] + /// # Source(#[from] DelaunayTriangulationConstructionErrorWithStatistics), + /// # #[error(transparent)] + /// # Coordinate(#[from] delaunay::prelude::geometry::CoordinateConversionError), + /// # } + /// # fn main() -> Result<(), ExampleError> { + /// let vertices = vec![ + /// delaunay::vertex![0.0, 0.0, 0.0]?, + /// delaunay::vertex![1.0, 0.0, 0.0]?, + /// delaunay::vertex![0.0, 1.0, 0.0]?, + /// delaunay::vertex![0.0, 0.0, 1.0]?, + /// ]; + /// let kernel = RobustKernel::new(); + /// + /// let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + /// .build_with_kernel_and_statistics(&kernel)?; + /// + /// assert_eq!(dt.number_of_vertices(), stats.inserted); + /// # Ok(()) + /// # } + /// ``` + #[expect( + clippy::result_large_err, + reason = "Public API intentionally returns by-value construction statistics" + )] + pub fn build_with_kernel_and_statistics( + self, + kernel: &K, + ) -> Result< + (DelaunayTriangulation, ConstructionStatistics), + DelaunayTriangulationConstructionErrorWithStatistics, + > + where + K: Kernel, + { + let construction_started = Instant::now(); + + let result = if self.explicit_simplices.is_some() + || matches!(&self.topology, BuilderTopology::PeriodicImagePoint(_)) + { + self.build_with_kernel(kernel) + .map(Self::with_minimal_success_statistics) + .map_err(Self::with_default_error_statistics) + } else { + let global_topology = self.global_topology_or_default(); + let vertices = self.vertices; + let topology_guarantee = self.topology_guarantee; + let construction_options = self.construction_options; + let batch_topology = match self.topology { + BuilderTopology::Euclidean => BatchBuilderTopology::Euclidean, + BuilderTopology::Canonicalized(domain) => { + BatchBuilderTopology::Canonicalized(domain) + } + BuilderTopology::PeriodicImagePoint(_) => { + return Self::record_construction_total_timing( + self.build_with_kernel(kernel) + .map(Self::with_minimal_success_statistics) + .map_err(Self::with_default_error_statistics), + construction_started, + ); + } + }; + + Self::prepare_batch_vertices(batch_topology, global_topology, vertices) + .map_err(Self::with_default_error_statistics) + .and_then(|prepared| { + DelaunayTriangulation::build_with_kernel_options_and_statistics( + kernel, + prepared.as_slice(), + topology_guarantee, + construction_options, + ) + }) + }; + + Self::record_construction_total_timing(result, construction_started) + } + + #[expect( + clippy::result_large_err, + reason = "Internal helper preserves the public by-value construction-statistics error" + )] + fn record_construction_total_timing( + result: Result< + (DelaunayTriangulation, ConstructionStatistics), + DelaunayTriangulationConstructionErrorWithStatistics, + >, + construction_started: Instant, + ) -> Result< + (DelaunayTriangulation, ConstructionStatistics), + DelaunayTriangulationConstructionErrorWithStatistics, + > { + let elapsed_nanos = duration_nanos_saturating(construction_started.elapsed()); + match result { + Ok((dt, mut statistics)) => { + statistics + .telemetry + .record_construction_total_timing(elapsed_nanos); + Ok((dt, statistics)) + } + Err(mut error) => { + error + .statistics + .telemetry + .record_construction_total_timing(elapsed_nanos); + Err(error) } } } + fn with_minimal_success_statistics( + dt: DelaunayTriangulation, + ) -> (DelaunayTriangulation, ConstructionStatistics) { + let statistics = ConstructionStatistics { + inserted: dt.number_of_vertices(), + ..ConstructionStatistics::default() + }; + (dt, statistics) + } + + fn with_default_error_statistics( + error: DelaunayTriangulationConstructionError, + ) -> DelaunayTriangulationConstructionErrorWithStatistics { + DelaunayTriangulationConstructionErrorWithStatistics { + error, + statistics: ConstructionStatistics::default(), + } + } + /// Checks whether explicit-simplex construction can honor the requested options. /// /// Explicit connectivity bypasses insertion ordering, deduplication, and @@ -1508,7 +1868,7 @@ where /// 11. Validate geometric nondegeneracy (reject zero-volume simplices). /// 12. Validate the Euclidean Level 5 Delaunay property unless final /// enforcement was disabled in [`ConstructionOptions`]. - fn build_explicit( + fn build_explicit( kernel: &K, vertices: &[Vertex], simplices: ValidatedExplicitSimplices<'_>, @@ -1518,7 +1878,6 @@ where ) -> Result, DelaunayTriangulationConstructionError> where K: Kernel, - V: DataType, { Self::reject_explicit_non_euclidean_topology(global_topology)?; @@ -1667,12 +2026,11 @@ where /// this API boundary. Explicit non-Euclidean topology is rejected earlier in /// `build_explicit` until quotient embedding validation exists for explicit /// connectivity. - fn enforce_explicit_delaunay_property( + fn enforce_explicit_delaunay_property( candidate: &DelaunayTriangulationCandidate, ) -> Result where K: Kernel, - V: DataType, { candidate.validate_delaunay_property().map_err(|source| { ExplicitConstructionError::DelaunayValidation { @@ -1827,7 +2185,7 @@ where clippy::too_many_lines, reason = "Image-point periodic DT algorithm is inherently multi-step; splitting would harm readability" )] - fn build_periodic( + fn build_periodic( kernel: &K, canonical_vertices: &[Vertex], topology_model: &M, @@ -1836,7 +2194,6 @@ where ) -> Result, DelaunayTriangulationConstructionError> where K: Kernel, - V: DataType, M: GlobalTopologyModel, { // Keep `build_periodic` self-protecting even if future call paths bypass outer validation. @@ -1997,7 +2354,7 @@ where }), }; let full_dt: DelaunayTriangulation = - match DelaunayTriangulation::try_with_topology_guarantee_and_options( + match DelaunayTriangulation::build_with_kernel_options( kernel, &expanded, TopologyGuarantee::Pseudomanifold, @@ -2984,7 +3341,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 3); assert_eq!(dt.dim(), 2); @@ -3000,7 +3357,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert!(dt.validate().is_ok()); @@ -3015,7 +3372,7 @@ mod tests { ]; let result = DelaunayTriangulationBuilder::new(&vertices) .global_topology(GlobalTopology::Spherical) - .build::<()>(); + .build(); assert_matches!( result, @@ -3027,6 +3384,39 @@ mod tests { ); } + #[test] + fn test_builder_statistics_rejects_non_euclidean_global_topology_with_default_stats() { + let vertices = vec![ + vertex!([0.0, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + ]; + let err = DelaunayTriangulationBuilder::new(&vertices) + .global_topology(GlobalTopology::Spherical) + .build_with_statistics() + .unwrap_err(); + + assert_matches!( + err.error, + DelaunayTriangulationConstructionError::Triangulation( + DelaunayConstructionFailure::EuclideanUnsupportedGlobalTopology { + topology: TopologyKind::Spherical, + } + ) + ); + assert_eq!(err.statistics.inserted, 0); + assert_eq!(err.statistics.skipped_duplicate, 0); + assert_eq!(err.statistics.skipped_degeneracy, 0); + assert_eq!(err.statistics.total_attempts, 0); + assert!(err.statistics.attempts_histogram.is_empty()); + assert!(err.statistics.slow_insertions.is_empty()); + assert!(err.statistics.skip_samples.is_empty()); + assert_eq!(err.statistics.telemetry.insertion_wall_time_calls, 0); + assert_eq!(err.statistics.telemetry.construction_preprocessing_nanos, 0); + assert_eq!(err.statistics.telemetry.construction_insert_loop_nanos, 0); + assert_eq!(err.statistics.telemetry.construction_finalize_nanos, 0); + } + #[test] fn test_builder_topology_guarantee_propagated() { let vertices = vec![ @@ -3036,7 +3426,7 @@ mod tests { ]; let dt = DelaunayTriangulationBuilder::new(&vertices) .topology_guarantee(TopologyGuarantee::Pseudomanifold) - .build::<()>() + .build() .unwrap(); assert_eq!(dt.topology_guarantee(), TopologyGuarantee::Pseudomanifold); } @@ -3052,7 +3442,7 @@ mod tests { ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); let dt = DelaunayTriangulationBuilder::new(&vertices) .construction_options(opts) - .build::<()>() + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 3); } @@ -3074,7 +3464,7 @@ mod tests { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap(); // Every vertex coordinate must lie within [0, 1) × [0, 1) @@ -3097,7 +3487,7 @@ mod tests { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap(); for (_, v) in dt.vertices() { @@ -3118,7 +3508,7 @@ mod tests { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert_eq!(dt.dim(), 2); @@ -3137,7 +3527,7 @@ mod tests { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert_eq!(dt.dim(), 2); @@ -3160,7 +3550,7 @@ mod tests { .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() .global_topology(topology) - .build::<()>(); + .build(); assert_matches!( result, @@ -3217,7 +3607,7 @@ mod tests { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0, 1.0]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .unwrap(); assert_eq!(dt.number_of_vertices(), n); assert!(dt.tds().is_valid().is_ok()); @@ -3236,7 +3626,7 @@ mod tests { let result = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0, 1.0, 1.0, 1.0]) .unwrap() - .build::<()>(); + .build(); assert_matches!( result, @@ -3257,7 +3647,7 @@ mod tests { .global_topology(GlobalTopology::Spherical) .try_toroidal([1.0, 1.0]) .unwrap() - .build::<()>(); + .build(); assert_matches!( result, @@ -3280,7 +3670,7 @@ mod tests { .try_toroidal([1.0, 1.0]) .unwrap() .global_topology(GlobalTopology::Euclidean) - .build::<()>(); + .build(); assert_matches!( result, @@ -3306,7 +3696,7 @@ mod tests { .try_toroidal([1.0, 1.0]) .unwrap() .global_topology(requested_topology) - .build::<()>(); + .build(); assert_matches!( result, @@ -3333,7 +3723,7 @@ mod tests { .try_toroidal([1.0, 1.0]) .unwrap() .global_topology(requested_topology) - .build::<()>(); + .build(); assert_matches!( result, @@ -3360,7 +3750,7 @@ mod tests { .try_toroidal([1.0, 1.0]) .unwrap() .global_topology(topology) - .build::<()>() + .build() .unwrap(); assert_eq!(dt.global_topology(), topology); @@ -3375,12 +3765,12 @@ mod tests { vertex!([0.4, 0.9]).unwrap(), ]; let dt_euclidean = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .unwrap(); let dt_toroidal = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap(); assert_eq!( dt_euclidean.number_of_vertices(), @@ -3408,7 +3798,7 @@ mod tests { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 3); @@ -3436,7 +3826,7 @@ mod tests { ]; let kernel = RobustKernel::::new(); let dt = DelaunayTriangulationBuilder::new(&vertices) - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert!(dt.validate().is_ok()); @@ -3638,7 +4028,7 @@ mod tests { vertex!([0.7_f64, 0.9_f64]).unwrap(), vertex!([0.5_f64, 0.4_f64]).unwrap(), ]; - let result = DelaunayTriangulationBuilder::<(), 2>::build_periodic::<_, (), _>( + let result = DelaunayTriangulationBuilder::<(), 2>::build_periodic::<_, _>( &kernel, &canonical_vertices, &MissingPeriodicDomainModel, diff --git a/src/delaunay/construction.rs b/src/delaunay/construction.rs index 21ac5a19..9c6822e7 100644 --- a/src/delaunay/construction.rs +++ b/src/delaunay/construction.rs @@ -2,7 +2,7 @@ //! //! This module contains the configuration surface used by //! [`DelaunayTriangulationBuilder`](crate::builder::DelaunayTriangulationBuilder) -//! and the batch constructors on [`DelaunayTriangulation`](crate::DelaunayTriangulation). +//! and its batch-construction terminals. //! Use it when you need deterministic insertion ordering, duplicate handling, //! initial-simplex selection, retry behavior, or construction telemetry without //! importing flip editing or validation-only APIs. @@ -33,7 +33,7 @@ //! //! let dt = DelaunayTriangulationBuilder::new(&vertices) //! .construction_options(options) -//! .build::<()>()?; +//! .build()?; //! //! assert_eq!(dt.number_of_vertices(), 3); //! # Ok(()) @@ -90,11 +90,15 @@ use crate::geometry::point::Point; use crate::geometry::traits::coordinate::{ CoordinateConversionError, CoordinateValidationError, CoordinateValues, }; -use crate::geometry::util::{RandomPointGenerationError, safe_usize_to_scalar, simplex_volume}; +use crate::geometry::util::{ + RandomPointGenerationError, RandomTriangulationBuilderError, safe_usize_to_scalar, + simplex_volume, +}; use crate::io::visualization::{VisualizationDataValidationError, VisualizationExportError}; use crate::locality::{ accumulate_live_simplex_seeds, clear_simplex_seed_set, retain_live_simplex_seeds, }; +use crate::query::SimplexDataFillError; use crate::repair::DelaunayRepairPolicy; use crate::topology::traits::{ GlobalTopology, GlobalTopologyModelError, TopologyKind, ToroidalConstructionMode, @@ -223,8 +227,9 @@ pub(crate) mod test_hooks { /// /// Use [`DelaunayResult`] for examples, binaries, and quick workflows whose /// fallible operations stay inside coordinate conversion, construction, -/// checked auxiliary-data mutation, insertion/deletion, explicit flip editing, -/// validation, mesh export, and export-schema validation: +/// random-triangulation builder setup, checked auxiliary-data mutation, +/// post-construction simplex-data filling, insertion/deletion, explicit flip +/// editing, validation, mesh export, and export-schema validation: /// /// ```rust /// use delaunay::prelude::construction::{ @@ -238,7 +243,7 @@ pub(crate) mod test_hooks { /// vertex![0.0, 1.0]?, /// ]; /// -/// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// dt.insert_vertex(vertex![0.25, 0.25]?)?; /// dt.validate()?; /// @@ -296,6 +301,14 @@ pub enum DelaunayError { source: Box, }, + /// Post-construction simplex payload filling failed. + #[error("{source}")] + SimplexDataFill { + /// Underlying simplex-data fill failure. + #[source] + source: Box, + }, + /// Validation policy configuration failed. #[error("{source}")] ValidationConfiguration { @@ -345,6 +358,12 @@ impl From for DelaunayError { } } +impl From for DelaunayError { + fn from(source: RandomTriangulationBuilderError) -> Self { + DelaunayTriangulationConstructionError::from(source).into() + } +} + impl From for DelaunayError { fn from(source: CoordinateConversionError) -> Self { Self::CoordinateConversion { @@ -385,6 +404,14 @@ impl From for DelaunayError { } } +impl From for DelaunayError { + fn from(source: SimplexDataFillError) -> Self { + Self::SimplexDataFill { + source: Box::new(source), + } + } +} + impl From for DelaunayError { fn from(source: ValidationConfigurationError) -> Self { Self::ValidationConfiguration { @@ -430,8 +457,8 @@ impl From for DelaunayError { /// This is equivalent to `Result` with [`DelaunayError`] as /// the error type, and is intended for caller-facing examples and applications /// that use the standard construction, checked auxiliary-data mutation, -/// insertion/deletion, explicit flip editing, validation, mesh export, and -/// export-schema validation APIs. +/// post-construction simplex-data filling, insertion/deletion, explicit flip +/// editing, validation, mesh export, and export-schema validation APIs. pub type DelaunayResult = Result; /// Errors that can occur during Delaunay triangulation construction. @@ -2193,6 +2220,44 @@ pub(crate) const fn default_duplicate_tolerance() -> f64 { 1e-10_f64 } +/// Shared numeric parameters derived from a deduplication policy. +/// +/// Keeping the hash-grid cell size and epsilon tolerance together prevents the +/// preprocessing path from drifting on the observable zero-epsilon contract: +/// [`DedupPolicy::Epsilon`] with zero tolerance still uses a positive grid size +/// internally while comparing vertices with exactly zero epsilon. +struct DedupGridParameters { + /// Positive cell size used to construct the spatial hash grid. + grid_cell_size: f64, + /// Epsilon passed to the epsilon-deduplication comparison. + epsilon: f64, +} + +/// Derives hash-grid sizing and epsilon deduplication tolerance together. +/// +/// Exact and disabled deduplication do not use epsilon comparison, but they +/// still receive the default positive tolerance so their hash-grid setup stays +/// parse-don't-validate-safe. +fn dedup_grid_parameters(dedup_policy: DedupPolicy, default_tolerance: f64) -> DedupGridParameters { + match dedup_policy { + DedupPolicy::Epsilon { tolerance } => { + let epsilon = tolerance.get(); + DedupGridParameters { + grid_cell_size: if epsilon > 0.0 { + epsilon + } else { + default_tolerance + }, + epsilon, + } + } + DedupPolicy::Exact | DedupPolicy::Off => DedupGridParameters { + grid_cell_size: default_tolerance, + epsilon: default_tolerance, + }, + } +} + /// Builds a hash-grid index after construction has validated its tolerance. /// /// This preserves the parse-don't-validate boundary between public @@ -2990,271 +3055,6 @@ fn order_vertices_hilbert( // Most common case: f64 with AdaptiveKernel, no vertex or simplex data. impl DelaunayTriangulation, (), (), D> { - /// Creates a Delaunay triangulation from `f64` vertices with no attached data. - /// - /// This convenience constructor uses [`AdaptiveKernel`] and the default - /// construction options. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may contain fewer vertices - /// than the input slice. Use - /// [`try_new_with_construction_statistics`](Self::try_new_with_construction_statistics) - /// when skipped-input observability is required. - /// - /// # Errors - /// Returns an error if the initial simplex cannot be constructed, if - /// insertion fails, or if final validation fails. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{DelaunayResult, DelaunayTriangulation}; - /// - /// # fn main() -> DelaunayResult<()> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// - /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulation::try_new(&vertices)?; - /// assert_eq!(dt.number_of_vertices(), 4); - /// # Ok(()) - /// # } - /// ``` - pub fn try_new( - vertices: &[Vertex<(), D>], - ) -> Result { - Self::try_with_kernel(&AdaptiveKernel::::new(), vertices) - } - - /// Creates a default `f64` triangulation and returns aggregate construction - /// statistics. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may have skipped some input - /// vertices. Inspect [`ConstructionStatistics::total_skipped`] and - /// [`ConstructionStatistics::skip_samples`] when the caller requires a - /// strict all-inputs-inserted contract. - /// - /// # Errors - /// Returns [`DelaunayTriangulationConstructionErrorWithStatistics`] if - /// construction fails. The error includes partial statistics collected - /// before failure. If - /// [`ConstructionOptions::without_final_delaunay_enforcement`] is selected, - /// construction still fails when Levels 1–4 validation fails; only final - /// Level 5 Delaunay repair and validation are skipped. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::DelaunayTriangulation; - /// - /// # #[derive(Debug, thiserror::Error)] - /// # enum ExampleError { - /// # #[error(transparent)] - /// # Source(#[from] delaunay::DelaunayTriangulationConstructionErrorWithStatistics), - /// # #[error(transparent)] - /// # Coordinate(#[from] delaunay::prelude::geometry::CoordinateConversionError), - /// # } - /// # fn main() -> Result<(), ExampleError> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// - /// let (dt, stats): (DelaunayTriangulation<_, (), (), 3>, _) = - /// DelaunayTriangulation::try_new_with_construction_statistics(&vertices)?; - /// assert_eq!(dt.number_of_vertices(), 4); - /// assert_eq!(stats.inserted, dt.number_of_vertices()); - /// # Ok(()) - /// # } - /// ``` - #[expect( - clippy::result_large_err, - reason = "Public API intentionally returns by-value construction statistics" - )] - pub fn try_new_with_construction_statistics( - vertices: &[Vertex<(), D>], - ) -> Result<(Self, ConstructionStatistics), DelaunayTriangulationConstructionErrorWithStatistics> - { - let kernel = AdaptiveKernel::::new(); - Self::try_with_options_and_statistics( - &kernel, - vertices, - TopologyGuarantee::DEFAULT, - ConstructionOptions::default(), - ) - } - - /// Creates a default `f64` triangulation with explicit construction options - /// and returns aggregate construction statistics. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may have skipped some input - /// vertices. Inspect [`ConstructionStatistics::total_skipped`] and - /// [`ConstructionStatistics::skip_samples`] when the caller requires a - /// strict all-inputs-inserted contract. - /// - /// # Errors - /// Returns [`DelaunayTriangulationConstructionErrorWithStatistics`] if - /// construction fails. The error includes partial statistics collected - /// before failure. If - /// [`ConstructionOptions::without_final_delaunay_enforcement`] is selected, - /// construction still fails when Levels 1–4 validation fails; only final - /// Level 5 Delaunay repair and validation are skipped. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{ - /// ConstructionOptions, DelaunayTriangulation, RetryPolicy, - /// }; - /// use std::num::NonZeroUsize; - /// - /// # #[derive(Debug, thiserror::Error)] - /// # enum ExampleError { - /// # #[error(transparent)] - /// # Source(#[from] delaunay::DelaunayTriangulationConstructionErrorWithStatistics), - /// # #[error(transparent)] - /// # Coordinate(#[from] delaunay::prelude::geometry::CoordinateConversionError), - /// # } - /// # fn main() -> Result<(), ExampleError> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// - /// let Some(attempts) = NonZeroUsize::new(2) else { - /// return Ok(()); - /// }; - /// let options = ConstructionOptions::default().with_retry_policy(RetryPolicy::Shuffled { - /// attempts, - /// base_seed: Some(7), - /// }); - /// let (dt, stats): (DelaunayTriangulation<_, (), (), 3>, _) = - /// DelaunayTriangulation::try_new_with_options_and_construction_statistics( - /// &vertices, - /// options, - /// )?; - /// assert_eq!(dt.number_of_vertices(), 4); - /// assert_eq!(stats.inserted, dt.number_of_vertices()); - /// # Ok(()) - /// # } - /// ``` - #[expect( - clippy::result_large_err, - reason = "Public API intentionally returns by-value construction statistics" - )] - pub fn try_new_with_options_and_construction_statistics( - vertices: &[Vertex<(), D>], - options: ConstructionOptions, - ) -> Result<(Self, ConstructionStatistics), DelaunayTriangulationConstructionErrorWithStatistics> - { - let kernel = AdaptiveKernel::::new(); - Self::try_with_options_and_statistics( - &kernel, - vertices, - TopologyGuarantee::DEFAULT, - options, - ) - } - - /// Creates a default `f64` triangulation with explicit construction options. - /// - /// This batch constructor may successfully build a triangulation after - /// skipping duplicate or retry-exhausted degenerate input vertices. Use - /// [`try_new_with_options_and_construction_statistics`](Self::try_new_with_options_and_construction_statistics) - /// when skipped-input observability is required. - /// - /// # Errors - /// Returns an error if construction fails, or if the selected options are - /// invalid. If [`ConstructionOptions::without_final_delaunay_enforcement`] - /// is selected, construction still fails when Levels 1–4 validation fails; - /// only final Level 5 Delaunay repair and validation are skipped. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{ - /// ConstructionOptions, DedupPolicy, DelaunayResult, DelaunayTriangulation, - /// InsertionOrderStrategy, - /// }; - /// - /// # fn main() -> DelaunayResult<()> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// let options = ConstructionOptions::default() - /// .with_insertion_order(InsertionOrderStrategy::Hilbert) - /// .with_dedup_policy(DedupPolicy::Exact); - /// - /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulation::try_new_with_options(&vertices, options)?; - /// assert_eq!(dt.number_of_vertices(), 4); - /// # Ok(()) - /// # } - /// ``` - pub fn try_new_with_options( - vertices: &[Vertex<(), D>], - options: ConstructionOptions, - ) -> Result { - let kernel = AdaptiveKernel::::new(); - Self::try_with_topology_guarantee_and_options( - &kernel, - vertices, - TopologyGuarantee::DEFAULT, - options, - ) - } - - /// Creates a default `f64` triangulation with an explicit topology guarantee. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may contain fewer vertices - /// than the input slice. Use - /// [`try_new_with_construction_statistics`](Self::try_new_with_construction_statistics) - /// when skipped-input observability is required. - /// - /// # Errors - /// Returns an error if construction fails or if the requested topology - /// guarantee cannot be satisfied. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{ - /// DelaunayResult, DelaunayTriangulation, TopologyGuarantee, - /// }; - /// - /// # fn main() -> DelaunayResult<()> { - /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt: DelaunayTriangulation<_, (), (), 2> = - /// DelaunayTriangulation::try_new_with_topology_guarantee( - /// &vertices, - /// TopologyGuarantee::PLManifold, - /// )?; - /// assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); - /// # Ok(()) - /// # } - /// ``` - pub fn try_new_with_topology_guarantee( - vertices: &[Vertex<(), D>], - topology_guarantee: TopologyGuarantee, - ) -> Result { - let kernel = AdaptiveKernel::::new(); - Self::try_with_topology_guarantee(&kernel, vertices, topology_guarantee) - } - /// Creates an empty default `f64` triangulation with no attached data. /// /// # Examples @@ -3304,7 +3104,7 @@ impl DelaunayTriangulation, (), (), D> { /// /// # fn main() -> DelaunayResult<()> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_vertices(), 3); /// # Ok(()) /// # } @@ -3319,8 +3119,8 @@ impl DelaunayTriangulation, (), (), D> { // CONSTRUCTION (Requires f64 Coordinate Storage) // ============================================================================= // -// Batch and incremental constructors, preprocessing, Hilbert ordering, spatial -// hashing, and deduplication operate on f64-backed vertices. +// Batch builder backends, incremental insertion, preprocessing, Hilbert ordering, +// spatial hashing, and deduplication operate on f64-backed vertices. impl DelaunayTriangulation where @@ -5068,137 +4868,12 @@ where U: DataType, V: DataType, { - /// Creates a Delaunay triangulation from vertices with an explicit kernel. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may contain fewer vertices - /// than the input slice. Use - /// [`try_with_options_and_statistics`](Self::try_with_options_and_statistics) when - /// skipped-input observability is required. - /// - /// # Errors - /// Returns an error if construction fails or final validation fails. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{DelaunayResult, DelaunayTriangulation}; - /// use delaunay::prelude::geometry::RobustKernel; - /// - /// # fn main() -> DelaunayResult<()> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// let kernel = RobustKernel::::new(); - /// let dt: DelaunayTriangulation, (), (), 3> = - /// DelaunayTriangulation::try_with_kernel(&kernel, &vertices)?; - /// assert_eq!(dt.number_of_vertices(), 4); - /// # Ok(()) - /// # } - /// ``` - pub fn try_with_kernel( - kernel: &K, - vertices: &[Vertex], - ) -> Result { - Self::try_with_topology_guarantee(kernel, vertices, TopologyGuarantee::DEFAULT) - } - - /// Creates a Delaunay triangulation with an explicit topology guarantee. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may contain fewer vertices - /// than the input slice. Use - /// [`try_with_options_and_statistics`](Self::try_with_options_and_statistics) when - /// skipped-input observability is required. - /// - /// # Errors - /// Returns an error if construction fails or if the requested topology - /// guarantee cannot be satisfied. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{ - /// DelaunayResult, DelaunayTriangulation, TopologyGuarantee, - /// }; - /// use delaunay::prelude::geometry::RobustKernel; - /// - /// # fn main() -> DelaunayResult<()> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// let kernel = RobustKernel::::new(); - /// let dt: DelaunayTriangulation, (), (), 3> = - /// DelaunayTriangulation::try_with_topology_guarantee( - /// &kernel, - /// &vertices, - /// TopologyGuarantee::PLManifold, - /// )?; - /// assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); - /// # Ok(()) - /// # } - /// ``` - pub fn try_with_topology_guarantee( - kernel: &K, - vertices: &[Vertex], - topology_guarantee: TopologyGuarantee, - ) -> Result { - Self::try_with_topology_guarantee_and_options( - kernel, - vertices, - topology_guarantee, - ConstructionOptions::default(), - ) - } - - /// Creates a Delaunay triangulation with topology and construction options. + /// Shared backend for the public fluent builder terminals. /// - /// This batch constructor may successfully build a triangulation after - /// skipping duplicate or retry-exhausted degenerate input vertices. Use - /// [`try_with_options_and_statistics`](Self::try_with_options_and_statistics) when - /// skipped-input observability is required. - /// - /// # Errors - /// Returns an error if construction fails, if validation fails, or if the - /// selected preprocessing options are invalid. If - /// [`ConstructionOptions::without_final_delaunay_enforcement`] is selected, - /// construction still fails when Levels 1–4 validation fails; only final - /// Level 5 Delaunay repair and validation are skipped. - /// - /// # Examples - /// - /// ```rust - /// use delaunay::prelude::construction::{ - /// ConstructionOptions, DelaunayResult, DelaunayTriangulation, TopologyGuarantee, - /// }; - /// use delaunay::prelude::geometry::RobustKernel; - /// - /// # fn main() -> DelaunayResult<()> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// let kernel = RobustKernel::::new(); - /// let dt: DelaunayTriangulation, (), (), 3> = - /// DelaunayTriangulation::try_with_topology_guarantee_and_options( - /// &kernel, - /// &vertices, - /// TopologyGuarantee::PLManifold, - /// ConstructionOptions::default(), - /// )?; - /// assert_eq!(dt.number_of_vertices(), 4); - /// # Ok(()) - /// # } - /// ``` - pub fn try_with_topology_guarantee_and_options( + /// Keeping this crate-private preserves one public batch-construction path + /// while letting the builder delegate to the established preprocessing, + /// retry, repair, fallback, and final-validation machinery. + pub(crate) fn build_with_kernel_options( kernel: &K, vertices: &[Vertex], topology_guarantee: TopologyGuarantee, @@ -5280,65 +4955,20 @@ where result } - /// Creates a Delaunay triangulation with construction statistics. - /// - /// Batch construction is a best-effort ingestion path for duplicate or - /// degenerate inputs: a successful construction may have skipped some input - /// vertices. Inspect [`ConstructionStatistics::total_skipped`] and - /// [`ConstructionStatistics::skip_samples`] when the caller requires a - /// strict all-inputs-inserted contract. - /// - /// # Errors - /// Returns [`DelaunayTriangulationConstructionErrorWithStatistics`] if - /// construction fails. The error includes the partial statistics collected - /// before failure. If - /// [`ConstructionOptions::without_final_delaunay_enforcement`] is selected, - /// construction still fails when Levels 1–4 validation fails; only final - /// Level 5 Delaunay repair and validation are skipped. - /// - /// # Examples + /// Statistics-returning backend for the public fluent builder terminals. /// - /// ```rust - /// use delaunay::prelude::construction::{ - /// ConstructionOptions, DelaunayTriangulation, TopologyGuarantee, - /// }; - /// use delaunay::prelude::geometry::RobustKernel; - /// - /// # #[derive(Debug, thiserror::Error)] - /// # enum ExampleError { - /// # #[error(transparent)] - /// # Source(#[from] delaunay::DelaunayTriangulationConstructionErrorWithStatistics), - /// # #[error(transparent)] - /// # Coordinate(#[from] delaunay::prelude::geometry::CoordinateConversionError), - /// # } - /// # fn main() -> Result<(), ExampleError> { - /// let vertices = vec![ - /// delaunay::vertex![0.0, 0.0, 0.0]?, - /// delaunay::vertex![1.0, 0.0, 0.0]?, - /// delaunay::vertex![0.0, 1.0, 0.0]?, - /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]; - /// let kernel = RobustKernel::::new(); - /// let (dt, stats) = - /// DelaunayTriangulation::, (), (), 3>::try_with_options_and_statistics( - /// &kernel, - /// &vertices, - /// TopologyGuarantee::PLManifold, - /// ConstructionOptions::default(), - /// )?; - /// assert_eq!(dt.number_of_vertices(), stats.inserted); - /// # Ok(()) - /// # } - /// ``` + /// This mirrors [`Self::build_with_kernel_options`] but preserves partial + /// construction telemetry for builder callers that choose a statistics + /// terminal. #[expect( clippy::result_large_err, - reason = "Public API intentionally returns by-value construction statistics" + reason = "Builder statistics terminal intentionally returns by-value construction statistics" )] #[expect( clippy::too_many_lines, reason = "Statistics constructor handles preprocessing, retry, and fallback aggregation" )] - pub fn try_with_options_and_statistics( + pub(crate) fn build_with_kernel_options_and_statistics( kernel: &K, vertices: &[Vertex], topology_guarantee: TopologyGuarantee, @@ -5477,20 +5107,9 @@ where initial_simplex: InitialSimplexStrategy, ) -> PreprocessVerticesResult { let default_tolerance = default_duplicate_tolerance(); - - let epsilon = if let DedupPolicy::Epsilon { tolerance } = dedup_policy { - Some(tolerance.get()) - } else { - None - }; - - let grid_cell_size_value = - if let (DedupPolicy::Epsilon { .. }, Some(eps)) = (dedup_policy, epsilon) { - if eps > 0.0 { eps } else { default_tolerance } - } else { - default_tolerance - }; - let mut grid = hash_grid_from_validated_cell_size::(grid_cell_size_value)?; + let dedup_parameters = dedup_grid_parameters(dedup_policy, default_tolerance); + let mut grid = + hash_grid_from_validated_cell_size::(dedup_parameters.grid_cell_size)?; // Deduplicate first to reduce work for ordering strategies. let mut owned_vertices: Option>> = match dedup_policy { @@ -5504,7 +5123,7 @@ where } } DedupPolicy::Epsilon { .. } => { - let epsilon = epsilon.expect("epsilon validated above"); + let epsilon = dedup_parameters.epsilon; let vertices = vertices.to_vec(); if hash_grid_usable_for_vertices(&grid, &vertices) { Some(dedup_vertices_epsilon_hash_grid( @@ -6022,7 +5641,7 @@ mod tests { CoordinateConversionError, CoordinateConversionValue, CoordinateValues, InvalidCoordinateValue, }; - use crate::geometry::util::RandomPointGenerationError; + use crate::geometry::util::{RandomPointCountError, RandomPointGenerationError}; use crate::repair::DelaunayRepairPolicy; use crate::topology::characteristics::euler::TopologyClassification; use crate::topology::traits::topological_space::TopologyKind; @@ -6084,6 +5703,36 @@ mod tests { assert_eq!(preserved_source, &source); } + #[test] + fn test_delaunay_error_preserves_random_builder_error_source() { + let error = DelaunayError::from(RandomTriangulationBuilderError::PointCount { + source: RandomPointCountError::InsufficientPoints { + actual: 2, + expected: 3, + dimension: 2, + }, + }); + + assert_matches!( + error, + DelaunayError::Construction { + source, + } if matches!( + source.as_ref(), + DelaunayTriangulationConstructionError::Triangulation( + DelaunayConstructionFailure::InsufficientVertices { + dimension: 2, + source: SimplexValidationError::InsufficientVertices { + actual: 2, + expected: 3, + dimension: 2, + }, + }, + ) + ) + ); + } + fn init_tracing() { static INIT: Once = Once::new(); INIT.call_once(|| { @@ -6122,11 +5771,10 @@ mod tests { vertex!([0.0, 2.0]).unwrap(), vertex!([2.0, 1.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::Pseudomanifold, - ) - .unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .build() + .unwrap(); assert!( dt.tri.tds.number_of_simplices() > 1, "regression setup needs an interior facet" @@ -6164,11 +5812,10 @@ mod tests { vertex!([2.0, 0.0]).unwrap(), vertex!([0.0, 2.0]).unwrap(), ]; - let mut dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::Pseudomanifold, - ) - .unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .build() + .unwrap(); dt.insertion_state.delaunay_repair_policy = DelaunayRepairPolicy::Never; dt.insertion_state.use_global_repair_fallback = true; @@ -6207,13 +5854,10 @@ mod tests { let kernel = RobustKernel::::new(); let options = ConstructionOptions::default().without_global_repair_fallback(); - let stats_dt = - DelaunayTriangulation::, (), (), 2>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::Pseudomanifold, - options, - ) + let stats_dt = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .construction_options(options) + .build_with_kernel_and_statistics(&kernel) .expect("stats construction should succeed") .0; assert!( @@ -6221,13 +5865,10 @@ mod tests { "stats construction should preserve the disabled fallback policy" ); - let no_stats_dt = DelaunayTriangulation::, (), (), 2>:: - try_with_topology_guarantee_and_options( - &kernel, - &vertices, - TopologyGuarantee::Pseudomanifold, - options, - ) + let no_stats_dt = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .construction_options(options) + .build_with_kernel(&kernel) .expect("non-stats construction should succeed"); assert!( !no_stats_dt.insertion_state.use_global_repair_fallback, @@ -6248,7 +5889,7 @@ mod tests { let expected_vertices = vertices.len(); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_vertices(), expected_vertices); assert!(dt.number_of_simplices() > 1); @@ -6307,7 +5948,7 @@ mod tests { } let dt_batch: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt_bootstrap.number_of_vertices(), dt_batch.number_of_vertices()); assert_eq!( @@ -6369,7 +6010,9 @@ mod tests { ]; let dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_kernel(&FastKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build_with_kernel(&FastKernel::new()) + .unwrap(); assert_eq!(dt.number_of_vertices(), 3); assert_eq!(dt.number_of_simplices(), 1); @@ -6385,7 +6028,9 @@ mod tests { ]; let dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_kernel(&RobustKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build_with_kernel(&RobustKernel::new()) + .unwrap(); assert_eq!(dt.number_of_vertices(), 3); assert_eq!(dt.number_of_simplices(), 1); @@ -6397,7 +6042,7 @@ mod tests { let vertices = vec![vertex!([0.0, 0.0]).unwrap(), vertex!([1.0, 0.0]).unwrap()]; let result: Result, (), (), 2>, _> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices); + DelaunayTriangulationBuilder::new(&vertices).build(); match result.unwrap_err() { DelaunayTriangulationConstructionError::Triangulation( @@ -6417,7 +6062,7 @@ mod tests { ]; let result: Result, (), (), 3>, _> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices); + DelaunayTriangulationBuilder::new(&vertices).build(); match result.unwrap_err() { DelaunayTriangulationConstructionError::Triangulation( @@ -6447,7 +6092,7 @@ mod tests { ); let result: Result, (), (), 2>, _> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices); + DelaunayTriangulationBuilder::new(&vertices).build(); match result.unwrap_err() { DelaunayTriangulationConstructionError::Triangulation( @@ -6470,7 +6115,7 @@ mod tests { vertex!([0.3, 0.3, 0.3]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_vertices(), 5); assert!(dt.validate().is_ok()); } @@ -6485,9 +6130,9 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), vertex!([0.3, 0.3, 0.3]).unwrap(), ]; - let (dt, stats) = - DelaunayTriangulation::<_, (), (), 3>::try_new_with_construction_statistics(&vertices) - .unwrap(); + let (dt, stats) = DelaunayTriangulation::builder(&vertices) + .build_with_statistics() + .unwrap(); assert_eq!(dt.number_of_vertices(), 5); assert_eq!(stats.inserted, 5); assert!(dt.validate().is_ok()); @@ -6508,10 +6153,12 @@ mod tests { let _guard = ForceRepairNonconvergentGuard::enable(); let kernel = RobustKernel::::new(); - let dt = DelaunayTriangulation::, (), (), 4>::try_with_kernel( - &kernel, &vertices, - ) - .expect("D>=4 construction should continue after forced local repair non-convergence"); + let dt: DelaunayTriangulation, (), (), 4> = + DelaunayTriangulationBuilder::new(&vertices) + .build_with_kernel(&kernel) + .expect( + "D>=4 construction should continue after forced local repair non-convergence", + ); assert_eq!(dt.number_of_vertices(), vertices.len()); assert!(dt.validate().is_ok()); @@ -6532,13 +6179,9 @@ mod tests { let _guard = ForceRepairNonconvergentGuard::enable(); let kernel = RobustKernel::::new(); - let (dt, stats) = - DelaunayTriangulation::, (), (), 4>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::DEFAULT, - ConstructionOptions::default(), - ) + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .construction_options(ConstructionOptions::default()) + .build_with_kernel_and_statistics(&kernel) .expect( "D>=4 stats construction should continue after forced local repair non-convergence", ); @@ -6566,13 +6209,9 @@ mod tests { let kernel = RobustKernel::::new(); let options = ConstructionOptions::default() .with_batch_repair_policy(DelaunayRepairPolicy::EveryN(NonZeroUsize::new(2).unwrap())); - let (dt, stats) = - DelaunayTriangulation::, (), (), 4>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::DEFAULT, - options, - ) + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .construction_options(options) + .build_with_kernel_and_statistics(&kernel) .expect("EveryN batch repair should soft-fail forced local non-convergence and finish"); assert_eq!(dt.number_of_vertices(), vertices.len()); @@ -7388,8 +7027,10 @@ mod tests { let opts = ConstructionOptions::default() .with_dedup_policy(DedupPolicy::try_epsilon(1e-10).unwrap()) .with_retry_policy(RetryPolicy::Disabled); - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_options(&vertices, opts).unwrap(); + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() + .unwrap(); assert_eq!(dt.number_of_vertices(), 5); assert!(dt.validate().is_ok()); @@ -7575,7 +7216,7 @@ mod tests { } #[test] - fn test_try_new_with_options_hilbert_smoke_3d() { + fn test_builder_with_options_hilbert_smoke_3d() { init_tracing(); let vertices: Vec> = vec![ vertex!([0.0, 0.0, 0.0]).unwrap(), @@ -7589,15 +7230,17 @@ mod tests { .with_insertion_order(InsertionOrderStrategy::Hilbert) .with_retry_policy(RetryPolicy::Disabled); - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_options(&vertices, opts).unwrap(); + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() + .unwrap(); assert_eq!(dt.number_of_vertices(), 5); assert!(dt.validate().is_ok()); } #[test] - fn test_try_new_with_options_shuffled_retry_policy_smoke_3d() { + fn test_builder_with_options_shuffled_retry_policy_smoke_3d() { init_tracing(); let vertices: Vec> = vec![ vertex!([0.0, 0.0, 0.0]).unwrap(), @@ -7614,15 +7257,17 @@ mod tests { base_seed: Some(123), }); - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_options(&vertices, opts).unwrap(); + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() + .unwrap(); assert_eq!(dt.number_of_vertices(), 5); assert!(dt.validate().is_ok()); } #[test] - fn test_try_new_with_options_smoke_3d() { + fn test_builder_with_options_smoke_3d() { init_tracing(); let vertices: Vec> = vec![ vertex!([0.0, 0.0, 0.0]).unwrap(), @@ -7632,8 +7277,10 @@ mod tests { ]; let opts = ConstructionOptions::default().with_retry_policy(RetryPolicy::Disabled); - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_options(&vertices, opts).unwrap(); + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() + .unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert_eq!(dt.number_of_simplices(), 1); @@ -7644,9 +7291,9 @@ mod tests { init_tracing(); let vertices = simplex_vertices::(); - let (dt, stats) = - DelaunayTriangulation::<_, (), (), D>::try_new_with_construction_statistics(&vertices) - .unwrap(); + let (dt, stats) = DelaunayTriangulation::builder(&vertices) + .build_with_statistics() + .unwrap(); assert_eq!(dt.number_of_vertices(), D + 1); assert_eq!(stats.inserted, D + 1); @@ -7661,7 +7308,7 @@ mod tests { let vertices = simplex_vertices::(); let dt: DelaunayTriangulation<_, (), (), D> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!( *dt.as_triangulation().tds.construction_state(), @@ -7674,9 +7321,9 @@ mod tests { let mut vertices = simplex_vertices::(); let _removed_vertex = vertices.pop().expect("simplex has at least one vertex"); - let err = - DelaunayTriangulation::<_, (), (), D>::try_new_with_construction_statistics(&vertices) - .unwrap_err(); + let err = DelaunayTriangulation::builder(&vertices) + .build_with_statistics() + .unwrap_err(); assert_eq!( err.error, @@ -7709,10 +7356,10 @@ mod tests { .with_insertion_order(InsertionOrderStrategy::Input) .with_retry_policy(RetryPolicy::Disabled); - let (dt, stats) = DelaunayTriangulation::<_, (), (), D>::try_new_with_options_and_construction_statistics( - &vertices, opts, - ) - .unwrap(); + let (dt, stats) = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build_with_statistics() + .unwrap(); assert_eq!(dt.number_of_vertices(), D + 1); assert_eq!(stats.inserted, D + 1); @@ -7740,7 +7387,7 @@ mod tests { ($dim:literal) => { pastey::paste! { #[test] - fn []() { + fn []() { assert_initial_simplex_statistics::<$dim>(); } @@ -7750,12 +7397,12 @@ mod tests { } #[test] - fn []() { + fn []() { assert_empty_partial_stats_error::<$dim>(); } #[test] - fn []() { + fn []() { assert_duplicate_skip_statistics::<$dim>(); } } @@ -7776,11 +7423,9 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); diff --git a/src/delaunay/delaunayize.rs b/src/delaunay/delaunayize.rs index f9778065..f0b7de7d 100644 --- a/src/delaunay/delaunayize.rs +++ b/src/delaunay/delaunayize.rs @@ -40,7 +40,7 @@ //! delaunay::vertex![0.0, 1.0, 0.0]?, //! delaunay::vertex![0.0, 0.0, 1.0]?, //! ]; -//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default())?; //! assert!(outcome.topology_repair.succeeded); @@ -63,6 +63,7 @@ pub use crate::flips::{ pub use crate::tds::SimplexValidationError; pub use crate::{PlManifoldRepairError, PlManifoldRepairStage, PlManifoldRepairStats}; +use crate::builder::DelaunayTriangulationBuilder; use crate::core::algorithms::pl_manifold_repair::{ PlManifoldRepairConfig, repair_pl_manifold_topology, }; @@ -209,7 +210,7 @@ impl Default for DelaunayizeConfig { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default())?; /// assert!(outcome.topology_repair.succeeded); @@ -676,7 +677,9 @@ where U: DataType, V: DataType, { - let mut rebuilt = DelaunayTriangulation::try_with_kernel(kernel, snapshot.vertices())?; + let mut rebuilt = DelaunayTriangulationBuilder::new(snapshot.vertices()) + .simplex_data_type::() + .build_with_kernel(kernel)?; restore_simplex_data(&mut rebuilt, snapshot.simplex_data())?; Ok(rebuilt) } @@ -923,7 +926,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default())?; /// assert!(outcome.topology_repair.succeeded); @@ -1173,7 +1176,7 @@ mod tests { /// Creates a Delaunay wrapper whose TDS has a boundary-ridge violation. fn boundary_ridge_multiplicity_dt() -> DelaunayTriangulation, (), (), 3> { let vertices = unit_simplex_vertices::<3>(); - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); *dt.tds_mut() = make_boundary_ridge_multiplicity_tds(); dt } @@ -1181,7 +1184,7 @@ mod tests { /// Creates a Delaunay wrapper whose TDS has a vertex-link violation. fn cone_on_torus_dt() -> DelaunayTriangulation, (), (), 3> { let vertices = unit_simplex_vertices::<3>(); - let mut dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let mut dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); *dt.tds_mut() = make_cone_on_torus_tds(); dt } @@ -1206,7 +1209,7 @@ mod tests { init_tracing(); let vertices = unit_simplex_vertices::(); let mut dt: DelaunayTriangulation<_, (), (), D> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); insert_duplicate_simplex_copies(&mut dt, 2); @@ -1234,7 +1237,7 @@ mod tests { init_tracing(); let vertices = unit_simplex_vertices::<2>(); let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); insert_duplicate_simplex_copies(&mut dt, 3); let before_simplex_count = dt.number_of_simplices(); @@ -1358,7 +1361,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); assert!(outcome.topology_repair.succeeded); @@ -1376,7 +1379,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); assert!(outcome.topology_repair.succeeded); @@ -1398,7 +1401,7 @@ mod tests { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); assert!(outcome.topology_repair.succeeded); @@ -1501,7 +1504,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let before_simplex_count = dt.number_of_simplices(); let before_simplex_uuids = sorted_simplex_uuids(&dt); let snapshot_error = SimplexValidationError::VertexKeyNotFound { @@ -1726,7 +1729,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Fallback should not be triggered on a valid triangulation. let config = DelaunayizeConfig { @@ -1765,7 +1768,7 @@ mod tests { init_tracing(); let vertices = unit_simplex_vertices::<2>(); let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); insert_duplicate_simplex_copies(&mut dt, 3); let outcome = delaunayize_by_flips( @@ -1794,7 +1797,7 @@ mod tests { init_tracing(); let vertices = [vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; let mut dt: DelaunayTriangulation<_, (), (), 1> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome = delaunayize_by_flips( &mut dt, @@ -1825,7 +1828,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let _guard = ForceDelaunayRepairFailureGuard::enable(); let outcome = delaunayize_by_flips( @@ -1875,7 +1878,8 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt = DelaunayTriangulationBuilder::new(&vertices) - .build::() + .simplex_data_type::() + .build() .unwrap(); let original_simplex_key = dt.simplices().next().unwrap().0; dt.set_simplex_data(original_simplex_key, Some(42)).unwrap(); @@ -1914,7 +1918,10 @@ mod tests { let snapshot = snapshot_rebuild_state(&tds).unwrap(); let kernel = AdaptiveKernel::new(); let mut rebuilt: DelaunayTriangulation<_, (), i32, 2> = - DelaunayTriangulation::try_with_kernel(&kernel, snapshot.vertices()).unwrap(); + DelaunayTriangulationBuilder::new(snapshot.vertices()) + .simplex_data_type::() + .build_with_kernel(&kernel) + .unwrap(); restore_simplex_data(&mut rebuilt, snapshot.simplex_data()).unwrap(); @@ -1941,11 +1948,11 @@ mod tests { let config = DelaunayizeConfig::default(); let mut dt1: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome1 = delaunayize_by_flips(&mut dt1, config).unwrap(); let mut dt2: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome2 = delaunayize_by_flips(&mut dt2, config).unwrap(); assert_eq!( diff --git a/src/delaunay/deletion.rs b/src/delaunay/deletion.rs index ca691612..b62395b8 100644 --- a/src/delaunay/deletion.rs +++ b/src/delaunay/deletion.rs @@ -152,7 +152,7 @@ where /// delaunay::vertex![0.0, 1.0]?, /// interior, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Find the key of a known interior vertex. /// let Some((vertex_key, _)) = dt.vertices().find(|(_, v)| v.uuid() == interior_uuid) else { @@ -192,7 +192,7 @@ where /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((vertex_key, _)) = dt.vertices().next() else { /// return Ok(()); /// }; @@ -472,7 +472,7 @@ mod tests { let vertices = simplex_vertices::(); let mut dt: DelaunayTriangulation, (), (), D> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_topology_guarantee(TopologyGuarantee::PLManifold); let simplex_key = dt.simplices().next().unwrap().0; @@ -494,7 +494,7 @@ mod tests { let vertices = simplex_vertices::(); let mut dt: DelaunayTriangulation, (), (), D> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_topology_guarantee(TopologyGuarantee::PLManifold); let mut inserted_vertices = Vec::new(); @@ -559,7 +559,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_topology_guarantee(TopologyGuarantee::PLManifold); let original_vertex_count = dt.number_of_vertices(); let original_simplex_count = dt.number_of_simplices(); @@ -597,7 +597,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let vertex_key = dt.insert_vertex(vertex![0.25, 0.25].unwrap()).unwrap(); let hint_simplex = dt.simplices().next().map(|(key, _)| key); @@ -639,7 +639,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let vertex_key = dt.insert_vertex(vertex![0.25, 0.25].unwrap()).unwrap(); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::Never); @@ -683,7 +683,7 @@ mod tests { ]; let deleted_uuid = vertices[7].uuid(); let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_topology_guarantee(TopologyGuarantee::PLManifold); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::EveryInsertion); dt.as_triangulation().validate().unwrap(); @@ -718,7 +718,7 @@ mod tests { ]; let deleted_uuid = vertices[4].uuid(); let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.is_valid_delaunay().unwrap(); dt.set_topology_guarantee(TopologyGuarantee::PLManifold); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::Never); diff --git a/src/delaunay/diagnostics.rs b/src/delaunay/diagnostics.rs index 110fc18c..a2f0b2da 100644 --- a/src/delaunay/diagnostics.rs +++ b/src/delaunay/diagnostics.rs @@ -69,6 +69,8 @@ pub struct ConstructionTelemetry { /// Maximum wall-clock nanoseconds spent in one transactional insertion call. pub insertion_wall_time_nanos_max: u64, + /// Wall-clock nanoseconds spent in the public construction entry point. + pub construction_total_nanos: u64, /// Wall-clock nanoseconds spent preprocessing vertices before topology construction. pub construction_preprocessing_nanos: u64, /// Wall-clock nanoseconds spent in the bulk insertion loop, including batch local repair. @@ -237,6 +239,7 @@ impl ConstructionTelemetry { pub const fn has_data(&self) -> bool { self.insertion_wall_time_calls > 0 || self.insertion_wall_time_nanos > 0 + || self.construction_total_nanos > 0 || self.construction_preprocessing_nanos > 0 || self.construction_insert_loop_nanos > 0 || self.construction_finalize_nanos > 0 @@ -276,6 +279,11 @@ impl ConstructionTelemetry { self.insertion_wall_time_nanos_max = self.insertion_wall_time_nanos_max.max(elapsed_nanos); } + /// Records the wall-clock duration of the public construction entry point. + pub(crate) const fn record_construction_total_timing(&mut self, elapsed_nanos: u64) { + self.construction_total_nanos = self.construction_total_nanos.saturating_add(elapsed_nanos); + } + /// Records the wall-clock duration of construction preprocessing. pub(crate) const fn record_construction_preprocessing_timing(&mut self, elapsed_nanos: u64) { self.construction_preprocessing_nanos = self @@ -662,6 +670,9 @@ impl ConstructionTelemetry { /// Keeps construction-phase merge accounting isolated so aggregate merges stay readable. const fn merge_construction_phase_timings_from(&mut self, other: &Self) { + self.construction_total_nanos = self + .construction_total_nanos + .saturating_add(other.construction_total_nanos); self.construction_preprocessing_nanos = self .construction_preprocessing_nanos .saturating_add(other.construction_preprocessing_nanos); @@ -872,6 +883,7 @@ mod tests { postcondition_nanos: 500_000, }); summary.record_repair_seed_accumulation(500_000, 7); + summary.record_construction_total_timing(5_000); summary.record_construction_preprocessing_timing(10_000); summary.record_construction_insert_loop_timing(20_000); summary.record_construction_finalize_timing(30_000); @@ -884,6 +896,7 @@ mod tests { assert_eq!(summary.insertion_wall_time_calls, 1); assert_eq!(summary.insertion_wall_time_nanos, 1_000_000); assert_eq!(summary.insertion_wall_time_nanos_max, 1_000_000); + assert_eq!(summary.construction_total_nanos, 5_000); assert_eq!(summary.construction_preprocessing_nanos, 10_000); assert_eq!(summary.construction_insert_loop_nanos, 20_000); assert_eq!(summary.construction_finalize_nanos, 30_000); @@ -1021,12 +1034,14 @@ mod tests { ridge_nanos: 50, postcondition_nanos: 30, }); + right.record_construction_total_timing(200); right.record_construction_insert_loop_timing(300); right.record_construction_final_delaunay_validation_timing(400); left.merge_from(&right); assert!(left.has_data()); + assert_eq!(left.construction_total_nanos, 200); assert_eq!(left.construction_insert_loop_nanos, 400); assert_eq!(left.construction_final_delaunay_validation_nanos, 600); assert_eq!(left.local_repair_calls, 2); diff --git a/src/delaunay/flips.rs b/src/delaunay/flips.rs index 279d01a1..5d7c353b 100644 --- a/src/delaunay/flips.rs +++ b/src/delaunay/flips.rs @@ -63,7 +63,7 @@ use crate::triangulation::DelaunayTriangulation; /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -108,7 +108,7 @@ pub trait BistellarFlips { /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -156,7 +156,7 @@ pub trait BistellarFlips { /// ]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -206,7 +206,7 @@ pub trait BistellarFlips { /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -248,7 +248,7 @@ pub trait BistellarFlips { /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -295,7 +295,7 @@ pub trait BistellarFlips { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// delaunay::vertex![0.5, 0.5, 0.3]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Find an interior facet and attempt a k=2 flip /// // Note: k=2 flips require specific geometric conditions @@ -346,7 +346,7 @@ pub trait BistellarFlips { /// &simplices, /// ) /// .map_err(DelaunayTriangulationConstructionError::from)? - /// .build::<()>()?; + /// .build()?; /// /// let mut accepted = None; /// 'simplices: for (simplex_key, simplex) in dt.simplices() { @@ -404,7 +404,7 @@ pub trait BistellarFlips { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// delaunay::vertex![1.0, 1.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // k=3 flips require specific ridge configurations in 3D and above /// // This is an illustrative example; actual ridge selection depends on topology @@ -441,7 +441,7 @@ pub trait BistellarFlips { /// ]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -491,7 +491,7 @@ pub trait BistellarFlips { /// ]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -550,7 +550,7 @@ pub trait BistellarFlips { /// ]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((_, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -794,11 +794,9 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); let mut tri = dt.as_triangulation().clone(); let simplex_key = tri.simplices().next().unwrap().0; @@ -824,7 +822,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; dt.insertion_state.last_inserted_simplex = Some(simplex_key); @@ -850,11 +848,9 @@ mod tests { vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); let tri = dt.as_triangulation().clone(); let simplex_key = tri.simplices().next().unwrap().0; diff --git a/src/delaunay/insertion.rs b/src/delaunay/insertion.rs index 1b5c61f4..612d222b 100644 --- a/src/delaunay/insertion.rs +++ b/src/delaunay/insertion.rs @@ -208,7 +208,7 @@ where /// vertex![0.0, 0.0, 1.0, 0.0]?, /// vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_vertices(), 5); /// /// // Insert additional interior vertex @@ -707,7 +707,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_vertices(), 3); assert_eq!(dt.number_of_simplices(), 1); @@ -732,7 +732,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Insert 3 interior points sequentially dt.insert_vertex(vertex![0.3, 0.3].unwrap()).unwrap(); @@ -759,7 +759,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Insert 3 interior points sequentially (well inside the tetrahedron) dt.insert_vertex(vertex![0.1, 0.1, 0.1].unwrap()).unwrap(); @@ -784,7 +784,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::Never); // Initially no last_inserted_simplex @@ -827,7 +827,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let _guard = ForceRepairNonconvergentGuard::enable(); let result = dt.insert_vertex(vertex![0.5, 0.5].unwrap()); @@ -863,7 +863,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::EveryN(NonZeroUsize::new(3).unwrap())); dt.set_delaunay_check_policy(DelaunayCheckPolicy::EndOnly); @@ -891,7 +891,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let vertex_count_before = dt.number_of_vertices(); let simplex_count_before = dt.number_of_simplices(); let hint_before = dt.simplices().next().map(|(key, _)| key); @@ -1021,7 +1021,7 @@ mod tests { vertex![0.5, 0.5].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let all_simplices: Vec = dt.simplices().map(|(simplex_key, _)| simplex_key).collect(); diff --git a/src/delaunay/locality.rs b/src/delaunay/locality.rs index 1a8740ed..b6cc6ba4 100644 --- a/src/delaunay/locality.rs +++ b/src/delaunay/locality.rs @@ -188,7 +188,7 @@ mod tests { vertex!([0.5, 0.5]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let all_simplices: Vec = dt.simplices().map(|(simplex_key, _)| simplex_key).collect(); assert!( @@ -243,7 +243,7 @@ mod tests { vertex!([0.5, 0.5]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let all_simplices: Vec = dt.simplices().map(|(simplex_key, _)| simplex_key).collect(); assert!( @@ -282,7 +282,7 @@ mod tests { vertex!([0.5, 0.5]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let all_simplices: Vec = dt.simplices().map(|(simplex_key, _)| simplex_key).collect(); assert!( diff --git a/src/delaunay/pachner.rs b/src/delaunay/pachner.rs index 397dfd13..94d26396 100644 --- a/src/delaunay/pachner.rs +++ b/src/delaunay/pachner.rs @@ -50,7 +50,7 @@ use crate::tds::{EdgeKey, FacetHandle, SimplexKey, TopologyOwner, TopologyOwnerI /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -128,7 +128,7 @@ pub enum PachnerMove { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -180,7 +180,7 @@ impl PachnerProposal { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -213,7 +213,7 @@ impl PachnerProposal { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -248,7 +248,7 @@ impl PachnerProposal { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -291,7 +291,7 @@ impl PachnerProposal { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -346,7 +346,7 @@ impl PachnerProposal { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -398,7 +398,7 @@ impl PachnerProposal { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -453,7 +453,7 @@ impl PachnerProposal { /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -550,7 +550,7 @@ impl From> for FlipInfo { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<()>()?; +/// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -632,7 +632,7 @@ pub trait PachnerMoves: BistellarFlips + TopologyOwner { /// let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; /// let dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; diff --git a/src/delaunay/property_validation.rs b/src/delaunay/property_validation.rs index 1e1caf5d..ab17ffbe 100644 --- a/src/delaunay/property_validation.rs +++ b/src/delaunay/property_validation.rs @@ -119,7 +119,7 @@ pub enum DelaunayValidationError { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let report = delaunay_violation_report(dt.tds(), None)?; /// assert!(report.is_valid()); @@ -448,7 +448,7 @@ pub fn is_delaunay_property_only( /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Find all violating simplices (should be empty for valid Delaunay triangulation) @@ -559,7 +559,7 @@ pub fn find_delaunay_violations( /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let report = delaunay_violation_report(dt.tds(), None)?; /// assert!(report.violating_simplices.is_empty()); @@ -830,7 +830,7 @@ mod tests { test_vertex([0.0, 0.0, 1.0]), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let tds = &dt.as_triangulation().tds; // Basic Delaunay helpers should report no violations. @@ -1055,7 +1055,7 @@ mod tests { test_vertex([0.0, 1.0, 0.0]), test_vertex([0.0, 0.0, 1.0]), ]; - let dt = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt = DelaunayTriangulation::builder(&vertices).build().unwrap(); let report = delaunay_violation_report(dt.tds(), None).unwrap(); diff --git a/src/delaunay/query.rs b/src/delaunay/query.rs index a45ff9c8..c51402b6 100644 --- a/src/delaunay/query.rs +++ b/src/delaunay/query.rs @@ -1,20 +1,23 @@ -//! Read-only Delaunay triangulation query, traversal, and accessor methods. +//! Delaunay triangulation query, traversal, accessor, and payload-fill methods. //! //! This module owns the high-level forwarding surface for inspecting a //! `DelaunayTriangulation`: counts, iterator access, TDS views, topology -//! metadata accessors, and adjacency traversal helpers. It also keeps the small -//! cache invalidation helpers next to the accessors they protect. +//! metadata accessors, adjacency traversal helpers, and checked follow-on +//! payload assignment. It also keeps the small cache invalidation helpers next +//! to the accessors they protect. #![forbid(unsafe_code)] use crate::core::adjacency::{ EdgeIndex, IncidenceView, SimplexNeighborIndex, TopologyIndexBuildError, TriangulationAdjacency, }; +use crate::core::collections::SimplexSecondaryMap; use crate::core::edge::{EdgeKey, EdgeKeyError}; use crate::core::facet::{AllFacetsIter, BoundaryFacetsIter, FacetHandle}; use crate::core::query::QueryError; use crate::core::simplex::Simplex; use crate::core::tds::{InvariantError, SimplexKey, Tds, TdsError, TdsMutationError, VertexKey}; +use crate::core::traits::data_type::DataCopy; use crate::core::triangulation::Triangulation; use crate::core::validation::{TopologyGuarantee, ValidationConfigurationError, ValidationPolicy}; use crate::core::vertex::Vertex; @@ -22,6 +25,37 @@ use crate::repair::{DelaunayCheckPolicy, DelaunayRepairPolicy}; use crate::topology::traits::topological_space::{GlobalTopology, TopologyKind}; use crate::triangulation::DelaunayTriangulation; use crate::validation::DelaunayTriangulationValidationError; +use thiserror::Error; + +/// Error returned when filling simplex payloads from a secondary map. +/// +/// The error is reported before any payload mutation when the supplied map is +/// incomplete or names a simplex key outside the receiving triangulation. +#[derive(Clone, Debug, Error, PartialEq)] +#[non_exhaustive] +pub enum SimplexDataFillError { + /// A live simplex did not have a corresponding secondary-map entry. + #[error("secondary simplex data is missing live simplex key {simplex_key:?}")] + MissingSimplexData { + /// Live simplex key with no supplied data. + simplex_key: SimplexKey, + }, + /// The secondary map contains a key that is not live in this triangulation. + #[error("secondary simplex data contains stale simplex key {simplex_key:?}")] + StaleSimplexData { + /// Stale or foreign simplex key supplied by the secondary map. + simplex_key: SimplexKey, + }, + /// The final payload write failed after preflight. + #[error("failed to set simplex data for {simplex_key:?}: {source}")] + TdsMutation { + /// Simplex key whose payload write failed. + simplex_key: SimplexKey, + /// Underlying TDS mutation failure. + #[source] + source: Box, + }, +} // ============================================================================= // QUERY, ACCESSORS, AND CONFIGURATION (Minimal Bounds) @@ -56,7 +90,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.2, 0.2, 0.2, 0.2]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.number_of_vertices(), 6); /// # Ok(()) /// # } @@ -89,7 +123,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// // One 4-simplex in 4D /// assert_eq!(dt.number_of_simplices(), 1); /// # Ok(()) @@ -125,7 +159,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.dim(), 4); /// # Ok(()) /// # } @@ -166,7 +200,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// for (simplex_key, simplex) in dt.simplices() { /// println!("Simplex {:?} has {} vertices", simplex_key, simplex.number_of_vertices()); @@ -209,7 +243,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// for (vertex_key, vertex) in dt.vertices() { /// println!("Vertex {:?} at {:?}", vertex_key, vertex.point()); @@ -258,7 +292,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0; data = 20]?, /// delaunay::vertex![0.0, 1.0; data = 30]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((key, _)) = dt.vertices().next() else { /// return Ok(()); /// }; @@ -317,7 +351,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).simplex_data_type::().build()?; /// let Some((key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -341,6 +375,130 @@ impl DelaunayTriangulation { self.tri.tds.set_simplex_data(key, data) } + /// Fills every existing simplex with data computed from a borrowed simplex view. + /// + /// This is the ergonomic follow-on step for triangulations whose simplex + /// payload type was selected at construction time. It does not change + /// geometry, topology, runtime keys, UUIDs, or validation generation. + /// + /// # Examples + /// + /// ```rust + /// use delaunay::prelude::construction::{ + /// DelaunayResult, DelaunayTriangulation, DelaunayTriangulationBuilder, + /// }; + /// + /// # fn main() -> DelaunayResult<()> { + /// let vertices = vec![ + /// delaunay::vertex![0.0, 0.0]?, + /// delaunay::vertex![1.0, 0.0]?, + /// delaunay::vertex![0.0, 1.0]?, + /// ]; + /// + /// let mut dt: DelaunayTriangulation<_, _, usize, 2> = + /// DelaunayTriangulationBuilder::new(&vertices) + /// .simplex_data_type::() + /// .build()?; + /// dt.fill_simplex_data(|_, simplex| simplex.number_of_vertices()); + /// + /// for (_, simplex) in dt.simplices() { + /// assert_eq!(simplex.data(), Some(&3)); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn fill_simplex_data(&mut self, data_for: F) + where + F: FnMut(SimplexKey, &Simplex) -> V, + { + self.tri.tds.fill_simplex_data(data_for); + } + + /// Fills every existing simplex from a complete secondary map. + /// + /// The method preflights the entire map before mutating canonical simplex + /// storage. It returns [`SimplexDataFillError::StaleSimplexData`] if the + /// map contains a key that does not belong to this triangulation, and + /// [`SimplexDataFillError::MissingSimplexData`] if any live simplex has no + /// entry. On either preflight error, no simplex payload is changed. + /// + /// # Errors + /// + /// Returns [`SimplexDataFillError`] when the secondary map is stale, + /// incomplete, or when the final TDS payload write fails. + /// + /// Because values are copied out of a borrowed secondary map, this method + /// requires [`DataCopy`]. Use [`Self::fill_simplex_data`] when payloads can + /// be computed by value for each simplex. + /// + /// # Examples + /// + /// ```rust + /// use delaunay::prelude::collections::SimplexSecondaryMap; + /// use delaunay::prelude::construction::{ + /// DelaunayResult, DelaunayTriangulation, DelaunayTriangulationBuilder, + /// }; + /// + /// # fn main() -> DelaunayResult<()> { + /// let vertices = vec![ + /// delaunay::vertex![0.0, 0.0]?, + /// delaunay::vertex![1.0, 0.0]?, + /// delaunay::vertex![0.0, 1.0]?, + /// ]; + /// + /// let mut dt: DelaunayTriangulation<_, _, usize, 2> = + /// DelaunayTriangulationBuilder::new(&vertices) + /// .simplex_data_type::() + /// .build()?; + /// let mut data = SimplexSecondaryMap::new(); + /// for (simplex_key, simplex) in dt.simplices() { + /// data.insert(simplex_key, simplex.number_of_vertices()); + /// } + /// + /// dt.try_fill_simplex_data_from(&data)?; + /// for (_, simplex) in dt.simplices() { + /// assert_eq!(simplex.data(), Some(&3)); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn try_fill_simplex_data_from( + &mut self, + data: &SimplexSecondaryMap, + ) -> Result<(), SimplexDataFillError> + where + V: DataCopy, + { + for (simplex_key, _) in data { + if !self.tri.tds.contains_simplex(simplex_key) { + return Err(SimplexDataFillError::StaleSimplexData { simplex_key }); + } + } + + let assignments = self + .tri + .tds + .simplex_keys() + .map(|simplex_key| { + data.get(simplex_key) + .copied() + .map(|simplex_data| (simplex_key, simplex_data)) + .ok_or(SimplexDataFillError::MissingSimplexData { simplex_key }) + }) + .collect::, _>>()?; + + for (simplex_key, simplex_data) in assignments { + self.tri + .tds + .set_simplex_data(simplex_key, Some(simplex_data)) + .map_err(|source| SimplexDataFillError::TdsMutation { + simplex_key, + source: Box::new(source), + })?; + } + Ok(()) + } + /// Returns a reference to the underlying triangulation data structure. /// /// This provides access to the purely combinatorial Tds layer for @@ -371,7 +529,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// assert_eq!(tds.number_of_vertices(), 5); /// # Ok(()) @@ -445,7 +603,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// assert_eq!(hull.number_of_facets(), 4); /// # Ok(()) @@ -490,7 +648,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let boundary_count = dt /// .boundary_facets()? @@ -542,7 +700,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert_eq!(dt.validation_policy(), ValidationPolicy::ExplicitOnly); /// # Ok(()) @@ -665,7 +823,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert_eq!(dt.delaunay_repair_policy(), DelaunayRepairPolicy::EveryInsertion); /// # Ok(()) @@ -699,7 +857,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// dt.set_delaunay_repair_policy(DelaunayRepairPolicy::Never); /// assert_eq!(dt.delaunay_repair_policy(), DelaunayRepairPolicy::Never); @@ -730,7 +888,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert_eq!(dt.delaunay_check_policy(), DelaunayCheckPolicy::EndOnly); /// # Ok(()) @@ -765,7 +923,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some(every_two) = NonZeroUsize::new(2) else { /// return Ok(()); /// }; @@ -808,7 +966,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); /// # Ok(()) /// # } @@ -837,7 +995,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert!(dt.global_topology().is_euclidean()); /// # Ok(()) /// # } @@ -866,7 +1024,7 @@ impl DelaunayTriangulation { /// # } /// # fn main() -> Result<(), ExampleError> { /// let vertices = vec![delaunay::vertex![0.0, 0.0]?, delaunay::vertex![1.0, 0.0]?, delaunay::vertex![0.0, 1.0]?]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// assert_eq!(dt.topology_kind(), TopologyKind::Euclidean); /// # Ok(()) /// # } @@ -909,7 +1067,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// dt.try_set_global_topology(GlobalTopology::Euclidean)?; /// assert!(dt.global_topology().is_euclidean()); /// # Ok(()) @@ -990,7 +1148,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let facet_count = dt /// .facets() @@ -1041,7 +1199,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let adjacency = dt.adjacency()?; /// /// assert_eq!(adjacency.number_of_edges(), 6); @@ -1121,7 +1279,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let edges: std::collections::HashSet<_> = dt.edges().collect(); /// assert_eq!(edges.len(), 6); /// # Ok(()) @@ -1159,7 +1317,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((v0, _)) = dt.vertices().next() else { /// return Ok(()); /// }; @@ -1203,7 +1361,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1249,7 +1407,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); @@ -1289,7 +1447,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Find the key for a known vertex by matching coordinates. /// let Some(v_key) = dt @@ -1357,7 +1515,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1413,7 +1571,7 @@ impl DelaunayTriangulation { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((_simplex_key, simplex)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1434,14 +1592,19 @@ impl DelaunayTriangulation { #[cfg(test)] mod tests { use super::*; + use crate::builder::DelaunayTriangulationBuilder; + use crate::construction::{DelaunayError, DelaunayResult}; use crate::core::operations::DelaunayInsertionState; use crate::core::tds::TdsError; use crate::core::validation::TriangulationValidationError; - use crate::geometry::kernel::{AdaptiveKernel, FastKernel}; + use crate::geometry::kernel::FastKernel; use crate::vertex; + use slotmap::KeyData; use std::{assert_matches, collections::HashSet, num::NonZeroUsize, sync::Once}; struct Payload; + #[derive(Copy, Clone)] + struct CopyOnly; fn init_tracing() { static INIT: Once = Once::new(); @@ -1465,14 +1628,16 @@ mod tests { ]; let dt_new: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt_new.topology_guarantee(), TopologyGuarantee::PLManifold); let dt_empty: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::empty(); assert_eq!(dt_empty.topology_guarantee(), TopologyGuarantee::PLManifold); let dt_with_kernel: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); assert_eq!( dt_with_kernel.topology_guarantee(), @@ -1509,7 +1674,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let err = dt .try_set_global_topology(GlobalTopology::Spherical) @@ -1550,7 +1715,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let (simplex_key, _) = dt.tri.tds.simplices().next().unwrap(); let first_vertex = dt.tri.tds.simplex(simplex_key).unwrap().vertices()[0]; @@ -1587,6 +1752,208 @@ mod tests { ); } + #[test] + fn test_fill_simplex_data_accepts_non_datatype_payloads() { + let mut dt: DelaunayTriangulation, Payload, Payload, 2> = + DelaunayTriangulation { + tri: Triangulation::new_empty(FastKernel::new()), + insertion_state: DelaunayInsertionState::new(), + spatial_index: None, + }; + + dt.fill_simplex_data(|_, _| Payload); + + assert_eq!(dt.number_of_simplices(), 0); + } + + #[test] + fn test_try_fill_simplex_data_from_only_requires_copy_payloads() { + let mut dt: DelaunayTriangulation, Payload, CopyOnly, 2> = + DelaunayTriangulation { + tri: Triangulation::new_empty(FastKernel::new()), + insertion_state: DelaunayInsertionState::new(), + spatial_index: None, + }; + let data = SimplexSecondaryMap::new(); + + dt.try_fill_simplex_data_from(&data).unwrap(); + + assert_eq!(dt.number_of_simplices(), 0); + } + + #[test] + fn test_try_fill_simplex_data_from_updates_all_live_simplices() { + let vertices: Vec> = vec![ + vertex!([0.0, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + vertex!([0.25, 0.25]).unwrap(), + ]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); + assert!(dt.number_of_simplices() > 1); + let simplex_identity_before: HashSet<_> = dt + .simplices() + .map(|(simplex_key, simplex)| (simplex_key, simplex.uuid())) + .collect(); + let vertex_identity_before: HashSet<_> = dt + .vertices() + .map(|(vertex_key, vertex)| (vertex_key, vertex.uuid())) + .collect(); + + let mut data = SimplexSecondaryMap::new(); + for (simplex_key, simplex) in dt.simplices() { + data.insert(simplex_key, simplex.number_of_vertices()); + } + + dt.try_fill_simplex_data_from(&data).unwrap(); + + let simplex_identity_after: HashSet<_> = dt + .simplices() + .map(|(simplex_key, simplex)| (simplex_key, simplex.uuid())) + .collect(); + let vertex_identity_after: HashSet<_> = dt + .vertices() + .map(|(vertex_key, vertex)| (vertex_key, vertex.uuid())) + .collect(); + + assert_eq!(simplex_identity_after, simplex_identity_before); + assert_eq!(vertex_identity_after, vertex_identity_before); + for (_, simplex) in dt.simplices() { + assert_eq!(simplex.data(), Some(&3)); + } + assert!(dt.validate().is_ok()); + } + + #[test] + fn test_fill_simplex_data_preserves_topology_identity() { + let vertices: Vec> = vec![ + vertex!([0.0, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + ]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); + let simplex_identity_before: HashSet<_> = dt + .simplices() + .map(|(simplex_key, simplex)| (simplex_key, simplex.uuid())) + .collect(); + let vertex_identity_before: HashSet<_> = dt + .vertices() + .map(|(vertex_key, vertex)| (vertex_key, vertex.uuid())) + .collect(); + + dt.fill_simplex_data(|_, simplex| simplex.number_of_vertices()); + + let simplex_identity_after: HashSet<_> = dt + .simplices() + .map(|(simplex_key, simplex)| (simplex_key, simplex.uuid())) + .collect(); + let vertex_identity_after: HashSet<_> = dt + .vertices() + .map(|(vertex_key, vertex)| (vertex_key, vertex.uuid())) + .collect(); + + assert_eq!(simplex_identity_after, simplex_identity_before); + assert_eq!(vertex_identity_after, vertex_identity_before); + for (_, simplex) in dt.simplices() { + assert_eq!(simplex.data(), Some(&3)); + } + assert!(dt.validate().is_ok()); + } + + #[test] + fn test_try_fill_simplex_data_from_missing_entry_has_no_side_effects() { + let vertices: Vec> = vec![ + vertex!([0.0, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + vertex!([0.25, 0.25]).unwrap(), + ]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); + assert!(dt.number_of_simplices() > 1); + dt.fill_simplex_data(|_, _| 7); + + let mut data = SimplexSecondaryMap::new(); + let Some((first_simplex_key, _)) = dt.simplices().next() else { + panic!("fixture should contain at least one simplex"); + }; + data.insert(first_simplex_key, 3); + + let err = dt.try_fill_simplex_data_from(&data).unwrap_err(); + assert_matches!(err, SimplexDataFillError::MissingSimplexData { .. }); + for (_, simplex) in dt.simplices() { + assert_eq!(simplex.data(), Some(&7)); + } + assert!(dt.validate().is_ok()); + } + + #[test] + fn test_try_fill_simplex_data_from_stale_entry_has_no_side_effects() { + let vertices: Vec> = vec![ + vertex!([0.0, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + vertex!([0.25, 0.25]).unwrap(), + ]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build() + .unwrap(); + dt.fill_simplex_data(|_, _| 7); + + let mut data = SimplexSecondaryMap::new(); + for (simplex_key, simplex) in dt.simplices() { + data.insert(simplex_key, simplex.number_of_vertices()); + } + let stale = SimplexKey::from(KeyData::from_ffi(0xFEED)); + data.insert(stale, 99); + + let err = dt.try_fill_simplex_data_from(&data).unwrap_err(); + assert_matches!( + err, + SimplexDataFillError::StaleSimplexData { simplex_key } if simplex_key == stale + ); + for (_, simplex) in dt.simplices() { + assert_eq!(simplex.data(), Some(&7)); + } + assert!(dt.validate().is_ok()); + } + + #[test] + fn test_delaunay_result_accepts_simplex_data_fill_errors() { + fn attempt_fill_from_incomplete_map() -> DelaunayResult<()> { + let vertices: Vec> = vec![ + vertex!([0.0, 0.0])?, + vertex!([1.0, 0.0])?, + vertex!([0.0, 1.0])?, + ]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build()?; + let data = SimplexSecondaryMap::new(); + dt.try_fill_simplex_data_from(&data)?; + Ok(()) + } + + let err = attempt_fill_from_incomplete_map().unwrap_err(); + assert_matches!( + err, + DelaunayError::SimplexDataFill { source } + if matches!( + source.as_ref(), + SimplexDataFillError::MissingSimplexData { .. } + ) + ); + } + #[test] fn test_validation_policy_defaults_to_topology_guarantee_policy() { init_tracing(); @@ -1602,12 +1969,14 @@ mod tests { // new() -> with_kernel() -> explicit validation_policy initialization let dt_new: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt_new.validation_policy(), ValidationPolicy::ExplicitOnly); // with_kernel() constructor path should also use the default policy let dt_with_kernel: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), &vertices).unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .build() + .unwrap(); assert_eq!( dt_with_kernel.validation_policy(), ValidationPolicy::ExplicitOnly @@ -1635,7 +2004,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Getter reflects the underlying Triangulation policy. assert_eq!(dt.validation_policy(), ValidationPolicy::ExplicitOnly); @@ -1687,7 +2056,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_vertices(), 4); } @@ -1703,7 +2072,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Minimal 3D simplex has exactly 1 tetrahedron assert_eq!(dt.number_of_simplices(), 1); @@ -1719,7 +2088,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_simplices(), 1); @@ -1737,7 +2106,9 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt_2d: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); assert_eq!(dt_2d.dim(), 2); let vertices_3d = vec![ @@ -1747,7 +2118,9 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt_3d: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices_3d).unwrap(); + DelaunayTriangulation::builder(&vertices_3d) + .build() + .unwrap(); assert_eq!(dt_3d.dim(), 3); let vertices_4d = vec![ @@ -1758,7 +2131,9 @@ mod tests { vertex!([0.0, 0.0, 0.0, 1.0]).unwrap(), ]; let dt_4d: DelaunayTriangulation<_, (), (), 4> = - DelaunayTriangulation::try_new(&vertices_4d).unwrap(); + DelaunayTriangulation::builder(&vertices_4d) + .build() + .unwrap(); assert_eq!(dt_4d.dim(), 4); } @@ -1772,7 +2147,9 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt_2d: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_2d).unwrap(); + DelaunayTriangulation::builder(&vertices_2d) + .build() + .unwrap(); assert_eq!(dt_2d.number_of_vertices(), 3); assert_eq!(dt_2d.number_of_simplices(), 1); @@ -1784,7 +2161,9 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt_3d: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices_3d).unwrap(); + DelaunayTriangulation::builder(&vertices_3d) + .build() + .unwrap(); assert_eq!(dt_3d.number_of_vertices(), 4); assert_eq!(dt_3d.number_of_simplices(), 1); } @@ -1798,7 +2177,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Access TDS via immutable reference let tds = dt.tds(); @@ -1820,7 +2199,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_vertices(), 4); @@ -1843,7 +2222,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Before insertion assert_eq!(dt.tds().number_of_vertices(), 3); @@ -1867,7 +2246,7 @@ mod tests { vertex!([0.0, 0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 4> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Verify TDS is valid through accessor assert!(dt.tds().is_valid().is_ok()); @@ -1893,7 +2272,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let tri = dt.as_triangulation(); let edges_dt: HashSet<_> = dt.edges().collect(); diff --git a/src/delaunay/repair.rs b/src/delaunay/repair.rs index 5b01e536..1b4976cc 100644 --- a/src/delaunay/repair.rs +++ b/src/delaunay/repair.rs @@ -377,7 +377,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let stats = dt.repair_delaunay_with_flips()?; /// assert!(stats.facets_checked >= stats.flips_performed); @@ -600,7 +600,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let outcome = dt /// .repair_delaunay_with_flips_advanced(DelaunayRepairHeuristicConfig::default()) @@ -1022,7 +1022,7 @@ mod tests { init_tracing(); let vertices: Vec> = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; let dt: DelaunayTriangulation<_, (), (), 1> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_simplices(), 1); assert_eq!( @@ -1054,7 +1054,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_simplices(), 1); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::Never); @@ -1070,7 +1070,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::EveryN(NonZeroUsize::new(2).unwrap())); let topology = dt.topology_guarantee(); @@ -1089,7 +1089,7 @@ mod tests { vertex!([0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let topology = dt.topology_guarantee(); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::EveryN(NonZeroUsize::new(2).unwrap())); @@ -1109,7 +1109,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Insert a vertex normally (no heuristic rebuild during insert). let inserted = vertex!([0.25, 0.25]).unwrap(); @@ -1156,7 +1156,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let global_topology = GlobalTopology::Euclidean; dt.try_set_global_topology(global_topology).unwrap(); @@ -1183,7 +1183,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.tri.global_topology = GlobalTopology::Spherical; let _guard = ForceHeuristicRebuildGuard::enable(); @@ -1209,7 +1209,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); dt.set_topology_guarantee(TopologyGuarantee::PLManifold); let result = dt.repair_delaunay_with_flips(); @@ -1233,7 +1233,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let _guard = ForceRepairNonconvergentGuard::enable(); let outcome = dt @@ -1264,7 +1264,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Sub-case 1: Already Delaunay — max_flips=0 should succeed (no flips needed). let config = DelaunayRepairHeuristicConfig { @@ -1292,7 +1292,7 @@ mod tests { vertex!([1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let _guard = ForceRepairNonconvergentGuard::enable(); let config = DelaunayRepairHeuristicConfig { @@ -1388,7 +1388,7 @@ mod tests { init_tracing(); let vertices: Vec> = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; let mut dt: DelaunayTriangulation, (), (), 1> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let result = dt.repair_delaunay_with_flips(); assert!( @@ -1413,7 +1413,7 @@ mod tests { init_tracing(); let vertices: Vec> = vec![vertex!([0.0]).unwrap(), vertex!([1.0]).unwrap()]; let mut dt: DelaunayTriangulation, (), (), 1> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let result = dt.repair_delaunay_with_flips_advanced(DelaunayRepairHeuristicConfig::default()); diff --git a/src/delaunay/rollback.rs b/src/delaunay/rollback.rs index c4f5b41c..dacaa2f1 100644 --- a/src/delaunay/rollback.rs +++ b/src/delaunay/rollback.rs @@ -132,7 +132,7 @@ mod tests { fn test_triangulation() -> DelaunayTriangulation, (), (), D> { let vertices = simplex_vertices::(); - DelaunayTriangulation::try_new(&vertices).unwrap() + DelaunayTriangulation::builder(&vertices).build().unwrap() } fn seed_spatial_index( diff --git a/src/delaunay/serialization.rs b/src/delaunay/serialization.rs index 88090510..c55f19a0 100644 --- a/src/delaunay/serialization.rs +++ b/src/delaunay/serialization.rs @@ -63,7 +63,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let json = serde_json::to_string(&dt)?; /// /// let tds: Tds<(), (), 3> = serde_json::from_str(&json)?; @@ -162,7 +162,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let json = serde_json::to_string(&dt).unwrap(); diff --git a/src/delaunay/triangulation.rs b/src/delaunay/triangulation.rs index f9a3fc85..70f8caa2 100644 --- a/src/delaunay/triangulation.rs +++ b/src/delaunay/triangulation.rs @@ -61,7 +61,7 @@ use crate::core::triangulation::Triangulation; /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert_eq!(dt.number_of_simplices(), 1); /// # Ok(()) diff --git a/src/delaunay/validation.rs b/src/delaunay/validation.rs index 86a19736..9c3ab074 100644 --- a/src/delaunay/validation.rs +++ b/src/delaunay/validation.rs @@ -331,7 +331,7 @@ impl From<&DelaunayVerificationError> for DelaunayVerificationErrorKind { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let result: Result<(), DelaunayTriangulationValidationError> = dt.validate(); /// assert!(result.is_ok()); @@ -549,7 +549,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// /// // Level 5: Delaunay property only /// assert!(dt.is_valid_delaunay().is_ok()); @@ -580,7 +580,7 @@ where /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.delaunay_diagnostic().is_none()); /// # Ok(()) @@ -619,7 +619,7 @@ where /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// assert!(dt.delaunay_report().is_ok()); /// # Ok(()) @@ -693,7 +693,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Fast O(N) verification /// assert!(dt.is_delaunay_via_flips().is_ok()); @@ -730,7 +730,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// /// // Levels 1–5: elements + structure + topology + embedding + Delaunay property /// assert!(dt.validate().is_ok()); @@ -769,7 +769,7 @@ where /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Returns Ok(()) on success; otherwise returns a report listing all violations. /// let report = dt.validation_report(); @@ -907,7 +907,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // Reconstruct DelaunayTriangulation from a Tds snapshot. /// let tds: Tds<(), (), 4> = dt.tds().clone(); @@ -952,7 +952,7 @@ where /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let reconstructed = DelaunayTriangulation::try_from_tds_with_topology_guarantee( /// dt.tds().clone(), @@ -1006,7 +1006,7 @@ where /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let reconstructed = DelaunayTriangulation::try_from_tds_with_topology_context( /// dt.tds().clone(), @@ -1329,7 +1329,7 @@ mod tests { test_vertex([0.0, 0.0, 1.0]), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let vk = tds.vertex_keys().next().unwrap(); @@ -1355,7 +1355,7 @@ mod tests { test_vertex([0.0, 0.0, 1.0]), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let mut tds = dt.tds().clone(); let _ = tds @@ -1385,7 +1385,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert!(dt.validation_report().is_ok()); } @@ -1400,7 +1400,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Break UUID↔key mappings: remove one vertex UUID entry. let uuid = dt.tri.tds.vertices().next().unwrap().1.uuid(); @@ -1435,7 +1435,7 @@ mod tests { ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Corrupt a `Vertex::incident_simplex` pointer. let vertex_key = dt.tri.tds.vertices().next().unwrap().0; @@ -1491,7 +1491,7 @@ mod tests { test_vertex([0.0, 0.0, 1.0]), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Break vertex mapping so Level 2 structural validation fails. let vk = dt.tds().vertex_keys().next().unwrap(); @@ -1517,7 +1517,7 @@ mod tests { test_vertex([0.0, 0.0, 1.0]), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Add an isolated vertex so Level 3 (topology) fails. let _ = dt diff --git a/src/geometry/algorithms/convex_hull.rs b/src/geometry/algorithms/convex_hull.rs index 0b61bbdd..1030525b 100644 --- a/src/geometry/algorithms/convex_hull.rs +++ b/src/geometry/algorithms/convex_hull.rs @@ -36,7 +36,7 @@ //! delaunay::vertex![0.0, 0.0, 1.0]?, //! ]; //! let dt: DelaunayTriangulation<_, (), (), 3> = -//! DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! DelaunayTriangulationBuilder::new(&vertices).build()?; //! let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; //! let outside = Point::try_from([2.0, 2.0, 2.0])?; //! assert!(hull.is_point_outside(&outside, dt.as_triangulation())?); @@ -326,7 +326,7 @@ pub enum ConvexHullConstructionError { /// # vertex![1.0, 0.0, 0.0]?, /// # vertex![0.0, 1.0, 0.0]?, /// # vertex![0.0, 0.0, 1.0]?, -/// # ]).build::<()>()?; +/// # ]).build()?; /// let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// assert!(hull.is_valid_for_triangulation(dt.as_triangulation())); // Valid initially /// @@ -370,7 +370,7 @@ pub enum ConvexHullConstructionError { /// vertex![1.0, 0.0, 0.0]?, /// vertex![0.0, 1.0, 0.0]?, /// vertex![0.0, 0.0, 1.0]?, -/// ]).build::<()>()?; +/// ]).build()?; /// /// // Create initial hull (note: immutable binding) /// let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; @@ -490,7 +490,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -541,7 +541,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -593,7 +593,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -654,7 +654,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// /// for facet in hull.try_facets(dt.as_triangulation())? { @@ -715,7 +715,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// assert!(!hull.is_empty()); @@ -758,7 +758,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 1.0]?, /// ]; /// let dt_2d: DelaunayTriangulation<_, (), (), 2> = - /// DelaunayTriangulationBuilder::new(&vertices_2d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_2d).build()?; /// let hull_2d = /// ConvexHull::try_from_triangulation(dt_2d.as_triangulation())?; /// assert_eq!(hull_2d.dimension(), 2); @@ -770,7 +770,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt_3d: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices_3d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_3d).build()?; /// let hull_3d = /// ConvexHull::try_from_triangulation(dt_3d.as_triangulation())?; /// assert_eq!(hull_3d.dimension(), 3); @@ -826,7 +826,7 @@ impl ConvexHull { /// delaunay::vertex![1.0, 0.0, 0.0]?, /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, - /// ]).build::<()>()?; + /// ]).build()?; /// /// // Create hull and verify it's valid /// let hull = ConvexHull::try_from_triangulation(dt.as_triangulation())?; @@ -1035,7 +1035,7 @@ impl ConvexHull { /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -1111,7 +1111,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt_3d: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices_3d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_3d).build()?; /// let hull_3d: ConvexHull<(), (), 3> = /// ConvexHull::try_from_triangulation(dt_3d.as_triangulation())?; /// assert_eq!(hull_3d.number_of_facets(), 4); // Tetrahedron has 4 faces @@ -1125,7 +1125,7 @@ where /// delaunay::vertex![0.0, 0.0, 0.0, 1.0]?, /// ]; /// let dt_4d: DelaunayTriangulation<_, (), (), 4> = - /// DelaunayTriangulationBuilder::new(&vertices_4d).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices_4d).build()?; /// let hull_4d = /// ConvexHull::try_from_triangulation(dt_4d.as_triangulation())?; /// assert_eq!(hull_4d.number_of_facets(), 5); // 4-simplex has 5 facets @@ -1278,7 +1278,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -1672,7 +1672,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -1766,7 +1766,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -1901,7 +1901,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -1967,7 +1967,7 @@ where /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; /// let dt: DelaunayTriangulation<_, (), (), 3> = - /// DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// DelaunayTriangulationBuilder::new(&vertices).build()?; /// let hull = /// ConvexHull::try_from_triangulation(dt.as_triangulation())?; /// @@ -2089,6 +2089,7 @@ pub type ConvexHull4D = ConvexHull; #[cfg(test)] mod tests { use super::*; + use crate::builder::DelaunayTriangulationBuilder; use crate::construction::{ DelaunayConstructionFailure, DelaunayTriangulationConstructionError, }; @@ -2127,12 +2128,14 @@ mod tests { /// Helper function to create a `DelaunayTriangulation` from vertices. /// - /// This uses `DelaunayTriangulation::try_with_kernel` and returns the owned + /// This uses `DelaunayTriangulationBuilder::build_with_kernel` and returns the owned /// triangulation directly. fn create_triangulation( vertices: &[Vertex<(), D>], ) -> DelaunayTriangulation, (), (), D> { - DelaunayTriangulation::try_with_kernel(&AdaptiveKernel::new(), vertices).unwrap() + DelaunayTriangulationBuilder::new(vertices) + .build_with_kernel(&AdaptiveKernel::new()) + .unwrap() } /// Helper function to extract vertices from a facet handle. @@ -3198,11 +3201,10 @@ mod tests { vertex!([0.0, 1.0, 0.0]; data = 3i32).unwrap(), vertex!([0.0, 0.0, 1.0]; data = 4i32).unwrap(), ]; - let dt_int = DelaunayTriangulation::, i32, (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - &vertices_int, - ) - .unwrap(); + let dt_int: DelaunayTriangulation, i32, (), 3> = + DelaunayTriangulationBuilder::new(&vertices_int) + .build_with_kernel(&AdaptiveKernel::new()) + .unwrap(); let hull_int = ConvexHull::try_from_triangulation(dt_int.as_triangulation()).unwrap(); assert!( hull_int.validate(dt_int.as_triangulation()).is_ok(), @@ -3216,11 +3218,10 @@ mod tests { vertex!([0.0, 1.0, 0.0]; data = 'C').unwrap(), vertex!([0.0, 0.0, 1.0]; data = 'D').unwrap(), ]; - let dt_char = DelaunayTriangulation::, char, (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - &vertices_char, - ) - .unwrap(); + let dt_char: DelaunayTriangulation, char, (), 3> = + DelaunayTriangulationBuilder::new(&vertices_char) + .build_with_kernel(&AdaptiveKernel::new()) + .unwrap(); let hull_char = ConvexHull::try_from_triangulation(dt_char.as_triangulation()).unwrap(); assert!( hull_char.validate(dt_char.as_triangulation()).is_ok(), @@ -3268,7 +3269,7 @@ mod tests { for (vertices, desc) in extreme_vertices { let dt_result: Result, _> = - DelaunayTriangulation::try_new(&vertices); + DelaunayTriangulation::builder(&vertices).build(); match dt_result { Ok(dt) => { @@ -3289,7 +3290,7 @@ mod tests { // quantization resolution, leaving fewer than D+1 vertices. test_debug!( " \x1b[33mWarning:\x1b[0m skipping {desc} extreme coordinate hull validation \ - due to geometric degeneracy or insufficient vertices in DelaunayTriangulation::try_new", + due to geometric degeneracy or insufficient vertices in DelaunayTriangulationBuilder::build", ); } Err(other) => { @@ -3550,11 +3551,10 @@ mod tests { vertex!([0.0, 1.0, 0.0]; data = 3i32).unwrap(), vertex!([0.0, 0.0, 1.0]; data = 4i32).unwrap(), ]; - let dt_int = DelaunayTriangulation::, i32, (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - &vertices_int, - ) - .unwrap(); + let dt_int: DelaunayTriangulation, i32, (), 3> = + DelaunayTriangulationBuilder::new(&vertices_int) + .build_with_kernel(&AdaptiveKernel::new()) + .unwrap(); let hull_int = ConvexHull::try_from_triangulation(dt_int.as_triangulation()).unwrap(); assert_eq!(hull_int.number_of_facets(), 4); @@ -3568,11 +3568,10 @@ mod tests { vertex!([0.0, 1.0, 0.0]; data = 'C').unwrap(), vertex!([0.0, 0.0, 1.0]; data = 'D').unwrap(), ]; - let dt_char = DelaunayTriangulation::, char, (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - &vertices_char, - ) - .unwrap(); + let dt_char: DelaunayTriangulation, char, (), 3> = + DelaunayTriangulationBuilder::new(&vertices_char) + .build_with_kernel(&AdaptiveKernel::new()) + .unwrap(); let hull_char = ConvexHull::try_from_triangulation(dt_char.as_triangulation()).unwrap(); assert_eq!(hull_char.number_of_facets(), 4); @@ -3586,12 +3585,10 @@ mod tests { vertex!([0.0, 1.0, 0.0]; data = Some(3)).unwrap(), vertex!([0.0, 0.0, 1.0]; data = Some(4)).unwrap(), ]; - let dt_with_data = - DelaunayTriangulation::, Option, (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - &vertices_with_data, - ) - .unwrap(); + let dt_with_data: DelaunayTriangulation, Option, (), 3> = + DelaunayTriangulationBuilder::new(&vertices_with_data) + .build_with_kernel(&AdaptiveKernel::new()) + .unwrap(); let hull_with_data = ConvexHull::try_from_triangulation(dt_with_data.as_triangulation()).unwrap(); @@ -4208,7 +4205,7 @@ mod tests { vertex!([0.0, 0.0, f64::MIN_POSITIVE]).unwrap(), ]; - let dt_extreme_result = DelaunayTriangulation::try_new(&vertices_extreme); + let dt_extreme_result = DelaunayTriangulation::builder(&vertices_extreme).build(); match dt_extreme_result { Ok(dt_extreme) => { @@ -4244,7 +4241,7 @@ mod tests { )) => { // On some platforms, these extreme coordinates may be judged too // numerically unstable to form a reliable 3D simplex. In that - // case, it's acceptable for DelaunayTriangulation::try_new to fail with geometric + // case, it's acceptable for DelaunayTriangulationBuilder::build to fail with geometric // degeneracy; later parts of this test still exercise max-scale // behavior. test_debug!( @@ -5625,7 +5622,7 @@ mod tests { vertex!([1e10, 1e10, 1e10]).unwrap(), ]; - match DelaunayTriangulation::try_new(&large_vertices) { + match DelaunayTriangulation::builder(&large_vertices).build() { Ok(large_dt) => { match ConvexHull::try_from_triangulation(large_dt.as_triangulation()) { Ok(large_hull) => { @@ -5673,7 +5670,7 @@ mod tests { vertex!([1e-15, 1e-15, 1e-15]).unwrap(), ]; - match DelaunayTriangulation::try_new(&small_vertices) { + match DelaunayTriangulation::builder(&small_vertices).build() { Ok(small_dt) => match ConvexHull::try_from_triangulation(small_dt.as_triangulation()) { Ok(small_hull) => { assert!(small_hull.validate(small_dt.as_triangulation()).is_ok()); @@ -5855,7 +5852,7 @@ mod tests { vertex!([0.5, 0.5, 1.0]).unwrap(), // Out of plane ]; - match DelaunayTriangulation::try_new(&near_collinear_vertices) { + match DelaunayTriangulation::builder(&near_collinear_vertices).build() { Ok(collinear_dt) => { match ConvexHull::try_from_triangulation(collinear_dt.as_triangulation()) { Ok(collinear_hull) => { @@ -5906,7 +5903,7 @@ mod tests { vertex!([0.0, 0.0, 1e-6]).unwrap(), ]; - match DelaunayTriangulation::try_new(&tiny_area_vertices) { + match DelaunayTriangulation::builder(&tiny_area_vertices).build() { Ok(tiny_dt) => { match ConvexHull::try_from_triangulation(tiny_dt.as_triangulation()) { Ok(tiny_hull) => { @@ -6064,7 +6061,7 @@ mod tests { for (description, vertices) in edge_test_cases { test_debug!(" Testing {description}..."); - match DelaunayTriangulation::try_new(&vertices) { + match DelaunayTriangulation::builder(&vertices).build() { Ok(edge_dt) => match ConvexHull::try_from_triangulation(edge_dt.as_triangulation()) { Ok(edge_hull) => { @@ -6127,7 +6124,7 @@ mod tests { vertex!([2.0, 0.0]).unwrap(), // Collinear with first two ]; - match DelaunayTriangulation::try_new(&collinear_2d_vertices) { + match DelaunayTriangulation::builder(&collinear_2d_vertices).build() { Ok(collinear_dt) => { match ConvexHull::try_from_triangulation(collinear_dt.as_triangulation()) { Ok(collinear_hull) => { @@ -6187,7 +6184,7 @@ mod tests { vertex!([2.0, -1e-12]).unwrap(), // Nearly on the line y=0 ]; - match DelaunayTriangulation::try_new(&nearly_collinear_vertices) { + match DelaunayTriangulation::builder(&nearly_collinear_vertices).build() { Ok(nearly_dt) => { match ConvexHull::try_from_triangulation(nearly_dt.as_triangulation()) { Ok(nearly_hull) => { @@ -6229,7 +6226,7 @@ mod tests { vertex!([3.0, 3.0, 3.0]).unwrap(), // Also collinear ]; - match DelaunayTriangulation::try_new(&collinear_3d_vertices) { + match DelaunayTriangulation::builder(&collinear_3d_vertices).build() { Ok(_) => { test_debug!( " Warning: 3D collinear DelaunayTriangulation constructed (unexpected but handled)" @@ -6263,7 +6260,7 @@ mod tests { vertex!([1.0, 1.0, 0.0]).unwrap(), // All in z=0 plane ]; - match DelaunayTriangulation::try_new(&coplanar_3d_vertices) { + match DelaunayTriangulation::builder(&coplanar_3d_vertices).build() { Ok(_) => { test_debug!( " Warning: Coplanar 3D DelaunayTriangulation constructed (may indicate insufficient degeneracy detection)" @@ -6285,7 +6282,7 @@ mod tests { vertex!([1.0, 1.0, 1e-10]).unwrap(), ]; - match DelaunayTriangulation::try_new(&nearly_coplanar_vertices) { + match DelaunayTriangulation::builder(&nearly_coplanar_vertices).build() { Ok(nearly_coplanar_dt) => { match ConvexHull::try_from_triangulation(nearly_coplanar_dt.as_triangulation()) { Ok(nearly_coplanar_hull) => { @@ -6368,7 +6365,7 @@ mod tests { vertex!([1.0, 1.0, 1.0, 0.0]).unwrap(), // All in w=0 hyperplane ]; - match DelaunayTriangulation::try_new(&coplanar_4d_vertices) { + match DelaunayTriangulation::builder(&coplanar_4d_vertices).build() { Ok(_) => { test_debug!( " Warning: Coplanar 4D DelaunayTriangulation constructed (may indicate insufficient degeneracy detection)" @@ -6398,7 +6395,7 @@ mod tests { vertex!([0.0, 0.0, 0.0]).unwrap(), // Exact duplicate of first vertex ]; - match DelaunayTriangulation::try_new(&duplicate_vertices) { + match DelaunayTriangulation::builder(&duplicate_vertices).build() { Ok(dup_dt) => { test_debug!(" Duplicate vertices DelaunayTriangulation constructed"); match ConvexHull::try_from_triangulation(dup_dt.as_triangulation()) { @@ -6446,7 +6443,7 @@ mod tests { vertex!([1e-15, 1e-15, 1e-15]).unwrap(), // Nearly coincident with first ]; - match DelaunayTriangulation::try_new(&nearly_coincident_vertices) { + match DelaunayTriangulation::builder(&nearly_coincident_vertices).build() { Ok(nearly_coin_dt) => { match ConvexHull::try_from_triangulation(nearly_coin_dt.as_triangulation()) { Ok(nearly_coin_hull) => { @@ -6514,7 +6511,7 @@ mod tests { vertex!([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]).unwrap(), // Interior point to make it non-degenerate ]; - match DelaunayTriangulation::try_new(&vertices_6d) { + match DelaunayTriangulation::builder(&vertices_6d).build() { Ok(dt_6d) => { match ConvexHull::try_from_triangulation(dt_6d.as_triangulation()) { Ok(hull_6d) => { @@ -6572,7 +6569,7 @@ mod tests { vertex!([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]).unwrap(), ]; - match DelaunayTriangulation::try_new(&vertices_7d) { + match DelaunayTriangulation::builder(&vertices_7d).build() { Ok(dt_7d) => match ConvexHull::try_from_triangulation(dt_7d.as_triangulation()) { Ok(hull_7d) => { test_debug!(" ✓ 7D hull constructed successfully"); @@ -6603,7 +6600,7 @@ mod tests { vertex!([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]).unwrap(), ]; - match DelaunayTriangulation::try_new(&vertices_8d) { + match DelaunayTriangulation::builder(&vertices_8d).build() { Ok(dt_8d) => { match ConvexHull::try_from_triangulation(dt_8d.as_triangulation()) { Ok(hull_8d) => { @@ -6663,10 +6660,9 @@ mod tests { // Use AdaptiveKernel: it tries a fast floating-point filter first and // falls back to exact arithmetic only for near-degenerate inputs. - match DelaunayTriangulation::<_, (), (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - &large_vertices, - ) { + match DelaunayTriangulationBuilder::new(&large_vertices) + .build_with_kernel(&AdaptiveKernel::new()) + { Ok(large_dt) => { let dt_construction_time = start_time.elapsed(); test_debug!( @@ -6747,10 +6743,9 @@ mod tests { // Use AdaptiveKernel: fast filter for most queries, exact fallback // only for near-degenerate inputs. - match DelaunayTriangulation::<_, (), (), 2>::try_with_kernel( - &AdaptiveKernel::::new(), - &large_2d_vertices, - ) { + match DelaunayTriangulationBuilder::new(&large_2d_vertices) + .build_with_kernel(&AdaptiveKernel::::new()) + { Ok(large_2d_dt) => { match ConvexHull::try_from_triangulation(large_2d_dt.as_triangulation()) { Ok(large_2d_hull) => { @@ -7131,12 +7126,13 @@ mod tests { test_debug!("Testing stale hull detection after invalidate_cache (regression test)"); // Step 1: Create hull from initial DelaunayTriangulation - let mut dt = DelaunayTriangulation::try_new(&[ + let mut dt = DelaunayTriangulation::builder(&[ vertex!([0.0, 0.0, 0.0]).unwrap(), vertex!([1.0, 0.0, 0.0]).unwrap(), vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]) + .build() .unwrap(); let initial_gen = dt.as_triangulation().tds.generation(); let hull = ConvexHull::try_from_triangulation(dt.as_triangulation()).unwrap(); @@ -7235,9 +7231,9 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt1: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let dt2: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!( dt1.as_triangulation().tds.generation(), dt2.as_triangulation().tds.generation(), @@ -7276,7 +7272,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt1: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let dt2 = dt1.clone(); assert_eq!( dt1.as_triangulation().tds.generation(), @@ -7316,7 +7312,7 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let hull = ConvexHull::try_from_triangulation(dt.as_triangulation()).unwrap(); let cache_before = hull @@ -7399,12 +7395,13 @@ mod tests { fn test_stale_hull_all_operations_fail() { test_debug!("Testing that all hull operations fail on stale hull"); - let mut dt = DelaunayTriangulation::try_new(&[ + let mut dt = DelaunayTriangulation::builder(&[ vertex!([0.0, 0.0, 0.0]).unwrap(), vertex!([1.0, 0.0, 0.0]).unwrap(), vertex!([0.0, 1.0, 0.0]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]) + .build() .unwrap(); let hull = ConvexHull::try_from_triangulation(dt.as_triangulation()).unwrap(); @@ -7477,12 +7474,13 @@ mod tests { test_debug!("Testing fallback visibility check for degenerate facets"); // Create hull with nearly coplanar points (z << x,y) but still valid simplex - let dt = DelaunayTriangulation::try_new(&[ + let dt = DelaunayTriangulation::builder(&[ vertex!([0.0, 0.0, 0.0]).unwrap(), vertex!([1.0, 0.0, 1e-6]).unwrap(), vertex!([0.0, 1.0, 1e-6]).unwrap(), vertex!([0.0, 0.0, 1.0]).unwrap(), ]) + .build() .unwrap(); let hull = ConvexHull::try_from_triangulation(dt.as_triangulation()).unwrap(); @@ -7503,12 +7501,13 @@ mod tests { test_debug!("Testing convex hull with extreme aspect ratio facets"); // Create a very flat triangulation (extreme aspect ratio) - let dt = DelaunayTriangulation::try_new(&[ + let dt = DelaunayTriangulation::builder(&[ vertex!([0.0, 0.0, 0.0]).unwrap(), vertex!([1000.0, 0.0, 0.0]).unwrap(), // Very long in x vertex!([0.0, 0.001, 0.0]).unwrap(), // Very short in y vertex!([0.0, 0.0, 0.001]).unwrap(), // Very short in z ]) + .build() .unwrap(); let hull = ConvexHull::try_from_triangulation(dt.as_triangulation()).unwrap(); @@ -7624,7 +7623,8 @@ mod tests { $dim, vertices_vec.len() ); - let dt = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices_vec) + let dt = DelaunayTriangulation::builder(&vertices_vec) + .build() .expect(&format!("Failed to create {}D DelaunayTriangulation", $dim)); test_debug!(" Constructing {}D convex hull...", $dim); diff --git a/src/geometry/kernel.rs b/src/geometry/kernel.rs index 8cf7731b..d15095b3 100644 --- a/src/geometry/kernel.rs +++ b/src/geometry/kernel.rs @@ -361,8 +361,9 @@ impl_exact_predicates_for_supported_dims!(0, 1, 2, 3, 4, 5); /// /// Use [`AdaptiveKernel`] (the default) for all 3D+ work. `FastKernel` remains /// suitable for 2D triangulations with well-conditioned input, or when explicitly -/// opted into via [`DelaunayTriangulation::try_with_kernel`](crate::DelaunayTriangulation::try_with_kernel) for advanced use cases -/// where the caller has verified the input is non-degenerate. +/// opted into via +/// [`DelaunayTriangulationBuilder::build_with_kernel`](crate::DelaunayTriangulationBuilder::build_with_kernel) +/// for advanced use cases where the caller has verified the input is non-degenerate. /// /// # Performance /// @@ -578,8 +579,8 @@ impl Kernel for RobustKernel { /// Adaptive precision kernel with Simulation of Simplicity. /// -/// This is the **default kernel** for [`DelaunayTriangulation`] convenience -/// constructors (`try_new`, `empty`, `try_new_with_options`, etc.). +/// This is the **default kernel** for [`DelaunayTriangulation`] builder +/// construction and empty triangulation constructors. /// /// [`DelaunayTriangulation`]: crate::DelaunayTriangulation /// diff --git a/src/geometry/quality.rs b/src/geometry/quality.rs index 476ab0fd..638b677c 100644 --- a/src/geometry/quality.rs +++ b/src/geometry/quality.rs @@ -367,7 +367,7 @@ fn scale_aware_epsilon( /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.5, 0.866]?, // approximately sqrt(3)/2 /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -479,7 +479,7 @@ where /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -582,7 +582,7 @@ mod tests { use crate::triangulation::DelaunayTriangulation; use crate::vertex; use approx::assert_relative_eq; - use std::assert_matches; + use std::{array, assert_matches}; // sqrt(3) constant computed at compile time // const SQRT_3: f64 = 1.732_050_807_568_877_3; @@ -612,8 +612,8 @@ mod tests { #[test] fn $test_name() { let vertices = $vertices; - let dt: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::try_new(&vertices).unwrap(); -let simplex_key = dt.simplices().next().unwrap().0; + let dt: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::builder(&vertices).build().unwrap(); + let simplex_key = dt.simplices().next().unwrap().0; // Test radius_ratio let ratio = radius_ratio(dt.as_triangulation(), simplex_key).unwrap(); @@ -633,16 +633,16 @@ let simplex_key = dt.simplices().next().unwrap().0; fn [<$test_name _scale_invariance>]() { // Test scale invariance by scaling coordinates let vertices_base = $vertices; - let dt_base: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::try_new(&vertices_base).unwrap(); -let key_base = dt_base.simplices().next().unwrap().0; + let dt_base: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::builder(&vertices_base).build().unwrap(); + let key_base = dt_base.simplices().next().unwrap().0; - // Scale by 10x - let vertices_scaled: Vec<_> = vertices_base.iter().map(|v| { - let coords: [f64; $dim] = std::array::from_fn(|idx| v.point().coords()[idx] * 10.0); - vertex!(coords).unwrap() - }).collect(); - let dt_scaled: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::try_new(&vertices_scaled).unwrap(); -let key_scaled = dt_scaled.simplices().next().unwrap().0; + // Scale by 10x + let vertices_scaled: Vec<_> = vertices_base.iter().map(|v| { + let coords: [f64; $dim] = array::from_fn(|idx| v.point().coords()[idx] * 10.0); + vertex!(coords).unwrap() + }).collect(); + let dt_scaled: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::builder(&vertices_scaled).build().unwrap(); + let key_scaled = dt_scaled.simplices().next().unwrap().0; let ratio_base = radius_ratio(dt_base.as_triangulation(), key_base).unwrap(); let ratio_scaled = radius_ratio(dt_scaled.as_triangulation(), key_scaled).unwrap(); @@ -657,16 +657,16 @@ let key_scaled = dt_scaled.simplices().next().unwrap().0; fn [<$test_name _translation_invariance>]() { // Test translation invariance let vertices_base = $vertices; - let dt_base: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::try_new(&vertices_base).unwrap(); -let key_base = dt_base.simplices().next().unwrap().0; + let dt_base: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::builder(&vertices_base).build().unwrap(); + let key_base = dt_base.simplices().next().unwrap().0; - // Translate by [5.0, 5.0, ...] - let vertices_translated: Vec<_> = vertices_base.iter().map(|v| { - let coords: [f64; $dim] = std::array::from_fn(|idx| v.point().coords()[idx] + 5.0); - vertex!(coords).unwrap() - }).collect(); - let dt_translated: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::try_new(&vertices_translated).unwrap(); -let key_translated = dt_translated.simplices().next().unwrap().0; + // Translate by [5.0, 5.0, ...] + let vertices_translated: Vec<_> = vertices_base.iter().map(|v| { + let coords: [f64; $dim] = array::from_fn(|idx| v.point().coords()[idx] + 5.0); + vertex!(coords).unwrap() + }).collect(); + let dt_translated: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::builder(&vertices_translated).build().unwrap(); + let key_translated = dt_translated.simplices().next().unwrap().0; let ratio_base = radius_ratio(dt_base.as_triangulation(), key_base).unwrap(); let ratio_translated = radius_ratio(dt_translated.as_triangulation(), key_translated).unwrap(); @@ -770,7 +770,7 @@ let key_translated = dt_translated.simplices().next().unwrap().0; vertex!([2.0, 0.001]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Test radius_ratio @@ -927,7 +927,9 @@ let key_translated = dt_translated.simplices().next().unwrap().0; vertex!([0.5, 0.866_025]).unwrap(), ]; let dt_good: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_good).unwrap(); + DelaunayTriangulation::builder(&vertices_good) + .build() + .unwrap(); let simplex_key_good = dt_good.simplices().next().unwrap().0; // Poor quality triangle (very flat) @@ -937,7 +939,9 @@ let key_translated = dt_translated.simplices().next().unwrap().0; vertex!([0.5, 0.01]).unwrap(), // Nearly flat ]; let dt_poor: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_poor).unwrap(); + DelaunayTriangulation::builder(&vertices_poor) + .build() + .unwrap(); let simplex_key_poor = dt_poor.simplices().next().unwrap().0; let ratio_good = radius_ratio(dt_good.as_triangulation(), simplex_key_good).unwrap(); @@ -970,7 +974,7 @@ let key_translated = dt_translated.simplices().next().unwrap().0; let dt_result: Result< DelaunayTriangulation<_, (), (), 2>, DelaunayTriangulationConstructionError, - > = DelaunayTriangulation::try_new(&vertices); + > = DelaunayTriangulation::builder(&vertices).build(); // Construction may succeed with collinear points, but quality metrics // should detect the degeneracy @@ -997,7 +1001,7 @@ let key_translated = dt_translated.simplices().next().unwrap().0; vertex!([1.000_000_1, 0.000_000_1]).unwrap(), // Nearly duplicate ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Either should error or produce very poor quality @@ -1015,7 +1019,7 @@ let key_translated = dt_translated.simplices().next().unwrap().0; vertex!([1e-10, 1e10]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Should compute without panicking @@ -1044,7 +1048,7 @@ let key_translated = dt_translated.simplices().next().unwrap().0; vertex!([0.0, 0.0, 0.0, 0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 6> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let ratio = radius_ratio(dt.as_triangulation(), simplex_key).unwrap(); @@ -1066,7 +1070,7 @@ let key_translated = dt_translated.simplices().next().unwrap().0; #[test] fn $test_name() { let vertices = $vertices; - let dt: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::try_new(&vertices).unwrap(); + let dt: DelaunayTriangulation<_, (), (), $dim> = DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; if let Ok(ratio) = radius_ratio(dt.as_triangulation(), simplex_key) { @@ -1145,7 +1149,7 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.5, 0.866_025]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Create an invalid key (not in the SlotMap) let invalid_key = SimplexKey::from(KeyData::from_ffi(u64::MAX)); @@ -1202,7 +1206,9 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.5, 0.866_025]).unwrap(), ]; let dt_best: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_best).unwrap(); + DelaunayTriangulation::builder(&vertices_best) + .build() + .unwrap(); let key_best = dt_best.simplices().next().unwrap().0; // Medium: right triangle (acceptable quality) @@ -1212,7 +1218,9 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.0, 4.0]).unwrap(), ]; let dt_medium: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_medium).unwrap(); + DelaunayTriangulation::builder(&vertices_medium) + .build() + .unwrap(); let key_medium = dt_medium.simplices().next().unwrap().0; // Worst: very flat (poor quality) @@ -1222,7 +1230,9 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([5.0, 0.1]).unwrap(), ]; let dt_worst: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_worst).unwrap(); + DelaunayTriangulation::builder(&vertices_worst) + .build() + .unwrap(); let key_worst = dt_worst.simplices().next().unwrap().0; let ratio_best = radius_ratio(dt_best.as_triangulation(), key_best).unwrap(); @@ -1258,7 +1268,9 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.0, 1.0]).unwrap(), ]; let dt_right: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_right).unwrap(); + DelaunayTriangulation::builder(&vertices_right) + .build() + .unwrap(); let key_right = dt_right.simplices().next().unwrap().0; let ratio_right = radius_ratio(dt_right.as_triangulation(), key_right).unwrap(); assert_relative_eq!(ratio_right, 1.0 + 2.0_f64.sqrt(), epsilon = 0.1); @@ -1270,7 +1282,9 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([1.0, 2.0]).unwrap(), ]; let dt_iso: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices_iso).unwrap(); + DelaunayTriangulation::builder(&vertices_iso) + .build() + .unwrap(); let key_iso = dt_iso.simplices().next().unwrap().0; let ratio_iso = radius_ratio(dt_iso.as_triangulation(), key_iso).unwrap(); assert!(ratio_iso > 2.0 && ratio_iso < 5.0); @@ -1293,7 +1307,7 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.5, 0.866_025]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; let points = simplex_points(dt.as_triangulation(), simplex_key).unwrap(); @@ -1309,7 +1323,7 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.5, 0.866_025]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let invalid_key = SimplexKey::from(KeyData::from_ffi(u64::MAX)); let result = simplex_points(dt.as_triangulation(), invalid_key); @@ -1388,7 +1402,7 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.5, 0.866_025]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Normal case should succeed @@ -1406,7 +1420,7 @@ let simplex_key = dt.simplices().next().unwrap().0; vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let simplex_key = dt.simplices().next().unwrap().0; // Should succeed with correct count @@ -1425,7 +1439,7 @@ let simplex_key = dt.simplices().next().unwrap().0; let dt_result: Result< DelaunayTriangulation<_, (), (), 2>, DelaunayTriangulationConstructionError, - > = DelaunayTriangulation::try_new(&vertices); + > = DelaunayTriangulation::builder(&vertices).build(); match dt_result { Ok(dt) => { @@ -1464,7 +1478,7 @@ let simplex_key = dt.simplices().next().unwrap().0; let dt_result: Result< DelaunayTriangulation<_, (), (), 3>, DelaunayTriangulationConstructionError, - > = DelaunayTriangulation::try_new(&vertices); + > = DelaunayTriangulation::builder(&vertices).build(); match dt_result { Ok(dt) => { @@ -1557,7 +1571,7 @@ let simplex_key = dt.simplices().next().unwrap().0; let dt_result: Result< DelaunayTriangulation<_, (), (), 2>, DelaunayTriangulationConstructionError, - > = DelaunayTriangulation::try_new(&vertices); + > = DelaunayTriangulation::builder(&vertices).build(); match dt_result { Ok(dt) => { diff --git a/src/geometry/util/measures.rs b/src/geometry/util/measures.rs index d268793b..2a9cd690 100644 --- a/src/geometry/util/measures.rs +++ b/src/geometry/util/measures.rs @@ -803,7 +803,7 @@ fn facet_measure_gram_matrix( /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let tds = dt.tds(); /// /// // Get boundary facets as FacetViews @@ -1607,7 +1607,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let boundary_facets: Vec<_> = dt .tds() .one_sided_facets() @@ -1656,7 +1656,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let boundary_facets: Vec<_> = dt .tds() .one_sided_facets() @@ -1987,9 +1987,9 @@ mod tests { vertex!([0.0, 0.0, 1.0]).unwrap(), // v8 ]; let dt1: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices1).unwrap(); + DelaunayTriangulation::builder(&vertices1).build().unwrap(); let dt2: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices2).unwrap(); + DelaunayTriangulation::builder(&vertices2).build().unwrap(); let boundary_facets1: Vec<_> = dt1 .tds() @@ -2069,7 +2069,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let boundary_facets: Vec<_> = dt .tds() .one_sided_facets() @@ -2098,7 +2098,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 4> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let boundary_facets: Vec<_> = dt .tds() .one_sided_facets() @@ -2134,7 +2134,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let boundary_facets: Vec<_> = dt .tds() .one_sided_facets() @@ -2208,7 +2208,7 @@ mod tests { ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let boundary_facets: Vec<_> = dt .tds() .one_sided_facets() diff --git a/src/geometry/util/triangulation_generation.rs b/src/geometry/util/triangulation_generation.rs index 3010f4b7..e5887282 100644 --- a/src/geometry/util/triangulation_generation.rs +++ b/src/geometry/util/triangulation_generation.rs @@ -9,6 +9,7 @@ use super::point_generation::{ RandomPointGenerationError, generate_random_points_in_range, generate_random_points_in_range_seeded, }; +use crate::builder::DelaunayTriangulationBuilder; use crate::construction::{ ConstructionOptions, DelaunayConstructionFailure, DelaunayTriangulationConstructionError, InsertionOrderStrategy, RetryPolicy, @@ -25,12 +26,213 @@ use crate::triangulation::DelaunayTriangulation; use rand::SeedableRng; use rand::rngs::StdRng; use rand::seq::SliceRandom; -use std::num::NonZeroUsize; +use std::{marker::PhantomData, num::NonZeroUsize}; const RANDOM_TRIANGULATION_MAX_SHUFFLE_ATTEMPTS: usize = 6; const RANDOM_TRIANGULATION_MAX_POINTSET_ATTEMPTS: usize = 6; const RANDOM_TRIANGULATION_POINTSET_SEED_MIX: u64 = 0x9E37_79B9_7F4A_7C15; +/// Error returned when a random triangulation point count cannot support dimension `D`. +/// +/// A d-dimensional triangulation needs at least `D + 1` points before point +/// generation or Delaunay construction can even attempt an initial simplex. +/// +/// # Examples +/// +/// ```rust +/// use delaunay::prelude::generators::RandomPointCountError; +/// +/// let err = RandomPointCountError::InsufficientPoints { +/// actual: 2, +/// expected: 3, +/// dimension: 2, +/// }; +/// std::assert_matches!(err, RandomPointCountError::InsufficientPoints { .. }); +/// ``` +#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)] +#[non_exhaustive] +pub enum RandomPointCountError { + /// The requested count is positive but cannot form one full `D`-simplex. + #[error( + "random triangulation in dimension {dimension} requires at least {expected} points, got {actual}" + )] + InsufficientPoints { + /// Requested point count. + actual: usize, + /// Minimum point count required for dimension `D`. + expected: usize, + /// Spatial dimension. + dimension: usize, + }, +} + +impl From for DelaunayTriangulationConstructionError { + fn from(error: RandomPointCountError) -> Self { + match error { + RandomPointCountError::InsufficientPoints { + actual, + expected, + dimension, + } => TriangulationConstructionError::InsufficientVertices { + dimension, + source: SimplexValidationError::InsufficientVertices { + actual, + expected, + dimension, + }, + } + .into(), + } + } +} + +/// Positive random point count proven large enough to seed a `D`-dimensional triangulation. +/// +/// This type carries both non-zero-ness and the stronger `count >= D + 1` +/// invariant. Random triangulation builders store this proof-bearing count so +/// an unbuildable point count cannot survive past construction of the builder. +/// +/// # Examples +/// +/// ```rust +/// use delaunay::prelude::generators::{RandomPointCount, RandomPointCountError}; +/// use std::num::NonZeroUsize; +/// +/// # fn main() -> Result<(), RandomPointCountError> { +/// let Some(four) = NonZeroUsize::new(4) else { +/// return Ok(()); +/// }; +/// let count = RandomPointCount::<3>::try_new(four)?; +/// +/// assert_eq!(count.get(), 4); +/// assert_eq!(RandomPointCount::<3>::minimum(), 4); +/// # Ok(()) +/// # } +/// ``` +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[must_use] +pub struct RandomPointCount(NonZeroUsize); + +impl RandomPointCount { + /// Returns the minimum point count needed to construct one `D`-simplex. + #[must_use] + pub const fn minimum() -> usize { + D + 1 + } + + /// Parses a positive count into a dimension-sufficient random point count. + /// + /// # Errors + /// + /// Returns [`RandomPointCountError::InsufficientPoints`] when `count < D + 1`. + /// + /// # Examples + /// + /// ```rust + /// use delaunay::prelude::generators::{RandomPointCount, RandomPointCountError}; + /// use std::num::NonZeroUsize; + /// + /// # fn main() -> Result<(), RandomPointCountError> { + /// let Some(ten) = NonZeroUsize::new(10) else { + /// return Ok(()); + /// }; + /// let count = RandomPointCount::<5>::try_new(ten)?; + /// assert_eq!(count.get(), 10); + /// # Ok(()) + /// # } + /// ``` + pub const fn try_new(count: NonZeroUsize) -> Result { + let actual = count.get(); + let expected = Self::minimum(); + if actual < expected { + return Err(RandomPointCountError::InsufficientPoints { + actual, + expected, + dimension: D, + }); + } + Ok(Self(count)) + } + + /// Returns the validated point count as a raw `usize`. + #[must_use] + pub const fn get(self) -> usize { + self.0.get() + } + + /// Returns the validated point count as a `NonZeroUsize`. + #[must_use] + pub const fn as_nonzero(self) -> NonZeroUsize { + self.0 + } +} + +impl TryFrom for RandomPointCount { + type Error = RandomPointCountError; + + fn try_from(count: NonZeroUsize) -> Result { + Self::try_new(count) + } +} + +impl From> for NonZeroUsize { + fn from(count: RandomPointCount) -> Self { + count.as_nonzero() + } +} + +/// Error returned while constructing a [`RandomTriangulationBuilder`]. +/// +/// Raw builder inputs are parsed before storage: point counts must become +/// [`RandomPointCount`], and raw tuple bounds must become [`CoordinateRange`]. +/// +/// # Examples +/// +/// ```rust +/// use delaunay::prelude::generators::{ +/// RandomTriangulationBuilder, RandomTriangulationBuilderError, +/// }; +/// use std::num::NonZeroUsize; +/// +/// let Some(two) = NonZeroUsize::new(2) else { +/// return; +/// }; +/// let Err(err) = RandomTriangulationBuilder::<2>::try_new(two, (-1.0, 1.0)) else { +/// return; +/// }; +/// std::assert_matches!(err, RandomTriangulationBuilderError::PointCount { .. }); +/// ``` +#[derive(Clone, Debug, PartialEq, thiserror::Error)] +#[non_exhaustive] +pub enum RandomTriangulationBuilderError { + /// Requested point count cannot seed the requested dimension. + #[error("{source}")] + PointCount { + /// Point-count validation failure. + #[from] + source: RandomPointCountError, + }, + + /// Raw coordinate bounds are not a finite, strictly increasing range. + #[error("{source}")] + CoordinateRange { + /// Coordinate-range validation failure. + #[from] + source: CoordinateRangeError, + }, +} + +impl From for DelaunayTriangulationConstructionError { + fn from(error: RandomTriangulationBuilderError) -> Self { + match error { + RandomTriangulationBuilderError::PointCount { source } => source.into(), + RandomTriangulationBuilderError::CoordinateRange { source } => { + random_point_generation_error(source.into()) + } + } + } +} + /// Wraps random point generation failures in the construction error hierarchy. /// /// Keeping this mapping centralized preserves the public contract that invalid @@ -95,12 +297,7 @@ where fn random_triangulation_is_acceptable( dt: &DelaunayTriangulation, min_vertices: usize, -) -> bool -where - K: Kernel, - U: DataType, - V: DataType, -{ +) -> bool { dt.number_of_vertices() >= min_vertices } @@ -126,12 +323,11 @@ where .with_insertion_order(InsertionOrderStrategy::Input) .with_retry_policy(RetryPolicy::Disabled); - let dt = DelaunayTriangulation::try_with_topology_guarantee_and_options( - kernel, - vertices, - topology_guarantee, - options, - )?; + let dt = DelaunayTriangulationBuilder::new(vertices) + .simplex_data_type::() + .topology_guarantee(topology_guarantee) + .construction_options(options) + .build_with_kernel(kernel)?; let dt = validate_random_triangulation(dt)?; Ok(random_triangulation_is_acceptable(&dt, min_vertices).then_some(dt)) @@ -149,16 +345,10 @@ fn random_triangulation_build_vertices( where U: Copy, { - match vertex_data { - Some(data) => points - .into_iter() - .map(|point| Vertex::from_validated_point(point, Some(data))) - .collect(), - None => points - .into_iter() - .map(|point| Vertex::from_validated_point(point, None)) - .collect(), - } + points + .into_iter() + .map(|point| Vertex::from_validated_point(point, vertex_data)) + .collect() } /// Creates an [`AdaptiveKernel`] for triangulation construction. @@ -285,12 +475,12 @@ where /// /// # Examples /// -/// ```no_run -/// use delaunay::prelude::construction::DelaunayTriangulationConstructionError; +/// ``` +/// use delaunay::prelude::construction::DelaunayResult; /// use delaunay::prelude::generators::try_generate_random_triangulation; /// use std::num::NonZeroUsize; /// -/// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { +/// # fn main() -> DelaunayResult<()> { /// # let Some(fifty) = NonZeroUsize::new(50) else { /// # return Ok(()); /// # }; @@ -359,7 +549,7 @@ where /// /// - [`try_generate_random_points`](crate::geometry::util::try_generate_random_points) - For generating points without triangulation from raw bounds /// - [`try_generate_random_points_seeded`](crate::geometry::util::try_generate_random_points_seeded) - For seeded random point generation from raw bounds -/// - [`DelaunayTriangulationBuilder`](crate::DelaunayTriangulationBuilder) - For creating triangulations from existing vertices +/// - [`DelaunayTriangulationBuilder`] - For creating triangulations from existing vertices /// - [`RandomTriangulationBuilder`] - For more control over construction options pub fn try_generate_random_triangulation( n_points: NonZeroUsize, @@ -413,13 +603,13 @@ where /// /// # Examples /// -/// ```no_run -/// use delaunay::prelude::construction::DelaunayTriangulationConstructionError; +/// ``` +/// use delaunay::prelude::construction::DelaunayResult; /// use delaunay::prelude::generators::try_generate_random_triangulation_with_topology_guarantee; /// use delaunay::prelude::TopologyGuarantee; /// use std::num::NonZeroUsize; /// -/// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { +/// # fn main() -> DelaunayResult<()> { /// # let Some(twenty) = NonZeroUsize::new(20) else { /// # return Ok(()); /// # }; @@ -487,9 +677,10 @@ where /// /// # Examples /// -/// ```no_run +/// ``` /// use delaunay::prelude::construction::{ -/// DelaunayConstructionFailure, DelaunayTriangulationConstructionError, +/// DelaunayConstructionFailure, DelaunayResult, +/// DelaunayTriangulationConstructionError, /// }; /// use delaunay::prelude::generators::{ /// CoordinateRange, CoordinateRangeError, generate_random_triangulation_in_range, @@ -499,7 +690,7 @@ where /// # fn make_range() -> Result, CoordinateRangeError> { /// # CoordinateRange::try_new(-1.0_f64, 1.0) /// # } -/// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { +/// # fn main() -> DelaunayResult<()> { /// # let Some(twelve) = NonZeroUsize::new(12) else { /// # return Ok(()); /// # }; @@ -566,9 +757,10 @@ where /// /// # Examples /// -/// ```no_run +/// ``` /// use delaunay::prelude::construction::{ -/// DelaunayConstructionFailure, DelaunayTriangulationConstructionError, +/// DelaunayConstructionFailure, DelaunayResult, +/// DelaunayTriangulationConstructionError, /// }; /// use delaunay::prelude::generators::{ /// CoordinateRange, CoordinateRangeError, @@ -580,7 +772,7 @@ where /// # fn make_range() -> Result, CoordinateRangeError> { /// # CoordinateRange::try_new(-1.0_f64, 1.0) /// # } -/// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { +/// # fn main() -> DelaunayResult<()> { /// # let Some(twelve) = NonZeroUsize::new(12) else { /// # return Ok(()); /// # }; @@ -614,19 +806,7 @@ where U: DataType, V: DataType, { - let n_points = n_points.get(); - - if n_points < D + 1 { - return Err(TriangulationConstructionError::InsufficientVertices { - dimension: D, - source: SimplexValidationError::InsufficientVertices { - actual: n_points, - expected: D + 1, - dimension: D, - }, - } - .into()); - } + let n_points = RandomPointCount::::try_new(n_points)?.get(); let points: Vec> = random_points_with_seed(n_points, bounds, seed).map_err(random_point_generation_error)?; @@ -699,26 +879,17 @@ where /// /// # Examples /// -/// ```no_run +/// ``` /// use delaunay::prelude::construction::{ -/// DelaunayConstructionFailure, DelaunayTriangulationConstructionError, +/// DelaunayResult, DelaunayTriangulation, /// }; /// use delaunay::prelude::generators::{ -/// CoordinateRangeError, InsertionOrderStrategy, RandomTriangulationBuilder, +/// InsertionOrderStrategy, RandomTriangulationBuilder, /// }; /// use delaunay::prelude::TopologyGuarantee; /// use std::num::NonZeroUsize; /// -/// # fn random_generation_error( -/// # source: CoordinateRangeError, -/// # ) -> DelaunayTriangulationConstructionError { -/// # DelaunayTriangulationConstructionError::Triangulation( -/// # DelaunayConstructionFailure::RandomPointGeneration { -/// # source: source.into(), -/// # }, -/// # ) -/// # } -/// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { +/// # fn main() -> DelaunayResult<()> { /// # let Some(twenty) = NonZeroUsize::new(20) else { /// # return Ok(()); /// # }; @@ -726,43 +897,46 @@ where /// # return Ok(()); /// # }; /// // Override the default `Hilbert` ordering with `Input` ordering. -/// let dt = RandomTriangulationBuilder::try_new(twenty, (-3.0, 3.0)) -/// .map_err(random_generation_error)? -/// .seed(666) -/// .insertion_order(InsertionOrderStrategy::Input) -/// .build::<(), (), 3>()?; +/// let dt: DelaunayTriangulation<_, (), (), 3> = +/// RandomTriangulationBuilder::try_new(twenty, (-3.0, 3.0))? +/// .seed(666) +/// .insertion_order(InsertionOrderStrategy::Input) +/// .build()?; /// /// // Build with PLManifold guarantee -/// let dt_manifold = RandomTriangulationBuilder::try_new(one_hundred, (-10.0, 10.0)) -/// .map_err(random_generation_error)? -/// .seed(777) -/// .topology_guarantee(TopologyGuarantee::PLManifold) -/// .build::<(), (), 4>()?; +/// let dt_manifold: DelaunayTriangulation<_, (), (), 4> = +/// RandomTriangulationBuilder::try_new(one_hundred, (-10.0, 10.0))? +/// .seed(777) +/// .topology_guarantee(TopologyGuarantee::PLManifold) +/// .build()?; /// # Ok(()) /// # } /// ``` #[must_use] -pub struct RandomTriangulationBuilder { - n_points: NonZeroUsize, +pub struct RandomTriangulationBuilder { + n_points: RandomPointCount, bounds: CoordinateRange, seed: Option, topology_guarantee: TopologyGuarantee, construction_options: ConstructionOptions, + vertex_data: Option, + _simplex_data: PhantomData, } -impl RandomTriangulationBuilder { +impl RandomTriangulationBuilder { /// Creates a new builder with the specified number of points and raw coordinate bounds. /// /// # Arguments /// - /// * `n_points` - Non-zero number of random points to generate + /// * `n_points` - Non-zero point count to parse as [`RandomPointCount`] /// * `bounds` - Coordinate bounds as `(min, max)` tuple /// /// # Errors /// - /// Returns [`CoordinateRangeError::NonFiniteBound`] if either bound is - /// non-finite, or [`CoordinateRangeError::NonIncreasing`] if the bounds are - /// equal, decreasing, or incomparable. + /// Returns [`RandomTriangulationBuilderError::PointCount`] when the point + /// count is less than `D + 1`, or + /// [`RandomTriangulationBuilderError::CoordinateRange`] when either bound is + /// non-finite, equal to the other bound, decreasing, or incomparable. /// /// # Defaults /// @@ -776,17 +950,16 @@ impl RandomTriangulationBuilder { /// /// # Examples /// - /// ```no_run - /// use delaunay::prelude::generators::{ - /// CoordinateRangeError, RandomTriangulationBuilder, - /// }; + /// ``` + /// use delaunay::prelude::construction::DelaunayResult; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; /// use std::num::NonZeroUsize; /// - /// # fn main() -> Result<(), CoordinateRangeError> { + /// # fn main() -> DelaunayResult<()> { /// # let Some(ten) = NonZeroUsize::new(10) else { /// # return Ok(()); /// # }; - /// let builder = RandomTriangulationBuilder::try_new(ten, (-1.0, 1.0))?.seed(42); + /// let builder = RandomTriangulationBuilder::<2>::try_new(ten, (-1.0, 1.0))?.seed(42); /// let _ = builder; /// # Ok(()) /// # } @@ -794,70 +967,84 @@ impl RandomTriangulationBuilder { pub fn try_new( n_points: NonZeroUsize, bounds: (f64, f64), - ) -> Result> { + ) -> Result { Ok(Self { - n_points, + n_points: RandomPointCount::try_new(n_points)?, bounds: CoordinateRange::try_from(bounds)?, seed: None, topology_guarantee: TopologyGuarantee::DEFAULT, construction_options: ConstructionOptions::default(), + vertex_data: None, + _simplex_data: PhantomData, }) } /// Creates a new builder from already-validated coordinate bounds. /// /// Use this constructor when the caller has already parsed raw tuple bounds - /// into a [`CoordinateRange`]. It is infallible because `bounds` already - /// carries the finite, strictly-increasing range invariant. + /// into a [`CoordinateRange`] and raw point counts into [`RandomPointCount`]. + /// It is infallible because both inputs already carry their invariants. /// /// # Arguments /// - /// * `n_points` - Non-zero number of random points to generate. + /// * `n_points` - Validated point count for dimension `D`. /// * `bounds` - Validated coordinate bounds shared by every coordinate axis. /// /// # Examples /// - /// ```no_run + /// ``` /// use delaunay::prelude::generators::{ - /// CoordinateRange, CoordinateRangeError, RandomTriangulationBuilder, + /// CoordinateRange, CoordinateRangeError, RandomPointCount, RandomPointCountError, + /// RandomTriangulationBuilder, /// }; /// use std::num::NonZeroUsize; /// - /// # fn main() -> Result<(), CoordinateRangeError> { + /// # #[derive(Debug, thiserror::Error)] + /// # enum ExampleError { + /// # #[error(transparent)] + /// # Count(#[from] RandomPointCountError), + /// # #[error(transparent)] + /// # Range(#[from] CoordinateRangeError), + /// # } + /// # fn main() -> Result<(), ExampleError> { /// # let Some(ten) = NonZeroUsize::new(10) else { /// # return Ok(()); /// # }; /// let range = CoordinateRange::try_new(-1.0_f64, 1.0)?; - /// let builder = RandomTriangulationBuilder::new_in_range(ten, range).seed(42); + /// let count = RandomPointCount::<2>::try_new(ten)?; + /// let builder = RandomTriangulationBuilder::<2>::new_in_range(count, range).seed(42); /// let _ = builder; /// # Ok(()) /// # } /// ``` - pub fn new_in_range(n_points: NonZeroUsize, bounds: CoordinateRange) -> Self { + pub fn new_in_range(n_points: RandomPointCount, bounds: CoordinateRange) -> Self { Self { n_points, bounds, seed: None, topology_guarantee: TopologyGuarantee::DEFAULT, construction_options: ConstructionOptions::default(), + vertex_data: None, + _simplex_data: PhantomData, } } +} +impl RandomTriangulationBuilder { /// Sets the random seed for reproducible triangulation generation. /// /// # Examples /// - /// ```no_run - /// use delaunay::prelude::generators::{ - /// CoordinateRangeError, RandomTriangulationBuilder, - /// }; + /// ``` + /// use delaunay::prelude::construction::DelaunayResult; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; /// use std::num::NonZeroUsize; /// - /// # fn main() -> Result<(), CoordinateRangeError> { + /// # fn main() -> DelaunayResult<()> { /// # let Some(ten) = NonZeroUsize::new(10) else { /// # return Ok(()); /// # }; - /// let builder = RandomTriangulationBuilder::try_new(ten, (-1.0, 1.0))? + /// let builder = RandomTriangulationBuilder::<2>::try_new(ten, (-1.0, 1.0))? /// .seed(42); /// let _ = builder; /// # Ok(()) @@ -874,18 +1061,17 @@ impl RandomTriangulationBuilder { /// /// # Examples /// - /// ```no_run - /// use delaunay::prelude::generators::{ - /// CoordinateRangeError, RandomTriangulationBuilder, - /// }; + /// ``` + /// use delaunay::prelude::construction::DelaunayResult; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; /// use delaunay::prelude::TopologyGuarantee; /// use std::num::NonZeroUsize; /// - /// # fn main() -> Result<(), CoordinateRangeError> { + /// # fn main() -> DelaunayResult<()> { /// # let Some(ten) = NonZeroUsize::new(10) else { /// # return Ok(()); /// # }; - /// let builder = RandomTriangulationBuilder::try_new(ten, (-1.0, 1.0))? + /// let builder = RandomTriangulationBuilder::<2>::try_new(ten, (-1.0, 1.0))? /// .topology_guarantee(TopologyGuarantee::Pseudomanifold); /// let _ = builder; /// # Ok(()) @@ -900,34 +1086,25 @@ impl RandomTriangulationBuilder { /// /// # Examples /// - /// ```no_run + /// ``` /// use delaunay::prelude::construction::{ - /// DelaunayConstructionFailure, DelaunayTriangulationConstructionError, + /// DelaunayResult, DelaunayTriangulation, /// }; /// use delaunay::prelude::generators::{ - /// CoordinateRangeError, InsertionOrderStrategy, RandomTriangulationBuilder, + /// InsertionOrderStrategy, RandomTriangulationBuilder, /// }; /// use std::num::NonZeroUsize; /// - /// # fn random_generation_error( - /// # source: CoordinateRangeError, - /// # ) -> DelaunayTriangulationConstructionError { - /// # DelaunayTriangulationConstructionError::Triangulation( - /// # DelaunayConstructionFailure::RandomPointGeneration { - /// # source: source.into(), - /// # }, - /// # ) - /// # } - /// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { + /// # fn main() -> DelaunayResult<()> { /// # let Some(twenty) = NonZeroUsize::new(20) else { /// # return Ok(()); /// # }; /// // Override the default `Hilbert` ordering with `Input` ordering. - /// let dt = RandomTriangulationBuilder::try_new(twenty, (-3.0, 3.0)) - /// .map_err(random_generation_error)? - /// .seed(666) - /// .insertion_order(InsertionOrderStrategy::Input) - /// .build::<(), (), 3>()?; + /// let dt: DelaunayTriangulation<_, (), (), 3> = + /// RandomTriangulationBuilder::try_new(twenty, (-3.0, 3.0))? + /// .seed(666) + /// .insertion_order(InsertionOrderStrategy::Input) + /// .build()?; /// # Ok(()) /// # } /// ``` @@ -942,18 +1119,16 @@ impl RandomTriangulationBuilder { /// /// # Examples /// - /// ```no_run - /// use delaunay::prelude::construction::ConstructionOptions; - /// use delaunay::prelude::generators::{ - /// CoordinateRangeError, RandomTriangulationBuilder, - /// }; + /// ``` + /// use delaunay::prelude::construction::{ConstructionOptions, DelaunayResult}; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; /// use std::num::NonZeroUsize; /// - /// # fn main() -> Result<(), CoordinateRangeError> { + /// # fn main() -> DelaunayResult<()> { /// # let Some(ten) = NonZeroUsize::new(10) else { /// # return Ok(()); /// # }; - /// let builder = RandomTriangulationBuilder::try_new(ten, (-1.0, 1.0))? + /// let builder = RandomTriangulationBuilder::<2>::try_new(ten, (-1.0, 1.0))? /// .construction_options(ConstructionOptions::default()); /// let _ = builder; /// # Ok(()) @@ -963,118 +1138,192 @@ impl RandomTriangulationBuilder { self.construction_options = options; self } - /// Builds the random triangulation with the configured options. - /// - /// The builder stores a [`CoordinateRange`] after [`Self::try_new`] or - /// [`Self::new_in_range`] has run, so this method consumes validated bounds - /// and does not perform raw tuple range parsing. + + /// Attaches the same vertex payload to every generated vertex. /// - /// # Type Parameters + /// This is a type-state setter: it changes the builder's vertex storage + /// type from `U` to `W` before vertices are allocated. Use it when generated + /// random vertices should all carry the same persisted payload. /// - /// * `U` - Vertex data type (must implement `DataType`) - /// * `V` - Simplex data type (must implement `DataType`) - /// * `D` - Dimensionality (const generic parameter) + /// # Examples /// - /// # Errors + /// ``` + /// use delaunay::prelude::construction::{ + /// DelaunayResult, DelaunayTriangulation, + /// }; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; + /// use std::num::NonZeroUsize; /// - /// Returns `Err` if: - /// - Insufficient vertices for the requested dimension (`n_points < D + 1`) - /// - Triangulation construction fails (geometric degeneracy, etc.) - /// - Construction fails after the configured retry policy (no robust-kernel fallback here) + /// # fn main() -> DelaunayResult<()> { + /// # let Some(twelve) = NonZeroUsize::new(12) else { + /// # return Ok(()); + /// # }; + /// let dt: DelaunayTriangulation<_, u32, (), 3> = + /// RandomTriangulationBuilder::try_new(twelve, (-2.0, 2.0))? + /// .seed(7) + /// .vertex_data(42_u32) + /// .build()?; + /// assert_eq!(dt.dim(), 3); + /// # Ok(()) + /// # } + /// ``` + pub fn vertex_data(self, data: W) -> RandomTriangulationBuilder { + let Self { + n_points, + bounds, + seed, + topology_guarantee, + construction_options, + vertex_data: _, + _simplex_data: _, + } = self; + RandomTriangulationBuilder { + n_points, + bounds, + seed, + topology_guarantee, + construction_options, + vertex_data: Some(data), + _simplex_data: PhantomData, + } + } + + /// Selects the generated vertex payload type without assigning payload values. /// - /// Invalid coordinate bounds cannot occur here because builder construction - /// has already parsed them into a [`CoordinateRange`]. + /// Generated vertices are created with `None` payloads. This is useful when + /// callers want typed vertex storage up front and plan to fill vertex data + /// later from a secondary map or from geometry-dependent computation. /// /// # Examples /// - /// ```no_run + /// ``` /// use delaunay::prelude::construction::{ - /// DelaunayConstructionFailure, DelaunayTriangulationConstructionError, + /// DelaunayResult, DelaunayTriangulation, /// }; - /// use delaunay::prelude::generators::{CoordinateRangeError, RandomTriangulationBuilder}; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; /// use std::num::NonZeroUsize; /// - /// # fn random_generation_error( - /// # source: CoordinateRangeError, - /// # ) -> DelaunayTriangulationConstructionError { - /// # DelaunayTriangulationConstructionError::Triangulation( - /// # DelaunayConstructionFailure::RandomPointGeneration { - /// # source: source.into(), - /// # }, - /// # ) + /// # fn main() -> DelaunayResult<()> { + /// # let Some(twelve) = NonZeroUsize::new(12) else { + /// # return Ok(()); + /// # }; + /// let dt: DelaunayTriangulation<_, u32, (), 2> = + /// RandomTriangulationBuilder::try_new(twelve, (-1.0, 1.0))? + /// .vertex_data_type::() + /// .build()?; + /// assert_eq!(dt.dim(), 2); + /// # Ok(()) /// # } - /// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { + /// ``` + pub fn vertex_data_type(self) -> RandomTriangulationBuilder { + let Self { + n_points, + bounds, + seed, + topology_guarantee, + construction_options, + vertex_data: _, + _simplex_data: _, + } = self; + RandomTriangulationBuilder { + n_points, + bounds, + seed, + topology_guarantee, + construction_options, + vertex_data: None, + _simplex_data: PhantomData, + } + } + + /// Selects the simplex payload type before topology storage is allocated. + /// + /// This does not compute simplex payload values. It only chooses the + /// persisted simplex storage type so callers can fill values after + /// construction without rebuilding topology or remapping simplex keys. + /// + /// # Examples + /// + /// ``` + /// use delaunay::prelude::construction::{ + /// DelaunayResult, DelaunayTriangulation, + /// }; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; + /// use std::num::NonZeroUsize; + /// + /// # fn main() -> DelaunayResult<()> { /// # let Some(twelve) = NonZeroUsize::new(12) else { /// # return Ok(()); /// # }; - /// let dt = RandomTriangulationBuilder::try_new(twelve, (-2.0, 2.0)) - /// .map_err(random_generation_error)? - /// .seed(7) - /// .build::<(), (), 3>()?; - /// assert_eq!(dt.dim(), 3); + /// let mut dt: DelaunayTriangulation<_, (), usize, 2> = + /// RandomTriangulationBuilder::try_new(twelve, (-1.0, 1.0))? + /// .simplex_data_type::() + /// .build()?; + /// dt.fill_simplex_data(|_, simplex| simplex.number_of_vertices()); + /// assert_eq!(dt.dim(), 2); /// # Ok(()) /// # } /// ``` - pub fn build( - self, - ) -> Result< - DelaunayTriangulation, U, V, D>, - DelaunayTriangulationConstructionError, - > - where - U: DataType, - V: DataType, - { - self.build_with_vertex_data(None) + pub fn simplex_data_type(self) -> RandomTriangulationBuilder { + let Self { + n_points, + bounds, + seed, + topology_guarantee, + construction_options, + vertex_data, + _simplex_data: _, + } = self; + RandomTriangulationBuilder { + n_points, + bounds, + seed, + topology_guarantee, + construction_options, + vertex_data, + _simplex_data: PhantomData, + } } - /// Builds the random triangulation with vertex data attached to each vertex. + /// Builds the random triangulation with the configured options. /// /// The builder stores a [`CoordinateRange`] after [`Self::try_new`] or /// [`Self::new_in_range`] has run, so this method consumes validated bounds /// and does not perform raw tuple range parsing. /// - /// # Arguments - /// - /// * `vertex_data` - Optional data to attach to each generated vertex - /// /// # Errors /// - /// Returns `Err` if construction fails (see [`build`](Self::build) for details). + /// Returns `Err` if: + /// - Triangulation construction fails (geometric degeneracy, etc.) + /// - Construction fails after the configured retry policy (no robust-kernel fallback here) + /// + /// Invalid coordinate bounds and dimension-insufficient point counts cannot + /// occur here because builder construction has already parsed them into a + /// [`CoordinateRange`] and [`RandomPointCount`]. /// /// # Examples /// - /// ```no_run + /// ``` /// use delaunay::prelude::construction::{ - /// DelaunayConstructionFailure, DelaunayTriangulationConstructionError, + /// DelaunayResult, DelaunayTriangulation, /// }; - /// use delaunay::prelude::generators::{CoordinateRangeError, RandomTriangulationBuilder}; + /// use delaunay::prelude::generators::RandomTriangulationBuilder; /// use std::num::NonZeroUsize; /// - /// # fn random_generation_error( - /// # source: CoordinateRangeError, - /// # ) -> DelaunayTriangulationConstructionError { - /// # DelaunayTriangulationConstructionError::Triangulation( - /// # DelaunayConstructionFailure::RandomPointGeneration { - /// # source: source.into(), - /// # }, - /// # ) - /// # } - /// # fn main() -> Result<(), DelaunayTriangulationConstructionError> { + /// # fn main() -> DelaunayResult<()> { /// # let Some(twelve) = NonZeroUsize::new(12) else { /// # return Ok(()); /// # }; - /// let dt = RandomTriangulationBuilder::try_new(twelve, (-2.0, 2.0)) - /// .map_err(random_generation_error)? - /// .seed(7) - /// .build_with_vertex_data::<(), (), 3>(None)?; + /// let dt: DelaunayTriangulation<_, (), (), 3> = + /// RandomTriangulationBuilder::try_new(twelve, (-2.0, 2.0))? + /// .seed(7) + /// .build()?; /// assert_eq!(dt.dim(), 3); /// # Ok(()) /// # } /// ``` - pub fn build_with_vertex_data( + pub fn build( self, - vertex_data: Option, ) -> Result< DelaunayTriangulation, U, V, D>, DelaunayTriangulationConstructionError, @@ -1085,23 +1334,11 @@ impl RandomTriangulationBuilder { { let n_points = self.n_points.get(); - if n_points < D + 1 { - return Err(TriangulationConstructionError::InsufficientVertices { - dimension: D, - source: SimplexValidationError::InsufficientVertices { - actual: n_points, - expected: D + 1, - dimension: D, - }, - } - .into()); - } - let points: Vec> = random_points_with_seed(n_points, self.bounds, self.seed) .map_err(random_point_generation_error)?; // Convert to vertices - let vertices = random_triangulation_build_vertices(points, vertex_data); + let vertices = random_triangulation_build_vertices(points, self.vertex_data); // Build triangulation with configured options #[cfg(debug_assertions)] @@ -1112,15 +1349,15 @@ impl RandomTriangulationBuilder { insertion_order = ?self.construction_options.insertion_order(), dedup_policy = ?self.construction_options.dedup_policy(), retry_policy = ?self.construction_options.retry_policy(), - "random_triangulation_builder: single call to with_topology_guarantee_and_options" + "random_triangulation_builder: single call through DelaunayTriangulationBuilder" ); } - let dt = DelaunayTriangulation::try_with_topology_guarantee_and_options( - &make_adaptive_kernel(), - &vertices, - self.topology_guarantee, - self.construction_options, - )?; + let kernel = make_adaptive_kernel(); + let dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .topology_guarantee(self.topology_guarantee) + .construction_options(self.construction_options) + .build_with_kernel(&kernel)?; validate_random_triangulation(dt) } } @@ -1140,6 +1377,78 @@ mod tests { NonZeroUsize::new(value).expect("test point count must be non-zero") } + #[test] + fn test_random_point_count_conversions_preserve_validated_nonzero_count() { + let count = RandomPointCount::<3>::try_new(nonzero(4)).unwrap(); + assert_eq!(count.as_nonzero(), nonzero(4)); + + let parsed = RandomPointCount::<3>::try_from(nonzero(5)).unwrap(); + let raw: NonZeroUsize = parsed.into(); + assert_eq!(raw, nonzero(5)); + + let err = RandomPointCount::<3>::try_from(nonzero(3)).unwrap_err(); + assert_eq!( + err, + RandomPointCountError::InsufficientPoints { + actual: 3, + expected: 4, + dimension: 3, + } + ); + } + + #[test] + fn test_random_triangulation_builder_error_coordinate_range_maps_to_construction_error() { + let Err(err) = RandomTriangulationBuilder::<2>::try_new(nonzero(10), (1.0, 0.0)) else { + panic!("expected invalid bounds to fail"); + }; + let err = DelaunayTriangulationConstructionError::from(err); + + let DelaunayTriangulationConstructionError::Triangulation( + DelaunayConstructionFailure::RandomPointGeneration { source }, + ) = err + else { + panic!("expected coordinate-range builder error to map to random-point generation"); + }; + assert_eq!( + source, + RandomPointGenerationError::InvalidCoordinateRange { + source: CoordinateRangeError::NonIncreasing { + ordering: CoordinateRangeOrdering::Decreasing, + min: 1.0, + max: 0.0, + }, + } + ); + } + + #[test] + fn test_random_triangulation_builder_type_state_selectors_build_typed_storage() { + let mut triangulation: DelaunayTriangulation<_, u32, usize, 2> = + RandomTriangulationBuilder::try_new(nonzero(12), (-2.0, 2.0)) + .unwrap() + .seed(44) + .vertex_data_type::() + .simplex_data_type::() + .build() + .unwrap(); + + assert!(triangulation.number_of_simplices() > 0); + assert!( + triangulation + .tds() + .vertices() + .all(|(_, vertex)| vertex.data().is_none()) + ); + + triangulation.fill_simplex_data(|_, simplex| simplex.number_of_vertices()); + + for (_, simplex) in triangulation.simplices() { + assert_eq!(simplex.data(), Some(&3)); + } + assert!(triangulation.validate().is_ok()); + } + // ============================================================================= // RANDOM TRIANGULATION GENERATION TESTS // ============================================================================= @@ -1260,8 +1569,9 @@ mod tests { )) if bound == CoordinateRangeBound::Minimum && value == InvalidCoordinateValue::Nan ); - let Err(CoordinateRangeError::NonFiniteBound { bound, value }) = - RandomTriangulationBuilder::try_new(nonzero(10), (0.0, f64::INFINITY)) + let Err(RandomTriangulationBuilderError::CoordinateRange { + source: CoordinateRangeError::NonFiniteBound { bound, value }, + }) = RandomTriangulationBuilder::<2>::try_new(nonzero(10), (0.0, f64::INFINITY)) else { panic!("expected invalid infinite bounds to fail"); }; @@ -1279,10 +1589,12 @@ mod tests { assert_eq!(triangulation.dim(), 2); triangulation.is_valid_delaunay().unwrap(); - let builder_triangulation = RandomTriangulationBuilder::new_in_range(nonzero(10), range) - .seed(43) - .build::<(), (), 2>() - .unwrap(); + let count = RandomPointCount::<2>::try_new(nonzero(10)).unwrap(); + let builder_triangulation: DelaunayTriangulation<_, (), (), 2> = + RandomTriangulationBuilder::new_in_range(count, range) + .seed(43) + .build() + .unwrap(); assert_eq!(builder_triangulation.dim(), 2); builder_triangulation.is_valid_delaunay().unwrap(); @@ -1304,20 +1616,23 @@ mod tests { #[test] fn test_random_triangulation_builder_success_and_error_paths() { - let triangulation = RandomTriangulationBuilder::try_new(nonzero(10), (-5.0, 5.0)) - .unwrap() - .seed(42) - .build::<(), (), 2>() - .unwrap(); + let triangulation: DelaunayTriangulation<_, (), (), 2> = + RandomTriangulationBuilder::try_new(nonzero(10), (-5.0, 5.0)) + .unwrap() + .seed(42) + .build() + .unwrap(); assert_eq!(triangulation.dim(), 2); assert!(triangulation.number_of_vertices() >= 3); triangulation.is_valid_delaunay().unwrap(); - let triangulation_with_data = RandomTriangulationBuilder::try_new(nonzero(10), (-5.0, 5.0)) - .unwrap() - .seed(43) - .build_with_vertex_data::(Some(7)) - .unwrap(); + let triangulation_with_data: DelaunayTriangulation<_, u32, (), 2> = + RandomTriangulationBuilder::try_new(nonzero(10), (-5.0, 5.0)) + .unwrap() + .seed(43) + .vertex_data(7_u32) + .build() + .unwrap(); let vertex_data: Vec<_> = triangulation_with_data .tds() .vertices() @@ -1330,35 +1645,35 @@ mod tests { assert!(vertex_data.iter().all(|&data| data == 7)); triangulation_with_data.is_valid_delaunay().unwrap(); - let too_few_vertices = RandomTriangulationBuilder::try_new(nonzero(2), (-1.0, 1.0)) - .unwrap() - .build::<(), (), 2>(); - let Err(DelaunayTriangulationConstructionError::Triangulation( - DelaunayConstructionFailure::InsufficientVertices { dimension, source }, - )) = too_few_vertices - else { - panic!("expected InsufficientVertices error"); + let too_few_vertices = RandomTriangulationBuilder::<2>::try_new(nonzero(2), (-1.0, 1.0)); + let Err(RandomTriangulationBuilderError::PointCount { source }) = too_few_vertices else { + panic!("expected builder point-count error"); }; - assert_eq!(dimension, 2); assert_eq!( source, - SimplexValidationError::InsufficientVertices { + RandomPointCountError::InsufficientPoints { actual: 2, expected: 3, dimension: 2, } ); - let invalid_bounds = RandomTriangulationBuilder::try_new(nonzero(10), (5.0, 1.0)); - let Err(CoordinateRangeError::NonIncreasing { ordering, min, max }) = invalid_bounds else { + let invalid_bounds = RandomTriangulationBuilder::<2>::try_new(nonzero(10), (5.0, 1.0)); + let Err(RandomTriangulationBuilderError::CoordinateRange { + source: CoordinateRangeError::NonIncreasing { ordering, min, max }, + }) = invalid_bounds + else { panic!("expected invalid bounds to fail"); }; assert_eq!(ordering, CoordinateRangeOrdering::Decreasing); assert_relative_eq!(min, 5.0, epsilon = f64::EPSILON); assert_relative_eq!(max, 1.0, epsilon = f64::EPSILON); - let equal_bounds = RandomTriangulationBuilder::try_new(nonzero(10), (2.0, 2.0)); - let Err(CoordinateRangeError::NonIncreasing { ordering, min, max }) = equal_bounds else { + let equal_bounds = RandomTriangulationBuilder::<2>::try_new(nonzero(10), (2.0, 2.0)); + let Err(RandomTriangulationBuilderError::CoordinateRange { + source: CoordinateRangeError::NonIncreasing { ordering, min, max }, + }) = equal_bounds + else { panic!("expected equal bounds to fail"); }; assert_eq!(ordering, CoordinateRangeOrdering::Equal); diff --git a/src/io/visualization.rs b/src/io/visualization.rs index 316553c1..ae9f7660 100644 --- a/src/io/visualization.rs +++ b/src/io/visualization.rs @@ -236,7 +236,7 @@ impl<'de> Deserialize<'de> for VisualizationTopologyGuarantee { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; -/// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let export = triangulation.to_visualization_data()?; /// let json = serde_json::to_string(&export)?; @@ -321,7 +321,7 @@ impl()?; + /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let export = triangulation.to_mesh_export()?; /// /// export.validate()?; @@ -928,7 +928,7 @@ impl DelaunayTriangulation { /// vertex![0.0, 1.0, 0.0]?, /// vertex![0.0, 0.0, 1.0]?, /// ]; - /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let export = triangulation.to_visualization_data()?; /// @@ -1032,7 +1032,7 @@ impl DelaunayTriangulation { /// vertex![0.0, 1.0, 0.0]?, /// vertex![0.0, 0.0, 1.0]?, /// ]; - /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let export = triangulation.to_mesh_export()?; /// diff --git a/src/lib.rs b/src/lib.rs index 62961630..358631ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,7 @@ //! vertex![0.0, 1.0, 0.0]?, //! vertex![0.0, 0.0, 1.0]?, //! ]; -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! // Levels 1–2: elements + structural (TDS) //! assert!(dt.tds().validate().is_ok()); @@ -146,7 +146,7 @@ //! vertex![0.0, 1.0, 0.0]?, //! vertex![0.0, 0.0, 1.0]?, //! ]; -//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); //! assert_eq!(dt.validation_policy(), ValidationPolicy::ExplicitOnly); @@ -174,7 +174,7 @@ //! vertex![1.0, 0.0]?, //! vertex![0.0, 1.0]?, //! ]; -//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! let before_vertices = dt.number_of_vertices(); //! let before_simplices = dt.number_of_simplices(); @@ -307,7 +307,7 @@ //! vertex![0.0, 1.0, 0.0]?, //! vertex![0.0, 0.0, 1.0]?, //! ]; -//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! // Caller-owned validation mode: keep mandatory topology checks, but run full //! // Level 3 validation only through explicit validation calls. @@ -357,7 +357,7 @@ //! vertex![0.0, 1.0, 0.0]?, //! vertex![0.0, 0.0, 1.0]?, //! ]; -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! // For `TopologyGuarantee::PLManifold`, full certification includes a completion-time //! // vertex-link validation pass. @@ -378,7 +378,7 @@ //! vertex![0.0, 1.0, 0.0]?, //! vertex![0.0, 0.0, 1.0]?, //! ]; -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! // `validate()` returns the first violation; `validation_report()` is intended for //! // debugging/telemetry where you want the full set of violated invariants. @@ -836,6 +836,7 @@ pub use crate::delaunay_property_validation::{ DelaunayValidationError, DelaunayViolationDetail, DelaunayViolationReport, delaunay_violation_report, find_delaunay_violations, }; +pub use crate::delaunay_query::SimplexDataFillError; pub use crate::deletion::DeleteVertexError; pub use crate::io::visualization::{ AdjacencyRecord, MESH_EXPORT_SCHEMA, MESH_EXPORT_SCHEMA_VERSION, MeshAdjacencyRecord, @@ -944,7 +945,7 @@ pub fn try_vertices_from_points( /// vertex![0.0, 1.0, 0.0]?, /// vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology())?; /// assert_eq!(result.chi, 1); // Tetrahedron has χ = 1 @@ -1124,10 +1125,12 @@ pub mod algorithms { }; } -/// Public read-only traversal, adjacency, convex-hull, and set-comparison APIs. +/// Public traversal, adjacency, convex-hull, set-comparison, and query support APIs. /// /// This module is intended for callers who need to inspect a triangulation or /// compare derived topology without importing construction and repair surfaces. +/// It also re-exports query-adjacent support types such as +/// [`SimplexDataFillError`] for follow-on simplex payload assignment. /// /// # Examples /// @@ -1146,6 +1149,7 @@ pub mod algorithms { /// # } /// ``` pub mod query { + pub use crate::SimplexDataFillError; pub use crate::assert_jaccard_gte; pub use crate::core::query::QueryError; pub use crate::core::traits::data_type::{ @@ -1181,7 +1185,7 @@ pub mod prelude { // Re-export the public low-level facades. pub use crate::query::{ DataCopy, DataDebug, DataDeserialize, DataIdentity, DataSerde, DataSerialize, DataType, - FacetIncidenceAnalysis, QueryError, + FacetIncidenceAnalysis, QueryError, SimplexDataFillError, }; pub use crate::tds::*; pub use crate::vertex; @@ -1308,7 +1312,7 @@ pub mod prelude { /// vertex![0.0, 1.0]?, /// ]; /// let triangulation = DelaunayTriangulationBuilder::new(&vertices) - /// .build::<()>()?; + /// .build()?; /// /// assert_eq!(triangulation.number_of_vertices(), 3); /// # Ok(()) @@ -1446,7 +1450,7 @@ pub mod prelude { /// ]; /// let mut dt = DelaunayTriangulationBuilder::new(&vertices) /// .topology_guarantee(TopologyGuarantee::PLManifold) - /// .build::<()>()?; + /// .build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1484,7 +1488,7 @@ pub mod prelude { /// vertex![0.0, 1.0, 0.0]?, /// vertex![0.0, 0.0, 1.0]?, /// ]; - /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let mut dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let Some((simplex_key, _)) = dt.simplices().next() else { /// return Ok(()); /// }; @@ -1837,7 +1841,7 @@ pub mod prelude { /// vertex![1.0, 0.0]?, /// vertex![0.0, 1.0]?, /// ]; - /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let export = triangulation.to_mesh_export()?; /// /// assert_eq!(export.metadata.schema, MESH_EXPORT_SCHEMA); @@ -1906,7 +1910,8 @@ pub mod prelude { pub use crate::query::{ AllFacetsIter, BoundaryFacetsIter, DataCopy, DataDebug, DataDeserialize, DataIdentity, DataSerde, DataSerialize, DataType, FacetIncidenceAnalysis, FacetView, - OneSidedFacetsIter, QueryError, Simplex, SimplexFacetsIter, Vertex, + OneSidedFacetsIter, QueryError, Simplex, SimplexDataFillError, SimplexFacetsIter, + Vertex, }; // Read-only predicates (useful in benchmarks / lightweight geometry checks) @@ -1958,8 +1963,10 @@ pub mod prelude { InvalidCoordinateValue, }; pub use crate::geometry::util::{ - InvalidPositiveScalar, RandomPointGenerationError, RandomTriangulationBuilder, - generate_grid_points, generate_poisson_points_in_range, generate_random_points_in_ball, + InvalidPositiveScalar, RandomPointCount, RandomPointCountError, + RandomPointGenerationError, RandomTriangulationBuilder, + RandomTriangulationBuilderError, generate_grid_points, + generate_poisson_points_in_range, generate_random_points_in_ball, generate_random_points_in_ball_seeded, generate_random_points_in_range, generate_random_points_in_range_seeded, generate_random_points_periodic, generate_random_triangulation_in_range, @@ -2185,7 +2192,9 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let dt: RepairDelaunayTriangulation<_, (), (), 2> = - RepairDelaunayTriangulation::try_new(&vertices).unwrap(); + RepairDelaunayTriangulation::builder(&vertices) + .build() + .unwrap(); let kernel = AdaptiveKernel::::new(); assert!(verify_delaunay_for_triangulation(dt.as_triangulation()).is_ok()); @@ -2228,7 +2237,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Get a simplex to test quality functions let (simplex_key, _) = dt.simplices().next().unwrap(); @@ -2306,7 +2315,7 @@ mod tests { vertex![0.0, 0.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert_eq!(dt.number_of_simplices(), 1); @@ -2332,7 +2341,7 @@ mod tests { vertex![0.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // Test locate function with kernel let kernel = FastKernel::::new(); @@ -2390,7 +2399,7 @@ mod tests { vertex![0.0, 0.0, 1.0].unwrap(), ]; let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // ConvexHull type should be accessible let hull = ConvexHull::try_from_triangulation(dt.as_triangulation()).unwrap(); diff --git a/src/topology/characteristics/euler.rs b/src/topology/characteristics/euler.rs index 82ff4b4b..841bccc6 100644 --- a/src/topology/characteristics/euler.rs +++ b/src/topology/characteristics/euler.rs @@ -25,7 +25,7 @@ //! delaunay::vertex![0.0, 1.0, 0.0]?, //! delaunay::vertex![0.0, 0.0, 1.0]?, //! ]; -//! let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +//! let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; //! //! let counts = euler::count_simplices(dt.tds())?; //! let chi = euler::euler_characteristic(&counts); @@ -224,7 +224,7 @@ pub enum TopologyClassification { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.5, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let counts = euler::count_simplices(dt.tds())?; /// assert_eq!(counts.count(0), 3); // 3 vertices @@ -477,7 +477,7 @@ fn insert_simplices_of_size( /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let boundary_counts = euler::count_boundary_simplices(dt.tds(), dt.global_topology())?; /// let boundary_chi = euler::euler_characteristic(&boundary_counts); @@ -768,7 +768,7 @@ pub(crate) fn triangulated_surface_boundary_component_count( /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let classification = classify_triangulation(dt.tds(), dt.global_topology())?; /// assert_eq!(classification, TopologyClassification::SingleSimplex(3)); diff --git a/src/topology/characteristics/validation.rs b/src/topology/characteristics/validation.rs index 216cb490..77381b73 100644 --- a/src/topology/characteristics/validation.rs +++ b/src/topology/characteristics/validation.rs @@ -42,7 +42,7 @@ use crate::topology::{ /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology())?; /// assert_eq!(result.chi, 1); @@ -138,7 +138,7 @@ impl TopologyCheckResult { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.5, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology())?; /// assert_eq!(result.chi, 1); diff --git a/src/topology/manifold.rs b/src/topology/manifold.rs index d9809006..33a72791 100644 --- a/src/topology/manifold.rs +++ b/src/topology/manifold.rs @@ -340,7 +340,7 @@ pub enum BoundaryFacetClassification { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.5, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// let facet_index = dt.tds().build_facet_to_simplices_index()?; /// /// let Some(incidence) = facet_index.iter().find(|incidence| incidence.is_one_sided()) diff --git a/src/topology/ridge.rs b/src/topology/ridge.rs index 81a2f490..2c93380d 100644 --- a/src/topology/ridge.rs +++ b/src/topology/ridge.rs @@ -97,7 +97,7 @@ pub enum RidgeCandidateError { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // In 2D, a ridge is a vertex because it has arity D - 1. /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; @@ -150,7 +150,7 @@ impl RidgeCandidate { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<3>::try_from_vertices(dt.tds().vertex_keys().take(2))?; /// assert_eq!(ridge.as_slice().len(), 2); @@ -212,7 +212,7 @@ impl RidgeCandidate { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<3>::try_from_vertices(dt.tds().vertex_keys().take(2))?; /// assert_eq!(ridge.as_slice().len(), 2); @@ -250,7 +250,7 @@ impl RidgeCandidate { /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<3>::try_from_vertices(dt.tds().vertex_keys().take(2))?; /// assert_eq!(ridge.iter().count(), ridge.as_slice().len()); @@ -295,7 +295,7 @@ impl RidgeCandidate { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let query = ridge.query(dt.tds())?; @@ -346,7 +346,7 @@ impl RidgeCandidate { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let view = ridge.view(dt.tds())?; @@ -413,7 +413,7 @@ impl<'tds, U, V, const D: usize> RidgeQuery<'tds, U, V, D> { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let query = RidgeQuery::try_new(dt.tds(), ridge)?; @@ -506,7 +506,7 @@ impl<'tds, U, V, const D: usize> RidgeQuery<'tds, U, V, D> { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let query = ridge.query(dt.tds())?; @@ -599,7 +599,7 @@ impl<'tds, U, V, const D: usize> RidgeView<'tds, U, V, D> { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let view = RidgeView::try_new(dt.tds(), ridge)?; @@ -696,7 +696,7 @@ impl<'tds, U, V, const D: usize> RidgeView<'tds, U, V, D> { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; - /// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + /// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let view = ridge.view(dt.tds())?; @@ -777,7 +777,7 @@ impl Eq for RidgeView<'_, U, V, D> {} /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let view = ridge.view(dt.tds())?; @@ -1049,7 +1049,7 @@ pub(crate) fn simplex_star_simplices( /// delaunay::vertex![0.0, 1.0, 0.0]?, /// delaunay::vertex![0.0, 0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// // In 3D, a ridge is an edge because it has arity D - 1. /// let ridge = RidgeCandidate::<3>::try_from_vertices(dt.tds().vertex_keys().take(2))?; diff --git a/src/topology/spaces/toroidal.rs b/src/topology/spaces/toroidal.rs index b28247f5..887ec58a 100644 --- a/src/topology/spaces/toroidal.rs +++ b/src/topology/spaces/toroidal.rs @@ -63,7 +63,7 @@ use std::{ /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let view = ridge.view(dt.tds())?; @@ -157,7 +157,7 @@ impl LiftedVertexId { /// delaunay::vertex![1.0, 0.0]?, /// delaunay::vertex![0.0, 1.0]?, /// ]; -/// let dt = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; +/// let dt = DelaunayTriangulationBuilder::new(&vertices).build()?; /// /// let ridge = RidgeCandidate::<2>::try_from_vertices(dt.tds().vertex_keys().take(1))?; /// let view = ridge.view(dt.tds())?; diff --git a/tests/dedup_batch_construction.rs b/tests/dedup_batch_construction.rs index 7b794169..d4344ae9 100644 --- a/tests/dedup_batch_construction.rs +++ b/tests/dedup_batch_construction.rs @@ -138,7 +138,9 @@ macro_rules! gen_dedup_batch_tests { let opts = exact_dedup_options(); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new_with_options(&vertices, opts) + DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() .expect(concat!( stringify!($dim), "D construction with duplicates should succeed" )); @@ -166,7 +168,7 @@ macro_rules! gen_dedup_batch_tests { let vertices = all_identical_vertices::<$dim>($dim + 2); let result: Result, _> = - DelaunayTriangulation::try_new(&vertices); + DelaunayTriangulation::builder(&vertices).build(); assert!( result @@ -184,7 +186,9 @@ macro_rules! gen_dedup_batch_tests { let vertices = all_identical_vertices::<$dim>($dim + 2); let result: Result, _> = - DelaunayTriangulation::try_new_with_options(&vertices, exact_dedup_options()); + DelaunayTriangulation::builder(&vertices) + .construction_options(exact_dedup_options()) + .build(); assert!( matches!( @@ -208,7 +212,9 @@ macro_rules! gen_dedup_batch_tests { .with_dedup_policy(DedupPolicy::Exact); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new_with_options(&vertices, opts) + DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() .expect(concat!( stringify!($dim), "D: DedupPolicy::Exact + Input should succeed" @@ -243,7 +249,9 @@ macro_rules! gen_dedup_batch_tests { assert_eq!(vertices.len(), distinct_count * 5); let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new_with_options(&vertices, exact_dedup_options()) + DelaunayTriangulation::builder(&vertices) + .construction_options(exact_dedup_options()) + .build() .expect(concat!( stringify!($dim), "D: many-duplicate construction should succeed" @@ -281,9 +289,10 @@ fn test_hilbert_dedup_quantized_collision_2d() { vertices.push(vertex!([0.5 + 1e-10, 0.5]).unwrap()); // quantizes to same simplex let total = vertices.len(); - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_options(&vertices, exact_dedup_options()) - .expect("2D construction with quantized-collision should succeed"); + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .construction_options(exact_dedup_options()) + .build() + .expect("2D construction with quantized-collision should succeed"); // Hilbert dedup should collapse the near-coincident pair. assert_eq!( diff --git a/tests/delaunay_edge_cases.rs b/tests/delaunay_edge_cases.rs index 3e276b5d..76445ae2 100644 --- a/tests/delaunay_edge_cases.rs +++ b/tests/delaunay_edge_cases.rs @@ -10,8 +10,8 @@ use delaunay::construction::DelaunayConstructionRetryFailure; use delaunay::prelude::construction::{ - DelaunayConstructionFailure, DelaunayTriangulation, DelaunayTriangulationConstructionError, - TopologyGuarantee, Vertex, + DelaunayConstructionFailure, DelaunayTriangulation, DelaunayTriangulationBuilder, + DelaunayTriangulationConstructionError, TopologyGuarantee, Vertex, }; #[cfg(feature = "diagnostics")] use delaunay::prelude::diagnostics::debug_print_first_delaunay_violation; @@ -154,10 +154,7 @@ macro_rules! test_regression_config { let vertices = $vertices; let dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() .unwrap_or_else(|err| { panic!( "{}D regression configuration failed to construct: {err}", @@ -260,11 +257,9 @@ fn debug_issue_120_empty_circumsphere_5d() { .unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 5> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 5> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap_or_else(|err| panic!("5D debug configuration failed to construct: {err}")); match dt.repair_delaunay_with_flips() { Ok(stats) => { @@ -306,10 +301,10 @@ fn debug_issue_120_empty_circumsphere_5d() { for attempt in 0..20 { let mut shuffled = vertices.clone(); shuffled.shuffle(&mut rng); - if let Ok(dt_alt) = DelaunayTriangulation::<_, (), (), 5>::try_new_with_topology_guarantee( - &shuffled, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt_alt) = DelaunayTriangulation::builder(&shuffled) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { if dt_alt.is_valid_delaunay().is_ok() { test_debug_info!( "[Issue #120 debug] found valid triangulation after shuffle attempt {}", @@ -448,11 +443,9 @@ fn test_regression_proptest_insertion_order_4d_euler_mismatch() { .unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 4> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 4> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap_or_else(|err| panic!("4D regression configuration failed to construct: {err}")); assert!( @@ -667,11 +660,9 @@ fn test_exact_minimum_vertices_2d() { vertex!([0.0, 1.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 3); @@ -688,11 +679,9 @@ fn test_exact_minimum_vertices_3d() { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); @@ -710,11 +699,9 @@ fn test_exact_minimum_vertices_4d() { vertex!([0.0, 0.0, 0.0, 1.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 4> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 4> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 5); @@ -736,11 +723,9 @@ fn test_multiple_interior_points_2d() { vertex!([2.5, 1.5]).unwrap(), // interior ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 6); @@ -759,11 +744,9 @@ fn test_multiple_interior_points_3d() { vertex!([0.5, 0.6, 0.5]).unwrap(), // interior ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 7); @@ -786,11 +769,9 @@ fn test_square_with_center_2d() { vertex!([1.0, 1.0]).unwrap(), // center ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 5); @@ -811,11 +792,10 @@ fn test_cube_vertices_3d() { vertex!([1.0, 1.0, 1.0]).unwrap(), ]; - let err = DelaunayTriangulation::<_, (), (), 3>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .expect_err("exact cube corners should fail before storing a zero-volume simplex"); + let err = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .expect_err("exact cube corners should fail before storing a zero-volume simplex"); assert!( construction_error_is_degenerate_simplex(&err), @@ -836,11 +816,9 @@ fn test_large_coordinates_2d() { vertex!([1500.0, 1500.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); @@ -857,11 +835,9 @@ fn test_small_coordinates_3d() { vertex!([0.0015, 0.0015, 0.0015]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 5); @@ -877,11 +853,9 @@ fn test_negative_coordinates_2d() { vertex!([0.0, 0.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); @@ -903,12 +877,10 @@ fn test_robust_kernel_with_edge_case() { ]; let dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_topology_guarantee( - &RobustKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build_with_kernel(&RobustKernel::new()) + .unwrap(); assert_eq!(dt.number_of_vertices(), 4); assert!(dt.number_of_simplices() > 0); @@ -931,10 +903,9 @@ fn test_collinear_points_2d() { ]; let result: Result, _> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &collinear, - TopologyGuarantee::PLManifold, - ); + DelaunayTriangulation::builder(&collinear) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build(); // Verify it fails with GeometricDegeneracy due to collinear simplex. // The default retry policy may wrap the final typed degeneracy once all @@ -972,11 +943,9 @@ fn regression_issue_228_exact_predicate_paths_3d_fast() { .map(|p| vertex!(p.into()).unwrap()) .collect(); - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::Pseudomanifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .build() .expect("3D 16-point construction must not fail (#228 fast regression)"); assert!( @@ -1003,11 +972,9 @@ fn test_5d_simplex_plus_interior() { vertex!([0.2, 0.2, 0.2, 0.2, 0.2]).unwrap(), // interior ]; - let dt: DelaunayTriangulation<_, (), (), 5> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 5> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 7); diff --git a/tests/delaunay_incremental_insertion.rs b/tests/delaunay_incremental_insertion.rs index 8f45bc43..77b362e1 100644 --- a/tests/delaunay_incremental_insertion.rs +++ b/tests/delaunay_incremental_insertion.rs @@ -14,7 +14,8 @@ use delaunay::geometry::kernel::RobustKernel; use delaunay::prelude::algorithms::{LocateResult, find_conflict_region, locate}; use delaunay::prelude::collections::MAX_PRACTICAL_DIMENSION_SIZE; use delaunay::prelude::construction::{ - ConstructionOptions, DedupPolicy, DelaunayTriangulation, TopologyGuarantee, Vertex, vertex, + ConstructionOptions, DedupPolicy, DelaunayTriangulation, DelaunayTriangulationBuilder, + TopologyGuarantee, Vertex, vertex, }; use delaunay::prelude::geometry::{AdaptiveKernel, Point}; use delaunay::prelude::tds::{ @@ -141,11 +142,10 @@ macro_rules! test_insert_single_point { ]; let mut dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let initial_vertices = vertices.len(); assert_eq!(dt.number_of_simplices(), 1); @@ -209,11 +209,10 @@ macro_rules! test_insert_5_points { ]; let mut dt: DelaunayTriangulation<_, (), (), $dim> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let initial_vertices = vertices.len(); assert_eq!(dt.number_of_simplices(), 1); @@ -302,12 +301,10 @@ macro_rules! test_local_neighbor_repair_guardrails { $(vertex!($simplex).unwrap()),+ ]; let mut dt: DelaunayTriangulation, (), (), $dim> = - DelaunayTriangulation::try_with_topology_guarantee( - &AdaptiveKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); for point in [$(vertex!($point).unwrap()),+] { dt.insert_vertex(point).unwrap(); @@ -399,20 +396,16 @@ fn test_adaptive_kernel_vs_robust_kernel_2d() { ]; let dt_adaptive: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_topology_guarantee( - &AdaptiveKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let dt_robust: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_topology_guarantee( - &RobustKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build_with_kernel(&RobustKernel::new()) + .unwrap(); // Both should produce same vertex count assert_eq!( @@ -435,12 +428,10 @@ fn test_robust_kernel_incremental_insertion() { ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_topology_guarantee( - &RobustKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build_with_kernel(&RobustKernel::new()) + .unwrap(); // Insert points with robust kernel dt.insert_vertex(vertex![0.3, 0.3].unwrap()).unwrap(); @@ -463,11 +454,9 @@ fn test_clustered_points_2d() { vertex![5.0, 10.0].unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); // Insert 5 points clustered around (3.0, 3.0) @@ -489,11 +478,9 @@ fn test_grid_pattern_2d() { vertex![1.5, 3.0].unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); // Insert 4 points in a grid @@ -523,11 +510,10 @@ fn test_batch_vs_incremental_same_vertex_count() { // Batch construction let dt_batch: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &all_vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulation::builder(&all_vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); // Incremental construction let initial = vec![ @@ -536,11 +522,10 @@ fn test_batch_vs_incremental_same_vertex_count() { vertex![2.0, 4.0].unwrap(), ]; let mut dt_incremental: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &initial, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulation::builder(&initial) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); dt_incremental .insert_vertex(vertex![1.0, 1.0].unwrap()) @@ -578,8 +563,10 @@ fn test_bulk_construction_skips_near_duplicate_3d() { let opts = ConstructionOptions::default().with_dedup_policy(DedupPolicy::try_epsilon(1e-10).unwrap()); - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_options(&vertices, opts).unwrap(); + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() + .unwrap(); // The near-duplicate should be skipped, so only 5 unique vertices remain. assert_eq!(dt.number_of_vertices(), 5); @@ -597,11 +584,9 @@ fn test_insert_at_centroid() { vertex![1.5, 3.0].unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); // Insert point at approximate centroid @@ -621,11 +606,9 @@ fn test_minimal_simplex_then_insert() { vertex![0.0, 0.0, 1.0].unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); assert_eq!(dt.number_of_vertices(), 4); @@ -652,12 +635,10 @@ fn test_f64_coordinates() { ]; let mut dt: DelaunayTriangulation, (), (), 2> = - DelaunayTriangulation::try_with_topology_guarantee( - &AdaptiveKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); dt.insert_vertex(vertex![0.3, 0.3].unwrap()).unwrap(); diff --git a/tests/delaunay_repair_fallback.rs b/tests/delaunay_repair_fallback.rs index e2b86023..d37a3087 100644 --- a/tests/delaunay_repair_fallback.rs +++ b/tests/delaunay_repair_fallback.rs @@ -52,11 +52,9 @@ fn repair_fallback_produces_valid_triangulation() { vertex!([4.0, 2.0]).unwrap(), vertex!([1.0, 2.0]).unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .expect("fixture construction should succeed"); let mut candidate_facets = Vec::new(); @@ -172,11 +170,9 @@ fn repair_fallback_2d() { vertex!([1.0, 3.0]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .expect("2D construction should succeed with fallback if needed"); dt.validate() @@ -199,11 +195,9 @@ fn explicit_repair_call_validates_result() { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .expect("Construction should succeed"); // Call explicit repair (should be a no-op if already valid, or fix any issues) diff --git a/tests/delaunayize_workflow.rs b/tests/delaunayize_workflow.rs index d7ccc0a4..60969b83 100644 --- a/tests/delaunayize_workflow.rs +++ b/tests/delaunayize_workflow.rs @@ -10,7 +10,7 @@ use delaunay::flips::BistellarFlips; use delaunay::flips::FacetHandle; -use delaunay::prelude::construction::TriangulationConstructionError; +use delaunay::prelude::construction::{DelaunayTriangulation, TriangulationConstructionError}; use delaunay::prelude::delaunayize::*; use delaunay::vertex; use std::{error::Error, mem::size_of}; @@ -53,7 +53,7 @@ fn test_non_delaunay_pl_manifold_repaired_2d() { vertex!([2.0, 2.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); // The triangulation is already Delaunay. delaunayize should be a no-op. let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); @@ -75,7 +75,7 @@ fn test_non_delaunay_pl_manifold_repaired_3d() { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); assert!(outcome.topology_repair.succeeded); @@ -97,7 +97,7 @@ fn test_fallback_off_does_not_rebuild() { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let config = DelaunayizeConfig { fallback_rebuild: false, @@ -117,7 +117,7 @@ fn test_fallback_on_does_not_trigger_on_valid() { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let config = DelaunayizeConfig { fallback_rebuild: true, @@ -144,7 +144,7 @@ fn test_outcome_stats_populated_3d() { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); @@ -174,11 +174,11 @@ fn test_repeat_run_determinism_2d() { let config = DelaunayizeConfig::default(); let mut dt1: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome1 = delaunayize_by_flips(&mut dt1, config).unwrap(); let mut dt2: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome2 = delaunayize_by_flips(&mut dt2, config).unwrap(); // Stats should be identical across runs on the same input. @@ -215,11 +215,11 @@ fn test_repeat_run_determinism_3d() { let config = DelaunayizeConfig::default(); let mut dt1: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome1 = delaunayize_by_flips(&mut dt1, config).unwrap(); let mut dt2: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let outcome2 = delaunayize_by_flips(&mut dt2, config).unwrap(); assert_eq!( @@ -247,7 +247,7 @@ fn test_vertex_count_preserved_after_delaunayize() { vertex!([1.0, 1.0, 1.0]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let vertex_count_before = dt.number_of_vertices(); let _outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); @@ -275,7 +275,7 @@ fn test_flip_breaks_delaunay_then_delaunayize_restores() { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert!(dt.validate().is_ok(), "Should start valid"); // Collect candidate interior facets (immutable borrow ends before mutation). @@ -483,7 +483,7 @@ fn test_delaunayize_with_explicit_flip_budget_3d() { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let config = DelaunayizeConfig { delaunay_max_flips: Some(1000), @@ -508,7 +508,7 @@ fn test_delaunayize_with_flip_budget_and_fallback_2d() { vertex!([0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let config = DelaunayizeConfig { delaunay_max_flips: Some(500), @@ -535,7 +535,7 @@ fn test_flip_breaks_then_delaunayize_with_budget_restores_3d() { vertex!([0.5, 0.5, 0.5]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); assert!(dt.validate().is_ok()); // Collect candidate interior facets. @@ -590,7 +590,7 @@ fn test_full_validation_passes_after_delaunayize() { vertex!([0.25, 0.75]).unwrap(), ]; let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new(&vertices).unwrap(); + DelaunayTriangulation::builder(&vertices).build().unwrap(); let _outcome = delaunayize_by_flips(&mut dt, DelaunayizeConfig::default()).unwrap(); diff --git a/tests/euler_characteristic.rs b/tests/euler_characteristic.rs index 3994928a..44215292 100644 --- a/tests/euler_characteristic.rs +++ b/tests/euler_characteristic.rs @@ -63,11 +63,10 @@ fn test_2d_single_triangle() { vertex!([0.5, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology()).unwrap(); assert_eq!(result.counts.count(0), 3, "Should have 3 vertices"); @@ -89,11 +88,10 @@ fn test_euler_rejects_open_single_simplex_in_closed_topology() { vertex!([0.5, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let classify_err = euler::classify_triangulation(dt.tds(), GlobalTopology::Spherical).unwrap_err(); @@ -134,11 +132,10 @@ fn test_2d_multiple_triangles() { vertex!([0.5, 0.3]).unwrap(), // Interior point ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology()).unwrap(); assert_eq!(result.counts.count(0), 4, "Should have 4 vertices"); @@ -163,11 +160,10 @@ fn test_3d_single_tetrahedron() { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology()).unwrap(); assert_eq!(result.counts.count(0), 4, "Should have 4 vertices"); @@ -193,11 +189,10 @@ fn test_3d_with_interior_vertex() { vertex!([0.25, 0.25, 0.25]).unwrap(), // Interior point ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology()).unwrap(); assert_eq!(result.counts.count(0), 5, "Should have 5 vertices"); @@ -224,11 +219,10 @@ fn test_4d_single_simplex() { vertex!([0.0, 0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology()).unwrap(); assert_eq!(result.counts.count(0), 5, "Should have 5 vertices"); @@ -256,11 +250,10 @@ fn test_5d_single_simplex() { vertex!([0.0, 0.0, 0.0, 0.0, 1.0]).unwrap(), ]; - let dt = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .unwrap(); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology()).unwrap(); assert_eq!(result.counts.count(0), 6, "Should have 6 vertices"); @@ -318,7 +311,7 @@ fn test_2d_toroidal_explicit_construction_rejected() { .unwrap() .global_topology(topology) .topology_guarantee(TopologyGuarantee::Pseudomanifold) - .build::<()>() + .build() .expect_err("explicit toroidal connectivity requires a quotient embedding validator"); match err { @@ -400,7 +393,7 @@ fn test_3d_toroidal_explicit_construction_rejected() { .unwrap() .global_topology(topology) .topology_guarantee(TopologyGuarantee::Pseudomanifold) - .build::<()>() + .build() .expect_err("explicit toroidal connectivity requires a quotient embedding validator"); match err { @@ -427,9 +420,11 @@ macro_rules! test_complex_with_interior { ($test_name:ident, $dim:expr, $vertices:expr, $expected_boundary_chi:expr) => { #[test] fn $test_name() { - type DT = DelaunayTriangulation, (), (), $dim>; - let dt = DT::try_new_with_topology_guarantee($vertices, TopologyGuarantee::PLManifold) - .unwrap(); + let dt: DelaunayTriangulation, (), (), $dim> = + DelaunayTriangulation::builder($vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .unwrap(); // Full complex should have χ = 1 (D-ball) let full_result = diff --git a/tests/example_workflows.rs b/tests/example_workflows.rs index bdce8559..8fc10de5 100644 --- a/tests/example_workflows.rs +++ b/tests/example_workflows.rs @@ -24,7 +24,8 @@ fn triangulation_and_hull_workflow_remains_valid() -> Result<(), WorkflowTestErr vertex!([0.20, 0.60, 0.25])?, ]; - let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::try_new(&vertices)?; + let dt: DelaunayTriangulation<_, (), (), 3> = + DelaunayTriangulation::builder(&vertices).build()?; dt.validate()?; let edge_index = dt.build_edge_index()?; diff --git a/tests/large_scale_debug.rs b/tests/large_scale_debug.rs index eb21eee5..9b71b71e 100644 --- a/tests/large_scale_debug.rs +++ b/tests/large_scale_debug.rs @@ -52,7 +52,7 @@ //! # Box half-width (default: 100) [used when distribution=box] //! DELAUNAY_LARGE_DEBUG_BOX_HALF_WIDTH=100 \ //! # Construction mode: -//! # - "new" (default): build via DelaunayTriangulation::try_new() which applies Hilbert ordering +//! # - "new" (default): build via DelaunayTriangulation::builder(...).build() which applies Hilbert ordering //! # - "incremental": manual insert loop (debug/profiling) //! DELAUNAY_LARGE_DEBUG_CONSTRUCTION_MODE=new \ //! # Initial simplex strategy for batch construction: "max-volume" (default), "balanced", or "first" @@ -103,8 +103,8 @@ use delaunay::geometry::kernel::{ExactPredicates, Kernel, RobustKernel}; use delaunay::geometry::util::safe_usize_to_scalar; use delaunay::prelude::construction::{ ConstructionOptions, ConstructionStatistics, DelaunayRepairPolicy, DelaunayTriangulation, - DelaunayTriangulationConstructionErrorWithStatistics, InitialSimplexStrategy, - TopologyGuarantee, Vertex, + DelaunayTriangulationBuilder, DelaunayTriangulationConstructionErrorWithStatistics, + InitialSimplexStrategy, TopologyGuarantee, Vertex, }; use delaunay::prelude::diagnostics::ConstructionTelemetry; use delaunay::prelude::generators::{ @@ -415,7 +415,7 @@ enum PointDistribution { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ConstructionMode { - /// Build via `DelaunayTriangulation::try_new()` (batch construction + Hilbert ordering). + /// Build via `DelaunayTriangulation::builder(...).build()` (batch construction + Hilbert ordering). New, /// Insert vertices one by one (manual incremental construction). Incremental, @@ -1332,7 +1332,7 @@ where let mut dt: DelaunayTriangulation<_, (), (), D> = match mode { ConstructionMode::New => { - // `DelaunayTriangulation::try_new()` applies Hilbert ordering during batch construction. + // `DelaunayTriangulation::builder(...).build()` applies Hilbert ordering during batch construction. // Use the statistics-returning variant so we can report aggregate insertion telemetry. let kernel = RobustKernel::::new(); println!("Starting batch construction (new)..."); @@ -1340,12 +1340,11 @@ where let options = ConstructionOptions::default() .with_initial_simplex_strategy(initial_simplex_strategy) .with_batch_repair_policy(repair_policy); - match DelaunayTriangulation::try_with_options_and_statistics( - &kernel, - &vertices, - topology_guarantee, - options, - ) { + match DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(topology_guarantee) + .construction_options(options) + .build_with_kernel_and_statistics(&kernel) + { Ok((dt, stats)) => { let summary: InsertionSummary = stats.into(); print_insertion_summary(&summary, t_insert.elapsed(), true); @@ -1777,11 +1776,9 @@ fn regression_issue_228_3d_1000_flip_repair_convergence() { // Use the default kernel (AdaptiveKernel — exact+SoS predicates) to match the // actual regression scenario. PLManifold enables full orientation validation // now that #258 is fixed. - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .expect("construction must not fail (#228 regression)"); assert!( @@ -1818,13 +1815,10 @@ fn regression_issue_230_4d_100_orientation() { .collect(); let kernel = RobustKernel::::new(); - let (dt, stats) = - DelaunayTriangulation::, (), (), 4>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::PLManifoldStrict, - ConstructionOptions::default(), - ) + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifoldStrict) + .construction_options(ConstructionOptions::default()) + .build_with_kernel_and_statistics(&kernel) .expect("construction must not fail (#230 regression)"); println!( diff --git a/tests/mesh_export.rs b/tests/mesh_export.rs index acdfac33..b67cef67 100644 --- a/tests/mesh_export.rs +++ b/tests/mesh_export.rs @@ -48,7 +48,7 @@ fn sample_export() -> Result, MeshExportTestError> { vertex![0.0, 2.0]?, vertex![0.25, 0.25]?, ]; - let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; Ok(triangulation.to_mesh_export()?) } @@ -60,7 +60,7 @@ fn sample_delaunay_result_export() -> DelaunayResult> { vertex![0.0, 2.0]?, vertex![0.25, 0.25]?, ]; - let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; Ok(triangulation.to_mesh_export()?) } @@ -82,7 +82,7 @@ fn sample_vertices_for_dimension() -> Result>, /// Exports one dimension-sweep fixture through the public builder path. fn sample_export_for_dimension() -> Result, MeshExportTestError> { let vertices = sample_vertices_for_dimension::()?; - let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; Ok(triangulation.to_mesh_export()?) } @@ -810,7 +810,7 @@ fn mesh_export_ids_match_triangulation_uuids() -> Result<(), MeshExportTestError vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let triangulation = DelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let triangulation = DelaunayTriangulationBuilder::new(&vertices).build()?; let export = triangulation.to_mesh_export()?; let exported_vertices: HashMap<_, _> = export diff --git a/tests/pachner_roundtrip.rs b/tests/pachner_roundtrip.rs index 753837dd..dd065912 100644 --- a/tests/pachner_roundtrip.rs +++ b/tests/pachner_roundtrip.rs @@ -595,13 +595,11 @@ fn build_dt_4d(points: &[[f64; 4]], fixture_name: &str) -> Dt4 { let options = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); - DelaunayTriangulation::try_with_topology_guarantee_and_options( - &RobustKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - options, - ) - .unwrap_or_else(|err| panic!("{fixture_name} 4D fixture should build: {err}")) + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .construction_options(options) + .build_with_kernel(&RobustKernel::new()) + .unwrap_or_else(|err| panic!("{fixture_name} 4D fixture should build: {err}")) } /// Builds a minimal Euclidean D-simplex fixture for dimension smoke tests. @@ -610,13 +608,11 @@ fn build_minimal_simplex_dt() -> Dt { let options = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); - DelaunayTriangulation::try_with_topology_guarantee_and_options( - &RobustKernel::new(), - &vertices, - TopologyGuarantee::PLManifold, - options, - ) - .unwrap_or_else(|err| panic!("{D}D minimal simplex fixture should build: {err}")) + DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .construction_options(options) + .build_with_kernel(&RobustKernel::new()) + .unwrap_or_else(|err| panic!("{D}D minimal simplex fixture should build: {err}")) } /// Returns the origin plus coordinate unit vectors as a nondegenerate D-simplex. @@ -641,7 +637,7 @@ fn build_flippable_dt_2d() -> Dt2 { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .expect("explicit 2D fixture connectivity should parse") - .build_with_kernel::<_, ()>(&RobustKernel::new()) + .build_with_kernel(&RobustKernel::new()) .expect("stable 2D fixture should build"); assert_topology_and_delaunay_valid(&dt, "stable 2D fixture before local edits"); dt @@ -657,7 +653,7 @@ fn build_single_triangle_dt_2d() -> Dt2 { DelaunayTriangulationBuilder::new(&vertices) .topology_guarantee(TopologyGuarantee::PLManifold) - .build_with_kernel::<_, ()>(&RobustKernel::new()) + .build_with_kernel(&RobustKernel::new()) .expect("single-triangle fixture should build") } @@ -673,7 +669,7 @@ fn build_canonicalized_toroidal_dt_2d() -> Dt2 { DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .expect("canonicalized toroidal domain should parse") - .build_with_kernel::<_, ()>(&RobustKernel::new()) + .build_with_kernel(&RobustKernel::new()) .expect("canonicalized toroidal fixture should build") } diff --git a/tests/prelude_exports.rs b/tests/prelude_exports.rs index 2af72e10..9e76bb8c 100644 --- a/tests/prelude_exports.rs +++ b/tests/prelude_exports.rs @@ -41,16 +41,19 @@ use delaunay::prelude::algorithms::LocateResult; #[cfg(feature = "diagnostics")] use delaunay::prelude::collections::SimplexKeyBuffer; use delaunay::prelude::collections::{ - SecureHashMap as ScopedSecureHashMap, SecureHashSet as ScopedSecureHashSet, Uuid, + SecureHashMap as ScopedSecureHashMap, SecureHashSet as ScopedSecureHashSet, + SimplexSecondaryMap as ScopedSimplexSecondaryMap, Uuid, }; use delaunay::prelude::construction::{ CavityFillingError, CavityRepairStage, ConstructionOptions, ConstructionSkipSample, - ConstructionSlowInsertionSample, CoordinateRangeError as ConstructionCoordinateRangeError, + ConstructionSlowInsertionSample, ConstructionStatistics, + CoordinateRangeError as ConstructionCoordinateRangeError, CoordinateRangeOrdering as ConstructionCoordinateRangeOrdering, CoordinateValidationError as ConstructionCoordinateValidationError, DedupPolicy, DedupTolerance, DeduplicationError, DelaunayConstructionFailure, DelaunayConstructionRetryFailure, DelaunayError, DelaunayRepairPolicy, DelaunayResult, DelaunayTriangulation, DelaunayTriangulationBuilder, DelaunayTriangulationConstructionError, + DelaunayTriangulationConstructionErrorWithStatistics, DelaunayTriangulationValidationError as ConstructionDelaunayTriangulationValidationError, DelaunayVerificationError as ConstructionDelaunayVerificationError, DeleteVertexError, ExplicitConstructionError, FinalDelaunayValidationContext, FinalTopologyValidationContext, @@ -89,10 +92,10 @@ use delaunay::prelude::export::{ VisualizationTopologyKind as ExportPreludeVisualizationTopologyKind, }; use delaunay::prelude::generators::{ - CoordinateRange, CoordinateRangeError, InvalidPositiveScalar, - RandomPointGenerationError as GeneratorRandomPointGenerationError, RandomTriangulationBuilder, - generate_grid_points, generate_random_points_in_range_seeded, - try_generate_random_points_seeded, + CoordinateRange, CoordinateRangeError, InvalidPositiveScalar, RandomPointCount, + RandomPointCountError, RandomPointGenerationError as GeneratorRandomPointGenerationError, + RandomTriangulationBuilder, RandomTriangulationBuilderError, generate_grid_points, + generate_random_points_in_range_seeded, try_generate_random_points_seeded, }; #[cfg(feature = "diagnostics")] use delaunay::prelude::geometry::AdaptiveKernel; @@ -137,8 +140,9 @@ use delaunay::prelude::query::{ FacetIncidenceAnalysis as QueryFacetIncidenceAnalysis, FacetIncidenceView as QueryFacetIncidenceView, IncidenceView as QueryIncidenceView, OneSidedFacetsIter as QueryOneSidedFacetsIter, QueryError, - SimplexFacetsIter as QuerySimplexFacetsIter, SimplexNeighborIndex as QuerySimplexNeighborIndex, - TopologyIndexBuildError, TriangulationAdjacency as QueryTriangulationAdjacency, + SimplexDataFillError as QuerySimplexDataFillError, SimplexFacetsIter as QuerySimplexFacetsIter, + SimplexNeighborIndex as QuerySimplexNeighborIndex, TopologyIndexBuildError, + TriangulationAdjacency as QueryTriangulationAdjacency, }; use delaunay::prelude::repair::{ DelaunayCheckPolicy, DelaunayRepairDiagnostics, DelaunayRepairError, @@ -195,9 +199,11 @@ use delaunay::prelude::validation::{ find_delaunay_violations as focused_find_delaunay_violations, }; use delaunay::prelude::{ + ConstructionStatistics as RootPreludeConstructionStatistics, CoordinateRange as RootCoordinateRange, DelaunayError as RootDelaunayError, DelaunayResult as RootDelaunayResult, DelaunayTriangulation as RootDelaunayTriangulation, DelaunayTriangulationBuilder as RootDelaunayTriangulationBuilder, + DelaunayTriangulationConstructionErrorWithStatistics as RootPreludeConstructionErrorWithStatistics, DelaunayViolationDetail as RootDelaunayViolationDetail, DelaunayViolationReport as RootDelaunayViolationReport, EdgeIndex as RootEdgeIndex, FacetIncidenceView as RootFacetIncidenceView, FlipFailureKind as RootFlipFailureKind, @@ -207,6 +213,7 @@ use delaunay::prelude::{ InitialSimplexUnexpectedInsertionStage as RootInitialSimplexUnexpectedInsertionStage, PeriodicDomainPeriodError as RootPeriodicDomainPeriodError, PlManifoldRepairStage as RootPreludePlManifoldRepairStage, SecureHashMap, SecureHashSet, + SimplexDataFillError as RootPreludeSimplexDataFillError, SimplexNeighborIndex as RootSimplexNeighborIndex, TopologyError as RootTopologyError, TopologyGuarantee as RootTopologyGuarantee, TopologyKind as RootTopologyKind, ToroidalConstructionMode as RootToroidalConstructionMode, ToroidalDomain as RootToroidalDomain, @@ -221,6 +228,7 @@ use delaunay::query::{ AllFacetsIter as QueryFacadeAllFacetsIter, BoundaryFacetsIter as QueryFacadeBoundaryFacetsIter, EdgeIndex as QueryFacadeEdgeIndex, FacetHandle as QueryFacadeFacetHandle, IncidenceView as QueryFacadeIncidenceView, OneSidedFacetsIter as QueryFacadeOneSidedFacetsIter, + SimplexDataFillError as QueryFacadeSimplexDataFillError, SimplexFacetsIter as QueryFacadeSimplexFacetsIter, SimplexNeighborIndex as QueryFacadeSimplexNeighborIndex, TriangulationAdjacency as QueryFacadeTriangulationAdjacency, @@ -246,6 +254,7 @@ use delaunay::{ MeshExportError as RootMeshExportError, MeshExportValidationError as RootMeshExportValidationError, PlManifoldRepairStage as RootPlManifoldRepairStage, + SimplexDataFillError as RootSimplexDataFillError, ValidatedMeshExport as RootValidatedMeshExport, ValidatedVisualizationData as RootValidatedVisualizationData, VisualizationTopologyGuarantee as RootVisualizationTopologyGuarantee, @@ -282,8 +291,14 @@ enum PreludeExportTestError { #[error(transparent)] RandomPointGeneration(#[from] RandomPointGenerationError), #[error(transparent)] + RandomPointCount(#[from] RandomPointCountError), + #[error(transparent)] + RandomTriangulationBuilder(#[from] RandomTriangulationBuilderError), + #[error(transparent)] Construction(#[from] DelaunayTriangulationConstructionError), #[error(transparent)] + ConstructionWithStatistics(#[from] Box), + #[error(transparent)] DelaunayValidation(#[from] DelaunayValidationError), #[error(transparent)] DelaunayRepair(#[from] DelaunayRepairError), @@ -319,6 +334,12 @@ enum PreludeExportTestError { GenericTriangulationConstruction(#[from] GenericTriangulationConstructionError), } +impl From for PreludeExportTestError { + fn from(source: DelaunayTriangulationConstructionErrorWithStatistics) -> Self { + Self::ConstructionWithStatistics(Box::new(source)) + } +} + /// Proves the direct flips module keeps the expert trait bound available. const fn assert_bistellar_flips(_: &impl BistellarFlips<3, VertexData = ()>) {} @@ -484,7 +505,7 @@ fn construction_prelude_exports_common_delaunay_error_aliases() { let no_vertices: [Vertex<(), 2>; 0] = []; let construction = DelaunayTriangulationBuilder::new(&no_vertices) - .build::<()>() + .build() .expect_err("empty Delaunay construction should fail"); assert_matches!( DelaunayError::from(construction), @@ -529,6 +550,78 @@ fn construction_prelude_exports_common_delaunay_error_aliases() { ); } +#[test] +fn construction_prelude_exports_builder_statistics_terminal() -> Result<(), PreludeExportTestError> +{ + let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; + + let (dt, focused_stats): (DelaunayTriangulation<_, (), (), 2>, ConstructionStatistics) = + DelaunayTriangulationBuilder::new(&vertices).build_with_statistics()?; + let root_prelude_stats: RootPreludeConstructionStatistics = focused_stats; + assert_eq!(dt.number_of_vertices(), root_prelude_stats.inserted); + assert!(root_prelude_stats.telemetry.has_data()); + + let no_vertices: [Vertex<(), 2>; 0] = []; + let focused_error: DelaunayTriangulationConstructionErrorWithStatistics = + match DelaunayTriangulationBuilder::new(&no_vertices).build_with_statistics() { + Ok(_) => panic!("empty construction should fail"), + Err(error) => error, + }; + let root_prelude_error: RootPreludeConstructionErrorWithStatistics = focused_error; + assert_matches!( + root_prelude_error.error, + DelaunayTriangulationConstructionError::Triangulation( + DelaunayConstructionFailure::InsufficientVertices { dimension: 2, .. } + ) + ); + + Ok(()) +} + +#[test] +fn query_preludes_export_simplex_data_fill_error() -> Result<(), PreludeExportTestError> { + let vertices = vec![vertex![0.0, 0.0]?, vertex![1.0, 0.0]?, vertex![0.0, 1.0]?]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build()?; + let data: ScopedSimplexSecondaryMap = ScopedSimplexSecondaryMap::new(); + + let err = dt + .try_fill_simplex_data_from(&data) + .expect_err("empty secondary map should miss the live simplex"); + let query_error: QuerySimplexDataFillError = err.clone(); + let query_facade_error: QueryFacadeSimplexDataFillError = err.clone(); + let root_prelude_error: RootPreludeSimplexDataFillError = err.clone(); + let root_error: RootSimplexDataFillError = err.clone(); + + assert_matches!( + query_error, + QuerySimplexDataFillError::MissingSimplexData { .. } + ); + assert_matches!( + query_facade_error, + QueryFacadeSimplexDataFillError::MissingSimplexData { .. } + ); + assert_matches!( + root_prelude_error, + RootPreludeSimplexDataFillError::MissingSimplexData { .. } + ); + assert_matches!( + root_error, + RootSimplexDataFillError::MissingSimplexData { .. } + ); + assert_matches!( + DelaunayError::from(err), + DelaunayError::SimplexDataFill { source } + if matches!( + source.as_ref(), + RootSimplexDataFillError::MissingSimplexData { .. } + ) + ); + + Ok(()) +} + #[test] fn construction_prelude_exports_validation_and_result_aliases() { let configuration = @@ -782,7 +875,7 @@ fn root_exports_cover_flattened_public_api() -> Result<(), RootApiExportTestErro .with_insertion_order(ConstructionModuleInsertionOrderStrategy::Input); let builder: BuilderModuleBuilder<'_, (), 3> = RootDelaunayTriangulationBuilder::new(&vertices).construction_options(options); - let mut dt: RootDelaunayTriangulation<_, (), (), 3> = builder.build::<()>()?; + let mut dt: RootDelaunayTriangulation<_, (), (), 3> = builder.build()?; assert_eq!(dt.topology_guarantee(), RootTopologyGuarantee::PLManifold); assert_eq!(dt.validation_policy(), RootValidationPolicy::ExplicitOnly); @@ -1028,7 +1121,9 @@ fn preludes_cover_bench_apis() -> Result<(), PreludeExportTestError> { options.batch_repair_policy(), DelaunayRepairPolicy::EveryInsertion ); - let dt = DelaunayTriangulation::try_new_with_options(&vertices, options)?; + let dt = DelaunayTriangulation::builder(&vertices) + .construction_options(options) + .build()?; assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifold); assert_export_prelude_exports(&dt)?; @@ -1073,6 +1168,7 @@ fn preludes_cover_bench_apis() -> Result<(), PreludeExportTestError> { assert_send_sync_unpin::(); assert_send_sync_unpin::(); assert_send_sync_unpin::(); + assert_send_sync_unpin::(); assert_send_sync_unpin::(); assert_send_sync_unpin::(); assert_send_sync_unpin::(); @@ -1120,7 +1216,7 @@ fn query_preludes_cover_borrowed_adjacency_view() -> Result<(), PreludeExportTes vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let dt = DelaunayTriangulation::try_new(&vertices)?; + let dt = DelaunayTriangulation::builder(&vertices).build()?; let query_adjacency: QueryTriangulationAdjacency<'_> = dt.adjacency()?; let generic_adjacency: GenericTriangulationAdjacency<'_> = dt.as_triangulation().adjacency()?; @@ -1383,10 +1479,20 @@ fn generator_prelude_covers_validated_coordinate_ranges() -> Result<(), PreludeE let range_points: Vec> = generate_random_points_in_range_seeded(3, generated_range, 42)?; let grid_points: Vec> = generate_grid_points(NonZeroUsize::MIN, 1.0, [0.0, 0.0])?; - let builder = RandomTriangulationBuilder::try_new(NonZeroUsize::MIN, (-1.0_f64, 1.0))? + let Some(three) = NonZeroUsize::new(3) else { + return Ok(()); + }; + let point_count = RandomPointCount::<2>::try_new(three)?; + assert_eq!(point_count.get(), 3); + let builder = RandomTriangulationBuilder::<2>::try_new(three, (-1.0_f64, 1.0))? .seed(7) .insertion_order(InsertionOrderStrategy::Input); let _ = builder; + let invalid_builder = + RandomTriangulationBuilder::<2>::try_new(NonZeroUsize::MIN, (-1.0_f64, 1.0)); + let Err(RandomTriangulationBuilderError::PointCount { .. }) = invalid_builder else { + panic!("expected RandomTriangulationBuilderError::PointCount"); + }; let root_range = RootCoordinateRange::try_new(-1.0_f64, 1.0)?; assert_relative_eq!(root_range.bounds().0, -1.0, epsilon = f64::EPSILON); @@ -1654,7 +1760,7 @@ fn degenerate_prelude_vertices() fn assert_single_simplex_ridge_star( vertices: &[Vertex<(), D>], ) -> Result<(), PreludeExportTestError> { - let dt = DelaunayTriangulation::try_new(vertices)?; + let dt = DelaunayTriangulation::builder(vertices).build()?; let ridge = RidgeCandidate::::try_from_vertices(dt.tds().vertex_keys().take(D - 1))?; let star = ridge_star_simplices(dt.tds(), &ridge)?; let ridge_query: RidgeQuery<'_, (), (), D> = ridge.query(dt.tds())?; @@ -1686,7 +1792,7 @@ fn assert_single_simplex_ridge_star( fn assert_cospherical_ridge_star() -> Result<(), PreludeExportTestError> { let vertices = cospherical_prelude_vertices::()?; - let dt = DelaunayTriangulation::try_new(&vertices)?; + let dt = DelaunayTriangulation::builder(&vertices).build()?; let ridge = RidgeCandidate::::try_from_vertices(dt.tds().vertex_keys().take(D - 1))?; let star = ridge_star_simplices(dt.tds(), &ridge)?; @@ -1719,13 +1825,13 @@ fn assert_topology_prelude_dimension() -> Result<(), PreludeExpo let near_boundary_vertices = simplex_prelude_vertices::(f64::EPSILON, 1.0)?; assert_single_simplex_ridge_star(&near_boundary_vertices)?; - let dt = DelaunayTriangulation::try_new(&simplex_vertices)?; + let dt = DelaunayTriangulation::builder(&simplex_vertices).build()?; let keys = dt.tds().vertex_keys().collect::>(); assert_ridge_candidate_reject_adversarial_keys::(&keys); assert_cospherical_ridge_star::()?; assert_matches!( - DelaunayTriangulation::try_new(°enerate_prelude_vertices::()?), + DelaunayTriangulation::builder(°enerate_prelude_vertices::()?).build(), Err(DelaunayTriangulationConstructionError::Triangulation( DelaunayConstructionFailure::GeometricDegeneracy { .. } )) @@ -1940,7 +2046,7 @@ fn diagnostic_preludes_cover_repair_apis() -> Result<(), PreludeExportTestError> vertex![0.0, 1.0, 0.0]?, vertex![0.0, 0.0, 1.0]?, ]; - let mut dt = DelaunayizeDelaunayTriangulationBuilder::new(&vertices).build::<()>()?; + let mut dt = DelaunayizeDelaunayTriangulationBuilder::new(&vertices).build()?; let repair_stats = DelaunayRepairStats::default(); let repair_outcome = DelaunayRepairOutcome { diff --git a/tests/proptest_convex_hull.rs b/tests/proptest_convex_hull.rs index 053597b2..391ae7e8 100644 --- a/tests/proptest_convex_hull.rs +++ b/tests/proptest_convex_hull.rs @@ -10,8 +10,8 @@ //! Tests are generated for dimensions 2D-5D using macros to reduce duplication. use delaunay::assert_jaccard_gte; +use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee}; use delaunay::prelude::query::*; -use delaunay::prelude::topology::validation::*; use delaunay::try_vertices_from_points; use proptest::prelude::*; @@ -55,10 +55,7 @@ macro_rules! test_minimal_simplex_hull { let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt_result = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ); + let dt_result = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assert!( dt_result.is_ok(), "{}D minimal simplex triangulation should construct: {:?}", @@ -104,10 +101,7 @@ macro_rules! test_convex_hull_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - let dt_result = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ); + let dt_result = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_result.is_ok()); let dt = dt_result.expect("assumed valid random triangulation"); @@ -150,10 +144,7 @@ macro_rules! test_convex_hull_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - let dt_result = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ); + let dt_result = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_result.is_ok()); let dt = dt_result.expect("assumed valid random triangulation"); let hull_result = ConvexHull::try_from_triangulation(dt.as_triangulation()); @@ -204,10 +195,7 @@ macro_rules! test_convex_hull_properties { ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")), new_point in prop::array::[](finite_coordinate()).prop_map(|coords| Point::try_new(coords).expect("finite point coordinates")) ) { - let dt_result = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &initial_vertices, - TopologyGuarantee::PLManifold, - ); + let dt_result = DelaunayTriangulation::builder(&initial_vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_result.is_ok()); let mut dt = dt_result.expect("assumed valid random triangulation"); @@ -269,10 +257,7 @@ macro_rules! test_convex_hull_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - let dt_result = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ); + let dt_result = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_result.is_ok()); let dt = dt_result.expect("assumed valid random triangulation"); let hull_result = ConvexHull::try_from_triangulation(dt.as_triangulation()); @@ -314,10 +299,7 @@ macro_rules! test_convex_hull_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - let dt_result = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ); + let dt_result = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_result.is_ok()); let dt = dt_result.expect("assumed valid random triangulation"); let hull1_result = ConvexHull::try_from_triangulation(dt.as_triangulation()); diff --git a/tests/proptest_delaunay_triangulation.rs b/tests/proptest_delaunay_triangulation.rs index ebd85054..8ef402ba 100644 --- a/tests/proptest_delaunay_triangulation.rs +++ b/tests/proptest_delaunay_triangulation.rs @@ -21,7 +21,7 @@ //! - **Insertion-order robustness** - Levels 1–3 validity across insertion orders (2D-5D; Delaunay property not asserted; see Issue #120) //! - **Duplicate cloud integration** - Full pipeline with messy real-world inputs (2D-5D: duplicates + near-duplicates) //! -//! All structural tests construct with `DelaunayTriangulation::try_new_with_topology_guarantee(..., TopologyGuarantee::PLManifold)` +//! All structural tests construct with `DelaunayTriangulation::builder(...).topology_guarantee(TopologyGuarantee::PLManifold).build()` //! and then use `insert()`, ensuring PL-manifoldness by construction while maintaining //! invariants through the incremental cavity-based insertion algorithm. //! @@ -46,6 +46,7 @@ use proptest::test_runner::{Config, TestCaseError, TestRunner}; use rand::{SeedableRng, seq::SliceRandom}; use std::cell::RefCell; use std::collections::HashSet; +use std::time::Instant; fn init_tracing() { static INIT: std::sync::Once = std::sync::Once::new(); @@ -63,6 +64,7 @@ fn init_tracing() { .try_init(); }); } + // ============================================================================= // TEST CONFIGURATION // ============================================================================= @@ -515,11 +517,9 @@ fn assert_non_finite_point_rejected_and_preserves_validity( empty_validation.err() ); - let dt = - DelaunayTriangulation::, (), (), D>::try_new_with_topology_guarantee( - initial_vertices, - TopologyGuarantee::PLManifold, - ) + let dt = DelaunayTriangulation::builder(initial_vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .map_err(|err| { TestCaseError::fail(format!( "{D}D finite initial simplex construction failed: {err:?}" @@ -711,10 +711,7 @@ macro_rules! gen_incremental_insertion_validity { }); prop_assume!(!is_dup); - let dt = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &initial_vertices, - TopologyGuarantee::PLManifold, - ); + let dt = DelaunayTriangulation::builder(&initial_vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); if let Err(err) = &dt { if std::env::var_os("DELAUNAY_PROPTEST_CONSTRUCTION_ERRORS").is_some() { tracing::warn!( @@ -788,10 +785,7 @@ proptest! { prop_assume!(has_no_nearly_coplanar_tetrahedra_3d(&all_vertices)); prop_assume!(has_no_cospherical_5_tuples_3d(&all_vertices)); - let dt = DelaunayTriangulation::<_, (), (), 3>::try_new_with_topology_guarantee( - &initial_vertices, - TopologyGuarantee::PLManifold, - ); + let dt = DelaunayTriangulation::builder(&initial_vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt.is_ok()); let mut dt = dt.unwrap(); prop_assert_levels_1_to_3_valid!(3, &dt, "initial triangulation"); @@ -901,20 +895,17 @@ macro_rules! gen_duplicate_coords_test { ) { let options = ConstructionOptions::default() .with_dedup_policy(DedupPolicy::Exact); - let dt = DelaunayTriangulation::, (), (), $dim>::try_new_with_options( - &vertices, - options, - ); + let dt = DelaunayTriangulation::builder(&vertices).construction_options(options).build(); prop_assume!(dt.is_ok()); let mut dt = dt.unwrap(); dt.try_set_validation_policy(ValidationPolicy::ExplicitOnly) .expect("explicit-only validation policy should be compatible"); dt.set_delaunay_repair_policy(DelaunayRepairPolicy::Never); // Select a vertex that is actually present in the triangulation. - // `DelaunayTriangulation::try_new_with_options` may skip some input vertices (e.g., due to degeneracy), + // `DelaunayTriangulation::builder(...).construction_options(...).build()` may skip some input vertices (e.g., due to degeneracy), // so we must use stored vertices to test duplicate rejection. let (_, existing_vertex) = dt.vertices().next() - .expect("DelaunayTriangulation::try_new_with_options returned Ok but has no vertices"); + .expect("builder construction returned Ok but has no vertices"); let p = *existing_vertex.point(); let dup = try_vertices_from_points(&[p]).expect("finite point coordinates")[0]; let result = dt.insert_vertex(dup); @@ -996,11 +987,8 @@ proptest! { // coordinate hyperplane (x_i == 0.0). prop_assume!(has_no_coordinate_hyperplane_degeneracy(&vertices)); - // Use DelaunayTriangulation::try_new_with_topology_guarantee() to triangulate ALL vertices together - let dt = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ); + // Use the builder topology guarantee to triangulate ALL vertices together. + let dt = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build(); if let Err(err) = &dt { if std::env::var_os("DELAUNAY_PROPTEST_CONSTRUCTION_ERRORS").is_some() { tracing::warn!( @@ -1096,10 +1084,7 @@ macro_rules! gen_high_dim_delaunay_smoke { ))); } - let mut dt = match DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + let mut dt = match DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { Ok(dt) => dt, Err(err) => { stats.rejected_construction_failed += 1; @@ -1147,10 +1132,7 @@ macro_rules! gen_high_dim_delaunay_smoke { try_vertices_from_points(&cloud_points).expect("finite point coordinates"); let options = ConstructionOptions::default() .with_dedup_policy(DedupPolicy::try_epsilon(1e-6).unwrap()); - let cloud_dt = match DelaunayTriangulation::<_, (), (), $dim>::try_new_with_options( - &cloud_vertices, - options, - ) { + let cloud_dt = match DelaunayTriangulation::builder(&cloud_vertices).construction_options(options).build() { Ok(dt) => dt, Err(err) => { stats.rejected_duplicate_cloud_failed += 1; @@ -1303,10 +1285,7 @@ macro_rules! gen_insertion_order_robustness_test { prop_assume!(has_no_coordinate_hyperplane_degeneracy(&points)); // Build first triangulation with natural order - let dt_a = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &points, - TopologyGuarantee::PLManifold, - ); + let dt_a = DelaunayTriangulation::builder(&points).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_a.is_ok()); let dt_a = dt_a.unwrap(); @@ -1323,10 +1302,7 @@ macro_rules! gen_insertion_order_robustness_test { let mut points_shuffled = points; points_shuffled.shuffle(&mut rng); - let dt_b = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &points_shuffled, - TopologyGuarantee::PLManifold, - ); + let dt_b = DelaunayTriangulation::builder(&points_shuffled).topology_guarantee(TopologyGuarantee::PLManifold).build(); prop_assume!(dt_b.is_ok()); let dt_b = dt_b.unwrap(); @@ -1579,31 +1555,25 @@ fn prop_insertion_order_robustness_3d() { verts_b ); - // Parity check: the high-level constructor path (`DelaunayTriangulation::try_new_with_topology_guarantee`) should also + // Parity check: the high-level builder path should also // succeed for the same generated inputs. This helps prevent maintenance drift vs the // 2D/4D/5D insertion-order tests which use `new()` directly. - let dt_new_a = match DelaunayTriangulation::<_, (), (), 3>::try_new_with_topology_guarantee( - &points, - TopologyGuarantee::PLManifold, - ) { + let dt_new_a = match DelaunayTriangulation::builder(&points).topology_guarantee(TopologyGuarantee::PLManifold).build() { Ok(dt) => dt, Err(e) => { stats.rejected_new_a_failed += 1; return Err(TestCaseError::reject(format!( - "3D: DelaunayTriangulation::try_new_with_topology_guarantee() failed for generated inputs (order A; treated as out of scope): {e}" + "3D: builder PL-manifold construction failed for generated inputs (order A; treated as out of scope): {e}" ))); } }; - let dt_new_b = match DelaunayTriangulation::<_, (), (), 3>::try_new_with_topology_guarantee( - &points_shuffled, - TopologyGuarantee::PLManifold, - ) { + let dt_new_b = match DelaunayTriangulation::builder(&points_shuffled).topology_guarantee(TopologyGuarantee::PLManifold).build() { Ok(dt) => dt, Err(e) => { stats.rejected_new_b_failed += 1; return Err(TestCaseError::reject(format!( - "3D: DelaunayTriangulation::try_new_with_topology_guarantee() failed for generated inputs (order B; treated as out of scope): {e}" + "3D: builder PL-manifold construction failed for generated inputs (order B; treated as out of scope): {e}" ))); } }; @@ -1611,7 +1581,7 @@ fn prop_insertion_order_robustness_3d() { if dt_new_a.number_of_vertices() != points.len() { stats.rejected_new_a_skipped_vertices += 1; return Err(TestCaseError::reject(format!( - "3D: try_new_with_topology_guarantee() skipped vertices for generated inputs (order A; treated as out of scope): expected {}, got {}", + "3D: builder PL-manifold construction skipped vertices for generated inputs (order A; treated as out of scope): expected {}, got {}", points.len(), dt_new_a.number_of_vertices() ))); @@ -1620,7 +1590,7 @@ fn prop_insertion_order_robustness_3d() { if dt_new_b.number_of_vertices() != points.len() { stats.rejected_new_b_skipped_vertices += 1; return Err(TestCaseError::reject(format!( - "3D: try_new_with_topology_guarantee() skipped vertices for generated inputs (order B; treated as out of scope): expected {}, got {}", + "3D: builder PL-manifold construction skipped vertices for generated inputs (order B; treated as out of scope): expected {}, got {}", points.len(), dt_new_b.number_of_vertices() ))); @@ -1630,7 +1600,7 @@ fn prop_insertion_order_robustness_3d() { if let Err(e) = validation_new_a { stats.rejected_new_a_invalid_levels_1_to_3 += 1; return Err(TestCaseError::reject(format!( - "3D: Triangulation A (try_new_with_topology_guarantee()) failed Levels 1–3 validation (treated as out of scope): {e:?}" + "3D: Triangulation A (builder PL-manifold construction) failed Levels 1–3 validation (treated as out of scope): {e:?}" ))); } @@ -1638,7 +1608,7 @@ fn prop_insertion_order_robustness_3d() { if let Err(e) = validation_new_b { stats.rejected_new_b_invalid_levels_1_to_3 += 1; return Err(TestCaseError::reject(format!( - "3D: Triangulation B (try_new_with_topology_guarantee()) failed Levels 1–3 validation (treated as out of scope): {e:?}" + "3D: Triangulation B (builder PL-manifold construction) failed Levels 1–3 validation (treated as out of scope): {e:?}" ))); } @@ -1783,10 +1753,7 @@ macro_rules! gen_insertion_order_robustness_high_dim_impl { ))); } - let dt_a = match DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &points, - TopologyGuarantee::PLManifold, - ) { + let dt_a = match DelaunayTriangulation::builder(&points).topology_guarantee(TopologyGuarantee::PLManifold).build() { Ok(dt) => dt, Err(e) => { stats.rejected_new_a_failed += 1; @@ -1810,10 +1777,7 @@ macro_rules! gen_insertion_order_robustness_high_dim_impl { let mut points_shuffled = points; points_shuffled.shuffle(&mut rng); - let dt_b = match DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &points_shuffled, - TopologyGuarantee::PLManifold, - ) { + let dt_b = match DelaunayTriangulation::builder(&points_shuffled).topology_guarantee(TopologyGuarantee::PLManifold).build() { Ok(dt) => dt, Err(e) => { stats.rejected_new_b_failed += 1; @@ -2005,13 +1969,10 @@ macro_rules! gen_duplicate_cloud_test { let vertices: Vec> = try_vertices_from_points(&points).expect("finite point coordinates"); - let build_start = std::time::Instant::now(); + let build_start = Instant::now(); let options = ConstructionOptions::default() .with_dedup_policy(DedupPolicy::try_epsilon(1e-6).unwrap()); - let dt = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_options( - &vertices, - options, - ); + let dt = DelaunayTriangulation::builder(&vertices).construction_options(options).build(); let build_elapsed = build_start.elapsed(); if let Err(err) = &dt { if std::env::var_os("DELAUNAY_PROPTEST_CONSTRUCTION_ERRORS").is_some() { @@ -2037,7 +1998,7 @@ macro_rules! gen_duplicate_cloud_test { prop_assert_levels_1_to_3_valid!($dim, &dt, "triangulation (kept subset)"); // Delaunay validity (Level 5) for kept subset - let validate_start = std::time::Instant::now(); + let validate_start = Instant::now(); let delaunay = dt.is_valid_delaunay(); let validate_elapsed = validate_start.elapsed(); if log_coverage { diff --git a/tests/proptest_euler_characteristic.rs b/tests/proptest_euler_characteristic.rs index 2db895af..2bd308a9 100644 --- a/tests/proptest_euler_characteristic.rs +++ b/tests/proptest_euler_characteristic.rs @@ -73,10 +73,10 @@ macro_rules! test_euler_properties { ) ) { // Attempt to build triangulation - if let Ok(dt) = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Validate Euler characteristic let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology())?; @@ -106,10 +106,10 @@ macro_rules! test_euler_properties { $min_vertices..$max_vertices ) ) { - if let Ok(dt) = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let counts = euler::count_simplices(dt.tds())?; // Basic sanity checks @@ -146,10 +146,10 @@ macro_rules! test_euler_properties { $min_vertices..$max_vertices ) ) { - if let Ok(dt) = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let result = validation::validate_triangulation_euler(dt.tds(), dt.global_topology())?; diff --git a/tests/proptest_facet.rs b/tests/proptest_facet.rs index 6de14665..67c00318 100644 --- a/tests/proptest_facet.rs +++ b/tests/proptest_facet.rs @@ -8,9 +8,9 @@ //! //! Tests are generated for dimensions 2D-5D using macros to reduce duplication. +use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee}; use delaunay::prelude::query::*; use delaunay::prelude::tds::facet_key_from_vertices; -use delaunay::prelude::topology::validation::*; use delaunay::try_vertices_from_points; use proptest::prelude::*; use std::collections::HashMap; @@ -42,10 +42,7 @@ macro_rules! test_facet_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { let tds = dt.tds(); for simplex_key in tds.simplex_keys() { // Each simplex has D+1 facets (one opposite each vertex) @@ -74,10 +71,7 @@ macro_rules! test_facet_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { let tds = dt.tds(); for simplex_key in tds.simplex_keys() { prop_assert!( @@ -111,10 +105,7 @@ macro_rules! test_facet_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { let tds = dt.tds(); // Check that each facet is valid for simplex_key in tds.simplex_keys() { @@ -140,10 +131,7 @@ macro_rules! test_facet_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { let tds = dt.tds(); for simplex_key in tds.simplex_keys() { let mut facet_count = 0; @@ -187,10 +175,7 @@ macro_rules! test_facet_multiplicity { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { let tds = dt.tds(); // Ensure we're checking a valid triangulation to avoid degenerate edge cases prop_assume!(tds.is_valid().is_ok()); diff --git a/tests/proptest_flips.rs b/tests/proptest_flips.rs index 087828eb..48c6d9c9 100644 --- a/tests/proptest_flips.rs +++ b/tests/proptest_flips.rs @@ -249,11 +249,9 @@ fn check_k1_roundtrip( edge_lengths: [f64; D], ) -> Result<(), TestCaseError> { let vertices = axis_aligned_simplex_vertices::(origin, edge_lengths); - let simplex = - DelaunayTriangulation::, (), (), D>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let simplex = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .map_err(|err| { TestCaseError::fail(format!( "{D}D axis-aligned simplex construction failed: {err:?}" diff --git a/tests/proptest_orientation.rs b/tests/proptest_orientation.rs index aef8b629..3c07ce51 100644 --- a/tests/proptest_orientation.rs +++ b/tests/proptest_orientation.rs @@ -37,10 +37,7 @@ macro_rules! gen_orientation_construction_and_tamper_props { try_vertices_from_points(&points).expect("finite point coordinates") }) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { prop_assert!( dt.tds().is_coherently_oriented(), "{}D: constructed triangulation must be coherently oriented", @@ -65,10 +62,7 @@ macro_rules! gen_orientation_construction_and_tamper_props { try_vertices_from_points(&points).expect("finite point coordinates") }) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { prop_assume!(dt.tds().number_of_simplices() >= 2); prop_assert!(dt.tds().is_coherently_oriented()); diff --git a/tests/proptest_serialization.rs b/tests/proptest_serialization.rs index 94582c73..e163ef4a 100644 --- a/tests/proptest_serialization.rs +++ b/tests/proptest_serialization.rs @@ -10,8 +10,9 @@ use approx::relative_eq; use delaunay::prelude::Tds; +use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee}; +use delaunay::prelude::geometry::AdaptiveKernel; use delaunay::prelude::query::*; -use delaunay::prelude::topology::validation::*; use delaunay::try_vertices_from_points; use proptest::prelude::*; @@ -61,10 +62,7 @@ macro_rules! test_serialization_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { // Serialize to JSON let json = serde_json::to_string(&dt).expect("Serialization failed"); @@ -106,10 +104,7 @@ macro_rules! test_serialization_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { if dt.tds().validate().is_ok() { // Serialize and deserialize via try_from_tds let json = serde_json::to_string(&dt).expect("Serialization failed"); @@ -139,10 +134,7 @@ macro_rules! test_serialization_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { // Filter: Skip minimal/degenerate configurations // Need more than minimal simplex (D+1) to have meaningful serialization test prop_assume!(dt.number_of_vertices() > $dim + 1); @@ -200,10 +192,7 @@ macro_rules! test_serialization_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { // Count original neighbor relationships let mut original_neighbor_count = 0; for (_key, simplex) in dt.simplices() { diff --git a/tests/proptest_simplex.rs b/tests/proptest_simplex.rs index ac150622..2242e85a 100644 --- a/tests/proptest_simplex.rs +++ b/tests/proptest_simplex.rs @@ -9,8 +9,8 @@ //! //! Tests are generated for dimensions 2D-5D using macros to reduce duplication. +use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee}; use delaunay::prelude::query::*; -use delaunay::prelude::topology::validation::*; use delaunay::try_vertices_from_points; use proptest::prelude::*; use std::collections::HashSet; @@ -42,10 +42,7 @@ macro_rules! test_simplex_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { for (_simplex_key, simplex) in dt.simplices() { let vertex_keys = simplex.vertices(); let unique_vertices: HashSet<_> = vertex_keys.iter().collect(); @@ -63,10 +60,7 @@ macro_rules! test_simplex_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { for (_simplex_key, simplex) in dt.simplices() { prop_assert_eq!(simplex.vertices().len(), $expected_vertices); } @@ -82,10 +76,7 @@ macro_rules! test_simplex_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { for (_simplex_key, simplex) in dt.simplices() { if let Some(neighbors) = simplex.neighbors() { prop_assert!(neighbors.len() <= $max_neighbors); @@ -103,10 +94,7 @@ macro_rules! test_simplex_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { let mut seen_uuids = HashSet::new(); for (_simplex_key, simplex) in dt.simplices() { prop_assert!(seen_uuids.insert(simplex.uuid())); @@ -123,10 +111,7 @@ macro_rules! test_simplex_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).topology_guarantee(TopologyGuarantee::PLManifold).build() { if dt.tds().validate().is_ok() { for (_simplex_key, simplex) in dt.simplices() { prop_assert_eq!(simplex.vertices().len(), $expected_vertices); diff --git a/tests/proptest_tds.rs b/tests/proptest_tds.rs index 46f210a5..1f1f5def 100644 --- a/tests/proptest_tds.rs +++ b/tests/proptest_tds.rs @@ -22,6 +22,7 @@ //! All tests use `dt.tds().is_valid()` (Level 2 structural validation). use delaunay::prelude::collections::{SimplexVertexBuffer, SimplexVertexKeyBuffer}; +use delaunay::prelude::construction::{DelaunayTriangulation, Vertex}; use delaunay::prelude::query::*; use delaunay::prelude::tds::{Tds, jaccard_index}; use delaunay::try_vertices_from_points; @@ -114,7 +115,7 @@ macro_rules! gen_tds_validity { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { prop_assert!(dt.tds().is_valid().is_ok(), "{}D Tds should be valid: {:?}", $dim, dt.tds().is_valid().err()); @@ -132,7 +133,7 @@ macro_rules! gen_neighbor_symmetry { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { let tds = dt.tds(); for (simplex_key, simplex) in dt.simplices() { if let Some(neighbors) = simplex.neighbors() { @@ -193,7 +194,7 @@ macro_rules! gen_neighbor_index_semantics { #[test] fn [](vertices in []()) { // Use stack-allocated buffer for D facet vertices (D ≤ 7 typical) - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { prop_assume!(dt.tds().is_valid().is_ok()); let tds = dt.tds(); for (simplex_key, simplex) in dt.simplices() { @@ -238,7 +239,7 @@ macro_rules! gen_simplex_vertices_exist_in_tds { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { let all_vertex_keys: HashSet<_> = dt.tds().vertex_keys().collect(); for (_simplex_key, simplex) in dt.simplices() { for vertex_key in simplex.vertices() { @@ -260,7 +261,7 @@ macro_rules! gen_no_duplicate_simplices { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { let mut seen = HashSet::new(); for (_simplex_key, simplex) in dt.simplices() { // Use stack-allocated buffer for D+1 vertices (D ≤ 7 typical) @@ -282,7 +283,7 @@ macro_rules! gen_dimension_consistency { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { if dt.number_of_vertices() >= $min_vertices && dt.number_of_simplices() > 0 { prop_assert_eq!(dt.dim(), $dim as i32, "{}D Tds dimension mismatch", $dim); } @@ -300,7 +301,7 @@ macro_rules! gen_vertex_count_consistency { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { let keys = dt.tds().vertex_keys().count(); let n = dt.number_of_vertices(); prop_assert_eq!(keys, n, "{}D vertex keys count should match number_of_vertices", $dim); @@ -318,7 +319,7 @@ macro_rules! gen_simplex_vertex_count { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { for (_, c) in dt.simplices() { prop_assert_eq!(c.number_of_vertices(), $expected, "{}D simplices must have exactly {} vertices (D+1)", $dim, $expected); @@ -438,7 +439,7 @@ macro_rules! gen_is_connected { $(#[$attr])* #[test] fn [](vertices in []()) { - if let Ok(dt) = DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices).build() { prop_assert!( dt.tds().is_connected(), "{}D successfully-built triangulation must be connected ({} simplices)", @@ -493,7 +494,7 @@ macro_rules! gen_high_dim_tds_smoke { let mut stats = stats.borrow_mut(); stats.generated += 1; - let dt = match DelaunayTriangulation::<_, (), (), $dim>::try_new(&vertices) { + let dt = match DelaunayTriangulation::builder(&vertices).build() { Ok(dt) => dt, Err(err) => { stats.rejected_construction_failed += 1; diff --git a/tests/proptest_triangulation.rs b/tests/proptest_triangulation.rs index 17edbe77..2481b98e 100644 --- a/tests/proptest_triangulation.rs +++ b/tests/proptest_triangulation.rs @@ -36,7 +36,9 @@ use ::uuid::Uuid; use delaunay::geometry::quality::QualityError; -use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee}; +use delaunay::prelude::construction::{ + DelaunayTriangulation, DelaunayTriangulationBuilder, TopologyGuarantee, +}; use delaunay::prelude::geometry::*; use delaunay::prelude::tds::SimplexKey; use delaunay::try_vertices_from_points; @@ -75,8 +77,8 @@ fn finite_coordinate() -> impl Strategy { /// Returns `Ok(())` if metrics match within tolerance, `Err(TestCaseError)` otherwise. /// /// # Returns -/// * `Ok(())` - At least one simplex was successfully matched and compared -/// * `Err(TestCaseError)` - A metric comparison failed +/// * `Ok(())` - Every simplex was successfully matched and compared +/// * `Err(TestCaseError)` - A simplex match was missing or a metric comparison failed /// /// # Purpose /// This function centralizes the UUID-based simplex matching logic used across multiple @@ -87,10 +89,9 @@ fn finite_coordinate() -> impl Strategy { /// 3. Find matching simplex in transformed triangulation /// 4. Compare quality metrics between matched simplices /// -/// Degenerate Delaunay inputs can have more than one valid simplex set, so an -/// independently constructed transformed triangulation may choose a different -/// valid tessellation. Unmatched simplices are skipped; cases with no comparable -/// simplices are rejected rather than treated as metric failures. +/// Degenerate Delaunay inputs can have more than one valid simplex set, but +/// these invariance properties require a full one-to-one simplex match before +/// comparing metrics. A missing match is treated as a property failure. fn compare_transformed_simplices( dt_orig: &DelaunayTriangulation, (), (), D>, dt_transformed: &DelaunayTriangulation, (), (), D>, @@ -104,7 +105,15 @@ where { let tds_orig = dt_orig.tds(); let tds_transformed = dt_transformed.tds(); + let orig_simplex_count = tds_orig.simplex_keys().count(); + let transformed_simplex_count = tds_transformed.simplex_keys().count(); + prop_assert_eq!( + orig_simplex_count, + transformed_simplex_count, + "transformed triangulation must have the same simplex count for a one-to-one match" + ); let mut matched_simplices = 0usize; + let mut matched_transformed = Vec::with_capacity(transformed_simplex_count); // Iterate through all simplices in original triangulation for orig_key in tds_orig.simplex_keys() { @@ -125,6 +134,7 @@ where "all original simplex UUIDs should map to transformed UUIDs" ); + let mut matched_key = None; for trans_key in tds_transformed.simplex_keys() { prop_assert!( tds_transformed.simplex(trans_key).is_some(), @@ -138,16 +148,36 @@ where .iter() .all(|u| trans_simplex_uuids.contains(u)) { - // Found matching simplex - compare quality metrics - compare_fn(orig_key, trans_key)?; - matched_simplices += 1; - break; // Found the match, no need to check other simplices + matched_key = Some(trans_key); + break; } } } + + prop_assert!( + matched_key.is_some(), + "missing transformed simplex matching original simplex {orig_key:?}" + ); + let trans_key = matched_key.expect("matched key was checked above"); + prop_assert!( + !matched_transformed.contains(&trans_key), + "transformed simplex {trans_key:?} matched more than one original simplex" + ); + matched_transformed.push(trans_key); + compare_fn(orig_key, trans_key)?; + matched_simplices += 1; } - prop_assume!(matched_simplices >= 1); + prop_assert_eq!( + matched_simplices, + orig_simplex_count, + "every original simplex should have a transformed counterpart" + ); + prop_assert_eq!( + matched_simplices, + transformed_simplex_count, + "every transformed simplex should be matched exactly once" + ); Ok(()) } @@ -358,11 +388,10 @@ macro_rules! test_quality_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let tds = dt.tds(); let tri = dt.as_triangulation(); for simplex_key in tds.simplex_keys() { @@ -389,11 +418,10 @@ macro_rules! test_quality_properties { ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")), translation in prop::array::[](finite_coordinate()) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Translate all vertices let translated_vertices: Vec<_> = vertices .iter() @@ -410,11 +438,10 @@ macro_rules! test_quality_properties { let translated_vertices = try_vertices_from_points(&translated_vertices) .expect("finite point coordinates"); - if let Ok(dt_translated) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &translated_vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt_translated) = DelaunayTriangulationBuilder::new(&translated_vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Build mapping from original UUIDs to translated UUIDs let uuid_map: HashMap<_, _> = vertices.iter() .zip(translated_vertices.iter()) @@ -464,11 +491,10 @@ macro_rules! test_quality_properties { ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")), translation in prop::array::[](finite_coordinate()) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Translate all vertices let translated_vertices: Vec<_> = vertices .iter() @@ -485,11 +511,10 @@ macro_rules! test_quality_properties { let translated_vertices = try_vertices_from_points(&translated_vertices) .expect("finite point coordinates"); - if let Ok(dt_translated) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &translated_vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt_translated) = DelaunayTriangulationBuilder::new(&translated_vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Build UUID mapping let uuid_map: HashMap<_, _> = vertices.iter() .zip(translated_vertices.iter()) @@ -539,11 +564,10 @@ macro_rules! test_quality_properties { ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")), scale in 0.1f64..10.0f64 ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Scale all vertices uniformly let scaled_vertices: Vec<_> = vertices .iter() @@ -560,11 +584,10 @@ macro_rules! test_quality_properties { let scaled_vertices = try_vertices_from_points(&scaled_vertices) .expect("finite point coordinates"); - if let Ok(dt_scaled) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &scaled_vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt_scaled) = DelaunayTriangulationBuilder::new(&scaled_vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { // Build UUID mapping let uuid_map: HashMap<_, _> = vertices.iter() .zip(scaled_vertices.iter()) @@ -613,11 +636,10 @@ macro_rules! test_quality_properties { $min_vertices..=$max_vertices ).prop_map(|v| try_vertices_from_points(&v).expect("finite point coordinates")) ) { - if let Ok(dt) = DelaunayTriangulation::, (), (), $dim>::try_with_topology_guarantee( - &AdaptiveKernel::default(), - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let tds = dt.tds(); let tri = dt.as_triangulation(); for simplex_key in tds.simplex_keys() { @@ -695,10 +717,10 @@ macro_rules! test_facet_topology_invariant { ) ) { // Build triangulation - if let Ok(dt) = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let tri = dt.as_triangulation(); // Get all simplex keys @@ -725,10 +747,10 @@ macro_rules! test_facet_topology_invariant { ) ) { // Build triangulation - if let Ok(dt) = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let mut tri = dt.as_triangulation().clone(); // Get all simplex keys @@ -761,10 +783,10 @@ macro_rules! test_facet_topology_invariant { ) ) { // Build triangulation - if let Ok(dt) = DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + if let Ok(dt) = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { let tri = dt.as_triangulation(); // Empty simplex list should always return None @@ -821,10 +843,10 @@ macro_rules! gen_high_dim_facet_topology_smoke { let mut stats = stats.borrow_mut(); stats.generated += 1; - let dt = match DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) { + let dt = match DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + { Ok(dt) => dt, Err(err) => { stats.rejected_construction_failed += 1; diff --git a/tests/public_topology_api.rs b/tests/public_topology_api.rs index d4084683..800f9674 100644 --- a/tests/public_topology_api.rs +++ b/tests/public_topology_api.rs @@ -52,11 +52,9 @@ fn standard_simplex_vertices() fn assert_split_topology_single_simplex() -> Result<(), PublicTopologyApiTestError> { let vertices = standard_simplex_vertices::()?; - let dt: DelaunayTriangulation<_, (), (), D> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - )?; + let dt: DelaunayTriangulation<_, (), (), D> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build()?; let tri = dt.as_triangulation(); let expected_edges = D * (D + 1) / 2; @@ -126,11 +124,9 @@ fn edges_and_incident_edges_on_single_tetrahedron() -> Result<(), PublicTopology vertex!([0.0, 0.0, 1.0])?, ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - )?; + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build()?; let tri = dt.as_triangulation(); assert_eq!(dt.number_of_vertices(), 4); @@ -194,11 +190,9 @@ fn split_topology_indexes_on_double_tetrahedron() -> Result<(), PublicTopologyAp vertex!([1.0, 0.7, -1.5])?, ]; - let dt: DelaunayTriangulation<_, (), (), 3> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - )?; + let dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build()?; let tri = dt.as_triangulation(); assert_eq!(tri.number_of_vertices(), 5); diff --git a/tests/regressions.rs b/tests/regressions.rs index e5d7ddde..c69c62dd 100644 --- a/tests/regressions.rs +++ b/tests/regressions.rs @@ -281,13 +281,10 @@ fn regression_issue_447_exact_layered_strip_preserves_collinear_boundary_vertice "exact degenerate construction mode should document that Level 5 enforcement is disabled", ); - let (default_dt, default_stats) = - DelaunayTriangulation::<_, u32, i32, 2>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::DEFAULT, - exact_degenerate_options, - ) + let (default_dt, default_stats) = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .construction_options(exact_degenerate_options) + .build_with_kernel_and_statistics(&kernel) .expect("exact layered CDT strip point construction should succeed"); assert_exact_strip_construction_result( @@ -301,13 +298,11 @@ fn regression_issue_447_exact_layered_strip_preserves_collinear_boundary_vertice .with_insertion_order(InsertionOrderStrategy::Input) .with_retry_policy(RetryPolicy::Disabled) .without_final_delaunay_enforcement(); - let (input_dt, input_stats) = - DelaunayTriangulation::<_, u32, i32, 2>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::Pseudomanifold, - input_options, - ) + let (input_dt, input_stats) = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .construction_options(input_options) + .build_with_kernel_and_statistics(&kernel) .expect("input-order exact layered CDT strip construction should succeed"); assert_exact_strip_construction_result( @@ -317,16 +312,16 @@ fn regression_issue_447_exact_layered_strip_preserves_collinear_boundary_vertice &input_signatures, ); - let no_stats_dt = - DelaunayTriangulation::<_, u32, i32, 2>::try_with_topology_guarantee_and_options( - &kernel, - &vertices, - TopologyGuarantee::Pseudomanifold, + let no_stats_dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .topology_guarantee(TopologyGuarantee::Pseudomanifold) + .construction_options( ConstructionOptions::default() .with_insertion_order(InsertionOrderStrategy::Input) .with_retry_policy(RetryPolicy::Disabled) .without_final_delaunay_enforcement(), ) + .build_with_kernel(&kernel) .expect("non-stat exact layered CDT strip construction should honor non-enforcing policy"); assert_eq!( @@ -353,7 +348,8 @@ fn regression_issue_447_explicit_exact_strip_default_strict_level5_fails() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .expect("exact CDT strip explicit simplex specs should validate") - .build::() + .simplex_data_type::() + .build() .expect_err("strict explicit construction should reject the non-Delaunay CDT strip"); assert!( @@ -377,6 +373,7 @@ fn regression_issue_447_explicit_exact_strip_preserves_vertices_without_level5_e let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .expect("exact CDT strip explicit simplex specs should validate") + .simplex_data_type::() .construction_options( ConstructionOptions::default() .without_final_delaunay_enforcement() @@ -384,7 +381,7 @@ fn regression_issue_447_explicit_exact_strip_preserves_vertices_without_level5_e NonZeroUsize::new(2).unwrap(), )), ) - .build_with_kernel::<_, i32>(&kernel) + .build_with_kernel(&kernel) .expect("explicit exact CDT strip should import under the non-enforcing policy"); assert_eq!( @@ -486,11 +483,9 @@ fn regression_empty_circumsphere_2d_minimal_case() { vertex!([-93.661_180_847_043, 1.562_430_007_326_195_9]).unwrap(), ]; - let mut dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let mut dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); if dt.is_valid_delaunay().is_err() { @@ -519,11 +514,9 @@ fn regression_issue_120_minimal_failing_input_2d() { vertex!([0.0, 38.424]).unwrap(), ]; - let dt: DelaunayTriangulation<_, (), (), 2> = - DelaunayTriangulation::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) + let dt: DelaunayTriangulation<_, (), (), 2> = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() .unwrap(); if let Err(err) = dt.validate() { @@ -558,7 +551,7 @@ fn regression_periodic_neighbor_validation_uses_lifted_vertex_offsets() { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0_f64; 2]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect("periodic 2D build should succeed"); assert!( @@ -594,7 +587,7 @@ fn regression_issue_306_3d_construction_succeeds() { .collect(); let dt: Result, _> = - DelaunayTriangulation::try_new(&vertices); + DelaunayTriangulation::builder(&vertices).build(); assert!( dt.is_ok(), "35-vertex 3D construction with seed 0x{seed:X} should succeed \ @@ -616,13 +609,10 @@ fn regression_issue_307_4d_bulk_repair_keeps_positive_orientation() { let options = ConstructionOptions::default() .with_insertion_order(InsertionOrderStrategy::Input) .with_retry_policy(RetryPolicy::Disabled); - let (dt, stats) = - DelaunayTriangulation::, (), (), 4>::try_with_options_and_statistics( - &kernel, - &vertices, - TopologyGuarantee::PLManifoldStrict, - options, - ) + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifoldStrict) + .construction_options(options) + .build_with_kernel_and_statistics(&kernel) .expect("4D bulk construction should not fail after repair orientation cleanup"); assert_eq!( @@ -680,15 +670,15 @@ fn regression_issue_204_4d_500_local_repair_budget() { .map(|p| vertex!(p.into()).unwrap()) .collect(); - let (dt, stats) = - DelaunayTriangulation::<_, (), (), 4>::try_new_with_construction_statistics(&vertices) - .unwrap_or_else(|e| { - panic!( - "#204 regression: 4D {n_points}-point construction with seed 0x{seed:X} \ + let (dt, stats) = DelaunayTriangulation::builder(&vertices) + .build_with_statistics() + .unwrap_or_else(|e| { + panic!( + "#204 regression: 4D {n_points}-point construction with seed 0x{seed:X} \ (ball radius {ball_radius}) must succeed after Fix 2; got: {}", - e.error - ) - }); + e.error + ) + }); assert_eq!( stats.inserted, n_points, diff --git a/tests/semgrep/src/project_rules/rust_style.rs b/tests/semgrep/src/project_rules/rust_style.rs index db55ba72..6692ccd3 100644 --- a/tests/semgrep/src/project_rules/rust_style.rs +++ b/tests/semgrep/src/project_rules/rust_style.rs @@ -376,23 +376,112 @@ pub fn triangulation_fallible_constructor_names_bad( ); } +pub fn legacy_delaunay_batch_constructors_bad( + vertices: &[Vertex<(), 3>], + options: ConstructionOptions, +) { + let topology_guarantee = TopologyGuarantee::PLManifold; + + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + let _dt = DelaunayTriangulation::try_new(vertices); + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + let _dt = DelaunayTriangulation::try_new_with_construction_statistics(vertices); + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + let _dt = DelaunayTriangulation::, (), (), 3>::try_new_with_options( + vertices, + options, + ); + let _dt = + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + DelaunayTriangulation::try_new_with_topology_guarantee(vertices, topology_guarantee); + let _dt = + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + DelaunayTriangulation::try_new_with_options_and_construction_statistics(vertices, options); + let _dt = + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + DelaunayTriangulation::, (), (), 3>::try_with_kernel( + &AdaptiveKernel::new(), + vertices, + ); + let _dt = + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + DelaunayTriangulation::try_with_topology_guarantee(vertices, topology_guarantee); + let _dt = + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + DelaunayTriangulation::try_with_topology_guarantee_and_options( + vertices, + topology_guarantee, + options, + ); + let _dt = + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructors + DelaunayTriangulation::try_with_options_and_statistics(vertices, options); +} + +impl DelaunayTriangulation { + #[doc(hidden)] + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructor-definitions + pub fn try_new( + _vertices: &[Vertex], + ) -> Result { + todo!() + } + + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructor-definitions + pub(crate) fn try_with_kernel( + _kernel: &K, + _vertices: &[Vertex], + ) -> Result { + todo!() + } +} + +impl DelaunayTriangulation, (), (), D> { + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructor-definitions + pub(crate) fn try_new_with_construction_statistics( + _vertices: &[Vertex<(), D>], + ) -> Result<(Self, ConstructionStatistics), DelaunayTriangulationConstructionErrorWithStatistics> + { + todo!() + } +} + +impl DelaunayTriangulation +where + K: Kernel, + U: DataType, + V: DataType, +{ + // ruleid: delaunay.rust.no-legacy-delaunay-try-new-constructor-definitions + pub(crate) fn try_with_options_and_statistics( + _vertices: &[Vertex], + _options: ConstructionOptions, + ) -> Result<(Self, ConstructionStatistics), DelaunayTriangulationConstructionErrorWithStatistics> + { + todo!() + } +} + pub fn triangulation_fallible_constructor_names_ok( vertices: &[Vertex<(), 3>], options: ConstructionOptions, ) { // ok: delaunay.rust.no-infallible-fallible-triangulation-constructors - let _dt = DelaunayTriangulation::try_new(vertices); + let _dt = DelaunayTriangulation::builder(vertices).build(); // ok: delaunay.rust.no-infallible-fallible-triangulation-constructors - let _dt = DelaunayTriangulation::<_, (), (), 3>::try_new_with_options(vertices, options); + let _dt = DelaunayTriangulation::builder(vertices) + .construction_options(options) + .build(); // ok: delaunay.rust.no-infallible-fallible-triangulation-constructors - let _dt = DelaunayTriangulation::, (), (), 3>::try_with_kernel( - &AdaptiveKernel::new(), - vertices, - ); + let _dt = DelaunayTriangulationBuilder::new(vertices).build_with_kernel(&AdaptiveKernel::new()); // ok: delaunay.rust.no-infallible-fallible-triangulation-constructors let _dt: DelaunayTriangulation<_, (), (), 3> = DelaunayTriangulation::empty(); // ok: delaunay.rust.no-infallible-fallible-triangulation-constructors let _builder = DelaunayTriangulationBuilder::new(vertices); + // ok: delaunay.rust.no-legacy-delaunay-try-new-constructors + let _dt = DelaunayTriangulationBuilder::new(vertices) + .construction_options(options) + .build(); } pub fn convex_hull_fallible_constructor_names_bad( diff --git a/tests/serialization_vertex_preservation.rs b/tests/serialization_vertex_preservation.rs index 1509d32e..b7990322 100644 --- a/tests/serialization_vertex_preservation.rs +++ b/tests/serialization_vertex_preservation.rs @@ -53,11 +53,10 @@ fn test_vertex_preservation_with_duplicates_3d() { ); // Construct triangulation - duplicates should be skipped - let dt = DelaunayTriangulation::<_, (), (), 3>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .expect("Tds construction succeeded"); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .expect("DelaunayTriangulation::builder(...).build() failed"); let tds = dt.tds(); let tds_vertex_count = tds.vertices().count(); @@ -113,11 +112,10 @@ fn test_vertex_preservation_without_duplicates_3d() { ]; let vertices = try_vertices_from_points(&points).expect("finite point coordinates"); - let dt = DelaunayTriangulation::<_, (), (), 3>::try_new_with_topology_guarantee( - &vertices, - TopologyGuarantee::PLManifold, - ) - .expect("Tds construction failed"); + let dt = DelaunayTriangulation::builder(&vertices) + .topology_guarantee(TopologyGuarantee::PLManifold) + .build() + .expect("Tds construction failed"); let tds = dt.tds(); let tds_vertex_count = tds.vertices().count(); diag_debug!( @@ -184,7 +182,9 @@ fn test_vertex_preservation_many_duplicates_3d() { // Use Input ordering to avoid Hilbert dedup collapsing duplicates before the initial simplex let opts = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); - let dt = DelaunayTriangulation::<_, (), (), 3>::try_new_with_options(&vertices, opts) + let dt = DelaunayTriangulation::builder(&vertices) + .construction_options(opts) + .build() .expect("Tds construction succeeded"); let tds = dt.tds(); let tds_vertex_count = tds.vertices().count(); diff --git a/tests/triangulation_builder.rs b/tests/triangulation_builder.rs index ece2cf3d..18d4df04 100644 --- a/tests/triangulation_builder.rs +++ b/tests/triangulation_builder.rs @@ -11,7 +11,7 @@ use std::collections::HashMap; use std::f64::consts::TAU; use delaunay::prelude::construction::{ - ConstructionOptions, DelaunayConstructionFailure, DelaunayTriangulation, + ConstructionOptions, DelaunayConstructionFailure, DelaunayResult, DelaunayTriangulation, DelaunayTriangulationBuilder, DelaunayTriangulationConstructionError, ExplicitConstructionError, InsertionOrderStrategy, TopologyGuarantee, Vertex, }; @@ -28,7 +28,7 @@ use delaunay::prelude::validation::{TriangulationValidationError, ValidationPoli // Euclidean path // ============================================================================= -/// Builder with no options set should produce the same triangulation as `new()`. +/// The associated builder alias should match the explicit builder path. #[test] fn test_builder_euclidean_matches_new_2d() { let vertices = vec![ @@ -37,9 +37,11 @@ fn test_builder_euclidean_matches_new_2d() { vertex!([0.0, 1.0]).unwrap(), ]; - let dt_new = DelaunayTriangulation::try_new(&vertices).expect("new() should succeed"); + let dt_new = DelaunayTriangulation::builder(&vertices) + .build() + .expect("associated builder should succeed"); let dt_builder = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .expect("builder should succeed"); assert_eq!(dt_new.number_of_vertices(), dt_builder.number_of_vertices()); @@ -60,7 +62,7 @@ fn test_builder_euclidean_3d() { vertex!([0.0, 0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .expect("3D build should succeed"); assert_eq!(dt.number_of_vertices(), 4); assert_eq!(dt.dim(), 3); @@ -77,7 +79,7 @@ fn test_builder_topology_guarantee_propagated() { ]; let dt = DelaunayTriangulationBuilder::new(&vertices) .topology_guarantee(TopologyGuarantee::Pseudomanifold) - .build::<()>() + .build() .expect("build should succeed"); assert_eq!(dt.topology_guarantee(), TopologyGuarantee::Pseudomanifold); } @@ -94,13 +96,33 @@ fn test_builder_validation_policy_derived_from_topology_guarantee() { let dt = DelaunayTriangulationBuilder::new(&vertices) .topology_guarantee(TopologyGuarantee::PLManifoldStrict) - .build::<()>() + .build() .expect("build should succeed"); assert_eq!(dt.topology_guarantee(), TopologyGuarantee::PLManifoldStrict); assert_eq!(dt.validation_policy(), ValidationPolicy::Always); } +#[test] +fn test_builder_constructs_typed_simplex_storage_for_follow_on_fill() -> DelaunayResult<()> { + let vertices = vec![ + vertex!([0.0_f64, 0.0])?, + vertex!([1.0, 0.0])?, + vertex!([0.0, 1.0])?, + ]; + let mut dt = DelaunayTriangulationBuilder::new(&vertices) + .simplex_data_type::() + .build()?; + + dt.fill_simplex_data(|_, simplex| simplex.number_of_vertices()); + + for (_, simplex) in dt.simplices() { + assert_eq!(simplex.data(), Some(&3)); + } + dt.validate()?; + Ok(()) +} + /// Custom `ConstructionOptions` are accepted and the triangulation is valid. #[test] fn test_builder_custom_construction_options() { @@ -112,12 +134,55 @@ fn test_builder_custom_construction_options() { let opts = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); let dt = DelaunayTriangulationBuilder::new(&vertices) .construction_options(opts) - .build::<()>() + .build() .expect("build should succeed"); assert_eq!(dt.number_of_vertices(), 3); assert!(dt.validate().is_ok()); } +/// The fluent statistics terminal exposes insertion stats and total timing for Euclidean builds. +#[test] +fn test_builder_build_with_statistics_euclidean_records_telemetry() { + let vertices = vec![ + vertex!([0.0_f64, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + ]; + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .build_with_statistics() + .expect("Euclidean statistics build should succeed"); + + assert_eq!(dt.number_of_vertices(), stats.inserted); + assert_eq!(stats.total_skipped(), 0); + assert!(stats.total_attempts >= stats.inserted); + assert!(stats.telemetry.has_data()); + assert!(stats.telemetry.construction_total_nanos > 0); + assert!(dt.validate().is_ok()); +} + +/// Canonicalized toroidal construction has the same fluent statistics terminal. +#[test] +fn test_builder_build_with_statistics_canonicalized_toroidal_records_telemetry() { + let vertices = vec![ + vertex!([0.2_f64, 0.3]).unwrap(), + vertex!([1.8, 0.1]).unwrap(), + vertex!([0.5, 0.7]).unwrap(), + vertex!([-0.4, 0.9]).unwrap(), + ]; + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .try_canonicalized_toroidal([1.0, 1.0]) + .unwrap() + .build_with_statistics() + .expect("canonicalized toroidal statistics build should succeed"); + + assert_eq!(dt.number_of_vertices(), stats.inserted); + assert_eq!(stats.total_skipped(), 0); + assert!(stats.total_attempts >= stats.inserted); + assert!(stats.telemetry.has_data()); + assert!(stats.telemetry.construction_total_nanos > 0); + assert!(dt.validate().is_ok()); +} + // ============================================================================= // Convenience entry point // ============================================================================= @@ -131,7 +196,7 @@ fn test_builder_convenience() { vertex!([0.0, 1.0]).unwrap(), ]; let dt = DelaunayTriangulation::builder(&vertices) - .build::<()>() + .build() .expect("builder() convenience method should succeed"); assert_eq!(dt.number_of_vertices(), 3); assert!(dt.validate().is_ok()); @@ -149,7 +214,7 @@ fn test_builder_canonicalized_toroidal_convenience() { let dt = DelaunayTriangulation::builder(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("canonicalized toroidal builder should succeed"); assert_eq!(dt.number_of_vertices(), 4); assert!(dt.as_triangulation().validate().is_ok()); @@ -181,13 +246,13 @@ fn test_builder_canonicalized_toroidal_canonicalizes_coordinates() { let dt_canonical = DelaunayTriangulationBuilder::new(&canonical_vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("canonical build should succeed"); let dt_shifted = DelaunayTriangulationBuilder::new(&shifted_vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("shifted build should succeed"); assert_eq!( @@ -214,7 +279,7 @@ fn test_builder_canonicalized_toroidal_validates_2d() { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("canonicalized toroidal build should succeed"); assert!( @@ -235,7 +300,7 @@ fn test_builder_canonicalized_toroidal_delaunay_property_valid_2d() { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("canonicalized toroidal build should succeed"); assert!( @@ -256,7 +321,7 @@ fn test_builder_canonicalized_toroidal_2d_euler_dimension() { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("build should succeed"); assert_eq!( @@ -275,12 +340,12 @@ fn test_builder_canonicalized_toroidal_matches_euclidean_on_canonical_input() { vertex!([0.4, 0.9]).unwrap(), ]; let dt_euclidean = DelaunayTriangulationBuilder::new(&vertices) - .build::<()>() + .build() .expect("euclidean build should succeed"); let dt_toroidal = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("canonicalized toroidal build should succeed"); assert_eq!( @@ -313,7 +378,7 @@ fn test_builder_canonicalized_toroidal_larger_point_set_2d() { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .expect("larger canonicalized toroidal build should succeed"); assert_eq!(dt.number_of_vertices(), 8); @@ -338,7 +403,7 @@ fn test_builder_canonicalized_toroidal_robust_kernel_3d() { let dt = DelaunayTriangulationBuilder::new(&vertices) .try_canonicalized_toroidal([1.0, 1.0, 1.0]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect("canonicalized toroidal robust kernel 3D build should succeed"); assert_eq!(dt.number_of_vertices(), 5); @@ -382,7 +447,7 @@ fn build_toroidal_triangulation() let dt = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0_f64; D]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect("periodic build should succeed"); assert_eq!(dt.number_of_vertices(), expected_vertices); @@ -442,12 +507,12 @@ fn test_builder_toroidal_boundary_query_is_topology_aware() { let kernel = RobustKernel::new(); let euclidean = DelaunayTriangulationBuilder::new(&vertices) - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect("Euclidean build should succeed for toroidal fixture points"); let toroidal = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0_f64; 2]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect("periodic toroidal builder should succeed"); assert_eq!( @@ -473,13 +538,34 @@ fn test_builder_toroidal_convenience() { let dt = DelaunayTriangulation::builder(&vertices) .try_toroidal([1.0_f64; 2]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect("periodic toroidal builder should succeed"); assert!(dt.global_topology().is_periodic()); assert!(dt.tds().is_valid().is_ok()); } +/// Periodic quotient construction exposes the same fluent statistics terminal. +#[test] +fn test_builder_build_with_statistics_periodic_toroidal_records_telemetry() { + let vertices = toroidal_vertices::<2>(); + let kernel = RobustKernel::new(); + let (dt, stats) = DelaunayTriangulationBuilder::new(&vertices) + .try_toroidal([1.0_f64; 2]) + .unwrap() + .build_with_kernel_and_statistics(&kernel) + .expect("periodic toroidal statistics build should succeed"); + + assert_eq!(dt.number_of_vertices(), stats.inserted); + assert_eq!(stats.total_skipped(), 0); + assert_eq!(stats.total_attempts, 0); + assert_eq!(stats.telemetry.insertion_wall_time_calls, 0); + assert!(stats.telemetry.has_data()); + assert!(stats.telemetry.construction_total_nanos > 0); + assert!(dt.global_topology().is_periodic()); + assert!(dt.validate().is_ok()); +} + macro_rules! gen_toroidal_validation_test { ($dim:literal, $label:ident, $run_level4:expr $(, #[$attr:meta])?) => { pastey::paste! { @@ -533,7 +619,7 @@ fn test_builder_toroidal_3d_fails_fast_until_scalable_quotient() { let err = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0_f64; 3]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect_err("compact periodic 3D quotient remains pending scalable selection"); match err { @@ -561,7 +647,7 @@ macro_rules! gen_toroidal_high_dim_guardrail_test { let err = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0_f64; $dim]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect_err(concat!( stringify!($dim), "D periodic quotient construction should fail fast pending #416" @@ -596,7 +682,7 @@ fn test_builder_toroidal_large_dimension_fails_before_expansion_math() { let err = DelaunayTriangulationBuilder::new(&vertices) .try_toroidal([1.0_f64; 64]) .unwrap() - .build_with_kernel::<_, ()>(&kernel) + .build_with_kernel(&kernel) .expect_err("64D periodic quotient should fail before computing 3^D image count"); match err { @@ -644,7 +730,7 @@ fn test_explicit_toroidal_heawood_torus_rejected() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() .global_topology(topology) - .build::<()>() + .build() .expect_err("explicit toroidal connectivity requires a quotient embedding validator"); match err { @@ -677,7 +763,7 @@ fn test_explicit_toroidal_torus_euler_mismatch_without_override() { // Build with default Euclidean topology — should fail at Euler validation. let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect_err("explicit torus without Toroidal metadata should fail Euler validation"); match err { @@ -735,7 +821,7 @@ fn test_explicit_2d_two_triangle_quad() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("explicit 2D build should succeed"); assert_eq!(dt.number_of_vertices(), 4); @@ -764,6 +850,32 @@ fn test_explicit_2d_two_triangle_quad() { ); } +/// Explicit connectivity exposes the same fluent statistics terminal. +#[test] +fn test_builder_build_with_statistics_explicit_records_telemetry() { + let vertices = vec![ + vertex!([0.0_f64, 0.0]).unwrap(), + vertex!([1.0, 0.0]).unwrap(), + vertex!([1.0, 1.0]).unwrap(), + vertex!([0.0, 1.0]).unwrap(), + ]; + let simplices = vec![vec![0, 1, 2], vec![0, 2, 3]]; + + let (dt, stats) = + DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) + .unwrap() + .build_with_statistics() + .expect("explicit statistics build should succeed"); + + assert_eq!(dt.number_of_vertices(), stats.inserted); + assert_eq!(stats.total_skipped(), 0); + assert_eq!(stats.total_attempts, 0); + assert_eq!(stats.telemetry.insertion_wall_time_calls, 0); + assert!(stats.telemetry.has_data()); + assert!(stats.telemetry.construction_total_nanos > 0); + assert!(dt.validate().is_ok()); +} + /// Explicit construction normalizes incoherent local simplex orderings. /// /// Swapping two vertices in one simplex flips its local orientation relative to an @@ -782,7 +894,7 @@ fn test_explicit_normalizes_incoherent_simplex_order() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("explicit build should normalize incoherent simplex ordering"); assert!( @@ -806,7 +918,7 @@ fn test_explicit_3d_two_tetrahedra() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("explicit 3D build should succeed"); assert_eq!(dt.number_of_vertices(), 5); @@ -828,8 +940,9 @@ fn test_explicit_round_trip_3d() { vertex!([1.0, 1.0, 1.0]).unwrap(), ]; - let dt_original = - DelaunayTriangulation::try_new(&vertices).expect("Delaunay build should succeed"); + let dt_original = DelaunayTriangulation::builder(&vertices) + .build() + .expect("Delaunay build should succeed"); let original_vertex_count = dt_original.number_of_vertices(); let original_simplex_count = dt_original.number_of_simplices(); @@ -861,7 +974,7 @@ fn test_explicit_round_trip_3d() { &simplex_specs, ) .unwrap() - .build::<()>() + .build() .expect("explicit 3D reconstruction should succeed"); assert_eq!(dt_reconstructed.number_of_vertices(), original_vertex_count); @@ -886,8 +999,9 @@ fn test_explicit_round_trip_2d() { ]; // Build via standard Delaunay. - let dt_original = - DelaunayTriangulation::try_new(&vertices).expect("Delaunay build should succeed"); + let dt_original = DelaunayTriangulation::builder(&vertices) + .build() + .expect("Delaunay build should succeed"); let original_vertex_count = dt_original.number_of_vertices(); let original_simplex_count = dt_original.number_of_simplices(); @@ -921,7 +1035,7 @@ fn test_explicit_round_trip_2d() { &simplex_specs, ) .unwrap() - .build::<()>() + .build() .expect("explicit reconstruction should succeed"); assert_eq!(dt_reconstructed.number_of_vertices(), original_vertex_count); @@ -947,7 +1061,11 @@ fn test_explicit_error_empty_simplices() { let result = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices); - assert!(result.is_err(), "Empty simplices should produce an error"); + assert_matches!( + result.err(), + Some(ExplicitConstructionError::EmptySimplices), + "Empty simplices should produce an error" + ); } /// Error: wrong simplex arity. @@ -964,7 +1082,15 @@ fn test_explicit_error_wrong_arity() { let result = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices); - assert!(result.is_err(), "Wrong arity should produce an error"); + assert_matches!( + result.err(), + Some(ExplicitConstructionError::InvalidSimplexArity { + simplex_index: 0, + actual: 2, + expected: 3, + }), + "Wrong arity should produce an error" + ); } /// Error: out-of-bounds vertex index. @@ -980,8 +1106,13 @@ fn test_explicit_error_index_out_of_bounds() { let result = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices); - assert!( - result.is_err(), + assert_matches!( + result.err(), + Some(ExplicitConstructionError::IndexOutOfBounds { + simplex_index: 0, + vertex_index: 99, + bound: 3, + }), "Out-of-bounds index should produce an error" ); } @@ -999,7 +1130,14 @@ fn test_explicit_error_duplicate_vertex_in_simplex() { let result = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices); - assert!(result.is_err(), "Duplicate vertex should produce an error"); + assert_matches!( + result.err(), + Some(ExplicitConstructionError::DuplicateVertexInSimplex { + simplex_index: 0, + vertex_index: 1, + }), + "Duplicate vertex should produce an error" + ); } /// Minimal case: a single triangle in 2D. @@ -1014,7 +1152,7 @@ fn test_explicit_2d_single_triangle() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("single triangle should succeed"); assert_eq!(dt.number_of_vertices(), 3); @@ -1035,7 +1173,7 @@ fn test_explicit_3d_single_tetrahedron() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("single tetrahedron should succeed"); assert_eq!(dt.number_of_vertices(), 4); @@ -1066,7 +1204,7 @@ fn test_explicit_non_delaunay_mesh() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect_err("non-Delaunay mesh must not construct a DelaunayTriangulation"); assert!( @@ -1097,7 +1235,7 @@ fn test_explicit_topology_guarantee_propagated() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() .topology_guarantee(TopologyGuarantee::Pseudomanifold) - .build::<()>() + .build() .expect("build should succeed"); assert_eq!(dt.topology_guarantee(), TopologyGuarantee::Pseudomanifold); @@ -1115,7 +1253,7 @@ fn test_explicit_preserves_vertex_data() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("explicit build with vertex data should succeed"); let mut data: Vec = dt @@ -1143,7 +1281,7 @@ fn test_explicit_validate_delaunay_mesh() { let dt = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .expect("build should succeed"); assert!( @@ -1165,7 +1303,7 @@ fn test_explicit_unreferenced_vertices_rejected() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .unwrap_err(); assert!( @@ -1234,7 +1372,7 @@ fn test_explicit_error_variant_non_manifold_facet() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .unwrap_err(); let DelaunayTriangulationConstructionError::ExplicitConstruction( @@ -1269,7 +1407,10 @@ fn test_explicit_error_variant_duplicate_vertex_in_simplex() { assert!( matches!( err, - ExplicitConstructionError::DuplicateVertexInSimplex { simplex_index: 0 } + ExplicitConstructionError::DuplicateVertexInSimplex { + simplex_index: 0, + vertex_index: 1, + } ), "Expected DuplicateVertexInSimplex, got: {err}" ); @@ -1289,7 +1430,7 @@ fn test_explicit_error_variant_incompatible_topology() { .unwrap() .try_canonicalized_toroidal([1.0, 1.0]) .unwrap() - .build::<()>() + .build() .unwrap_err(); assert!( @@ -1318,7 +1459,7 @@ fn test_explicit_error_variant_unsupported_construction_options() { .construction_options( ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input), ) - .build::<()>() + .build() .unwrap_err(); assert!( @@ -1339,7 +1480,7 @@ fn test_explicit_error_variant_unsupported_construction_options() { .without_final_delaunay_enforcement() .with_insertion_order(InsertionOrderStrategy::Input), ) - .build::<()>() + .build() .unwrap_err(); assert!( @@ -1365,7 +1506,7 @@ fn test_explicit_error_variant_duplicate_simplices_structural_validation() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .unwrap_err(); let DelaunayTriangulationConstructionError::ExplicitConstruction( @@ -1393,7 +1534,7 @@ fn test_explicit_error_variant_geometric_nondegeneracy() { let err = DelaunayTriangulationBuilder::try_from_vertices_and_simplices(&vertices, &simplices) .unwrap() - .build::<()>() + .build() .unwrap_err(); match err { diff --git a/uv.lock b/uv.lock index c9a30a26..fa37ae54 100644 --- a/uv.lock +++ b/uv.lock @@ -467,7 +467,7 @@ dev = [ { name = "semgrep", specifier = "==1.168.0" }, { name = "shellcheck-py", specifier = "==0.11.0.1" }, { name = "shfmt-py", specifier = "==4.0.0" }, - { name = "ty", specifier = "==0.0.55" }, + { name = "ty", specifier = "==0.0.56" }, { name = "yamllint", specifier = "==1.38.0" }, ] notebooks = [ @@ -1154,11 +1154,11 @@ wheels = [ [[package]] name = "narwhals" -version = "2.22.1" +version = "2.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/3c/c4ef2164a71c1a63d7f1ae411c4082c5fa872405106db60a4b7114989ad7/narwhals-2.22.1.tar.gz", hash = "sha256:d62920805a0a43b7ff8b54b0c0d3142d796f8a9301836ada37e573d6a33cbcd9", size = 647493, upload-time = "2026-06-05T12:34:34.051Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/66ed1fc6e38a0c0f330627ec5c5d597990d6159b6712b82af0ad2c65f06c/narwhals-2.23.0.tar.gz", hash = "sha256:13e7ff5b4bb4a2f77b907c2e4d8a76e273dfc1323a3c997440a2f9fd26aed408", size = 656209, upload-time = "2026-07-01T11:21:53.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/ca/36339329c4604adbcc99c899b7eb1ce1a555c499b6a6860757dc9bfed36d/narwhals-2.22.1-py3-none-any.whl", hash = "sha256:60567d774edf77db53906f89d9fbd164e66e56d66d388e1e6990f17ac33cfb53", size = 454815, upload-time = "2026-06-05T12:34:32.289Z" }, + { url = "https://files.pythonhosted.org/packages/f4/4e/afc8c31605cb8be1d3bb4438c4d979daa104dab6306cd2b87abe9c3a7299/narwhals-2.23.0-py3-none-any.whl", hash = "sha256:769e7b9ab102c93d8fa019f6b4cd1a657909b04a20bf6210e5a35aae06814ae9", size = 458938, upload-time = "2026-07-01T11:21:51.677Z" }, ] [[package]] @@ -1460,35 +1460,52 @@ wheels = [ [[package]] name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, ] [[package]] @@ -2288,36 +2305,36 @@ wheels = [ [[package]] name = "ty" -version = "0.0.55" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" }, - { url = "https://files.pythonhosted.org/packages/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" }, - { url = "https://files.pythonhosted.org/packages/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" }, - { url = "https://files.pythonhosted.org/packages/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" }, - { url = "https://files.pythonhosted.org/packages/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" }, - { url = "https://files.pythonhosted.org/packages/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" }, - { url = "https://files.pythonhosted.org/packages/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" }, - { url = "https://files.pythonhosted.org/packages/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" }, +version = "0.0.56" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/07/fb29aea5235b0aa8ecfc4d1cc6ddf9fba8b863d67d96c6d345694d644c43/ty-0.0.56.tar.gz", hash = "sha256:84d114dc3796361c0fc72945016eabd74d46b9ee64f198cb0e485719704681e5", size = 6050123, upload-time = "2026-07-01T16:44:56.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/48/bce79e7ca5c1cc529d3e0d37ddd1121aea4b68a4f749974ad1cc77161871/ty-0.0.56-py3-none-linux_armv6l.whl", hash = "sha256:186d4a53e15747c947e1ec3d7eec8e345d8e40a1ca10e634c585db52497e87dd", size = 11643066, upload-time = "2026-07-01T16:44:18.374Z" }, + { url = "https://files.pythonhosted.org/packages/80/d1/22555d8a1d719661f10050f3865d877bbf497da908961c75fe22217dd18a/ty-0.0.56-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aae1a980fd9535da0469b7ba2b2e1b54a907743a5e0f442dd57eee9f5bfd034c", size = 11407487, upload-time = "2026-07-01T16:44:20.956Z" }, + { url = "https://files.pythonhosted.org/packages/cf/2d/b3b7a74ce8bc59ef48843ad80179bb0d9598bbd6cfc0d11d519bdf6b1352/ty-0.0.56-py3-none-macosx_11_0_arm64.whl", hash = "sha256:afd3058c0a6c5f241e814734f133008c93ee805f61c9cf4ce7412b8822b5d9ad", size = 10962270, upload-time = "2026-07-01T16:44:22.959Z" }, + { url = "https://files.pythonhosted.org/packages/64/ac/6c2fd7de0304a8a7218a756af74f7e62a5e8540fdb175e0a869e51042345/ty-0.0.56-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:058b52f7a823ac13aae3cae30809dd6b5145794b64d8478f9ef38c75d79b4483", size = 11471406, upload-time = "2026-07-01T16:44:25.327Z" }, + { url = "https://files.pythonhosted.org/packages/50/b6/11d861156861c03c7726b74558f9a0e0092661aff83a4fda1279df28c425/ty-0.0.56-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c66e00c1522add1f2bbdd2e45828c953b35c306b7bef03ec9169c75a63699a0", size = 11445612, upload-time = "2026-07-01T16:44:27.531Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ba/09df108582090f3c0770ec4bc8675affed60248f6793a78d909be16211d9/ty-0.0.56-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40903d71c669a30691b5a5d5728056c7877a1bd6be4f233a38883a8b28cf34d7", size = 12093889, upload-time = "2026-07-01T16:44:29.548Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f7/dbb4b4ccb69cd64c209ae55b1ab788ace8222c2bc1f6845be9e7cbedbf25/ty-0.0.56-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63fe3947fe0c46c69a7d950e6832ee70a9ec17321fefbff3d2e3c20baf9e5bd0", size = 12666337, upload-time = "2026-07-01T16:44:31.586Z" }, + { url = "https://files.pythonhosted.org/packages/86/e9/73f903fe4a3d9ea02f26f57c1eb07e3b1029ec92b0e8c2364718893440e3/ty-0.0.56-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71a0c1a72f9854532e710e119b6871ffe4542c8a65146f1f65dcd78fecd885b4", size = 12280247, upload-time = "2026-07-01T16:44:33.637Z" }, + { url = "https://files.pythonhosted.org/packages/d6/90/cebd222495832f1a00dcd321ba25f3cab804221a4991b992c2178bec68ee/ty-0.0.56-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70d1665596494e24d8ebd198438872b5a56ec3cae5f2bcf6c673be797acc4e3c", size = 11991107, upload-time = "2026-07-01T16:44:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/b7/07/8f7337a07250f42d975cdb6decf47fc5b421e6c7da5e3e7be1e85f63a7e5/ty-0.0.56-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:778f99e51558afc1dbbe48ee38ab6aae7b31390ed8c1a1ef1499b295e9f1e82f", size = 12298970, upload-time = "2026-07-01T16:44:38.243Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b9/a52cd59034a48f5f18c6b155cc2cc36861d874b6d0af204b12c898024c3d/ty-0.0.56-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:867bc5708e0066bb4ff6c7db524bd5deea2676c62bfe71d3303138b3be850af0", size = 11425683, upload-time = "2026-07-01T16:44:40.473Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2e/48e42d33357d52eefb695c0c3fcfc96879b73668a7447d1d1e0ad774fedc/ty-0.0.56-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6012f4189c928edb330a37deb9930f982380bd4aa7c4b8e0428eec9651c7551", size = 11469258, upload-time = "2026-07-01T16:44:42.513Z" }, + { url = "https://files.pythonhosted.org/packages/d5/01/ad1b4138be1e3fa97863af3925aa2134f17a593240c35dc38c3429fb5ad1/ty-0.0.56-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8ee83de1a7ff4cc32837ec06134ce391d441bc5b35ecd8d3cfe053f120f3e4c1", size = 11758736, upload-time = "2026-07-01T16:44:44.567Z" }, + { url = "https://files.pythonhosted.org/packages/09/34/9d81967ff240eaa57e9249728ef7b7790747cf6d3c9a98ec86b2cfdcc8ee/ty-0.0.56-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:62619b3b0e2c6248ef30d3f0e2f2217ae9893040585be07f32324242f197cd6f", size = 12100242, upload-time = "2026-07-01T16:44:46.584Z" }, + { url = "https://files.pythonhosted.org/packages/c3/36/f51d4666d2de6cf33c1f3a1fcc4bb6b70b197dd6ceaa491eef71d78fe8e8/ty-0.0.56-py3-none-win32.whl", hash = "sha256:b30687bb5cd9729d34c889a289edf32770388d9bb05243e534e723fb45e0381b", size = 11093759, upload-time = "2026-07-01T16:44:49.171Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b4/8fb5d4acfa4afb152245b20fa263069a7547bd1f8e4bfca4eda280c897d7/ty-0.0.56-py3-none-win_amd64.whl", hash = "sha256:ad4c8c47b6f4e3f9ed3fc0b1a5d650088d229e17dd8f63c1826d6bbe94cc3235", size = 12100327, upload-time = "2026-07-01T16:44:51.26Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fc/6a183e71edde90d0c35c2303f23f7a45b6891d1a2c45daf7b8f869831e19/ty-0.0.56-py3-none-win_arm64.whl", hash = "sha256:57538f273d444a5f1293fa7860e967178afe3917611fc5eff16b64e1204fe0d6", size = 11538780, upload-time = "2026-07-01T16:44:53.8Z" }, ] [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]]