Context nullification, cached monitor nodes

This commit is contained in:
Adam
2025-07-16 01:42:03 -07:00
parent 1398405529
commit cf0a32b9b1
82 changed files with 2235 additions and 1684 deletions
+51 -21
View File
@@ -5,8 +5,10 @@ use crate::messages::prelude::*;
#[derive(Debug, Default)]
pub struct Dispatcher {
buffered_queue: Vec<Message>,
queueing_messages: bool,
evaluation_queue: Vec<Message>,
introspection_queue: Vec<Message>,
queueing_evaluation_messages: bool,
queueing_introspection_messages: bool,
message_queues: Vec<VecDeque<Message>>,
pub responses: Vec<FrontendMessage>,
pub message_handlers: DispatcherMessageHandlers,
@@ -41,6 +43,9 @@ impl DispatcherMessageHandlers {
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
/// 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::Document(DocumentMessageDiscriminant::PropertiesPanel(
PropertiesPanelMessageDiscriminant::Refresh,
))),
@@ -91,13 +96,6 @@ impl Dispatcher {
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
let message = message.into();
// Add all additional messages to the queue if it exists (except from the end queue message)
if !matches!(message, Message::EndQueue) {
if self.queueing_messages {
self.buffered_queue.push(message);
return;
}
}
// If we are not maintaining the buffer, simply add to the current queue
Self::schedule_execution(&mut self.message_queues, process_after_all_current, [message]);
@@ -117,7 +115,21 @@ impl Dispatcher {
continue;
}
}
// Add all messages to the queue if queuing messages (except from the end queue message)
if !matches!(message, Message::EndEvaluationQueue) {
if self.queueing_evaluation_messages {
self.evaluation_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);
@@ -126,22 +138,40 @@ impl Dispatcher {
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
match message {
Message::StartQueue => {
self.queueing_messages = true;
Message::StartEvaluationQueue => {
self.queueing_evaluation_messages = true;
}
Message::EndQueue => {
self.queueing_messages = false;
Message::EndEvaluationQueue => {
self.queueing_evaluation_messages = false;
}
Message::ProcessQueue((render_output_metadata, introspected_inputs)) => {
let message = PortfolioMessage::ProcessEvaluationResponse {
Message::ProcessEvaluationQueue(render_output_metadata) => {
let update_message = PortfolioMessage::ProcessEvaluationResponse {
evaluation_metadata: render_output_metadata,
introspected_inputs,
};
// Add the message to update the state with the render output
Self::schedule_execution(&mut self.message_queues, true, [message]);
}
.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 (in the order they were added)
Self::schedule_execution(&mut self.message_queues, true, std::mem::take(&mut self.buffered_queue));
// 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(introspected_inputs) => {
let update_message = PortfolioMessage::ProcessIntrospectionResponse { introspected_inputs }.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));
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 => {}
Message::Init => {
@@ -43,7 +43,7 @@ impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for Exp
ExportDialogMessage::TransparentBackground(transparent_background) => self.transparent_background = transparent_background,
ExportDialogMessage::ExportBounds(export_area) => self.bounds = export_area,
ExportDialogMessage::Submit => responses.add_front(PortfolioMessage::ActiveDocumentExport {
ExportDialogMessage::Submit => responses.add_front(PortfolioMessage::ExportActiveDocument {
file_name: portfolio.active_document().map(|document| document.name.clone()).unwrap_or_default(),
file_type: self.file_type,
scale_factor: self.scale_factor,
@@ -1,7 +1,7 @@
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use glam::{IVec2, UVec2};
use graph_craft::document::NodeId;
use graphene_std::uuid::NodeId;
/// A dialog to allow users to set some initial options about a new document.
#[derive(Debug, Clone, Default, ExtractField)]
@@ -24,17 +24,14 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
if create_artboard {
responses.add(Message::StartQueue);
responses.add(GraphOperationMessage::NewArtboard {
id: NodeId::new(),
artboard: graphene_std::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
});
}
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
// Currently, it is necessary to use `FrontendMessage::TriggerDelayedZoomCanvasToFitAll` rather than `DocumentMessage::ZoomCanvasToFitAll` because the size of the viewport is not yet populated
responses.add(Message::StartQueue);
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
responses.add(Message::StartEvaluationQueue);
responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(Message::EndEvaluationQueue);
responses.add(DocumentMessage::DeselectAllLayers);
}
}
@@ -7,7 +7,7 @@ use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, La
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData;
use graph_craft::document::NodeId;
use graphene_std::uuid::NodeId;
use graphene_std::raster::color::Color;
use graphene_std::text::Font;
@@ -3,8 +3,7 @@ use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, Mou
use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo;
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*;
use glam::DVec2;
use std::time::Duration;
use glam::{DAffine2, DVec2};
#[derive(ExtractField)]
pub struct InputPreprocessorMessageContext {
+12 -15
View File
@@ -1,27 +1,24 @@
use std::sync::Arc;
use crate::messages::prelude::*;
use graphene_std::{IntrospectMode, uuid::CompiledProtonodeInput};
use crate::{messages::prelude::*, node_graph_executor::IntrospectionResponse};
use graphite_proc_macros::*;
#[impl_message]
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Message {
NoOp,
Init,
Batched(Box<[Message]>),
// Adds any subsequent messages to the queue
StartQueue,
StartEvaluationQueue,
// Stop adding messages to the queue.
EndQueue,
// Processes all messages that are queued, which occurs on the evaluation response. This allows a message to be run with data from after the evaluation is complete
ProcessQueue(
(
graphene_std::renderer::RenderMetadata,
Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
),
),
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),
#[child]
Animation(AnimationMessage),
#[child]
@@ -7,11 +7,10 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
use crate::messages::portfolio::utility_types::PanelType;
use crate::messages::prelude::*;
use glam::DAffine2;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::vector::click_target::ClickTarget;
use graphene_std::Color;
use graphene_std::raster::BlendMode;
use graphene_std::raster::Image;
use graphene_std::renderer::ClickTarget;
use graphene_std::transform::Footprint;
use graphene_std::uuid::NodeId;
use graphene_std::vector::style::ViewMode;
@@ -3,7 +3,7 @@ use super::node_graph::utility_types::Transform;
use super::overlays::utility_types::Pivot;
use super::utility_types::error::EditorError;
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
use super::utility_types::network_interface::{self, NodeNetworkInterface, TransactionStatus};
use super::utility_types::network_interface::{NodeNetworkInterface, TransactionStatus};
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
use crate::application::{GRAPHITE_GIT_COMMIT_HASH, generate_uuid};
use crate::consts::{ASYMPTOTIC_EFFECT, COLOR_OVERLAY_GRAY, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL};
@@ -13,10 +13,9 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Transf
use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext;
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings};
use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeTemplate};
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
@@ -24,11 +23,10 @@ use crate::messages::tool::common_functionality::graph_modification_utils::{self
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::ToolType;
use crate::node_graph_executor::NodeGraphExecutor;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graph_craft::document::{InputConnector, NodeInput, NodeNetwork, OldNodeNetwork};
use graphene_std::math::quad::Quad;
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
use graphene_std::raster::BlendMode;
@@ -37,18 +35,16 @@ use graphene_std::uuid::NodeId;
use graphene_std::vector::PointId;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::style::ViewMode;
use std::sync::Arc;
use std::time::Duration;
#[derive(ExtractField)]
pub struct DocumentMessageContext<'a> {
pub document_id: DocumentId,
pub struct DocumentMessageData<'a> {
pub ipp: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub current_tool: &'a ToolType,
pub preferences: &'a PreferencesMessageHandler,
pub device_pixel_ratio: f64,
// pub introspected_inputs: &HashMap<CompiledProtonodeInput, Box<dyn std::any::Any + Send + Sync>>,
// pub introspected_inputs: &HashMap<CompiledProtonodeInput, Arc<dyn std::any::Any + Send + Sync>>,
// pub downcasted_inputs: &mut HashMap<CompiledProtonodeInput, TaggedValue>,
}
@@ -173,10 +169,9 @@ impl Default for DocumentMessageHandler {
}
#[message_handler_data]
impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMessageHandler {
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, context: DocumentMessageContext) {
let DocumentMessageContext {
document_id,
impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
let DocumentMessageData {
ipp,
persistent_data,
current_tool,
@@ -222,12 +217,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
);
}
DocumentMessage::PropertiesPanel(message) => {
let context = PropertiesPanelMessageContext {
let properties_panel_message_handler_data = super::properties_panel::PropertiesPanelMessageHandlerData {
network_interface: &mut self.network_interface,
selection_network_path: &self.selection_network_path,
document_name: self.name.as_str(),
executor,
persistent_data,
};
self.properties_panel_message_handler.process_message(message, responses, context);
}
@@ -239,7 +232,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
network_interface: &mut self.network_interface,
selection_network_path: &self.selection_network_path,
breadcrumb_network_path: &self.breadcrumb_network_path,
document_id,
collapsed: &mut self.collapsed,
ipp,
graph_view_overlay_open: self.graph_view_overlay_open,
@@ -1432,7 +1424,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
center: Key::Alt,
duplicate: Key::Alt,
}));
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(PortfolioMessage::EvaluateActiveDocument);
} else {
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
return;
@@ -1492,7 +1484,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
// Connect the current output data to the artboard's input data, and the artboard's output to the document output
responses.add(NodeGraphMessage::InsertNodeBetween {
node_id,
input_connector: network_interface::InputConnector::Export(0),
input_connector: InputConnector::Export(0),
insert_node_input_index: 1,
});
@@ -1914,13 +1906,14 @@ impl DocumentMessageHandler {
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(Message::StartEvaluationQueue);
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(NodeGraphMessage::SelectedNodesUpdated);
// TODO: Remove once the footprint is used to load the imports/export distances from the edge
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(Message::StartQueue);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires);
responses.add(Message::EndEvaluationQueue);
Some(previous_network)
}
pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
@@ -1946,12 +1939,14 @@ impl DocumentMessageHandler {
network_interface.set_document_to_viewport_transform(transform);
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(Message::StartEvaluationQueue);
// 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(PortfolioMessage::CompileActiveDocument);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires);
responses.add(Message::EndEvaluationQueue);
Some(previous_network)
}
@@ -2108,7 +2103,7 @@ impl DocumentMessageHandler {
/// Loads all of the fonts in the document.
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>) {
let mut fonts = HashSet::new();
for (_node_id, node, _) in self.document_network().recursive_nodes() {
for (_node_path, node) in self.document_network().recursive_nodes() {
for input in &node.inputs {
if let Some(TaggedValue::Font(font)) = input.as_value() {
fonts.insert(font.clone());
@@ -2581,7 +2576,7 @@ impl DocumentMessageHandler {
layout: Layout::WidgetLayout(document_bar_layout),
layout_target: LayoutTarget::DocumentBar,
});
responses.add(NodeGraphMessage::ForceRunDocumentGraph);
responses.add(PortfolioMessage::EvaluateActiveDocument);
}
pub fn update_layers_panel_control_bar_widgets(&self, responses: &mut VecDeque<Message>) {
@@ -4,12 +4,12 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::{DAffine2, IVec2};
use graph_craft::document::NodeId;
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, RasterDataTable};
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::uuid::NodeId;
use graphene_std::vector::PointId;
use graphene_std::vector::VectorModificationType;
use graphene_std::vector::style::{Fill, Stroke};
@@ -2,12 +2,13 @@ use super::transform_utils;
use super::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector};
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::{InputConnector, OutputConnector, NodeInput};
use graphene_std::uuid::NodeId;
use graphene_std::Color;
use graphene_std::renderer::Quad;
use graphene_std::renderer::convert_usvg_path::convert_usvg_path;
@@ -1,8 +1,9 @@
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::{InputConnector, NodeInput};
use graphene_std::uuid::NodeId;
use graphene_std::vector::PointId;
/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`.
@@ -1,18 +1,19 @@
use super::transform_utils;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector};
use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface};
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::{DAffine2, IVec2};
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::{InputConnector, NodeInput, OutputConnector};
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, RasterDataTable};
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::uuid::NodeId;
use graphene_std::vector::style::{Fill, Stroke};
use graphene_std::vector::{PointId, VectorModificationType};
use graphene_std::vector::{VectorData, VectorDataTable};
@@ -11,7 +11,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeId;
use graphene_std::uuid::NodeId;
#[derive(ExtractField)]
pub struct NavigationMessageContext<'a> {
@@ -10,7 +10,6 @@ use crate::messages::portfolio::document::utility_types::network_interface::{
};
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::Message;
use crate::node_graph_executor::NodeGraphExecutor;
use glam::DVec2;
use graph_craft::ProtoNodeIdentifier;
use graph_craft::concrete;
@@ -23,6 +22,7 @@ use graphene_std::raster_types::{CPU, RasterDataTable};
use graphene_std::text::{Font, TypesettingConfig};
#[allow(unused_imports)]
use graphene_std::transform::Footprint;
use graphene_std::uuid::NodeId;
use graphene_std::vector::VectorDataTable;
use graphene_std::*;
use std::collections::{HashMap, HashSet, VecDeque};
@@ -1,13 +1,12 @@
use super::utility_types::Direction;
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector};
use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, NodeTemplate};
use crate::messages::prelude::*;
use glam::IVec2;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
use graph_craft::document::{InputConnector, NodeInput, OutputConnector};
use graphene_std::uuid::NodeId;
#[impl_message(Message, DocumentMessage, NodeGraph)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -111,8 +110,6 @@ pub enum NodeGraphMessage {
start_index: usize,
end_index: usize,
},
RunDocumentGraph,
ForceRunDocumentGraph,
SelectedNodesAdd {
nodes: Vec<NodeId>,
},
@@ -9,9 +9,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType};
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, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, TypeSource,
};
use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, Previewing, TypeSource};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
use crate::messages::prelude::*;
@@ -20,9 +18,10 @@ use crate::messages::tool::common_functionality::graph_modification_utils::get_c
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
use graph_craft::document::{DocumentNodeImplementation, InputConnector, NodeInput, OutputConnector};
use graph_craft::proto::GraphErrors;
use graphene_std::math::math_ext::QuadExt;
use graphene_std::uuid::NodeId;
use graphene_std::*;
use renderer::Quad;
use std::cmp::Ordering;
@@ -32,7 +31,6 @@ pub struct NodeGraphMessageContext<'a> {
pub network_interface: &'a mut NodeNetworkInterface,
pub selection_network_path: &'a [NodeId],
pub breadcrumb_network_path: &'a [NodeId],
pub document_id: DocumentId,
pub collapsed: &'a mut CollapsedLayers,
pub ipp: &'a InputPreprocessorMessageHandler,
pub graph_view_overlay_open: bool,
@@ -99,7 +97,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
network_interface,
selection_network_path,
breadcrumb_network_path,
document_id,
collapsed,
ipp,
graph_view_overlay_open,
@@ -767,14 +764,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.initial_disconnecting = false;
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
if let Some((output_type, source)) = clicked_output
.node_id()
.map(|node_id| network_interface.output_type(&node_id, clicked_output.index(), breadcrumb_network_path))
{
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(&output_type, &source);
} else {
self.wire_in_progress_type = FrontendGraphDataType::General;
}
let (output_type, source) = network_interface.output_type(&clicked_output, breadcrumb_network_path);
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(&output_type, &source);
self.update_node_graph_hints(responses);
return;
@@ -922,7 +913,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
false
}
});
let vector_wire = build_vector_wire(
let (vector_wire, _) = build_vector_wire(
wire_in_progress_from_connector,
wire_in_progress_to_connector,
from_connector_is_layer,
@@ -936,6 +927,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
data_type: self.wire_in_progress_type,
thick: false,
dashed: false,
center: None,
input_sni: None,
};
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) });
}
@@ -1078,15 +1071,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
};
// Get the compatible type from the output connector
let compatible_type = output_connector.and_then(|output_connector| {
output_connector.node_id().and_then(|node_id| {
// Get the output types from the network interface
let (output_type, type_source) = network_interface.output_type(&node_id, output_connector.index(), selection_network_path);
// Get the output types from the network interface
let (output_type, type_source) = network_interface.output_type(&output_connector, selection_network_path);
match type_source {
TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None,
_ => Some(format!("type:{}", output_type.nested_type())),
}
})
match type_source {
TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None,
_ => Some(format!("type:{}", output_type.nested_type())),
}
});
let appear_right_of_mouse = if ipp.mouse.position.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
let appear_above_mouse = if ipp.mouse.position.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
@@ -1188,7 +1179,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<_>>();
@@ -1290,12 +1281,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::SendGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
}
PortfolioMessage::CompileActiveDocument => {
responses.add(PortfolioMessage::SubmitGraphRender { document_id, ignore_hash: false });
}
NodeGraphMessage::ForceRunDocumentGraph => {
responses.add(PortfolioMessage::SubmitGraphRender { document_id, ignore_hash: true });
}
NodeGraphMessage::SelectedNodesAdd { nodes } => {
let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else {
log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesAdd");
@@ -1340,14 +1325,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return;
};
let document_bbox: [DVec2; 2] = ipp.document_bounds();
let document_bbox = ipp.document_bounds(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse());
let mut nodes = Vec::new();
for node_id in &self.frontend_nodes {
let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else {
log::error!("Could not get bbox for node: {:?}", node_id);
continue;
};
if node_bbox[1].x >= document_bbox[0].x && node_bbox[0].x <= document_bbox[1].x && node_bbox[1].y >= document_bbox[0].y && node_bbox[0].y <= document_bbox[1].y {
nodes.push(*node_id);
}
@@ -1393,6 +1377,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(PropertiesPanelMessage::Refresh);
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(Message::EndEvaluationQueue);
}
}
NodeGraphMessage::SetInput { input_connector, input } => {
@@ -1688,8 +1675,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
)
.into_iter()
.next();
responses.add(NodeGraphMessage::UpdateVisibleNodes);
responses.add(NodeGraphMessage::SendWires);
responses.add(FrontendMessage::UpdateImportsExports {
imports,
exports,
@@ -2182,6 +2167,7 @@ impl NodeGraphMessageHandler {
fn collect_nodes(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec<FrontendNode> {
let Some(outward_wires) = network_interface.outward_wires(breadcrumb_network_path).cloned() else {
log::error!("Could not collect outward wires in collect_nodes");
return Vec::new();
};
let mut can_be_layer_lookup = HashSet::new();
@@ -2232,7 +2218,7 @@ impl NodeGraphMessageHandler {
let primary_input = inputs.next().flatten();
let exposed_inputs = inputs.flatten().collect();
let (output_type, type_source) = network_interface.output_type(&node_id, 0, breadcrumb_network_path);
let (output_type, type_source) = network_interface.output_type(&OutputConnector::node(node_id, 0), breadcrumb_network_path);
let frontend_data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source);
let connected_to = outward_wires.get(&OutputConnector::node(node_id, 0)).cloned().unwrap_or_default();
@@ -2253,7 +2239,7 @@ impl NodeGraphMessageHandler {
if output_index == 0 && network_interface.has_primary_output(&node_id, breadcrumb_network_path) {
continue;
}
let (output_type, type_source) = network_interface.output_type(&node_id, 0, breadcrumb_network_path);
let (output_type, type_source) = network_interface.output_type(&OutputConnector::node(node_id, 0), breadcrumb_network_path);
let data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source);
let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get(&node_id) else {
@@ -2292,18 +2278,19 @@ impl NodeGraphMessageHandler {
let locked = network_interface.is_locked(&node_id, breadcrumb_network_path);
let errors = self
.node_graph_errors
.iter()
.find(|error| error.node_path == node_id_path)
.map(|error| format!("{:?}", error.error.clone()))
.or_else(|| {
if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) {
Some("Node graph type error within this node".to_string())
} else {
None
}
});
let errors = None; // TODO: Recursive traversal from export over all protonodes and match metadata with error
// self
// .node_graph_errors
// .iter()
// .find(|error| error.stable_node_id == node_id_path)
// .map(|error| format!("{:?}", error.error.clone()))
// .or_else(|| {
// if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) {
// Some("Node graph type error within this node".to_string())
// } else {
// None
// }
// });
nodes.push(FrontendNode {
id: node_id,
@@ -3,14 +3,13 @@
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
use super::utility_types::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::*;
use choice::enum_choice;
use dyn_any::DynAny;
use glam::{DAffine2, DVec2, IVec2, UVec2};
use graph_craft::Type;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, InputConnector, NodeInput};
use graphene_std::animation::RealTimeMode;
use graphene_std::extract_xy::XY;
use graphene_std::path_bool::BooleanOperation;
@@ -22,6 +21,7 @@ use graphene_std::raster::{
use graphene_std::raster_types::{CPU, GPU, RasterDataTable};
use graphene_std::text::Font;
use graphene_std::transform::{Footprint, ReferencePoint};
use graphene_std::uuid::NodeId;
use graphene_std::vector::VectorDataTable;
use graphene_std::vector::misc::GridType;
use graphene_std::vector::misc::{ArcType, MergeByDistanceAlgorithm};
@@ -1,6 +1,7 @@
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector, TypeSource};
use graph_craft::document::NodeId;
use crate::messages::portfolio::document::utility_types::network_interface::{TypeSource};
use graph_craft::document::{InputConnector, OutputConnector};
use graph_craft::document::value::TaggedValue;
use graphene_std::uuid::NodeId;
use graphene_std::Type;
use std::borrow::Cow;
@@ -72,7 +73,7 @@ pub struct FrontendGraphOutput {
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNode {
pub id: graph_craft::document::NodeId,
pub id: NodeId,
#[serde(rename = "isLayer")]
pub is_layer: bool,
#[serde(rename = "canBeLayer")]
@@ -1,7 +1,7 @@
mod properties_panel_message;
pub mod properties_panel_message_handler;
mod properties_panel_message_handler;
#[doc(inline)]
pub use properties_panel_message::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant};
#[doc(inline)]
pub use properties_panel_message_handler::PropertiesPanelMessageHandler;
pub use properties_panel_message_handler::{PropertiesPanelMessageHandler, PropertiesPanelMessageHandlerData};
@@ -7,7 +7,6 @@ use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use graph_craft::document::NodeId;
pub struct PropertiesPanelMessageHandlerData<'a> {
pub network_interface: &'a mut NodeNetworkInterface,
pub selection_network_path: &'a [NodeId],
@@ -1,5 +1,6 @@
use graphene_std::uuid::NodeId;
use super::network_interface::NodeTemplate;
use graph_craft::document::NodeId;
#[repr(u8)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
@@ -4,9 +4,9 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Modify
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
use crate::messages::tool::common_functionality::graph_modification_utils;
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeId;
use graphene_std::math::quad::Quad;
use graphene_std::transform::Footprint;
use graphene_std::uuid::NodeId;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, VectorData};
use std::collections::{HashMap, HashSet};
@@ -11,15 +11,13 @@ use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, InputConnector, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork, OutputConnector};
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, InputConnector, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork, OutputConnector};
use graph_craft::{Type, concrete};
use graphene_std::math::quad::Quad;
use graphene_std::transform::Footprint;
use graphene_std::uuid::{CompiledProtonodeInput, NodeId};
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, VectorData, VectorModificationType};
use graphene_std::{CompiledProtonodeInput, SNI};
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
use interpreted_executor::node_registry::NODE_REGISTRY;
use serde_json::{Value, json};
use std::collections::{HashMap, HashSet, VecDeque};
@@ -38,8 +36,9 @@ pub struct NodeNetworkInterface {
#[serde(skip)]
document_metadata: DocumentMetadata,
/// All input/output types based on the compiled network.
/// TODO: Move to portfolio message handler
#[serde(skip)]
pub resolved_types: ResolvedDocumentNodeTypes,
pub resolved_types: HashMap<SNI, Vec<Type>>,
#[serde(skip)]
transaction_status: TransactionStatus,
#[serde(skip)]
@@ -54,6 +53,7 @@ impl Clone for NodeNetworkInterface {
document_metadata: Default::default(),
resolved_types: Default::default(),
transaction_status: TransactionStatus::Finished,
current_hash: 0,
}
}
}
@@ -490,7 +490,7 @@ impl NodeNetworkInterface {
}
/// Try and get the [`DocumentNodeDefinition`] for a node
pub fn get_node_definition(&self, network_path: &[NodeId], node_id: NodeId) -> Option<&DocumentNodeDefinition> {
pub fn node_definition(&self, node_id: NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeDefinition> {
let metadata = self.node_metadata(&node_id, network_path)?;
resolve_document_node_type(metadata.persistent_metadata.reference.as_ref()?)
}
@@ -512,13 +512,13 @@ impl NodeNetworkInterface {
}
}
pub fn downstream_caller_from_output(&self, output_connector: OutputConnector, network_path: &[NodeId]) -> Option<&CompiledProtonodeInput> {
pub fn downstream_caller_from_output(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<&CompiledProtonodeInput> {
match output_connector {
OutputConnector::Node { node_id, output_index } => match self.implementation(&node_id, network_path)? {
DocumentNodeImplementation::Network(node_network) => {
OutputConnector::Node { node_id, output_index } => match self.implementation(node_id, network_path)? {
DocumentNodeImplementation::Network(_) => {
let mut nested_path = network_path.to_vec();
nested_path.push(node_id);
self.downstream_caller_from_input(InputConnector::Export(output_index), &nested_path)
nested_path.push(*node_id);
self.downstream_caller_from_input(&InputConnector::Export(*output_index), &nested_path)
}
DocumentNodeImplementation::ProtoNode(_) => self.node_metadata(&node_id, network_path)?.transient_metadata.caller.as_ref(),
DocumentNodeImplementation::Extract => todo!(),
@@ -526,44 +526,44 @@ impl NodeNetworkInterface {
OutputConnector::Import(import_index) => {
let mut encapsulating_path = network_path.to_vec();
let node_id = encapsulating_path.pop().expect("No imports in document network");
self.downstream_caller_from_input(InputConnector::node(node_id, import_index), &encapsulating_path)
self.downstream_caller_from_input(&InputConnector::node(node_id, *import_index), &encapsulating_path)
}
}
}
// Returns the path and input index to the protonode which called the input, which has to be the same every time is is called for a given input.
// This has to be done by iterating upstream, since a downstream traversal may lead to an uncompiled branch.
// This requires that value inputs store their caller. Caller input metadata from compilation has to be stored for
pub fn downstream_caller_from_input(&self, &input_connector: InputConnector, network_path: &[NodeId]) -> Option<&CompiledProtonodeInput> {
pub fn downstream_caller_from_input(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<&CompiledProtonodeInput> {
// Cases: Node/Value input to protonode, Node/Value input to network node
let input = self.input_from_connector(input_connector, network_path)?;
let caller_input = match input {
NodeInput::Node { node_id, output_index, lambda } => {
NodeInput::Node { node_id, output_index, .. } => {
match self.implementation(node_id, network_path)? {
DocumentNodeImplementation::Network(node_network) => {
DocumentNodeImplementation::Network(_) => {
// Continue traversal within network
let mut nested_path = network_path.to_vec();
nested_path.push(*node_id);
self.downstream_caller_from_input(InputConnector::Export(*output_index), &nested_path)
self.downstream_caller_from_input(&InputConnector::Export(*output_index), &nested_path)
}
DocumentNodeImplementation::ProtoNode(proto_node_identifier) => self.node_metadata(node_id, network_path)?.transient_metadata.caller.as_ref(),
DocumentNodeImplementation::ProtoNode(_) => self.node_metadata(node_id, network_path)?.transient_metadata.caller.as_ref(),
// If connected to a protonode, use the data in the node metadata
DocumentNodeImplementation::Extract => todo!(),
}
}
// Can either be an input to a protonode, network node, or export
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Reflection(_) => match input_connector {
InputConnector::Node { node_id, input_index } => self.input_metadata(node_id, *index, network_path)?.transient_metadata.caller.as_ref(),
InputConnector::Export(export_index) => self.network_metadata(network_path)?.transient_metadata.callers.get(export_index)?.as_ref(),
InputConnector::Node { node_id, .. } => self.transient_input_metadata(node_id, input_connector.input_index(), network_path)?.caller.as_ref(),
InputConnector::Export(export_index) => self.network_metadata(network_path)?.transient_metadata.callers.get(*export_index)?.as_ref(),
},
NodeInput::Network { import_index } => {
NodeInput::Network { import_index, .. } => {
let mut encapsulating_path = network_path.to_vec();
let node_id = encapsulating_path.pop().expect("No imports in document network");
self.downstream_caller_from_input(InputConnector::node(node_id, *import_index), &encapsulating_path)
self.downstream_caller_from_input(&InputConnector::node(node_id, *import_index), &encapsulating_path)
}
NodeInput::Inline(inline_rust) => None,
NodeInput::Inline(_) => None,
};
let Some(caller_input) = caller_input else {
log::error!("Could not get compiled caller input for input: {:?}", input_connector);
log::error!("Could not get compiled caller input for input: {:?} in network: {:?}", input_connector, network_path);
return None;
};
Some(caller_input)
@@ -631,7 +631,7 @@ impl NodeNetworkInterface {
{
let mut inner_path = network_path.to_vec();
inner_path.push(node_id);
let result = self.guess_type_from_node(child_id, child_input_index, inner_path);
let result = self.guess_type_from_node(child_id, child_input_index, &inner_path);
inner_path.pop();
return result;
}
@@ -645,6 +645,10 @@ impl NodeNetworkInterface {
/// Get the [`Type`] for any InputConnector
pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
if let Some(NodeInput::Value { tagged_value, .. }) = self.input_from_connector(input_connector, network_path) {
return (tagged_value.ty(), TypeSource::TaggedValue);
}
if let Some(compiled_type) = self
.downstream_caller_from_input(input_connector, network_path)
.and_then(|(sni, input_index)| self.resolved_types.get(sni).and_then(|protonode_input_types| protonode_input_types.get(*input_index)))
@@ -657,17 +661,14 @@ impl NodeNetworkInterface {
return (concrete!(()), TypeSource::Error("input connector is not a node"));
};
self.guess_type_from_node(node_id, input_connector.input_index(), network_path);
self.guess_type_from_node(node_id, input_connector.input_index(), network_path)
}
pub fn compiled_output_type(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<&Type> {
let (sni, input_index) = self.downstream_caller_from_output(output_connector, network_path)?;
let protonode_input_types = self.resolved_types.get(sni)?;
protonode_input_types.get(*input_index)
}
pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
if let Some(output_type) = self.compiled_output_type(output_connector, network_path) {
pub fn output_type(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
if let Some(output_type) = self
.downstream_caller_from_output(output_connector, network_path)
.and_then(|(sni, input_index)| self.resolved_types.get(sni).and_then(|protonode_input_types| protonode_input_types.get(*input_index)))
{
return (output_type.clone(), TypeSource::Compiled);
}
(concrete!(()), TypeSource::Error("Not compiled"))
@@ -678,10 +679,10 @@ impl NodeNetworkInterface {
}
pub fn remove_type(&mut self, sni: SNI) {
self.resolved_types.remove(sni);
self.resolved_types.remove(&sni);
}
pub fn set_node_caller(&mut self, node: &NodeId, caller: CompiledProtonodeInput, network_path: &[NodeId]) {
pub fn set_node_caller(&mut self, node_id: &NodeId, caller: CompiledProtonodeInput, network_path: &[NodeId]) {
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
@@ -692,6 +693,7 @@ impl NodeNetworkInterface {
match input_connector {
InputConnector::Node { node_id, input_index } => {
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("node metadata must exist when setting input caller for node {}, input index {}", node_id, input_index);
return;
};
let Some(input_metadata) = metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
@@ -770,84 +772,6 @@ impl NodeNetworkInterface {
}
}
/// Retrieves the output types for a given document node and its exports.
///
/// This function traverses the node and its nested network structure (if applicable) to determine
/// the types of all outputs, including the primary output and any additional exports.
///
/// # Arguments
///
/// * `node` - A reference to the `DocumentNode` for which to determine output types.
/// * `resolved_types` - A reference to `ResolvedDocumentNodeTypes` containing pre-resolved type information.
/// * `node_id_path` - A slice of `NodeId`s representing the path to the current node in the document graph.
///
/// # Returns
///
/// A `Vec<Option<Type>>` where:
/// - The first element is the primary output type of the node.
/// - Subsequent elements are types of additional exports (if the node is a network).
/// - `None` values indicate that a type couldn't be resolved for a particular output.
///
/// # Behavior
///
/// 1. Retrieves the primary output type from `resolved_types`.
/// 2. If the node is a network:
/// - Iterates through its exports (skipping the first/primary export).
/// - For each export, traverses the network until reaching a protonode or terminal condition.
/// - Determines the output type based on the final node/value encountered.
/// 3. Collects and returns all resolved types.
///
/// # Note
///
/// This function assumes that export indices and node IDs always exist within their respective
/// collections. It will panic if these assumptions are violated.
///
pub fn output_type(&self, node_id: &NodeId, output_index: usize, network_path: &[NodeId]) -> (Type, TypeSource) {
let Some(implementation) = self.implementation(node_id, network_path) else {
log::error!("Could not get output type for node {node_id} output index {output_index}. This node is no longer supported, and needs to be upgraded.");
return (concrete!(()), TypeSource::Error("Could not get implementation"));
};
// If the node is not a protonode, get types by traversing across exports until a proto node is reached.
match &implementation {
graph_craft::document::DocumentNodeImplementation::Network(internal_network) => {
let Some(export) = internal_network.exports.get(output_index) else {
return (concrete!(()), TypeSource::Error("Could not get export index"));
};
match export {
NodeInput::Node {
node_id: nested_node_id,
output_index,
..
} => self.output_type(nested_node_id, *output_index, &[network_path, &[*node_id]].concat()),
NodeInput::Value { tagged_value, .. } => (tagged_value.ty(), TypeSource::TaggedValue),
NodeInput::Network { .. } => {
// let mut encapsulating_path = network_path.to_vec();
// let encapsulating_node = encapsulating_path.pop().expect("No imports exist in document network");
// self.input_type(&InputConnector::node(encapsulating_node, *import_index), network_path)
(concrete!(()), TypeSource::Error("Could not type from network"))
}
NodeInput::Scope(_) => todo!(),
NodeInput::Inline(_) => todo!(),
NodeInput::Reflection(_) => todo!(),
}
}
graph_craft::document::DocumentNodeImplementation::ProtoNode(protonode) => {
let node_id_path = &[network_path, &[*node_id]].concat();
self.resolved_types
.types
.get(node_id_path)
.map(|ty| (ty.output.clone(), TypeSource::Compiled))
.or_else(|| {
let node_types = random_protonode_implementation(protonode)?;
Some((node_types.return_value.clone(), TypeSource::RandomProtonodeImplementation))
})
.unwrap_or((concrete!(()), TypeSource::Error("Could not get protonode implementation")))
}
graph_craft::document::DocumentNodeImplementation::Extract => (concrete!(()), TypeSource::Error("extract node")),
}
}
pub fn position(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> {
let top_left_position = self
.node_click_targets(node_id, network_path)
@@ -1231,7 +1155,7 @@ impl NodeNetworkInterface {
/// Returns the description of the node, or an empty string if it is not set.
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
self.get_node_definition(network_path, *node_id)
self.node_definition(*node_id, network_path)
.map(|node_definition| node_definition.description.to_string())
.filter(|description| description != "TODO")
.unwrap_or_default()
@@ -1568,7 +1492,7 @@ impl NodeNetworkInterface {
continue;
};
nested_network.exports = old_network.exports;
nested_network.scope_injections = old_network.scope_injections.into_iter().collect();
// nested_network.scope_injections = old_network.scope_injections.into_iter().collect();
let Some(nested_network_metadata) = network_metadata.nested_metadata_mut(&network_path) else {
log::error!("Could not get nested network in from_old_network");
continue;
@@ -1621,6 +1545,7 @@ impl NodeNetworkInterface {
document_metadata: DocumentMetadata::default(),
resolved_types: HashMap::new(),
transaction_status: TransactionStatus::Finished,
current_hash: 0,
}
}
}
@@ -2789,7 +2714,7 @@ 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: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
let (vector_wire, _) = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
let mut path_string = String::new();
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
@@ -2799,6 +2724,8 @@ impl NodeNetworkInterface {
data_type,
thick,
dashed: false,
center: None,
input_sni: None,
});
Some(WirePathUpdate {
@@ -2809,14 +2736,14 @@ 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)> {
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;
};
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return Some((Subpath::from_anchors(std::iter::empty(), false), false));
return Some((Subpath::from_anchors(std::iter::empty(), false), false, DVec2::default()));
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get dom rect for wire start: {:?}", upstream_output);
@@ -2825,19 +2752,23 @@ 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;
Some((build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style), thick))
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> {
let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
let (vector_wire, thick, center) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
let mut path_string = String::new();
let _ = 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.downstream_caller_from_input(input, network_path).map(|caller| NodeId(caller.0.0 + caller.1 as u64));
Some(WirePath {
path_string,
data_type,
thick,
dashed,
center: Some((center.x, center.y)),
input_sni,
})
}
@@ -6562,7 +6493,6 @@ impl InputPersistentMetadata {
struct InputTransientMetadata {
wire: TransientMetadata<WirePathUpdate>,
caller: Option<CompiledProtonodeInput>,
input_type: Option<Type>,
}
// TODO: Eventually remove this migration document upgrade code
@@ -12,6 +12,9 @@ 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>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
@@ -53,7 +56,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> {
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 => {
@@ -85,7 +88,7 @@ pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical
let delta01 = DVec2::new((locations[1].x - locations[0].x) * smoothing, (locations[1].y - locations[0].y) * smoothing);
let delta23 = DVec2::new((locations[3].x - locations[2].x) * smoothing, (locations[3].y - locations[2].y) * smoothing);
Subpath::new(
let subpath = Subpath::new(
vec![
ManipulatorGroup {
anchor: locations[0],
@@ -113,7 +116,9 @@ pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical
},
],
false,
)
);
let center = subpath.center().unwrap();
(subpath, center)
}
GraphWireStyle::GridAligned => {
let locations = straight_wire_paths(output_position, input_position, vertical_out, vertical_in);
@@ -446,13 +451,13 @@ fn straight_wire_paths(output_position: DVec2, input_position: DVec2, vertical_o
vec![IVec2::new(x1, y1), IVec2::new(x20, y1), IVec2::new(x20, y3), IVec2::new(x4, y3)]
}
fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
fn straight_wire_subpath(locations: Vec<IVec2>) -> (Subpath<PointId>, DVec2) {
if locations.is_empty() {
return Subpath::new(Vec::new(), false);
return (Subpath::new(Vec::new(), false), DVec2::default());
}
if locations.len() == 2 {
return Subpath::new(
let subpath = Subpath::new(
vec![
ManipulatorGroup {
anchor: locations[0].into(),
@@ -469,6 +474,8 @@ fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
],
false,
);
let center = subpath.center().unwrap();
return (subpath, center);
}
let corner_radius = 10;
@@ -585,5 +592,7 @@ fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
out_handle: None,
id: PointId::generate(),
});
Subpath::new(path, false)
let subpath = Subpath::new(path, false);
let center = subpath.center().unwrap();
(subpath, center)
}
@@ -3,12 +3,13 @@
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector};
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::prelude::DocumentMessageHandler;
use bezier_rs::Subpath;
use glam::IVec2;
use graph_craft::document::DocumentNode;
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
use graph_craft::document::{InputConnector, OutputConnector};
use graphene_std::ProtoNodeIdentifier;
use graphene_std::text::TypesettingConfig;
use graphene_std::uuid::NodeId;
@@ -492,7 +493,8 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
}
});
for (node_id, node, network_path) in network.recursive_nodes() {
for (node_path, node) in network.recursive_nodes() {
let (node_id, network_path) = node_path.split_last().unwrap();
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
let node_path_without_type_args = protonode_id.name.split('<').next();
if let Some(new) = node_path_without_type_args.and_then(|node_path| replacements.get(node_path)) {
@@ -509,9 +511,9 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
.network_interface
.document_network()
.recursive_nodes()
.map(|(node_id, node, path)| (*node_id, node.clone(), path))
.collect::<Vec<(NodeId, graph_craft::document::DocumentNode, Vec<NodeId>)>>();
for (node_id, node, network_path) in &nodes {
.map(|(node_path, node)| (node_path, node.clone()))
.collect::<Vec<(Vec<NodeId>, graph_craft::document::DocumentNode)>>();
for (node_path, node) in &nodes {
migrate_node(node_id, node, network_path, document, reset_node_definitions_on_open);
}
}
@@ -523,7 +525,6 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document.network_interface.replace_implementation(node_id, network_path, &mut node_definition.default_node_template());
}
}
// Upgrade old nodes to use `Context` instead of `()` or `Footprint` for manual composition
if node.manual_composition == Some(graph_craft::concrete!(())) || node.manual_composition == Some(graph_craft::concrete!(graphene_std::transform::Footprint)) {
document
@@ -1,17 +1,15 @@
use std::sync::Arc;
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use super::utility_types::PanelType;
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::prelude::*;
use crate::node_graph_executor::CompilationResponse;
use crate::node_graph_executor::IntrospectionResponse;
use graph_craft::document::CompilationMetadata;
use graphene_std::Color;
use graphene_std::raster::Image;
use graphene_std::renderer::RenderMetadata;
use graphene_std::text::Font;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::{Color, IntrospectMode};
#[impl_message(Message, Portfolio)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -24,11 +22,24 @@ 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.2
// 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 {
inputs_to_introspect: HashSet<CompiledProtonodeInput>,
},
ExportActiveDocument {
file_name: String,
file_type: FileType,
scale_factor: f64,
bounds: ExportBounds,
transparent_background: bool,
},
// Processes the compilation response and updates the data stored in the network interface for the active document
// TODO: Add document ID in response for stability
ProcessCompilationResponse {
@@ -36,12 +47,13 @@ pub enum PortfolioMessage {
},
ProcessEvaluationResponse {
evaluation_metadata: RenderMetadata,
},
ProcessIntrospectionResponse {
#[serde(skip)]
introspected_inputs: Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
},
ProcessThumbnails {
inputs_to_render: HashSet<CompiledProtonodeInput>,
introspected_inputs: IntrospectionResponse,
},
RenderThumbnails,
ProcessThumbnails,
DocumentPassMessage {
document_id: DocumentId,
message: DocumentMessage,
@@ -134,13 +146,6 @@ pub enum PortfolioMessage {
SelectDocument {
document_id: DocumentId,
},
SubmitDocumentExport {
file_name: String,
file_type: FileType,
scale_factor: f64,
bounds: ExportBounds,
transparent_background: bool,
},
ToggleRulers,
UpdateDocumentWidgets,
UpdateOpenDocumentsList,
@@ -6,24 +6,29 @@ use crate::application::generate_uuid;
use crate::consts::{DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX};
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
use crate::messages::dialog::simple_dialogs;
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
use crate::messages::frontend::utility_types::{ExportBounds, FrontendDocumentDetails};
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::DocumentMessageContext;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
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_migration::*;
use crate::messages::portfolio::spreadsheet::{InspectInputConnector, SpreadsheetMessageHandlerData};
use crate::messages::portfolio::spreadsheet::SpreadsheetMessageHandlerData;
use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
use crate::node_graph_executor::{CompilationRequest, ExportConfig, NodeGraphExecutor};
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeId;
use graphene_std::renderer::Quad;
use graph_craft::document::value::EditorMetadata;
use graph_craft::document::{AbsoluteInputConnector, InputConnector, NodeInput, OutputConnector};
use graphene_std::any::EditorContext;
use graphene_std::application_io::TimingInformation;
use graphene_std::memo::IntrospectMode;
use graphene_std::renderer::{Quad, RenderMetadata};
use graphene_std::text::Font;
use std::vec;
use graphene_std::transform::{Footprint, RenderQuality};
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
use std::sync::Arc;
#[derive(ExtractField)]
pub struct PortfolioMessageContext<'a> {
@@ -54,9 +59,10 @@ pub struct PortfolioMessageHandler {
pub reset_node_definitions_on_open: bool,
// Data from the node graph. Data for inputs are set to be collected on each evaluation, and added on the evaluation response
// Data from old nodes get deleted after a compilation
pub introspected_input_data: HashMap<CompiledProtonodeInput, Box<dyn std::any::Any + Send + Sync>>,
pub downcasted_input_data: HashMap<CompiledProtonodeInput, TaggedValue>,
pub context_data: HashMap<CompiledProtonodeInput, Context>,
// Always take data after requesting it
pub introspected_data: HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
pub introspected_call_argument: HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
pub previous_thumbnail_data: HashMap<CompiledProtonodeInput, Arc<dyn std::any::Any + Send + Sync>>,
}
#[message_handler_data]
@@ -105,13 +111,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
self.menu_bar_message_handler.process_message(message, responses, ());
}
PortfolioMessage::Spreadsheet(message) => {
self.spreadsheet.process_message(message, responses, SpreadsheetMessageHandlerData {introspected_data});
self.spreadsheet.process_message(
message,
responses,
SpreadsheetMessageHandlerData {
introspected_data: &self.introspected_data,
},
);
}
PortfolioMessage::Document(message) => {
if let Some(document_id) = self.active_document_id {
if let Some(document) = self.documents.get_mut(&document_id) {
let document_inputs = DocumentMessageContext {
document_id,
let document_inputs = DocumentMessageData {
ipp,
persistent_data: &self.persistent_data,
current_tool,
@@ -143,8 +154,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
PortfolioMessage::DocumentPassMessage { document_id, message } => {
if let Some(document) = self.documents.get_mut(&document_id) {
let document_inputs = DocumentMessageContext {
document_id,
let document_inputs = DocumentMessageData {
ipp,
persistent_data: &self.persistent_data,
current_tool,
@@ -356,12 +366,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
PortfolioMessage::NewDocumentWithName { name } => {
let mut new_document = DocumentMessageHandler::default();
new_document.name = name;
responses.add(DocumentMessage::PTZUpdate);
let document_id = DocumentId(generate_uuid());
if self.active_document().is_some() {
responses.add(BroadcastEvent::ToolAbort);
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
}
self.load_document(new_document, document_id, responses, false);
@@ -664,13 +672,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
if create_document {
// Wait for the document to be rendered so the click targets can be calculated in order to determine the artboard size that will encompass the pasted image
responses.add(Message::StartQueue);
responses.add(Message::StartEvaluationQueue);
responses.add(DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true });
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
// Currently, it is necessary to use `FrontendMessage::TriggerDelayedZoomCanvasToFitAll` rather than `DocumentMessage::ZoomCanvasToFitAll` because the size of the viewport is not yet populated
responses.add(Message::StartQueue);
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(Message::EndEvaluationQueue);
}
}
PortfolioMessage::PasteSvg {
@@ -696,13 +701,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
if create_document {
// Wait for the document to be rendered so the click targets can be calculated in order to determine the artboard size that will encompass the pasted image
responses.add(Message::StartQueue);
responses.add(Message::StartEvaluationQueue);
responses.add(DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true });
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
// Currently, it is necessary to use `FrontendMessage::TriggerDelayedZoomCanvasToFitAll` rather than `DocumentMessage::ZoomCanvasToFitAll` because the size of the viewport is not yet populated
responses.add(Message::StartQueue);
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(Message::EndEvaluationQueue);
}
}
PortfolioMessage::PrevDocument => {
@@ -747,7 +749,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(OverlaysMessage::Draw);
responses.add(BroadcastEvent::ToolAbort);
responses.add(BroadcastEvent::SelectionChanged);
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
if node_graph_open {
@@ -768,8 +769,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
}
PortfolioMessage::CompileActiveDocument => {
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: {:?}", document_id);
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;
};
if document.network_interface.hash_changed() {
@@ -788,37 +789,49 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
},
});
}
// Always evaluate after a recompile
responses.add(PortfolioMessage::EvaluateActiveDocument);
}
PortfolioMessage::ProcessCompilationResponse { compilation_metadata } => {
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get_mut(&document_id)) else {
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
return;
};
for (AbsoluteInputConnector { network_path, connector }, caller) in compilation_metadata.protonode_callers_for_value {
document.network_interface.set_input_caller(connector, caller, &network_path)
for (value_connectors, caller) in compilation_metadata.protonode_caller_for_values {
for AbsoluteInputConnector { network_path, connector } in value_connectors {
let (first, network_path) = network_path.split_first().unwrap();
if first != &NodeId(0) {
continue;
}
document.network_interface.set_input_caller(&connector, caller, network_path)
}
}
for (protonode_path, caller) in compilation_metadata.protonode_callers_for_node {
let (node_id, network_path) = protonode_path.to_vec().split_last().expect("Protonode path cannot be empty");
document.network_interface.set_node_caller(node_id, caller, &network_path)
for (protonode_paths, caller) in compilation_metadata.protonode_caller_for_nodes {
for protonode_path in protonode_paths {
let (first, node_path) = protonode_path.split_first().unwrap();
if first != &NodeId(0) {
continue;
}
let (node_id, network_path) = node_path.split_last().expect("Protonode path cannot be empty");
document.network_interface.set_node_caller(node_id, caller, &network_path)
}
}
for (sni, input_types) in compilation_metadata.types_to_add {
document.network_interface.add_type(sni, input_types);
}
for ((sni, number_of_inputs)) in compilation_metadata.types_to_remove {
// Removed saves type of the document node
let mut cleared_thumbnails = Vec::new();
for (sni, number_of_inputs) in compilation_metadata.types_to_remove {
// Removed saved type of the document node
document.network_interface.remove_type(sni);
// Remove introspection data for all monitor nodes and the thumbnails
let mut cleared_thumbnails = Vec::new();
for monitor_index in 0..number_of_inputs {
self.introspected_input_data.remove((sni, monitor_index));
self.downcasted_input_data.remove((sni, monitor_index));
self.context_data.remove((sni, monitor_index));
cleared_thumbnails.push(NodeId(sni.0+monitor_index as u64 +1));
// Remove all thumbnails
for input_index in 0..number_of_inputs {
cleared_thumbnails.push(NodeId(sni.0 + input_index as u64 + 1));
}
responses.add(FrontendMessage::UpdateThumbnails { add: Vec::new(), clear: cleared_thumbnails })
}
responses.add(FrontendMessage::UpdateThumbnails {
add: Vec::new(),
clear: cleared_thumbnails,
});
// Always evaluate after a recompile
responses.add(PortfolioMessage::EvaluateActiveDocument);
}
PortfolioMessage::EvaluateActiveDocument => {
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
@@ -827,100 +840,54 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
};
// Get all the inputs to save data for. This includes vector modify, thumbnails, and spreadsheet data
let inputs_to_monitor = HashSet::new();
let inputs_to_render = HashSet::new();
let inspect_input = None;
// Get the protonode input for all side layer inputs connected to the export in the document network for thumbnails in the layer panel
for caller in document
.network_interface
.document_metadata()
.all_layers()
.filter_map(|layer| {
let input = InputConnector::Node {
node_id: layer.to_node(),
input_index: 1,
};
document
.network_interface
.downstream_caller_from_input(&input, &[])
}) {
inputs_to_monitor.insert((*caller, IntrospectMode::Data));
inputs_to_render.insert(*caller);
}
// Save data for all inputs in the viewed node graph
if document.graph_view_overlay_open {
let Some(viewed_network) = document.network_interface.nested_network(&document.breadcrumb_network_path) else {
return;
};
for (export_index, export) in viewed_network.exports.iter().enumerate() {
if let Some(caller) = document
.network_interface
.downstream_caller_from_input(InputConnector::Export(export_index), &document.breadcrumb_network_path)
{
inputs_to_monitor.push((*caller, IntrospectMode::Data))
};
if let Some(NodeInput::Node { node_id, .. }) = export {
for upstream_node in document
.network_interface
.upstream_flow_back_from_nodes(vec![*node_id], &document.breadcrumb_network_path, network_interface::FlowType::UpstreamFlow)
{
let node = viewed_network.nodes[&upstream_node];
for (index, _) in node.inputs.iter().enumerate().filter(|(_, node_input)| node_input.is_exposed()) {
if let Some(caller) = document
.network_interface
.downstream_caller_from_input(InputConnector::Node(node_id, index), &document.breadcrumb_network_path)
{
inputs_to_monitor.insert((*caller, IntrospectMode::Data));
inputs_to_render.insert(*caller);
};
}
}
}
}
}
// 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(node_id, &[]).unwrap_or_default().as_deref().unwrap_or_default();
if reference == "Path" || reference == "Transform" {
let input_connector = InputConnector::Node { 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.push(*downstream_caller)
}
}
},
_ => {},
}
// 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 };
let Some(caller) = document
.network_interface
.downstream_caller_from_input(&selected_connector, selected_network_path) else {
log::error!("Could not get downstream caller for {:?}", selected_node);
};
inputs_to_monitor.push((*caller, IntrospectMode::Data));
inspect_input = Some(InspectInputConnector { input_connector: AbsoluteInputConnector { network_path: selected_network_path.clone(), connector: selected_connector }, protonode_input: *caller });
}
}
// 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.,
@@ -929,67 +896,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// };
let mut context = EditorContext::default();
// context.footprint = Some(Footprint {
// transform: document.metadata().document_to_viewport,
// resolution: ipp.viewport_bounds.size().as_uvec2(),
// quality: RenderQuality::Full,
// });
// context.animation_time = Some(animation_time);
// context.real_time = Some(ipp.time);
// context.downstream_transform = Some(DAffine2::IDENTITY);
let render_config = RenderConfig {
viewport: Footprint {
transform: document.metadata().document_to_viewport,
resolution: ipp.viewport_bounds.size().as_uvec2(),
..Default::default()
},
time: animation.timing_information(),
#[cfg(any(feature = "resvg", feature = "vello"))]
export_format: graphene_std::application_io::ExportFormat::Canvas,
#[cfg(not(any(feature = "resvg", feature = "vello")))]
export_format: graphene_std::application_io::ExportFormat::Svg,
view_mode: document.view_mode,
hide_artboards: false,
for_export: false,
};
context.footprint = Some(Footprint {
transform: document.metadata().document_to_viewport,
resolution: ipp.viewport_bounds.size().as_uvec2(),
quality: RenderQuality::Full,
});
context.animation_time = Some(timing_information.animation_time.as_secs_f64());
context.real_time = Some(ipp.time);
context.downstream_transform = Some(DAffine2::IDENTITY);
context.render_config = render_config;
self.executor.submit_node_graph_evaluation(
context,
inputs_to_monitor,
None,
None,
);
// Queue messages to be run after the evaluation returns data for the inputs to monitor
responses.add(Message::StartQueue);
if let Some(inspect_input) = inspect_input {
responses.add(SpreadsheetMessage::UpdateLayout { inpect_input });
}
responses.add(PortfolioMessage::ProcessThumbnails {inputs_to_render});
responses.add(Message::EndQueue);
self.executor.submit_node_graph_evaluation(context, None, None);
}
PortfolioMessage::ProcessEvaluationResponse { evaluation_metadata, introspected_inputs } => {
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;
};
for (input, mode, data) in introspected_inputs {
match mode {
IntrospectMode::Input => {
let Some(context) = data.downcast_ref()
self.introspected_input_data.extend(introspected_inputs);
},
IntrospectMode::Data => {
self.introspected_input_data.extend(introspected_inputs);
},
}
}
PortfolioMessage::ProcessEvaluationResponse { evaluation_metadata } => {
let RenderMetadata {
upstream_footprints: footprints,
local_transforms,
@@ -1009,25 +927,117 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// AnimationState::Playing { .. } => responses.add(PortfolioMessage::EvaluateActiveDocument),
// _ => {}
// };
},
PortfolioMessage::ProcessThumbnails { inputs_to_render } => {
let mut thumbnail_response = ThumbnailRenderResponse::default();
for thumbnail_input in inputs_to_render {
let monitor_node_id = thumbnail_input.0.0 + thumbnail_input.1 as u64 + 1;
match self.try_render_thumbnail(&thumbnail_input) {
ThumbnailRenderResult::NoChange => {}
ThumbnailRenderResult::ClearThumbnail => thumbnail_response.clear.push(NodeId(monitor_node_id)),
ThumbnailRenderResult::UpdateThumbnail(thumbnail) => {
thumbnail_response.add.push((NodeId(monitor_node_id), thumbnail));
},
// After an evaluation, always render all thumbnails
responses.add(PortfolioMessage::RenderThumbnails);
}
PortfolioMessage::IntrospectActiveDocument { inputs_to_introspect } => {
self.executor.submit_node_graph_introspection(inputs_to_introspect);
}
PortfolioMessage::ProcessIntrospectionResponse { introspected_inputs } => {
for (input, mode, data) in introspected_inputs.0.into_iter() {
match mode {
IntrospectMode::Input => {
self.introspected_call_argument.insert(input, data);
}
IntrospectMode::Data => {
self.introspected_data.insert(input, data);
}
}
}
responses.add(FrontendMessage::UpdateThumbnails { add: thumbnail_response.add, clear: thumbnail_response.clear })
},
PortfolioMessage::ActiveDocumentExport {
}
PortfolioMessage::ClearIntrospectedData => {
self.introspected_call_argument.clear();
self.introspected_data.clear()
}
PortfolioMessage::RenderThumbnails => {
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;
};
let mut inputs_to_render = HashSet::new();
// Get the protonode input for all side layer inputs connected to the export in the document network for thumbnails in the layer panel
for caller in document.network_interface.document_metadata().all_layers().filter_map(|layer| {
let input = InputConnector::Node {
node_id: layer.to_node(),
input_index: 1,
};
document.network_interface.downstream_caller_from_input(&input, &[])
}) {
inputs_to_render.insert(*caller);
}
// Save data for all inputs in the viewed node graph
if document.graph_view_overlay_open {
let Some(viewed_network) = document.network_interface.nested_network(&document.breadcrumb_network_path) else {
return;
};
for (export_index, export) in viewed_network.exports.iter().enumerate() {
match document
.network_interface
.downstream_caller_from_input(&InputConnector::Export(export_index), &document.breadcrumb_network_path)
{
Some(caller) => {
// inputs_to_monitor.insert((*caller, IntrospectMode::Data));
inputs_to_render.insert(*caller);
}
None => {}
};
if let NodeInput::Node { node_id, .. } = export {
for upstream_node in document
.network_interface
.upstream_flow_back_from_nodes(vec![*node_id], &document.breadcrumb_network_path, network_interface::FlowType::UpstreamFlow)
{
let node = &viewed_network.nodes[&upstream_node];
for (index, _) in node.inputs.iter().enumerate().filter(|(_, node_input)| node_input.is_exposed()) {
if let Some(caller) = document
.network_interface
.downstream_caller_from_input(&InputConnector::node(upstream_node, index), &document.breadcrumb_network_path)
{
// inputs_to_monitor.insert((*caller, IntrospectMode::Data));
inputs_to_render.insert(*caller);
};
}
}
}
}
};
responses.add(PortfolioMessage::IntrospectActiveDocument {
inputs_to_introspect: inputs_to_render,
});
responses.add(Message::StartIntrospectionQueue);
responses.add(PortfolioMessage::ProcessThumbnails);
responses.add(Message::EndIntrospectionQueue);
}
PortfolioMessage::ProcessThumbnails => {
let mut thumbnail_response = ThumbnailRenderResponse::default();
for (thumbnail_input, introspected_data) in self.introspected_data.drain() {
let input_node_id = thumbnail_input.0.0 + thumbnail_input.1 as u64;
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_input);
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(NodeId(input_node_id)),
graph_craft::document::value::ThumbnailRenderResult::UpdateThumbnail(thumbnail) => thumbnail_response.add.push((NodeId(input_node_id), thumbnail)),
}
self.previous_thumbnail_data.insert(thumbnail_input, evaluated_data);
}
responses.add(FrontendMessage::UpdateThumbnails {
add: thumbnail_response.add,
clear: thumbnail_response.clear,
})
}
PortfolioMessage::ExportActiveDocument {
file_name,
file_type,
animation_export_data,
scale_factor,
bounds,
transparent_background,
@@ -1035,57 +1045,45 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let document = self.active_document_id.and_then(|id| self.documents.get_mut(&id)).expect("Tried to render non-existent document");
// Update the scope inputs with the render settings
// self.executor.submit_node_graph_compilation(CompilationRequest {
// network: document.network_interface.document_network().clone(),
// font_cache: self.persistent_data.font_cache.clone(),
// editor_metadata: EditorMetadata {
// #[cfg(any(feature = "resvg", feature = "vello"))]
// use_vello: preferences.use_vello(),
// #[cfg(not(any(feature = "resvg", feature = "vello")))]
// use_vello: false,
// hide_artboards: transparent_background,
// for_export: true,
// view_mode: document.view_mode,
// transform_to_viewport: true,
// },
// });
self.executor.submit_node_graph_compilation(CompilationRequest {
network: document.network_interface.document_network().clone(),
font_cache: self.persistent_data.font_cache.clone(),
editor_metadata: EditorMetadata {
#[cfg(any(feature = "resvg", feature = "vello"))]
use_vello: preferences.use_vello(),
#[cfg(not(any(feature = "resvg", feature = "vello")))]
use_vello: false,
hide_artboards: transparent_background,
for_export: true,
view_mode: document.view_mode,
transform_to_viewport: true,
},
});
let document_to_viewport = document.metadata().document_to_viewport;
// Calculate the bounding box of the region to be exported
let document_bounds = match bounds {
let Some(document_bounds) = (match bounds {
ExportBounds::AllArtwork => document.network_interface.document_bounds_document_space(!transparent_background),
ExportBounds::Selection => document.network_interface.selected_bounds_document_space(!transparent_background, &[]),
ExportBounds::Artboard(id) => document.metadata().bounding_box_document(id),
// ExportBounds::Viewport => ipp.document_bounds(document_to_viewport),
}
.ok_or_else(|| "No bounding box".to_string())?;
// ExportBounds::Viewport => ipp.document_bounds(document.metadata().document_to_viewport),
}) else {
log::error!("No bounding box when exporting");
return;
};
let size = document_bounds[1] - document_bounds[0];
let scaled_size = size * scale_factor;
let transform = DAffine2::from_translation(document_bounds[0]).inverse();
let mut context = EditorContext::default();
// context.footprint = Footprint {
// document_to_viewport: DAffine2::from_scale(DVec2::splat(scale_factor)) * transform,
// resolution: scaled_size.as_uvec2(),
// ..Default::default()
// };
// context.real_time = Some(ipp.time);
// context.downstream_transform = Some(DAffine2::IDENTITY);
context.footprint = Some(Footprint {
transform: DAffine2::from_scale(DVec2::splat(scale_factor)) * transform,
resolution: scaled_size.as_uvec2(),
..Default::default()
});
context.real_time = Some(ipp.time);
context.downstream_transform = Some(DAffine2::IDENTITY);
let render_config = RenderConfig {
viewport: Footprint {
transform: DAffine2::from_scale(DVec2::splat(scale_factor)) * transform,
resolution: (size * scale_factor).as_uvec2(),
..Default::default()
},
time: Default::default(),
export_format: graphene_std::application_io::ExportFormat::Svg,
view_mode: document.view_mode,
hide_artboards: transparent_background,
for_export: true,
};
context.render_config = render_config;
// Special handling for exporting the artwork
let file_suffix = &format!(".{file_type:?}").to_lowercase();
let file_name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
@@ -1093,28 +1091,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
false => file_name + file_suffix,
};
let export_config = ExportConfig {
file_name,
file_type,
scale_factor,
bounds,
transparent_background,
..Default::default()
};
self.executor.submit_node_graph_evaluation(
context,
Vec::new(),
None,
Some(ExportConfig {
file_name,
file_type,
scale_factor,
bounds,
transparent_background,
size: scaled_size,
}),
);
None,
Some(ExportConfig {
file_name,
file_type,
scale_factor,
bounds,
transparent_background,
size: scaled_size,
}),
);
// if let Some((start, end, fps)) = animation_export_data {
// let total_frames = ((start - end) * fps) as u32;
@@ -1155,20 +1143,20 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// }
// Reset the scope nodes for hide artboards/hide_artboard name
// self.executor.submit_node_graph_compilation(CompilationRequest {
// network: document.network_interface.document_network().clone(),
// font_cache: self.persistent_data.font_cache.clone(),
// editor_metadata: EditorMetadata {
// #[cfg(any(feature = "resvg", feature = "vello"))]
// use_vello: preferences.use_vello().use_vello,
// #[cfg(not(any(feature = "resvg", feature = "vello")))]
// use_vello: false,
// hide_artboards: false,
// for_export: false,
// view_mode: document.view_mode,
// transform_to_viewport: true,
// },
// });
self.executor.submit_node_graph_compilation(CompilationRequest {
network: document.network_interface.document_network().clone(),
font_cache: self.persistent_data.font_cache.clone(),
editor_metadata: EditorMetadata {
#[cfg(any(feature = "resvg", feature = "vello"))]
use_vello: preferences.use_vello(),
#[cfg(not(any(feature = "resvg", feature = "vello")))]
use_vello: false,
hide_artboards: false,
for_export: false,
view_mode: document.view_mode,
transform_to_viewport: true,
},
});
}
PortfolioMessage::ToggleRulers => {
@@ -1181,7 +1169,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
PortfolioMessage::UpdateDocumentWidgets => {
if let Some(document) = self.active_document() {
document.update_document_widgets(responses, animation.is_playing(), animation_time);
document.update_document_widgets(responses, animation.is_playing(), timing_information.animation_time);
}
}
PortfolioMessage::UpdateOpenDocumentsList => {
@@ -1333,57 +1321,11 @@ 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::ProcessQueue((graphene_std::renderer::EvaluationMetadata::default(), Vec::new())));
responses.add(Message::ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata::default()));
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
}
result
}
// Returns an error if the data could not be introspected, returns None if the data type could not be rendered.
fn try_render_thumbnail(&self, protonode_input: &CompiledProtonodeInput) -> ThumbnailRenderResult {
let Ok(introspected_data) = self.introspected_input_data.get(protonode_input) else {
log::error!("Could not introspect node from input: {:?}", protonode_input);
return ThumbnailRenderResult::ClearThumbnail;
};
if let Some(previous_tagged_value) = self.downcasted_input_data.get(protonode_input) {
if previous_tagged_value.compare_value_to_dyn_any(introspected_data) {
return ThumbnailRenderResult::NoChange;
}
}
let Ok(new_tagged_value) = TaggedValue::try_from_std_any_ref(&introspected_data) else {
return ThumbnailRenderResult::ClearThumbnail;
};
let Some(renderable_data) = TaggedValue::as_renderable(&new_tagged_value) else {
// New value is not renderable
return ThumbnailRenderResult::ClearThumbnail;
};
let render_params = RenderParams {
view_mode: ViewMode::Normal,
culling_bounds: bounds,
thumbnail: true,
hide_artboards: false,
for_export: false,
for_mask: false,
alignment_parent_transform: None,
};
// Render the thumbnail data into an SVG string
let mut render = SvgRender::new();
renderable_data.render_svg(&mut render, &render_params);
// Give the SVG a viewbox and outer <svg>...</svg> wrapper tag
let [min, max] = renderable_data.bounding_box(DAffine2::IDENTITY, true).unwrap_or_default();
render.format_svg(min, max);
self.downcasted_input_data.insert(protonode_input, new_tagged_value);
ThumbnailRenderResult::UpdateThumbnail(render.svg.to_svg_string())
}
}
#[derive(Clone, Debug, Default)]
@@ -1391,10 +1333,3 @@ pub struct ThumbnailRenderResponse {
add: Vec<(SNI, String)>,
clear: Vec<SNI>,
}
pub enum ThumbnailRenderResult {
NoChange,
// Cleared if there is an error or the data could not be rendered
ClearThumbnail,
UpdateThumbnail(String),
}
@@ -1,3 +1,4 @@
use crate::messages::prelude::*;
use graph_craft::document::AbsoluteInputConnector;
use graphene_std::uuid::CompiledProtonodeInput;
@@ -7,7 +8,7 @@ use graphene_std::uuid::CompiledProtonodeInput;
pub enum SpreadsheetMessage {
ToggleOpen,
UpdateLayout { inpect_input: InspectInputConnector },
UpdateLayout { inspect_input: InspectInputConnector },
PushToInstancePath { index: usize },
TruncateInstancePath { len: usize },
@@ -15,7 +16,7 @@ pub enum SpreadsheetMessage {
ViewVectorDataDomain { domain: VectorDataDomain },
}
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug)]
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
pub enum VectorDataDomain {
#[default]
Points,
@@ -3,21 +3,17 @@ use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup,
use crate::messages::portfolio::spreadsheet::InspectInputConnector;
use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::*;
use graph_craft::document::{AbsoluteInputConnector, NodeId};
use graphene_std::Color;
use graphene_std::Context;
use graphene_std::GraphicGroupTable;
use graphene_std::instances::Instances;
use graphene_std::memo::IORecord;
use graphene_std::raster::Image;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::vector::{VectorData, VectorDataTable};
use graphene_std::{Artboard, ArtboardGroupTable, GraphicElement};
use std::any::Any;
use std::sync::Arc;
pub struct SpreadsheetMessageHandlerData {
pub introspected_data: &HashMap<CompiledProtonodeInput, Box<dyn std::any::Any + Send + Sync>>;
pub struct SpreadsheetMessageHandlerData<'a> {
pub introspected_data: &'a HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
}
/// The spreadsheet UI allows for instance data to be previewed.
@@ -35,7 +31,7 @@ pub struct SpreadsheetMessageHandler {
#[message_handler_data]
impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for SpreadsheetMessageHandler {
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, data: SpreadsheetMessageHandlerData) {
let {introspected_data} = data;
let SpreadsheetMessageHandlerData { introspected_data } = data;
match message {
SpreadsheetMessage::ToggleOpen => {
self.spreadsheet_view_open = !self.spreadsheet_view_open;
@@ -48,12 +44,12 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for Sprea
}
// Update checked UI state for open
responses.add(MenuBarMessage::SendLayout);
self.update_layout(responses);
self.update_layout(introspected_data, responses);
}
// Queued on introspection request, runs on introspection response when the data has been sent back to the editor
SpreadsheetMessage::UpdateLayout { inpect_input } => {
self.inspect_input = Some(inpect_input);
SpreadsheetMessage::UpdateLayout { inspect_input } => {
self.inspect_input = Some(inspect_input);
self.update_layout(introspected_data, responses);
}
@@ -79,7 +75,7 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for Sprea
}
impl SpreadsheetMessageHandler {
fn update_layout(&mut self, introspected_data: &HashMap<CompiledProtonodeInput, Box<dyn std::any::Any + Send + Sync>>, responses: &mut VecDeque<Message>) {
fn update_layout(&mut self, introspected_data: &HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>, responses: &mut VecDeque<Message>) {
responses.add(FrontendMessage::UpdateSpreadsheetState {
// The node is sent when the data is available
node: None,
@@ -94,18 +90,20 @@ impl SpreadsheetMessageHandler {
breadcrumbs: Vec::new(),
vector_data_domain: self.viewing_vector_data_domain,
};
let mut layout = match self.inspect_input {
let mut layout = match &self.inspect_input {
Some(inspect_input) => {
match introspected_data.get(&inspect_input.protonode_input){
Some(data) => {
match generate_layout(instrospected_data, &mut layout_data) {
match introspected_data.get(&inspect_input.protonode_input) {
Some(data) => match data {
Some(instrospected_data) => match generate_layout(instrospected_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."),
},
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("Error getting introspected data"),
}
},
}
None => label("No input selected to show data for."),
};
@@ -130,7 +128,7 @@ struct LayoutData<'a> {
vector_data_domain: VectorDataDomain,
}
fn generate_layout(introspected_data: &Box<dyn std::any::Any + Send + Sync + 'static>, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'static>, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
// We simply try random types. TODO: better strategy.
#[allow(clippy::manual_map)]
if let Some(io) = introspected_data.downcast_ref::<ArtboardGroupTable>() {
@@ -2,7 +2,7 @@ use graphene_std::text::FontCache;
#[derive(Debug, Default)]
pub struct PersistentData {
pub font_cache: Arc<FontCache>,
pub font_cache: std::sync::Arc<FontCache>,
}
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
@@ -20,10 +20,8 @@ impl PreferencesMessageHandler {
self.selection_mode
}
pub fn editor_preferences(&self) -> EditorPreferences {
EditorPreferences {
use_vello: self.use_vello && self.supports_wgpu(),
}
pub fn use_vello(&self) -> bool {
self.use_vello && self.supports_wgpu()
}
pub fn supports_wgpu(&self) -> bool {
@@ -3,16 +3,15 @@ use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::Responses;
use crate::messages::prelude::{PortfolioMessage, Responses};
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_polygon_parameters, inside_polygon, inside_star, polygon_outline, polygon_vertex_position, star_outline};
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position};
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{InputConnector, NodeInput};
use std::collections::VecDeque;
use std::f64::consts::TAU;
@@ -3,15 +3,15 @@ use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector};
use crate::messages::prelude::FrontendMessage;
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext};
use crate::messages::prelude::{FrontendMessage, PortfolioMessage};
use crate::messages::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
use crate::messages::tool::common_functionality::shapes::shape_utility::{draw_snapping_ticks, extract_polygon_parameters, polygon_outline, polygon_vertex_position, star_outline};
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position};
use glam::DVec2;
use graph_craft::document::NodeInput;
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_4, PI, SQRT_2};
@@ -1,12 +1,12 @@
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate};
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::DVec2;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::Color;
use graphene_std::NodeInputDecleration;
@@ -154,8 +154,9 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
});
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(Message::StartQueue);
responses.add(Message::StartEvaluationQueue);
responses.add(PenToolMessage::RecalculateLatestPointsPosition);
responses.add(Message::EndEvaluationQueue);
}
/// Merge the `first_endpoint` with `second_endpoint`.
@@ -3,11 +3,11 @@ use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::{ NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::NodeInput;
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
@@ -3,14 +3,14 @@ use crate::consts::{BOUNDS_SELECT_THRESHOLD, LINE_ROTATE_SNAP_ANGLE};
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::tool::common_functionality::graph_modification_utils;
pub use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapTypeConfiguration};
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DVec2;
use graph_craft::document::NodeInput;
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
@@ -5,7 +5,7 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Transf
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDial;
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDialState;
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandle;
@@ -16,6 +16,7 @@ use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGiz
use crate::messages::tool::common_functionality::shapes::shape_utility::polygon_outline;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::InputConnector;
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
@@ -3,11 +3,11 @@ use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::NodeInput;
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
@@ -2,7 +2,6 @@ use super::ShapeToolData;
use crate::messages::message::Message;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage, Responses};
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
@@ -11,7 +10,7 @@ use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::*;
use bezier_rs::Subpath;
use glam::{DAffine2, DMat2, DVec2};
use graph_craft::document::NodeInput;
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::document::value::TaggedValue;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::misc::dvec2_to_point;
@@ -4,7 +4,7 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Transf
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::{NumberOfPointsDial, NumberOfPointsDialState};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::{PointRadiusHandle, PointRadiusHandleState};
use crate::messages::tool::common_functionality::graph_modification_utils;
@@ -13,7 +13,7 @@ use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGi
use crate::messages::tool::tool_messages::tool_prelude::*;
use core::f64;
use glam::DAffine2;
use graph_craft::document::NodeInput;
use graph_craft::document::{InputConnector, NodeInput};
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
@@ -10,8 +10,8 @@ use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint;
use crate::messages::tool::common_functionality::snapping::SnapData;
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::common_functionality::transformation_cage::*;
use graph_craft::document::NodeId;
use graphene_std::renderer::Quad;
use graphene_std::uuid::NodeId;
#[derive(Default, ExtractField)]
pub struct ArtboardTool {
@@ -5,11 +5,11 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use graph_craft::document::NodeId;
use graph_craft::document::value::TaggedValue;
use graphene_std::Color;
use graphene_std::brush::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
use graphene_std::raster::BlendMode;
use graphene_std::uuid::NodeId;
const BRUSH_MAX_SIZE: f64 = 5000.;
@@ -379,8 +379,9 @@ impl Fsm for BrushToolFsmState {
else {
new_brush_layer(document, responses);
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(Message::StartQueue);
responses.add(Message::StartEvaluationQueue);
responses.add(BrushToolMessage::DragStart);
responses.add(Message::EndEvaluationQueue);
BrushToolFsmState::Ready
}
}
@@ -8,7 +8,7 @@ use crate::messages::tool::common_functionality::color_selector::{ToolColorOptio
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::utility_functions::should_extend;
use glam::DVec2;
use graph_craft::document::NodeId;
use graphene_std::uuid::NodeId;
use graphene_std::Color;
use graphene_std::vector::VectorModificationType;
use graphene_std::vector::{PointId, SegmentId};
@@ -251,7 +251,6 @@ impl Fsm for FreehandToolFsmState {
let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses);
responses.add(Message::StartQueue);
tool_options.fill.apply_fill(layer, responses);
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
tool_data.layer = Some(layer);
@@ -643,12 +643,12 @@ impl PathToolData {
self.drag_start_pos = input.mouse.position;
if input.time - self.last_click_time > DOUBLE_CLICK_MILLISECONDS {
if input.time as u64 - self.last_click_time > DOUBLE_CLICK_MILLISECONDS {
self.saved_points_before_anchor_convert_smooth_sharp.clear();
self.stored_selection = None;
}
self.last_click_time = input.time;
self.last_click_time = input.time as u64;
let old_selection = shape_editor.selected_points().cloned().collect::<Vec<_>>();
@@ -12,8 +12,8 @@ use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
use crate::messages::tool::common_functionality::utility_functions::{calculate_segment_angle, closest_point, should_extend};
use bezier_rs::{Bezier, BezierHandles};
use graph_craft::document::NodeId;
use graphene_std::Color;
use graphene_std::uuid::NodeId;
use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, StrokeId, VectorData};
use graphene_std::vector::{PointId, VectorModificationType};
@@ -1258,9 +1258,10 @@ impl PenToolData {
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
// This causes the following message to be run only after the next graph evaluation runs and the transforms are updated
responses.add(Message::StartQueue);
responses.add(Message::StartEvaluationQueue);
// It is necessary to defer this until the transform of the layer can be accurately computed (quite hacky)
responses.add(PenToolMessage::AddPointLayerPosition { layer, viewport });
responses.add(Message::EndEvaluationQueue);
}
/// Perform extension of an existing path
@@ -22,11 +22,11 @@ use crate::messages::tool::common_functionality::transformation_cage::*;
use crate::messages::tool::common_functionality::utility_functions::{resize_bounds, rotate_bounds, skew_bounds, text_bounding_box, transforming_transform_cage};
use bezier_rs::Subpath;
use glam::DMat2;
use graph_craft::document::NodeId;
use graphene_std::path_bool::BooleanOperation;
use graphene_std::renderer::Quad;
use graphene_std::renderer::Rect;
use graphene_std::transform::ReferencePoint;
use graphene_std::uuid::NodeId;
use std::fmt;
#[derive(Default, ExtractField)]
@@ -3,7 +3,6 @@ use crate::consts::{DEFAULT_STROKE_WIDTH, SNAP_POINT_TOLERANCE};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoManager;
@@ -19,9 +18,10 @@ use crate::messages::tool::common_functionality::snapping::{self, SnapCandidateP
use crate::messages::tool::common_functionality::transformation_cage::{BoundingBoxManager, EdgeBool};
use crate::messages::tool::common_functionality::utility_functions::{closest_point, resize_bounds, rotate_bounds, skew_bounds, transforming_transform_cage};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::{InputConnector, NodeInput};
use graphene_std::Color;
use graphene_std::renderer::Quad;
use graphene_std::uuid::NodeId;
use graphene_std::vector::misc::ArcType;
use std::vec;
@@ -599,17 +599,16 @@ impl Fsm for ShapeToolFsmState {
let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, document.new_layer_bounding_artboard(input), responses);
responses.add(Message::StartQueue);
match tool_data.current_shape {
ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star => {
responses.add(Message::StartEvaluationQueue);
responses.add(GraphOperationMessage::TransformSet {
layer,
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
transform_in: TransformIn::Viewport,
skip_rerender: false,
});
responses.add(Message::EndEvaluationQueue);
tool_options.fill.apply_fill(layer, responses);
}
ShapeType::Line => {
@@ -617,6 +616,7 @@ impl Fsm for ShapeToolFsmState {
tool_data.line_data.editing_layer = Some(layer);
}
}
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
@@ -10,7 +10,8 @@ use crate::messages::tool::common_functionality::color_selector::{ToolColorOptio
use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points};
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint};
use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend};
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::NodeInput;
use graphene_std::uuid::NodeId;
use graphene_std::Color;
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
@@ -360,8 +361,6 @@ impl Fsm for SplineToolFsmState {
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
tool_data.current_layer = Some(layer);
responses.add(Message::StartQueue);
SplineToolFsmState::Drawing
}
(SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => {
@@ -5,7 +5,6 @@ use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_RED, DRAG_THRESHOLD};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name};
@@ -14,10 +13,11 @@ use crate::messages::tool::common_functionality::snapping::{self, SnapCandidateP
use crate::messages::tool::common_functionality::transformation_cage::*;
use crate::messages::tool::common_functionality::utility_functions::text_bounding_box;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::document::{InputConnector, NodeInput};
use graphene_std::Color;
use graphene_std::renderer::Quad;
use graphene_std::text::{Font, FontCache, TypesettingConfig, lines_clipping, load_font};
use graphene_std::uuid::NodeId;
use graphene_std::vector::style::Fill;
#[derive(Default, ExtractField)]
@@ -381,7 +381,7 @@ impl TextToolData {
parent: document.new_layer_parent(true),
insert_index: 0,
});
responses.add(Message::StartQueue);
responses.add(Message::StartEvaluationQueue);
responses.add(GraphOperationMessage::FillSet {
layer: self.layer,
fill: if editing_text.color.is_some() {
@@ -394,15 +394,14 @@ impl TextToolData {
layer: self.layer,
transform: editing_text.transform,
transform_in: TransformIn::Viewport,
skip_rerender: true,
skip_rerender: false,
});
self.editing_text = Some(editing_text);
self.set_editing(true, font_cache, responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
responses.add(PortfolioMessage::CompileActiveDocument);
responses.add(Message::EndEvaluationQueue);
}
fn check_click(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) -> Option<LayerNodeIdentifier> {
+98 -94
View File
@@ -1,32 +1,18 @@
use std::sync::Arc;
use crate::consts::FILE_SAVE_SUFFIX;
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use crate::messages::frontend::utility_types::FileType;
use crate::messages::prelude::*;
use dyn_any::DynAny;
use glam::DAffine2;
use graph_craft::document::value::{NetworkOutput, TaggedValue};
use graph_craft::document::{
AbsoluteInputConnector, AbsoluteOutputConnector, CompilationMetadata, CompiledNodeMetadata, DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, generate_uuid,
};
use graph_craft::document::value::{EditorMetadata, RenderOutput, TaggedValue};
use graph_craft::document::{CompilationMetadata, DocumentNode, NodeNetwork, generate_uuid};
use graph_craft::proto::GraphErrors;
use graph_craft::wasm_application_io::{EditorCompilationMetadata, EditorEvaluationMetadata, EditorMetadata};
use graphene_std::application_io::{CompilationMetadata, TimingInformation};
use graphene_std::application_io::{EditorEvaluationMetadata, NodeGraphUpdateMessage};
use graphene_std::any::EditorContext;
use graphene_std::memo::IntrospectMode;
use graphene_std::renderer::{EvaluationMetadata, format_transform_matrix};
use graphene_std::renderer::{RenderMetadata, RenderSvgSegmentList};
use graphene_std::renderer::{RenderParams, SvgRender};
use graphene_std::renderer::format_transform_matrix;
use graphene_std::text::FontCache;
use graphene_std::transform::{Footprint, RenderQuality};
use graphene_std::uuid::{CompiledProtonodeInput, ProtonodePath, SNI};
use graphene_std::vector::VectorData;
use graphene_std::vector::style::ViewMode;
use graphene_std::wasm_application_io::NetworkOutput;
use graphene_std::{CompiledProtonodeInput, OwnedContextImpl, SNI};
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
mod runtime_io;
use interpreted_executor::dynamic_executor::{EditorContext, ResolvedDocumentNodeMetadata};
pub use runtime_io::NodeRuntimeIO;
mod runtime;
@@ -35,7 +21,7 @@ pub use runtime::*;
#[derive(Clone, Debug, Default, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub struct CompilationRequest {
pub network: NodeNetwork,
// Data which is avaialable from scope inputs (currently WasmEditorApi, but will be split)
// Data which is available from scope inputs
pub font_cache: Arc<FontCache>,
pub editor_metadata: EditorMetadata,
}
@@ -46,27 +32,34 @@ pub struct CompilationResponse {
}
// Metadata the editor sends when evaluating the network
#[derive(Debug, Default, DynAny)]
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
pub struct EvaluationRequest {
pub evaluation_id: u64,
pub inputs_to_monitor: Vec<(CompiledProtonodeInput, IntrospectMode)>,
#[serde(skip)]
pub context: EditorContext,
// pub custom_node_to_evaluate: Option<SNI>,
pub node_to_evaluate: Option<SNI>,
}
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
pub struct EvaluationResponse {
evaluation_id: u64,
result: Result<TaggedValue, String>,
introspected_inputs: Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
// TODO: Handle transforming node graph output in the node graph itself
transform: DAffine2,
}
#[derive(Debug, Clone, Default)]
pub struct IntrospectionResponse(pub Vec<((NodeId, usize), IntrospectMode, Option<Arc<dyn std::any::Any + Send + Sync>>)>);
impl PartialEq for IntrospectionResponse {
fn eq(&self, _other: &Self) -> bool {
false
}
}
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeGraphUpdate {
CompilationResponse(CompilationResponse),
EvaluationResponse(EvaluationResponse),
IntrospectionResponse(IntrospectionResponse),
}
#[derive(Debug, Default)]
@@ -98,6 +91,7 @@ impl NodeGraphExecutor {
let node_runtime = NodeRuntime::new(request_receiver, response_sender);
let node_executor = Self {
busy: false,
futures: HashMap::new(),
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
};
@@ -118,34 +112,39 @@ impl NodeGraphExecutor {
/// Compile the network
pub fn submit_node_graph_compilation(&mut self, compilation_request: CompilationRequest) {
self.runtime_io.send(GraphRuntimeRequest::CompilationRequest(compilation_request)).map_err(|e| e.to_string());
if let Err(error) = self.runtime_io.send(GraphRuntimeRequest::CompilationRequest(compilation_request)) {
log::error!("Could not send evaluation request. {:?}", error);
return;
}
}
/// Adds an evaluate request for whatever current network is cached.
pub fn submit_node_graph_evaluation(
&mut self,
context: EditorContext,
inputs_to_monitor: Vec<(CompiledProtonodeInput, IntrospectMode)>,
custom_node_to_evaluate: Option<SNI>,
export_config: Option<ExportConfig>,
) {
/// 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>) {
let evaluation_id = generate_uuid();
self.runtime_io.send(GraphRuntimeRequest::EvaluationRequest(editor_evaluation_request)).map_err(|e| e.to_string());
if let Err(error) = self.runtime_io.send(GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
evaluation_id,
context,
node_to_evaluate,
})) {
log::error!("Could not send evaluation request. {:?}", error);
return;
}
let evaluation_context = EvaluationContext { export_config };
self.futures.insert(evaluation_id, evaluation_context);
}
pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<CompiledProtonodeInput>) {
if let Err(error) = self.runtime_io.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> {
// Moved into portfolio message handler, since this is where the introspected inputs are saved
for response in self.runtime_io.receive() {
match response {
NodeGraphUpdate::EvaluationResponse(EvaluationResponse {
evaluation_id,
result,
transform,
introspected_inputs,
}) => {
NodeGraphUpdate::EvaluationResponse(EvaluationResponse { evaluation_id, result }) => {
responses.add(OverlaysMessage::Draw);
let node_graph_output = match result {
@@ -160,18 +159,14 @@ impl NodeGraphExecutor {
let render_output = match node_graph_output {
TaggedValue::RenderOutput(render_output) => render_output,
value => {
return Err("Incorrect render type for exporting (expected NetworkOutput)".to_string());
return Err(format!("Incorrect render type for exporting {:?} (expected NetworkOutput)", value.ty()));
}
};
let evaluation_context = self.futures.remove(&evaluation_id).ok_or_else(|| "Invalid generation ID".to_string())?;
if let Some(export_config) = evaluation_context.export_config {
// Export
let TaggedValue::RenderOutput(RenderOutput {
data: graphene_std::wasm_application_io::RenderOutputType::Svg(svg),
..
}) = node_graph_output
else {
let graphene_std::wasm_application_io::RenderOutputType::Svg(svg) = render_output.data else {
return Err("Incorrect render type for exporting (expected RenderOutput::Svg)".to_string());
};
@@ -193,7 +188,7 @@ impl NodeGraphExecutor {
}
} else {
// Update artwork
self.process_node_graph_output(render_output, introspected_inputs, transform, responses);
self.process_node_graph_output(render_output, responses)?
}
}
NodeGraphUpdate::CompilationResponse(compilation_response) => {
@@ -213,58 +208,35 @@ impl NodeGraphExecutor {
Ok(result) => result,
};
responses.add(PortfolioMessage::ProcessCompilationResponse { compilation_metadata });
responses.add(NodeGraphMessage::SendGraph);
}
NodeGraphUpdate::IntrospectionResponse(introspection_response) => {
responses.add(Message::ProcessIntrospectionQueue(introspection_response));
}
}
}
Ok(())
}
fn process_node_graph_output(
&mut self,
node_graph_output: TaggedValue,
introspected_inputs: Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
transform: DAffine2,
responses: &mut VecDeque<Message>,
) -> Result<(), String> {
let mut render_output_metadata = RenderMetadata::default();
match node_graph_output {
TaggedValue::RenderOutput(render_output) => {
match render_output.data {
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
// Send to frontend
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
}
graphene_std::wasm_application_io::RenderOutputType::CanvasFrame(frame) => {
let matrix = format_transform_matrix(frame.transform);
let transform = if matrix.is_empty() { String::new() } else { format!(" transform=\"{}\"", matrix) };
let svg = format!(
r#"<svg><foreignObject width="{}" height="{}"{transform}><div data-canvas-placeholder="canvas{}"></div></foreignObject></svg>"#,
frame.resolution.x, frame.resolution.y, frame.surface_id.0
);
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
}
_ => {
return Err(format!("Invalid node graph output type: {:#?}", render_output.data));
}
}
render_output_metadata = render_output.metadata;
fn process_node_graph_output(&self, render_output: RenderOutput, responses: &mut VecDeque<Message>) -> Result<(), String> {
match render_output.data {
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
// Send to frontend
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
}
graphene_std::wasm_application_io::RenderOutputType::CanvasFrame(frame) => {
let matrix = format_transform_matrix(frame.transform);
let transform = if matrix.is_empty() { String::new() } else { format!(" transform=\"{}\"", matrix) };
let svg = format!(
r#"<svg><foreignObject width="{}" height="{}"{transform}><div data-canvas-placeholder="canvas{}"></div></foreignObject></svg>"#,
frame.resolution.x, frame.resolution.y, frame.surface_id.0
);
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
}
// TaggedValue::Bool(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::String(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::F64(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::DVec2(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::OptionalColor(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::VectorData(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::GraphicGroup(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::RasterData(render_object) => Self::debug_render(render_object, transform, responses),
// TaggedValue::Palette(render_object) => Self::debug_render(render_object, transform, responses),
_ => {
return Err(format!("Invalid node graph output type: {node_graph_output:#?}"));
return Err(format!("Invalid node graph output type: {:#?}", render_output.data));
}
};
responses.add(Message::ProcessQueue((render_output_metadata, introspected_inputs)));
}
responses.add(Message::ProcessEvaluationQueue(render_output.metadata));
Ok(())
}
}
@@ -404,3 +376,35 @@ impl NodeGraphExecutor {
// }
// }
// }
// Passed as a scope input
#[derive(Clone, Debug, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub struct EditorMetadata {
// pub imaginate_hostname: String,
pub use_vello: bool,
pub hide_artboards: bool,
// If exporting, hide the artboard name and do not collect metadata
pub for_export: bool,
pub view_mode: graphene_core::vector::style::ViewMode,
pub transform_to_viewport: bool,
}
unsafe impl dyn_any::StaticType for EditorMetadata {
type Static = EditorMetadata;
}
impl Default for EditorMetadata {
fn default() -> Self {
Self {
// imaginate_hostname: "http://localhost:7860/".into(),
#[cfg(target_arch = "wasm32")]
use_vello: false,
#[cfg(not(target_arch = "wasm32"))]
use_vello: true,
hide_artboards: false,
for_export: false,
view_mode: graphene_core::vector::style::ViewMode::Normal,
transform_to_viewport: true,
}
}
}
+57 -100
View File
@@ -1,24 +1,12 @@
use super::*;
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use glam::{DAffine2, DVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeNetwork};
use graph_craft::graphene_compiler::Compiler;
use glam::DVec2;
use graph_craft::document::NodeNetwork;
use graph_craft::proto::GraphErrors;
use graph_craft::wasm_application_io::EditorPreferences;
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::Context;
use graphene_std::application_io::{NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_std::instances::Instance;
use graphene_std::memo::IORecord;
use graphene_std::renderer::{GraphicElementRendered, RenderParams, SvgRender};
use graphene_std::renderer::{RenderSvgSegmentList, SvgSegment};
use graphene_std::text::FontCache;
use graphene_std::uuid::{CompiledProtonodeInput, NodeId};
use graphene_std::vector::style::ViewMode;
use graphene_std::vector::{VectorData, VectorDataTable};
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::wasm_application_io::WasmApplicationIo;
use interpreted_executor::dynamic_executor::DynamicExecutor;
use interpreted_executor::util::wrap_network_in_scope;
use once_cell::sync::Lazy;
use spin::Mutex;
@@ -41,9 +29,6 @@ pub struct NodeRuntime {
node_graph_errors: GraphErrors,
/// Which node is inspected and which monitor node is used (if any) for the current execution
inspect_state: Option<InspectState>,
/// Mapping of the fully-qualified node paths to their preprocessor substitutions.
substitutions: HashMap<ProtoNodeIdentifier, DocumentNode>,
@@ -61,10 +46,10 @@ pub enum GraphRuntimeRequest {
// Renders thumbnails for the data from the last execution
// If the upstream node stores data for the context override, then another evaluation must be performed at the input
// This is performed separately from execution requests, since thumbnails for animation should be updated once every 50ms or so.
ThumbnailRenderRequest(HashSet<CompiledProtonodeInput>),
// ThumbnailRenderRequest(HashSet<CompiledProtonodeInput>),
// Request the data from a list of node inputs. For example, used by vector modify to get the data at the input of every Path node.
// Can also be used by the spreadsheet/introspection system
IntrospectionRequest(HashSet<(CompiledProtonodeInput, IntrospectMode)>),
IntrospectionRequest(HashSet<CompiledProtonodeInput>),
}
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -87,6 +72,9 @@ 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")
}
}
pub static NODE_RUNTIME: Lazy<Mutex<Option<NodeRuntime>>> = Lazy::new(|| Mutex::new(None));
@@ -103,119 +91,103 @@ impl NodeRuntime {
node_graph_errors: Vec::new(),
substitutions: preprocessor::generate_node_substitutions(),
thumbnail_render_tagged_values: HashSet::new(),
inspect_state: None,
}
}
pub async fn run(&mut self) {
if self.application_io.is_none() {
#[cfg(not(test))]
// #[cfg(not(test))]
self.application_io = Some(Arc::new(WasmApplicationIo::new().await));
#[cfg(test)]
self.application_io = Some(Arc::new(WasmApplicationIo::new_offscreen().await));
// #[cfg(test)]
// self.application_io = Some(Arc::new(WasmApplicationIo::new_offscreen().await));
}
// TODO: This deduplication of messages will probably cause more issues than it solved
// let mut graph = None;
// let mut execution = None;
// let mut thumbnails = None;
// let mut introspection = None;
// for request in self.receiver.try_iter() {
// match request {
// GraphRuntimeRequest::CompilationRequest(_) => graph = Some(request),
// GraphRuntimeRequest::EvaluationRequest(_) => execution = Some(request),
// GraphRuntimeRequest::ThumbnailRenderResponse(_) => thumbnails = Some(request),
// GraphRuntimeRequest::IntrospectionResponse(_) => introspection = Some(request),
// }
// }
// let requests = [font, preferences, graph, execution].into_iter().flatten();
// TODO: This deduplication of messages will probably 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(CompilationRequest {
mut network,
font_cache,
editor_metadata,
}) => {
GraphRuntimeRequest::CompilationRequest(_) => compilation = Some(request),
GraphRuntimeRequest::EvaluationRequest(_) => evaluation = Some(request),
GraphRuntimeRequest::IntrospectionRequest(_) => introspection = Some(request),
}
}
let requests = [compilation, evaluation, introspection].into_iter().flatten();
for request in requests {
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).await;
let result = self.update_network(network, font_cache, editor_metadata).await;
self.sender.send_compilation_response(CompilationResponse {
result,
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,
inputs_to_monitor,
// custom_node_to_evaluate
node_to_evaluate,
}) => {
for (protonode_input, introspect_mode) in inputs_to_monitor {
self.executor.set_introspect(protonode_input, introspect_mode)
}
let transform = context.render_config.viewport.transform;
// for (protonode_input, introspect_mode) in &inputs_to_monitor {
// self.executor.set_introspect(*protonode_input, *introspect_mode)
// }
let result = self.executor.evaluate_from_node(context, node_to_evaluate).await;
let result = self.execute_network(render_config).await;
let introspected_inputs = Vec::new();
for (protonode_input, mode) in inputs_to_introspect {
let Ok(introspected_data) = self.executor.introspect(protonode_input, mode) else {
log::error!("Could not introspect node from input: {:?}", protonode_input);
continue;
self.sender.send_evaluation_response(EvaluationResponse { evaluation_id, result });
}
// GraphRuntimeRequest::ThumbnailRenderRequest(_) => {}
GraphRuntimeRequest::IntrospectionRequest(inputs) => {
let mut introspected_inputs = Vec::new();
for protonode_input in inputs {
let introspected_data = match self.executor.introspect(protonode_input, IntrospectMode::Data) {
Ok(introspected_data) => introspected_data,
Err(e) => {
log::error!("Could not introspect input: {:?}, error: {:?}", protonode_input, e);
continue;
}
};
introspected_inputs.push((protonode_input, mode, introspected_data));
introspected_inputs.push((protonode_input, IntrospectMode::Data, introspected_data));
}
self.sender.send_evaluation_response(EvaluationResponse {
evaluation_id,
result,
transform,
introspected_inputs,
});
}
GraphRuntimeRequest::ThumbnailRenderRequest(input_to_render) => {
let mut thumbnail_response = ThumbnailRenderResponse::default();
for input in input_to_render {}
self.sender.send_thumbnail_render_response(thumbnail_response);
}
GraphRuntimeRequest::IntrospectionRequest(inputs_to_introspect) => {
self.sender.send_introspection_response(introspection_response);
self.sender.send_introspection_response(IntrospectionResponse(introspected_inputs));
}
}
}
}
async fn update_network(&mut self, mut graph: NodeNetwork) -> Result<CompilationMetadata, String> {
async fn update_network(&mut self, mut graph: NodeNetwork, font_cache: Arc<FontCache>, editor_metadata: EditorMetadata) -> Result<CompilationMetadata, String> {
preprocessor::expand_network(&mut graph, &self.substitutions);
// Creates a network where the node paths to the document network are prefixed with NodeId(0)
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
let mut scoped_network = wrap_network_in_scope(graph, font_cache, editor_metadata, self.application_io.as_ref().unwrap().clone());
// We assume only one output
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
// 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, protonode_callers_for_value, protonode_callers_for_node) = match scoped_network.flatten() {
let (proto_network, protonode_caller_for_values, protonode_caller_for_nodes) = match scoped_network.flatten() {
Ok(network) => network,
Err(e) => {
log::error!("Error compiling network: {e:?}");
return;
return Err(e);
}
};
assert_ne!(proto_network.len(), 0, "No proto nodes exist?");
let result = match self.executor.update(proto_network).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
Ok(CompilationMetadata {
protonode_callers_for_value,
protonode_callers_for_node,
protonode_caller_for_values,
protonode_caller_for_nodes,
types_to_add,
types_to_remove,
})
@@ -225,23 +197,8 @@ impl NodeRuntime {
Err(format!("{e:?}"))
}
};
}
async fn execute_network(&mut self, render_config: RenderConfig) -> Result<TaggedValue, String> {
use graph_craft::graphene_compiler::Executor;
let result = match self.executor.input_type() {
Some(t) if t == concrete!(RenderConfig) => (&self.executor).execute(render_config).await.map_err(|e| e.to_string()),
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
Some(t) => Err(format!("Invalid input type {t:?}")),
_ => Err(format!("No input type:\n{:?}", self.node_graph_errors)),
};
let result = match result {
Ok(value) => value,
Err(e) => return Err(e),
};
Ok(result)
// log::debug!("result: {:?}", result);
result
}
}