use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use xxhash_rust::xxh3::Xxh3;
use crate::{
CanvasInfo, CoordSpace, EvalCtx, EvalError, Node, PortKind, PortSpec, PortValue, RasterBuf,
};
pub(super) struct Mock {
op: &'static str,
inputs: Vec<PortSpec>,
output: PortKind,
pad_grow: u32,
space: CoordSpace,
}
impl Mock {
pub(super) fn new(op: &'static str, inputs: Vec<PortSpec>, output: PortKind) -> Self {
Self {
op,
inputs,
output,
pad_grow: 0,
space: CoordSpace::Inherit,
}
}
pub(super) fn with_pad_grow(mut self, g: u32) -> Self {
self.pad_grow = g;
self
}
pub(super) fn boxed(self) -> Box<dyn Node> {
Box::new(self)
}
}
impl Node for Mock {
fn op_name(&self) -> &'static str {
self.op
}
fn inputs(&self) -> &[PortSpec] {
&self.inputs
}
fn output(&self, _input_kinds: &[Option<PortKind>]) -> PortKind {
self.output
}
fn coord_space(&self) -> CoordSpace {
self.space
}
fn required_pad(&self, downstream: u32) -> u32 {
downstream + self.pad_grow
}
fn eval(
&self,
ctx: &EvalCtx<'_>,
_inputs: &[Option<PortValue>],
) -> Result<PortValue, EvalError> {
let size = ctx.canvas.padded_size();
Ok(match self.output {
PortKind::Raster => {
PortValue::Raster(Arc::new(RasterBuf::filled(size, size, [0, 0, 0, 255])))
}
other => panic!("mock node has no default output for {other:?}"),
})
}
fn param_hash(&self, h: &mut Xxh3) {
h.update(self.op.as_bytes());
h.update(&self.pad_grow.to_le_bytes());
}
}
pub(super) fn src(kind: PortKind) -> Box<dyn Node> {
Mock::new("src", vec![], kind).boxed()
}
pub(super) fn passthrough(input: PortKind, output: PortKind) -> Box<dyn Node> {
let accepts: &'static [PortKind] = match input {
PortKind::Features => &[PortKind::Features],
PortKind::Raster => &[PortKind::Raster],
PortKind::Sprite => &[PortKind::Sprite],
PortKind::Brush => &[PortKind::Brush],
PortKind::Scalar => &[PortKind::Scalar],
PortKind::ScalarField => &[PortKind::ScalarField],
};
Mock::new("pass", vec![PortSpec::new("input", accepts)], output).boxed()
}
pub(super) fn small_canvas() -> CanvasInfo {
CanvasInfo {
tile_size: 8,
pad: 0,
}
}
pub(super) struct Counter {
op: &'static str,
output: PortKind,
inputs: Vec<PortSpec>,
count: AtomicU32,
salt: u32,
}
impl Counter {
pub(super) fn new(op: &'static str, output: PortKind) -> Arc<Self> {
Arc::new(Self {
op,
output,
inputs: vec![],
count: 0.into(),
salt: 0,
})
}
pub(super) fn count(&self) -> u32 {
self.count.load(Ordering::SeqCst)
}
}
impl Node for Counter {
fn op_name(&self) -> &'static str {
self.op
}
fn inputs(&self) -> &[PortSpec] {
&self.inputs
}
fn output(&self, _input_kinds: &[Option<PortKind>]) -> PortKind {
self.output
}
fn eval(
&self,
ctx: &EvalCtx<'_>,
_inputs: &[Option<PortValue>],
) -> Result<PortValue, EvalError> {
self.count.fetch_add(1, Ordering::SeqCst);
let size = ctx.canvas.padded_size();
Ok(PortValue::Raster(Arc::new(RasterBuf::filled(
size,
size,
[64, 0, 0, 64],
))))
}
fn param_hash(&self, h: &mut Xxh3) {
h.update(self.op.as_bytes());
h.update(&self.salt.to_le_bytes());
}
}
pub(super) struct Forward(pub Arc<dyn Node>);
impl Node for Forward {
fn op_name(&self) -> &'static str {
self.0.op_name()
}
fn inputs(&self) -> &[PortSpec] {
self.0.inputs()
}
fn output(&self, input_kinds: &[Option<PortKind>]) -> PortKind {
self.0.output(input_kinds)
}
fn coord_space(&self) -> CoordSpace {
self.0.coord_space()
}
fn required_pad(&self, d: u32) -> u32 {
self.0.required_pad(d)
}
fn eval(
&self,
ctx: &EvalCtx<'_>,
inputs: &[Option<PortValue>],
) -> Result<PortValue, EvalError> {
self.0.eval(ctx, inputs)
}
fn param_hash(&self, h: &mut Xxh3) {
self.0.param_hash(h)
}
}