diff options
| author | s-ol <s+removethis@s-ol.nu> | 2026-01-23 17:21:46 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2026-01-23 17:21:46 +0000 |
| commit | d217d2d03ac8cb6046d3435226cbd9bcef13335a (patch) | |
| tree | c262e171556c61711ebb76e5136ad4529a8531af | |
| parent | generic save / save as (diff) | |
| download | nodetoy-d217d2d03ac8cb6046d3435226cbd9bcef13335a.tar.gz nodetoy-d217d2d03ac8cb6046d3435226cbd9bcef13335a.zip | |
variadic arithmetic
| -rw-r--r-- | src/examples/shadertoy_plasma.nt.json | 2 | ||||
| -rw-r--r-- | src/library.rs | 99 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/node.rs | 5 | ||||
| -rw-r--r-- | src/snarl_ext.rs | 13 | ||||
| -rw-r--r-- | src/types.rs | 29 |
6 files changed, 112 insertions, 39 deletions
diff --git a/src/examples/shadertoy_plasma.nt.json b/src/examples/shadertoy_plasma.nt.json index 0df1b75..cf2d53b 100644 --- a/src/examples/shadertoy_plasma.nt.json +++ b/src/examples/shadertoy_plasma.nt.json @@ -1 +1 @@ -{"nodes":{"0":{"value":{"Input":"UV"},"pos":{"x":163.98965,"y":294.79865},"open":true},"1":{"value":{"Output":null},"pos":{"x":1357.2115,"y":327.50165},"open":true},"2":{"value":{"Arithmetic":"Multiply"},"pos":{"x":1100.7025,"y":315.54364},"open":true},"3":{"value":{"Constant":{"Float":0.5}},"pos":{"x":922.1152,"y":389.79797},"open":true},"4":{"value":{"Arithmetic":"Add"},"pos":{"x":1241.6924,"y":324.00272},"open":true},"7":{"value":{"Input":"Time"},"pos":{"x":549.9027,"y":214.97101},"open":true},"8":{"value":{"Conversion":{"SplitVector":null}},"pos":{"x":357.21643,"y":295.80508},"open":true},"9":{"value":{"Conversion":{"CombineVector":null}},"pos":{"x":530.1639,"y":293.92538},"open":true},"10":{"value":{"Arithmetic":"Add"},"pos":{"x":761.3871,"y":255.3878},"open":true},"11":{"value":{"Arithmetic":"Add"},"pos":{"x":875.119,"y":307.0838},"open":true},"12":{"value":{"Constant":{"Vec3":[0.0,2.0,4.0]}},"pos":{"x":726.6099,"y":349.38098},"open":true},"13":{"value":{"Builtin":{"Thru1":"Cos"}},"pos":{"x":988.8504,"y":308.02408},"open":true}},"wires":[{"out_pin":{"node":12,"output":0},"in_pin":{"node":11,"input":1}},{"out_pin":{"node":4,"output":0},"in_pin":{"node":1,"input":0}},{"out_pin":{"node":8,"output":1},"in_pin":{"node":9,"input":1}},{"out_pin":{"node":11,"output":0},"in_pin":{"node":13,"input":0}},{"out_pin":{"node":9,"output":0},"in_pin":{"node":10,"input":1}},{"out_pin":{"node":3,"output":0},"in_pin":{"node":2,"input":1}},{"out_pin":{"node":8,"output":0},"in_pin":{"node":9,"input":2}},{"out_pin":{"node":3,"output":0},"in_pin":{"node":4,"input":1}},{"out_pin":{"node":7,"output":0},"in_pin":{"node":10,"input":0}},{"out_pin":{"node":13,"output":0},"in_pin":{"node":2,"input":0}},{"out_pin":{"node":0,"output":0},"in_pin":{"node":8,"input":0}},{"out_pin":{"node":2,"output":0},"in_pin":{"node":4,"input":0}},{"out_pin":{"node":8,"output":0},"in_pin":{"node":9,"input":0}},{"out_pin":{"node":10,"output":0},"in_pin":{"node":11,"input":0}}]} +{"nodes":{"0":{"value":{"Input":"UV"},"pos":{"x":163.98965,"y":294.79865},"open":true},"1":{"value":{"Output":null},"pos":{"x":1357.2115,"y":327.50165},"open":true},"2":{"value":{"Arithmetic":"Multiply"},"pos":{"x":1093.4141,"y":307.34406},"open":true},"3":{"value":{"Constant":{"Float":0.5}},"pos":{"x":855.6077,"y":394.3533},"open":true},"4":{"value":{"Arithmetic":"Add"},"pos":{"x":1237.1371,"y":340.40186},"open":true},"7":{"value":{"Input":"Time"},"pos":{"x":549.9027,"y":214.97101},"open":true},"8":{"value":{"Conversion":{"SplitVector":null}},"pos":{"x":357.21643,"y":295.80508},"open":true},"9":{"value":{"Conversion":{"CombineVector":null}},"pos":{"x":530.1639,"y":293.92538},"open":true},"11":{"value":{"Arithmetic":"Add"},"pos":{"x":815.119,"y":269.0838},"open":true},"12":{"value":{"Constant":{"Vec3":[0.0,2.0,4.0]}},"pos":{"x":571.64056,"y":415.3007},"open":true},"13":{"value":{"Builtin":{"Thru1":"Cos"}},"pos":{"x":970.6291,"y":287.06967},"open":true}},"wires":[{"out_pin":{"node":11,"output":0},"in_pin":{"node":13,"input":0}},{"out_pin":{"node":4,"output":0},"in_pin":{"node":1,"input":0}},{"out_pin":{"node":13,"output":0},"in_pin":{"node":2,"input":0}},{"out_pin":{"node":2,"output":0},"in_pin":{"node":4,"input":0}},{"out_pin":{"node":12,"output":0},"in_pin":{"node":11,"input":2}},{"out_pin":{"node":7,"output":0},"in_pin":{"node":11,"input":0}},{"out_pin":{"node":9,"output":0},"in_pin":{"node":11,"input":1}},{"out_pin":{"node":0,"output":0},"in_pin":{"node":8,"input":0}},{"out_pin":{"node":8,"output":0},"in_pin":{"node":9,"input":0}},{"out_pin":{"node":8,"output":0},"in_pin":{"node":9,"input":2}},{"out_pin":{"node":8,"output":1},"in_pin":{"node":9,"input":1}},{"out_pin":{"node":3,"output":0},"in_pin":{"node":2,"input":1}},{"out_pin":{"node":3,"output":0},"in_pin":{"node":4,"input":1}}]}
\ No newline at end of file diff --git a/src/library.rs b/src/library.rs index a45ff24..cd3b36c 100644 --- a/src/library.rs +++ b/src/library.rs @@ -108,11 +108,16 @@ impl<T: FixedNode> ConcreteNode for T { fn compile( &self, signature: TypeSignature, - inputs: Vec<String>, + inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, ) -> fmt::Result { - self.compile(signature, inputs, outputs, f) + let inputs: Option<Vec<String>> = inputs.into_iter().collect(); + if let Some(inputs) = inputs { + self.compile(signature, inputs, outputs, f) + } else { + Err(fmt::Error) + } } } @@ -152,7 +157,7 @@ impl<T: OutputOnlyNode> ConcreteNode for T { fn compile( &self, signature: TypeSignature, - inputs: Vec<String>, + inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, ) -> fmt::Result { @@ -170,43 +175,77 @@ pub enum BinArithmetic { Divide, } -impl FixedNode for BinArithmetic { - fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { - // @TODO: matrix and matrix/vector operations - // componentwise and vector-scalar operations - Box::new( - [GEN_F_TYPES, GEN_D_TYPES, GEN_I_TYPES, GEN_U_TYPES] - .into_iter() - .flatten() - .flat_map(|t| { - [ - TypeSignature::new([t, t], [t]), - TypeSignature::new([t, t.scalar().into()], [t]), - TypeSignature::new([t.scalar().into(), t], [t]), - ] - }) - .dedup(), - ) +fn check_gentype(inputs: &[Option<Type>]) -> Result<Option<Type>, ()> { + let mut seen: Option<Type> = None; + + for input in inputs { + seen = match (seen, input) { + (a, None) => a, + (None, a) => *a, + (Some(a), Some(b)) => Some(Type::upcast_gentype(a, *b)?), + } + } + + Ok(seen) +} + +impl ConcreteNode for BinArithmetic { + fn max_inputs(&self) -> usize { + 10 + } + + fn num_inputs(&self, connected: &[Option<Type>]) -> usize { + connected + .iter() + .enumerate() + .filter(|(_, v)| v.is_some()) + .map(|(i, _)| i + 2) + .last() + .unwrap_or(2) + } + + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature> { + if let Ok(out) = check_gentype(connected) { + let out = out.unwrap_or_default(); + + vec![TypeSignature { + inputs: connected.iter().map(|v| v.unwrap_or_default()).collect(), + outputs: Box::new([out]), + }] + } else { + vec![] + } + } + + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature { + let out = check_gentype(connected) + .ok() + .flatten() + .unwrap_or(Scalar(Float(Single))); + + TypeSignature { + inputs: connected.iter().map(|v| v.unwrap_or(out)).collect(), + outputs: Box::new([out]), + } } fn compile( &self, signature: TypeSignature, - inputs: Vec<String>, + inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, ) -> fmt::Result { let out_typ = &signature.outputs[0]; let out_name = &outputs[0]; - let left = &inputs[0]; - let right = &inputs[1]; let sym = match self { - Self::Add => "+", - Self::Subtract => "-", - Self::Multiply => "*", - Self::Divide => "/", + Self::Add => " + ", + Self::Subtract => " - ", + Self::Multiply => " * ", + Self::Divide => " / ", }; - writeln!(f, "{out_typ} {out_name} = {left} {sym} {right};") + let expr = inputs.iter().filter_map(|s| s.as_ref()).join(sym); + writeln!(f, "{out_typ} {out_name} = {expr};") } } @@ -926,7 +965,7 @@ where fn compile( &self, _signature: TypeSignature, - _inputs: Vec<String>, + _inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, ) -> fmt::Result { @@ -1080,7 +1119,7 @@ impl ConcreteNode for Input { fn compile( &self, signature: TypeSignature, - _inputs: Vec<String>, + _inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, ) -> fmt::Result { diff --git a/src/main.rs b/src/main.rs index ba1c356..35a3ca3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -86,8 +86,7 @@ impl SnarlViewer<AnyNode> for Viewer { } fn inputs(&mut self, id: NodeId, snarl: &Snarl<AnyNode>) -> usize { - let node = &snarl[id]; - node.max_inputs() + snarl.get_num_inputs(id) } fn outputs(&mut self, id: NodeId, snarl: &Snarl<AnyNode>) -> usize { diff --git a/src/node.rs b/src/node.rs index 659d67f..3bcd73b 100644 --- a/src/node.rs +++ b/src/node.rs @@ -9,6 +9,9 @@ use crate::types::{Type, TypeSignature}; #[enum_dispatch] pub trait ConcreteNode { fn max_inputs(&self) -> usize; + fn num_inputs(&self, _connected: &[Option<Type>]) -> usize { + self.max_inputs() + } // set of possible input type combinations given current connections fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature>; @@ -18,7 +21,7 @@ pub trait ConcreteNode { fn compile( &self, signature: TypeSignature, - inputs: Vec<String>, + inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, ) -> fmt::Result; diff --git a/src/snarl_ext.rs b/src/snarl_ext.rs index 7d0464d..e36eb2e 100644 --- a/src/snarl_ext.rs +++ b/src/snarl_ext.rs @@ -10,6 +10,7 @@ pub trait WithSignatures { fn out_pin_type(&self, pin: OutPinId) -> Type; fn get_input_types(&self, node: NodeId) -> Vec<Option<Type>>; fn get_node_signature(&self, node: NodeId) -> TypeSignature; + fn get_num_inputs(&self, node: NodeId) -> usize; } pub trait Compilable { @@ -43,6 +44,10 @@ impl WithSignatures for Snarl<AnyNode> { fn get_node_signature(&self, node: NodeId) -> TypeSignature { self[node].signature(&self.get_input_types(node)) } + + fn get_num_inputs(&self, node: NodeId) -> usize { + self[node].num_inputs(&self.get_input_types(node)) + } } fn compile_output(pin: OutPinId) -> String { @@ -85,11 +90,11 @@ void main() {{ fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> fmt::Result { let signature = self.get_node_signature(node); - let inputs: Result<Vec<String>, fmt::Error> = (0..signature.inputs.len()) + let inputs: Vec<Option<String>> = (0..signature.inputs.len()) .map( |input| match &*self.in_pin(InPinId { node, input }).remotes { - [] => Err(fmt::Error), - [pin] => Ok(compile_output(*pin)), + [] => None, + [pin] => Some(compile_output(*pin)), _ => unreachable!("cannot connect to multiple inputs"), }, ) @@ -99,6 +104,6 @@ void main() {{ .map(|output| compile_output(OutPinId { node, output })) .collect(); - self[node].compile(signature, inputs?, outputs, f) + self[node].compile(signature, inputs, outputs, f) } } diff --git a/src/types.rs b/src/types.rs index 5614ab7..febdace 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,8 @@ use std::fmt; -#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[derive(Copy, Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub enum FloatPrecision { + #[default] Single, Double, } @@ -33,6 +34,11 @@ impl ScalarType { None } } +impl Default for ScalarType { + fn default() -> Self { + Self::Float(Default::default()) + } +} impl fmt::Display for ScalarType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -91,6 +97,21 @@ impl Type { } } + pub fn upcast_gentype(self, other: Self) -> Result<Self, ()> { + if self == other { + // same scalar or complex type + Ok(self) + } else if Self::Scalar(self.scalar()) == other { + // complex and scalar type + Ok(self) + } else if Self::Scalar(other.scalar()) == self { + // complex and scalar type + Ok(other) + } else { + Err(()) + } + } + pub fn pick(ui: &mut egui::Ui) -> Option<Self> { let mut result: Option<Self> = ScalarType::pick(ui).map(Self::Scalar); @@ -149,6 +170,12 @@ impl fmt::Display for Type { } } +impl Default for Type { + fn default() -> Self { + Self::Scalar(Default::default()) + } +} + /// a single concrete type signature for a function #[derive(PartialEq, Debug)] pub struct TypeSignature { |
