rustpix_algorithms/
spatial.rs1#[derive(Debug, Default)]
10pub struct SpatialGrid<T> {
11 cell_size: usize,
12 width_cells: usize,
13 height_cells: usize,
14 cells: Vec<Vec<T>>,
16}
17
18impl<T: Clone> SpatialGrid<T> {
19 #[must_use]
26 pub fn new(cell_size: usize, width: usize, height: usize) -> Self {
27 let cell_size = cell_size.max(1);
29
30 let width_cells = width.div_ceil(cell_size);
32 let height_cells = height.div_ceil(cell_size);
33 let total_cells = width_cells * height_cells;
34
35 let mut cells = Vec::with_capacity(total_cells);
37 for _ in 0..total_cells {
38 cells.push(Vec::with_capacity(4)); }
40
41 Self {
42 cell_size,
43 width_cells,
44 height_cells,
45 cells,
46 }
47 }
48
49 pub fn clear(&mut self) {
51 for cell in &mut self.cells {
52 cell.clear();
53 }
54 }
55
56 pub fn ensure_dimensions(&mut self, width: usize, height: usize) {
60 let req_width_cells = width.div_ceil(self.cell_size);
61 let req_height_cells = height.div_ceil(self.cell_size);
62
63 if req_width_cells > self.width_cells || req_height_cells > self.height_cells {
64 let new_width_cells = req_width_cells.max(self.width_cells);
65 let new_height_cells = req_height_cells.max(self.height_cells);
66 let total_cells = new_width_cells * new_height_cells;
67
68 self.width_cells = new_width_cells;
69 self.height_cells = new_height_cells;
70
71 self.cells = Vec::with_capacity(total_cells);
73 for _ in 0..total_cells {
74 self.cells.push(Vec::with_capacity(4));
75 }
76 }
77 }
78
79 #[inline]
83 pub fn insert(&mut self, x: i32, y: i32, value: T) {
84 if x < 0 || y < 0 {
85 return;
86 }
87
88 let cx = usize::try_from(x).unwrap_or(0) / self.cell_size;
89 let cy = usize::try_from(y).unwrap_or(0) / self.cell_size;
90
91 if cx < self.width_cells && cy < self.height_cells {
92 let idx = cy * self.width_cells + cx;
93 if let Some(cell) = self.cells.get_mut(idx) {
94 cell.push(value);
95 }
96 }
97 }
98
99 #[inline]
101 fn get_cell_index(&self, cx: i32, cy: i32) -> Option<usize> {
102 if cx < 0 || cy < 0 {
103 return None;
104 }
105 let cx = usize::try_from(cx).ok()?;
106 let cy = usize::try_from(cy).ok()?;
107
108 if cx < self.width_cells && cy < self.height_cells {
109 Some(cy * self.width_cells + cx)
110 } else {
111 None
112 }
113 }
114
115 pub fn remove(&mut self, x: i32, y: i32, value: &T)
117 where
118 T: PartialEq,
119 {
120 if let Some(idx) = self.get_cell_index(x, y) {
121 if let Some(cell) = self.cells.get_mut(idx) {
122 if let Some(pos) = cell.iter().position(|item| item == value) {
123 cell.swap_remove(pos);
124 }
125 }
126 }
127 }
128
129 #[inline]
131 #[must_use]
132 pub fn get_cell_slice(&self, x: i32, y: i32) -> Option<&[T]> {
133 if x < 0 || y < 0 {
134 return None;
135 }
136 let cell_size = i32::try_from(self.cell_size).unwrap_or(i32::MAX);
137 let cx = x / cell_size;
138 let cy = y / cell_size;
139 self.get_cell_index(cx, cy)
140 .and_then(|idx| self.cells.get(idx).map(Vec::as_slice))
141 }
142
143 #[inline]
145 #[must_use]
146 pub fn width_cells(&self) -> usize {
147 self.width_cells
148 }
149
150 #[inline]
152 #[must_use]
153 pub fn height_cells(&self) -> usize {
154 self.height_cells
155 }
156
157 #[inline]
159 #[must_use]
160 pub fn cell_size(&self) -> usize {
161 self.cell_size
162 }
163
164 #[inline]
168 pub fn query_neighborhood(&self, x: i32, y: i32, buffer: &mut Vec<T>) {
169 let cell_size = i32::try_from(self.cell_size).unwrap_or(i32::MAX);
170 let cx = x / cell_size;
171 let cy = y / cell_size;
172 let height_cells_i32 = i32::try_from(self.height_cells).unwrap_or(i32::MAX);
173 let width_cells_i32 = i32::try_from(self.width_cells).unwrap_or(i32::MAX);
174
175 for dy in -1..=1 {
177 let ny = cy + dy;
178 if ny < 0 || ny >= height_cells_i32 {
179 continue;
180 }
181
182 for dx in -1..=1 {
183 let nx = cx + dx;
184 if nx < 0 || nx >= width_cells_i32 {
185 continue;
186 }
187
188 let (Ok(row_idx), Ok(col_idx)) = (usize::try_from(ny), usize::try_from(nx)) else {
189 continue;
190 };
191 let idx = row_idx * self.width_cells + col_idx;
192 if let Some(cell) = self.cells.get(idx) {
193 buffer.extend_from_slice(cell);
194 }
195 }
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_spatial_grid() {
206 let mut grid: SpatialGrid<usize> = SpatialGrid::new(32, 512, 512);
207 grid.insert(100, 100, 0);
208 grid.insert(105, 105, 1);
209 grid.insert(300, 300, 2);
210
211 let mut neighbors = Vec::new();
212 grid.query_neighborhood(100, 100, &mut neighbors);
213
214 assert!(neighbors.contains(&0));
215 assert!(neighbors.contains(&1));
216 assert!(!neighbors.contains(&2));
217 }
218
219 #[test]
220 fn test_spatial_grid_boundaries() {
221 let mut grid: SpatialGrid<usize> = SpatialGrid::new(50, 200, 200);
222
223 grid.insert(0, 0, 1);
225 grid.insert(199, 199, 2);
226 grid.insert(200, 200, 3); let mut neighbors = Vec::new();
229 grid.query_neighborhood(0, 0, &mut neighbors);
230 assert_eq!(neighbors.len(), 1);
231 assert_eq!(neighbors[0], 1);
232
233 neighbors.clear();
234 grid.query_neighborhood(199, 199, &mut neighbors);
235 assert_eq!(neighbors.len(), 1);
236 assert_eq!(neighbors[0], 2);
237 }
238}