spade/
flood_fill_iterator.rs

1use alloc::{collections::VecDeque, vec::Vec};
2
3#[cfg(not(feature = "std"))]
4use hashbrown::HashSet;
5#[cfg(feature = "std")]
6use std::collections::HashSet;
7
8use num_traits::{one, zero, Float};
9use smallvec::smallvec;
10use smallvec::SmallVec;
11
12use crate::delaunay_core::math;
13use crate::handles::VertexHandle;
14use crate::{
15    handles::{
16        DirectedEdgeHandle, FixedDirectedEdgeHandle, FixedVertexHandle, UndirectedEdgeHandle,
17    },
18    HasPosition, Point2, SpadeNum, Triangulation,
19};
20
21pub trait DistanceMetric<S>
22where
23    S: SpadeNum,
24{
25    fn is_edge_inside(&self, points: [Point2<S>; 2]) -> bool;
26
27    fn is_handle_inside<V, DE, UE, F>(&self, handle: UndirectedEdgeHandle<V, DE, UE, F>) -> bool
28    where
29        V: HasPosition<Scalar = S>,
30    {
31        self.is_edge_inside(handle.positions())
32    }
33
34    fn distance_to_point(&self, point: Point2<S>) -> S;
35
36    fn is_point_inside(&self, point: Point2<S>) -> bool {
37        self.distance_to_point(point) <= zero()
38    }
39}
40
41/// Defines the shape of circle.
42///
43/// This is only exported to allow referring to the return type of
44/// [crate::FloatTriangulation::get_edges_in_circle].
45#[derive(Debug, PartialOrd, PartialEq, Clone, Copy, Hash)]
46pub struct CircleMetric<S: SpadeNum> {
47    center: Point2<S>,
48    radius_2: S,
49}
50
51impl<S: SpadeNum> CircleMetric<S> {
52    pub(crate) fn new(center: Point2<S>, radius_2: S) -> Self {
53        assert!(radius_2 >= zero());
54
55        Self { center, radius_2 }
56    }
57}
58
59impl<S> DistanceMetric<S> for CircleMetric<S>
60where
61    S: SpadeNum + Float,
62{
63    fn is_edge_inside(&self, points: [Point2<S>; 2]) -> bool {
64        let [p0, p1] = points;
65        math::distance_2(p0, p1, self.center) <= self.radius_2
66    }
67
68    fn distance_to_point(&self, point: Point2<S>) -> S {
69        self.center.distance_2(point) - self.radius_2
70    }
71}
72
73/// Defines the shape of a rectangle.
74///
75/// This is only exported to allow referring to the return type of
76/// [crate::FloatTriangulation::get_edges_in_rectangle].
77#[derive(Debug, PartialOrd, PartialEq, Clone, Copy, Hash)]
78pub struct RectangleMetric<S>
79where
80    S: SpadeNum,
81{
82    lower: Point2<S>,
83    upper: Point2<S>,
84}
85
86impl<S> RectangleMetric<S>
87where
88    S: SpadeNum,
89{
90    pub(crate) fn new(lower: Point2<S>, upper: Point2<S>) -> Self {
91        Self { lower, upper }
92    }
93}
94
95impl<S> DistanceMetric<S> for RectangleMetric<S>
96where
97    S: SpadeNum,
98    S: Float,
99{
100    fn is_edge_inside(&self, points: [Point2<S>; 2]) -> bool {
101        let [from, to] = points;
102        if self.is_point_inside(from) || self.is_point_inside(to) {
103            return true;
104        }
105
106        if self.lower == self.upper {
107            return math::side_query(from, to, self.lower).is_on_line();
108        }
109
110        for [v0, v1] in self.edges() {
111            let [s0, s1] = get_edge_intersections(v0, v1, from, to);
112            if s0.is_infinite() {
113                // Edges are colinear
114                if !math::side_query(from, to, v0).is_on_line() {
115                    // Edges are parallel but not overlapping
116                    continue;
117                }
118
119                let p1 = math::project_point(from, to, v0);
120                let p2 = math::project_point(from, to, v1);
121
122                if p1.is_on_edge() || p2.is_on_edge() {
123                    return true;
124                }
125            } else if (zero()..=one()).contains(&s0) && (zero()..=one()).contains(&s1) {
126                return true;
127            }
128        }
129        false
130    }
131
132    fn distance_to_point(&self, point: Point2<S>) -> S {
133        if self.lower == self.upper {
134            return point.distance_2(self.lower);
135        }
136
137        if self.is_point_inside(point) {
138            zero()
139        } else {
140            let [d0, d1, d2, d3] = self.edges().map(|[p0, p1]| math::distance_2(p0, p1, point));
141
142            d0.min(d1).min(d2).min(d3)
143        }
144    }
145}
146
147impl<S> RectangleMetric<S>
148where
149    S: SpadeNum,
150{
151    fn is_point_inside(&self, point: Point2<S>) -> bool {
152        point.all_component_wise(self.lower, |a, b| a >= b)
153            && point.all_component_wise(self.upper, |a, b| a <= b)
154    }
155
156    fn edges(&self) -> [[Point2<S>; 2]; 4] {
157        let lower = self.lower;
158        let upper = self.upper;
159        let v0 = lower;
160        let v1 = Point2::new(lower.x, upper.y);
161        let v2 = upper;
162        let v3 = Point2::new(upper.x, lower.y);
163
164        [[v0, v1], [v1, v2], [v2, v3], [v3, v0]]
165    }
166}
167
168fn get_edge_intersections<S: Float>(
169    v0: Point2<S>,
170    v1: Point2<S>,
171    e0: Point2<S>,
172    e1: Point2<S>,
173) -> [S; 2] {
174    let x4 = v1.x;
175    let x3 = v0.x;
176    let x2 = e1.x;
177    let x1 = e0.x;
178    let y4 = v1.y;
179    let y3 = v0.y;
180    let y2 = e1.y;
181    let y1 = e0.y;
182    let divisor = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
183    let (s0, s1);
184    if divisor == zero() {
185        s0 = S::infinity();
186        s1 = S::infinity();
187    } else {
188        s0 = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / divisor;
189        s1 = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / divisor;
190    }
191    [s0, s1]
192}
193
194/// Implements logic required to flood-fill a convex shape in the triangulation.
195/// Flood filling will work by
196///  - identifying an initial face that covers the face
197///  - expanding the edge hull formed by that face until all of the shape is covered (or the
198///    convex hull is reached)
199///
200/// This type doesn't implement `Iterator` directly - instead, it does the heavy lifting for
201/// [VerticesInShapeIterator] and [EdgesInShapeIterator].
202#[derive(Clone, Debug, PartialEq, Eq)]
203pub(crate) struct FloodFillIterator<'a, T, M>
204where
205    T: Triangulation,
206    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
207{
208    t: &'a T,
209    edge_loop: VecDeque<FixedDirectedEdgeHandle>,
210    pending: Option<FixedDirectedEdgeHandle>,
211    already_visited: HashSet<FixedVertexHandle>,
212    metric: M,
213}
214
215/// An iterator over vertices within a shape (e.g. a rectangle or circle).
216///
217/// Constructed by calling [crate::FloatTriangulation::get_vertices_in_rectangle] or
218/// [crate::FloatTriangulation::get_vertices_in_circle]
219///
220/// The item type is [VertexHandle]
221#[derive(Clone, Debug, PartialEq, Eq)]
222pub struct VerticesInShapeIterator<'a, T, M>
223where
224    T: Triangulation,
225    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
226{
227    initial_elements: SmallVec<[FixedVertexHandle; 3]>,
228    inner_iter: FloodFillIterator<'a, T, M>,
229}
230
231impl<'a, T, M> VerticesInShapeIterator<'a, T, M>
232where
233    T: Triangulation,
234    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
235{
236    pub(crate) fn new(inner_iter: FloodFillIterator<'a, T, M>) -> Self {
237        let initial_elements = if inner_iter.t.num_vertices() == 1 {
238            // The flood fill iterator requires at least a single edge to work properly. We'll have
239            // to special case triangulations that contain vertices but no edges.
240            smallvec![FixedVertexHandle::new(0)]
241        } else {
242            inner_iter.already_visited.iter().copied().collect()
243        };
244
245        Self {
246            initial_elements,
247            inner_iter,
248        }
249    }
250}
251
252impl<'a, T, M> Iterator for VerticesInShapeIterator<'a, T, M>
253where
254    T: Triangulation,
255    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
256{
257    type Item = VertexHandle<'a, T::Vertex, T::DirectedEdge, T::UndirectedEdge, T::Face>;
258
259    fn next(&mut self) -> Option<Self::Item> {
260        while let Some(next) = self.initial_elements.pop() {
261            let vertex = self.inner_iter.t.vertex(next);
262            if self.inner_iter.metric.is_point_inside(vertex.position()) {
263                return Some(vertex);
264            }
265        }
266
267        while let Some((_, vertex)) = self.inner_iter.next() {
268            if let Some(vertex) = vertex {
269                if self.inner_iter.metric.is_point_inside(vertex.position()) {
270                    return Some(vertex);
271                }
272            }
273        }
274        None
275    }
276}
277
278/// An iterator over edges within a shape (e.g. a rectangle or circle).
279///
280/// Constructed by calling [crate::FloatTriangulation::get_edges_in_rectangle] or
281/// [crate::FloatTriangulation::get_edges_in_circle]
282///
283/// The item type is [UndirectedEdgeHandle]
284#[derive(Clone, Debug, PartialEq, Eq)]
285pub struct EdgesInShapeIterator<'a, T, M>
286where
287    T: Triangulation,
288    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
289{
290    pub(crate) inner_iter: FloodFillIterator<'a, T, M>,
291}
292
293impl<'a, T, M> Iterator for EdgesInShapeIterator<'a, T, M>
294where
295    T: Triangulation,
296    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
297{
298    type Item = UndirectedEdgeHandle<'a, T::Vertex, T::DirectedEdge, T::UndirectedEdge, T::Face>;
299
300    fn next(&mut self) -> Option<Self::Item> {
301        self.inner_iter
302            .next()
303            .map(|(handle, _)| handle.as_undirected())
304    }
305}
306
307impl<'a, T, M> FloodFillIterator<'a, T, M>
308where
309    T: Triangulation,
310    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
311{
312    pub fn new(
313        t: &'a T,
314        metric: M,
315        start_point: Point2<<T::Vertex as HasPosition>::Scalar>,
316    ) -> Self {
317        let start_edges = Self::get_start_edges(t, &metric, start_point);
318        let already_visited = start_edges
319            .iter()
320            .map(|edge| t.directed_edge(*edge).from().fix())
321            .collect();
322
323        Self {
324            t,
325            edge_loop: start_edges.into(),
326            pending: None,
327            already_visited,
328            metric,
329        }
330    }
331
332    #[allow(clippy::type_complexity)]
333    fn get_start_edges(
334        t: &'a T,
335        metric: &M,
336        start_point: Point2<<T::Vertex as HasPosition>::Scalar>,
337    ) -> Vec<FixedDirectedEdgeHandle> {
338        use crate::PositionInTriangulation::*;
339        if !metric.is_point_inside(start_point) {
340            // Used to indicate an empty metric, e.g. a rectangle metric with upper < lower
341            return Vec::new();
342        }
343
344        if t.all_vertices_on_line() {
345            return t
346                .undirected_edges()
347                .filter(|edge| metric.is_handle_inside(*edge))
348                .flat_map(|edge| [edge.as_directed().fix(), edge.as_directed().rev().fix()])
349                .collect();
350        }
351
352        let start_face = match t.locate(start_point) {
353            OnVertex(point) => t
354                .vertex(point)
355                .out_edges()
356                .flat_map(|edge| edge.face().as_inner())
357                .next(),
358            OnEdge(edge) => {
359                let edge = t.directed_edge(edge);
360                edge.face()
361                    .as_inner()
362                    .or_else(|| edge.rev().face().as_inner())
363            }
364            OnFace(face) => Some(t.face(face)),
365            OutsideOfConvexHull(edge) => {
366                // The provided point does not lie on any face of the triangulation.
367                // Attempt to find an overlapping edge by iterating over the convex hull and
368                // minimizing the edge distance
369                let mut current_edge = t.directed_edge(edge);
370                let [from_distance, to_distance] = current_edge
371                    .positions()
372                    .map(|p| metric.distance_to_point(p));
373                let walk_forward;
374                let mut min_distance;
375                if from_distance > to_distance {
376                    walk_forward = true;
377                    min_distance = to_distance;
378                } else {
379                    walk_forward = false;
380                    min_distance = from_distance;
381                }
382
383                loop {
384                    if metric.is_handle_inside(current_edge.as_undirected()) {
385                        break current_edge.rev().face().as_inner();
386                    }
387
388                    let next_distance = if walk_forward {
389                        current_edge = current_edge.next();
390                        metric.distance_to_point(current_edge.to().position())
391                    } else {
392                        current_edge = current_edge.prev();
393                        metric.distance_to_point(current_edge.from().position())
394                    };
395
396                    if next_distance > min_distance {
397                        // Advancing the convex hull is increasing the distance to the convex
398                        // shape we won't find an intersection.
399                        break None;
400                    }
401
402                    min_distance = next_distance;
403                }
404            }
405            NoTriangulation => None,
406        };
407
408        start_face
409            .into_iter()
410            .flat_map(|face| face.adjacent_edges().into_iter().rev())
411            .map(|edge| edge.fix().rev())
412            .collect()
413    }
414}
415
416impl<'a, T, M> FloodFillIterator<'a, T, M>
417where
418    T: Triangulation,
419    M: DistanceMetric<<T::Vertex as HasPosition>::Scalar>,
420{
421    /// An iterator over all edges within a convex shape
422    ///
423    /// To do so, the iterator will, starting from a start edge within the target shape, create a
424    /// growing closed edge loop that expands with each iteration. All edges within that loop are also
425    /// within the target shape.
426    ///
427    /// Each iteration step will replace the last edge from the current loop and expand it outwards:
428    ///
429    /// For a loop with `n` edges, this process looks like this:
430    /// Before the iteration:
431    ///
432    /// ---edge n-1-->O--- edge n--> O ---edge 0-->
433    ///
434    /// After the iteration:
435    ///                      O<--- v_new
436    ///                    /   \              
437    ///        new edge n /     \ new edge n+1
438    ///                  /       \
439    /// --- edge n-1 -> O - - - - O --- edge 0 --->
440    ///                      ^
441    ///                      |
442    ///   old edge n (gets returned and removed from the loop)
443    ///
444    /// Where
445    /// `new edge n` = (edge n).prev().rev();
446    /// `new edge n+1` = (edge n).next().rev();
447    ///
448    /// This procedure has one caveat: The loop may expand into itself - in the above
449    /// depiction, `v_new` may already be part of the loop. This leads to
450    /// issues as the loop will contain a "sub loop" that doesn't get cleaned up properly.
451    ///
452    /// For this reason, a hash set stores all vertices that have already been visited. If a collision
453    /// is found, the iterator will skip the edge (by appending it to the front of the loop)
454    /// and attempt to replace edge n-1.
455    ///
456    /// The return type is somewhat unexpected: Since this method is used to iterate both over vertices _and_ edges,
457    /// it must both return any new edge and any new vertex. The possible cases are:
458    /// - return `None` if the iteration has finished, independent of the element type
459    /// - return `Some((edge, None))` if the iteration step produced a new edge but no new vertex
460    /// - return `Some((edge, Some(vertex)))` if the iteration produced a new vertex. This always happens if a new edge is
461    ///   also available.
462    #[allow(clippy::type_complexity)]
463    fn next(
464        &mut self,
465    ) -> Option<(
466        DirectedEdgeHandle<'a, T::Vertex, T::DirectedEdge, T::UndirectedEdge, T::Face>,
467        Option<VertexHandle<'a, T::Vertex, T::DirectedEdge, T::UndirectedEdge, T::Face>>,
468    )> {
469        if let Some(pending) = self.pending.take() {
470            let pending = self.t.directed_edge(pending);
471            if self.metric.is_handle_inside(pending.as_undirected()) {
472                return Some((pending, None));
473            }
474        }
475
476        while let Some(next) = self.edge_loop.pop_front() {
477            let next = self.t.directed_edge(next);
478
479            if !self.metric.is_handle_inside(next.as_undirected()) {
480                continue;
481            }
482
483            // Check if `next` and `next.rev()` are next to each other in the loop.
484            // This can commonly happen in two situations:
485            // - For degenerate triangulations, the algorithm will simply pre-filter any contained
486            //   edge `e` and append `[e, e.rev()]` to the edge loop. The if-statements below make
487            //   sure that exactly one edge of these pre-filtered edges gets returned.
488            // - Sometimes, the last edges returned in the iteration will all be inside the target
489            //   shape and completely surrounded by the edge loop. With each iteration, the edge
490            //   loop becomes smaller and smaller until it collapses into a single `[edge, edge.rev()]`
491            //   pair. This check makes sure that the iteration stops by clearing the loop.
492            if self.edge_loop.front() == Some(&next.rev().fix()) {
493                self.edge_loop.pop_front();
494                return Some((next, None));
495            }
496
497            // Same check as before but for the other neighbor of `next`
498            if self.edge_loop.back() == Some(&next.rev().fix()) {
499                self.edge_loop.pop_back();
500                return Some((next, None));
501            }
502
503            if next.is_outer_edge() {
504                return Some((next, None));
505            }
506
507            // The new edges that this edge may be expanded into
508            let new_edge_1 = next.prev().rev();
509            let new_edge_2 = next.next().rev();
510
511            let first = self.edge_loop.front().copied();
512            if first == Some(next.next().fix()) {
513                // Another "special case" happens if the first and last edge in the
514                // loop are direct neighbors.
515                // In this case, expanding the last edge would create a duplicated edge in the
516                // loop. Instead, we may only push one of the two usual successors.
517                self.already_visited.remove(&next.to().fix());
518                self.pending = self.edge_loop.pop_front();
519                self.edge_loop.push_front(new_edge_1.fix());
520                return Some((next, None));
521            }
522
523            let last = self.edge_loop.back().copied();
524            if last == Some(next.prev().fix()) {
525                // Same optimization as above but with the edge that comes before next (and not after)
526                self.already_visited.remove(&next.from().fix());
527                self.pending = self.edge_loop.pop_back();
528                self.edge_loop.push_back(next.next().rev().fix());
529                return Some((next, None));
530            }
531
532            // This line checks that the new vertex is not already part of the loop (see main
533            // comment above).
534            let new_vertex = new_edge_1.to();
535            if !self.already_visited.insert(new_vertex.fix()) {
536                let is_e1_inside = self.metric.is_handle_inside(new_edge_1.as_undirected());
537                let is_e2_inside = self.metric.is_handle_inside(new_edge_2.as_undirected());
538
539                match (is_e1_inside, is_e2_inside) {
540                    (true, true) => {
541                        self.edge_loop.push_back(next.fix());
542                        continue;
543                    }
544                    (true, false) => self.edge_loop.push_back(new_edge_1.fix()),
545                    (false, true) => self.edge_loop.push_back(new_edge_2.fix()),
546                    (false, false) => {}
547                }
548            } else {
549                self.edge_loop.push_back(new_edge_1.fix());
550                self.edge_loop.push_back(new_edge_2.fix());
551                return Some((next, Some(new_vertex)));
552            }
553
554            return Some((next, None));
555        }
556        None
557    }
558}
559
560#[cfg(test)]
561mod test {
562
563    use alloc::{vec, vec::Vec};
564
565    use crate::{
566        flood_fill_iterator::{CircleMetric, DistanceMetric, RectangleMetric},
567        test_utilities::random_points_with_seed,
568        ConstrainedDelaunayTriangulation, DelaunayTriangulation, FloatTriangulation,
569        InsertionError, Point2, Triangulation,
570    };
571
572    use super::get_edge_intersections;
573
574    #[test]
575    fn test_empty() {
576        let d = DelaunayTriangulation::<Point2<_>>::default();
577        let edges = d.get_edges_in_rectangle(Point2::new(-1.0, -2.0), Point2::new(2.0, 2.0));
578        assert_eq!(edges.count(), 0);
579    }
580
581    #[test]
582    fn test_single_vertex() -> Result<(), InsertionError> {
583        let mut d = DelaunayTriangulation::<Point2<f64>>::default();
584        d.insert(Point2::new(0.2, 1.0))?;
585        test_rectangle_iterator(&d);
586        Ok(())
587    }
588
589    #[test]
590    fn test_single_face() -> Result<(), InsertionError> {
591        let vertices = vec![
592            Point2::new(3.0, 2.0),
593            Point2::new(-2.0, 1.0),
594            Point2::new(-3.0, -4.0),
595        ];
596        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
597        test_rectangle_iterator(&d);
598        Ok(())
599    }
600
601    #[test]
602    fn test_rectangle_center_on_vertex() -> Result<(), InsertionError> {
603        let vertices = vec![
604            Point2::new(3.0, 2.0),
605            Point2::new(0.0, 0.0),
606            Point2::new(-2.0, 1.0),
607            Point2::new(-3.0, -4.0),
608        ];
609        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
610        test_rectangle_iterator(&d);
611        Ok(())
612    }
613
614    #[test]
615    fn test_small() -> Result<(), InsertionError> {
616        let vertices = vec![
617            Point2::new(3.0, 2.0),
618            Point2::new(-2.0, 1.0),
619            Point2::new(2.0, 1.0),
620            Point2::new(1.0, -4.0),
621        ];
622        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
623        let edges = d.get_edges_in_rectangle(Point2::new(-10.0, -10.0), Point2::new(10.0, 10.0));
624        assert_eq!(edges.count(), d.num_undirected_edges());
625
626        Ok(())
627    }
628
629    #[test]
630    fn test_random() -> Result<(), InsertionError> {
631        for size in [4, 52, 122] {
632            let vertices = random_points_with_seed(size, crate::test_utilities::SEED);
633            let d = DelaunayTriangulation::<_>::bulk_load(vertices.clone())?;
634            test_rectangle_iterator(&d);
635            let mut c = ConstrainedDelaunayTriangulation::<_>::bulk_load(vertices)?;
636            let constraints = random_points_with_seed(size, crate::test_utilities::SEED2);
637
638            for points in constraints.as_slice().chunks_exact(2) {
639                let from = c.insert(points[0])?;
640                let to = c.insert(points[1])?;
641                if c.can_add_constraint(from, to) {
642                    c.add_constraint(from, to);
643                }
644            }
645            test_rectangle_iterator(&c);
646        }
647        Ok(())
648    }
649
650    fn test_rectangle_iterator(d: &impl Triangulation<Vertex = Point2<f64>>) {
651        let areas = [
652            (Point2::new(-10.0, -10.0), Point2::new(10.0, 10.0)),
653            (Point2::new(-2.0, -2.0), Point2::new(-0.1, -0.1)),
654            (Point2::new(-0.5, -0.5), Point2::new(0.5, 0.5)),
655            (Point2::new(-0.1, -10.), Point2::new(0.1, 10.)),
656            (Point2::new(-5.0, -0.1), Point2::new(5.0, 0.1)),
657            (Point2::new(-0.9, -0.9), Point2::new(0.9, 0.9)),
658            (Point2::new(0.0, 0.0), Point2::new(0.0, 0.0)),
659            (Point2::new(20.1, 20.1), Point2::new(10.0, 10.0)),
660            (Point2::new(-2.0, -2.0), Point2::new(0.0, 0.0)),
661            (Point2::new(-2.0, 0.0), Point2::new(0.0, 2.0)),
662            (Point2::new(0.0, 0.0), Point2::new(2.0, 2.0)),
663            (Point2::new(-2.0, -2.0), Point2::new(-1.0, -1.0)),
664            (Point2::new(-2.0, -2.0), Point2::new(-1.0, -0.5)),
665            (Point2::new(-2.0, -2.0), Point2::new(-1.0, 0.0)),
666        ];
667
668        for (lower, upper) in areas {
669            let rectangle_metric = RectangleMetric::new(lower, upper);
670
671            // Test edge iteration
672            let edges = d.get_edges_in_rectangle(lower, upper);
673            let expected_count = d
674                .undirected_edges()
675                .filter(|edge| rectangle_metric.is_handle_inside(*edge))
676                .count();
677
678            assert_eq!(edges.count(), expected_count);
679
680            let center = lower.add(upper).mul(0.5);
681            let radius = (upper.x - lower.x).max(upper.y - lower.y);
682            let radius_2 = radius * radius;
683            let circle_metric = CircleMetric::new(center, radius_2);
684
685            let edges = d.get_edges_in_circle(center, radius_2);
686            let expected_count = d
687                .undirected_edges()
688                .filter(|edge| circle_metric.is_handle_inside(*edge))
689                .count();
690
691            assert_eq!(edges.count(), expected_count);
692
693            // Check vertex iteration
694            let vertices = d.get_vertices_in_rectangle(lower, upper);
695            let expected = d
696                .vertices()
697                .filter(|vertex| rectangle_metric.is_point_inside(vertex.position()))
698                .count();
699            assert_eq!(vertices.count(), expected);
700
701            let vertices = d.get_vertices_in_circle(center, radius_2);
702            let expected = d
703                .vertices()
704                .filter(|vertex| vertex.position().distance_2(center) <= radius_2)
705                .count();
706
707            assert_eq!(vertices.count(), expected);
708        }
709    }
710
711    #[test]
712    fn test_medium_triangulation() -> Result<(), InsertionError> {
713        let vertices = vec![
714            Point2::new(-7., -5.5),
715            Point2::new(-4., -6.5),
716            Point2::new(-5., -9.),
717            Point2::new(-6., 6.),
718            Point2::new(-8., -6.),
719            Point2::new(3., 3.),
720        ];
721        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
722        test_rectangle_iterator(&d);
723        Ok(())
724    }
725
726    #[test]
727    fn test_convex_hull_edge_cases() -> Result<(), InsertionError> {
728        let coords = [-1.0f64, 0.1, 1.0];
729        let mut vertices = Vec::new();
730        for x in &coords {
731            for y in &coords {
732                vertices.push(Point2::new(*x, *y));
733            }
734        }
735        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
736        test_rectangle_iterator(&d);
737        Ok(())
738    }
739
740    #[test]
741    fn test_degenerate() -> Result<(), InsertionError> {
742        let vertices = vec![Point2::new(0.0, 0.0), Point2::new(1.0, 0.5)];
743        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
744        test_rectangle_iterator(&d);
745
746        let vertices = vec![
747            Point2::new(0.0, 0.0),
748            Point2::new(1.0, 0.5),
749            Point2::new(2.0, 1.0),
750            Point2::new(4.0, 2.0),
751        ];
752        let d = DelaunayTriangulation::<_>::bulk_load(vertices)?;
753        test_rectangle_iterator(&d);
754
755        Ok(())
756    }
757
758    #[test]
759    fn test_edge_intersections() {
760        let sut = get_edge_intersections;
761        let v0 = Point2::new(0.0, 0.0);
762        let v1 = Point2::new(2.0, 0.0);
763
764        let [s0, s1] = sut(v0, v1, Point2::new(1.0f64, 1.0), Point2::new(1.0, -1.0));
765        assert_eq!(s0, 0.5);
766        assert_eq!(s1, 0.5);
767
768        let [s0, s1] = sut(v0, v1, Point2::new(0.5, 1.0), Point2::new(0.5, -1.0));
769        assert_eq!(s0, 0.25);
770        assert_eq!(s1, 0.5);
771
772        let [s0, s1] = sut(v0, v1, v0, v1);
773        assert!(s0.is_infinite());
774        assert!(s1.is_infinite());
775
776        let [s0, s1] = sut(v0, v1, Point2::new(1.0, 0.0), Point2::new(20.0, 0.0));
777        assert!(s0.is_infinite());
778        assert!(s1.is_infinite());
779
780        let [s0, s1] = sut(v0, v1, Point2::new(1.0, 3.0), Point2::new(20.0, 3.0));
781        assert!(s0.is_infinite());
782        assert!(s1.is_infinite());
783    }
784
785    #[test]
786    fn test_is_edge_inside() {
787        fn check(from: Point2<f64>, to: Point2<f64>, expected: bool) {
788            let metric = RectangleMetric::new(Point2::new(-1.0, -1.0), Point2::new(1.0, 1.0));
789            assert_eq!(metric.is_edge_inside([from, to]), expected);
790            assert_eq!(metric.is_edge_inside([to, from]), expected);
791        }
792
793        check(Point2::new(0.0, 0.0), Point2::new(2.0, 3.0), true);
794        check(Point2::new(-2.0, -3.0), Point2::new(2.0, 4.0), true);
795        check(Point2::new(-2.0, 0.5), Point2::new(2.0, 0.5), true);
796
797        check(Point2::new(-0.25, -0.25), Point2::new(0.1, 0.1), true);
798
799        check(Point2::new(-0.25, -0.25), Point2::new(1.0, 1.0), true);
800        check(Point2::new(-1.0, -1.0), Point2::new(0.5, 1.0), true);
801
802        check(Point2::new(-1.0, -1.0), Point2::new(1.0, 1.0), true);
803        check(Point2::new(-1.0, -1.0), Point2::new(2.0, 2.0), true);
804
805        check(Point2::new(1.0, 1.0), Point2::new(3.0, 4.0), true);
806
807        check(Point2::new(-1.0, -1.0), Point2::new(-2.0, 0.0), true);
808
809        check(Point2::new(-1.0, -1.0), Point2::new(-1.0, 1.0), true);
810        check(Point2::new(-1.0, -1.0), Point2::new(-1.0, 2.0), true);
811        check(Point2::new(-1.0, -2.0), Point2::new(-1.0, 2.0), true);
812        check(Point2::new(-2.0, -1.0), Point2::new(-0.5, -1.0), true);
813
814        check(Point2::new(-2.0, 2.0), Point2::new(3.0, -3.0), true);
815        check(Point2::new(-0.25, 0.25), Point2::new(-20.0, 20.0), true);
816
817        check(Point2::new(-2.0, 0.0), Point2::new(0.0, -2.0), true);
818
819        check(Point2::new(-2.0, -2.0), Point2::new(-1.0, -3.0), false);
820        check(Point2::new(-2.0, 0.0), Point2::new(0.0, -2.1), false);
821        check(Point2::new(-1.0, -2.0), Point2::new(1.0, -2.0), false);
822
823        check(Point2::new(-2.0, -1.0), Point2::new(-1.5, -1.0), false);
824        check(Point2::new(1.5, -1.0), Point2::new(2.0, -1.0), false);
825    }
826}