use crate::coords::*;
use crate::util_funcs::*;
use slab::Slab;
use std::fmt::Debug;
use std::num::NonZeroU32;
use std::ops::ControlFlow;
pub(crate) type ChunkStorage<const N: usize, C, L> = Slab<ChunkContainer<N, C, L>>;
pub(crate) type NodeStorage<const B: usize> = Slab<TreeNode<B>>;
#[derive(Clone, Debug)]
pub struct Tree<const N: usize, const B: usize, C: Sized, L: LodVec<N>> {
pub(crate) chunks: ChunkStorage<N, C, L>,
pub(crate) nodes: NodeStorage<B>,
new_nodes: Slab<TreeNode<B>>,
}
pub enum Entry<'a, C: Sized> {
Occupied(&'a mut C),
Vacant(&'a mut C),
}
impl<const N: usize, const B: usize, C, L> Tree<N, B, C, L>
where
C: Sized,
L: LodVec<N>,
{
pub fn with_capacity_unsafe(nodes_capacity: usize, chunks_capacity: usize) -> Self {
assert_eq!(1<<N, B, "Relation between N and B is not met, once feature(generic_const_exprs) is mature this will be statically checked");
debug_assert!(nodes_capacity >= 1);
debug_assert!(chunks_capacity >= 1);
let mut nodes = Slab::with_capacity(nodes_capacity);
let r = nodes.insert(TreeNode::new());
debug_assert_eq!(r, 0);
Self {
chunks: Slab::with_capacity(chunks_capacity),
nodes,
new_nodes: Slab::new(),
}
}
fn follow_nodes_to_position_mut(
&mut self,
position: L,
) -> Result<(usize, &mut TreeNode<B>), (usize, &mut TreeNode<B>)> {
let mut addr = TreePos {
idx: 0,
pos: L::root(),
};
debug_assert_ne!(position, addr.pos);
loop {
let current = unsafe {
(self.nodes.get_unchecked_mut(addr.idx) as *mut TreeNode<B>)
.as_mut()
.unwrap_unchecked()
};
let child_idx = addr.pos.get_child_index(position);
let child_pos = addr.pos.get_child(child_idx);
if child_pos == position {
return Ok((child_idx, current));
}
addr = match current.children[child_idx] {
Some(idx) => TreePos {
idx: idx.get() as usize,
pos: addr.pos.get_child(child_idx),
},
None => {
return Err((child_idx, current));
}
};
}
}
fn follow_nodes_to_position(
&self,
position: L,
) -> Result<(usize, &TreeNode<B>), (usize, &TreeNode<B>)> {
let mut addr = TreePos {
idx: 0,
pos: L::root(),
};
debug_assert_ne!(position, addr.pos);
loop {
let current = unsafe { self.nodes.get_unchecked(addr.idx) };
let child_idx = addr.pos.get_child_index(position);
let child_pos = addr.pos.get_child(child_idx);
if child_pos == position {
return Ok((child_idx, current));
}
addr = match current.children[child_idx] {
Some(idx) => TreePos {
idx: idx.get() as usize,
pos: addr.pos.get_child(child_idx),
},
None => {
return Err((child_idx, current));
}
};
}
}
#[inline]
pub fn get_num_chunks(&self) -> usize {
self.chunks.len()
}
#[inline]
pub fn get_chunk(&self, index: usize) -> &ChunkContainer<N, C, L> {
&self.chunks[index]
}
#[inline]
pub fn get_chunk_mut(&mut self, index: usize) -> &mut ChunkContainer<N, C, L> {
&mut self.chunks[index]
}
#[inline]
pub fn get_chunk_by_position(&self, position: L) -> Option<&C> {
let (idx, node) = self.follow_nodes_to_position(position).ok()?;
let chunk_index = node.chunk[idx].get()?;
Some(&self.chunks[chunk_index].chunk)
}
#[inline]
pub fn get_chunk_by_position_mut(&mut self, position: L) -> Option<&mut C> {
let (idx, node) = self.follow_nodes_to_position(position).ok()?;
let chunk_index = node.chunk[idx].get()?;
Some(&mut self.chunks[chunk_index].chunk)
}
#[inline]
pub fn entry<V>(&mut self, _position: L, mut _chunk_creator: V) -> Entry<C>
where
V: FnMut(L) -> C,
{
todo!()
}
#[inline]
pub fn get_chunk_position(&self, index: usize) -> Option<L> {
Some(self.chunks.get(index)?.position)
}
pub fn insert_many<T, V>(&mut self, mut targets: T, mut chunk_creator: V)
where
T: Iterator<Item = L>,
V: FnMut(L) -> C,
{
debug_assert!(!self.nodes.is_empty());
let mut queue = arrayvec::ArrayVec::<TreePos<N, L>, { MAX_DEPTH as usize }>::new();
let mut addr = TreePos {
pos: L::root(),
idx: 0,
};
let mut tgt = targets.next().expect("Expected at least one target");
loop {
debug_assert_ne!(tgt, L::root(), "Root node is not a valid target!");
loop {
addr = match self.insert_inner(addr, tgt, &mut chunk_creator) {
ControlFlow::Break(_) => {
break;
}
ControlFlow::Continue(a) => {
queue.push(addr);
a
}
};
}
tgt = match targets.next() {
Some(t) => t,
None => {
break;
}
};
debug_assert_ne!(tgt, L::root(), "Root node is not a valid target!");
while !addr.pos.contains_child_node(tgt) {
addr = queue
.pop()
.expect("This should not happen, as we keep root in the stack");
}
}
}
#[inline]
pub fn pop_chunk_by_position(&mut self, pos: L) -> Option<C> {
let (child, node) = self.follow_nodes_to_position_mut(pos).ok()?;
let chunk_idx = node.chunk[child].take()?;
let chunk_rec = self.chunks.remove(chunk_idx);
Some(chunk_rec.chunk)
}
#[inline]
fn insert_inner<V>(
&mut self,
addr: TreePos<N, L>,
tgt: L,
chunk_creator: &mut V,
) -> ControlFlow<usize, TreePos<N, L>>
where
V: FnMut(L) -> C,
{
let child_idx = addr.pos.get_child_index(tgt);
let child_pos = addr.pos.get_child(child_idx);
let current_node = self.nodes.get_mut(addr.idx).expect("Node index broken!");
if child_pos == tgt {
let chunk = chunk_creator(tgt);
let inserted = match current_node.chunk[child_idx].get() {
Some(ci) => {
self.chunks[ci].chunk = chunk;
ci
}
None => {
let chunk_idx = self.chunks.insert(ChunkContainer {
chunk,
position: child_pos,
node_idx: addr.idx as u32,
child_idx: child_idx as u8,
});
current_node.chunk[child_idx] = ChunkPtr::from(Some(chunk_idx));
chunk_idx
}
};
return ControlFlow::Break(inserted);
}
let idx = match current_node.children[child_idx] {
Some(idx) => idx.get() as usize,
None => {
let idx = self.nodes.insert(TreeNode::new());
self.nodes[addr.idx].children[child_idx] = NonZeroU32::new(idx as u32);
idx
}
};
ControlFlow::Continue(TreePos {
idx,
pos: child_pos,
})
}
pub fn insert<V>(&mut self, tgt: L, mut chunk_creator: V) -> usize
where
V: FnMut(L) -> C,
{
debug_assert!(!self.nodes.is_empty());
debug_assert_ne!(tgt, L::root(), "Root node is not a valid target!");
let mut addr = TreePos {
pos: L::root(),
idx: 0,
};
loop {
addr = match self.insert_inner(addr, tgt, &mut chunk_creator) {
ControlFlow::Continue(a) => a,
ControlFlow::Break(idx) => {
break idx;
}
};
}
}
#[inline]
pub fn iter_chunks_mut(&mut self) -> slab::IterMut<ChunkContainer<N, C, L>> {
self.chunks.iter_mut()
}
#[inline]
pub fn iter_chunks(&mut self) -> slab::Iter<ChunkContainer<N, C, L>> {
self.chunks.iter()
}
#[inline]
pub fn clear(&mut self) {
self.chunks.clear();
let root = self.nodes.remove(0);
self.nodes.clear();
self.nodes.insert(root);
}
#[inline]
pub fn defragment_chunks(&mut self) {
let nodes = &mut self.nodes;
self.chunks.compact(|chunk, cur, new| {
assert_eq!(
nodes[chunk.node_idx as usize].chunk[chunk.child_idx as usize]
.get()
.unwrap(),
cur
);
nodes[chunk.node_idx as usize].chunk[chunk.child_idx as usize] =
ChunkPtr::from(Some(new));
true
});
}
pub fn prune_nodes(&mut self) {
todo!()
}
pub fn defragment_nodes(&mut self) {
let num_nodes = self.nodes.len();
self.new_nodes.reserve(num_nodes);
self.new_nodes.insert(self.nodes.remove(0));
for n in 0..num_nodes {
let children = self.new_nodes[n].children;
for (i, old_idx) in iter_treenode_children(&children) {
let old_node = self.nodes.remove(old_idx);
if old_node.is_empty() {
self.new_nodes[n].children[i] = None;
continue;
}
let new_idx = self.new_nodes.insert(old_node);
debug_assert_eq!(new_idx, n + i + 1);
self.new_nodes[n].children[i] = Some(NonZeroU32::new(new_idx as u32).unwrap());
if new_idx != old_idx {
for (_, chunk_idx) in self.new_nodes[new_idx].iter_existing_chunks() {
self.chunks[chunk_idx].node_idx = new_idx as u32;
}
}
}
}
std::mem::swap(&mut self.nodes, &mut self.new_nodes);
self.new_nodes.clear();
}
pub fn lod_update<V, W>(
&mut self,
targets: &[L],
detail: u32,
mut chunk_creator: V,
mut evict_callback: W,
) where
V: FnMut(L) -> C,
W: FnMut(L, C),
{
let num_nodes = self.nodes.len();
self.new_nodes.reserve(num_nodes);
let mut new_positions = std::collections::VecDeque::with_capacity(B);
self.new_nodes.insert(self.nodes.remove(0));
new_positions.push_back(L::root());
for n in 0..usize::MAX {
let pos = match new_positions.pop_front() {
Some(p) => p,
None => break,
};
let children = self.new_nodes[n].children;
for (b, maybe_child) in children.iter().enumerate() {
let child_pos = pos.get_child(b);
let subdivide = targets.iter().any(|x| x.can_subdivide(child_pos, detail));
match (self.new_nodes[n].chunk[b].get(), subdivide) {
(None, true) => {
}
(Some(chunk_idx), true) => {
let cont = self.chunks.remove(chunk_idx);
debug_assert_eq!(cont.position, child_pos);
evict_callback(child_pos, cont.chunk);
self.new_nodes[n].chunk[b] = ChunkPtr::None;
}
(None, false) => {
let chunk_idx = self.chunks.insert(ChunkContainer {
chunk: chunk_creator(child_pos),
position: child_pos,
node_idx: n as u32,
child_idx: b as u8,
});
self.new_nodes[n].chunk[b] = ChunkPtr::from(Some(chunk_idx));
}
(Some(chunk_idx), false) => {
self.chunks[chunk_idx].node_idx = n as u32;
}
}
match (maybe_child, subdivide) {
(None, true) => {
let new_idx = self.new_nodes.insert(TreeNode::new());
new_positions.push_back(child_pos);
self.new_nodes[n].children[b] =
Some(NonZeroU32::new(new_idx as u32).unwrap());
}
(None, false) => {}
(Some(child_idx), true) => {
let new_idx = self
.new_nodes
.insert(self.nodes.remove(child_idx.get() as usize));
new_positions.push_back(child_pos);
self.new_nodes[n].children[b] =
Some(NonZeroU32::new(new_idx as u32).unwrap());
}
(Some(child_idx), false) => {
for node in traverse(&self.nodes, &self.nodes[child_idx.get() as usize]) {
for (_, cid) in node.iter_existing_chunks() {
let cont = self.chunks.remove(cid);
debug_assert!(child_pos.contains_child_node(cont.position));
evict_callback(cont.position, cont.chunk);
}
}
self.new_nodes[n].children[b] = None;
}
};
}
}
std::mem::swap(&mut self.nodes, &mut self.new_nodes);
self.new_nodes.clear();
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.chunks.shrink_to_fit();
self.nodes.shrink_to_fit();
self.new_nodes.shrink_to_fit();
}
}
#[inline]
pub fn traverse<'a, const B: usize>(
nodes: &'a NodeStorage<B>,
start: &'a TreeNode<B>,
) -> TraverseIter<'a, B> {
let mut to_visit = Vec::with_capacity(8 * B); to_visit.push(start);
TraverseIter { nodes, to_visit }
}
pub struct TraverseIter<'a, const B: usize> {
nodes: &'a NodeStorage<B>,
to_visit: Vec<&'a TreeNode<B>>,
}
impl<'a, const B: usize> Iterator for TraverseIter<'a, B> {
type Item = &'a TreeNode<B>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let current = self.to_visit.pop()?;
for (_, c) in iter_treenode_children(¤t.children) {
self.to_visit.push(&self.nodes[c]);
}
Some(current)
}
}
pub type OctTree<C, L> = Tree<3, 8, C, L>;
impl<C, L> OctTree<C, L>
where
C: Sized,
L: LodVec<3>,
{
pub fn new() -> Self {
Self::with_capacity(1, 1)
}
pub fn with_capacity(nodes_capacity: usize, chunks_capacity: usize) -> Self {
Tree::with_capacity_unsafe(nodes_capacity, chunks_capacity)
}
}
pub type QuadTree<C, L> = Tree<2, 4, C, L>;
impl<C, L> QuadTree<C, L>
where
C: Sized,
L: LodVec<2>,
{
pub fn new() -> Self {
Self::with_capacity(1, 1)
}
pub fn with_capacity(nodes_capacity: usize, chunks_capacity: usize) -> Self {
Tree::with_capacity_unsafe(nodes_capacity, chunks_capacity)
}
}
impl<C, L> Default for OctTree<C, L>
where
C: Sized,
L: LodVec<3>,
{
fn default() -> Self {
Self::new()
}
}
impl<C, L> Default for QuadTree<C, L>
where
C: Sized,
L: LodVec<2>,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestChunk;
#[test]
fn lod_update() {
let mut tree = QuadTree::<TestChunk, QuadVec>::new();
let targets = [QuadVec::build((1 << 2) - 1, (1 << 2) - 1, 2)];
println!("=====> Update with targets {targets:?}");
tree.lod_update(
&targets,
0,
|p| {
println!("Creating chunk at {p:?}");
TestChunk {}
},
|p, _| {
println!("Evicting chunk at {p:?}");
},
);
let targets = [QuadVec::build(0, 0, 2)];
println!("=====> Update with targets {targets:?}");
tree.lod_update(
&targets,
0,
|p| {
println!("Creating chunk at {p:?}");
TestChunk {}
},
|p, _| {
println!("Evicting chunk at {p:?}");
},
);
}
#[test]
fn insert_into_tree() {
let mut tree = QuadTree::<TestChunk, QuadVec>::new();
let targets = [
QuadVec::build(1u8, 1u8, 1u8),
QuadVec::build(2u8, 3u8, 2u8),
QuadVec::build(2u8, 2u8, 2u8),
QuadVec::build(0u8, 1u8, 1u8),
];
tree.insert_many(targets.iter().copied(), |_| TestChunk {});
let mut tree2 = QuadTree::<TestChunk, QuadVec>::new();
for t in targets {
tree2.insert(t, |_| TestChunk {});
}
}
#[test]
pub fn defragment() {
let mut tree = QuadTree::<TestChunk, QuadVec>::new();
let targets = [
QuadVec::build(0u8, 0u8, 2u8),
QuadVec::build(0u8, 1u8, 2u8),
QuadVec::build(3u8, 3u8, 2u8),
QuadVec::build(2u8, 2u8, 2u8),
];
tree.insert_many(targets.iter().copied(), |_| TestChunk {});
assert_eq!(tree.chunks.capacity(), tree.chunks.len());
tree.pop_chunk_by_position(targets[1]);
assert_eq!(tree.chunks.capacity(), 4);
assert_eq!(tree.chunks.len(), 3);
tree.defragment_chunks();
tree.shrink_to_fit();
assert_eq!(tree.chunks.capacity(), tree.chunks.len());
dbg!(tree.nodes.len());
tree.pop_chunk_by_position(targets[0]);
dbg!(tree.nodes.len());
}
#[test]
pub fn alignment() {
assert_eq!(
std::mem::size_of::<TreeNode<8>>(),
64,
"Octree node should be 64 bytes"
);
assert_eq!(
std::mem::size_of::<TreeNode<4>>(),
32,
"Quadtree node should be 32 bytes"
);
}
}