pub mod builder;
pub mod lod_view;
pub mod sampling;
pub use builder::*;
pub use lod_view::*;
pub use sampling::*;
use crate::{
Array, ChunkIndexer, ChunkKey, ChunkReadStorage, ChunkWriteStorage, FillExtent, ForEach, Get,
GetMut, GetRef, IterChunkKeys, MultiRef,
};
use building_blocks_core::{bounding_extent, ExtentN, IntegerPoint, PointN};
use either::Either;
pub trait Chunk {
type Array;
fn array(&self) -> &Self::Array;
fn array_mut(&mut self) -> &mut Self::Array;
}
impl<N, Chan> Chunk for Array<N, Chan> {
type Array = Self;
#[inline]
fn array(&self) -> &Self::Array {
self
}
#[inline]
fn array_mut(&mut self) -> &mut Self::Array {
self
}
}
pub struct ChunkMap<N, T, Bldr, Store> {
pub indexer: ChunkIndexer<N>,
storage: Store,
builder: Bldr,
ambient_value: T, }
pub type ChunkMap2<T, Bldr, Store> = ChunkMap<[i32; 2], T, Bldr, Store>;
pub type ChunkMap3<T, Bldr, Store> = ChunkMap<[i32; 3], T, Bldr, Store>;
pub type ChunkMapNx1<N, T, Store> = ChunkMap<N, T, ChunkMapBuilderNx1<N, T>, Store>;
pub type ChunkMap2x1<T, Store> = ChunkMapNx1<[i32; 2], T, Store>;
pub type ChunkMap3x1<T, Store> = ChunkMapNx1<[i32; 3], T, Store>;
impl<N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store>
where
PointN<N>: IntegerPoint<N>,
Bldr: ChunkMapBuilder<N, T>,
{
fn new(builder: Bldr, storage: Store) -> Self {
let indexer = ChunkIndexer::new(builder.chunk_shape());
let ambient_value = builder.ambient_value();
Self {
indexer,
storage,
builder,
ambient_value,
}
}
}
impl<N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store> {
#[inline]
pub fn take_storage(self) -> Store {
self.storage
}
#[inline]
pub fn storage(&self) -> &Store {
&self.storage
}
#[inline]
pub fn storage_mut(&mut self) -> &mut Store {
&mut self.storage
}
#[inline]
pub fn builder(&self) -> &Bldr {
&self.builder
}
#[inline]
pub fn lod_view(&self, lod: u8) -> ChunkMapLodView<&'_ Self> {
ChunkMapLodView {
delegate: self,
lod,
}
}
#[inline]
pub fn lod_view_mut(&mut self, lod: u8) -> ChunkMapLodView<&'_ mut Self> {
ChunkMapLodView {
delegate: self,
lod,
}
}
}
impl<N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store>
where
Bldr: ChunkMapBuilder<N, T>,
{
#[inline]
pub fn chunk_shape(&self) -> PointN<N> {
self.builder().chunk_shape()
}
#[inline]
pub fn ambient_value(&self) -> T {
self.builder().ambient_value()
}
}
impl<N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store>
where
PointN<N>: IntegerPoint<N>,
Bldr: ChunkMapBuilder<N, T>,
Store: ChunkReadStorage<N, Bldr::Chunk>,
{
#[inline]
pub fn get_chunk(&self, key: ChunkKey<N>) -> Option<&Bldr::Chunk> {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.get(key)
}
#[inline]
pub fn clone_point(&self, lod: u8, p: PointN<N>) -> T
where
T: Clone,
<Bldr::Chunk as Chunk>::Array: Get<PointN<N>, Item = T>,
{
let chunk_min = self.indexer.min_of_chunk_containing_point(p);
self.get_chunk(ChunkKey::new(lod, chunk_min))
.map(|chunk| chunk.array().get(p))
.unwrap_or_else(|| self.ambient_value.clone())
}
#[inline]
pub fn get_point<'a, Ref>(&'a self, lod: u8, p: PointN<N>) -> Ref
where
<Bldr::Chunk as Chunk>::Array: GetRef<'a, PointN<N>, Item = Ref>,
Ref: MultiRef<'a, Data = T>,
{
let chunk_min = self.indexer.min_of_chunk_containing_point(p);
self.get_chunk(ChunkKey::new(lod, chunk_min))
.map(|chunk| chunk.array().get_ref(p))
.unwrap_or_else(|| Ref::from_data_ref(&self.ambient_value))
}
#[inline]
pub fn visit_chunks(
&self,
lod: u8,
extent: &ExtentN<N>,
mut visitor: impl FnMut(Either<&Bldr::Chunk, (&ExtentN<N>, AmbientExtent<N, T>)>),
) {
for chunk_min in self.indexer.chunk_mins_for_extent(extent) {
if let Some(chunk) = self.get_chunk(ChunkKey::new(lod, chunk_min)) {
visitor(Either::Left(chunk))
} else {
let chunk_extent = self.indexer.extent_for_chunk_with_min(chunk_min);
visitor(Either::Right((
&chunk_extent,
AmbientExtent::new(self.builder.ambient_value()),
)))
}
}
}
#[inline]
pub fn visit_occupied_chunks(
&self,
lod: u8,
extent: &ExtentN<N>,
mut visitor: impl FnMut(&Bldr::Chunk),
) {
for chunk_min in self.indexer.chunk_mins_for_extent(extent) {
if let Some(chunk) = self.get_chunk(ChunkKey::new(lod, chunk_min)) {
visitor(chunk)
}
}
}
}
impl<N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store>
where
PointN<N>: IntegerPoint<N>,
Bldr: ChunkMapBuilder<N, T>,
Store: ChunkWriteStorage<N, Bldr::Chunk>,
{
#[inline]
pub fn write_chunk(&mut self, key: ChunkKey<N>, chunk: Bldr::Chunk) {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.write(key, chunk);
}
#[inline]
pub fn replace_chunk(&mut self, key: ChunkKey<N>, chunk: Bldr::Chunk) -> Option<Bldr::Chunk> {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.replace(key, chunk)
}
#[inline]
pub fn get_mut_chunk(&mut self, key: ChunkKey<N>) -> Option<&mut Bldr::Chunk> {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.get_mut(key)
}
#[inline]
pub fn get_mut_chunk_or_insert_with(
&mut self,
key: ChunkKey<N>,
create_chunk: impl FnOnce() -> Bldr::Chunk,
) -> &mut Bldr::Chunk {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.get_mut_or_insert_with(key, create_chunk)
}
#[inline]
pub fn get_mut_chunk_or_insert_ambient(&mut self, key: ChunkKey<N>) -> &mut Bldr::Chunk {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
let Self {
indexer,
storage,
builder,
..
} = self;
let chunk_min = key.minimum;
storage.get_mut_or_insert_with(key, || {
builder.new_ambient(indexer.extent_for_chunk_with_min(chunk_min))
})
}
#[inline]
pub fn get_mut_point<'a, Mut>(&'a mut self, lod: u8, p: PointN<N>) -> Mut
where
<Bldr::Chunk as Chunk>::Array: GetMut<'a, PointN<N>, Item = Mut>,
{
let chunk_min = self.indexer.min_of_chunk_containing_point(p);
let chunk = self.get_mut_chunk_or_insert_ambient(ChunkKey::new(lod, chunk_min));
chunk.array_mut().get_mut(p)
}
#[inline]
pub fn visit_mut_chunks(
&mut self,
lod: u8,
extent: &ExtentN<N>,
mut visitor: impl FnMut(&mut Bldr::Chunk),
) {
for chunk_min in self.indexer.chunk_mins_for_extent(extent) {
visitor(self.get_mut_chunk_or_insert_ambient(ChunkKey::new(lod, chunk_min)));
}
}
#[inline]
pub fn visit_occupied_mut_chunks(
&mut self,
lod: u8,
extent: &ExtentN<N>,
mut visitor: impl FnMut(&mut Bldr::Chunk),
) {
for chunk_min in self.indexer.chunk_mins_for_extent(extent) {
if let Some(chunk) = self.get_mut_chunk(ChunkKey::new(lod, chunk_min)) {
visitor(chunk)
}
}
}
#[inline]
pub fn delete_chunk(&mut self, key: ChunkKey<N>) {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.delete(key);
}
#[inline]
pub fn pop_chunk(&mut self, key: ChunkKey<N>) -> Option<Bldr::Chunk> {
debug_assert!(self.indexer.chunk_min_is_valid(key.minimum));
self.storage.pop(key)
}
}
impl<N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store>
where
for<'r> ChunkMapLodView<&'r mut Self>: FillExtent<N, Item = T>,
{
#[inline]
pub fn fill_extent(&mut self, lod: u8, extent: &ExtentN<N>, value: T) {
self.lod_view_mut(lod).fill_extent(extent, value)
}
}
impl<'a, N, T, Bldr, Store> ChunkMap<N, T, Bldr, Store>
where
PointN<N>: IntegerPoint<N>,
Store: IterChunkKeys<'a, N>,
{
pub fn bounding_extent(&'a self, lod: u8) -> ExtentN<N> {
bounding_extent(
self.storage
.chunk_keys()
.filter(|key| key.lod == lod)
.flat_map(|key| {
let chunk_extent = self.indexer.extent_for_chunk_with_min(key.minimum);
vec![chunk_extent.minimum, chunk_extent.max()].into_iter()
}),
)
}
}
#[derive(Copy, Clone)]
pub struct AmbientExtent<N, T> {
pub value: T,
_n: std::marker::PhantomData<N>,
}
impl<N, T> AmbientExtent<N, T> {
pub fn new(value: T) -> Self {
Self {
value,
_n: Default::default(),
}
}
pub fn get(&self) -> T
where
T: Clone,
{
self.value.clone()
}
}
impl<N, T> ForEach<N, PointN<N>> for AmbientExtent<N, T>
where
T: Clone,
PointN<N>: IntegerPoint<N>,
{
type Item = T;
fn for_each(&self, extent: &ExtentN<N>, mut f: impl FnMut(PointN<N>, Self::Item)) {
for p in extent.iter_points() {
f(p, self.value.clone());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{access_traits::*, Array3x1};
use building_blocks_core::prelude::*;
const CHUNK_SHAPE: Point3i = PointN([16; 3]);
const BUILDER: ChunkMapBuilder3x1<i32> = ChunkMapBuilder3x1::new(CHUNK_SHAPE, 0);
#[test]
fn write_and_read_points() {
let mut map = BUILDER.build_with_hash_map_storage();
let mut lod0 = map.lod_view_mut(0);
let points = [
[0, 0, 0],
[1, 2, 3],
[16, 0, 0],
[0, 16, 0],
[0, 0, 16],
[15, 0, 0],
[-15, 0, 0],
];
for p in points.iter().cloned() {
assert_eq!(lod0.get_mut(PointN(p)), &mut 0);
*lod0.get_mut(PointN(p)) = 1;
assert_eq!(lod0.get_mut(PointN(p)), &mut 1);
}
}
#[test]
fn write_extent_with_for_each_then_read() {
let mut map = BUILDER.build_with_hash_map_storage();
let mut lod0 = map.lod_view_mut(0);
let write_extent = Extent3i::from_min_and_shape(Point3i::fill(10), Point3i::fill(80));
lod0.for_each_mut(&write_extent, |_p, value| *value = 1);
let read_extent = Extent3i::from_min_and_shape(Point3i::ZERO, Point3i::fill(100));
for p in read_extent.iter_points() {
if write_extent.contains(p) {
assert_eq!(lod0.get(p), 1);
} else {
assert_eq!(lod0.get(p), 0);
}
}
}
#[test]
fn copy_extent_from_array_then_read() {
let extent_to_copy = Extent3i::from_min_and_shape(Point3i::fill(10), Point3i::fill(80));
let array = Array3x1::fill(extent_to_copy, 1);
let mut map = BUILDER.build_with_hash_map_storage();
let mut lod0 = map.lod_view_mut(0);
copy_extent(&extent_to_copy, &array, &mut lod0);
let read_extent = Extent3i::from_min_and_shape(Point3i::ZERO, Point3i::fill(100));
for p in read_extent.iter_points() {
if extent_to_copy.contains(p) {
assert_eq!(lod0.get(p), 1);
} else {
assert_eq!(lod0.get(p), 0);
}
}
}
#[test]
fn multichannel_accessors() {
let builder = ChunkMapBuilder3x2::new(CHUNK_SHAPE, (0, 'a'));
let mut map = builder.build_with_hash_map_storage();
let mut lod0 = map.lod_view_mut(0);
assert_eq!(lod0.get(Point3i::fill(1)), (0, 'a'));
assert_eq!(lod0.get_ref(Point3i::fill(1)), (&0, &'a'));
assert_eq!(lod0.get_mut(Point3i::fill(1)), (&mut 0, &mut 'a'));
let extent = Extent3i::from_min_and_shape(Point3i::fill(10), Point3i::fill(80));
lod0.for_each_mut(&extent, |_p, (num, letter)| {
*num = 1;
*letter = 'b';
});
lod0.for_each(&extent, |_p, (num, letter)| {
assert_eq!(num, 1);
assert_eq!(letter, 'b');
});
map.fill_extent(0, &extent, (1, 'b'));
}
#[cfg(feature = "lz4")]
#[test]
fn multichannel_compressed_accessors() {
use crate::{FastCompressibleChunkStorageNx2, LocalChunkCache, Lz4};
let builder = ChunkMapBuilder3x2::new(CHUNK_SHAPE, (0, 'a'));
let mut map = builder.build_with_write_storage(
FastCompressibleChunkStorageNx2::with_bytes_compression(Lz4 { level: 10 }),
);
let mut lod0 = map.lod_view_mut(0);
assert_eq!(lod0.get_mut(Point3i::fill(1)), (&mut 0, &mut 'a'));
let extent = Extent3i::from_min_and_shape(Point3i::fill(10), Point3i::fill(80));
lod0.for_each_mut(&extent, |_p, (num, letter)| {
*num = 1;
*letter = 'b';
});
let local_cache = LocalChunkCache::new();
let reader = map.reader(&local_cache);
let lod0 = reader.lod_view(0);
assert_eq!(lod0.get(Point3i::fill(1)), (0, 'a'));
assert_eq!(lod0.get_ref(Point3i::fill(1)), (&0, &'a'));
lod0.for_each(&extent, |_p, (num, letter)| {
assert_eq!(num, 1);
assert_eq!(letter, 'b');
});
}
}