Skip to main content

wisp/scene/
container.rs

1//! `Container` — scene-graph node holding children, transform, alpha, visible, blend mode.
2//!
3//! Filters (`Vec<Box<dyn Filter>>`) are declared in the design but land
4//! in M0.16+ when the per-container filter pipeline exists. Mask
5//! clipping ([`Container::clip`]) ships in M-MASK.1.
6
7use crate::blend::BlendMode;
8use crate::scene::Transform;
9use crate::scene::clip::MaskShape;
10use crate::scene::node::NodeId;
11
12/// Scene-graph container.
13///
14/// Holds child node IDs and the per-node transform/alpha/visibility/blend
15/// state. `parent` is `None` for orphans and for the stage root.
16#[derive(Debug, Clone)]
17pub struct Container {
18    /// Local affine transform.
19    pub transform: Transform,
20    /// Alpha multiplier in `[0.0, 1.0]`. Multiplied with parent alpha at render.
21    pub alpha: f32,
22    /// Skip rendering when `false`. Children are also skipped.
23    pub visible: bool,
24    /// Blend mode used when compositing this node's output over its target.
25    pub blend_mode: BlendMode,
26    /// Optional mask region (M-MASK.1). When `Some`, the container's
27    /// rendered subtree is clipped to the shape: pixels outside the
28    /// mask have their alpha zeroed before being composited onto the
29    /// parent. The renderer routes clipped containers through an
30    /// offscreen pass — see `crate::render::clip`.
31    pub clip: Option<MaskShape>,
32    pub(crate) children: Vec<NodeId>,
33    pub(crate) parent: Option<NodeId>,
34}
35
36impl Container {
37    /// Construct a default container — identity transform, full alpha, visible, normal blend.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Iterate child node IDs in insertion order.
44    #[must_use = "iterator must be consumed"]
45    pub fn children(&self) -> impl DoubleEndedIterator<Item = NodeId> + ExactSizeIterator + '_ {
46        self.children.iter().copied()
47    }
48
49    /// Number of direct children.
50    #[must_use]
51    pub fn child_count(&self) -> usize {
52        self.children.len()
53    }
54
55    /// Parent node ID, if any.
56    #[must_use]
57    pub fn parent(&self) -> Option<NodeId> {
58        self.parent
59    }
60}
61
62impl Default for Container {
63    fn default() -> Self {
64        Self {
65            transform: Transform::IDENTITY,
66            alpha: 1.0,
67            visible: true,
68            blend_mode: BlendMode::Normal,
69            clip: None,
70            children: Vec::new(),
71            parent: None,
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn default_values() {
82        let c = Container::default();
83        assert_eq!(c.transform, Transform::IDENTITY);
84        assert!((c.alpha - 1.0).abs() < f32::EPSILON);
85        assert!(c.visible);
86        assert_eq!(c.blend_mode, BlendMode::Normal);
87        assert_eq!(c.child_count(), 0);
88        assert_eq!(c.parent(), None);
89    }
90
91    #[test]
92    fn new_equals_default() {
93        let a = Container::new();
94        let b = Container::default();
95        assert_eq!(a.transform, b.transform);
96        assert!((a.alpha - b.alpha).abs() < f32::EPSILON);
97        assert_eq!(a.visible, b.visible);
98        assert_eq!(a.blend_mode, b.blend_mode);
99    }
100}