#[allow(dead_code)]
mod flatindex;
#[allow(dead_code)]
mod iters;
#[allow(dead_code)]
mod storage;
#[allow(dead_code)]
pub mod util;
use crate::ParticleLike;
#[cfg(feature = "rayon")]
use crate::rayon::ParallelIterator;
use flatindex::FlatIndex;
use hashbrown::HashMap;
#[doc(inline)]
pub use iters::GridCell;
pub use iters::neighborhood;
use nalgebra::SimdPartialOrd;
use num_traits::{AsPrimitive, ConstOne, ConstZero, Float, NumAssignOps};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use storage::{CellSliceMeta, CellStorage};
#[doc(inline)]
pub use util::{Aabb, GridInfo};
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CellGrid<P, const N: usize = 3, T: Float = f64>
where
T: NumAssignOps + ConstOne + AsPrimitive<i32> + std::fmt::Debug,
{
cells: HashMap<i32, CellSliceMeta>,
cell_lists: CellStorage<P>,
index: FlatIndex<N, T>,
}
impl<P: ParticleLike<[T; N]>, const N: usize, T> CellGrid<P, N, T>
where
T: Float
+ NumAssignOps
+ ConstOne
+ ConstZero
+ AsPrimitive<i32>
+ SimdPartialOrd
+ std::fmt::Debug
+ Default,
{
pub fn new<I>(particles: I, cutoff: T) -> Self
where
I: IntoIterator<Item = P> + Clone,
P: Default,
{
CellGrid::default().rebuild(particles, Some(cutoff))
}
#[must_use = "rebuild() consumes `self` and returns the rebuilt `CellGrid`"]
pub fn rebuild<I>(self, particles: I, cutoff: Option<T>) -> Self
where
I: IntoIterator<Item = P> + Clone,
P: Default,
{
let cutoff = cutoff.unwrap_or(self.index.grid_info.cutoff);
let index = FlatIndex::from_particles(particles.clone(), cutoff);
let mut cell_lists = CellStorage::with_capacity(index.index.len());
let mut cells = if index == self.index {
self.cells
} else {
let mut cells: HashMap<i32, CellSliceMeta> = HashMap::new();
index.index.iter().for_each(|idx| {
cells.entry(*idx).or_default().move_cursor(1);
});
cells
};
cells.iter_mut().for_each(|(_, slice)| {
*slice = cell_lists.reserve_cell(slice.cursor());
});
index
.index
.iter()
.zip(particles)
.for_each(|(cell, particle)| {
cell_lists.push(
particle.clone(),
cells
.get_mut(cell)
.expect("cell grid should contain every cell in the grid index"),
)
});
Self {
cells,
cell_lists,
index,
}
}
pub fn rebuild_mut<I>(&mut self, particles: I, cutoff: Option<T>)
where
I: IntoIterator<Item = P> + Clone,
P: Default,
{
if self.index.rebuild_mut(particles.clone(), cutoff) {
self.cells.clear();
self.index.index.iter().for_each(|idx| {
self.cells.entry(*idx).or_default().move_cursor(1);
});
self.cells.shrink_to_fit();
}
self.cell_lists.clear();
self.cells.iter_mut().for_each(|(_, slice)| {
*slice = self.cell_lists.reserve_cell(slice.cursor());
});
self.index
.index
.iter()
.zip(particles)
.for_each(|(cell, particle)| {
self.cell_lists.push(
particle.clone(),
self.cells
.get_mut(cell)
.expect("cell grid should contain every cell in the grid index"),
)
});
}
}
impl<P: ParticleLike<[T; N]>, const N: usize, T> CellGrid<P, N, T>
where
T: Float + ConstOne + AsPrimitive<i32> + std::fmt::Debug + NumAssignOps,
{
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn particle_pairs(&self) -> impl Iterator<Item = (&P, &P)> + Clone {
self.iter().flat_map(|cell| cell.particle_pairs())
}
pub fn info(&self) -> &GridInfo<N, T> {
&self.index.grid_info
}
pub fn query<Q: ParticleLike<[T; N]>>(&self, particle: Q) -> Option<GridCell<'_, P, N, T>> {
self.info()
.try_cell_index(particle.coords())
.map(|index| self.info().flatten_index(index))
.map(|index| GridCell { grid: self, index })
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn query_neighbors<Q: ParticleLike<[T; N]>>(
&self,
particle: Q,
) -> Option<impl Iterator<Item = &P> + Clone> {
self.query(particle).map(|this| {
this.iter().chain(
this.neighbors::<neighborhood::Full>()
.flat_map(|cell| cell.iter()),
)
})
}
#[doc(hidden)]
pub fn cell_storage(&self) -> &[P] {
&self.cell_lists.buffer
}
}
#[cfg(feature = "rayon")]
impl<P, const N: usize, T> CellGrid<P, N, T>
where
T: Float + NumAssignOps + ConstOne + AsPrimitive<i32> + Sync + std::fmt::Debug,
P: ParticleLike<[T; N]> + Send + Sync,
{
pub fn par_particle_pairs(&self) -> impl ParallelIterator<Item = (&P, &P)> {
self.par_iter().flat_map_iter(|cell| cell.particle_pairs())
}
}