aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-12-23 17:54:15 +0000
committers-ol <s+removethis@s-ol.nu>2025-12-23 17:54:35 +0000
commitd278e7eee3d76158ebb7b90f7c97cda1e0ae743b (patch)
treefaef9394a9b32c5cf3a1048f54b293ada9321253
parentwgpu, logging integrations (diff)
downloadnodetoy-d278e7eee3d76158ebb7b90f7c97cda1e0ae743b.tar.gz
nodetoy-d278e7eee3d76158ebb7b90f7c97cda1e0ae743b.zip
attempt generic
-rw-r--r--Cargo.lock17
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs968
3 files changed, 422 insertions, 565 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 725608a..1da4c45 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/Cargo.toml b/Cargo.toml
index 81b029b..c4cc6cf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)
- }
+ */
}
}