Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions benches/boundary_uuid_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_m
use delaunay::prelude::construction::{DelaunayTriangulation, vertex};
use delaunay::prelude::generators::generate_random_points_in_range_seeded;
use delaunay::prelude::geometry::CoordinateRange;
use delaunay::prelude::query::BoundaryAnalysis;
use delaunay::prelude::query::FacetIncidenceAnalysis;
use delaunay::try_vertices_from_points;
use uuid::Uuid;

Expand Down Expand Up @@ -76,13 +76,13 @@ fn bench_boundary_facets_micro(c: &mut Criterion) {
.collect::<Result<Vec<_>, _>>()
.or_abort();
group.bench_with_input(
BenchmarkId::new("is_boundary_facet_3d", requested_vertices),
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_boundary_facet(facet).or_abort())
.filter(|facet| dt.tds().is_one_sided_facet(facet).or_abort())
.count();
black_box(confirmed);
});
Expand Down
18 changes: 9 additions & 9 deletions benches/common/flip_workflows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use delaunay::prelude::geometry::{CoordinateConversionError, Point, RobustKernel
use delaunay::prelude::query::{JaccardComputationError, format_jaccard_report};
use delaunay::prelude::tds::{FacetError, InvariantError, TdsError, VertexKey};
use delaunay::prelude::topology::validation::{
ManifoldError, RidgeVertices, RidgeVerticesError, ridge_star_simplices,
ManifoldError, RidgeCandidate, RidgeCandidateError, ridge_star_simplices,
};
use delaunay::prelude::validation::DelaunayTriangulationValidationError;
use thiserror::Error;
Expand Down Expand Up @@ -93,14 +93,14 @@ pub enum FlipWorkflowError {
source: Box<FlipError>,
},

/// Ridge vertices could not be parsed into a valid ridge vertex set.
#[error("invalid ridge vertices for {ridge:?}: {source}")]
InvalidRidgeVertices {
/// Ridge vertices could not be parsed into a valid ridge candidate.
#[error("invalid ridge candidate for {ridge:?}: {source}")]
InvalidRidgeCandidate {
/// Ridge handle being inspected.
ridge: RidgeHandle,
/// Underlying ridge vertex parsing failure.
/// Underlying ridge candidate parsing failure.
#[source]
source: RidgeVerticesError,
source: RidgeCandidateError,
},

/// Ridge-star support collection failed.
Expand Down Expand Up @@ -1289,16 +1289,16 @@ fn ridge_support_points<const D: usize>(
});
}

let ridge_vertices = RidgeVertices::<D>::try_from_vertices(
let ridge_candidate = RidgeCandidate::<D>::try_from_vertices(
simplex
.vertices()
.iter()
.enumerate()
.filter(|(index, _)| *index != omit_a && *index != omit_b)
.map(|(_, vertex_key)| *vertex_key),
)
.map_err(|source| FlipWorkflowError::InvalidRidgeVertices { ridge, source })?;
let star_simplices = ridge_star_simplices(dt.tds(), &ridge_vertices)
.map_err(|source| FlipWorkflowError::InvalidRidgeCandidate { ridge, source })?;
let star_simplices = ridge_star_simplices(dt.tds(), &ridge_candidate)
.map_err(|source| ridge_star_error(ridge, source))?;

let mut keys = Vec::new();
Expand Down
4 changes: 2 additions & 2 deletions benches/profiling_suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,15 +1138,15 @@ fn bench_bottlenecks(c: &mut Criterion) {
},
|dt| {
if let Some(dt) = dt {
let boundary_facets = match dt.tds().boundary_facets() {
let boundary_facets = match dt.tds().one_sided_facets() {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"boundary_facets failed: {error}"
));
}
};
black_box(boundary_facets);
black_box(boundary_facets.len());
}
},
BatchSize::LargeInput,
Expand Down
28 changes: 21 additions & 7 deletions docs/api_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ fn main() -> DelaunayResult<()> {

### Advanced Construction: `DelaunayTriangulationBuilder`

For advanced configuration (toroidal topology, custom validation policies, etc.),
For advanced configuration (domain wrapping, toroidal topology, custom validation policies, etc.),
use `DelaunayTriangulationBuilder`:

```rust
Expand All @@ -103,7 +103,7 @@ use delaunay::prelude::construction::{
use delaunay::prelude::validation::ValidationPolicy;

fn main() -> DelaunayResult<()> {
// Canonicalized toroidal triangulation in 2D
// Euclidean triangulation of points canonicalized into a toroidal domain.
let vertices = vec![
vertex![0.1, 0.1]?,
vertex![0.9, 0.9]?,
Expand All @@ -112,7 +112,7 @@ fn main() -> DelaunayResult<()> {

let mut dt = DelaunayTriangulationBuilder::new(&vertices)
.try_canonicalized_toroidal([1.0, 1.0])
? // Canonicalized toroidal construction
? // Wrap input coordinates before Euclidean construction.
.topology_guarantee(TopologyGuarantee::PLManifoldStrict)
.build::<()>()?;

Expand All @@ -127,7 +127,8 @@ fn main() -> DelaunayResult<()> {
**When to use the Builder:**

- **Toroidal construction**: Use `.try_toroidal()` for periodic image-point construction or
`.try_canonicalized_toroidal()` for canonicalized construction with explicit domain periods.
`.try_canonicalized_toroidal()` for Euclidean construction after wrapping input coordinates
into explicit domain periods.
The periodic image-point path is release-validated in 2D and compact 3D; 4D/5D
fail fast pending scalable quotient construction in issue #416.
- **Custom topology guarantees**: Set stricter or more relaxed manifold checks
Expand Down Expand Up @@ -390,8 +391,9 @@ Topology APIs use names to make ownership visible:

- `*View` values borrow the canonical owner or are lifetime-bound to it, so they
cannot outlive the storage they observe. Examples include `FacetView<'tds>`,
`IncidenceView<'tds>`, `EdgeIndex<'tds>`, `SimplexNeighborIndex<'tds>`, and
`TriangulationAdjacency<'tds>`.
`EdgeView<'tds>`, `RidgeView<'tds>`, `RidgeLinkView<'tds>`,
`IncidenceView<'tds>`, `EdgeIndex<'tds>`, `SimplexNeighborIndex<'tds>`,
and `TriangulationAdjacency<'tds>`.
- Borrowed slices over canonical storage follow the same rule. For example,
`Tds::simplex_vertices(simplex_key)` validates the key relation, then returns
the simplex's stored `&[VertexKey]` instead of copying detached keys into a
Expand All @@ -401,10 +403,22 @@ Topology APIs use names to make ownership visible:
them against a live owner before reading through them. Examples include
`VertexKey`, `SimplexKey`, `FacetHandle`, `RidgeHandle`, `EdgeKey`, and
`TriangleHandle`.
- Proof-bearing runtime candidates such as `RidgeCandidate<D>` may validate
local arity, uniqueness, and canonical ordering without borrowing an owner,
but they are still detached storage-local values. Convert them to
`RidgeQuery<'tds>` before asking live-TDS questions that may have an empty
answer, or to `RidgeView<'tds>` when the API requires an existing ridge.
`RidgeView` construction proves the candidate vertices are live and have a
non-empty incident simplex star.
- Toroidal covering-space identities such as `LiftedVertexId` and
`LiftedLinkEdge` live under `topology::spaces::toroidal`. They are runtime
graph identities, not TDS storage entries or durable IDs. They preserve
periodic image identity for link traversal and validation; collapsing them to
bare `VertexKey`s is an explicit quotient-space operation.
- Owned snapshots are allowed only when the data must cross a persistence,
detached-analysis, or cache boundary. `TdsSnapshot`/`RawTdsSnapshot` are the
durable UUID persistence boundary. `ConvexHull` is a logically immutable hull
snapshot that stores `FacetHandle`s, while `ConvexHull::facets(triangulation)`
snapshot that stores `FacetHandle`s, while `ConvexHull::try_facets(triangulation)`
returns borrowed `FacetView` values and `ConvexHull::facet_handles()` exposes
the detached handles explicitly.
- Transactional rollback state may own cloned topology or exact mutation
Expand Down
39 changes: 27 additions & 12 deletions docs/code_organization.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ delaunay/
│ │ │ ├── spatial_hash_grid.rs
│ │ │ └── triangulation_maps.rs
│ │ ├── traits/
│ │ │ ├── boundary_analysis.rs
│ │ │ ├── data_type.rs
│ │ │ ├── facet_incidence_analysis.rs
│ │ │ └── facet_cache.rs
│ │ ├── util/
│ │ │ ├── canonical_points.rs
Expand All @@ -198,10 +198,10 @@ delaunay/
│ │ │ ├── measurement.rs
│ │ │ └── uuid.rs
│ │ ├── adjacency.rs
│ │ ├── boundary.rs
│ │ ├── construction.rs
│ │ ├── edge.rs
│ │ ├── facet.rs
│ │ ├── facet_incidence.rs
│ │ ├── insertion.rs
│ │ ├── operations.rs
│ │ ├── orientation.rs
Expand Down Expand Up @@ -478,9 +478,10 @@ paths instead.
`TriangulationAdjacency` view (opt-in)
- `collections/` - Optimized collection types and spatial acceleration structures
- `spatial_hash_grid.rs` - Hash-grid spatial index for duplicate detection and locate-hint selection
- `boundary.rs` - Boundary detection and analysis
- `facet_incidence.rs` - TDS-level one-sided/two-sided facet incidence analysis; true boundary
classification lives in the topology-aware `Triangulation`/manifold layer
- `algorithms/` - Core algorithms (incremental insertion, flips, point location, PL-manifold repair)
- `traits/` - Core trait definitions including FacetCacheProvider for performance optimization
- `traits/` - Core boundary/data trait definitions plus internal facet-cache plumbing for performance-sensitive algorithms
- `util/` - General utility functions organized by functionality (replaced single `util.rs` file)
- `uuid.rs` - UUID generation and validation
- `hashing.rs` - Stable, deterministic hash primitives
Expand All @@ -494,6 +495,18 @@ paths instead.
- `canonical_points.rs` - Canonical vertex-ordering helpers for geometric predicate call sites (SoS consistency)
- `operations.rs` - Semantic classification and telemetry for topological operations

`edge.rs` and `facet.rs` stay in `src/core/` because they are direct TDS
traversal primitives: `EdgeKey`/`EdgeView` validate endpoint incidence against
canonical storage, and `FacetHandle`/`FacetView` describe a codimension-1 face
of one stored simplex via `simplex_key + facet_index`. They are useful for
adjacency, boundary, hull, and query operations without invoking Level 3
manifold reasoning. `src/topology/ridge.rs` is intentionally different: a ridge
is a codimension-2 topology construct whose concrete shape depends on `D`
(vertex in 2D, edge in 3D, triangle in 4D, and so on). Its candidate/query/view
types support ridge stars, lifted toroidal links, and PL-manifold validation, so
they belong to the topology layer. Dependency direction should remain
`topology` -> `core`, not the reverse.

Public namespace policy: `crate::core` is the internal implementation namespace
for the low-level TDS and algorithm layer. The public low-level surface is
exposed through curated modules and focused preludes (`delaunay::tds`,
Expand Down Expand Up @@ -521,8 +534,8 @@ benchmarks, and tests clear about which part of the API they exercise.
| Points, coordinate ranges, kernels, predicates, and geometric measures | `use delaunay::prelude::geometry::*` |
| Random points or triangulations for examples, tests, and benchmarks | `use delaunay::prelude::generators::*` |
| Read-only traversal, adjacency, convex hulls, and comparison helpers | `use delaunay::prelude::query::*` |
| Topological spaces and topology traits | `use delaunay::prelude::topology::spaces::*` |
| Topology validation and Euler characteristic helpers | `use delaunay::prelude::topology::validation::*` |
| Topological spaces, topology traits, and lifted toroidal IDs | `use delaunay::prelude::topology::spaces::*` |
| Topology validation, Euler characteristic helpers, and ridge queries | `use delaunay::prelude::topology::validation::*` |

`use delaunay::prelude::*` remains available for quick experiments and broad
interactive use, but repository examples and benchmarks prefer focused preludes.
Expand Down Expand Up @@ -582,8 +595,10 @@ than through a `delaunay::delaunay` or `delaunay::triangulation` facade.

- `characteristics/euler.rs` - Euler characteristic computation for full complexes and boundaries
- `characteristics/validation.rs` - Topological validation functions
- `manifold.rs` - Topology-only manifold invariants (e.g., closed boundary checks; see
[`invariants.md`](invariants.md))
- `manifold.rs` - Topology-only manifold invariants and boundary classification
over declared global topology (e.g., closed boundary checks; see [`invariants.md`](invariants.md))
- `ridge.rs` - Ridge candidates, borrowed ridge queries/views, lifted ridge-link
views, and ridge-star map builders used by topology validation and localized repair
- `spaces/euclidean.rs` - Euclidean space topology helper implementation (f64-oriented)
- `spaces/spherical.rs` - Spherical space topology helper implementation (f64-oriented)
- `spaces/toroidal.rs` - Toroidal space topology helper implementation (f64-oriented)
Expand All @@ -601,7 +616,7 @@ than through a `delaunay::delaunay` or `delaunay::triangulation` facade.
- **`benches/`** - Performance benchmarks with automated baseline management (2D-5D coverage) and memory allocation tracking
(see: [benches/profiling_suite.rs](../benches/README.md#profiling-suite) and
[benches/allocation_hot_paths.rs](../benches/README.md))
- **`tests/`** - Integration tests including basic TDS validation (creation, neighbor assignment, boundary analysis),
- **`tests/`** - Integration tests including basic TDS validation (creation, neighbor assignment, facet-incidence analysis),
debugging utilities, regression testing, allocation-measurement smoke coverage
(see: [tests/allocation_api.rs](../tests/README.md#allocation_apirs)), and robust predicates validation
- **`docs/`** - User and contributor documentation, including architecture/reference guides,
Expand Down Expand Up @@ -785,7 +800,7 @@ This `justfile`-based workflow provides consistent, cross-platform development c
## Module Organization Patterns

The canonical organizational patterns found across key modules in the codebase:
`simplex.rs`, `vertex.rs`, `facet.rs`, `boundary.rs`, and the `util/` submodules
`simplex.rs`, `vertex.rs`, `facet.rs`, `facet_incidence.rs`, and the `util/` submodules
under `src/core/util/`.

### Canonical Section Sequence
Expand Down Expand Up @@ -1150,10 +1165,10 @@ mod tests {
- Adjacency testing
- Error handling for geometric constraints

#### `boundary.rs` (small module)
#### `facet_incidence.rs` (small module)

- Trait implementation focused
- Algorithm-specific testing
- TDS one-sided/two-sided incidence testing
- Performance benchmarking
- Integration with TDS

Expand Down
2 changes: 1 addition & 1 deletion docs/dev/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ views must borrow the canonical owner, or return values lifetime-bound to that
owner, so the view cannot outlive the data it observes. Detached, copyable
runtime references should be named `*Handle` or `*Key` instead, and APIs that
turn handles back into views must revalidate the handle against a live owner at
the conversion boundary. For example, `ConvexHull::facets(triangulation)`
the conversion boundary. For example, `ConvexHull::try_facets(triangulation)`
returns borrowed `FacetView<'_>` values, while `ConvexHull::facet_handles()`
exposes the stored `FacetHandle`s explicitly.

Expand Down
38 changes: 38 additions & 0 deletions docs/dev/tooling-alignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ The useful updates ported in this pass are:
are blocked alongside unqualified impls. These rules complement the existing
constructor and direct-storage serde guards without making the validated
snapshot representation public API.
- Repository-owned Semgrep now encodes the #461 borrowed-view naming
convention: types named `*View` and `RidgeQuery` must carry a leading
lifetime parameter, while detached ridge values use `RidgeCandidate` and
fallible `try_from_vertices`/`try_new` constructors. This keeps Views,
Handles, Keys, Candidates, Snapshots, and Reports orthogonal in both naming
and lifetime semantics. Semgrep also guards the topology-boundary convention:
raw one-sided facet incidence from `facet_to_simplices.values()` must not be
used as semantic boundary classification; callers should use topology-aware
manifold helpers so periodic self-identifications remain closed topology and
open one-sided facets in closed spaces stay errors.
- The borrowed-view rule's generic-parameter detector is now written as a YAML
block scalar with a literal Rust lifetime lookahead (`(?!')`) instead of the
ambiguous single-quoted YAML spelling (`(?!'')`). This makes the configured
regex visibly reject lifetime-bound views such as `struct FooView<'tds>` and
match only non-lifetime generic parameters, preserving the intended #461
View/Handle/Candidate convention.
- `.github/workflows/rust-clippy.yml` now matches the hardened SARIF pipeline:
`set -euo pipefail`, `clippy::cargo`, and guarded upload that skips missing
SARIF files and forked pull-request uploads.
Expand Down Expand Up @@ -297,6 +313,28 @@ Some causal-triangulations tooling remains project-specific and was not ported:
was folded into that harness so `.github/workflows/profiling-benchmarks.yml`
and `just profile-dev` exercise the same real construction, memory,
validation, and traversal workloads.

### Large-Scale Smoke Parameters

`just perf-large-scale-smoke` is a Delaunay-specific local guard over the
release-mode `debug_large_scale_{2,3,4,5}d` tests, not a sibling-repository
tooling convention. It exists to catch obvious construction, repair, and
validation slowdowns before a PR leaves a developer machine, while keeping
benchmark-quality regression detection in `just perf-no-regressions` and the
Criterion harnesses.

The current smoke sizes are calibrated per dimension: 32,000 vertices in 2D,
9,000 in 3D, 1,000 in 4D, and 160 in 5D. Lower dimensions use larger point
clouds because they need more vertices to expose traversal and repair costs;
higher dimensions scale down aggressively because simplex counts, exact
predicate work, and topology checks grow much faster. The progress chunks
of 2,000, 500, 100, and 20 vertices keep timeout/progress reporting visible
without turning logging and validation cadence into the measured workload.
These values are intentionally coarse canaries designed to finish in roughly
50 seconds per dimension, leaving headroom under the default 60-second cap.
They should be recalibrated only from same-machine local runs when the
construction envelope changes.

- CDT's concise `docs/dev/commands.md` structure; Delaunay keeps its more
detailed benchmark-profile guidance because it documents the `perf` profile,
local performance-regression guard, calibrated benchmark canaries, and release
Expand Down
Loading
Loading