use crate::cell::{CellObject, GridCell};
use crate::storage::{cell_range, CellIdx, SparseStorage};
use crate::Vec2;
use slotmapd::{new_key_type, SlotMap};
use std::marker::PhantomData;
pub type GridObjects<O, V2> = SlotMap<GridHandle, StoreObject<O, V2>>;
new_key_type! {
pub struct GridHandle;
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ObjectState<V2: Vec2> {
Unchanged,
NewPos(V2),
Relocate(V2, CellIdx),
Removed,
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StoreObject<O, V2: Vec2> {
obj: O,
pub state: ObjectState<V2>,
pub pos: V2,
pub cell_id: CellIdx,
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Grid<O, V2: Vec2> {
storage: SparseStorage<GridCell<V2>>,
objects: GridObjects<O, V2>,
to_relocate: Vec<CellObject<V2>>,
_phantom: PhantomData<V2>,
}
impl<O: Copy, V2: Vec2> Grid<O, V2> {
pub fn new(cell_size: i32) -> Self {
Self {
storage: SparseStorage::new(cell_size),
objects: SlotMap::with_key(),
to_relocate: vec![],
_phantom: Default::default(),
}
}
pub fn insert(&mut self, pos: V2, obj: O) -> GridHandle {
let (cell_id, cell) = self.storage.cell_mut(pos);
let handle = self.objects.insert(StoreObject {
obj,
state: ObjectState::Unchanged,
pos,
cell_id,
});
cell.objs.push((handle, pos));
handle
}
pub fn set_position(&mut self, handle: GridHandle, pos: V2) {
let obj = match self.objects.get_mut(handle) {
Some(x) => x,
None => {
debug_assert!(false, "Object not in grid anymore");
return;
}
};
if matches!(obj.state, ObjectState::Removed) {
return;
}
let target_id = self.storage.cell_id(pos);
obj.state = if target_id == obj.cell_id {
ObjectState::NewPos(pos)
} else {
ObjectState::Relocate(pos, target_id)
};
self.storage.cell_mut_unchecked(obj.cell_id).dirty = true;
}
pub fn remove(&mut self, handle: GridHandle) -> Option<O> {
let obj = self.objects.get_mut(handle)?;
obj.state = ObjectState::Removed;
self.storage.cell_mut_unchecked(obj.cell_id).dirty = true;
Some(obj.obj)
}
pub fn remove_maintain(&mut self, handle: GridHandle) -> Option<O> {
let obj = self.objects.remove(handle)?;
let cell = self.storage.cell_mut_unchecked(obj.cell_id);
for i in 0..cell.objs.len() {
if cell.objs[i].0 == handle {
cell.objs.swap_remove(i);
break;
}
}
Some(obj.obj)
}
pub fn clear(&mut self) -> impl Iterator<Item = (V2, O)> {
let objects = std::mem::take(&mut self.objects);
self.storage = SparseStorage::new(self.storage.cell_size());
self.to_relocate.clear();
objects.into_iter().map(|(_, x)| (x.pos, x.obj))
}
pub fn maintain(&mut self) {
let Self {
storage,
objects,
to_relocate,
..
} = self;
storage.modify(|cell| {
cell.maintain(objects, to_relocate);
cell.objs.is_empty()
});
for (handle, pos) in to_relocate.drain(..) {
storage.cell_mut(pos).1.objs.push((handle, pos));
}
}
pub fn maintain_deterministic(&mut self) {
let Self {
storage,
objects,
to_relocate,
..
} = self;
storage.modify(|cell| {
cell.maintain(objects, to_relocate);
cell.objs.is_empty()
});
to_relocate.sort_unstable_by_key(|obj| obj.0);
for (handle, pos) in to_relocate.drain(..) {
storage.cell_mut(pos).1.objs.push((handle, pos));
}
}
pub fn handles(&self) -> impl Iterator<Item = GridHandle> + '_ {
self.objects.keys()
}
pub fn objects(&self) -> impl Iterator<Item = (V2, &O)> + '_ {
self.objects.values().map(|x| (x.pos, &x.obj))
}
pub fn get(&self, id: GridHandle) -> Option<(V2, &O)> {
self.objects.get(id).map(|x| (x.pos, &x.obj))
}
pub fn get_mut(&mut self, id: GridHandle) -> Option<(V2, &mut O)> {
self.objects.get_mut(id).map(|x| (x.pos, &mut x.obj))
}
pub fn storage(&self) -> &SparseStorage<GridCell<V2>> {
&self.storage
}
pub fn query_around(&self, pos: V2, radius: f32) -> impl Iterator<Item = CellObject<V2>> + '_ {
let ll = [pos.x() - radius, pos.y() - radius];
let ur = [pos.x() + radius, pos.y() + radius];
let radius2 = radius * radius;
self.query(ll.into(), ur.into())
.filter(move |(_, pos_obj)| {
let x = pos_obj.x() - pos.x();
let y = pos_obj.y() - pos.y();
x * x + y * y < radius2
})
}
pub fn query_aabb(&self, ll_: V2, ur_: V2) -> impl Iterator<Item = CellObject<V2>> + '_ {
let ll = [ll_.x().min(ur_.x()), ll_.y().min(ur_.y())];
let ur = [ll_.x().max(ur_.x()), ll_.y().max(ur_.y())];
self.query(ll.into(), ur.into())
.filter(move |(_, pos_obj)| {
(ll[0]..=ur[0]).contains(&pos_obj.x()) && (ll[1]..=ur[1]).contains(&pos_obj.y())
})
}
pub fn query_aabb_visitor(&self, ll_: V2, ur_: V2, mut visitor: impl FnMut(CellObject<V2>)) {
let ll = [ll_.x().min(ur_.x()), ll_.y().min(ur_.y())];
let ur = [ll_.x().max(ur_.x()), ll_.y().max(ur_.y())];
self.query_visitor(ll.into(), ur.into(), move |cell| {
if (ll[0]..=ur[0]).contains(&cell.1.x()) && (ll[1]..=ur[1]).contains(&cell.1.y()) {
visitor(cell)
}
});
}
pub fn query(&self, ll: V2, ur: V2) -> impl Iterator<Item = CellObject<V2>> + '_ {
let ll_id = self.storage.cell_id(ll);
let ur_id = self.storage.cell_id(ur);
cell_range(ll_id, ur_id)
.flat_map(move |id| self.storage.cell(id))
.flat_map(|x| x.objs.iter().copied())
}
pub fn query_visitor(&self, ll: V2, ur: V2, mut visitor: impl FnMut(CellObject<V2>)) {
let ll_id = self.storage.cell_id(ll);
let ur_id = self.storage.cell_id(ur);
for celly in ll_id.1..=ur_id.1 {
for cellx in ll_id.0..=ur_id.0 {
let cell = match self.storage.cell((cellx, celly)) {
Some(x) => x,
None => continue,
};
for h in cell.objs.iter() {
visitor(*h);
}
}
}
}
pub fn len(&self) -> usize {
self.objects.len()
}
pub fn is_empty(&self) -> bool {
self.objects.is_empty()
}
}