pub mod dirty;
pub mod stream;
use crate::{Real, RuntimeShape3};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChunkId {
pub coords: [i32; 3],
}
impl ChunkId {
#[must_use]
pub const fn new(coords: [i32; 3]) -> Self {
Self { coords }
}
#[must_use]
pub fn neighbour(self, axis: usize, step: i32) -> Self {
let mut coords = self.coords;
coords[axis] += step;
Self { coords }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ChunkLayout<R: Real> {
cells: u32,
cell_size: R,
origin: [R; 3],
}
impl<R: Real> ChunkLayout<R> {
pub fn new(cells: u32, cell_size: R, origin: [R; 3]) -> crate::Result<Self> {
if cells == 0 {
return Err(crate::Error::GridTooSmall {
size: [cells + 1; 3],
});
}
if !cell_size.is_finite() || cell_size <= R::ZERO {
return Err(crate::Error::InvalidCellSize {
value: f64::from(cell_size.as_f32()),
});
}
Ok(Self {
cells,
cell_size,
origin,
})
}
#[must_use]
pub fn cells(&self) -> u32 {
self.cells
}
#[must_use]
pub fn cell_size(&self) -> R {
self.cell_size
}
pub fn at_lod(&self, level: u32) -> crate::Result<Self> {
let mut cell_size = self.cell_size;
for _ in 0..level {
cell_size = cell_size + cell_size;
}
Self::new(self.cells, cell_size, self.origin)
}
pub fn sample_shape(&self) -> crate::Result<RuntimeShape3> {
RuntimeShape3::new([self.cells + 1; 3])
}
#[must_use]
pub fn base_sample(&self, id: ChunkId) -> [i64; 3] {
let n = i64::from(self.cells);
[
i64::from(id.coords[0]) * n,
i64::from(id.coords[1]) * n,
i64::from(id.coords[2]) * n,
]
}
#[must_use]
pub fn world_of_sample(&self, sample: [i64; 3]) -> [R; 3] {
[
self.origin[0] + self.cell_size * R::from_f64(sample[0] as f64),
self.origin[1] + self.cell_size * R::from_f64(sample[1] as f64),
self.origin[2] + self.cell_size * R::from_f64(sample[2] as f64),
]
}
#[must_use]
pub fn sample_origin(&self, id: ChunkId) -> [R; 3] {
self.world_of_sample(self.base_sample(id))
}
#[must_use]
pub fn chunk_of(&self, point: [R; 3]) -> ChunkId {
let inv = self.cell_size.recip();
let n = R::from_f64(f64::from(self.cells));
let mut coords = [0i32; 3];
for (axis, slot) in coords.iter_mut().enumerate() {
let cell = ((point[axis] - self.origin[axis]) * inv / n).floor();
*slot = if cell.is_finite() {
cell.as_f32() as i32
} else {
0
};
}
ChunkId { coords }
}
#[must_use]
pub fn cell_of(&self, point: [R; 3]) -> [i64; 3] {
let inv = self.cell_size.recip();
let mut cells = [0i64; 3];
for (axis, slot) in cells.iter_mut().enumerate() {
let cell = ((point[axis] - self.origin[axis]) * inv).floor();
*slot = if cell.is_finite() {
cell.as_f32() as i64
} else {
0
};
}
cells
}
#[must_use]
pub fn global_sample(&self, id: ChunkId, local: [u32; 3]) -> [i64; 3] {
let base = self.base_sample(id);
[
base[0] + i64::from(local[0]),
base[1] + i64::from(local[1]),
base[2] + i64::from(local[2]),
]
}
#[must_use]
pub fn local_sample(&self, sample: [i64; 3]) -> (ChunkId, [u32; 3]) {
let n = i64::from(self.cells);
let mut coords = [0i32; 3];
let mut local = [0u32; 3];
for axis in 0..3 {
let c = sample[axis].div_euclid(n);
let l = sample[axis].rem_euclid(n);
coords[axis] = c as i32;
local[axis] = l as u32;
}
(ChunkId { coords }, local)
}
}
#[cfg(test)]
mod tests;