use crate::hash::component::CellHashMap;
use crate::hash::component::CellId;
use crate::hash::map::CellLookup;
use crate::hash::ChangedCells;
use crate::hash::SpatialHashFilter;
use crate::hash::SpatialHashSystems;
use crate::partition::map::PartitionLookup;
use crate::partition::PartitionId;
use bevy_app::prelude::*;
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*;
use bevy_ecs::world::FromWorld;
use core::marker::PhantomData;
pub struct PartitionChangePlugin<F: SpatialHashFilter = ()>(PhantomData<F>);
impl<F: SpatialHashFilter> PartitionChangePlugin<F> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl Default for PartitionChangePlugin<()> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<F: SpatialHashFilter> Plugin for PartitionChangePlugin<F> {
fn build(&self, app: &mut App) {
app.init_resource::<PartitionEntities<F>>().add_systems(
PostUpdate,
PartitionEntities::<F>::update
.in_set(SpatialHashSystems::UpdatePartitionChange)
.after(SpatialHashSystems::UpdatePartitionLookup),
);
}
}
#[derive(Resource)]
pub struct PartitionEntities<F: SpatialHashFilter = ()> {
pub map: EntityHashMap<PartitionId>,
pub changed: EntityHashMap<(Option<PartitionId>, Option<PartitionId>)>,
spooky: PhantomData<F>,
}
impl<F: SpatialHashFilter> FromWorld for PartitionEntities<F> {
fn from_world(_world: &mut World) -> Self {
Self {
map: Default::default(),
changed: Default::default(),
spooky: PhantomData,
}
}
}
impl<F: SpatialHashFilter> PartitionEntities<F> {
fn update(
mut this: ResMut<Self>,
cells: Res<CellLookup<F>>,
changed_cells: Res<ChangedCells<F>>,
all_hashes: Query<(Entity, &CellId), F>,
mut old_reverse: Local<CellHashMap<PartitionId>>,
partitions: Res<PartitionLookup<F>>,
) {
this.changed.clear();
for entity in changed_cells.iter() {
match all_hashes.get(*entity) {
Ok((entity_id, cell_hash)) => {
let new_pid = partitions.get(cell_hash).copied();
let old_pid = this.map.get(&entity_id).copied();
let partition_change = match (old_pid, new_pid) {
(Some(o), Some(n)) if o == n => None, (None, None) => None, other => Some(other), };
if let Some((from, to)) = partition_change {
if let Some((existing_from, existing_to)) = this.changed.get_mut(entity) {
if existing_from.is_none() {
*existing_from = from;
}
*existing_to = to;
} else {
this.changed.insert(*entity, (from, to));
}
}
}
Err(_) => {
if let Some(prev_pid) = this.map.get(entity).copied() {
this.changed.insert(*entity, (Some(prev_pid), None));
}
}
}
}
for (cell_id, entry) in cells.all_entries() {
if let Some(&new_pid) = partitions.get(cell_id) {
if let Some(&old_pid) = old_reverse.get(cell_id) {
if new_pid != old_pid {
for entity in entry.entities.iter().copied() {
this.changed
.entry(entity)
.or_insert((Some(old_pid), Some(new_pid)));
}
}
}
}
}
let PartitionEntities { map, changed, .. } = this.as_mut();
for (entity, (_source, destination)) in changed.iter() {
match destination {
Some(pid) => {
map.insert(*entity, *pid);
}
None => {
map.remove(entity);
}
};
}
*old_reverse = partitions.reverse_map.clone();
}
}