use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_platform::collections::HashMap;
use smallvec::SmallVec;
use crate::cell::GeoCell;
const BUCKET_INLINE: usize = 4;
type Bucket = SmallVec<[Entity; BUCKET_INLINE]>;
#[derive(Resource, Default, Debug)]
pub struct CellEntityIndex {
map: HashMap<u64, Bucket>,
last_cell: HashMap<Entity, u64>,
}
impl CellEntityIndex {
pub fn entities_in(&self, cell: u64) -> &[Entity] {
self.map.get(&cell).map(SmallVec::as_slice).unwrap_or(&[])
}
pub fn iter(&self) -> impl Iterator<Item = (&u64, &[Entity])> {
self.map.iter().map(|(c, b)| (c, b.as_slice()))
}
pub fn occupied_cell_count(&self) -> usize {
self.map.len()
}
fn insert(&mut self, entity: Entity, cell: u64) {
if let Some(prev) = self.last_cell.insert(entity, cell) {
if prev == cell {
return;
}
if let Some(bucket) = self.map.get_mut(&prev) {
bucket.retain(|e| *e != entity);
if bucket.is_empty() {
self.map.remove(&prev);
}
}
}
self.map.entry(cell).or_default().push(entity);
}
fn remove(&mut self, entity: Entity) {
if let Some(prev) = self.last_cell.remove(&entity)
&& let Some(bucket) = self.map.get_mut(&prev)
{
bucket.retain(|e| *e != entity);
if bucket.is_empty() {
self.map.remove(&prev);
}
}
}
}
pub struct CellHashPlugin;
impl Plugin for CellHashPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<CellEntityIndex>();
app.add_systems(PostUpdate, update_cell_entity_index);
}
}
pub fn update_cell_entity_index(
mut index: ResMut<CellEntityIndex>,
changed: Query<(Entity, &GeoCell), Changed<GeoCell>>,
mut removed: RemovedComponents<GeoCell>,
) {
for entity in removed.read() {
index.remove(entity);
}
for (entity, cell) in changed.iter() {
index.insert(entity, cell.raw());
}
}