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 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 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 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 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 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 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 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 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 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 let e1 = if rotate_ccw {
594 e0
595 } else {
596 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 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 e0 = rotated;
613 e0_query = rotated_query;
614 continue;
615 }
616
617 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 e0 = e2.rev();
633 e0_query = e2_query.reversed();
634 if !e0.is_outer_edge() {
635 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 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 if edge1.side_query(target_position).is_on_left_side() {
727 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 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 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 (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 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::RngExt;
926 use rand::{seq::SliceRandom, SeedableRng};
927
928 use alloc::{vec, vec::Vec};
929
930 #[test]
931 fn test_empty() {
932 let d = DelaunayTriangulation::<Point2<f32>>::default();
933 assert_eq!(d.num_vertices(), 0);
934 assert_eq!(d.num_all_faces(), 1);
935 assert_eq!(d.num_undirected_edges(), 0);
936 }
937
938 #[test]
939 fn test_insert_first() -> Result<(), InsertionError> {
940 let mut d = DelaunayTriangulation::<Point2<f32>>::default();
941 d.insert(Point2::default())?;
942 assert_eq!(d.num_vertices(), 1);
943 assert_eq!(d.num_all_faces(), 1);
944 assert_eq!(d.num_undirected_edges(), 0);
945 Ok(())
946 }
947
948 #[test]
949 fn test_insert_second() -> Result<(), InsertionError> {
950 let mut d = DelaunayTriangulation::<_>::default();
951 d.insert(Point2::default())?;
952 d.insert(Point2::new(0.123, 1.234))?;
953 assert_eq!(d.num_vertices(), 2);
954 assert_eq!(d.num_all_faces(), 1);
955 assert_eq!(d.num_undirected_edges(), 1);
956 d.sanity_check();
957 Ok(())
958 }
959
960 #[test]
961 fn test_insert_third_point() -> Result<(), InsertionError> {
962 let mut d = DelaunayTriangulation::<_>::default();
963 d.insert(Point2::new(1f64, 0f64))?;
964 d.insert(Point2::new(0f64, 1f64))?;
965 d.insert(Point2::new(1f64, 1f64))?;
966
967 assert_eq!(d.num_vertices(), 3);
968 assert_eq!(d.num_all_faces(), 2);
969 d.sanity_check();
970 Ok(())
971 }
972
973 #[test]
974 fn test_insert_five_points() -> Result<(), InsertionError> {
975 let mut d = DelaunayTriangulation::<_>::default();
976 d.insert(Point2::new(1f64, 0f64))?;
977 d.insert(Point2::new(0f64, 1f64))?;
978
979 let v3 = Point2::new(0.433_833_144_214_401f64, 0.900_993_231_373_602_9f64);
980 let v4 = Point2::new(2.0, 2.0);
981 let v5 = Point2::new(0.5, 0.25);
982 d.insert(v3)?;
983 d.sanity_check();
984 d.insert(v4)?;
985 d.s().sanity_check();
986 d.insert(v5)?;
987 d.sanity_check();
988 Ok(())
989 }
990
991 #[test]
992 fn test_small_triangulation_iterators() -> Result<(), InsertionError> {
993 let mut d = DelaunayTriangulation::<_>::default();
994 assert_eq!(d.all_faces().count(), 1);
995 assert_eq!(d.inner_faces().count(), 0);
996
997 d.insert(Point2::new(1f64, 1f64))?;
998 assert_eq!(d.all_faces().count(), 1);
999 assert_eq!(d.inner_faces().count(), 0);
1000
1001 d.insert(Point2::new(-1f64, 1f64))?;
1002 assert_eq!(d.all_faces().count(), 1);
1003 assert_eq!(d.inner_faces().count(), 0);
1004 Ok(())
1005 }
1006
1007 #[test]
1008 #[should_panic]
1009 fn test_locate_nan_empty() {
1010 let d = DelaunayTriangulation::<Point2<f64>>::default();
1011 d.locate(Point2::new(0.0, f64::NAN));
1012 }
1013
1014 #[test]
1015 #[should_panic]
1016 fn test_locate_nan() {
1017 let points = random_points_with_seed(20, SEED);
1018 let d = DelaunayTriangulation::<Point2<f64>>::bulk_load(points);
1019 if let Ok(d) = d {
1020 d.locate(Point2::new(0.0, f64::NAN));
1021 }
1022 }
1023
1024 #[test]
1025 fn test_iterate_faces() -> Result<(), InsertionError> {
1026 const SIZE: usize = 1000;
1027 let points = random_points_with_seed(SIZE, SEED);
1028 let mut d = DelaunayTriangulation::<Point2<f64>>::bulk_load(points)?;
1029 d.sanity_check();
1030
1031 assert_eq!(d.all_faces().count(), d.num_all_faces());
1032 assert_eq!(d.inner_faces().count(), d.num_inner_faces());
1033
1034 for _ in 0..SIZE / 2 {
1035 d.remove(FixedVertexHandle::new(5));
1036 }
1037
1038 assert_eq!(d.all_faces().count(), d.num_all_faces());
1039 assert_eq!(d.inner_faces().count(), d.num_inner_faces());
1040
1041 d.sanity_check();
1042 Ok(())
1043 }
1044
1045 #[test]
1046 fn test_insert_many_points() -> Result<(), InsertionError> {
1047 const SIZE: usize = 10000;
1048 let points = random_points_with_seed(SIZE, SEED);
1049
1050 let mut d = DelaunayTriangulation::<_>::new();
1051 for point in points {
1052 d.insert(point)?;
1053 }
1054
1055 d.sanity_check();
1056 Ok(())
1057 }
1058
1059 #[test]
1060 fn test_insert_outside_convex_hull() -> anyhow::Result<()> {
1061 const NUM: usize = 100;
1062 let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1063 let range = Uniform::new(0., 2.0 * core::f64::consts::PI)?;
1064
1065 let mut d = DelaunayTriangulation::<_>::default();
1066
1067 for _ in 0..NUM {
1069 let ang = range.sample(&mut rng);
1070 let vec = Point2::new(ang.sin(), ang.cos());
1071 d.insert(vec)?;
1072 }
1073 assert_eq!(d.num_vertices(), NUM);
1074 d.sanity_check();
1075 Ok(())
1076 }
1077
1078 #[test]
1079 fn test_insert_same_point_small() -> Result<(), InsertionError> {
1080 let points = vec![
1081 Point2::new(0.2, 0.1),
1082 Point2::new(1.3, 2.2),
1083 Point2::new(0.0, 0.0),
1084 ];
1085 let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1086
1087 for p in &points {
1088 d.insert(*p)?;
1089 d.sanity_check();
1090 }
1091 assert_eq!(d.num_vertices(), points.len());
1092 d.sanity_check();
1093 Ok(())
1094 }
1095
1096 #[test]
1097 fn test_insert_same_point() -> Result<(), InsertionError> {
1098 const SIZE: usize = 300;
1099 let points = random_points_with_seed(SIZE, SEED);
1100 let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1101 for p in points {
1102 d.insert(p)?;
1103 }
1104 assert_eq!(d.num_vertices(), SIZE);
1105 d.sanity_check();
1106 Ok(())
1107 }
1108
1109 #[test]
1110 fn test_insert_point_on_ch_edge() -> Result<(), InsertionError> {
1111 let points = vec![
1112 Point2::new(0., 0f64),
1113 Point2::new(1., 0.),
1114 Point2::new(0., 1.),
1115 Point2::new(0., 0.4),
1116 ];
1117 let d = DelaunayTriangulation::<_>::bulk_load(points)?;
1118 d.sanity_check();
1119 Ok(())
1120 }
1121
1122 #[test]
1123 fn test_insert_on_edges() -> Result<(), InsertionError> {
1124 let points = vec![Point2::new(0., 0f64), Point2::new(1., 0.)];
1125 let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1126
1127 d.insert(Point2::new(1., 1.))?;
1128 d.sanity_check();
1129 d.insert(Point2::new(0.5, 0.5))?;
1130 d.sanity_check();
1131 d.insert(Point2::new(0., 0.4))?;
1132 d.sanity_check();
1133 d.insert(Point2::new(1., 0.5))?;
1134 d.sanity_check();
1135 d.insert(Point2::new(0.5, 1.))?;
1136 d.sanity_check();
1137 d.insert(Point2::new(0.7, 0.))?;
1138 d.sanity_check();
1139 Ok(())
1140 }
1141
1142 #[test]
1143 fn test_degenerate_triangulation() -> Result<(), InsertionError> {
1144 let mut d = DelaunayTriangulation::<_>::default();
1145 for i in -50..50 {
1146 d.insert(Point2::new(f64::from(i), 0.))?;
1147 }
1148
1149 d.sanity_check();
1150 Ok(())
1151 }
1152
1153 #[test]
1154 fn test_insert_points_on_line() -> Result<(), InsertionError> {
1155 let mut d = DelaunayTriangulation::<_>::default();
1156 d.insert(Point2::new(0.0, 1.0))?;
1157 for i in -50..50 {
1158 d.insert(Point2::new(f64::from(i), 0.))?;
1159 }
1160 d.sanity_check();
1161 Ok(())
1162 }
1163
1164 #[test]
1165 fn test_insert_points_on_line_2() -> Result<(), InsertionError> {
1166 let mut d = DelaunayTriangulation::<_>::default();
1167 d.insert(Point2::new(0.0, 1.0))?;
1168
1169 for i in -50..50 {
1170 d.insert(Point2::new(f64::from(i), 0.))?;
1171 d.sanity_check();
1172 }
1173
1174 for i in -0..10 {
1175 d.insert(Point2::new(f64::from(i), 0.5 * f64::from(i)))?;
1176 d.sanity_check();
1177 }
1178 d.sanity_check();
1179 Ok(())
1180 }
1181
1182 #[test]
1183 fn test_insert_points_on_grid2() -> Result<(), InsertionError> {
1184 let mut d = DelaunayTriangulation::<_>::default();
1185
1186 for y in 0..20 {
1187 for x in 0..7 {
1188 d.insert(Point2::new(f64::from(x), f64::from(y)))?;
1189 d.sanity_check();
1190 }
1191 }
1192 d.sanity_check();
1193 Ok(())
1194 }
1195
1196 #[test]
1197 fn test_insert_points_with_increasing_distance() -> Result<(), InsertionError> {
1198 let mut points = random_points_with_seed(1000, SEED);
1199 points.sort_by(|p1, p2| p1.length2().partial_cmp(&p2.length2()).unwrap());
1200 let mut d = DelaunayTriangulation::<_>::new();
1201 for point in points {
1202 d.insert(point)?;
1203 }
1204 d.sanity_check();
1205 Ok(())
1206 }
1207
1208 #[test]
1209 fn test_insert_points_on_grid_with_increasing_distance() -> Result<(), InsertionError> {
1210 let mut points = Vec::new();
1212 const SIZE: i64 = 7;
1213 for x in -SIZE..SIZE {
1214 for y in -SIZE..SIZE {
1215 let point = Point2::new(x as f64, y as f64);
1216 points.push(point);
1217 }
1218 }
1219 points.sort_by(|p1, p2| p1.length2().partial_cmp(&p2.length2()).unwrap());
1220 let d = DelaunayTriangulation::<_>::bulk_load(points)?;
1221 d.sanity_check();
1222 Ok(())
1223 }
1224
1225 #[test]
1226 fn test_remove_in_triangle() -> Result<(), InsertionError> {
1227 let points = vec![
1228 Point2::new(-1.0, 0.0f64),
1229 Point2::new(1.0, 0.0f64),
1230 Point2::new(0.0, 1.0f64),
1231 ];
1232 let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1233 let to_remove = d.insert(Point2::new(0.0, 0.5))?;
1234 d.remove(to_remove);
1235 assert_eq!(d.num_vertices(), 3);
1236 d.insert(Point2::new(0.0, 0.5))?;
1238 d.sanity_check();
1239 Ok(())
1240 }
1241
1242 #[test]
1243 fn test_remove_complex_single_outer_vertex() -> Result<(), InsertionError> {
1244 let mut d = DelaunayTriangulation::<_>::default();
1245 d.insert(Point2::new(0.0, 0.0))?;
1246 d.insert(Point2::new(0.0, 1.0))?;
1247 d.insert(Point2::new(4.0, 0.0))?;
1248 d.insert(Point2::new(4.0, 1.0))?;
1249 d.insert(Point2::new(2.0, 0.5))?;
1250 d.insert(Point2::new(1.0, 0.5))?;
1251 d.insert(Point2::new(3.0, 0.5))?;
1252
1253 let v4_position = Point2::new(2.5, 2.0);
1254 let v4 = d.insert(v4_position)?;
1255
1256 let removed = d.remove(v4);
1257 d.sanity_check();
1258 assert_eq!(removed, v4_position);
1259 assert_eq!(d.num_vertices(), 7);
1260 Ok(())
1261 }
1262
1263 #[test]
1264 fn test_remove_single_outer() -> Result<(), InsertionError> {
1265 let mut d = DelaunayTriangulation::<_>::default();
1266 d.insert(Point2::new(0.0, 0.0))?;
1267 d.insert(Point2::new(0.0, 1.0))?;
1268 d.insert(Point2::new(1.0, 0.0))?;
1269 let v4_position = Point2::new(1.5, 1.5);
1270 let v4 = d.insert(v4_position)?;
1271
1272 let removed = d.remove(v4);
1273 d.sanity_check();
1274 assert_eq!(removed, v4_position);
1275 assert_eq!(d.num_vertices(), 3);
1276 Ok(())
1277 }
1278
1279 #[test]
1280 fn test_remove_in_quad() -> Result<(), InsertionError> {
1281 let points = vec![
1282 Point2::new(0.0, 0.0f64),
1283 Point2::new(1.0, 0.0f64),
1284 Point2::new(0.0, 1.0f64),
1285 Point2::new(1.0, 1.0f64),
1286 ];
1287
1288 let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1289
1290 let to_remove = d.insert(Point2::new(0.5, 0.6))?;
1291 d.remove(to_remove);
1292 assert_eq!(d.num_vertices(), 4);
1293 let to_remove = d.insert(Point2::new(0.5, 0.6))?;
1294 d.remove(to_remove);
1295 assert_eq!(d.num_vertices(), 4);
1296 d.insert(Point2::new(0.5, 0.6))?;
1297 d.sanity_check();
1298 Ok(())
1299 }
1300
1301 #[test]
1302 fn test_remove_star_shaped() -> Result<(), InsertionError> {
1303 let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1304 let mut points = vec![
1305 Point2::new(0.0, 0.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(-1.0, -1.0),
1310 Point2::new(0.0, 3.0),
1311 Point2::new(0.0, -3.0),
1312 Point2::new(3.0, 0.0),
1313 Point2::new(-3.0, 0.0),
1314 ];
1315
1316 points.shuffle(&mut rng);
1317 points.shuffle(&mut rng);
1318 points.shuffle(&mut rng);
1319 for _ in 0..20 {
1320 points.shuffle(&mut rng);
1321 let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1322 d.locate_and_remove(Point2::new(0.0, 0.0));
1323 d.sanity_check();
1324 }
1325 Ok(())
1326 }
1327
1328 #[test]
1329 fn test_remove_inner() -> Result<(), InsertionError> {
1330 use ::rand::SeedableRng;
1331 use PositionInTriangulation::OnVertex;
1332
1333 let mut points = random_points_with_seed(1000, SEED);
1334 let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1335
1336 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 d.insert(Point2::new(2.0, 2.0))?;
1342 let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1344 points.shuffle(&mut rng);
1345 assert_eq!(d.num_vertices(), 1004);
1346 for point in points {
1347 match d.locate(point) {
1348 OnVertex(handle) => {
1349 d.remove(handle);
1350 }
1351 _ => panic!("Point lookup failed: {:?}", point),
1352 }
1353 }
1354 assert_eq!(d.num_vertices(), 4);
1355 d.sanity_check();
1356 Ok(())
1357 }
1358
1359 #[test]
1360 fn test_remove_outer() -> Result<(), InsertionError> {
1361 use PositionInTriangulation::OnVertex;
1362
1363 let mut points = random_points_with_seed(1000, SEED);
1364 let mut d = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1365
1366 points.sort_by(|p1, p2| p1.length2().partial_cmp(&p2.length2()).unwrap());
1367 for (index, point) in points[3..].iter().rev().enumerate() {
1368 match d.locate(*point) {
1369 OnVertex(handle) => {
1370 d.remove(handle);
1371 if index % 50 == 0 {
1372 d.sanity_check();
1374 }
1375 }
1376 _ => panic!("Point lookup failed: {:?}", point),
1377 }
1378 }
1379 d.sanity_check();
1380 Ok(())
1381 }
1382
1383 #[test]
1384 fn test_removal_and_insertion() -> anyhow::Result<()> {
1385 let points = random_points_with_seed(1000, SEED);
1386 let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1387
1388 let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1389 for _ in 0..1000 {
1390 if rng.random() {
1391 let x = rng.random();
1393 let y = rng.random();
1394 d.insert(Point2::new(x, y))?;
1395 } else {
1396 let range = Uniform::new(1, d.num_vertices())?;
1398 let handle = range.sample(&mut rng);
1399 d.remove(FixedVertexHandle::new(handle));
1400 }
1401 }
1402 d.sanity_check();
1403 Ok(())
1404 }
1405
1406 #[test]
1407 fn test_remove_until_empty() -> Result<(), InsertionError> {
1408 let mut d = DelaunayTriangulation::<_>::bulk_load(vec![
1409 Point2::new(0.0, 0.0),
1410 Point2::new(0.0, 1.0),
1411 Point2::new(1.0, 2.0),
1412 ])?;
1413
1414 while let Some(to_remove) = d.vertices().next() {
1415 let to_remove = to_remove.fix();
1416 d.remove(to_remove);
1417 d.sanity_check();
1418 }
1419
1420 assert_eq!(d.num_vertices(), 0);
1421
1422 d.insert(Point2::new(1.0, 0.0))?;
1423 d.insert(Point2::new(1.0, 1.0))?;
1424 d.insert(Point2::new(1.0, 2.0))?;
1425 d.insert(Point2::new(2.3, 1.4))?;
1426 d.sanity_check();
1427 Ok(())
1428 }
1429
1430 #[test]
1431 fn test_remove_until_degenerate() -> Result<(), InsertionError> {
1432 let points = vec![
1433 Point2::new(0., 0f64),
1434 Point2::new(1., 0.),
1435 Point2::new(0., 1.),
1436 Point2::new(0., 0.5),
1437 Point2::new(0., 0.25),
1438 Point2::new(0., 0.75),
1439 ];
1440 let mut d = DelaunayTriangulation::<_>::bulk_load(points)?;
1441
1442 assert_eq!(d.num_all_faces(), 5);
1443 d.locate_and_remove(Point2::new(1., 0.));
1444 d.sanity_check();
1445 assert!(d.all_vertices_on_line());
1446
1447 while let Some(to_remove) = d.vertices().next() {
1448 let to_remove = to_remove.fix();
1449 d.remove(to_remove);
1450 d.sanity_check();
1451 }
1452
1453 d.sanity_check();
1454 d.insert(Point2::new(0.5, 0.5))?;
1455 d.insert(Point2::new(0.2, 0.5))?;
1456 d.insert(Point2::new(1.5, 0.0))?;
1457 d.sanity_check();
1458 Ok(())
1459 }
1460
1461 #[test]
1462 fn test_remove_few_points() -> Result<(), InsertionError> {
1463 let mut triangulation = DelaunayTriangulation::<_>::bulk_load(vec![
1464 Point2::new(0.0, 1.0),
1465 Point2::new(100.0, 1.0),
1466 Point2::new(0.0, 110.0),
1467 Point2::new(110.110, 110.0),
1468 Point2::new(50.0, 50.0),
1469 Point2::new(50.0, 80.0),
1470 Point2::new(75.0, 80.0),
1471 ])?;
1472
1473 triangulation.remove(FixedVertexHandle::new(5));
1474 triangulation.sanity_check();
1475 triangulation.remove(FixedVertexHandle::new(5));
1476 triangulation.sanity_check();
1477 Ok(())
1478 }
1479
1480 #[test]
1481 fn test_remove_on_line_small() -> Result<(), InsertionError> {
1482 let mut triangulation = DelaunayTriangulation::<_>::bulk_load(vec![
1483 Point2::new(0.0, 0.0),
1484 Point2::new(1.0, 0.0), Point2::new(2.0, 0.0),
1486 ])?;
1487 triangulation.remove(FixedVertexHandle::new(2));
1488 triangulation.sanity_check();
1489 Ok(())
1490 }
1491
1492 #[test]
1493 fn test_remove_on_line_big() -> Result<(), InsertionError> {
1494 let mut triangulation = DelaunayTriangulation::<_>::default();
1495 for x in 2..100 {
1496 triangulation.insert(Point2::new(f64::from(x), 0.0))?;
1497 }
1498 let mut rng = rand::rngs::StdRng::from_seed(*SEED);
1499 while triangulation.num_vertices() > 3 {
1500 if rng.random_bool(0.5) {
1501 triangulation.remove(FixedVertexHandle::new(1));
1502 } else {
1503 triangulation.remove(FixedVertexHandle::new(2));
1504 }
1505
1506 triangulation.sanity_check();
1507 }
1508 Ok(())
1509 }
1510
1511 #[test]
1512 fn test_small_insert_on_line() -> Result<(), InsertionError> {
1513 let mut d = DelaunayTriangulation::<_>::default();
1514 d.insert(Point2::new(0.0, 0.0))?;
1515 d.insert(Point2::new(2.0, 0.0))?;
1516 d.insert(Point2::new(1.0, 0.0))?;
1517 d.sanity_check();
1518 Ok(())
1519 }
1520
1521 #[test]
1522 fn test_locate_when_empty() {
1523 let triangulation = DelaunayTriangulation::<Point2<_>>::default();
1524 assert_eq!(
1525 triangulation.locate(Point2::new(0.0, 0.0)),
1526 PositionInTriangulation::NoTriangulation
1527 )
1528 }
1529
1530 #[test]
1531 fn test_locate_with_single_vertex() -> Result<(), InsertionError> {
1532 let mut triangulation = DelaunayTriangulation::<_>::default();
1533 let point = Point2::new(0.0, 0.0);
1534 triangulation.insert(point)?;
1535 assert_eq!(
1536 triangulation.locate(point),
1537 PositionInTriangulation::OnVertex(FixedVertexHandle::new(0))
1538 );
1539 assert_eq!(
1540 triangulation.locate(Point2::new(1.0, 1.0)),
1541 PositionInTriangulation::NoTriangulation
1542 );
1543 Ok(())
1544 }
1545
1546 #[test]
1547 fn test_locate_with_two_vertices() -> Result<(), InsertionError> {
1548 let mut triangulation = DelaunayTriangulation::<_>::default();
1549 let p0 = Point2::new(0.0, 0.0);
1550 let v0 = triangulation.insert(p0)?;
1551 let p1 = Point2::new(1.0, 1.0);
1552 let v1 = triangulation.insert(p1)?;
1553
1554 assert_eq!(
1555 triangulation.locate(p0),
1556 PositionInTriangulation::OnVertex(v0)
1557 );
1558 assert_eq!(
1559 triangulation.locate(p1),
1560 PositionInTriangulation::OnVertex(v1)
1561 );
1562 let on_edge = triangulation.locate(Point2::new(0.5, 0.5));
1563 match on_edge {
1564 PositionInTriangulation::OnEdge(_) => {}
1565 _ => panic!("Expected OnEdge(_), was {:?}", on_edge),
1566 }
1567
1568 let hull0 = triangulation.locate(Point2::new(0.0, 1.0));
1569 let hull1 = triangulation.locate(Point2::new(0.0, -1.0));
1570 let hull2 = triangulation.locate(Point2::new(-1.0, -1.0));
1571 let hull3 = triangulation.locate(Point2::new(2.0, 2.0));
1572
1573 let [e0, e1, _, _] = [hull0, hull1, hull2, hull3].map(|hull| {
1574 if let PositionInTriangulation::OutsideOfConvexHull(edge) = hull {
1575 edge
1576 } else {
1577 panic!("Expected OutsideConvexHull, was {:?}", hull)
1578 }
1579 });
1580
1581 assert_eq!(e0, e1.rev());
1582
1583 Ok(())
1584 }
1585
1586 #[test]
1587 fn test_remove_on_line_end() -> Result<(), InsertionError> {
1588 let mut triangulation = DelaunayTriangulation::<_>::bulk_load(vec![
1589 Point2::new(0.0, 0.0),
1590 Point2::new(1.0, 0.0),
1591 Point2::new(2.0, 0.0),
1592 ])?;
1593 triangulation.remove(FixedVertexHandle::new(2));
1594 triangulation.sanity_check();
1595 Ok(())
1596 }
1597}