1use alloc::vec::Vec;
2use core::fmt::Formatter;
3
4use num_traits::{Float, NumCast};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use crate::cdt::ConflictRegionEnd::{EdgeOverlap, Existing};
9use crate::delaunay_core::dcel_operations::{
10 append_unconnected_vertex, flip_cw, new_with_fixed_vertices,
11};
12use crate::delaunay_core::{bulk_load_cdt, try_bulk_load_cdt};
13use crate::{
14 delaunay_core::Dcel, intersection_iterator::LineIntersectionIterator, PositionInTriangulation,
15 SpadeNum,
16};
17use crate::{handles::*, intersection_iterator::Intersection};
18use crate::{
19 mitigate_underflow, DelaunayTriangulation, HasPosition, HintGenerator, InsertionError,
20 LastUsedVertexHintGenerator, Point2, Triangulation, TriangulationExt,
21};
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(
35 feature = "serde",
36 derive(Serialize, Deserialize),
37 serde(crate = "serde")
38)]
39pub struct CdtEdge<UE>(bool, UE);
40
41impl<UE> CdtEdge<UE> {
42 pub fn is_constraint_edge(&self) -> bool {
44 self.0
45 }
46
47 fn make_constraint_edge(&mut self) {
48 assert!(!self.is_constraint_edge());
49 self.0 = true;
50 }
51
52 fn unmake_constraint_edge(&mut self) {
53 assert!(self.is_constraint_edge());
54 self.0 = false;
55 }
56
57 pub fn data(&self) -> &UE {
59 &self.1
60 }
61
62 pub fn data_mut(&mut self) -> &mut UE {
64 &mut self.1
65 }
66
67 pub(crate) fn deconstruct(self) -> (bool, UE) {
68 (self.0, self.1)
69 }
70}
71
72impl<UE: Default> Default for CdtEdge<UE> {
73 fn default() -> Self {
74 CdtEdge(false, UE::default())
75 }
76}
77
78impl<UE> AsRef<UE> for CdtEdge<UE> {
79 fn as_ref(&self) -> &UE {
80 self.data()
81 }
82}
83
84impl<UE> AsMut<UE> for CdtEdge<UE> {
85 fn as_mut(&mut self) -> &mut UE {
86 self.data_mut()
87 }
88}
89
90#[doc = include_str!("../images/cdt.svg")]
98#[doc(alias = "CDT")]
147#[derive(Clone)]
148#[cfg_attr(
149 feature = "serde",
150 derive(Serialize, Deserialize),
151 serde(crate = "serde")
152)]
153pub struct ConstrainedDelaunayTriangulation<
154 V,
155 DE = (),
156 UE = (),
157 F = (),
158 L = LastUsedVertexHintGenerator,
159> where
160 V: HasPosition,
161 DE: Default,
162 UE: Default,
163 F: Default,
164 L: HintGenerator<<V as HasPosition>::Scalar>,
165{
166 dcel: Dcel<V, DE, CdtEdge<UE>, F>,
167 num_constraints: usize,
168 hint_generator: L,
169}
170
171impl<V, DE, UE, F, L> Default for ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
172where
173 V: HasPosition,
174 DE: Default,
175 UE: Default,
176 F: Default,
177 L: HintGenerator<<V as HasPosition>::Scalar>,
178{
179 fn default() -> Self {
180 ConstrainedDelaunayTriangulation {
181 dcel: Default::default(),
182 num_constraints: 0,
183 hint_generator: Default::default(),
184 }
185 }
186}
187
188impl<V, DE, UE, F, L> Triangulation for ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
189where
190 V: HasPosition,
191 DE: Default,
192 UE: Default,
193 F: Default,
194 L: HintGenerator<<V as HasPosition>::Scalar>,
195{
196 type Vertex = V;
197 type DirectedEdge = DE;
198 type UndirectedEdge = CdtEdge<UE>;
199 type Face = F;
200 type HintGenerator = L;
201
202 fn s(&self) -> &Dcel<V, DE, CdtEdge<UE>, F> {
203 &self.dcel
204 }
205
206 fn s_mut(&mut self) -> &mut Dcel<V, DE, CdtEdge<UE>, F> {
207 &mut self.dcel
208 }
209
210 fn is_defined_legal(&self, edge: FixedUndirectedEdgeHandle) -> bool {
211 self.is_constraint_edge(edge)
212 }
213
214 fn handle_legal_edge_split(&mut self, handles: [FixedDirectedEdgeHandle; 2]) {
215 self.num_constraints += 1;
216 for handle in handles.iter().map(|e| e.as_undirected()) {
217 if !self.is_constraint_edge(handle) {
218 self.dcel
219 .undirected_edge_data_mut(handle)
220 .make_constraint_edge();
221 }
222 }
223 }
224
225 fn hint_generator(&self) -> &Self::HintGenerator {
226 &self.hint_generator
227 }
228
229 fn hint_generator_mut(&mut self) -> &mut Self::HintGenerator {
230 &mut self.hint_generator
231 }
232
233 fn from_parts(
234 dcel: Dcel<Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face>,
235 hint_generator: Self::HintGenerator,
236 num_constraints: usize,
237 ) -> Self {
238 Self {
239 dcel,
240 num_constraints,
241 hint_generator,
242 }
243 }
244
245 fn remove(&mut self, vertex: FixedVertexHandle) -> V {
253 let removed_constraints = self
254 .dcel
255 .vertex(vertex)
256 .out_edges()
257 .filter(|edge| edge.is_constraint_edge())
258 .map(|e| e.as_undirected().fix())
259 .collect::<Vec<_>>();
260
261 for e in removed_constraints {
262 self.remove_constraint_edge(e);
263 }
264
265 self.remove_and_notify(vertex)
266 }
267
268 fn into_parts(
269 self,
270 ) -> (
271 Dcel<Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face>,
272 Self::HintGenerator,
273 usize,
274 ) {
275 (self.dcel, self.hint_generator, self.num_constraints)
276 }
277
278 fn clear(&mut self) {
279 self.num_constraints = 0;
280 self.s_mut().clear();
281 let new_hint_generator = HintGenerator::initialize_from_triangulation(self);
282 *self.hint_generator_mut() = new_hint_generator;
283 }
284}
285
286impl<V, DE, UE, F, L> From<DelaunayTriangulation<V, DE, UE, F, L>>
287 for ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
288where
289 V: HasPosition,
290 DE: Default,
291 UE: Default,
292 F: Default,
293 L: HintGenerator<<V as HasPosition>::Scalar>,
294{
295 fn from(value: DelaunayTriangulation<V, DE, UE, F, L>) -> Self {
296 let dcel = value.dcel;
297 let s = dcel.map_undirected_edges(|edge| CdtEdge(false, edge));
298 let lookup = value.hint_generator;
299
300 ConstrainedDelaunayTriangulation {
301 dcel: s,
302 num_constraints: 0,
303 hint_generator: lookup,
304 }
305 }
306}
307
308impl<V, DE, UE, F, L> ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
309where
310 V: HasPosition,
311 DE: Default,
312 UE: Default,
313 F: Default,
314 L: HintGenerator<<V as HasPosition>::Scalar>,
315{
316 pub fn bulk_load_cdt(vertices: Vec<V>, edges: Vec<[usize; 2]>) -> Result<Self, InsertionError> {
356 bulk_load_cdt(vertices, edges)
357 }
358
359 pub fn try_bulk_load_cdt(
398 vertices: Vec<V>,
399 edges: Vec<[usize; 2]>,
400 on_conflict_found: impl FnMut([usize; 2]),
401 ) -> Result<Self, InsertionError> {
402 try_bulk_load_cdt(vertices, edges, on_conflict_found)
403 }
404
405 #[deprecated(
407 since = "1.15.0",
408 note = "Use bulk_load_cdt instead. It is now stable by default."
409 )]
410 pub fn bulk_load_cdt_stable(
411 vertices: Vec<V>,
412 edges: Vec<[usize; 2]>,
413 ) -> Result<Self, InsertionError> {
414 Self::bulk_load_cdt(vertices, edges)
415 }
416
417 pub fn remove(&mut self, vertex: FixedVertexHandle) -> V {
421 <Self as Triangulation>::remove(self, vertex)
422 }
423
424 pub fn num_constraints(&self) -> usize {
426 self.num_constraints
427 }
428
429 pub fn is_constraint_edge(&self, edge: FixedUndirectedEdgeHandle) -> bool {
431 self.dcel.undirected_edge_data(edge).is_constraint_edge()
432 }
433
434 pub fn exists_constraint(&self, from: FixedVertexHandle, to: FixedVertexHandle) -> bool {
436 self.get_edge_from_neighbors(from, to)
437 .map(|e| e.is_constraint_edge())
438 .unwrap_or(false)
439 }
440
441 pub fn can_add_constraint(&self, from: FixedVertexHandle, to: FixedVertexHandle) -> bool {
446 let line_intersection_iterator = LineIntersectionIterator::new_from_handles(self, from, to);
447 !self.contains_any_constraint_edge(line_intersection_iterator)
448 }
449
450 pub fn intersects_constraint(
455 &self,
456 line_from: Point2<V::Scalar>,
457 line_to: Point2<V::Scalar>,
458 ) -> bool {
459 let line_intersection_iterator = LineIntersectionIterator::new(self, line_from, line_to);
460 self.contains_any_constraint_edge(line_intersection_iterator)
461 }
462
463 fn contains_any_constraint_edge(
464 &self,
465 mut line_intersection_iterator: LineIntersectionIterator<V, DE, CdtEdge<UE>, F>,
466 ) -> bool {
467 line_intersection_iterator.any(|intersection| match intersection {
468 Intersection::EdgeIntersection(edge) => edge.is_constraint_edge(),
469 _ => false,
470 })
471 }
472
473 pub fn add_constraint_edges(
506 &mut self,
507 vertices: impl IntoIterator<Item = V>,
508 closed: bool,
509 ) -> Result<(), InsertionError> {
510 let mut iter = vertices.into_iter();
511 if let Some(first) = iter.next() {
512 let first_handle = self.insert(first)?;
513 let mut previous_handle = first_handle;
514 let mut current_handle = first_handle;
515 for current in iter {
516 current_handle = self.insert(current)?;
517 self.add_constraint(previous_handle, current_handle);
518 previous_handle = current_handle;
519 }
520
521 if closed && current_handle != first_handle {
522 self.add_constraint(current_handle, first_handle);
523 }
524 }
525
526 Ok(())
527 }
528
529 pub fn add_constraint_edge(&mut self, from: V, to: V) -> Result<bool, InsertionError> {
538 let from_handle = self.insert(from)?;
539 let to_handle = self.insert(to)?;
540 Ok(self.add_constraint(from_handle, to_handle))
541 }
542
543 pub fn add_constraint(&mut self, from: FixedVertexHandle, to: FixedVertexHandle) -> bool {
559 let initial_num_constraints = self.num_constraints();
560 self.resolve_splitting_constraint_request(from, to, None);
561
562 self.num_constraints != initial_num_constraints
563 }
564
565 fn resolve_conflict_region(
611 &mut self,
612 conflict_edges: Vec<FixedDirectedEdgeHandle>,
613 target_vertex: FixedVertexHandle,
614 ) -> Option<FixedDirectedEdgeHandle> {
615 let first = conflict_edges.first()?;
616
617 let mut temporary_constraint_edges = Vec::new();
618
619 let first = self.directed_edge(*first);
620
621 let first_border_edge = first.rev().prev().fix();
624 let last_border_edge = first.rev().next().fix();
625
626 for edge in &conflict_edges {
628 flip_cw(self.s_mut(), edge.as_undirected());
629 }
630
631 let mut make_temporary_edge = |cdt: &mut Self, edge: FixedUndirectedEdgeHandle| {
636 if !cdt.undirected_edge(edge).is_constraint_edge() {
639 temporary_constraint_edges.push(edge);
640 cdt.undirected_edge_data_mut(edge).make_constraint_edge();
641 }
642 };
643
644 make_temporary_edge(self, first_border_edge.as_undirected());
645 make_temporary_edge(self, last_border_edge.as_undirected());
646
647 let mut current = first_border_edge;
648
649 let mut result = None;
650
651 while current != last_border_edge.rev() {
656 let handle = self.directed_edge(current);
657 let fixed = handle.fix();
658 let next = handle.next().fix().as_undirected();
659
660 current = handle.ccw().fix();
661 if target_vertex == handle.to().fix() {
662 self.make_constraint_edge(fixed.as_undirected());
665 result = Some(fixed);
666 }
667 make_temporary_edge(self, next);
668 }
669
670 self.legalize_edges_after_removal(
671 &mut conflict_edges
672 .into_iter()
673 .map(|edge| edge.as_undirected())
674 .collect(),
675 |_| false,
676 );
677
678 for edge in temporary_constraint_edges {
680 self.undirected_edge_data_mut(edge).0 = false;
681 }
682
683 result
684 }
685
686 pub fn get_conflicting_edges_between_points(
692 &self,
693 from: Point2<<V as HasPosition>::Scalar>,
694 to: Point2<<V as HasPosition>::Scalar>,
695 ) -> impl Iterator<Item = DirectedEdgeHandle<'_, V, DE, CdtEdge<UE>, F>> {
696 LineIntersectionIterator::new(self, from, to)
697 .flat_map(|intersection| intersection.as_edge_intersection())
698 .filter(|e| e.is_constraint_edge())
699 }
700
701 pub fn get_conflicting_edges_between_vertices(
708 &self,
709 from: FixedVertexHandle,
710 to: FixedVertexHandle,
711 ) -> impl Iterator<Item = DirectedEdgeHandle<'_, V, DE, CdtEdge<UE>, F>> {
712 LineIntersectionIterator::new_from_handles(self, from, to)
713 .flat_map(|intersection| intersection.as_edge_intersection())
714 .filter(|e| e.is_constraint_edge())
715 }
716
717 fn make_constraint_edge(&mut self, edge: FixedUndirectedEdgeHandle) -> bool {
718 if !self.is_constraint_edge(edge) {
719 self.dcel
720 .undirected_edge_data_mut(edge)
721 .make_constraint_edge();
722 self.num_constraints += 1;
723 true
724 } else {
725 false
726 }
727 }
728
729 #[cfg(any(test, fuzzing))]
730 #[allow(missing_docs)]
731 pub fn cdt_sanity_check(&self) {
732 self.cdt_sanity_check_with_params(true);
733 }
734
735 #[cfg(any(test, fuzzing))]
736 #[allow(missing_docs)]
737 pub fn cdt_sanity_check_with_params(&self, check_convexity: bool) {
738 let num_constraints = self
739 .dcel
740 .undirected_edges()
741 .filter(|e| e.is_constraint_edge())
742 .count();
743
744 assert_eq!(num_constraints, self.num_constraints());
745
746 if self.num_constraints() == 0 && check_convexity {
747 self.sanity_check();
748 } else {
749 self.basic_sanity_check(check_convexity);
750 }
751 }
752
753 pub fn remove_constraint_edge(&mut self, edge: FixedUndirectedEdgeHandle) -> bool {
758 if self.is_constraint_edge(edge) {
759 self.dcel
760 .undirected_edge_data_mut(edge)
761 .unmake_constraint_edge();
762 self.num_constraints -= 1;
763 self.legalize_edge(edge.as_directed(), true);
764 true
765 } else {
766 false
767 }
768 }
769
770 pub fn try_add_constraint(
808 &mut self,
809 from: FixedVertexHandle,
810 to: FixedVertexHandle,
811 ) -> Vec<FixedDirectedEdgeHandle> {
812 let initial_conflict_regions = self.get_conflict_resolutions(from, to);
817 self.resolve_conflict_groups(initial_conflict_regions)
818 }
819
820 fn get_conflict_resolutions(
821 &mut self,
822 from: FixedVertexHandle,
823 to: FixedVertexHandle,
824 ) -> Vec<InitialConflictRegion> {
825 let mut conflict_groups = Vec::new();
826 let mut current_group = Vec::new();
827 let mut ignore_next_vertex = false;
828 for intersection in LineIntersectionIterator::new_from_handles(self, from, to) {
829 match intersection {
830 Intersection::EdgeIntersection(edge) => {
831 if !edge.is_constraint_edge() {
832 current_group.push(edge.fix());
833 continue;
834 }
835
836 return Vec::new();
837 }
838 Intersection::VertexIntersection(v) => {
839 if ignore_next_vertex {
840 ignore_next_vertex = false;
841 continue;
842 }
843 let group_end = Existing(v.fix());
844 let conflict_edges = core::mem::take(&mut current_group);
845 conflict_groups.push(InitialConflictRegion {
846 conflict_edges,
847 group_end,
848 });
849 }
850 Intersection::EdgeOverlap(edge) => {
851 conflict_groups.push(InitialConflictRegion {
852 conflict_edges: Vec::new(),
853 group_end: EdgeOverlap(edge.fix()),
854 });
855 ignore_next_vertex = true;
859 }
860 }
861 }
862
863 conflict_groups
864 }
865
866 fn resolve_splitting_constraint_request(
867 &mut self,
868 mut from: FixedVertexHandle,
869 to: FixedVertexHandle,
870 vertex_constructor: Option<&dyn Fn(Point2<f64>) -> V>,
871 ) -> Vec<FixedDirectedEdgeHandle> {
872 let mut result = Vec::new();
873 let mut conflict_edges = Vec::new();
874 let mut legalize_buffer = Vec::new();
875 let mut iterator = LineIntersectionIterator::new_from_handles(self, from, to);
876 iterator.next();
877
878 while let Some(intersection) = iterator.next() {
885 match intersection {
886 Intersection::EdgeOverlap(edge) => {
887 result.push(edge.fix());
888 from = edge.to().fix();
889 }
890 Intersection::EdgeIntersection(mut edge) => {
891 if !edge.is_constraint_edge() {
892 conflict_edges.push(edge.fix());
894 continue;
895 }
896 let [p0, p1] = edge.positions().map(|p| p.to_f64());
898
899 let from_pos = self.vertex(from).position().to_f64();
900 let to_pos = self.vertex(to).position().to_f64();
901
902 let line_intersection = get_edge_intersections(p0, p1, from_pos, to_pos);
905 let line_intersection = mitigate_underflow(line_intersection);
906 let new_vertex = vertex_constructor
907 .expect("The new constraint edge intersects an existing constraint edge.")(
908 line_intersection,
909 );
910
911 let position = new_vertex.position();
914
915 let alternative_vertex = self.validate_split_position(edge, position);
922
923 let final_vertex =
924 if let Some((alternative_vertex, is_end_vertex)) = alternative_vertex {
925 if !is_end_vertex {
926 let is_on_same_side = edge.opposite_vertex().map(|v| v.fix())
928 == Some(alternative_vertex);
929 if !is_on_same_side {
930 edge = edge.rev();
931 }
932 let prev = edge.prev().fix();
947 let next = edge.next().fix();
948
949 let edge = edge.fix();
950 self.undirected_edge_data_mut(edge.as_undirected())
951 .unmake_constraint_edge();
952 self.num_constraints -= 1;
953
954 self.make_constraint_edge(prev.as_undirected());
955 self.make_constraint_edge(next.as_undirected());
956
957 legalize_buffer.push(edge.as_undirected());
958 self.legalize_edges_after_removal(&mut legalize_buffer, |_| false);
959 }
960
961 alternative_vertex
962 } else {
963 let edge = edge.fix();
964 let new_vertex = append_unconnected_vertex(self.s_mut(), new_vertex);
965 let [e0, e1] = self.insert_on_edge(edge, new_vertex);
966 self.handle_legal_edge_split([e0, e1]);
967 self.legalize_vertex(new_vertex);
968 new_vertex
969 };
970
971 let previous_region = self.try_add_constraint(from, final_vertex);
976 assert!(!previous_region.is_empty() || from == final_vertex);
979 result.extend(previous_region);
980 conflict_edges.clear();
981
982 from = final_vertex;
983 iterator = LineIntersectionIterator::new_from_handles(self, from, to);
987
988 iterator.next();
990 }
991 Intersection::VertexIntersection(vertex) => {
992 let vertex = vertex.fix();
995 let copy = core::mem::take(&mut conflict_edges);
996 let new_edge = self.resolve_conflict_region(copy, vertex);
997 result.extend(new_edge);
998 iterator = LineIntersectionIterator::new_from_handles(self, vertex, to);
999 iterator.next();
1000 from = vertex;
1001 }
1002 }
1003 }
1004
1005 for edge in &result {
1006 self.make_constraint_edge(edge.as_undirected());
1007 }
1008
1009 result
1010 }
1011
1012 fn validate_split_position(
1013 &self,
1014 conflict_edge: DirectedEdgeHandle<V, DE, CdtEdge<UE>, F>,
1015 split_position: Point2<<V as HasPosition>::Scalar>,
1016 ) -> Option<(FixedVertexHandle, bool)> {
1017 let is_valid = match self.locate_with_hint(split_position, conflict_edge.from().fix()) {
1033 PositionInTriangulation::OnEdge(real_edge) => {
1034 real_edge.as_undirected() == conflict_edge.fix().as_undirected()
1035 }
1036 PositionInTriangulation::OnFace(face) => {
1037 let face = face.adjust_inner_outer();
1038 face == conflict_edge.face().fix() || face == conflict_edge.rev().face().fix()
1039 }
1040 PositionInTriangulation::OutsideOfConvexHull(_) => {
1041 conflict_edge.is_part_of_convex_hull()
1042 }
1043 PositionInTriangulation::OnVertex(_) => false,
1044 PositionInTriangulation::NoTriangulation => unreachable!(),
1045 };
1046
1047 if is_valid {
1048 None
1049 } else {
1050 let split_position = split_position.to_f64();
1051 let [d_from, d_to] = [conflict_edge.from(), conflict_edge.to()]
1052 .map(|v| v.position().to_f64().distance_2(split_position));
1053
1054 let mut min_distance = d_from;
1055 let mut min_vertex = conflict_edge.from();
1056 let mut is_end_vertex = true;
1057 if d_to < min_distance {
1058 min_distance = d_to;
1059 min_vertex = conflict_edge.to();
1060 }
1061
1062 if let Some(opposite) = conflict_edge.opposite_vertex() {
1063 let d_left = opposite.position().to_f64().distance_2(split_position);
1064
1065 if d_left < min_distance {
1066 min_distance = d_left;
1067 min_vertex = conflict_edge.next().to();
1068
1069 is_end_vertex = false;
1070 }
1071 }
1072
1073 if let Some(opposite) = conflict_edge.rev().opposite_vertex() {
1074 let d_right = opposite.position().to_f64().distance_2(split_position);
1075
1076 if d_right < min_distance {
1077 min_vertex = conflict_edge.rev().next().to();
1078 is_end_vertex = false;
1079 }
1080 }
1081
1082 Some((min_vertex.fix(), is_end_vertex))
1083 }
1084 }
1085
1086 fn resolve_conflict_groups(
1087 &mut self,
1088 conflict_groups: Vec<InitialConflictRegion>,
1089 ) -> Vec<FixedDirectedEdgeHandle> {
1090 let mut constraint_edges = Vec::new();
1091
1092 for InitialConflictRegion {
1093 conflict_edges,
1094 group_end,
1095 } in conflict_groups
1096 {
1097 let target_vertex = match group_end {
1098 Existing(v) => v,
1099 EdgeOverlap(edge) => {
1100 constraint_edges.push(edge);
1101
1102 continue;
1105 }
1106 };
1107
1108 constraint_edges.extend(self.resolve_conflict_region(conflict_edges, target_vertex));
1109 }
1110
1111 for edge in &constraint_edges {
1112 self.make_constraint_edge(edge.as_undirected());
1113 }
1114
1115 constraint_edges
1116 }
1117
1118 pub(crate) fn create_with_fixed_vertices(
1119 elements: Vec<V>,
1120 first_vertex: FixedVertexHandle,
1121 second_vertex: FixedVertexHandle,
1122 ) -> Self {
1123 Self {
1124 dcel: new_with_fixed_vertices(elements, first_vertex, second_vertex),
1125 num_constraints: 0,
1126 hint_generator: Default::default(),
1129 }
1130 }
1131
1132 pub(crate) fn adjust_hint_generator<L2>(
1134 self,
1135 ) -> ConstrainedDelaunayTriangulation<V, DE, UE, F, L2>
1136 where
1137 L2: HintGenerator<<V as HasPosition>::Scalar>,
1138 {
1139 let hint_generator = L2::initialize_from_triangulation(&self);
1140 let (dcel, _, num_constraints) = self.into_parts();
1141 ConstrainedDelaunayTriangulation::from_parts(dcel, hint_generator, num_constraints)
1142 }
1143}
1144
1145impl<V, DE, UE, F, L> ConstrainedDelaunayTriangulation<V, DE, UE, F, L>
1146where
1147 V: HasPosition,
1148 V::Scalar: Float,
1149 DE: Default,
1150 UE: Default,
1151 F: Default,
1152 L: HintGenerator<<V as HasPosition>::Scalar>,
1153{
1154 #[doc = include_str!("../images/add_constraint_and_split_initial.svg")]
1172 #[doc = include_str!("../images/add_constraint_and_split_added.svg")]
1176 pub fn add_constraint_and_split<C>(
1228 &mut self,
1229 from: FixedVertexHandle,
1230 to: FixedVertexHandle,
1231 vertex_constructor: C,
1232 ) -> Vec<FixedDirectedEdgeHandle>
1233 where
1234 C: Fn(Point2<<V as HasPosition>::Scalar>) -> V,
1235 {
1236 let r = &|p: Point2<f64>| {
1237 let [x, y] = [p.x, p.y].map(|s| {
1238 <<V as HasPosition>::Scalar as NumCast>::from(s)
1239 .unwrap_or_else(|| (s as f32).into())
1240 });
1241 vertex_constructor(Point2::new(x, y))
1242 };
1243
1244 self.resolve_splitting_constraint_request(from, to, Some(r))
1245 }
1246}
1247
1248enum ConflictRegionEnd {
1251 Existing(FixedVertexHandle),
1253 EdgeOverlap(FixedDirectedEdgeHandle),
1258}
1259
1260impl core::fmt::Debug for ConflictRegionEnd {
1261 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1262 match self {
1263 Existing(handle) => write!(f, "Existing({handle:?})"),
1264 EdgeOverlap(edge) => write!(f, "EdgeOverlap({edge:?})"),
1265 }
1266 }
1267}
1268
1269struct InitialConflictRegion {
1274 conflict_edges: Vec<FixedDirectedEdgeHandle>,
1275 group_end: ConflictRegionEnd,
1276}
1277
1278impl core::fmt::Debug for InitialConflictRegion {
1279 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1280 f.debug_struct("InitialConflictRegion")
1281 .field("conflict_edges", &self.conflict_edges)
1282 .field("group_end", &self.group_end)
1283 .finish()
1284 }
1285}
1286
1287pub fn get_edge_intersections<S: SpadeNum + Float>(
1288 p1: Point2<S>,
1289 p2: Point2<S>,
1290 p3: Point2<S>,
1291 p4: Point2<S>,
1292) -> Point2<S> {
1293 let p1 = p1.to_f64();
1294 let p2 = p2.to_f64();
1295 let p3 = p3.to_f64();
1296 let p4 = p4.to_f64();
1297
1298 let a1 = p2.y - p1.y;
1299 let b1 = p1.x - p2.x;
1300 let c1 = a1 * p1.x + b1 * p1.y;
1301
1302 let a2 = p4.y - p3.y;
1303 let b2 = p3.x - p4.x;
1304 let c2 = a2 * p3.x + b2 * p3.y;
1305
1306 let determinant = a1 * b2 - a2 * b1;
1307
1308 let x: f64;
1309 let y: f64;
1310 if determinant == 0.0 {
1311 x = f64::infinity();
1312 y = f64::infinity();
1313 } else {
1314 x = (b2 * c1 - b1 * c2) / determinant;
1315 y = (a1 * c2 - a2 * c1) / determinant;
1316 }
1317
1318 [x, y]
1319 .map(|s| <S as NumCast>::from(s).unwrap_or_else(|| (s as f32).into()))
1320 .into()
1321}
1322
1323#[cfg(test)]
1324mod test {
1325 use std::collections::HashSet;
1326
1327 use approx::assert_abs_diff_eq;
1328 use proptest::prelude::*;
1329
1330 use alloc::{vec, vec::Vec};
1331
1332 use rand::distr::{Distribution, Uniform};
1333 use rand::seq::IndexedRandom as _;
1334 use rand::{Rng, SeedableRng};
1335
1336 use crate::delaunay_core::{FixedDirectedEdgeHandle, TriangulationExt};
1337 use crate::handles::FixedVertexHandle;
1338 use crate::test_utilities::*;
1339 use crate::{DelaunayTriangulation, InsertionError, Point2, Triangulation};
1340
1341 use super::ConstrainedDelaunayTriangulation;
1342
1343 type Cdt = ConstrainedDelaunayTriangulation<Point2<f64>>;
1344 type Delaunay = DelaunayTriangulation<Point2<f64>>;
1345
1346 #[test]
1347 fn test_into() -> Result<(), InsertionError> {
1348 let points = random_points_with_seed(100, SEED);
1349 let delaunay = DelaunayTriangulation::<_>::bulk_load(points.clone())?;
1350
1351 let cdt = Cdt::from(delaunay.clone());
1352
1353 assert_eq!(delaunay.num_vertices(), cdt.num_vertices());
1354 assert_eq!(delaunay.num_directed_edges(), cdt.num_directed_edges());
1355 assert_eq!(cdt.num_constraints, 0);
1356
1357 Ok(())
1358 }
1359
1360 #[test]
1361 fn test_add_same_from_and_to_constraint() -> Result<(), InsertionError> {
1362 let mut cdt = Cdt::new();
1363 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1364 cdt.insert(Point2::new(2.0, 2.0))?;
1365 cdt.insert(Point2::new(1.0, 2.0))?;
1366
1367 assert!(!cdt.add_constraint(v0, v0));
1368 assert!(cdt.try_add_constraint(v0, v0).is_empty());
1369
1370 let new_point = Point2::new(3.1, 2.0);
1371 assert!(!cdt.add_constraint_edge(new_point, new_point)?);
1372
1373 assert_eq!(0, cdt.num_constraints());
1374 assert_eq!(4, cdt.num_vertices());
1375
1376 cdt.cdt_sanity_check();
1377
1378 Ok(())
1379 }
1380
1381 #[test]
1382 fn test_add_single_simple_constraint() -> Result<(), InsertionError> {
1383 let mut cdt = Cdt::new();
1384 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1385 let v1 = cdt.insert(Point2::new(2.0, 2.0))?;
1386 let v2 = cdt.insert(Point2::new(1.0, 0.5))?;
1387 let v3 = cdt.insert(Point2::new(0.5, 1.0))?;
1388 assert!(cdt.get_edge_from_neighbors(v0, v1).is_none());
1389 assert!(cdt.get_edge_from_neighbors(v2, v3).is_some());
1390
1391 assert!(cdt.add_constraint(v1, v0));
1392 assert!(!cdt.add_constraint(v0, v1));
1393 let edge = cdt
1394 .get_edge_from_neighbors(v0, v1)
1395 .expect("Expected constraint edge")
1396 .as_undirected()
1397 .fix();
1398 assert!(cdt.get_edge_from_neighbors(v2, v3).is_none());
1399 assert!(cdt.is_constraint_edge(edge));
1400 cdt.cdt_sanity_check();
1401 Ok(())
1402 }
1403
1404 #[test]
1405 fn test_existing_edge_constraint() -> Result<(), InsertionError> {
1406 let mut cdt = Cdt::new();
1407 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1408 let v1 = cdt.insert(Point2::new(2.0, 2.0))?;
1409 let v2 = cdt.insert(Point2::new(1.0, 0.0))?;
1410 assert!(cdt.add_constraint(v0, v1));
1411 assert!(cdt.add_constraint(v0, v2));
1412 assert!(cdt.add_constraint(v1, v2));
1413 for edge in cdt.undirected_edges() {
1414 assert!(cdt.is_constraint_edge(edge.fix()));
1415 }
1416 assert!(!cdt.add_constraint(v1, v0));
1417 assert!(!cdt.add_constraint(v1, v2));
1418 assert_eq!(cdt.num_constraints, 3);
1419 Ok(())
1420 }
1421
1422 #[test]
1423 fn test_mid_overlapping_constraint() -> Result<(), InsertionError> {
1424 let mut cdt = Cdt::new();
1425 let v0 = cdt.insert(Point2::new(0.0, 0.5))?;
1426 let v1 = cdt.insert(Point2::new(2.0, 0.5))?;
1427 let v2 = cdt.insert(Point2::new(3.0, 0.5))?;
1428 let v3 = cdt.insert(Point2::new(5.0, 0.5))?;
1429 cdt.insert(Point2::new(1.0, 1.0))?;
1430 cdt.insert(Point2::new(1.0, 0.0))?;
1431 cdt.insert(Point2::new(3.0, 1.0))?;
1432 cdt.insert(Point2::new(3.0, 0.0))?;
1433 assert!(cdt.get_edge_from_neighbors(v1, v2).is_some());
1434 let mut copy = cdt.clone();
1435 assert!(cdt.add_constraint(v0, v3));
1436 assert_eq!(cdt.num_constraints(), 3);
1437
1438 copy.add_constraint(v2, v3);
1439 assert_eq!(copy.num_constraints(), 1);
1440 copy.add_constraint(v0, v3);
1441 assert_eq!(copy.num_constraints(), 3);
1442 Ok(())
1443 }
1444
1445 #[test]
1446 fn test_add_single_complex_constraint() -> Result<(), InsertionError> {
1447 let mut cdt = Cdt::new();
1448 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1449 cdt.insert(Point2::new(1.0, 0.0))?;
1450 cdt.insert(Point2::new(0.0, 1.0))?;
1451 cdt.insert(Point2::new(2.0, 1.0))?;
1452
1453 let v1 = cdt.insert(Point2::new(2.0, 2.0))?;
1454 assert!(cdt.get_edge_from_neighbors(v0, v1).is_none());
1455 cdt.add_constraint(v0, v1);
1456 cdt.cdt_sanity_check();
1457 let edge = cdt
1458 .get_edge_from_neighbors(v0, v1)
1459 .expect("Expected constraint edge")
1460 .fix()
1461 .as_undirected();
1462 assert!(cdt.is_constraint_edge(edge));
1463 Ok(())
1464 }
1465
1466 #[test]
1467 fn test_add_single_constraint() -> Result<(), InsertionError> {
1468 let points = random_points_with_seed(1000, SEED);
1469 let mut cdt = Cdt::new();
1470 assert_eq!(cdt.num_constraints(), 0);
1471 let mut handles = Vec::new();
1472 cdt.cdt_sanity_check();
1473 for point in points.into_iter() {
1474 handles.push(cdt.insert(point)?);
1475 }
1476 cdt.add_constraint(handles[40], handles[200]);
1477 assert_eq!(cdt.num_constraints(), 1);
1478 cdt.cdt_sanity_check();
1479 Ok(())
1480 }
1481
1482 #[test]
1483 fn test_add_border_constraint() -> Result<(), InsertionError> {
1484 let points = random_points_with_seed(1000, SEED);
1485 let mut cdt = Cdt::new();
1486 let mut max_y = -f64::MAX;
1487 for point in points {
1488 max_y = max_y.max(point.y);
1489 cdt.insert(point)?;
1490 }
1491 let v0 = cdt.insert(Point2::new(-20., max_y + 10.))?;
1492 let v1 = cdt.insert(Point2::new(20., max_y + 10.))?;
1493 cdt.add_constraint(v0, v1);
1494 assert_eq!(cdt.num_constraints(), 1);
1495 cdt.cdt_sanity_check();
1496 Ok(())
1497 }
1498
1499 #[test]
1500 fn test_add_multiple_constraints_overlapping() -> Result<(), InsertionError> {
1501 test_add_multiple_constraints(true)
1502 }
1503
1504 #[test]
1505 fn test_add_multiple_constraints_non_overlapping() -> Result<(), InsertionError> {
1506 test_add_multiple_constraints(false)
1507 }
1508
1509 fn test_add_multiple_constraints(overlapping: bool) -> Result<(), InsertionError> {
1510 const RANGE: f64 = 10.;
1511 let seed = if overlapping { SEED } else { SEED2 };
1512 let points = random_points_in_range(RANGE, 1000, seed);
1513 let mut cdt = Cdt::new();
1514 for point in points {
1515 cdt.insert(point)?;
1516 }
1517 let seed = if overlapping { SEED } else { SEED2 };
1518 let delaunay_points = random_points_in_range(RANGE * 0.9, 80, seed);
1519 let mut d = Delaunay::new();
1521 for p in delaunay_points {
1522 d.insert(p)?;
1523 }
1524 let mut used_vertices = HashSet::new();
1525
1526 let mut inserted_constraints = Vec::new();
1527 for v in d.vertices() {
1528 if overlapping || used_vertices.insert(v.fix()) {
1531 let out_edge = v.out_edge().unwrap();
1532 let to = out_edge.to();
1533
1534 used_vertices.insert(to.fix());
1535
1536 let h0 = cdt.insert(v.position())?;
1537 let h1 = cdt.insert(to.position())?;
1538
1539 if cdt.add_constraint(h0, h1) {
1540 inserted_constraints.push((h0, h1));
1541 }
1542 cdt.cdt_sanity_check();
1543
1544 assert_eq!(cdt.num_constraints(), inserted_constraints.len());
1545 }
1546 }
1547 for (from, to) in inserted_constraints {
1549 assert!(cdt.exists_constraint(from, to));
1550 }
1551 cdt.cdt_sanity_check();
1552 Ok(())
1553 }
1554
1555 #[test]
1556 fn crash_case() -> Result<(), InsertionError> {
1557 let mut cdt = Cdt::new();
1558 cdt.insert(Point2::new(-8.403036273981348, -0.2248814041797189))?;
1559 cdt.insert(Point2::new(-8.353215494321136, 0.6088667888877364))?;
1560 cdt.insert(Point2::new(-7.811923439447166, -0.20003314976217013))?;
1561 cdt.insert(Point2::new(-7.710431174668773, 0.40691184742787456))?;
1562
1563 let v0 = cdt.insert(Point2::new(-8.907731924022768, 1.7433952434737847))?;
1564 let v1 = cdt.insert(Point2::new(-7.899415172394501, -1.4867902598716558))?;
1565 cdt.cdt_sanity_check();
1566 cdt.add_constraint(v0, v1);
1567 cdt.cdt_sanity_check();
1568 Ok(())
1569 }
1570
1571 #[test]
1572 fn test_split_constraint() -> Result<(), InsertionError> {
1573 let mut cdt = Cdt::new();
1574 cdt.insert(Point2::new(0.0, 0.0))?;
1575 cdt.insert(Point2::new(1.0, 0.0))?;
1576 cdt.insert(Point2::new(0.0, 1.0))?;
1577 let v0 = cdt.insert(Point2::new(0.0, 0.5))?;
1578 let v_last = cdt.insert(Point2::new(1.0, 0.5))?;
1579 cdt.add_constraint(v0, v_last);
1580 assert_eq!(cdt.num_constraints(), 1);
1581 let v1 = cdt.insert(Point2::new(0.25, 0.5))?;
1583 assert_eq!(cdt.num_constraints(), 2);
1584 let v2 = cdt.insert(Point2::new(0.75, 0.5))?;
1585 assert_eq!(cdt.num_constraints(), 3);
1586 assert!(cdt.exists_constraint(v0, v1));
1587 assert!(cdt.exists_constraint(v1, v2));
1588 assert!(cdt.exists_constraint(v2, v_last));
1589 cdt.cdt_sanity_check();
1590 Ok(())
1591 }
1592
1593 #[test]
1594 fn test_simple_retriangulation() -> Result<(), InsertionError> {
1595 let mut cdt = Cdt::new();
1596 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1597 cdt.insert(Point2::new(1.0, 0.25))?;
1598 cdt.insert(Point2::new(1.0, -0.25))?;
1599 let v3 = cdt.insert(Point2::new(2.0, 0.75))?;
1600 let v4 = cdt.insert(Point2::new(2.5, -0.3))?;
1601 cdt.insert(Point2::new(2.75, 0.75))?;
1602 cdt.insert(Point2::new(3.0, 0.75))?;
1603 cdt.insert(Point2::new(4.0, 0.25))?;
1604 cdt.insert(Point2::new(4.0, -0.25))?;
1605 let v7 = cdt.insert(Point2::new(5.0, 0.0))?;
1606 assert!(cdt.get_edge_from_neighbors(v3, v4).is_some());
1607 cdt.add_constraint(v0, v7);
1608 assert!(cdt.get_edge_from_neighbors(v0, v7).is_some());
1609 assert!(cdt.get_edge_from_neighbors(v3, v4).is_none());
1610
1611 cdt.cdt_sanity_check();
1612 Ok(())
1613 }
1614
1615 #[test]
1616 fn test_add_constraint_over_point() -> Result<(), InsertionError> {
1617 let mut cdt = Cdt::new();
1618 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1619 let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
1620 let v2 = cdt.insert(Point2::new(2.0, 0.0))?;
1621 cdt.insert(Point2::new(0.0, 1.0))?;
1622 cdt.add_constraint(v0, v2);
1623 assert_eq!(cdt.num_constraints(), 2);
1624 assert!(cdt.exists_constraint(v0, v1));
1625 assert!(cdt.exists_constraint(v1, v2));
1626 cdt.cdt_sanity_check();
1627 Ok(())
1628 }
1629
1630 fn test_cdt() -> Result<Cdt, InsertionError> {
1631 let mut cdt = Cdt::new();
1632 let v0 = cdt.insert(Point2::new(1.0, 0.0))?;
1633 let v1 = cdt.insert(Point2::new(0.0, 1.0))?;
1634 cdt.insert(Point2::new(0.0, 0.0))?;
1635 cdt.insert(Point2::new(1.0, 1.0))?;
1636 cdt.add_constraint(v0, v1);
1637 Ok(cdt)
1638 }
1639
1640 #[test]
1641 fn test_check_intersects_constraint_edge() -> Result<(), InsertionError> {
1642 let cdt = test_cdt()?;
1643 let from = Point2::new(0.2, 0.2);
1644 let to = Point2::new(0.6, 0.7);
1645 assert!(cdt.intersects_constraint(from, to));
1646 assert!(cdt.intersects_constraint(to, from));
1647 let to = Point2::new(-0.5, 0.2);
1648 assert!(!cdt.intersects_constraint(from, to));
1649 let from = Point2::new(0.5, 0.5);
1650 assert!(cdt.intersects_constraint(from, to));
1651 assert!(cdt.intersects_constraint(to, from));
1652 Ok(())
1653 }
1654
1655 #[test]
1656 fn test_add_constraint_degenerate() -> Result<(), InsertionError> {
1657 let mut cdt = Cdt::new();
1658 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1659 let v1 = cdt.insert(Point2::new(0.0, 1.0))?;
1660 assert!(cdt.add_constraint(v0, v1));
1661 assert!(!cdt.add_constraint(v1, v0));
1662 assert_eq!(cdt.num_constraints(), 1);
1663 let mut cdt = Cdt::new();
1664 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1665 let v1 = cdt.insert(Point2::new(0.0, 2.0))?;
1666 cdt.insert(Point2::new(0.0, 1.0))?;
1667 assert!(cdt.add_constraint(v0, v1));
1668 assert_eq!(cdt.num_constraints(), 2);
1669 Ok(())
1670 }
1671
1672 fn random_points_on_line<R>(
1673 range: i64,
1674 num_points: usize,
1675 rng: &mut R,
1676 line_dir: Point2<f64>,
1677 ) -> Vec<Point2<f64>>
1678 where
1679 R: Rng,
1680 {
1681 let mut result = Vec::with_capacity(num_points);
1682 let range = Uniform::new(-range, range).unwrap();
1683 for _ in 0..num_points {
1684 let factor = range.sample(rng);
1685 result.push(line_dir.mul(factor as f64));
1686 }
1687 result
1688 }
1689
1690 #[test]
1691 fn fuzz_test_on_line() -> Result<(), InsertionError> {
1692 let seed = SEED;
1695 const RANGE: i64 = 10000;
1696 const NUM_POINTS: usize = 1000;
1697 let mut rng = rand::rngs::StdRng::from_seed(*seed);
1698 let points = random_points_on_line(RANGE, NUM_POINTS, &mut rng, Point2::new(1.0, 0.0));
1699 let mut cdt = ConstrainedDelaunayTriangulation::<_>::new();
1700 for ps in points.chunks(2) {
1701 let from = ps[0];
1702 let to = ps[1];
1703 let from = cdt.insert(from)?;
1704 let to = cdt.insert(to)?;
1705 let should_add_constraint: bool = rng.random();
1706 if from != to && should_add_constraint {
1707 cdt.add_constraint(from, to);
1708 }
1709
1710 cdt.cdt_sanity_check();
1711 }
1712 Ok(())
1713 }
1714
1715 #[test]
1716 fn fuzz_test_on_grid() -> anyhow::Result<()> {
1717 use rand::seq::SliceRandom;
1718 let seed = SEED;
1721 let mut points = Vec::with_capacity((RANGE * RANGE) as usize);
1722 const RANGE: i64 = 30;
1723 const NUM_CONSTRAINTS: usize = 2000;
1724 for x in -RANGE..RANGE {
1725 for y in -RANGE..RANGE {
1726 points.push(Point2::new(x as f64, y as f64));
1727 }
1728 }
1729 let mut rng = rand::rngs::StdRng::from_seed(*seed);
1730 points.shuffle(&mut rng);
1731 let mut cdt = Cdt::new();
1732 for p in points {
1733 cdt.insert(p)?;
1734 }
1735 let range = Uniform::new(-RANGE, RANGE)?;
1736 let directions_and_offset = [
1737 (Point2::new(1.0, 0.0), Point2::new(0.0, 1.0)),
1738 (Point2::new(0.0, 1.0), Point2::new(1.0, 0.0)),
1739 (Point2::new(1.0, 1.0), Point2::new(0.0, 0.0)),
1740 ];
1741 for _ in 0..NUM_CONSTRAINTS {
1742 let &(direction, offset) = directions_and_offset.choose(&mut rng).unwrap();
1743 let factor1 = range.sample(&mut rng);
1744 let factor2 = range.sample(&mut rng);
1745 let p1 = offset.add(direction.mul(factor1 as f64));
1746 let p2 = offset.add(direction.mul(factor2 as f64));
1747 if p1 != p2 {
1748 cdt.add_constraint_edge(p1, p2)?;
1749 }
1750 }
1751 cdt.cdt_sanity_check();
1752 Ok(())
1753 }
1754
1755 #[test]
1756 #[should_panic]
1757 fn test_panic_when_intersecting_a_constraint_edge() {
1758 let mut cdt = Cdt::new();
1759 let v0 = cdt.insert(Point2::new(0.0, 0.0)).unwrap();
1760 let v1 = cdt.insert(Point2::new(1.0, 0.0)).unwrap();
1761 cdt.add_constraint(v0, v1);
1762 cdt.add_constraint(v0, v1);
1763 cdt.add_constraint_edge(Point2::new(0.0, 0.0), Point2::new(1.0, 0.0))
1764 .unwrap();
1765 cdt.add_constraint_edge(Point2::new(0.5, 0.5), Point2::new(0.5, -0.5))
1766 .unwrap();
1767 }
1768
1769 #[test]
1770 #[should_panic]
1771 fn test_panic_when_intersecting_a_complex_constraint_edge() {
1772 let mut cdt = Cdt::new();
1773 let v0 = cdt.insert(Point2::new(0.5, 2.0)).unwrap();
1774 cdt.insert(Point2::new(0.0, 1.5)).unwrap();
1775 cdt.insert(Point2::new(1.0, 1.5)).unwrap();
1776 cdt.add_constraint_edge(Point2::new(0.0, 0.5), Point2::new(1.0, 0.5))
1777 .unwrap();
1778 let v1 = cdt.insert(Point2::new(0.5, 0.0)).unwrap();
1779
1780 cdt.add_constraint(v0, v1);
1781 }
1782
1783 #[test]
1784 fn test_cdt_remove_degenerate() -> Result<(), InsertionError> {
1785 let mut cdt = Cdt::new();
1786 let v0 = cdt.insert(Point2::new(0.0, 0.0))?;
1787 let v1 = cdt.insert(Point2::new(1.0, 0.0))?;
1788 let v2 = cdt.insert(Point2::new(0.0, 1.0))?;
1789 cdt.add_constraint(v0, v1);
1790 cdt.add_constraint(v1, v2);
1791 cdt.add_constraint(v2, v0);
1792 assert_eq!(cdt.num_constraints(), 3);
1793 cdt.remove(v1);
1794 assert_eq!(cdt.num_constraints(), 1);
1795 cdt.cdt_sanity_check();
1796 Ok(())
1797 }
1798
1799 #[test]
1800 fn test_crash_scenario() -> Result<(), InsertionError> {
1801 let mut cdt = Cdt::new();
1802 for point in get_points().iter().cloned() {
1803 cdt.insert(point)?;
1804 }
1805
1806 let from = cdt.insert(Point2::new(3.2348222581121586, -8.136734693290444))?;
1807 cdt.cdt_sanity_check();
1808 let to = cdt.insert(Point2::new(-8.839844309691154, -8.930685085211245))?;
1809 cdt.cdt_sanity_check();
1810
1811 cdt.add_constraint(from, to);
1812 cdt.cdt_sanity_check();
1813
1814 Ok(())
1815 }
1816
1817 fn get_points() -> Vec<Point2<f64>> {
1818 vec![
1819 Point2::new(-3.947938514986289, -8.016680534876258),
1820 Point2::new(-4.016029045366132, -9.680855465455608),
1821 Point2::new(-4.46653326962287, -8.462568264351527),
1822 Point2::new(-7.033691993749462, -8.88072731817851),
1823 Point2::new(-6.058360215097096, -8.644637388990939),
1824 ]
1825 }
1826
1827 #[test]
1828 fn test_add_constraint_edges() -> Result<(), InsertionError> {
1829 for is_closed in [true, false] {
1830 let mut cdt = Cdt::new();
1831
1832 const NUM_VERTICES: usize = 51;
1833 let vertices = (0..NUM_VERTICES).map(|i| {
1834 let angle = core::f64::consts::PI * 2.0 * i as f64 / NUM_VERTICES as f64;
1835 let (sin, cos) = angle.sin_cos();
1836 Point2::new(sin, cos)
1837 });
1838
1839 cdt.add_constraint_edges(vertices, is_closed)?;
1840
1841 if is_closed {
1842 assert_eq!(NUM_VERTICES, cdt.num_constraints());
1843 } else {
1844 assert_eq!(NUM_VERTICES - 1, cdt.num_constraints());
1845 }
1846
1847 cdt.cdt_sanity_check();
1848 }
1849
1850 Ok(())
1851 }
1852
1853 #[test]
1854 fn test_add_constraint_edges_empty() -> Result<(), InsertionError> {
1855 let mut cdt = Cdt::new();
1856
1857 cdt.add_constraint_edges(core::iter::empty(), false)?;
1858 cdt.add_constraint_edges(core::iter::empty(), true)?;
1859
1860 assert_eq!(cdt.num_vertices(), 0);
1861 assert_eq!(cdt.num_constraints(), 0);
1862
1863 Ok(())
1864 }
1865
1866 #[test]
1867 fn test_add_constraint_edges_single() -> Result<(), InsertionError> {
1868 let mut cdt = Cdt::new();
1869
1870 cdt.add_constraint_edges([Point2::new(1.0, 1.0)], true)?;
1871 cdt.add_constraint_edges([Point2::new(2.0, 3.0)], false)?;
1872
1873 assert_eq!(cdt.num_vertices(), 2);
1874 assert_eq!(cdt.num_constraints(), 0);
1875
1876 Ok(())
1877 }
1878
1879 #[test]
1880 fn test_add_constraint_edges_duplicate() -> Result<(), InsertionError> {
1881 let mut cdt = Cdt::new();
1882 let point = Point2::new(0.0, 1.0);
1883 cdt.add_constraint_edges([point, point], true)?;
1884 cdt.add_constraint_edges([point, point], false)?;
1885 cdt.add_constraint_edges([point, point, point], true)?;
1886 cdt.add_constraint_edges([point, point, point], false)?;
1887
1888 assert_eq!(cdt.num_vertices(), 1);
1889 assert_eq!(cdt.num_constraints(), 0);
1890
1891 cdt.cdt_sanity_check();
1892 Ok(())
1893 }
1894
1895 #[test]
1896 fn test_clear() -> Result<(), InsertionError> {
1897 let mut cdt = test_cdt()?;
1898 cdt.clear();
1899
1900 assert_eq!(cdt.num_constraints(), 0);
1901 assert_eq!(cdt.num_all_faces(), 1);
1902 assert_eq!(cdt.num_vertices(), 0);
1903 assert_eq!(cdt.num_directed_edges(), 0);
1904 Ok(())
1905 }
1906
1907 #[test]
1908 fn test_cdt_edge_split_degenerate() -> Result<(), InsertionError> {
1909 let mut cdt = Cdt::new();
1910 cdt.add_constraint_edge(Point2::new(-10.0, -10.0), Point2::new(20.0, -10.0))?;
1911 cdt.insert(Point2::new(0.0, -10.0))?;
1912
1913 assert_eq!(cdt.num_constraints(), 2);
1914
1915 Ok(())
1916 }
1917
1918 #[test]
1919 fn infinite_loop_bug() -> Result<(), InsertionError> {
1920 let mut triangulation = Cdt::default();
1922
1923 let start = Point2::new(-21.296192169189453, 9.872323036193848);
1924 let edges = [
1925 (
1926 Point2::new(-20.926544189453125, 16.53529167175293),
1927 Point2::new(-27.772645950317383, 4.197676658630371),
1928 ),
1929 (
1930 Point2::new(-20.03745460510254, 12.93730354309082),
1931 Point2::new(-20.930097579956055, 11.93786907196045),
1932 ),
1933 (
1934 Point2::new(-15.576859474182129, 8.772907257080078),
1935 Point2::new(-22.373262405395508, 12.348699569702148),
1936 ),
1937 (
1938 Point2::new(-10.038422584533691, 5.663522243499756),
1939 Point2::new(-16.382625579833984, 9.09498119354248),
1940 ),
1941 (
1942 Point2::new(0.0, 0.0),
1943 Point2::new(-13.11422061920166, 7.30709171295166),
1944 ),
1945 (
1946 Point2::new(-19.230497360229492, -3.7645812034606934),
1947 Point2::new(-7.411926746368408, 3.486957311630249),
1948 ),
1949 (
1950 Point2::new(-25.072885513305664, -9.239323616027832),
1951 Point2::new(-19.462360382080078, -1.621320366859436),
1952 ),
1953 (
1954 Point2::new(-32.41080856323242, -13.72575855255127),
1955 Point2::new(-22.58626365661621, -2.076631784439087),
1956 ),
1957 (
1958 Point2::new(-32.41080856323242, -13.72575855255127),
1959 Point2::new(-25.57504653930664, -4.952820301055908),
1960 ),
1961 (
1962 Point2::new(-33.08932113647461, 0.31093916296958923),
1963 Point2::new(-25.955543518066406, 0.18878456950187683),
1964 ),
1965 ];
1966
1967 for (p1, p2) in edges {
1968 let p1 = triangulation.insert(p1)?;
1969 let p2 = triangulation.insert(p2)?;
1970 assert!(triangulation.can_add_constraint(p1, p2));
1971 triangulation.add_constraint(p1, p2);
1972 }
1973
1974 triangulation.insert(start)?;
1975 Ok(())
1976 }
1977
1978 #[test]
1979 pub fn infinite_loop_2() -> Result<(), InsertionError> {
1980 let lines = [
1981 [
1982 Point2::new(0.9296344883099084, 0.03071359966930065),
1983 Point2::new(0.26031306872107085, 0.34491289915959455),
1984 ],
1985 [
1986 Point2::new(0.7384289920396423, 0.4981747664368982),
1987 Point2::new(0.06543525273452533, 0.34139896206401854),
1988 ],
1989 [
1990 Point2::new(0.9535295221136963, 0.9114305148801416),
1991 Point2::new(0.8306091165247367, 0.08959389670590667),
1992 ],
1993 ];
1994
1995 let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::new();
1996
1997 for [a, b] in lines {
1998 let a = cdt.insert(a)?;
1999 let b = cdt.insert(b)?;
2000
2001 cdt.add_constraint_and_split(a, b, |v| v);
2002 }
2003
2004 cdt.insert(Point2::new(0.5138795569454557, 0.3186272217036502))?;
2006 Ok(())
2007 }
2008
2009 fn get_cdt_for_try_add_constraint() -> Result<Cdt, InsertionError> {
2010 let vertices = vec![
2011 Point2::new(0.0, -10.0),
2012 Point2::new(76.0, 0.0),
2013 Point2::new(20.0, 20.0),
2014 Point2::new(20.0, -30.0),
2015 Point2::new(45.0, 25.0),
2016 Point2::new(32.0, -35.0),
2017 Point2::new(60.0, 20.0),
2018 Point2::new(60.0, -30.0),
2019 Point2::new(50.0, -34.0),
2020 ];
2021
2022 Cdt::bulk_load_cdt(vertices, vec![[3, 2], [5, 4], [7, 6]])
2023 }
2024
2025 #[test]
2026 fn get_try_cdt_for_duplicate_vertices() -> Result<(), InsertionError> {
2027 let vertices = vec![
2028 Point2::new(0.0, -10.0),
2029 Point2::new(76.0, 0.0),
2030 Point2::new(76.0, 0.0), Point2::new(20.0, -30.0),
2032 Point2::new(45.0, 25.0),
2033 Point2::new(32.0, -35.0),
2034 Point2::new(60.0, 20.0),
2035 Point2::new(60.0, -30.0),
2036 Point2::new(50.0, -34.0),
2037 ];
2038 let mut conflicting_edges = Vec::new();
2039 let cdt = Cdt::try_bulk_load_cdt(vertices, vec![[3, 2], [5, 4], [7, 6]], |e| {
2040 conflicting_edges.push(e)
2041 })?;
2042 assert_eq!(&conflicting_edges, &[[5, 6,], [3, 4,]]);
2044 assert_eq!(cdt.num_constraints, 1);
2045 Ok(())
2046 }
2047
2048 #[test]
2049 fn test_single_split() -> Result<(), InsertionError> {
2050 let vertices = vec![
2051 Point2::new(-1.0, 0.0),
2052 Point2::new(1.0, 0.0),
2053 Point2::new(0.0, -1.0),
2054 Point2::new(0.0, 1.0),
2055 ];
2056
2057 let mut cdt = Cdt::bulk_load_cdt(vertices, vec![[2, 3]])?;
2058
2059 let initial_num_vertices = cdt.num_vertices();
2060 let from = FixedVertexHandle::from_index(0);
2061 let to = FixedVertexHandle::from_index(1);
2062
2063 let edges = cdt.add_constraint_and_split(from, to, |v| v);
2064
2065 assert_eq!(cdt.num_vertices(), initial_num_vertices + 1);
2066 assert_eq!(edges.len(), 2);
2067 check_returned_edges(&cdt, &edges, from, to);
2068
2069 Ok(())
2070 }
2071
2072 #[test]
2073 fn test_multiple_splits() -> Result<(), InsertionError> {
2074 let mut cdt = get_cdt_for_try_add_constraint()?;
2075
2076 let initial_num_vertices = cdt.num_vertices();
2077 let from = FixedVertexHandle::from_index(0);
2078 let to = FixedVertexHandle::from_index(1);
2079
2080 let edges = cdt.add_constraint_and_split(from, to, |v| v);
2081
2082 assert_eq!(cdt.num_vertices(), initial_num_vertices + 3);
2084 assert_eq!(edges.len(), 4);
2085 check_returned_edges(&cdt, &edges, from, to);
2086
2087 Ok(())
2088 }
2089
2090 #[test]
2091 fn test_try_add_constraint() -> Result<(), InsertionError> {
2092 let mut cdt = get_cdt_for_try_add_constraint()?;
2093
2094 let initial_num_vertices = cdt.num_vertices();
2095 let initial_num_constraints = cdt.num_constraints();
2096 let from = FixedVertexHandle::from_index(0);
2097 let to = FixedVertexHandle::from_index(1);
2098
2099 let edges = cdt.try_add_constraint(from, to);
2101 assert_eq!(edges, Vec::new());
2102 assert_eq!(cdt.num_vertices(), initial_num_vertices);
2103 assert_eq!(cdt.num_constraints(), initial_num_constraints);
2104
2105 let from = FixedVertexHandle::from_index(2);
2106 let to = FixedVertexHandle::from_index(3);
2107
2108 let edges = cdt.try_add_constraint(from, to);
2110 assert_eq!(edges.len(), 1);
2111
2112 Ok(())
2113 }
2114
2115 #[test]
2116 fn test_remove_vertex_respects_constraints() -> Result<(), InsertionError> {
2117 let mut triangulation = ConstrainedDelaunayTriangulation::<Point2<f32>>::bulk_load_cdt(
2118 vec![
2119 Point2::new(-1.0, 0.0),
2120 Point2::new(0.0, 0.0),
2121 Point2::new(0.0, 1.0),
2122 Point2::new(1.0, 1.0),
2123 ],
2124 vec![[0, 1], [1, 2], [2, 3]],
2125 )?;
2126
2127 let mut copy = triangulation.clone();
2128
2129 triangulation.remove(FixedVertexHandle::from_index(1));
2130 triangulation.cdt_sanity_check();
2131
2132 copy.locate_and_remove(Point2::new(0.0, 0.0));
2133 copy.cdt_sanity_check();
2134
2135 Ok(())
2136 }
2137
2138 #[test]
2139 fn test_remove_constraint_edge() -> Result<(), InsertionError> {
2140 let mut cdt = get_cdt_for_try_add_constraint()?;
2141 for edge in cdt.fixed_undirected_edges() {
2142 cdt.remove_constraint_edge(edge);
2143 }
2144 assert_eq!(cdt.num_constraints, 0);
2145 cdt.sanity_check();
2146
2147 let added_edges = cdt.try_add_constraint(
2148 FixedVertexHandle::from_index(0),
2149 FixedVertexHandle::from_index(1),
2150 );
2151 assert_eq!(added_edges.len(), 1);
2152
2153 assert!(cdt.remove_constraint_edge(added_edges.first().unwrap().as_undirected()));
2154 assert_eq!(cdt.num_constraints, 0);
2155 cdt.sanity_check();
2156
2157 Ok(())
2158 }
2159
2160 #[test]
2161 fn edge_intersection_precision_test_2() -> Result<(), InsertionError> {
2162 let edges = [
2163 [
2164 Point2 {
2165 x: 18.69314193725586,
2166 y: 19.589109420776367,
2167 },
2168 Point2 {
2169 x: 18.69314193725586,
2170 y: 20.40362548828125,
2171 },
2172 ],
2173 [
2174 Point2 {
2175 x: 19.507659912109375,
2176 y: 20.40362548828125,
2177 },
2178 Point2 {
2179 x: 17.878625869750977,
2180 y: 18.774595260620117,
2181 },
2182 ],
2183 [
2184 Point2 {
2185 x: 20.322175979614258,
2186 y: 21.218143463134766,
2187 },
2188 Point2 {
2189 x: 15.435077667236328,
2190 y: 16.331045150756836,
2191 },
2192 ],
2193 ];
2194 let mut cdt: ConstrainedDelaunayTriangulation<Point2<f64>> =
2195 ConstrainedDelaunayTriangulation::new();
2196 for edge in edges {
2197 let point_a = cdt.insert(edge[0])?;
2198 let point_b = cdt.insert(edge[1])?;
2199 cdt.cdt_sanity_check();
2200 cdt.add_constraint_and_split(point_a, point_b, |v| v);
2201 cdt.cdt_sanity_check();
2202 }
2203
2204 Ok(())
2205 }
2206
2207 #[test]
2208 fn edge_intersection_precision_test_3() -> Result<(), InsertionError> {
2209 let edges = [
2210 [
2211 Point2 {
2212 x: -11.673287,
2213 y: -28.37192,
2214 },
2215 Point2 {
2216 x: -16.214716,
2217 y: -43.81278,
2218 },
2219 ],
2220 [
2221 Point2 {
2222 x: 7.4022045,
2223 y: -51.355137,
2224 },
2225 Point2 {
2226 x: -13.92232,
2227 y: -36.01863,
2228 },
2229 ],
2230 ];
2231
2232 let mut cdt: ConstrainedDelaunayTriangulation<Point2<f32>> =
2235 ConstrainedDelaunayTriangulation::new();
2236 let mut returned_constraint_edge_counts = Vec::new();
2237 for edge in edges {
2238 let point_a = cdt.insert(edge[0])?;
2239 let point_b = cdt.insert(edge[1])?;
2240 returned_constraint_edge_counts
2241 .push(cdt.add_constraint_and_split(point_a, point_b, |v| v).len());
2242 cdt.cdt_sanity_check();
2243 }
2244
2245 assert_eq!(cdt.num_constraints, 3);
2249 assert_eq!(returned_constraint_edge_counts, vec![1, 1]);
2250
2251 Ok(())
2252 }
2253
2254 #[test]
2255 fn edge_intersection_precision_test_4() -> Result<(), InsertionError> {
2256 let points = [
2257 (1.0f32, 1.0f32),
2258 (63.137257, 87.04635),
2259 (1.0, 37.488983),
2260 (59.171143, 97.93353),
2261 (68.78571, 71.541046),
2262 ];
2263
2264 run_bulk_load_proptest(&points)
2265 }
2266
2267 #[test]
2268 fn edge_intersection_precision_test_5() -> Result<(), InsertionError> {
2269 let points = [
2270 (12.645211, 67.07809),
2271 (49.93318, 15.726259),
2272 (22.433575, 53.597496),
2273 (6.3033395, 32.111538),
2274 (89.09958, 1.0720834),
2275 ];
2276 run_bulk_load_proptest(&points)
2277 }
2278
2279 #[test]
2280 fn edge_intersection_precision_test_6() -> Result<(), InsertionError> {
2281 let points = [
2282 (32.944817, 13.24526),
2283 (91.99222, 22.252642),
2284 (12.645211, 67.07809),
2285 (49.93318, 15.726259),
2286 (22.433575, 53.597496),
2287 (6.3033395, 32.111538),
2288 (89.09958, 1.0720834),
2289 ];
2290 run_bulk_load_proptest(&points)
2291 }
2292
2293 #[test]
2294 fn edge_intersection_precision_test_7() {
2295 let edges = [
2296 (
2297 Point2 {
2298 x: 15.435077667236328,
2299 y: 16.331045150756836,
2300 },
2301 Point2 {
2302 x: 20.322175979614258,
2303 y: 21.218143463134766,
2304 },
2305 ),
2306 (
2307 Point2 {
2308 x: 18.69314193725586,
2309 y: 19.589109420776367,
2310 },
2311 Point2 {
2312 x: 19.507659912109375,
2313 y: 20.40362548828125,
2314 },
2315 ),
2316 (
2317 Point2 {
2318 x: 19.507659912109375,
2319 y: 21.218143463134766,
2320 },
2321 Point2 {
2322 x: 19.507659912109375,
2323 y: 20.40362548828125,
2324 },
2325 ),
2326 (
2327 Point2 {
2328 x: 17.878625869750977,
2329 y: 18.774595260620117,
2330 },
2331 Point2 {
2332 x: 19.507659912109375,
2333 y: 20.40362548828125,
2334 },
2335 ),
2336 ];
2337 let mut cdt: ConstrainedDelaunayTriangulation<Point2<f64>> =
2338 ConstrainedDelaunayTriangulation::new();
2339 for edge in edges {
2340 let point_a = cdt.insert(edge.0).unwrap();
2341 let point_b = cdt.insert(edge.1).unwrap();
2342 cdt.add_constraint_and_split(point_a, point_b, |v| v);
2343 cdt.cdt_sanity_check();
2344 }
2345 }
2346
2347 #[test]
2348 fn cdt_test_inserting_few_points() -> Result<(), InsertionError> {
2349 let vertices = [
2350 Point2::new(43.44877, 78.04408),
2351 Point2::new(43.498154, 81.00147),
2352 Point2::new(42.54009, 79.197945),
2353 Point2::new(42.702503, 77.25916),
2354 Point2::new(43.033974, 76.91306),
2355 Point2::new(43.843018, 76.06832),
2356 Point2::new(43.72607, 77.74121),
2357 Point2::new(42.55491, 79.02104),
2358 ];
2359 let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new();
2360
2361 for point in vertices {
2362 cdt.insert(point)?;
2363 }
2364
2365 let v = |n| FixedVertexHandle::from_index(n);
2366
2367 cdt.add_constraint(v(0), v(4));
2368 cdt.add_constraint(v(1), v(6));
2369 cdt.add_constraint(v(2), v(7));
2370 cdt.add_constraint(v(3), v(4));
2371 cdt.add_constraint(v(4), v(5));
2372 cdt.add_constraint(v(5), v(6));
2373 cdt.add_constraint(v(7), v(3));
2374 cdt.add_constraint(v(6), v(7));
2375
2376 cdt.cdt_sanity_check();
2377 Ok(())
2378 }
2379
2380 #[test]
2381 fn edge_intersection_precision_test_8() -> Result<(), InsertionError> {
2382 let points = [
2383 (43.44877, 78.04408),
2384 (15.193367, 1.0),
2385 (1.0, 1.0),
2386 (1.0, 1.0),
2387 (41.485252, 91.78975),
2388 (49.090862, 1.0),
2389 (43.498154, 81.00147),
2390 (1.0, 1.0),
2391 (23.288902, 97.52936),
2392 (75.59119, 42.91933),
2393 (25.808325, 97.32154),
2394 ];
2395 run_bulk_load_proptest(&points)
2396 }
2397
2398 fn run_bulk_load_proptest(points: &[(f32, f32)]) -> Result<(), InsertionError> {
2399 let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new();
2400
2401 let mut last_handle = None;
2402 for &(x, y) in points {
2403 let next_handle = cdt.insert(Point2::new(x, y))?;
2404 if let Some(last_handle) = last_handle {
2405 let edges = cdt.add_constraint_and_split(last_handle, next_handle, |p| p);
2406 if !edges.is_empty() {
2407 check_returned_edges(&cdt, &edges, last_handle, next_handle);
2408 }
2409 }
2410 last_handle = Some(next_handle);
2411
2412 cdt.cdt_sanity_check();
2413 }
2414
2415 Ok(())
2416 }
2417
2418 #[test]
2419 fn edge_intersection_precision_test_9() -> Result<(), InsertionError> {
2420 let points = [
2421 (17.315233, 1.0),
2422 (36.59658, 1.0),
2423 (1.0, 62.270954),
2424 (1.0, 1.0),
2425 (38.64656, 24.732662),
2426 ];
2427
2428 run_bulk_load_proptest(&points)
2429 }
2430
2431 #[test]
2432 fn edge_intersection_precision_test_10() -> Result<(), InsertionError> {
2433 let points = [
2434 (57.128956, 53.16448),
2435 (78.60271, 71.51385),
2436 (94.13138, 1.0),
2437 (1.0, 1.0),
2438 (65.829636, 82.399826),
2439 (84.53016, 25.01849),
2440 (1.0, 1.0),
2441 (52.712097, 98.32006),
2442 (77.15014, 51.253986),
2443 (94.362755, 59.469707),
2444 (44.423584, 69.76751),
2445 (1.0, 1.0),
2446 (72.84374, 64.58434),
2447 (27.416088, 88.37326),
2448 ];
2449
2450 run_bulk_load_proptest(&points)
2451 }
2452
2453 proptest! {
2454 #![proptest_config(ProptestConfig::with_cases(200))]
2455 #[test]
2456 #[ignore]
2457 fn
2458 prop_test_bulk_load_cdt_stable(
2459 points in proptest::collection::vec((1.0f32..100.0f32, 1.0f32..100.0f32), 1..100),
2460 edges in proptest::collection::vec((0usize..100, 0usize..100), 1..100)) {
2461 prop_test_bulk_load_cdt_stable_impl(points, edges);
2462 }
2463 }
2464
2465 #[test]
2466 fn fuzz_test_bulk_load_cdt() {
2467 let points = vec![
2468 Point2::new(-2.7049442424493675e-11, -2.704772938494617e-11),
2469 Point2::new(-2.7049442424493675e-11, -2.2374774353910703e-35),
2470 Point2::new(-2.704944241771741e-11, -2.704922037988875e-11),
2471 Point2::new(-2.7049442424493675e-11, -2.7049442424493572e-11),
2472 Point2::new(-2.7049442424493675e-11, 0.0),
2473 ];
2474
2475 let cdt =
2476 ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, Vec::new())
2477 .unwrap();
2478
2479 cdt.cdt_sanity_check();
2480 }
2481
2482 #[test]
2483 fn fuzz_test_bulk_load_cdt_2() {
2484 {
2485 let points = vec![
2486 Point2::new(0.11617647291654172, -2.7049442424493675e-11),
2487 Point2::new(-3.684373062283352e-14, -2.7049442424493675e-11),
2488 Point2::new(-2.7049442424493268e-11, -2.7049442424493675e-11),
2489 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2490 Point2::new(-2.704943949713427e-11, -2.7049442424493856e-11),
2491 ];
2492 let edges = vec![[3, 0]];
2493
2494 let cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, edges)
2495 .unwrap();
2496
2497 cdt.cdt_sanity_check();
2498 }
2499 }
2500
2501 #[test]
2502 fn fuzz_test_bulk_load_cdt_3() {
2503 {
2504 let points = vec![
2505 Point2::new(-2.7049442424378697e-11, -2.7049442424493675e-11),
2506 Point2::new(-2.7049434889184558e-11, -2.7049442424493675e-11),
2507 Point2::new(-2.7049442395058873e-11, -2.7049442424493675e-11),
2508 Point2::new(-2.7049442424546208e-11, -2.7049442424493675e-11),
2509 Point2::new(0.0004538143382352941, -2.705036194996328e-11),
2510 Point2::new(-2.704944239484691e-11, -2.7049442424493675e-11),
2511 Point2::new(-2.7049442424546615e-11, -2.7049442424493675e-11),
2512 Point2::new(-2.7049442424493882e-11, -2.70494406897702e-11),
2513 Point2::new(0.11617551691391888, -2.7049442424493675e-11),
2514 Point2::new(-2.7049442424493572e-11, -2.7049442424493675e-11),
2515 Point2::new(-2.704944241771741e-11, -2.704944242438945e-11),
2516 Point2::new(-2.7049442424493678e-11, -2.7049442424493662e-11),
2517 Point2::new(-2.704944241771741e-11, -2.7049442424493675e-11),
2518 Point2::new(-2.7049442424467205e-11, -2.7049442424493675e-11),
2519 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2520 Point2::new(-2.7049442424493675e-11, -2.7049441611342046e-11),
2521 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2522 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2523 Point2::new(-2.704944241771741e-11, -2.7049442424493675e-11),
2524 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2525 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2526 Point2::new(-2.704944242448623e-11, -2.7049442424493675e-11),
2527 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2528 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2529 Point2::new(-2.7049442424493675e-11, -2.6502324517958398e-11),
2530 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2531 Point2::new(-2.7049442424493675e-11, -2.7049442423646642e-11),
2532 Point2::new(-2.704944242437787e-11, -2.7049442424493675e-11),
2533 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2534 Point2::new(-2.7049442424493675e-11, -2.7049442424493675e-11),
2535 Point2::new(-2.7049442424493646e-11, -2.6375537048546205e-11),
2536 ];
2537 let edges = vec![];
2538
2539 let cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, edges)
2540 .unwrap();
2541
2542 cdt.cdt_sanity_check();
2543 }
2544 }
2545
2546 #[test]
2547 fn fuzz_test_bulk_load_cdt_4() {
2548 {
2549 let points = vec![
2550 Point2::new(5.532608426405843e-5, -2.705000112790462e-11),
2551 Point2::new(-2.7049442384471368e-11, -2.6555608406219246e-11),
2552 Point2::new(-4.6189490530823523e-10, -2.7049442424337338e-11),
2553 Point2::new(-2.688601759526906e-11, -2.7049442424517663e-11),
2554 Point2::new(-2.704944242448623e-11, -2.7049442424493675e-11),
2555 Point2::new(-2.704944240840005e-11, -2.7049442424493675e-11),
2556 Point2::new(-1.1276731182827942e-13, -2.7049442424493675e-11),
2557 ];
2558
2559 let edges = vec![];
2560
2561 ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load(points.clone()).unwrap();
2562
2563 let cdt = ConstrainedDelaunayTriangulation::<Point2<f64>>::bulk_load_cdt(points, edges)
2564 .unwrap();
2565
2566 cdt.cdt_sanity_check();
2567 }
2568 }
2569
2570 fn prop_test_bulk_load_cdt_stable_impl(points: Vec<(f32, f32)>, edges: Vec<(usize, usize)>) {
2571 let max_index = points.len() - 1;
2572 let edges = edges
2573 .iter()
2574 .copied()
2575 .map(|(from, to)| [from.min(max_index), to.min(max_index)])
2576 .collect::<Vec<_>>();
2577
2578 let vertices = points
2579 .iter()
2580 .copied()
2581 .map(|(x, y)| Point2::new(x, y))
2582 .collect::<Vec<_>>();
2583 let result = ConstrainedDelaunayTriangulation::<Point2<f32>>::try_bulk_load_cdt(
2584 vertices,
2585 edges,
2586 |_| {},
2587 )
2588 .unwrap();
2589
2590 result.cdt_sanity_check();
2591 }
2592
2593 #[test]
2594 fn bulk_load_cdt_stable_proptest_1() {
2595 let points = vec![(1.0, 1.0), (61.541332, 1.0), (1.0, 69.54), (1.0, 19.13391)];
2596 let edges = vec![(3, 3)];
2597 prop_test_bulk_load_cdt_stable_impl(points, edges)
2598 }
2599
2600 #[test]
2601 fn bulk_load_cdt_stable_proptest_2() {
2602 let points = vec![
2603 (1.0, 98.26258),
2604 (61.541332, 59.135162),
2605 (1.0, 1.0),
2606 (57.31029, 1.0),
2607 ];
2608 let edges = vec![(0, 3)];
2609
2610 prop_test_bulk_load_cdt_stable_impl(points, edges)
2611 }
2612
2613 fn check_returned_edges<S: crate::SpadeNum>(
2614 cdt: &ConstrainedDelaunayTriangulation<Point2<S>>,
2615 edges: &[FixedDirectedEdgeHandle],
2616 first_vertex: FixedVertexHandle,
2617 last_vertex: FixedVertexHandle,
2618 ) {
2619 cdt.cdt_sanity_check();
2620
2621 let first_pos = cdt.vertex(first_vertex).position().to_f64();
2622 let last_pos = cdt.vertex(last_vertex).position().to_f64();
2623
2624 let last = edges.last().expect("Edges cannot be empty");
2625 let last = cdt.directed_edge(*last);
2626
2627 let mut current_from = first_vertex;
2628
2629 for edge in edges {
2630 let edge = cdt.directed_edge(*edge);
2631 assert_eq!(edge.from().fix(), current_from);
2632 let position = edge.to().position().to_f64();
2633 let distance =
2634 crate::delaunay_core::math::distance_2(first_pos, last_pos, position).sqrt();
2635 assert_abs_diff_eq!(distance, 0.0, epsilon = 3.0e-2f64);
2638 current_from = edge.to().fix();
2639 }
2640
2641 assert_eq!(last.to().fix(), last_vertex);
2642 }
2643
2644 proptest! {
2645 #![proptest_config(ProptestConfig::with_cases(1000))]
2646 #[test]
2647 #[ignore]
2648 fn test_add_constraint_and_split(points in proptest::collection::vec((1.0f32..100.0f32, 1.0f32..100.0f32), 0..100)) {
2649 let mut cdt = ConstrainedDelaunayTriangulation::<Point2<f32>>::new();
2650
2651 let mut last_handle = None;
2652 for (x, y) in points {
2653 let next_handle = cdt.insert(Point2::new(x,y)).unwrap();
2654 if let Some(last_handle) = last_handle {
2655 let edges = cdt.add_constraint_and_split(last_handle, next_handle, |p|p);
2656 if !edges.is_empty() {
2657 check_returned_edges(&cdt, &edges, last_handle, next_handle);
2658 }
2659 }
2660 last_handle = Some(next_handle);
2661 cdt.cdt_sanity_check();
2662 }
2663
2664 }
2665 }
2666}