spade/delaunay_core/
triangulation_ext.rs

1use smallvec::SmallVec;
2
3use super::dcel_operations;
4use super::dcel_operations::IsolateVertexResult;
5use super::handles::*;
6use super::math;
7
8use crate::delaunay_core::dcel_operations::append_unconnected_vertex;
9use crate::delaunay_core::Dcel;
10use crate::HintGenerator;
11use crate::Point2;
12use crate::{HasPosition, InsertionError, PositionInTriangulation, Triangulation};
13
14use alloc::vec::Vec;
15
16impl<T> TriangulationExt for T where T: Triangulation {}
17
18#[derive(Copy, Clone, Debug, Ord, PartialOrd, Hash, Eq, PartialEq)]
19pub enum PositionWhenAllVerticesOnLine {
20    OnEdge(FixedDirectedEdgeHandle),
21    OnVertex(FixedVertexHandle),
22    NotOnLine(FixedDirectedEdgeHandle),
23    ExtendingLine(FixedVertexHandle),
24}
25
26#[derive(Copy, Clone, Debug, Ord, PartialOrd, Hash, Eq, PartialEq)]
27pub enum VertexToInsert<V> {
28    NewVertex(V),
29    ExistingVertex(FixedVertexHandle),
30}
31
32impl<V> VertexToInsert<V> {
33    pub fn into_vertex(self) -> V {
34        match self {
35            VertexToInsert::NewVertex(v) => v,
36            VertexToInsert::ExistingVertex(_) => panic!("Cannot convert existing vertex to vertex"),
37        }
38    }
39
40    pub fn position<DE, UE, F>(
41        &self,
42        dcel: &Dcel<V, DE, UE, F>,
43    ) -> Point2<<V as HasPosition>::Scalar>
44    where
45        V: HasPosition,
46    {
47        match self {
48            VertexToInsert::NewVertex(v) => v.position(),
49            VertexToInsert::ExistingVertex(handle) => dcel.vertex(*handle).position(),
50        }
51    }
52
53    pub fn resolve<DE, UE, F>(self, dcel: &mut Dcel<V, DE, UE, F>) -> FixedVertexHandle {
54        match self {
55            VertexToInsert::NewVertex(v) => append_unconnected_vertex(dcel, v),
56            VertexToInsert::ExistingVertex(handle) => handle,
57        }
58    }
59}
60
61impl PositionWhenAllVerticesOnLine {
62    fn to_regular_position_in_triangulation<T: Triangulation>(
63        self,
64        triangulation: &T,
65    ) -> PositionInTriangulation
66    where
67        T::Vertex: HasPosition,
68    {
69        use PositionWhenAllVerticesOnLine::*;
70        match self {
71            OnEdge(edge) => PositionInTriangulation::OnEdge(edge),
72            OnVertex(v) => PositionInTriangulation::OnVertex(v),
73            NotOnLine(edge) => PositionInTriangulation::OutsideOfConvexHull(edge),
74            ExtendingLine(v) => {
75                let out_edge = triangulation
76                    .vertex(v)
77                    .out_edge()
78                    .expect("Could not find an out edge. This is a bug in spade.")
79                    .fix();
80                PositionInTriangulation::OutsideOfConvexHull(out_edge)
81            }
82        }
83    }
84}
85
86pub enum InsertionResult {
87    NewlyInserted(FixedVertexHandle),
88    Updated(FixedVertexHandle),
89}
90
91pub struct RemovalResult<V> {
92    pub removed_vertex: V,
93    pub swapped_in_vertex: Option<FixedVertexHandle>,
94}
95
96pub trait TriangulationExt: Triangulation {
97    fn insert_with_hint_option(
98        &mut self,
99        t: Self::Vertex,
100        hint: Option<FixedVertexHandle>,
101    ) -> Result<FixedVertexHandle, InsertionError> {
102        math::validate_vertex(&t)?;
103        let position = t.position();
104        let result = self.insert_with_hint_option_impl(VertexToInsert::NewVertex(t), hint);
105
106        self.hint_generator_mut()
107            .notify_vertex_inserted(result, position);
108        Ok(result)
109    }
110
111    fn insert_with_hint_option_impl(
112        &mut self,
113        t: VertexToInsert<Self::Vertex>,
114        hint: Option<FixedVertexHandle>,
115    ) -> FixedVertexHandle {
116        use PositionInTriangulation::*;
117
118        let insertion_result = match self.num_vertices() {
119            0 => return dcel_operations::append_unconnected_vertex(self.s_mut(), t.into_vertex()),
120            1 => self.insert_second_vertex(t.into_vertex()),
121            _ => {
122                let pos = t.position(self.s());
123
124                if self.all_vertices_on_line() {
125                    let location = self.locate_when_all_vertices_on_line(pos);
126                    self.insert_when_all_vertices_on_line(location, t)
127                } else {
128                    let position_in_triangulation = self.locate_with_hint_option_core(pos, hint);
129                    match position_in_triangulation {
130                        OutsideOfConvexHull(edge) => {
131                            let resolved = t.resolve(self.s_mut());
132                            InsertionResult::NewlyInserted(
133                                self.insert_outside_of_convex_hull(edge, resolved),
134                            )
135                        }
136                        OnFace(face) => {
137                            let resolved = t.resolve(self.s_mut());
138                            InsertionResult::NewlyInserted(self.insert_into_face(face, resolved))
139                        }
140                        OnEdge(edge) => {
141                            let new_handle = t.resolve(self.s_mut());
142                            let split_parts = self.insert_on_edge(edge, new_handle);
143
144                            if self.is_defined_legal(edge.as_undirected()) {
145                                // If the edge is defined as legal the resulting edges must
146                                // be redefined as legal
147                                self.handle_legal_edge_split(split_parts);
148                            }
149                            self.legalize_vertex(new_handle);
150
151                            InsertionResult::NewlyInserted(new_handle)
152                        }
153                        OnVertex(vertex) => {
154                            self.s_mut().update_vertex(vertex, t.into_vertex());
155                            InsertionResult::Updated(vertex)
156                        }
157                        NoTriangulation => panic!("Error during vertex lookup. This is a bug."),
158                    }
159                }
160            }
161        };
162
163        match insertion_result {
164            InsertionResult::NewlyInserted(new_handle) => new_handle,
165            InsertionResult::Updated(update_handle) => update_handle,
166        }
167    }
168
169    fn locate_when_all_vertices_on_line(
170        &self,
171        position: Point2<<Self::Vertex as HasPosition>::Scalar>,
172    ) -> PositionWhenAllVerticesOnLine {
173        use PositionWhenAllVerticesOnLine::*;
174
175        let edge = self
176            .directed_edges()
177            .next()
178            .expect("Must not be called on empty triangulations");
179        let query = edge.side_query(position);
180        if query.is_on_left_side() {
181            return NotOnLine(edge.fix());
182        }
183        if query.is_on_right_side() {
184            return NotOnLine(edge.fix().rev());
185        }
186
187        let mut vertices: Vec<_> = self.vertices().filter(|v| v.out_edge().is_some()).collect();
188        vertices.sort_by(|left, right| left.position().partial_cmp(&right.position()).unwrap());
189
190        let index_to_insert =
191            match vertices.binary_search_by(|v| v.position().partial_cmp(&position).unwrap()) {
192                Ok(index) => return OnVertex(vertices[index].fix()),
193                Err(index) => index,
194            };
195
196        if index_to_insert == 0 {
197            return ExtendingLine(vertices.first().unwrap().fix());
198        }
199        if index_to_insert == vertices.len() {
200            return ExtendingLine(vertices.last().unwrap().fix());
201        }
202
203        let v1 = vertices[index_to_insert];
204        let v2 = vertices[index_to_insert - 1];
205
206        let edge = self
207            .get_edge_from_neighbors(v1.fix(), v2.fix())
208            .expect("Expected edge between sorted neighbors. This is a bug in spade.");
209        OnEdge(edge.fix())
210    }
211
212    fn insert_second_vertex(&mut self, vertex: Self::Vertex) -> InsertionResult {
213        assert_eq!(self.num_vertices(), 1);
214
215        let first_vertex = FixedVertexHandle::new(0);
216        if self.vertex(first_vertex).position() == vertex.position() {
217            self.s_mut().update_vertex(first_vertex, vertex);
218            return InsertionResult::Updated(first_vertex);
219        }
220
221        let second_vertex = dcel_operations::append_unconnected_vertex(self.s_mut(), vertex);
222        dcel_operations::setup_initial_two_vertices(self.s_mut(), first_vertex, second_vertex);
223
224        InsertionResult::NewlyInserted(second_vertex)
225    }
226
227    fn insert_when_all_vertices_on_line(
228        &mut self,
229        location: PositionWhenAllVerticesOnLine,
230        new_vertex: VertexToInsert<Self::Vertex>,
231    ) -> InsertionResult {
232        match location {
233            PositionWhenAllVerticesOnLine::OnEdge(edge) => {
234                let new_vertex = new_vertex.resolve(self.s_mut());
235                let is_constraint_edge = self.is_defined_legal(edge.as_undirected());
236                let new_edges = dcel_operations::split_edge_when_all_vertices_on_line(
237                    self.s_mut(),
238                    edge,
239                    new_vertex,
240                );
241
242                if is_constraint_edge {
243                    self.handle_legal_edge_split(new_edges);
244                }
245                InsertionResult::NewlyInserted(new_vertex)
246            }
247            PositionWhenAllVerticesOnLine::OnVertex(vertex) => InsertionResult::Updated(vertex),
248            PositionWhenAllVerticesOnLine::NotOnLine(edge) => {
249                let new_vertex = new_vertex.resolve(self.s_mut());
250                let result = self.insert_outside_of_convex_hull(edge, new_vertex);
251                InsertionResult::NewlyInserted(result)
252            }
253            PositionWhenAllVerticesOnLine::ExtendingLine(end_vertex) => {
254                let new_vertex = new_vertex.resolve(self.s_mut());
255                let result = dcel_operations::extend_line(self.s_mut(), end_vertex, new_vertex);
256                InsertionResult::NewlyInserted(result)
257            }
258        }
259    }
260
261    fn locate_with_hint_option_core(
262        &self,
263        point: Point2<<Self::Vertex as HasPosition>::Scalar>,
264        hint: Option<FixedVertexHandle>,
265    ) -> PositionInTriangulation {
266        let start = hint.unwrap_or_else(|| self.hint_generator().get_hint(point));
267        self.locate_with_hint_fixed_core(point, start)
268    }
269
270    fn insert_outside_of_convex_hull(
271        &mut self,
272        convex_hull_edge: FixedDirectedEdgeHandle,
273        new_vertex: FixedVertexHandle,
274    ) -> FixedVertexHandle {
275        let position = self.vertex(new_vertex).position();
276
277        assert!(self
278            .directed_edge(convex_hull_edge)
279            .side_query(position)
280            .is_on_left_side());
281
282        let result = dcel_operations::create_new_face_adjacent_to_edge(
283            self.s_mut(),
284            convex_hull_edge,
285            new_vertex,
286        );
287
288        let ccw_walk_start = self.directed_edge(convex_hull_edge).prev().rev().fix();
289        let cw_walk_start = self.directed_edge(convex_hull_edge).next().rev().fix();
290
291        self.legalize_edge(convex_hull_edge, false);
292
293        let mut current_edge = ccw_walk_start;
294        loop {
295            let handle = self.directed_edge(current_edge);
296            let prev = handle.prev();
297            current_edge = prev.fix();
298            if prev.side_query(position).is_on_left_side() {
299                let new_edge = dcel_operations::create_single_face_between_edge_and_next(
300                    self.s_mut(),
301                    current_edge,
302                );
303                self.legalize_edge(current_edge, false);
304                current_edge = new_edge;
305            } else {
306                break;
307            }
308        }
309
310        let mut current_edge = cw_walk_start;
311        loop {
312            let handle = self.directed_edge(current_edge);
313            let next = handle.next();
314            let next_fix = next.fix();
315            if next.side_query(position).is_on_left_side() {
316                let new_edge = dcel_operations::create_single_face_between_edge_and_next(
317                    self.s_mut(),
318                    current_edge,
319                );
320                self.legalize_edge(next_fix, false);
321                current_edge = new_edge;
322            } else {
323                break;
324            }
325        }
326
327        result
328    }
329
330    fn insert_into_face(
331        &mut self,
332        face: FixedFaceHandle<InnerTag>,
333        t: FixedVertexHandle,
334    ) -> FixedVertexHandle {
335        let new_handle = dcel_operations::insert_into_triangle(self.s_mut(), t, face);
336        self.legalize_vertex(new_handle);
337        new_handle
338    }
339
340    fn insert_on_edge(
341        &mut self,
342        edge: FixedDirectedEdgeHandle,
343        new_vertex: FixedVertexHandle,
344    ) -> [FixedDirectedEdgeHandle; 2] {
345        let edge_handle = self.directed_edge(edge);
346        if edge_handle.is_outer_edge() {
347            let [e0, e1] = dcel_operations::split_half_edge(self.s_mut(), edge.rev(), new_vertex);
348            [e1.rev(), e0.rev()]
349        } else if edge_handle.rev().is_outer_edge() {
350            dcel_operations::split_half_edge(self.s_mut(), edge, new_vertex)
351        } else {
352            dcel_operations::split_edge(self.s_mut(), edge, new_vertex)
353        }
354    }
355
356    fn legalize_vertex(&mut self, new_handle: FixedVertexHandle) {
357        let edges: SmallVec<[_; 4]> = self
358            .vertex(new_handle)
359            .out_edges()
360            .filter(|e| !e.is_outer_edge())
361            .map(|edge| edge.next().fix())
362            .collect();
363
364        for edge_to_legalize in edges {
365            self.legalize_edge(edge_to_legalize, false);
366        }
367    }
368
369    /// The Delaunay property refers to the property that no point lies inside
370    /// the circumcircle of the triangulation's triangles. Adding a
371    /// new point into the triangulations may violate this property, this method
372    /// "repairs" it by strategically flipping edges until the property
373    /// holds again. Every flip produces more "illegal" edges that may have to
374    /// be flipped. However, since any edge is flipped at most once, this
375    /// algorithm is known to terminate.
376    ///
377    /// The given position is the position of a new point may have
378    /// invalidated the Delaunay property, the point must be on the left side
379    /// of the given edge.
380    ///
381    /// "Flipping an edge" refers to switching to the other diagonal in a
382    /// four sided polygon.
383    ///
384    /// Returns `true` if at least one edge was flipped. This will always include the initial edge.
385    ///
386    /// Some additional checks are performed if `fully_legalize` is `true`. This is more costly
387    /// and often not required.
388    fn legalize_edge(&mut self, edge: FixedDirectedEdgeHandle, fully_legalize: bool) -> bool {
389        let mut edges: SmallVec<[FixedDirectedEdgeHandle; 8]> = Default::default();
390        edges.push(edge);
391
392        let mut result = false;
393
394        while let Some(e) = edges.pop() {
395            if !self.is_defined_legal(e.as_undirected()) {
396                let edge = self.directed_edge(e);
397
398                //         v2------ v0
399                //          |     / |
400                //          |    /  |
401                //          |   /<-edge that might be flipped ("edge")
402                //          |  /    |
403                //          | V     |
404                //         v1-------v3
405                //
406                //
407                // If the edge is flipped, the new quad will look like this:
408                //
409                //      New illegal edge
410                //              |
411                //              V
412                //         v2-------v0
413                //          | ^     |
414                // New      |  \    |
415                // illegal->|   \   |
416                // edge     |    \  |
417                //          |     \ |
418                //         v1-------v3
419                let v2 = edge.rev().opposite_position();
420                let v3 = edge.opposite_position();
421
422                if let (Some(v2), Some(v3)) = (v2, v3) {
423                    let v0 = edge.from().position();
424                    let v1 = edge.to().position();
425                    debug_assert!(math::is_ordered_ccw(v2, v1, v0));
426                    let should_flip = math::contained_in_circumference(v2, v1, v0, v3);
427                    result |= should_flip;
428
429                    if should_flip {
430                        edges.push(edge.rev().next().fix());
431                        edges.push(edge.rev().prev().fix());
432
433                        if fully_legalize {
434                            // Check all adjacent edges of the input edge. This is more costly but
435                            // can fix Delaunay violations that lie on the left side of this edge.
436                            edges.push(edge.next().fix());
437                            edges.push(edge.prev().fix());
438                        }
439
440                        dcel_operations::flip_cw(self.s_mut(), e.as_undirected());
441                    }
442                }
443            }
444        }
445        result
446    }
447
448    fn validate_vertex_handle(&self, handle: FixedVertexHandle) -> FixedVertexHandle {
449        if handle.index() < self.num_vertices() {
450            handle
451        } else {
452            FixedVertexHandle::new(0)
453        }
454    }
455
456    fn walk_to_nearest_neighbor(
457        &self,
458        start: FixedVertexHandle,
459        position: Point2<<Self::Vertex as HasPosition>::Scalar>,
460    ) -> VertexHandle<'_, Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face> {
461        let start_position = self.vertex(start).position();
462
463        if start_position == position {
464            return self.vertex(start);
465        }
466
467        let mut current_minimal_distance = position.distance_2(start_position);
468        let mut current_minimum_vertex = self.vertex(start);
469
470        while let Some((next_minimum_index, next_minimal_distance)) = current_minimum_vertex
471            .out_edges()
472            .filter_map(|out_edge| {
473                let next_candidate = out_edge.to();
474                let new_distance = next_candidate.position().distance_2(position);
475                if new_distance < current_minimal_distance {
476                    Some((next_candidate, new_distance))
477                } else {
478                    None
479                }
480            })
481            .next()
482        {
483            current_minimal_distance = next_minimal_distance;
484            current_minimum_vertex = next_minimum_index;
485        }
486
487        current_minimum_vertex
488    }
489
490    /// "Walks" through the triangulation until it finds the target point.
491    fn locate_with_hint_fixed_core(
492        &self,
493        target_position: Point2<<Self::Vertex as HasPosition>::Scalar>,
494        start: FixedVertexHandle,
495    ) -> PositionInTriangulation {
496        let pos = target_position.to_f64();
497        assert!(!f64::is_nan(pos.x));
498        assert!(!f64::is_nan(pos.y));
499
500        if self.num_vertices() < 2 {
501            return match self.vertices().next() {
502                Some(single_vertex) if single_vertex.position() == target_position => {
503                    PositionInTriangulation::OnVertex(single_vertex.fix())
504                }
505                _ => PositionInTriangulation::NoTriangulation,
506            };
507        }
508
509        if self.all_vertices_on_line() {
510            return self
511                .locate_when_all_vertices_on_line(target_position)
512                .to_regular_position_in_triangulation(self);
513        }
514
515        let start = self.validate_vertex_handle(start);
516        let closest_vertex = self.walk_to_nearest_neighbor(start, target_position);
517
518        // From here on spade attempts to find the location by inspecting the neighborhood of
519        // the closest vertex and iteratively getting closer to the target position.
520        // For regular Delaunay triangulations, the vertex should always be on an adjacent edge or
521        // face (or on the closest vertex itself).
522        // However, that doesn't hold for CDTs - these can have arbitrarily many faces between the
523        // closest vertex and the target position. Thus, a "walk" to cross the remaining distance
524        // is necessary. In addition, `walk_to_nearest_neighbor` does not use precise arithmetics -
525        // it may fail to always report the real closest vertex. The algorithm below only uses
526        // precise queries and should always return the correct answer.
527        //
528        // It works like this:
529        // 1. Choose an arbitrary vertex V (closest_vertex in this case, but can be anything).
530        // 2. Rotate around this vertex until we find the angle segment in which the target
531        //    position lies:     V
532        //                      / \
533        //                   e0/   \e1
534        //                    /__e2_\
535        //                   .\     / .
536        //                  .  \   /   .
537        //                 .     O  <---.--- "opposite" vertex
538        //                .              .
539        //                   T      target position T lies between the segment spanned by the
540        //                   ^----- outgoing edges e0 and e1
541        //
542        // 4. If the segment spans a triangle that contains the target position, we're done - the
543        //    target lies in the face lined by e0, e1 and e2
544        // 3. Otherwise, set V to the _opposite vertex_ (O in the diagram above), Then, go to step 2
545
546        // e0 implicitly defines the rotation vertex V: It is always e0.from(). The segment is
547        // adjacent to this edge iff `rotate_ccw == true` (see below)
548        let mut e0 = closest_vertex
549            .out_edge()
550            .expect("No out edge found. This is a bug.");
551
552        let mut e0_query = e0.side_query(target_position);
553
554        // Choose the rotation direction (CW or CCW) to minimize the angle distance to the target
555        // position.
556        let mut rotate_ccw = e0_query.is_on_left_side_or_on_line();
557
558        let mut loop_counter = self.s().num_directed_edges();
559        loop {
560            // Prevent accidental infinite loops (easier to debug). Should never happen
561            if loop_counter == 0 {
562                panic!("Failed to locate position. This is a bug in spade.")
563            }
564            loop_counter -= 1;
565
566            let [from, to] = e0.vertices();
567            if from.position() == target_position {
568                self.hint_generator().notify_vertex_lookup(from.fix());
569                return PositionInTriangulation::OnVertex(from.fix());
570            }
571            if to.position() == target_position {
572                self.hint_generator().notify_vertex_lookup(to.fix());
573                return PositionInTriangulation::OnVertex(to.fix());
574            }
575
576            if e0_query.is_on_line() {
577                if e0.is_outer_edge() {
578                    e0 = e0.rev();
579                }
580
581                // Special case: the current_edge is collinear with the target position.
582                // This means no segment exists that could be used to advance the rotation vertex.
583                // Instead, the algorithm changes to rotate around current_edge.prev().from()
584                // Since the triangulation is not degenerate, this vertex cannot have an out edge
585                // that is collinear.
586                e0 = e0.prev();
587                e0_query = e0.side_query(target_position);
588                rotate_ccw = e0_query.is_on_left_side_or_on_line();
589                continue;
590            }
591
592            // Delineates the other side of the segment
593            let e1 = if rotate_ccw {
594                e0
595            } else {
596                // Reverse the edge to ensure that the current segment is adjacent.
597                e0.rev()
598            };
599
600            let Some(face) = e1.face().as_inner() else {
601                self.hint_generator().notify_vertex_lookup(e1.from().fix());
602                return PositionInTriangulation::OutsideOfConvexHull(e1.fix());
603            };
604
605            // rotate around `V = current_edge.from()` (see diagram above)
606            // The current segment is spanned by `current_edge` and `rotated`
607            let rotated = if rotate_ccw { e0.ccw() } else { e0.cw() };
608
609            let rotated_query = rotated.side_query(target_position);
610            if rotated_query.is_on_line() || rotated_query.is_on_left_side() == rotate_ccw {
611                // Segment does not contain the target vertex. Continue rotating.
612                e0 = rotated;
613                e0_query = rotated_query;
614                continue;
615            }
616
617            // Stores the last edge that is adjacent to the segment triangle
618            let e2 = if rotate_ccw { e1.next() } else { e1.prev() };
619
620            let e2_query = e2.side_query(target_position);
621            if e2_query.is_on_line() {
622                self.hint_generator().notify_vertex_lookup(e2.to().fix());
623                return PositionInTriangulation::OnEdge(e2.fix());
624            }
625            if e2_query.is_on_left_side() {
626                self.hint_generator().notify_vertex_lookup(e2.to().fix());
627                return PositionInTriangulation::OnFace(face.fix());
628            }
629
630            // Check if an opposite vertex ("O" in the diagram above) exists. Otherwise, just
631            // rotate around any vertex from e2 instead.
632            e0 = e2.rev();
633            e0_query = e2_query.reversed();
634            if !e0.is_outer_edge() {
635                // Opposite vertex exists - use any of its out edges and use that for the next
636                // rotation.
637                e0 = e0.prev();
638                e0_query = e0.side_query(target_position);
639            }
640
641            rotate_ccw = e0_query.is_on_left_side_or_on_line();
642        }
643    }
644
645    fn remove_and_notify(&mut self, vertex_to_remove: FixedVertexHandle) -> Self::Vertex {
646        let position = self.vertex(vertex_to_remove).position();
647        let removal_result = self.remove_core(vertex_to_remove);
648
649        let swapped_in_point = removal_result
650            .swapped_in_vertex
651            .map(|_| self.vertex(vertex_to_remove).position());
652
653        self.hint_generator_mut().notify_vertex_removed(
654            swapped_in_point,
655            vertex_to_remove,
656            position,
657        );
658
659        removal_result.removed_vertex
660    }
661
662    fn remove_core(&mut self, vertex_to_remove: FixedVertexHandle) -> RemovalResult<Self::Vertex> {
663        if self.num_all_faces() <= 1 {
664            return dcel_operations::remove_when_degenerate(self.s_mut(), vertex_to_remove);
665        }
666        let vertex = self.vertex(vertex_to_remove);
667
668        let mut border_loop = Vec::with_capacity(10);
669        let mut convex_hull_edge = None;
670        for edge in vertex.out_edges().rev() {
671            if edge.is_outer_edge() {
672                convex_hull_edge = Some(edge.fix());
673                break;
674            }
675            let border_edge = edge.next();
676            border_loop.push(border_edge.fix());
677        }
678
679        if let Some(convex_hull_edge) = convex_hull_edge {
680            let mut isolation_result = self.isolate_convex_hull_vertex(convex_hull_edge);
681
682            dcel_operations::cleanup_isolated_vertex(self.s_mut(), &mut isolation_result);
683            dcel_operations::swap_remove_vertex(self.s_mut(), vertex_to_remove)
684        } else {
685            let mut isolation_result = dcel_operations::isolate_vertex_and_fill_hole(
686                self.s_mut(),
687                border_loop,
688                vertex_to_remove,
689            );
690            // Not exactly elegant. IsolateVertexResult should maybe be split into two parts
691            let mut new_edges = core::mem::take(&mut isolation_result.new_edges);
692            self.legalize_edges_after_removal(&mut new_edges, |edge| {
693                !isolation_result.is_new_edge(edge)
694            });
695            dcel_operations::cleanup_isolated_vertex(self.s_mut(), &mut isolation_result);
696            dcel_operations::swap_remove_vertex(self.s_mut(), vertex_to_remove)
697        }
698    }
699
700    fn isolate_convex_hull_vertex(
701        &mut self,
702        convex_hull_out_edge: FixedDirectedEdgeHandle,
703    ) -> IsolateVertexResult {
704        let mut edges_to_validate = Vec::with_capacity(10);
705        let mut convex_edges: Vec<FixedDirectedEdgeHandle> = Vec::with_capacity(10);
706
707        let loop_end = self.directed_edge(convex_hull_out_edge);
708        let loop_start = loop_end.ccw().fix();
709        let mut current = loop_start;
710        let loop_end_next = loop_end.next().fix();
711        let loop_end = loop_end.fix();
712
713        loop {
714            let current_handle = self.directed_edge(current);
715            current = current_handle.ccw().fix();
716            let edge = current_handle.next().fix();
717            convex_edges.push(edge);
718
719            while let &[.., edge1, edge2] = &*convex_edges {
720                let edge1 = self.directed_edge(edge1);
721                let edge2 = self.directed_edge(edge2);
722
723                let target_position = edge2.to().position();
724                // Check if the new edge would violate the convex hull property by turning left
725                // The convex hull must only contain curves turning right
726                if edge1.side_query(target_position).is_on_left_side() {
727                    // Violation detected. It is resolved by flipping the edge that connects
728                    // the most recently added edge (edge2) with the point that was removed
729                    let edge_to_flip = edge2.prev().fix().rev();
730                    dcel_operations::flip_cw(self.s_mut(), edge_to_flip.as_undirected());
731                    convex_edges.pop();
732                    convex_edges.pop();
733                    convex_edges.push(edge_to_flip);
734                    edges_to_validate.push(edge_to_flip.as_undirected());
735                } else {
736                    break;
737                }
738            }
739
740            if current == loop_end {
741                break;
742            }
743        }
744
745        convex_edges.push(loop_end_next);
746        let result = dcel_operations::disconnect_edge_strip(self.s_mut(), convex_edges);
747        self.legalize_edges_after_removal(&mut edges_to_validate, |_| false);
748        result
749    }
750
751    /// After a vertex removal, the created hole is stitched together by connecting
752    /// all vertices to a single vertex at the border (triangle fan).
753    /// Since these new edges can violate the Delaunay property, it must be restored.
754    ///
755    /// The algorithm works like this:
756    ///  - Add all new edges to an "invalid" list
757    ///  - While the invalid list is not empty: Determine if flipping the top edge is
758    ///    required to restore the Delaunay property locally.
759    ///  - If the edge was flipped: Determine the flip polygon. A flip refers
760    ///    to switching the diagonal in a four sided polygon which defines the
761    ///    flip polygon.
762    ///  - Add all edges of the flip polygon to the invalid list if they were
763    ///    newly created. Otherwise, the edge is part of the border loop surrounding
764    ///    the hole created after the vertex removal. These are known to be valid and
765    ///    need not be checked
766    ///
767    /// For more details, refer to
768    /// Olivier Devillers. Vertex Removal in Two Dimensional Delaunay Triangulation:
769    /// Speed-up by Low Degrees Optimization.
770    /// <https://doi.org/10.1016/j.comgeo.2010.10.001>
771    ///
772    /// Note that the described low degrees optimization is not yet part of this library.
773    fn legalize_edges_after_removal<F>(
774        &mut self,
775        edges_to_validate: &mut Vec<FixedUndirectedEdgeHandle>,
776        edge_must_not_be_flipped_predicate: F,
777    ) where
778        F: Fn(FixedUndirectedEdgeHandle) -> bool,
779    {
780        while let Some(next_edge) = edges_to_validate.pop() {
781            // left----to
782            //  |     ^ |
783            //  |    /  |
784            //  |   /<-edge that might be flipped ("next_edge")
785            //  |  /    |
786            //  | /     |
787            // from----right
788            //
789            // left, from, right and to define the flip polygon.
790            if self.is_defined_legal(next_edge) || edge_must_not_be_flipped_predicate(next_edge) {
791                continue;
792            }
793
794            let edge = self.directed_edge(next_edge.as_directed());
795            let e2 = edge.prev();
796            let e4 = edge.rev().prev();
797
798            let from = edge.from().position();
799            let to = edge.to().position();
800            let left = edge.opposite_position();
801            let right = edge.rev().opposite_position();
802
803            let should_flip = match (left, right) {
804                (Some(left), Some(right)) => {
805                    math::contained_in_circumference(from, to, left, right)
806                }
807                // Handle special cases when evaluating edges next to the convex hull
808                (None, Some(right)) => math::is_ordered_ccw(right, from, to),
809                (Some(left), None) => math::is_ordered_ccw(left, to, from),
810                (None, None) => {
811                    panic!("Unexpected geometry. This is a bug in spade.")
812                }
813            };
814
815            if should_flip {
816                let e1 = edge.next();
817                let e3 = edge.rev().next();
818
819                let mut push_if_not_contained = |handle| {
820                    if !edges_to_validate.contains(&handle) {
821                        edges_to_validate.push(handle);
822                    }
823                };
824
825                push_if_not_contained(e1.fix().as_undirected());
826                push_if_not_contained(e2.fix().as_undirected());
827                push_if_not_contained(e3.fix().as_undirected());
828                push_if_not_contained(e4.fix().as_undirected());
829
830                let fixed = edge.fix();
831                dcel_operations::flip_cw(self.s_mut(), fixed.as_undirected());
832            }
833        }
834    }
835
836    #[cfg(any(test, fuzzing))]
837    fn basic_sanity_check(&self, check_convexity: bool) {
838        self.s().sanity_check();
839        let all_vertices_on_line = self.s().num_faces() <= 1;
840
841        for face in self.s().inner_faces() {
842            let triangle = face.vertices();
843            // Check that all vertices are stored in ccw orientation
844            assert!(math::side_query(
845                triangle[0].position(),
846                triangle[1].position(),
847                triangle[2].position()
848            )
849            .is_on_left_side(),);
850        }
851
852        for edge in self.s().directed_edges() {
853            if all_vertices_on_line {
854                assert_eq!(edge.face().fix(), dcel_operations::OUTER_FACE_HANDLE)
855            } else {
856                assert_ne!(edge.face(), edge.rev().face());
857            }
858            assert_ne!(edge.from(), edge.to());
859        }
860
861        if all_vertices_on_line {
862            if self.s().num_vertices() > 1 {
863                assert_eq!(self.s().num_undirected_edges(), self.s().num_vertices() - 1);
864            } else {
865                assert_eq!(self.s().num_undirected_edges(), 0);
866            }
867            assert_eq!(self.s().num_faces(), 1);
868        } else {
869            let num_inner_edges = self
870                .s()
871                .directed_edges()
872                .filter(|e| !e.face().is_outer())
873                .count();
874
875            let num_inner_faces = self.s().num_faces() - 1;
876            assert_eq!(num_inner_faces * 3, num_inner_edges);
877
878            if check_convexity {
879                for edge in self.convex_hull() {
880                    for vert in self.vertices() {
881                        assert!(edge
882                            .side_query(vert.position())
883                            .is_on_right_side_or_on_line(),);
884                    }
885                }
886            }
887        }
888    }
889
890    #[cfg(any(test, fuzzing))]
891    fn sanity_check(&self) {
892        self.basic_sanity_check(true);
893
894        for edge in self.undirected_edges() {
895            let edge = edge.as_directed();
896            let rev = edge.rev();
897
898            if let (Some(edge_opposite), Some(rev_opposite)) =
899                (edge.opposite_position(), rev.opposite_position())
900            {
901                let from = edge.from().position();
902                let to = edge.to().position();
903                assert!(!math::contained_in_circumference(
904                    from,
905                    to,
906                    edge_opposite,
907                    rev_opposite
908                ))
909            }
910        }
911    }
912}
913
914#[cfg(test)]
915mod test {
916    use crate::test_utilities::SEED;
917    use crate::test_utilities::*;
918    use crate::PositionInTriangulation;
919    use crate::TriangulationExt;
920    use crate::{
921        handles::FixedVertexHandle, DelaunayTriangulation, InsertionError, Point2, Triangulation,
922    };
923    use rand::distr::Distribution;
924    use rand::distr::Uniform;
925    use rand::{seq::SliceRandom, Rng, SeedableRng};
926
927    use alloc::{vec, vec::Vec};
928
929    #[test]
930    fn test_empty() {
931        let d = DelaunayTriangulation::<Point2<f32>>::default();
932        assert_eq!(d.num_vertices(), 0);
933        assert_eq!(d.num_all_faces(), 1);
934        assert_eq!(d.num_undirected_edges(), 0);
935    }
936
937    #[test]
938    fn test_insert_first() -> Result<(), InsertionError> {
939        let mut d = DelaunayTriangulation::<Point2<f32>>::default();
940        d.insert(Point2::default())?;
941        assert_eq!(d.num_vertices(), 1);
942        assert_eq!(d.num_all_faces(), 1);
943        assert_eq!(d.num_undirected_edges(), 0);
944        Ok(())
945    }
946
947    #[test]
948    fn test_insert_second() -> Result<(), InsertionError> {
949        let mut d = DelaunayTriangulation::<_>::default();
950        d.insert(Point2::default())?;
951        d.insert(Point2::new(0.123, 1.234))?;
952        assert_eq!(d.num_vertices(), 2);
953        assert_eq!(d.num_all_faces(), 1);
954        assert_eq!(d.num_undirected_edges(), 1);
955        d.sanity_check();
956        Ok(())
957    }
958
959    #[test]
960    fn test_insert_third_point() -> Result<(), InsertionError> {
961        let mut d = DelaunayTriangulation::<_>::default();
962        d.insert(Point2::new(1f64, 0f64))?;
963        d.insert(Point2::new(0f64, 1f64))?;
964        d.insert(Point2::new(1f64, 1f64))?;
965
966        assert_eq!(d.num_vertices(), 3);
967        assert_eq!(d.num_all_faces(), 2);
968        d.sanity_check();
969        Ok(())
970    }
971
972    #[test]
973    fn test_insert_five_points() -> Result<(), InsertionError> {
974        let mut d = DelaunayTriangulation::<_>::default();
975        d.insert(Point2::new(1f64, 0f64))?;
976        d.insert(Point2::new(0f64, 1f64))?;
977
978        let v3 = Point2::new(0.433_833_144_214_401f64, 0.900_993_231_373_602_9f64);
979        let v4 = Point2::new(2.0, 2.0);
980        let v5 = Point2::new(0.5, 0.25);
981        d.insert(v3)?;
982        d.sanity_check();
983        d.insert(v4)?;
984        d.s().sanity_check();
985        d.insert(v5)?;
986        d.sanity_check();
987        Ok(())
988    }
989
990    #[test]
991    fn test_small_triangulation_iterators() -> Result<(), InsertionError> {
992        let mut d = DelaunayTriangulation::<_>::default();
993        assert_eq!(d.all_faces().count(), 1);
994        assert_eq!(d.inner_faces().count(), 0);
995
996        d.insert(Point2::new(1f64, 1f64))?;
997        assert_eq!(d.all_faces().count(), 1);
998        assert_eq!(d.inner_faces().count(), 0);
999
1000        d.insert(Point2::new(-1f64, 1f64))?;
1001        assert_eq!(d.all_faces().count(), 1);
1002        assert_eq!(d.inner_faces().count(), 0);
1003        Ok(())
1004    }
1005
1006    #[test]
1007    #[should_panic]
1008    fn test_locate_nan_empty() {
1009        let d = DelaunayTriangulation::<Point2<f64>>::default();
1010        d.locate(Point2::new(0.0, f64::NAN));
1011    }
1012
1013    #[test]
1014    #[should_panic]
1015    fn test_locate_nan() {
1016        let points = random_points_with_seed(20, SEED);
1017        let d = DelaunayTriangulation::<Point2<f64>>::bulk_load(points);
1018        if let Ok(d) = d {
1019            d.locate(Point2::new(0.0, f64::NAN));
1020        }
1021    }
1022
1023    #[test]
1024    fn test_iterate_faces() -> Result<(), InsertionError> {
1025        const SIZE: usize = 1000;
1026        let points = random_points_with_seed(SIZE, SEED);
1027        let mut d = DelaunayTriangulation::<Point2<f64>>::bulk_load(points)?;
1028        d.sanity_check();
1029
1030        assert_eq!(d.all_faces().count(), d.num_all_faces());
1031        assert_eq!(d.inner_faces().count(), d.num_inner_faces());
1032
1033        for _ in 0..SIZE / 2 {
1034            d.remove(FixedVertexHandle::new(5));
1035        }
1036
1037        assert_eq!(d.all_faces().count(), d.num_all_faces());
1038        assert_eq!(d.inner_faces().count(), d.num_inner_faces());
1039
1040        d.sanity_check();
1041        Ok(())
1042    }
1043
1044    #[test]
1045    fn test_insert_many_points() -> Result<(), InsertionError> {
1046        const SIZE: usize = 10000;
1047        let points = random_points_with_seed(SIZE, SEED);
1048
1049        let mut d = DelaunayTriangulation::<_>::new();
1050        for point in points {
1051            d.insert(point)?;
1052        }
1053
1054        d.sanity_check();
1055        Ok(())
1056    }
1057
1058    #[test]
1059    fn test_insert_outside_convex_hull() -> anyhow::Result<()> {
1060        const NUM: usize = 100;
1061        let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1062        let range = Uniform::new(0., 2.0 * core::f64::consts::PI)?;
1063
1064        let mut d = DelaunayTriangulation::<_>::default();
1065
1066        // Insert points on a circle. Every new point lies outside the convex hull.
1067        for _ in 0..NUM {
1068            let ang = range.sample(&mut rng);
1069            let vec = Point2::new(ang.sin(), ang.cos());
1070            d.insert(vec)?;
1071        }
1072        assert_eq!(d.num_vertices(), NUM);
1073        d.sanity_check();
1074        Ok(())
1075    }
1076
1077    #[test]
1078    fn test_insert_same_point_small() -> Result<(), InsertionError> {
1079        let points = vec![
1080            Point2::new(0.2, 0.1),
1081            Point2::new(1.3, 2.2),
1082            Point2::new(0.0, 0.0),
1083        ];
1084        let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1085
1086        for p in &points {
1087            d.insert(*p)?;
1088            d.sanity_check();
1089        }
1090        assert_eq!(d.num_vertices(), points.len());
1091        d.sanity_check();
1092        Ok(())
1093    }
1094
1095    #[test]
1096    fn test_insert_same_point() -> Result<(), InsertionError> {
1097        const SIZE: usize = 300;
1098        let points = random_points_with_seed(SIZE, SEED);
1099        let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1100        for p in points {
1101            d.insert(p)?;
1102        }
1103        assert_eq!(d.num_vertices(), SIZE);
1104        d.sanity_check();
1105        Ok(())
1106    }
1107
1108    #[test]
1109    fn test_insert_point_on_ch_edge() -> Result<(), InsertionError> {
1110        let points = vec![
1111            Point2::new(0., 0f64),
1112            Point2::new(1., 0.),
1113            Point2::new(0., 1.),
1114            Point2::new(0., 0.4),
1115        ];
1116        let d = DelaunayTriangulation::<_>::bulk_load(points)?;
1117        d.sanity_check();
1118        Ok(())
1119    }
1120
1121    #[test]
1122    fn test_insert_on_edges() -> Result<(), InsertionError> {
1123        let points = vec![Point2::new(0., 0f64), Point2::new(1., 0.)];
1124        let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1125
1126        d.insert(Point2::new(1., 1.))?;
1127        d.sanity_check();
1128        d.insert(Point2::new(0.5, 0.5))?;
1129        d.sanity_check();
1130        d.insert(Point2::new(0., 0.4))?;
1131        d.sanity_check();
1132        d.insert(Point2::new(1., 0.5))?;
1133        d.sanity_check();
1134        d.insert(Point2::new(0.5, 1.))?;
1135        d.sanity_check();
1136        d.insert(Point2::new(0.7, 0.))?;
1137        d.sanity_check();
1138        Ok(())
1139    }
1140
1141    #[test]
1142    fn test_degenerate_triangulation() -> Result<(), InsertionError> {
1143        let mut d = DelaunayTriangulation::<_>::default();
1144        for i in -50..50 {
1145            d.insert(Point2::new(f64::from(i), 0.))?;
1146        }
1147
1148        d.sanity_check();
1149        Ok(())
1150    }
1151
1152    #[test]
1153    fn test_insert_points_on_line() -> Result<(), InsertionError> {
1154        let mut d = DelaunayTriangulation::<_>::default();
1155        d.insert(Point2::new(0.0, 1.0))?;
1156        for i in -50..50 {
1157            d.insert(Point2::new(f64::from(i), 0.))?;
1158        }
1159        d.sanity_check();
1160        Ok(())
1161    }
1162
1163    #[test]
1164    fn test_insert_points_on_line_2() -> Result<(), InsertionError> {
1165        let mut d = DelaunayTriangulation::<_>::default();
1166        d.insert(Point2::new(0.0, 1.0))?;
1167
1168        for i in -50..50 {
1169            d.insert(Point2::new(f64::from(i), 0.))?;
1170            d.sanity_check();
1171        }
1172
1173        for i in -0..10 {
1174            d.insert(Point2::new(f64::from(i), 0.5 * f64::from(i)))?;
1175            d.sanity_check();
1176        }
1177        d.sanity_check();
1178        Ok(())
1179    }
1180
1181    #[test]
1182    fn test_insert_points_on_grid2() -> Result<(), InsertionError> {
1183        let mut d = DelaunayTriangulation::<_>::default();
1184
1185        for y in 0..20 {
1186            for x in 0..7 {
1187                d.insert(Point2::new(f64::from(x), f64::from(y)))?;
1188                d.sanity_check();
1189            }
1190        }
1191        d.sanity_check();
1192        Ok(())
1193    }
1194
1195    #[test]
1196    fn test_insert_points_with_increasing_distance() -> Result<(), InsertionError> {
1197        let mut points = random_points_with_seed(1000, SEED);
1198        points.sort_by(|p1, p2| p1.length2().partial_cmp(&p2.length2()).unwrap());
1199        let mut d = DelaunayTriangulation::<_>::new();
1200        for point in points {
1201            d.insert(point)?;
1202        }
1203        d.sanity_check();
1204        Ok(())
1205    }
1206
1207    #[test]
1208    fn test_insert_points_on_grid_with_increasing_distance() -> Result<(), InsertionError> {
1209        // This test inserts points on a grid with increasing distance from (0., 0.)
1210        let mut points = Vec::new();
1211        const SIZE: i64 = 7;
1212        for x in -SIZE..SIZE {
1213            for y in -SIZE..SIZE {
1214                let point = Point2::new(x as f64, y as f64);
1215                points.push(point);
1216            }
1217        }
1218        points.sort_by(|p1, p2| p1.length2().partial_cmp(&p2.length2()).unwrap());
1219        let d = DelaunayTriangulation::<_>::bulk_load(points)?;
1220        d.sanity_check();
1221        Ok(())
1222    }
1223
1224    #[test]
1225    fn test_remove_in_triangle() -> Result<(), InsertionError> {
1226        let points = vec![
1227            Point2::new(-1.0, 0.0f64),
1228            Point2::new(1.0, 0.0f64),
1229            Point2::new(0.0, 1.0f64),
1230        ];
1231        let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1232        let to_remove = d.insert(Point2::new(0.0, 0.5))?;
1233        d.remove(to_remove);
1234        assert_eq!(d.num_vertices(), 3);
1235        // Reinsert the last point, just to see if a crash occurs
1236        d.insert(Point2::new(0.0, 0.5))?;
1237        d.sanity_check();
1238        Ok(())
1239    }
1240
1241    #[test]
1242    fn test_remove_complex_single_outer_vertex() -> Result<(), InsertionError> {
1243        let mut d = DelaunayTriangulation::<_>::default();
1244        d.insert(Point2::new(0.0, 0.0))?;
1245        d.insert(Point2::new(0.0, 1.0))?;
1246        d.insert(Point2::new(4.0, 0.0))?;
1247        d.insert(Point2::new(4.0, 1.0))?;
1248        d.insert(Point2::new(2.0, 0.5))?;
1249        d.insert(Point2::new(1.0, 0.5))?;
1250        d.insert(Point2::new(3.0, 0.5))?;
1251
1252        let v4_position = Point2::new(2.5, 2.0);
1253        let v4 = d.insert(v4_position)?;
1254
1255        let removed = d.remove(v4);
1256        d.sanity_check();
1257        assert_eq!(removed, v4_position);
1258        assert_eq!(d.num_vertices(), 7);
1259        Ok(())
1260    }
1261
1262    #[test]
1263    fn test_remove_single_outer() -> Result<(), InsertionError> {
1264        let mut d = DelaunayTriangulation::<_>::default();
1265        d.insert(Point2::new(0.0, 0.0))?;
1266        d.insert(Point2::new(0.0, 1.0))?;
1267        d.insert(Point2::new(1.0, 0.0))?;
1268        let v4_position = Point2::new(1.5, 1.5);
1269        let v4 = d.insert(v4_position)?;
1270
1271        let removed = d.remove(v4);
1272        d.sanity_check();
1273        assert_eq!(removed, v4_position);
1274        assert_eq!(d.num_vertices(), 3);
1275        Ok(())
1276    }
1277
1278    #[test]
1279    fn test_remove_in_quad() -> Result<(), InsertionError> {
1280        let points = vec![
1281            Point2::new(0.0, 0.0f64),
1282            Point2::new(1.0, 0.0f64),
1283            Point2::new(0.0, 1.0f64),
1284            Point2::new(1.0, 1.0f64),
1285        ];
1286
1287        let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1288
1289        let to_remove = d.insert(Point2::new(0.5, 0.6))?;
1290        d.remove(to_remove);
1291        assert_eq!(d.num_vertices(), 4);
1292        let to_remove = d.insert(Point2::new(0.5, 0.6))?;
1293        d.remove(to_remove);
1294        assert_eq!(d.num_vertices(), 4);
1295        d.insert(Point2::new(0.5, 0.6))?;
1296        d.sanity_check();
1297        Ok(())
1298    }
1299
1300    #[test]
1301    fn test_remove_star_shaped() -> Result<(), InsertionError> {
1302        let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1303        let mut points = vec![
1304            Point2::new(0.0, 0.0),
1305            Point2::new(1.0, 1.0),
1306            Point2::new(1.0, -1.0),
1307            Point2::new(-1.0, 1.0),
1308            Point2::new(-1.0, -1.0),
1309            Point2::new(0.0, 3.0),
1310            Point2::new(0.0, -3.0),
1311            Point2::new(3.0, 0.0),
1312            Point2::new(-3.0, 0.0),
1313        ];
1314
1315        points.shuffle(&mut rng);
1316        points.shuffle(&mut rng);
1317        points.shuffle(&mut rng);
1318        for _ in 0..20 {
1319            points.shuffle(&mut rng);
1320            let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1321            d.locate_and_remove(Point2::new(0.0, 0.0));
1322            d.sanity_check();
1323        }
1324        Ok(())
1325    }
1326
1327    #[test]
1328    fn test_remove_inner() -> Result<(), InsertionError> {
1329        use ::rand::SeedableRng;
1330        use PositionInTriangulation::OnVertex;
1331
1332        let mut points = random_points_with_seed(1000, SEED);
1333        let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1334
1335        // Insert an outer quad since we don't want to remove vertices from
1336        // the convex hull.
1337        d.insert(Point2::new(-2.0, -2.0))?;
1338        d.insert(Point2::new(-2.0, 2.0))?;
1339        d.insert(Point2::new(2.0, -2.0))?;
1340        d.insert(Point2::new(2.0, 2.0))?;
1341        // Now remove all inner points
1342        let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1343        points.shuffle(&mut rng);
1344        assert_eq!(d.num_vertices(), 1004);
1345        for point in points {
1346            match d.locate(point) {
1347                OnVertex(handle) => {
1348                    d.remove(handle);
1349                }
1350                _ => panic!("Point lookup failed: {:?}", point),
1351            }
1352        }
1353        assert_eq!(d.num_vertices(), 4);
1354        d.sanity_check();
1355        Ok(())
1356    }
1357
1358    #[test]
1359    fn test_remove_outer() -> Result<(), InsertionError> {
1360        use PositionInTriangulation::OnVertex;
1361
1362        let mut points = random_points_with_seed(1000, SEED);
1363        let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1364
1365        points.sort_by(|p1, p2| p1.length2().partial_cmp(&p2.length2()).unwrap());
1366        for (index, point) in points[3..].iter().rev().enumerate() {
1367            match d.locate(*point) {
1368                OnVertex(handle) => {
1369                    d.remove(handle);
1370                    if index % 50 == 0 {
1371                        // Check only every 50 iterations to reduce test runtime
1372                        d.sanity_check();
1373                    }
1374                }
1375                _ => panic!("Point lookup failed: {:?}", point),
1376            }
1377        }
1378        d.sanity_check();
1379        Ok(())
1380    }
1381
1382    #[test]
1383    fn test_removal_and_insertion() -> anyhow::Result<()> {
1384        let points = random_points_with_seed(1000, SEED);
1385        let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1386
1387        let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1388        for _ in 0..1000 {
1389            if rng.random() {
1390                // Insert new random point
1391                let x = rng.random();
1392                let y = rng.random();
1393                d.insert(Point2::new(x, y))?;
1394            } else {
1395                // Remove random point
1396                let range = Uniform::new(1, d.num_vertices())?;
1397                let handle = range.sample(&mut rng);
1398                d.remove(FixedVertexHandle::new(handle));
1399            }
1400        }
1401        d.sanity_check();
1402        Ok(())
1403    }
1404
1405    #[test]
1406    fn test_remove_until_empty() -> Result<(), InsertionError> {
1407        let mut d = DelaunayTriangulation::<_>::bulk_load(vec![
1408            Point2::new(0.0, 0.0),
1409            Point2::new(0.0, 1.0),
1410            Point2::new(1.0, 2.0),
1411        ])?;
1412
1413        while let Some(to_remove) = d.vertices().next() {
1414            let to_remove = to_remove.fix();
1415            d.remove(to_remove);
1416            d.sanity_check();
1417        }
1418
1419        assert_eq!(d.num_vertices(), 0);
1420
1421        d.insert(Point2::new(1.0, 0.0))?;
1422        d.insert(Point2::new(1.0, 1.0))?;
1423        d.insert(Point2::new(1.0, 2.0))?;
1424        d.insert(Point2::new(2.3, 1.4))?;
1425        d.sanity_check();
1426        Ok(())
1427    }
1428
1429    #[test]
1430    fn test_remove_until_degenerate() -> Result<(), InsertionError> {
1431        let points = vec![
1432            Point2::new(0., 0f64),
1433            Point2::new(1., 0.),
1434            Point2::new(0., 1.),
1435            Point2::new(0., 0.5),
1436            Point2::new(0., 0.25),
1437            Point2::new(0., 0.75),
1438        ];
1439        let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1440
1441        assert_eq!(d.num_all_faces(), 5);
1442        d.locate_and_remove(Point2::new(1., 0.));
1443        d.sanity_check();
1444        assert!(d.all_vertices_on_line());
1445
1446        while let Some(to_remove) = d.vertices().next() {
1447            let to_remove = to_remove.fix();
1448            d.remove(to_remove);
1449            d.sanity_check();
1450        }
1451
1452        d.sanity_check();
1453        d.insert(Point2::new(0.5, 0.5))?;
1454        d.insert(Point2::new(0.2, 0.5))?;
1455        d.insert(Point2::new(1.5, 0.0))?;
1456        d.sanity_check();
1457        Ok(())
1458    }
1459
1460    #[test]
1461    fn test_remove_few_points() -> Result<(), InsertionError> {
1462        let mut triangulation = DelaunayTriangulation::<_>::bulk_load(vec![
1463            Point2::new(0.0, 1.0),
1464            Point2::new(100.0, 1.0),
1465            Point2::new(0.0, 110.0),
1466            Point2::new(110.110, 110.0),
1467            Point2::new(50.0, 50.0),
1468            Point2::new(50.0, 80.0),
1469            Point2::new(75.0, 80.0),
1470        ])?;
1471
1472        triangulation.remove(FixedVertexHandle::new(5));
1473        triangulation.sanity_check();
1474        triangulation.remove(FixedVertexHandle::new(5));
1475        triangulation.sanity_check();
1476        Ok(())
1477    }
1478
1479    #[test]
1480    fn test_remove_on_line_small() -> Result<(), InsertionError> {
1481        let mut triangulation = DelaunayTriangulation::<_>::bulk_load(vec![
1482            Point2::new(0.0, 0.0),
1483            Point2::new(1.0, 0.0), // This point will be removed
1484            Point2::new(2.0, 0.0),
1485        ])?;
1486        triangulation.remove(FixedVertexHandle::new(2));
1487        triangulation.sanity_check();
1488        Ok(())
1489    }
1490
1491    #[test]
1492    fn test_remove_on_line_big() -> Result<(), InsertionError> {
1493        let mut triangulation = DelaunayTriangulation::<_>::default();
1494        for x in 2..100 {
1495            triangulation.insert(Point2::new(f64::from(x), 0.0))?;
1496        }
1497        let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1498        while triangulation.num_vertices() > 3 {
1499            if rng.random() {
1500                triangulation.remove(FixedVertexHandle::new(1));
1501            } else {
1502                triangulation.remove(FixedVertexHandle::new(2));
1503            }
1504
1505            triangulation.sanity_check();
1506        }
1507        Ok(())
1508    }
1509
1510    #[test]
1511    fn test_small_insert_on_line() -> Result<(), InsertionError> {
1512        let mut d = DelaunayTriangulation::<_>::default();
1513        d.insert(Point2::new(0.0, 0.0))?;
1514        d.insert(Point2::new(2.0, 0.0))?;
1515        d.insert(Point2::new(1.0, 0.0))?;
1516        d.sanity_check();
1517        Ok(())
1518    }
1519
1520    #[test]
1521    fn test_locate_when_empty() {
1522        let triangulation = DelaunayTriangulation::<Point2<_>>::default();
1523        assert_eq!(
1524            triangulation.locate(Point2::new(0.0, 0.0)),
1525            PositionInTriangulation::NoTriangulation
1526        )
1527    }
1528
1529    #[test]
1530    fn test_locate_with_single_vertex() -> Result<(), InsertionError> {
1531        let mut triangulation = DelaunayTriangulation::<_>::default();
1532        let point = Point2::new(0.0, 0.0);
1533        triangulation.insert(point)?;
1534        assert_eq!(
1535            triangulation.locate(point),
1536            PositionInTriangulation::OnVertex(FixedVertexHandle::new(0))
1537        );
1538        assert_eq!(
1539            triangulation.locate(Point2::new(1.0, 1.0)),
1540            PositionInTriangulation::NoTriangulation
1541        );
1542        Ok(())
1543    }
1544
1545    #[test]
1546    fn test_locate_with_two_vertices() -> Result<(), InsertionError> {
1547        let mut triangulation = DelaunayTriangulation::<_>::default();
1548        let p0 = Point2::new(0.0, 0.0);
1549        let v0 = triangulation.insert(p0)?;
1550        let p1 = Point2::new(1.0, 1.0);
1551        let v1 = triangulation.insert(p1)?;
1552
1553        assert_eq!(
1554            triangulation.locate(p0),
1555            PositionInTriangulation::OnVertex(v0)
1556        );
1557        assert_eq!(
1558            triangulation.locate(p1),
1559            PositionInTriangulation::OnVertex(v1)
1560        );
1561        let on_edge = triangulation.locate(Point2::new(0.5, 0.5));
1562        match on_edge {
1563            PositionInTriangulation::OnEdge(_) => {}
1564            _ => panic!("Expected OnEdge(_), was {:?}", on_edge),
1565        }
1566
1567        let hull0 = triangulation.locate(Point2::new(0.0, 1.0));
1568        let hull1 = triangulation.locate(Point2::new(0.0, -1.0));
1569        let hull2 = triangulation.locate(Point2::new(-1.0, -1.0));
1570        let hull3 = triangulation.locate(Point2::new(2.0, 2.0));
1571
1572        let [e0, e1, _, _] = [hull0, hull1, hull2, hull3].map(|hull| {
1573            if let PositionInTriangulation::OutsideOfConvexHull(edge) = hull {
1574                edge
1575            } else {
1576                panic!("Expected OutsideConvexHull, was {:?}", hull)
1577            }
1578        });
1579
1580        assert_eq!(e0, e1.rev());
1581
1582        Ok(())
1583    }
1584
1585    #[test]
1586    fn test_remove_on_line_end() -> Result<(), InsertionError> {
1587        let mut triangulation = DelaunayTriangulation::<_>::bulk_load(vec![
1588            Point2::new(0.0, 0.0),
1589            Point2::new(1.0, 0.0),
1590            Point2::new(2.0, 0.0),
1591        ])?;
1592        triangulation.remove(FixedVertexHandle::new(2));
1593        triangulation.sanity_check();
1594        Ok(())
1595    }
1596}