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
83 changes: 79 additions & 4 deletions src/cdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::cdt::ConflictRegionEnd::{EdgeOverlap, Existing};
use crate::delaunay_core::dcel_operations::flip_cw;
use crate::delaunay_core::{bulk_load_cdt, bulk_load_stable};
use crate::delaunay_core::{bulk_load_cdt, bulk_load_stable, try_bulk_load_cdt};
use crate::{
delaunay_core::Dcel, intersection_iterator::LineIntersectionIterator, PositionInTriangulation,
SpadeNum,
Expand Down Expand Up @@ -345,7 +345,42 @@
///
/// Panics if any constraint edges overlap. Panics if the edges contain an invalid index (out of range).
pub fn bulk_load_cdt(vertices: Vec<V>, edges: Vec<[usize; 2]>) -> Result<Self, InsertionError> {
let mut result = bulk_load_cdt(vertices, edges)?;
let mut result = bulk_load_cdt(vertices, edges).unwrap();
*result.hint_generator_mut() = L::initialize_from_triangulation(&result);
Ok(result)
}

/// Same behaviour as [bulk_load_cdt], but rather than panicking,
/// ignores and calls the parameter function `on_conflict_found` for each conflicting edges encountered.
///
/// # Example
/// ```
/// # fn main() -> Result<(), spade::InsertionError> {
/// use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation};
/// let mut vertices = vec![
/// Point2::new(0.0, 1.0),
/// Point2::new(1.0, 2.0),
/// Point2::new(3.0, -3.0),
/// Point2::new(-1.0, -2.0),
/// Point2::new(-4.0, -5.0),
/// ];
/// let mut conflicting_edges = Vec::new();
/// let mut edges = vec![[0, 1], [1, 2], [2, 3], [3, 4]];
/// let cdt = ConstrainedDelaunayTriangulation::<_>::try_bulk_load_cdt(vertices.clone(), edges, |e| conflicting_edges.push(e))?;
///
/// assert_eq!(cdt.num_vertices(), 5);
/// assert_eq!(cdt.num_constraints(), 4);
/// // The order will usually change
/// assert_ne!(cdt.vertices().map(|v| v.position()).collect::<Vec<_>>(), vertices);
/// # Ok(())
/// # }
/// ```
pub fn try_bulk_load_cdt(
vertices: Vec<V>,
edges: Vec<[usize; 2]>,
on_conflict_found: impl FnMut([usize; 2]),
) -> Result<Self, InsertionError> {
let mut result = try_bulk_load_cdt(vertices, edges, on_conflict_found)?;
*result.hint_generator_mut() = L::initialize_from_triangulation(&result);
Ok(result)
}
Expand Down Expand Up @@ -406,12 +441,29 @@
/// # Ok(())
/// # }
/// ```
///
/// # See also
///
/// See also [Self::try_bulk_load_cdt_stable]
pub fn bulk_load_cdt_stable(
vertices: Vec<V>,
edges: Vec<[usize; 2]>,
) -> Result<Self, InsertionError> {
let mut result: Self =
bulk_load_stable(move |vertices| bulk_load_cdt(vertices, edges), vertices)?;
Self::try_bulk_load_cdt_stable(vertices, edges, |e| {
panic!("Conflicting edge encountered: {};{}", e[0], e[1])
})
}

/// See [Self::bulk_load_cdt_stable]
pub fn try_bulk_load_cdt_stable(
vertices: Vec<V>,
edges: Vec<[usize; 2]>,
on_conflict_found: impl FnMut([usize; 2]),
) -> Result<Self, InsertionError> {
let mut result: Self = bulk_load_stable(
|vertices| try_bulk_load_cdt(vertices, edges, on_conflict_found),
vertices,
)?;
*result.hint_generator_mut() = L::initialize_from_triangulation(&result);
Ok(result)
}
Expand Down Expand Up @@ -691,7 +743,7 @@
///
/// See also [Self::get_conflicting_edges_between_vertices]
pub fn get_conflicting_edges_between_points(
&self,

Check warning on line 746 in src/cdt.rs

View workflow job for this annotation

GitHub Actions / Fuzz

lifetime flowing from input to output with different syntax can be confusing
from: Point2<<V as HasPosition>::Scalar>,
to: Point2<<V as HasPosition>::Scalar>,
) -> impl Iterator<Item = DirectedEdgeHandle<V, DE, CdtEdge<UE>, F>> {
Expand All @@ -707,7 +759,7 @@
///
/// See also [Self::get_conflicting_edges_between_points]
pub fn get_conflicting_edges_between_vertices(
&self,

Check warning on line 762 in src/cdt.rs

View workflow job for this annotation

GitHub Actions / Fuzz

lifetime flowing from input to output with different syntax can be confusing
from: FixedVertexHandle,
to: FixedVertexHandle,
) -> impl Iterator<Item = DirectedEdgeHandle<V, DE, CdtEdge<UE>, F>> {
Expand Down Expand Up @@ -1995,6 +2047,29 @@
Cdt::bulk_load_cdt_stable(vertices, vec![[3, 2], [5, 4], [7, 6]])
}

#[test]
fn get_try_cdt_for_duplicate_vertices() -> Result<(), InsertionError> {
let vertices = vec![
Point2::new(0.0, -10.0),
Point2::new(76.0, 0.0),
Point2::new(76.0, 0.0), // Duplicated vertex
Point2::new(20.0, -30.0),
Point2::new(45.0, 25.0),
Point2::new(32.0, -35.0),
Point2::new(60.0, 20.0),
Point2::new(60.0, -30.0),
Point2::new(50.0, -34.0),
];
let mut conflicting_edges = Vec::new();
let cdt = Cdt::try_bulk_load_cdt_stable(vertices, vec![[3, 2], [5, 4], [7, 6]], |e| {
conflicting_edges.push(e)
})?;
// Hardcoded values, may change if CDT algorithm change
assert_eq!(&conflicting_edges, &[[6, 7,], [4, 5,]]);
assert_eq!(cdt.num_constraints, 1);
Ok(())
}

#[test]
fn test_single_split() -> Result<(), InsertionError> {
let vertices = vec![
Expand Down
34 changes: 32 additions & 2 deletions src/delaunay_core/bulk_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,38 @@ where
Ok(result)
}

/// Returns the [`ConstrainedDelaunayTriangulation`].
///
/// Panics if it encounters any conflicting edges. See [try_bulk_load_cdt] for a non-panicking version.
///
/// # See also
///
/// See also [ConstrainedDelaunayTriangulation::bulk_load_cdt]
pub fn bulk_load_cdt<V, DE, UE, F, L>(
elements: Vec<V>,
edges: Vec<[usize; 2]>,
) -> Result<ConstrainedDelaunayTriangulation<V, DE, UE, F, L>, InsertionError>
where
V: HasPosition,
DE: Default,
UE: Default,
F: Default,
L: HintGenerator<<V as HasPosition>::Scalar>,
{
try_bulk_load_cdt(elements, edges, |e| {
panic!("Conflicting edge encountered: {};{}", e[0], e[1])
})
}

/// Efficient bulk loading of a constraint delaunay triangulation, including both vertices and constraint edges.
/// See [ConstrainedDelaunayTriangulation::bulk_load_cdt] for a related example and documentation.
///
/// This function does not panic if any two constraints intersect.
/// It will instead call `on_conflict_found` on all edges that could not be added as they intersect a previously added constraint.
pub fn try_bulk_load_cdt<V, DE, UE, F, L>(
elements: Vec<V>,
mut edges: Vec<[usize; 2]>,
mut on_conflict_found: impl FnMut([usize; 2]),
) -> Result<ConstrainedDelaunayTriangulation<V, DE, UE, F, L>, InsertionError>
where
V: HasPosition,
Expand Down Expand Up @@ -232,7 +261,6 @@ where
let mut next_constraint = edges.pop();

let mut buffer = Vec::new();

let mut add_constraints_for_new_vertex =
|result: &mut ConstrainedDelaunayTriangulation<V, DE, UE, F, L>, index| {
while let Some([from, to]) = next_constraint {
Expand All @@ -241,7 +269,9 @@ where
let [new_from, new_to] =
[from, to].map(|v| FixedVertexHandle::new(old_to_new[v]));
// Insert constraint edge
result.add_constraint(new_from, new_to);
if result.try_add_constraint(new_from, new_to).is_empty() {
on_conflict_found([from, to]);
}
next_constraint = edges.pop();
} else {
break;
Expand Down
2 changes: 1 addition & 1 deletion src/delaunay_core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod refinement;
pub mod interpolation;
pub mod math;

pub use bulk_load::{bulk_load, bulk_load_cdt, bulk_load_stable};
pub use bulk_load::{bulk_load, bulk_load_cdt, bulk_load_stable, try_bulk_load_cdt};

pub use triangulation_ext::{RemovalResult, TriangulationExt};

Expand Down
Loading