quadtree_rs/
handle_iter.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use {
16    crate::{area::Area, qtinner::QTInner, traversal::Traversal},
17    num::PrimInt,
18    std::{collections::HashSet, default::Default, iter::FusedIterator},
19};
20
21#[derive(Clone, Debug)]
22pub(crate) struct HandleIter<'a, U>
23where
24    U: PrimInt + Default,
25{
26    search_area: Area<U>,
27    handle_stack: Vec<u64>,
28    qt_stack: Vec<&'a QTInner<U>>,
29    visited: HashSet<u64>,
30}
31
32impl<'a, U> HandleIter<'a, U>
33where
34    U: PrimInt + Default,
35{
36    pub(crate) fn new(qt: &'a QTInner<U>, search_area: Area<U>) -> HandleIter<'a, U> {
37        HandleIter {
38            search_area,
39            handle_stack: vec![],
40            qt_stack: vec![qt],
41            visited: HashSet::new(),
42        }
43    }
44
45    // Descent is an optimization for queries. We don't want to traverse the entire tree searching
46    // for handles which (mostly) correspond to regions our @req doesn't intersect with.
47    //
48    // Instead, we can make a beeline for the lowest region which totally contains the @req (but no
49    // lower). We then have to actually evaluate every handle below that node.
50    //
51    // Along the way, if our query is meant to be of type Traversal::Overlapping, we collect the
52    // handles we meet along the way. They are guaranteed to intersect @req.
53    pub(crate) fn query_optimization(&mut self, req: Area<U>, traversal_method: Traversal) {
54        // This method expects to be called at a point in time when the HandleIter has just been
55        // created but has not yet been called.
56        assert!(self.qt_stack.len() == 1);
57        assert!(self.handle_stack.is_empty());
58        assert!(self.visited.is_empty());
59
60        self.descend_recurse_step(req, traversal_method);
61    }
62
63    fn descend_recurse_step(&mut self, req: Area<U>, traversal_method: Traversal) {
64        assert!(self.qt_stack.len() == 1);
65        // Peek into the stack. We have to peek rather than pop, because if we are about to go too
66        // far down we'd rather stop and return the HandleIter as-is.
67        if let Some(qt) = self.qt_stack.last() {
68            // If the region doesn't contain our @req, we're already too far down. Return here.
69            if !qt.region().contains(req) {
70                return;
71            }
72            assert!(qt.region().contains(req));
73
74            if let Some(subquadrants) = qt.subquadrants().as_ref() {
75                for subquadrant in subquadrants.iter() {
76                    // If we find a subquadrant which totally contains the @req, we want to make
77                    // that our new sole qt.
78                    if subquadrant.region().contains(req) {
79                        if traversal_method == Traversal::Overlapping {
80                            self.handle_stack.extend(qt.handles());
81                        }
82
83                        // TODO(ambuc): Could this be done with Vec::swap() or std::mem::replace()?
84                        assert!(self.qt_stack.len() == 1);
85                        self.qt_stack = vec![subquadrant];
86
87                        // Recurse on this step. It will naturally return, but we want to propogate
88                        // that return rather than continue to search the other subquadrants.
89                        return self.descend_recurse_step(req, traversal_method);
90                    }
91                }
92            }
93            // If there aren't any subquadrants, we're probably done.
94        }
95    }
96}
97
98impl<U> Iterator for HandleIter<'_, U>
99where
100    U: PrimInt + Default,
101{
102    type Item = u64;
103
104    #[inline]
105    fn next(&mut self) -> Option<Self::Item> {
106        loop {
107            while let Some(handle) = self.handle_stack.pop() {
108                if self.visited.insert(handle) {
109                    return Some(handle);
110                }
111            }
112
113            // Then check the qt_stack.
114            if let Some(qt) = self.qt_stack.pop() {
115                // Push my sub quadrants onto the qt_stack too.
116                if let Some(sub_quadrants) = qt.subquadrants().as_ref() {
117                    for sub_quadrant in sub_quadrants {
118                        if sub_quadrant.region().intersects(self.search_area) {
119                            self.qt_stack.push(sub_quadrant)
120                        }
121                    }
122                }
123
124                // Push my regions onto the region stack
125                match qt.handles().len() {
126                    0 => (),
127                    1 => {
128                        if self.visited.insert(qt.handles()[0]) {
129                            return Some(qt.handles()[0]);
130                        }
131                    }
132                    _ => self.handle_stack.extend(qt.handles()),
133                }
134
135                continue;
136            }
137
138            // Else there's nothing left to search.
139            return None;
140        }
141    }
142
143    #[inline]
144    fn size_hint(&self) -> (usize, Option<usize>) {
145        (0, None)
146    }
147}
148
149impl<U> FusedIterator for HandleIter<'_, U> where U: PrimInt + Default {}