diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-12-24 14:35:36 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-12-24 14:35:36 +0000 |
| commit | 56f6384148c3475b4d99a5fe3f8d1913d8f66aac (patch) | |
| tree | 2a6891b58b0bb372ae79920677a790a7a56891e5 /src | |
| parent | attempt generic (diff) | |
| download | nodetoy-56f6384148c3475b4d99a5fe3f8d1913d8f66aac.tar.gz nodetoy-56f6384148c3475b4d99a5fe3f8d1913d8f66aac.zip | |
typing working
Diffstat (limited to 'src')
| -rw-r--r-- | src/library.rs | 380 | ||||
| -rw-r--r-- | src/main.rs | 616 |
2 files changed, 512 insertions, 484 deletions
diff --git a/src/library.rs b/src/library.rs new file mode 100644 index 0000000..d0b97ef --- /dev/null +++ b/src/library.rs @@ -0,0 +1,380 @@ +use enum_dispatch::enum_dispatch; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; + +use crate::{ + ConcreteNode, Dimension::*, FloatPrecision::*, ScalarType::*, Type, Type::*, TypeSignature, +}; + +const GEN_F_TYPES: [Type; 4] = [ + Scalar(Float(Single)), + Vector(Float(Single), D2), + Vector(Float(Single), D3), + Vector(Float(Single), D4), +]; +const GEN_D_TYPES: [Type; 4] = [ + Scalar(Float(Double)), + Vector(Float(Double), D2), + Vector(Float(Double), D3), + Vector(Float(Double), D4), +]; +const GEN_I_TYPES: [Type; 4] = [ + Scalar(Int), + Vector(Int, D2), + Vector(Int, D3), + Vector(Int, D4), +]; +const GEN_U_TYPES: [Type; 4] = [ + Scalar(UInt), + Vector(UInt, D2), + Vector(UInt, D3), + Vector(UInt, D4), +]; +const GEN_B_TYPES: [Type; 4] = [ + Scalar(Bool), + Vector(Bool, D2), + Vector(Bool, D3), + Vector(Bool, D4), +]; + +/// Helper trait for Nodes with fixed number of inputs and outputs +trait FixedNode { + const NI: usize; + const NO: usize; + + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>>; +} +impl<T: FixedNode> ConcreteNode for T { + fn inputs(&self) -> usize { + Self::NI + } + fn outputs(&self) -> usize { + Self::NO + } + + // set of possible input type combinations given current connections + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature> { + self.all_signatures() + .filter(|sig| sig.matches_inputs(connected)) + .collect() + } + + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature { + self.all_signatures() + .find(|sig| sig.matches_inputs(connected)) + .expect("have to have fallback") + } +} + +/// Arithmetic Operations +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum BinArithmetic { + Add, + Subtract, + Multiply, + Divide, +} +impl BinArithmetic { + pub fn all() -> impl Iterator<Item = Self> { + [ + BinArithmetic::Add, + BinArithmetic::Subtract, + BinArithmetic::Multiply, + BinArithmetic::Divide, + ] + .into_iter() + } +} +impl FixedNode for BinArithmetic { + const NI: usize = 2; + const NO: usize = 1; + + 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()], [t]), + TypeSignature::new([t.scalar(), t], [t]), + ] + }) + .dedup(), + ) + } +} + +/// 1-1 thru operations +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Thru1 { + Abs, + Sign, + Floor, + Trunc, + Round, + RoundEven, + Ceil, + Fract, +} +impl Thru1 { + pub fn all() -> impl Iterator<Item = Self> { + [ + Thru1::Abs, + Thru1::Sign, + Thru1::Floor, + Thru1::Trunc, + Thru1::Round, + Thru1::RoundEven, + Thru1::Ceil, + Thru1::Fract, + ] + .into_iter() + } +} +impl FixedNode for Thru1 { + const NI: usize = 1; + const NO: usize = 1; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + match self { + Thru1::Abs | Thru1::Sign => vec![GEN_F_TYPES, GEN_I_TYPES, GEN_D_TYPES], + _ => vec![GEN_F_TYPES, GEN_D_TYPES], + } + .into_iter() + .flatten() + .map(|t| TypeSignature::new([t], [t])), + ) + } +} + +/// Modulo +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Mod; +impl Mod { + pub fn all() -> impl Iterator<Item = Self> { + std::iter::once(Mod) + } +} +impl FixedNode for Mod { + const NI: usize = 2; + const NO: usize = 1; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + [GEN_F_TYPES, GEN_D_TYPES] + .into_iter() + .flatten() + .flat_map(|t| { + [ + TypeSignature::new([t, t.scalar()], [t]), + TypeSignature::new([t, t], [t]), + ] + }) + .dedup(), + ) + } +} + +/// Modf +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Modf; +impl Modf { + pub fn all() -> impl Iterator<Item = Self> { + std::iter::once(Modf) + } +} +impl FixedNode for Modf { + const NI: usize = 1; + const NO: usize = 2; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + [GEN_F_TYPES, GEN_D_TYPES] + .into_iter() + .flatten() + .map(|t| TypeSignature::new([t], [t, t])), + ) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum MinMax { + Min, + Max, +} +impl MinMax { + pub fn all() -> impl Iterator<Item = Self> { + [MinMax::Min, MinMax::Max].into_iter() + } +} +impl FixedNode for MinMax { + const NI: usize = 2; + const NO: usize = 1; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + [GEN_F_TYPES, GEN_D_TYPES, GEN_I_TYPES, GEN_U_TYPES] + .into_iter() + .flatten() + .flat_map(|t| { + [ + TypeSignature::new([t, t.scalar()], [t]), + TypeSignature::new([t, t], [t]), + ] + }) + .dedup(), + ) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Clamp; +impl Clamp { + pub fn all() -> impl Iterator<Item = Self> { + std::iter::once(Clamp) + } +} +impl FixedNode for Clamp { + const NI: usize = 3; + const NO: usize = 1; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + [GEN_F_TYPES, GEN_D_TYPES, GEN_I_TYPES, GEN_U_TYPES] + .into_iter() + .flatten() + .flat_map(|t| { + [ + TypeSignature::new([t, t.scalar(), t.scalar()], [t]), + TypeSignature::new([t, t, t], [t]), + ] + }) + .dedup(), + ) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Mix; +impl Mix { + pub fn all() -> impl Iterator<Item = Self> { + std::iter::once(Mix) + } +} +impl FixedNode for Mix { + const NI: usize = 3; + const NO: usize = 1; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + [GEN_F_TYPES, GEN_D_TYPES] + .into_iter() + .flatten() + .flat_map(|t| { + let b = match t { + Scalar(_) => Scalar(Bool), + Vector(_, d) => Vector(Bool, d), + _ => unreachable!("mix doesnt exist for matrices"), + }; + + [ + TypeSignature::new([t, t, t.scalar()], [t]), + TypeSignature::new([t, t, t], [t]), + TypeSignature::new([t, t, b], [t]), + ] + }) + .dedup(), + ) + } +} + +#[enum_dispatch(ConcreteNode)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum BuiltinFunction { + BinArithmetic(BinArithmetic), + Thru1(Thru1), + Mod(Mod), + Modf(Modf), + MinMax(MinMax), + Clamp(Clamp), + Mix(Mix), +} + +impl BuiltinFunction { + pub fn all() -> impl Iterator<Item = Self> { + BinArithmetic::all() + .map(BuiltinFunction::from) + .chain(Thru1::all().map(BuiltinFunction::from)) + .chain(Mod::all().map(BuiltinFunction::from)) + .chain(Modf::all().map(BuiltinFunction::from)) + .chain(MinMax::all().map(BuiltinFunction::from)) + .chain(Clamp::all().map(BuiltinFunction::from)) + .chain(Mix::all().map(BuiltinFunction::from)) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Output; +impl FixedNode for Output { + const NI: usize = 1; + const NO: usize = 0; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new( + [ + TypeSignature::new([Vector(Float(Single), D3)], []), + TypeSignature::new([Vector(Float(Single), D4)], []), + ] + .into_iter(), + ) + } +} +impl Output { + pub fn all() -> impl Iterator<Item = Self> { + std::iter::once(Output) + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Constant { + typ: Type, + value: String, +} + +impl FixedNode for Constant { + const NI: usize = 0; + const NO: usize = 1; + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new(std::iter::once(TypeSignature::new([], [self.typ]))) + } +} + +impl Constant { + pub fn all() -> impl Iterator<Item = Constant> { + [ + Constant { + typ: Scalar(Float(Single)), + value: "0.0".to_string(), + }, + Constant { + typ: Vector(Float(Single), D2), + value: "vec2(0)".to_string(), + }, + Constant { + typ: Vector(Float(Single), D3), + value: "vec3(0)".to_string(), + }, + Constant { + typ: Vector(Float(Single), D4), + value: "vec4(0)".to_string(), + }, + Constant { + typ: Scalar(Int), + value: "0".to_string(), + }, + Constant { + typ: Scalar(UInt), + value: "0".to_string(), + }, + ] + .into_iter() + } +} diff --git a/src/main.rs b/src/main.rs index 294df7d..de5581b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,287 +11,89 @@ use egui_snarl::{ get_selected_nodes, }, }; -use itertools::Itertools; +use enum_dispatch::enum_dispatch; -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] enum FloatPrecision { - Float, - Double, + Single, + Double, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] enum ScalarType { - Float(FloatPrecision), - Int, - UInt, - Bool, + Float(FloatPrecision), + Int, + UInt, + Bool, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] enum Dimension { - D2, - D3, - D4, + D2, + D3, + D4, } -#[derive(Clone, Debug, PartialEq)] -enum ConcreteType { - Scalar(ScalarType), - Vector(ScalarType, Dimension), - Matrix(FloatPrecision, Dimension, Dimension), +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +enum Type { + Scalar(ScalarType), + Vector(ScalarType, Dimension), + Matrix(FloatPrecision, Dimension, Dimension), } -impl ConcreteType { - fn is_scalar(&self) -> bool { - match self { - ConcreteType::Scalar(_) => true, - _ => false, - } - } - - fn scalar(&self) -> ScalarType { - match self { - ConcreteType::Scalar(t) => t, - ConcreteType::Vector(t, _) => t, - ConcreteType::Matrix(p, _) => ScalarType::Float(p), +impl Type { + fn scalar(&self) -> Type { + match self { + Type::Scalar(_) => *self, + Type::Vector(t, _) => Type::Scalar(*t), + Type::Matrix(p, _, _) => Type::Scalar(ScalarType::Float(*p)), + } } - } } /// a single concrete type signature for a function #[derive(PartialEq)] -struct TypeSignature<const NI: usize, const NO: usize> { - inputs: [ConcreteType; NI], - outputs: [ConcreteType; NO], -} - -trait ConcreteNode<const NI: usize, const NO: usize> { - const NUM_INPUTS: usize = NI; - const NUM_OUTPUTS: usize = NO; - - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<{ Self::NI }, { Self::NO }>>; -} - -trait GenericSignature { - fn inputs(&self) -> &[ConcreteType]; - fn outputs(&self) -> &[ConcreteType]; -} - -impl<const NI: usize, const NO: usize> TypeSignature<NI, NO> { - fn matches_inputs(&self, connected: &[Option<ConcreteType>; NI]) -> bool { - self.inputs.iter().zip(connected.iter()).all(|expected_inp, possibly_connected_inp| { - if let Some(connected_inp) = possibly_connected_inp { - connected_inp == expected_inp - } else { true } - }) - } -} - -impl<const NI: usize, const NO: usize> GenericSignature for TypeSignature<NI, NO> { - fn inputs(&self) -> &[ConcreteType] { - &self.inputs - } - - fn outputs(&self) -> &[ConcreteType] { - &self.outputs - } +struct TypeSignature { + inputs: Box<[Type]>, + outputs: Box<[Type]>, } -trait GenericNode { - fn all_signatures(&self) -> impl Iterator<Item = &impl GenericSignature>; -} - -impl<const NI: usize, const NO: usize> dyn ConcreteNode<NI, NO> { - // set of possible input type combinations given current connections - fn signatures_matching(&self, connected: &[Option<ConcreteType>; NI]) -> impl Iterator<Item = [ConcreteType; NI]> { - self.all_signatures().filter(|sig| sig.matches_inputs(connected)) - } - - fn get_signature(&self, connected: &[Option<ConcreteType>; NI]) -> [ConcreteType; NI] { - self.find(|sig| sig.matches_inputs(connected)) - } - - /// the concrete output type, given all inputs are connected - fn output_types(&self, inputs: [ConcreteType; NI]) -> [ConcreteType; NO] { - self.all_signatures.find(|sig| sig.inputs == inputs) - .expect("invalid type signature connected") - .outputs - } - - fn get_input_types(&self, node: NodeId, snarl: &Snarl<DemoNode>) { - let mut types: [Option<ConcreteType>; NI] = [None; NI]; - for input in 0..NI { - match &*snarl.in_pin(InPinId { node, input }).remotes { - [] => {}, - [out_pin] => types[input] = DemoViewer::get_out_type(*out_pin, snarl), - _ => unreachable!("cannot connect to multiple inputs"), - } +impl TypeSignature { + pub fn new<const NI: usize, const NO: usize>(inputs: [Type; NI], outputs: [Type; NO]) -> Self { + Self { + inputs: Box::new(inputs), + outputs: Box::new(outputs), + } } - types - } } -const GEN_F_TYPES: Vec<ConcreteType> = vec![ - ConcreteType::Scalar(ScalarType::Float(FloatPrecision::Float)), - ConcreteType::Vector(ScalarType::Float(FloatPrecision::Float), Dimension::D2), - ConcreteType::Vector(ScalarType::Float(FloatPrecision::Float), Dimension::D3), - ConcreteType::Vector(ScalarType::Float(FloatPrecision::Float), Dimension::D4), -]; -const GEN_D_TYPES: Vec<ConcreteType> = vec![ - ConcreteType::Scalar(ScalarType::Float(FloatPrecision::Double)), - ConcreteType::Vector(ScalarType::Float(FloatPrecision::Double), Dimension::D2), - ConcreteType::Vector(ScalarType::Float(FloatPrecision::Double), Dimension::D3), - ConcreteType::Vector(ScalarType::Float(FloatPrecision::Double), Dimension::D4), -]; -const GEN_I_TYPES: Vec<ConcreteType> = vec![ - ConcreteType::Scalar(ScalarType::Int), - ConcreteType::Vector(ScalarType::Int, Dimension::D2), - ConcreteType::Vector(ScalarType::Int, Dimension::D3), - ConcreteType::Vector(ScalarType::Int, Dimension::D4), -]; -const GEN_U_TYPES: Vec<ConcreteType> = vec![ - ConcreteType::Scalar(ScalarType::UInt), - ConcreteType::Vector(ScalarType::UInt, Dimension::D2), - ConcreteType::Vector(ScalarType::UInt, Dimension::D3), - ConcreteType::Vector(ScalarType::UInt, Dimension::D4), -]; -const GEN_B_TYPES: Vec<ConcreteType> = vec![ - ConcreteType::Scalar(ScalarType::Bool), - ConcreteType::Vector(ScalarType::Bool, Dimension::D2), - ConcreteType::Vector(ScalarType::Bool, Dimension::D3), - ConcreteType::Vector(ScalarType::Bool, Dimension::D4), -]; - -enum BinArithmetic { - Add, - Subtract, - Multiply, - Divide, -} +#[enum_dispatch] +trait ConcreteNode { + fn inputs(&self) -> usize; + fn outputs(&self) -> usize; -enum Thru1FDI { - Abs, - Sign, -} + // set of possible input type combinations given current connections + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature>; -enum Thru1FD { - Floor, - Trunc, - Round, - RoundEven, - Ceil, - Fract, + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature; } -struct Mod; -struct Modf; - -enum MinMax { - Min, - Max, -} - -struct Clamp; - -struct Mix; - -impl ConcreteNode<1, 1> for BinArithmetic { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - // @TODO: matrix and matrix/vector operations - // componentwise and vector-scalar operations - vec![ GEN_F_TYPES, GEN_D_TYPES, GEN_I_TYPES, GEN_U_TYPES ] - .into_iter() - .flatten() - .flat_map(|t| [ - TypeSignature { inputs: [t, t], outputs: [t] }, - TypeSignature { inputs: [t, t.scalar()], outputs: [t] }, - TypeSignature { inputs: [t.scalar(), t], outputs: [t] }, - ]) - .dedup() - } -} -impl ConcreteNode<1, 1> for Thru1FDI { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![GEN_F_TYPES, GEN_I_TYPES, GEN_D_TYPES] - .into_iter() - .flatten() - .map(|t| TypeSignature { inputs: [t], outputs: [t] }) - } -} - -impl ConcreteNode<1, 1> for Thru1FD { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![GEN_F_TYPES, GEN_D_TYPES] - .into_iter() - .flatten() - .map(|t| TypeSignature { inputs: [t], outputs: [t] }) - } -} -impl ConcreteNode<1, 2> for Mod { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![ GEN_F_TYPES, GEN_D_TYPES ] - .into_iter() - .flatten() - .flat_map(|t| [ - TypeSignature { inputs: [t, t.scalar()], outputs: [t] }, - TypeSignature { inputs: [t, t], outputs: [t] }, - ]) - .dedup() - } -} -impl ConcreteNode<1, 2> for Modf { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![ GEN_F_TYPES, GEN_D_TYPES ] - .into_iter() - .flatten() - .map(|t| TypeSignature { inputs: [t], outputs: [t, t] }) - } -} -impl ConcreteNode<2, 1> for MinMax { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![ GEN_F_TYPES, GEN_D_TYPES, GEN_I_TYPES, GEN_U_TYPES ] - .into_iter() - .flatten() - .flat_map(|t| [ - TypeSignature { inputs: [t, t.scalar()], outputs: [t] }, - TypeSignature { inputs: [t, t], outputs: [t] }, - ]) - .dedup() - } -} -impl ConcreteNode<3, 1> for Clamp { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![ GEN_F_TYPES, GEN_D_TYPES, GEN_I_TYPES, GEN_U_TYPES ] - .into_iter() - .flatten() - .flat_map(|t| [ - TypeSignature { inputs: [t, t.scalar(), t.scalar()], outputs: [t] }, - TypeSignature { inputs: [t, t, t], outputs: [t] }, - ]) - .dedup() - } -} -impl ConcreteNode<3, 1> for Mix { - fn all_signatures(&self) -> impl Iterator<Item = TypeSignature<Self::NI, Self::NO>> { - vec![ GEN_F_TYPES, GEN_D_TYPES ] - .into_iter() - .flatten() - .flat_map(|t| { - let b = match t { - ConcreteType::Scalar(_) => ConcreteType::Scalar(ScalarType::Bool), - ConcreteType::Vector(_, d) => ConcreteType::Vector(ScalarType::Bool, d), - _ => unreachable!("mix doesnt exist for matrices"), - }; - - [ - TypeSignature { inputs: [t, t, t.scalar()], outputs: [t] }, - TypeSignature { inputs: [t, t, t], outputs: [t] }, - TypeSignature { inputs: [t, t, b], outputs: [t] }, - ]}) - .dedup() - } +impl TypeSignature { + fn matches_inputs(&self, connected: &[Option<Type>]) -> bool { + self.inputs + .iter() + .zip(connected.iter()) + .all(|(expected, connected_input)| { + if let Some(input) = connected_input { + input == expected + } else { + true + } + }) + } } +mod library; mod preview; const STRING_COLOR: Color32 = Color32::from_rgb(0x00, 0xb0, 0x00); @@ -299,79 +101,65 @@ const NUMBER_COLOR: Color32 = Color32::from_rgb(0xb0, 0x00, 0x00); const IMAGE_COLOR: Color32 = Color32::from_rgb(0xb0, 0x00, 0xb0); const UNTYPED_COLOR: Color32 = Color32::from_rgb(0xb0, 0xb0, 0xb0); -#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[enum_dispatch(ConcreteNode)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] enum DemoNode { - /// Node with single input. - /// Displays the value of the input. - Sink, - Constant(ConcreteType, String), - Builtin(BinArithmetic), + Constant(library::Constant), + Builtin(library::BuiltinFunction), + Output(library::Output), } - impl DemoNode { - const fn name(&self) -> &str { - match self { - DemoNode::Sink => "Sink", - DemoNode::Constant(_, _) => "Constant", - DemoNode::Builtin(_) => "builtin", - } + fn get_input_types(&self, node: NodeId, snarl: &Snarl<DemoNode>) -> Vec<Option<Type>> { + (0..self.inputs()) + .map( + |input| match &*snarl.in_pin(InPinId { node, input }).remotes { + [] => None, + [out_pin] => Some(DemoViewer::get_out_type(*out_pin, snarl)), + _ => unreachable!("cannot connect to multiple inputs"), + }, + ) + .collect() + } + + pub fn get_node_signature(&self, node: NodeId, snarl: &Snarl<DemoNode>) -> TypeSignature { + let connected = self.get_input_types(node, snarl); + self.signature(&connected) + } + + pub fn all() -> impl Iterator<Item = Self> { + library::Constant::all() + .map(DemoNode::from) + .chain(library::Output::all().map(DemoNode::from)) + .chain(library::BuiltinFunction::all().map(DemoNode::from)) } } struct DemoViewer; impl DemoViewer { - fn get_in_type(pin: InPinId, snarl: &Snarl<DemoNode>) -> Option<ConcreteType> { - match (&snarl[pin.node], pin.input) { - (DemoNode::Constant(_, _), _) => unreachable!("Constant has no inputs"), - - // generic node - (DemoNode::Sink, _) => { - match &*snarl.in_pin(pin).remotes { - // unconnected - show generic - [] => Type::Generic, - - // connected - pass through from connection - [out_pin] => DemoViewer::get_out_type(*out_pin, snarl), - - _ => unreachable!("cannot connect to multiple inputs"), - } - }, - - (node @ DemoNode::Builtin(b), i) => { - b.get_signature(b.get_input_types(pin.node)).inputs[i] - }, - } + fn get_in_type(pin: InPinId, snarl: &Snarl<DemoNode>) -> Option<Type> { + let node = &snarl[pin.node]; + let sig = &node.get_node_signature(pin.node, snarl); + Some(sig.inputs[pin.input]) } - fn get_out_type(pin: OutPinId, snarl: &Snarl<DemoNode>) -> Option<ConcreteType> { - match (&snarl[pin.node], pin.output) { - (DemoNode::Sink, _) => unreachable!("Sink node has no outputs"), - - // node with known, fixed output type - (DemoNode::Constant(t, _), _) => t.clone(), - - // generic node - pass through from input - (DemoNode::AbsNode, _) => DemoViewer::get_in_type( - InPinId { - node: pin.node, - input: 0, - }, - snarl, - ), - } + fn get_out_type(pin: OutPinId, snarl: &Snarl<DemoNode>) -> Type { + let node = &snarl[pin.node]; + let sig = &node.get_node_signature(pin.node, snarl); + sig.outputs[pin.output] } } impl SnarlViewer<DemoNode> for DemoViewer { #[inline] fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<DemoNode>) { - // Validate connection - #[allow(clippy::match_same_arms)] // For match clarity - match (&snarl[from.id.node], &snarl[to.id.node]) { - (DemoNode::Sink, _) => unreachable!("Sink node has no outputs"), - (_, DemoNode::Constant(_, _)) => unreachable!("Constant node has no inputs"), - (DemoNode::Constant(_, _) | DemoNode::AbsNode, DemoNode::Sink | DemoNode::AbsNode) => {} + let to_node = &snarl[to.id.node]; + let mut inputs = to_node.get_input_types(to.id.node, snarl); + inputs[to.id.input] = Some(DemoViewer::get_out_type(from.id, snarl)); + let signatures = to_node.signatures_matching(&inputs); + + if signatures.is_empty() { + return; } for &remote in &to.remotes { @@ -382,156 +170,43 @@ impl SnarlViewer<DemoNode> for DemoViewer { } fn title(&mut self, node: &DemoNode) -> String { - match node { - DemoNode::Sink => "Sink".to_owned(), - DemoNode::AbsNode => "Absolute Value".to_owned(), - DemoNode::Constant(typ, _) => format!("Constant {typ:?}"), - } + format!("{node:?}") } fn inputs(&mut self, node: &DemoNode) -> usize { - match node { - DemoNode::Sink | DemoNode::AbsNode => 1, - DemoNode::Constant(_, _) => 0, - } + node.inputs() } fn outputs(&mut self, node: &DemoNode) -> usize { - match node { - DemoNode::Sink => 0, - DemoNode::AbsNode | DemoNode::Constant(_, _) => 1, - // DemoNode::Number(_) - // | DemoNode::String(_) - // | DemoNode::ShowImage(_) - // | DemoNode::ExprNode(_) => 1, - } + node.outputs() } #[allow(clippy::too_many_lines)] #[allow(refining_impl_trait)] - fn show_input(&mut self, pin: &InPin, ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { - if snarl[pin.id.node] == DemoNode::Sink { - return PinInfo::circle().with_fill(IMAGE_COLOR); - } - + fn show_input(&mut self, pin: &InPin, _ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { match DemoViewer::get_in_type(pin.id, snarl) { - Type::Generic - | Type::Float(None) - | Type::Double(None) - | Type::Int(None) - | Type::UInt(None) - | Type::Bool(None) => PinInfo::circle().with_fill(UNTYPED_COLOR), - Type::Float(Some(ref d)) - | Type::Double(Some(ref d)) - | Type::Int(Some(ref d)) - | Type::UInt(Some(ref d)) - | Type::Bool(Some(ref d)) => PinInfo::circle().with_fill(match d { - Dimension::D1 => NUMBER_COLOR, - Dimension::D2 => IMAGE_COLOR, - Dimension::D3 => STRING_COLOR, - Dimension::D4 => UNTYPED_COLOR, + None => PinInfo::circle().with_fill(UNTYPED_COLOR), + Some(ref t) => PinInfo::circle().with_fill(match t { + Type::Scalar(_) => NUMBER_COLOR, + Type::Vector(_, Dimension::D2) => IMAGE_COLOR, + Type::Vector(_, Dimension::D3) => STRING_COLOR, + Type::Vector(_, Dimension::D4) => UNTYPED_COLOR, + Type::Matrix(_, _, _) => UNTYPED_COLOR, }), } - - /* - match snarl[pin.id.node] { - DemoNode::Sink => { - assert_eq!(pin.id.input, 0, "Sink node has only one input"); - - match &*pin.remotes { - [] => { - ui.label("None"); - PinInfo::circle().with_fill(UNTYPED_COLOR) - } - [remote] => match snarl[remote.node] { - DemoNode::Sink => unreachable!("Sink node has no outputs"), - DemoNode::Number(value) => { - assert_eq!(remote.output, 0, "Number node has only one output"); - ui.label(format_float(value)); - PinInfo::circle().with_fill(NUMBER_COLOR) - } - DemoNode::String(ref value) => { - assert_eq!(remote.output, 0, "String node has only one output"); - ui.label(format!("{value:?}")); - - PinInfo::circle().with_fill(STRING_COLOR).with_wire_style( - WireStyle::AxisAligned { - corner_radius: 10.0, - }, - ) - } - DemoNode::ExprNode(ref expr) => { - assert_eq!(remote.output, 0, "Expr node has only one output"); - ui.label(format_float(expr.eval())); - PinInfo::circle().with_fill(NUMBER_COLOR) - } - DemoNode::ShowImage(ref uri) => { - assert_eq!(remote.output, 0, "ShowImage node has only one output"); - - let image = egui::Image::new(uri).show_loading_spinner(true); - ui.add(image); - - PinInfo::circle().with_fill(IMAGE_COLOR) - } - }, - _ => unreachable!("Sink input has only one wire"), - } - } - DemoNode::Constant(_, _) => { - unreachable!("Number node has no inputs") - } - DemoNode::ExprNode(ref expr_node) => { - if pin.id.input <= expr_node.bindings.len() { - match &*pin.remotes { - [] => { - let node = &mut snarl[pin.id.node]; - ui.label(node.label_in(pin.id.input)); - ui.add(egui::DragValue::new(node.number_in(pin.id.input))); - PinInfo::circle().with_fill(NUMBER_COLOR) - } - [remote] => { - let new_value = snarl[remote.node].number_out(); - let node = &mut snarl[pin.id.node]; - ui.label(node.label_in(pin.id.input)); - ui.label(format_float(new_value)); - *node.number_in(pin.id.input) = new_value; - PinInfo::circle().with_fill(NUMBER_COLOR) - } - _ => unreachable!("Expr pins has only one wire"), - } - } else { - ui.label("Removed"); - PinInfo::circle().with_fill(Color32::BLACK) - } - } - } - */ } #[allow(refining_impl_trait)] fn show_output(&mut self, pin: &OutPin, ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { let typ = DemoViewer::get_out_type(pin.id, snarl); - match typ { - Type::Generic - | Type::Float(None) - | Type::Double(None) - | Type::Int(None) - | Type::UInt(None) - | Type::Bool(None) => PinInfo::circle().with_fill(UNTYPED_COLOR), - Type::Float(Some(ref d)) - | Type::Double(Some(ref d)) - | Type::Int(Some(ref d)) - | Type::UInt(Some(ref d)) - | Type::Bool(Some(ref d)) => { - ui.label(format!("{typ:?}")); - PinInfo::circle().with_fill(match d { - Dimension::D1 => NUMBER_COLOR, - Dimension::D2 => IMAGE_COLOR, - Dimension::D3 => STRING_COLOR, - Dimension::D4 => UNTYPED_COLOR, - }) - } - } + ui.label(format!("{typ:?}")); + PinInfo::circle().with_fill(match typ { + Type::Scalar(_) => NUMBER_COLOR, + Type::Vector(_, Dimension::D2) => IMAGE_COLOR, + Type::Vector(_, Dimension::D3) => STRING_COLOR, + Type::Vector(_, Dimension::D4) => UNTYPED_COLOR, + Type::Matrix(_, _, _) => UNTYPED_COLOR, + }) } fn has_graph_menu(&mut self, _pos: egui::Pos2, _snarl: &mut Snarl<DemoNode>) -> bool { @@ -540,34 +215,12 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn show_graph_menu(&mut self, pos: egui::Pos2, ui: &mut Ui, snarl: &mut Snarl<DemoNode>) { ui.label("Add node"); - if ui.button("Constant::float").clicked() { - snarl.insert_node( - pos, - DemoNode::Constant(Type::Float(Some(Dimension::D1)), "0.0".to_owned()), - ); - ui.close(); - } - if ui.button("Constant::vec2").clicked() { - snarl.insert_node( - pos, - DemoNode::Constant(Type::Float(Some(Dimension::D2)), "0, 0".to_owned()), - ); - ui.close(); - } - if ui.button("Constant::bvec3").clicked() { - snarl.insert_node( - pos, - DemoNode::Constant(Type::Bool(Some(Dimension::D3)), "0.0".to_owned()), - ); - ui.close(); - } - if ui.button("Abs").clicked() { - snarl.insert_node(pos, DemoNode::AbsNode); - ui.close(); - } - if ui.button("Sink").clicked() { - snarl.insert_node(pos, DemoNode::Sink); - ui.close(); + + for node in DemoNode::all() { + if ui.button(format!("{node:?}")).clicked() { + snarl.insert_node(pos, node); + ui.close(); + } } } @@ -577,10 +230,10 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn show_dropped_wire_menu( &mut self, - pos: egui::Pos2, - ui: &mut Ui, - src_pins: AnyPins, - snarl: &mut Snarl<DemoNode>, + _pos: egui::Pos2, + _ui: &mut Ui, + _src_pins: AnyPins, + _snarl: &mut Snarl<DemoNode>, ) { /* // In this demo, we create a context-aware node graph menu, and connect a wire @@ -723,11 +376,11 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn show_on_hover_popup( &mut self, - node: NodeId, + _node: NodeId, _inputs: &[InPin], _outputs: &[OutPin], - ui: &mut Ui, - snarl: &mut Snarl<DemoNode>, + _ui: &mut Ui, + _snarl: &mut Snarl<DemoNode>, ) { /* match snarl[node] { @@ -753,10 +406,10 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn header_frame( &mut self, frame: egui::Frame, - node: NodeId, + _node: NodeId, _inputs: &[InPin], _outputs: &[OutPin], - snarl: &Snarl<DemoNode>, + _snarl: &Snarl<DemoNode>, ) -> egui::Frame { frame /* @@ -871,7 +524,7 @@ impl App for DemoApp { for (id, node) in selected { ui.horizontal(|ui| { ui.label(format!("{id:?}")); - ui.label(node.name()); + ui.label(format!("{node:?}")); ui.add_space(ui.spacing().item_spacing.x); if ui.button("Remove").clicked() { remove = Some(id); @@ -954,8 +607,3 @@ fn main() { .expect("failed to start eframe"); }); } - -fn format_float(v: f64) -> String { - let v = (v * 1000.0).round() / 1000.0; - format!("{v}") -} |
