Skip to main content

ezu_graph/
value.rs

1//! `PortValue` — the runtime values flowing along DAG edges.
2
3use std::sync::Arc;
4
5use crate::buf::{OpaqueValue, RasterBuf, ScalarField};
6use crate::port::PortKind;
7
8/// One value flowing along an edge. Cloning is cheap (Arc / Copy).
9#[derive(Debug, Clone)]
10pub enum PortValue {
11    Features(OpaqueValue),
12    Raster(Arc<RasterBuf>),
13    Sprite(Arc<RasterBuf>),
14    Brush(OpaqueValue),
15    /// Label placement candidates or decisions (see [`PortKind::Labels`]).
16    Labels(OpaqueValue),
17    Scalar(ScalarValue),
18    ScalarField(Arc<ScalarField>),
19}
20
21impl PortValue {
22    pub fn kind(&self) -> PortKind {
23        match self {
24            PortValue::Features(_) => PortKind::Features,
25            PortValue::Raster(_) => PortKind::Raster,
26            PortValue::Sprite(_) => PortKind::Sprite,
27            PortValue::Brush(_) => PortKind::Brush,
28            PortValue::Labels(_) => PortKind::Labels,
29            PortValue::Scalar(_) => PortKind::Scalar,
30            PortValue::ScalarField(_) => PortKind::ScalarField,
31        }
32    }
33
34    pub fn as_scalar_field(&self) -> Option<&Arc<ScalarField>> {
35        if let PortValue::ScalarField(f) = self {
36            Some(f)
37        } else {
38            None
39        }
40    }
41
42    pub fn as_raster(&self) -> Option<&Arc<RasterBuf>> {
43        if let PortValue::Raster(r) = self {
44            Some(r)
45        } else {
46            None
47        }
48    }
49
50    pub fn as_sprite(&self) -> Option<&Arc<RasterBuf>> {
51        if let PortValue::Sprite(s) = self {
52            Some(s)
53        } else {
54            None
55        }
56    }
57
58    pub fn as_scalar(&self) -> Option<&ScalarValue> {
59        if let PortValue::Scalar(s) = self {
60            Some(s)
61        } else {
62            None
63        }
64    }
65}
66
67/// A constant value carried on a `Scalar` port.
68#[derive(Debug, Clone, Copy, PartialEq)]
69pub enum ScalarValue {
70    /// Straight (non-premultiplied) sRGB-encoded RGBA, components in
71    /// `[0, 1]` — the same convention as a parsed `#rrggbb[aa]`
72    /// literal. Consumers linearize / premultiply as needed.
73    Color([f32; 4]),
74    Number(f64),
75    Bool(bool),
76}
77
78impl ScalarValue {
79    pub fn as_color(&self) -> Option<[f32; 4]> {
80        if let ScalarValue::Color(c) = self {
81            Some(*c)
82        } else {
83            None
84        }
85    }
86
87    pub fn as_number(&self) -> Option<f64> {
88        if let ScalarValue::Number(n) = self {
89            Some(*n)
90        } else {
91            None
92        }
93    }
94
95    pub fn as_bool(&self) -> Option<bool> {
96        if let ScalarValue::Bool(b) = self {
97            Some(*b)
98        } else {
99            None
100        }
101    }
102
103    /// Short kind name for error messages (`number` / `color` / `bool`).
104    pub fn kind_name(&self) -> &'static str {
105        match self {
106            ScalarValue::Color(_) => "color",
107            ScalarValue::Number(_) => "number",
108            ScalarValue::Bool(_) => "bool",
109        }
110    }
111
112    /// Feed this value into a cache-key hasher. Stable across runs.
113    pub fn hash_into(&self, h: &mut xxhash_rust::xxh3::Xxh3) {
114        match self {
115            ScalarValue::Color(c) => {
116                h.update(b"C");
117                for ch in c {
118                    h.update(&ch.to_le_bytes());
119                }
120            }
121            ScalarValue::Number(n) => {
122                h.update(b"N");
123                h.update(&n.to_le_bytes());
124            }
125            ScalarValue::Bool(b) => {
126                h.update(b"B");
127                h.update(&[*b as u8]);
128            }
129        }
130    }
131}