simple_octree/
lib.rs

1// Linting
2#![warn(rust_2018_idioms)]
3#![deny(clippy::all)]
4#![warn(clippy::pedantic, clippy::nursery)]
5#![allow(clippy::module_name_repetitions)]
6
7#[cfg(test)]
8#[macro_use]
9extern crate approx;
10
11mod managed_octree;
12
13pub use managed_octree::{
14    ManagedHashMapOctree,
15    ManagedOctree,
16    ManagedOctreeData,
17    ManagedVecOctree,
18};
19use std::{
20    borrow::{Borrow, BorrowMut},
21    convert::{AsMut, AsRef},
22};
23
24/// A barebones octree offering just the methods required for accessing and
25/// modifying its contents. Other management structures/functions will be needed
26/// to make this more useful, especially for the purpose of querying contents.
27#[derive(Default)]
28pub struct Octree<D>
29where
30    D: Default,
31{
32    children: [Option<Box<Octree<D>>>; 8],
33    data: D,
34}
35
36#[derive(Debug)]
37pub enum AddChildError {
38    AlreadyAdded,
39    OutOfBoundsIdx,
40}
41
42impl<D> Octree<D>
43where
44    D: Default,
45{
46    #[must_use]
47    pub fn new() -> Self { Self::default() }
48
49    #[must_use]
50    pub fn new_with_data(data: D) -> Self {
51        Self {
52            data,
53            ..Self::default()
54        }
55    }
56
57    /// Adds and returns a reference to a child at a particular index.
58    ///
59    /// # Errors
60    /// Returns an error if the idx is out of range (i.e. idx >= 8) or if the
61    /// child is already added.
62    pub fn add_child(
63        &mut self,
64        idx: usize,
65        child: Self,
66    ) -> Result<&mut Self, AddChildError> {
67        if idx >= self.children.len() {
68            Err(AddChildError::OutOfBoundsIdx)
69        } else if self.children[idx].is_some() {
70            Err(AddChildError::AlreadyAdded)
71        } else {
72            self.children[idx] = Some(Box::new(child));
73            self.get_child_mut(idx).ok_or(AddChildError::OutOfBoundsIdx)
74        }
75    }
76
77    /// Adds and returns a reference to a child at an index based on whether the
78    /// child is at the positive or negative side of each axis.
79    ///
80    /// # Arguments
81    /// * `pos_x` - positive x axis if true, negative if false.
82    /// * `pos_y` - positive y axis if true, negative if false.
83    /// * `pos_z` - positive z axis if true, negative if false.
84    ///
85    /// # Errors
86    /// Returns an error if the child is already added.
87    pub fn add_child_at_pos(
88        &mut self,
89        pos_x: bool,
90        pos_y: bool,
91        pos_z: bool,
92        child: Self,
93    ) -> Result<&mut Self, AddChildError> {
94        self.add_child(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z), child)
95    }
96
97    /// Removes a child and returns the owned value, if it exists.
98    pub fn remove_child(&mut self, idx: usize) -> Option<Self> {
99        if self.children.get(idx).is_none() {
100            None
101        } else {
102            self.children[idx].take().map(|c| *c)
103        }
104    }
105
106    /// Removes a child at an index based on whether the child is at the
107    /// positive or negative side of each access and returns the owned value, if
108    /// it exists.
109    ///
110    /// # Arguments
111    /// * `pos_x` - positive x axis if true, negative if false.
112    /// * `pos_y` - positive y axis if true, negative if false.
113    /// * `pos_z` - positive z axis if true, negative if false.
114    pub fn remove_child_at_pos(
115        &mut self,
116        pos_x: bool,
117        pos_y: bool,
118        pos_z: bool,
119    ) -> Option<Self> {
120        self.remove_child(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z))
121    }
122
123    /// Gets a reference to a child given an index.
124    #[must_use]
125    pub fn get_child(&self, idx: usize) -> Option<&Self> {
126        if idx >= self.children.len() {
127            None
128        } else {
129            self.children[idx].as_ref().map(AsRef::as_ref)
130        }
131    }
132
133    /// Gets a mutable reference to a child given an index.
134    #[must_use]
135    pub fn get_child_mut(&mut self, idx: usize) -> Option<&mut Self> {
136        if idx >= self.children.len() {
137            None
138        } else {
139            self.children[idx].as_mut().map(AsMut::as_mut)
140        }
141    }
142
143    /// Gets a child index given whether the child is at the positive or
144    /// negative side of an axis.
145    ///
146    /// ## Arguments
147    /// * `pos_x` - positive x axis if true, negative if false.
148    /// * `pos_y` - positive y axis if true, negative if false.
149    /// * `pos_z` - positive z axis if true, negative if false.
150    fn get_child_idx_at_pos(pos_x: bool, pos_y: bool, pos_z: bool) -> usize {
151        match (pos_x, pos_y, pos_z) {
152            (false, false, false) => 0,
153            (false, false, true) => 1,
154            (false, true, false) => 2,
155            (false, true, true) => 3,
156            (true, false, false) => 4,
157            (true, false, true) => 5,
158            (true, true, false) => 6,
159            (true, true, true) => 7,
160        }
161    }
162
163    /// Panics if idx > 7
164    fn get_child_pos_at_idx(idx: usize) -> (bool, bool, bool) {
165        match idx {
166            0 => (false, false, false),
167            1 => (false, false, true),
168            2 => (false, true, false),
169            3 => (false, true, true),
170            4 => (true, false, false),
171            5 => (true, false, true),
172            6 => (true, true, false),
173            7 => (true, true, true),
174            _ => panic!("idx > 7"),
175        }
176    }
177
178    /// Gets a reference to a child given whether the child is at the positive
179    /// or negative side of an axis.
180    ///
181    /// ## Arguments
182    /// * `pos_x` - positive x axis if true, negative if false.
183    /// * `pos_y` - positive y axis if true, negative if false.
184    /// * `pos_z` - positive z axis if true, negative if false.
185    #[must_use]
186    pub fn get_child_at_pos(
187        &self,
188        pos_x: bool,
189        pos_y: bool,
190        pos_z: bool,
191    ) -> Option<&Self> {
192        self.get_child(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z))
193    }
194
195    /// Gets a mutable reference to a child given whether the child is at the
196    /// positive or negative side of an axis.
197    ///
198    /// ## Arguments
199    /// * `pos_x` - positive x axis if true, negative if false.
200    /// * `pos_y` - positive y axis if true, negative if false.
201    /// * `pos_z` - positive z axis if true, negative if false.
202    #[must_use]
203    pub fn get_child_mut_at_pos(
204        &mut self,
205        pos_x: bool,
206        pos_y: bool,
207        pos_z: bool,
208    ) -> Option<&mut Self> {
209        self.get_child_mut(Self::get_child_idx_at_pos(pos_x, pos_y, pos_z))
210    }
211
212    /// Gets a reference to the underlying data in the node.
213    #[must_use]
214    pub fn get_data(&self) -> &D { self.data.borrow() }
215
216    /// Gets a mutable reference to the underlying data in the node.
217    #[must_use]
218    pub fn get_data_mut(&mut self) -> &mut D { self.data.borrow_mut() }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::Octree;
224
225    #[test]
226    fn test_get_child_out_of_bounds_initial() {
227        let o = Octree::<Vec<(f32, f32, f32)>>::new();
228        assert!(o.get_child(999).is_none());
229    }
230
231    #[test]
232    fn test_get_child_initial() {
233        let o = Octree::<Vec<(f32, f32, f32)>>::new();
234        assert!(o.get_child(0).is_none());
235    }
236
237    #[test]
238    fn test_get_child_pos_initial() {
239        let o = Octree::<Vec<(f32, f32, f32)>>::new();
240        assert!(o.get_child_at_pos(false, false, false).is_none());
241    }
242
243    #[test]
244    fn test_add_child() {
245        let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
246        let result = o.add_child(0, Octree::new());
247        assert!(result.is_ok());
248    }
249
250    #[test]
251    fn test_add_child_at_pos() {
252        let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
253        let result = o.add_child_at_pos(false, false, false, Octree::new());
254        assert!(result.is_ok());
255    }
256
257    #[test]
258    fn test_remove_child() {
259        let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
260        o.add_child(0, Octree::new()).unwrap();
261        let result = o.remove_child(0);
262        assert!(result.is_some());
263        assert!(o.get_child(0).is_none());
264    }
265
266    #[test]
267    fn test_remove_child_at_pos() {
268        let mut o = Octree::<Vec<(f32, f32, f32)>>::new();
269        o.add_child_at_pos(false, false, false, Octree::new())
270            .unwrap();
271        let result = o.remove_child_at_pos(false, false, false);
272        assert!(result.is_some());
273        assert!(o.get_child_at_pos(false, false, false).is_none());
274    }
275}