use crate::grid::cell::CellCoord;
use crate::hash::component::CellHashSet;
use crate::hash::component::CellId;
use crate::precision::GridPrecision;
use bevy_ecs::prelude::*;
use bevy_platform::prelude::*;
#[derive(Debug)]
pub struct Partition {
grid: Entity,
tables: Vec<CellHashSet>,
min: CellCoord,
max: CellCoord,
}
impl Partition {
#[inline]
pub fn contains(&self, hash: &CellId) -> bool {
self.tables.iter().any(|table| table.contains(hash))
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &CellId> {
self.tables.iter().flat_map(|table| table.iter())
}
#[inline]
pub fn num_cells(&self) -> usize {
self.tables.iter().map(CellHashSet::len).sum()
}
#[inline]
pub fn grid(&self) -> Entity {
self.grid
}
pub fn max(&self) -> CellCoord {
self.max
}
pub fn min(&self) -> CellCoord {
self.min
}
pub fn is_empty(&self) -> bool {
self.tables.is_empty()
}
}
impl Partition {
pub(crate) fn new(
grid: Entity,
tables: Vec<CellHashSet>,
min: CellCoord,
max: CellCoord,
) -> Self {
Self {
grid,
min,
max,
tables,
}
}
#[inline]
fn smallest_table(&self) -> Option<usize> {
self.tables
.iter()
.enumerate()
.map(|(i, t)| (i, t.len()))
.min_by_key(|(_, len)| *len)
.map(|(i, _len)| i)
}
const MIN_TABLE_SIZE: usize = 20_000;
#[inline]
pub(crate) fn insert(&mut self, cell: CellId) {
if self.contains(&cell) {
return;
}
if let Some(i) = self.smallest_table() {
self.tables[i].insert(cell);
} else {
let mut table = CellHashSet::default();
table.insert(cell);
self.tables.push(table);
}
self.min = self.min.min(cell.coord());
self.max = self.max.max(cell.coord());
}
#[inline]
pub(crate) fn extend(&mut self, mut other: Partition) {
assert_eq!(self.grid, other.grid);
for other_table in other.tables.drain(..) {
if other_table.len() < Self::MIN_TABLE_SIZE {
if let Some(i) = self.smallest_table() {
self.tables[i].reserve(other_table.len());
self.tables[i].extend(other_table);
} else {
self.tables.push(other_table);
}
} else {
self.tables.push(other_table);
}
}
self.min = self.min.min(other.min);
self.max = self.max.max(other.max);
}
#[inline]
pub(crate) fn remove(&mut self, cell: &CellId) -> bool {
let Some(i_table) = self
.tables
.iter_mut()
.enumerate()
.find_map(|(i, table)| table.remove(cell).then_some(i))
else {
return false;
};
if self.tables[i_table].is_empty() {
self.tables.swap_remove(i_table);
}
let (removed, min, max) = (cell.coord(), self.min, self.max);
if min.x == removed.x || min.y == removed.y || min.z == removed.z {
self.compute_min();
}
if max.x == removed.x || max.y == removed.y || max.z == removed.z {
self.compute_max();
}
true
}
#[inline]
fn compute_min(&mut self) {
if let Some(min) = self.iter().map(CellId::coord).reduce(|acc, e| acc.min(e)) {
self.min = min;
} else {
self.min = CellCoord::ONE * 1e10f64 as GridPrecision;
}
}
#[inline]
fn compute_max(&mut self) {
if let Some(max) = self.iter().map(CellId::coord).reduce(|acc, e| acc.max(e)) {
self.max = max;
} else {
self.min = CellCoord::ONE * -1e10 as GridPrecision;
}
}
}