#[derive(Debug, Clone)]
pub struct SceneHierarchy {
pub parent: Vec<Option<u32>>,
pub children: Vec<Vec<u32>>,
}
impl SceneHierarchy {
pub fn with_capacity(capacity: usize) -> Self {
Self {
parent: vec![None; capacity],
children: vec![Vec::new(); capacity],
}
}
pub fn set_parent(&mut self, child: u32, new_parent: Option<u32>) {
let c = child as usize;
if c >= self.parent.len() {
return;
}
if let Some(old_p) = self.parent[c] {
let old_p = old_p as usize;
if old_p < self.children.len() {
self.children[old_p].retain(|&id| id != child);
}
}
self.parent[c] = new_parent;
if let Some(p) = new_parent {
let p = p as usize;
if p < self.children.len() && !self.children[p].contains(&child) {
self.children[p].push(child);
}
}
}
pub fn children_of(&self, entity: u32) -> &[u32] {
let idx = entity as usize;
if idx < self.children.len() {
&self.children[idx]
} else {
&[]
}
}
pub fn roots(&self) -> Vec<u32> {
self.parent
.iter()
.enumerate()
.filter_map(|(i, p)| if p.is_none() { Some(i as u32) } else { None })
.collect()
}
pub fn len(&self) -> usize {
self.parent.len()
}
pub fn is_empty(&self) -> bool {
self.parent.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_capacity_creates_roots() {
let h = SceneHierarchy::with_capacity(10);
assert_eq!(h.len(), 10);
assert_eq!(h.roots().len(), 10); }
#[test]
fn set_parent_and_children() {
let mut h = SceneHierarchy::with_capacity(5);
h.set_parent(1, Some(0));
h.set_parent(2, Some(0));
assert_eq!(h.children_of(0), &[1, 2]);
assert_eq!(h.parent[1], Some(0));
}
#[test]
fn reparent_removes_from_old() {
let mut h = SceneHierarchy::with_capacity(5);
h.set_parent(2, Some(0));
assert_eq!(h.children_of(0), &[2]);
h.set_parent(2, Some(1));
assert!(h.children_of(0).is_empty());
assert_eq!(h.children_of(1), &[2]);
}
#[test]
fn make_root() {
let mut h = SceneHierarchy::with_capacity(3);
h.set_parent(1, Some(0));
h.set_parent(1, None);
assert!(h.children_of(0).is_empty());
assert_eq!(h.parent[1], None);
}
#[test]
fn roots_returns_parentless() {
let mut h = SceneHierarchy::with_capacity(4);
h.set_parent(1, Some(0));
h.set_parent(2, Some(0));
let roots = h.roots();
assert!(roots.contains(&0));
assert!(roots.contains(&3));
assert!(!roots.contains(&1));
}
#[test]
fn out_of_bounds_safe() {
let mut h = SceneHierarchy::with_capacity(2);
h.set_parent(99, Some(0)); assert!(h.children_of(99).is_empty());
}
}