mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 05:28:11 +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).
|
||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::CompileActiveDocument),
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::EvaluateActiveDocument),
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::IntrospectActiveDocument),
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::EvaluateActiveDocumentWithThumbnails),
|
||||
// MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::IntrospectActiveDocument),
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||
PropertiesPanelMessageDiscriminant::Refresh,
|
||||
))),
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||
PropertiesPanelMessageDiscriminant::Refresh,
|
||||
))),
|
||||
@@ -123,13 +126,13 @@ impl Dispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
// Add all messages to the queue if queuing messages (except from the end queue message)
|
||||
if !matches!(message, Message::EndIntrospectionQueue) {
|
||||
if self.queueing_introspection_messages {
|
||||
self.introspection_queue.push(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// // Add all messages to the queue if queuing messages (except from the end queue message)
|
||||
// if !matches!(message, Message::EndIntrospectionQueue) {
|
||||
// if self.queueing_introspection_messages {
|
||||
// self.introspection_queue.push(message);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// 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);
|
||||
|
||||
@@ -144,9 +147,10 @@ impl Dispatcher {
|
||||
Message::EndEvaluationQueue => {
|
||||
self.queueing_evaluation_messages = false;
|
||||
}
|
||||
Message::ProcessEvaluationQueue(render_output_metadata) => {
|
||||
Message::ProcessEvaluationQueue(render_output_metadata, introspected_nodes) => {
|
||||
let update_message = PortfolioMessage::ProcessEvaluationResponse {
|
||||
evaluation_metadata: render_output_metadata,
|
||||
introspected_nodes,
|
||||
}
|
||||
.into();
|
||||
// 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)
|
||||
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();
|
||||
// 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]);
|
||||
}
|
||||
Message::NoOp => {}
|
||||
|
||||
@@ -84,7 +84,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
||||
}
|
||||
AnimationMessage::SetFrameIndex { frame } => {
|
||||
self.frame_index = frame;
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||
// Update the restart and pause/play buttons
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
||||
}
|
||||
AnimationMessage::UpdateTime => {
|
||||
if self.is_playing() {
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||
|
||||
if self.live_preview_recently_zero {
|
||||
// Update the restart and pause/play buttons
|
||||
@@ -116,7 +116,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
||||
_ => AnimationState::Stopped,
|
||||
};
|
||||
self.live_preview_recently_zero = true;
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||
// Update the restart and pause/play buttons
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,8 +31,8 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
||||
}
|
||||
responses.add(Message::StartEvaluationQueue);
|
||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||
responses.add(Message::EndEvaluationQueue);
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
responses.add(Message::EndEvaluationQueue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
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::wires::{WirePath, WirePathUpdate};
|
||||
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate, WireSNIUpdate};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::HintData;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::text::Font;
|
||||
use graphene_std::uuid::{NodeId, SNI};
|
||||
|
||||
#[impl_message(Message, Frontend)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -128,6 +128,10 @@ pub enum FrontendMessage {
|
||||
#[serde(rename = "box")]
|
||||
box_selection: Option<BoxSelection>,
|
||||
},
|
||||
UpdateContextDuringEvaluation {
|
||||
#[serde(rename = "contextDuringEvaluation")]
|
||||
context_during_evaluation: Vec<(SNI, usize, String)>,
|
||||
},
|
||||
UpdateContextMenuInformation {
|
||||
#[serde(rename = "contextMenuInformation")]
|
||||
context_menu_information: Option<ContextMenuInformation>,
|
||||
@@ -224,6 +228,10 @@ pub enum FrontendMessage {
|
||||
#[serde(rename = "setColorChoice")]
|
||||
set_color_choice: Option<String>,
|
||||
},
|
||||
UpdateGraphBreadcrumbPath {
|
||||
#[serde(rename = "breadcrumbPath")]
|
||||
breadcrumb_path: Vec<NodeId>,
|
||||
},
|
||||
UpdateGraphFadeArtwork {
|
||||
percentage: f64,
|
||||
},
|
||||
@@ -263,7 +271,7 @@ pub enum FrontendMessage {
|
||||
UpdateNodeGraphWires {
|
||||
wires: Vec<WirePathUpdate>,
|
||||
},
|
||||
ClearAllNodeGraphWires,
|
||||
ClearAllNodeGraphWirePaths,
|
||||
UpdateNodeGraphControlBarLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
@@ -287,7 +295,10 @@ pub enum FrontendMessage {
|
||||
UpdateThumbnails {
|
||||
add: Vec<(NodeId, String)>,
|
||||
clear: Vec<NodeId>,
|
||||
// remove: Vec<NodeId>,
|
||||
#[serde(rename = "wireSNIUpdates")]
|
||||
wire_sni_updates: Vec<WireSNIUpdate>,
|
||||
#[serde(rename = "layerSNIUpdates")]
|
||||
layer_sni_updates: Vec<FrontendNodeSNIUpdate>,
|
||||
},
|
||||
UpdateToolOptionsLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
|
||||
@@ -13,12 +13,7 @@ pub enum Message {
|
||||
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
|
||||
#[serde(skip)]
|
||||
ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata),
|
||||
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),
|
||||
ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata, IntrospectionResponse),
|
||||
#[child]
|
||||
Animation(AnimationMessage),
|
||||
#[child]
|
||||
|
||||
@@ -442,7 +442,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
DocumentMessage::EnterNestedNetwork { node_id } => {
|
||||
self.breadcrumb_network_path.push(node_id);
|
||||
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
||||
responses.add(NodeGraphMessage::UnloadWires);
|
||||
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||
@@ -472,7 +472,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
self.breadcrumb_network_path.pop();
|
||||
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
|
||||
}
|
||||
responses.add(NodeGraphMessage::UnloadWires);
|
||||
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||
@@ -539,7 +539,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
responses.add(DocumentMessage::RenderScrollbars);
|
||||
if opened {
|
||||
responses.add(NodeGraphMessage::UnloadWires);
|
||||
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||
}
|
||||
if open {
|
||||
responses.add(ToolMessage::DeactivateTools);
|
||||
@@ -1424,7 +1424,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
center: Key::Alt,
|
||||
duplicate: Key::Alt,
|
||||
}));
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails);
|
||||
} else {
|
||||
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
|
||||
return;
|
||||
@@ -1440,7 +1440,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
responses.add(NodeGraphMessage::UpdateEdges);
|
||||
responses.add(NodeGraphMessage::UpdateBoxSelection);
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
|
||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||
responses.add(NodeGraphMessage::SendWirePaths);
|
||||
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
||||
transform: Transform {
|
||||
scale: transform.matrix2.x_axis.x,
|
||||
@@ -1909,10 +1910,7 @@ impl DocumentMessageHandler {
|
||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||
responses.add(Message::StartEvaluationQueue);
|
||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||
responses.add(NodeGraphMessage::UnloadWires);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(Message::EndEvaluationQueue);
|
||||
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
|
||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
||||
responses.add(NodeGraphMessage::UnloadWires);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||
responses.add(NodeGraphMessage::SendWirePaths);
|
||||
responses.add(Message::EndEvaluationQueue);
|
||||
Some(previous_network)
|
||||
}
|
||||
@@ -2576,7 +2574,7 @@ impl DocumentMessageHandler {
|
||||
layout: Layout::WidgetLayout(document_bar_layout),
|
||||
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>) {
|
||||
|
||||
@@ -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."),
|
||||
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 {
|
||||
identifier: "Merge",
|
||||
category: "General",
|
||||
@@ -529,21 +448,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::scope("editor-api")],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
||||
skip_deduplication: true,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
manual_composition: Some(concrete!(Context)),
|
||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
exports: vec![NodeInput::node(NodeId(0), 0)],
|
||||
nodes: [DocumentNode {
|
||||
inputs: vec![NodeInput::scope("editor-api")],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
||||
skip_deduplication: true,
|
||||
cache_output: true,
|
||||
..Default::default()
|
||||
}]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
@@ -556,24 +468,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
output_names: vec!["Image".to_string()],
|
||||
network_metadata: Some(NodeNetworkMetadata {
|
||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||
node_metadata: [
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Create Canvas".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
node_metadata: [DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Create Canvas".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Cache".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
..Default::default()
|
||||
}]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
@@ -595,19 +497,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::scope("editor-api")],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
|
||||
manual_composition: Some(concrete!(Context)),
|
||||
skip_deduplication: true,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
||||
manual_composition: Some(concrete!(Context)),
|
||||
cache_output: true,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
@@ -650,14 +547,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
},
|
||||
..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 {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Rasterize".to_string(),
|
||||
@@ -1408,33 +1297,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(3), 0)],
|
||||
nodes: vec![
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::network(concrete!(VectorDataTable), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
|
||||
manual_composition: Some(generic!(T)),
|
||||
..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()
|
||||
},
|
||||
]
|
||||
exports: vec![NodeInput::node(NodeId(0), 0)],
|
||||
nodes: vec![DocumentNode {
|
||||
inputs: vec![NodeInput::network(concrete!(VectorDataTable), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
|
||||
manual_composition: Some(generic!(T)),
|
||||
cache_output: true,
|
||||
..Default::default()
|
||||
}]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
@@ -1450,40 +1320,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
network_metadata: Some(NodeNetworkMetadata {
|
||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||
node_metadata: [
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Boolean Operation".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
node_metadata: [DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Boolean Operation".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..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()
|
||||
},
|
||||
]
|
||||
..Default::default()
|
||||
}]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
@@ -1506,7 +1350,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(4), 0)],
|
||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0)],
|
||||
@@ -1526,24 +1370,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
NodeInput::node(NodeId(0), 0),
|
||||
],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(vector::sample_polyline::IDENTIFIER),
|
||||
manual_composition: Some(generic!(T)),
|
||||
..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),
|
||||
cache_output: true,
|
||||
manual_composition: Some(generic!(T)),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -1585,30 +1412,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
},
|
||||
..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()
|
||||
.enumerate()
|
||||
@@ -1671,96 +1474,17 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
category: "Vector: Modifier",
|
||||
node_template: NodeTemplate {
|
||||
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![
|
||||
NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorDataTable::default()), true),
|
||||
NodeInput::value(TaggedValue::F64(10.), false),
|
||||
NodeInput::value(TaggedValue::U32(0), false),
|
||||
NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0),
|
||||
NodeInput::network(concrete!(f64), 1),
|
||||
NodeInput::network(concrete!(u32), 2),
|
||||
],
|
||||
cache_output: true,
|
||||
manual_composition: Some(generic!(T)),
|
||||
implementation: DocumentNodeImplementation::ProtoNode(vector::poisson_disk_points::IDENTIFIER),
|
||||
..Default::default()
|
||||
},
|
||||
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![
|
||||
("Vector Data", "TODO").into(),
|
||||
InputMetadata::with_name_description_override(
|
||||
|
||||
@@ -121,8 +121,8 @@ pub enum NodeGraphMessage {
|
||||
},
|
||||
SendClickTargets,
|
||||
EndSendClickTargets,
|
||||
UnloadWires,
|
||||
SendWires,
|
||||
UnloadWirePaths,
|
||||
SendWirePaths,
|
||||
UpdateVisibleNodes,
|
||||
SendGraph,
|
||||
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::graph_operation::utility_types::ModifyInputsContext;
|
||||
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::misc::GroupFolderType;
|
||||
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::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::tool::common_functionality::auto_panning::AutoPanning;
|
||||
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
|
||||
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
|
||||
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
|
||||
frontend_wires: HashSet<(NodeId, usize)>,
|
||||
}
|
||||
@@ -918,7 +918,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
wire_in_progress_to_connector,
|
||||
from_connector_is_layer,
|
||||
to_connector_is_layer,
|
||||
GraphWireStyle::Direct,
|
||||
&GraphWireStyle::Direct,
|
||||
);
|
||||
let mut path_string = String::new();
|
||||
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,
|
||||
dashed: false,
|
||||
center: None,
|
||||
input_sni: None,
|
||||
};
|
||||
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) });
|
||||
}
|
||||
@@ -1179,7 +1178,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
{
|
||||
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))
|
||||
})
|
||||
.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)),
|
||||
}),
|
||||
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) {
|
||||
network_interface.unload_wire(&input, breadcrumb_network_path);
|
||||
}
|
||||
|
||||
responses.add(FrontendMessage::ClearAllNodeGraphWires);
|
||||
responses.add(FrontendMessage::ClearAllNodeGraphWirePaths);
|
||||
}
|
||||
NodeGraphMessage::SendWires => {
|
||||
let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path);
|
||||
NodeGraphMessage::SendWirePaths => {
|
||||
let wires = self.collect_wires_paths(network_interface, &preferences.graph_wire_style, breadcrumb_network_path);
|
||||
responses.add(FrontendMessage::UpdateNodeGraphWires { wires });
|
||||
}
|
||||
NodeGraphMessage::UpdateVisibleNodes => {
|
||||
@@ -1347,8 +1346,20 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
let nodes = self.collect_nodes(network_interface, breadcrumb_network_path);
|
||||
self.frontend_nodes = nodes.iter().map(|node| node.id).collect();
|
||||
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);
|
||||
|
||||
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);
|
||||
// Send the new edges to the frontend
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
responses.add(NodeGraphMessage::SendWirePaths);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||
responses.add(Message::StartEvaluationQueue);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
responses.add(NodeGraphMessage::SendWirePaths);
|
||||
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 => {
|
||||
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 } => {
|
||||
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 } => {
|
||||
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(NodeGraphMessage::SendGraph);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
responses.add(NodeGraphMessage::SendWirePaths);
|
||||
}
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
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
|
||||
.node_graph_input_connectors(breadcrumb_network_path)
|
||||
.iter()
|
||||
@@ -2278,18 +2313,24 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
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
|
||||
self.node_graph_errors
|
||||
let errors: Option<String> = self
|
||||
.node_graph_errors
|
||||
.iter()
|
||||
.find(|error| match &error.original_location {
|
||||
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()))
|
||||
.or_else(|| {
|
||||
if self.node_graph_errors.iter().any(|error| match &error.original_location {
|
||||
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())
|
||||
} 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 {
|
||||
id: node_id,
|
||||
is_layer: network_interface
|
||||
.node_metadata(&node_id, breadcrumb_network_path)
|
||||
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
|
||||
is_layer,
|
||||
layer_thumbnail_sni: 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),
|
||||
reference: network_interface.reference(&node_id, breadcrumb_network_path).cloned().unwrap_or_default(),
|
||||
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 graph_craft::document::{InputConnector, OutputConnector};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::TypeSource;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graph_craft::document::{InputConnector, OutputConnector};
|
||||
use graphene_std::Type;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -76,6 +76,8 @@ pub struct FrontendNode {
|
||||
pub id: NodeId,
|
||||
#[serde(rename = "isLayer")]
|
||||
pub is_layer: bool,
|
||||
#[serde(rename = "layerThumbnailSNI")]
|
||||
pub layer_thumbnail_sni: Option<NodeId>,
|
||||
#[serde(rename = "canBeLayer")]
|
||||
pub can_be_layer: bool,
|
||||
pub reference: Option<String>,
|
||||
@@ -98,6 +100,12 @@ pub struct FrontendNode {
|
||||
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)]
|
||||
pub struct FrontendNodeType {
|
||||
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 })
|
||||
}
|
||||
|
||||
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
|
||||
if !self.wire_is_loaded(input, network_path) {
|
||||
self.load_wire(input, graph_wire_style, network_path);
|
||||
} else {
|
||||
pub fn viewport_loaded_thumbnail_position(&mut self, input: &InputConnector, graph_wire_style: &GraphWireStyle, network_path: &[NodeId]) -> Option<DVec2> {
|
||||
let wire = self.wire_path_from_input(input, graph_wire_style, false, network_path)?;
|
||||
let network_metadata = self.network_metadata(network_path)?;
|
||||
if wire.thick {
|
||||
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 } => {
|
||||
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
|
||||
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
|
||||
log::error!("Could not load wire for input: {:?}", input);
|
||||
return None;
|
||||
};
|
||||
wire.clone()
|
||||
Some(wire)
|
||||
}
|
||||
InputConnector::Export(export_index) => {
|
||||
let network_metadata = self.network_metadata(network_path)?;
|
||||
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;
|
||||
};
|
||||
wire.clone()
|
||||
}
|
||||
};
|
||||
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,
|
||||
}
|
||||
Some(wire)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Previewing::Yes { .. } => match input {
|
||||
InputConnector::Node { .. } => false,
|
||||
@@ -2698,7 +2690,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
|
||||
/// 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 current_export = self.upstream_output_connector(&input, network_path)?;
|
||||
|
||||
@@ -2733,7 +2725,6 @@ impl NodeNetworkInterface {
|
||||
thick,
|
||||
dashed: false,
|
||||
center: None,
|
||||
input_sni: None,
|
||||
});
|
||||
|
||||
Some(WirePathUpdate {
|
||||
@@ -2744,7 +2735,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
log::error!("Could not get dom rect for wire end: {:?}", input);
|
||||
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_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
|
||||
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))
|
||||
}
|
||||
|
||||
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 mut path_string = String::new();
|
||||
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 input_sni = self.protonode_from_input(input, network_path);
|
||||
Some(WirePath {
|
||||
path_string,
|
||||
data_type,
|
||||
thick,
|
||||
dashed,
|
||||
center: Some((center.x, center.y)),
|
||||
input_sni,
|
||||
center: Some((center.x as i32, center.y as i32)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@ pub struct WirePath {
|
||||
pub data_type: FrontendGraphDataType,
|
||||
pub thick: bool,
|
||||
pub dashed: bool,
|
||||
pub center: Option<(f64, f64)>,
|
||||
#[serde(rename = "inputSni")]
|
||||
pub input_sni: Option<NodeId>,
|
||||
pub center: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -27,6 +25,14 @@ pub struct WirePathUpdate {
|
||||
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)]
|
||||
pub enum GraphWireStyle {
|
||||
#[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.;
|
||||
match graph_wire_style {
|
||||
GraphWireStyle::Direct => {
|
||||
|
||||
@@ -394,14 +394,6 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||
node: graphene_std::transform_nodes::transform::IDENTIFIER,
|
||||
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 {
|
||||
node: graphene_std::vector::spline::IDENTIFIER,
|
||||
|
||||
@@ -9,7 +9,7 @@ use graphene_std::Color;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::renderer::RenderMetadata;
|
||||
use graphene_std::text::Font;
|
||||
use graphene_std::uuid::{SNI};
|
||||
use graphene_std::uuid::SNI;
|
||||
|
||||
#[impl_message(Message, Portfolio)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
@@ -22,17 +22,18 @@ pub enum PortfolioMessage {
|
||||
#[child]
|
||||
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
|
||||
CompileActiveDocument,
|
||||
// Sends a request to evaluate the network. Should occur when any context value changes.
|
||||
EvaluateActiveDocument,
|
||||
// Sends a request to introspect data in the network, and return it to the editor
|
||||
IntrospectActiveDocument {
|
||||
// Nodes to introspect is in addition to all nodes to render thumbnails for
|
||||
EvaluateActiveDocumentWithThumbnails,
|
||||
EvaluateActiveDocument {
|
||||
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 {
|
||||
file_name: String,
|
||||
file_type: FileType,
|
||||
@@ -47,12 +48,11 @@ pub enum PortfolioMessage {
|
||||
},
|
||||
ProcessEvaluationResponse {
|
||||
evaluation_metadata: RenderMetadata,
|
||||
},
|
||||
ProcessIntrospectionResponse {
|
||||
#[serde(skip)]
|
||||
introspection_response: IntrospectionResponse,
|
||||
introspected_nodes: IntrospectionResponse,
|
||||
},
|
||||
RenderThumbnails,
|
||||
// Introspected data is cleared after queued messages are complete
|
||||
ClearIntrospectedData,
|
||||
ProcessThumbnails,
|
||||
DocumentPassMessage {
|
||||
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::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
||||
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::spreadsheet::SpreadsheetMessageHandlerData;
|
||||
use crate::messages::preferences::SelectionMode;
|
||||
@@ -21,8 +22,9 @@ use crate::node_graph_executor::{CompilationRequest, ExportConfig, NodeGraphExec
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::document::value::EditorMetadata;
|
||||
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::memo::MonitorIntrospectResult;
|
||||
use graphene_std::renderer::{Quad, RenderMetadata};
|
||||
use graphene_std::text::Font;
|
||||
use graphene_std::transform::{Footprint, RenderQuality};
|
||||
@@ -57,10 +59,10 @@ pub struct PortfolioMessageHandler {
|
||||
device_pixel_ratio: Option<f64>,
|
||||
pub reset_node_definitions_on_open: bool,
|
||||
// Data from the node graph, which is populated after an introspection request.
|
||||
// To access the data, schedule messages with StartIntrospectionQueue [messages] EndIntrospectionQueue
|
||||
// The data is no longer accessible after EndIntrospectionQueue
|
||||
pub introspected_data: HashMap<SNI, Option<Arc<dyn std::any::Any + Send + Sync>>>,
|
||||
pub previous_thumbnail_data: HashMap<SNI, Arc<dyn std::any::Any + Send + Sync>>,
|
||||
// To access the data, schedule messages with StartEvaluationQueue [messages] EndEvaluationQueue
|
||||
// The data is no longer accessible after EndEvaluationQueue
|
||||
pub introspected_data: HashMap<SNI, MonitorIntrospectResult>,
|
||||
thumbnails_to_clear: Vec<SNI>,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
@@ -791,8 +793,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
transform_to_viewport: true,
|
||||
},
|
||||
});
|
||||
// Also evaluate the document after compilation
|
||||
responses.add_front(PortfolioMessage::EvaluateActiveDocument);
|
||||
}
|
||||
}
|
||||
PortfolioMessage::ProcessCompilationResponse { compilation_metadata } => {
|
||||
@@ -814,67 +814,23 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
// Remove all thumbnails
|
||||
cleared_thumbnails.push(sni);
|
||||
}
|
||||
responses.add(FrontendMessage::UpdateThumbnails {
|
||||
add: Vec::new(),
|
||||
clear: cleared_thumbnails,
|
||||
});
|
||||
|
||||
self.thumbnails_to_clear.extend(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 {
|
||||
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||
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 {
|
||||
// AnimationState::Stopped => 0.,
|
||||
// AnimationState::Playing { start } => ipp.time - start,
|
||||
@@ -891,9 +847,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
context.real_time = Some(ipp.time);
|
||||
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 {
|
||||
upstream_footprints: footprints,
|
||||
local_transforms,
|
||||
@@ -915,104 +880,71 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
// 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::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 {
|
||||
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||
return;
|
||||
};
|
||||
// All possible inputs, later check if they are connected to any nodes
|
||||
let mut nodes_to_render = HashSet::new();
|
||||
|
||||
// Get all inputs to render thumbnails for
|
||||
// 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(),
|
||||
let mut wire_sni_updates = document
|
||||
.network_interface
|
||||
.document_metadata()
|
||||
.all_layers()
|
||||
.map(|layer| WireSNIUpdate {
|
||||
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(compiled_input) = document.network_interface.protonode_from_input(&connector, &[]) {
|
||||
nodes_to_render.insert(compiled_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
sni: document.network_interface.protonode_from_input(&InputConnector::node(layer.to_node(), 1), &[]),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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 {
|
||||
let Some(viewed_network) = document.network_interface.nested_network(&document.breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
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 (layer_updates, wire_updates) = NodeGraphMessageHandler::graph_sni_updates(&document.network_interface, &document.breadcrumb_network_path);
|
||||
layer_sni_updates.extend(layer_updates);
|
||||
wire_sni_updates.extend(wire_updates);
|
||||
}
|
||||
|
||||
let mut clear = std::mem::take(&mut self.thumbnails_to_clear);
|
||||
clear.extend(thumbnail_response.clear);
|
||||
|
||||
responses.add(FrontendMessage::UpdateThumbnails {
|
||||
add: thumbnail_response.add,
|
||||
clear: thumbnail_response.clear,
|
||||
})
|
||||
clear,
|
||||
wire_sni_updates,
|
||||
layer_sni_updates,
|
||||
});
|
||||
}
|
||||
PortfolioMessage::ExportActiveDocument {
|
||||
file_name,
|
||||
@@ -1081,6 +1013,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
transparent_background,
|
||||
size: scaled_size,
|
||||
}),
|
||||
HashSet::new(),
|
||||
);
|
||||
|
||||
// if let Some((start, end, fps)) = animation_export_data {
|
||||
@@ -1300,11 +1233,100 @@ impl PortfolioMessageHandler {
|
||||
/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.
|
||||
.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 });
|
||||
}
|
||||
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)]
|
||||
|
||||
@@ -7,6 +7,7 @@ use graph_craft::document::OutputConnector;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::GraphicGroupTable;
|
||||
use graphene_std::instances::Instances;
|
||||
use graphene_std::memo::MonitorIntrospectResult;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::uuid::{NodeId, SNI};
|
||||
use graphene_std::vector::{VectorData, VectorDataTable};
|
||||
@@ -15,7 +16,7 @@ use std::sync::Arc;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
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
|
||||
pub network_interface: &'a NodeNetworkInterface,
|
||||
}
|
||||
@@ -26,7 +27,7 @@ pub struct SpreadsheetMessageHandler {
|
||||
/// Sets whether or not the spreadsheet is drawn.
|
||||
pub spreadsheet_view_open: bool,
|
||||
// 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>,
|
||||
|
||||
instances_path: Vec<usize>,
|
||||
@@ -73,10 +74,10 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData<'_>> for S
|
||||
let mut nodes_to_introspect = HashSet::new();
|
||||
nodes_to_introspect.insert(protonode_id);
|
||||
|
||||
responses.add(PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect });
|
||||
responses.add(Message::StartIntrospectionQueue);
|
||||
responses.add(PortfolioMessage::EvaluateActiveDocument { nodes_to_introspect });
|
||||
responses.add(Message::StartEvaluationQueue);
|
||||
responses.add(SpreadsheetMessage::ProcessUpdateLayout { node_to_inspect, protonode_id });
|
||||
responses.add(Message::EndIntrospectionQueue);
|
||||
responses.add(Message::EndEvaluationQueue);
|
||||
|
||||
self.update_layout(responses);
|
||||
}
|
||||
@@ -93,7 +94,6 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData<'_>> for S
|
||||
self.instances_path.truncate(len);
|
||||
self.update_layout(responses);
|
||||
}
|
||||
|
||||
SpreadsheetMessage::ViewVectorDataDomain { domain } => {
|
||||
self.viewing_vector_data_domain = domain;
|
||||
self.update_layout(responses);
|
||||
@@ -126,11 +126,13 @@ impl SpreadsheetMessageHandler {
|
||||
Some(_) => {
|
||||
match &self.inspection_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,
|
||||
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
|
||||
None => label("The output of this node could not be determined"),
|
||||
|
||||
@@ -82,8 +82,8 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
|
||||
}
|
||||
PreferencesMessage::GraphWireStyle { style } => {
|
||||
self.graph_wire_style = style;
|
||||
responses.add(NodeGraphMessage::UnloadWires);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
responses.add(NodeGraphMessage::UnloadWirePaths);
|
||||
responses.add(NodeGraphMessage::SendWirePaths);
|
||||
}
|
||||
PreferencesMessage::ViewportZoomWheelRate { 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::{CompilationMetadata, DocumentNode, NodeNetwork, generate_uuid};
|
||||
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::text::FontCache;
|
||||
use graphene_std::uuid::SNI;
|
||||
@@ -30,6 +31,11 @@ pub struct CompilationResponse {
|
||||
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
|
||||
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EvaluationRequest {
|
||||
@@ -37,16 +43,18 @@ pub struct EvaluationRequest {
|
||||
#[serde(skip)]
|
||||
pub context: EditorContext,
|
||||
pub node_to_evaluate: Option<SNI>,
|
||||
pub nodes_to_introspect: HashSet<SNI>,
|
||||
}
|
||||
|
||||
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct EvaluationResponse {
|
||||
evaluation_id: u64,
|
||||
result: Result<TaggedValue, String>,
|
||||
introspected_nodes: IntrospectionResponse,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
@@ -58,7 +66,6 @@ impl PartialEq for IntrospectionResponse {
|
||||
pub enum NodeGraphUpdate {
|
||||
CompilationResponse(CompilationResponse),
|
||||
EvaluationResponse(EvaluationResponse),
|
||||
IntrospectionResponse(IntrospectionResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -83,18 +90,18 @@ impl Default for NodeGraphExecutor {
|
||||
|
||||
impl NodeGraphExecutor {
|
||||
/// A local runtime is useful on threads since having global state causes flakes
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new_with_local_runtime() -> (NodeRuntime, Self) {
|
||||
let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
||||
let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
||||
let node_runtime = NodeRuntime::new(request_receiver, response_sender);
|
||||
// #[cfg(test)]
|
||||
// pub(crate) fn new_with_local_runtime() -> (NodeRuntime, Self) {
|
||||
// let (request_sender, request_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_executor = Self {
|
||||
futures: HashMap::new(),
|
||||
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
|
||||
};
|
||||
(node_runtime, node_executor)
|
||||
}
|
||||
// let node_executor = Self {
|
||||
// futures: HashMap::new(),
|
||||
// runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
|
||||
// };
|
||||
// (node_runtime, node_executor)
|
||||
// }
|
||||
|
||||
/// Updates the network to monitor all inputs. Useful for the testing.
|
||||
// #[cfg(test)]
|
||||
@@ -110,19 +117,28 @@ impl NodeGraphExecutor {
|
||||
|
||||
/// Compile the network
|
||||
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);
|
||||
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.
|
||||
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();
|
||||
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,
|
||||
context,
|
||||
node_to_evaluate,
|
||||
nodes_to_introspect,
|
||||
})) {
|
||||
log::error!("Could not send evaluation request. {:?}", error);
|
||||
return;
|
||||
@@ -131,18 +147,43 @@ impl NodeGraphExecutor {
|
||||
self.futures.insert(evaluation_id, evaluation_context);
|
||||
}
|
||||
|
||||
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)) {
|
||||
log::error!("Could not send evaluation request. {:?}", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<SNI>) {
|
||||
// if let Err(error) = self.runtime_io.try_send(GraphRuntimeRequest::IntrospectionRequest(nodes_to_introspect)) {
|
||||
// log::error!("Could not send evaluation request. {:?}", error);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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> {
|
||||
for response in self.runtime_io.receive() {
|
||||
if let Ok(response) = self.runtime_io.receive() {
|
||||
self.runtime_io.busy = false;
|
||||
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);
|
||||
|
||||
let node_graph_output = match result {
|
||||
@@ -186,36 +227,17 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
} else {
|
||||
// Update artwork
|
||||
self.process_node_graph_output(render_output, responses)?
|
||||
self.process_node_graph_output(render_output, introspected_nodes, responses)?
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
responses.add(PortfolioMessage::ProcessCompilationResponse { compilation_metadata });
|
||||
}
|
||||
NodeGraphUpdate::IntrospectionResponse(introspection_response) => {
|
||||
responses.add(Message::ProcessIntrospectionQueue(introspection_response));
|
||||
}
|
||||
} // NodeGraphUpdate::IntrospectionResponse(introspection_response) => {
|
||||
// responses.add_front(Message::ProcessIntrospectionQueue(introspection_response));
|
||||
// }
|
||||
}
|
||||
}
|
||||
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 {
|
||||
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
|
||||
// Send to frontend
|
||||
@@ -234,7 +256,16 @@ impl NodeGraphExecutor {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ pub struct NodeRuntime {
|
||||
executor: DynamicExecutor,
|
||||
receiver: Receiver<GraphRuntimeRequest>,
|
||||
sender: NodeGraphRuntimeSender,
|
||||
context_sender: Sender<(SNI, usize, EditorContext)>,
|
||||
|
||||
application_io: Option<Arc<WasmApplicationIo>>,
|
||||
|
||||
@@ -46,7 +47,6 @@ pub enum GraphRuntimeRequest {
|
||||
// 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.
|
||||
// Can also be used by the spreadsheet/introspection system
|
||||
IntrospectionRequest(HashSet<SNI>),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -69,19 +69,20 @@ impl NodeGraphRuntimeSender {
|
||||
fn send_evaluation_response(&self, response: EvaluationResponse) {
|
||||
self.0.send(NodeGraphUpdate::EvaluationResponse(response)).expect("Failed to send evaluation response")
|
||||
}
|
||||
fn send_introspection_response(&self, response: IntrospectionResponse) {
|
||||
self.0.send(NodeGraphUpdate::IntrospectionResponse(response)).expect("Failed to send introspection response")
|
||||
}
|
||||
// fn send_introspection_response(&self, response: IntrospectionResponse) {
|
||||
// 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));
|
||||
|
||||
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 {
|
||||
executor: DynamicExecutor::default(),
|
||||
receiver,
|
||||
sender: NodeGraphRuntimeSender(sender.clone()),
|
||||
context_sender,
|
||||
|
||||
application_io: None,
|
||||
|
||||
@@ -100,24 +101,26 @@ impl NodeRuntime {
|
||||
}
|
||||
|
||||
// TODO: This deduplication of messages may cause issues
|
||||
let mut compilation = None;
|
||||
let mut evaluation = None;
|
||||
let mut introspection = None;
|
||||
for request in self.receiver.try_iter() {
|
||||
match request {
|
||||
GraphRuntimeRequest::CompilationRequest(_) => compilation = Some(request),
|
||||
GraphRuntimeRequest::EvaluationRequest(_) => evaluation = Some(request),
|
||||
GraphRuntimeRequest::IntrospectionRequest(_) => introspection = Some(request),
|
||||
}
|
||||
}
|
||||
let requests = [compilation, evaluation, introspection].into_iter().flatten();
|
||||
// let mut compilation = None;
|
||||
// let mut cache_enable = None;
|
||||
// let mut evaluation = None;
|
||||
// let mut introspection = None;
|
||||
// for request in self.receiver.try_iter() {
|
||||
// match request {
|
||||
// GraphRuntimeRequest::CompilationRequest(_) => compilation = Some(request),
|
||||
// GraphRuntimeRequest::CacheEnableRequest(_) => cache_enable = Some(request),
|
||||
// GraphRuntimeRequest::EvaluationRequest(_) => evaluation = Some(request),
|
||||
// GraphRuntimeRequest::IntrospectionRequest(_) => introspection = Some(request),
|
||||
// }
|
||||
// }
|
||||
|
||||
for request in requests {
|
||||
// let requests = [compilation, cache_enable, evaluation, introspection].into_iter().flatten();
|
||||
|
||||
if let Ok(request) = self.receiver.try_recv() {
|
||||
match request {
|
||||
GraphRuntimeRequest::CompilationRequest(CompilationRequest { network, font_cache, editor_metadata }) => {
|
||||
// Insert the monitor node to manage the inspection
|
||||
// self.inspect_state = inspect_node.map(|inspect| InspectState::monitor_inspect_node(&mut network, inspect));
|
||||
|
||||
self.node_graph_errors.clear();
|
||||
let result = self.update_network(network, font_cache, editor_metadata).await;
|
||||
self.sender.send_compilation_response(CompilationResponse {
|
||||
@@ -125,25 +128,20 @@ impl NodeRuntime {
|
||||
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 {
|
||||
evaluation_id,
|
||||
context,
|
||||
node_to_evaluate,
|
||||
nodes_to_introspect,
|
||||
}) => {
|
||||
// for (protonode_input, introspect_mode) in &inputs_to_monitor {
|
||||
// self.executor.set_introspect(*protonode_input, *introspect_mode)
|
||||
// }
|
||||
for node in &nodes_to_introspect {
|
||||
self.executor.cache_first_evaluation(node)
|
||||
}
|
||||
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();
|
||||
for protonode in nodes {
|
||||
let introspected_data = match self.executor.introspect(protonode, true) {
|
||||
for protonode in nodes_to_introspect {
|
||||
let introspected_data = match self.executor.introspect(protonode) {
|
||||
Ok(introspected_data) => introspected_data,
|
||||
Err(e) => {
|
||||
log::error!("Could not introspect protonode: {:?}, error: {:?}", protonode, e);
|
||||
@@ -153,7 +151,11 @@ impl NodeRuntime {
|
||||
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
|
||||
// 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,
|
||||
Err(e) => {
|
||||
log::error!("Error compiling network: {e:?}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
let result = match self.executor.update(proto_network).await {
|
||||
let result: Result<CompilationMetadata, String> = match self.executor.update(proto_network, None).await {
|
||||
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
|
||||
// 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 std::sync::mpsc::TryRecvError;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -14,12 +15,13 @@ extern "C" {
|
||||
/// Handles communication with the NodeRuntime, either locally or via Tauri
|
||||
#[derive(Debug)]
|
||||
pub struct NodeRuntimeIO {
|
||||
// Send to
|
||||
pub busy: bool,
|
||||
#[cfg(any(not(feature = "tauri"), test))]
|
||||
sender: Sender<GraphRuntimeRequest>,
|
||||
#[cfg(all(feature = "tauri", not(test)))]
|
||||
sender: Sender<NodeGraphUpdate>,
|
||||
receiver: Receiver<NodeGraphUpdate>,
|
||||
pub context_receiver: Receiver<(SNI, usize, EditorContext)>,
|
||||
}
|
||||
|
||||
impl Default for NodeRuntimeIO {
|
||||
@@ -35,11 +37,14 @@ impl NodeRuntimeIO {
|
||||
{
|
||||
let (response_sender, response_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 {
|
||||
busy: false,
|
||||
sender: request_sender,
|
||||
receiver: response_receiver,
|
||||
context_receiver,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,19 +54,25 @@ impl NodeRuntimeIO {
|
||||
Self {
|
||||
sender: response_sender,
|
||||
receiver: response_receiver,
|
||||
context_receiver,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub fn with_channels(sender: Sender<GraphRuntimeRequest>, receiver: Receiver<NodeGraphUpdate>) -> Self {
|
||||
Self { sender, receiver }
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// pub fn with_channels(sender: Sender<GraphRuntimeRequest>, receiver: Receiver<NodeGraphUpdate>) -> Self {
|
||||
// Self { sender, receiver }
|
||||
// }
|
||||
|
||||
/// 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))]
|
||||
{
|
||||
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)))]
|
||||
@@ -76,7 +87,7 @@ impl NodeRuntimeIO {
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[cfg(all(feature = "tauri", not(test)))]
|
||||
{
|
||||
@@ -90,7 +101,7 @@ impl NodeRuntimeIO {
|
||||
}
|
||||
});
|
||||
}
|
||||
self.receiver.try_iter()
|
||||
self.receiver.try_recv()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user