mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 14:38:13 +08:00
WIP: Thumbnails
This commit is contained in:
+15
-25
@@ -44,8 +44,11 @@ impl DispatcherMessageHandlers {
|
|||||||
/// In addition, these messages do not change any state in the backend (aside from caches).
|
/// In addition, these messages do not change any state in the backend (aside from caches).
|
||||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::CompileActiveDocument),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::CompileActiveDocument),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::EvaluateActiveDocument),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::EvaluateActiveDocumentWithThumbnails),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::IntrospectActiveDocument),
|
// MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::IntrospectActiveDocument),
|
||||||
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||||
|
PropertiesPanelMessageDiscriminant::Refresh,
|
||||||
|
))),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||||
PropertiesPanelMessageDiscriminant::Refresh,
|
PropertiesPanelMessageDiscriminant::Refresh,
|
||||||
))),
|
))),
|
||||||
@@ -123,13 +126,13 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all messages to the queue if queuing messages (except from the end queue message)
|
// // Add all messages to the queue if queuing messages (except from the end queue message)
|
||||||
if !matches!(message, Message::EndIntrospectionQueue) {
|
// if !matches!(message, Message::EndIntrospectionQueue) {
|
||||||
if self.queueing_introspection_messages {
|
// if self.queueing_introspection_messages {
|
||||||
self.introspection_queue.push(message);
|
// self.introspection_queue.push(message);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// Print the message at a verbosity level of `info`
|
// Print the message at a verbosity level of `info`
|
||||||
self.log_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
self.log_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
||||||
|
|
||||||
@@ -144,9 +147,10 @@ impl Dispatcher {
|
|||||||
Message::EndEvaluationQueue => {
|
Message::EndEvaluationQueue => {
|
||||||
self.queueing_evaluation_messages = false;
|
self.queueing_evaluation_messages = false;
|
||||||
}
|
}
|
||||||
Message::ProcessEvaluationQueue(render_output_metadata) => {
|
Message::ProcessEvaluationQueue(render_output_metadata, introspected_nodes) => {
|
||||||
let update_message = PortfolioMessage::ProcessEvaluationResponse {
|
let update_message = PortfolioMessage::ProcessEvaluationResponse {
|
||||||
evaluation_metadata: render_output_metadata,
|
evaluation_metadata: render_output_metadata,
|
||||||
|
introspected_nodes,
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
// Update the state with the render output and introspected inputs
|
// Update the state with the render output and introspected inputs
|
||||||
@@ -154,23 +158,9 @@ impl Dispatcher {
|
|||||||
|
|
||||||
// Schedule all queued messages to be run, which use the introspected inputs (in the order they were added)
|
// Schedule all queued messages to be run, which use the introspected inputs (in the order they were added)
|
||||||
Self::schedule_execution(&mut self.message_queues, true, std::mem::take(&mut self.evaluation_queue));
|
Self::schedule_execution(&mut self.message_queues, true, std::mem::take(&mut self.evaluation_queue));
|
||||||
}
|
|
||||||
Message::StartIntrospectionQueue => {
|
|
||||||
self.queueing_introspection_messages = true;
|
|
||||||
}
|
|
||||||
Message::EndIntrospectionQueue => {
|
|
||||||
self.queueing_introspection_messages = false;
|
|
||||||
}
|
|
||||||
Message::ProcessIntrospectionQueue(introspection_response) => {
|
|
||||||
let update_message = PortfolioMessage::ProcessIntrospectionResponse { introspection_response }.into();
|
|
||||||
// Update the state with the render output and introspected inputs
|
|
||||||
Self::schedule_execution(&mut self.message_queues, true, [update_message]);
|
|
||||||
|
|
||||||
// Schedule all queued messages to be run, which use the introspected inputs (in the order they were added)
|
|
||||||
Self::schedule_execution(&mut self.message_queues, true, std::mem::take(&mut self.introspection_queue));
|
|
||||||
|
|
||||||
|
// Clear all introspected data after the queued messages are execucted
|
||||||
let clear_message = PortfolioMessage::ClearIntrospectedData.into();
|
let clear_message = PortfolioMessage::ClearIntrospectedData.into();
|
||||||
// Clear the introspected inputs since they are no longer required, and will cause a memory leak if not removed
|
|
||||||
Self::schedule_execution(&mut self.message_queues, true, [clear_message]);
|
Self::schedule_execution(&mut self.message_queues, true, [clear_message]);
|
||||||
}
|
}
|
||||||
Message::NoOp => {}
|
Message::NoOp => {}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
|||||||
}
|
}
|
||||||
AnimationMessage::SetFrameIndex { frame } => {
|
AnimationMessage::SetFrameIndex { frame } => {
|
||||||
self.frame_index = frame;
|
self.frame_index = frame;
|
||||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||||
// Update the restart and pause/play buttons
|
// Update the restart and pause/play buttons
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
|||||||
}
|
}
|
||||||
AnimationMessage::UpdateTime => {
|
AnimationMessage::UpdateTime => {
|
||||||
if self.is_playing() {
|
if self.is_playing() {
|
||||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||||
|
|
||||||
if self.live_preview_recently_zero {
|
if self.live_preview_recently_zero {
|
||||||
// Update the restart and pause/play buttons
|
// Update the restart and pause/play buttons
|
||||||
@@ -116,7 +116,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
|||||||
_ => AnimationState::Stopped,
|
_ => AnimationState::Stopped,
|
||||||
};
|
};
|
||||||
self.live_preview_recently_zero = true;
|
self.live_preview_recently_zero = true;
|
||||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||||
// Update the restart and pause/play buttons
|
// Update the restart and pause/play buttons
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -31,8 +31,8 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
|||||||
}
|
}
|
||||||
responses.add(Message::StartEvaluationQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||||
responses.add(Message::EndEvaluationQueue);
|
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
responses.add(DocumentMessage::DeselectAllLayers);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
|
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
use crate::messages::portfolio::document::node_graph::utility_types::{
|
||||||
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, Transform,
|
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeSNIUpdate, FrontendNodeType, Transform,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate, WireSNIUpdate};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::HintData;
|
use crate::messages::tool::utility_types::HintData;
|
||||||
use graphene_std::uuid::NodeId;
|
|
||||||
use graphene_std::raster::color::Color;
|
use graphene_std::raster::color::Color;
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
|
use graphene_std::uuid::{NodeId, SNI};
|
||||||
|
|
||||||
#[impl_message(Message, Frontend)]
|
#[impl_message(Message, Frontend)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
@@ -128,6 +128,10 @@ pub enum FrontendMessage {
|
|||||||
#[serde(rename = "box")]
|
#[serde(rename = "box")]
|
||||||
box_selection: Option<BoxSelection>,
|
box_selection: Option<BoxSelection>,
|
||||||
},
|
},
|
||||||
|
UpdateContextDuringEvaluation {
|
||||||
|
#[serde(rename = "contextDuringEvaluation")]
|
||||||
|
context_during_evaluation: Vec<(SNI, usize, String)>,
|
||||||
|
},
|
||||||
UpdateContextMenuInformation {
|
UpdateContextMenuInformation {
|
||||||
#[serde(rename = "contextMenuInformation")]
|
#[serde(rename = "contextMenuInformation")]
|
||||||
context_menu_information: Option<ContextMenuInformation>,
|
context_menu_information: Option<ContextMenuInformation>,
|
||||||
@@ -224,6 +228,10 @@ pub enum FrontendMessage {
|
|||||||
#[serde(rename = "setColorChoice")]
|
#[serde(rename = "setColorChoice")]
|
||||||
set_color_choice: Option<String>,
|
set_color_choice: Option<String>,
|
||||||
},
|
},
|
||||||
|
UpdateGraphBreadcrumbPath {
|
||||||
|
#[serde(rename = "breadcrumbPath")]
|
||||||
|
breadcrumb_path: Vec<NodeId>,
|
||||||
|
},
|
||||||
UpdateGraphFadeArtwork {
|
UpdateGraphFadeArtwork {
|
||||||
percentage: f64,
|
percentage: f64,
|
||||||
},
|
},
|
||||||
@@ -263,7 +271,7 @@ pub enum FrontendMessage {
|
|||||||
UpdateNodeGraphWires {
|
UpdateNodeGraphWires {
|
||||||
wires: Vec<WirePathUpdate>,
|
wires: Vec<WirePathUpdate>,
|
||||||
},
|
},
|
||||||
ClearAllNodeGraphWires,
|
ClearAllNodeGraphWirePaths,
|
||||||
UpdateNodeGraphControlBarLayout {
|
UpdateNodeGraphControlBarLayout {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
@@ -287,7 +295,10 @@ pub enum FrontendMessage {
|
|||||||
UpdateThumbnails {
|
UpdateThumbnails {
|
||||||
add: Vec<(NodeId, String)>,
|
add: Vec<(NodeId, String)>,
|
||||||
clear: Vec<NodeId>,
|
clear: Vec<NodeId>,
|
||||||
// remove: Vec<NodeId>,
|
#[serde(rename = "wireSNIUpdates")]
|
||||||
|
wire_sni_updates: Vec<WireSNIUpdate>,
|
||||||
|
#[serde(rename = "layerSNIUpdates")]
|
||||||
|
layer_sni_updates: Vec<FrontendNodeSNIUpdate>,
|
||||||
},
|
},
|
||||||
UpdateToolOptionsLayout {
|
UpdateToolOptionsLayout {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
|
|||||||
@@ -13,12 +13,7 @@ pub enum Message {
|
|||||||
EndEvaluationQueue,
|
EndEvaluationQueue,
|
||||||
// Processes all messages that are queued to be run after evaluation, which occurs on the evaluation response. This allows a message to be run with data from after the evaluation is complete
|
// Processes all messages that are queued to be run after evaluation, which occurs on the evaluation response. This allows a message to be run with data from after the evaluation is complete
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata),
|
ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata, IntrospectionResponse),
|
||||||
StartIntrospectionQueue,
|
|
||||||
EndIntrospectionQueue,
|
|
||||||
// Processes all messages that are queued to be run after introspection, which occurs on the evaluation response. This allows a message to be run with data from after the evaluation is complete
|
|
||||||
#[serde(skip)]
|
|
||||||
ProcessIntrospectionQueue(IntrospectionResponse),
|
|
||||||
#[child]
|
#[child]
|
||||||
Animation(AnimationMessage),
|
Animation(AnimationMessage),
|
||||||
#[child]
|
#[child]
|
||||||
|
|||||||
@@ -442,7 +442,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
DocumentMessage::EnterNestedNetwork { node_id } => {
|
DocumentMessage::EnterNestedNetwork { node_id } => {
|
||||||
self.breadcrumb_network_path.push(node_id);
|
self.breadcrumb_network_path.push(node_id);
|
||||||
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
@@ -472,7 +472,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
self.breadcrumb_network_path.pop();
|
self.breadcrumb_network_path.pop();
|
||||||
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
||||||
}
|
}
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
@@ -539,7 +539,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
responses.add(DocumentMessage::RenderScrollbars);
|
||||||
if opened {
|
if opened {
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||||
}
|
}
|
||||||
if open {
|
if open {
|
||||||
responses.add(ToolMessage::DeactivateTools);
|
responses.add(ToolMessage::DeactivateTools);
|
||||||
@@ -1424,7 +1424,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
center: Key::Alt,
|
center: Key::Alt,
|
||||||
duplicate: Key::Alt,
|
duplicate: Key::Alt,
|
||||||
}));
|
}));
|
||||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||||
} else {
|
} else {
|
||||||
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
|
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
|
||||||
return;
|
return;
|
||||||
@@ -1440,7 +1440,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
responses.add(NodeGraphMessage::UpdateEdges);
|
responses.add(NodeGraphMessage::UpdateEdges);
|
||||||
responses.add(NodeGraphMessage::UpdateBoxSelection);
|
responses.add(NodeGraphMessage::UpdateBoxSelection);
|
||||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||||
|
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||||
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
||||||
transform: Transform {
|
transform: Transform {
|
||||||
scale: transform.matrix2.x_axis.x,
|
scale: transform.matrix2.x_axis.x,
|
||||||
@@ -1909,10 +1910,7 @@ impl DocumentMessageHandler {
|
|||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
responses.add(Message::StartEvaluationQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
|
||||||
responses.add(Message::EndEvaluationQueue);
|
responses.add(Message::EndEvaluationQueue);
|
||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
@@ -1944,8 +1942,8 @@ impl DocumentMessageHandler {
|
|||||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
responses.add(Message::EndEvaluationQueue);
|
responses.add(Message::EndEvaluationQueue);
|
||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
@@ -2576,7 +2574,7 @@ impl DocumentMessageHandler {
|
|||||||
layout: Layout::WidgetLayout(document_bar_layout),
|
layout: Layout::WidgetLayout(document_bar_layout),
|
||||||
layout_target: LayoutTarget::DocumentBar,
|
layout_target: LayoutTarget::DocumentBar,
|
||||||
});
|
});
|
||||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_layers_panel_control_bar_widgets(&self, responses: &mut VecDeque<Message>) {
|
pub fn update_layers_panel_control_bar_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||||
|
|||||||
@@ -138,87 +138,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
description: Cow::Borrowed("A default node network you can use to create your own custom nodes."),
|
description: Cow::Borrowed("A default node network you can use to create your own custom nodes."),
|
||||||
properties: None,
|
properties: None,
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
|
||||||
identifier: "Cache",
|
|
||||||
category: "General",
|
|
||||||
node_template: NodeTemplate {
|
|
||||||
document_node: DocumentNode {
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
|
||||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
|
||||||
nodes: [
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::network(generic!(T), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
inputs: vec![NodeInput::value(TaggedValue::None, true)],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
|
||||||
network_metadata: Some(NodeNetworkMetadata {
|
|
||||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
|
||||||
node_metadata: [
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Memoize".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Freeze Real Time".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Boundless Footprint".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
input_metadata: vec![("Data", "TODO").into()],
|
|
||||||
output_names: vec!["Data".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: Cow::Borrowed("TODO"),
|
|
||||||
properties: None,
|
|
||||||
},
|
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
identifier: "Merge",
|
identifier: "Merge",
|
||||||
category: "General",
|
category: "General",
|
||||||
@@ -529,21 +448,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
exports: vec![NodeInput::node(NodeId(0), 0)],
|
||||||
nodes: [
|
nodes: [DocumentNode {
|
||||||
DocumentNode {
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
inputs: vec![NodeInput::scope("editor-api")],
|
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
skip_deduplication: true,
|
||||||
skip_deduplication: true,
|
cache_output: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}]
|
||||||
DocumentNode {
|
|
||||||
manual_composition: Some(concrete!(Context)),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
.map(|(id, node)| (NodeId(id as u64), node))
|
||||||
@@ -556,24 +468,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
output_names: vec!["Image".to_string()],
|
output_names: vec!["Image".to_string()],
|
||||||
network_metadata: Some(NodeNetworkMetadata {
|
network_metadata: Some(NodeNetworkMetadata {
|
||||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||||
node_metadata: [
|
node_metadata: [DocumentNodeMetadata {
|
||||||
DocumentNodeMetadata {
|
persistent_metadata: DocumentNodePersistentMetadata {
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
display_name: "Create Canvas".to_string(),
|
||||||
display_name: "Create Canvas".to_string(),
|
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeMetadata {
|
..Default::default()
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
}]
|
||||||
display_name: "Cache".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
.map(|(id, node)| (NodeId(id as u64), node))
|
||||||
@@ -595,19 +497,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![NodeInput::scope("editor-api")],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
||||||
manual_composition: Some(concrete!(Context)),
|
manual_composition: Some(concrete!(Context)),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
..Default::default()
|
cache_output: true,
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
manual_composition: Some(concrete!(Context)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
@@ -650,14 +547,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Cache".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 2)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
DocumentNodeMetadata {
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
persistent_metadata: DocumentNodePersistentMetadata {
|
||||||
display_name: "Rasterize".to_string(),
|
display_name: "Rasterize".to_string(),
|
||||||
@@ -1408,33 +1297,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(3), 0)],
|
exports: vec![NodeInput::node(NodeId(0), 0)],
|
||||||
nodes: vec![
|
nodes: vec![DocumentNode {
|
||||||
DocumentNode {
|
inputs: vec![NodeInput::network(concrete!(VectorDataTable), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
|
||||||
inputs: vec![NodeInput::network(concrete!(VectorDataTable), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
|
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
|
manual_composition: Some(generic!(T)),
|
||||||
manual_composition: Some(generic!(T)),
|
cache_output: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}]
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(2), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
.map(|(id, node)| (NodeId(id as u64), node))
|
||||||
@@ -1450,40 +1320,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||||
network_metadata: Some(NodeNetworkMetadata {
|
network_metadata: Some(NodeNetworkMetadata {
|
||||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||||
node_metadata: [
|
node_metadata: [DocumentNodeMetadata {
|
||||||
DocumentNodeMetadata {
|
persistent_metadata: DocumentNodePersistentMetadata {
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
display_name: "Boolean Operation".to_string(),
|
||||||
display_name: "Boolean Operation".to_string(),
|
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeMetadata {
|
..Default::default()
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
}]
|
||||||
display_name: "Memoize".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Freeze Real Time".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Boundless Footprint".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(21, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
.map(|(id, node)| (NodeId(id as u64), node))
|
||||||
@@ -1506,7 +1350,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(4), 0)],
|
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0)],
|
inputs: vec![NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0)],
|
||||||
@@ -1526,24 +1370,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
NodeInput::node(NodeId(0), 0),
|
NodeInput::node(NodeId(0), 0),
|
||||||
],
|
],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(vector::sample_polyline::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(vector::sample_polyline::IDENTIFIER),
|
||||||
manual_composition: Some(generic!(T)),
|
cache_output: true,
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(2), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(3), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
manual_composition: Some(generic!(T)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1585,30 +1412,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Memoize".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Freeze Real Time".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(21, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Boundless Footprint".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(28, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -1671,96 +1474,17 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
category: "Vector: Modifier",
|
category: "Vector: Modifier",
|
||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
|
||||||
exports: vec![NodeInput::node(NodeId(3), 0)],
|
|
||||||
nodes: [
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![
|
|
||||||
NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0),
|
|
||||||
NodeInput::network(concrete!(f64), 1),
|
|
||||||
NodeInput::network(concrete!(u32), 2),
|
|
||||||
],
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(vector::poisson_disk_points::IDENTIFIER),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(2), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorDataTable::default()), true),
|
NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0),
|
||||||
NodeInput::value(TaggedValue::F64(10.), false),
|
NodeInput::network(concrete!(f64), 1),
|
||||||
NodeInput::value(TaggedValue::U32(0), false),
|
NodeInput::network(concrete!(u32), 2),
|
||||||
],
|
],
|
||||||
|
cache_output: true,
|
||||||
|
manual_composition: Some(generic!(T)),
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode(vector::poisson_disk_points::IDENTIFIER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||||
network_metadata: Some(NodeNetworkMetadata {
|
|
||||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
|
||||||
node_metadata: [
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Poisson-Disk Points".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Memoize".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Freeze Real Time".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Boundless Footprint".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(21, 0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
input_metadata: vec![
|
input_metadata: vec![
|
||||||
("Vector Data", "TODO").into(),
|
("Vector Data", "TODO").into(),
|
||||||
InputMetadata::with_name_description_override(
|
InputMetadata::with_name_description_override(
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ pub enum NodeGraphMessage {
|
|||||||
},
|
},
|
||||||
SendClickTargets,
|
SendClickTargets,
|
||||||
EndSendClickTargets,
|
EndSendClickTargets,
|
||||||
UnloadWires,
|
UnloadWirePaths,
|
||||||
SendWires,
|
SendWirePaths,
|
||||||
UpdateVisibleNodes,
|
UpdateVisibleNodes,
|
||||||
SendGraph,
|
SendGraph,
|
||||||
SetGridAlignedEdges,
|
SetGridAlignedEdges,
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ use crate::messages::layout::utility_types::widget_prelude::*;
|
|||||||
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType};
|
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, FrontendNodeSNIUpdate};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, Previewing, TypeSource};
|
use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, Previewing, TypeSource};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
|
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
|
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, WireSNIUpdate, build_vector_wire};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
|
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
|
||||||
@@ -84,7 +84,7 @@ pub struct NodeGraphMessageHandler {
|
|||||||
/// The end index of the moved port
|
/// The end index of the moved port
|
||||||
end_index: Option<usize>,
|
end_index: Option<usize>,
|
||||||
/// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend
|
/// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend
|
||||||
frontend_nodes: Vec<NodeId>,
|
pub frontend_nodes: Vec<NodeId>,
|
||||||
/// Used to keep track of what wires are sent to the front end so the old ones can be removed
|
/// Used to keep track of what wires are sent to the front end so the old ones can be removed
|
||||||
frontend_wires: HashSet<(NodeId, usize)>,
|
frontend_wires: HashSet<(NodeId, usize)>,
|
||||||
}
|
}
|
||||||
@@ -918,7 +918,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
wire_in_progress_to_connector,
|
wire_in_progress_to_connector,
|
||||||
from_connector_is_layer,
|
from_connector_is_layer,
|
||||||
to_connector_is_layer,
|
to_connector_is_layer,
|
||||||
GraphWireStyle::Direct,
|
&GraphWireStyle::Direct,
|
||||||
);
|
);
|
||||||
let mut path_string = String::new();
|
let mut path_string = String::new();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
||||||
@@ -928,7 +928,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
thick: false,
|
thick: false,
|
||||||
dashed: false,
|
dashed: false,
|
||||||
center: None,
|
center: None,
|
||||||
input_sni: None,
|
|
||||||
};
|
};
|
||||||
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) });
|
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) });
|
||||||
}
|
}
|
||||||
@@ -1179,7 +1178,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (wire, is_stack, _) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
let (wire, is_stack, _) = network_interface.vector_wire_from_input(&input, &preferences.graph_wire_style, selection_network_path)?;
|
||||||
wire.rectangle_intersections_exist(bounding_box[0], bounding_box[1]).then_some((input, is_stack))
|
wire.rectangle_intersections_exist(bounding_box[0], bounding_box[1]).then_some((input, is_stack))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -1310,15 +1309,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
|
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
|
||||||
}),
|
}),
|
||||||
NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }),
|
NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }),
|
||||||
NodeGraphMessage::UnloadWires => {
|
NodeGraphMessage::UnloadWirePaths => {
|
||||||
for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) {
|
for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) {
|
||||||
network_interface.unload_wire(&input, breadcrumb_network_path);
|
network_interface.unload_wire(&input, breadcrumb_network_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(FrontendMessage::ClearAllNodeGraphWires);
|
responses.add(FrontendMessage::ClearAllNodeGraphWirePaths);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SendWires => {
|
NodeGraphMessage::SendWirePaths => {
|
||||||
let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path);
|
let wires = self.collect_wires_paths(network_interface, &preferences.graph_wire_style, breadcrumb_network_path);
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphWires { wires });
|
responses.add(FrontendMessage::UpdateNodeGraphWires { wires });
|
||||||
}
|
}
|
||||||
NodeGraphMessage::UpdateVisibleNodes => {
|
NodeGraphMessage::UpdateVisibleNodes => {
|
||||||
@@ -1347,8 +1346,20 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
let nodes = self.collect_nodes(network_interface, breadcrumb_network_path);
|
let nodes = self.collect_nodes(network_interface, breadcrumb_network_path);
|
||||||
self.frontend_nodes = nodes.iter().map(|node| node.id).collect();
|
self.frontend_nodes = nodes.iter().map(|node| node.id).collect();
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes });
|
responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes });
|
||||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
let (layer_sni_updates, wire_sni_updates) = NodeGraphMessageHandler::graph_sni_updates(network_interface, breadcrumb_network_path);
|
||||||
|
responses.add(FrontendMessage::UpdateThumbnails {
|
||||||
|
add: Vec::new(),
|
||||||
|
clear: Vec::new(),
|
||||||
|
wire_sni_updates,
|
||||||
|
layer_sni_updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||||
|
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||||
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
|
responses.add(FrontendMessage::UpdateGraphBreadcrumbPath {
|
||||||
|
breadcrumb_path: breadcrumb_network_path.to_vec(),
|
||||||
|
});
|
||||||
let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path);
|
let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path);
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||||
@@ -1366,6 +1377,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
network_interface.set_grid_aligned_edges(DVec2::new(ipp.viewport_bounds.bottom_right.x - ipp.viewport_bounds.top_left.x, 0.), breadcrumb_network_path);
|
network_interface.set_grid_aligned_edges(DVec2::new(ipp.viewport_bounds.bottom_right.x - ipp.viewport_bounds.top_left.x, 0.), breadcrumb_network_path);
|
||||||
// Send the new edges to the frontend
|
// Send the new edges to the frontend
|
||||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||||
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
|
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
|
||||||
@@ -1378,7 +1390,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
if !(network_interface.reference(&node_id, selection_network_path).is_none() || input_index == 0) && network_interface.connected_to_output(&node_id, selection_network_path) {
|
if !(network_interface.reference(&node_id, selection_network_path).is_none() || input_index == 0) && network_interface.connected_to_output(&node_id, selection_network_path) {
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
responses.add(Message::StartEvaluationQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
responses.add(Message::EndEvaluationQueue);
|
responses.add(Message::EndEvaluationQueue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1437,7 +1449,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleSelectedAsLayersOrNodes => {
|
NodeGraphMessage::ToggleSelectedAsLayersOrNodes => {
|
||||||
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
|
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
|
||||||
@@ -1458,7 +1470,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
NodeGraphMessage::ShiftNodePosition { node_id, x, y } => {
|
NodeGraphMessage::ShiftNodePosition { node_id, x, y } => {
|
||||||
network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path);
|
network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path);
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => {
|
NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => {
|
||||||
if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) {
|
if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) {
|
||||||
@@ -1472,7 +1484,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
});
|
});
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetDisplayName {
|
NodeGraphMessage::SetDisplayName {
|
||||||
node_id,
|
node_id,
|
||||||
@@ -2130,7 +2142,30 @@ impl NodeGraphMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_wires(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec<WirePathUpdate> {
|
pub fn graph_sni_updates(network_interface: &NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> (Vec<FrontendNodeSNIUpdate>, Vec<WireSNIUpdate>) {
|
||||||
|
let mut layer_updates = Vec::new();
|
||||||
|
let wires = network_interface
|
||||||
|
.node_graph_input_connectors(breadcrumb_network_path)
|
||||||
|
.iter()
|
||||||
|
.map(|input_connector| {
|
||||||
|
if let Some(node_id) = input_connector.node_id() {
|
||||||
|
if network_interface.is_layer(&node_id, breadcrumb_network_path) {
|
||||||
|
layer_updates.push(FrontendNodeSNIUpdate {
|
||||||
|
id: node_id,
|
||||||
|
sni: network_interface.protonode_from_output(&OutputConnector::node(node_id, 0), breadcrumb_network_path),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WireSNIUpdate {
|
||||||
|
id: input_connector.node_id().unwrap_or(NodeId(u64::MAX)),
|
||||||
|
input_index: input_connector.input_index(),
|
||||||
|
sni: network_interface.protonode_from_input(input_connector, breadcrumb_network_path),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
(layer_updates, wires)
|
||||||
|
}
|
||||||
|
fn collect_wires_paths(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: &GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec<WirePathUpdate> {
|
||||||
let mut added_wires = network_interface
|
let mut added_wires = network_interface
|
||||||
.node_graph_input_connectors(breadcrumb_network_path)
|
.node_graph_input_connectors(breadcrumb_network_path)
|
||||||
.iter()
|
.iter()
|
||||||
@@ -2278,18 +2313,24 @@ impl NodeGraphMessageHandler {
|
|||||||
|
|
||||||
let locked = network_interface.is_locked(&node_id, breadcrumb_network_path);
|
let locked = network_interface.is_locked(&node_id, breadcrumb_network_path);
|
||||||
|
|
||||||
let errors = None; // TODO: Recursive traversal from export over all protonodes and match metadata with error
|
let errors: Option<String> = self
|
||||||
self.node_graph_errors
|
.node_graph_errors
|
||||||
.iter()
|
.iter()
|
||||||
.find(|error| match &error.original_location {
|
.find(|error| match &error.original_location {
|
||||||
graph_craft::proto::OriginalLocation::Value(_) => false,
|
graph_craft::proto::OriginalLocation::Value(_) => false,
|
||||||
graph_craft::proto::OriginalLocation::Node(node_ids) => node_ids == &node_id_path,
|
graph_craft::proto::OriginalLocation::Node(prefixed_node_path) => {
|
||||||
|
let (prefix, node_path) = prefixed_node_path.split_first().unwrap();
|
||||||
|
node_path == &node_id_path
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.map(|error| format!("{:?}", error.error.clone()))
|
.map(|error| format!("{:?}", error.error.clone()))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if self.node_graph_errors.iter().any(|error| match &error.original_location {
|
if self.node_graph_errors.iter().any(|error| match &error.original_location {
|
||||||
graph_craft::proto::OriginalLocation::Value(_) => false,
|
graph_craft::proto::OriginalLocation::Value(_) => false,
|
||||||
graph_craft::proto::OriginalLocation::Node(node_ids) => node_ids.starts_with(&node_id_path),
|
graph_craft::proto::OriginalLocation::Node(prefixed_node_path) => {
|
||||||
|
let (prefix, node_path) = prefixed_node_path.split_first().unwrap();
|
||||||
|
node_path.starts_with(&node_id_path)
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
Some("Node graph type error within this node".to_string())
|
Some("Node graph type error within this node".to_string())
|
||||||
} else {
|
} else {
|
||||||
@@ -2297,11 +2338,16 @@ impl NodeGraphMessageHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let is_layer = network_interface
|
||||||
|
.node_metadata(&node_id, breadcrumb_network_path)
|
||||||
|
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer());
|
||||||
|
|
||||||
nodes.push(FrontendNode {
|
nodes.push(FrontendNode {
|
||||||
id: node_id,
|
id: node_id,
|
||||||
is_layer: network_interface
|
is_layer,
|
||||||
.node_metadata(&node_id, breadcrumb_network_path)
|
layer_thumbnail_sni: is_layer
|
||||||
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
|
.then(|| network_interface.protonode_from_input(&InputConnector::Node { node_id, input_index: 1 }, breadcrumb_network_path))
|
||||||
|
.flatten(),
|
||||||
can_be_layer: can_be_layer_lookup.contains(&node_id),
|
can_be_layer: can_be_layer_lookup.contains(&node_id),
|
||||||
reference: network_interface.reference(&node_id, breadcrumb_network_path).cloned().unwrap_or_default(),
|
reference: network_interface.reference(&node_id, breadcrumb_network_path).cloned().unwrap_or_default(),
|
||||||
display_name: network_interface.display_name(&node_id, breadcrumb_network_path),
|
display_name: network_interface.display_name(&node_id, breadcrumb_network_path),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::messages::portfolio::document::utility_types::network_interface::{TypeSource};
|
use crate::messages::portfolio::document::utility_types::network_interface::TypeSource;
|
||||||
use graph_craft::document::{InputConnector, OutputConnector};
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::uuid::NodeId;
|
use graph_craft::document::{InputConnector, OutputConnector};
|
||||||
use graphene_std::Type;
|
use graphene_std::Type;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
@@ -76,6 +76,8 @@ pub struct FrontendNode {
|
|||||||
pub id: NodeId,
|
pub id: NodeId,
|
||||||
#[serde(rename = "isLayer")]
|
#[serde(rename = "isLayer")]
|
||||||
pub is_layer: bool,
|
pub is_layer: bool,
|
||||||
|
#[serde(rename = "layerThumbnailSNI")]
|
||||||
|
pub layer_thumbnail_sni: Option<NodeId>,
|
||||||
#[serde(rename = "canBeLayer")]
|
#[serde(rename = "canBeLayer")]
|
||||||
pub can_be_layer: bool,
|
pub can_be_layer: bool,
|
||||||
pub reference: Option<String>,
|
pub reference: Option<String>,
|
||||||
@@ -98,6 +100,12 @@ pub struct FrontendNode {
|
|||||||
pub ui_only: bool,
|
pub ui_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct FrontendNodeSNIUpdate {
|
||||||
|
pub id: NodeId,
|
||||||
|
pub sni: Option<NodeId>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendNodeType {
|
pub struct FrontendNodeType {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
|
|||||||
@@ -2521,56 +2521,48 @@ impl NodeNetworkInterface {
|
|||||||
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
|
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
|
pub fn viewport_loaded_thumbnail_position(&mut self, input: &InputConnector, graph_wire_style: &GraphWireStyle, network_path: &[NodeId]) -> Option<DVec2> {
|
||||||
if !self.wire_is_loaded(input, network_path) {
|
let wire = self.wire_path_from_input(input, graph_wire_style, false, network_path)?;
|
||||||
self.load_wire(input, graph_wire_style, network_path);
|
let network_metadata = self.network_metadata(network_path)?;
|
||||||
} else {
|
if wire.thick {
|
||||||
return None;
|
return None;
|
||||||
}
|
};
|
||||||
|
wire.center.map(|center| {
|
||||||
|
let node_graph_position = DVec2::new(center.0 as f64, center.1 as f64);
|
||||||
|
network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.transform_point2(node_graph_position)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let wire = match input {
|
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: &GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
|
||||||
|
match self.cached_wire(input, network_path) {
|
||||||
|
Some(loaded) => None,
|
||||||
|
None => {
|
||||||
|
self.load_wire(input, graph_wire_style, network_path);
|
||||||
|
self.cached_wire(input, network_path).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cached_wire(&self, input: &InputConnector, network_path: &[NodeId]) -> Option<&WirePathUpdate> {
|
||||||
|
match input {
|
||||||
InputConnector::Node { node_id, input_index } => {
|
InputConnector::Node { node_id, input_index } => {
|
||||||
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
|
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
|
||||||
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
|
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
|
||||||
log::error!("Could not load wire for input: {:?}", input);
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
wire.clone()
|
Some(wire)
|
||||||
}
|
}
|
||||||
InputConnector::Export(export_index) => {
|
InputConnector::Export(export_index) => {
|
||||||
let network_metadata = self.network_metadata(network_path)?;
|
let network_metadata = self.network_metadata(network_path)?;
|
||||||
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
|
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
|
||||||
log::error!("Could not load wire for input: {:?}", input);
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
wire.clone()
|
Some(wire)
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(wire)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool {
|
|
||||||
match input {
|
|
||||||
InputConnector::Node { node_id, input_index } => {
|
|
||||||
let Some(input_metadata) = self.transient_input_metadata(node_id, *input_index, network_path) else {
|
|
||||||
log::error!("Input metadata should always exist for input");
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
input_metadata.wire.is_loaded()
|
|
||||||
}
|
|
||||||
InputConnector::Export(export_index) => {
|
|
||||||
let Some(network_metadata) = self.network_metadata(network_path) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
match network_metadata.transient_metadata.wires.get(*export_index) {
|
|
||||||
Some(wire) => wire.is_loaded(),
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) {
|
fn load_wire(&mut self, input: &InputConnector, graph_wire_style: &GraphWireStyle, network_path: &[NodeId]) {
|
||||||
let dashed = match self.previewing(network_path) {
|
let dashed = match self.previewing(network_path) {
|
||||||
Previewing::Yes { .. } => match input {
|
Previewing::Yes { .. } => match input {
|
||||||
InputConnector::Node { .. } => false,
|
InputConnector::Node { .. } => false,
|
||||||
@@ -2698,7 +2690,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// When previewing, there may be a second path to the root node.
|
/// When previewing, there may be a second path to the root node.
|
||||||
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
|
pub fn wire_to_root(&mut self, graph_wire_style: &GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
|
||||||
let input = InputConnector::Export(0);
|
let input = InputConnector::Export(0);
|
||||||
let current_export = self.upstream_output_connector(&input, network_path)?;
|
let current_export = self.upstream_output_connector(&input, network_path)?;
|
||||||
|
|
||||||
@@ -2733,7 +2725,6 @@ impl NodeNetworkInterface {
|
|||||||
thick,
|
thick,
|
||||||
dashed: false,
|
dashed: false,
|
||||||
center: None,
|
center: None,
|
||||||
input_sni: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(WirePathUpdate {
|
Some(WirePathUpdate {
|
||||||
@@ -2744,7 +2735,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the vector subpath and a boolean of whether the wire should be thick.
|
/// Returns the vector subpath and a boolean of whether the wire should be thick.
|
||||||
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(Subpath<PointId>, bool, DVec2)> {
|
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: &GraphWireStyle, network_path: &[NodeId]) -> Option<(Subpath<PointId>, bool, DVec2)> {
|
||||||
let Some(input_position) = self.get_input_center(input, network_path) else {
|
let Some(input_position) = self.get_input_center(input, network_path) else {
|
||||||
log::error!("Could not get dom rect for wire end: {:?}", input);
|
log::error!("Could not get dom rect for wire end: {:?}", input);
|
||||||
return None;
|
return None;
|
||||||
@@ -2760,23 +2751,21 @@ impl NodeNetworkInterface {
|
|||||||
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
|
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
|
||||||
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
|
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
|
||||||
let thick = vertical_end && vertical_start;
|
let thick = vertical_end && vertical_start;
|
||||||
let (wire, center) = build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style);
|
let (wire, center) = build_vector_wire(output_position, input_position, vertical_start, vertical_end, &wire_style);
|
||||||
Some((wire, thick, center))
|
Some((wire, thick, center))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
|
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: &GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
|
||||||
let (vector_wire, thick, center) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
|
let (vector_wire, thick, center) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
|
||||||
let mut path_string = String::new();
|
let mut path_string = String::new();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
||||||
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
|
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
|
||||||
let input_sni = self.protonode_from_input(input, network_path);
|
|
||||||
Some(WirePath {
|
Some(WirePath {
|
||||||
path_string,
|
path_string,
|
||||||
data_type,
|
data_type,
|
||||||
thick,
|
thick,
|
||||||
dashed,
|
dashed,
|
||||||
center: Some((center.x, center.y)),
|
center: Some((center.x as i32, center.y as i32)),
|
||||||
input_sni,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ pub struct WirePath {
|
|||||||
pub data_type: FrontendGraphDataType,
|
pub data_type: FrontendGraphDataType,
|
||||||
pub thick: bool,
|
pub thick: bool,
|
||||||
pub dashed: bool,
|
pub dashed: bool,
|
||||||
pub center: Option<(f64, f64)>,
|
pub center: Option<(i32, i32)>,
|
||||||
#[serde(rename = "inputSni")]
|
|
||||||
pub input_sni: Option<NodeId>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
@@ -27,6 +25,14 @@ pub struct WirePathUpdate {
|
|||||||
pub wire_path_update: Option<WirePath>,
|
pub wire_path_update: Option<WirePath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct WireSNIUpdate {
|
||||||
|
pub id: NodeId,
|
||||||
|
#[serde(rename = "inputIndex")]
|
||||||
|
pub input_index: usize,
|
||||||
|
pub sni: Option<NodeId>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub enum GraphWireStyle {
|
pub enum GraphWireStyle {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -56,7 +62,7 @@ impl GraphWireStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical_out: bool, vertical_in: bool, graph_wire_style: GraphWireStyle) -> (Subpath<PointId>, DVec2) {
|
pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical_out: bool, vertical_in: bool, graph_wire_style: &GraphWireStyle) -> (Subpath<PointId>, DVec2) {
|
||||||
let grid_spacing = 24.;
|
let grid_spacing = 24.;
|
||||||
match graph_wire_style {
|
match graph_wire_style {
|
||||||
GraphWireStyle::Direct => {
|
GraphWireStyle::Direct => {
|
||||||
|
|||||||
@@ -394,14 +394,6 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
|||||||
node: graphene_std::transform_nodes::transform::IDENTIFIER,
|
node: graphene_std::transform_nodes::transform::IDENTIFIER,
|
||||||
aliases: &["graphene_core::transform::TransformNode"],
|
aliases: &["graphene_core::transform::TransformNode"],
|
||||||
},
|
},
|
||||||
NodeReplacement {
|
|
||||||
node: graphene_std::transform_nodes::boundless_footprint::IDENTIFIER,
|
|
||||||
aliases: &["graphene_core::transform::BoundlessFootprintNode"],
|
|
||||||
},
|
|
||||||
NodeReplacement {
|
|
||||||
node: graphene_std::transform_nodes::freeze_real_time::IDENTIFIER,
|
|
||||||
aliases: &["graphene_core::transform::FreezeRealTimeNode"],
|
|
||||||
},
|
|
||||||
// ???
|
// ???
|
||||||
NodeReplacement {
|
NodeReplacement {
|
||||||
node: graphene_std::vector::spline::IDENTIFIER,
|
node: graphene_std::vector::spline::IDENTIFIER,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use graphene_std::Color;
|
|||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::renderer::RenderMetadata;
|
use graphene_std::renderer::RenderMetadata;
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
use graphene_std::uuid::{SNI};
|
use graphene_std::uuid::SNI;
|
||||||
|
|
||||||
#[impl_message(Message, Portfolio)]
|
#[impl_message(Message, Portfolio)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -22,17 +22,18 @@ pub enum PortfolioMessage {
|
|||||||
#[child]
|
#[child]
|
||||||
Spreadsheet(SpreadsheetMessage),
|
Spreadsheet(SpreadsheetMessage),
|
||||||
|
|
||||||
// Introspected data is cleared after all queued messages which relied on the introspection are complete
|
|
||||||
ClearIntrospectedData,
|
|
||||||
|
|
||||||
// Sends a request to compile the network. Should occur when any value, preference, or font changes
|
// Sends a request to compile the network. Should occur when any value, preference, or font changes
|
||||||
CompileActiveDocument,
|
CompileActiveDocument,
|
||||||
// Sends a request to evaluate the network. Should occur when any context value changes.
|
// Sends a request to evaluate the network. Should occur when any context value changes.
|
||||||
EvaluateActiveDocument,
|
// Nodes to introspect is in addition to all nodes to render thumbnails for
|
||||||
// Sends a request to introspect data in the network, and return it to the editor
|
EvaluateActiveDocumentWithThumbnails,
|
||||||
IntrospectActiveDocument {
|
EvaluateActiveDocument {
|
||||||
nodes_to_introspect: HashSet<SNI>,
|
nodes_to_introspect: HashSet<SNI>,
|
||||||
},
|
},
|
||||||
|
// Sends a request to introspect data in the network, and return it to the editor
|
||||||
|
// IntrospectActiveDocument {
|
||||||
|
// nodes_to_introspect: HashSet<SNI>,
|
||||||
|
// },
|
||||||
ExportActiveDocument {
|
ExportActiveDocument {
|
||||||
file_name: String,
|
file_name: String,
|
||||||
file_type: FileType,
|
file_type: FileType,
|
||||||
@@ -47,12 +48,11 @@ pub enum PortfolioMessage {
|
|||||||
},
|
},
|
||||||
ProcessEvaluationResponse {
|
ProcessEvaluationResponse {
|
||||||
evaluation_metadata: RenderMetadata,
|
evaluation_metadata: RenderMetadata,
|
||||||
},
|
|
||||||
ProcessIntrospectionResponse {
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
introspection_response: IntrospectionResponse,
|
introspected_nodes: IntrospectionResponse,
|
||||||
},
|
},
|
||||||
RenderThumbnails,
|
// Introspected data is cleared after queued messages are complete
|
||||||
|
ClearIntrospectedData,
|
||||||
ProcessThumbnails,
|
ProcessThumbnails,
|
||||||
DocumentPassMessage {
|
DocumentPassMessage {
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::messages::portfolio::document::DocumentMessageContext;
|
|||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
||||||
|
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WireSNIUpdate};
|
||||||
use crate::messages::portfolio::document_migration::*;
|
use crate::messages::portfolio::document_migration::*;
|
||||||
use crate::messages::portfolio::spreadsheet::SpreadsheetMessageHandlerData;
|
use crate::messages::portfolio::spreadsheet::SpreadsheetMessageHandlerData;
|
||||||
use crate::messages::preferences::SelectionMode;
|
use crate::messages::preferences::SelectionMode;
|
||||||
@@ -21,8 +22,9 @@ use crate::node_graph_executor::{CompilationRequest, ExportConfig, NodeGraphExec
|
|||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::value::EditorMetadata;
|
use graph_craft::document::value::EditorMetadata;
|
||||||
use graph_craft::document::{InputConnector, NodeInput, OutputConnector};
|
use graph_craft::document::{InputConnector, NodeInput, OutputConnector};
|
||||||
use graphene_std::any::EditorContext;
|
use graphene_std::EditorContext;
|
||||||
use graphene_std::application_io::TimingInformation;
|
use graphene_std::application_io::TimingInformation;
|
||||||
|
use graphene_std::memo::MonitorIntrospectResult;
|
||||||
use graphene_std::renderer::{Quad, RenderMetadata};
|
use graphene_std::renderer::{Quad, RenderMetadata};
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
use graphene_std::transform::{Footprint, RenderQuality};
|
use graphene_std::transform::{Footprint, RenderQuality};
|
||||||
@@ -57,10 +59,10 @@ pub struct PortfolioMessageHandler {
|
|||||||
device_pixel_ratio: Option<f64>,
|
device_pixel_ratio: Option<f64>,
|
||||||
pub reset_node_definitions_on_open: bool,
|
pub reset_node_definitions_on_open: bool,
|
||||||
// Data from the node graph, which is populated after an introspection request.
|
// Data from the node graph, which is populated after an introspection request.
|
||||||
// To access the data, schedule messages with StartIntrospectionQueue [messages] EndIntrospectionQueue
|
// To access the data, schedule messages with StartEvaluationQueue [messages] EndEvaluationQueue
|
||||||
// The data is no longer accessible after EndIntrospectionQueue
|
// The data is no longer accessible after EndEvaluationQueue
|
||||||
pub introspected_data: HashMap<SNI, Option<Arc<dyn std::any::Any + Send + Sync>>>,
|
pub introspected_data: HashMap<SNI, MonitorIntrospectResult>,
|
||||||
pub previous_thumbnail_data: HashMap<SNI, Arc<dyn std::any::Any + Send + Sync>>,
|
thumbnails_to_clear: Vec<SNI>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
@@ -791,8 +793,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
transform_to_viewport: true,
|
transform_to_viewport: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Also evaluate the document after compilation
|
|
||||||
responses.add_front(PortfolioMessage::EvaluateActiveDocument);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::ProcessCompilationResponse { compilation_metadata } => {
|
PortfolioMessage::ProcessCompilationResponse { compilation_metadata } => {
|
||||||
@@ -814,67 +814,23 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
// Remove all thumbnails
|
// Remove all thumbnails
|
||||||
cleared_thumbnails.push(sni);
|
cleared_thumbnails.push(sni);
|
||||||
}
|
}
|
||||||
responses.add(FrontendMessage::UpdateThumbnails {
|
|
||||||
add: Vec::new(),
|
self.thumbnails_to_clear.extend(cleared_thumbnails);
|
||||||
clear: cleared_thumbnails,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
PortfolioMessage::EvaluateActiveDocument => {
|
PortfolioMessage::EvaluateActiveDocumentWithThumbnails => {
|
||||||
|
responses.add(PortfolioMessage::EvaluateActiveDocument {
|
||||||
|
nodes_to_introspect: self.nodes_to_try_render(ipp.viewport_bounds()[1], &preferences.graph_wire_style),
|
||||||
|
});
|
||||||
|
responses.add(Message::StartEvaluationQueue);
|
||||||
|
responses.add(PortfolioMessage::ProcessThumbnails);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
|
}
|
||||||
|
PortfolioMessage::EvaluateActiveDocument { nodes_to_introspect } => {
|
||||||
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
|
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
|
||||||
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get all the inputs to save data for. This includes vector modify, thumbnails, and spreadsheet data
|
|
||||||
|
|
||||||
// Save vector data for all path/transform nodes in the document network
|
|
||||||
// match document.network_interface.input_from_connector(&InputConnector::Export(0), &[]) {
|
|
||||||
// Some(NodeInput::Node { node_id, .. }) => {
|
|
||||||
// for upstream_node in document.network_interface.upstream_flow_back_from_nodes(vec![*node_id], &[], network_interface::FlowType::UpstreamFlow) {
|
|
||||||
// let reference = document.network_interface.reference(&upstream_node, &[]).and_then(|reference| reference.as_deref());
|
|
||||||
// if reference == Some("Path") || reference == Some("Transform") {
|
|
||||||
// let input_connector = InputConnector::Node { node_id: *node_id, input_index: 0 };
|
|
||||||
// let Some(downstream_caller) = document.network_interface.downstream_caller_from_input(&input_connector, &[]) else {
|
|
||||||
// log::error!("could not get downstream caller for node : {:?}", node_id);
|
|
||||||
// continue;
|
|
||||||
// };
|
|
||||||
// inputs_to_monitor.insert((*downstream_caller, IntrospectMode::Data));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// _ => {}
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Introspect data for the currently selected node (eventually thumbnail) if the spreadsheet view is open
|
|
||||||
// if self.spreadsheet.spreadsheet_view_open {
|
|
||||||
// let selected_network_path = &document.selection_network_path;
|
|
||||||
// // TODO: Replace with selected thumbnail
|
|
||||||
// if let Some(selected_node) = document
|
|
||||||
// .network_interface
|
|
||||||
// .selected_nodes_in_nested_network(selected_network_path)
|
|
||||||
// .and_then(|selected_nodes| if selected_nodes.0.len() == 1 { selected_nodes.0.first().copied() } else { None })
|
|
||||||
// {
|
|
||||||
// // TODO: Introspect any input rather than just the first input of the selected node
|
|
||||||
// let selected_connector = InputConnector::Node {
|
|
||||||
// node_id: selected_node,
|
|
||||||
// input_index: 0,
|
|
||||||
// };
|
|
||||||
// match document.network_interface.downstream_caller_from_input(&selected_connector, selected_network_path) {
|
|
||||||
// Some(caller) => {
|
|
||||||
// inputs_to_monitor.insert((*caller, IntrospectMode::Data));
|
|
||||||
// inspect_input = Some(InspectInputConnector {
|
|
||||||
// input_connector: AbsoluteInputConnector {
|
|
||||||
// network_path: selected_network_path.clone(),
|
|
||||||
// connector: selected_connector,
|
|
||||||
// },
|
|
||||||
// protonode_input: *caller,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// None => log::error!("Could not get downstream caller for {:?}", selected_connector),
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let animation_time = match animation.timing_information().animation_time {
|
// let animation_time = match animation.timing_information().animation_time {
|
||||||
// AnimationState::Stopped => 0.,
|
// AnimationState::Stopped => 0.,
|
||||||
// AnimationState::Playing { start } => ipp.time - start,
|
// AnimationState::Playing { start } => ipp.time - start,
|
||||||
@@ -891,9 +847,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
context.real_time = Some(ipp.time);
|
context.real_time = Some(ipp.time);
|
||||||
context.downstream_transform = Some(DAffine2::IDENTITY);
|
context.downstream_transform = Some(DAffine2::IDENTITY);
|
||||||
|
|
||||||
self.executor.submit_node_graph_evaluation(context, None, None);
|
let nodes_to_try_render = self.nodes_to_try_render(ipp.viewport_bounds()[1], &preferences.graph_wire_style);
|
||||||
|
|
||||||
|
self.executor.submit_node_graph_evaluation(context, None, None, nodes_to_try_render);
|
||||||
}
|
}
|
||||||
PortfolioMessage::ProcessEvaluationResponse { evaluation_metadata } => {
|
PortfolioMessage::ProcessEvaluationResponse {
|
||||||
|
evaluation_metadata,
|
||||||
|
introspected_nodes,
|
||||||
|
} => {
|
||||||
|
for (protonode, data) in introspected_nodes.0.into_iter() {
|
||||||
|
self.introspected_data.insert(protonode, data);
|
||||||
|
}
|
||||||
|
|
||||||
let RenderMetadata {
|
let RenderMetadata {
|
||||||
upstream_footprints: footprints,
|
upstream_footprints: footprints,
|
||||||
local_transforms,
|
local_transforms,
|
||||||
@@ -915,104 +880,71 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
// AnimationState::Playing { .. } => responses.add(PortfolioMessage::EvaluateActiveDocument),
|
// AnimationState::Playing { .. } => responses.add(PortfolioMessage::EvaluateActiveDocument),
|
||||||
// _ => {}
|
// _ => {}
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// After an evaluation, always render all thumbnails
|
|
||||||
responses.add(PortfolioMessage::RenderThumbnails);
|
|
||||||
}
|
|
||||||
PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect } => {
|
|
||||||
self.executor.submit_node_graph_introspection(nodes_to_introspect);
|
|
||||||
}
|
|
||||||
PortfolioMessage::ProcessIntrospectionResponse { introspection_response } => {
|
|
||||||
for (protonode, data) in introspection_response.0.into_iter() {
|
|
||||||
self.introspected_data.insert(protonode, data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect } => {
|
||||||
|
// self.executor.submit_node_graph_introspection(nodes_to_introspect);
|
||||||
|
// }
|
||||||
|
// PortfolioMessage::RenderThumbnails => {
|
||||||
|
// let nodes_to_render = self.nodes_to_render();
|
||||||
|
// responses.add(PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect: nodes_to_render });
|
||||||
|
// responses.add(Message::StartIntrospectionQueue);
|
||||||
|
// responses.add(PortfolioMessage::ProcessThumbnails);
|
||||||
|
// responses.add(Message::EndIntrospectionQueue);
|
||||||
|
// }
|
||||||
PortfolioMessage::ClearIntrospectedData => self.introspected_data.clear(),
|
PortfolioMessage::ClearIntrospectedData => self.introspected_data.clear(),
|
||||||
PortfolioMessage::RenderThumbnails => {
|
PortfolioMessage::ProcessThumbnails => {
|
||||||
|
let mut thumbnail_response = ThumbnailRenderResponse::default();
|
||||||
|
|
||||||
|
for (thumbnail_node, monitor_result) in self.introspected_data.drain() {
|
||||||
|
let evaluated_data = match monitor_result {
|
||||||
|
MonitorIntrospectResult::Error => continue,
|
||||||
|
MonitorIntrospectResult::Disabled => continue,
|
||||||
|
MonitorIntrospectResult::NotEvaluated => continue,
|
||||||
|
MonitorIntrospectResult::Evaluated((data, changed)) => {
|
||||||
|
// If the evaluated value is the same as the previous, then just remap the ID
|
||||||
|
if !changed {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match graph_craft::document::value::render_thumbnail(&evaluated_data) {
|
||||||
|
Some(thumbnail) => thumbnail_response.add.push((thumbnail_node, thumbnail)),
|
||||||
|
None => thumbnail_response.clear.push(thumbnail_node),
|
||||||
|
}
|
||||||
|
}
|
||||||
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
|
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
|
||||||
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// All possible inputs, later check if they are connected to any nodes
|
let mut wire_sni_updates = document
|
||||||
let mut nodes_to_render = HashSet::new();
|
.network_interface
|
||||||
|
.document_metadata()
|
||||||
// Get all inputs to render thumbnails for
|
.all_layers()
|
||||||
// Get all protonodes for all connected side layer inputs connected to the export in the document network
|
.map(|layer| WireSNIUpdate {
|
||||||
for layer in document.network_interface.document_metadata().all_layers() {
|
id: layer.to_node(),
|
||||||
let connector = InputConnector::Node {
|
|
||||||
node_id: layer.to_node(),
|
|
||||||
input_index: 1,
|
input_index: 1,
|
||||||
};
|
sni: document.network_interface.protonode_from_input(&InputConnector::node(layer.to_node(), 1), &[]),
|
||||||
if document.network_interface.input_from_connector(&connector, &[]).is_some_and(|input| input.is_wire()) {
|
})
|
||||||
if let Some(compiled_input) = document.network_interface.protonode_from_input(&connector, &[]) {
|
.collect::<Vec<_>>();
|
||||||
nodes_to_render.insert(compiled_input);
|
let mut layer_sni_updates = Vec::new();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save data for all inputs in the viewed node graph
|
// Only update wires/nodes in the node graph if they are open, but this will require syncing when the node graph resent.
|
||||||
if document.graph_view_overlay_open {
|
if document.graph_view_overlay_open {
|
||||||
let Some(viewed_network) = document.network_interface.nested_network(&document.breadcrumb_network_path) else {
|
let (layer_updates, wire_updates) = NodeGraphMessageHandler::graph_sni_updates(&document.network_interface, &document.breadcrumb_network_path);
|
||||||
return;
|
layer_sni_updates.extend(layer_updates);
|
||||||
};
|
wire_sni_updates.extend(wire_updates);
|
||||||
let mut wire_stack = viewed_network
|
|
||||||
.exports
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(export_index, export)| export.is_wire().then_some(InputConnector::Export(export_index)))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
while let Some(input_connector) = wire_stack.pop() {
|
|
||||||
let Some(input) = document.network_interface.input_from_connector(&input_connector, &document.breadcrumb_network_path) else {
|
|
||||||
log::error!("Could not get input from connector: {:?}", input_connector);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if let NodeInput::Node { node_id, .. } = input {
|
|
||||||
let Some(node) = document.network_interface.document_node(node_id, &document.breadcrumb_network_path) else {
|
|
||||||
log::error!("Could not get node");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for (wire_input_index, _) in node.inputs.iter().enumerate().filter(|(_, input)| input.is_wire()) {
|
|
||||||
wire_stack.push(InputConnector::Node {
|
|
||||||
node_id: *node_id,
|
|
||||||
input_index: wire_input_index,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let Some(protonode) = document.network_interface.protonode_from_input(&input_connector, &document.breadcrumb_network_path) else {
|
|
||||||
// The protonode has not been compiled, so it is not connected to the export
|
|
||||||
wire_stack = Vec::new();
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
nodes_to_render.insert(protonode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
responses.add(PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect: nodes_to_render });
|
|
||||||
responses.add(Message::StartIntrospectionQueue);
|
|
||||||
responses.add(PortfolioMessage::ProcessThumbnails);
|
|
||||||
responses.add(Message::EndIntrospectionQueue);
|
|
||||||
}
|
|
||||||
PortfolioMessage::ProcessThumbnails => {
|
|
||||||
let mut thumbnail_response = ThumbnailRenderResponse::default();
|
|
||||||
for (thumbnail_node, introspected_data) in self.introspected_data.drain() {
|
|
||||||
let Some(evaluated_data) = introspected_data else {
|
|
||||||
// Input was not evaluated, do not change its thumbnail
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let previous_thumbnail_data = self.previous_thumbnail_data.get(&thumbnail_node);
|
|
||||||
|
|
||||||
match graph_craft::document::value::render_thumbnail_if_change(&evaluated_data, previous_thumbnail_data) {
|
|
||||||
graph_craft::document::value::ThumbnailRenderResult::NoChange => return,
|
|
||||||
graph_craft::document::value::ThumbnailRenderResult::ClearThumbnail => thumbnail_response.clear.push(thumbnail_node),
|
|
||||||
graph_craft::document::value::ThumbnailRenderResult::UpdateThumbnail(thumbnail) => thumbnail_response.add.push((thumbnail_node, thumbnail)),
|
|
||||||
}
|
|
||||||
self.previous_thumbnail_data.insert(thumbnail_node, evaluated_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut clear = std::mem::take(&mut self.thumbnails_to_clear);
|
||||||
|
clear.extend(thumbnail_response.clear);
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateThumbnails {
|
responses.add(FrontendMessage::UpdateThumbnails {
|
||||||
add: thumbnail_response.add,
|
add: thumbnail_response.add,
|
||||||
clear: thumbnail_response.clear,
|
clear,
|
||||||
})
|
wire_sni_updates,
|
||||||
|
layer_sni_updates,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
PortfolioMessage::ExportActiveDocument {
|
PortfolioMessage::ExportActiveDocument {
|
||||||
file_name,
|
file_name,
|
||||||
@@ -1081,6 +1013,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
transparent_background,
|
transparent_background,
|
||||||
size: scaled_size,
|
size: scaled_size,
|
||||||
}),
|
}),
|
||||||
|
HashSet::new(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// if let Some((start, end, fps)) = animation_export_data {
|
// if let Some((start, end, fps)) = animation_export_data {
|
||||||
@@ -1300,11 +1233,100 @@ impl PortfolioMessageHandler {
|
|||||||
/text>"#
|
/text>"#
|
||||||
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
||||||
.to_string();
|
.to_string();
|
||||||
responses.add(Message::ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata::default()));
|
responses.add(Message::ProcessEvaluationQueue(
|
||||||
|
graphene_std::renderer::RenderMetadata::default(),
|
||||||
|
crate::node_graph_executor::IntrospectionResponse(Vec::new()),
|
||||||
|
));
|
||||||
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn nodes_to_try_render(&mut self, viewport_size: DVec2, graph_wire_style: &GraphWireStyle) -> HashSet<SNI> {
|
||||||
|
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get_mut(&document_id)) else {
|
||||||
|
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||||
|
return HashSet::new();
|
||||||
|
};
|
||||||
|
// All possible inputs, later check if they are connected to any nodes
|
||||||
|
let mut nodes_to_render = HashSet::new();
|
||||||
|
|
||||||
|
if document.graph_view_overlay_open {
|
||||||
|
let Some(viewed_network) = document.network_interface.nested_network(&document.breadcrumb_network_path) else {
|
||||||
|
return HashSet::new();
|
||||||
|
};
|
||||||
|
let mut wire_stack = viewed_network
|
||||||
|
.exports
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(export_index, export)| export.is_wire().then_some(InputConnector::Export(export_index)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
while let Some(input_connector) = wire_stack.pop() {
|
||||||
|
let Some(input) = document.network_interface.input_from_connector(&input_connector, &document.breadcrumb_network_path) else {
|
||||||
|
log::error!("Could not get input from connector: {:?}", input_connector);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let NodeInput::Node { node_id, .. } = input {
|
||||||
|
let Some(node) = document.network_interface.document_node(node_id, &document.breadcrumb_network_path) else {
|
||||||
|
log::error!("Could not get node");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for (wire_input_index, _) in node.inputs.iter().enumerate().filter(|(_, input)| input.is_wire()) {
|
||||||
|
wire_stack.push(InputConnector::Node {
|
||||||
|
node_id: *node_id,
|
||||||
|
input_index: wire_input_index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save data for all thin wires with visible thumbnails in the viewed node graph
|
||||||
|
if let Some(viewport_position) = document
|
||||||
|
.network_interface
|
||||||
|
.viewport_loaded_thumbnail_position(&input_connector, graph_wire_style, &document.breadcrumb_network_path)
|
||||||
|
{
|
||||||
|
log::debug!("viewport position: {:?}, input: {:?}", viewport_position, input_connector);
|
||||||
|
let in_view = viewport_position.x < 0.0 || viewport_position.y < 0.0 || viewport_position.x > viewport_size.x || viewport_position.y > viewport_size.y;
|
||||||
|
if in_view {
|
||||||
|
let Some(protonode) = document.network_interface.protonode_from_input(&input_connector, &document.breadcrumb_network_path) else {
|
||||||
|
// The input is not connected to the export, which occurs if inside a disconnected node
|
||||||
|
wire_stack = Vec::new();
|
||||||
|
nodes_to_render.clear();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
nodes_to_render.insert(protonode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get thumbnails for all visible layer
|
||||||
|
for visible_node in &document.node_graph_handler.frontend_nodes {
|
||||||
|
if document.network_interface.is_layer(&visible_node, &document.breadcrumb_network_path) {
|
||||||
|
log::debug!("visible_node: {:?}", visible_node);
|
||||||
|
let Some(protonode) = document
|
||||||
|
.network_interface
|
||||||
|
.protonode_from_output(&OutputConnector::node(*visible_node, 1), &document.breadcrumb_network_path)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
nodes_to_render.insert(protonode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all protonodes for all connected side layer inputs connected to the export in the document network
|
||||||
|
for layer in document.network_interface.document_metadata().all_layers() {
|
||||||
|
let connector = InputConnector::Node {
|
||||||
|
node_id: layer.to_node(),
|
||||||
|
input_index: 1,
|
||||||
|
};
|
||||||
|
if document.network_interface.input_from_connector(&connector, &[]).is_some_and(|input| input.is_wire()) {
|
||||||
|
if let Some(protonode) = document.network_interface.protonode_from_input(&connector, &[]) {
|
||||||
|
nodes_to_render.insert(protonode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes_to_render
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use graph_craft::document::OutputConnector;
|
|||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::GraphicGroupTable;
|
use graphene_std::GraphicGroupTable;
|
||||||
use graphene_std::instances::Instances;
|
use graphene_std::instances::Instances;
|
||||||
|
use graphene_std::memo::MonitorIntrospectResult;
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::uuid::{NodeId, SNI};
|
use graphene_std::uuid::{NodeId, SNI};
|
||||||
use graphene_std::vector::{VectorData, VectorDataTable};
|
use graphene_std::vector::{VectorData, VectorDataTable};
|
||||||
@@ -15,7 +16,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct SpreadsheetMessageHandlerData<'a> {
|
pub struct SpreadsheetMessageHandlerData<'a> {
|
||||||
pub introspected_data: &'a HashMap<SNI, Option<Arc<dyn std::any::Any + Send + Sync>>>,
|
pub introspected_data: &'a HashMap<SNI, MonitorIntrospectResult>,
|
||||||
// Network interface of the selected document
|
// Network interface of the selected document
|
||||||
pub network_interface: &'a NodeNetworkInterface,
|
pub network_interface: &'a NodeNetworkInterface,
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,7 @@ pub struct SpreadsheetMessageHandler {
|
|||||||
/// Sets whether or not the spreadsheet is drawn.
|
/// Sets whether or not the spreadsheet is drawn.
|
||||||
pub spreadsheet_view_open: bool,
|
pub spreadsheet_view_open: bool,
|
||||||
// Path to the document node that is introspected. The protonode is found by traversing from the primary output
|
// Path to the document node that is introspected. The protonode is found by traversing from the primary output
|
||||||
inspection_data: Option<Option<Arc<dyn std::any::Any + Send + Sync>>>,
|
inspection_data: Option<MonitorIntrospectResult>,
|
||||||
node_to_inspect: Option<NodeId>,
|
node_to_inspect: Option<NodeId>,
|
||||||
|
|
||||||
instances_path: Vec<usize>,
|
instances_path: Vec<usize>,
|
||||||
@@ -73,10 +74,10 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData<'_>> for S
|
|||||||
let mut nodes_to_introspect = HashSet::new();
|
let mut nodes_to_introspect = HashSet::new();
|
||||||
nodes_to_introspect.insert(protonode_id);
|
nodes_to_introspect.insert(protonode_id);
|
||||||
|
|
||||||
responses.add(PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect });
|
responses.add(PortfolioMessage::EvaluateActiveDocument { nodes_to_introspect });
|
||||||
responses.add(Message::StartIntrospectionQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(SpreadsheetMessage::ProcessUpdateLayout { node_to_inspect, protonode_id });
|
responses.add(SpreadsheetMessage::ProcessUpdateLayout { node_to_inspect, protonode_id });
|
||||||
responses.add(Message::EndIntrospectionQueue);
|
responses.add(Message::EndEvaluationQueue);
|
||||||
|
|
||||||
self.update_layout(responses);
|
self.update_layout(responses);
|
||||||
}
|
}
|
||||||
@@ -93,7 +94,6 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData<'_>> for S
|
|||||||
self.instances_path.truncate(len);
|
self.instances_path.truncate(len);
|
||||||
self.update_layout(responses);
|
self.update_layout(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpreadsheetMessage::ViewVectorDataDomain { domain } => {
|
SpreadsheetMessage::ViewVectorDataDomain { domain } => {
|
||||||
self.viewing_vector_data_domain = domain;
|
self.viewing_vector_data_domain = domain;
|
||||||
self.update_layout(responses);
|
self.update_layout(responses);
|
||||||
@@ -126,11 +126,13 @@ impl SpreadsheetMessageHandler {
|
|||||||
Some(_) => {
|
Some(_) => {
|
||||||
match &self.inspection_data {
|
match &self.inspection_data {
|
||||||
Some(data) => match data {
|
Some(data) => match data {
|
||||||
Some(inspected_data) => match generate_layout(&inspected_data, &mut layout_data) {
|
MonitorIntrospectResult::Error => label("The introspected node is a type that cannot be cloned"),
|
||||||
|
MonitorIntrospectResult::Disabled => label("Error: The introspected node must be set to StoreFirstEvaluation before introspection"),
|
||||||
|
MonitorIntrospectResult::NotEvaluated => label("Introspected data is not available for this input. This input may be cached."),
|
||||||
|
MonitorIntrospectResult::Evaluated((data, _)) => match generate_layout(data, &mut layout_data) {
|
||||||
Some(layout) => layout,
|
Some(layout) => layout,
|
||||||
None => label("The introspected data is not a supported type to be displayed."),
|
None => label("The introspected data is not a supported type to be displayed."),
|
||||||
},
|
},
|
||||||
None => label("Introspected data is not available for this input. This input may be cached."),
|
|
||||||
},
|
},
|
||||||
// There should always be an entry for each protonode input. If its empty then it was not requested or an error occured
|
// There should always be an entry for each protonode input. If its empty then it was not requested or an error occured
|
||||||
None => label("The output of this node could not be determined"),
|
None => label("The output of this node could not be determined"),
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
|
|||||||
}
|
}
|
||||||
PreferencesMessage::GraphWireStyle { style } => {
|
PreferencesMessage::GraphWireStyle { style } => {
|
||||||
self.graph_wire_style = style;
|
self.graph_wire_style = style;
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWirePaths);
|
||||||
}
|
}
|
||||||
PreferencesMessage::ViewportZoomWheelRate { rate } => {
|
PreferencesMessage::ViewportZoomWheelRate { rate } => {
|
||||||
self.viewport_zoom_wheel_rate = rate;
|
self.viewport_zoom_wheel_rate = rate;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use dyn_any::DynAny;
|
|||||||
use graph_craft::document::value::{EditorMetadata, RenderOutput, TaggedValue};
|
use graph_craft::document::value::{EditorMetadata, RenderOutput, TaggedValue};
|
||||||
use graph_craft::document::{CompilationMetadata, DocumentNode, NodeNetwork, generate_uuid};
|
use graph_craft::document::{CompilationMetadata, DocumentNode, NodeNetwork, generate_uuid};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
use graphene_std::any::EditorContext;
|
use graphene_std::EditorContext;
|
||||||
|
use graphene_std::memo::MonitorIntrospectResult;
|
||||||
use graphene_std::renderer::format_transform_matrix;
|
use graphene_std::renderer::format_transform_matrix;
|
||||||
use graphene_std::text::FontCache;
|
use graphene_std::text::FontCache;
|
||||||
use graphene_std::uuid::SNI;
|
use graphene_std::uuid::SNI;
|
||||||
@@ -30,6 +31,11 @@ pub struct CompilationResponse {
|
|||||||
node_graph_errors: GraphErrors,
|
node_graph_errors: GraphErrors,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct CacheEnableRequest {
|
||||||
|
nodes_to_enable: HashSet<SNI>,
|
||||||
|
}
|
||||||
|
|
||||||
// Metadata the editor sends when evaluating the network
|
// Metadata the editor sends when evaluating the network
|
||||||
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct EvaluationRequest {
|
pub struct EvaluationRequest {
|
||||||
@@ -37,16 +43,18 @@ pub struct EvaluationRequest {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub context: EditorContext,
|
pub context: EditorContext,
|
||||||
pub node_to_evaluate: Option<SNI>,
|
pub node_to_evaluate: Option<SNI>,
|
||||||
|
pub nodes_to_introspect: HashSet<SNI>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
|
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct EvaluationResponse {
|
pub struct EvaluationResponse {
|
||||||
evaluation_id: u64,
|
evaluation_id: u64,
|
||||||
result: Result<TaggedValue, String>,
|
result: Result<TaggedValue, String>,
|
||||||
|
introspected_nodes: IntrospectionResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct IntrospectionResponse(pub Vec<(SNI, Option<Arc<dyn std::any::Any + Send + Sync>>)>);
|
pub struct IntrospectionResponse(pub Vec<(SNI, MonitorIntrospectResult)>);
|
||||||
|
|
||||||
impl PartialEq for IntrospectionResponse {
|
impl PartialEq for IntrospectionResponse {
|
||||||
fn eq(&self, _other: &Self) -> bool {
|
fn eq(&self, _other: &Self) -> bool {
|
||||||
@@ -58,7 +66,6 @@ impl PartialEq for IntrospectionResponse {
|
|||||||
pub enum NodeGraphUpdate {
|
pub enum NodeGraphUpdate {
|
||||||
CompilationResponse(CompilationResponse),
|
CompilationResponse(CompilationResponse),
|
||||||
EvaluationResponse(EvaluationResponse),
|
EvaluationResponse(EvaluationResponse),
|
||||||
IntrospectionResponse(IntrospectionResponse),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@@ -83,18 +90,18 @@ impl Default for NodeGraphExecutor {
|
|||||||
|
|
||||||
impl NodeGraphExecutor {
|
impl NodeGraphExecutor {
|
||||||
/// A local runtime is useful on threads since having global state causes flakes
|
/// A local runtime is useful on threads since having global state causes flakes
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
pub(crate) fn new_with_local_runtime() -> (NodeRuntime, Self) {
|
// pub(crate) fn new_with_local_runtime() -> (NodeRuntime, Self) {
|
||||||
let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
// let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
||||||
let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
// let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
||||||
let node_runtime = NodeRuntime::new(request_receiver, response_sender);
|
// let node_runtime = NodeRuntime::new(request_receiver, response_sender);
|
||||||
|
|
||||||
let node_executor = Self {
|
// let node_executor = Self {
|
||||||
futures: HashMap::new(),
|
// futures: HashMap::new(),
|
||||||
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
|
// runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
|
||||||
};
|
// };
|
||||||
(node_runtime, node_executor)
|
// (node_runtime, node_executor)
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Updates the network to monitor all inputs. Useful for the testing.
|
/// Updates the network to monitor all inputs. Useful for the testing.
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
@@ -110,19 +117,28 @@ impl NodeGraphExecutor {
|
|||||||
|
|
||||||
/// Compile the network
|
/// Compile the network
|
||||||
pub fn submit_node_graph_compilation(&mut self, compilation_request: CompilationRequest) {
|
pub fn submit_node_graph_compilation(&mut self, compilation_request: CompilationRequest) {
|
||||||
if let Err(error) = self.runtime_io.send(GraphRuntimeRequest::CompilationRequest(compilation_request)) {
|
if let Err(error) = self.runtime_io.try_send(GraphRuntimeRequest::CompilationRequest(compilation_request)) {
|
||||||
log::error!("Could not send evaluation request. {:?}", error);
|
log::error!("Could not send evaluation request. {:?}", error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Adds a request to set disabled cache nodes (automatically placed on the output of each node) to save the value of their first exeuction
|
||||||
|
// pub fn submit_node_graph_cache_enable(&mut self, nodes_to_enable: HashSet<SNI>) {
|
||||||
|
// if let Err(error) = self.runtime_io.try_send(GraphRuntimeRequest::CacheEnableRequest(CacheEnableRequest { nodes_to_enable })) {
|
||||||
|
// log::error!("Could not send evaluation request. {:?}", error);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
/// Adds an evaluation request for whatever current network is cached.
|
/// Adds an evaluation request for whatever current network is cached.
|
||||||
pub fn submit_node_graph_evaluation(&mut self, context: EditorContext, node_to_evaluate: Option<SNI>, export_config: Option<ExportConfig>) {
|
pub fn submit_node_graph_evaluation(&mut self, context: EditorContext, node_to_evaluate: Option<SNI>, export_config: Option<ExportConfig>, nodes_to_introspect: HashSet<SNI>) {
|
||||||
let evaluation_id = generate_uuid();
|
let evaluation_id = generate_uuid();
|
||||||
if let Err(error) = self.runtime_io.send(GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
|
if let Err(error) = self.runtime_io.try_send(GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
|
||||||
evaluation_id,
|
evaluation_id,
|
||||||
context,
|
context,
|
||||||
node_to_evaluate,
|
node_to_evaluate,
|
||||||
|
nodes_to_introspect,
|
||||||
})) {
|
})) {
|
||||||
log::error!("Could not send evaluation request. {:?}", error);
|
log::error!("Could not send evaluation request. {:?}", error);
|
||||||
return;
|
return;
|
||||||
@@ -131,18 +147,43 @@ impl NodeGraphExecutor {
|
|||||||
self.futures.insert(evaluation_id, evaluation_context);
|
self.futures.insert(evaluation_id, evaluation_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<SNI>) {
|
// pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<SNI>) {
|
||||||
if let Err(error) = self.runtime_io.send(GraphRuntimeRequest::IntrospectionRequest(nodes_to_introspect)) {
|
// if let Err(error) = self.runtime_io.try_send(GraphRuntimeRequest::IntrospectionRequest(nodes_to_introspect)) {
|
||||||
log::error!("Could not send evaluation request. {:?}", error);
|
// log::error!("Could not send evaluation request. {:?}", error);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Continuously poll the executor (called by request animation frame)
|
// Continuously poll the executor (called by request animation frame)
|
||||||
pub fn poll_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
pub fn poll_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
||||||
for response in self.runtime_io.receive() {
|
if let Ok(response) = self.runtime_io.receive() {
|
||||||
|
self.runtime_io.busy = false;
|
||||||
match response {
|
match response {
|
||||||
NodeGraphUpdate::EvaluationResponse(EvaluationResponse { evaluation_id, result }) => {
|
NodeGraphUpdate::CompilationResponse(compilation_response) => {
|
||||||
|
let CompilationResponse { node_graph_errors, result } = compilation_response;
|
||||||
|
let compilation_metadata = match result {
|
||||||
|
Err(e) => {
|
||||||
|
// Clear the click targets while the graph is in an un-renderable state
|
||||||
|
document.network_interface.update_click_targets(HashMap::new());
|
||||||
|
document.network_interface.update_vector_modify(HashMap::new());
|
||||||
|
|
||||||
|
document.node_graph_handler.node_graph_errors = node_graph_errors;
|
||||||
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
|
|
||||||
|
log::trace!("{e}");
|
||||||
|
return Err(format!("Node graph evaluation failed:\n{e}"));
|
||||||
|
}
|
||||||
|
Ok(result) => result,
|
||||||
|
};
|
||||||
|
// Always evaluate after a compilation
|
||||||
|
responses.add_front(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||||
|
responses.add_front(PortfolioMessage::ProcessCompilationResponse { compilation_metadata });
|
||||||
|
}
|
||||||
|
NodeGraphUpdate::EvaluationResponse(EvaluationResponse {
|
||||||
|
evaluation_id,
|
||||||
|
result,
|
||||||
|
introspected_nodes,
|
||||||
|
}) => {
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|
||||||
let node_graph_output = match result {
|
let node_graph_output = match result {
|
||||||
@@ -186,36 +227,17 @@ impl NodeGraphExecutor {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update artwork
|
// Update artwork
|
||||||
self.process_node_graph_output(render_output, responses)?
|
self.process_node_graph_output(render_output, introspected_nodes, responses)?
|
||||||
}
|
}
|
||||||
}
|
} // NodeGraphUpdate::IntrospectionResponse(introspection_response) => {
|
||||||
NodeGraphUpdate::CompilationResponse(compilation_response) => {
|
// responses.add_front(Message::ProcessIntrospectionQueue(introspection_response));
|
||||||
let CompilationResponse { node_graph_errors, result } = compilation_response;
|
// }
|
||||||
let compilation_metadata = match result {
|
|
||||||
Err(e) => {
|
|
||||||
// Clear the click targets while the graph is in an un-renderable state
|
|
||||||
document.network_interface.update_click_targets(HashMap::new());
|
|
||||||
document.network_interface.update_vector_modify(HashMap::new());
|
|
||||||
|
|
||||||
document.node_graph_handler.node_graph_errors = node_graph_errors;
|
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
|
||||||
|
|
||||||
log::trace!("{e}");
|
|
||||||
return Err(format!("Node graph evaluation failed:\n{e}"));
|
|
||||||
}
|
|
||||||
Ok(result) => result,
|
|
||||||
};
|
|
||||||
responses.add(PortfolioMessage::ProcessCompilationResponse { compilation_metadata });
|
|
||||||
}
|
|
||||||
NodeGraphUpdate::IntrospectionResponse(introspection_response) => {
|
|
||||||
responses.add(Message::ProcessIntrospectionQueue(introspection_response));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_node_graph_output(&self, render_output: RenderOutput, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
fn process_node_graph_output(&self, render_output: RenderOutput, introspected_nodes: IntrospectionResponse, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
||||||
match render_output.data {
|
match render_output.data {
|
||||||
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
|
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
|
||||||
// Send to frontend
|
// Send to frontend
|
||||||
@@ -234,7 +256,16 @@ impl NodeGraphExecutor {
|
|||||||
return Err(format!("Invalid node graph output type: {:#?}", render_output.data));
|
return Err(format!("Invalid node graph output type: {:#?}", render_output.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responses.add(Message::ProcessEvaluationQueue(render_output.metadata));
|
let context_during_evaluation = self
|
||||||
|
.runtime_io
|
||||||
|
.context_receiver
|
||||||
|
.try_iter()
|
||||||
|
.map(|(ids, index, context)| (ids, index, format!("{:?}", context)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if context_during_evaluation.len() != 0 {
|
||||||
|
responses.add_front(FrontendMessage::UpdateContextDuringEvaluation { context_during_evaluation });
|
||||||
|
}
|
||||||
|
responses.add_front(Message::ProcessEvaluationQueue(render_output.metadata, introspected_nodes));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub struct NodeRuntime {
|
|||||||
executor: DynamicExecutor,
|
executor: DynamicExecutor,
|
||||||
receiver: Receiver<GraphRuntimeRequest>,
|
receiver: Receiver<GraphRuntimeRequest>,
|
||||||
sender: NodeGraphRuntimeSender,
|
sender: NodeGraphRuntimeSender,
|
||||||
|
context_sender: Sender<(SNI, usize, EditorContext)>,
|
||||||
|
|
||||||
application_io: Option<Arc<WasmApplicationIo>>,
|
application_io: Option<Arc<WasmApplicationIo>>,
|
||||||
|
|
||||||
@@ -46,7 +47,6 @@ pub enum GraphRuntimeRequest {
|
|||||||
// ThumbnailRenderRequest(HashSet<CompiledProtonodeInput>),
|
// ThumbnailRenderRequest(HashSet<CompiledProtonodeInput>),
|
||||||
// Request the data from a list of node inputs. For example, used by vector modify to get the data at the input of every Path node.
|
// Request the data from a list of node inputs. For example, used by vector modify to get the data at the input of every Path node.
|
||||||
// Can also be used by the spreadsheet/introspection system
|
// Can also be used by the spreadsheet/introspection system
|
||||||
IntrospectionRequest(HashSet<SNI>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -69,19 +69,20 @@ impl NodeGraphRuntimeSender {
|
|||||||
fn send_evaluation_response(&self, response: EvaluationResponse) {
|
fn send_evaluation_response(&self, response: EvaluationResponse) {
|
||||||
self.0.send(NodeGraphUpdate::EvaluationResponse(response)).expect("Failed to send evaluation response")
|
self.0.send(NodeGraphUpdate::EvaluationResponse(response)).expect("Failed to send evaluation response")
|
||||||
}
|
}
|
||||||
fn send_introspection_response(&self, response: IntrospectionResponse) {
|
// fn send_introspection_response(&self, response: IntrospectionResponse) {
|
||||||
self.0.send(NodeGraphUpdate::IntrospectionResponse(response)).expect("Failed to send introspection response")
|
// self.0.send(NodeGraphUpdate::IntrospectionResponse(response)).expect("Failed to send introspection response")
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static NODE_RUNTIME: Lazy<Mutex<Option<NodeRuntime>>> = Lazy::new(|| Mutex::new(None));
|
pub static NODE_RUNTIME: Lazy<Mutex<Option<NodeRuntime>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
|
||||||
impl NodeRuntime {
|
impl NodeRuntime {
|
||||||
pub fn new(receiver: Receiver<GraphRuntimeRequest>, sender: Sender<NodeGraphUpdate>) -> Self {
|
pub fn new(receiver: Receiver<GraphRuntimeRequest>, sender: Sender<NodeGraphUpdate>, context_sender: Sender<(SNI, usize, EditorContext)>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
executor: DynamicExecutor::default(),
|
executor: DynamicExecutor::default(),
|
||||||
receiver,
|
receiver,
|
||||||
sender: NodeGraphRuntimeSender(sender.clone()),
|
sender: NodeGraphRuntimeSender(sender.clone()),
|
||||||
|
context_sender,
|
||||||
|
|
||||||
application_io: None,
|
application_io: None,
|
||||||
|
|
||||||
@@ -100,24 +101,26 @@ impl NodeRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This deduplication of messages may cause issues
|
// TODO: This deduplication of messages may cause issues
|
||||||
let mut compilation = None;
|
// let mut compilation = None;
|
||||||
let mut evaluation = None;
|
// let mut cache_enable = None;
|
||||||
let mut introspection = None;
|
// let mut evaluation = None;
|
||||||
for request in self.receiver.try_iter() {
|
// let mut introspection = None;
|
||||||
match request {
|
// for request in self.receiver.try_iter() {
|
||||||
GraphRuntimeRequest::CompilationRequest(_) => compilation = Some(request),
|
// match request {
|
||||||
GraphRuntimeRequest::EvaluationRequest(_) => evaluation = Some(request),
|
// GraphRuntimeRequest::CompilationRequest(_) => compilation = Some(request),
|
||||||
GraphRuntimeRequest::IntrospectionRequest(_) => introspection = Some(request),
|
// GraphRuntimeRequest::CacheEnableRequest(_) => cache_enable = Some(request),
|
||||||
}
|
// GraphRuntimeRequest::EvaluationRequest(_) => evaluation = Some(request),
|
||||||
}
|
// GraphRuntimeRequest::IntrospectionRequest(_) => introspection = Some(request),
|
||||||
let requests = [compilation, evaluation, introspection].into_iter().flatten();
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
for request in requests {
|
// let requests = [compilation, cache_enable, evaluation, introspection].into_iter().flatten();
|
||||||
|
|
||||||
|
if let Ok(request) = self.receiver.try_recv() {
|
||||||
match request {
|
match request {
|
||||||
GraphRuntimeRequest::CompilationRequest(CompilationRequest { network, font_cache, editor_metadata }) => {
|
GraphRuntimeRequest::CompilationRequest(CompilationRequest { network, font_cache, editor_metadata }) => {
|
||||||
// Insert the monitor node to manage the inspection
|
// Insert the monitor node to manage the inspection
|
||||||
// self.inspect_state = inspect_node.map(|inspect| InspectState::monitor_inspect_node(&mut network, inspect));
|
// self.inspect_state = inspect_node.map(|inspect| InspectState::monitor_inspect_node(&mut network, inspect));
|
||||||
|
|
||||||
self.node_graph_errors.clear();
|
self.node_graph_errors.clear();
|
||||||
let result = self.update_network(network, font_cache, editor_metadata).await;
|
let result = self.update_network(network, font_cache, editor_metadata).await;
|
||||||
self.sender.send_compilation_response(CompilationResponse {
|
self.sender.send_compilation_response(CompilationResponse {
|
||||||
@@ -125,25 +128,20 @@ impl NodeRuntime {
|
|||||||
node_graph_errors: self.node_graph_errors.clone(),
|
node_graph_errors: self.node_graph_errors.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Inputs to monitor is sent from the editor, and represents a list of input connectors to track the data through
|
|
||||||
// During the execution. If the value is None, then the node was not evaluated, which can occur due to caching
|
|
||||||
GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
|
GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
|
||||||
evaluation_id,
|
evaluation_id,
|
||||||
context,
|
context,
|
||||||
node_to_evaluate,
|
node_to_evaluate,
|
||||||
|
nodes_to_introspect,
|
||||||
}) => {
|
}) => {
|
||||||
// for (protonode_input, introspect_mode) in &inputs_to_monitor {
|
for node in &nodes_to_introspect {
|
||||||
// self.executor.set_introspect(*protonode_input, *introspect_mode)
|
self.executor.cache_first_evaluation(node)
|
||||||
// }
|
}
|
||||||
let result = self.executor.evaluate_from_node(context, node_to_evaluate).await;
|
let result = self.executor.evaluate_from_node(context, node_to_evaluate).await;
|
||||||
|
|
||||||
self.sender.send_evaluation_response(EvaluationResponse { evaluation_id, result });
|
|
||||||
}
|
|
||||||
// GraphRuntimeRequest::ThumbnailRenderRequest(_) => {}
|
|
||||||
GraphRuntimeRequest::IntrospectionRequest(nodes) => {
|
|
||||||
let mut introspected_nodes = Vec::new();
|
let mut introspected_nodes = Vec::new();
|
||||||
for protonode in nodes {
|
for protonode in nodes_to_introspect {
|
||||||
let introspected_data = match self.executor.introspect(protonode, true) {
|
let introspected_data = match self.executor.introspect(protonode) {
|
||||||
Ok(introspected_data) => introspected_data,
|
Ok(introspected_data) => introspected_data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not introspect protonode: {:?}, error: {:?}", protonode, e);
|
log::error!("Could not introspect protonode: {:?}, error: {:?}", protonode, e);
|
||||||
@@ -153,7 +151,11 @@ impl NodeRuntime {
|
|||||||
introspected_nodes.push((protonode, introspected_data));
|
introspected_nodes.push((protonode, introspected_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sender.send_introspection_response(IntrospectionResponse(introspected_nodes));
|
self.sender.send_evaluation_response(EvaluationResponse {
|
||||||
|
evaluation_id,
|
||||||
|
result,
|
||||||
|
introspected_nodes: IntrospectionResponse(introspected_nodes),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,15 +172,14 @@ impl NodeRuntime {
|
|||||||
|
|
||||||
// Modifies the NodeNetwork so the tagged values are removed and the document nodes with protonode implementations have their protonode ids set
|
// Modifies the NodeNetwork so the tagged values are removed and the document nodes with protonode implementations have their protonode ids set
|
||||||
// Needs to return a mapping of absolute input connectors to protonode callers, types for protonodes, and callers for protonodes, add/remove delta for resolved types
|
// Needs to return a mapping of absolute input connectors to protonode callers, types for protonodes, and callers for protonodes, add/remove delta for resolved types
|
||||||
let (proto_network, original_locations) = match scoped_network.flatten() {
|
let (proto_network, original_locations) = match scoped_network.compile() {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Error compiling network: {e:?}");
|
log::error!("Error compiling network: {e:?}");
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let result: Result<CompilationMetadata, String> = match self.executor.update(proto_network, None).await {
|
||||||
let result = match self.executor.update(proto_network).await {
|
|
||||||
Ok((types_to_add, types_to_remove)) => {
|
Ok((types_to_add, types_to_remove)) => {
|
||||||
// Used to remove thumbnails from the mapping of SNI to rendered SVG strings on the frontend, which occurs when the SNI is removed
|
// Used to remove thumbnails from the mapping of SNI to rendered SVG strings on the frontend, which occurs when the SNI is removed
|
||||||
// When native frontend rendering is possible, the strings can just be stored in the network interface for each protonode with the rest of the type metadata
|
// When native frontend rendering is possible, the strings can just be stored in the network interface for each protonode with the rest of the type metadata
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use std::sync::mpsc::TryRecvError;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
@@ -14,12 +15,13 @@ extern "C" {
|
|||||||
/// Handles communication with the NodeRuntime, either locally or via Tauri
|
/// Handles communication with the NodeRuntime, either locally or via Tauri
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NodeRuntimeIO {
|
pub struct NodeRuntimeIO {
|
||||||
// Send to
|
pub busy: bool,
|
||||||
#[cfg(any(not(feature = "tauri"), test))]
|
#[cfg(any(not(feature = "tauri"), test))]
|
||||||
sender: Sender<GraphRuntimeRequest>,
|
sender: Sender<GraphRuntimeRequest>,
|
||||||
#[cfg(all(feature = "tauri", not(test)))]
|
#[cfg(all(feature = "tauri", not(test)))]
|
||||||
sender: Sender<NodeGraphUpdate>,
|
sender: Sender<NodeGraphUpdate>,
|
||||||
receiver: Receiver<NodeGraphUpdate>,
|
receiver: Receiver<NodeGraphUpdate>,
|
||||||
|
pub context_receiver: Receiver<(SNI, usize, EditorContext)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NodeRuntimeIO {
|
impl Default for NodeRuntimeIO {
|
||||||
@@ -35,11 +37,14 @@ impl NodeRuntimeIO {
|
|||||||
{
|
{
|
||||||
let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
||||||
let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
||||||
futures::executor::block_on(replace_node_runtime(NodeRuntime::new(request_receiver, response_sender)));
|
let (context_sender, context_receiver) = std::sync::mpsc::channel();
|
||||||
|
futures::executor::block_on(replace_node_runtime(NodeRuntime::new(request_receiver, response_sender, context_sender)));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
busy: false,
|
||||||
sender: request_sender,
|
sender: request_sender,
|
||||||
receiver: response_receiver,
|
receiver: response_receiver,
|
||||||
|
context_receiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,19 +54,25 @@ impl NodeRuntimeIO {
|
|||||||
Self {
|
Self {
|
||||||
sender: response_sender,
|
sender: response_sender,
|
||||||
receiver: response_receiver,
|
receiver: response_receiver,
|
||||||
|
context_receiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
pub fn with_channels(sender: Sender<GraphRuntimeRequest>, receiver: Receiver<NodeGraphUpdate>) -> Self {
|
// pub fn with_channels(sender: Sender<GraphRuntimeRequest>, receiver: Receiver<NodeGraphUpdate>) -> Self {
|
||||||
Self { sender, receiver }
|
// Self { sender, receiver }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Sends a message to the NodeRuntime
|
/// Sends a message to the NodeRuntime
|
||||||
pub fn send(&self, message: GraphRuntimeRequest) -> Result<(), String> {
|
pub fn try_send(&mut self, message: GraphRuntimeRequest) -> Result<(), String> {
|
||||||
#[cfg(any(not(feature = "tauri"), test))]
|
#[cfg(any(not(feature = "tauri"), test))]
|
||||||
{
|
{
|
||||||
self.sender.send(message).map_err(|e| e.to_string())
|
if !self.busy {
|
||||||
|
self.busy = true;
|
||||||
|
self.sender.send(message).map_err(|e| e.to_string())
|
||||||
|
} else {
|
||||||
|
Err("Executor busy".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "tauri", not(test)))]
|
#[cfg(all(feature = "tauri", not(test)))]
|
||||||
@@ -76,7 +87,7 @@ impl NodeRuntimeIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Receives any pending updates from the NodeRuntime
|
/// Receives any pending updates from the NodeRuntime
|
||||||
pub fn receive(&self) -> impl Iterator<Item = NodeGraphUpdate> + use<'_> {
|
pub fn receive(&mut self) -> Result<NodeGraphUpdate, TryRecvError> {
|
||||||
// TODO: This introduces extra latency
|
// TODO: This introduces extra latency
|
||||||
#[cfg(all(feature = "tauri", not(test)))]
|
#[cfg(all(feature = "tauri", not(test)))]
|
||||||
{
|
{
|
||||||
@@ -90,7 +101,7 @@ impl NodeRuntimeIO {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.receiver.try_iter()
|
self.receiver.try_recv()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,6 @@ fn runtime_message(message: String) -> Result<(), String> {
|
|||||||
return Err("Failed to deserialize message".into());
|
return Err("Failed to deserialize message".into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let response = NODE_RUNTIME_IO.lock().as_ref().unwrap().as_ref().unwrap().send(message);
|
let response = NODE_RUNTIME_IO.lock().as_ref().unwrap().as_ref().unwrap().try_send(message);
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,12 @@
|
|||||||
updateLayerInTree(targetId, targetLayer);
|
updateLayerInTree(targetId, targetLayer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (updateDocumentLayerDetails) => {
|
||||||
|
const targetLayer = updateDocumentLayerDetails.data;
|
||||||
|
const targetId = targetLayer.id;
|
||||||
|
|
||||||
|
updateLayerInTree(targetId, targetLayer);
|
||||||
|
});
|
||||||
addEventListener("pointermove", clippingHover);
|
addEventListener("pointermove", clippingHover);
|
||||||
addEventListener("keydown", clippingKeyPress);
|
addEventListener("keydown", clippingKeyPress);
|
||||||
addEventListener("keyup", clippingKeyPress);
|
addEventListener("keyup", clippingKeyPress);
|
||||||
@@ -540,8 +546,8 @@
|
|||||||
<IconLabel icon="Clipped" class="clipped-arrow" tooltip={"Clipping mask is active (Alt-click border to release)"} />
|
<IconLabel icon="Clipped" class="clipped-arrow" tooltip={"Clipping mask is active (Alt-click border to release)"} />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
|
{#if $nodeGraph.thumbnails.get($nodeGraph.wires.get(listing.entry.id)?.get(BigInt(1))?.sni) !== undefined}
|
||||||
{@html $nodeGraph.thumbnails.get(listing.entry.id)}
|
{@html $nodeGraph.thumbnails.get($nodeGraph.wires.get(listing.entry.id)?.get(BigInt(1))?.sni)}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if listing.entry.name === "Artboard"}
|
{#if listing.entry.name === "Artboard"}
|
||||||
|
|||||||
@@ -230,6 +230,22 @@
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateCheckerboardString() {
|
||||||
|
let rects = "";
|
||||||
|
|
||||||
|
for (let y = 0; y < 6; y++) {
|
||||||
|
for (let x = 0; x < 8; x++) {
|
||||||
|
const isDark = (x + y) % 2 === 0;
|
||||||
|
const fill = isDark ? "#cccccc" : "#ffffff";
|
||||||
|
rects += `<rect x="${x * 4 - 16}" y="${y * 4 - 12}" width="${4.1}" height="${4.1}" fill="${fill}" />`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rects += ``;
|
||||||
|
|
||||||
|
return rects;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -317,10 +333,11 @@
|
|||||||
<div class="wires" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
<div class="wires" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||||
<svg>
|
<svg>
|
||||||
{#each $nodeGraph.wires.values() as map}
|
{#each $nodeGraph.wires.values() as map}
|
||||||
{#each map.values() as { pathString, dataType, thick, dashed }}
|
{#each map.values() as { pathString, dataType, thick, dashed, center, sni }}
|
||||||
{#if thick}
|
{#if thick}
|
||||||
<path
|
<path
|
||||||
d={pathString}
|
d={pathString}
|
||||||
|
class="wire-path"
|
||||||
style:--data-line-width={"8px"}
|
style:--data-line-width={"8px"}
|
||||||
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||||
@@ -513,8 +530,8 @@
|
|||||||
<span class="node-error hover" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
<span class="node-error hover" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
{#if $nodeGraph.thumbnails.has(node.id)}
|
{#if node.layerThumbnailSNI && $nodeGraph.thumbnails.has(node.layerThumbnailSNI)}
|
||||||
{@html $nodeGraph.thumbnails.get(node.id)}
|
{@html $nodeGraph.thumbnails.get(node.layerThumbnailSNI)}
|
||||||
{/if}
|
{/if}
|
||||||
<!-- Layer stacking top output -->
|
<!-- Layer stacking top output -->
|
||||||
{#if node.primaryOutput}
|
{#if node.primaryOutput}
|
||||||
@@ -613,8 +630,8 @@
|
|||||||
<div class="wires">
|
<div class="wires">
|
||||||
<svg>
|
<svg>
|
||||||
{#each $nodeGraph.wires.values() as map}
|
{#each $nodeGraph.wires.values() as map}
|
||||||
{#each map.values() as { pathString, dataType, thick, dashed, center, monitorSni }}
|
{#each map.values() as { pathString, dataType, thick, dashed, center, sni }}
|
||||||
{#if !thick}
|
{#if !thick && pathString}
|
||||||
<path
|
<path
|
||||||
d={pathString}
|
d={pathString}
|
||||||
class="wire-path"
|
class="wire-path"
|
||||||
@@ -623,20 +640,16 @@
|
|||||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||||
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
||||||
/>
|
/>
|
||||||
{/if}
|
<!-- <g transform={`translate(${center[0] - 30}, ${center[1] - 10})`}>
|
||||||
<!-- {console.log("center: ", center)} -->
|
<text fill="white">{sni}</text>
|
||||||
<!-- {console.log("monitorSni: ", monitorSni)} -->
|
</g> -->
|
||||||
{#if center && monitorSni}
|
|
||||||
<!-- {console.log("thumbnails: ", $nodeGraph.thumbnails.get(monitorSni))} -->
|
|
||||||
<g transform={`translate(${center[0]}, ${center[1]})`}>
|
|
||||||
{@html $nodeGraph.thumbnails.get(monitorSni)}
|
|
||||||
</g>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
{#if $nodeGraph.wirePathInProgress}
|
{#if $nodeGraph.wirePathInProgress}
|
||||||
<path
|
<path
|
||||||
d={$nodeGraph.wirePathInProgress?.pathString}
|
d={$nodeGraph.wirePathInProgress?.pathString}
|
||||||
|
class="wire-path"
|
||||||
style:--data-line-width={`${$nodeGraph.wirePathInProgress.thick ? 8 : 2}px`}
|
style:--data-line-width={`${$nodeGraph.wirePathInProgress.thick ? 8 : 2}px`}
|
||||||
style:--data-color={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()}-dim)`}
|
||||||
@@ -644,6 +657,22 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</svg>
|
</svg>
|
||||||
|
{#each $nodeGraph.wires.values() as map}
|
||||||
|
{#each map.values() as { pathString, center, sni, dataType, thick }}
|
||||||
|
{#if !thick && pathString && sni && center !== undefined}
|
||||||
|
<div
|
||||||
|
class="wire-thumbnail"
|
||||||
|
style:--offset-left-px={`${center[0] - 12}px`}
|
||||||
|
style:--offset-top-px={`${center[1] - 8}px`}
|
||||||
|
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||||
|
>
|
||||||
|
{#if sni && $nodeGraph.thumbnails.has(sni)}
|
||||||
|
{@html $nodeGraph.thumbnails.get(sni)}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Nodes -->
|
<!-- Nodes -->
|
||||||
@@ -891,7 +920,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -904,6 +932,23 @@
|
|||||||
stroke-dasharray: var(--data-dasharray);
|
stroke-dasharray: var(--data-dasharray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wire-thumbnail {
|
||||||
|
// background: var(--color-2-mildblack);
|
||||||
|
border: 1px solid var(--data-color-dim);
|
||||||
|
border-radius: 2px;
|
||||||
|
position: absolute;
|
||||||
|
left: var(--offset-left-px);
|
||||||
|
top: var(--offset-top-px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 24px;
|
||||||
|
height: 16px;
|
||||||
|
// background-color: white;
|
||||||
|
background-image: var(--color-transparent-checkered-background);
|
||||||
|
background-size: var(--color-transparent-checkered-background-size-mini);
|
||||||
|
background-position: var(--color-transparent-checkered-background-position-mini);
|
||||||
|
background-repeat: var(--color-transparent-checkered-background-repeat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.imports-and-exports {
|
.imports-and-exports {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export class JsMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TupleToVec2 = Transform(({ value }: { value: [number, number] | undefined }) => (value === undefined ? undefined : { x: value[0], y: value[1] }));
|
const TupleToVec2 = Transform(({ value }: { value: [number, number] | undefined }) => (value === undefined ? undefined : { x: value[0], y: value[1] }));
|
||||||
|
|
||||||
const ImportsToVec2Array = Transform(({ obj: { imports } }: { obj: { imports: [FrontendGraphOutput, number, number][] } }) =>
|
const ImportsToVec2Array = Transform(({ obj: { imports } }: { obj: { imports: [FrontendGraphOutput, number, number][] } }) =>
|
||||||
imports.map(([outputMetadata, x, y]) => ({ outputMetadata, position: { x, y } })),
|
imports.map(([outputMetadata, x, y]) => ({ outputMetadata, position: { x, y } })),
|
||||||
);
|
);
|
||||||
@@ -36,6 +37,10 @@ export class UpdateBox extends JsMessage {
|
|||||||
readonly box!: Box | undefined;
|
readonly box!: Box | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class UpdateGraphBreadcrumbPath extends JsMessage {
|
||||||
|
readonly breadcrumbPath!: bigint[];
|
||||||
|
}
|
||||||
|
|
||||||
export class UpdateClickTargets extends JsMessage {
|
export class UpdateClickTargets extends JsMessage {
|
||||||
readonly clickTargets!: FrontendClickTargets | undefined;
|
readonly clickTargets!: FrontendClickTargets | undefined;
|
||||||
}
|
}
|
||||||
@@ -57,6 +62,10 @@ export class UpdateContextMenuInformation extends JsMessage {
|
|||||||
readonly contextMenuInformation!: ContextMenuInformation | undefined;
|
readonly contextMenuInformation!: ContextMenuInformation | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class UpdateContextDuringEvaluation extends JsMessage {
|
||||||
|
readonly contextDuringEvaluation!: [bigint, number, string][];
|
||||||
|
}
|
||||||
|
|
||||||
export class UpdateImportsExports extends JsMessage {
|
export class UpdateImportsExports extends JsMessage {
|
||||||
@ImportsToVec2Array
|
@ImportsToVec2Array
|
||||||
readonly imports!: { outputMetadata: FrontendGraphOutput; position: XY }[];
|
readonly imports!: { outputMetadata: FrontendGraphOutput; position: XY }[];
|
||||||
@@ -109,7 +118,7 @@ export class UpdateNodeGraphWires extends JsMessage {
|
|||||||
readonly wires!: WireUpdate[];
|
readonly wires!: WireUpdate[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ClearAllNodeGraphWires extends JsMessage {}
|
export class ClearAllNodeGraphWirePaths extends JsMessage {}
|
||||||
|
|
||||||
export class UpdateNodeGraphTransform extends JsMessage {
|
export class UpdateNodeGraphTransform extends JsMessage {
|
||||||
readonly transform!: NodeGraphTransform;
|
readonly transform!: NodeGraphTransform;
|
||||||
@@ -128,6 +137,10 @@ export class UpdateThumbnails extends JsMessage {
|
|||||||
readonly add!: [bigint, string][];
|
readonly add!: [bigint, string][];
|
||||||
|
|
||||||
readonly clear!: bigint[];
|
readonly clear!: bigint[];
|
||||||
|
|
||||||
|
readonly wireSNIUpdates!: WireSNIUpdate[];
|
||||||
|
|
||||||
|
readonly layerSNIUpdates!: FrontendLayerSNIUpdate[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateNodeGraphSelection extends JsMessage {
|
export class UpdateNodeGraphSelection extends JsMessage {
|
||||||
@@ -266,6 +279,8 @@ export class FrontendGraphOutput {
|
|||||||
export class FrontendNode {
|
export class FrontendNode {
|
||||||
readonly isLayer!: boolean;
|
readonly isLayer!: boolean;
|
||||||
|
|
||||||
|
layerThumbnailSNI!: bigint | undefined;
|
||||||
|
|
||||||
readonly canBeLayer!: boolean;
|
readonly canBeLayer!: boolean;
|
||||||
|
|
||||||
readonly id!: bigint;
|
readonly id!: bigint;
|
||||||
@@ -302,6 +317,12 @@ export class FrontendNode {
|
|||||||
readonly uiOnly!: boolean;
|
readonly uiOnly!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FrontendLayerSNIUpdate {
|
||||||
|
readonly id!: bigint;
|
||||||
|
|
||||||
|
readonly sni!: bigint | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export class FrontendNodeType {
|
export class FrontendNodeType {
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
|
|
||||||
@@ -317,13 +338,12 @@ export class NodeGraphTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class WirePath {
|
export class WirePath {
|
||||||
readonly pathString!: string;
|
pathString!: string;
|
||||||
readonly dataType!: FrontendGraphDataType;
|
readonly dataType!: FrontendGraphDataType;
|
||||||
readonly thick!: boolean;
|
readonly thick!: boolean;
|
||||||
readonly dashed!: boolean;
|
readonly dashed!: boolean;
|
||||||
@TupleToVec2
|
readonly center!: [number, number] | undefined;
|
||||||
readonly center!: XY | undefined;
|
sni!: bigint | undefined;
|
||||||
readonly inputSni!: bigint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WireUpdate {
|
export class WireUpdate {
|
||||||
@@ -332,6 +352,12 @@ export class WireUpdate {
|
|||||||
readonly wirePathUpdate!: WirePath | undefined;
|
readonly wirePathUpdate!: WirePath | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class WireSNIUpdate {
|
||||||
|
readonly id!: bigint;
|
||||||
|
readonly inputIndex!: number;
|
||||||
|
readonly sni!: bigint | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export class IndexedDbDocumentDetails extends DocumentDetails {
|
export class IndexedDbDocumentDetails extends DocumentDetails {
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
id!: string;
|
id!: string;
|
||||||
@@ -1624,7 +1650,7 @@ type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHand
|
|||||||
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
||||||
|
|
||||||
export const messageMakers: Record<string, MessageMaker> = {
|
export const messageMakers: Record<string, MessageMaker> = {
|
||||||
ClearAllNodeGraphWires,
|
ClearAllNodeGraphWirePaths,
|
||||||
DisplayDialog,
|
DisplayDialog,
|
||||||
DisplayDialogDismiss,
|
DisplayDialogDismiss,
|
||||||
DisplayDialogPanic,
|
DisplayDialogPanic,
|
||||||
@@ -1653,8 +1679,10 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
TriggerVisitLink,
|
TriggerVisitLink,
|
||||||
UpdateActiveDocument,
|
UpdateActiveDocument,
|
||||||
UpdateBox,
|
UpdateBox,
|
||||||
|
UpdateGraphBreadcrumbPath,
|
||||||
UpdateClickTargets,
|
UpdateClickTargets,
|
||||||
UpdateContextMenuInformation,
|
UpdateContextMenuInformation,
|
||||||
|
UpdateContextDuringEvaluation,
|
||||||
UpdateDialogButtons,
|
UpdateDialogButtons,
|
||||||
UpdateDialogColumn1,
|
UpdateDialogColumn1,
|
||||||
UpdateDialogColumn2,
|
UpdateDialogColumn2,
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ import {
|
|||||||
type FrontendNode,
|
type FrontendNode,
|
||||||
type FrontendNodeType,
|
type FrontendNodeType,
|
||||||
type WirePath,
|
type WirePath,
|
||||||
ClearAllNodeGraphWires,
|
ClearAllNodeGraphWirePaths,
|
||||||
SendUIMetadata,
|
SendUIMetadata,
|
||||||
UpdateBox,
|
UpdateBox,
|
||||||
|
UpdateGraphBreadcrumbPath,
|
||||||
UpdateClickTargets,
|
UpdateClickTargets,
|
||||||
UpdateContextMenuInformation,
|
UpdateContextMenuInformation,
|
||||||
|
UpdateContextDuringEvaluation,
|
||||||
UpdateInSelectedNetwork,
|
UpdateInSelectedNetwork,
|
||||||
UpdateImportReorderIndex,
|
UpdateImportReorderIndex,
|
||||||
UpdateExportReorderIndex,
|
UpdateExportReorderIndex,
|
||||||
@@ -32,6 +34,7 @@ import {
|
|||||||
export function createNodeGraphState(editor: Editor) {
|
export function createNodeGraphState(editor: Editor) {
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
box: undefined as Box | undefined,
|
box: undefined as Box | undefined,
|
||||||
|
breadcrumbPath: [] as bigint[],
|
||||||
clickTargets: undefined as FrontendClickTargets | undefined,
|
clickTargets: undefined as FrontendClickTargets | undefined,
|
||||||
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
||||||
layerWidths: new Map<bigint, number>(),
|
layerWidths: new Map<bigint, number>(),
|
||||||
@@ -43,8 +46,9 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
addExport: undefined as { x: number; y: number } | undefined,
|
addExport: undefined as { x: number; y: number } | undefined,
|
||||||
nodes: new Map<bigint, FrontendNode>(),
|
nodes: new Map<bigint, FrontendNode>(),
|
||||||
visibleNodes: new Set<bigint>(),
|
visibleNodes: new Set<bigint>(),
|
||||||
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
|
/// The first key is the document node id. The index is the actual input index. The exports have a first key value of u32::MAX.
|
||||||
wires: new Map<bigint, Map<number, WirePath>>(),
|
wires: new Map<bigint, Map<number, WirePath>>(),
|
||||||
|
/// The first key is the caller stable node id
|
||||||
wirePathInProgress: undefined as WirePath | undefined,
|
wirePathInProgress: undefined as WirePath | undefined,
|
||||||
nodeDescriptions: new Map<string, string>(),
|
nodeDescriptions: new Map<string, string>(),
|
||||||
nodeTypes: [] as FrontendNodeType[],
|
nodeTypes: [] as FrontendNodeType[],
|
||||||
@@ -70,6 +74,13 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateGraphBreadcrumbPath, (updateGraphBreadcrumbPath) => {
|
||||||
|
update((state) => {
|
||||||
|
state.breadcrumbPath = updateGraphBreadcrumbPath.breadcrumbPath;
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => {
|
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.clickTargets = UpdateClickTargets.clickTargets;
|
state.clickTargets = UpdateClickTargets.clickTargets;
|
||||||
@@ -82,6 +93,11 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// editor.subscriptions.subscribeJsMessage(UpdateContextDuringEvaluation, (updateContextDuringEvaluation) => {
|
||||||
|
// update((state) => {
|
||||||
|
// return state;
|
||||||
|
// });
|
||||||
|
// });
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateImportReorderIndex, (updateImportReorderIndex) => {
|
editor.subscriptions.subscribeJsMessage(UpdateImportReorderIndex, (updateImportReorderIndex) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.reorderImportIndex = updateImportReorderIndex.importIndex;
|
state.reorderImportIndex = updateImportReorderIndex.importIndex;
|
||||||
@@ -118,7 +134,6 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
||||||
// console.log(updateNodeGraphNodes);
|
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.nodes.clear();
|
state.nodes.clear();
|
||||||
updateNodeGraphNodes.nodes.forEach((node) => {
|
updateNodeGraphNodes.nodes.forEach((node) => {
|
||||||
@@ -127,6 +142,7 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => {
|
editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.visibleNodes = new Set<bigint>(updateVisibleNodes.nodes);
|
state.visibleNodes = new Set<bigint>(updateVisibleNodes.nodes);
|
||||||
@@ -143,17 +159,33 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
state.wires.set(wireUpdate.id, inputMap);
|
state.wires.set(wireUpdate.id, inputMap);
|
||||||
}
|
}
|
||||||
if (wireUpdate.wirePathUpdate !== undefined) {
|
if (wireUpdate.wirePathUpdate !== undefined) {
|
||||||
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
|
const existing = inputMap.get(wireUpdate.inputIndex);
|
||||||
|
if (existing) {
|
||||||
|
inputMap.set(wireUpdate.inputIndex, {
|
||||||
|
...wireUpdate.wirePathUpdate,
|
||||||
|
sni: existing.sni,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
inputMap.delete(wireUpdate.inputIndex);
|
const existing = inputMap.get(wireUpdate.inputIndex);
|
||||||
|
if (existing) {
|
||||||
|
existing.pathString = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWires, (_) => {
|
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWirePaths, (_) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.wires.clear();
|
for (const [, innerMap] of state.wires) {
|
||||||
|
for (const [, wirePath] of innerMap) {
|
||||||
|
wirePath.pathString = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -178,7 +210,22 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
for (const id of updateThumbnails.clear) {
|
for (const id of updateThumbnails.clear) {
|
||||||
state.thumbnails.set(id, "");
|
state.thumbnails.set(id, "");
|
||||||
}
|
}
|
||||||
// console.log("thumbnails: ", state.thumbnails);
|
updateThumbnails.wireSNIUpdates.forEach((wireUpdate) => {
|
||||||
|
const inputMap = state.wires.get(wireUpdate.id);
|
||||||
|
if (inputMap) {
|
||||||
|
const wire = inputMap.get(wireUpdate.inputIndex);
|
||||||
|
if (wire) {
|
||||||
|
wire.sni = wireUpdate.sni;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateThumbnails.layerSNIUpdates.forEach((wireUpdate) => {
|
||||||
|
const node = state.nodes.get(wireUpdate.id);
|
||||||
|
if (node) {
|
||||||
|
node.layerThumbnailSNI = wireUpdate.sni;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+138
-26
@@ -1,7 +1,9 @@
|
|||||||
|
use dyn_any::StaticType;
|
||||||
use glam::{DAffine2, UVec2};
|
use glam::{DAffine2, UVec2};
|
||||||
|
|
||||||
use crate::transform::Footprint;
|
use crate::transform::Footprint;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::fmt;
|
||||||
use std::panic::Location;
|
use std::panic::Location;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -26,6 +28,14 @@ pub trait ExtractRealTime {
|
|||||||
fn try_real_time(&self) -> Option<f64>;
|
fn try_real_time(&self) -> Option<f64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ModifyDownstreamTransform: ExtractAll + CloneVarArgs {
|
||||||
|
fn apply_modification(self, modification: &DAffine2) -> Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WithIndex: ExtractAll + CloneVarArgs {
|
||||||
|
fn with_index(&self, index: usize) -> Context;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ExtractAnimationTime {
|
pub trait ExtractAnimationTime {
|
||||||
fn try_animation_time(&self) -> Option<f64>;
|
fn try_animation_time(&self) -> Option<f64>;
|
||||||
}
|
}
|
||||||
@@ -50,7 +60,7 @@ pub trait ExtractAll: ExtractFootprint + ExtractDownstreamTransform + ExtractInd
|
|||||||
|
|
||||||
impl<T: ?Sized + ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
|
impl<T: ?Sized + ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ContextDependency {
|
pub enum ContextDependency {
|
||||||
ExtractFootprint = 0b10000000,
|
ExtractFootprint = 0b10000000,
|
||||||
@@ -62,7 +72,7 @@ pub enum ContextDependency {
|
|||||||
ExtractVarArgs = 0b00000100,
|
ExtractVarArgs = 0b00000100,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct ContextDependencies(pub u8);
|
pub struct ContextDependencies(pub u8);
|
||||||
|
|
||||||
impl ContextDependencies {
|
impl ContextDependencies {
|
||||||
@@ -99,6 +109,34 @@ impl ContextDependencies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ContextDependencies {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut set = Vec::new();
|
||||||
|
let bits = self.0;
|
||||||
|
|
||||||
|
if bits & ContextDependency::ExtractFootprint as u8 != 0 {
|
||||||
|
set.push("ExtractFootprint");
|
||||||
|
}
|
||||||
|
if bits & ContextDependency::ExtractDownstreamTransform as u8 != 0 {
|
||||||
|
set.push("ExtractDownstreamTransform");
|
||||||
|
}
|
||||||
|
if bits & ContextDependency::ExtractRealTime as u8 != 0 {
|
||||||
|
set.push("ExtractRealTime");
|
||||||
|
}
|
||||||
|
if bits & ContextDependency::ExtractAnimationTime as u8 != 0 {
|
||||||
|
set.push("ExtractAnimationTime");
|
||||||
|
}
|
||||||
|
if bits & ContextDependency::ExtractIndex as u8 != 0 {
|
||||||
|
set.push("ExtractIndex");
|
||||||
|
}
|
||||||
|
if bits & ContextDependency::ExtractVarArgs as u8 != 0 {
|
||||||
|
set.push("ExtractVarArgs");
|
||||||
|
}
|
||||||
|
|
||||||
|
f.debug_list().entries(set).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum VarArgsResult {
|
pub enum VarArgsResult {
|
||||||
IndexOutOfBounds,
|
IndexOutOfBounds,
|
||||||
@@ -110,7 +148,7 @@ impl Ctx for () {}
|
|||||||
impl Ctx for Footprint {}
|
impl Ctx for Footprint {}
|
||||||
impl ExtractFootprint for () {
|
impl ExtractFootprint for () {
|
||||||
fn try_footprint(&self) -> Option<&Footprint> {
|
fn try_footprint(&self) -> Option<&Footprint> {
|
||||||
log::error!("tried to extract footprint form (), {}", Location::caller());
|
log::error!("tried to extract footprint from (), {}", Location::caller());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +167,7 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Option<T> {
|
|||||||
|
|
||||||
impl ExtractDownstreamTransform for () {
|
impl ExtractDownstreamTransform for () {
|
||||||
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
log::error!("tried to extract downstream transform form (), {}", Location::caller());
|
log::error!("tried to extract downstream transform from (), {}", Location::caller());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,6 +184,42 @@ impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Option
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Arc<T> {
|
||||||
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
|
(**self).try_downstream_transform()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtractDownstreamTransform for OwnedContextImpl {
|
||||||
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
|
self.downstream_transform.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExtractAll + CloneVarArgs + Sync> ModifyDownstreamTransform for Option<T> {
|
||||||
|
fn apply_modification(self, modification: &DAffine2) -> Context {
|
||||||
|
if let Some(inner) = self {
|
||||||
|
let mut context = OwnedContextImpl::from(inner);
|
||||||
|
context.try_apply_downstream_transform(modification);
|
||||||
|
context.into_context()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ctx + ExtractAll + CloneVarArgs + Sync> WithIndex for Option<T> {
|
||||||
|
fn with_index(&self, index: usize) -> Context {
|
||||||
|
if let Some(inner) = self {
|
||||||
|
let mut context = OwnedContextImpl::from(inner.clone());
|
||||||
|
context.set_index(index);
|
||||||
|
context.into_context()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ExtractRealTime + Sync> ExtractRealTime for Option<T> {
|
impl<T: ExtractRealTime + Sync> ExtractRealTime for Option<T> {
|
||||||
fn try_real_time(&self) -> Option<f64> {
|
fn try_real_time(&self) -> Option<f64> {
|
||||||
self.as_ref().and_then(|x| x.try_real_time())
|
self.as_ref().and_then(|x| x.try_real_time())
|
||||||
@@ -178,12 +252,6 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Arc<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Arc<T> {
|
|
||||||
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
|
||||||
(**self).try_downstream_transform()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ExtractRealTime + Sync> ExtractRealTime for Arc<T> {
|
impl<T: ExtractRealTime + Sync> ExtractRealTime for Arc<T> {
|
||||||
fn try_real_time(&self) -> Option<f64> {
|
fn try_real_time(&self) -> Option<f64> {
|
||||||
(**self).try_real_time()
|
(**self).try_real_time()
|
||||||
@@ -237,12 +305,6 @@ impl ExtractFootprint for OwnedContextImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtractDownstreamTransform for OwnedContextImpl {
|
|
||||||
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
|
||||||
self.downstream_transform.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtractRealTime for OwnedContextImpl {
|
impl ExtractRealTime for OwnedContextImpl {
|
||||||
fn try_real_time(&self) -> Option<f64> {
|
fn try_real_time(&self) -> Option<f64> {
|
||||||
self.real_time
|
self.real_time
|
||||||
@@ -395,6 +457,16 @@ impl OwnedContextImpl {
|
|||||||
self.parent = None
|
self.parent = None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_editor_context(&self) -> EditorContext {
|
||||||
|
EditorContext {
|
||||||
|
footprint: self.footprint,
|
||||||
|
downstream_transform: self.downstream_transform,
|
||||||
|
real_time: self.real_time,
|
||||||
|
animation_time: self.animation_time,
|
||||||
|
index: self.index,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OwnedContextImpl {
|
impl OwnedContextImpl {
|
||||||
@@ -404,9 +476,9 @@ impl OwnedContextImpl {
|
|||||||
pub fn set_downstream_transform(&mut self, transform: DAffine2) {
|
pub fn set_downstream_transform(&mut self, transform: DAffine2) {
|
||||||
self.downstream_transform = Some(transform);
|
self.downstream_transform = Some(transform);
|
||||||
}
|
}
|
||||||
pub fn try_apply_downstream_transform(&mut self, transform: DAffine2) {
|
pub fn try_apply_downstream_transform(&mut self, transform: &DAffine2) {
|
||||||
if let Some(downstream_transform) = self.downstream_transform {
|
if let Some(downstream_transform) = self.downstream_transform {
|
||||||
self.downstream_transform = Some(downstream_transform * transform);
|
self.downstream_transform = Some(downstream_transform * *transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_real_time(&mut self, time: f64) {
|
pub fn set_real_time(&mut self, time: f64) {
|
||||||
@@ -434,14 +506,10 @@ impl OwnedContextImpl {
|
|||||||
self.animation_time = Some(animation_time);
|
self.animation_time = Some(animation_time);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn with_index(mut self, index: usize) -> Self {
|
// pub fn with_index(mut self, index: usize) -> Self {
|
||||||
if let Some(current_index) = &mut self.index {
|
// self.index = Some(index);
|
||||||
current_index.push(index);
|
// self
|
||||||
} else {
|
// }
|
||||||
self.index = Some(vec![index]);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn into_context(self) -> Option<Arc<Self>> {
|
pub fn into_context(self) -> Option<Arc<Self>> {
|
||||||
Some(Arc::new(self))
|
Some(Arc::new(self))
|
||||||
}
|
}
|
||||||
@@ -465,6 +533,50 @@ impl OwnedContextImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct EditorContext {
|
||||||
|
pub footprint: Option<Footprint>,
|
||||||
|
pub downstream_transform: Option<DAffine2>,
|
||||||
|
pub real_time: Option<f64>,
|
||||||
|
pub animation_time: Option<f64>,
|
||||||
|
pub index: Option<usize>,
|
||||||
|
// #[serde(skip)]
|
||||||
|
// pub editor_var_args: Option<(Vec<String>, Vec<Arc<Box<[dyn std::any::Any + 'static + std::panic::UnwindSafe]>>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl StaticType for EditorContext {
|
||||||
|
type Static = EditorContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditorContext {
|
||||||
|
pub fn to_owned_context(&self) -> OwnedContextImpl {
|
||||||
|
let mut context = OwnedContextImpl::default();
|
||||||
|
if let Some(footprint) = self.footprint {
|
||||||
|
context.set_footprint(footprint);
|
||||||
|
}
|
||||||
|
if let Some(footprint) = self.footprint {
|
||||||
|
context.set_footprint(footprint);
|
||||||
|
}
|
||||||
|
// if let Some(downstream_transform) = self.downstream_transform {
|
||||||
|
// context.set_downstream_transform(downstream_transform);
|
||||||
|
// }
|
||||||
|
if let Some(real_time) = self.real_time {
|
||||||
|
context.set_real_time(real_time);
|
||||||
|
}
|
||||||
|
if let Some(animation_time) = self.animation_time {
|
||||||
|
context.set_animation_time(animation_time);
|
||||||
|
}
|
||||||
|
if let Some(index) = self.index {
|
||||||
|
context.set_index(index);
|
||||||
|
}
|
||||||
|
context
|
||||||
|
// if let Some(editor_var_args) = self.editor_var_args {
|
||||||
|
// let (variable_names, values)
|
||||||
|
// context.set_varargs((variable_names, values))
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #[derive(Default, Clone, Copy, dyn_any::DynAny)]
|
// #[derive(Default, Clone, Copy, dyn_any::DynAny)]
|
||||||
// pub struct ContextImpl<'a> {
|
// pub struct ContextImpl<'a> {
|
||||||
// pub(crate) footprint: Option<&'a Footprint>,
|
// pub(crate) footprint: Option<&'a Footprint>,
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ use crate::instances::{Instance, Instances};
|
|||||||
use crate::math::quad::Quad;
|
use crate::math::quad::Quad;
|
||||||
use crate::raster::image::Image;
|
use crate::raster::image::Image;
|
||||||
use crate::raster_types::{CPU, GPU, Raster, RasterDataTable};
|
use crate::raster_types::{CPU, GPU, Raster, RasterDataTable};
|
||||||
use crate::transform::TransformMut;
|
|
||||||
use crate::uuid::NodeId;
|
use crate::uuid::NodeId;
|
||||||
use crate::vector::{VectorData, VectorDataTable};
|
use crate::vector::{VectorData, VectorDataTable};
|
||||||
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
use crate::{ Color, Context, Ctx, ModifyDownstreamTransform};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
@@ -457,7 +456,7 @@ async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTabl
|
|||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
|
async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
|
||||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
ctx: impl Ctx + ModifyDownstreamTransform,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
Context -> GraphicGroupTable,
|
Context -> GraphicGroupTable,
|
||||||
Context -> VectorDataTable,
|
Context -> VectorDataTable,
|
||||||
@@ -471,13 +470,8 @@ async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
|
|||||||
background: Color,
|
background: Color,
|
||||||
clip: bool,
|
clip: bool,
|
||||||
) -> Artboard {
|
) -> Artboard {
|
||||||
let footprint = ctx.try_footprint().copied();
|
let modified_ctx = ctx.apply_modification(&DAffine2::from_translation(location.as_dvec2()));
|
||||||
let mut new_ctx = OwnedContextImpl::from(ctx);
|
let graphic_group = contents.eval(modified_ctx).await;
|
||||||
if let Some(mut footprint) = footprint {
|
|
||||||
footprint.translate(location.as_dvec2());
|
|
||||||
new_ctx = new_ctx.with_footprint(footprint);
|
|
||||||
}
|
|
||||||
let graphic_group = contents.eval(new_ctx.into_context()).await;
|
|
||||||
|
|
||||||
Artboard {
|
Artboard {
|
||||||
graphic_group: graphic_group.into(),
|
graphic_group: graphic_group.into(),
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ pub mod value;
|
|||||||
pub mod vector;
|
pub mod vector;
|
||||||
|
|
||||||
pub use crate as graphene_core;
|
pub use crate as graphene_core;
|
||||||
|
use crate::memo::MonitorIntrospectResult;
|
||||||
pub use blending::*;
|
pub use blending::*;
|
||||||
pub use context::*;
|
pub use context::*;
|
||||||
pub use ctor;
|
pub use ctor;
|
||||||
@@ -61,10 +62,14 @@ pub trait Node<'i, Input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If check if evaluated is true, then it returns None if the node has not been evaluated since the last introspection
|
// If check if evaluated is true, then it returns None if the node has not been evaluated since the last introspection
|
||||||
fn introspect(&self, _check_if_evaluated: bool) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
fn introspect(&self) -> MonitorIntrospectResult {
|
||||||
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
|
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
|
||||||
None
|
MonitorIntrospectResult::Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn permanently_enable_cache(&self) {}
|
||||||
|
|
||||||
|
fn cache_first_evaluation(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
|
|||||||
+117
-31
@@ -7,14 +7,37 @@ use std::ops::Deref;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MonitorMemoNodeState {
|
||||||
|
Disabled,
|
||||||
|
// Stores the first execution, then gets set first execution result, which stores if the value changed
|
||||||
|
StoreFirstEvaluation,
|
||||||
|
// Gets set back to disabled on introspection, and stores a boolean for if the value changed since the last introspection
|
||||||
|
FirstEvaluationResult(bool),
|
||||||
|
// Acts as a normal cache node, and stores a boolean for if the value changed since the last introspection
|
||||||
|
Enabled(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum MonitorIntrospectResult {
|
||||||
|
// If trying to inspect a none that cannot be introspected
|
||||||
|
Error,
|
||||||
|
Disabled,
|
||||||
|
// The cache node has not been evaluated since the state was set to StoreFirstEvaluation/Enabled
|
||||||
|
NotEvaluated,
|
||||||
|
// If the monitor node was evaluated, then its data must exist, so it is not an option.
|
||||||
|
// The boolean represents if the data changed since the last introspection
|
||||||
|
Evaluated((std::sync::Arc<dyn std::any::Any + Send + Sync>, bool)),
|
||||||
|
}
|
||||||
|
|
||||||
/// Caches the output of a given Node and acts as a proxy
|
/// Caches the output of a given Node and acts as a proxy
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MonitorMemoNode<T, CachedNode> {
|
pub struct MonitorMemoNode<T, CachedNode> {
|
||||||
// Introspection cache, uses the hash of the nullified context with default var args
|
// Introspection cache, uses the hash of the nullified context with default var args
|
||||||
// cache: Arc<Mutex<std::collections::HashMap<u64, Arc<T>>>>,
|
// cache: Arc<Mutex<std::collections::HashMap<u64, Arc<T>>>>,
|
||||||
cache: Arc<Mutex<Option<(u64, Arc<T>)>>>,
|
cache: Arc<Mutex<Option<(u64, Arc<T>)>>>,
|
||||||
node: CachedNode,
|
node: CachedNode,
|
||||||
changed_since_last_eval: Arc<Mutex<bool>>,
|
hash_on_last_introspection: Arc<Mutex<u64>>,
|
||||||
|
state: Arc<Mutex<MonitorMemoNodeState>>,
|
||||||
}
|
}
|
||||||
impl<'i, I: Hash + 'i + std::fmt::Debug, T: 'static + Clone + Send + Sync, CachedNode: 'i> Node<'i, I> for MonitorMemoNode<T, CachedNode>
|
impl<'i, I: Hash + 'i + std::fmt::Debug, T: 'static + Clone + Send + Sync, CachedNode: 'i> Node<'i, I> for MonitorMemoNode<T, CachedNode>
|
||||||
where
|
where
|
||||||
@@ -26,47 +49,110 @@ where
|
|||||||
type Output = DynFuture<'i, T>;
|
type Output = DynFuture<'i, T>;
|
||||||
|
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
fn eval(&'i self, input: I) -> Self::Output {
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut state = self.state.lock().unwrap();
|
||||||
input.hash(&mut hasher);
|
|
||||||
let hash = hasher.finish();
|
|
||||||
|
|
||||||
if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) {
|
// log::debug!("Monitor memo node state: {:?}", *state);
|
||||||
let cloned_data = (*data).clone();
|
|
||||||
Box::pin(async move { cloned_data })
|
if matches!(*state, MonitorMemoNodeState::Disabled | MonitorMemoNodeState::FirstEvaluationResult(_)) {
|
||||||
} else {
|
return Box::pin(self.node.eval(input));
|
||||||
let fut = self.node.eval(input);
|
}
|
||||||
let cache = self.cache.clone();
|
|
||||||
*self.changed_since_last_eval.lock().unwrap() = true;
|
let hash = {
|
||||||
Box::pin(async move {
|
let mut hasher = DefaultHasher::new();
|
||||||
let value = fut.await;
|
input.hash(&mut hasher);
|
||||||
*cache.lock().unwrap() = Some((hash, Arc::new(value.clone())));
|
hasher.finish()
|
||||||
value
|
};
|
||||||
})
|
|
||||||
|
// log::debug!(
|
||||||
|
// "Monitor memo node input: {:?}, hash: {:?}, previous_hash: {:?}",
|
||||||
|
// input,
|
||||||
|
// hash,
|
||||||
|
// self.cache.lock().unwrap().as_ref().map(|(h, _)| *h)
|
||||||
|
// );
|
||||||
|
|
||||||
|
let last_hash = *self.hash_on_last_introspection.lock().unwrap();
|
||||||
|
|
||||||
|
match &mut *state {
|
||||||
|
MonitorMemoNodeState::Enabled(changed) => {
|
||||||
|
*changed = last_hash != hash;
|
||||||
|
}
|
||||||
|
MonitorMemoNodeState::StoreFirstEvaluation => {
|
||||||
|
*state = MonitorMemoNodeState::FirstEvaluationResult(last_hash != hash);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cache_guard = self.cache.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some((cached_hash, cached_data)) = cache_guard.as_ref() {
|
||||||
|
if *cached_hash == hash {
|
||||||
|
let cloned_data = (**cached_data).clone();
|
||||||
|
return Box::pin(async move { cloned_data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(cache_guard);
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let value = self.node.eval(input).await;
|
||||||
|
*self.cache.lock().unwrap() = Some((hash, Arc::new(value.clone())));
|
||||||
|
value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn introspect(&self) -> MonitorIntrospectResult {
|
||||||
|
let mut state_guard = self.state.lock().unwrap();
|
||||||
|
match *state_guard {
|
||||||
|
MonitorMemoNodeState::Disabled => {
|
||||||
|
// Make sure to set the state to "StoreFirstEvaluation" or "Enabled" before trying to introspect
|
||||||
|
log::error!("Cannot introspect disabled monitor memo node");
|
||||||
|
MonitorIntrospectResult::Disabled
|
||||||
|
}
|
||||||
|
MonitorMemoNodeState::StoreFirstEvaluation => MonitorIntrospectResult::NotEvaluated,
|
||||||
|
MonitorMemoNodeState::FirstEvaluationResult(changed_since_last_introspection) => {
|
||||||
|
let (hash, cache_value) = self
|
||||||
|
.cache
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.map(|(hash, data)| (*hash, (*data).clone() as Arc<dyn std::any::Any + Send + Sync>))
|
||||||
|
.expect("Evaluated cache node must store data");
|
||||||
|
*self.hash_on_last_introspection.lock().unwrap() = hash;
|
||||||
|
*state_guard = MonitorMemoNodeState::Disabled;
|
||||||
|
MonitorIntrospectResult::Evaluated((cache_value, changed_since_last_introspection))
|
||||||
|
}
|
||||||
|
MonitorMemoNodeState::Enabled(changed_since_last_introspection) => {
|
||||||
|
let cache = self.cache.lock().unwrap().as_ref().map(|(hash, data)| (*hash, (*data).clone() as Arc<dyn std::any::Any + Send + Sync>));
|
||||||
|
match cache {
|
||||||
|
Some((hash, cache_value)) => {
|
||||||
|
*self.hash_on_last_introspection.lock().unwrap() = hash;
|
||||||
|
MonitorIntrospectResult::Evaluated((cache_value, changed_since_last_introspection))
|
||||||
|
}
|
||||||
|
None => MonitorIntrospectResult::NotEvaluated,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Consider returning a reference to the entire cache so the frontend reference is automatically updated as the context changes
|
fn permanently_enable_cache(&self) {
|
||||||
fn introspect(&self, check_if_evaluated: bool) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
*self.state.lock().unwrap() = MonitorMemoNodeState::Enabled(false);
|
||||||
let mut changed = self.changed_since_last_eval.lock().unwrap();
|
}
|
||||||
if check_if_evaluated {
|
|
||||||
if !*changed {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*changed = false;
|
|
||||||
|
|
||||||
let cache_guard = self.cache.lock().unwrap();
|
fn cache_first_evaluation(&self) {
|
||||||
let cached = cache_guard.as_ref().expect("Cached data should always be evaluated before introspection");
|
if matches!(*self.state.lock().unwrap(), MonitorMemoNodeState::Enabled(_)) {
|
||||||
Some(cached.1.clone() as Arc<dyn std::any::Any + Send + Sync>)
|
return;
|
||||||
|
}
|
||||||
|
*self.state.lock().unwrap() = MonitorMemoNodeState::StoreFirstEvaluation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, CachedNode> MonitorMemoNode<T, CachedNode> {
|
impl<T, CachedNode> MonitorMemoNode<T, CachedNode> {
|
||||||
pub fn new(node: CachedNode) -> MonitorMemoNode<T, CachedNode> {
|
pub fn new(node: CachedNode, state: MonitorMemoNodeState) -> MonitorMemoNode<T, CachedNode> {
|
||||||
MonitorMemoNode {
|
MonitorMemoNode {
|
||||||
cache: Default::default(),
|
cache: Default::default(),
|
||||||
node,
|
node,
|
||||||
changed_since_last_eval: Arc::new(Mutex::new(true)),
|
hash_on_last_introspection: Arc::new(Mutex::new(0)),
|
||||||
|
state: Arc::new(Mutex::new(state)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::memo::MonitorMemoNodeState;
|
||||||
use crate::{ContextDependencies, Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend};
|
use crate::{ContextDependencies, Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend};
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@@ -134,7 +135,7 @@ pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
|
|||||||
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
|
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
|
||||||
|
|
||||||
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
||||||
pub type CacheConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
|
pub type CacheConstructor = fn(SharedNodeContainer, MonitorMemoNodeState) -> TypeErasedBox<'static>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NodeContainer {
|
pub struct NodeContainer {
|
||||||
@@ -290,8 +291,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn introspect(&self, check_if_evaluated: bool) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
fn introspect(&self) -> crate::memo::MonitorIntrospectResult {
|
||||||
self.node.introspect(check_if_evaluated)
|
self.node.introspect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn permanently_enable_cache(&self) {
|
||||||
|
self.node.permanently_enable_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cache_first_evaluation(&self) {
|
||||||
|
self.node.cache_first_evaluation();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&self) {
|
fn reset(&self) {
|
||||||
|
|||||||
@@ -110,8 +110,10 @@ impl Footprint {
|
|||||||
quality: RenderQuality::Full,
|
quality: RenderQuality::Full,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox {
|
pub fn viewport_bounds_in_local_space(&self, downstream_transform: &DAffine2) -> AxisAlignedBbox {
|
||||||
let inverse = self.transform.inverse();
|
// TODO: Check if this is the correct way to apply downstream transforms
|
||||||
|
let transform = self.transform * *downstream_transform;
|
||||||
|
let inverse = transform.inverse();
|
||||||
let start = inverse.transform_point2((0., 0.).into());
|
let start = inverse.transform_point2((0., 0.).into());
|
||||||
let end = inverse.transform_point2(self.resolution.as_dvec2());
|
let end = inverse.transform_point2(self.resolution.as_dvec2());
|
||||||
AxisAlignedBbox { start, end }
|
AxisAlignedBbox { start, end }
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::instances::Instances;
|
use crate::instances::Instances;
|
||||||
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
||||||
use crate::transform::{ApplyTransform, Footprint, Transform};
|
use crate::transform::{Transform};
|
||||||
use crate::vector::VectorDataTable;
|
use crate::vector::VectorDataTable;
|
||||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, GraphicGroupTable, OwnedContextImpl};
|
use crate::{ Context, Ctx, GraphicGroupTable, ModifyDownstreamTransform, };
|
||||||
use core::f64;
|
use core::f64;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
async fn transform<T: 'n + 'static>(
|
async fn transform<T: 'n + 'static>(
|
||||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
ctx: impl Ctx + ModifyDownstreamTransform,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
Context -> VectorDataTable,
|
Context -> VectorDataTable,
|
||||||
Context -> GraphicGroupTable,
|
Context -> GraphicGroupTable,
|
||||||
@@ -23,15 +23,9 @@ async fn transform<T: 'n + 'static>(
|
|||||||
) -> Instances<T> {
|
) -> Instances<T> {
|
||||||
let matrix = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., skew.y, skew.x, 1., 0., 0.]);
|
let matrix = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., skew.y, skew.x, 1., 0., 0.]);
|
||||||
|
|
||||||
let footprint = ctx.try_footprint().copied();
|
let modified_ctx = ctx.apply_modification(&matrix);
|
||||||
|
|
||||||
let mut ctx = OwnedContextImpl::from(ctx);
|
let mut transform_target = transform_target.eval(modified_ctx).await;
|
||||||
if let Some(mut footprint) = footprint {
|
|
||||||
footprint.apply_transform(&matrix);
|
|
||||||
ctx = ctx.with_footprint(footprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut transform_target = transform_target.eval(ctx.into_context()).await;
|
|
||||||
|
|
||||||
for data_transform in transform_target.instance_mut_iter() {
|
for data_transform in transform_target.instance_mut_iter() {
|
||||||
*data_transform.transform = matrix * *data_transform.transform;
|
*data_transform.transform = matrix * *data_transform.transform;
|
||||||
@@ -51,39 +45,3 @@ fn replace_transform<Data, TransformInput: Transform>(
|
|||||||
}
|
}
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Debug"))]
|
|
||||||
async fn boundless_footprint<T: 'n + 'static>(
|
|
||||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
|
||||||
#[implementations(
|
|
||||||
Context -> VectorDataTable,
|
|
||||||
Context -> GraphicGroupTable,
|
|
||||||
Context -> RasterDataTable<CPU>,
|
|
||||||
Context -> RasterDataTable<GPU>,
|
|
||||||
Context -> String,
|
|
||||||
Context -> f64,
|
|
||||||
)]
|
|
||||||
transform_target: impl Node<Context<'static>, Output = T>,
|
|
||||||
) -> T {
|
|
||||||
let ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::BOUNDLESS);
|
|
||||||
|
|
||||||
transform_target.eval(ctx.into_context()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Debug"))]
|
|
||||||
async fn freeze_real_time<T: 'n + 'static>(
|
|
||||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
|
||||||
#[implementations(
|
|
||||||
Context -> VectorDataTable,
|
|
||||||
Context -> GraphicGroupTable,
|
|
||||||
Context -> RasterDataTable<CPU>,
|
|
||||||
Context -> RasterDataTable<GPU>,
|
|
||||||
Context -> String,
|
|
||||||
Context -> f64,
|
|
||||||
)]
|
|
||||||
transform_target: impl Node<Context<'static>, Output = T>,
|
|
||||||
) -> T {
|
|
||||||
let ctx = OwnedContextImpl::from(ctx).with_real_time(0.);
|
|
||||||
|
|
||||||
transform_target.eval(ctx.into_context()).await
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use crate::instances::{InstanceRef, Instances};
|
use crate::instances::{InstanceRef, Instances};
|
||||||
use crate::raster_types::{CPU, RasterDataTable};
|
use crate::raster_types::{CPU, RasterDataTable};
|
||||||
use crate::vector::VectorDataTable;
|
use crate::vector::VectorDataTable;
|
||||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, GraphicElement, GraphicGroupTable, OwnedContextImpl};
|
use crate::{ Context, Ctx, ExtractIndex, ExtractVarArgs, GraphicElement, GraphicGroupTable, WithIndex};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
|
|
||||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||||
async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
|
async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
|
||||||
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx,
|
ctx: impl Ctx + WithIndex,
|
||||||
points: VectorDataTable,
|
points: VectorDataTable,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
Context -> GraphicGroupTable,
|
Context -> GraphicGroupTable,
|
||||||
@@ -22,8 +22,7 @@ async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + '
|
|||||||
let mut iteration = async |index, point| {
|
let mut iteration = async |index, point| {
|
||||||
let transformed_point = transform.transform_point2(point);
|
let transformed_point = transform.transform_point2(point);
|
||||||
|
|
||||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(("Transformed point", Box::new(transformed_point)));
|
let generated_instance = instance.eval(ctx.with_index(index)).await;
|
||||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
|
||||||
|
|
||||||
for mut instanced in generated_instance.instance_iter() {
|
for mut instanced in generated_instance.instance_iter() {
|
||||||
instanced.transform.translation = transformed_point;
|
instanced.transform.translation = transformed_point;
|
||||||
@@ -48,7 +47,7 @@ async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + '
|
|||||||
|
|
||||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||||
async fn instance_repeat<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
|
async fn instance_repeat<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
|
||||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
ctx: impl Ctx + WithIndex,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
Context -> GraphicGroupTable,
|
Context -> GraphicGroupTable,
|
||||||
Context -> VectorDataTable,
|
Context -> VectorDataTable,
|
||||||
@@ -65,8 +64,7 @@ async fn instance_repeat<T: Into<GraphicElement> + Default + Send + Clone + 'sta
|
|||||||
for index in 0..count {
|
for index in 0..count {
|
||||||
let index = if reverse { count - index - 1 } else { index };
|
let index = if reverse { count - index - 1 } else { index };
|
||||||
|
|
||||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index);
|
let generated_instance = instance.eval(ctx.with_index(index)).await;
|
||||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
|
||||||
|
|
||||||
for instanced in generated_instance.instance_iter() {
|
for instanced in generated_instance.instance_iter() {
|
||||||
result_table.push(instanced);
|
result_table.push(instanced);
|
||||||
|
|||||||
@@ -8,19 +8,14 @@ use crate::bounds::BoundingBox;
|
|||||||
use crate::instances::{Instance, InstanceMut, Instances};
|
use crate::instances::{Instance, InstanceMut, Instances};
|
||||||
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
||||||
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
|
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
|
||||||
use crate::transform::{Footprint, ReferencePoint, Transform};
|
use crate::transform::{ReferencePoint, Transform};
|
||||||
use crate::vector::PointDomain;
|
|
||||||
use crate::vector::algorithms::bezpath_algorithms::{eval_pathseg_euclidean, is_linear};
|
|
||||||
use crate::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
use crate::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||||
use crate::vector::misc::{MergeByDistanceAlgorithm, PointSpacingType};
|
use crate::vector::misc::{MergeByDistanceAlgorithm, PointSpacingType};
|
||||||
use crate::vector::misc::{handles_to_segment, segment_to_handles};
|
use crate::vector::misc::{handles_to_segment, segment_to_handles};
|
||||||
use crate::vector::style::{PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
use crate::vector::style::{PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
use crate::vector::{FillId, RegionId};
|
use crate::vector::{FillId, PointDomain, RegionId};
|
||||||
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicElement, GraphicGroupTable, OwnedContextImpl};
|
use crate::{Color, Ctx, GraphicElement, GraphicGroupTable};
|
||||||
|
use bezier_rs::{Join, ManipulatorGroup, Subpath};
|
||||||
use bezier_rs::{BezierHandles, Join, ManipulatorGroup, Subpath};
|
|
||||||
use core::f64::consts::PI;
|
|
||||||
use core::hash::{Hash, Hasher};
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Shape};
|
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Shape};
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
@@ -2063,10 +2058,7 @@ async fn path_length(_: impl Ctx, source: VectorDataTable) -> f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<Context<'static>, Output = VectorDataTable>) -> f64 {
|
async fn area(_ctx: impl Ctx, vector_data: VectorDataTable) -> f64 {
|
||||||
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
|
|
||||||
let vector_data = vector_data.eval(new_ctx).await;
|
|
||||||
|
|
||||||
vector_data
|
vector_data
|
||||||
.instance_ref_iter()
|
.instance_ref_iter()
|
||||||
.map(|vector_data_instance| {
|
.map(|vector_data_instance| {
|
||||||
@@ -2077,10 +2069,7 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<Context<'static>, Output = VectorDataTable>, centroid_type: CentroidType) -> DVec2 {
|
async fn centroid(_ctx: impl Ctx, vector_data: VectorDataTable, centroid_type: CentroidType) -> DVec2 {
|
||||||
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
|
|
||||||
let vector_data = vector_data.eval(new_ctx).await;
|
|
||||||
|
|
||||||
if vector_data.is_empty() {
|
if vector_data.is_empty() {
|
||||||
return DVec2::ZERO;
|
return DVec2::ZERO;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ fn compile_to_proto(c: &mut Criterion) {
|
|||||||
|
|
||||||
for name in DEMO_ART {
|
for name in DEMO_ART {
|
||||||
let network = load_from_name(name);
|
let network = load_from_name(name);
|
||||||
c.bench_function(name, |b: &mut criterion::Bencher<'_>| b.iter_batched(|| network.clone(), |mut network| black_box(network.flatten()), criterion::BatchSize::SmallInput));
|
c.bench_function(name, |b: &mut criterion::Bencher<'_>| {
|
||||||
|
b.iter_batched(|| network.clone(), |mut network| black_box(network.compile()), criterion::BatchSize::SmallInput)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main}
|
|||||||
#[library_benchmark]
|
#[library_benchmark]
|
||||||
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = load_from_name)]
|
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = load_from_name)]
|
||||||
pub fn compile_to_proto(mut input: NodeNetwork) {
|
pub fn compile_to_proto(mut input: NodeNetwork) {
|
||||||
let _ = black_box(input.flatten());
|
let _ = black_box(input.compile());
|
||||||
}
|
}
|
||||||
|
|
||||||
library_benchmark_group!(name = compile_group; benchmarks = compile_to_proto);
|
library_benchmark_group!(name = compile_group; benchmarks = compile_to_proto);
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ pub struct DocumentNode {
|
|||||||
/// Represents the eye icon for hiding/showing the node in the graph UI. When hidden, a node gets replaced with an identity node during the graph flattening step.
|
/// Represents the eye icon for hiding/showing the node in the graph UI. When hidden, a node gets replaced with an identity node during the graph flattening step.
|
||||||
#[serde(default = "return_true")]
|
#[serde(default = "return_true")]
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
|
// Represents whether the output of the node should be cached. This is set to true whenever a node feeds into another node with more context dependencies
|
||||||
|
#[serde(default)]
|
||||||
|
pub cache_output: bool,
|
||||||
pub manual_composition: Option<Type>,
|
pub manual_composition: Option<Type>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub skip_deduplication: bool,
|
pub skip_deduplication: bool,
|
||||||
@@ -50,6 +53,7 @@ impl Hash for DocumentNode {
|
|||||||
self.inputs.hash(state);
|
self.inputs.hash(state);
|
||||||
self.implementation.hash(state);
|
self.implementation.hash(state);
|
||||||
self.visible.hash(state);
|
self.visible.hash(state);
|
||||||
|
self.cache_output.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +63,7 @@ impl Default for DocumentNode {
|
|||||||
inputs: Default::default(),
|
inputs: Default::default(),
|
||||||
implementation: Default::default(),
|
implementation: Default::default(),
|
||||||
visible: true,
|
visible: true,
|
||||||
|
cache_output: false,
|
||||||
manual_composition: Some(generic!(T)),
|
manual_composition: Some(generic!(T)),
|
||||||
skip_deduplication: false,
|
skip_deduplication: false,
|
||||||
}
|
}
|
||||||
@@ -518,7 +523,7 @@ impl NodeNetwork {
|
|||||||
/// Functions for compiling the network
|
/// Functions for compiling the network
|
||||||
impl NodeNetwork {
|
impl NodeNetwork {
|
||||||
// Returns a topologically sorted vec of protonodes, as well as metadata extracted during compilation
|
// Returns a topologically sorted vec of protonodes, as well as metadata extracted during compilation
|
||||||
pub fn flatten(&mut self) -> Result<(ProtoNetwork, Vec<(OriginalLocation, SNI)>), String> {
|
pub fn compile(&mut self) -> Result<(ProtoNetwork, Vec<(OriginalLocation, SNI)>), String> {
|
||||||
// These three arrays are stored in parallel
|
// These three arrays are stored in parallel
|
||||||
let mut protonetwork = Vec::new();
|
let mut protonetwork = Vec::new();
|
||||||
|
|
||||||
@@ -544,8 +549,8 @@ impl NodeNetwork {
|
|||||||
let Some(upstream_metadata) = upstream_metadata else {
|
let Some(upstream_metadata) = upstream_metadata else {
|
||||||
panic!("All inputs should be when the upstream SNI was generated");
|
panic!("All inputs should be when the upstream SNI was generated");
|
||||||
};
|
};
|
||||||
if upstream_metadata.is_value {
|
if !upstream_metadata.is_value {
|
||||||
context_dependencies.add_dependencies(&upstream_metadata.context_dependencies);
|
context_dependencies.add_dependencies(&upstream_metadata.nullify);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The context_dependencies are now the union of all inputs and the dependencies of the protonode. Set the dependencies of each input to the difference, which represents the data to nullify
|
// The context_dependencies are now the union of all inputs and the dependencies of the protonode. Set the dependencies of each input to the difference, which represents the data to nullify
|
||||||
@@ -554,9 +559,9 @@ impl NodeNetwork {
|
|||||||
panic!("All inputs should be when the upstream SNI was generated");
|
panic!("All inputs should be when the upstream SNI was generated");
|
||||||
};
|
};
|
||||||
match upstream_metadata.is_value {
|
match upstream_metadata.is_value {
|
||||||
true => upstream_metadata.context_dependencies.difference(&context_dependencies),
|
false => upstream_metadata.nullify.difference(&context_dependencies),
|
||||||
// If the upstream node is a Value node, do not nullify the context
|
// If the upstream node is a Value node, do not nullify the context
|
||||||
false => upstream_metadata.context_dependencies = ContextDependencies::none(),
|
true => upstream_metadata.nullify = ContextDependencies::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(context_dependencies.clone(), false)
|
(context_dependencies.clone(), false)
|
||||||
@@ -575,10 +580,7 @@ impl NodeNetwork {
|
|||||||
};
|
};
|
||||||
(deduplicated_protonode.callers, deduplicated_protonode.original_location)
|
(deduplicated_protonode.callers, deduplicated_protonode.original_location)
|
||||||
} else {
|
} else {
|
||||||
(
|
(std::mem::take(&mut protonode.callers), protonode.original_location.clone())
|
||||||
std::mem::take(&mut protonode.callers),
|
|
||||||
std::mem::replace(&mut protonode.original_location, OriginalLocation::Node(Vec::new())),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map the callers inputs to the generated stable node id
|
// Map the callers inputs to the generated stable node id
|
||||||
@@ -592,7 +594,7 @@ impl NodeNetwork {
|
|||||||
assert!(caller_index > current_protonode_index, "Caller index must be higher than current index");
|
assert!(caller_index > current_protonode_index, "Caller index must be higher than current index");
|
||||||
nodes.inputs[input_index] = Some(UpstreamInputMetadata {
|
nodes.inputs[input_index] = Some(UpstreamInputMetadata {
|
||||||
input_sni: stable_node_id,
|
input_sni: stable_node_id,
|
||||||
context_dependencies: protonode_context_dependencies.clone(),
|
nullify: protonode_context_dependencies.clone(),
|
||||||
is_value: upstream_is_value,
|
is_value: upstream_is_value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -726,7 +728,7 @@ impl NodeNetwork {
|
|||||||
log::error!("The node which was supposed to be flattened does not exist in the network, id {upstream_node_id}");
|
log::error!("The node which was supposed to be flattened does not exist in the network, id {upstream_node_id}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let cache_output = upstream_document_node.cache_output;
|
||||||
match &upstream_document_node.implementation {
|
match &upstream_document_node.implementation {
|
||||||
DocumentNodeImplementation::Network(_node_network) => {
|
DocumentNodeImplementation::Network(_node_network) => {
|
||||||
let traversal_input = AbsoluteInputConnector {
|
let traversal_input = AbsoluteInputConnector {
|
||||||
@@ -766,6 +768,7 @@ impl NodeNetwork {
|
|||||||
identifier,
|
identifier,
|
||||||
inputs: vec![None; number_of_inputs],
|
inputs: vec![None; number_of_inputs],
|
||||||
context_dependencies,
|
context_dependencies,
|
||||||
|
cache_output,
|
||||||
});
|
});
|
||||||
let protonode = ProtoNode {
|
let protonode = ProtoNode {
|
||||||
construction_args,
|
construction_args,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use graphene_core::uuid::NodeId;
|
|||||||
use graphene_core::vector::style::Fill;
|
use graphene_core::vector::style::Fill;
|
||||||
use graphene_core::{Color, MemoHash, Node, Type};
|
use graphene_core::{Color, MemoHash, Node, Type};
|
||||||
use graphene_svg_renderer::{GraphicElementRendered, RenderMetadata};
|
use graphene_svg_renderer::{GraphicElementRendered, RenderMetadata};
|
||||||
|
use std::cell::Cell;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -396,6 +397,7 @@ impl Display for TaggedValue {
|
|||||||
|
|
||||||
pub struct UpcastNode {
|
pub struct UpcastNode {
|
||||||
value: MemoHash<TaggedValue>,
|
value: MemoHash<TaggedValue>,
|
||||||
|
inspected: Cell<bool>,
|
||||||
}
|
}
|
||||||
impl<'input> Node<'input, DAny<'input>> for UpcastNode {
|
impl<'input> Node<'input, DAny<'input>> for UpcastNode {
|
||||||
type Output = FutureAny<'input>;
|
type Output = FutureAny<'input>;
|
||||||
@@ -403,10 +405,15 @@ impl<'input> Node<'input, DAny<'input>> for UpcastNode {
|
|||||||
fn eval(&'input self, _: DAny<'input>) -> Self::Output {
|
fn eval(&'input self, _: DAny<'input>) -> Self::Output {
|
||||||
Box::pin(async move { self.value.clone().into_inner().to_dynany() })
|
Box::pin(async move { self.value.clone().into_inner().to_dynany() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn introspect(&self) -> graphene_core::memo::MonitorIntrospectResult {
|
||||||
|
let inspected = self.inspected.replace(true);
|
||||||
|
graphene_core::memo::MonitorIntrospectResult::Evaluated((Arc::new(self.value.clone().into_inner()) as Arc<dyn std::any::Any + Send + Sync>, !inspected))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl UpcastNode {
|
impl UpcastNode {
|
||||||
pub fn new(value: MemoHash<TaggedValue>) -> Self {
|
pub fn new(value: MemoHash<TaggedValue>) -> Self {
|
||||||
Self { value }
|
Self { value, inspected: Cell::new(false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
@@ -426,8 +433,6 @@ impl<T: AsRef<U> + Sync + Send, U: Sync + Send> UpcastAsRefNode<T, U> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct RenderOutput {
|
pub struct RenderOutput {
|
||||||
pub data: RenderOutputType,
|
pub data: RenderOutputType,
|
||||||
@@ -540,25 +545,13 @@ mod fake_hash {
|
|||||||
|
|
||||||
macro_rules! thumbnail_render {
|
macro_rules! thumbnail_render {
|
||||||
( $( $ty:ty ),* $(,)? ) => {
|
( $( $ty:ty ),* $(,)? ) => {
|
||||||
pub fn render_thumbnail_if_change(new_value: &Arc<dyn std::any::Any + Send + Sync>, old_value: Option<&Arc<dyn std::any::Any + Send + Sync>>) -> ThumbnailRenderResult {
|
pub fn render_thumbnail(new_value: &Arc<dyn std::any::Any + Send + Sync>) -> Option<String> {
|
||||||
$(
|
$(
|
||||||
if let Some(new_value) = new_value.downcast_ref::<$ty>() {
|
if let Some(new_value) = new_value.downcast_ref::<$ty>() {
|
||||||
match old_value {
|
return Some(new_value.render_thumbnail());
|
||||||
None => return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail()),
|
|
||||||
Some(old_value) => {
|
|
||||||
if let Some(old_value) = old_value.downcast_ref::<$ty>() {
|
|
||||||
match new_value == old_value {
|
|
||||||
true => return ThumbnailRenderResult::NoChange,
|
|
||||||
false => return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
return ThumbnailRenderResult::ClearThumbnail;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ impl ProtoNetwork {
|
|||||||
pub struct UpstreamInputMetadata {
|
pub struct UpstreamInputMetadata {
|
||||||
pub input_sni: SNI,
|
pub input_sni: SNI,
|
||||||
// Context dependencies are accumulated during compilation, then replaced with the difference between the node's dependencies and the inputs dependencies
|
// Context dependencies are accumulated during compilation, then replaced with the difference between the node's dependencies and the inputs dependencies
|
||||||
pub context_dependencies: ContextDependencies,
|
pub nullify: ContextDependencies,
|
||||||
// If the upstream node is a value node, then do not nullify since the value nodes do not have a cache inserted after them
|
// If the upstream node is a value node, then do not nullify since the value nodes do not have a cache inserted after them
|
||||||
pub is_value: bool,
|
pub is_value: bool,
|
||||||
}
|
}
|
||||||
@@ -112,6 +112,7 @@ pub struct NodeConstructionArgs {
|
|||||||
pub inputs: Vec<Option<UpstreamInputMetadata>>,
|
pub inputs: Vec<Option<UpstreamInputMetadata>>,
|
||||||
// The union of all input context dependencies and the nodes context dependency. Used to generate the context nullification for the editor entry point
|
// The union of all input context dependencies and the nodes context dependency. Used to generate the context nullification for the editor entry point
|
||||||
pub context_dependencies: ContextDependencies,
|
pub context_dependencies: ContextDependencies,
|
||||||
|
pub cache_output: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -258,7 +259,7 @@ impl Debug for GraphErrorType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct GraphError {
|
pub struct GraphError {
|
||||||
pub original_location: OriginalLocation,
|
pub original_location: OriginalLocation,
|
||||||
pub identifier: Cow<'static, str>,
|
pub identifier: Cow<'static, str>,
|
||||||
@@ -279,11 +280,7 @@ impl GraphError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for GraphError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("NodeGraphError").field("identifier", &self.identifier.to_string()).field("error", &self.error).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub type GraphErrors = Vec<GraphError>;
|
pub type GraphErrors = Vec<GraphError>;
|
||||||
|
|
||||||
/// The `TypingContext` is used to store the types of the nodes indexed by their stable node id.
|
/// The `TypingContext` is used to store the types of the nodes indexed by their stable node id.
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ fn compile_graph(document_string: String, application_io: Arc<WasmApplicationIo>
|
|||||||
|
|
||||||
let mut wrapped_network = wrap_network_in_scope(network, Arc::new(FontCache::default()), EditorMetadata::default(), application_io);
|
let mut wrapped_network = wrap_network_in_scope(network, Arc::new(FontCache::default()), EditorMetadata::default(), application_io);
|
||||||
|
|
||||||
wrapped_network.flatten().map(|result| result.0).map_err(|x| x.into())
|
wrapped_network.compile().map(|result| result.0).map_err(|x| x.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
|
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use crate::adjustments::{CellularDistanceFunction, CellularReturnType, DomainWar
|
|||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use fastnoise_lite;
|
use fastnoise_lite;
|
||||||
use glam::{DAffine2, DVec2, Vec2};
|
use glam::{DAffine2, DVec2, Vec2};
|
||||||
|
use graphene_core::ExtractDownstreamTransform;
|
||||||
use graphene_core::blending::AlphaBlending;
|
use graphene_core::blending::AlphaBlending;
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::color::{Alpha, AlphaMut, Channel, LinearChannel, Luminance, RGBMut};
|
use graphene_core::color::{Alpha, AlphaMut, Channel, LinearChannel, Luminance, RGBMut};
|
||||||
@@ -30,7 +31,7 @@ impl From<std::io::Error> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Debug: Raster"))]
|
#[node_macro::node(category("Debug: Raster"))]
|
||||||
pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: RasterDataTable<CPU>) -> RasterDataTable<CPU> {
|
pub fn sample_image(ctx: impl ExtractFootprint + ExtractDownstreamTransform + Clone + Send, image_frame: RasterDataTable<CPU>) -> RasterDataTable<CPU> {
|
||||||
let mut result_table = RasterDataTable::default();
|
let mut result_table = RasterDataTable::default();
|
||||||
|
|
||||||
for mut image_frame_instance in image_frame.instance_iter() {
|
for mut image_frame_instance in image_frame.instance_iter() {
|
||||||
@@ -40,8 +41,17 @@ pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Rast
|
|||||||
// Resize the image using the image crate
|
// Resize the image using the image crate
|
||||||
let data = bytemuck::cast_vec(image.data.clone());
|
let data = bytemuck::cast_vec(image.data.clone());
|
||||||
|
|
||||||
let footprint = ctx.footprint();
|
// TODO: Feed as input for error handling
|
||||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
let Some(footprint) = ctx.try_footprint().cloned() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(downstream_transform) = ctx.try_downstream_transform() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport_bounds = footprint.viewport_bounds_in_local_space(downstream_transform);
|
||||||
|
|
||||||
let image_bounds = Bbox::from_transform(image_frame_transform).to_axis_aligned_bbox();
|
let image_bounds = Bbox::from_transform(image_frame_transform).to_axis_aligned_bbox();
|
||||||
let intersection = viewport_bounds.intersect(&image_bounds);
|
let intersection = viewport_bounds.intersect(&image_bounds);
|
||||||
let image_size = DAffine2::from_scale(DVec2::new(image.width as f64, image.height as f64));
|
let image_size = DAffine2::from_scale(DVec2::new(image.width as f64, image.height as f64));
|
||||||
@@ -313,7 +323,7 @@ pub fn image_value(_: impl Ctx, _primary: (), image: RasterDataTable<CPU>) -> Ra
|
|||||||
#[node_macro::node(category("Raster: Pattern"))]
|
#[node_macro::node(category("Raster: Pattern"))]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn noise_pattern(
|
pub fn noise_pattern(
|
||||||
ctx: impl ExtractFootprint + Ctx,
|
ctx: impl ExtractFootprint + ExtractDownstreamTransform + Ctx,
|
||||||
_primary: (),
|
_primary: (),
|
||||||
clip: bool,
|
clip: bool,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
@@ -331,8 +341,15 @@ pub fn noise_pattern(
|
|||||||
cellular_return_type: CellularReturnType,
|
cellular_return_type: CellularReturnType,
|
||||||
cellular_jitter: f64,
|
cellular_jitter: f64,
|
||||||
) -> RasterDataTable<CPU> {
|
) -> RasterDataTable<CPU> {
|
||||||
let footprint = ctx.footprint();
|
// TODO: Feed as input for error handling
|
||||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
let Some(footprint) = ctx.try_footprint().copied() else {
|
||||||
|
return RasterDataTable::default();
|
||||||
|
};
|
||||||
|
let Some(downstream_transform) = ctx.try_downstream_transform() else {
|
||||||
|
return RasterDataTable::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport_bounds = footprint.viewport_bounds_in_local_space(downstream_transform);
|
||||||
|
|
||||||
let mut size = viewport_bounds.size();
|
let mut size = viewport_bounds.size();
|
||||||
let mut offset = viewport_bounds.start;
|
let mut offset = viewport_bounds.start;
|
||||||
@@ -468,9 +485,17 @@ pub fn noise_pattern(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Raster: Pattern"))]
|
#[node_macro::node(category("Raster: Pattern"))]
|
||||||
pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> RasterDataTable<CPU> {
|
pub fn mandelbrot(ctx: impl ExtractFootprint + ExtractDownstreamTransform + Send) -> RasterDataTable<CPU> {
|
||||||
let footprint = ctx.footprint();
|
// TODO: Feed as input for error handling
|
||||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
let Some(footprint) = ctx.try_footprint().cloned() else {
|
||||||
|
return RasterDataTable::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(downstream_transform) = ctx.try_downstream_transform() else {
|
||||||
|
return RasterDataTable::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport_bounds = footprint.viewport_bounds_in_local_space(downstream_transform);
|
||||||
|
|
||||||
let image_bounds = Bbox::from_transform(DAffine2::IDENTITY).to_axis_aligned_bbox();
|
let image_bounds = Bbox::from_transform(DAffine2::IDENTITY).to_axis_aligned_bbox();
|
||||||
let intersection = viewport_bounds.intersect(&image_bounds);
|
let intersection = viewport_bounds.intersect(&image_bounds);
|
||||||
|
|||||||
+42
-59
@@ -1,15 +1,16 @@
|
|||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::DAffine2;
|
|
||||||
pub use graph_craft::proto::{Any, NodeContainer, TypeErasedBox, TypeErasedNode};
|
pub use graph_craft::proto::{Any, NodeContainer, TypeErasedBox, TypeErasedNode};
|
||||||
use graph_craft::proto::{DynFuture, FutureAny, SharedNodeContainer};
|
use graph_craft::proto::{DynFuture, FutureAny, SharedNodeContainer};
|
||||||
use graphene_core::Context;
|
use graphene_core::Context;
|
||||||
use graphene_core::ContextDependencies;
|
use graphene_core::ContextDependencies;
|
||||||
|
use graphene_core::EditorContext;
|
||||||
use graphene_core::NodeIO;
|
use graphene_core::NodeIO;
|
||||||
use graphene_core::OwnedContextImpl;
|
use graphene_core::OwnedContextImpl;
|
||||||
use graphene_core::WasmNotSend;
|
use graphene_core::WasmNotSend;
|
||||||
pub use graphene_core::registry::{DowncastBothNode, DynAnyNode, FutureWrapperNode, PanicNode};
|
pub use graphene_core::registry::{DowncastBothNode, DynAnyNode, FutureWrapperNode, PanicNode};
|
||||||
use graphene_core::transform::Footprint;
|
use graphene_core::uuid::SNI;
|
||||||
pub use graphene_core::{Node, generic, ops};
|
pub use graphene_core::{Node, generic, ops};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
pub trait IntoTypeErasedNode<'n> {
|
pub trait IntoTypeErasedNode<'n> {
|
||||||
fn into_type_erased(self) -> TypeErasedBox<'n>;
|
fn into_type_erased(self) -> TypeErasedBox<'n>;
|
||||||
@@ -72,63 +73,6 @@ pub fn downcast_node<I: StaticType, O: StaticType>(n: SharedNodeContainer) -> Do
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct EditorContext {
|
|
||||||
pub footprint: Option<Footprint>,
|
|
||||||
pub downstream_transform: Option<DAffine2>,
|
|
||||||
pub real_time: Option<f64>,
|
|
||||||
pub animation_time: Option<f64>,
|
|
||||||
pub index: Option<usize>,
|
|
||||||
// #[serde(skip)]
|
|
||||||
// pub editor_var_args: Option<(Vec<String>, Vec<Arc<Box<[dyn std::any::Any + 'static + std::panic::UnwindSafe]>>>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl StaticType for EditorContext {
|
|
||||||
type Static = EditorContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl Default for EditorContext {
|
|
||||||
// fn default() -> Self {
|
|
||||||
// EditorContext {
|
|
||||||
// footprint: None,
|
|
||||||
// downstream_transform: None,
|
|
||||||
// real_time: None,
|
|
||||||
// animation_time: None,
|
|
||||||
// index: None,
|
|
||||||
// // editor_var_args: None,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl EditorContext {
|
|
||||||
pub fn to_owned_context(&self) -> OwnedContextImpl {
|
|
||||||
let mut context = OwnedContextImpl::default();
|
|
||||||
if let Some(footprint) = self.footprint {
|
|
||||||
context.set_footprint(footprint);
|
|
||||||
}
|
|
||||||
if let Some(footprint) = self.footprint {
|
|
||||||
context.set_footprint(footprint);
|
|
||||||
}
|
|
||||||
// if let Some(downstream_transform) = self.downstream_transform {
|
|
||||||
// context.set_downstream_transform(downstream_transform);
|
|
||||||
// }
|
|
||||||
if let Some(real_time) = self.real_time {
|
|
||||||
context.set_real_time(real_time);
|
|
||||||
}
|
|
||||||
if let Some(animation_time) = self.animation_time {
|
|
||||||
context.set_animation_time(animation_time);
|
|
||||||
}
|
|
||||||
if let Some(index) = self.index {
|
|
||||||
context.set_index(index);
|
|
||||||
}
|
|
||||||
context
|
|
||||||
// if let Some(editor_var_args) = self.editor_var_args {
|
|
||||||
// let (variable_names, values)
|
|
||||||
// context.set_varargs((variable_names, values))
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NullificationNode {
|
pub struct NullificationNode {
|
||||||
first: SharedNodeContainer,
|
first: SharedNodeContainer,
|
||||||
nullify: ContextDependencies,
|
nullify: ContextDependencies,
|
||||||
@@ -142,7 +86,9 @@ impl<'i> Node<'i, Any<'i>> for NullificationNode {
|
|||||||
Ok(context) => match *context {
|
Ok(context) => match *context {
|
||||||
Some(context) => {
|
Some(context) => {
|
||||||
let mut new_context: OwnedContextImpl = OwnedContextImpl::from(context);
|
let mut new_context: OwnedContextImpl = OwnedContextImpl::from(context);
|
||||||
|
// log::debug!("Nullifying context: {:?} fields: {:?}", new_context, self.nullify);
|
||||||
new_context.nullify(&self.nullify);
|
new_context.nullify(&self.nullify);
|
||||||
|
// log::debug!("Evaluating input with: {:?}", new_context);
|
||||||
Box::new(new_context.into_context()) as Any<'i>
|
Box::new(new_context.into_context()) as Any<'i>
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -162,3 +108,40 @@ impl NullificationNode {
|
|||||||
Self { first, nullify }
|
Self { first, nullify }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ContextMonitorNode {
|
||||||
|
sni: SNI,
|
||||||
|
input_index: usize,
|
||||||
|
first: SharedNodeContainer,
|
||||||
|
sender: Sender<(SNI, usize, EditorContext)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> Node<'i, Any<'i>> for ContextMonitorNode {
|
||||||
|
type Output = DynFuture<'i, Any<'i>>;
|
||||||
|
fn eval(&'i self, input: Any<'i>) -> Self::Output {
|
||||||
|
Box::pin(async move {
|
||||||
|
let new_input = match dyn_any::try_downcast::<Context>(input) {
|
||||||
|
Ok(context) => match *context {
|
||||||
|
Some(context) => {
|
||||||
|
let editor_context = context.to_editor_context();
|
||||||
|
let _ = self.sender.clone().send((self.sni, self.input_index, editor_context));
|
||||||
|
Box::new(Some(context)) as Any<'i>
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let none: Context = None;
|
||||||
|
// self.sender.clone().send((self.sni, self.input_index, editor_context));
|
||||||
|
Box::new(none) as Any<'i>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(other_input) => other_input,
|
||||||
|
};
|
||||||
|
self.first.eval(new_input).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextMonitorNode {
|
||||||
|
pub fn new(sni: SNI, input_index: usize, first: SharedNodeContainer, sender: Sender<(SNI, usize, EditorContext)>) -> ContextMonitorNode {
|
||||||
|
ContextMonitorNode { sni, input_index, first, sender }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -212,11 +212,13 @@ pub trait GraphicElementRendered: BoundingBox + RenderComplexity {
|
|||||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams);
|
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams);
|
||||||
|
|
||||||
fn render_thumbnail(&self) -> String {
|
fn render_thumbnail(&self) -> String {
|
||||||
let bounds = self.bounding_box(DAffine2::IDENTITY, true);
|
let Some(bounds) = self.bounding_box(DAffine2::IDENTITY, true) else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
|
||||||
let render_params = RenderParams {
|
let render_params = RenderParams {
|
||||||
view_mode: ViewMode::Normal,
|
view_mode: ViewMode::Normal,
|
||||||
culling_bounds: bounds,
|
culling_bounds: Some(bounds),
|
||||||
thumbnail: true,
|
thumbnail: true,
|
||||||
hide_artboards: false,
|
hide_artboards: false,
|
||||||
for_export: false,
|
for_export: false,
|
||||||
@@ -224,15 +226,22 @@ pub trait GraphicElementRendered: BoundingBox + RenderComplexity {
|
|||||||
alignment_parent_transform: None,
|
alignment_parent_transform: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render the thumbnail data into an SVG string
|
|
||||||
let mut render = SvgRender::new();
|
let mut render = SvgRender::new();
|
||||||
self.render_svg(&mut render, &render_params);
|
self.render_svg(&mut render, &render_params);
|
||||||
|
|
||||||
|
// let center = (bounds[0] + bounds[1]) / 2.;
|
||||||
|
|
||||||
|
// let size = bounds[1] - bounds[0];
|
||||||
|
|
||||||
|
// let scale_x = 32.0 / size.x;
|
||||||
|
// let scale_y = 24.0 / size.y;
|
||||||
|
// let scale = scale_x.min(scale_y);
|
||||||
|
|
||||||
|
// render.wrap_with_transform()
|
||||||
// Give the SVG a viewbox and outer <svg>...</svg> wrapper tag
|
// Give the SVG a viewbox and outer <svg>...</svg> wrapper tag
|
||||||
// let [min, max] = bounds.unwrap_or_default();
|
render.format_svg(bounds[0], bounds[1]);
|
||||||
// render.format_svg(min, max);
|
|
||||||
|
|
||||||
render.svg.to_svg_string()
|
render.svg.to_svg_string()
|
||||||
|
// format!(r#"<g transform="scale({}) translate({}, {})">{}</g>"#, scale, -center.x, -center.y, render.svg.to_svg_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection.
|
/// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use interpreted_executor::dynamic_executor::DynamicExecutor;
|
|||||||
|
|
||||||
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
||||||
let mut network = load_from_name(name);
|
let mut network = load_from_name(name);
|
||||||
let proto_network = network.flatten().unwrap().0;
|
let proto_network = network.compile().unwrap().0;
|
||||||
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
|
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
|
||||||
(executor, proto_network)
|
(executor, proto_network)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use interpreted_executor::dynamic_executor::DynamicExecutor;
|
|||||||
|
|
||||||
fn update_executor<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
fn update_executor<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
||||||
let mut network = load_from_name(name);
|
let mut network = load_from_name(name);
|
||||||
let proto_network = network.flatten().unwrap().0;
|
let proto_network = network.compile().unwrap().0;
|
||||||
let empty = ProtoNetwork::default();
|
let empty = ProtoNetwork::default();
|
||||||
|
|
||||||
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
||||||
@@ -15,7 +15,7 @@ fn update_executor<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
|||||||
c.bench_function(name, |b| {
|
c.bench_function(name, |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| (executor.clone(), proto_network.clone()),
|
|| (executor.clone(), proto_network.clone()),
|
||||||
|(mut executor, network)| futures::executor::block_on(executor.update(black_box(network))),
|
|(mut executor, network)| futures::executor::block_on(executor.update(black_box(network, None))),
|
||||||
criterion::BatchSize::SmallInput,
|
criterion::BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -30,7 +30,7 @@ fn update_executor_demo(c: &mut Criterion) {
|
|||||||
|
|
||||||
fn run_once<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
fn run_once<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
||||||
let mut network = load_from_name(name);
|
let mut network = load_from_name(name);
|
||||||
let proto_network = network.flatten().unwrap().0;
|
let proto_network = network.compile().unwrap().0;
|
||||||
|
|
||||||
let executor = futures::executor::block_on(DynamicExecutor::new(proto_network)).unwrap();
|
let executor = futures::executor::block_on(DynamicExecutor::new(proto_network)).unwrap();
|
||||||
let context = graphene_std::any::EditorContext::default();
|
let context = graphene_std::any::EditorContext::default();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fn update_executor(c: &mut Criterion) {
|
|||||||
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
||||||
(executor, proto_network)
|
(executor, proto_network)
|
||||||
},
|
},
|
||||||
|(mut executor, network)| futures::executor::block_on(executor.update(criterion::black_box(network))),
|
|(mut executor, network)| futures::executor::block_on(executor.update(criterion::black_box(network, None))),
|
||||||
criterion::BatchSize::SmallInput,
|
criterion::BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ use graph_craft::document::value::{TaggedValue, UpcastNode};
|
|||||||
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNetwork, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext, UpstreamInputMetadata};
|
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNetwork, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext, UpstreamInputMetadata};
|
||||||
use graph_craft::proto::{GraphErrorType, GraphErrors};
|
use graph_craft::proto::{GraphErrorType, GraphErrors};
|
||||||
use graph_craft::{Type, concrete};
|
use graph_craft::{Type, concrete};
|
||||||
use graphene_std::any::{EditorContext, NullificationNode};
|
use graphene_std::any::{ContextMonitorNode, NullificationNode};
|
||||||
|
use graphene_std::memo::{MonitorIntrospectResult, MonitorMemoNodeState};
|
||||||
use graphene_std::uuid::{NodeId, SNI};
|
use graphene_std::uuid::{NodeId, SNI};
|
||||||
use graphene_std::{Context, ContextDependencies, NodeIOTypes};
|
use graphene_std::{Context, ContextDependencies, EditorContext, NodeIOTypes};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -21,7 +22,7 @@ pub struct DynamicExecutor {
|
|||||||
typing_context: TypingContext,
|
typing_context: TypingContext,
|
||||||
// TODO: Add lifetime for removed nodes so that if a SNI changes, then changes back to its previous SNI, the node does
|
// TODO: Add lifetime for removed nodes so that if a SNI changes, then changes back to its previous SNI, the node does
|
||||||
// not have to be reinserted
|
// not have to be reinserted
|
||||||
// lifetime: HashSet<(SNI, usize)>,
|
// lifetime: HashSet<(Vec<NodeId>, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DynamicExecutor {
|
impl Default for DynamicExecutor {
|
||||||
@@ -45,13 +46,14 @@ impl DynamicExecutor {
|
|||||||
|
|
||||||
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
||||||
#[cfg_attr(debug_assertions, inline(never))]
|
#[cfg_attr(debug_assertions, inline(never))]
|
||||||
pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<(Vec<(SNI, NodeIOTypes)>, Vec<SNI>), GraphErrors> {
|
pub async fn update(&mut self, proto_network: ProtoNetwork, context_sender: Option<&Sender<(SNI, usize, EditorContext)>>) -> Result<(Vec<(SNI, NodeIOTypes)>, Vec<SNI>), GraphErrors> {
|
||||||
self.output = Some(proto_network.output);
|
self.output = Some(proto_network.output);
|
||||||
self.typing_context.update(&proto_network)?;
|
self.typing_context.update(&proto_network)?;
|
||||||
let (add, orphaned_proto_nodes) = self.tree.update(proto_network, &self.typing_context).await?;
|
let (add, orphaned_proto_nodes) = self.tree.update(proto_network, &self.typing_context, context_sender).await?;
|
||||||
let mut remove = Vec::new();
|
let mut remove = Vec::new();
|
||||||
for sni in orphaned_proto_nodes {
|
for sni in orphaned_proto_nodes {
|
||||||
remove.push(sni);
|
remove.push(sni);
|
||||||
|
self.tree.free_node(&sni);
|
||||||
self.typing_context.remove_inference(&sni);
|
self.typing_context.remove_inference(&sni);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,9 +72,18 @@ impl DynamicExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Introspect the cached output of any protonode
|
// Introspect the cached output of any protonode
|
||||||
pub fn introspect(&self, protonode: SNI, check_if_evaluated: bool) -> Result<Option<Arc<dyn std::any::Any + Send + Sync>>, IntrospectError> {
|
pub fn introspect(&self, protonode: SNI) -> Result<MonitorIntrospectResult, IntrospectError> {
|
||||||
let inserted_node = self.tree.nodes.get(&protonode).ok_or(IntrospectError::ProtoNodeNotFound(protonode))?;
|
let inserted_node = self.tree.nodes.get(&protonode).ok_or(IntrospectError::ProtoNodeNotFound(protonode))?;
|
||||||
Ok(inserted_node.cached_protonode.introspect(check_if_evaluated))
|
Ok(inserted_node.cached_protonode.introspect())
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the cache is disabled, then it sets the state to save the first evaluation. If its enabled, then it does nothing
|
||||||
|
pub fn cache_first_evaluation(&self, protonode: &SNI) {
|
||||||
|
let Some(inserted_node) = self.tree.nodes.get(protonode) else {
|
||||||
|
log::error!("Could not get inserted protonode when setting cache_first_evaluation {:?}", protonode);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
inserted_node.cached_protonode.cache_first_evaluation();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_type(&self) -> Option<Type> {
|
pub fn input_type(&self) -> Option<Type> {
|
||||||
@@ -188,7 +199,6 @@ struct InsertedProtonode {
|
|||||||
/// A store of the dynamically typed nodes and also the source map.
|
/// A store of the dynamically typed nodes and also the source map.
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct BorrowTree {
|
pub struct BorrowTree {
|
||||||
// A hashmap of node IDs to dynamically typed proto nodes, as well as the auto inserted MonitorCache nodes, and editor entry point
|
|
||||||
nodes: HashMap<SNI, InsertedProtonode>,
|
nodes: HashMap<SNI, InsertedProtonode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,13 +206,18 @@ impl BorrowTree {
|
|||||||
pub async fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<BorrowTree, GraphErrors> {
|
pub async fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<BorrowTree, GraphErrors> {
|
||||||
let mut nodes = BorrowTree::default();
|
let mut nodes = BorrowTree::default();
|
||||||
for node in proto_network.into_nodes() {
|
for node in proto_network.into_nodes() {
|
||||||
nodes.push_node(node, typing_context).await?
|
nodes.push_node(node, typing_context, None).await?
|
||||||
}
|
}
|
||||||
Ok(nodes)
|
Ok(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes new nodes into the tree and returns a vec of document nodes that had their types changed, and a vec of all nodes that were removed (including auto inserted value nodes)
|
/// Pushes new nodes into the tree and returns a vec of document nodes that had their types changed, and a vec of all nodes that were removed (including auto inserted value nodes)
|
||||||
pub async fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<(Vec<SNI>, HashSet<SNI>), GraphErrors> {
|
pub async fn update(
|
||||||
|
&mut self,
|
||||||
|
proto_network: ProtoNetwork,
|
||||||
|
typing_context: &TypingContext,
|
||||||
|
context_sender: Option<&Sender<(SNI, usize, EditorContext)>>,
|
||||||
|
) -> Result<(Vec<SNI>, HashSet<SNI>), GraphErrors> {
|
||||||
let mut old_nodes = self.nodes.keys().copied().into_iter().collect::<HashSet<_>>();
|
let mut old_nodes = self.nodes.keys().copied().into_iter().collect::<HashSet<_>>();
|
||||||
// List of all document node paths that need to be updated, which occurs if their path changes or type changes
|
// List of all document node paths that need to be updated, which occurs if their path changes or type changes
|
||||||
let mut nodes_with_new_type = Vec::new();
|
let mut nodes_with_new_type = Vec::new();
|
||||||
@@ -211,7 +226,7 @@ impl BorrowTree {
|
|||||||
old_nodes.remove(&sni);
|
old_nodes.remove(&sni);
|
||||||
if !self.nodes.contains_key(&sni) {
|
if !self.nodes.contains_key(&sni) {
|
||||||
nodes_with_new_type.push(sni);
|
nodes_with_new_type.push(sni);
|
||||||
self.push_node(node, typing_context).await?;
|
self.push_node(node, typing_context, context_sender.clone()).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +348,7 @@ impl BorrowTree {
|
|||||||
/// - Uses the constructor function from the `typing_context` for `Nodes` construction arguments.
|
/// - Uses the constructor function from the `typing_context` for `Nodes` construction arguments.
|
||||||
/// - Returns an error if no constructor is found for the given node ID.
|
/// - Returns an error if no constructor is found for the given node ID.
|
||||||
/// Thumbnails is a mapping of the protonode input to the rendered thumbnail through the monitor cache node
|
/// Thumbnails is a mapping of the protonode input to the rendered thumbnail through the monitor cache node
|
||||||
async fn push_node(&mut self, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), GraphErrors> {
|
async fn push_node(&mut self, proto_node: ProtoNode, typing_context: &TypingContext, context_sender: Option<&Sender<(SNI, usize, EditorContext)>>) -> Result<(), GraphErrors> {
|
||||||
let sni = proto_node.stable_node_id;
|
let sni = proto_node.stable_node_id;
|
||||||
match proto_node.construction_args {
|
match proto_node.construction_args {
|
||||||
ConstructionArgs::Value(value) => {
|
ConstructionArgs::Value(value) => {
|
||||||
@@ -371,10 +386,23 @@ impl BorrowTree {
|
|||||||
let protonode_inputs = construction_nodes
|
let protonode_inputs = construction_nodes
|
||||||
.iter()
|
.iter()
|
||||||
.zip(node_construction_args.inputs.into_iter())
|
.zip(node_construction_args.inputs.into_iter())
|
||||||
.map(|(inserted_protonode, input_metadata)| {
|
.enumerate()
|
||||||
let previous_input = inserted_protonode.cached_protonode.clone();
|
.map(|(input_index, (upstream_inserted_protonode, input_metadata))| {
|
||||||
let input_context_dependencies = input_metadata.unwrap().context_dependencies;
|
let mut previous_input = upstream_inserted_protonode.cached_protonode.clone();
|
||||||
|
|
||||||
|
// Insert context monitoring if enabled
|
||||||
|
if let Some(context_sender) = context_sender {
|
||||||
|
let context_monitor = ContextMonitorNode::new(sni, input_index, previous_input, context_sender.clone());
|
||||||
|
let node = Box::new(context_monitor) as TypeErasedBox<'_>;
|
||||||
|
previous_input = NodeContainer::new(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_context_dependencies = input_metadata.unwrap().nullify;
|
||||||
if !input_context_dependencies.is_empty() {
|
if !input_context_dependencies.is_empty() {
|
||||||
|
// If nullifying the inputs such that the context is completely empty, then cache the upstream output
|
||||||
|
if upstream_inserted_protonode.nullify_when_calling == ContextDependencies::all_context_dependencies() {
|
||||||
|
upstream_inserted_protonode.cached_protonode.permanently_enable_cache();
|
||||||
|
}
|
||||||
let nullification_node = NullificationNode::new(previous_input, input_context_dependencies);
|
let nullification_node = NullificationNode::new(previous_input, input_context_dependencies);
|
||||||
let node = Box::new(nullification_node) as TypeErasedBox<'_>;
|
let node = Box::new(nullification_node) as TypeErasedBox<'_>;
|
||||||
NodeContainer::new(node)
|
NodeContainer::new(node)
|
||||||
@@ -387,18 +415,20 @@ impl BorrowTree {
|
|||||||
let node = constructor(protonode_inputs).await;
|
let node = constructor(protonode_inputs).await;
|
||||||
let protonode = NodeContainer::new(node);
|
let protonode = NodeContainer::new(node);
|
||||||
|
|
||||||
// Insert cache nodes on the output if possible
|
// When evaluating the node from the editor, nullify all context fields it is not dependent on
|
||||||
|
let nullify_when_calling = node_construction_args.context_dependencies.inverse();
|
||||||
|
|
||||||
let cached_protonode = if let Some(cache_constructor) = typing_context.cache_constructor(&types.return_value.nested_type()) {
|
let cached_protonode = if let Some(cache_constructor) = typing_context.cache_constructor(&types.return_value.nested_type()) {
|
||||||
let cache = cache_constructor(protonode);
|
let cache = cache_constructor(protonode, MonitorMemoNodeState::Disabled);
|
||||||
let cache_node_container = NodeContainer::new(cache);
|
let cache_node_container = NodeContainer::new(cache);
|
||||||
|
if node_construction_args.cache_output {
|
||||||
|
cache_node_container.permanently_enable_cache();
|
||||||
|
}
|
||||||
cache_node_container
|
cache_node_container
|
||||||
} else {
|
} else {
|
||||||
protonode
|
protonode
|
||||||
};
|
};
|
||||||
|
|
||||||
// When evaluating the node from the editor, nullify all context fields it is not dependent on
|
|
||||||
let nullify_when_calling = node_construction_args.context_dependencies.inverse();
|
|
||||||
|
|
||||||
let inserted_protonode = InsertedProtonode {
|
let inserted_protonode = InsertedProtonode {
|
||||||
cached_protonode,
|
cached_protonode,
|
||||||
nullify_when_calling,
|
nullify_when_calling,
|
||||||
@@ -411,27 +441,27 @@ impl BorrowTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod test {
|
// mod test {
|
||||||
use super::*;
|
// use super::*;
|
||||||
use graph_craft::{document::value::TaggedValue, proto::NodeValueArgs};
|
// use graph_craft::{document::value::TaggedValue, proto::NodeValueArgs};
|
||||||
use graphene_std::uuid::NodeId;
|
// use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn push_node_sync() {
|
// fn push_node_sync() {
|
||||||
let mut tree = BorrowTree::default();
|
// let mut tree = BorrowTree::default();
|
||||||
let val_1_protonode = ProtoNode::value(
|
// let val_1_protonode = ProtoNode::value(
|
||||||
ConstructionArgs::Value(NodeValueArgs {
|
// ConstructionArgs::Value(NodeValueArgs {
|
||||||
value: Some(TaggedValue::U32(2u32).into()),
|
// value: Some(TaggedValue::U32(2u32).into()),
|
||||||
connector_paths: Vec::new(),
|
// connector_paths: Vec::new(),
|
||||||
}),
|
// }),
|
||||||
NodeId(0),
|
// NodeId(0),
|
||||||
);
|
// );
|
||||||
let context = TypingContext::default();
|
// let context = TypingContext::default();
|
||||||
let future = tree.push_node(val_1_protonode, &context);
|
// let future = tree.push_node(val_1_protonode, &context);
|
||||||
futures::executor::block_on(future).unwrap();
|
// futures::executor::block_on(future).unwrap();
|
||||||
let _node = tree.nodes.get(&NodeId(0)).expect("Node should be added to tree");
|
// let _node = tree.nodes.get(&NodeId(0)).expect("Node should be added to tree");
|
||||||
let result = futures::executor::block_on(tree.eval_tagged_value(NodeId(0), ()));
|
// let result = futures::executor::block_on(tree.eval_tagged_value(NodeId(0), ()));
|
||||||
assert_eq!(result, Some(TaggedValue::U32(2u32).into()));
|
// assert_eq!(result, Some(TaggedValue::U32(2u32).into()));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::dynamic_executor::DynamicExecutor;
|
use crate::dynamic_executor::DynamicExecutor;
|
||||||
|
|
||||||
let protonetwork = network.flatten().map(|result| result.0).expect("Graph should be generated");
|
let protonetwork = network.compile().map(|result| result.0).expect("Graph should be generated");
|
||||||
|
|
||||||
let _exec = block_on(DynamicExecutor::new(protonetwork)).map(|_e| panic!("The network should not type check ")).unwrap_err();
|
let _exec = block_on(DynamicExecutor::new(protonetwork)).map(|_e| panic!("The network should not type check ")).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,8 +332,8 @@ mod node_registry_macros {
|
|||||||
|
|
||||||
macro_rules! cache_node {
|
macro_rules! cache_node {
|
||||||
($type:ty) => {
|
($type:ty) => {
|
||||||
(concrete!($type), |arg| {
|
(concrete!($type), |arg, state| {
|
||||||
let node = <graphene_core::memo::MonitorMemoNode<_, _>>::new(graphene_std::registry::downcast_node::<graphene_std::Context, $type>(arg));
|
let node = <graphene_core::memo::MonitorMemoNode<_, _>>::new(graphene_std::registry::downcast_node::<graphene_std::Context, $type>(arg), state);
|
||||||
let any: DynAnyNode<_, _, _> = graphene_std::any::DynAnyNode::new(node);
|
let any: DynAnyNode<_, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
Box::new(any) as TypeErasedBox
|
Box::new(any) as TypeErasedBox
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -643,6 +643,14 @@ impl ParsedNodeFn {
|
|||||||
"ExtractAnimationTime" => dependency_tokens.push(quote::quote! {ExtractAnimationTime}),
|
"ExtractAnimationTime" => dependency_tokens.push(quote::quote! {ExtractAnimationTime}),
|
||||||
"ExtractIndex" => dependency_tokens.push(quote::quote! {ExtractIndex}),
|
"ExtractIndex" => dependency_tokens.push(quote::quote! {ExtractIndex}),
|
||||||
"ExtractVarArgs" => dependency_tokens.push(quote::quote! {ExtractVarArgs}),
|
"ExtractVarArgs" => dependency_tokens.push(quote::quote! {ExtractVarArgs}),
|
||||||
|
"ExtractAll" => {
|
||||||
|
dependency_tokens.push(quote::quote! {ExtractFootprint});
|
||||||
|
dependency_tokens.push(quote::quote! {ExtractDownstreamTransform});
|
||||||
|
dependency_tokens.push(quote::quote! {ExtractRealTime});
|
||||||
|
dependency_tokens.push(quote::quote! {ExtractAnimationTime});
|
||||||
|
dependency_tokens.push(quote::quote! {ExtractIndex});
|
||||||
|
dependency_tokens.push(quote::quote! {ExtractVarArgs});
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user