From 989c8ad5f819dfa6ba5477885ecea069cc8847c2 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Thu, 15 Jun 2023 17:32:25 +0200 Subject: [PATCH] Implement skeleton for graphene-cli --- Cargo.lock | 26 +++++ Cargo.toml | 1 + node-graph/graphene-cli/Cargo.toml | 40 ++++++++ node-graph/graphene-cli/src/main.rs | 148 ++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 node-graph/graphene-cli/Cargo.toml create mode 100644 node-graph/graphene-cli/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 0ae7da2fd..45be289a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1743,6 +1743,32 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "graphene-cli" +version = "0.1.0" +dependencies = [ + "bezier-rs", + "bitflags 1.3.2", + "dyn-any", + "env_logger", + "future-executor", + "futures", + "glam", + "gpu-executor", + "graph-craft", + "graphene-core", + "graphene-std", + "graphite-document-legacy", + "image", + "interpreted-executor", + "log", + "serde", + "serde_json", + "test-case", + "wasm-bindgen", + "wgpu-executor", +] + [[package]] name = "graphene-core" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 96347157d..2d8aec230 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "node-graph/gcore", "node-graph/gstd", "node-graph/graph-craft", + "node-graph/graphene-cli", "node-graph/interpreted-executor", "node-graph/node-macro", "node-graph/compilation-server", diff --git a/node-graph/graphene-cli/Cargo.toml b/node-graph/graphene-cli/Cargo.toml new file mode 100644 index 000000000..9a28fecf3 --- /dev/null +++ b/node-graph/graphene-cli/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "graphene-cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +log = "0.4" +bitflags = "1.2.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0" } +bezier-rs = { path = "../../libraries/bezier-rs" } +glam = { version = "0.22", features = ["serde"] } + +# Node graph +image = { version = "0.24", default-features = false, features = [ + "bmp", + "png", +] } +graph-craft = { path = "../graph-craft" } +wgpu-executor = { path = "../wgpu-executor", optional = true } +gpu-executor = { path = "../gpu-executor", optional = true } +interpreted-executor = { path = "../interpreted-executor" } +dyn-any = { path = "../../libraries/dyn-any" } +graphene-core = { path = "../gcore" } +graphene-std = { path = "../gstd" } +future-executor = { path = "../future-executor", optional = true } + +wasm-bindgen = { version = "0.2.86", optional = true } +futures = "0.3.28" + +[dependencies.document-legacy] +path = "../../document-legacy" +package = "graphite-document-legacy" + +[dev-dependencies] +env_logger = "0.8.4" +test-case = "2.1" diff --git a/node-graph/graphene-cli/src/main.rs b/node-graph/graphene-cli/src/main.rs new file mode 100644 index 000000000..64e95edf7 --- /dev/null +++ b/node-graph/graphene-cli/src/main.rs @@ -0,0 +1,148 @@ +use std::{collections::HashMap, error::Error}; + +use document_legacy::{ + document::Document, + layers::layer_info::{LayerData, LayerDataType}, +}; +use futures::executor::block_on; +use graph_craft::{ + concrete, + document::{value::TaggedValue, *}, + graphene_compiler::{Compiler, Executor}, + imaginate_input::ImaginatePreferences, + NodeIdentifier, Type, TypeDescriptor, +}; +use graphene_core::{application_io::NodeGraphUpdateSender, raster::ImageFrame, text::FontCache, Cow}; +use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi}; +use interpreted_executor::dynamic_executor::DynamicExecutor; + +struct UpdateLogger {} + +impl NodeGraphUpdateSender for UpdateLogger { + fn send(&self, message: graphene_core::application_io::NodeGraphUpdateMessage) { + println!("{:?}", message); + } +} + +fn main() -> Result<(), Box> { + let document_path = std::env::args().nth(1).expect("No document path provided"); + + let document_string = std::fs::read_to_string(&document_path).expect("Failed to read document"); + + let document: serde_json::Value = serde_json::from_str(&document_string).expect("Failed to parse document"); + let document = serde_json::from_value::(document["document_legacy"].clone()).expect("Failed to parse document"); + + let Some(LayerDataType::Layer(ref node_graph)) = document.root.iter().find(|layer| matches!(layer.data, LayerDataType::Layer(_))).map(|x|&x.data) else { panic!("failed to extract node graph from docmuent") }; + let network = &node_graph.network; + + let wrapped_network = wrap_network_in_scope(network.clone()); + + let compiler = Compiler {}; + let protograph = compiler.compile_single(wrapped_network, true)?; + + let mut executor = block_on(DynamicExecutor::new(protograph))?; + + let editor_api = WasmEditorApi { + image_frame: None, + font_cache: &FontCache::default(), + application_io: &WasmApplicationIo::default(), + node_graph_message_sender: &UpdateLogger {}, + imaginate_preferences: &ImaginatePreferences::default(), + }; + + let result = block_on((&executor).execute(editor_api))?; + println!("result: {:?}", result); + Ok(()) +} + +pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork { + let node_ids = network.nodes.keys().copied().collect::>(); + + network.generate_node_paths(&[]); + for id in node_ids { + network.flatten(id); + } + + let mut network_inputs = Vec::new(); + let mut input_type = None; + for (id, node) in network.nodes.iter() { + for input in node.inputs.iter() { + if let NodeInput::Network(_) = input { + if input_type.is_none() { + input_type = Some(input.clone()); + } + assert_eq!(input, input_type.as_ref().unwrap(), "Networks wrapped in scope must have the same input type"); + network_inputs.push(*id); + } + } + } + let len = network_inputs.len(); + network.inputs = network_inputs; + + // if the network has no inputs, it doesn't need to be wrapped in a scope + if len == 0 { + return network; + } + + let inner_network = DocumentNode { + name: "Scope".to_string(), + implementation: DocumentNodeImplementation::Network(network), + inputs: core::iter::repeat(NodeInput::node(0, 1)).take(len).collect(), + ..Default::default() + }; + + // wrap the inner network in a scope + let nodes = vec![begin_scope(), inner_network, end_scope()]; + NodeNetwork { + inputs: vec![0], + outputs: vec![NodeOutput::new(2, 0)], + nodes: nodes.into_iter().enumerate().map(|(id, node)| (id as NodeId, node)).collect(), + ..Default::default() + } +} + +fn begin_scope() -> DocumentNode { + DocumentNode { + name: "Begin Scope".to_string(), + implementation: DocumentNodeImplementation::Network(NodeNetwork { + inputs: vec![0], + outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(2, 0)], + nodes: [ + DocumentNode { + name: "SetNode".to_string(), + inputs: vec![NodeInput::ShortCircut(concrete!(WasmEditorApi))], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::SomeNode")), + ..Default::default() + }, + DocumentNode { + name: "LetNode".to_string(), + inputs: vec![NodeInput::node(0, 0)], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::LetNode<_>")), + ..Default::default() + }, + DocumentNode { + name: "RefNode".to_string(), + inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::lambda(1, 0)], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::RefNode<_, _>")), + ..Default::default() + }, + ] + .into_iter() + .enumerate() + .map(|(id, node)| (id as NodeId, node)) + .collect(), + + ..Default::default() + }), + inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))], + ..Default::default() + } +} +fn end_scope() -> DocumentNode { + DocumentNode { + name: "End Scope".to_string(), + implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_>"), + inputs: vec![NodeInput::value(TaggedValue::None, true), NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true)], + ..Default::default() + } +}