/rust/registry/src/index.crates.io-1949cf8c6b5b557f/spade-2.15.0/src/delaunay_triangulation.rs
Line | Count | Source |
1 | | use super::delaunay_core::Dcel; |
2 | | use crate::{ |
3 | | handles::VertexHandle, ConstrainedDelaunayTriangulation, HasPosition, HintGenerator, |
4 | | InsertionError, LastUsedVertexHintGenerator, NaturalNeighbor, Point2, Triangulation, |
5 | | TriangulationExt, |
6 | | }; |
7 | | use alloc::vec; |
8 | | use alloc::vec::Vec; |
9 | | |
10 | | use num_traits::Float; |
11 | | |
12 | | #[cfg(feature = "serde")] |
13 | | use serde::{Deserialize, Serialize}; |
14 | | |
15 | | /// A two-dimensional [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation). |
16 | | /// |
17 | | /// A Delaunay triangulation a triangulation that fulfills the *Delaunay Property*: No |
18 | | /// vertex of the triangulation is contained in the |
19 | | /// [circumcircle](https://en.wikipedia.org/wiki/Circumscribed_circle) of any triangle. |
20 | | /// As a consequence, Delaunay triangulations are well suited to support interpolation |
21 | | /// algorithms and nearest neighbor searches. It is often constructed in order to retrieve its dual |
22 | | /// graph, the [Voronoi diagram](#voronoi-diagram). |
23 | | /// |
24 | | #[doc = include_str!("../images/circumcircle.svg")] |
25 | | /// |
26 | | /// *An example triangulation with a few circumcircles drawn. No circumcircle contains more than three vertices.* |
27 | | /// |
28 | | /// Most methods on this type require the [Triangulation] trait. Refer to its documentation |
29 | | /// for more details on how to use `DelaunayTriangulation`. |
30 | | /// |
31 | | /// # Basic Usage |
32 | | /// Vertices need to implement the [HasPosition] trait. Spade bundles |
33 | | /// the [Point2] struct for basic use cases. |
34 | | /// |
35 | | /// ## Basic example |
36 | | /// ``` |
37 | | /// use spade::{DelaunayTriangulation, Triangulation, Point2, InsertionError}; |
38 | | /// |
39 | | /// fn main() -> Result<(), InsertionError> { |
40 | | /// |
41 | | /// let mut triangulation: DelaunayTriangulation<_> = DelaunayTriangulation::new(); |
42 | | /// |
43 | | /// // Insert three vertices that span one triangle (face) |
44 | | /// triangulation.insert(Point2::new(0.0, 1.0))?; |
45 | | /// triangulation.insert(Point2::new(1.0, 1.0))?; |
46 | | /// triangulation.insert(Point2::new(0.5, -1.0))?; |
47 | | /// |
48 | | /// assert_eq!(triangulation.num_vertices(), 3); |
49 | | /// assert_eq!(triangulation.num_inner_faces(), 1); |
50 | | /// assert_eq!(triangulation.num_undirected_edges(), 3); |
51 | | /// Ok(()) |
52 | | /// } |
53 | | /// ``` |
54 | | /// ## Right handed and left-handed coordinate systems |
55 | | /// For simplicity, all method names and their documentation assume that the underlying coordinate system |
56 | | /// is right-handed (e.g. x-axis points to the right, y-axis points upwards). If a left-handed system |
57 | | /// (lhs) is used, any term related to orientation needs to be reversed: |
58 | | /// - "left" becomes "right" (example: the face of a directed edge is on the right side for a lhs) |
59 | | /// - "counter clock wise" becomes "clockwise" (example: the vertices of a face are returned in clock wise order for a lhs) |
60 | | /// |
61 | | /// <table> |
62 | | /// <tr><th>left-handed system</th><th>right-handed system</th></tr> |
63 | | /// <tr><td> |
64 | | #[doc = concat!(include_str!("../images/lhs.svg"), "</td><td>",include_str!("../images/rhs.svg"), " </td></tr></table>")] |
65 | | /// # Extracting geometry information |
66 | | /// |
67 | | /// Spade uses [handles](crate::handles) to extract the triangulation's geometry. |
68 | | /// Handles are usually retrieved by inserting a vertex or by iterating. |
69 | | /// |
70 | | /// ## Example |
71 | | /// ``` |
72 | | /// fn main() -> Result<(), spade::InsertionError> { |
73 | | /// use crate::spade::{DelaunayTriangulation, Triangulation, Point2}; |
74 | | /// |
75 | | /// let mut triangulation: DelaunayTriangulation<Point2<f64>> = DelaunayTriangulation::new(); |
76 | | /// |
77 | | /// triangulation.insert(Point2::new(0.0, 1.0))?; |
78 | | /// triangulation.insert(Point2::new(1.0, 1.0))?; |
79 | | /// triangulation.insert(Point2::new(0.5, -1.0))?; |
80 | | /// |
81 | | /// for face in triangulation.inner_faces() { |
82 | | /// // face is a FaceHandle |
83 | | /// // edges is an array containing 3 directed edge handles |
84 | | /// let edges = face.adjacent_edges(); |
85 | | /// for edge in &edges { |
86 | | /// let from = edge.from(); |
87 | | /// let to = edge.to(); |
88 | | /// // from and to are vertex handles |
89 | | /// println!("found an edge: {:?} -> {:?}", from, to); |
90 | | /// } |
91 | | /// |
92 | | /// // vertices is an array containing 3 vertex handles |
93 | | /// let vertices = face.vertices(); |
94 | | /// for vertex in &vertices { |
95 | | /// println!("Found vertex with position {:?}", vertex.position()); |
96 | | /// } |
97 | | /// } |
98 | | /// # Ok(()) } |
99 | | /// ``` |
100 | | /// |
101 | | /// # Type parameters |
102 | | /// The triangulation's vertices, edges and faces can contain custom data. |
103 | | /// By default, the edge and face types are set to `()`. The vertex type must |
104 | | /// be specified. |
105 | | /// |
106 | | /// * `V: HasPosition` The vertex type |
107 | | /// * `DE: Default` The directed edge type. |
108 | | /// * `UE: Default` The undirected edge type. |
109 | | /// * `F: Default` The face type. |
110 | | /// |
111 | | /// Only vertices can be inserted directly. Faces and edges are create via `Default::default()`. |
112 | | /// Usually, edge and face data will need to be modified in a separate pass. |
113 | | /// |
114 | | /// Setting any custom data works by calling [vertex_data_mut](Triangulation::vertex_data_mut), |
115 | | /// [directed_edge_data_mut](Triangulation::directed_edge_data_mut), |
116 | | /// [undirected_edge_data_mut](Triangulation::undirected_edge_data_mut) and |
117 | | /// [face_data_mut](Triangulation::face_data_mut). |
118 | | /// |
119 | | /// ## Example |
120 | | /// ``` |
121 | | /// fn main() -> Result<(), spade::InsertionError> { |
122 | | /// use crate::spade::{DelaunayTriangulation, Triangulation, Point2}; |
123 | | /// |
124 | | /// // A custom undirected edge type used to cache the length of an edge |
125 | | /// #[derive(Default)] |
126 | | /// struct EdgeWithLength { length: f64 } |
127 | | /// |
128 | | /// // Creates a new triangulation with a custom undirected edge type |
129 | | /// let mut triangulation: DelaunayTriangulation<Point2<f64>, (), EdgeWithLength> |
130 | | /// = DelaunayTriangulation::new(); |
131 | | /// |
132 | | /// triangulation.insert(Point2::new(0.0, 1.0))?; |
133 | | /// triangulation.insert(Point2::new(1.0, 1.0))?; |
134 | | /// triangulation.insert(Point2::new(0.5, -1.0))?; |
135 | | /// |
136 | | /// for edge in triangulation.fixed_undirected_edges() { |
137 | | /// let positions = triangulation.undirected_edge(edge).positions(); |
138 | | /// let length = positions[0].distance_2(positions[1]).sqrt(); |
139 | | /// // Write length into the edge data |
140 | | /// triangulation.undirected_edge_data_mut(edge).length = length; |
141 | | /// } |
142 | | /// |
143 | | /// for edge in triangulation.undirected_edges() { |
144 | | /// let length = edge.data().length; |
145 | | /// assert!(length > 0.0); |
146 | | /// } |
147 | | /// # Ok(()) } |
148 | | /// ``` |
149 | | /// |
150 | | /// # Outer face |
151 | | /// Every triangulation contains an *outer face* that is adjacent to all edges of the |
152 | | /// triangulation's convex hull. The outer face is even present for a triangulation without |
153 | | /// vertices. It is either referenced by calling [Triangulation::outer_face()] or |
154 | | /// [handles::OUTER_FACE](crate::handles::OUTER_FACE) |
155 | | /// |
156 | | #[doc = include_str!("../images/outer_faces.svg")] |
157 | | /// |
158 | | /// # Voronoi Diagram |
159 | | /// |
160 | | /// the dual graph of the Delaunay triangulation is the *Voronoi diagram*. The Voronoi diagram |
161 | | /// subdivides the plane into several *Voronoi cells* (called `VoronoiFace` by Spade for consistency) |
162 | | /// which contain all points in the plane that share a common nearest neighbor. |
163 | | /// |
164 | | /// Keep in mind that "faces" and "vertices" are swapped - an (inner) Voronoi *vertex* |
165 | | /// corresponds to a single Delaunay *face*. |
166 | | /// The position of an inner voronoi vertex is the *circumcenter* of its dual Delaunay |
167 | | /// face. |
168 | | /// |
169 | | #[doc = include_str!("../images/basic_voronoi.svg")] |
170 | | /// |
171 | | /// *A Delaunay triangulation (black lines) and its dual graph, the Voronoi diagram (blue lines)* |
172 | | /// |
173 | | /// ## Extracting the Voronoi Diagram |
174 | | /// Spade defines various functions to extract information about the Voronoi diagram: |
175 | | /// |
176 | | /// **Types** |
177 | | /// * [DirectedVoronoiEdge](crate::handles::DirectedVoronoiEdge) |
178 | | /// * [UndirectedVoronoiEdge](crate::handles::UndirectedVoronoiEdge) |
179 | | /// * [VoronoiVertex](crate::handles::VoronoiVertex) |
180 | | /// * [VoronoiFace](crate::handles::VoronoiFace) |
181 | | /// |
182 | | /// **Iterators** |
183 | | /// * [Triangulation::directed_voronoi_edges()] |
184 | | /// * [Triangulation::undirected_voronoi_edges()] |
185 | | /// |
186 | | /// **Conversion** |
187 | | /// * [DirectedVoronoiEdge::as_undirected()](crate::handles::DirectedVoronoiEdge::as_undirected()) |
188 | | /// * [UndirectedVoronoiEdge::as_directed()](crate::handles::UndirectedVoronoiEdge::as_directed()) |
189 | | /// * [DirectedEdgeHandle::as_voronoi_edge()](crate::handles::DirectedEdgeHandle::as_voronoi_edge()) |
190 | | /// * [DirectedVoronoiEdge::as_delaunay_edge()](crate::handles::DirectedVoronoiEdge::as_delaunay_edge()) |
191 | | /// * [UndirectedEdgeHandle::as_voronoi_edge()](crate::handles::UndirectedEdgeHandle::as_voronoi_edge()) |
192 | | /// * [UndirectedVoronoiEdge::as_delaunay_edge()](crate::handles::UndirectedVoronoiEdge::as_delaunay_edge()) |
193 | | /// |
194 | | /// ## Extracting the Voronoi Diagram (Example) |
195 | | /// Extracting the geometry of the voronoi diagram can be slightly tricky as some of the voronoi |
196 | | /// extend into infinity (see the dashed lines in the example above). |
197 | | /// |
198 | | /// ``` |
199 | | /// use spade::handles::{VoronoiVertex::Inner, VoronoiVertex::Outer}; |
200 | | /// use spade::{DelaunayTriangulation, Point2, Triangulation}; |
201 | | /// |
202 | | /// // Prints out the location of all voronoi edges in a triangulation |
203 | | /// fn log_voronoi_diagram(triangulation: &DelaunayTriangulation<Point2<f64>>) { |
204 | | /// for edge in triangulation.undirected_voronoi_edges() { |
205 | | /// match edge.vertices() { |
206 | | /// [Inner(from), Inner(to)] => { |
207 | | /// // "from" and "to" are inner faces of the Delaunay triangulation |
208 | | /// println!( |
209 | | /// "Found voronoi edge between {:?} and {:?}", |
210 | | /// from.circumcenter(), |
211 | | /// to.circumcenter() |
212 | | /// ); |
213 | | /// } |
214 | | /// [Inner(from), Outer(edge)] | [Outer(edge), Inner(from)] => { |
215 | | /// // Some lines don't have a finite end and extend into infinity. |
216 | | /// println!( |
217 | | /// "Found infinite voronoi edge going out of {:?} into the direction {:?}", |
218 | | /// from.circumcenter(), |
219 | | /// edge.direction_vector() |
220 | | /// ); |
221 | | /// } |
222 | | /// [Outer(_), Outer(_)] => { |
223 | | /// // This case only happens if all vertices of the triangulation lie on the |
224 | | /// // same line and can probably be ignored. |
225 | | /// } |
226 | | /// } |
227 | | /// } |
228 | | /// } |
229 | | /// ``` |
230 | | /// |
231 | | /// # Performance tuning |
232 | | /// |
233 | | /// Fine-tuning a Delaunay triangulation can be more tricky from time to time. However, some will *nearly always* be |
234 | | /// the right thing to do: |
235 | | /// |
236 | | /// - Measure, don't guess. Execution times are hard to predict. |
237 | | /// - If you plan to perform several random access queries (e.g. looking up the point at an arbitrary position): |
238 | | /// Consider using `[HierarchyHintGenerator](crate::HierarchyHintGenerator) |
239 | | /// - For data sets with uniformly distributed vertices: Use [HierarchyHintGenerator](crate::HierarchyHintGenerator) if |
240 | | /// bulk loading is not applicable. |
241 | | /// - For data sets where vertices are inserted in close local proximity (each vertex is not too far away from the |
242 | | /// previously inserted vertex): Consider using [LastUsedVertexHintGenerator](LastUsedVertexHintGenerator). |
243 | | /// - Try to avoid large custom data types for edges, vertices and faces. |
244 | | /// - Using `f64` and `f32` as scalar type will usually end up roughly having the same run time performance. |
245 | | /// - Prefer using [bulk_load](Triangulation::bulk_load) over [insert](Triangulation::insert). |
246 | | /// - The run time of all vertex operations (insertion, removal and lookup) is roughly the same for larger triangulations. |
247 | | /// |
248 | | /// ## Complexity classes |
249 | | /// |
250 | | /// This table display the average and amortized cost for inserting a vertex into a triangulation with `n` vertices. |
251 | | /// |
252 | | /// | | Uniformly distributed vertices | Insertion of vertices with local proximity | |
253 | | /// |-----------------------------|--------------------------------|--------------------------------------------| |
254 | | /// | LastUsedVertexHintGenerator | O(sqrt(n)) (worst case) | O(1) (best case), fastest | |
255 | | /// | HierarchyHintGenerator | O(log(n)) (Average case) | O(1) (best case) | |
256 | | /// |
257 | | /// # See also |
258 | | /// Delaunay triangulations are closely related to [constrained Delaunay triangulations](crate::ConstrainedDelaunayTriangulation) |
259 | | #[doc(alias = "Voronoi")] |
260 | | #[doc(alias = "Voronoi diagram")] |
261 | | #[doc(alias = "Delaunay")] |
262 | | #[derive(Debug, Clone)] |
263 | | #[cfg_attr( |
264 | | feature = "serde", |
265 | | derive(Serialize, Deserialize), |
266 | | serde(crate = "serde") |
267 | | )] |
268 | | pub struct DelaunayTriangulation<V, DE = (), UE = (), F = (), L = LastUsedVertexHintGenerator> |
269 | | where |
270 | | V: HasPosition, |
271 | | DE: Default, |
272 | | UE: Default, |
273 | | F: Default, |
274 | | L: HintGenerator<<V as HasPosition>::Scalar>, |
275 | | { |
276 | | pub(crate) dcel: Dcel<V, DE, UE, F>, |
277 | | pub(crate) hint_generator: L, |
278 | | } |
279 | | |
280 | | impl<V, DE, UE, F, L> DelaunayTriangulation<V, DE, UE, F, L> |
281 | | where |
282 | | V: HasPosition, |
283 | | DE: Default, |
284 | | UE: Default, |
285 | | F: Default, |
286 | | L: HintGenerator<<V as HasPosition>::Scalar>, |
287 | | { |
288 | | /// Returns the nearest neighbor of a given input vertex. |
289 | | /// |
290 | | /// Returns `None` if the triangulation is empty. |
291 | | /// |
292 | | /// # Runtime |
293 | | /// This method takes `O(sqrt(n))` on average where n is the number of vertices. |
294 | 0 | pub fn nearest_neighbor( |
295 | 0 | &self, |
296 | 0 | position: Point2<<V as HasPosition>::Scalar>, |
297 | 0 | ) -> Option<VertexHandle<'_, V, DE, UE, F>> { |
298 | 0 | if self.num_vertices() == 0 { |
299 | 0 | return None; |
300 | 0 | } |
301 | | |
302 | 0 | let hint = self.hint_generator().get_hint(position); |
303 | 0 | let hint = self.validate_vertex_handle(hint); |
304 | | |
305 | 0 | let vertex = self.walk_to_nearest_neighbor(hint, position); |
306 | 0 | self.hint_generator().notify_vertex_lookup(vertex.fix()); |
307 | 0 | Some(vertex) |
308 | 0 | } |
309 | | |
310 | | /// Creates a new delaunay triangulation with an efficient bulk loading strategy. |
311 | | /// |
312 | | /// In contrast to [Triangulation::bulk_load], this method will create a triangulation with |
313 | | /// vertices returned *in the same order* as the input vertices. |
314 | | /// |
315 | | /// # Duplicate handling |
316 | | /// |
317 | | /// For every set of vertices with identical positions, only the vertex with the lowest index |
318 | | /// is kept. |
319 | | /// |
320 | | /// For example, if the input vertices are `[v0, v1, v2, v1]` (where v1 is duplicated), the |
321 | | /// resulting triangulation will be `[v0, v1, v2]`. |
322 | | /// |
323 | | /// Consider checking the triangulation's size after calling this method to ensure that no |
324 | | /// duplicates were present. Removing duplicates requires additional work and slightly |
325 | | /// increases the run time. |
326 | | /// |
327 | | /// # Run Time |
328 | | /// |
329 | | /// `O(n*log(n))` for `n` input vertices. Slightly (5% - 10%) slower than [Triangulation::bulk_load]. |
330 | | /// |
331 | | /// # Example |
332 | | /// ``` |
333 | | /// # use spade::InsertionError; |
334 | | /// use spade::{DelaunayTriangulation, Point2, Triangulation}; |
335 | | /// |
336 | | /// # fn main() -> Result<(), InsertionError> { |
337 | | /// let vertices = vec![ |
338 | | /// Point2::new(0.5, 1.0), |
339 | | /// Point2::new(-0.5, 2.0), |
340 | | /// Point2::new(0.2, 3.0), |
341 | | /// Point2::new(0.0, 4.0), |
342 | | /// Point2::new(-0.2, 5.0) |
343 | | /// ]; |
344 | | /// let triangulation = DelaunayTriangulation::<Point2<f64>>::bulk_load_stable(vertices.clone())?; |
345 | | /// // This assert will not hold for regular bulk loading! |
346 | | /// assert_eq!(triangulation.vertices().map(|v| *v.data()).collect::<Vec<_>>(), vertices); |
347 | | /// |
348 | | /// // This is how you would check for duplicates: |
349 | | /// let duplicates_found = triangulation.num_vertices() < vertices.len(); |
350 | | /// assert!(!duplicates_found); |
351 | | /// # Ok(()) } |
352 | | /// ``` |
353 | 0 | pub fn bulk_load_stable(elements: Vec<V>) -> Result<Self, InsertionError> { |
354 | 0 | let cdt = ConstrainedDelaunayTriangulation::bulk_load_cdt(elements, vec![])?; |
355 | 0 | let result = Self::from_cdt(cdt); |
356 | | |
357 | 0 | Ok(result) |
358 | 0 | } |
359 | | |
360 | 0 | fn from_cdt( |
361 | 0 | cdt: ConstrainedDelaunayTriangulation<V, DE, UE, F, L>, |
362 | 0 | ) -> DelaunayTriangulation<V, DE, UE, F, L> { |
363 | 0 | let (dcel, hint_generator, num_constraints) = cdt.into_parts(); |
364 | 0 | let dcel = dcel.map_undirected_edges(|cdt_edge| cdt_edge.deconstruct().1); |
365 | 0 | assert_eq!(num_constraints, 0); |
366 | 0 | DelaunayTriangulation { |
367 | 0 | dcel, |
368 | 0 | hint_generator, |
369 | 0 | } |
370 | 0 | } |
371 | | } |
372 | | |
373 | | impl<V, DE, UE, F, L> Default for DelaunayTriangulation<V, DE, UE, F, L> |
374 | | where |
375 | | V: HasPosition, |
376 | | DE: Default, |
377 | | UE: Default, |
378 | | F: Default, |
379 | | L: HintGenerator<<V as HasPosition>::Scalar>, |
380 | | { |
381 | 0 | fn default() -> Self { |
382 | 0 | Self { |
383 | 0 | dcel: Default::default(), |
384 | 0 | hint_generator: Default::default(), |
385 | 0 | } |
386 | 0 | } |
387 | | } |
388 | | |
389 | | impl<V, DE, UE, F, L> DelaunayTriangulation<V, DE, UE, F, L> |
390 | | where |
391 | | V: HasPosition, |
392 | | DE: Default, |
393 | | UE: Default, |
394 | | F: Default, |
395 | | V::Scalar: Float, |
396 | | L: HintGenerator<<V as HasPosition>::Scalar>, |
397 | | { |
398 | | /// Allows using natural neighbor interpolation on this triangulation. Refer to the documentation |
399 | | /// of [NaturalNeighbor] for more information. |
400 | 0 | pub fn natural_neighbor(&self) -> NaturalNeighbor<'_, Self> { |
401 | 0 | NaturalNeighbor::new(self) |
402 | 0 | } |
403 | | } |
404 | | |
405 | | impl<V, DE, UE, F, L> Triangulation for DelaunayTriangulation<V, DE, UE, F, L> |
406 | | where |
407 | | V: HasPosition, |
408 | | DE: Default, |
409 | | UE: Default, |
410 | | F: Default, |
411 | | L: HintGenerator<<V as HasPosition>::Scalar>, |
412 | | { |
413 | | type Vertex = V; |
414 | | type DirectedEdge = DE; |
415 | | type UndirectedEdge = UE; |
416 | | type Face = F; |
417 | | type HintGenerator = L; |
418 | | |
419 | 0 | fn s(&self) -> &Dcel<V, DE, UE, F> { |
420 | 0 | &self.dcel |
421 | 0 | } |
422 | | |
423 | 0 | fn s_mut(&mut self) -> &mut Dcel<V, DE, UE, F> { |
424 | 0 | &mut self.dcel |
425 | 0 | } |
426 | | |
427 | 0 | fn hint_generator(&self) -> &Self::HintGenerator { |
428 | 0 | &self.hint_generator |
429 | 0 | } |
430 | | |
431 | 0 | fn hint_generator_mut(&mut self) -> &mut Self::HintGenerator { |
432 | 0 | &mut self.hint_generator |
433 | 0 | } |
434 | | |
435 | 0 | fn from_parts( |
436 | 0 | dcel: Dcel<Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face>, |
437 | 0 | hint_generator: Self::HintGenerator, |
438 | 0 | num_constraints: usize, |
439 | 0 | ) -> Self { |
440 | 0 | assert_eq!(num_constraints, 0); |
441 | 0 | Self { |
442 | 0 | dcel, |
443 | 0 | hint_generator, |
444 | 0 | } |
445 | 0 | } |
446 | | |
447 | 0 | fn into_parts( |
448 | 0 | self, |
449 | 0 | ) -> ( |
450 | 0 | Dcel<Self::Vertex, Self::DirectedEdge, Self::UndirectedEdge, Self::Face>, |
451 | 0 | Self::HintGenerator, |
452 | 0 | usize, |
453 | 0 | ) { |
454 | 0 | (self.dcel, self.hint_generator, 0) |
455 | 0 | } |
456 | | } |
457 | | |
458 | | #[cfg(test)] |
459 | | mod test { |
460 | | use crate::test_utilities::{random_points_with_seed, SEED}; |
461 | | |
462 | | use crate::{DelaunayTriangulation, InsertionError, Point2, Triangulation}; |
463 | | |
464 | | #[allow(unused)] |
465 | | #[cfg(feature = "serde")] |
466 | | // Just needs to compile |
467 | | fn check_serde() { |
468 | | use serde::{Deserialize, Serialize}; |
469 | | |
470 | | use crate::{HierarchyHintGenerator, LastUsedVertexHintGenerator, Point2}; |
471 | | |
472 | | fn requires_serde<'de, T: Serialize + Deserialize<'de>>() {} |
473 | | |
474 | | type DT<L> = DelaunayTriangulation<Point2<f64>, (), (), (), L>; |
475 | | |
476 | | requires_serde::<DT<LastUsedVertexHintGenerator>>(); |
477 | | requires_serde::<DT<HierarchyHintGenerator<f64>>>(); |
478 | | } |
479 | | |
480 | | #[test] |
481 | | fn test_nearest_neighbor() -> Result<(), InsertionError> { |
482 | | const SIZE: usize = 54; |
483 | | let points = random_points_with_seed(SIZE, SEED); |
484 | | |
485 | | let d = DelaunayTriangulation::<_>::bulk_load(points.clone())?; |
486 | | |
487 | | let sample_points = random_points_with_seed(SIZE * 3, SEED); |
488 | | for p in sample_points { |
489 | | let nn_delaunay = d.nearest_neighbor(p); |
490 | | let nn_linear_search = points.iter().min_by(|l, r| { |
491 | | let d1 = l.distance_2(p); |
492 | | let d2 = r.distance_2(p); |
493 | | d1.partial_cmp(&d2).unwrap() |
494 | | }); |
495 | | assert_eq!(nn_delaunay.map(|p| p.position()), nn_linear_search.cloned()); |
496 | | } |
497 | | Ok(()) |
498 | | } |
499 | | |
500 | | #[test] |
501 | | fn test_nearest_neighbor_small() -> Result<(), InsertionError> { |
502 | | let mut d = DelaunayTriangulation::<_>::new(); |
503 | | assert_eq!(None, d.nearest_neighbor(Point2::new(0.0, 0.0))); |
504 | | |
505 | | d.insert(Point2::new(0.0, 0.0))?; |
506 | | assert!(d.nearest_neighbor(Point2::new(0.0, 1.0)).is_some()); |
507 | | Ok(()) |
508 | | } |
509 | | |
510 | | #[test] |
511 | | #[allow(clippy::redundant_clone)] |
512 | | #[allow(unused_must_use)] |
513 | | fn test_clone_is_implemented() { |
514 | | // Just needs to compile |
515 | | DelaunayTriangulation::<Point2<f64>>::new().clone(); |
516 | | } |
517 | | } |