use std::sync::Arc;
use crate::brush::eval::{BrushNodeEvaluator, EvalContext};
use crate::brush::node::BrushNodeRegistration;
use crate::brush::wgsl::{CompileWgslCtx, DabField, NodeWgsl, UniformField, WgslType};
use crate::brush::wire::{BrushWireType, ScalarValue};
use crate::gpu::params::ParamDef;
use crate::nodegraph::{NodeRegistration, PortDef};
pub const TYPE_ID: &str = "random";
pub fn register() -> BrushNodeRegistration {
BrushNodeRegistration::compute(
NodeRegistration {
type_id: TYPE_ID,
category: "input",
display_name: "Random",
description: "A new random value for every brush mark — useful for jittering size, color, angle, etc.",
ports: vec![PortDef::output("value", BrushWireType::Scalar)
.with_natural_range(0.0, 1.0)
.with_description("Random value in [0, 1)")],
params: &[
ParamDef::Enum {
name: "mode",
options: &["Per-Dab", "Per-Stroke"],
default: 0,
},
],
is_gpu: false,
is_terminal: false,
supports_erase: true,
},
|| Box::new(RandomEvaluator),
)
}
pub struct RandomEvaluator;
impl BrushNodeEvaluator for RandomEvaluator {
fn evaluate_cpu(&self, ctx: &EvalContext) -> Vec<(String, ScalarValue)> {
let mode = match ctx.params.first() {
Some(crate::gpu::params::ParamValue::Int(v)) => *v,
_ => 0,
};
let value = match mode {
1 => ctx.prng_at(0), _ => ctx.prng_at(ctx.dab_index), };
vec![("value".into(), ScalarValue::Scalar(value))]
}
fn compile_wgsl(&self, cctx: &CompileWgslCtx) -> Result<NodeWgsl, String> {
let mut wgsl = NodeWgsl::default();
if !cctx.consumed_outputs.contains("value") {
return Ok(wgsl);
}
let mode = match cctx.params.first() {
Some(crate::gpu::params::ParamValue::Int(v)) => *v,
_ => 0,
};
let per_stroke = mode == 1;
if per_stroke {
let field_name = cctx.uniform_field_name("value");
let key = field_name.clone();
wgsl.uniform_fields.push(UniformField {
name: field_name.clone(),
ty: WgslType::F32,
pack: Arc::new(move |outputs, bytes| {
let v = outputs.get(&key).map(|s| s.as_f32()).unwrap_or(0.0);
bytes.extend_from_slice(bytemuck::bytes_of(&v));
}),
});
wgsl.outputs
.insert("value".into(), format!("u.{}", field_name));
} else {
let field_name = cctx.dab_field_name("value");
let key = field_name.clone();
wgsl.dab_fields.push(DabField {
name: field_name.clone(),
ty: WgslType::F32,
pack: Arc::new(move |outputs, bytes| {
let v = outputs.get(&key).map(|s| s.as_f32()).unwrap_or(0.0);
bytes.extend_from_slice(bytemuck::bytes_of(&v));
}),
});
wgsl.outputs
.insert("value".into(), format!("d.{}", field_name));
}
Ok(wgsl)
}
}