Skip to main content

ezu_graph/
eval.rs

1//! Evaluation context, asset loader, and error types used during
2//! `Node::eval`.
3
4use std::collections::HashMap;
5use std::sync::Arc;
6
7use crate::buf::{OpaqueValue, RasterBuf, ScalarField};
8use crate::value::ScalarValue;
9
10/// Tile coordinate (z/x/y in TMS-ish form; the meaning is up to the host).
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct TileId {
13    pub z: u8,
14    pub x: u32,
15    pub y: u32,
16}
17
18/// Per-render canvas geometry. The padded buffer is the actual size all
19/// `Raster` ports must produce; the final tile is the inner `tile_size`
20/// region.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct CanvasInfo {
23    pub tile_size: u32,
24    pub pad: u32,
25}
26
27impl CanvasInfo {
28    pub fn padded_size(&self) -> u32 {
29        self.tile_size + 2 * self.pad
30    }
31}
32
33/// One asset fetched by an [`AssetLoader`].
34///
35/// The shape is uniform across input kinds (images, brushes, feature
36/// layers, …) so every source-style node consumes the host through the
37/// same trait — like a shader sampling a typed uniform binding.
38/// `Features` carries a type-erased payload; by convention the
39/// concrete type is `Arc<ezu_features::FeatureLayer>`.
40#[derive(Debug, Clone)]
41pub enum Asset {
42    Image(Arc<RasterBuf>),
43    Brush(OpaqueValue),
44    Features(OpaqueValue),
45    ScalarField(Arc<ScalarField>),
46}
47
48#[derive(Debug, thiserror::Error)]
49pub enum AssetError {
50    #[error("asset not found: `{0}`")]
51    NotFound(String),
52    #[error("asset decode failed for `{src}`: {msg}")]
53    Decode { src: String, msg: String },
54    #[error("asset error: {0}")]
55    Other(String),
56}
57
58/// Pluggable backend for resolving named asset bindings (images,
59/// brushes, tile features, …). Names that start with `tile.` are by
60/// convention tile-scoped — the host rebinds them per render.
61///
62/// `hash` returns a stable content/identity hash the evaluator folds
63/// into every consuming node's cache key, so changes in a bound asset
64/// invalidate caches automatically. Implementations may return `0` if
65/// the binding never changes (document-scoped, fixed disk file, etc.).
66pub trait AssetLoader: Send + Sync {
67    fn load(&self, name: &str) -> Result<Asset, AssetError>;
68
69    /// Content/identity hash for cache invalidation. The default of
70    /// `0` is safe for assets that never change for the lifetime of a
71    /// loader (typical for in-memory image / brush banks).
72    fn hash(&self, _name: &str) -> u128 {
73        0
74    }
75}
76
77/// A no-op asset loader. Every load returns `NotFound`. Useful for
78/// tests of graphs that don't touch any asset.
79pub struct NoAssets;
80impl AssetLoader for NoAssets {
81    fn load(&self, name: &str) -> Result<Asset, AssetError> {
82        Err(AssetError::NotFound(name.to_string()))
83    }
84}
85
86/// Resolved parameter values for one render. Nodes look up `$name`
87/// references here.
88#[derive(Debug, Default, Clone)]
89pub struct ParamValues {
90    pub values: HashMap<String, ScalarValue>,
91}
92
93impl ParamValues {
94    pub fn new() -> Self {
95        Self::default()
96    }
97
98    pub fn set(&mut self, name: impl Into<String>, value: ScalarValue) {
99        self.values.insert(name.into(), value);
100    }
101
102    pub fn get(&self, name: &str) -> Option<ScalarValue> {
103        self.values.get(name).copied()
104    }
105}
106
107/// Read-only environment a node sees during `eval`.
108pub struct EvalCtx<'a> {
109    pub tile: TileId,
110    pub canvas: CanvasInfo,
111    pub assets: &'a dyn AssetLoader,
112    pub params: &'a ParamValues,
113    /// Deterministic root seed for this render. World-anchored nodes
114    /// hash this with world coordinates to produce per-feature seeds.
115    pub rng_seed: u64,
116}
117
118#[derive(Debug, thiserror::Error)]
119pub enum EvalError {
120    #[error("input port `{0}` was not supplied")]
121    MissingInput(String),
122    #[error("input `{port}` has wrong kind: expected {expected:?}, got {got:?}")]
123    InputKindMismatch {
124        port: String,
125        expected: crate::port::PortKind,
126        got: crate::port::PortKind,
127    },
128    #[error(transparent)]
129    Asset(#[from] AssetError),
130    #[error("{0}")]
131    Other(String),
132}