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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ combinatorial and geometric checks.
well-conditioned exploratory work; not accepted by explicit repair APIs)
- [x] Bulk insertion ordering (`InsertionOrderStrategy`): [Hilbert curve] (default) or input order
- [x] Batch construction options (`ConstructionOptions`): optional deduplication and deterministic retries
- [x] Incremental construction APIs: insertion plus vertex removal (`remove_vertex`)
- [x] Incremental construction APIs: insertion plus transactional vertex removal (`remove_vertex`)
- [x] 4-level validation hierarchy (element validity → TDS structural validity → manifold topology → Delaunay property), including full diagnostics via `validation_report`
- [x] Local topology validation ([PL-manifold] default, [Pseudomanifold] opt-out)
- [x] Coherent combinatorial orientation validation/normalization for cells, maintaining oriented simplicial complexes
Expand Down Expand Up @@ -144,6 +144,8 @@ For the full periodic image-point method (Phase 2), see the [`DelaunayTriangulat
see [`docs/workflows.md`](docs/workflows.md) for a minimal example and [`docs/api_design.md`](docs/api_design.md) for details.
- **Flip-based Delaunay repair**, including the heuristic rebuild fallback (`repair_delaunay_with_flips*`):
see [`docs/workflows.md`](docs/workflows.md).
- **Repair diagnostics and mutating-operation rollback**:
`remove_vertex` rolls back if post-removal repair or orientation canonicalization fails, and repair failures preserve typed source errors for debugging.
- **Insertion outcomes and statistics** (`insert_with_statistics`, `InsertionOutcome`, `InsertionStatistics`):
see [`docs/workflows.md`](docs/workflows.md) and [`docs/numerical_robustness_guide.md`](docs/numerical_robustness_guide.md).
- **Topology guarantees** (`TopologyGuarantee`) and **automatic topology validation** (`ValidationPolicy`):
Expand Down
19 changes: 15 additions & 4 deletions docs/api_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ let mut dt: DelaunayTriangulation<_, (), (), 3> =
let new_vertex = vertex!([0.5, 0.5, 0.5]);
dt.insert(new_vertex).unwrap();

// Vertex removal (maintains Delaunay property via fan retriangulation)
// Vertex removal (topology-preserving, with automatic repair when enabled)
let vertex_key = dt.vertices().next().unwrap().0;
dt.remove_vertex(vertex_key).unwrap();
```
Expand Down Expand Up @@ -125,14 +125,21 @@ for topology guarantee and validation policy details.

### Key Characteristics

- **Automatic property preservation**: The Delaunay empty-circumsphere property is maintained automatically
- **Automatic property preservation**: Insertion maintains the Delaunay
empty-circumsphere property; removal runs flip-based repair when the active
`DelaunayRepairPolicy` permits it
- **Cavity-based insertion**: New vertices are inserted by identifying conflicting cells, removing them, and filling the cavity
- **Fan retriangulation**: Vertex removal uses fan-based retriangulation of the vertex star
- **Transactional vertex removal**: Vertex removal uses an inverse k=1 fast path
when possible and fan-based retriangulation otherwise. If post-removal
Delaunay repair or orientation canonicalization fails, the triangulation and
internal caches are restored to their pre-removal state.
- **Auxiliary data**: Vertices and cells carry optional user data (`U` / `V`). Read via `vertex.data()` /
`cell.data()`, write via `dt.set_vertex_data(key, data)` / `dt.set_cell_data(key, data)` (O(1),
invariant-preserving). See [`workflows.md`](workflows.md) for examples.
- **Error handling**: Operations fail gracefully if they would violate invariants (see
[`invariants.md`](invariants.md)).
[`invariants.md`](invariants.md)). Mutating operations that invoke repair use
typed repair diagnostics where available, for example
`RepairOperationFailed { operation, source }`.
- **Validation**: The active `ValidationPolicy` (set with
`dt.set_validation_policy(...)`) governs automatic topology validation for
subsequent construction/modification operations
Expand Down Expand Up @@ -370,6 +377,10 @@ assert!(outcome.topology_repair.succeeded);
empty-circumsphere property.
3. **Optional fallback rebuild** — rebuilds from the vertex set when both
repair passes fail (`DelaunayizeConfig { fallback_rebuild: true, .. }`).
If a failed topology repair is recovered by fallback rebuild,
`outcome.topology_repair.succeeded` remains `false`; use
`outcome.used_fallback_rebuild` to distinguish successful rebuild recovery
from direct repair success.

### Configuration

Expand Down
26 changes: 26 additions & 0 deletions docs/dev/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Agents must follow these rules when modifying or adding Rust code.
- [Core Principles](#core-principles)
- [Safety](#safety)
- [Dimension Generic Architecture](#dimension-generic-architecture)
- [Numeric Conversions](#numeric-conversions)
- [Borrowing and Ownership](#borrowing-and-ownership)
- [Error Handling](#error-handling)
- [Panic Policy](#panic-policy)
Expand Down Expand Up @@ -100,6 +101,31 @@ Algorithms should operate generically over `D` whenever practical.

---

## Numeric Conversions

Avoid unchecked numeric casts in geometry, topology, tests, and benchmarks when
precision or range can matter.

Prefer repository helpers from `crate::geometry::util`, for example:

- `safe_usize_to_scalar::<T>(value)`
- `safe_scalar_to_f64(value)`
- `safe_scalar_from_f64::<T>(value)`
- `safe_coords_to_f64(coords)`
- `safe_coords_from_f64::<T, D>(coords)`

Do not silence `clippy::cast_precision_loss` with `#[expect(...)]` simply
because the current values are small. Use a safe conversion helper and handle
or justify the `Result` at the call site. A lint expectation is appropriate only
when no safe conversion applies and the invariant is documented in the code.

Avoid fallback conversions such as `unwrap_or(f64::NAN)`,
`unwrap_or(f64::INFINITY)`, or silently clamping failed conversions. These hide
the numerical state that geometric predicates and validation layers need in
order to fail explicitly.

---

## Borrowing and Ownership

Prefer **borrowing APIs** whenever possible.
Expand Down
20 changes: 14 additions & 6 deletions docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,10 @@ For guidance on retry/skip behavior and choosing `RobustKernel`, see

## Builder API: removing a vertex

Vertex removal is supported and preserves Levels 1–3, but it may not preserve the Delaunay
property in all cases. If you need the Delaunay property after removals, run a repair pass and/or
validate explicitly.
Vertex removal is supported and preserves Levels 1–3. It uses an inverse k=1 fast path when
possible and fan retriangulation otherwise, then runs flip-based Delaunay repair when the active
`DelaunayRepairPolicy` allows it. If post-removal repair or orientation canonicalization fails,
the operation rolls back to the pre-removal triangulation.

```rust
use delaunay::prelude::triangulation::*;
Expand All @@ -328,11 +329,18 @@ let _cells_removed = dt.remove_vertex(vertex_key).unwrap();
// Topology should still be valid:
assert!(dt.as_triangulation().validate().is_ok());

// If you need Delaunay after edits (requires K: ExactPredicates):
// dt.repair_delaunay_with_flips().unwrap();
// dt.is_valid().unwrap();
// If automatic repair is enabled, successful removal has already attempted to
// restore the Delaunay property.
dt.is_valid().unwrap();
```

When automatic repair fails after the mutation, `remove_vertex` reports
`InvariantError::Delaunay(DelaunayTriangulationValidationError::RepairOperationFailed { operation:
DelaunayRepairOperation::VertexRemoval, source })`, preserving the underlying
`DelaunayRepairError` for callers that need to inspect the exact repair failure.
Successful removals invalidate internal locate hints and the spatial index so subsequent queries do
not observe stale topology-dependent cache entries.

## Edit API: minimal flip example

The Edit API exposes explicit bistellar flips. These operations do **not** automatically restore
Expand Down
6 changes: 3 additions & 3 deletions scripts/tests/test_benchmark_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1070,9 +1070,9 @@ def test_written_baseline_round_trips_through_parser(self, mock_git, tmp_path) -
assert actual.time_mean == pytest.approx(expected.time_mean)
assert actual.time_high == pytest.approx(expected.time_high)
assert actual.time_unit == expected.time_unit
assert actual.throughput_low == expected.throughput_low
assert actual.throughput_mean == expected.throughput_mean
assert actual.throughput_high == expected.throughput_high
assert actual.throughput_low == pytest.approx(expected.throughput_low)
assert actual.throughput_mean == pytest.approx(expected.throughput_mean)
assert actual.throughput_high == pytest.approx(expected.throughput_high)
assert actual.throughput_unit == expected.throughput_unit
mock_git.assert_called_once()

Expand Down
Loading
Loading