use crate::math::rect::Rect;
use crate::math::vect::Vect;
use std::collections::HashSet;
use std::hash::BuildHasherDefault;
use hashers::fnv::FNV1aHasher32;
use crate::entities::{FastHash};
use std::hash::Hash;
pub struct SpatialHasher<T: Hash + Eq + Copy + Clone> {
pub map: Vec<Vec<HashSet<T, FastHash>>>,
tile_size: Vect,
w: usize,
h: usize,
}
impl<T: Hash + Eq + Copy + Clone> SpatialHasher<T> {
pub fn new(w: usize, h: usize, tile_size: Vect) -> Self {
Self {map: vec![vec![HashSet::with_hasher(BuildHasherDefault::<FNV1aHasher32>::default()); w]; h], tile_size, w, h}
}
#[inline]
pub fn get_coord(&self, pos: Vect) -> (usize, usize) {
(clamp!((pos.x / self.tile_size.x) as i32, 0, self.w as i32-1) as usize,
clamp!((pos.y/self.tile_size.y) as i32, 0, self.h as i32 -1) as usize)
}
#[inline]
pub fn insert(&mut self, pos: Vect, id: T) -> (usize, usize) {
let (x, y) = self.get_coord(pos);
self.map[y][x].insert(id);
(x, y)
}
#[inline]
pub fn remove(&mut self, pos: (usize, usize), id: T) -> bool {
self.map[pos.1][pos.0].remove(&id)
}
pub fn slow_remove(&mut self, id: T) -> bool {
for row in self.map.iter_mut() {
for tile in row.iter_mut() {
if tile.remove(&id) {
return true;
}
}
}
false
}
#[inline]
pub fn update(&mut self, old: (usize, usize), new: Vect, id: T) -> (usize, usize) {
let new = self.get_coord(new);
if old == new {
return new;
}
if !self.map[old.1][old.0].remove(&id) {
return (usize::MAX, usize::MAX);
}
self.map[new.1][new.0].insert(id);
new
}
#[inline]
pub fn query(&self, rect: &Rect, collector: &mut Vec<T>) {
let mut min = self.get_coord(rect.min);
let mut max = self.get_coord(rect.max);
min = (
if min.0 == 0 {0} else { (min.0-1).min(self.w)},
if min.1 == 0 {0} else {(min.1-1).min(self.h)}
);
max = ((max.0+2).min(self.w), (max.1+2).min(self.h));
for y in min.1..max.1 {
for x in min.0..max.0 {
collector.extend(&self.map[y][x]);
}
}
}
#[inline]
pub fn query_point(&self, pos: Vect, collector: &mut Vec<T>) {
let pos = self.get_coord(pos);
let min = (
if pos.0 == 0 {0} else { (pos.0-1).min(self.w)},
if pos.1 == 0 {0} else {(pos.1-1).min(self.h)}
);
let max = ((pos.0+2).min(self.w),(pos.1+2).min(self.h));
for y in min.1..max.1 {
for x in min.0..max.0 {
collector.extend(&self.map[y][x]);
}
}
}
pub fn get_shape_count(&self) -> usize {
let mut count = 0;
for row in self.map.iter() {
for tile in row.iter() {
count += tile.len();
}
}
count
}
}
#[cfg(test)]
mod tests {
use crate::math::vect::Vect;
use rand::Rng;
use crate::entities::detection::spatial_hash::SpatialHasher;
#[test]
fn insert_test() {
let mut map: SpatialHasher<usize> = SpatialHasher::new(10, 10, Vect::new(100f32, 100f32));
let mut rng = rand::thread_rng();
for i in 0..100 {
map.insert(Vect::new(rng.gen::<f32>()*1000f32, rng.gen::<f32>()*1000f32), i);
}
assert_eq!(100 as usize, map.get_shape_count());
}
#[test]
fn remove_test() {
let mut map: SpatialHasher<usize> = SpatialHasher::new(10, 10, Vect::new(100f32, 100f32));
let mut rng = rand::thread_rng();
let mut poss = Vec::new();
let mut addresses = Vec::new();
for _ in 0..100 {
poss.push(Vect::new(rng.gen::<f32>()*1000f32, rng.gen::<f32>()*1000f32));
}
for i in 0..100 {
addresses.push(map.insert(poss[i], i));
}
for i in 0..100 {
map.remove(addresses[i], i);
}
assert_eq!(0 as usize, map.get_shape_count());
}
}