use ahash::RandomState;
use bitflags::bitflags;
use cgmath::*;
use dashmap::{mapref::one::*, DashMap};
bitflags! {
pub struct OctreeIndex : u8 {
const RIGHT = 1 << 0;
const TOP = 1 << 1;
const FOREMOST = 1 << 2;
}
}
impl<T> From<OctreeIndex> for Vector3<T>
where
T: From<i8>,
{
fn from(x: OctreeIndex) -> Self {
let result = Vector3::new(
if x.contains(OctreeIndex::RIGHT) {
T::from(1)
} else {
T::from(-1)
},
if x.contains(OctreeIndex::TOP) {
T::from(1)
} else {
T::from(-1)
},
if x.contains(OctreeIndex::FOREMOST) {
T::from(-1)
} else {
T::from(1)
},
);
result
}
}
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
pub struct MortonKey(pub u64);
impl MortonKey {
pub const fn root() -> Self {
Self(1)
}
pub const fn none() -> Self {
Self(0)
}
pub fn parent(self) -> Self {
Self(self.0 >> 3)
}
pub fn child(self, index: OctreeIndex) -> Self {
assert!(self.level() < Self::max_level());
Self(self.0 << 3 | index.bits as u64)
}
pub fn level(self) -> u32 {
assert_ne!(self.0, 0);
(63 - self.0.leading_zeros()) / 3
}
pub const fn max_level() -> u32 {
63 / 3
}
pub fn position(self) -> Point3<f32> {
let (mut x, mut y, mut z) = (0, 0, 0);
let level = self.level();
let mut bits = self.0;
for i in 1..=level {
x |= (bits & 1) << i;
bits >>= 1;
y |= (bits & 1) << i;
bits >>= 1;
z |= (bits & 1) << i;
bits >>= 1;
}
let max_level_k = (1 << level) as f32;
Point3::new(
(x + 1) as f32 / max_level_k - 1.,
(y + 1) as f32 / max_level_k - 1.,
(z + 1) as f32 / max_level_k - 1.,
)
}
pub fn closest_to_position(position: Point3<f32>, level: u32) -> Self {
assert!(level < Self::max_level());
let position = point3(
position.x.clamp(-1., 1.),
position.y.clamp(-1., 1.),
position.z.clamp(-1., 1.),
);
let max_level_k = (1 << level) as f32;
let (x, y, z) = (
((position.x + 1.) * max_level_k - 1.) as u64,
((position.y + 1.) * max_level_k - 1.) as u64,
((position.z + 1.) * max_level_k - 1.) as u64,
);
let mut bits = 1u64;
for i in (1..=level).rev() {
bits <<= 1;
bits |= (z >> i) & 1;
bits <<= 1;
bits |= (y >> i) & 1;
bits <<= 1;
bits |= (x >> i) & 1;
}
Self(bits)
}
pub fn until_level(self, level: u32) -> Self {
assert!(self.level() >= level);
Self(self.0 >> (self.level() - level) * 3)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HashedOctree<T: Copy> {
values: DashMap<MortonKey, T, RandomState>,
}
impl<T: Copy> HashedOctree<T> {
pub fn new(root_value: T) -> Self {
Self {
values: {
let x = DashMap::with_capacity_and_hasher(256, RandomState::new());
x.insert(MortonKey::root(), root_value);
x
},
}
}
pub fn value(&self, key: MortonKey) -> Option<Ref<'_, MortonKey, T, RandomState>> {
self.values.get(&key)
}
pub fn value_mut(&mut self, key: MortonKey) -> Option<RefMut<'_, MortonKey, T, RandomState>> {
self.values.get_mut(&key)
}
pub fn subdivide(&mut self, key: MortonKey) -> impl std::iter::Iterator<Item = MortonKey> {
assert!(
!self.is_subdivided(key),
"Tried to subdivide already subdivided node"
);
let child_value = *self
.value(key)
.expect("Tried to subdivide null octree node");
let vec = (0..8)
.into_iter()
.map(move |child| {
let child_key = key.child(unsafe { OctreeIndex::from_bits_unchecked(child) });
self.values.insert(child_key, child_value);
child_key
})
.collect::<Vec<_>>();
vec.into_iter()
}
pub fn children(&self, key: MortonKey) -> Option<impl std::iter::Iterator<Item = MortonKey>> {
if self.is_subdivided(key) {
Some((0..8).into_iter().map(move |child| {
let child_key = key.child(unsafe { OctreeIndex::from_bits_unchecked(child) });
child_key
}))
} else {
None
}
}
pub fn node_exists(&self, key: MortonKey) -> bool {
self.values.contains_key(&key)
}
pub fn is_subdivided(&self, key: MortonKey) -> bool {
self.values.contains_key(&(key.child(OctreeIndex::empty())))
}
pub fn leaves(&self, parent: MortonKey) -> Vec<MortonKey> {
let mut leaves = Vec::with_capacity(128);
let mut to_process = Vec::with_capacity(64);
to_process.push(parent);
while let Some(node_to_process) = to_process.pop() {
if let Some(children) = self.children(node_to_process) {
for child in children {
to_process.push(child);
}
} else {
leaves.push(node_to_process);
}
}
leaves
}
pub fn node_count(&self) -> usize {
self.values.len()
}
}