Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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