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::{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 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 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 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 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 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 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 let x = rng.random();
1392 let y = rng.random();
1393 d.insert(Point2::new(x, y))?;
1394 } else {
1395 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), 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}