diff --git a/src/cdt.rs b/src/cdt.rs index 0da2092..05b2655 100644 --- a/src/cdt.rs +++ b/src/cdt.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; 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, @@ -345,7 +345,42 @@ where /// /// Panics if any constraint edges overlap. Panics if the edges contain an invalid index (out of range). pub fn bulk_load_cdt(vertices: Vec, edges: Vec<[usize; 2]>) -> Result { - 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::>(), vertices); + /// # Ok(()) + /// # } + /// ``` + pub fn try_bulk_load_cdt( + vertices: Vec, + edges: Vec<[usize; 2]>, + on_conflict_found: impl FnMut([usize; 2]), + ) -> Result { + let mut result = try_bulk_load_cdt(vertices, edges, on_conflict_found)?; *result.hint_generator_mut() = L::initialize_from_triangulation(&result); Ok(result) } @@ -406,12 +441,29 @@ where /// # Ok(()) /// # } /// ``` + /// + /// # See also + /// + /// See also [Self::try_bulk_load_cdt_stable] pub fn bulk_load_cdt_stable( vertices: Vec, edges: Vec<[usize; 2]>, ) -> Result { - 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, + edges: Vec<[usize; 2]>, + on_conflict_found: impl FnMut([usize; 2]), + ) -> Result { + 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) } @@ -1995,6 +2047,29 @@ mod test { 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![ diff --git a/src/delaunay_core/bulk_load.rs b/src/delaunay_core/bulk_load.rs index 9d51f0e..1de2d41 100644 --- a/src/delaunay_core/bulk_load.rs +++ b/src/delaunay_core/bulk_load.rs @@ -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( + elements: Vec, + edges: Vec<[usize; 2]>, +) -> Result, InsertionError> +where + V: HasPosition, + DE: Default, + UE: Default, + F: Default, + L: HintGenerator<::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( elements: Vec, mut edges: Vec<[usize; 2]>, + mut on_conflict_found: impl FnMut([usize; 2]), ) -> Result, InsertionError> where V: HasPosition, @@ -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, index| { while let Some([from, to]) = next_constraint { @@ -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; diff --git a/src/delaunay_core/mod.rs b/src/delaunay_core/mod.rs index 2509653..016de02 100644 --- a/src/delaunay_core/mod.rs +++ b/src/delaunay_core/mod.rs @@ -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};