flat_spatial/
aabbgrid.rs

1use crate::cell::AABBGridCell;
2use crate::storage::{cell_range, SparseStorage};
3use crate::AABB;
4use slotmapd::{new_key_type, SlotMap};
5
6pub type AABBGridObjects<O, AB> = SlotMap<AABBGridHandle, StoreObject<O, AB>>;
7
8new_key_type! {
9    /// This handle is used to modify the associated object or to update its position.
10    /// It is returned by the _insert_ method of a AABBGrid.
11    pub struct AABBGridHandle;
12}
13
14/// The actual object stored in the store
15#[derive(Clone, Copy)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct StoreObject<O: Copy, AB: AABB> {
18    /// User-defined object to be associated with a value
19    pub obj: O,
20    pub aabb: AB,
21}
22
23/// `AABBGrid` is a generic aabb-based spatial partitioning structure that uses a generic storage of cells which acts as a
24/// grid instead of a tree.
25///
26/// ## Fast queries
27/// In theory, `AABBGrid` should be faster than a quadtree/r-tree because it has no log costs
28/// (calculating the cells around a point is trivial).  
29/// However, it only works if the cell size is adapted to the problem, much like how a tree has to
30/// be balanced to be efficient.  
31///
32/// ## Dynamicity
33/// `AABBGrid's` allows eager removals and position updates, however for big aabbs (spanning many cells)
34/// this can be expensive, so beware.
35///
36/// Use this grid for mostly static objects with the occasional removal/position update if needed.
37///
38/// A `SlotMap` is used for objects managing, adding a level of indirection between aabbs and objects.
39/// `SlotMap` is used because removal doesn't alter handles given to the user, while still having constant time access.
40/// However it requires O to be copy, but `SlotMap's` author stated that they were working on a similar
41/// map where Copy isn't required.
42///
43/// ## About object management
44///
45/// In theory, you don't have to use the object management directly, you can make your custom
46/// Handle -> Object map by specifying "`()`" to be the object type.
47/// _(This can be useful if your object is not Copy)_
48/// Since `()` is zero sized, it should probably optimize away a lot of the object management code.
49///
50/// ```rust
51/// use flat_spatial::AABBGrid;
52/// use euclid::default::Rect;
53///
54/// let mut g: AABBGrid<(), Rect<f32>> = AABBGrid::new(10);
55/// let handle = g.insert(Rect::new([0.0, 0.0].into(), [10.0, 10.0].into()), ());
56/// // Use handle however you want
57/// ```
58#[derive(Clone)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60pub struct AABBGrid<O: Copy, AB: AABB> {
61    storage: SparseStorage<AABBGridCell>,
62    objects: AABBGridObjects<O, AB>,
63}
64
65impl<O: Copy, AB: AABB> AABBGrid<O, AB> {
66    /// Creates an empty grid.
67    /// The cell size should be about the same magnitude as your queries size.
68    pub fn new(cell_size: i32) -> Self {
69        Self {
70            storage: SparseStorage::new(cell_size),
71            objects: AABBGridObjects::default(),
72        }
73    }
74
75    /// Clears the grid.
76    pub fn clear(&mut self) -> impl Iterator<Item = (AB, O)> {
77        self.storage = SparseStorage::new(self.storage.cell_size());
78        let objs = std::mem::take(&mut self.objects);
79        objs.into_iter().map(|(_, o)| (o.aabb, o.obj))
80    }
81
82    /// Inserts a new object with a position and an associated object
83    /// Returns the unique and stable handle to be used with `get_obj`
84    pub fn insert(&mut self, aabb: AB, obj: O) -> AABBGridHandle {
85        let Self {
86            storage, objects, ..
87        } = self;
88
89        let h = objects.insert(StoreObject { obj, aabb });
90        cells_apply(storage, &aabb, |cell, sing_cell| {
91            cell.objs.push((h, sing_cell));
92        });
93        h
94    }
95
96    /// Updates the aabb of an object.
97    pub fn set_aabb(&mut self, handle: AABBGridHandle, aabb: AB) {
98        let obj = self
99            .objects
100            .get_mut(handle)
101            .expect("Object not in grid anymore");
102
103        let storage = &mut self.storage;
104
105        let old_ll = storage.cell_mut(obj.aabb.ll()).0;
106        let old_ur = storage.cell_mut(obj.aabb.ur()).0;
107
108        let ll = storage.cell_mut(aabb.ll()).0;
109        let ur = storage.cell_mut(aabb.ur()).0;
110
111        obj.aabb = aabb;
112
113        if old_ll == ll && old_ur == ur {
114            return;
115        }
116
117        for id in cell_range(old_ll, old_ur) {
118            let cell = storage.cell_mut_unchecked(id);
119            let p = match cell.objs.iter().position(|(x, _)| *x == handle) {
120                Some(x) => x,
121                None => return,
122            };
123            cell.objs.swap_remove(p);
124        }
125
126        let sing_cell = ll == ur;
127        for id in cell_range(ll, ur) {
128            let cell = storage.cell_mut_unchecked(id);
129            cell.objs.push((handle, sing_cell))
130        }
131    }
132
133    /// Removes an object from the grid.
134    pub fn remove(&mut self, handle: AABBGridHandle) -> Option<O> {
135        let st = self.objects.remove(handle)?;
136
137        let storage = &mut self.storage;
138        cells_apply(storage, &st.aabb, |cell, _| {
139            for i in 0..cell.objs.len() {
140                if cell.objs[i].0 == handle {
141                    cell.objs.swap_remove(i);
142                    return;
143                }
144            }
145        });
146
147        Some(st.obj)
148    }
149
150    /// Iterate over all handles
151    pub fn handles(&self) -> impl Iterator<Item = AABBGridHandle> + '_ {
152        self.objects.keys()
153    }
154
155    /// Iterate over all objects
156    pub fn objects(&self) -> impl Iterator<Item = &O> + '_ {
157        self.objects.values().map(|x| &x.obj)
158    }
159
160    /// Returns a reference to the associated object and its position, using the handle.
161    pub fn get(&self, id: AABBGridHandle) -> Option<&StoreObject<O, AB>> {
162        self.objects.get(id)
163    }
164
165    /// Returns a mutable reference to the associated object and its position, using the handle.
166    pub fn get_mut(&mut self, id: AABBGridHandle) -> Option<&mut StoreObject<O, AB>> {
167        self.objects.get_mut(id)
168    }
169
170    /// The underlying storage
171    pub fn storage(&self) -> &SparseStorage<AABBGridCell> {
172        &self.storage
173    }
174
175    /// Queries for objects intersecting a given AABB.
176    pub fn query(&self, aabb: AB) -> impl Iterator<Item = (AABBGridHandle, &AB, &O)> + '_ {
177        self.query_broad(aabb).filter_map(move |h| {
178            // Safety: All objects in the cells are guaranteed to be valid.
179            let obj = unsafe { self.objects.get_unchecked(h) };
180            if aabb.intersects(&obj.aabb) {
181                Some((h, &obj.aabb, &obj.obj))
182            } else {
183                None
184            }
185        })
186    }
187
188    /// Queries for all objects in the cells intersecting the given AABB
189    pub fn query_broad(&self, bbox: AB) -> impl Iterator<Item = AABBGridHandle> + '_ {
190        let storage = &self.storage;
191
192        let ll_id = storage.cell_id(bbox.ll());
193        let ur_id = storage.cell_id(bbox.ur());
194
195        let iter = cell_range(ll_id, ur_id)
196            .flat_map(move |id| storage.cell(id))
197            .flat_map(|x| x.objs.iter().copied());
198
199        if ll_id == ur_id {
200            QueryIter::Simple(iter)
201        } else {
202            QueryIter::Dedup(
203                fnv::FnvHashSet::with_hasher(fnv::FnvBuildHasher::default()),
204                iter,
205            )
206        }
207    }
208
209    /// Queries for objects intersecting a given AABB.
210    /// Uses a visitor for slightly better performance.
211    pub fn query_visitor(&self, aabb: AB, mut visitor: impl FnMut(AABBGridHandle, &AB, &O)) {
212        self.query_broad_visitor(aabb, move |h| {
213            // Safety: All objects in the cells are guaranteed to be valid.
214            let obj = unsafe { self.objects.get_unchecked(h) };
215            if aabb.intersects(&obj.aabb) {
216                visitor(h, &obj.aabb, &obj.obj)
217            }
218        })
219    }
220
221    /// Queries for all objects in the cells intersecting the given AABB
222    /// Uses a visitor for slightly better performance.
223    pub fn query_broad_visitor(&self, bbox: AB, mut visitor: impl FnMut(AABBGridHandle)) {
224        let storage = &self.storage;
225
226        let ll_id = storage.cell_id(bbox.ll());
227        let ur_id = storage.cell_id(bbox.ur());
228
229        if ll_id == ur_id {
230            let cell = storage.cell(ll_id).unwrap();
231            for (h, _) in cell.objs.iter() {
232                visitor(*h);
233            }
234            return;
235        }
236
237        let mut dedup = fnv::FnvHashSet::with_hasher(fnv::FnvBuildHasher::default());
238
239        for celly in ll_id.1..=ur_id.1 {
240            for cellx in ll_id.0..=ur_id.0 {
241                let cell = match storage.cell((cellx, celly)) {
242                    Some(x) => x,
243                    None => continue,
244                };
245
246                for (h, sing_cell) in cell.objs.iter() {
247                    if *sing_cell {
248                        visitor(*h);
249                        continue;
250                    }
251                    if dedup.insert(*h) {
252                        visitor(*h);
253                    }
254                }
255            }
256        }
257    }
258
259    /// Returns the number of objects currently available
260    pub fn len(&self) -> usize {
261        self.objects.len()
262    }
263
264    /// Checks if the grid contains objects or not
265    pub fn is_empty(&self) -> bool {
266        self.objects.is_empty()
267    }
268}
269
270fn cells_apply<AB: AABB>(
271    storage: &mut SparseStorage<AABBGridCell>,
272    bbox: &AB,
273    f: impl Fn(&mut AABBGridCell, bool),
274) {
275    let ll = storage.cell_mut(bbox.ll()).0;
276    let ur = storage.cell_mut(bbox.ur()).0;
277    for id in cell_range(ll, ur) {
278        f(storage.cell_mut_unchecked(id), ll == ur)
279    }
280}
281
282enum QueryIter<T: Iterator<Item = (AABBGridHandle, bool)>> {
283    Simple(T),
284    Dedup(fnv::FnvHashSet<AABBGridHandle>, T),
285}
286
287impl<T: Iterator<Item = (AABBGridHandle, bool)>> Iterator for QueryIter<T> {
288    type Item = AABBGridHandle;
289
290    fn next(&mut self) -> Option<Self::Item> {
291        match self {
292            QueryIter::Simple(x) => x.next().map(|(x, _)| x),
293            QueryIter::Dedup(seen, x) => {
294                for (v, sing_cell) in x {
295                    if sing_cell {
296                        return Some(v);
297                    }
298                    if seen.insert(v) {
299                        return Some(v);
300                    }
301                }
302                None
303            }
304        }
305    }
306}