aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-12-24 14:35:36 +0000
committers-ol <s+removethis@s-ol.nu>2025-12-24 14:35:36 +0000
commit56f6384148c3475b4d99a5fe3f8d1913d8f66aac (patch)
tree2a6891b58b0bb372ae79920677a790a7a56891e5 /src
parentattempt generic (diff)
downloadnodetoy-56f6384148c3475b4d99a5fe3f8d1913d8f66aac.tar.gz
nodetoy-56f6384148c3475b4d99a5fe3f8d1913d8f66aac.zip
typing working
Diffstat (limited to 'src')
-rw-r--r--src/library.rs380
-rw-r--r--src/main.rs616
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}")
-}