1#![doc = include_str!("../README.md")]
2
3use cgmath::Vector3;
4use itertools::{ConsTuples, Product};
5use std::fmt::{Debug, Formatter};
6use std::ops::RangeInclusive;
7
8
9type BackingStorage<D> = Vec<D>;
10
11pub struct SpatialHashGrid<D: Sized> {
13 dims: Vector3<usize>,
14 cubes: BackingStorage<D>,
15}
16
17#[inline]
18fn pos_to_index(dims: Vector3<usize>, v: Vector3<u32>) -> Option<usize> {
20 let (x, y, z) = (v.x as usize, v.y as usize, v.z as usize);
21 if (x >= dims[0] ) || (y >= dims[1] ) || (z >= dims[2])
22 {
23 return None;
24 }
25 Some(
26 x +y * dims[0] + z * (dims[0] * dims[1]),
27 )
28}
29
30
31impl<D: Sized> SpatialHashGrid<D> {
32 pub fn new<V>(x: usize, y: usize, z: usize, filler: V) -> Self
34 where V:FnMut()->D {
35 let cap = x * y * z;
36 let mut d = Vec::with_capacity(cap);
38 d.resize_with(cap, filler);
40 Self {
41 dims: Vector3::new(x, y, z),
42 cubes: d,
43 }
44 }
45
46 #[inline]
48 pub fn size(&self)-> Vector3<usize>{
49 self.dims
50 }
51
52 #[inline(always)]
54 pub fn get_mut(&mut self, idx:usize)->Option<&mut D>{
55 self.cubes.get_mut(idx)
56 }
57
58 #[inline(always)]
60 pub fn get(&mut self, idx:usize)->Option<& D>{
61 self.cubes.get(idx)
62 }
63
64 #[inline]
65 pub fn pos_to_index(&self, v: Vector3<u32>) -> Option<usize> {
67 pos_to_index(self.dims, v)
68 }
69 #[inline]
71 pub fn iter_cube_indices(&self, min: Vector3<u32>, max: Vector3<u32>) -> BoxIdxIterator {
72 BoxIdxIterator {
73 dims: self.dims,
74 iter: itertools::iproduct!(min.x..=max.x, min.y..=max.y, min.z..=max.z),
75 }
76 }
77
78 #[inline]
80 pub fn iter_cubes(&self, min: Vector3<u32>, max: Vector3<u32>) -> BoxIterator<D> {
81 BoxIterator {
82 data: &self.cubes,
83 iter: self.iter_cube_indices(min, max)
84 }
85 }
86 #[inline]
88 pub fn iter_cubes_mut(&mut self, min: Vector3<u32>, max: Vector3<u32>) -> BoxIteratorMut<D> {
89 let inner_iter = self.iter_cube_indices(min, max);
90 BoxIteratorMut {
91 data: &mut self.cubes,
92 iter: inner_iter,
93 }
94 }
95}
96
97impl<D: Debug> Debug for SpatialHashGrid<D> {
98 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99 writeln!(
100 f,
101 "<SpatialHashGrid {}x{}x{}:",
102 self.dims[0], self.dims[1],self.dims[2]
103 )?;
104 for z in 0..self.dims[2] {
105 writeln!(f, "#Slice z={}:", z)?;
106 for x in 0..self.dims[0] {
107 for y in 0..self.dims[1] {
108 let v = Vector3::new(x as u32, y as u32, z as u32);
109 let idx = self.pos_to_index(v).expect("This can not go wrong");
110 write!(f, "{:?}, ", &self.cubes[idx])?;
111 }
112 writeln!(f)?; }
114 }
115 write!(f, ">")
116 }
117}
118
119
120pub struct BoxIdxIterator {
121 dims: Vector3<usize>,
122 #[allow(clippy::type_complexity)]
123 iter: ConsTuples<
124 Product<Product<RangeInclusive<u32>, RangeInclusive<u32>>, RangeInclusive<u32>>>,
125}
126
127
128
129
130impl Iterator for BoxIdxIterator {
131 type Item = (Vector3<u32>, usize);
132
133 #[inline]
134 fn next(&mut self) -> Option<Self::Item> {
135 loop {
136 let (x, y, z) = self.iter.next()?;
137 let c = Vector3 { x, y, z };
138 let idx = match pos_to_index(self.dims, c) {
139 Some(idx) => idx,
140 None => {
141 continue;
142 }
143 };
144 return Some((c, idx));
145 }
146 }
147 #[inline]
148 fn size_hint(&self) -> (usize, Option<usize>) {
149 let (_min, max) = self.iter.size_hint();
150 (0, max)
151 }
152}
153
154
155pub struct BoxIterator<'a, D: Sized> {
156 data: &'a BackingStorage<D>,
157 iter: BoxIdxIterator
158}
159impl <'a, D: Sized> BoxIterator<'a, D>{
160 pub fn with_index(self)->BoxIteratorWithIndex<'a, D>{
162 BoxIteratorWithIndex{
163 data:self.data,
164 iter:self.iter
165 }
166 }
167}
168
169
170impl<'a, D: Sized> Iterator for BoxIterator<'a, D> {
171 type Item = (Vector3<u32>, &'a D);
172
173 #[inline]
174 fn next(&mut self) -> Option<Self::Item> {
175 let (pos, idx) = self.iter.next()?;
176 let d = self.data.get(idx);
177 Some((pos, d.unwrap()))
178 }
179 #[inline]
180 fn size_hint(&self) -> (usize, Option<usize>) {
181 let (_min, max) = self.iter.size_hint();
182 (0, max)
183 }
184}
185
186pub struct BoxIteratorWithIndex<'a, D: Sized> {
187 data: &'a BackingStorage<D>,
188 iter: BoxIdxIterator
189}
190
191
192impl<'a, D: Sized> Iterator for BoxIteratorWithIndex<'a, D> {
193 type Item = (Vector3<u32>, usize, &'a D);
194
195 #[inline]
196 fn next(&mut self) -> Option<Self::Item> {
197 let (pos, idx) = self.iter.next()?;
198 let d = self.data.get(idx);
199 Some((pos, idx, d.unwrap()))
200 }
201 #[inline]
202 fn size_hint(&self) -> (usize, Option<usize>) {
203 let (_min, max) = self.iter.size_hint();
204 (0, max)
205 }
206}
207
208pub struct BoxIteratorMut<'a, D: Sized> {
209 data: &'a mut BackingStorage<D>,
210 iter: BoxIdxIterator,
211}
212
213impl<'a, D: Sized> Iterator for BoxIteratorMut<'a, D> {
214 type Item = (Vector3<u32>, usize, &'a mut D);
215
216 #[inline]
217 fn next(&mut self) -> Option<Self::Item> {
218 let (pos, idx) = self.iter.next()?;
219 unsafe {
221 let d = self.data.get_unchecked_mut(idx) as *mut D;
224 return Some((pos,idx, d.as_mut().unwrap_unchecked()));
228 }
229 }
230 #[inline]
231 fn size_hint(&self) -> (usize, Option<usize>) {
232 let (_min, max) = self.iter.size_hint();
233 (0, max)
234 }
235}
236
237
238impl<D: Sized> std::ops::Index<Vector3<u32>> for SpatialHashGrid<D> {
239 type Output = D;
240 #[inline]
242 fn index(&self, index: Vector3<u32>) -> &Self::Output {
243 let i = self.pos_to_index(index).expect("Index out of bounds");
244 self.cubes.index(i)
245 }
246}
247
248impl<D: Sized> std::ops::IndexMut<Vector3<u32>> for SpatialHashGrid<D> {
249 #[inline]
251 fn index_mut(&mut self, index: Vector3<u32>) -> &mut Self::Output {
252 let i = self.pos_to_index(index).expect("Index out of bounds");
253 self.cubes.index_mut(i)
254 }
255}
256
257
258
259impl<D: Sized> std::ops::Index<usize> for SpatialHashGrid<D> {
260 type Output = D;
261 #[inline]
263 fn index(&self, index: usize) -> &Self::Output {
264 self.cubes.index(index)
265 }
266}
267
268impl<D: Sized> std::ops::IndexMut<usize> for SpatialHashGrid<D> {
269 #[inline]
271 fn index_mut(&mut self, index:usize) -> &mut Self::Output {
272 self.cubes.index_mut(index)
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use crate::SpatialHashGrid;
279 use cgmath::Vector3;
280
281 #[derive(Debug, Clone)]
282 pub struct Data {
283 x: u32,
284 y: u32,
285 z: u32,
286 }
287
288 impl Default for Data {
289 fn default() -> Self {
290 Data { x: 0, y: 0, z: 0 }
291 }
292 }
293
294 #[test]
295 fn test_iter_cubes_mut() {
296 let mut sh: SpatialHashGrid<u32> = SpatialHashGrid::new(5, 5, 5, u32::default);
297
298 let sx = 3;
299 let sy = 4;
300 let sz = 5;
301 for (i, j, k) in itertools::iproduct!(0..sx, 0..sy, 0..sz) {
302 let pos = Vector3::new(i, j, k);
303 sh[pos] = 1;
304 }
305
306 let min = Vector3::new(0, 0, 0);
307 let b = Vector3::new(2, 1, 2);
308 let max = Vector3::new(sx, sy, sz);
309
310 for (_p, _i, d) in sh.iter_cubes_mut(min, b) {
311 *d += 1;
312 }
313
314 let mut count = 0;
315 for (_p, d) in sh.iter_cubes(min, max) {
316 count += *d;
317 }
318 assert_eq!(
319 count,
320 (sx * sy * sz) + (b.x + 1) * (b.y + 1) * (b.z + 1),
321 "Incorrect sum of values"
322 );
323 let mut count = 0;
324 for (p, idx, d) in sh.iter_cubes(min, max).with_index() {
325 assert_eq!(sh.pos_to_index(p).expect("position should be valid"), idx);
326 count += *d;
327 }
328 assert_eq!(
329 count,
330 (sx * sy * sz) + (b.x + 1) * (b.y + 1) * (b.z + 1),
331 "Incorrect sum of values"
332 );
333 }
334 #[test]
335 fn test_indexing() {
336 let mut sh: SpatialHashGrid<Option<u32>> = SpatialHashGrid::new(5, 5, 5, || None);
337
338 let p = Vector3 { x: 3, y: 3, z: 3 };
339 sh[p] = Some(42);
340 assert_eq!(sh[p], Some(42));
341 sh[p] = None;
342 assert_eq!(sh[p], None);
343 println!("{:?}", sh);
344 }
345 #[test]
346 fn test_debug_trait() {
347 let mut sh: SpatialHashGrid<Data> = SpatialHashGrid::new(3, 3, 2, Data::default);
348 let p = Vector3 { x: 1, y: 1, z: 1 };
349 sh[p].x = 42;
350 sh[p].y = 42;
351 sh[p].z = 42;
352 println!("{:?}", sh);
353 }
354}