diff options
| author | s-ol <s+removethis@s-ol.nu> | 2026-01-17 12:34:26 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2026-01-17 12:34:26 +0000 |
| commit | 87fecd4c87a3b4f33c795beead1b8f9f19fd7e47 (patch) | |
| tree | 2ce0f0f107ced3547a8e1ebecdd2bab3e82b0518 | |
| parent | Vector combine (diff) | |
| download | nodetoy-87fecd4c87a3b4f33c795beead1b8f9f19fd7e47.tar.gz nodetoy-87fecd4c87a3b4f33c795beead1b8f9f19fd7e47.zip | |
split main into smaller modules
| -rw-r--r-- | src/library.rs | 7 | ||||
| -rw-r--r-- | src/main.rs | 392 | ||||
| -rw-r--r-- | src/node.rs | 75 | ||||
| -rw-r--r-- | src/snarl_ext.rs | 104 | ||||
| -rw-r--r-- | src/types.rs | 145 |
5 files changed, 371 insertions, 352 deletions
diff --git a/src/library.rs b/src/library.rs index 4c3a8c1..9764ad5 100644 --- a/src/library.rs +++ b/src/library.rs @@ -3,9 +3,10 @@ use itertools::{Itertools, izip}; use serde::{Deserialize, Serialize}; use std::fmt; -use crate::{ - ConcreteNode, Dimension, Dimension::*, FloatPrecision::*, ScalarType, ScalarType::*, Type, - Type::*, TypeSignature, +use crate::node::ConcreteNode; +use crate::types::{ + Dimension, Dimension::*, FloatPrecision::*, ScalarType, ScalarType::*, Type, Type::*, + TypeSignature, }; const GEN_F_TYPES: [Type; 4] = [ diff --git a/src/main.rs b/src/main.rs index e94bcdb..0ca84de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,75 +1,21 @@ #![allow(clippy::use_self)] -use eframe::{App, CreationContext}; use egui::{Color32, Id, Ui}; use egui_snarl::{ - InPin, InPinId, NodeId, OutPin, OutPinId, Snarl, + InPin, NodeId, OutPin, Snarl, ui::{NodeLayout, PinInfo, PinPlacement, SnarlStyle, SnarlViewer, SnarlWidget}, }; -use enum_dispatch::enum_dispatch; use library::NodeIndex; use log::*; -use std::fmt; -#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -enum FloatPrecision { - Single, - Double, -} - -#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -enum ScalarType { - Float(FloatPrecision), - Int, - UInt, - Bool, -} - -#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -#[repr(u8)] -enum Dimension { - D2 = 2, - D3 = 3, - D4 = 4, -} -impl fmt::Display for Dimension { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", *self as u8) - } -} -impl Dimension { - const fn from(v: usize) -> Dimension { - match v { - 2 => Self::D2, - 3 => Self::D3, - 4 => Self::D4, - _ => panic!("invalid dimension value"), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -enum Type { - Scalar(ScalarType), - Vector(ScalarType, Dimension), - Matrix(FloatPrecision, Dimension, Dimension), -} - -impl From<ScalarType> for Type { - fn from(val: ScalarType) -> Self { - Self::Scalar(val) - } -} - -impl Type { - fn scalar(&self) -> ScalarType { - match self { - Type::Scalar(s) => *s, - Type::Vector(s, _) => *s, - Type::Matrix(p, _, _) => ScalarType::Float(*p), - } - } -} +mod library; +mod node; +mod preview; +mod snarl_ext; +mod types; +use node::{AnyNode, ConcreteNode}; +use snarl_ext::{Compilable, WithSignatures}; +use types::{Dimension, FloatPrecision, ScalarType, Type}; impl Into<PinInfo> for Type { fn into(self) -> PinInfo { @@ -90,267 +36,17 @@ impl Into<PinInfo> for Type { } } -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Type::Scalar(s) => write!( - f, - "{}", - match s { - ScalarType::Float(FloatPrecision::Single) => "float", - ScalarType::Float(FloatPrecision::Double) => "double", - ScalarType::Int => "int", - ScalarType::UInt => "uint", - ScalarType::Bool => "bool", - } - ), - Type::Vector(s, d) => write!( - f, - "{}vec{d}", - match s { - ScalarType::Float(FloatPrecision::Single) => "", - ScalarType::Float(FloatPrecision::Double) => "d", - ScalarType::Int => "i", - ScalarType::UInt => "u", - ScalarType::Bool => "b", - } - ), - Type::Matrix(p, r, c) => { - let pr = match p { - FloatPrecision::Single => "", - FloatPrecision::Double => "d", - }; - if r == c { - write!(f, "{pr}mat{r}") - } else { - write!(f, "{pr}mat{r}{c}") - } - } - } - } -} - -/// a single concrete type signature for a function -#[derive(PartialEq, Debug)] -struct TypeSignature { - inputs: Box<[Type]>, - outputs: Box<[Type]>, -} - -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), - } - } -} - -#[enum_dispatch] -trait ConcreteNode { - fn max_inputs(&self) -> usize; - - // set of possible input type combinations given current connections - fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature>; - - fn signature(&self, connected: &[Option<Type>]) -> TypeSignature; - - fn compile( - &self, - signature: TypeSignature, - inputs: Vec<String>, - outputs: Vec<String>, - f: &mut dyn fmt::Write, - ) -> fmt::Result; - - fn show_body(&mut self, ui: &mut egui::Ui) -> egui::Response { - ui.response() - } -} - -impl TypeSignature { - fn matches_inputs(&self, connected: &[Option<Type>]) -> bool { - let have_inputs = connected - .iter() - .enumerate() - .filter(|(_, t)| matches!(t, Some(_))) - .map(|(i, _)| i + 1) - .max() - .unwrap_or(0); - - if have_inputs > self.inputs.len() { - false - } else { - connected - .iter() - .zip(self.inputs.iter()) - .all(|(connected_input, expected)| { - if let Some(input) = connected_input { - *input == *expected - } else { - true - } - }) - } - } -} - -mod library; -mod preview; - -#[enum_dispatch(ConcreteNode)] -#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] -enum DemoNode { - Constant(library::AnyConstant), - Conversion(library::Conversion), - Input(library::Input), - Builtin(library::BuiltinFunction), - Output(library::Output), -} -impl DemoNode { - fn get_input_types(&self, node: NodeId, snarl: &Snarl<DemoNode>) -> Vec<Option<Type>> { - (0..snarl[node].max_inputs()) - .map( - move |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() - } - - fn compile_output(pin: OutPinId) -> String { - let node_id = pin.node.0; - let out_id = pin.output; - format!("n{node_id}_o{out_id}") - } - - pub fn compile_node( - &self, - node: NodeId, - snarl: &Snarl<DemoNode>, - f: &mut dyn fmt::Write, - ) -> fmt::Result { - let inp = self.get_input_types(node, snarl); - let sig = self.signature(&inp); - info!("compile {self} input types {inp:?} -> sig {sig:?}"); - - let signature = self.get_node_signature(node, snarl); - - let inputs: Result<Vec<String>, fmt::Error> = (0..signature.inputs.len()) - .map( - |input| match &*snarl.in_pin(InPinId { node, input }).remotes { - [] => Err(fmt::Error), - [pin] => Ok(DemoNode::compile_output(*pin)), - _ => unreachable!("cannot connect to multiple inputs"), - }, - ) - .collect(); - - let outputs: Vec<String> = (0..signature.outputs.len()) - .map(|output| DemoNode::compile_output(OutPinId { node, output })) - .collect(); - - snarl[node].compile(signature, inputs?, outputs, f) - } - - pub fn get_node_signature(&self, node: NodeId, snarl: &Snarl<DemoNode>) -> TypeSignature { - self.signature(&self.get_input_types(node, snarl)) - } -} - -impl NodeIndex for DemoNode { - const TITLE: &'static str = ""; - - fn all() -> impl Iterator<Item = Self> { - (library::AnyConstant::all().map(DemoNode::from)) - .chain(library::Conversion::all().map(DemoNode::from)) - .chain(library::Input::all().map(DemoNode::from)) - .chain(library::Output::all().map(DemoNode::from)) - .chain(library::BuiltinFunction::all().map(DemoNode::from)) - } - - fn pick_node(ui: &mut egui::Ui) -> Option<Self> { - ui.label("Add node"); - - let a = library::AnyConstant::pick_node(ui).map(DemoNode::from); - let b = library::Conversion::pick_node(ui).map(DemoNode::from); - let c = library::Input::pick_node(ui).map(DemoNode::from); - let d = library::Output::pick_node(ui).map(DemoNode::from); - let e = library::BuiltinFunction::pick_node(ui).map(DemoNode::from); - - a.or(b).or(c).or(d).or(e) - } -} - -impl fmt::Display for DemoNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Constant(x) => fmt::Display::fmt(x, f), - Self::Conversion(x) => fmt::Display::fmt(x, f), - Self::Input(x) => fmt::Display::fmt(x, f), - Self::Builtin(x) => fmt::Display::fmt(x, f), - Self::Output(x) => fmt::Display::fmt(x, f), - } - } -} - -struct DemoViewer { +pub struct Viewer { dirty: bool, } -impl DemoViewer { - 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); - sig.inputs.get(pin.input).copied() - } - - 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] - } - - pub fn compile(snarl: &Snarl<DemoNode>, f: &mut dyn fmt::Write) -> fmt::Result { - let mut order = topological_sort::TopologicalSort::<NodeId>::new(); - for (out, inp) in snarl.wires() { - order.add_dependency(out.node, inp.node); - } - - write!( - f, - " -#version 450 -in vec2 in_uv; -out vec4 out_color; - -layout(std140, binding = 0) uniform Uniforms {{ - vec2 in_resolution; - float in_time; - float _pad3; -}} uniforms; - -void main() {{ -" - )?; - while let Some(id) = order.pop() { - snarl[id].compile_node(id, snarl, f)?; - } - writeln!(f, "}}")?; - - Ok(()) - } -} - -impl SnarlViewer<DemoNode> for DemoViewer { +impl SnarlViewer<AnyNode> for Viewer { #[inline] - fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<DemoNode>) { + fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<AnyNode>) { 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 mut inputs = snarl.get_input_types(to.id.node); + inputs[to.id.input] = Some(snarl.out_pin_type(from.id)); let signatures = to_node.signatures_matching(&inputs[..]); if signatures.is_empty() { @@ -365,51 +61,49 @@ impl SnarlViewer<DemoNode> for DemoViewer { self.dirty = true; } - fn disconnect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<DemoNode>) { + fn disconnect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<AnyNode>) { snarl.disconnect(from.id, to.id); self.dirty = true; } - fn drop_outputs(&mut self, pin: &OutPin, snarl: &mut Snarl<DemoNode>) { + fn drop_outputs(&mut self, pin: &OutPin, snarl: &mut Snarl<AnyNode>) { snarl.drop_outputs(pin.id); } - fn drop_inputs(&mut self, pin: &InPin, snarl: &mut Snarl<DemoNode>) { + fn drop_inputs(&mut self, pin: &InPin, snarl: &mut Snarl<AnyNode>) { snarl.drop_inputs(pin.id); } - fn title(&mut self, node: &DemoNode) -> String { + fn title(&mut self, node: &AnyNode) -> String { format!("{node}") } - fn inputs(&mut self, id: NodeId, snarl: &Snarl<DemoNode>) -> usize { + fn inputs(&mut self, id: NodeId, snarl: &Snarl<AnyNode>) -> usize { let node = &snarl[id]; node.max_inputs() } - fn outputs(&mut self, id: NodeId, snarl: &Snarl<DemoNode>) -> usize { - let node = &snarl[id]; - let sig = &node.get_node_signature(id, snarl); - sig.outputs.len() + fn outputs(&mut self, id: NodeId, snarl: &Snarl<AnyNode>) -> usize { + snarl.get_node_signature(id).outputs.len() } #[allow(clippy::too_many_lines)] #[allow(refining_impl_trait)] - fn show_input(&mut self, pin: &InPin, _ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { - match DemoViewer::get_in_type(pin.id, snarl) { + fn show_input(&mut self, pin: &InPin, _ui: &mut Ui, snarl: &mut Snarl<AnyNode>) -> PinInfo { + match snarl.in_pin_type(pin.id) { None => PinInfo::star().with_fill(Color32::from_rgb(0xb0, 0xb0, 0xb0)), Some(t) => t.into(), } } #[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); + fn show_output(&mut self, pin: &OutPin, ui: &mut Ui, snarl: &mut Snarl<AnyNode>) -> PinInfo { + let typ = snarl.out_pin_type(pin.id); ui.label(format!("{typ}")); typ.into() } - fn has_body(&mut self, _node: &DemoNode) -> bool { + fn has_body(&mut self, _node: &AnyNode) -> bool { true } @@ -419,25 +113,25 @@ impl SnarlViewer<DemoNode> for DemoViewer { _inputs: &[InPin], _outputs: &[OutPin], ui: &mut Ui, - snarl: &mut Snarl<DemoNode>, + snarl: &mut Snarl<AnyNode>, ) { if snarl[node].show_body(ui).changed() { self.dirty = true; } } - fn has_graph_menu(&mut self, _pos: egui::Pos2, _snarl: &mut Snarl<DemoNode>) -> bool { + fn has_graph_menu(&mut self, _pos: egui::Pos2, _snarl: &mut Snarl<AnyNode>) -> bool { true } - fn show_graph_menu(&mut self, pos: egui::Pos2, ui: &mut Ui, snarl: &mut Snarl<DemoNode>) { - if let Some(node) = DemoNode::pick_node(ui) { + fn show_graph_menu(&mut self, pos: egui::Pos2, ui: &mut Ui, snarl: &mut Snarl<AnyNode>) { + if let Some(node) = AnyNode::pick_node(ui) { snarl.insert_node(pos, node); ui.close(); } } - fn has_node_menu(&mut self, _node: &DemoNode) -> bool { + fn has_node_menu(&mut self, _node: &AnyNode) -> bool { true } @@ -447,7 +141,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { _inputs: &[InPin], _outputs: &[OutPin], ui: &mut Ui, - snarl: &mut Snarl<DemoNode>, + snarl: &mut Snarl<AnyNode>, ) { ui.label("Node menu"); if ui.button("Remove").clicked() { @@ -457,9 +151,9 @@ impl SnarlViewer<DemoNode> for DemoViewer { } } -pub struct DemoApp { - snarl: Snarl<DemoNode>, - viewer: DemoViewer, +pub struct App { + snarl: Snarl<AnyNode>, + viewer: Viewer, preview: preview::Custom3d, } @@ -493,8 +187,8 @@ const fn default_style() -> SnarlStyle { } } -impl DemoApp { - pub fn new(cx: &CreationContext) -> Self { +impl App { + pub fn new(cx: &eframe::CreationContext) -> Self { cx.egui_ctx.style_mut(|style| style.animation_time *= 10.0); let snarl = cx.storage.map_or_else(Snarl::new, |storage| { @@ -507,15 +201,15 @@ impl DemoApp { info!("about to init"); - DemoApp { + App { snarl, - viewer: DemoViewer { dirty: true }, + viewer: Viewer { dirty: true }, preview: preview::Custom3d::new(cx).expect("Failed to init preview"), } } } -impl App for DemoApp { +impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { // The top panel is often a good place for a menu bar: @@ -550,7 +244,7 @@ impl App for DemoApp { if self.viewer.dirty { self.viewer.dirty = false; let mut buf = String::new(); - match DemoViewer::compile(&self.snarl, &mut buf) { + match self.snarl.compile(&mut buf) { Ok(()) => { info!("{}", buf); shader = Some(buf); @@ -591,7 +285,7 @@ fn main() -> eframe::Result<()> { eframe::run_native( "egui-snarl demo", native_options, - Box::new(|cx| Ok(Box::new(DemoApp::new(cx)))), + Box::new(|cx| Ok(Box::new(App::new(cx)))), ) } @@ -618,7 +312,7 @@ fn main() { .start( canvas, web_options, - Box::new(|cx| Ok(Box::new(DemoApp::new(cx)))), + Box::new(|cx| Ok(Box::new(App::new(cx)))), ) .await .expect("failed to start eframe"); diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..859799b --- /dev/null +++ b/src/node.rs @@ -0,0 +1,75 @@ +use enum_dispatch::enum_dispatch; +use std::fmt; + +use crate::library; +use crate::library::*; +use crate::types::{Type, TypeSignature}; + +/// an instantiable Node, parametrized by its connected input types +#[enum_dispatch] +pub trait ConcreteNode { + fn max_inputs(&self) -> usize; + + // set of possible input type combinations given current connections + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature>; + + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature; + + fn compile( + &self, + signature: TypeSignature, + inputs: Vec<String>, + outputs: Vec<String>, + f: &mut dyn fmt::Write, + ) -> fmt::Result; + + fn show_body(&mut self, ui: &mut egui::Ui) -> egui::Response { + ui.response() + } +} + +#[enum_dispatch(ConcreteNode)] +#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum AnyNode { + Constant(library::AnyConstant), + Conversion(library::Conversion), + Input(library::Input), + Builtin(library::BuiltinFunction), + Output(library::Output), +} + +impl NodeIndex for AnyNode { + const TITLE: &'static str = ""; + + fn all() -> impl Iterator<Item = Self> { + (library::AnyConstant::all().map(AnyNode::from)) + .chain(library::Conversion::all().map(AnyNode::from)) + .chain(library::Input::all().map(AnyNode::from)) + .chain(library::Output::all().map(AnyNode::from)) + .chain(library::BuiltinFunction::all().map(AnyNode::from)) + } + + fn pick_node(ui: &mut egui::Ui) -> Option<Self> { + ui.label("Add node"); + + let a = library::AnyConstant::pick_node(ui).map(AnyNode::from); + let b = library::Conversion::pick_node(ui).map(AnyNode::from); + let c = library::Input::pick_node(ui).map(AnyNode::from); + let d = library::Output::pick_node(ui).map(AnyNode::from); + let e = library::BuiltinFunction::pick_node(ui).map(AnyNode::from); + + a.or(b).or(c).or(d).or(e) + } +} + +impl fmt::Display for AnyNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Constant(x) => fmt::Display::fmt(x, f), + Self::Conversion(x) => fmt::Display::fmt(x, f), + Self::Input(x) => fmt::Display::fmt(x, f), + Self::Builtin(x) => fmt::Display::fmt(x, f), + Self::Output(x) => fmt::Display::fmt(x, f), + } + } +} diff --git a/src/snarl_ext.rs b/src/snarl_ext.rs new file mode 100644 index 0000000..7d0464d --- /dev/null +++ b/src/snarl_ext.rs @@ -0,0 +1,104 @@ +use egui_snarl::{InPinId, NodeId, OutPinId, Snarl}; +use std::fmt; +use topological_sort::TopologicalSort; + +use crate::node::{AnyNode, ConcreteNode}; +use crate::types::{Type, TypeSignature}; + +pub trait WithSignatures { + fn in_pin_type(&self, pin: InPinId) -> Option<Type>; + fn out_pin_type(&self, pin: OutPinId) -> Type; + fn get_input_types(&self, node: NodeId) -> Vec<Option<Type>>; + fn get_node_signature(&self, node: NodeId) -> TypeSignature; +} + +pub trait Compilable { + fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> fmt::Result; + fn compile(&self, f: &mut dyn fmt::Write) -> fmt::Result; +} + +impl WithSignatures for Snarl<AnyNode> { + fn in_pin_type(&self, pin: InPinId) -> Option<Type> { + let sig = &self.get_node_signature(pin.node); + sig.inputs.get(pin.input).copied() + } + + fn out_pin_type(&self, pin: OutPinId) -> Type { + let sig = &self.get_node_signature(pin.node); + sig.outputs[pin.output] + } + + fn get_input_types(&self, node: NodeId) -> Vec<Option<Type>> { + (0..self[node].max_inputs()) + .map( + move |input| match &*self.in_pin(InPinId { node, input }).remotes { + [] => None, + [out_pin] => Some(self.out_pin_type(*out_pin)), + _ => unreachable!("cannot connect to multiple inputs"), + }, + ) + .collect() + } + + fn get_node_signature(&self, node: NodeId) -> TypeSignature { + self[node].signature(&self.get_input_types(node)) + } +} + +fn compile_output(pin: OutPinId) -> String { + let node_id = pin.node.0; + let out_id = pin.output; + format!("n{node_id}_o{out_id}") +} + +impl Compilable for Snarl<AnyNode> { + fn compile(&self, f: &mut dyn fmt::Write) -> fmt::Result { + let mut order = TopologicalSort::<NodeId>::new(); + for (out, inp) in self.wires() { + order.add_dependency(out.node, inp.node); + } + + write!( + f, + " +#version 450 +in vec2 in_uv; +out vec4 out_color; + +layout(std140, binding = 0) uniform Uniforms {{ + vec2 in_resolution; + float in_time; + float _pad3; +}} uniforms; + +void main() {{ +" + )?; + while let Some(id) = order.pop() { + self.compile_node(id, f)?; + } + writeln!(f, "}}")?; + + Ok(()) + } + + fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> fmt::Result { + let signature = self.get_node_signature(node); + + let inputs: Result<Vec<String>, fmt::Error> = (0..signature.inputs.len()) + .map( + |input| match &*self.in_pin(InPinId { node, input }).remotes { + [] => Err(fmt::Error), + [pin] => Ok(compile_output(*pin)), + _ => unreachable!("cannot connect to multiple inputs"), + }, + ) + .collect(); + + let outputs: Vec<String> = (0..signature.outputs.len()) + .map(|output| compile_output(OutPinId { node, output })) + .collect(); + + self[node].compile(signature, inputs?, outputs, f) + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..b1ed2fb --- /dev/null +++ b/src/types.rs @@ -0,0 +1,145 @@ +use std::fmt; + +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum FloatPrecision { + Single, + Double, +} + +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum ScalarType { + Float(FloatPrecision), + Int, + UInt, + Bool, +} + +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[repr(u8)] +pub enum Dimension { + D2 = 2, + D3 = 3, + D4 = 4, +} +impl fmt::Display for Dimension { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", *self as u8) + } +} +impl Dimension { + pub const fn from(v: usize) -> Dimension { + match v { + 2 => Self::D2, + 3 => Self::D3, + 4 => Self::D4, + _ => panic!("invalid dimension value"), + } + } +} + +/// a GLSL type +#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum Type { + Scalar(ScalarType), + Vector(ScalarType, Dimension), + Matrix(FloatPrecision, Dimension, Dimension), +} + +impl From<ScalarType> for Type { + fn from(val: ScalarType) -> Self { + Self::Scalar(val) + } +} + +impl Type { + pub fn scalar(&self) -> ScalarType { + match self { + Type::Scalar(s) => *s, + Type::Vector(s, _) => *s, + Type::Matrix(p, _, _) => ScalarType::Float(*p), + } + } +} + +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Type::Scalar(s) => write!( + f, + "{}", + match s { + ScalarType::Float(FloatPrecision::Single) => "float", + ScalarType::Float(FloatPrecision::Double) => "double", + ScalarType::Int => "int", + ScalarType::UInt => "uint", + ScalarType::Bool => "bool", + } + ), + Type::Vector(s, d) => write!( + f, + "{}vec{d}", + match s { + ScalarType::Float(FloatPrecision::Single) => "", + ScalarType::Float(FloatPrecision::Double) => "d", + ScalarType::Int => "i", + ScalarType::UInt => "u", + ScalarType::Bool => "b", + } + ), + Type::Matrix(p, r, c) => { + let pr = match p { + FloatPrecision::Single => "", + FloatPrecision::Double => "d", + }; + if r == c { + write!(f, "{pr}mat{r}") + } else { + write!(f, "{pr}mat{r}{c}") + } + } + } + } +} + +/// a single concrete type signature for a function +#[derive(PartialEq, Debug)] +pub struct TypeSignature { + pub inputs: Box<[Type]>, + pub outputs: Box<[Type]>, +} + +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), + } + } +} + +impl TypeSignature { + pub fn matches_inputs(&self, connected: &[Option<Type>]) -> bool { + let have_inputs = connected + .iter() + .enumerate() + .filter(|(_, t)| matches!(t, Some(_))) + .map(|(i, _)| i + 1) + .max() + .unwrap_or(0); + + if have_inputs > self.inputs.len() { + false + } else { + connected + .iter() + .zip(self.inputs.iter()) + .all(|(connected_input, expected)| { + if let Some(input) = connected_input { + *input == *expected + } else { + true + } + }) + } + } +} |
