Introduce scopes (#1053)

* Implement let binding

* Add lambda inputs

* Fix tests

* Fix proto network formatting

* Generate a template Scoped network by default

* Add comment to explain the lambda parameter

* Move binding wrapping out of the template

* Fix errors cause by image frames
This commit is contained in:
Dennis Kobert 2023-03-02 17:13:28 +01:00 committed by Keavon Chambers
parent 0b813805d2
commit 7254c008f9
12 changed files with 366 additions and 118 deletions

View File

@ -623,7 +623,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
// Make layer the size of the image // Make layer the size of the image
let fit_image_size = DAffine2::from_scale_angle_translation(image_size, 0., image_size / -2.); let fit_image_size = DAffine2::from_scale_angle_translation(image_size, 0., image_size / -2.);
let transform = (center_in_viewport_layerspace * fit_image_size); let transform = center_in_viewport_layerspace * fit_image_size;
responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(DocumentMessage::StartTransaction.into());

View File

@ -44,6 +44,7 @@ impl FrontendGraphDataType {
TaggedValue::Bool(_) => Self::Boolean, TaggedValue::Bool(_) => Self::Boolean,
TaggedValue::DVec2(_) => Self::Vector, TaggedValue::DVec2(_) => Self::Vector,
TaggedValue::Image(_) => Self::Raster, TaggedValue::Image(_) => Self::Raster,
TaggedValue::ImageFrame(_) => Self::Raster,
TaggedValue::Color(_) => Self::Color, TaggedValue::Color(_) => Self::Color,
TaggedValue::RcSubpath(_) | TaggedValue::Subpath(_) => Self::Subpath, TaggedValue::RcSubpath(_) | TaggedValue::Subpath(_) => Self::Subpath,
_ => Self::General, _ => Self::General,
@ -279,6 +280,8 @@ impl NodeGraphMessageHandler {
if let NodeInput::Node { if let NodeInput::Node {
node_id: link_start, node_id: link_start,
output_index: link_start_index, output_index: link_start_index,
// TODO: add ui for lambdas
lambda,
} = *input } = *input
{ {
Some(FrontendNodeLink { Some(FrontendNodeLink {

View File

@ -108,7 +108,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
name: "Image", name: "Image",
category: "Ignore", category: "Ignore",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode"), identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
inputs: vec![DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), false)], inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), false)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"), properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
}, },
@ -130,20 +130,12 @@ fn static_nodes() -> Vec<DocumentNodeType> {
identifier: NodeImplementation::DocumentNode(NodeNetwork { identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1], inputs: vec![0, 1],
outputs: vec![NodeOutput::new(0, 0), NodeOutput::new(1, 0)], outputs: vec![NodeOutput::new(0, 0), NodeOutput::new(1, 0)],
nodes: [ nodes: [DocumentNode {
DocumentNode { name: "Identity".to_string(),
name: "Identity".to_string(), inputs: vec![NodeInput::Network(concrete!(ImageFrame))],
inputs: vec![NodeInput::Network(concrete!(ImageFrame))], implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")), metadata: Default::default(),
metadata: Default::default(), }]
},
DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network(concrete!(DAffine2))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
metadata: Default::default(),
},
]
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(id, node)| (id as NodeId, node)) .map(|(id, node)| (id as NodeId, node))
@ -158,24 +150,97 @@ fn static_nodes() -> Vec<DocumentNodeType> {
}, },
DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false), DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false),
], ],
outputs: vec![DocumentOutputType {
name: "Image Frame",
data_type: FrontendGraphDataType::Raster,
}],
properties: node_properties::input_properties,
},
DocumentNodeType {
name: "Begin Scope",
category: "Structural",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 2],
outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(3, 0)],
nodes: [
DocumentNode {
name: "SetNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::SomeNode")),
metadata: Default::default(),
},
DocumentNode {
name: "LetNode".to_string(),
inputs: vec![NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::LetNode<_>")),
metadata: Default::default(),
},
DocumentNode {
name: "RefNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::lambda(1, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::RefNode<_, _>")),
metadata: Default::default(),
},
DocumentNode {
name: "CloneNode".to_string(),
inputs: vec![NodeInput::node(2, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::CloneNode<_>")),
metadata: Default::default(),
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
}),
inputs: vec![DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
}],
outputs: vec![ outputs: vec![
DocumentOutputType { DocumentOutputType {
name: "Image Frame", name: "Scope",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::General,
}, },
DocumentOutputType { DocumentOutputType {
name: "Transform", name: "Binding",
data_type: FrontendGraphDataType::Number, data_type: FrontendGraphDataType::Raster,
}, },
], ],
properties: node_properties::input_properties, properties: |_document_node, _node_id, _context| node_properties::string_properties("Binds the input in a local scope as a variable"),
},
DocumentNodeType {
name: "End Scope",
category: "Structural",
identifier: NodeImplementation::proto("graphene_std::memo::EndLetNode<_>"),
inputs: vec![
DocumentInputType {
name: "Scope",
data_type: FrontendGraphDataType::General,
default: NodeInput::value(TaggedValue::None, true),
},
DocumentInputType {
name: "Data",
data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
},
],
outputs: vec![DocumentOutputType {
name: "Frame",
data_type: FrontendGraphDataType::Raster,
}],
properties: |_document_node, _node_id, _context| node_properties::string_properties("The graph's output is rendered into the frame"),
}, },
DocumentNodeType { DocumentNodeType {
name: "Output", name: "Output",
category: "Ignore", category: "Ignore",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode"), identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
inputs: vec![DocumentInputType { inputs: vec![DocumentInputType {
name: "In", name: "Output",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true), default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
}], }],
@ -198,8 +263,8 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::BlendNode<_, _, _, _>"), identifier: NodeImplementation::proto("graphene_core::raster::BlendNode<_, _, _, _>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Second", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Second", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("BlendMode", TaggedValue::BlendMode(BlendMode::Normal), false), DocumentInputType::value("BlendMode", TaggedValue::BlendMode(BlendMode::Normal), false),
DocumentInputType::value("Opacity", TaggedValue::F64(100.), false), DocumentInputType::value("Opacity", TaggedValue::F64(100.), false),
], ],
@ -214,7 +279,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentInputType { DocumentInputType {
name: "Image", name: "Image",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::Image(Image::empty()), true), default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
}, },
DocumentInputType { DocumentInputType {
name: "Shadows", name: "Shadows",
@ -253,7 +318,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentInputType { DocumentInputType {
name: "Image", name: "Image",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::Image(Image::empty()), true), default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
}, },
DocumentInputType { DocumentInputType {
name: "Tint", name: "Tint",
@ -299,7 +364,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::LuminanceNode<_>"), identifier: NodeImplementation::proto("graphene_core::raster::LuminanceNode<_>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false), DocumentInputType::value("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false),
], ],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
@ -336,7 +401,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
..Default::default() ..Default::default()
}), }),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Radius", TaggedValue::U32(3), false), DocumentInputType::value("Radius", TaggedValue::U32(3), false),
DocumentInputType::value("Sigma", TaggedValue::F64(1.), false), DocumentInputType::value("Sigma", TaggedValue::F64(1.), false),
], ],
@ -376,7 +441,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
.collect(), .collect(),
..Default::default() ..Default::default()
}), }),
inputs: vec![DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true)], inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: node_properties::no_properties, properties: node_properties::no_properties,
}, },
@ -386,7 +451,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode"), identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode"),
inputs: vec![ inputs: vec![
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType { DocumentInputType {
name: "Path", name: "Path",
data_type: FrontendGraphDataType::Text, data_type: FrontendGraphDataType::Text,
@ -405,7 +470,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentInputType { DocumentInputType {
name: "Image", name: "Image",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::Image(Image::empty()), true), default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
}, },
DocumentInputType { DocumentInputType {
name: "samples", name: "samples",
@ -425,7 +490,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
name: "Invert RGB", name: "Invert RGB",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::InvertRGBNode"), identifier: NodeImplementation::proto("graphene_core::raster::InvertRGBNode"),
inputs: vec![DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true)], inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: node_properties::no_properties, properties: node_properties::no_properties,
}, },
@ -434,7 +499,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::HueSaturationNode<_, _, _>"), identifier: NodeImplementation::proto("graphene_core::raster::HueSaturationNode<_, _, _>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Hue Shift", TaggedValue::F64(0.), false), DocumentInputType::value("Hue Shift", TaggedValue::F64(0.), false),
DocumentInputType::value("Saturation Shift", TaggedValue::F64(0.), false), DocumentInputType::value("Saturation Shift", TaggedValue::F64(0.), false),
DocumentInputType::value("Lightness Shift", TaggedValue::F64(0.), false), DocumentInputType::value("Lightness Shift", TaggedValue::F64(0.), false),
@ -447,7 +512,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _>"), identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Brightness", TaggedValue::F64(0.), false), DocumentInputType::value("Brightness", TaggedValue::F64(0.), false),
DocumentInputType::value("Contrast", TaggedValue::F64(0.), false), DocumentInputType::value("Contrast", TaggedValue::F64(0.), false),
], ],
@ -459,7 +524,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _>"), identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false), DocumentInputType::value("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false),
DocumentInputType::value("Threshold", TaggedValue::F64(50.), false), DocumentInputType::value("Threshold", TaggedValue::F64(50.), false),
], ],
@ -471,7 +536,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>"), identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Vibrance", TaggedValue::F64(0.), false), DocumentInputType::value("Vibrance", TaggedValue::F64(0.), false),
], ],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
@ -482,7 +547,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>"), identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Factor", TaggedValue::F64(100.), false), DocumentInputType::value("Factor", TaggedValue::F64(100.), false),
], ],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
@ -493,7 +558,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>"), identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Value", TaggedValue::F64(4.), false), DocumentInputType::value("Value", TaggedValue::F64(4.), false),
], ],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
@ -504,7 +569,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::ExposureNode<_, _, _>"), identifier: NodeImplementation::proto("graphene_core::raster::ExposureNode<_, _, _>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Exposure", TaggedValue::F64(0.), false), DocumentInputType::value("Exposure", TaggedValue::F64(0.), false),
DocumentInputType::value("Offset", TaggedValue::F64(0.), false), DocumentInputType::value("Offset", TaggedValue::F64(0.), false),
DocumentInputType::value("Gamma Correction", TaggedValue::F64(1.), false), DocumentInputType::value("Gamma Correction", TaggedValue::F64(1.), false),
@ -650,6 +715,7 @@ impl DocumentNodeType {
} }
NodeImplementation::DocumentNode(network) => network.clone(), NodeImplementation::DocumentNode(network) => network.clone(),
}; };
DocumentNodeImplementation::Network(inner_network) DocumentNodeImplementation::Network(inner_network)
} }
@ -663,15 +729,43 @@ impl DocumentNodeType {
} }
} }
pub fn wrap_network_in_scope(network: NodeNetwork) -> NodeNetwork {
assert_eq!(network.inputs.len(), 1, "Networks wrapped in scope must have exactly one input");
let input_type = network.nodes[&network.inputs[0]].inputs.iter().find(|&i| matches!(i, NodeInput::Network(_))).unwrap().clone();
let inner_network = DocumentNode {
name: "Scope".to_string(),
implementation: DocumentNodeImplementation::Network(network),
inputs: vec![NodeInput::node(0, 1)],
metadata: DocumentNodeMetadata::default(),
};
// wrap the inner network in a scope
let nodes = vec![
resolve_document_node_type("Begin Scope")
.expect("Begin Scope node type not found")
.to_document_node(vec![input_type.clone()], DocumentNodeMetadata::default()),
inner_network,
resolve_document_node_type("End Scope")
.expect("End Scope node type not found")
.to_document_node(vec![NodeInput::node(0, 0), NodeInput::node(1, 0)], DocumentNodeMetadata::default()),
];
let network = 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()
};
network
}
pub fn new_image_network(output_offset: i32, output_node_id: NodeId) -> NodeNetwork { pub fn new_image_network(output_offset: i32, output_node_id: NodeId) -> NodeNetwork {
NodeNetwork { NodeNetwork {
inputs: vec![0], inputs: vec![0],
outputs: vec![NodeOutput::new(1, 0)], outputs: vec![NodeOutput::new(1, 0)],
nodes: [ nodes: [
resolve_document_node_type("Input").expect("Input node does not exist").to_document_node( resolve_document_node_type("Input")
[NodeInput::Network(concrete!(Image)), NodeInput::value(TaggedValue::DAffine2(DAffine2::IDENTITY), false)], .expect("Input node does not exist")
DocumentNodeMetadata::position((8, 4)), .to_document_node([NodeInput::Network(concrete!(ImageFrame))], DocumentNodeMetadata::position((8, 4))),
),
resolve_document_node_type("Output") resolve_document_node_type("Output")
.expect("Output node does not exist") .expect("Output node does not exist")
.to_document_node([NodeInput::node(output_node_id, 0)], DocumentNodeMetadata::position((output_offset + 8, 4))), .to_document_node([NodeInput::node(output_node_id, 0)], DocumentNodeMetadata::position((output_offset + 8, 4))),

View File

@ -1,4 +1,5 @@
use crate::messages::frontend::utility_types::FrontendImageData; use crate::messages::frontend::utility_types::FrontendImageData;
use crate::messages::portfolio::document::node_graph::wrap_network_in_scope;
use crate::messages::portfolio::document::utility_types::misc::DocumentRenderMode; use crate::messages::portfolio::document::utility_types::misc::DocumentRenderMode;
use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@ -20,16 +21,18 @@ pub struct NodeGraphExecutor {
impl NodeGraphExecutor { impl NodeGraphExecutor {
/// Execute the network by flattening it and creating a borrow stack. Casts the output to the generic `T`. /// Execute the network by flattening it and creating a borrow stack. Casts the output to the generic `T`.
fn execute_network<T: dyn_any::StaticType>(&mut self, mut network: NodeNetwork, image_frame: ImageFrame) -> Result<T, String> { fn execute_network<T: dyn_any::StaticType>(&mut self, network: NodeNetwork, image_frame: ImageFrame) -> Result<T, String> {
network.duplicate_outputs(&mut generate_uuid); let mut scoped_network = wrap_network_in_scope(network);
network.remove_dead_nodes();
debug!("Execute document network:\n{network:#?}"); scoped_network.duplicate_outputs(&mut generate_uuid);
scoped_network.remove_dead_nodes();
debug!("Execute document network:\n{scoped_network:#?}");
// We assume only one output // We assume only one output
assert_eq!(network.outputs.len(), 1, "Graph with multiple outputs not yet handled"); assert_eq!(scoped_network.outputs.len(), 1, "Graph with multiple outputs not yet handled");
let c = Compiler {}; let c = Compiler {};
let proto_network = c.compile_single(network, true)?; let proto_network = c.compile_single(scoped_network, true)?;
debug!("Execute proto network:\n{proto_network}"); debug!("Execute proto network:\n{proto_network}");
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?"); assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
if let Err(e) = self.executor.update(proto_network) { if let Err(e) = self.executor.update(proto_network) {
@ -74,7 +77,7 @@ impl NodeGraphExecutor {
// If the input is just a value, return that value // If the input is just a value, return that value
NodeInput::Value { tagged_value, .. } => return dyn_any::downcast::<T>(tagged_value.clone().to_any()).map(|v| *v), NodeInput::Value { tagged_value, .. } => return dyn_any::downcast::<T>(tagged_value.clone().to_any()).map(|v| *v),
// If the input is from a node, set the node to be the output (so that is what is evaluated) // If the input is from a node, set the node to be the output (so that is what is evaluated)
NodeInput::Node { node_id, output_index } => { NodeInput::Node { node_id, output_index, .. } => {
inner_network.outputs[0] = NodeOutput::new(*node_id, *output_index); inner_network.outputs[0] = NodeOutput::new(*node_id, *output_index);
break 'outer; break 'outer;
} }

View File

@ -82,6 +82,13 @@ pub mod dynamic {
} }
}*/ }*/
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SomeNode;
#[node_macro::node_fn(SomeNode)]
fn some<T>(input: T) -> Option<T> {
Some(input)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct CloneNode<O>(PhantomData<O>); pub struct CloneNode<O>(PhantomData<O>);
impl<'i, 'n: 'i, O: Clone + 'i> Node<'i, &'n O> for CloneNode<O> { impl<'i, 'n: 'i, O: Clone + 'i> Node<'i, &'n O> for CloneNode<O> {

View File

@ -43,17 +43,17 @@ pub struct DocumentNode {
} }
impl DocumentNode { impl DocumentNode {
pub fn populate_first_network_input(&mut self, node_id: NodeId, output_index: usize, offset: usize) { pub fn populate_first_network_input(&mut self, node_id: NodeId, output_index: usize, offset: usize, lambda: bool) {
let input = self let input = self
.inputs .inputs
.iter() .iter()
.enumerate() .enumerate()
.filter(|(_, input)| matches!(input, NodeInput::Network(_))) .filter(|(_, input)| matches!(input, NodeInput::Network(_)))
.nth(offset) .nth(offset)
.expect("no network input"); .unwrap_or_else(|| panic!("no network input found for {self:#?} and offset: {offset}"));
let index = input.0; let index = input.0;
self.inputs[index] = NodeInput::Node { node_id, output_index }; self.inputs[index] = NodeInput::Node { node_id, output_index, lambda };
} }
fn resolve_proto_node(mut self) -> ProtoNode { fn resolve_proto_node(mut self) -> ProtoNode {
@ -62,12 +62,12 @@ impl DocumentNode {
if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation { if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
let (input, mut args) = match first { let (input, mut args) = match first {
NodeInput::Value { tagged_value, .. } => { NodeInput::Value { tagged_value, .. } => {
assert_eq!(self.inputs.len(), 0); assert_eq!(self.inputs.len(), 0, "{}, {:?}", &self.name, &self.inputs);
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value)) (ProtoNodeInput::None, ConstructionArgs::Value(tagged_value))
} }
NodeInput::Node { node_id, output_index } => { NodeInput::Node { node_id, output_index, lambda } => {
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode."); assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode.");
(ProtoNodeInput::Node(node_id), ConstructionArgs::Nodes(vec![])) (ProtoNodeInput::Node(node_id, lambda), ConstructionArgs::Nodes(vec![]))
} }
NodeInput::Network(ty) => (ProtoNodeInput::Network(ty), ConstructionArgs::Nodes(vec![])), NodeInput::Network(ty) => (ProtoNodeInput::Network(ty), ConstructionArgs::Nodes(vec![])),
}; };
@ -81,7 +81,7 @@ impl DocumentNode {
if let ConstructionArgs::Nodes(nodes) = &mut args { if let ConstructionArgs::Nodes(nodes) = &mut args {
nodes.extend(self.inputs.iter().map(|input| match input { nodes.extend(self.inputs.iter().map(|input| match input {
NodeInput::Node { node_id, .. } => *node_id, NodeInput::Node { node_id, lambda, .. } => (*node_id, *lambda),
_ => unreachable!(), _ => unreachable!(),
})); }));
} }
@ -103,11 +103,15 @@ impl DocumentNode {
P: Fn(String, usize) -> Option<NodeInput>, P: Fn(String, usize) -> Option<NodeInput>,
{ {
for (index, input) in self.inputs.iter_mut().enumerate() { for (index, input) in self.inputs.iter_mut().enumerate() {
let &mut NodeInput::Node{node_id: id, output_index} = input else { let &mut NodeInput::Node{node_id: id, output_index, lambda} = input else {
continue; continue;
}; };
if let Some(&new_id) = new_ids.get(&id) { if let Some(&new_id) = new_ids.get(&id) {
*input = NodeInput::Node { node_id: new_id, output_index }; *input = NodeInput::Node {
node_id: new_id,
output_index,
lambda,
};
} else if let Some(new_input) = default_input(self.name.clone(), index) { } else if let Some(new_input) = default_input(self.name.clone(), index) {
*input = new_input; *input = new_input;
} else { } else {
@ -121,21 +125,28 @@ impl DocumentNode {
#[derive(Debug, Clone, PartialEq, Hash, specta::Type)] #[derive(Debug, Clone, PartialEq, Hash, specta::Type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeInput { pub enum NodeInput {
Node { node_id: NodeId, output_index: usize }, Node { node_id: NodeId, output_index: usize, lambda: bool },
Value { tagged_value: value::TaggedValue, exposed: bool }, Value { tagged_value: value::TaggedValue, exposed: bool },
Network(Type), Network(Type),
} }
impl NodeInput { impl NodeInput {
pub const fn node(node_id: NodeId, output_index: usize) -> Self { pub const fn node(node_id: NodeId, output_index: usize) -> Self {
Self::Node { node_id, output_index } Self::Node { node_id, output_index, lambda: false }
}
pub const fn lambda(node_id: NodeId, output_index: usize) -> Self {
Self::Node { node_id, output_index, lambda: true }
} }
pub const fn value(tagged_value: value::TaggedValue, exposed: bool) -> Self { pub const fn value(tagged_value: value::TaggedValue, exposed: bool) -> Self {
Self::Value { tagged_value, exposed } Self::Value { tagged_value, exposed }
} }
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) { fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
if let &mut NodeInput::Node { node_id, output_index } = self { if let &mut NodeInput::Node { node_id, output_index, lambda } = self {
*self = NodeInput::Node { node_id: f(node_id), output_index } *self = NodeInput::Node {
node_id: f(node_id),
output_index,
lambda,
}
} }
} }
pub fn is_exposed(&self) -> bool { pub fn is_exposed(&self) -> bool {
@ -246,7 +257,7 @@ impl NodeNetwork {
} }
for input in &mut node.inputs { for input in &mut node.inputs {
let &mut NodeInput::Node { node_id, output_index} = input else { let &mut NodeInput::Node { node_id, output_index, .. } = input else {
continue; continue;
}; };
// Use the initial node when getting the first output // Use the initial node when getting the first output
@ -362,9 +373,9 @@ impl NodeNetwork {
for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) { for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
let offset = network_offsets.entry(network_input).or_insert(0); let offset = network_offsets.entry(network_input).or_insert(0);
match document_input { match document_input {
NodeInput::Node { node_id, output_index } => { NodeInput::Node { node_id, output_index, lambda } => {
let network_input = self.nodes.get_mut(network_input).unwrap(); let network_input = self.nodes.get_mut(network_input).unwrap();
network_input.populate_first_network_input(node_id, output_index, *offset); network_input.populate_first_network_input(node_id, output_index, *offset, lambda);
} }
NodeInput::Value { tagged_value, exposed } => { NodeInput::Value { tagged_value, exposed } => {
// Skip formatting very large values for seconds in performance speedup // Skip formatting very large values for seconds in performance speedup
@ -386,7 +397,7 @@ impl NodeNetwork {
assert!(!self.nodes.contains_key(&new_id)); assert!(!self.nodes.contains_key(&new_id));
self.nodes.insert(new_id, value_node); self.nodes.insert(new_id, value_node);
let network_input = self.nodes.get_mut(network_input).unwrap(); let network_input = self.nodes.get_mut(network_input).unwrap();
network_input.populate_first_network_input(new_id, 0, *offset); network_input.populate_first_network_input(new_id, 0, *offset, false);
} }
NodeInput::Network(_) => { NodeInput::Network(_) => {
*network_offsets.get_mut(network_input).unwrap() += 1; *network_offsets.get_mut(network_input).unwrap() += 1;
@ -403,6 +414,7 @@ impl NodeNetwork {
.map(|&NodeOutput { node_id, node_output_index }| NodeInput::Node { .map(|&NodeOutput { node_id, node_output_index }| NodeInput::Node {
node_id, node_id,
output_index: node_output_index, output_index: node_output_index,
lambda: false,
}) })
.collect(); .collect();
@ -660,7 +672,7 @@ mod test {
let reference = ProtoNode { let reference = ProtoNode {
identifier: "graphene_core::structural::ConsNode".into(), identifier: "graphene_core::structural::ConsNode".into(),
input: ProtoNodeInput::Network(concrete!(u32)), input: ProtoNodeInput::Network(concrete!(u32)),
construction_args: ConstructionArgs::Nodes(vec![0]), construction_args: ConstructionArgs::Nodes(vec![(0, false)]),
}; };
assert_eq!(proto_node, reference); assert_eq!(proto_node, reference);
} }
@ -675,7 +687,7 @@ mod test {
1, 1,
ProtoNode { ProtoNode {
identifier: "graphene_core::ops::IdNode".into(), identifier: "graphene_core::ops::IdNode".into(),
input: ProtoNodeInput::Node(11), input: ProtoNodeInput::Node(11, false),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
), ),
@ -684,14 +696,14 @@ mod test {
ProtoNode { ProtoNode {
identifier: "graphene_core::structural::ConsNode".into(), identifier: "graphene_core::structural::ConsNode".into(),
input: ProtoNodeInput::Network(concrete!(u32)), input: ProtoNodeInput::Network(concrete!(u32)),
construction_args: ConstructionArgs::Nodes(vec![14]), construction_args: ConstructionArgs::Nodes(vec![(14, false)]),
}, },
), ),
( (
11, 11,
ProtoNode { ProtoNode {
identifier: "graphene_core::ops::AddNode".into(), identifier: "graphene_core::ops::AddNode".into(),
input: ProtoNodeInput::Node(10), input: ProtoNodeInput::Node(10, false),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
), ),

View File

@ -43,7 +43,7 @@ impl core::fmt::Display for ProtoNetwork {
match &node.input { match &node.input {
ProtoNodeInput::None => f.write_str("None")?, ProtoNodeInput::None => f.write_str("None")?,
ProtoNodeInput::Network(ty) => f.write_fmt(format_args!("Network (type = {:?})", ty))?, ProtoNodeInput::Network(ty) => f.write_fmt(format_args!("Network (type = {:?})", ty))?,
ProtoNodeInput::Node(_) => f.write_str("Node")?, ProtoNodeInput::Node(_, _) => f.write_str("Node")?,
} }
f.write_str("\n")?; f.write_str("\n")?;
@ -54,7 +54,7 @@ impl core::fmt::Display for ProtoNetwork {
} }
ConstructionArgs::Nodes(nodes) => { ConstructionArgs::Nodes(nodes) => {
for id in nodes { for id in nodes {
write_node(f, network, *id, indent + 1)?; write_node(f, network, id.0, indent + 1)?;
} }
} }
} }
@ -71,7 +71,8 @@ impl core::fmt::Display for ProtoNetwork {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ConstructionArgs { pub enum ConstructionArgs {
Value(value::TaggedValue), Value(value::TaggedValue),
Nodes(Vec<NodeId>), // the bool indicates whether to treat the node as lambda node
Nodes(Vec<(NodeId, bool)>),
} }
impl PartialEq for ConstructionArgs { impl PartialEq for ConstructionArgs {
@ -101,7 +102,7 @@ impl Hash for ConstructionArgs {
impl ConstructionArgs { impl ConstructionArgs {
pub fn new_function_args(&self) -> Vec<String> { pub fn new_function_args(&self) -> Vec<String> {
match self { match self {
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("n{}", n)).collect(), ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("n{}", n.0)).collect(),
ConstructionArgs::Value(value) => vec![format!("{:?}", value)], ConstructionArgs::Value(value) => vec![format!("{:?}", value)],
} }
} }
@ -118,13 +119,14 @@ pub struct ProtoNode {
pub enum ProtoNodeInput { pub enum ProtoNodeInput {
None, None,
Network(Type), Network(Type),
Node(NodeId), // the bool indicates whether to treat the node as lambda node
Node(NodeId, bool),
} }
impl ProtoNodeInput { impl ProtoNodeInput {
pub fn unwrap_node(self) -> NodeId { pub fn unwrap_node(self) -> NodeId {
match self { match self {
ProtoNodeInput::Node(id) => id, ProtoNodeInput::Node(id, _) => id,
_ => panic!("tried to unwrap id from non node input \n node: {:#?}", self), _ => panic!("tried to unwrap id from non node input \n node: {:#?}", self),
} }
} }
@ -142,7 +144,7 @@ impl ProtoNode {
"network".hash(&mut hasher); "network".hash(&mut hasher);
ty.hash(&mut hasher); ty.hash(&mut hasher);
} }
ProtoNodeInput::Node(id) => id.hash(&mut hasher), ProtoNodeInput::Node(id, lambda) => (id, lambda).hash(&mut hasher),
}; };
Some(hasher.finish() as NodeId) Some(hasher.finish() as NodeId)
} }
@ -155,16 +157,18 @@ impl ProtoNode {
} }
} }
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) { pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId, skip_lambdas: bool) {
if let ProtoNodeInput::Node(id) = self.input { if let ProtoNodeInput::Node(id, lambda) = self.input {
self.input = ProtoNodeInput::Node(f(id)) if !(skip_lambdas && lambda) {
self.input = ProtoNodeInput::Node(f(id), lambda)
}
} }
if let ConstructionArgs::Nodes(ids) = &mut self.construction_args { if let ConstructionArgs::Nodes(ids) = &mut self.construction_args {
ids.iter_mut().for_each(|id| *id = f(*id)); ids.iter_mut().filter(|(_, lambda)| !(skip_lambdas && *lambda)).for_each(|(id, _)| *id = f(*id));
} }
} }
pub fn unwrap_construction_nodes(&self) -> Vec<NodeId> { pub fn unwrap_construction_nodes(&self) -> Vec<(NodeId, bool)> {
match &self.construction_args { match &self.construction_args {
ConstructionArgs::Nodes(nodes) => nodes.clone(), ConstructionArgs::Nodes(nodes) => nodes.clone(),
_ => panic!("tried to unwrap nodes from non node construction args \n node: {:#?}", self), _ => panic!("tried to unwrap nodes from non node construction args \n node: {:#?}", self),
@ -186,12 +190,12 @@ impl ProtoNetwork {
pub fn collect_outwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> { pub fn collect_outwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> {
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new(); let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (id, node) in &self.nodes { for (id, node) in &self.nodes {
if let ProtoNodeInput::Node(ref_id) = &node.input { if let ProtoNodeInput::Node(ref_id, _) = &node.input {
self.check_ref(ref_id, id); self.check_ref(ref_id, id);
edges.entry(*ref_id).or_default().push(*id) edges.entry(*ref_id).or_default().push(*id)
} }
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args { if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
for ref_id in ref_nodes { for (ref_id, _) in ref_nodes {
self.check_ref(ref_id, id); self.check_ref(ref_id, id);
edges.entry(*ref_id).or_default().push(*id) edges.entry(*ref_id).or_default().push(*id)
} }
@ -210,7 +214,7 @@ impl ProtoNetwork {
let mut lookup = self.nodes.iter().map(|(id, _)| (*id, *id)).collect::<HashMap<_, _>>(); let mut lookup = self.nodes.iter().map(|(id, _)| (*id, *id)).collect::<HashMap<_, _>>();
if let Some(sni) = self.nodes[index].1.stable_node_id() { if let Some(sni) = self.nodes[index].1.stable_node_id() {
lookup.insert(self.nodes[index].0, sni); lookup.insert(self.nodes[index].0, sni);
self.replace_node_references(&lookup); self.replace_node_references(&lookup, false);
self.nodes[index].0 = sni; self.nodes[index].0 = sni;
sni sni
} else { } else {
@ -221,12 +225,12 @@ impl ProtoNetwork {
pub fn collect_inwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> { pub fn collect_inwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> {
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new(); let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (id, node) in &self.nodes { for (id, node) in &self.nodes {
if let ProtoNodeInput::Node(ref_id) = &node.input { if let ProtoNodeInput::Node(ref_id, _) = &node.input {
self.check_ref(ref_id, id); self.check_ref(ref_id, id);
edges.entry(*id).or_default().push(*ref_id) edges.entry(*id).or_default().push(*ref_id)
} }
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args { if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
for ref_id in ref_nodes { for (ref_id, _) in ref_nodes {
self.check_ref(ref_id, id); self.check_ref(ref_id, id);
edges.entry(*id).or_default().push(*ref_id) edges.entry(*id).or_default().push(*ref_id)
} }
@ -248,21 +252,22 @@ impl ProtoNetwork {
let resolved_lookup = resolved.clone(); let resolved_lookup = resolved.clone();
if let Some((input_node, id, input)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| { if let Some((input_node, id, input)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| {
if let ProtoNodeInput::Node(input_node) = node.input { if let ProtoNodeInput::Node(input_node, false) = node.input {
resolved.insert(*id); resolved.insert(*id);
let pre_node_input = inputs.get(input_node as usize).expect("input node should exist"); let pre_node_input = inputs.get(input_node as usize).expect("input node should exist");
Some((input_node, *id, pre_node_input.clone())) Some((input_node, *id, pre_node_input.clone()))
} else { } else {
resolved.insert(*id);
None None
} }
}) { }) {
lookup.insert(id, compose_node_id); lookup.insert(id, compose_node_id);
self.replace_node_references(&lookup); self.replace_node_references(&lookup, true);
self.nodes.push(( self.nodes.push((
compose_node_id, compose_node_id,
ProtoNode { ProtoNode {
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"), identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
construction_args: ConstructionArgs::Nodes(vec![input_node, id]), construction_args: ConstructionArgs::Nodes(vec![(input_node, false), (id, true)]),
input, input,
}, },
)); ));
@ -338,13 +343,13 @@ impl ProtoNetwork {
(pos as NodeId, node) (pos as NodeId, node)
}) })
.collect(); .collect();
self.replace_node_references(&lookup); self.replace_node_references(&lookup, false);
assert_eq!(order.len(), self.nodes.len()); assert_eq!(order.len(), self.nodes.len());
} }
fn replace_node_references(&mut self, lookup: &HashMap<u64, u64>) { fn replace_node_references(&mut self, lookup: &HashMap<u64, u64>, skip_lambdas: bool) {
self.nodes.iter_mut().for_each(|(_, node)| { self.nodes.iter_mut().for_each(|(_, node)| {
node.map_ids(|id| *lookup.get(&id).expect("node not found in lookup table")); node.map_ids(|id| *lookup.get(&id).expect("node not found in lookup table"), skip_lambdas);
}); });
self.inputs = self.inputs.iter().filter_map(|id| lookup.get(id).copied()).collect(); self.inputs = self.inputs.iter().filter_map(|id| lookup.get(id).copied()).collect();
self.output = *lookup.get(&self.output).unwrap(); self.output = *lookup.get(&self.output).unwrap();
@ -403,7 +408,7 @@ impl TypingContext {
// If the node has nodes as parameters we can infer the types from the node outputs // If the node has nodes as parameters we can infer the types from the node outputs
ConstructionArgs::Nodes(ref nodes) => nodes ConstructionArgs::Nodes(ref nodes) => nodes
.iter() .iter()
.map(|id| { .map(|(id, _)| {
self.inferred self.inferred
.get(id) .get(id)
.ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context")) .ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context"))
@ -416,7 +421,7 @@ impl TypingContext {
let input = match node.input { let input = match node.input {
ProtoNodeInput::None => concrete!(()), ProtoNodeInput::None => concrete!(()),
ProtoNodeInput::Network(ref ty) => ty.clone(), ProtoNodeInput::Network(ref ty) => ty.clone(),
ProtoNodeInput::Node(id) => { ProtoNodeInput::Node(id, _) => {
let input = self let input = self
.inferred .inferred
.get(&id) .get(&id)
@ -573,7 +578,7 @@ mod test {
println!("{:#?}", construction_network); println!("{:#?}", construction_network);
assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value"); assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value");
assert_eq!(construction_network.nodes.len(), 6); assert_eq!(construction_network.nodes.len(), 6);
assert_eq!(construction_network.nodes[5].1.construction_args, ConstructionArgs::Nodes(vec![3, 4])); assert_eq!(construction_network.nodes[5].1.construction_args, ConstructionArgs::Nodes(vec![(3, false), (4, true)]));
} }
#[test] #[test]
@ -589,11 +594,11 @@ mod test {
ids, ids,
vec![ vec![
15907139529964845467, 15907139529964845467,
14192092348022507362, 1552706903207877482,
14714934190542167928, 15211082859148708110,
4518275895314664278, 3361684226823984981,
13912679582583718470, 16609475913638361514,
3236993912700824422 5640155373642511298
] ]
); );
} }
@ -607,7 +612,7 @@ mod test {
7, 7,
ProtoNode { ProtoNode {
identifier: "id".into(), identifier: "id".into(),
input: ProtoNodeInput::Node(11), input: ProtoNodeInput::Node(11, false),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
), ),
@ -615,7 +620,7 @@ mod test {
1, 1,
ProtoNode { ProtoNode {
identifier: "id".into(), identifier: "id".into(),
input: ProtoNodeInput::Node(11), input: ProtoNodeInput::Node(11, false),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
), ),
@ -624,14 +629,14 @@ mod test {
ProtoNode { ProtoNode {
identifier: "cons".into(), identifier: "cons".into(),
input: ProtoNodeInput::Network(concrete!(u32)), input: ProtoNodeInput::Network(concrete!(u32)),
construction_args: ConstructionArgs::Nodes(vec![14]), construction_args: ConstructionArgs::Nodes(vec![(14, false)]),
}, },
), ),
( (
11, 11,
ProtoNode { ProtoNode {
identifier: "add".into(), identifier: "add".into(),
input: ProtoNodeInput::Node(10), input: ProtoNodeInput::Node(10, false),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
), ),

View File

@ -40,6 +40,28 @@ impl<_I, _O, S0> DynAnyRefNode<_I, _O, S0> {
Self { node, _i: core::marker::PhantomData } Self { node, _i: core::marker::PhantomData }
} }
} }
pub struct DynAnyInRefNode<I, O, Node> {
node: Node,
_i: PhantomData<(I, O)>,
}
impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<'input, Any<'input>> for DynAnyInRefNode<_I, _O, N>
where
N: for<'any_input> Node<'any_input, &'any_input _I, Output = _O>,
{
type Output = Any<'input>;
fn eval<'node: 'input>(&'node self, input: Any<'input>) -> Self::Output {
{
let node_name = core::any::type_name::<N>();
let input: Box<&_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyNode Input, {e} in:\n{node_name}"));
Box::new(self.node.eval(*input))
}
}
}
impl<_I, _O, S0> DynAnyInRefNode<_I, _O, S0> {
pub const fn new(node: S0) -> Self {
Self { node, _i: core::marker::PhantomData }
}
}
pub trait IntoTypeErasedNode<'n> { pub trait IntoTypeErasedNode<'n> {
fn into_type_erased(self) -> TypeErasedPinned<'n>; fn into_type_erased(self) -> TypeErasedPinned<'n>;

View File

@ -1,3 +1,5 @@
use std::marker::PhantomData;
use graphene_core::Node; use graphene_core::Node;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -21,3 +23,71 @@ impl<T> CacheNode<T> {
CacheNode { cache: OnceCell::new() } CacheNode { cache: OnceCell::new() }
} }
} }
/// Caches the output of a given Node and acts as a proxy
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LetNode<T> {
cache: OnceCell<T>,
}
impl<'i, T: 'i> Node<'i, Option<T>> for LetNode<T> {
type Output = &'i T;
fn eval<'s: 'i>(&'s self, input: Option<T>) -> Self::Output {
match input {
Some(input) => {
self.cache.set(input).unwrap_or_else(|_| error!("Let node was set twice but is not mutable"));
self.cache.get().unwrap()
}
None => self.cache.get().expect("Let node was not initialized"),
}
}
}
impl<T> LetNode<T> {
pub const fn new() -> LetNode<T> {
LetNode { cache: OnceCell::new() }
}
}
/// Caches the output of a given Node and acts as a proxy
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EndLetNode<Input> {
input: Input,
}
impl<'i, T: 'i, Input> Node<'i, &'i T> for EndLetNode<Input>
where
Input: Node<'i, ()>,
{
type Output = <Input>::Output;
fn eval<'s: 'i>(&'s self, _: &'i T) -> Self::Output {
self.input.eval(())
}
}
impl<Input> EndLetNode<Input> {
pub const fn new(input: Input) -> EndLetNode<Input> {
EndLetNode { input }
}
}
pub use graphene_core::ops::SomeNode as InitNode;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct RefNode<T, Let> {
let_node: Let,
_t: PhantomData<T>,
}
impl<'i, T: 'i, Let> Node<'i, ()> for RefNode<T, Let>
where
Let: for<'a> Node<'a, Option<T>, Output = &'a T>,
{
type Output = &'i T;
fn eval<'s: 'i>(&'s self, _: ()) -> Self::Output {
self.let_node.eval(None)
}
}
impl<Let, T> RefNode<T, Let> {
pub const fn new(let_node: Let) -> RefNode<T, Let> {
RefNode { let_node, _t: PhantomData }
}
}

View File

@ -131,13 +131,14 @@ pub struct BlendImageNode<Second, MapFn> {
map_fn: MapFn, map_fn: MapFn,
} }
// TODO: Implement proper blending
#[node_macro::node_fn(BlendImageNode)] #[node_macro::node_fn(BlendImageNode)]
fn blend_image<MapFn>(image: Image, second: Image, map_fn: &'any_input MapFn) -> Image fn blend_image<MapFn>(image: ImageFrame, second: ImageFrame, map_fn: &'any_input MapFn) -> ImageFrame
where where
MapFn: for<'any_input> Node<'any_input, (Color, Color), Output = Color> + 'input, MapFn: for<'any_input> Node<'any_input, (Color, Color), Output = Color> + 'input,
{ {
let mut image = image; let mut image = image;
for (pixel, sec_pixel) in &mut image.data.iter_mut().zip(second.data.iter()) { for (pixel, sec_pixel) in &mut image.image.data.iter_mut().zip(second.image.data.iter()) {
*pixel = map_fn.eval((*pixel, *sec_pixel)); *pixel = map_fn.eval((*pixel, *sec_pixel));
} }
image image

View File

@ -152,6 +152,7 @@ impl BorrowTree {
self.store_node(Arc::new(node), id); self.store_node(Arc::new(node), id);
} }
ConstructionArgs::Nodes(ids) => { ConstructionArgs::Nodes(ids) => {
let ids: Vec<_> = ids.iter().map(|(id, _)| *id).collect();
let construction_nodes = self.node_refs(&ids); let construction_nodes = self.node_refs(&ids);
let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?; let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?;
let node = constructor(construction_nodes); let node = constructor(construction_nodes);

View File

@ -10,14 +10,14 @@ use graphene_core::structural::Then;
use graphene_core::value::{ClonedNode, ForgetNode, ValueNode}; use graphene_core::value::{ClonedNode, ForgetNode, ValueNode};
use graphene_core::{Node, NodeIO, NodeIOTypes}; use graphene_core::{Node, NodeIO, NodeIOTypes};
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode, TypeErasedPinnedRef}; use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyInRefNode, DynAnyNode, DynAnyRefNode, IntoTypeErasedNode, TypeErasedPinnedRef};
use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor}; use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor};
use graph_craft::proto::NodeConstructor; use graph_craft::proto::NodeConstructor;
use graphene_core::{concrete, generic}; use graphene_core::{concrete, generic};
use graphene_std::memo::CacheNode; use graphene_std::memo::{CacheNode, LetNode};
use crate::executor::NodeContainer; use crate::executor::NodeContainer;
@ -122,7 +122,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]), register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]),
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []), register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
register_node!(graphene_core::ops::AddNode, input: (u32, &u32), params: []), register_node!(graphene_core::ops::AddNode, input: (u32, &u32), params: []),
register_node!(graphene_core::ops::CloneNode<_>, input: &Image, params: []), register_node!(graphene_core::ops::CloneNode<_>, input: &ImageFrame, params: []),
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]), register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]), register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]), register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
@ -131,6 +131,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
register_node!(graphene_core::ops::SomeNode, input: ImageFrame, params: []),
vec![( vec![(
NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"), NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|args| { |args| {
@ -146,19 +147,19 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIdentifier::new("graphene_core::raster::BlendNode<_, _, _, _>"), NodeIdentifier::new("graphene_core::raster::BlendNode<_, _, _, _>"),
|args| { |args| {
use graphene_core::Node; use graphene_core::Node;
let image: DowncastBothNode<(), Image> = DowncastBothNode::new(args[0]); let image: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]);
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]); let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]);
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]); let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]);
let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(blend_mode.eval(())), ClonedNode::new(opacity.eval(()))); let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(blend_mode.eval(())), ClonedNode::new(opacity.eval(())));
let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node)); let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node));
let _ = &node as &dyn for<'i> Node<'i, Image, Output = Image>; let _ = &node as &dyn for<'i> Node<'i, ImageFrame, Output = ImageFrame>;
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); let any: DynAnyNode<ImageFrame, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased() any.into_type_erased()
}, },
NodeIOTypes::new( NodeIOTypes::new(
concrete!(Image), concrete!(ImageFrame),
concrete!(Image), concrete!(ImageFrame),
vec![(concrete!(()), concrete!(Image)), (concrete!(()), concrete!(BlendMode)), (concrete!(()), concrete!(f64))], vec![(concrete!(()), concrete!(ImageFrame)), (concrete!(()), concrete!(BlendMode)), (concrete!(()), concrete!(f64))],
), ),
)], )],
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]), raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]),
@ -171,6 +172,35 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]), raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]), raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
vec![ vec![
(
NodeIdentifier::new("graphene_std::memo::LetNode<_>"),
|_| {
let node: LetNode<ImageFrame> = graphene_std::memo::LetNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
},
NodeIOTypes::new(concrete!(Option<ImageFrame>), concrete!(&ImageFrame), vec![]),
),
(
NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"),
|args| {
let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<ImageFrame, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
any.into_type_erased()
},
NodeIOTypes::new(generic!(T), concrete!(ImageFrame), vec![(concrete!(()), concrete!(ImageFrame))]),
),
(
NodeIdentifier::new("graphene_std::memo::RefNode<_, _>"),
|args| {
let map_fn: DowncastBothRefNode<Option<ImageFrame>, ImageFrame> = DowncastBothRefNode::new(args[0]);
let node = graphene_std::memo::RefNode::new(map_fn);
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
},
NodeIOTypes::new(concrete!(()), concrete!(&ImageFrame), vec![]),
),
( (
NodeIdentifier::new("graphene_core::structural::MapImageNode"), NodeIdentifier::new("graphene_core::structural::MapImageNode"),
|args| { |args| {
@ -257,7 +287,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIdentifier::new("graphene_std::memo::CacheNode"), NodeIdentifier::new("graphene_std::memo::CacheNode"),
|_| { |_| {
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new(); let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node); let any = DynAnyRefNode::new(node);
any.into_type_erased() any.into_type_erased()
}, },
NodeIOTypes::new(concrete!(Image), concrete!(&Image), vec![]), NodeIOTypes::new(concrete!(Image), concrete!(&Image), vec![]),