#[derive(Debug, Default)]
pub struct SpatialGrid<T> {
cell_size: usize,
width_cells: usize,
height_cells: usize,
cells: Vec<Vec<T>>,
}
impl<T: Clone> SpatialGrid<T> {
#[must_use]
pub fn new(cell_size: usize, width: usize, height: usize) -> Self {
let cell_size = cell_size.max(1);
let width_cells = width.div_ceil(cell_size);
let height_cells = height.div_ceil(cell_size);
let total_cells = width_cells * height_cells;
let mut cells = Vec::with_capacity(total_cells);
for _ in 0..total_cells {
cells.push(Vec::with_capacity(4)); }
Self {
cell_size,
width_cells,
height_cells,
cells,
}
}
pub fn clear(&mut self) {
for cell in &mut self.cells {
cell.clear();
}
}
pub fn ensure_dimensions(&mut self, width: usize, height: usize) {
let req_width_cells = width.div_ceil(self.cell_size);
let req_height_cells = height.div_ceil(self.cell_size);
if req_width_cells > self.width_cells || req_height_cells > self.height_cells {
let new_width_cells = req_width_cells.max(self.width_cells);
let new_height_cells = req_height_cells.max(self.height_cells);
let total_cells = new_width_cells * new_height_cells;
self.width_cells = new_width_cells;
self.height_cells = new_height_cells;
self.cells = Vec::with_capacity(total_cells);
for _ in 0..total_cells {
self.cells.push(Vec::with_capacity(4));
}
}
}
#[inline]
pub fn insert(&mut self, x: i32, y: i32, value: T) {
if x < 0 || y < 0 {
return;
}
let cx = usize::try_from(x).unwrap_or(0) / self.cell_size;
let cy = usize::try_from(y).unwrap_or(0) / self.cell_size;
if cx < self.width_cells && cy < self.height_cells {
let idx = cy * self.width_cells + cx;
if let Some(cell) = self.cells.get_mut(idx) {
cell.push(value);
}
}
}
#[inline]
fn get_cell_index(&self, cx: i32, cy: i32) -> Option<usize> {
if cx < 0 || cy < 0 {
return None;
}
let cx = usize::try_from(cx).ok()?;
let cy = usize::try_from(cy).ok()?;
if cx < self.width_cells && cy < self.height_cells {
Some(cy * self.width_cells + cx)
} else {
None
}
}
pub fn remove(&mut self, x: i32, y: i32, value: &T)
where
T: PartialEq,
{
if let Some(idx) = self.get_cell_index(x, y) {
if let Some(cell) = self.cells.get_mut(idx) {
if let Some(pos) = cell.iter().position(|item| item == value) {
cell.swap_remove(pos);
}
}
}
}
#[inline]
#[must_use]
pub fn get_cell_slice(&self, x: i32, y: i32) -> Option<&[T]> {
if x < 0 || y < 0 {
return None;
}
let cell_size = i32::try_from(self.cell_size).unwrap_or(i32::MAX);
let cx = x / cell_size;
let cy = y / cell_size;
self.get_cell_index(cx, cy)
.and_then(|idx| self.cells.get(idx).map(Vec::as_slice))
}
#[inline]
#[must_use]
pub fn width_cells(&self) -> usize {
self.width_cells
}
#[inline]
#[must_use]
pub fn height_cells(&self) -> usize {
self.height_cells
}
#[inline]
#[must_use]
pub fn cell_size(&self) -> usize {
self.cell_size
}
#[inline]
pub fn query_neighborhood(&self, x: i32, y: i32, buffer: &mut Vec<T>) {
let cell_size = i32::try_from(self.cell_size).unwrap_or(i32::MAX);
let cx = x / cell_size;
let cy = y / cell_size;
let height_cells_i32 = i32::try_from(self.height_cells).unwrap_or(i32::MAX);
let width_cells_i32 = i32::try_from(self.width_cells).unwrap_or(i32::MAX);
for dy in -1..=1 {
let ny = cy + dy;
if ny < 0 || ny >= height_cells_i32 {
continue;
}
for dx in -1..=1 {
let nx = cx + dx;
if nx < 0 || nx >= width_cells_i32 {
continue;
}
let (Ok(row_idx), Ok(col_idx)) = (usize::try_from(ny), usize::try_from(nx)) else {
continue;
};
let idx = row_idx * self.width_cells + col_idx;
if let Some(cell) = self.cells.get(idx) {
buffer.extend_from_slice(cell);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spatial_grid() {
let mut grid: SpatialGrid<usize> = SpatialGrid::new(32, 512, 512);
grid.insert(100, 100, 0);
grid.insert(105, 105, 1);
grid.insert(300, 300, 2);
let mut neighbors = Vec::new();
grid.query_neighborhood(100, 100, &mut neighbors);
assert!(neighbors.contains(&0));
assert!(neighbors.contains(&1));
assert!(!neighbors.contains(&2));
}
#[test]
fn test_spatial_grid_boundaries() {
let mut grid: SpatialGrid<usize> = SpatialGrid::new(50, 200, 200);
grid.insert(0, 0, 1);
grid.insert(199, 199, 2);
grid.insert(200, 200, 3);
let mut neighbors = Vec::new();
grid.query_neighborhood(0, 0, &mut neighbors);
assert_eq!(neighbors.len(), 1);
assert_eq!(neighbors[0], 1);
neighbors.clear();
grid.query_neighborhood(199, 199, &mut neighbors);
assert_eq!(neighbors.len(), 1);
assert_eq!(neighbors[0], 2);
}
}