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