use rustsim_broadphase::UniformGrid2;
use crate::common::{Pedestrian, Vec2};
#[derive(Debug, Clone)]
pub struct NeighborGrid {
grid: UniformGrid2,
}
impl NeighborGrid {
pub fn new(cell_size: f64) -> Self {
Self {
grid: UniformGrid2::new(cell_size),
}
}
#[inline]
pub fn clear(&mut self) {
self.grid.clear();
}
pub fn rebuild(&mut self, peds: &[Pedestrian]) {
self.grid.clear();
for (i, p) in peds.iter().enumerate() {
self.grid.insert(i as u64, p.pos);
}
}
#[inline]
pub fn for_each_neighbor<F>(&self, i: usize, radius: f64, peds: &[Pedestrian], mut f: F)
where
F: FnMut(usize, &Pedestrian),
{
let pos_i = peds[i].pos;
self.grid.for_each_within(pos_i, radius, |k, _pos_j| {
let j = k as usize;
if j == i {
return;
}
if j >= peds.len() {
return;
}
f(j, &peds[j]);
});
}
#[inline]
pub fn inner(&self) -> &UniformGrid2 {
&self.grid
}
}
#[inline]
pub fn recommended_cell_size(radius: f64) -> f64 {
radius.max(0.1)
}
#[derive(Debug, Clone)]
pub struct Scratch {
pub(crate) buf: Vec<Vec2>,
pub grid: NeighborGrid,
}
impl Scratch {
#[inline]
pub fn new(cell_size: f64) -> Self {
Self {
buf: Vec::new(),
grid: NeighborGrid::new(cell_size),
}
}
#[inline]
pub fn with_capacity(n: usize, cell_size: f64) -> Self {
Self {
buf: Vec::with_capacity(n),
grid: NeighborGrid::new(cell_size),
}
}
#[inline]
pub fn clear(&mut self) {
self.buf.clear();
self.grid.clear();
}
#[inline]
pub(crate) fn prepare(&mut self, peds: &[Pedestrian]) {
let n = peds.len();
self.buf.clear();
self.buf.resize(n, [0.0, 0.0]);
self.grid.rebuild(peds);
}
#[inline]
pub(crate) fn split(&mut self) -> (&mut [Vec2], &NeighborGrid) {
(&mut self.buf, &self.grid)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ped_at(x: f64, y: f64) -> Pedestrian {
Pedestrian {
pos: [x, y],
vel: [0.0, 0.0],
radius: 0.25,
desired_speed: 1.34,
destination: [x + 10.0, y],
}
}
#[test]
fn rebuild_and_query_returns_only_nearby() {
let peds = vec![
ped_at(0.0, 0.0),
ped_at(0.3, 0.0),
ped_at(5.0, 0.0),
ped_at(0.0, 0.5),
];
let mut grid = NeighborGrid::new(1.0);
grid.rebuild(&peds);
let mut hits = Vec::new();
grid.for_each_neighbor(0, 1.0, &peds, |j, _| hits.push(j));
hits.sort();
assert_eq!(hits, vec![1, 3]);
}
#[test]
fn skips_self_index() {
let peds = vec![ped_at(0.0, 0.0), ped_at(0.1, 0.0)];
let mut grid = NeighborGrid::new(1.0);
grid.rebuild(&peds);
let mut hits = Vec::new();
grid.for_each_neighbor(0, 5.0, &peds, |j, _| hits.push(j));
assert_eq!(hits, vec![1]);
}
#[test]
fn empty_slice_is_safe() {
let peds: Vec<Pedestrian> = Vec::new();
let mut grid = NeighborGrid::new(1.0);
grid.rebuild(&peds);
}
#[test]
fn scratch_reuses_capacity_across_ticks() {
let peds = vec![ped_at(0.0, 0.0), ped_at(0.5, 0.0), ped_at(1.0, 0.0)];
let mut scratch = Scratch::with_capacity(3, 1.0);
scratch.prepare(&peds);
assert_eq!(scratch.buf.len(), 3);
let cap_after_first = scratch.buf.capacity();
scratch.prepare(&peds);
assert_eq!(scratch.buf.len(), 3);
assert_eq!(scratch.buf.capacity(), cap_after_first);
}
}