aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-01-17 12:34:26 +0000
committers-ol <s+removethis@s-ol.nu>2026-01-17 12:34:26 +0000
commit87fecd4c87a3b4f33c795beead1b8f9f19fd7e47 (patch)
tree2ce0f0f107ced3547a8e1ebecdd2bab3e82b0518 /src/main.rs
parentVector combine (diff)
downloadnodetoy-87fecd4c87a3b4f33c795beead1b8f9f19fd7e47.tar.gz
nodetoy-87fecd4c87a3b4f33c795beead1b8f9f19fd7e47.zip
split main into smaller modules
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs392
1 files changed, 43 insertions, 349 deletions
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");