spade/
cdt.rs

1use alloc::vec::Vec;
2use core::fmt::Formatter;
3
4use num_traits::{Float, NumCast};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use crate::cdt::ConflictRegionEnd::{EdgeOverlap, Existing};
9use crate::delaunay_core::dcel_operations::{
10    append_unconnected_vertex, flip_cw, new_with_fixed_vertices,
11};
12use crate::delaunay_core::{bulk_load_cdt, try_bulk_load_cdt};
13use crate::{
14    delaunay_core::Dcel, intersection_iterator::LineIntersectionIterator, PositionInTriangulation,
15    SpadeNum,
16};
17use crate::{handles::*, intersection_iterator::Intersection};
18use crate::{
19    mitigate_underflow, DelaunayTriangulation, HasPosition, HintGenerator, InsertionError,
20    LastUsedVertexHintGenerator, Point2, Triangulation, TriangulationExt,
21};
22
23/// Undirected edge type of a [ConstrainedDelaunayTriangulation] (CDT).
24///
25/// CDTs need to store if an undirected edge is a constrained edge. To do so, CDTs don't use
26/// the configured undirected edge type directly but wrap it into `CdtEdge<UE>` first.
27///
28/// This type will only be relevant if the triangulation's undirected edge type is being
29/// overwritten.
30///
31/// # Type parameters
32/// UE: The user configurable undirected edge type.
33#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(
35    feature = "serde",
36    derive(Serialize, Deserialize),
37    serde(crate = "serde")
38)]
39pub struct CdtEdge<UE>(bool, UE);
40
41impl<UE> CdtEdge<UE> {
42    /// Returns `true` if this edge is a constraint edge.
43    pub fn is_constraint_edge(&self) -> bool {
44        self.0
45    }
46
47    fn make_constraint_edge(&mut self) {
48        assert!(!self.is_constraint_edge());
49        self.0 = true;
50    }
51
52    fn unmake_constraint_edge(&mut self) {
53        assert!(self.is_constraint_edge());
54        self.0 = false;
55    }
56
57    /// Returns the wrapped undirected edge data type.
58    pub fn data(&self) -> &UE {
59        &self.1
60    }
61
62    /// Returns the wrapped undirected edge data type.
63    pub fn data_mut(&mut self) -> &mut UE {
64        &mut self.1
65    }
66
67    pub(crate) fn deconstruct(self) -> (bool, UE) {
68        (self.0, self.1)
69    }
70}
71
72impl<UE: Default> Default for CdtEdge<UE> {
73    fn default() -> Self {
74        CdtEdge(false, UE::default())
75    }
76}
77
78impl<UE> AsRef<UE> for CdtEdge<UE> {
79    fn as_ref(&self) -> &UE {
80        self.data()
81    }
82}
83
84impl<UE> AsMut<UE> for CdtEdge<UE> {
85    fn as_mut(&mut self) -> &mut UE {
86        self.data_mut()
87    }
88}
89
90/// A two-dimensional
91/// [constrained Delaunay triangulation](https://en.wikipedia.org/wiki/Constrained_Delaunay_triangulation).
92///
93/// A constrained Delaunay triangulation (CDT) is a triangulation that
94/// can contain _constraint edges_. These edges will always be present
95/// in the resulting triangulation.
96///
97#[doc = include_str!("../images/cdt.svg")]
98///
99/// *Left: A CDT with 4 constraint edges. Right: The same triangulation
100/// without constraint edges*
101///
102///
103/// The resulting triangulation
104/// does not necessarily fulfill the Delaunay property.
105///
106/// This implementation currently supports only _weakly intersecting_
107/// constraints, thus, constraint edges are allowed to touch at
108/// their start or end point but are not allowed to intersect at
109/// any interior point.
110///
111/// The constrained triangulation shares most of the implementation of
112/// the usual Delaunay triangulation, refer to `DelaunayTriangulation`
113/// for more information about type parameters, iteration, performance
114/// and more examples.
115///
116/// # Example
117///
118/// ```
119/// use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation};
120/// # fn try_main() -> Result<(), spade::InsertionError> {
121/// let mut cdt = ConstrainedDelaunayTriangulation::<Point2<_>>::new();
122/// let v0 = cdt.insert(Point2::new(0f64, 0.0))?;
123/// let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
124/// cdt.add_constraint(v0, v1);
125/// // Alternatively, consider using this shorthand
126/// cdt.add_constraint_edge(Point2::new(1.0, 1.0), Point2::new(1.0, 0.0))?;
127/// println!("Number of constraints: {}", cdt.num_constraints()); // 2 constraints
128/// // Constraints are bidirectional!
129/// assert!(cdt.exists_constraint(v1, v0));
130/// assert!(cdt.exists_constraint(v0, v1));
131/// // Check if a new constraint could be added
132/// let from = Point2::new(1.0, -2.0);
133/// let to = Point2::new(1.0, 0.0);
134/// if !cdt.intersects_constraint(from, to) {
135///     // No intersections, the edge can be added
136///     cdt.add_constraint_edge(from, to)?;
137/// }
138/// # Ok(()) }
139/// # fn main() { try_main().unwrap() }
140/// ```
141///
142/// # See also
143/// Refer to [Triangulation] for most implemented methods on this type.
144/// Refer to [DelaunayTriangulation](DelaunayTriangulation) for general
145/// information about using Delaunay triangulations.
146#[doc(alias = "CDT")]
147#[derive(Clone)]
148#[cfg_attr(
149    feature = "serde",
150    derive(Serialize, Deserialize),
151    serde(crate = "serde")
152)]
153pub struct ConstrainedDelaunayTriangulation<
154    V,
155    DE = (),
156    UE = (),
157    F = (),
158    L = LastUsedVertexHintGenerator,
159> where
160    V: HasPosition,
161    DE: Default,
162    UE: Default,
163    F: Default,
164    L: HintGenerator<<V as HasPosition>::Scalar>,
165{
166    dcel: Dcel<V, DE, CdtEdge<UE>, F>,
167    num_constraints: usize,
168    hint_generator: L,
169}
170
171impl<V, DE, UE, F, L> Default for ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
172where
173    V: HasPosition,
174    DE: Default,
175    UE: Default,
176    F: Default,
177    L: HintGenerator<<V as HasPosition>::Scalar>,
178{
179    fn default() -> Self {
180        ConstrainedDelaunayTriangulation {
181            dcel: Default::default(),
182            num_constraints: 0,
183            hint_generator: Default::default(),
184        }
185    }
186}
187
188impl<V, DE, UE, F, L> Triangulation for ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
189where
190    V: HasPosition,
191    DE: Default,
192    UE: Default,
193    F: Default,
194    L: HintGenerator<<V as HasPosition>::Scalar>,
195{
196    type Vertex = V;
197    type DirectedEdge = DE;
198    type UndirectedEdge = CdtEdge<UE>;
199    type Face = F;
200    type HintGenerator = L;
201
202    fn s(&self) -> &Dcel<V, DE, CdtEdge<UE>, F> {
203        &self.dcel
204    }
205
206    fn s_mut(&mut self) -> &mut Dcel<V, DE, CdtEdge<UE>, F> {
207        &mut self.dcel
208    }
209
210    fn is_defined_legal(&self, edge: FixedUndirectedEdgeHandle) -> bool {
211        self.is_constraint_edge(edge)
212    }
213
214    fn handle_legal_edge_split(&mut self, handles: [FixedDirectedEdgeHandle; 2]) {
215        self.num_constraints += 1;
216        for handle in handles.iter().map(|e| e.as_undirected()) {
217            if !self.is_constraint_edge(handle) {
218                self.dcel
219                    .undirected_edge_data_mut(handle)
220                    .make_constraint_edge();
221            }
222        }
223    }
224
225    fn hint_generator(&self) -> &Self::HintGenerator {
226        &self.hint_generator
227    }
228
229    fn hint_generator_mut(&mut self) -> &mut Self::HintGenerator {
230        &mut self.hint_generator
231    }
232
233    fn from_parts(
234        dcel: Dcel<Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face>,
235        hint_generator: Self::HintGenerator,
236        num_constraints: usize,
237    ) -> Self {
238        Self {
239            dcel,
240            num_constraints,
241            hint_generator,
242        }
243    }
244
245    /// Removes a vertex from the triangulation.
246    ///
247    /// This operation runs in O(n²), where n is the degree of the
248    /// removed vertex.
249    ///
250    /// # Handle invalidation
251    /// This method will invalidate all vertex, edge and face handles.
252    fn remove(&mut self, vertex: FixedVertexHandle) -> V {
253        let removed_constraints = self
254            .dcel
255            .vertex(vertex)
256            .out_edges()
257            .filter(|edge| edge.is_constraint_edge())
258            .map(|e| e.as_undirected().fix())
259            .collect::<Vec<_>>();
260
261        for e in removed_constraints {
262            self.remove_constraint_edge(e);
263        }
264
265        self.remove_and_notify(vertex)
266    }
267
268    fn into_parts(
269        self,
270    ) -> (
271        Dcel<Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face>,
272        Self::HintGenerator,
273        usize,
274    ) {
275        (self.dcel, self.hint_generator, self.num_constraints)
276    }
277
278    fn clear(&mut self) {
279        self.num_constraints = 0;
280        self.s_mut().clear();
281        let new_hint_generator = HintGenerator::initialize_from_triangulation(self);
282        *self.hint_generator_mut() = new_hint_generator;
283    }
284}
285
286impl<V, DE, UE, F, L> From<DelaunayTriangulation<V, DE, UE, F, L>>
287    for ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
288where
289    V: HasPosition,
290    DE: Default,
291    UE: Default,
292    F: Default,
293    L: HintGenerator<<V as HasPosition>::Scalar>,
294{
295    fn from(value: DelaunayTriangulation<V, DE, UE, F, L>) -> Self {
296        let dcel = value.dcel;
297        let s = dcel.map_undirected_edges(|edge| CdtEdge(false, edge));
298        let lookup = value.hint_generator;
299
300        ConstrainedDelaunayTriangulation {
301            dcel: s,
302            num_constraints: 0,
303            hint_generator: lookup,
304        }
305    }
306}
307
308impl<V, DE, UE, F, L> ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
309where
310    V: HasPosition,
311    DE: Default,
312    UE: Default,
313    F: Default,
314    L: HintGenerator<<V as HasPosition>::Scalar>,
315{
316    /// Efficient bulk loading of a constraint delaunay triangulation, including both vertices and constraint edges.
317    ///
318    /// The edges are given as pairs of vertex indices.
319    ///
320    /// The vertex order is preserved by this function.
321    ///
322    /// Input vertices may have the same position. However, only one vertex for each position will be kept. Edges
323    /// that go to a discarded vertex are rerouted and still inserted.
324    /// For each set of duplicate vertices, the vertex with the smallest index is kept. Any resulting gap is filled
325    /// by shifting the remaining vertices down.
326    ///
327    /// # Example
328    /// ```
329    /// # fn main() -> Result<(), spade::InsertionError> {
330    /// use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation};
331    /// let mut vertices = vec![
332    ///     Point2::new(0.0, 1.0),
333    ///     Point2::new(1.0, 2.0),
334    ///     Point2::new(3.0, -3.0),
335    ///     Point2::new(-1.0, -2.0),
336    ///     Point2::new(3.0, -3.0), // Duplicate
337    ///     Point2::new(-4.0, -5.0),
338    /// ];
339    /// let mut edges = vec![[0, 1], [1, 2], [2, 3], [4, 5]];
340    /// let cdt = ConstrainedDelaunayTriangulation::<_>::bulk_load_cdt(vertices.clone(), edges)?;
341    ///
342    /// assert_ne!(cdt.num_vertices(), vertices.len()); // Duplicates are removed
343    /// assert_eq!(cdt.num_constraints(), 4);
344    ///
345    /// // The order is preserved (excluding duplicates):
346    /// vertices.remove(4);
347    /// assert_eq!(cdt.vertices().map(|v| v.position()).collect::<Vec<_>>(), vertices);
348    /// # Ok(())
349    /// # }
350    /// ```
351    ///
352    /// # Panics
353    ///
354    /// Panics if any constraint edges overlap. Panics if the edges contain an invalid index (out of range).
355    pub fn bulk_load_cdt(vertices: Vec<V>, edges: Vec<[usize; 2]>) -> Result<Self, InsertionError> {
356        bulk_load_cdt(vertices, edges)
357    }
358
359    /// Same behavior as [bulk_load_cdt], but rather than panicking,
360    /// skips and calls the parameter function `on_conflict_found` whenever a conflict occurs.
361    ///
362    /// For any conflicting pair of constraint edges it is unspecified which constraint is reported.
363    ///
364    /// `on_conflict_found` is called with the edge indices _after_ potential duplicates were removed.
365    /// Consider checking for removed duplicates by comparing the size of the triangulation with the
366    /// input vertices.
367    ///
368    /// # Example
369    /// ```
370    /// # fn main() -> Result<(), spade::InsertionError> {
371    /// use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation};
372    /// let mut vertices = vec![
373    ///     Point2::new(0.0, 1.0),
374    ///     Point2::new(1.0, 2.0),
375    ///     Point2::new(3.0, -3.0),
376    ///     Point2::new(-4.0, -2.0),
377    ///     Point2::new(3.0, -3.0), // Duplicate
378    ///     Point2::new(-4.0, -5.0),
379    /// ];
380    /// let mut conflicting_edges = Vec::new();
381    /// let mut edges = vec![[0, 1], [1, 2], [2, 3], [4, 5], [5, 0]];
382    /// let cdt = ConstrainedDelaunayTriangulation::<_>::try_bulk_load_cdt(vertices.clone(), edges, |e| conflicting_edges.push(e))?;
383    ///
384    /// assert_eq!(cdt.num_vertices(), 5);
385    /// assert_eq!(cdt.num_constraints(), 4); // One constraint was not generated
386    ///
387    /// // The third and last edge generate a conflict. One of them will be reported via `on_conflict_found`.
388    /// // In this case, the last edge is returned. Note the index change from v5 to v4 due to the duplicate removal.
389    /// assert_eq!(conflicting_edges, [[4, 0]]);
390    ///
391    /// // The order is preserved (excluding duplicates):
392    /// vertices.remove(4);
393    /// assert_eq!(cdt.vertices().map(|v| v.position()).collect::<Vec<_>>(), vertices);
394    /// # Ok(())
395    /// # }
396    /// ```
397    pub fn try_bulk_load_cdt(
398        vertices: Vec<V>,
399        edges: Vec<[usize; 2]>,
400        on_conflict_found: impl FnMut([usize; 2]),
401    ) -> Result<Self, InsertionError> {
402        try_bulk_load_cdt(vertices, edges, on_conflict_found)
403    }
404
405    /// Deprecated. Use [Self::bulk_load_cdt] instead which is now stable by default.
406    #[deprecated(
407        since = "1.15.0",
408        note = "Use bulk_load_cdt instead. It is now stable by default."
409    )]
410    pub fn bulk_load_cdt_stable(
411        vertices: Vec<V>,
412        edges: Vec<[usize; 2]>,
413    ) -> Result<Self, InsertionError> {
414        Self::bulk_load_cdt(vertices, edges)
415    }
416
417    /// # Handle invalidation
418    /// See [Triangulation::remove]. This function was accidentally implemented separately for CDTs and will be removed in future releases.
419    /// Use the method from the trait to avoid breakage.
420    pub fn remove(&mut self, vertex: FixedVertexHandle) -> V {
421        <Self as Triangulation>::remove(self, vertex)
422    }
423
424    /// Returns the number of constraint edges.
425    pub fn num_constraints(&self) -> usize {
426        self.num_constraints
427    }
428
429    /// Returns `true` if a given edge is a constraint edge.
430    pub fn is_constraint_edge(&self, edge: FixedUndirectedEdgeHandle) -> bool {
431        self.dcel.undirected_edge_data(edge).is_constraint_edge()
432    }
433
434    /// Checks if two vertices are connected by a constraint edge.
435    pub fn exists_constraint(&self, from: FixedVertexHandle, to: FixedVertexHandle) -> bool {
436        self.get_edge_from_neighbors(from, to)
437            .map(|e| e.is_constraint_edge())
438            .unwrap_or(false)
439    }
440
441    /// Checks if a constraint edge can be added.
442    ///
443    /// Returns `false` if the line from `from` to `to` intersects another
444    /// constraint edge.
445    pub fn can_add_constraint(&self, from: FixedVertexHandle, to: FixedVertexHandle) -> bool {
446        let line_intersection_iterator = LineIntersectionIterator::new_from_handles(self, from, to);
447        !self.contains_any_constraint_edge(line_intersection_iterator)
448    }
449
450    /// Checks if a line intersects a constraint edge.
451    ///
452    /// Returns `true` if the edge from `from` to `to` intersects a
453    /// constraint edge.
454    pub fn intersects_constraint(
455        &self,
456        line_from: Point2<V::Scalar>,
457        line_to: Point2<V::Scalar>,
458    ) -> bool {
459        let line_intersection_iterator = LineIntersectionIterator::new(self, line_from, line_to);
460        self.contains_any_constraint_edge(line_intersection_iterator)
461    }
462
463    fn contains_any_constraint_edge(
464        &self,
465        mut line_intersection_iterator: LineIntersectionIterator<V, DE, CdtEdge<UE>, F>,
466    ) -> bool {
467        line_intersection_iterator.any(|intersection| match intersection {
468            Intersection::EdgeIntersection(edge) => edge.is_constraint_edge(),
469            _ => false,
470        })
471    }
472
473    /// Creates a several constraint edges by taking and connecting vertices from an iterator.
474    ///
475    /// Every two sequential vertices in the input iterator will be connected by a constraint edge.
476    /// If `closed` is set to true, the first and last vertex will also be connected.
477    ///
478    /// # Special cases:
479    ///  - Does nothing if input iterator is empty
480    ///  - Only inserts the single vertex if the input iterator contains exactly one element
481    ///
482    /// # Example
483    /// ```
484    /// # fn main() -> Result<(), spade::InsertionError> {
485    /// use spade::{ConstrainedDelaunayTriangulation, Point2};
486    ///
487    /// const NUM_VERTICES: usize = 51;
488    ///
489    /// let mut cdt = ConstrainedDelaunayTriangulation::<_>::default();
490    ///
491    /// // Iterates through vertices on a circle
492    /// let vertices = (0..NUM_VERTICES).map(|i| {
493    ///     let angle = std::f64::consts::PI * 2.0 * i as f64 / NUM_VERTICES as f64;
494    ///     let (sin, cos) = angle.sin_cos();
495    ///     Point2::new(sin, cos)
496    /// });
497    ///
498    /// cdt.add_constraint_edges(vertices, true)?;
499    /// # Ok(()) }
500    /// ```
501    ///
502    /// # Panics
503    ///
504    /// Panics if any of the generated constraints intersects with any other constraint edge.
505    pub fn add_constraint_edges(
506        &mut self,
507        vertices: impl IntoIterator<Item = V>,
508        closed: bool,
509    ) -> Result<(), InsertionError> {
510        let mut iter = vertices.into_iter();
511        if let Some(first) = iter.next() {
512            let first_handle = self.insert(first)?;
513            let mut previous_handle = first_handle;
514            let mut current_handle = first_handle;
515            for current in iter {
516                current_handle = self.insert(current)?;
517                self.add_constraint(previous_handle, current_handle);
518                previous_handle = current_handle;
519            }
520
521            if closed && current_handle != first_handle {
522                self.add_constraint(current_handle, first_handle);
523            }
524        }
525
526        Ok(())
527    }
528
529    /// Insert two points and creates a constraint between them.
530    ///
531    /// Returns `true` if at least one constraint edge was added.
532    ///
533    /// # Panics
534    ///
535    /// Panics if the new constraint edge intersects with an existing
536    /// constraint edge. Use [can_add_constraint](Self::can_add_constraint) to check.
537    pub fn add_constraint_edge(&mut self, from: V, to: V) -> Result<bool, InsertionError> {
538        let from_handle = self.insert(from)?;
539        let to_handle = self.insert(to)?;
540        Ok(self.add_constraint(from_handle, to_handle))
541    }
542
543    /// Adds a constraint edge between to vertices.
544    ///
545    /// Returns `true` if at least one constraint edge was added.
546    /// Note that the given constraint might be split into smaller edges
547    /// if a vertex in the triangulation lies exactly on the constraint edge.
548    /// Thus, `cdt.exists_constraint(from, to)` is not necessarily `true`
549    /// after a call to this function.
550    ///
551    /// Returns false and does nothing if `from == to`.
552    ///
553    /// # Panics
554    ///
555    /// Panics if the new constraint edge intersects an existing
556    /// constraint edge. Use [Self::try_add_constraint] or [Self::add_constraint_and_split] to work
557    /// around that.
558    pub fn add_constraint(&mut self, from: FixedVertexHandle, to: FixedVertexHandle) -> bool {
559        let initial_num_constraints = self.num_constraints();
560        self.resolve_splitting_constraint_request(from, to, None);
561
562        self.num_constraints != initial_num_constraints
563    }
564
565    /// Takes a conflict region (expressed as a list of intersecting edges) rotates edges to create
566    /// a new constraint edge. Then, the rotated edges (except the new constraint edge)
567    /// are legalized to restore the Delaunay property.
568    ///
569    /// Usually, this step is described as "delete all conflicting edges, then re-triangulate the
570    /// hole". Spade avoids the removal of edges by _rotating_ (flipping) them into place instead.
571    /// The final constraint edge is created implicitly.
572    /// This works as long as the intersecting edges are ordered "along the constraint edge", i.e.
573    /// the intersection points increase in distance from the constraint edge origin.
574    ///
575    /// # Example
576    ///
577    /// The input conflict region might look like this (assuming the target constraint edge goes
578    /// from v0 to v1):
579    ///
580    /// ```text
581    ///     v__________v
582    ///   / |        / |\
583    ///  /  |      /   | \
584    /// v0  |e0  /e1 e2| v1
585    ///  \  |  /       | /
586    ///   \ |/         |/
587    ///     v_________ v
588    /// ```
589    ///
590    /// `conflict_edges` would be set to `vec![e0, e1, e2]` in this case, `target_vertex` would be
591    /// `v1`.
592    ///
593    /// Now, flipping these edges _in this order_ will implicitly create the desired edge:
594    ///
595    /// After flipping the result looks like this with all edges going out of `v0`:
596    ///
597    /// ```text
598    ///     v_________v
599    ///   /     __---  \
600    ///  / __---        \
601    /// v0--------------v1  
602    ///  \ --___        /
603    ///   \     --___  /
604    ///     v---------v
605    ///```
606    ///
607    /// Now, the new edges can be legalized as usual.
608    ///
609    /// Returns a handle to the new constraint edge (pointing toward `target_vertex`).
610    fn resolve_conflict_region(
611        &mut self,
612        conflict_edges: Vec<FixedDirectedEdgeHandle>,
613        target_vertex: FixedVertexHandle,
614    ) -> Option<FixedDirectedEdgeHandle> {
615        let first = conflict_edges.first()?;
616
617        let mut temporary_constraint_edges = Vec::new();
618
619        let first = self.directed_edge(*first);
620
621        // These refer to the two edges that go out of the constraint edge origin initially.
622        // They are used below but need to be defined declared here to appease the borrow checker.
623        let first_border_edge = first.rev().prev().fix();
624        let last_border_edge = first.rev().next().fix();
625
626        // Flip all conflict edges in the input order - see function comment.
627        for edge in &conflict_edges {
628            flip_cw(self.s_mut(), edge.as_undirected());
629        }
630
631        // Small optimization: For the legalization, the algorithm doesn't need to look at edges
632        // outside the conflict region. They are known to be already legal.
633        // To do so, we will make the border edges that encompass the conflict region into temporary
634        // constraint edges. The legalization will then skip them. This is undone later,
635        let mut make_temporary_edge = |cdt: &mut Self, edge: FixedUndirectedEdgeHandle| {
636            // Exclude edges that are already a constraint - those should remain constraint edges
637            // and not be undone later!
638            if !cdt.undirected_edge(edge).is_constraint_edge() {
639                temporary_constraint_edges.push(edge);
640                cdt.undirected_edge_data_mut(edge).make_constraint_edge();
641            }
642        };
643
644        make_temporary_edge(self, first_border_edge.as_undirected());
645        make_temporary_edge(self, last_border_edge.as_undirected());
646
647        let mut current = first_border_edge;
648
649        let mut result = None;
650
651        // Loops around all border edges and adds them to the temporary constraint edge list.
652        // `first_border_edge` and `last_border_edge` refer to the two border edges that are
653        // initially going out of the constraint edge start (the two left most edges in the first
654        // ascii drawing of the function comment).
655        while current != last_border_edge.rev() {
656            let handle = self.directed_edge(current);
657            let fixed = handle.fix();
658            let next = handle.next().fix().as_undirected();
659
660            current = handle.ccw().fix();
661            if target_vertex == handle.to().fix() {
662                // This loop also finds the implicitly created constraint edge and makes it an
663                // official constraint edge!
664                self.make_constraint_edge(fixed.as_undirected());
665                result = Some(fixed);
666            }
667            make_temporary_edge(self, next);
668        }
669
670        self.legalize_edges_after_removal(
671            &mut conflict_edges
672                .into_iter()
673                .map(|edge| edge.as_undirected())
674                .collect(),
675            |_| false,
676        );
677
678        // Undo the previously made temporary constraint edges
679        for edge in temporary_constraint_edges {
680            self.undirected_edge_data_mut(edge).0 = false;
681        }
682
683        result
684    }
685
686    /// Returns all constraint edges that would prevent creating a new constraint between two points.
687    ///
688    /// # See also
689    ///
690    /// See also [Self::get_conflicting_edges_between_vertices]
691    pub fn get_conflicting_edges_between_points(
692        &self,
693        from: Point2<<V as HasPosition>::Scalar>,
694        to: Point2<<V as HasPosition>::Scalar>,
695    ) -> impl Iterator<Item = DirectedEdgeHandle<'_, V, DE, CdtEdge<UE>, F>> {
696        LineIntersectionIterator::new(self, from, to)
697            .flat_map(|intersection| intersection.as_edge_intersection())
698            .filter(|e| e.is_constraint_edge())
699    }
700
701    /// Returns all constraint edges that would prevent inserting a new constraint connecting two existing
702    /// vertices.
703    ///
704    /// # See also
705    ///
706    /// See also [Self::get_conflicting_edges_between_points]
707    pub fn get_conflicting_edges_between_vertices(
708        &self,
709        from: FixedVertexHandle,
710        to: FixedVertexHandle,
711    ) -> impl Iterator<Item = DirectedEdgeHandle<'_, V, DE, CdtEdge<UE>, F>> {
712        LineIntersectionIterator::new_from_handles(self, from, to)
713            .flat_map(|intersection| intersection.as_edge_intersection())
714            .filter(|e| e.is_constraint_edge())
715    }
716
717    fn make_constraint_edge(&mut self, edge: FixedUndirectedEdgeHandle) -> bool {
718        if !self.is_constraint_edge(edge) {
719            self.dcel
720                .undirected_edge_data_mut(edge)
721                .make_constraint_edge();
722            self.num_constraints += 1;
723            true
724        } else {
725            false
726        }
727    }
728
729    #[cfg(any(test, fuzzing))]
730    #[allow(missing_docs)]
731    pub fn cdt_sanity_check(&self) {
732        self.cdt_sanity_check_with_params(true);
733    }
734
735    #[cfg(any(test, fuzzing))]
736    #[allow(missing_docs)]
737    pub fn cdt_sanity_check_with_params(&self, check_convexity: bool) {
738        let num_constraints = self
739            .dcel
740            .undirected_edges()
741            .filter(|e| e.is_constraint_edge())
742            .count();
743
744        assert_eq!(num_constraints, self.num_constraints());
745
746        if self.num_constraints() == 0 && check_convexity {
747            self.sanity_check();
748        } else {
749            self.basic_sanity_check(check_convexity);
750        }
751    }
752
753    /// Removes a constraint edge.
754    ///
755    /// Does nothing and returns `false` if the given edge is not a constraint edge.
756    /// Otherwise, the edge is unmarked and the Delaunay property is restored in its vicinity.
757    pub fn remove_constraint_edge(&mut self, edge: FixedUndirectedEdgeHandle) -> bool {
758        if self.is_constraint_edge(edge) {
759            self.dcel
760                .undirected_edge_data_mut(edge)
761                .unmake_constraint_edge();
762            self.num_constraints -= 1;
763            self.legalize_edge(edge.as_directed(), true);
764            true
765        } else {
766            false
767        }
768    }
769
770    /// Attempts to add a constraint edge. Leaves the triangulation unchanged if the new edge would
771    /// intersect an existing constraint edge.
772    ///
773    /// Returns all constraint edges that connect `from` and `to`. This includes any constraint
774    /// edge that was already present.
775    /// Multiple edges are returned if the line from `from` to `to` intersects an existing vertex.
776    /// Returns an empty list if the new constraint would intersect any existing constraint or if
777    /// `from == to`.
778    ///
779    /// # Example
780    ///
781    /// ```
782    /// use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation};
783    /// # fn try_main() -> Result<(), spade::InsertionError> {
784    /// let mut cdt = ConstrainedDelaunayTriangulation::<Point2<_>>::new();
785    /// let v0 = cdt.insert(Point2::new(-1.0, 0.0))?;
786    /// let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
787    /// let v2 = cdt.insert(Point2::new(0.0, 1.0))?;
788    /// let v3 = cdt.insert(Point2::new(0.0, -1.0))?;
789    /// let first_constraints = cdt.try_add_constraint(v2, v3);
790    /// let second_constraints = cdt.try_add_constraint(v0, v1);
791    ///
792    /// // The first constraint edge can be added as there are no intersecting constraint edges
793    /// assert_eq!(first_constraints.len(), 1);
794    /// let edge = cdt.directed_edge(first_constraints[0]);
795    /// assert_eq!(edge.from().fix(), v2);
796    /// assert_eq!(edge.to().fix(), v3);
797    ///
798    /// // The second edge should not be created as it intersects the first edge.
799    /// assert!(second_constraints.is_empty());
800    ///
801    /// // Consider comparing this to the number of constraints prior to calling
802    /// // `try_add_constraint` to check if any new constraint edge was created.
803    /// assert_eq!(cdt.num_constraints(), 1);
804    /// # Ok(()) }
805    /// # fn main() { try_main().unwrap() }
806    /// ```
807    pub fn try_add_constraint(
808        &mut self,
809        from: FixedVertexHandle,
810        to: FixedVertexHandle,
811    ) -> Vec<FixedDirectedEdgeHandle> {
812        // Identify all potential constraint edge intersections (conflicts). This must be done
813        // beforehand in case that the caller chooses to cancel the operation if any conflict is
814        // detected. No mutation should happen in this case.
815        // The list of conflicts regions will be empty if a conflict occurred
816        let initial_conflict_regions = self.get_conflict_resolutions(from, to);
817        self.resolve_conflict_groups(initial_conflict_regions)
818    }
819
820    fn get_conflict_resolutions(
821        &mut self,
822        from: FixedVertexHandle,
823        to: FixedVertexHandle,
824    ) -> Vec<InitialConflictRegion> {
825        let mut conflict_groups = Vec::new();
826        let mut current_group = Vec::new();
827        let mut ignore_next_vertex = false;
828        for intersection in LineIntersectionIterator::new_from_handles(self, from, to) {
829            match intersection {
830                Intersection::EdgeIntersection(edge) => {
831                    if !edge.is_constraint_edge() {
832                        current_group.push(edge.fix());
833                        continue;
834                    }
835
836                    return Vec::new();
837                }
838                Intersection::VertexIntersection(v) => {
839                    if ignore_next_vertex {
840                        ignore_next_vertex = false;
841                        continue;
842                    }
843                    let group_end = Existing(v.fix());
844                    let conflict_edges = core::mem::take(&mut current_group);
845                    conflict_groups.push(InitialConflictRegion {
846                        conflict_edges,
847                        group_end,
848                    });
849                }
850                Intersection::EdgeOverlap(edge) => {
851                    conflict_groups.push(InitialConflictRegion {
852                        conflict_edges: Vec::new(),
853                        group_end: EdgeOverlap(edge.fix()),
854                    });
855                    // The next intersection is going to be edge.to(). It would be incorrect to
856                    // create a conflict region from that vertex as that region is already handled
857                    // by the GroupEnd::EdgeOverlap cases
858                    ignore_next_vertex = true;
859                }
860            }
861        }
862
863        conflict_groups
864    }
865
866    fn resolve_splitting_constraint_request(
867        &mut self,
868        mut from: FixedVertexHandle,
869        to: FixedVertexHandle,
870        vertex_constructor: Option<&dyn Fn(Point2<f64>) -> V>,
871    ) -> Vec<FixedDirectedEdgeHandle> {
872        let mut result = Vec::new();
873        let mut conflict_edges = Vec::new();
874        let mut legalize_buffer = Vec::new();
875        let mut iterator = LineIntersectionIterator::new_from_handles(self, from, to);
876        iterator.next();
877
878        // This methods adds a constraint edge between two vertices. Any existing constraint edge that would intersect
879        // is being split (or results in a panic). This can lead to a few special cases, see below for more information.
880        //
881        // Other than that, this method implements a "fast path" for adding a constraint edge if no existing edge is
882        // being intersected. The fast path does not need to identify the whole conflict region again as those
883        // edges are being tracked.
884        while let Some(intersection) = iterator.next() {
885            match intersection {
886                Intersection::EdgeOverlap(edge) => {
887                    result.push(edge.fix());
888                    from = edge.to().fix();
889                }
890                Intersection::EdgeIntersection(mut edge) => {
891                    if !edge.is_constraint_edge() {
892                        // No conflict. Add edge to conflict edge list for later resolution (fast path)
893                        conflict_edges.push(edge.fix());
894                        continue;
895                    }
896                    // Slow path. We have found a conflict which needs to be resolved.
897                    let [p0, p1] = edge.positions().map(|p| p.to_f64());
898
899                    let from_pos = self.vertex(from).position().to_f64();
900                    let to_pos = self.vertex(to).position().to_f64();
901
902                    // Perform all intersection operations on `f64` to avoid precision issues as much as
903                    // possible.
904                    let line_intersection = get_edge_intersections(p0, p1, from_pos, to_pos);
905                    let line_intersection = mitigate_underflow(line_intersection);
906                    let new_vertex = vertex_constructor
907                        .expect("The new constraint edge intersects an existing constraint edge.")(
908                        line_intersection,
909                    );
910
911                    // The position might have changed slightly for f32 vertices.
912                    // Ensure to use this rounded position for all further calculations.
913                    let position = new_vertex.position();
914
915                    // Now comes the yucky part. In most cases, the split vertex is precise enough and lies
916                    // far away from any other vertex or edge. It will reside either directly on the
917                    // split edge or on one of its neighboring faces. Such a vertex can be used directly
918                    // as splitting the constraint won't create any invalid geometry (after legalizing).
919                    // Otherwise, we'll use a close alternative vertex that is known to introduce no
920                    // inconsistencies.
921                    let alternative_vertex = self.validate_split_position(edge, position);
922
923                    let final_vertex =
924                        if let Some((alternative_vertex, is_end_vertex)) = alternative_vertex {
925                            if !is_end_vertex {
926                                // An opposite vertex needs some adjustment to the set of constraint edges
927                                let is_on_same_side = edge.opposite_vertex().map(|v| v.fix())
928                                    == Some(alternative_vertex);
929                                if !is_on_same_side {
930                                    edge = edge.rev();
931                                }
932                                // This face ("(c)" marks constraint edges):
933                                //          |\
934                                //          | \
935                                // edge(c)->|  a <-- alternative vertex
936                                //          | /
937                                //          |/
938                                //
939                                // Becomes this face:
940                                //          |\
941                                //          | \<-(c)
942                                //    edge->|  a
943                                //          | /<-(c)
944                                //          |/
945
946                                let prev = edge.prev().fix();
947                                let next = edge.next().fix();
948
949                                let edge = edge.fix();
950                                self.undirected_edge_data_mut(edge.as_undirected())
951                                    .unmake_constraint_edge();
952                                self.num_constraints -= 1;
953
954                                self.make_constraint_edge(prev.as_undirected());
955                                self.make_constraint_edge(next.as_undirected());
956
957                                legalize_buffer.push(edge.as_undirected());
958                                self.legalize_edges_after_removal(&mut legalize_buffer, |_| false);
959                            }
960
961                            alternative_vertex
962                        } else {
963                            let edge = edge.fix();
964                            let new_vertex = append_unconnected_vertex(self.s_mut(), new_vertex);
965                            let [e0, e1] = self.insert_on_edge(edge, new_vertex);
966                            self.handle_legal_edge_split([e0, e1]);
967                            self.legalize_vertex(new_vertex);
968                            new_vertex
969                        };
970
971                    // Earlier versions of this code attempted to re-use the list of conflict edges for
972                    // efficiency gains. However, due to the necessary legalization, any number of conflict
973                    // edges may have been flipped and needs to be recalculated. The simplest way is to call
974                    // try_add_constraint.
975                    let previous_region = self.try_add_constraint(from, final_vertex);
976                    // Ensure that this call really added a constraint edge. There shouldn't be any constraint
977                    // edge in the way.
978                    assert!(!previous_region.is_empty() || from == final_vertex);
979                    result.extend(previous_region);
980                    conflict_edges.clear();
981
982                    from = final_vertex;
983                    // Reset the line iterator to ensure we are following the line out of the split position.
984                    // This will be slightly offset from the original line but prevent inconsistent conflict
985                    // edge detections.
986                    iterator = LineIntersectionIterator::new_from_handles(self, from, to);
987
988                    // Skip The first intersection as it will be the split vertex
989                    iterator.next();
990                }
991                Intersection::VertexIntersection(vertex) => {
992                    // Fast path. Happens if no constraint edge in this conflict region needed to be split.
993                    // Re-use the collected list of conflict edges.
994                    let vertex = vertex.fix();
995                    let copy = core::mem::take(&mut conflict_edges);
996                    let new_edge = self.resolve_conflict_region(copy, vertex);
997                    result.extend(new_edge);
998                    iterator = LineIntersectionIterator::new_from_handles(self, vertex, to);
999                    iterator.next();
1000                    from = vertex;
1001                }
1002            }
1003        }
1004
1005        for edge in &result {
1006            self.make_constraint_edge(edge.as_undirected());
1007        }
1008
1009        result
1010    }
1011
1012    fn validate_split_position(
1013        &self,
1014        conflict_edge: DirectedEdgeHandle<V, DE, CdtEdge<UE>, F>,
1015        split_position: Point2<<V as HasPosition>::Scalar>,
1016    ) -> Option<(FixedVertexHandle, bool)> {
1017        // Not every split vertex may lead to a conflict region that will properly contain the
1018        // split vertex. This can happen as not all split positions can be represented precisely.
1019        //
1020        // Instead, these vertices will be handled by a slower fallback routine.
1021        //
1022        // A split position is considered to be valid if it lies either *on* the edge that was split
1023        // or *within any of the neighboring faces*. We know that connecting to that new vertex won't
1024        // lead to any inconsistent geometry.
1025        //
1026        // If the split position is not valid, we will *instead* use the closest vertex that is
1027        // either the start or end vertex of the conflict edge or one of its opposites.
1028        //
1029        // Returns Some((..., `true`)) if the alternative vertex is either `conflict_edge.from` or
1030        // `conflict_edge.to`. This is important as, in case an opposite vertex is chosen, the set of
1031        // constraint edges needs to be adjusted slightly.
1032        let is_valid = match self.locate_with_hint(split_position, conflict_edge.from().fix()) {
1033            PositionInTriangulation::OnEdge(real_edge) => {
1034                real_edge.as_undirected() == conflict_edge.fix().as_undirected()
1035            }
1036            PositionInTriangulation::OnFace(face) => {
1037                let face = face.adjust_inner_outer();
1038                face == conflict_edge.face().fix() || face == conflict_edge.rev().face().fix()
1039            }
1040            PositionInTriangulation::OutsideOfConvexHull(_) => {
1041                conflict_edge.is_part_of_convex_hull()
1042            }
1043            PositionInTriangulation::OnVertex(_) => false,
1044            PositionInTriangulation::NoTriangulation => unreachable!(),
1045        };
1046
1047        if is_valid {
1048            None
1049        } else {
1050            let split_position = split_position.to_f64();
1051            let [d_from, d_to] = [conflict_edge.from(), conflict_edge.to()]
1052                .map(|v| v.position().to_f64().distance_2(split_position));
1053
1054            let mut min_distance = d_from;
1055            let mut min_vertex = conflict_edge.from();
1056            let mut is_end_vertex = true;
1057            if d_to < min_distance {
1058                min_distance = d_to;
1059                min_vertex = conflict_edge.to();
1060            }
1061
1062            if let Some(opposite) = conflict_edge.opposite_vertex() {
1063                let d_left = opposite.position().to_f64().distance_2(split_position);
1064
1065                if d_left < min_distance {
1066                    min_distance = d_left;
1067                    min_vertex = conflict_edge.next().to();
1068
1069                    is_end_vertex = false;
1070                }
1071            }
1072
1073            if let Some(opposite) = conflict_edge.rev().opposite_vertex() {
1074                let d_right = opposite.position().to_f64().distance_2(split_position);
1075
1076                if d_right < min_distance {
1077                    min_vertex = conflict_edge.rev().next().to();
1078                    is_end_vertex = false;
1079                }
1080            }
1081
1082            Some((min_vertex.fix(), is_end_vertex))
1083        }
1084    }
1085
1086    fn resolve_conflict_groups(
1087        &mut self,
1088        conflict_groups: Vec<InitialConflictRegion>,
1089    ) -> Vec<FixedDirectedEdgeHandle> {
1090        let mut constraint_edges = Vec::new();
1091
1092        for InitialConflictRegion {
1093            conflict_edges,
1094            group_end,
1095        } in conflict_groups
1096        {
1097            let target_vertex = match group_end {
1098                Existing(v) => v,
1099                EdgeOverlap(edge) => {
1100                    constraint_edges.push(edge);
1101
1102                    // No need to resolve conflict regions - there are no conflicting edges in the
1103                    // GroupEnd::EdgeOverlap case
1104                    continue;
1105                }
1106            };
1107
1108            constraint_edges.extend(self.resolve_conflict_region(conflict_edges, target_vertex));
1109        }
1110
1111        for edge in &constraint_edges {
1112            self.make_constraint_edge(edge.as_undirected());
1113        }
1114
1115        constraint_edges
1116    }
1117
1118    pub(crate) fn create_with_fixed_vertices(
1119        elements: Vec<V>,
1120        first_vertex: FixedVertexHandle,
1121        second_vertex: FixedVertexHandle,
1122    ) -> Self {
1123        Self {
1124            dcel: new_with_fixed_vertices(elements, first_vertex, second_vertex),
1125            num_constraints: 0,
1126            // Doesn't make sense to initialize with fixed vertices. Bulk loading will initialize
1127            // and replace this afterwards anyway.
1128            hint_generator: Default::default(),
1129        }
1130    }
1131
1132    /// Swaps out and re-initializes the hint generator.
1133    pub(crate) fn adjust_hint_generator<L2>(
1134        self,
1135    ) -> ConstrainedDelaunayTriangulation<V, DE, UE, F, L2>
1136    where
1137        L2: HintGenerator<<V as HasPosition>::Scalar>,
1138    {
1139        let hint_generator = L2::initialize_from_triangulation(&self);
1140        let (dcel, _, num_constraints) = self.into_parts();
1141        ConstrainedDelaunayTriangulation::from_parts(dcel, hint_generator, num_constraints)
1142    }
1143}
1144
1145impl<V, DE, UE, F, L> ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
1146where
1147    V: HasPosition,
1148    V::Scalar: Float,
1149    DE: Default,
1150    UE: Default,
1151    F: Default,
1152    L: HintGenerator<<V as HasPosition>::Scalar>,
1153{
1154    /// Adds a constraint to the triangulation. Splits any existing constraint edge that would
1155    /// intersect the new constraint edge.
1156    ///
1157    /// The `vertex_constructor` closure is used to convert the position of the intersection into
1158    /// a vertex. The returned vertex must have exactly the same position as the argument of the
1159    /// closure.
1160    ///
1161    /// Returns all constraint edges that connect `from` and `to`. This includes any constraint
1162    /// edge that was already present.
1163    /// Multiple edges are returned if the line from `from` to `to` intersects any existing vertex
1164    /// or any existing constraint edge.
1165    /// Returns an empty list if `from == to`.
1166    ///
1167    /// # Image example
1168    ///
1169    /// This is an input CDT with 3 constraints:
1170    ///
1171    #[doc = include_str!("../images/add_constraint_and_split_initial.svg")]
1172    ///
1173    /// Calling `add_constraint_and_split(v0, v1, ...)` will result in this CDT:
1174    ///
1175    #[doc = include_str!("../images/add_constraint_and_split_added.svg")]
1176    ///
1177    /// # Code example
1178    ///
1179    ///```
1180    /// use spade::{ConstrainedDelaunayTriangulation, Point2, Triangulation};
1181    /// # fn try_main() -> Result<(), spade::InsertionError> {
1182    /// use spade::handles::FixedVertexHandle;
1183    /// let mut cdt = ConstrainedDelaunayTriangulation::<Point2<_>>::new();
1184    /// let v0 = cdt.insert(Point2::new(-1.0, 0.0))?;
1185    /// let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
1186    /// let v2 = cdt.insert(Point2::new(0.0, 1.0))?;
1187    /// let v3 = cdt.insert(Point2::new(0.0, -1.0))?;
1188    /// cdt.add_constraint(v2, v3);
1189    ///
1190    /// // Should create a new split vertex at the origin
1191    /// let second_constraints = cdt.add_constraint_and_split(v0, v1, |v| v);
1192    ///
1193    /// // Expect one additional point introduced by splitting the first constraint edge:
1194    /// assert_eq!(cdt.num_vertices(), 5);
1195    ///
1196    /// let v4 = FixedVertexHandle::from_index(4); // Newly created
1197    ///
1198    /// // Expect 4 constraints as the first constraint was split:
1199    /// assert_eq!(cdt.num_constraints(), 4);
1200    ///
1201    /// // The second edge should consist of two edges, v0 -> v4 and v4 -> v1
1202    /// assert_eq!(second_constraints.len(), 2);
1203    ///
1204    /// let [e0, e1] = [second_constraints[0], second_constraints[1]];
1205    /// let [e0, e1] = [e0, e1].map(|e| cdt.directed_edge(e));
1206    ///
1207    /// assert_eq!(e0.from().fix(), v0);
1208    /// assert_eq!(e0.to().fix(), v4);
1209    /// assert_eq!(e1.from().fix(), v4);
1210    /// assert_eq!(e1.to().fix(), v1);
1211    ///
1212    /// # Ok(()) }
1213    /// # fn main() { try_main().unwrap() }
1214    /// ```
1215    ///
1216    /// # Precision warning
1217    ///
1218    /// Intersection points may not _exactly_ lie on the line between `from` and `to`, either due to
1219    /// loss of precision or as the exact value may not be representable with the underlying
1220    /// floating point number.
1221    ///
1222    /// Thus, iterating a `LineIntersectionIterator::new_from_handles(&cdt, from, to)` will often
1223    /// not return only `Intersection::EdgeOverlap` as would be expected. Instead, use the returned
1224    /// `Vec` to identify the edges that form the new constraint.
1225    /// The absolute deviation from the correct position should be small, especially when using
1226    /// `f64` coordinates as storage type.
1227    pub fn add_constraint_and_split<C>(
1228        &mut self,
1229        from: FixedVertexHandle,
1230        to: FixedVertexHandle,
1231        vertex_constructor: C,
1232    ) -> Vec<FixedDirectedEdgeHandle>
1233    where
1234        C: Fn(Point2<<V as HasPosition>::Scalar>) -> V,
1235    {
1236        let r = &|p: Point2<f64>| {
1237            let [x, y] = [p.x, p.y].map(|s| {
1238                <<V as HasPosition>::Scalar as NumCast>::from(s)
1239                    .unwrap_or_else(|| (s as f32).into())
1240            });
1241            vertex_constructor(Point2::new(x, y))
1242        };
1243
1244        self.resolve_splitting_constraint_request(from, to, Some(r))
1245    }
1246}
1247
1248/// Describes all possible ways in which conflict regions which are created while adding a
1249/// constraint edge may end.
1250enum ConflictRegionEnd {
1251    /// Conflict group ends with an existing vertex
1252    Existing(FixedVertexHandle),
1253    /// Special case of "Existing" - the constraint edge overlaps any existing edge which implies
1254    /// that the conflict group also ends on an existing vertex.
1255    /// However, it makes sense to handle this specially to prevent having to look up the overlapped
1256    /// edge later.
1257    EdgeOverlap(FixedDirectedEdgeHandle),
1258}
1259
1260impl core::fmt::Debug for ConflictRegionEnd {
1261    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1262        match self {
1263            Existing(handle) => write!(f, "Existing({handle:?})"),
1264            EdgeOverlap(edge) => write!(f, "EdgeOverlap({edge:?})"),
1265        }
1266    }
1267}
1268
1269/// Represents a conflict region that does not yet fully exist as a vertex may be missing. This can
1270/// happen if adding a constraint edge should split any intersecting existing edge.
1271/// This will eventually be turned into a "real" conflict group (described as a list of edges) by
1272/// inserting the missing vertex.
1273struct InitialConflictRegion {
1274    conflict_edges: Vec<FixedDirectedEdgeHandle>,
1275    group_end: ConflictRegionEnd,
1276}
1277
1278impl core::fmt::Debug for InitialConflictRegion {
1279    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1280        f.debug_struct("InitialConflictRegion")
1281            .field("conflict_edges", &self.conflict_edges)
1282            .field("group_end", &self.group_end)
1283            .finish()
1284    }
1285}
1286
1287pub fn get_edge_intersections<S: SpadeNum + Float>(
1288    p1: Point2<S>,
1289    p2: Point2<S>,
1290    p3: Point2<S>,
1291    p4: Point2<S>,
1292) -> Point2<S> {
1293    let p1 = p1.to_f64();
1294    let p2 = p2.to_f64();
1295    let p3 = p3.to_f64();
1296    let p4 = p4.to_f64();
1297
1298    let a1 = p2.y - p1.y;
1299    let b1 = p1.x - p2.x;
1300    let c1 = a1 * p1.x + b1 * p1.y;
1301
1302    let a2 = p4.y - p3.y;
1303    let b2 = p3.x - p4.x;
1304    let c2 = a2 * p3.x + b2 * p3.y;
1305
1306    let determinant = a1 * b2 - a2 * b1;
1307
1308    let x: f64;
1309    let y: f64;
1310    if determinant == 0.0 {
1311        x = f64::infinity();
1312        y = f64::infinity();
1313    } else {
1314        x = (b2 * c1 - b1 * c2) / determinant;
1315        y = (a1 * c2 - a2 * c1) / determinant;
1316    }
1317
1318    [x, y]
1319        .map(|s| <S as NumCast>::from(s).unwrap_or_else(|| (s as f32).into()))
1320        .into()
1321}
1322
1323#[cfg(test)]
1324mod test {
1325    use std::collections::HashSet;
1326
1327    use approx::assert_abs_diff_eq;
1328    use proptest::prelude::*;
1329
1330    use alloc::{vec, vec::Vec};
1331
1332    use rand::distr::{Distribution, Uniform};
1333    use rand::seq::IndexedRandom as _;
1334    use rand::{Rng, SeedableRng};
1335
1336    use crate::delaunay_core::{FixedDirectedEdgeHandle, TriangulationExt};
1337    use crate::handles::FixedVertexHandle;
1338    use crate::test_utilities::*;
1339    use crate::{DelaunayTriangulation, InsertionError, Point2, Triangulation};
1340
1341    use super::ConstrainedDelaunayTriangulation;
1342
1343    type Cdt = ConstrainedDelaunayTriangulation<Point2<f64>>;
1344    type Delaunay = DelaunayTriangulation<Point2<f64>>;
1345
1346    #[test]
1347    fn test_into() -> Result<(), InsertionError> {
1348        let points = random_points_with_seed(100, SEED);
1349        let delaunay = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1350
1351        let cdt = Cdt::from(delaunay.clone());
1352
1353        assert_eq!(delaunay.num_vertices(), cdt.num_vertices());
1354        assert_eq!(delaunay.num_directed_edges(), cdt.num_directed_edges());
1355        assert_eq!(cdt.num_constraints, 0);
1356
1357        Ok(())
1358    }
1359
1360    #[test]
1361    fn test_add_same_from_and_to_constraint() -> Result<(), InsertionError> {
1362        let mut cdt = Cdt::new();
1363        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1364        cdt.insert(Point2::new(2.0, 2.0))?;
1365        cdt.insert(Point2::new(1.0, 2.0))?;
1366
1367        assert!(!cdt.add_constraint(v0, v0));
1368        assert!(cdt.try_add_constraint(v0, v0).is_empty());
1369
1370        let new_point = Point2::new(3.1, 2.0);
1371        assert!(!cdt.add_constraint_edge(new_point, new_point)?);
1372
1373        assert_eq!(0, cdt.num_constraints());
1374        assert_eq!(4, cdt.num_vertices());
1375
1376        cdt.cdt_sanity_check();
1377
1378        Ok(())
1379    }
1380
1381    #[test]
1382    fn test_add_single_simple_constraint() -> Result<(), InsertionError> {
1383        let mut cdt = Cdt::new();
1384        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1385        let v1 = cdt.insert(Point2::new(2.0, 2.0))?;
1386        let v2 = cdt.insert(Point2::new(1.0, 0.5))?;
1387        let v3 = cdt.insert(Point2::new(0.5, 1.0))?;
1388        assert!(cdt.get_edge_from_neighbors(v0, v1).is_none());
1389        assert!(cdt.get_edge_from_neighbors(v2, v3).is_some());
1390
1391        assert!(cdt.add_constraint(v1, v0));
1392        assert!(!cdt.add_constraint(v0, v1));
1393        let edge = cdt
1394            .get_edge_from_neighbors(v0, v1)
1395            .expect("Expected constraint edge")
1396            .as_undirected()
1397            .fix();
1398        assert!(cdt.get_edge_from_neighbors(v2, v3).is_none());
1399        assert!(cdt.is_constraint_edge(edge));
1400        cdt.cdt_sanity_check();
1401        Ok(())
1402    }
1403
1404    #[test]
1405    fn test_existing_edge_constraint() -> Result<(), InsertionError> {
1406        let mut cdt = Cdt::new();
1407        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1408        let v1 = cdt.insert(Point2::new(2.0, 2.0))?;
1409        let v2 = cdt.insert(Point2::new(1.0, 0.0))?;
1410        assert!(cdt.add_constraint(v0, v1));
1411        assert!(cdt.add_constraint(v0, v2));
1412        assert!(cdt.add_constraint(v1, v2));
1413        for edge in cdt.undirected_edges() {
1414            assert!(cdt.is_constraint_edge(edge.fix()));
1415        }
1416        assert!(!cdt.add_constraint(v1, v0));
1417        assert!(!cdt.add_constraint(v1, v2));
1418        assert_eq!(cdt.num_constraints, 3);
1419        Ok(())
1420    }
1421
1422    #[test]
1423    fn test_mid_overlapping_constraint() -> Result<(), InsertionError> {
1424        let mut cdt = Cdt::new();
1425        let v0 = cdt.insert(Point2::new(0.0, 0.5))?;
1426        let v1 = cdt.insert(Point2::new(2.0, 0.5))?;
1427        let v2 = cdt.insert(Point2::new(3.0, 0.5))?;
1428        let v3 = cdt.insert(Point2::new(5.0, 0.5))?;
1429        cdt.insert(Point2::new(1.0, 1.0))?;
1430        cdt.insert(Point2::new(1.0, 0.0))?;
1431        cdt.insert(Point2::new(3.0, 1.0))?;
1432        cdt.insert(Point2::new(3.0, 0.0))?;
1433        assert!(cdt.get_edge_from_neighbors(v1, v2).is_some());
1434        let mut copy = cdt.clone();
1435        assert!(cdt.add_constraint(v0, v3));
1436        assert_eq!(cdt.num_constraints(), 3);
1437
1438        copy.add_constraint(v2, v3);
1439        assert_eq!(copy.num_constraints(), 1);
1440        copy.add_constraint(v0, v3);
1441        assert_eq!(copy.num_constraints(), 3);
1442        Ok(())
1443    }
1444
1445    #[test]
1446    fn test_add_single_complex_constraint() -> Result<(), InsertionError> {
1447        let mut cdt = Cdt::new();
1448        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1449        cdt.insert(Point2::new(1.0, 0.0))?;
1450        cdt.insert(Point2::new(0.0, 1.0))?;
1451        cdt.insert(Point2::new(2.0, 1.0))?;
1452
1453        let v1 = cdt.insert(Point2::new(2.0, 2.0))?;
1454        assert!(cdt.get_edge_from_neighbors(v0, v1).is_none());
1455        cdt.add_constraint(v0, v1);
1456        cdt.cdt_sanity_check();
1457        let edge = cdt
1458            .get_edge_from_neighbors(v0, v1)
1459            .expect("Expected constraint edge")
1460            .fix()
1461            .as_undirected();
1462        assert!(cdt.is_constraint_edge(edge));
1463        Ok(())
1464    }
1465
1466    #[test]
1467    fn test_add_single_constraint() -> Result<(), InsertionError> {
1468        let points = random_points_with_seed(1000, SEED);
1469        let mut cdt = Cdt::new();
1470        assert_eq!(cdt.num_constraints(), 0);
1471        let mut handles = Vec::new();
1472        cdt.cdt_sanity_check();
1473        for point in points.into_iter() {
1474            handles.push(cdt.insert(point)?);
1475        }
1476        cdt.add_constraint(handles[40], handles[200]);
1477        assert_eq!(cdt.num_constraints(), 1);
1478        cdt.cdt_sanity_check();
1479        Ok(())
1480    }
1481
1482    #[test]
1483    fn test_add_border_constraint() -> Result<(), InsertionError> {
1484        let points = random_points_with_seed(1000, SEED);
1485        let mut cdt = Cdt::new();
1486        let mut max_y = -f64::MAX;
1487        for point in points {
1488            max_y = max_y.max(point.y);
1489            cdt.insert(point)?;
1490        }
1491        let v0 = cdt.insert(Point2::new(-20., max_y + 10.))?;
1492        let v1 = cdt.insert(Point2::new(20., max_y + 10.))?;
1493        cdt.add_constraint(v0, v1);
1494        assert_eq!(cdt.num_constraints(), 1);
1495        cdt.cdt_sanity_check();
1496        Ok(())
1497    }
1498
1499    #[test]
1500    fn test_add_multiple_constraints_overlapping() -> Result<(), InsertionError> {
1501        test_add_multiple_constraints(true)
1502    }
1503
1504    #[test]
1505    fn test_add_multiple_constraints_non_overlapping() -> Result<(), InsertionError> {
1506        test_add_multiple_constraints(false)
1507    }
1508
1509    fn test_add_multiple_constraints(overlapping: bool) -> Result<(), InsertionError> {
1510        const RANGE: f64 = 10.;
1511        let seed = if overlapping { SEED } else { SEED2 };
1512        let points = random_points_in_range(RANGE, 1000, seed);
1513        let mut cdt = Cdt::new();
1514        for point in points {
1515            cdt.insert(point)?;
1516        }
1517        let seed = if overlapping { SEED } else { SEED2 };
1518        let delaunay_points = random_points_in_range(RANGE * 0.9, 80, seed);
1519        // Use a delaunay triangulation to "generate" non-intersecting constraint edges
1520        let mut d = Delaunay::new();
1521        for p in delaunay_points {
1522            d.insert(p)?;
1523        }
1524        let mut used_vertices = HashSet::new();
1525
1526        let mut inserted_constraints = Vec::new();
1527        for v in d.vertices() {
1528            // Insert only edges that do not touch at the end points if
1529            // overlapping is false
1530            if overlapping || used_vertices.insert(v.fix()) {
1531                let out_edge = v.out_edge().unwrap();
1532                let to = out_edge.to();
1533
1534                used_vertices.insert(to.fix());
1535
1536                let h0 = cdt.insert(v.position())?;
1537                let h1 = cdt.insert(to.position())?;
1538
1539                if cdt.add_constraint(h0, h1) {
1540                    inserted_constraints.push((h0, h1));
1541                }
1542                cdt.cdt_sanity_check();
1543
1544                assert_eq!(cdt.num_constraints(), inserted_constraints.len());
1545            }
1546        }
1547        // Check if all constraints still exists
1548        for (from, to) in inserted_constraints {
1549            assert!(cdt.exists_constraint(from, to));
1550        }
1551        cdt.cdt_sanity_check();
1552        Ok(())
1553    }
1554
1555    #[test]
1556    fn crash_case() -> Result<(), InsertionError> {
1557        let mut cdt = Cdt::new();
1558        cdt.insert(Point2::new(-8.403036273981348, -0.2248814041797189))?;
1559        cdt.insert(Point2::new(-8.353215494321136, 0.6088667888877364))?;
1560        cdt.insert(Point2::new(-7.811923439447166, -0.20003314976217013))?;
1561        cdt.insert(Point2::new(-7.710431174668773, 0.40691184742787456))?;
1562
1563        let v0 = cdt.insert(Point2::new(-8.907731924022768, 1.7433952434737847))?;
1564        let v1 = cdt.insert(Point2::new(-7.899415172394501, -1.4867902598716558))?;
1565        cdt.cdt_sanity_check();
1566        cdt.add_constraint(v0, v1);
1567        cdt.cdt_sanity_check();
1568        Ok(())
1569    }
1570
1571    #[test]
1572    fn test_split_constraint() -> Result<(), InsertionError> {
1573        let mut cdt = Cdt::new();
1574        cdt.insert(Point2::new(0.0, 0.0))?;
1575        cdt.insert(Point2::new(1.0, 0.0))?;
1576        cdt.insert(Point2::new(0.0, 1.0))?;
1577        let v0 = cdt.insert(Point2::new(0.0, 0.5))?;
1578        let v_last = cdt.insert(Point2::new(1.0, 0.5))?;
1579        cdt.add_constraint(v0, v_last);
1580        assert_eq!(cdt.num_constraints(), 1);
1581        // These points split an existing constraint
1582        let v1 = cdt.insert(Point2::new(0.25, 0.5))?;
1583        assert_eq!(cdt.num_constraints(), 2);
1584        let v2 = cdt.insert(Point2::new(0.75, 0.5))?;
1585        assert_eq!(cdt.num_constraints(), 3);
1586        assert!(cdt.exists_constraint(v0, v1));
1587        assert!(cdt.exists_constraint(v1, v2));
1588        assert!(cdt.exists_constraint(v2, v_last));
1589        cdt.cdt_sanity_check();
1590        Ok(())
1591    }
1592
1593    #[test]
1594    fn test_simple_retriangulation() -> Result<(), InsertionError> {
1595        let mut cdt = Cdt::new();
1596        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1597        cdt.insert(Point2::new(1.0, 0.25))?;
1598        cdt.insert(Point2::new(1.0, -0.25))?;
1599        let v3 = cdt.insert(Point2::new(2.0, 0.75))?;
1600        let v4 = cdt.insert(Point2::new(2.5, -0.3))?;
1601        cdt.insert(Point2::new(2.75, 0.75))?;
1602        cdt.insert(Point2::new(3.0, 0.75))?;
1603        cdt.insert(Point2::new(4.0, 0.25))?;
1604        cdt.insert(Point2::new(4.0, -0.25))?;
1605        let v7 = cdt.insert(Point2::new(5.0, 0.0))?;
1606        assert!(cdt.get_edge_from_neighbors(v3, v4).is_some());
1607        cdt.add_constraint(v0, v7);
1608        assert!(cdt.get_edge_from_neighbors(v0, v7).is_some());
1609        assert!(cdt.get_edge_from_neighbors(v3, v4).is_none());
1610
1611        cdt.cdt_sanity_check();
1612        Ok(())
1613    }
1614
1615    #[test]
1616    fn test_add_constraint_over_point() -> Result<(), InsertionError> {
1617        let mut cdt = Cdt::new();
1618        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1619        let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
1620        let v2 = cdt.insert(Point2::new(2.0, 0.0))?;
1621        cdt.insert(Point2::new(0.0, 1.0))?;
1622        cdt.add_constraint(v0, v2);
1623        assert_eq!(cdt.num_constraints(), 2);
1624        assert!(cdt.exists_constraint(v0, v1));
1625        assert!(cdt.exists_constraint(v1, v2));
1626        cdt.cdt_sanity_check();
1627        Ok(())
1628    }
1629
1630    fn test_cdt() -> Result<Cdt, InsertionError> {
1631        let mut cdt = Cdt::new();
1632        let v0 = cdt.insert(Point2::new(1.0, 0.0))?;
1633        let v1 = cdt.insert(Point2::new(0.0, 1.0))?;
1634        cdt.insert(Point2::new(0.0, 0.0))?;
1635        cdt.insert(Point2::new(1.0, 1.0))?;
1636        cdt.add_constraint(v0, v1);
1637        Ok(cdt)
1638    }
1639
1640    #[test]
1641    fn test_check_intersects_constraint_edge() -> Result<(), InsertionError> {
1642        let cdt = test_cdt()?;
1643        let from = Point2::new(0.2, 0.2);
1644        let to = Point2::new(0.6, 0.7);
1645        assert!(cdt.intersects_constraint(from, to));
1646        assert!(cdt.intersects_constraint(to, from));
1647        let to = Point2::new(-0.5, 0.2);
1648        assert!(!cdt.intersects_constraint(from, to));
1649        let from = Point2::new(0.5, 0.5);
1650        assert!(cdt.intersects_constraint(from, to));
1651        assert!(cdt.intersects_constraint(to, from));
1652        Ok(())
1653    }
1654
1655    #[test]
1656    fn test_add_constraint_degenerate() -> Result<(), InsertionError> {
1657        let mut cdt = Cdt::new();
1658        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1659        let v1 = cdt.insert(Point2::new(0.0, 1.0))?;
1660        assert!(cdt.add_constraint(v0, v1));
1661        assert!(!cdt.add_constraint(v1, v0));
1662        assert_eq!(cdt.num_constraints(), 1);
1663        let mut cdt = Cdt::new();
1664        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1665        let v1 = cdt.insert(Point2::new(0.0, 2.0))?;
1666        cdt.insert(Point2::new(0.0, 1.0))?;
1667        assert!(cdt.add_constraint(v0, v1));
1668        assert_eq!(cdt.num_constraints(), 2);
1669        Ok(())
1670    }
1671
1672    fn random_points_on_line<R>(
1673        range: i64,
1674        num_points: usize,
1675        rng: &mut R,
1676        line_dir: Point2<f64>,
1677    ) -> Vec<Point2<f64>>
1678    where
1679        R: Rng,
1680    {
1681        let mut result = Vec::with_capacity(num_points);
1682        let range = Uniform::new(-range, range).unwrap();
1683        for _ in 0..num_points {
1684            let factor = range.sample(rng);
1685            result.push(line_dir.mul(factor as f64));
1686        }
1687        result
1688    }
1689
1690    #[test]
1691    fn fuzz_test_on_line() -> Result<(), InsertionError> {
1692        // Generates points on a single line and randomly connects
1693        // them with constraints.
1694        let seed = SEED;
1695        const RANGE: i64 = 10000;
1696        const NUM_POINTS: usize = 1000;
1697        let mut rng = rand::rngs::StdRng::from_seed(*seed);
1698        let points = random_points_on_line(RANGE, NUM_POINTS, &mut rng, Point2::new(1.0, 0.0));
1699        let mut cdt = ConstrainedDelaunayTriangulation::<_>::new();
1700        for ps in points.chunks(2) {
1701            let from = ps[0];
1702            let to = ps[1];
1703            let from = cdt.insert(from)?;
1704            let to = cdt.insert(to)?;
1705            let should_add_constraint: bool = rng.random();
1706            if from != to && should_add_constraint {
1707                cdt.add_constraint(from, to);
1708            }
1709
1710            cdt.cdt_sanity_check();
1711        }
1712        Ok(())
1713    }
1714
1715    #[test]
1716    fn fuzz_test_on_grid() -> anyhow::Result<()> {
1717        use rand::seq::SliceRandom;
1718        // Generates points on a grid and randomly connects
1719        // them with non-intersecting constraints
1720        let seed = SEED;
1721        let mut points = Vec::with_capacity((RANGE * RANGE) as usize);
1722        const RANGE: i64 = 30;
1723        const NUM_CONSTRAINTS: usize = 2000;
1724        for x in -RANGE..RANGE {
1725            for y in -RANGE..RANGE {
1726                points.push(Point2::new(x as f64, y as f64));
1727            }
1728        }
1729        let mut rng = rand::rngs::StdRng::from_seed(*seed);
1730        points.shuffle(&mut rng);
1731        let mut cdt = Cdt::new();
1732        for p in points {
1733            cdt.insert(p)?;
1734        }
1735        let range = Uniform::new(-RANGE, RANGE)?;
1736        let directions_and_offset = [
1737            (Point2::new(1.0, 0.0), Point2::new(0.0, 1.0)),
1738            (Point2::new(0.0, 1.0), Point2::new(1.0, 0.0)),
1739            (Point2::new(1.0, 1.0), Point2::new(0.0, 0.0)),
1740        ];
1741        for _ in 0..NUM_CONSTRAINTS {
1742            let &(direction, offset) = directions_and_offset.choose(&mut rng).unwrap();
1743            let factor1 = range.sample(&mut rng);
1744            let factor2 = range.sample(&mut rng);
1745            let p1 = offset.add(direction.mul(factor1 as f64));
1746            let p2 = offset.add(direction.mul(factor2 as f64));
1747            if p1 != p2 {
1748                cdt.add_constraint_edge(p1, p2)?;
1749            }
1750        }
1751        cdt.cdt_sanity_check();
1752        Ok(())
1753    }
1754
1755    #[test]
1756    #[should_panic]
1757    fn test_panic_when_intersecting_a_constraint_edge() {
1758        let mut cdt = Cdt::new();
1759        let v0 = cdt.insert(Point2::new(0.0, 0.0)).unwrap();
1760        let v1 = cdt.insert(Point2::new(1.0, 0.0)).unwrap();
1761        cdt.add_constraint(v0, v1);
1762        cdt.add_constraint(v0, v1);
1763        cdt.add_constraint_edge(Point2::new(0.0, 0.0), Point2::new(1.0, 0.0))
1764            .unwrap();
1765        cdt.add_constraint_edge(Point2::new(0.5, 0.5), Point2::new(0.5, -0.5))
1766            .unwrap();
1767    }
1768
1769    #[test]
1770    #[should_panic]
1771    fn test_panic_when_intersecting_a_complex_constraint_edge() {
1772        let mut cdt = Cdt::new();
1773        let v0 = cdt.insert(Point2::new(0.5, 2.0)).unwrap();
1774        cdt.insert(Point2::new(0.0, 1.5)).unwrap();
1775        cdt.insert(Point2::new(1.0, 1.5)).unwrap();
1776        cdt.add_constraint_edge(Point2::new(0.0, 0.5), Point2::new(1.0, 0.5))
1777            .unwrap();
1778        let v1 = cdt.insert(Point2::new(0.5, 0.0)).unwrap();
1779
1780        cdt.add_constraint(v0, v1);
1781    }
1782
1783    #[test]
1784    fn test_cdt_remove_degenerate() -> Result<(), InsertionError> {
1785        let mut cdt = Cdt::new();
1786        let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1787        let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
1788        let v2 = cdt.insert(Point2::new(0.0, 1.0))?;
1789        cdt.add_constraint(v0, v1);
1790        cdt.add_constraint(v1, v2);
1791        cdt.add_constraint(v2, v0);
1792        assert_eq!(cdt.num_constraints(), 3);
1793        cdt.remove(v1);
1794        assert_eq!(cdt.num_constraints(), 1);
1795        cdt.cdt_sanity_check();
1796        Ok(())
1797    }
1798
1799    #[test]
1800    fn test_crash_scenario() -> Result<(), InsertionError> {
1801        let mut cdt = Cdt::new();
1802        for point in get_points().iter().cloned() {
1803            cdt.insert(point)?;
1804        }
1805
1806        let from = cdt.insert(Point2::new(3.2348222581121586, -8.136734693290444))?;
1807        cdt.cdt_sanity_check();
1808        let to = cdt.insert(Point2::new(-8.839844309691154, -8.930685085211245))?;
1809        cdt.cdt_sanity_check();
1810
1811        cdt.add_constraint(from, to);
1812        cdt.cdt_sanity_check();
1813
1814        Ok(())
1815    }
1816
1817    fn get_points() -> Vec<Point2<f64>> {
1818        vec![
1819            Point2::new(-3.947938514986289, -8.016680534876258),
1820            Point2::new(-4.016029045366132, -9.680855465455608),
1821            Point2::new(-4.46653326962287, -8.462568264351527),
1822            Point2::new(-7.033691993749462, -8.88072731817851),
1823            Point2::new(-6.058360215097096, -8.644637388990939),
1824        ]
1825    }
1826
1827    #[test]
1828    fn test_add_constraint_edges() -> Result<(), InsertionError> {
1829        for is_closed in [true, false] {
1830            let mut cdt = Cdt::new();
1831
1832            const NUM_VERTICES: usize = 51;
1833            let vertices = (0..NUM_VERTICES).map(|i| {
1834                let angle = core::f64::consts::PI * 2.0 * i as f64 / NUM_VERTICES as f64;
1835                let (sin, cos) = angle.sin_cos();
1836                Point2::new(sin, cos)
1837            });
1838
1839            cdt.add_constraint_edges(vertices, is_closed)?;
1840
1841            if is_closed {
1842                assert_eq!(NUM_VERTICES, cdt.num_constraints());
1843            } else {
1844                assert_eq!(NUM_VERTICES - 1, cdt.num_constraints());
1845            }
1846
1847            cdt.cdt_sanity_check();
1848        }
1849
1850        Ok(())
1851    }
1852
1853    #[test]
1854    fn test_add_constraint_edges_empty() -> Result<(), InsertionError> {
1855        let mut cdt = Cdt::new();
1856
1857        cdt.add_constraint_edges(core::iter::empty(), false)?;
1858        cdt.add_constraint_edges(core::iter::empty(), true)?;
1859
1860        assert_eq!(cdt.num_vertices(), 0);
1861        assert_eq!(cdt.num_constraints(), 0);
1862
1863        Ok(())
1864    }
1865
1866    #[test]
1867    fn test_add_constraint_edges_single() -> Result<(), InsertionError> {
1868        let mut cdt = Cdt::new();
1869
1870        cdt.add_constraint_edges([Point2::new(1.0, 1.0)], true)?;
1871        cdt.add_constraint_edges([Point2::new(2.0, 3.0)], false)?;
1872
1873        assert_eq!(cdt.num_vertices(), 2);
1874        assert_eq!(cdt.num_constraints(), 0);
1875
1876        Ok(())
1877    }
1878
1879    #[test]
1880    fn test_add_constraint_edges_duplicate() -> Result<(), InsertionError> {
1881        let mut cdt = Cdt::new();
1882        let point = Point2::new(0.0, 1.0);
1883        cdt.add_constraint_edges([point, point], true)?;
1884        cdt.add_constraint_edges([point, point], false)?;
1885        cdt.add_constraint_edges([point, point, point], true)?;
1886        cdt.add_constraint_edges([point, point, point], false)?;
1887
1888        assert_eq!(cdt.num_vertices(), 1);
1889        assert_eq!(cdt.num_constraints(), 0);
1890
1891        cdt.cdt_sanity_check();
1892        Ok(())
1893    }
1894
1895    #[test]
1896    fn test_clear() -> Result<(), InsertionError> {
1897        let mut cdt = test_cdt()?;
1898        cdt.clear();
1899
1900        assert_eq!(cdt.num_constraints(), 0);
1901        assert_eq!(cdt.num_all_faces(), 1);
1902        assert_eq!(cdt.num_vertices(), 0);
1903        assert_eq!(cdt.num_directed_edges(), 0);
1904        Ok(())
1905    }
1906
1907    #[test]
1908    fn test_cdt_edge_split_degenerate() -> Result<(), InsertionError> {
1909        let mut cdt = Cdt::new();
1910        cdt.add_constraint_edge(Point2::new(-10.0, -10.0), Point2::new(20.0, -10.0))?;
1911        cdt.insert(Point2::new(0.0, -10.0))?;
1912
1913        assert_eq!(cdt.num_constraints(), 2);
1914
1915        Ok(())
1916    }
1917
1918    #[test]
1919    fn infinite_loop_bug() -> Result<(), InsertionError> {
1920        // See https://github.com/Stoeoef/spade/issues/98
1921        let mut triangulation = Cdt::default();
1922
1923        let start = Point2::new(-21.296192169189453, 9.872323036193848);
1924        let edges = [
1925            (
1926                Point2::new(-20.926544189453125, 16.53529167175293),
1927                Point2::new(-27.772645950317383, 4.197676658630371),
1928            ),
1929            (
1930                Point2::new(-20.03745460510254, 12.93730354309082),
1931                Point2::new(-20.930097579956055, 11.93786907196045),
1932            ),
1933            (
1934                Point2::new(-15.576859474182129, 8.772907257080078),
1935                Point2::new(-22.373262405395508, 12.348699569702148),
1936            ),
1937            (
1938                Point2::new(-10.038422584533691, 5.663522243499756),
1939                Point2::new(-16.382625579833984, 9.09498119354248),
1940            ),
1941            (
1942                Point2::new(0.0, 0.0),
1943                Point2::new(-13.11422061920166, 7.30709171295166),
1944            ),
1945            (
1946                Point2::new(-19.230497360229492, -3.7645812034606934),
1947                Point2::new(-7.411926746368408, 3.486957311630249),
1948            ),
1949            (
1950                Point2::new(-25.072885513305664, -9.239323616027832),
1951                Point2::new(-19.462360382080078, -1.621320366859436),
1952            ),
1953            (
1954                Point2::new(-32.41080856323242, -13.72575855255127),
1955                Point2::new(-22.58626365661621, -2.076631784439087),
1956            ),
1957            (
1958                Point2::new(-32.41080856323242, -13.72575855255127),
1959                Point2::new(-25.57504653930664, -4.952820301055908),
1960            ),
1961            (
1962                Point2::new(-33.08932113647461, 0.31093916296958923),
1963                Point2::new(-25.955543518066406, 0.18878456950187683),
1964            ),
1965        ];
1966
1967        for (p1, p2) in edges {
1968            let p1 = triangulation.insert(p1)?;
1969            let p2 = triangulation.insert(p2)?;
1970            assert!(triangulation.can_add_constraint(p1, p2));
1971            triangulation.add_constraint(p1, p2);
1972        }
1973
1974        triangulation.insert(start)?;
1975        Ok(())
1976    }
1977
1978    #[test]
1979    pub fn infinite_loop_2() -> Result<(), InsertionError> {
1980        let lines = [
1981            [
1982                Point2::new(0.9296344883099084, 0.03071359966930065),
1983                Point2::new(0.26031306872107085, 0.34491289915959455),
1984            ],
1985            [
1986                Point2::new(0.7384289920396423, 0.4981747664368982),
1987                Point2::new(0.06543525273452533, 0.34139896206401854),
1988            ],
1989            [
1990                Point2::new(0.9535295221136963, 0.9114305148801416),
1991                Point2::new(0.8306091165247367, 0.08959389670590667),
1992            ],
1993        ];
1994
1995        let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::new();
1996
1997        for [a, b] in lines {
1998            let a = cdt.insert(a)?;
1999            let b = cdt.insert(b)?;
2000
2001            cdt.add_constraint_and_split(a, b, |v| v);
2002        }
2003
2004        // This insertion used to fail as the position could not be located
2005        cdt.insert(Point2::new(0.5138795569454557, 0.3186272217036502))?;
2006        Ok(())
2007    }
2008
2009    fn get_cdt_for_try_add_constraint() -> Result<Cdt, InsertionError> {
2010        let vertices = vec![
2011            Point2::new(0.0, -10.0),
2012            Point2::new(76.0, 0.0),
2013            Point2::new(20.0, 20.0),
2014            Point2::new(20.0, -30.0),
2015            Point2::new(45.0, 25.0),
2016            Point2::new(32.0, -35.0),
2017            Point2::new(60.0, 20.0),
2018            Point2::new(60.0, -30.0),
2019            Point2::new(50.0, -34.0),
2020        ];
2021
2022        Cdt::bulk_load_cdt(vertices, vec![[3, 2], [5, 4], [7, 6]])
2023    }
2024
2025    #[test]
2026    fn get_try_cdt_for_duplicate_vertices() -> Result<(), InsertionError> {
2027        let vertices = vec![
2028            Point2::new(0.0, -10.0),
2029            Point2::new(76.0, 0.0),
2030            Point2::new(76.0, 0.0), // Duplicated vertex
2031            Point2::new(20.0, -30.0),
2032            Point2::new(45.0, 25.0),
2033            Point2::new(32.0, -35.0),
2034            Point2::new(60.0, 20.0),
2035            Point2::new(60.0, -30.0),
2036            Point2::new(50.0, -34.0),
2037        ];
2038        let mut conflicting_edges = Vec::new();
2039        let cdt = Cdt::try_bulk_load_cdt(vertices, vec![[3, 2], [5, 4], [7, 6]], |e| {
2040            conflicting_edges.push(e)
2041        })?;
2042        // Hardcoded values, may change if CDT algorithm change
2043        assert_eq!(&conflicting_edges, &[[5, 6,], [3, 4,]]);
2044        assert_eq!(cdt.num_constraints, 1);
2045        Ok(())
2046    }
2047
2048    #[test]
2049    fn test_single_split() -> Result<(), InsertionError> {
2050        let vertices = vec![
2051            Point2::new(-1.0, 0.0),
2052            Point2::new(1.0, 0.0),
2053            Point2::new(0.0, -1.0),
2054            Point2::new(0.0, 1.0),
2055        ];
2056
2057        let mut cdt = Cdt::bulk_load_cdt(vertices, vec![[2, 3]])?;
2058
2059        let initial_num_vertices = cdt.num_vertices();
2060        let from = FixedVertexHandle::from_index(0);
2061        let to = FixedVertexHandle::from_index(1);
2062
2063        let edges = cdt.add_constraint_and_split(from, to, |v| v);
2064
2065        assert_eq!(cdt.num_vertices(), initial_num_vertices + 1);
2066        assert_eq!(edges.len(), 2);
2067        check_returned_edges(&cdt, &edges, from, to);
2068
2069        Ok(())
2070    }
2071
2072    #[test]
2073    fn test_multiple_splits() -> Result<(), InsertionError> {
2074        let mut cdt = get_cdt_for_try_add_constraint()?;
2075
2076        let initial_num_vertices = cdt.num_vertices();
2077        let from = FixedVertexHandle::from_index(0);
2078        let to = FixedVertexHandle::from_index(1);
2079
2080        let edges = cdt.add_constraint_and_split(from, to, |v| v);
2081
2082        // 3 new points should be added as the constraint intersects all 3 existing edges
2083        assert_eq!(cdt.num_vertices(), initial_num_vertices + 3);
2084        assert_eq!(edges.len(), 4);
2085        check_returned_edges(&cdt, &edges, from, to);
2086
2087        Ok(())
2088    }
2089
2090    #[test]
2091    fn test_try_add_constraint() -> Result<(), InsertionError> {
2092        let mut cdt = get_cdt_for_try_add_constraint()?;
2093
2094        let initial_num_vertices = cdt.num_vertices();
2095        let initial_num_constraints = cdt.num_constraints();
2096        let from = FixedVertexHandle::from_index(0);
2097        let to = FixedVertexHandle::from_index(1);
2098
2099        // Is expected to fail (return an empty list)
2100        let edges = cdt.try_add_constraint(from, to);
2101        assert_eq!(edges, Vec::new());
2102        assert_eq!(cdt.num_vertices(), initial_num_vertices);
2103        assert_eq!(cdt.num_constraints(), initial_num_constraints);
2104
2105        let from = FixedVertexHandle::from_index(2);
2106        let to = FixedVertexHandle::from_index(3);
2107
2108        // Try to add on top of an existing edge
2109        let edges = cdt.try_add_constraint(from, to);
2110        assert_eq!(edges.len(), 1);
2111
2112        Ok(())
2113    }
2114
2115    #[test]
2116    fn test_remove_vertex_respects_constraints() -> Result<(), InsertionError> {
2117        let mut triangulation = ConstrainedDelaunayTriangulation::<Point2<f32>>::bulk_load_cdt(
2118            vec![
2119                Point2::new(-1.0, 0.0),
2120                Point2::new(0.0, 0.0),
2121                Point2::new(0.0, 1.0),
2122                Point2::new(1.0, 1.0),
2123            ],
2124            vec![[0, 1], [1, 2], [2, 3]],
2125        )?;
2126
2127        let mut copy = triangulation.clone();
2128
2129        triangulation.remove(FixedVertexHandle::from_index(1));
2130        triangulation.cdt_sanity_check();
2131
2132        copy.locate_and_remove(Point2::new(0.0, 0.0));
2133        copy.cdt_sanity_check();
2134
2135        Ok(())
2136    }
2137
2138    #[test]
2139    fn test_remove_constraint_edge() -> Result<(), InsertionError> {
2140        let mut cdt = get_cdt_for_try_add_constraint()?;
2141        for edge in cdt.fixed_undirected_edges() {
2142            cdt.remove_constraint_edge(edge);
2143        }
2144        assert_eq!(cdt.num_constraints, 0);
2145        cdt.sanity_check();
2146
2147        let added_edges = cdt.try_add_constraint(
2148            FixedVertexHandle::from_index(0),
2149            FixedVertexHandle::from_index(1),
2150        );
2151        assert_eq!(added_edges.len(), 1);
2152
2153        assert!(cdt.remove_constraint_edge(added_edges.first().unwrap().as_undirected()));
2154        assert_eq!(cdt.num_constraints, 0);
2155        cdt.sanity_check();
2156
2157        Ok(())
2158    }
2159
2160    #[test]
2161    fn edge_intersection_precision_test_2() -> Result<(), InsertionError> {
2162        let edges = [
2163            [
2164                Point2 {
2165                    x: 18.69314193725586,
2166                    y: 19.589109420776367,
2167                },
2168                Point2 {
2169                    x: 18.69314193725586,
2170                    y: 20.40362548828125,
2171                },
2172            ],
2173            [
2174                Point2 {
2175                    x: 19.507659912109375,
2176                    y: 20.40362548828125,
2177                },
2178                Point2 {
2179                    x: 17.878625869750977,
2180                    y: 18.774595260620117,
2181                },
2182            ],
2183            [
2184                Point2 {
2185                    x: 20.322175979614258,
2186                    y: 21.218143463134766,
2187                },
2188                Point2 {
2189                    x: 15.435077667236328,
2190                    y: 16.331045150756836,
2191                },
2192            ],
2193        ];
2194        let mut cdt: ConstrainedDelaunayTriangulation<Point2<f64>> =
2195            ConstrainedDelaunayTriangulation::new();
2196        for edge in edges {
2197            let point_a = cdt.insert(edge[0])?;
2198            let point_b = cdt.insert(edge[1])?;
2199            cdt.cdt_sanity_check();
2200            cdt.add_constraint_and_split(point_a, point_b, |v| v);
2201            cdt.cdt_sanity_check();
2202        }
2203
2204        Ok(())
2205    }
2206
2207    #[test]
2208    fn edge_intersection_precision_test_3() -> Result<(), InsertionError> {
2209        let edges = [
2210            [
2211                Point2 {
2212                    x: -11.673287,
2213                    y: -28.37192,
2214                },
2215                Point2 {
2216                    x: -16.214716,
2217                    y: -43.81278,
2218                },
2219            ],
2220            [
2221                Point2 {
2222                    x: 7.4022045,
2223                    y: -51.355137,
2224                },
2225                Point2 {
2226                    x: -13.92232,
2227                    y: -36.01863,
2228                },
2229            ],
2230        ];
2231
2232        // `f32` is important. This makes the intersection of the two edges coincide with an
2233        // existing vertex, triggering an edge case.
2234        let mut cdt: ConstrainedDelaunayTriangulation<Point2<f32>> =
2235            ConstrainedDelaunayTriangulation::new();
2236        let mut returned_constraint_edge_counts = Vec::new();
2237        for edge in edges {
2238            let point_a = cdt.insert(edge[0])?;
2239            let point_b = cdt.insert(edge[1])?;
2240            returned_constraint_edge_counts
2241                .push(cdt.add_constraint_and_split(point_a, point_b, |v| v).len());
2242            cdt.cdt_sanity_check();
2243        }
2244
2245        // Usually, 4 constraints should be present. However, due to the overlap of the intersection
2246        // point, the second call to `add_constraint_and_split` does not add 2 constraint edges.
2247        // See issue #113 for more information
2248        assert_eq!(cdt.num_constraints, 3);
2249        assert_eq!(returned_constraint_edge_counts, vec![1, 1]);
2250
2251        Ok(())
2252    }
2253
2254    #[test]
2255    fn edge_intersection_precision_test_4() -> Result<(), InsertionError> {
2256        let points = [
2257            (1.0f32, 1.0f32),
2258            (63.137257, 87.04635),
2259            (1.0, 37.488983),
2260            (59.171143, 97.93353),
2261            (68.78571, 71.541046),
2262        ];
2263
2264        run_bulk_load_proptest(&points)
2265    }
2266
2267    #[test]
2268    fn edge_intersection_precision_test_5() -> Result<(), InsertionError> {
2269        let points = [
2270            (12.645211, 67.07809),
2271            (49.93318, 15.726259),
2272            (22.433575, 53.597496),
2273            (6.3033395, 32.111538),
2274            (89.09958, 1.0720834),
2275        ];
2276        run_bulk_load_proptest(&points)
2277    }
2278
2279    #[test]
2280    fn edge_intersection_precision_test_6() -> Result<(), InsertionError> {
2281        let points = [
2282            (32.944817, 13.24526),
2283            (91.99222, 22.252642),
2284            (12.645211, 67.07809),
2285            (49.93318, 15.726259),
2286            (22.433575, 53.597496),
2287            (6.3033395, 32.111538),
2288            (89.09958, 1.0720834),
2289        ];
2290        run_bulk_load_proptest(&points)
2291    }
2292
2293    #[test]
2294    fn edge_intersection_precision_test_7() {
2295        let edges = [
2296            (
2297                Point2 {
2298                    x: 15.435077667236328,
2299                    y: 16.331045150756836,
2300                },
2301                Point2 {
2302                    x: 20.322175979614258,
2303                    y: 21.218143463134766,
2304                },
2305            ),
2306            (
2307                Point2 {
2308                    x: 18.69314193725586,
2309                    y: 19.589109420776367,
2310                },
2311                Point2 {
2312                    x: 19.507659912109375,
2313                    y: 20.40362548828125,
2314                },
2315            ),
2316            (
2317                Point2 {
2318                    x: 19.507659912109375,
2319                    y: 21.218143463134766,
2320                },
2321                Point2 {
2322                    x: 19.507659912109375,
2323                    y: 20.40362548828125,
2324                },
2325            ),
2326            (
2327                Point2 {
2328                    x: 17.878625869750977,
2329                    y: 18.774595260620117,
2330                },
2331                Point2 {
2332                    x: 19.507659912109375,
2333                    y: 20.40362548828125,
2334                },
2335            ),
2336        ];
2337        let mut cdt: ConstrainedDelaunayTriangulation<Point2<f64>> =
2338            ConstrainedDelaunayTriangulation::new();
2339        for edge in edges {
2340            let point_a = cdt.insert(edge.0).unwrap();
2341            let point_b = cdt.insert(edge.1).unwrap();
2342            cdt.add_constraint_and_split(point_a, point_b, |v| v);
2343            cdt.cdt_sanity_check();
2344        }
2345    }
2346
2347    #[test]
2348    fn cdt_test_inserting_few_points() -> Result<(), InsertionError> {
2349        let vertices = [
2350            Point2::new(43.44877, 78.04408),
2351            Point2::new(43.498154, 81.00147),
2352            Point2::new(42.54009, 79.197945),
2353            Point2::new(42.702503, 77.25916),
2354            Point2::new(43.033974, 76.91306),
2355            Point2::new(43.843018, 76.06832),
2356            Point2::new(43.72607, 77.74121),
2357            Point2::new(42.55491, 79.02104),
2358        ];
2359        let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new();
2360
2361        for point in vertices {
2362            cdt.insert(point)?;
2363        }
2364
2365        let v = |n| FixedVertexHandle::from_index(n);
2366
2367        cdt.add_constraint(v(0), v(4));
2368        cdt.add_constraint(v(1), v(6));
2369        cdt.add_constraint(v(2), v(7));
2370        cdt.add_constraint(v(3), v(4));
2371        cdt.add_constraint(v(4), v(5));
2372        cdt.add_constraint(v(5), v(6));
2373        cdt.add_constraint(v(7), v(3));
2374        cdt.add_constraint(v(6), v(7));
2375
2376        cdt.cdt_sanity_check();
2377        Ok(())
2378    }
2379
2380    #[test]
2381    fn edge_intersection_precision_test_8() -> Result<(), InsertionError> {
2382        let points = [
2383            (43.44877, 78.04408),
2384            (15.193367, 1.0),
2385            (1.0, 1.0),
2386            (1.0, 1.0),
2387            (41.485252, 91.78975),
2388            (49.090862, 1.0),
2389            (43.498154, 81.00147),
2390            (1.0, 1.0),
2391            (23.288902, 97.52936),
2392            (75.59119, 42.91933),
2393            (25.808325, 97.32154),
2394        ];
2395        run_bulk_load_proptest(&points)
2396    }
2397
2398    fn run_bulk_load_proptest(points: &[(f32, f32)]) -> Result<(), InsertionError> {
2399        let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new();
2400
2401        let mut last_handle = None;
2402        for &(x, y) in points {
2403            let next_handle = cdt.insert(Point2::new(x, y))?;
2404            if let Some(last_handle) = last_handle {
2405                let edges = cdt.add_constraint_and_split(last_handle, next_handle, |p| p);
2406                if !edges.is_empty() {
2407                    check_returned_edges(&cdt, &edges, last_handle, next_handle);
2408                }
2409            }
2410            last_handle = Some(next_handle);
2411
2412            cdt.cdt_sanity_check();
2413        }
2414
2415        Ok(())
2416    }
2417
2418    #[test]
2419    fn edge_intersection_precision_test_9() -> Result<(), InsertionError> {
2420        let points = [
2421            (17.315233, 1.0),
2422            (36.59658, 1.0),
2423            (1.0, 62.270954),
2424            (1.0, 1.0),
2425            (38.64656, 24.732662),
2426        ];
2427
2428        run_bulk_load_proptest(&points)
2429    }
2430
2431    #[test]
2432    fn edge_intersection_precision_test_10() -> Result<(), InsertionError> {
2433        let points = [
2434            (57.128956, 53.16448),
2435            (78.60271, 71.51385),
2436            (94.13138, 1.0),
2437            (1.0, 1.0),
2438            (65.829636, 82.399826),
2439            (84.53016, 25.01849),
2440            (1.0, 1.0),
2441            (52.712097, 98.32006),
2442            (77.15014, 51.253986),
2443            (94.362755, 59.469707),
2444            (44.423584, 69.76751),
2445            (1.0, 1.0),
2446            (72.84374, 64.58434),
2447            (27.416088, 88.37326),
2448        ];
2449
2450        run_bulk_load_proptest(&points)
2451    }
2452
2453    proptest! {
2454        #![proptest_config(ProptestConfig::with_cases(200))]
2455        #[test]
2456        #[ignore]
2457        fn
2458        prop_test_bulk_load_cdt_stable(
2459            points in proptest::collection::vec((1.0f32..100.0f32, 1.0f32..100.0f32), 1..100),
2460            edges in proptest::collection::vec((0usize..100, 0usize..100), 1..100)) {
2461            prop_test_bulk_load_cdt_stable_impl(points, edges);
2462        }
2463    }
2464
2465    #[test]
2466    fn fuzz_test_bulk_load_cdt() {
2467        let points = vec![
2468            Point2::new(-2.7049442424493675e-11, -2.704772938494617e-11),
2469            Point2::new(-2.7049442424493675e-11, -2.2374774353910703e-35),
2470            Point2::new(-2.704944241771741e-11, -2.704922037988875e-11),
2471            Point2::new(-2.7049442424493675e-11, -2.7049442424493572e-11),
2472            Point2::new(-2.7049442424493675e-11, 0.0),
2473        ];
2474
2475        let cdt =
2476            ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, Vec::new())
2477                .unwrap();
2478
2479        cdt.cdt_sanity_check();
2480    }
2481
2482    #[test]
2483    fn fuzz_test_bulk_load_cdt_2() {
2484        {
2485            let points = vec![
2486                Point2::new(0.11617647291654172, -2.7049442424493675e-11),
2487                Point2::new(-3.684373062283352e-14, -2.7049442424493675e-11),
2488                Point2::new(-2.7049442424493268e-11, -2.7049442424493675e-11),
2489                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2490                Point2::new(-2.704943949713427e-11, -2.7049442424493856e-11),
2491            ];
2492            let edges = vec![[3, 0]];
2493
2494            let cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, edges)
2495                .unwrap();
2496
2497            cdt.cdt_sanity_check();
2498        }
2499    }
2500
2501    #[test]
2502    fn fuzz_test_bulk_load_cdt_3() {
2503        {
2504            let points = vec![
2505                Point2::new(-2.7049442424378697e-11, -2.7049442424493675e-11),
2506                Point2::new(-2.7049434889184558e-11, -2.7049442424493675e-11),
2507                Point2::new(-2.7049442395058873e-11, -2.7049442424493675e-11),
2508                Point2::new(-2.7049442424546208e-11, -2.7049442424493675e-11),
2509                Point2::new(0.0004538143382352941, -2.705036194996328e-11),
2510                Point2::new(-2.704944239484691e-11, -2.7049442424493675e-11),
2511                Point2::new(-2.7049442424546615e-11, -2.7049442424493675e-11),
2512                Point2::new(-2.7049442424493882e-11, -2.70494406897702e-11),
2513                Point2::new(0.11617551691391888, -2.7049442424493675e-11),
2514                Point2::new(-2.7049442424493572e-11, -2.7049442424493675e-11),
2515                Point2::new(-2.704944241771741e-11, -2.704944242438945e-11),
2516                Point2::new(-2.7049442424493678e-11, -2.7049442424493662e-11),
2517                Point2::new(-2.704944241771741e-11, -2.7049442424493675e-11),
2518                Point2::new(-2.7049442424467205e-11, -2.7049442424493675e-11),
2519                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2520                Point2::new(-2.7049442424493675e-11, -2.7049441611342046e-11),
2521                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2522                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2523                Point2::new(-2.704944241771741e-11, -2.7049442424493675e-11),
2524                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2525                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2526                Point2::new(-2.704944242448623e-11, -2.7049442424493675e-11),
2527                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2528                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2529                Point2::new(-2.7049442424493675e-11, -2.6502324517958398e-11),
2530                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2531                Point2::new(-2.7049442424493675e-11, -2.7049442423646642e-11),
2532                Point2::new(-2.704944242437787e-11, -2.7049442424493675e-11),
2533                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2534                Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2535                Point2::new(-2.7049442424493646e-11, -2.6375537048546205e-11),
2536            ];
2537            let edges = vec![];
2538
2539            let cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, edges)
2540                .unwrap();
2541
2542            cdt.cdt_sanity_check();
2543        }
2544    }
2545
2546    #[test]
2547    fn fuzz_test_bulk_load_cdt_4() {
2548        {
2549            let points = vec![
2550                Point2::new(5.532608426405843e-5, -2.705000112790462e-11),
2551                Point2::new(-2.7049442384471368e-11, -2.6555608406219246e-11),
2552                Point2::new(-4.6189490530823523e-10, -2.7049442424337338e-11),
2553                Point2::new(-2.688601759526906e-11, -2.7049442424517663e-11),
2554                Point2::new(-2.704944242448623e-11, -2.7049442424493675e-11),
2555                Point2::new(-2.704944240840005e-11, -2.7049442424493675e-11),
2556                Point2::new(-1.1276731182827942e-13, -2.7049442424493675e-11),
2557            ];
2558
2559            let edges = vec![];
2560
2561            ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load(points.clone()).unwrap();
2562
2563            let cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, edges)
2564                .unwrap();
2565
2566            cdt.cdt_sanity_check();
2567        }
2568    }
2569
2570    fn prop_test_bulk_load_cdt_stable_impl(points: Vec<(f32, f32)>, edges: Vec<(usize, usize)>) {
2571        let max_index = points.len() - 1;
2572        let edges = edges
2573            .iter()
2574            .copied()
2575            .map(|(from, to)| [from.min(max_index), to.min(max_index)])
2576            .collect::<Vec<_>>();
2577
2578        let vertices = points
2579            .iter()
2580            .copied()
2581            .map(|(x, y)| Point2::new(x, y))
2582            .collect::<Vec<_>>();
2583        let result = ConstrainedDelaunayTriangulation::<Point2<f32>>::try_bulk_load_cdt(
2584            vertices,
2585            edges,
2586            |_| {},
2587        )
2588        .unwrap();
2589
2590        result.cdt_sanity_check();
2591    }
2592
2593    #[test]
2594    fn bulk_load_cdt_stable_proptest_1() {
2595        let points = vec![(1.0, 1.0), (61.541332, 1.0), (1.0, 69.54), (1.0, 19.13391)];
2596        let edges = vec![(3, 3)];
2597        prop_test_bulk_load_cdt_stable_impl(points, edges)
2598    }
2599
2600    #[test]
2601    fn bulk_load_cdt_stable_proptest_2() {
2602        let points = vec![
2603            (1.0, 98.26258),
2604            (61.541332, 59.135162),
2605            (1.0, 1.0),
2606            (57.31029, 1.0),
2607        ];
2608        let edges = vec![(0, 3)];
2609
2610        prop_test_bulk_load_cdt_stable_impl(points, edges)
2611    }
2612
2613    fn check_returned_edges<S: crate::SpadeNum>(
2614        cdt: &ConstrainedDelaunayTriangulation<Point2<S>>,
2615        edges: &[FixedDirectedEdgeHandle],
2616        first_vertex: FixedVertexHandle,
2617        last_vertex: FixedVertexHandle,
2618    ) {
2619        cdt.cdt_sanity_check();
2620
2621        let first_pos = cdt.vertex(first_vertex).position().to_f64();
2622        let last_pos = cdt.vertex(last_vertex).position().to_f64();
2623
2624        let last = edges.last().expect("Edges cannot be empty");
2625        let last = cdt.directed_edge(*last);
2626
2627        let mut current_from = first_vertex;
2628
2629        for edge in edges {
2630            let edge = cdt.directed_edge(*edge);
2631            assert_eq!(edge.from().fix(), current_from);
2632            let position = edge.to().position().to_f64();
2633            let distance =
2634                crate::delaunay_core::math::distance_2(first_pos, last_pos, position).sqrt();
2635            // We'll use some arbitrary epsilon that will just barely passes the test suite.
2636            // It doesn't seem to matter too much in practice as the distance is still small.
2637            assert_abs_diff_eq!(distance, 0.0, epsilon = 3.0e-2f64);
2638            current_from = edge.to().fix();
2639        }
2640
2641        assert_eq!(last.to().fix(), last_vertex);
2642    }
2643
2644    proptest! {
2645        #![proptest_config(ProptestConfig::with_cases(1000))]
2646        #[test]
2647        #[ignore]
2648        fn test_add_constraint_and_split(points in proptest::collection::vec((1.0f32..100.0f32, 1.0f32..100.0f32), 0..100)) {
2649            let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new();
2650
2651            let mut last_handle = None;
2652            for (x, y) in points {
2653                let next_handle = cdt.insert(Point2::new(x,y)).unwrap();
2654                if let Some(last_handle) = last_handle {
2655                    let edges = cdt.add_constraint_and_split(last_handle, next_handle, |p|p);
2656                    if !edges.is_empty() {
2657                        check_returned_edges(&cdt, &edges, last_handle, next_handle);
2658                    }
2659                }
2660                last_handle = Some(next_handle);
2661                cdt.cdt_sanity_check();
2662            }
2663
2664        }
2665    }
2666}