diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-12-23 17:54:15 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-12-23 17:54:35 +0000 |
| commit | d278e7eee3d76158ebb7b90f7c97cda1e0ae743b (patch) | |
| tree | faef9394a9b32c5cf3a1048f54b293ada9321253 | |
| parent | wgpu, logging integrations (diff) | |
| download | nodetoy-d278e7eee3d76158ebb7b90f7c97cda1e0ae743b.tar.gz nodetoy-d278e7eee3d76158ebb7b90f7c97cda1e0ae743b.zip | |
attempt generic
| -rw-r--r-- | Cargo.lock | 17 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/main.rs | 968 |
3 files changed, 422 insertions, 565 deletions
@@ -978,6 +978,12 @@ dependencies = [ ] [[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] name = "emath" version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1614,6 +1620,15 @@ dependencies = [ ] [[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] name = "itoa" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1919,10 +1934,10 @@ dependencies = [ "egui-probe", "egui-snarl", "egui_extras", + "itertools", "log", "serde", "serde_json", - "syn", "wasm-bindgen-futures", "web-sys", "wgpu", @@ -10,10 +10,10 @@ egui = "0.33" egui-probe = { version = "0.10", features = ["derive"] } egui-snarl = { version = "0.9", features = ["serde", "egui-probe"] } egui_extras = { version = "0.33" } +itertools = "0.14.0" log = { version = "0.4.29", features = ["serde", "std"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -syn = { version = "2.0", features = ["extra-traits"] } wgpu = { version = "27.0", features = ["glsl", "webgl"] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/main.rs b/src/main.rs index 75a5df4..294df7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ #![allow(clippy::use_self)] use log::*; -use std::collections::HashMap; use eframe::{App, CreationContext}; use egui::{Color32, Id, Ui}; @@ -9,9 +8,289 @@ use egui_snarl::{ InPin, InPinId, NodeId, OutPin, OutPinId, Snarl, ui::{ AnyPins, NodeLayout, PinInfo, PinPlacement, SnarlStyle, SnarlViewer, SnarlWidget, - WireStyle, get_selected_nodes, + get_selected_nodes, }, }; +use itertools::Itertools; + +#[derive(Clone, Debug, PartialEq)] +enum FloatPrecision { + Float, + Double, +} + +#[derive(Clone, Debug, PartialEq)] +enum ScalarType { + Float(FloatPrecision), + Int, + UInt, + Bool, +} + +#[derive(Clone, Debug, PartialEq)] +enum Dimension { + D2, + D3, + D4, +} + +#[derive(Clone, Debug, PartialEq)] +enum ConcreteType { + 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), + } + } +} + +/// 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 + } +} + +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"), + } + } + 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 Thru1FDI { + Abs, + Sign, +} + +enum Thru1FD { + Floor, + Trunc, + Round, + RoundEven, + Ceil, + Fract, +} + +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() + } +} mod preview; @@ -20,127 +299,79 @@ 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, serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] enum DemoNode { /// Node with single input. /// Displays the value of the input. Sink, - - /// Value node with a single output. - /// The value is editable in UI. - Number(f64), - - /// Value node with a single output. - String(String), - - /// Converts URI to Image - ShowImage(String), - - /// Expression node with a single output. - /// It has number of inputs equal to number of variables in the expression. - ExprNode(ExprNode), + Constant(ConcreteType, String), + Builtin(BinArithmetic), } impl DemoNode { const fn name(&self) -> &str { match self { DemoNode::Sink => "Sink", - DemoNode::Number(_) => "Number", - DemoNode::String(_) => "String", - DemoNode::ShowImage(_) => "ShowImage", - DemoNode::ExprNode(_) => "ExprNode", + DemoNode::Constant(_, _) => "Constant", + DemoNode::Builtin(_) => "builtin", } } +} - fn number_out(&self) -> f64 { - match self { - DemoNode::Number(value) => *value, - DemoNode::ExprNode(expr_node) => expr_node.eval(), - _ => unreachable!(), - } - } +struct DemoViewer; - fn number_in(&mut self, idx: usize) -> &mut f64 { - match self { - DemoNode::ExprNode(expr_node) => &mut expr_node.values[idx - 1], - _ => unreachable!(), - } - } +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"), - fn label_in(&mut self, idx: usize) -> &str { - match self { - DemoNode::ShowImage(_) if idx == 0 => "URL", - DemoNode::ExprNode(expr_node) => &expr_node.bindings[idx - 1], - _ => unreachable!(), - } - } + // generic node + (DemoNode::Sink, _) => { + match &*snarl.in_pin(pin).remotes { + // unconnected - show generic + [] => Type::Generic, - fn string_out(&self) -> &str { - match self { - DemoNode::String(value) => value, - _ => unreachable!(), - } - } + // connected - pass through from connection + [out_pin] => DemoViewer::get_out_type(*out_pin, snarl), - fn string_in(&mut self) -> &mut String { - match self { - DemoNode::ShowImage(uri) => uri, - DemoNode::ExprNode(expr_node) => &mut expr_node.text, - _ => unreachable!(), + _ => unreachable!("cannot connect to multiple inputs"), + } + }, + + (node @ DemoNode::Builtin(b), i) => { + b.get_signature(b.get_input_types(pin.node)).inputs[i] + }, } } - fn expr_node(&mut self) -> &mut ExprNode { - match self { - DemoNode::ExprNode(expr_node) => expr_node, - _ => unreachable!(), + 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, + ), } } } -struct DemoViewer; - 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::Sink) => {} - (_, DemoNode::Number(_)) => { - unreachable!("Number node has no inputs") - } - (_, DemoNode::String(_)) => { - unreachable!("String node has no inputs") - } - (DemoNode::Number(_), DemoNode::ShowImage(_)) => { - return; - } - (DemoNode::ShowImage(_), DemoNode::ShowImage(_)) => { - return; - } - (DemoNode::String(_), DemoNode::ShowImage(_)) => {} - (DemoNode::ExprNode(_), DemoNode::ExprNode(_)) if to.id.input == 0 => { - return; - } - (DemoNode::ExprNode(_), DemoNode::ExprNode(_)) => {} - (DemoNode::Number(_), DemoNode::ExprNode(_)) if to.id.input == 0 => { - return; - } - (DemoNode::Number(_), DemoNode::ExprNode(_)) => {} - (DemoNode::String(_), DemoNode::ExprNode(_)) if to.id.input == 0 => {} - (DemoNode::String(_), DemoNode::ExprNode(_)) => { - return; - } - (DemoNode::ShowImage(_), DemoNode::ExprNode(_)) => { - return; - } - (DemoNode::ExprNode(_), DemoNode::ShowImage(_)) => { - return; - } + (DemoNode::Sink, _) => unreachable!("Sink node has no outputs"), + (_, DemoNode::Constant(_, _)) => unreachable!("Constant node has no inputs"), + (DemoNode::Constant(_, _) | DemoNode::AbsNode, DemoNode::Sink | DemoNode::AbsNode) => {} } for &remote in &to.remotes { @@ -153,34 +384,56 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn title(&mut self, node: &DemoNode) -> String { match node { DemoNode::Sink => "Sink".to_owned(), - DemoNode::Number(_) => "Number".to_owned(), - DemoNode::String(_) => "String".to_owned(), - DemoNode::ShowImage(_) => "Show image".to_owned(), - DemoNode::ExprNode(_) => "Expr".to_owned(), + DemoNode::AbsNode => "Absolute Value".to_owned(), + DemoNode::Constant(typ, _) => format!("Constant {typ:?}"), } } fn inputs(&mut self, node: &DemoNode) -> usize { match node { - DemoNode::Sink | DemoNode::ShowImage(_) => 1, - DemoNode::Number(_) | DemoNode::String(_) => 0, - DemoNode::ExprNode(expr_node) => 1 + expr_node.bindings.len(), + DemoNode::Sink | DemoNode::AbsNode => 1, + DemoNode::Constant(_, _) => 0, } } fn outputs(&mut self, node: &DemoNode) -> usize { match node { DemoNode::Sink => 0, - DemoNode::Number(_) - | DemoNode::String(_) - | DemoNode::ShowImage(_) - | DemoNode::ExprNode(_) => 1, + DemoNode::AbsNode | DemoNode::Constant(_, _) => 1, + // DemoNode::Number(_) + // | DemoNode::String(_) + // | DemoNode::ShowImage(_) + // | DemoNode::ExprNode(_) => 1, } } #[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); + } + + 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, + }), + } + + /* match snarl[pin.id.node] { DemoNode::Sink => { assert_eq!(pin.id.input, 0, "Sink node has only one input"); @@ -224,142 +477,9 @@ impl SnarlViewer<DemoNode> for DemoViewer { _ => unreachable!("Sink input has only one wire"), } } - DemoNode::Number(_) => { + DemoNode::Constant(_, _) => { unreachable!("Number node has no inputs") } - DemoNode::String(_) => { - unreachable!("String node has no inputs") - } - DemoNode::ShowImage(_) => match &*pin.remotes { - [] => { - let input = snarl[pin.id.node].string_in(); - egui::TextEdit::singleline(input) - .clip_text(false) - .desired_width(0.0) - .margin(ui.spacing().item_spacing) - .show(ui); - PinInfo::circle().with_fill(STRING_COLOR).with_wire_style( - WireStyle::AxisAligned { - corner_radius: 10.0, - }, - ) - } - [remote] => { - let new_value = snarl[remote.node].string_out().to_owned(); - - egui::TextEdit::singleline(&mut &*new_value) - .clip_text(false) - .desired_width(0.0) - .margin(ui.spacing().item_spacing) - .show(ui); - - let input = snarl[pin.id.node].string_in(); - *input = new_value; - - PinInfo::circle().with_fill(STRING_COLOR).with_wire_style( - WireStyle::AxisAligned { - corner_radius: 10.0, - }, - ) - } - _ => unreachable!("Sink input has only one wire"), - }, - DemoNode::ExprNode(_) if pin.id.input == 0 => { - let changed = match &*pin.remotes { - [] => { - let input = snarl[pin.id.node].string_in(); - let r = egui::TextEdit::singleline(input) - .clip_text(false) - .desired_width(0.0) - .margin(ui.spacing().item_spacing) - .show(ui) - .response; - - r.changed() - } - [remote] => { - let new_string = snarl[remote.node].string_out().to_owned(); - - egui::TextEdit::singleline(&mut &*new_string) - .clip_text(false) - .desired_width(0.0) - .margin(ui.spacing().item_spacing) - .show(ui); - - let input = snarl[pin.id.node].string_in(); - if new_string == *input { - false - } else { - *input = new_string; - true - } - } - _ => unreachable!("Expr pins has only one wire"), - }; - - if changed { - let expr_node = snarl[pin.id.node].expr_node(); - - if let Ok(expr) = syn::parse_str(&expr_node.text) { - expr_node.expr = expr; - - let values = Iterator::zip( - expr_node.bindings.iter().map(String::clone), - expr_node.values.iter().copied(), - ) - .collect::<HashMap<String, f64>>(); - - let mut new_bindings = Vec::new(); - expr_node.expr.extend_bindings(&mut new_bindings); - - let old_bindings = - std::mem::replace(&mut expr_node.bindings, new_bindings.clone()); - - let new_values = new_bindings - .iter() - .map(|name| values.get(&**name).copied().unwrap_or(0.0)) - .collect::<Vec<_>>(); - - expr_node.values = new_values; - - let old_inputs = (0..old_bindings.len()) - .map(|idx| { - snarl.in_pin(InPinId { - node: pin.id.node, - input: idx + 1, - }) - }) - .collect::<Vec<_>>(); - - for (idx, name) in old_bindings.iter().enumerate() { - let new_idx = - new_bindings.iter().position(|new_name| *new_name == *name); - - match new_idx { - None => { - snarl.drop_inputs(old_inputs[idx].id); - } - Some(new_idx) if new_idx != idx => { - let new_in_pin = InPinId { - node: pin.id.node, - input: new_idx, - }; - for &remote in &old_inputs[idx].remotes { - snarl.disconnect(remote, old_inputs[idx].id); - snarl.connect(remote, new_in_pin); - } - } - _ => {} - } - } - } - } - PinInfo::circle() - .with_fill(STRING_COLOR) - .with_wire_style(WireStyle::AxisAligned { - corner_radius: 10.0, - }) - } DemoNode::ExprNode(ref expr_node) => { if pin.id.input <= expr_node.bindings.len() { match &*pin.remotes { @@ -385,41 +505,31 @@ impl SnarlViewer<DemoNode> for DemoViewer { } } } + */ } #[allow(refining_impl_trait)] fn show_output(&mut self, pin: &OutPin, ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { - match snarl[pin.id.node] { - DemoNode::Sink => { - unreachable!("Sink node has no outputs") - } - DemoNode::Number(ref mut value) => { - assert_eq!(pin.id.output, 0, "Number node has only one output"); - ui.add(egui::DragValue::new(value)); - PinInfo::circle().with_fill(NUMBER_COLOR) - } - DemoNode::String(ref mut value) => { - assert_eq!(pin.id.output, 0, "String node has only one output"); - let edit = egui::TextEdit::singleline(value) - .clip_text(false) - .desired_width(0.0) - .margin(ui.spacing().item_spacing); - ui.add(edit); - PinInfo::circle() - .with_fill(STRING_COLOR) - .with_wire_style(WireStyle::AxisAligned { - corner_radius: 10.0, - }) - } - DemoNode::ExprNode(ref expr_node) => { - let value = expr_node.eval(); - assert_eq!(pin.id.output, 0, "Expr node has only one output"); - ui.label(format_float(value)); - PinInfo::circle().with_fill(NUMBER_COLOR) - } - DemoNode::ShowImage(_) => { - ui.allocate_at_least(egui::Vec2::ZERO, egui::Sense::hover()); - PinInfo::circle().with_fill(IMAGE_COLOR) + 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, + }) } } } @@ -430,20 +540,29 @@ 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("Number").clicked() { - snarl.insert_node(pos, DemoNode::Number(0.0)); + 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("Expr").clicked() { - snarl.insert_node(pos, DemoNode::ExprNode(ExprNode::new())); + 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("String").clicked() { - snarl.insert_node(pos, DemoNode::String(String::new())); + 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("Show image").clicked() { - snarl.insert_node(pos, DemoNode::ShowImage(String::new())); + if ui.button("Abs").clicked() { + snarl.insert_node(pos, DemoNode::AbsNode); ui.close(); } if ui.button("Sink").clicked() { @@ -453,7 +572,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { } fn has_dropped_wire_menu(&mut self, _src_pins: AnyPins, _snarl: &mut Snarl<DemoNode>) -> bool { - true + false // true } fn show_dropped_wire_menu( @@ -463,6 +582,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { src_pins: AnyPins, snarl: &mut Snarl<DemoNode>, ) { + /* // In this demo, we create a context-aware node graph menu, and connect a wire // dropped on the fly based on user input to a new node created. // @@ -575,6 +695,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { } } } + */ } fn has_node_menu(&mut self, _node: &DemoNode) -> bool { @@ -597,7 +718,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { } fn has_on_hover_popup(&mut self, _: &DemoNode) -> bool { - true + false // true } fn show_on_hover_popup( @@ -608,6 +729,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { ui: &mut Ui, snarl: &mut Snarl<DemoNode>, ) { + /* match snarl[node] { DemoNode::Sink => { ui.label("Displays anything connected to it"); @@ -625,6 +747,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { ui.label("Evaluates algebraic expression with input for each unique variable name"); } } + */ } fn header_frame( @@ -635,6 +758,8 @@ impl SnarlViewer<DemoNode> for DemoViewer { _outputs: &[OutPin], snarl: &Snarl<DemoNode>, ) -> egui::Frame { + frame + /* match snarl[node] { DemoNode::Sink => frame.fill(egui::Color32::from_rgb(70, 70, 80)), DemoNode::Number(_) => frame.fill(egui::Color32::from_rgb(70, 40, 40)), @@ -642,290 +767,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { DemoNode::ShowImage(_) => frame.fill(egui::Color32::from_rgb(40, 40, 70)), DemoNode::ExprNode(_) => frame.fill(egui::Color32::from_rgb(70, 66, 40)), } - } -} - -#[derive(Clone, serde::Serialize, serde::Deserialize)] -struct ExprNode { - text: String, - bindings: Vec<String>, - values: Vec<f64>, - expr: Expr, -} - -impl ExprNode { - fn new() -> Self { - ExprNode { - text: "0".to_string(), - bindings: Vec::new(), - values: Vec::new(), - expr: Expr::Val(0.0), - } - } - - fn eval(&self) -> f64 { - self.expr.eval(&self.bindings, &self.values) - } -} - -#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)] -enum UnOp { - Pos, - Neg, -} - -#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)] -enum BinOp { - Add, - Sub, - Mul, - Div, -} - -#[derive(Clone, serde::Serialize, serde::Deserialize)] -enum Expr { - Var(String), - Val(f64), - UnOp { - op: UnOp, - expr: Box<Expr>, - }, - BinOp { - lhs: Box<Expr>, - op: BinOp, - rhs: Box<Expr>, - }, -} - -impl Expr { - fn eval(&self, bindings: &[String], args: &[f64]) -> f64 { - let binding_index = - |name: &str| bindings.iter().position(|binding| binding == name).unwrap(); - - match self { - Expr::Var(name) => args[binding_index(name)], - Expr::Val(value) => *value, - Expr::UnOp { op, expr } => match op { - UnOp::Pos => expr.eval(bindings, args), - UnOp::Neg => -expr.eval(bindings, args), - }, - Expr::BinOp { lhs, op, rhs } => match op { - BinOp::Add => lhs.eval(bindings, args) + rhs.eval(bindings, args), - BinOp::Sub => lhs.eval(bindings, args) - rhs.eval(bindings, args), - BinOp::Mul => lhs.eval(bindings, args) * rhs.eval(bindings, args), - BinOp::Div => lhs.eval(bindings, args) / rhs.eval(bindings, args), - }, - } - } - - fn extend_bindings(&self, bindings: &mut Vec<String>) { - match self { - Expr::Var(name) => { - if !bindings.contains(name) { - bindings.push(name.clone()); - } - } - Expr::Val(_) => {} - Expr::UnOp { expr, .. } => { - expr.extend_bindings(bindings); - } - Expr::BinOp { lhs, rhs, .. } => { - lhs.extend_bindings(bindings); - rhs.extend_bindings(bindings); - } - } - } -} - -impl syn::parse::Parse for UnOp { - fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![+]) { - input.parse::<syn::Token![+]>()?; - Ok(UnOp::Pos) - } else if lookahead.peek(syn::Token![-]) { - input.parse::<syn::Token![-]>()?; - Ok(UnOp::Neg) - } else { - Err(lookahead.error()) - } - } -} - -impl syn::parse::Parse for BinOp { - fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![+]) { - input.parse::<syn::Token![+]>()?; - Ok(BinOp::Add) - } else if lookahead.peek(syn::Token![-]) { - input.parse::<syn::Token![-]>()?; - Ok(BinOp::Sub) - } else if lookahead.peek(syn::Token![*]) { - input.parse::<syn::Token![*]>()?; - Ok(BinOp::Mul) - } else if lookahead.peek(syn::Token![/]) { - input.parse::<syn::Token![/]>()?; - Ok(BinOp::Div) - } else { - Err(lookahead.error()) - } - } -} - -impl syn::parse::Parse for Expr { - fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - let lhs; - if lookahead.peek(syn::token::Paren) { - let content; - syn::parenthesized!(content in input); - let expr = content.parse::<Expr>()?; - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - // } else if lookahead.peek(syn::LitFloat) { - // let lit = input.parse::<syn::LitFloat>()?; - // let value = lit.base10_parse::<f64>()?; - // let expr = Expr::Val(value); - // if input.is_empty() { - // return Ok(expr); - // } - // lhs = expr; - } else if lookahead.peek(syn::LitInt) { - let lit = input.parse::<syn::LitInt>()?; - let value = lit.base10_parse::<f64>()?; - let expr = Expr::Val(value); - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - } else if lookahead.peek(syn::Ident) { - let ident = input.parse::<syn::Ident>()?; - let expr = Expr::Var(ident.to_string()); - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - } else { - let unop = input.parse::<UnOp>()?; - - return Self::parse_with_unop(unop, input); - } - - let binop = input.parse::<BinOp>()?; - - Self::parse_binop(Box::new(lhs), binop, input) - } -} - -impl Expr { - fn parse_with_unop(op: UnOp, input: syn::parse::ParseStream) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - let lhs; - if lookahead.peek(syn::token::Paren) { - let content; - syn::parenthesized!(content in input); - let expr = Expr::UnOp { - op, - expr: Box::new(content.parse::<Expr>()?), - }; - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - } else if lookahead.peek(syn::LitFloat) { - let lit = input.parse::<syn::LitFloat>()?; - let value = lit.base10_parse::<f64>()?; - let expr = Expr::UnOp { - op, - expr: Box::new(Expr::Val(value)), - }; - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - } else if lookahead.peek(syn::LitInt) { - let lit = input.parse::<syn::LitInt>()?; - let value = lit.base10_parse::<f64>()?; - let expr = Expr::UnOp { - op, - expr: Box::new(Expr::Val(value)), - }; - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - } else if lookahead.peek(syn::Ident) { - let ident = input.parse::<syn::Ident>()?; - let expr = Expr::UnOp { - op, - expr: Box::new(Expr::Var(ident.to_string())), - }; - if input.is_empty() { - return Ok(expr); - } - lhs = expr; - } else { - return Err(lookahead.error()); - } - - let op = input.parse::<BinOp>()?; - - Self::parse_binop(Box::new(lhs), op, input) - } - - fn parse_binop(lhs: Box<Expr>, op: BinOp, input: syn::parse::ParseStream) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - let rhs; - if lookahead.peek(syn::token::Paren) { - let content; - syn::parenthesized!(content in input); - rhs = Box::new(content.parse::<Expr>()?); - if input.is_empty() { - return Ok(Expr::BinOp { lhs, op, rhs }); - } - } else if lookahead.peek(syn::LitFloat) { - let lit = input.parse::<syn::LitFloat>()?; - let value = lit.base10_parse::<f64>()?; - rhs = Box::new(Expr::Val(value)); - if input.is_empty() { - return Ok(Expr::BinOp { lhs, op, rhs }); - } - } else if lookahead.peek(syn::LitInt) { - let lit = input.parse::<syn::LitInt>()?; - let value = lit.base10_parse::<f64>()?; - rhs = Box::new(Expr::Val(value)); - if input.is_empty() { - return Ok(Expr::BinOp { lhs, op, rhs }); - } - } else if lookahead.peek(syn::Ident) { - let ident = input.parse::<syn::Ident>()?; - rhs = Box::new(Expr::Var(ident.to_string())); - if input.is_empty() { - return Ok(Expr::BinOp { lhs, op, rhs }); - } - } else { - return Err(lookahead.error()); - } - - let next_op = input.parse::<BinOp>()?; - - if let (BinOp::Add | BinOp::Sub, BinOp::Mul | BinOp::Div) = (op, next_op) { - let rhs = Self::parse_binop(rhs, next_op, input)?; - Ok(Self::BinOp { - lhs, - op, - rhs: Box::new(rhs), - }) - } else { - let lhs = Self::BinOp { lhs, op, rhs }; - Self::parse_binop(Box::new(lhs), next_op, input) - } + */ } } |
