#![warn(rust_2018_idioms)]
#![deny(clippy::all)]
#![warn(clippy::pedantic, clippy::nursery)]
#![allow(clippy::module_name_repetitions)]
#[cfg(test)]
#[macro_use]
extern crate approx;
mod managed_octree;
pub use managed_octree::{
ManagedHashMapOctree,
ManagedOctree,
ManagedOctreeData,
ManagedVecOctree,
};
use std::{
borrow::{Borrow, BorrowMut},
convert::{AsMut, AsRef},
};
#[derive(Default)]
pub struct Octree<D>
where
D: Default,
{
children: [Option<Box<Octree<D>>>; 8],
data: D,
}
#[derive(Debug)]
pub enum AddChildError {
AlreadyAdded,
OutOfBoundsIdx,
}
impl<D> Octree<D>
where
D: Default,
{
#[must_use]
pub fn new() -> Self { Self::default() }
#[must_use]
pub fn new_with_data(data: D) -> Self {
Self {
data,
..Self::default()
}
}
pub fn add_child(
&mut self,
idx: usize,
child: Self,
) -> Result<&mut Self, AddChildError> {
if idx >= self.children.len() {
Err(AddChildError::OutOfBoundsIdx)
} else if self.children[idx].is_some() {
Err(AddChildError::AlreadyAdded)
} else {
self.children[idx] = Some(Box::new(child));
self.get_child_mut(idx).ok_or(AddChildError::OutOfBoundsIdx)
}
}
pub fn add_child_at_pos(
&mut self,
pos_x: bool,
pos_y: bool,
pos_z: bool,
child: Self,
) -> Result<&mut Self, AddChildError> {
self.add_child(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z), child)
}
pub fn remove_child(&mut self, idx: usize) -> Option<Self> {
if self.children.get(idx).is_none() {
None
} else {
self.children[idx].take().map(|c| *c)
}
}
pub fn remove_child_at_pos(
&mut self,
pos_x: bool,
pos_y: bool,
pos_z: bool,
) -> Option<Self> {
self.remove_child(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z))
}
#[must_use]
pub fn get_child(&self, idx: usize) -> Option<&Self> {
if idx >= self.children.len() {
None
} else {
self.children[idx].as_ref().map(AsRef::as_ref)
}
}
#[must_use]
pub fn get_child_mut(&mut self, idx: usize) -> Option<&mut Self> {
if idx >= self.children.len() {
None
} else {
self.children[idx].as_mut().map(AsMut::as_mut)
}
}
fn get_child_idx_at_pos(pos_x: bool, pos_y: bool, pos_z: bool) -> usize {
match (pos_x, pos_y, pos_z) {
(false, false, false) => 0,
(false, false, true) => 1,
(false, true, false) => 2,
(false, true, true) => 3,
(true, false, false) => 4,
(true, false, true) => 5,
(true, true, false) => 6,
(true, true, true) => 7,
}
}
fn get_child_pos_at_idx(idx: usize) -> (bool, bool, bool) {
match idx {
0 => (false, false, false),
1 => (false, false, true),
2 => (false, true, false),
3 => (false, true, true),
4 => (true, false, false),
5 => (true, false, true),
6 => (true, true, false),
7 => (true, true, true),
_ => panic!("idx > 7"),
}
}
#[must_use]
pub fn get_child_at_pos(
&self,
pos_x: bool,
pos_y: bool,
pos_z: bool,
) -> Option<&Self> {
self.get_child(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z))
}
#[must_use]
pub fn get_child_mut_at_pos(
&mut self,
pos_x: bool,
pos_y: bool,
pos_z: bool,
) -> Option<&mut Self> {
self.get_child_mut(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z))
}
#[must_use]
pub fn get_data(&self) -> &D { self.data.borrow() }
#[must_use]
pub fn get_data_mut(&mut self) -> &mut D { self.data.borrow_mut() }
}
#[cfg(test)]
mod tests {
use super::Octree;
#[test]
fn test_get_child_out_of_bounds_initial() {
let o = Octree::<Vec<(f32, f32, f32)>>::new();
assert!(o.get_child(999).is_none());
}
#[test]
fn test_get_child_initial() {
let o = Octree::<Vec<(f32, f32, f32)>>::new();
assert!(o.get_child(0).is_none());
}
#[test]
fn test_get_child_pos_initial() {
let o = Octree::<Vec<(f32, f32, f32)>>::new();
assert!(o.get_child_at_pos(false, false, false).is_none());
}
#[test]
fn test_add_child() {
let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
let result = o.add_child(0, Octree::new());
assert!(result.is_ok());
}
#[test]
fn test_add_child_at_pos() {
let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
let result = o.add_child_at_pos(false, false, false, Octree::new());
assert!(result.is_ok());
}
#[test]
fn test_remove_child() {
let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
o.add_child(0, Octree::new()).unwrap();
let result = o.remove_child(0);
assert!(result.is_some());
assert!(o.get_child(0).is_none());
}
#[test]
fn test_remove_child_at_pos() {
let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
o.add_child_at_pos(false, false, false, Octree::new())
.unwrap();
let result = o.remove_child_at_pos(false, false, false);
assert!(result.is_some());
assert!(o.get_child_at_pos(false, false, false).is_none());
}
}