Skip to main content

wisp/
scene.rs

1//! Scene graph — `Container`, `Sprite`, `Graphics`, `Text`, `Mesh`, transforms, clip masks.
2//!
3//! M0.8 introduces the `Stage` root and the `Node`/`NodeId` storage. Submodules
4//! host the concrete node types as they land.
5
6pub mod callout;
7pub mod clip;
8pub mod container;
9pub mod dim_outside;
10pub mod flex_text;
11pub mod graphics;
12pub mod highlight;
13pub mod mesh;
14pub mod node;
15pub mod path;
16pub mod privacy_blur;
17pub mod sprite;
18pub mod text;
19pub mod transform;
20pub mod vector;
21
22use slotmap::SlotMap;
23
24pub use callout::Callout;
25pub use clip::MaskShape;
26pub use container::Container;
27pub use dim_outside::{DimOutside, DimStrength};
28pub use flex_text::FlexText;
29pub use graphics::{Fill, Graphics, Stroke};
30pub use highlight::Highlight;
31pub use mesh::Mesh;
32pub use node::{Node, NodeId};
33pub use path::{Path, PathBuilder, PathCommand};
34pub use privacy_blur::{BlurStrength, PrivacyBlur};
35pub use sprite::Sprite;
36pub use text::{Font, Text};
37pub use transform::Transform;
38pub use vector::{Vector, VectorShape, VectorStroke};
39
40/// Scene graph root.
41///
42/// Owns all nodes via a [`SlotMap`] keyed by [`NodeId`]. The tree is rooted at
43/// a `Container` returned by [`Stage::root`]. Child relationships are tracked
44/// inside each `Container`.
45pub struct Stage {
46    nodes: SlotMap<NodeId, Node>,
47    root: NodeId,
48}
49
50impl Stage {
51    /// Construct a new stage with an empty root container.
52    #[must_use]
53    pub fn new() -> Self {
54        let mut nodes = SlotMap::with_key();
55        let root = nodes.insert(Node::from(Container::default()));
56        Self { nodes, root }
57    }
58
59    /// The root node ID. Always a [`Container`].
60    #[must_use]
61    pub fn root(&self) -> NodeId {
62        self.root
63    }
64
65    /// Total node count across the whole graph (including the root).
66    #[must_use]
67    pub fn len(&self) -> usize {
68        self.nodes.len()
69    }
70
71    /// `true` iff only the root exists.
72    #[must_use]
73    pub fn is_empty(&self) -> bool {
74        self.nodes.len() == 1
75    }
76
77    /// Borrow a node by ID.
78    #[must_use]
79    pub fn get(&self, id: NodeId) -> Option<&Node> {
80        self.nodes.get(id)
81    }
82
83    /// Mutably borrow a node by ID.
84    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut Node> {
85        self.nodes.get_mut(id)
86    }
87
88    /// Insert `child` as a child of `parent`. Returns the new node ID.
89    ///
90    /// `child`'s `parent` field is set; `parent`'s `children` list is appended.
91    ///
92    /// Returns `None` if `parent` doesn't exist.
93    pub fn add_child(&mut self, parent: NodeId, child: impl Into<Node>) -> Option<NodeId> {
94        if !self.nodes.contains_key(parent) {
95            return None;
96        }
97        let id = self.nodes.insert(child.into());
98        if let Some(parent_node) = self.nodes.get_mut(parent) {
99            parent_node.container_mut().children.push(id);
100        }
101        if let Some(child_node) = self.nodes.get_mut(id) {
102            child_node.container_mut().parent = Some(parent);
103        }
104        Some(id)
105    }
106
107    /// Detach `child` from its parent without destroying it.
108    ///
109    /// After this call the child is an orphan still reachable via [`Stage::get`]
110    /// but no longer in any container's `children` list. Returns `true` if the
111    /// child was attached.
112    pub fn detach(&mut self, child: NodeId) -> bool {
113        let parent_id = self.nodes.get(child).and_then(|n| n.container().parent());
114        let Some(parent_id) = parent_id else {
115            return false;
116        };
117        if let Some(parent) = self.nodes.get_mut(parent_id) {
118            parent.container_mut().children.retain(|&id| id != child);
119        }
120        if let Some(child_node) = self.nodes.get_mut(child) {
121            child_node.container_mut().parent = None;
122        }
123        true
124    }
125
126    /// Destroy a node and (recursively) all its descendants. Detaches from
127    /// parent first.
128    ///
129    /// Returns `true` if the node existed.
130    pub fn destroy(&mut self, id: NodeId) -> bool {
131        if id == self.root {
132            return false;
133        }
134        if !self.nodes.contains_key(id) {
135            return false;
136        }
137        self.detach(id);
138        let descendants = self.collect_descendants(id);
139        for d in descendants {
140            self.nodes.remove(d);
141        }
142        self.nodes.remove(id);
143        true
144    }
145
146    /// Pre-order traversal from a starting node. Visits the start first, then
147    /// each child's subtree in insertion order.
148    pub fn traverse_pre_order(&self, start: NodeId, mut visit: impl FnMut(NodeId, &Node)) {
149        let mut stack: Vec<NodeId> = vec![start];
150        while let Some(id) = stack.pop() {
151            let Some(node) = self.nodes.get(id) else {
152                continue;
153            };
154            visit(id, node);
155            // Push children in reverse so they're popped in insertion order.
156            for child in node.container().children.iter().rev() {
157                stack.push(*child);
158            }
159        }
160    }
161
162    fn collect_descendants(&self, id: NodeId) -> Vec<NodeId> {
163        let mut out = Vec::new();
164        let mut stack: Vec<NodeId> = self
165            .nodes
166            .get(id)
167            .map(|n| n.container().children.clone())
168            .unwrap_or_default();
169        while let Some(curr) = stack.pop() {
170            out.push(curr);
171            if let Some(node) = self.nodes.get(curr) {
172                stack.extend(node.container().children.iter().copied());
173            }
174        }
175        out
176    }
177}
178
179impl Default for Stage {
180    fn default() -> Self {
181        Self::new()
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn new_stage_has_only_root() {
191        let stage = Stage::new();
192        assert_eq!(stage.len(), 1);
193        assert!(stage.is_empty());
194        assert!(stage.get(stage.root()).is_some());
195    }
196
197    #[test]
198    fn add_child_returns_id_and_links_both_sides() {
199        let mut stage = Stage::new();
200        let root = stage.root();
201        let child = stage.add_child(root, Container::default()).expect("added");
202
203        assert_eq!(stage.len(), 2);
204        assert!(!stage.is_empty());
205        assert_eq!(stage.get(child).unwrap().container().parent(), Some(root));
206        assert_eq!(stage.get(root).unwrap().container().child_count(), 1);
207    }
208
209    #[test]
210    fn add_child_to_destroyed_parent_returns_none() {
211        // Stale NodeIds (from destroy) carry an old generation and are
212        // rejected by the slotmap guard inside add_child.
213        let mut stage = Stage::new();
214        let temp = stage.add_child(stage.root(), Container::default()).unwrap();
215        assert!(stage.destroy(temp));
216        assert!(stage.add_child(temp, Container::default()).is_none());
217    }
218
219    #[test]
220    fn three_deep_hierarchy_traverses_pre_order() {
221        let mut stage = Stage::new();
222        let root = stage.root();
223        let a = stage.add_child(root, Container::default()).unwrap();
224        let b = stage.add_child(a, Container::default()).unwrap();
225        let c = stage.add_child(b, Container::default()).unwrap();
226        // Sibling of `a` to verify ordering.
227        let d = stage.add_child(root, Container::default()).unwrap();
228
229        let mut order = Vec::new();
230        stage.traverse_pre_order(root, |id, _| order.push(id));
231        assert_eq!(order, vec![root, a, b, c, d]);
232    }
233
234    #[test]
235    fn detach_removes_from_parent_but_keeps_node() {
236        let mut stage = Stage::new();
237        let root = stage.root();
238        let child = stage.add_child(root, Container::default()).unwrap();
239
240        assert!(stage.detach(child));
241        assert_eq!(stage.get(root).unwrap().container().child_count(), 0);
242        assert_eq!(stage.get(child).unwrap().container().parent(), None);
243        assert!(stage.get(child).is_some()); // still alive
244    }
245
246    #[test]
247    fn destroy_cascades_to_descendants() {
248        let mut stage = Stage::new();
249        let root = stage.root();
250        let a = stage.add_child(root, Container::default()).unwrap();
251        let b = stage.add_child(a, Container::default()).unwrap();
252        let _c = stage.add_child(b, Container::default()).unwrap();
253
254        assert_eq!(stage.len(), 4);
255        assert!(stage.destroy(a));
256        // Root + nothing else.
257        assert_eq!(stage.len(), 1);
258        assert_eq!(stage.get(root).unwrap().container().child_count(), 0);
259    }
260
261    #[test]
262    fn destroy_root_is_rejected() {
263        let mut stage = Stage::new();
264        assert!(!stage.destroy(stage.root()));
265        assert_eq!(stage.len(), 1);
266    }
267}