nightshade 0.14.1

A cross-platform data-oriented game engine.
Documentation
#[derive(Clone, Debug, Default)]
pub struct LayoutGraph {
    pub nodes: Vec<freecs::Entity>,
    pub node_index: std::collections::HashMap<freecs::Entity, u32>,
    pub parents: Vec<i32>,
    pub depths: Vec<u32>,
    pub child_offsets: Vec<u32>,
    pub child_counts: Vec<u32>,
    pub flat_children: Vec<u32>,
    pub max_depth: u32,
    pub valid: bool,
}

impl LayoutGraph {
    pub fn clear(&mut self) {
        self.nodes.clear();
        self.node_index.clear();
        self.parents.clear();
        self.depths.clear();
        self.child_offsets.clear();
        self.child_counts.clear();
        self.flat_children.clear();
        self.max_depth = 0;
        self.valid = false;
    }

    pub fn rebuild(
        &mut self,
        roots: &[freecs::Entity],
        children_of: impl Fn(freecs::Entity) -> Option<Vec<freecs::Entity>>,
    ) {
        self.clear();

        let mut stack: Vec<(freecs::Entity, i32, u32)> = Vec::new();
        for &root in roots.iter().rev() {
            stack.push((root, -1, 0));
        }

        while let Some((entity, parent, depth)) = stack.pop() {
            let node_idx = self.nodes.len() as u32;
            self.nodes.push(entity);
            self.node_index.insert(entity, node_idx);
            self.parents.push(parent);
            self.depths.push(depth);
            self.max_depth = self.max_depth.max(depth);

            let children = children_of(entity).unwrap_or_default();
            for &child in children.iter().rev() {
                stack.push((child, node_idx as i32, depth + 1));
            }
        }

        self.child_offsets.resize(self.nodes.len(), 0);
        self.child_counts.resize(self.nodes.len(), 0);

        let mut counts: Vec<u32> = vec![0; self.nodes.len()];
        for &parent in &self.parents {
            if parent >= 0 {
                counts[parent as usize] += 1;
            }
        }

        let mut offset = 0u32;
        for (index, count) in counts.iter().enumerate() {
            self.child_offsets[index] = offset;
            self.child_counts[index] = *count;
            offset += count;
        }

        self.flat_children.resize(offset as usize, 0);
        let mut writes: Vec<u32> = vec![0; self.nodes.len()];
        for (child_idx, &parent) in self.parents.iter().enumerate() {
            if parent >= 0 {
                let parent_idx = parent as usize;
                let slot = self.child_offsets[parent_idx] + writes[parent_idx];
                self.flat_children[slot as usize] = child_idx as u32;
                writes[parent_idx] += 1;
            }
        }

        self.valid = true;
    }

    pub fn children_of(&self, node_index: u32) -> &[u32] {
        let start = self.child_offsets[node_index as usize] as usize;
        let count = self.child_counts[node_index as usize] as usize;
        &self.flat_children[start..start + count]
    }

    pub fn parent_of(&self, node_index: u32) -> Option<u32> {
        let parent = self.parents[node_index as usize];
        if parent < 0 {
            None
        } else {
            Some(parent as u32)
        }
    }

    pub fn entities_at_depth(&self, depth: u32) -> Vec<u32> {
        self.depths
            .iter()
            .enumerate()
            .filter_map(|(idx, d)| if *d == depth { Some(idx as u32) } else { None })
            .collect()
    }
}