mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 08:38:11 +08:00
Context nullification, cached monitor nodes
This commit is contained in:
+51
-21
@@ -5,8 +5,10 @@ use crate::messages::prelude::*;
|
|||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Dispatcher {
|
pub struct Dispatcher {
|
||||||
buffered_queue: Vec<Message>,
|
evaluation_queue: Vec<Message>,
|
||||||
queueing_messages: bool,
|
introspection_queue: Vec<Message>,
|
||||||
|
queueing_evaluation_messages: bool,
|
||||||
|
queueing_introspection_messages: bool,
|
||||||
message_queues: Vec<VecDeque<Message>>,
|
message_queues: Vec<VecDeque<Message>>,
|
||||||
pub responses: Vec<FrontendMessage>,
|
pub responses: Vec<FrontendMessage>,
|
||||||
pub message_handlers: DispatcherMessageHandlers,
|
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.
|
/// 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).
|
/// In addition, these messages do not change any state in the backend (aside from caches).
|
||||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||||
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::CompileActiveDocument),
|
||||||
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::EvaluateActiveDocument),
|
||||||
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::IntrospectActiveDocument),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||||
PropertiesPanelMessageDiscriminant::Refresh,
|
PropertiesPanelMessageDiscriminant::Refresh,
|
||||||
))),
|
))),
|
||||||
@@ -91,13 +96,6 @@ impl Dispatcher {
|
|||||||
|
|
||||||
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
|
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
|
||||||
let message = message.into();
|
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
|
// 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]);
|
Self::schedule_execution(&mut self.message_queues, process_after_all_current, [message]);
|
||||||
@@ -117,7 +115,21 @@ impl Dispatcher {
|
|||||||
continue;
|
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`
|
// Print the message at a verbosity level of `info`
|
||||||
self.log_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
self.log_message(&message, &self.message_queues, self.message_handlers.debug_message_handler.message_logging_verbosity);
|
||||||
|
|
||||||
@@ -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
|
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
|
||||||
match message {
|
match message {
|
||||||
Message::StartQueue => {
|
Message::StartEvaluationQueue => {
|
||||||
self.queueing_messages = true;
|
self.queueing_evaluation_messages = true;
|
||||||
}
|
}
|
||||||
Message::EndQueue => {
|
Message::EndEvaluationQueue => {
|
||||||
self.queueing_messages = false;
|
self.queueing_evaluation_messages = false;
|
||||||
}
|
}
|
||||||
Message::ProcessQueue((render_output_metadata, introspected_inputs)) => {
|
Message::ProcessEvaluationQueue(render_output_metadata) => {
|
||||||
let message = PortfolioMessage::ProcessEvaluationResponse {
|
let update_message = PortfolioMessage::ProcessEvaluationResponse {
|
||||||
evaluation_metadata: render_output_metadata,
|
evaluation_metadata: render_output_metadata,
|
||||||
introspected_inputs,
|
}
|
||||||
};
|
.into();
|
||||||
// Add the message to update the state with the render output
|
// Update the state with the render output and introspected inputs
|
||||||
Self::schedule_execution(&mut self.message_queues, true, [message]);
|
Self::schedule_execution(&mut self.message_queues, true, [update_message]);
|
||||||
|
|
||||||
// Schedule all queued messages to be run (in the order they were added)
|
// Schedule all queued messages to be run, which use the introspected inputs (in the order they were added)
|
||||||
Self::schedule_execution(&mut self.message_queues, true, std::mem::take(&mut self.buffered_queue));
|
Self::schedule_execution(&mut self.message_queues, true, std::mem::take(&mut self.evaluation_queue));
|
||||||
|
}
|
||||||
|
Message::StartIntrospectionQueue => {
|
||||||
|
self.queueing_introspection_messages = true;
|
||||||
|
}
|
||||||
|
Message::EndIntrospectionQueue => {
|
||||||
|
self.queueing_introspection_messages = false;
|
||||||
|
}
|
||||||
|
Message::ProcessIntrospectionQueue(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::NoOp => {}
|
||||||
Message::Init => {
|
Message::Init => {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for Exp
|
|||||||
ExportDialogMessage::TransparentBackground(transparent_background) => self.transparent_background = transparent_background,
|
ExportDialogMessage::TransparentBackground(transparent_background) => self.transparent_background = transparent_background,
|
||||||
ExportDialogMessage::ExportBounds(export_area) => self.bounds = export_area,
|
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_name: portfolio.active_document().map(|document| document.name.clone()).unwrap_or_default(),
|
||||||
file_type: self.file_type,
|
file_type: self.file_type,
|
||||||
scale_factor: self.scale_factor,
|
scale_factor: self.scale_factor,
|
||||||
|
|||||||
+4
-7
@@ -1,7 +1,7 @@
|
|||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::{IVec2, UVec2};
|
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.
|
/// A dialog to allow users to set some initial options about a new document.
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[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;
|
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
|
||||||
if create_artboard {
|
if create_artboard {
|
||||||
responses.add(Message::StartQueue);
|
|
||||||
responses.add(GraphOperationMessage::NewArtboard {
|
responses.add(GraphOperationMessage::NewArtboard {
|
||||||
id: NodeId::new(),
|
id: NodeId::new(),
|
||||||
artboard: graphene_std::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
artboard: graphene_std::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
responses.add(Message::StartEvaluationQueue);
|
||||||
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
|
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||||
// 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::EndEvaluationQueue);
|
||||||
responses.add(Message::StartQueue);
|
|
||||||
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
|
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
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::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::HintData;
|
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::raster::color::Color;
|
||||||
use graphene_std::text::Font;
|
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::input_mapper::utility_types::misc::FrameTimeInfo;
|
||||||
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::DVec2;
|
use glam::{DAffine2, DVec2};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct InputPreprocessorMessageContext {
|
pub struct InputPreprocessorMessageContext {
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
use std::sync::Arc;
|
use crate::{messages::prelude::*, node_graph_executor::IntrospectionResponse};
|
||||||
|
|
||||||
use crate::messages::prelude::*;
|
|
||||||
use graphene_std::{IntrospectMode, uuid::CompiledProtonodeInput};
|
|
||||||
use graphite_proc_macros::*;
|
use graphite_proc_macros::*;
|
||||||
|
|
||||||
#[impl_message]
|
#[impl_message]
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
NoOp,
|
NoOp,
|
||||||
Init,
|
Init,
|
||||||
Batched(Box<[Message]>),
|
Batched(Box<[Message]>),
|
||||||
// Adds any subsequent messages to the queue
|
// Adds any subsequent messages to the queue
|
||||||
StartQueue,
|
StartEvaluationQueue,
|
||||||
// Stop adding messages to the queue.
|
// Stop adding messages to the queue.
|
||||||
EndQueue,
|
EndEvaluationQueue,
|
||||||
// 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
|
// 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
|
||||||
ProcessQueue(
|
#[serde(skip)]
|
||||||
(
|
ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata),
|
||||||
graphene_std::renderer::RenderMetadata,
|
StartIntrospectionQueue,
|
||||||
Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
|
EndIntrospectionQueue,
|
||||||
),
|
// Processes all messages that are queued to be run after introspection, which occurs on the evaluation response. This allows a message to be run with data from after the evaluation is complete
|
||||||
),
|
#[serde(skip)]
|
||||||
|
ProcessIntrospectionQueue(IntrospectionResponse),
|
||||||
#[child]
|
#[child]
|
||||||
Animation(AnimationMessage),
|
Animation(AnimationMessage),
|
||||||
#[child]
|
#[child]
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
|
|||||||
use crate::messages::portfolio::utility_types::PanelType;
|
use crate::messages::portfolio::utility_types::PanelType;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graphene_std::uuid::CompiledProtonodeInput;
|
use graphene_std::vector::click_target::ClickTarget;
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::raster::BlendMode;
|
use graphene_std::raster::BlendMode;
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::renderer::ClickTarget;
|
|
||||||
use graphene_std::transform::Footprint;
|
use graphene_std::transform::Footprint;
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::style::ViewMode;
|
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::overlays::utility_types::Pivot;
|
||||||
use super::utility_types::error::EditorError;
|
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::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 super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
||||||
use crate::application::{GRAPHITE_GIT_COMMIT_HASH, generate_uuid};
|
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};
|
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::node_graph::NodeGraphMessageContext;
|
||||||
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
|
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::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::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
|
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::document::utility_types::nodes::RawBuffer;
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
use crate::messages::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
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::select_tool::SelectToolPointerKeys;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::Key;
|
use crate::messages::tool::tool_messages::tool_prelude::Key;
|
||||||
use crate::messages::tool::utility_types::ToolType;
|
use crate::messages::tool::utility_types::ToolType;
|
||||||
use crate::node_graph_executor::NodeGraphExecutor;
|
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graph_craft::document::value::TaggedValue;
|
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::math::quad::Quad;
|
||||||
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
|
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
|
||||||
use graphene_std::raster::BlendMode;
|
use graphene_std::raster::BlendMode;
|
||||||
@@ -37,18 +35,16 @@ use graphene_std::uuid::NodeId;
|
|||||||
use graphene_std::vector::PointId;
|
use graphene_std::vector::PointId;
|
||||||
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
||||||
use graphene_std::vector::style::ViewMode;
|
use graphene_std::vector::style::ViewMode;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct DocumentMessageContext<'a> {
|
pub struct DocumentMessageData<'a> {
|
||||||
pub document_id: DocumentId,
|
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub persistent_data: &'a PersistentData,
|
pub persistent_data: &'a PersistentData,
|
||||||
pub current_tool: &'a ToolType,
|
pub current_tool: &'a ToolType,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
pub device_pixel_ratio: f64,
|
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>,
|
// pub downcasted_inputs: &mut HashMap<CompiledProtonodeInput, TaggedValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,10 +169,9 @@ impl Default for DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMessageHandler {
|
impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
|
||||||
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, context: DocumentMessageContext) {
|
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
|
||||||
let DocumentMessageContext {
|
let DocumentMessageData {
|
||||||
document_id,
|
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data,
|
persistent_data,
|
||||||
current_tool,
|
current_tool,
|
||||||
@@ -222,12 +217,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
DocumentMessage::PropertiesPanel(message) => {
|
DocumentMessage::PropertiesPanel(message) => {
|
||||||
let context = PropertiesPanelMessageContext {
|
let properties_panel_message_handler_data = super::properties_panel::PropertiesPanelMessageHandlerData {
|
||||||
network_interface: &mut self.network_interface,
|
network_interface: &mut self.network_interface,
|
||||||
selection_network_path: &self.selection_network_path,
|
selection_network_path: &self.selection_network_path,
|
||||||
document_name: self.name.as_str(),
|
document_name: self.name.as_str(),
|
||||||
executor,
|
|
||||||
persistent_data,
|
|
||||||
};
|
};
|
||||||
self.properties_panel_message_handler.process_message(message, responses, context);
|
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,
|
network_interface: &mut self.network_interface,
|
||||||
selection_network_path: &self.selection_network_path,
|
selection_network_path: &self.selection_network_path,
|
||||||
breadcrumb_network_path: &self.breadcrumb_network_path,
|
breadcrumb_network_path: &self.breadcrumb_network_path,
|
||||||
document_id,
|
|
||||||
collapsed: &mut self.collapsed,
|
collapsed: &mut self.collapsed,
|
||||||
ipp,
|
ipp,
|
||||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||||
@@ -1432,7 +1424,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
center: Key::Alt,
|
center: Key::Alt,
|
||||||
duplicate: Key::Alt,
|
duplicate: Key::Alt,
|
||||||
}));
|
}));
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
||||||
} else {
|
} else {
|
||||||
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
|
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
|
||||||
return;
|
return;
|
||||||
@@ -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
|
// Connect the current output data to the artboard's input data, and the artboard's output to the document output
|
||||||
responses.add(NodeGraphMessage::InsertNodeBetween {
|
responses.add(NodeGraphMessage::InsertNodeBetween {
|
||||||
node_id,
|
node_id,
|
||||||
input_connector: network_interface::InputConnector::Export(0),
|
input_connector: InputConnector::Export(0),
|
||||||
insert_node_input_index: 1,
|
insert_node_input_index: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1914,13 +1906,14 @@ impl DocumentMessageHandler {
|
|||||||
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
|
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
|
// 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(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
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(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(NodeGraphMessage::UnloadWires);
|
||||||
responses.add(Message::StartQueue);
|
responses.add(NodeGraphMessage::SendWires);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
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);
|
network_interface.set_document_to_viewport_transform(transform);
|
||||||
|
|
||||||
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
|
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
|
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
|
||||||
responses.add(NodeGraphMessage::UnloadWires);
|
responses.add(NodeGraphMessage::UnloadWires);
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWires);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2108,7 +2103,7 @@ impl DocumentMessageHandler {
|
|||||||
/// Loads all of the fonts in the document.
|
/// Loads all of the fonts in the document.
|
||||||
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>) {
|
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>) {
|
||||||
let mut fonts = HashSet::new();
|
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 {
|
for input in &node.inputs {
|
||||||
if let Some(TaggedValue::Font(font)) = input.as_value() {
|
if let Some(TaggedValue::Font(font)) = input.as_value() {
|
||||||
fonts.insert(font.clone());
|
fonts.insert(font.clone());
|
||||||
@@ -2581,7 +2576,7 @@ impl DocumentMessageHandler {
|
|||||||
layout: Layout::WidgetLayout(document_bar_layout),
|
layout: Layout::WidgetLayout(document_bar_layout),
|
||||||
layout_target: LayoutTarget::DocumentBar,
|
layout_target: LayoutTarget::DocumentBar,
|
||||||
});
|
});
|
||||||
responses.add(NodeGraphMessage::ForceRunDocumentGraph);
|
responses.add(PortfolioMessage::EvaluateActiveDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_layers_panel_control_bar_widgets(&self, responses: &mut VecDeque<Message>) {
|
pub fn update_layers_panel_control_bar_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, IVec2};
|
use glam::{DAffine2, IVec2};
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graphene_std::Artboard;
|
use graphene_std::Artboard;
|
||||||
use graphene_std::brush::brush_stroke::BrushStroke;
|
use graphene_std::brush::brush_stroke::BrushStroke;
|
||||||
use graphene_std::raster::BlendMode;
|
use graphene_std::raster::BlendMode;
|
||||||
use graphene_std::raster_types::{CPU, RasterDataTable};
|
use graphene_std::raster_types::{CPU, RasterDataTable};
|
||||||
use graphene_std::text::{Font, TypesettingConfig};
|
use graphene_std::text::{Font, TypesettingConfig};
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::PointId;
|
use graphene_std::vector::PointId;
|
||||||
use graphene_std::vector::VectorModificationType;
|
use graphene_std::vector::VectorModificationType;
|
||||||
use graphene_std::vector::style::{Fill, Stroke};
|
use graphene_std::vector::style::{Fill, Stroke};
|
||||||
|
|||||||
+3
-2
@@ -2,12 +2,13 @@ use super::transform_utils;
|
|||||||
use super::utility_types::ModifyInputsContext;
|
use super::utility_types::ModifyInputsContext;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
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::portfolio::document::utility_types::nodes::CollapsedLayers;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
|
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
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::Color;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::renderer::convert_usvg_path::convert_usvg_path;
|
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 bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::value::TaggedValue;
|
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;
|
use graphene_std::vector::PointId;
|
||||||
|
|
||||||
/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`.
|
/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`.
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
use super::transform_utils;
|
use super::transform_utils;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
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::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 crate::messages::prelude::*;
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, IVec2};
|
use glam::{DAffine2, IVec2};
|
||||||
use graph_craft::concrete;
|
use graph_craft::concrete;
|
||||||
use graph_craft::document::value::TaggedValue;
|
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::Artboard;
|
||||||
use graphene_std::brush::brush_stroke::BrushStroke;
|
use graphene_std::brush::brush_stroke::BrushStroke;
|
||||||
use graphene_std::raster::BlendMode;
|
use graphene_std::raster::BlendMode;
|
||||||
use graphene_std::raster_types::{CPU, RasterDataTable};
|
use graphene_std::raster_types::{CPU, RasterDataTable};
|
||||||
use graphene_std::text::{Font, TypesettingConfig};
|
use graphene_std::text::{Font, TypesettingConfig};
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::style::{Fill, Stroke};
|
use graphene_std::vector::style::{Fill, Stroke};
|
||||||
use graphene_std::vector::{PointId, VectorModificationType};
|
use graphene_std::vector::{PointId, VectorModificationType};
|
||||||
use graphene_std::vector::{VectorData, VectorDataTable};
|
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::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct NavigationMessageContext<'a> {
|
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::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::Message;
|
use crate::messages::prelude::Message;
|
||||||
use crate::node_graph_executor::NodeGraphExecutor;
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graph_craft::ProtoNodeIdentifier;
|
use graph_craft::ProtoNodeIdentifier;
|
||||||
use graph_craft::concrete;
|
use graph_craft::concrete;
|
||||||
@@ -23,6 +22,7 @@ use graphene_std::raster_types::{CPU, RasterDataTable};
|
|||||||
use graphene_std::text::{Font, TypesettingConfig};
|
use graphene_std::text::{Font, TypesettingConfig};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use graphene_std::transform::Footprint;
|
use graphene_std::transform::Footprint;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::VectorDataTable;
|
use graphene_std::vector::VectorDataTable;
|
||||||
use graphene_std::*;
|
use graphene_std::*;
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use super::utility_types::Direction;
|
use super::utility_types::Direction;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
|
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::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 crate::messages::prelude::*;
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{NodeId, NodeInput};
|
use graph_craft::document::{InputConnector, NodeInput, OutputConnector};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graphene_std::uuid::NodeId;
|
||||||
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
|
|
||||||
|
|
||||||
#[impl_message(Message, DocumentMessage, NodeGraph)]
|
#[impl_message(Message, DocumentMessage, NodeGraph)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -111,8 +110,6 @@ pub enum NodeGraphMessage {
|
|||||||
start_index: usize,
|
start_index: usize,
|
||||||
end_index: usize,
|
end_index: usize,
|
||||||
},
|
},
|
||||||
RunDocumentGraph,
|
|
||||||
ForceRunDocumentGraph,
|
|
||||||
SelectedNodesAdd {
|
SelectedNodesAdd {
|
||||||
nodes: Vec<NodeId>,
|
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::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{
|
use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, Previewing, TypeSource};
|
||||||
self, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, TypeSource,
|
|
||||||
};
|
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
|
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
|
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
|
||||||
use crate::messages::prelude::*;
|
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::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
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 graph_craft::proto::GraphErrors;
|
||||||
use graphene_std::math::math_ext::QuadExt;
|
use graphene_std::math::math_ext::QuadExt;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::*;
|
use graphene_std::*;
|
||||||
use renderer::Quad;
|
use renderer::Quad;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -32,7 +31,6 @@ pub struct NodeGraphMessageContext<'a> {
|
|||||||
pub network_interface: &'a mut NodeNetworkInterface,
|
pub network_interface: &'a mut NodeNetworkInterface,
|
||||||
pub selection_network_path: &'a [NodeId],
|
pub selection_network_path: &'a [NodeId],
|
||||||
pub breadcrumb_network_path: &'a [NodeId],
|
pub breadcrumb_network_path: &'a [NodeId],
|
||||||
pub document_id: DocumentId,
|
|
||||||
pub collapsed: &'a mut CollapsedLayers,
|
pub collapsed: &'a mut CollapsedLayers,
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub graph_view_overlay_open: bool,
|
pub graph_view_overlay_open: bool,
|
||||||
@@ -99,7 +97,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
network_interface,
|
network_interface,
|
||||||
selection_network_path,
|
selection_network_path,
|
||||||
breadcrumb_network_path,
|
breadcrumb_network_path,
|
||||||
document_id,
|
|
||||||
collapsed,
|
collapsed,
|
||||||
ipp,
|
ipp,
|
||||||
graph_view_overlay_open,
|
graph_view_overlay_open,
|
||||||
@@ -767,14 +764,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
self.initial_disconnecting = false;
|
self.initial_disconnecting = false;
|
||||||
|
|
||||||
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
|
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
|
||||||
if let Some((output_type, source)) = clicked_output
|
let (output_type, source) = network_interface.output_type(&clicked_output, breadcrumb_network_path);
|
||||||
.node_id()
|
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(&output_type, &source);
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update_node_graph_hints(responses);
|
self.update_node_graph_hints(responses);
|
||||||
return;
|
return;
|
||||||
@@ -922,7 +913,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let vector_wire = build_vector_wire(
|
let (vector_wire, _) = build_vector_wire(
|
||||||
wire_in_progress_from_connector,
|
wire_in_progress_from_connector,
|
||||||
wire_in_progress_to_connector,
|
wire_in_progress_to_connector,
|
||||||
from_connector_is_layer,
|
from_connector_is_layer,
|
||||||
@@ -936,6 +927,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
data_type: self.wire_in_progress_type,
|
data_type: self.wire_in_progress_type,
|
||||||
thick: false,
|
thick: false,
|
||||||
dashed: false,
|
dashed: false,
|
||||||
|
center: None,
|
||||||
|
input_sni: None,
|
||||||
};
|
};
|
||||||
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) });
|
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
|
// Get the compatible type from the output connector
|
||||||
let compatible_type = output_connector.and_then(|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
|
||||||
// Get the output types from the network interface
|
let (output_type, type_source) = network_interface.output_type(&output_connector, selection_network_path);
|
||||||
let (output_type, type_source) = network_interface.output_type(&node_id, output_connector.index(), selection_network_path);
|
|
||||||
|
|
||||||
match type_source {
|
match type_source {
|
||||||
TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None,
|
TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None,
|
||||||
_ => Some(format!("type:{}", output_type.nested_type())),
|
_ => 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_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. };
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
let (wire, is_stack, _) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
||||||
wire.rectangle_intersections_exist(bounding_box[0], bounding_box[1]).then_some((input, is_stack))
|
wire.rectangle_intersections_exist(bounding_box[0], bounding_box[1]).then_some((input, is_stack))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -1290,12 +1281,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
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 } => {
|
NodeGraphMessage::SelectedNodesAdd { nodes } => {
|
||||||
let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else {
|
let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else {
|
||||||
log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesAdd");
|
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 {
|
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
|
||||||
return;
|
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();
|
let mut nodes = Vec::new();
|
||||||
for node_id in &self.frontend_nodes {
|
for node_id in &self.frontend_nodes {
|
||||||
let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else {
|
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);
|
log::error!("Could not get bbox for node: {:?}", node_id);
|
||||||
continue;
|
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 {
|
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);
|
nodes.push(*node_id);
|
||||||
}
|
}
|
||||||
@@ -1393,6 +1377,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
responses.add(PropertiesPanelMessage::Refresh);
|
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) {
|
if !(network_interface.reference(&node_id, selection_network_path).is_none() || input_index == 0) && network_interface.connected_to_output(&node_id, selection_network_path) {
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
|
responses.add(Message::StartEvaluationQueue);
|
||||||
|
responses.add(NodeGraphMessage::SendWires);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetInput { input_connector, input } => {
|
NodeGraphMessage::SetInput { input_connector, input } => {
|
||||||
@@ -1688,8 +1675,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next();
|
.next();
|
||||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
|
||||||
responses.add(FrontendMessage::UpdateImportsExports {
|
responses.add(FrontendMessage::UpdateImportsExports {
|
||||||
imports,
|
imports,
|
||||||
exports,
|
exports,
|
||||||
@@ -2182,6 +2167,7 @@ impl NodeGraphMessageHandler {
|
|||||||
|
|
||||||
fn collect_nodes(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec<FrontendNode> {
|
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 {
|
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();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
let mut can_be_layer_lookup = HashSet::new();
|
let mut can_be_layer_lookup = HashSet::new();
|
||||||
@@ -2232,7 +2218,7 @@ impl NodeGraphMessageHandler {
|
|||||||
let primary_input = inputs.next().flatten();
|
let primary_input = inputs.next().flatten();
|
||||||
let exposed_inputs = inputs.flatten().collect();
|
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 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();
|
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) {
|
if output_index == 0 && network_interface.has_primary_output(&node_id, breadcrumb_network_path) {
|
||||||
continue;
|
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 data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source);
|
||||||
|
|
||||||
let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get(&node_id) else {
|
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 locked = network_interface.is_locked(&node_id, breadcrumb_network_path);
|
||||||
|
|
||||||
let errors = self
|
let errors = None; // TODO: Recursive traversal from export over all protonodes and match metadata with error
|
||||||
.node_graph_errors
|
// self
|
||||||
.iter()
|
// .node_graph_errors
|
||||||
.find(|error| error.node_path == node_id_path)
|
// .iter()
|
||||||
.map(|error| format!("{:?}", error.error.clone()))
|
// .find(|error| error.stable_node_id == node_id_path)
|
||||||
.or_else(|| {
|
// .map(|error| format!("{:?}", error.error.clone()))
|
||||||
if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) {
|
// .or_else(|| {
|
||||||
Some("Node graph type error within this node".to_string())
|
// if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) {
|
||||||
} else {
|
// Some("Node graph type error within this node".to_string())
|
||||||
None
|
// } else {
|
||||||
}
|
// None
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
nodes.push(FrontendNode {
|
nodes.push(FrontendNode {
|
||||||
id: node_id,
|
id: node_id,
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
|
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
|
||||||
use super::utility_types::FrontendGraphDataType;
|
use super::utility_types::FrontendGraphDataType;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use choice::enum_choice;
|
use choice::enum_choice;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2, IVec2, UVec2};
|
use glam::{DAffine2, DVec2, IVec2, UVec2};
|
||||||
use graph_craft::Type;
|
use graph_craft::Type;
|
||||||
use graph_craft::document::value::TaggedValue;
|
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::animation::RealTimeMode;
|
||||||
use graphene_std::extract_xy::XY;
|
use graphene_std::extract_xy::XY;
|
||||||
use graphene_std::path_bool::BooleanOperation;
|
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::raster_types::{CPU, GPU, RasterDataTable};
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
use graphene_std::transform::{Footprint, ReferencePoint};
|
use graphene_std::transform::{Footprint, ReferencePoint};
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::VectorDataTable;
|
use graphene_std::vector::VectorDataTable;
|
||||||
use graphene_std::vector::misc::GridType;
|
use graphene_std::vector::misc::GridType;
|
||||||
use graphene_std::vector::misc::{ArcType, MergeByDistanceAlgorithm};
|
use graphene_std::vector::misc::{ArcType, MergeByDistanceAlgorithm};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector, TypeSource};
|
use crate::messages::portfolio::document::utility_types::network_interface::{TypeSource};
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::{InputConnector, OutputConnector};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::Type;
|
use graphene_std::Type;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ pub struct FrontendGraphOutput {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendNode {
|
pub struct FrontendNode {
|
||||||
pub id: graph_craft::document::NodeId,
|
pub id: NodeId,
|
||||||
#[serde(rename = "isLayer")]
|
#[serde(rename = "isLayer")]
|
||||||
pub is_layer: bool,
|
pub is_layer: bool,
|
||||||
#[serde(rename = "canBeLayer")]
|
#[serde(rename = "canBeLayer")]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
mod properties_panel_message;
|
mod properties_panel_message;
|
||||||
pub mod properties_panel_message_handler;
|
mod properties_panel_message_handler;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use properties_panel_message::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant};
|
pub use properties_panel_message::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use properties_panel_message_handler::PropertiesPanelMessageHandler;
|
pub use properties_panel_message_handler::{PropertiesPanelMessageHandler, PropertiesPanelMessageHandlerData};
|
||||||
|
|||||||
-1
@@ -7,7 +7,6 @@ use crate::messages::portfolio::utility_types::PersistentData;
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||||
pub network_interface: &'a mut NodeNetworkInterface,
|
pub network_interface: &'a mut NodeNetworkInterface,
|
||||||
pub selection_network_path: &'a [NodeId],
|
pub selection_network_path: &'a [NodeId],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
use super::network_interface::NodeTemplate;
|
use super::network_interface::NodeTemplate;
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
|
#[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::portfolio::document::utility_types::network_interface::FlowType;
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graphene_std::math::quad::Quad;
|
use graphene_std::math::quad::Quad;
|
||||||
use graphene_std::transform::Footprint;
|
use graphene_std::transform::Footprint;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
||||||
use graphene_std::vector::{PointId, VectorData};
|
use graphene_std::vector::{PointId, VectorData};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|||||||
@@ -11,15 +11,13 @@ use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode;
|
|||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graph_craft::document::value::TaggedValue;
|
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 graph_craft::{Type, concrete};
|
||||||
use graphene_std::math::quad::Quad;
|
use graphene_std::math::quad::Quad;
|
||||||
use graphene_std::transform::Footprint;
|
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::click_target::{ClickTarget, ClickTargetType};
|
||||||
use graphene_std::vector::{PointId, VectorData, VectorModificationType};
|
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 interpreted_executor::node_registry::NODE_REGISTRY;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
@@ -38,8 +36,9 @@ pub struct NodeNetworkInterface {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
document_metadata: DocumentMetadata,
|
document_metadata: DocumentMetadata,
|
||||||
/// All input/output types based on the compiled network.
|
/// All input/output types based on the compiled network.
|
||||||
|
/// TODO: Move to portfolio message handler
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub resolved_types: ResolvedDocumentNodeTypes,
|
pub resolved_types: HashMap<SNI, Vec<Type>>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
transaction_status: TransactionStatus,
|
transaction_status: TransactionStatus,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -54,6 +53,7 @@ impl Clone for NodeNetworkInterface {
|
|||||||
document_metadata: Default::default(),
|
document_metadata: Default::default(),
|
||||||
resolved_types: Default::default(),
|
resolved_types: Default::default(),
|
||||||
transaction_status: TransactionStatus::Finished,
|
transaction_status: TransactionStatus::Finished,
|
||||||
|
current_hash: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,7 +490,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try and get the [`DocumentNodeDefinition`] for a node
|
/// 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)?;
|
let metadata = self.node_metadata(&node_id, network_path)?;
|
||||||
resolve_document_node_type(metadata.persistent_metadata.reference.as_ref()?)
|
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 {
|
match output_connector {
|
||||||
OutputConnector::Node { node_id, output_index } => match self.implementation(&node_id, network_path)? {
|
OutputConnector::Node { node_id, output_index } => match self.implementation(node_id, network_path)? {
|
||||||
DocumentNodeImplementation::Network(node_network) => {
|
DocumentNodeImplementation::Network(_) => {
|
||||||
let mut nested_path = network_path.to_vec();
|
let mut nested_path = network_path.to_vec();
|
||||||
nested_path.push(node_id);
|
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(_) => 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(),
|
||||||
DocumentNodeImplementation::Extract => todo!(),
|
DocumentNodeImplementation::Extract => todo!(),
|
||||||
@@ -526,44 +526,44 @@ impl NodeNetworkInterface {
|
|||||||
OutputConnector::Import(import_index) => {
|
OutputConnector::Import(import_index) => {
|
||||||
let mut encapsulating_path = network_path.to_vec();
|
let mut encapsulating_path = network_path.to_vec();
|
||||||
let node_id = encapsulating_path.pop().expect("No imports in document network");
|
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.
|
// 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 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
|
// 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
|
// Cases: Node/Value input to protonode, Node/Value input to network node
|
||||||
let input = self.input_from_connector(input_connector, network_path)?;
|
let input = self.input_from_connector(input_connector, network_path)?;
|
||||||
let caller_input = match input {
|
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)? {
|
match self.implementation(node_id, network_path)? {
|
||||||
DocumentNodeImplementation::Network(node_network) => {
|
DocumentNodeImplementation::Network(_) => {
|
||||||
// Continue traversal within network
|
// Continue traversal within network
|
||||||
let mut nested_path = network_path.to_vec();
|
let mut nested_path = network_path.to_vec();
|
||||||
nested_path.push(*node_id);
|
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
|
// If connected to a protonode, use the data in the node metadata
|
||||||
DocumentNodeImplementation::Extract => todo!(),
|
DocumentNodeImplementation::Extract => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Can either be an input to a protonode, network node, or export
|
// Can either be an input to a protonode, network node, or export
|
||||||
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Reflection(_) => match input_connector {
|
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::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(),
|
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 mut encapsulating_path = network_path.to_vec();
|
||||||
let node_id = encapsulating_path.pop().expect("No imports in document network");
|
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 {
|
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;
|
return None;
|
||||||
};
|
};
|
||||||
Some(caller_input)
|
Some(caller_input)
|
||||||
@@ -631,7 +631,7 @@ impl NodeNetworkInterface {
|
|||||||
{
|
{
|
||||||
let mut inner_path = network_path.to_vec();
|
let mut inner_path = network_path.to_vec();
|
||||||
inner_path.push(node_id);
|
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();
|
inner_path.pop();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -645,6 +645,10 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
/// Get the [`Type`] for any InputConnector
|
/// Get the [`Type`] for any InputConnector
|
||||||
pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
|
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
|
if let Some(compiled_type) = self
|
||||||
.downstream_caller_from_input(input_connector, network_path)
|
.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)))
|
.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"));
|
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> {
|
pub fn output_type(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
|
||||||
let (sni, input_index) = self.downstream_caller_from_output(output_connector, network_path)?;
|
if let Some(output_type) = self
|
||||||
let protonode_input_types = self.resolved_types.get(sni)?;
|
.downstream_caller_from_output(output_connector, network_path)
|
||||||
protonode_input_types.get(*input_index)
|
.and_then(|(sni, input_index)| self.resolved_types.get(sni).and_then(|protonode_input_types| 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) {
|
|
||||||
return (output_type.clone(), TypeSource::Compiled);
|
return (output_type.clone(), TypeSource::Compiled);
|
||||||
}
|
}
|
||||||
(concrete!(()), TypeSource::Error("Not compiled"))
|
(concrete!(()), TypeSource::Error("Not compiled"))
|
||||||
@@ -678,10 +679,10 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_type(&mut self, sni: SNI) {
|
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 {
|
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -692,6 +693,7 @@ impl NodeNetworkInterface {
|
|||||||
match input_connector {
|
match input_connector {
|
||||||
InputConnector::Node { node_id, input_index } => {
|
InputConnector::Node { node_id, input_index } => {
|
||||||
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
let Some(input_metadata) = metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
|
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> {
|
pub fn position(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> {
|
||||||
let top_left_position = self
|
let top_left_position = self
|
||||||
.node_click_targets(node_id, network_path)
|
.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.
|
/// 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 {
|
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())
|
.map(|node_definition| node_definition.description.to_string())
|
||||||
.filter(|description| description != "TODO")
|
.filter(|description| description != "TODO")
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@@ -1568,7 +1492,7 @@ impl NodeNetworkInterface {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
nested_network.exports = old_network.exports;
|
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 {
|
let Some(nested_network_metadata) = network_metadata.nested_metadata_mut(&network_path) else {
|
||||||
log::error!("Could not get nested network in from_old_network");
|
log::error!("Could not get nested network in from_old_network");
|
||||||
continue;
|
continue;
|
||||||
@@ -1621,6 +1545,7 @@ impl NodeNetworkInterface {
|
|||||||
document_metadata: DocumentMetadata::default(),
|
document_metadata: DocumentMetadata::default(),
|
||||||
resolved_types: HashMap::new(),
|
resolved_types: HashMap::new(),
|
||||||
transaction_status: TransactionStatus::Finished,
|
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_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 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 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 mut path_string = String::new();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
||||||
@@ -2799,6 +2724,8 @@ impl NodeNetworkInterface {
|
|||||||
data_type,
|
data_type,
|
||||||
thick,
|
thick,
|
||||||
dashed: false,
|
dashed: false,
|
||||||
|
center: None,
|
||||||
|
input_sni: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(WirePathUpdate {
|
Some(WirePathUpdate {
|
||||||
@@ -2809,14 +2736,14 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the vector subpath and a boolean of whether the wire should be thick.
|
/// Returns the vector subpath and a boolean of whether the wire should be thick.
|
||||||
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(Subpath<PointId>, bool)> {
|
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(Subpath<PointId>, bool, DVec2)> {
|
||||||
let Some(input_position) = self.get_input_center(input, network_path) else {
|
let Some(input_position) = self.get_input_center(input, network_path) else {
|
||||||
log::error!("Could not get dom rect for wire end: {:?}", input);
|
log::error!("Could not get dom rect for wire end: {:?}", input);
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
|
// 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 {
|
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 {
|
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);
|
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_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
|
||||||
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
|
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
|
||||||
let thick = vertical_end && vertical_start;
|
let thick = vertical_end && vertical_start;
|
||||||
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> {
|
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 mut path_string = String::new();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
||||||
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
|
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
|
||||||
|
let input_sni = self.downstream_caller_from_input(input, network_path).map(|caller| NodeId(caller.0.0 + caller.1 as u64));
|
||||||
Some(WirePath {
|
Some(WirePath {
|
||||||
path_string,
|
path_string,
|
||||||
data_type,
|
data_type,
|
||||||
thick,
|
thick,
|
||||||
dashed,
|
dashed,
|
||||||
|
center: Some((center.x, center.y)),
|
||||||
|
input_sni,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6562,7 +6493,6 @@ impl InputPersistentMetadata {
|
|||||||
struct InputTransientMetadata {
|
struct InputTransientMetadata {
|
||||||
wire: TransientMetadata<WirePathUpdate>,
|
wire: TransientMetadata<WirePathUpdate>,
|
||||||
caller: Option<CompiledProtonodeInput>,
|
caller: Option<CompiledProtonodeInput>,
|
||||||
input_type: Option<Type>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Eventually remove this migration document upgrade code
|
// TODO: Eventually remove this migration document upgrade code
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ pub struct WirePath {
|
|||||||
pub data_type: FrontendGraphDataType,
|
pub data_type: FrontendGraphDataType,
|
||||||
pub thick: bool,
|
pub thick: bool,
|
||||||
pub dashed: bool,
|
pub dashed: bool,
|
||||||
|
pub center: Option<(f64, f64)>,
|
||||||
|
#[serde(rename = "inputSni")]
|
||||||
|
pub input_sni: Option<NodeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
@@ -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.;
|
let grid_spacing = 24.;
|
||||||
match graph_wire_style {
|
match graph_wire_style {
|
||||||
GraphWireStyle::Direct => {
|
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 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);
|
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![
|
vec![
|
||||||
ManipulatorGroup {
|
ManipulatorGroup {
|
||||||
anchor: locations[0],
|
anchor: locations[0],
|
||||||
@@ -113,7 +116,9 @@ pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
false,
|
false,
|
||||||
)
|
);
|
||||||
|
let center = subpath.center().unwrap();
|
||||||
|
(subpath, center)
|
||||||
}
|
}
|
||||||
GraphWireStyle::GridAligned => {
|
GraphWireStyle::GridAligned => {
|
||||||
let locations = straight_wire_paths(output_position, input_position, vertical_out, vertical_in);
|
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)]
|
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() {
|
if locations.is_empty() {
|
||||||
return Subpath::new(Vec::new(), false);
|
return (Subpath::new(Vec::new(), false), DVec2::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
if locations.len() == 2 {
|
if locations.len() == 2 {
|
||||||
return Subpath::new(
|
let subpath = Subpath::new(
|
||||||
vec![
|
vec![
|
||||||
ManipulatorGroup {
|
ManipulatorGroup {
|
||||||
anchor: locations[0].into(),
|
anchor: locations[0].into(),
|
||||||
@@ -469,6 +474,8 @@ fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
|
|||||||
],
|
],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
let center = subpath.center().unwrap();
|
||||||
|
return (subpath, center);
|
||||||
}
|
}
|
||||||
|
|
||||||
let corner_radius = 10;
|
let corner_radius = 10;
|
||||||
@@ -585,5 +592,7 @@ fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
|
|||||||
out_handle: None,
|
out_handle: None,
|
||||||
id: PointId::generate(),
|
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::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::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 crate::messages::prelude::DocumentMessageHandler;
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use graph_craft::document::DocumentNode;
|
use graph_craft::document::DocumentNode;
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
||||||
|
use graph_craft::document::{InputConnector, OutputConnector};
|
||||||
use graphene_std::ProtoNodeIdentifier;
|
use graphene_std::ProtoNodeIdentifier;
|
||||||
use graphene_std::text::TypesettingConfig;
|
use graphene_std::text::TypesettingConfig;
|
||||||
use graphene_std::uuid::NodeId;
|
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 {
|
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
|
||||||
let node_path_without_type_args = protonode_id.name.split('<').next();
|
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)) {
|
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
|
.network_interface
|
||||||
.document_network()
|
.document_network()
|
||||||
.recursive_nodes()
|
.recursive_nodes()
|
||||||
.map(|(node_id, node, path)| (*node_id, node.clone(), path))
|
.map(|(node_path, node)| (node_path, node.clone()))
|
||||||
.collect::<Vec<(NodeId, graph_craft::document::DocumentNode, Vec<NodeId>)>>();
|
.collect::<Vec<(Vec<NodeId>, graph_craft::document::DocumentNode)>>();
|
||||||
for (node_id, node, network_path) in &nodes {
|
for (node_path, node) in &nodes {
|
||||||
migrate_node(node_id, node, network_path, document, reset_node_definitions_on_open);
|
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());
|
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
|
// 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)) {
|
if node.manual_composition == Some(graph_craft::concrete!(())) || node.manual_composition == Some(graph_craft::concrete!(graphene_std::transform::Footprint)) {
|
||||||
document
|
document
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use super::utility_types::PanelType;
|
use super::utility_types::PanelType;
|
||||||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::node_graph_executor::CompilationResponse;
|
use crate::node_graph_executor::IntrospectionResponse;
|
||||||
use graph_craft::document::CompilationMetadata;
|
use graph_craft::document::CompilationMetadata;
|
||||||
|
use graphene_std::Color;
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::renderer::RenderMetadata;
|
use graphene_std::renderer::RenderMetadata;
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
use graphene_std::uuid::CompiledProtonodeInput;
|
use graphene_std::uuid::CompiledProtonodeInput;
|
||||||
use graphene_std::{Color, IntrospectMode};
|
|
||||||
|
|
||||||
#[impl_message(Message, Portfolio)]
|
#[impl_message(Message, Portfolio)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -24,11 +22,24 @@ pub enum PortfolioMessage {
|
|||||||
#[child]
|
#[child]
|
||||||
Spreadsheet(SpreadsheetMessage),
|
Spreadsheet(SpreadsheetMessage),
|
||||||
|
|
||||||
|
// Introspected data is cleared after all queued messages which relied on the introspection are complete
|
||||||
|
ClearIntrospectedData,
|
||||||
|
|
||||||
// Sends a request to compile the network. Should occur when any value, preference, or font changes
|
// Sends a request to compile the network. Should occur when any value, preference, or font changes
|
||||||
CompileActiveDocument,
|
CompileActiveDocument,
|
||||||
// Sends a request to evaluate the network. Should occur when any context value changes.2
|
// Sends a request to evaluate the network. Should occur when any context value changes.
|
||||||
EvaluateActiveDocument,
|
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
|
// 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
|
// TODO: Add document ID in response for stability
|
||||||
ProcessCompilationResponse {
|
ProcessCompilationResponse {
|
||||||
@@ -36,12 +47,13 @@ pub enum PortfolioMessage {
|
|||||||
},
|
},
|
||||||
ProcessEvaluationResponse {
|
ProcessEvaluationResponse {
|
||||||
evaluation_metadata: RenderMetadata,
|
evaluation_metadata: RenderMetadata,
|
||||||
|
},
|
||||||
|
ProcessIntrospectionResponse {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
introspected_inputs: Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
|
introspected_inputs: IntrospectionResponse,
|
||||||
},
|
|
||||||
ProcessThumbnails {
|
|
||||||
inputs_to_render: HashSet<CompiledProtonodeInput>,
|
|
||||||
},
|
},
|
||||||
|
RenderThumbnails,
|
||||||
|
ProcessThumbnails,
|
||||||
DocumentPassMessage {
|
DocumentPassMessage {
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
message: DocumentMessage,
|
message: DocumentMessage,
|
||||||
@@ -134,13 +146,6 @@ pub enum PortfolioMessage {
|
|||||||
SelectDocument {
|
SelectDocument {
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
},
|
},
|
||||||
SubmitDocumentExport {
|
|
||||||
file_name: String,
|
|
||||||
file_type: FileType,
|
|
||||||
scale_factor: f64,
|
|
||||||
bounds: ExportBounds,
|
|
||||||
transparent_background: bool,
|
|
||||||
},
|
|
||||||
ToggleRulers,
|
ToggleRulers,
|
||||||
UpdateDocumentWidgets,
|
UpdateDocumentWidgets,
|
||||||
UpdateOpenDocumentsList,
|
UpdateOpenDocumentsList,
|
||||||
|
|||||||
@@ -6,24 +6,29 @@ use crate::application::generate_uuid;
|
|||||||
use crate::consts::{DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX};
|
use crate::consts::{DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX};
|
||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||||
use crate::messages::dialog::simple_dialogs;
|
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::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::DocumentMessageContext;
|
use crate::messages::portfolio::document::DocumentMessageContext;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::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::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
||||||
use crate::messages::portfolio::document_migration::*;
|
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::preferences::SelectionMode;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
|
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 glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::value::EditorMetadata;
|
||||||
use graphene_std::renderer::Quad;
|
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 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)]
|
#[derive(ExtractField)]
|
||||||
pub struct PortfolioMessageContext<'a> {
|
pub struct PortfolioMessageContext<'a> {
|
||||||
@@ -54,9 +59,10 @@ pub struct PortfolioMessageHandler {
|
|||||||
pub reset_node_definitions_on_open: bool,
|
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 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
|
// Data from old nodes get deleted after a compilation
|
||||||
pub introspected_input_data: HashMap<CompiledProtonodeInput, Box<dyn std::any::Any + Send + Sync>>,
|
// Always take data after requesting it
|
||||||
pub downcasted_input_data: HashMap<CompiledProtonodeInput, TaggedValue>,
|
pub introspected_data: HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
|
||||||
pub context_data: HashMap<CompiledProtonodeInput, Context>,
|
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]
|
#[message_handler_data]
|
||||||
@@ -105,13 +111,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
self.menu_bar_message_handler.process_message(message, responses, ());
|
self.menu_bar_message_handler.process_message(message, responses, ());
|
||||||
}
|
}
|
||||||
PortfolioMessage::Spreadsheet(message) => {
|
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) => {
|
PortfolioMessage::Document(message) => {
|
||||||
if let Some(document_id) = self.active_document_id {
|
if let Some(document_id) = self.active_document_id {
|
||||||
if let Some(document) = self.documents.get_mut(&document_id) {
|
if let Some(document) = self.documents.get_mut(&document_id) {
|
||||||
let document_inputs = DocumentMessageContext {
|
let document_inputs = DocumentMessageData {
|
||||||
document_id,
|
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data: &self.persistent_data,
|
persistent_data: &self.persistent_data,
|
||||||
current_tool,
|
current_tool,
|
||||||
@@ -143,8 +154,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
PortfolioMessage::DocumentPassMessage { document_id, message } => {
|
PortfolioMessage::DocumentPassMessage { document_id, message } => {
|
||||||
if let Some(document) = self.documents.get_mut(&document_id) {
|
if let Some(document) = self.documents.get_mut(&document_id) {
|
||||||
let document_inputs = DocumentMessageContext {
|
let document_inputs = DocumentMessageData {
|
||||||
document_id,
|
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data: &self.persistent_data,
|
persistent_data: &self.persistent_data,
|
||||||
current_tool,
|
current_tool,
|
||||||
@@ -356,12 +366,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
PortfolioMessage::NewDocumentWithName { name } => {
|
PortfolioMessage::NewDocumentWithName { name } => {
|
||||||
let mut new_document = DocumentMessageHandler::default();
|
let mut new_document = DocumentMessageHandler::default();
|
||||||
new_document.name = name;
|
new_document.name = name;
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
|
||||||
|
|
||||||
let document_id = DocumentId(generate_uuid());
|
let document_id = DocumentId(generate_uuid());
|
||||||
if self.active_document().is_some() {
|
if self.active_document().is_some() {
|
||||||
responses.add(BroadcastEvent::ToolAbort);
|
responses.add(BroadcastEvent::ToolAbort);
|
||||||
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.load_document(new_document, document_id, responses, false);
|
self.load_document(new_document, document_id, responses, false);
|
||||||
@@ -664,13 +672,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
|
|
||||||
if create_document {
|
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
|
// 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 });
|
responses.add(DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true });
|
||||||
|
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||||
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
|
responses.add(Message::EndEvaluationQueue);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::PasteSvg {
|
PortfolioMessage::PasteSvg {
|
||||||
@@ -696,13 +701,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
|
|
||||||
if create_document {
|
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
|
// 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 });
|
responses.add(DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true });
|
||||||
|
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||||
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
|
responses.add(Message::EndEvaluationQueue);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::PrevDocument => {
|
PortfolioMessage::PrevDocument => {
|
||||||
@@ -747,7 +749,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
responses.add(BroadcastEvent::ToolAbort);
|
responses.add(BroadcastEvent::ToolAbort);
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
||||||
if node_graph_open {
|
if node_graph_open {
|
||||||
@@ -768,8 +769,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::CompileActiveDocument => {
|
PortfolioMessage::CompileActiveDocument => {
|
||||||
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: {:?}", document_id);
|
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if document.network_interface.hash_changed() {
|
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 } => {
|
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);
|
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
for (AbsoluteInputConnector { network_path, connector }, caller) in compilation_metadata.protonode_callers_for_value {
|
for (value_connectors, caller) in compilation_metadata.protonode_caller_for_values {
|
||||||
document.network_interface.set_input_caller(connector, caller, &network_path)
|
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 {
|
for (protonode_paths, caller) in compilation_metadata.protonode_caller_for_nodes {
|
||||||
let (node_id, network_path) = protonode_path.to_vec().split_last().expect("Protonode path cannot be empty");
|
for protonode_path in protonode_paths {
|
||||||
document.network_interface.set_node_caller(node_id, caller, &network_path)
|
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 {
|
for (sni, input_types) in compilation_metadata.types_to_add {
|
||||||
document.network_interface.add_type(sni, input_types);
|
document.network_interface.add_type(sni, input_types);
|
||||||
}
|
}
|
||||||
for ((sni, number_of_inputs)) in compilation_metadata.types_to_remove {
|
let mut cleared_thumbnails = Vec::new();
|
||||||
// Removed saves type of the document node
|
for (sni, number_of_inputs) in compilation_metadata.types_to_remove {
|
||||||
|
// Removed saved type of the document node
|
||||||
document.network_interface.remove_type(sni);
|
document.network_interface.remove_type(sni);
|
||||||
// Remove introspection data for all monitor nodes and the thumbnails
|
// Remove all thumbnails
|
||||||
let mut cleared_thumbnails = Vec::new();
|
for input_index in 0..number_of_inputs {
|
||||||
for monitor_index in 0..number_of_inputs {
|
cleared_thumbnails.push(NodeId(sni.0 + input_index as u64 + 1));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
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 => {
|
PortfolioMessage::EvaluateActiveDocument => {
|
||||||
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
|
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
|
||||||
@@ -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
|
// 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
|
// Save vector data for all path/transform nodes in the document network
|
||||||
match document.network_interface.input_from_connector(&InputConnector::Export(0), &[]) {
|
// match document.network_interface.input_from_connector(&InputConnector::Export(0), &[]) {
|
||||||
Some(NodeInput::Node { node_id, .. }) => {
|
// Some(NodeInput::Node { node_id, .. }) => {
|
||||||
for upstream_node in document.network_interface.upstream_flow_back_from_nodes(vec![*node_id], &[], network_interface::FlowType::UpstreamFlow) {
|
// 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();
|
// let reference = document.network_interface.reference(&upstream_node, &[]).and_then(|reference| reference.as_deref());
|
||||||
if reference == "Path" || reference == "Transform" {
|
// if reference == Some("Path") || reference == Some("Transform") {
|
||||||
let input_connector = InputConnector::Node { node_id, input_index: 0 };
|
// 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{
|
// 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);
|
// log::error!("could not get downstream caller for node : {:?}", node_id);
|
||||||
continue;
|
// continue;
|
||||||
};
|
// };
|
||||||
inputs_to_monitor.push(*downstream_caller)
|
// inputs_to_monitor.insert((*downstream_caller, IntrospectMode::Data));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
// }
|
||||||
_ => {},
|
// _ => {}
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Introspect data for the currently selected node (eventually thumbnail) if the spreadsheet view is open
|
// Introspect data for the currently selected node (eventually thumbnail) if the spreadsheet view is open
|
||||||
if self.spreadsheet.spreadsheet_view_open {
|
// if self.spreadsheet.spreadsheet_view_open {
|
||||||
let selected_network_path = &document.selection_network_path;
|
// let selected_network_path = &document.selection_network_path;
|
||||||
// TODO: Replace with selected thumbnail
|
// // 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 let Some(selected_node) = document
|
||||||
if selected_nodes.0.len() == 1 {
|
// .network_interface
|
||||||
selected_nodes.0.first().copied()
|
// .selected_nodes_in_nested_network(selected_network_path)
|
||||||
} else {
|
// .and_then(|selected_nodes| if selected_nodes.0.len() == 1 { selected_nodes.0.first().copied() } else { None })
|
||||||
None
|
// {
|
||||||
}
|
// // TODO: Introspect any input rather than just the first input of the selected node
|
||||||
}) {
|
// let selected_connector = InputConnector::Node {
|
||||||
// TODO: Introspect any input rather than just the first input of the selected node
|
// node_id: selected_node,
|
||||||
let selected_connector = InputConnector::Node { node_id: selected_node, input_index: 0 };
|
// input_index: 0,
|
||||||
let Some(caller) = document
|
// };
|
||||||
.network_interface
|
// match document.network_interface.downstream_caller_from_input(&selected_connector, selected_network_path) {
|
||||||
.downstream_caller_from_input(&selected_connector, selected_network_path) else {
|
// Some(caller) => {
|
||||||
log::error!("Could not get downstream caller for {:?}", selected_node);
|
// inputs_to_monitor.insert((*caller, IntrospectMode::Data));
|
||||||
};
|
// inspect_input = Some(InspectInputConnector {
|
||||||
inputs_to_monitor.push((*caller, IntrospectMode::Data));
|
// input_connector: AbsoluteInputConnector {
|
||||||
inspect_input = Some(InspectInputConnector { input_connector: AbsoluteInputConnector { network_path: selected_network_path.clone(), connector: selected_connector }, protonode_input: *caller });
|
// network_path: selected_network_path.clone(),
|
||||||
}
|
// connector: selected_connector,
|
||||||
}
|
// },
|
||||||
|
// protonode_input: *caller,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// None => log::error!("Could not get downstream caller for {:?}", selected_connector),
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
// let animation_time = match animation.timing_information().animation_time {
|
// let animation_time = match animation.timing_information().animation_time {
|
||||||
// AnimationState::Stopped => 0.,
|
// AnimationState::Stopped => 0.,
|
||||||
@@ -929,67 +896,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
let mut context = EditorContext::default();
|
let mut context = EditorContext::default();
|
||||||
// context.footprint = Some(Footprint {
|
context.footprint = Some(Footprint {
|
||||||
// transform: document.metadata().document_to_viewport,
|
transform: document.metadata().document_to_viewport,
|
||||||
// resolution: ipp.viewport_bounds.size().as_uvec2(),
|
resolution: ipp.viewport_bounds.size().as_uvec2(),
|
||||||
// quality: RenderQuality::Full,
|
quality: RenderQuality::Full,
|
||||||
// });
|
});
|
||||||
// context.animation_time = Some(animation_time);
|
context.animation_time = Some(timing_information.animation_time.as_secs_f64());
|
||||||
// context.real_time = Some(ipp.time);
|
context.real_time = Some(ipp.time);
|
||||||
// context.downstream_transform = Some(DAffine2::IDENTITY);
|
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.render_config = render_config;
|
self.executor.submit_node_graph_evaluation(context, None, None);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
PortfolioMessage::ProcessEvaluationResponse { evaluation_metadata, introspected_inputs } => {
|
PortfolioMessage::ProcessEvaluationResponse { evaluation_metadata } => {
|
||||||
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);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let RenderMetadata {
|
let RenderMetadata {
|
||||||
upstream_footprints: footprints,
|
upstream_footprints: footprints,
|
||||||
local_transforms,
|
local_transforms,
|
||||||
@@ -1009,25 +927,117 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
// AnimationState::Playing { .. } => responses.add(PortfolioMessage::EvaluateActiveDocument),
|
// AnimationState::Playing { .. } => responses.add(PortfolioMessage::EvaluateActiveDocument),
|
||||||
// _ => {}
|
// _ => {}
|
||||||
// };
|
// };
|
||||||
},
|
|
||||||
PortfolioMessage::ProcessThumbnails { inputs_to_render } => {
|
// After an evaluation, always render all thumbnails
|
||||||
let mut thumbnail_response = ThumbnailRenderResponse::default();
|
responses.add(PortfolioMessage::RenderThumbnails);
|
||||||
for thumbnail_input in inputs_to_render {
|
}
|
||||||
let monitor_node_id = thumbnail_input.0.0 + thumbnail_input.1 as u64 + 1;
|
PortfolioMessage::IntrospectActiveDocument { inputs_to_introspect } => {
|
||||||
match self.try_render_thumbnail(&thumbnail_input) {
|
self.executor.submit_node_graph_introspection(inputs_to_introspect);
|
||||||
ThumbnailRenderResult::NoChange => {}
|
}
|
||||||
ThumbnailRenderResult::ClearThumbnail => thumbnail_response.clear.push(NodeId(monitor_node_id)),
|
PortfolioMessage::ProcessIntrospectionResponse { introspected_inputs } => {
|
||||||
ThumbnailRenderResult::UpdateThumbnail(thumbnail) => {
|
for (input, mode, data) in introspected_inputs.0.into_iter() {
|
||||||
thumbnail_response.add.push((NodeId(monitor_node_id), thumbnail));
|
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::ClearIntrospectedData => {
|
||||||
PortfolioMessage::ActiveDocumentExport {
|
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_name,
|
||||||
file_type,
|
file_type,
|
||||||
animation_export_data,
|
|
||||||
scale_factor,
|
scale_factor,
|
||||||
bounds,
|
bounds,
|
||||||
transparent_background,
|
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");
|
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
|
// Update the scope inputs with the render settings
|
||||||
// self.executor.submit_node_graph_compilation(CompilationRequest {
|
self.executor.submit_node_graph_compilation(CompilationRequest {
|
||||||
// network: document.network_interface.document_network().clone(),
|
network: document.network_interface.document_network().clone(),
|
||||||
// font_cache: self.persistent_data.font_cache.clone(),
|
font_cache: self.persistent_data.font_cache.clone(),
|
||||||
// editor_metadata: EditorMetadata {
|
editor_metadata: EditorMetadata {
|
||||||
// #[cfg(any(feature = "resvg", feature = "vello"))]
|
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||||
// use_vello: preferences.use_vello(),
|
use_vello: preferences.use_vello(),
|
||||||
// #[cfg(not(any(feature = "resvg", feature = "vello")))]
|
#[cfg(not(any(feature = "resvg", feature = "vello")))]
|
||||||
// use_vello: false,
|
use_vello: false,
|
||||||
// hide_artboards: transparent_background,
|
hide_artboards: transparent_background,
|
||||||
// for_export: true,
|
for_export: true,
|
||||||
// view_mode: document.view_mode,
|
view_mode: document.view_mode,
|
||||||
// transform_to_viewport: true,
|
transform_to_viewport: true,
|
||||||
// },
|
},
|
||||||
// });
|
});
|
||||||
|
|
||||||
let document_to_viewport = document.metadata().document_to_viewport;
|
|
||||||
// Calculate the bounding box of the region to be exported
|
// 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::AllArtwork => document.network_interface.document_bounds_document_space(!transparent_background),
|
||||||
ExportBounds::Selection => document.network_interface.selected_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::Artboard(id) => document.metadata().bounding_box_document(id),
|
||||||
// ExportBounds::Viewport => ipp.document_bounds(document_to_viewport),
|
// ExportBounds::Viewport => ipp.document_bounds(document.metadata().document_to_viewport),
|
||||||
}
|
}) else {
|
||||||
.ok_or_else(|| "No bounding box".to_string())?;
|
log::error!("No bounding box when exporting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let size = document_bounds[1] - document_bounds[0];
|
let size = document_bounds[1] - document_bounds[0];
|
||||||
let scaled_size = size * scale_factor;
|
let scaled_size = size * scale_factor;
|
||||||
let transform = DAffine2::from_translation(document_bounds[0]).inverse();
|
let transform = DAffine2::from_translation(document_bounds[0]).inverse();
|
||||||
|
|
||||||
let mut context = EditorContext::default();
|
let mut context = EditorContext::default();
|
||||||
// context.footprint = Footprint {
|
context.footprint = Some(Footprint {
|
||||||
// document_to_viewport: DAffine2::from_scale(DVec2::splat(scale_factor)) * transform,
|
transform: DAffine2::from_scale(DVec2::splat(scale_factor)) * transform,
|
||||||
// resolution: scaled_size.as_uvec2(),
|
resolution: scaled_size.as_uvec2(),
|
||||||
// ..Default::default()
|
..Default::default()
|
||||||
// };
|
});
|
||||||
// context.real_time = Some(ipp.time);
|
context.real_time = Some(ipp.time);
|
||||||
// context.downstream_transform = Some(DAffine2::IDENTITY);
|
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
|
// Special handling for exporting the artwork
|
||||||
let file_suffix = &format!(".{file_type:?}").to_lowercase();
|
let file_suffix = &format!(".{file_type:?}").to_lowercase();
|
||||||
let file_name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
|
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,
|
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(
|
self.executor.submit_node_graph_evaluation(
|
||||||
context,
|
context,
|
||||||
Vec::new(),
|
None,
|
||||||
None,
|
Some(ExportConfig {
|
||||||
Some(ExportConfig {
|
file_name,
|
||||||
file_name,
|
file_type,
|
||||||
file_type,
|
scale_factor,
|
||||||
scale_factor,
|
bounds,
|
||||||
bounds,
|
transparent_background,
|
||||||
transparent_background,
|
size: scaled_size,
|
||||||
size: scaled_size,
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// if let Some((start, end, fps)) = animation_export_data {
|
// if let Some((start, end, fps)) = animation_export_data {
|
||||||
// let total_frames = ((start - end) * fps) as u32;
|
// 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
|
// Reset the scope nodes for hide artboards/hide_artboard name
|
||||||
// self.executor.submit_node_graph_compilation(CompilationRequest {
|
self.executor.submit_node_graph_compilation(CompilationRequest {
|
||||||
// network: document.network_interface.document_network().clone(),
|
network: document.network_interface.document_network().clone(),
|
||||||
// font_cache: self.persistent_data.font_cache.clone(),
|
font_cache: self.persistent_data.font_cache.clone(),
|
||||||
// editor_metadata: EditorMetadata {
|
editor_metadata: EditorMetadata {
|
||||||
// #[cfg(any(feature = "resvg", feature = "vello"))]
|
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||||
// use_vello: preferences.use_vello().use_vello,
|
use_vello: preferences.use_vello(),
|
||||||
// #[cfg(not(any(feature = "resvg", feature = "vello")))]
|
#[cfg(not(any(feature = "resvg", feature = "vello")))]
|
||||||
// use_vello: false,
|
use_vello: false,
|
||||||
// hide_artboards: false,
|
hide_artboards: false,
|
||||||
// for_export: false,
|
for_export: false,
|
||||||
// view_mode: document.view_mode,
|
view_mode: document.view_mode,
|
||||||
// transform_to_viewport: true,
|
transform_to_viewport: true,
|
||||||
// },
|
},
|
||||||
// });
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PortfolioMessage::ToggleRulers => {
|
PortfolioMessage::ToggleRulers => {
|
||||||
@@ -1181,7 +1169,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
PortfolioMessage::UpdateDocumentWidgets => {
|
PortfolioMessage::UpdateDocumentWidgets => {
|
||||||
if let Some(document) = self.active_document() {
|
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 => {
|
PortfolioMessage::UpdateOpenDocumentsList => {
|
||||||
@@ -1333,57 +1321,11 @@ impl PortfolioMessageHandler {
|
|||||||
/text>"#
|
/text>"#
|
||||||
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
||||||
.to_string();
|
.to_string();
|
||||||
responses.add(Message::ProcessQueue((graphene_std::renderer::EvaluationMetadata::default(), Vec::new())));
|
responses.add(Message::ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata::default()));
|
||||||
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
||||||
}
|
}
|
||||||
result
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@@ -1391,10 +1333,3 @@ pub struct ThumbnailRenderResponse {
|
|||||||
add: Vec<(SNI, String)>,
|
add: Vec<(SNI, String)>,
|
||||||
clear: Vec<SNI>,
|
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 graph_craft::document::AbsoluteInputConnector;
|
||||||
use graphene_std::uuid::CompiledProtonodeInput;
|
use graphene_std::uuid::CompiledProtonodeInput;
|
||||||
|
|
||||||
@@ -7,7 +8,7 @@ use graphene_std::uuid::CompiledProtonodeInput;
|
|||||||
pub enum SpreadsheetMessage {
|
pub enum SpreadsheetMessage {
|
||||||
ToggleOpen,
|
ToggleOpen,
|
||||||
|
|
||||||
UpdateLayout { inpect_input: InspectInputConnector },
|
UpdateLayout { inspect_input: InspectInputConnector },
|
||||||
|
|
||||||
PushToInstancePath { index: usize },
|
PushToInstancePath { index: usize },
|
||||||
TruncateInstancePath { len: usize },
|
TruncateInstancePath { len: usize },
|
||||||
@@ -15,7 +16,7 @@ pub enum SpreadsheetMessage {
|
|||||||
ViewVectorDataDomain { domain: VectorDataDomain },
|
ViewVectorDataDomain { domain: VectorDataDomain },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum VectorDataDomain {
|
pub enum VectorDataDomain {
|
||||||
#[default]
|
#[default]
|
||||||
Points,
|
Points,
|
||||||
|
|||||||
@@ -3,21 +3,17 @@ use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup,
|
|||||||
use crate::messages::portfolio::spreadsheet::InspectInputConnector;
|
use crate::messages::portfolio::spreadsheet::InspectInputConnector;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use graph_craft::document::{AbsoluteInputConnector, NodeId};
|
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::Context;
|
|
||||||
use graphene_std::GraphicGroupTable;
|
use graphene_std::GraphicGroupTable;
|
||||||
use graphene_std::instances::Instances;
|
use graphene_std::instances::Instances;
|
||||||
use graphene_std::memo::IORecord;
|
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::uuid::CompiledProtonodeInput;
|
use graphene_std::uuid::CompiledProtonodeInput;
|
||||||
use graphene_std::vector::{VectorData, VectorDataTable};
|
use graphene_std::vector::{VectorData, VectorDataTable};
|
||||||
use graphene_std::{Artboard, ArtboardGroupTable, GraphicElement};
|
use graphene_std::{Artboard, ArtboardGroupTable, GraphicElement};
|
||||||
use std::any::Any;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct SpreadsheetMessageHandlerData {
|
pub struct SpreadsheetMessageHandlerData<'a> {
|
||||||
pub introspected_data: &HashMap<CompiledProtonodeInput, Box<dyn std::any::Any + Send + Sync>>;
|
pub introspected_data: &'a HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The spreadsheet UI allows for instance data to be previewed.
|
/// The spreadsheet UI allows for instance data to be previewed.
|
||||||
@@ -35,7 +31,7 @@ pub struct SpreadsheetMessageHandler {
|
|||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for SpreadsheetMessageHandler {
|
impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for SpreadsheetMessageHandler {
|
||||||
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, data: SpreadsheetMessageHandlerData) {
|
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, data: SpreadsheetMessageHandlerData) {
|
||||||
let {introspected_data} = data;
|
let SpreadsheetMessageHandlerData { introspected_data } = data;
|
||||||
match message {
|
match message {
|
||||||
SpreadsheetMessage::ToggleOpen => {
|
SpreadsheetMessage::ToggleOpen => {
|
||||||
self.spreadsheet_view_open = !self.spreadsheet_view_open;
|
self.spreadsheet_view_open = !self.spreadsheet_view_open;
|
||||||
@@ -48,12 +44,12 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for Sprea
|
|||||||
}
|
}
|
||||||
// Update checked UI state for open
|
// Update checked UI state for open
|
||||||
responses.add(MenuBarMessage::SendLayout);
|
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
|
// Queued on introspection request, runs on introspection response when the data has been sent back to the editor
|
||||||
SpreadsheetMessage::UpdateLayout { inpect_input } => {
|
SpreadsheetMessage::UpdateLayout { inspect_input } => {
|
||||||
self.inspect_input = Some(inpect_input);
|
self.inspect_input = Some(inspect_input);
|
||||||
self.update_layout(introspected_data, responses);
|
self.update_layout(introspected_data, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +75,7 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for Sprea
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SpreadsheetMessageHandler {
|
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 {
|
responses.add(FrontendMessage::UpdateSpreadsheetState {
|
||||||
// The node is sent when the data is available
|
// The node is sent when the data is available
|
||||||
node: None,
|
node: None,
|
||||||
@@ -94,18 +90,20 @@ impl SpreadsheetMessageHandler {
|
|||||||
breadcrumbs: Vec::new(),
|
breadcrumbs: Vec::new(),
|
||||||
vector_data_domain: self.viewing_vector_data_domain,
|
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) => {
|
Some(inspect_input) => {
|
||||||
match introspected_data.get(&inspect_input.protonode_input){
|
match introspected_data.get(&inspect_input.protonode_input) {
|
||||||
Some(data) => {
|
Some(data) => match data {
|
||||||
match generate_layout(instrospected_data, &mut layout_data) {
|
Some(instrospected_data) => match generate_layout(instrospected_data, &mut layout_data) {
|
||||||
Some(layout) => layout,
|
Some(layout) => layout,
|
||||||
None => label("The introspected data is not a supported type to be displayed."),
|
None => label("The introspected data is not a supported type to be displayed."),
|
||||||
}
|
},
|
||||||
|
None => label("Introspected data is not available for this input. This input may be cached."),
|
||||||
},
|
},
|
||||||
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."),
|
None => label("No input selected to show data for."),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,7 +128,7 @@ struct LayoutData<'a> {
|
|||||||
vector_data_domain: VectorDataDomain,
|
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.
|
// We simply try random types. TODO: better strategy.
|
||||||
#[allow(clippy::manual_map)]
|
#[allow(clippy::manual_map)]
|
||||||
if let Some(io) = introspected_data.downcast_ref::<ArtboardGroupTable>() {
|
if let Some(io) = introspected_data.downcast_ref::<ArtboardGroupTable>() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use graphene_std::text::FontCache;
|
|||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PersistentData {
|
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)]
|
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ impl PreferencesMessageHandler {
|
|||||||
self.selection_mode
|
self.selection_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn editor_preferences(&self) -> EditorPreferences {
|
pub fn use_vello(&self) -> bool {
|
||||||
EditorPreferences {
|
self.use_vello && self.supports_wgpu()
|
||||||
use_vello: self.use_vello && self.supports_wgpu(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supports_wgpu(&self) -> bool {
|
pub fn supports_wgpu(&self) -> bool {
|
||||||
|
|||||||
+2
-3
@@ -3,16 +3,15 @@ use crate::messages::frontend::utility_types::MouseCursorIcon;
|
|||||||
use crate::messages::message::Message;
|
use crate::messages::message::Message;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
use crate::messages::prelude::{PortfolioMessage, Responses};
|
||||||
use crate::messages::prelude::Responses;
|
|
||||||
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage};
|
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage};
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
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_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 crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::NodeInput;
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::f64::consts::TAU;
|
use std::f64::consts::TAU;
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -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::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::message::Message;
|
use crate::messages::message::Message;
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector};
|
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext};
|
||||||
use crate::messages::prelude::FrontendMessage;
|
use crate::messages::prelude::{FrontendMessage, PortfolioMessage};
|
||||||
use crate::messages::prelude::Responses;
|
use crate::messages::prelude::Responses;
|
||||||
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
|
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
|
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::{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 crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_4, PI, SQRT_2};
|
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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions;
|
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::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 crate::messages::prelude::*;
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{NodeId, NodeInput};
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::{ProtoNodeIdentifier, concrete};
|
use graph_craft::{ProtoNodeIdentifier, concrete};
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::NodeInputDecleration;
|
use graphene_std::NodeInputDecleration;
|
||||||
@@ -154,8 +154,9 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
|
|||||||
});
|
});
|
||||||
|
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
responses.add(Message::StartQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(PenToolMessage::RecalculateLatestPointsPosition);
|
responses.add(PenToolMessage::RecalculateLatestPointsPosition);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge the `first_endpoint` with `second_endpoint`.
|
/// 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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
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::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::common_functionality::graph_modification_utils;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use std::collections::VecDeque;
|
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::node_graph::document_node_definitions::resolve_document_node_type;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::common_functionality::graph_modification_utils;
|
||||||
pub use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
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::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapTypeConfiguration};
|
||||||
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
|
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use std::collections::VecDeque;
|
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::node_graph::document_node_definitions::resolve_document_node_type;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::NumberOfPointsDial;
|
||||||
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDialState;
|
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;
|
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::common_functionality::shapes::shape_utility::polygon_outline;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
|
use graph_craft::document::InputConnector;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::NodeInput;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use std::collections::VecDeque;
|
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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
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::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::common_functionality::graph_modification_utils;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use super::ShapeToolData;
|
|||||||
use crate::messages::message::Message;
|
use crate::messages::message::Message;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
|
||||||
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage, Responses};
|
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage, Responses};
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
||||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
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 crate::messages::tool::utility_types::*;
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::vector::click_target::ClickTargetType;
|
use graphene_std::vector::click_target::ClickTargetType;
|
||||||
use graphene_std::vector::misc::dvec2_to_point;
|
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::node_graph::document_node_definitions::resolve_document_node_type;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::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::gizmos::shape_gizmos::point_radius_handle::{PointRadiusHandle, PointRadiusHandleState};
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
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 crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use core::f64;
|
use core::f64;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use std::collections::VecDeque;
|
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::SnapData;
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
#[derive(Default, ExtractField)]
|
#[derive(Default, ExtractField)]
|
||||||
pub struct ArtboardTool {
|
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::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
|
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
|
||||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::brush::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
|
use graphene_std::brush::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
|
||||||
use graphene_std::raster::BlendMode;
|
use graphene_std::raster::BlendMode;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
const BRUSH_MAX_SIZE: f64 = 5000.;
|
const BRUSH_MAX_SIZE: f64 = 5000.;
|
||||||
|
|
||||||
@@ -379,8 +379,9 @@ impl Fsm for BrushToolFsmState {
|
|||||||
else {
|
else {
|
||||||
new_brush_layer(document, responses);
|
new_brush_layer(document, responses);
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
responses.add(PortfolioMessage::CompileActiveDocument);
|
||||||
responses.add(Message::StartQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(BrushToolMessage::DragStart);
|
responses.add(BrushToolMessage::DragStart);
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
BrushToolFsmState::Ready
|
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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::utility_functions::should_extend;
|
use crate::messages::tool::common_functionality::utility_functions::should_extend;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graph_craft::document::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::vector::VectorModificationType;
|
use graphene_std::vector::VectorModificationType;
|
||||||
use graphene_std::vector::{PointId, SegmentId};
|
use graphene_std::vector::{PointId, SegmentId};
|
||||||
@@ -251,7 +251,6 @@ impl Fsm for FreehandToolFsmState {
|
|||||||
let nodes = vec![(NodeId(0), node)];
|
let nodes = vec![(NodeId(0), node)];
|
||||||
|
|
||||||
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses);
|
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.fill.apply_fill(layer, responses);
|
||||||
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
||||||
tool_data.layer = Some(layer);
|
tool_data.layer = Some(layer);
|
||||||
|
|||||||
@@ -643,12 +643,12 @@ impl PathToolData {
|
|||||||
|
|
||||||
self.drag_start_pos = input.mouse.position;
|
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.saved_points_before_anchor_convert_smooth_sharp.clear();
|
||||||
self.stored_selection = None;
|
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<_>>();
|
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::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
|
||||||
use crate::messages::tool::common_functionality::utility_functions::{calculate_segment_angle, closest_point, should_extend};
|
use crate::messages::tool::common_functionality::utility_functions::{calculate_segment_angle, closest_point, should_extend};
|
||||||
use bezier_rs::{Bezier, BezierHandles};
|
use bezier_rs::{Bezier, BezierHandles};
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, StrokeId, VectorData};
|
use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, StrokeId, VectorData};
|
||||||
use graphene_std::vector::{PointId, VectorModificationType};
|
use graphene_std::vector::{PointId, VectorModificationType};
|
||||||
|
|
||||||
@@ -1258,9 +1258,10 @@ impl PenToolData {
|
|||||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
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
|
// 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)
|
// 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(PenToolMessage::AddPointLayerPosition { layer, viewport });
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform extension of an existing path
|
/// 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 crate::messages::tool::common_functionality::utility_functions::{resize_bounds, rotate_bounds, skew_bounds, text_bounding_box, transforming_transform_cage};
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::DMat2;
|
use glam::DMat2;
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graphene_std::path_bool::BooleanOperation;
|
use graphene_std::path_bool::BooleanOperation;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::renderer::Rect;
|
use graphene_std::renderer::Rect;
|
||||||
use graphene_std::transform::ReferencePoint;
|
use graphene_std::transform::ReferencePoint;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Default, ExtractField)]
|
#[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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::auto_panning::AutoPanning;
|
||||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||||
use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoManager;
|
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::transformation_cage::{BoundingBoxManager, EdgeBool};
|
||||||
use crate::messages::tool::common_functionality::utility_functions::{closest_point, resize_bounds, rotate_bounds, skew_bounds, transforming_transform_cage};
|
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::value::TaggedValue;
|
||||||
use graph_craft::document::{NodeId, NodeInput};
|
use graph_craft::document::{InputConnector, NodeInput};
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::misc::ArcType;
|
use graphene_std::vector::misc::ArcType;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
@@ -599,17 +599,16 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
let nodes = vec![(NodeId(0), node)];
|
let nodes = vec![(NodeId(0), node)];
|
||||||
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, document.new_layer_bounding_artboard(input), responses);
|
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 {
|
match tool_data.current_shape {
|
||||||
ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star => {
|
ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star => {
|
||||||
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(GraphOperationMessage::TransformSet {
|
responses.add(GraphOperationMessage::TransformSet {
|
||||||
layer,
|
layer,
|
||||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||||
transform_in: TransformIn::Viewport,
|
transform_in: TransformIn::Viewport,
|
||||||
skip_rerender: false,
|
skip_rerender: false,
|
||||||
});
|
});
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
tool_options.fill.apply_fill(layer, responses);
|
tool_options.fill.apply_fill(layer, responses);
|
||||||
}
|
}
|
||||||
ShapeType::Line => {
|
ShapeType::Line => {
|
||||||
@@ -617,6 +616,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
tool_data.line_data.editing_layer = Some(layer);
|
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);
|
||||||
|
|
||||||
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::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::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint};
|
||||||
use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend};
|
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::Color;
|
||||||
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
|
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_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
||||||
tool_data.current_layer = Some(layer);
|
tool_data.current_layer = Some(layer);
|
||||||
|
|
||||||
responses.add(Message::StartQueue);
|
|
||||||
|
|
||||||
SplineToolFsmState::Drawing
|
SplineToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
(SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => {
|
(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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::auto_panning::AutoPanning;
|
||||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
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};
|
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::transformation_cage::*;
|
||||||
use crate::messages::tool::common_functionality::utility_functions::text_bounding_box;
|
use crate::messages::tool::common_functionality::utility_functions::text_bounding_box;
|
||||||
use graph_craft::document::value::TaggedValue;
|
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::Color;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::text::{Font, FontCache, TypesettingConfig, lines_clipping, load_font};
|
use graphene_std::text::{Font, FontCache, TypesettingConfig, lines_clipping, load_font};
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::style::Fill;
|
use graphene_std::vector::style::Fill;
|
||||||
|
|
||||||
#[derive(Default, ExtractField)]
|
#[derive(Default, ExtractField)]
|
||||||
@@ -381,7 +381,7 @@ impl TextToolData {
|
|||||||
parent: document.new_layer_parent(true),
|
parent: document.new_layer_parent(true),
|
||||||
insert_index: 0,
|
insert_index: 0,
|
||||||
});
|
});
|
||||||
responses.add(Message::StartQueue);
|
responses.add(Message::StartEvaluationQueue);
|
||||||
responses.add(GraphOperationMessage::FillSet {
|
responses.add(GraphOperationMessage::FillSet {
|
||||||
layer: self.layer,
|
layer: self.layer,
|
||||||
fill: if editing_text.color.is_some() {
|
fill: if editing_text.color.is_some() {
|
||||||
@@ -394,15 +394,14 @@ impl TextToolData {
|
|||||||
layer: self.layer,
|
layer: self.layer,
|
||||||
transform: editing_text.transform,
|
transform: editing_text.transform,
|
||||||
transform_in: TransformIn::Viewport,
|
transform_in: TransformIn::Viewport,
|
||||||
skip_rerender: true,
|
skip_rerender: false,
|
||||||
});
|
});
|
||||||
self.editing_text = Some(editing_text);
|
self.editing_text = Some(editing_text);
|
||||||
|
|
||||||
self.set_editing(true, font_cache, responses);
|
self.set_editing(true, font_cache, responses);
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
|
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
|
||||||
|
responses.add(Message::EndEvaluationQueue);
|
||||||
responses.add(PortfolioMessage::CompileActiveDocument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_click(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) -> Option<LayerNodeIdentifier> {
|
fn check_click(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) -> Option<LayerNodeIdentifier> {
|
||||||
|
|||||||
@@ -1,32 +1,18 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::consts::FILE_SAVE_SUFFIX;
|
use crate::messages::frontend::utility_types::FileType;
|
||||||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::DAffine2;
|
use graph_craft::document::value::{EditorMetadata, RenderOutput, TaggedValue};
|
||||||
use graph_craft::document::value::{NetworkOutput, TaggedValue};
|
use graph_craft::document::{CompilationMetadata, DocumentNode, NodeNetwork, generate_uuid};
|
||||||
use graph_craft::document::{
|
|
||||||
AbsoluteInputConnector, AbsoluteOutputConnector, CompilationMetadata, CompiledNodeMetadata, DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, generate_uuid,
|
|
||||||
};
|
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
use graph_craft::wasm_application_io::{EditorCompilationMetadata, EditorEvaluationMetadata, EditorMetadata};
|
use graphene_std::any::EditorContext;
|
||||||
use graphene_std::application_io::{CompilationMetadata, TimingInformation};
|
|
||||||
use graphene_std::application_io::{EditorEvaluationMetadata, NodeGraphUpdateMessage};
|
|
||||||
use graphene_std::memo::IntrospectMode;
|
use graphene_std::memo::IntrospectMode;
|
||||||
use graphene_std::renderer::{EvaluationMetadata, format_transform_matrix};
|
use graphene_std::renderer::format_transform_matrix;
|
||||||
use graphene_std::renderer::{RenderMetadata, RenderSvgSegmentList};
|
|
||||||
use graphene_std::renderer::{RenderParams, SvgRender};
|
|
||||||
use graphene_std::text::FontCache;
|
use graphene_std::text::FontCache;
|
||||||
use graphene_std::transform::{Footprint, RenderQuality};
|
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
|
||||||
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};
|
|
||||||
|
|
||||||
mod runtime_io;
|
mod runtime_io;
|
||||||
use interpreted_executor::dynamic_executor::{EditorContext, ResolvedDocumentNodeMetadata};
|
|
||||||
pub use runtime_io::NodeRuntimeIO;
|
pub use runtime_io::NodeRuntimeIO;
|
||||||
|
|
||||||
mod runtime;
|
mod runtime;
|
||||||
@@ -35,7 +21,7 @@ pub use runtime::*;
|
|||||||
#[derive(Clone, Debug, Default, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct CompilationRequest {
|
pub struct CompilationRequest {
|
||||||
pub network: NodeNetwork,
|
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 font_cache: Arc<FontCache>,
|
||||||
pub editor_metadata: EditorMetadata,
|
pub editor_metadata: EditorMetadata,
|
||||||
}
|
}
|
||||||
@@ -46,27 +32,34 @@ pub struct CompilationResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Metadata the editor sends when evaluating the network
|
// Metadata the editor sends when evaluating the network
|
||||||
#[derive(Debug, Default, DynAny)]
|
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct EvaluationRequest {
|
pub struct EvaluationRequest {
|
||||||
pub evaluation_id: u64,
|
pub evaluation_id: u64,
|
||||||
pub inputs_to_monitor: Vec<(CompiledProtonodeInput, IntrospectMode)>,
|
#[serde(skip)]
|
||||||
pub context: EditorContext,
|
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))]
|
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct EvaluationResponse {
|
pub struct EvaluationResponse {
|
||||||
evaluation_id: u64,
|
evaluation_id: u64,
|
||||||
result: Result<TaggedValue, String>,
|
result: Result<TaggedValue, String>,
|
||||||
introspected_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))]
|
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub enum NodeGraphUpdate {
|
pub enum NodeGraphUpdate {
|
||||||
CompilationResponse(CompilationResponse),
|
CompilationResponse(CompilationResponse),
|
||||||
EvaluationResponse(EvaluationResponse),
|
EvaluationResponse(EvaluationResponse),
|
||||||
|
IntrospectionResponse(IntrospectionResponse),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@@ -98,6 +91,7 @@ impl NodeGraphExecutor {
|
|||||||
let node_runtime = NodeRuntime::new(request_receiver, response_sender);
|
let node_runtime = NodeRuntime::new(request_receiver, response_sender);
|
||||||
|
|
||||||
let node_executor = Self {
|
let node_executor = Self {
|
||||||
|
busy: false,
|
||||||
futures: HashMap::new(),
|
futures: HashMap::new(),
|
||||||
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
|
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
|
||||||
};
|
};
|
||||||
@@ -118,34 +112,39 @@ impl NodeGraphExecutor {
|
|||||||
|
|
||||||
/// Compile the network
|
/// Compile the network
|
||||||
pub fn submit_node_graph_compilation(&mut self, compilation_request: CompilationRequest) {
|
pub fn submit_node_graph_compilation(&mut self, compilation_request: CompilationRequest) {
|
||||||
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.
|
/// Adds an evaluation request for whatever current network is cached.
|
||||||
pub fn submit_node_graph_evaluation(
|
pub fn submit_node_graph_evaluation(&mut self, context: EditorContext, node_to_evaluate: Option<SNI>, export_config: Option<ExportConfig>) {
|
||||||
&mut self,
|
|
||||||
context: EditorContext,
|
|
||||||
inputs_to_monitor: Vec<(CompiledProtonodeInput, IntrospectMode)>,
|
|
||||||
custom_node_to_evaluate: Option<SNI>,
|
|
||||||
export_config: Option<ExportConfig>,
|
|
||||||
) {
|
|
||||||
let evaluation_id = generate_uuid();
|
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 };
|
let evaluation_context = EvaluationContext { export_config };
|
||||||
self.futures.insert(evaluation_id, evaluation_context);
|
self.futures.insert(evaluation_id, evaluation_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<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)
|
// Continuously poll the executor (called by request animation frame)
|
||||||
pub fn poll_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
pub fn poll_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
||||||
// Moved into portfolio message handler, since this is where the introspected inputs are saved
|
|
||||||
for response in self.runtime_io.receive() {
|
for response in self.runtime_io.receive() {
|
||||||
match response {
|
match response {
|
||||||
NodeGraphUpdate::EvaluationResponse(EvaluationResponse {
|
NodeGraphUpdate::EvaluationResponse(EvaluationResponse { evaluation_id, result }) => {
|
||||||
evaluation_id,
|
|
||||||
result,
|
|
||||||
transform,
|
|
||||||
introspected_inputs,
|
|
||||||
}) => {
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|
||||||
let node_graph_output = match result {
|
let node_graph_output = match result {
|
||||||
@@ -160,18 +159,14 @@ impl NodeGraphExecutor {
|
|||||||
let render_output = match node_graph_output {
|
let render_output = match node_graph_output {
|
||||||
TaggedValue::RenderOutput(render_output) => render_output,
|
TaggedValue::RenderOutput(render_output) => render_output,
|
||||||
value => {
|
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())?;
|
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 {
|
if let Some(export_config) = evaluation_context.export_config {
|
||||||
// Export
|
// Export
|
||||||
let TaggedValue::RenderOutput(RenderOutput {
|
let graphene_std::wasm_application_io::RenderOutputType::Svg(svg) = render_output.data else {
|
||||||
data: graphene_std::wasm_application_io::RenderOutputType::Svg(svg),
|
|
||||||
..
|
|
||||||
}) = node_graph_output
|
|
||||||
else {
|
|
||||||
return Err("Incorrect render type for exporting (expected RenderOutput::Svg)".to_string());
|
return Err("Incorrect render type for exporting (expected RenderOutput::Svg)".to_string());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -193,7 +188,7 @@ impl NodeGraphExecutor {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update artwork
|
// 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) => {
|
NodeGraphUpdate::CompilationResponse(compilation_response) => {
|
||||||
@@ -213,58 +208,35 @@ impl NodeGraphExecutor {
|
|||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
};
|
};
|
||||||
responses.add(PortfolioMessage::ProcessCompilationResponse { compilation_metadata });
|
responses.add(PortfolioMessage::ProcessCompilationResponse { compilation_metadata });
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
}
|
||||||
|
NodeGraphUpdate::IntrospectionResponse(introspection_response) => {
|
||||||
|
responses.add(Message::ProcessIntrospectionQueue(introspection_response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_node_graph_output(
|
fn process_node_graph_output(&self, render_output: RenderOutput, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
||||||
&mut self,
|
match render_output.data {
|
||||||
node_graph_output: TaggedValue,
|
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
|
||||||
introspected_inputs: Vec<(CompiledProtonodeInput, IntrospectMode, Box<dyn std::any::Any + Send + Sync>)>,
|
// Send to frontend
|
||||||
transform: DAffine2,
|
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
|
||||||
responses: &mut VecDeque<Message>,
|
}
|
||||||
) -> Result<(), String> {
|
graphene_std::wasm_application_io::RenderOutputType::CanvasFrame(frame) => {
|
||||||
let mut render_output_metadata = RenderMetadata::default();
|
let matrix = format_transform_matrix(frame.transform);
|
||||||
match node_graph_output {
|
let transform = if matrix.is_empty() { String::new() } else { format!(" transform=\"{}\"", matrix) };
|
||||||
TaggedValue::RenderOutput(render_output) => {
|
let svg = format!(
|
||||||
match render_output.data {
|
r#"<svg><foreignObject width="{}" height="{}"{transform}><div data-canvas-placeholder="canvas{}"></div></foreignObject></svg>"#,
|
||||||
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
|
frame.resolution.x, frame.resolution.y, frame.surface_id.0
|
||||||
// Send to frontend
|
);
|
||||||
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
|
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;
|
|
||||||
}
|
}
|
||||||
// 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(())
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::DVec2;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::NodeNetwork;
|
||||||
use graph_craft::document::{NodeId, NodeNetwork};
|
|
||||||
use graph_craft::graphene_compiler::Compiler;
|
|
||||||
use graph_craft::proto::GraphErrors;
|
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::text::FontCache;
|
||||||
use graphene_std::uuid::{CompiledProtonodeInput, NodeId};
|
use graphene_std::uuid::CompiledProtonodeInput;
|
||||||
use graphene_std::vector::style::ViewMode;
|
use graphene_std::wasm_application_io::WasmApplicationIo;
|
||||||
use graphene_std::vector::{VectorData, VectorDataTable};
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
|
|
||||||
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
|
|
||||||
use interpreted_executor::util::wrap_network_in_scope;
|
use interpreted_executor::util::wrap_network_in_scope;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
@@ -41,9 +29,6 @@ pub struct NodeRuntime {
|
|||||||
|
|
||||||
node_graph_errors: GraphErrors,
|
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.
|
/// Mapping of the fully-qualified node paths to their preprocessor substitutions.
|
||||||
substitutions: HashMap<ProtoNodeIdentifier, DocumentNode>,
|
substitutions: HashMap<ProtoNodeIdentifier, DocumentNode>,
|
||||||
|
|
||||||
@@ -61,10 +46,10 @@ pub enum GraphRuntimeRequest {
|
|||||||
// Renders thumbnails for the data from the last execution
|
// 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
|
// 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.
|
// 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.
|
// Request the data from a list of node inputs. For example, used by vector modify to get the data at the input of every Path node.
|
||||||
// Can also be used by the spreadsheet/introspection system
|
// Can also be used by the spreadsheet/introspection system
|
||||||
IntrospectionRequest(HashSet<(CompiledProtonodeInput, IntrospectMode)>),
|
IntrospectionRequest(HashSet<CompiledProtonodeInput>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -87,6 +72,9 @@ impl NodeGraphRuntimeSender {
|
|||||||
fn send_evaluation_response(&self, response: EvaluationResponse) {
|
fn send_evaluation_response(&self, response: EvaluationResponse) {
|
||||||
self.0.send(NodeGraphUpdate::EvaluationResponse(response)).expect("Failed to send evaluation response")
|
self.0.send(NodeGraphUpdate::EvaluationResponse(response)).expect("Failed to send evaluation response")
|
||||||
}
|
}
|
||||||
|
fn send_introspection_response(&self, response: IntrospectionResponse) {
|
||||||
|
self.0.send(NodeGraphUpdate::IntrospectionResponse(response)).expect("Failed to send introspection response")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static NODE_RUNTIME: Lazy<Mutex<Option<NodeRuntime>>> = Lazy::new(|| Mutex::new(None));
|
pub static NODE_RUNTIME: Lazy<Mutex<Option<NodeRuntime>>> = Lazy::new(|| Mutex::new(None));
|
||||||
@@ -103,119 +91,103 @@ impl NodeRuntime {
|
|||||||
node_graph_errors: Vec::new(),
|
node_graph_errors: Vec::new(),
|
||||||
|
|
||||||
substitutions: preprocessor::generate_node_substitutions(),
|
substitutions: preprocessor::generate_node_substitutions(),
|
||||||
thumbnail_render_tagged_values: HashSet::new(),
|
|
||||||
inspect_state: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) {
|
pub async fn run(&mut self) {
|
||||||
if self.application_io.is_none() {
|
if self.application_io.is_none() {
|
||||||
#[cfg(not(test))]
|
// #[cfg(not(test))]
|
||||||
self.application_io = Some(Arc::new(WasmApplicationIo::new().await));
|
self.application_io = Some(Arc::new(WasmApplicationIo::new().await));
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
self.application_io = Some(Arc::new(WasmApplicationIo::new_offscreen().await));
|
// self.application_io = Some(Arc::new(WasmApplicationIo::new_offscreen().await));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This deduplication of messages will probably cause more issues than it solved
|
// TODO: This deduplication of messages will probably cause issues
|
||||||
// let mut graph = None;
|
let mut compilation = None;
|
||||||
// let mut execution = None;
|
let mut evaluation = None;
|
||||||
// let mut thumbnails = None;
|
let mut introspection = 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();
|
|
||||||
|
|
||||||
for request in self.receiver.try_iter() {
|
for request in self.receiver.try_iter() {
|
||||||
match request {
|
match request {
|
||||||
GraphRuntimeRequest::CompilationRequest(CompilationRequest {
|
GraphRuntimeRequest::CompilationRequest(_) => compilation = Some(request),
|
||||||
mut network,
|
GraphRuntimeRequest::EvaluationRequest(_) => evaluation = Some(request),
|
||||||
font_cache,
|
GraphRuntimeRequest::IntrospectionRequest(_) => introspection = Some(request),
|
||||||
editor_metadata,
|
}
|
||||||
}) => {
|
}
|
||||||
|
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
|
// Insert the monitor node to manage the inspection
|
||||||
// self.inspect_state = inspect_node.map(|inspect| InspectState::monitor_inspect_node(&mut network, inspect));
|
// self.inspect_state = inspect_node.map(|inspect| InspectState::monitor_inspect_node(&mut network, inspect));
|
||||||
|
|
||||||
self.node_graph_errors.clear();
|
self.node_graph_errors.clear();
|
||||||
let result = self.update_network(network).await;
|
let result = self.update_network(network, font_cache, editor_metadata).await;
|
||||||
self.sender.send_compilation_response(CompilationResponse {
|
self.sender.send_compilation_response(CompilationResponse {
|
||||||
result,
|
result,
|
||||||
node_graph_errors: self.node_graph_errors.clone(),
|
node_graph_errors: self.node_graph_errors.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Inputs to monitor is sent from the editor, and represents a list of input connectors to track the data through
|
||||||
|
// During the execution. If the value is None, then the node was not evaluated, which can occur due to caching
|
||||||
GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
|
GraphRuntimeRequest::EvaluationRequest(EvaluationRequest {
|
||||||
evaluation_id,
|
evaluation_id,
|
||||||
context,
|
context,
|
||||||
inputs_to_monitor,
|
node_to_evaluate,
|
||||||
// custom_node_to_evaluate
|
|
||||||
}) => {
|
}) => {
|
||||||
for (protonode_input, introspect_mode) in inputs_to_monitor {
|
// for (protonode_input, introspect_mode) in &inputs_to_monitor {
|
||||||
self.executor.set_introspect(protonode_input, introspect_mode)
|
// self.executor.set_introspect(*protonode_input, *introspect_mode)
|
||||||
}
|
// }
|
||||||
let transform = context.render_config.viewport.transform;
|
let result = self.executor.evaluate_from_node(context, node_to_evaluate).await;
|
||||||
|
|
||||||
let result = self.execute_network(render_config).await;
|
self.sender.send_evaluation_response(EvaluationResponse { evaluation_id, result });
|
||||||
|
}
|
||||||
let introspected_inputs = Vec::new();
|
// GraphRuntimeRequest::ThumbnailRenderRequest(_) => {}
|
||||||
for (protonode_input, mode) in inputs_to_introspect {
|
GraphRuntimeRequest::IntrospectionRequest(inputs) => {
|
||||||
let Ok(introspected_data) = self.executor.introspect(protonode_input, mode) else {
|
let mut introspected_inputs = Vec::new();
|
||||||
log::error!("Could not introspect node from input: {:?}", protonode_input);
|
for protonode_input in inputs {
|
||||||
continue;
|
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 {
|
self.sender.send_introspection_response(IntrospectionResponse(introspected_inputs));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
preprocessor::expand_network(&mut graph, &self.substitutions);
|
||||||
|
|
||||||
// Creates a network where the node paths to the document network are prefixed with NodeId(0)
|
// 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
|
// We assume only one output
|
||||||
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
|
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
|
// Modifies the NodeNetwork so the tagged values are removed and the document nodes with protonode implementations have their protonode ids set
|
||||||
// Needs to return a mapping of absolute input connectors to protonode callers, types for protonodes, and callers for protonodes, add/remove delta for resolved types
|
// Needs to return a mapping of absolute input connectors to protonode callers, types for protonodes, and callers for protonodes, add/remove delta for resolved types
|
||||||
let (proto_network, 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,
|
Ok(network) => network,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Error compiling network: {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 {
|
let result = match self.executor.update(proto_network).await {
|
||||||
Ok((types_to_add, types_to_remove)) => {
|
Ok((types_to_add, types_to_remove)) => {
|
||||||
// Used to remove thumbnails from the mapping of SNI to rendered SVG strings on the frontend, which occurs when the SNI is removed
|
// Used to remove thumbnails from the mapping of SNI to rendered SVG strings on the frontend, which occurs when the SNI is removed
|
||||||
// When native frontend rendering is possible, the strings can just be stored in the network interface for each protonode with the rest of the type metadata
|
// When native frontend rendering is possible, the strings can just be stored in the network interface for each protonode with the rest of the type metadata
|
||||||
Ok(CompilationMetadata {
|
Ok(CompilationMetadata {
|
||||||
protonode_callers_for_value,
|
protonode_caller_for_values,
|
||||||
protonode_callers_for_node,
|
protonode_caller_for_nodes,
|
||||||
types_to_add,
|
types_to_add,
|
||||||
types_to_remove,
|
types_to_remove,
|
||||||
})
|
})
|
||||||
@@ -225,23 +197,8 @@ impl NodeRuntime {
|
|||||||
Err(format!("{e:?}"))
|
Err(format!("{e:?}"))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
// log::debug!("result: {:?}", result);
|
||||||
|
result
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -613,16 +613,25 @@
|
|||||||
<div class="wires">
|
<div class="wires">
|
||||||
<svg>
|
<svg>
|
||||||
{#each $nodeGraph.wires.values() as map}
|
{#each $nodeGraph.wires.values() as map}
|
||||||
{#each map.values() as { pathString, dataType, thick, dashed }}
|
{#each map.values() as { pathString, dataType, thick, dashed, center, monitorSni }}
|
||||||
{#if !thick}
|
{#if !thick}
|
||||||
<path
|
<path
|
||||||
d={pathString}
|
d={pathString}
|
||||||
|
class="wire-path"
|
||||||
style:--data-line-width={"2px"}
|
style:--data-line-width={"2px"}
|
||||||
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||||
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
<!-- {console.log("center: ", center)} -->
|
||||||
|
<!-- {console.log("monitorSni: ", monitorSni)} -->
|
||||||
|
{#if center && monitorSni}
|
||||||
|
<!-- {console.log("thumbnails: ", $nodeGraph.thumbnails.get(monitorSni))} -->
|
||||||
|
<g transform={`translate(${center[0]}, ${center[1]})`}>
|
||||||
|
{@html $nodeGraph.thumbnails.get(monitorSni)}
|
||||||
|
</g>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
{#if $nodeGraph.wirePathInProgress}
|
{#if $nodeGraph.wirePathInProgress}
|
||||||
@@ -888,7 +897,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
||||||
path {
|
.wire-path {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: var(--data-color-dim);
|
stroke: var(--data-color-dim);
|
||||||
stroke-width: var(--data-line-width);
|
stroke-width: var(--data-line-width);
|
||||||
|
|||||||
@@ -321,6 +321,9 @@ export class WirePath {
|
|||||||
readonly dataType!: FrontendGraphDataType;
|
readonly dataType!: FrontendGraphDataType;
|
||||||
readonly thick!: boolean;
|
readonly thick!: boolean;
|
||||||
readonly dashed!: boolean;
|
readonly dashed!: boolean;
|
||||||
|
@TupleToVec2
|
||||||
|
readonly center!: XY | undefined;
|
||||||
|
readonly inputSni!: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WireUpdate {
|
export class WireUpdate {
|
||||||
@@ -1683,7 +1686,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
UpdateNodeGraphControlBarLayout,
|
UpdateNodeGraphControlBarLayout,
|
||||||
UpdateNodeGraphSelection,
|
UpdateNodeGraphSelection,
|
||||||
UpdateThumbnails: UpdateThumbnail,
|
UpdateThumbnails,
|
||||||
UpdateOpenDocumentsList,
|
UpdateOpenDocumentsList,
|
||||||
UpdatePropertyPanelSectionsLayout,
|
UpdatePropertyPanelSectionsLayout,
|
||||||
UpdateSpreadsheetLayout,
|
UpdateSpreadsheetLayout,
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
||||||
|
// console.log(updateNodeGraphNodes);
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.nodes.clear();
|
state.nodes.clear();
|
||||||
updateNodeGraphNodes.nodes.forEach((node) => {
|
updateNodeGraphNodes.nodes.forEach((node) => {
|
||||||
@@ -168,14 +169,16 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateThumbnails, (updateThumbnail) => {
|
editor.subscriptions.subscribeJsMessage(UpdateThumbnails, (updateThumbnails) => {
|
||||||
|
// console.log("thumbnail update: ", updateThumbnails);
|
||||||
update((state) => {
|
update((state) => {
|
||||||
for (const [id, value] of updateThumbnail.add) {
|
for (const [id, value] of updateThumbnails.add) {
|
||||||
state.thumbnails.set(id, value);
|
state.thumbnails.set(id, value);
|
||||||
}
|
}
|
||||||
for (const id of updateThumbnail.clear) {
|
for (const id of updateThumbnails.clear) {
|
||||||
state.thumbnails.set(id, "");
|
state.thumbnails.set(id, "");
|
||||||
}
|
}
|
||||||
|
// console.log("thumbnails: ", state.thumbnails);
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ use editor::messages::portfolio::document::utility_types::network_interface::Imp
|
|||||||
use editor::messages::portfolio::utility_types::Platform;
|
use editor::messages::portfolio::utility_types::Platform;
|
||||||
use editor::messages::prelude::*;
|
use editor::messages::prelude::*;
|
||||||
use editor::messages::tool::tool_messages::tool_prelude::WidgetId;
|
use editor::messages::tool::tool_messages::tool_prelude::WidgetId;
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
use graphene_std::raster::color::Color;
|
use graphene_std::raster::color::Color;
|
||||||
|
use graphene_std::uuid::NodeId;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_wasm_bindgen::{self, from_value};
|
use serde_wasm_bindgen::{self, from_value};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|||||||
@@ -486,6 +486,21 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn center(&self) -> Option<DVec2> {
|
||||||
|
let len = self.manipulator_groups.len();
|
||||||
|
if len == 0 {
|
||||||
|
return None;
|
||||||
|
} else if len % 2 == 1 {
|
||||||
|
return Some(self.manipulator_groups[len / 2].anchor);
|
||||||
|
} else {
|
||||||
|
let p0 = self.manipulator_groups[len / 2 - 1].anchor;
|
||||||
|
let p1 = self.manipulator_groups[len / 2 - 1].out_handle;
|
||||||
|
let p2 = self.manipulator_groups[len / 2].in_handle;
|
||||||
|
let p3 = self.manipulator_groups[len / 2].anchor;
|
||||||
|
Some(0.125 * p0 + 0.375 * p1.unwrap_or(p0) + 0.375 * p2.unwrap_or(p3) + 0.125 * p3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the necessary information to create a round join with the provided center.
|
/// Returns the necessary information to create a round join with the provided center.
|
||||||
/// The returned items correspond to:
|
/// The returned items correspond to:
|
||||||
/// - The `out_handle` for the last manipulator group of `self`
|
/// - The `out_handle` for the last manipulator group of `self`
|
||||||
|
|||||||
@@ -123,6 +123,21 @@ pub fn downcast<'a, V: StaticType + 'a>(i: Box<dyn DynAny<'a> + 'a>) -> Result<B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub fn try_downcast<'a, V: StaticType + 'a>(i: Box<dyn DynAny<'a> + 'a>) -> Result<Box<V>, Box<dyn DynAny<'a> + 'a>> {
|
||||||
|
let type_id = DynAny::type_id(i.as_ref());
|
||||||
|
if type_id == core::any::TypeId::of::<<V as StaticType>::Static>() {
|
||||||
|
// SAFETY: caller guarantees that T is the correct type
|
||||||
|
let ptr = Box::into_raw(i) as *mut V;
|
||||||
|
Ok(unsafe { Box::from_raw(ptr) })
|
||||||
|
} else {
|
||||||
|
if type_id == core::any::TypeId::of::<&dyn DynAny<'static>>() {
|
||||||
|
panic!("downcast error: type_id == core::any::TypeId::of::<dyn DynAny<'a>>()");
|
||||||
|
}
|
||||||
|
Err(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe trait StaticType {
|
pub unsafe trait StaticType {
|
||||||
type Static: 'static + ?Sized;
|
type Static: 'static + ?Sized;
|
||||||
fn type_id(&self) -> core::any::TypeId {
|
fn type_id(&self) -> core::any::TypeId {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
||||||
use glam::{DAffine2, UVec2};
|
use glam::{DAffine2, UVec2};
|
||||||
use graphene_core::text::FontCache;
|
|
||||||
use graphene_core::transform::Footprint;
|
use graphene_core::transform::Footprint;
|
||||||
use graphene_core::vector::style::ViewMode;
|
use graphene_core::vector::style::ViewMode;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@@ -236,69 +235,23 @@ pub struct RenderConfig {
|
|||||||
pub for_export: bool,
|
pub for_export: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Logger;
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct ApplicationIoValue<Io>(pub Option<Arc<Io>>);
|
||||||
|
|
||||||
impl NodeGraphUpdateSender for Logger {
|
unsafe impl<T: StaticTypeSized> StaticType for ApplicationIoValue<T> {
|
||||||
fn send(&self, message: NodeGraphUpdateMessage) {
|
type Static = ApplicationIoValue<T::Static>;
|
||||||
log::warn!("dispatching message with fallback node graph update sender {:?}", message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DummyPreferences;
|
impl<T> Eq for ApplicationIoValue<T> {}
|
||||||
|
|
||||||
impl GetEditorPreferences for DummyPreferences {
|
impl<T> Hash for ApplicationIoValue<T> {
|
||||||
fn use_vello(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EditorApi<Io> {
|
|
||||||
/// Font data (for rendering text) made available to the graph through the [`WasmEditorApi`].
|
|
||||||
pub font_cache: FontCache,
|
|
||||||
/// Gives access to APIs like a rendering surface (native window handle or HTML5 canvas) and WGPU (which becomes WebGPU on web).
|
|
||||||
pub application_io: Option<Arc<Io>>,
|
|
||||||
pub node_graph_message_sender: Box<dyn NodeGraphUpdateSender + Send + Sync>,
|
|
||||||
/// Editor preferences made available to the graph through the [`WasmEditorApi`].
|
|
||||||
pub editor_preferences: Box<dyn GetEditorPreferences + Send + Sync>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Io> Eq for EditorApi<Io> {}
|
|
||||||
|
|
||||||
impl<Io: Default> Default for EditorApi<Io> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
font_cache: FontCache::default(),
|
|
||||||
application_io: None,
|
|
||||||
node_graph_message_sender: Box::new(Logger),
|
|
||||||
editor_preferences: Box::new(DummyPreferences),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Io> Hash for EditorApi<Io> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.font_cache.hash(state);
|
self.0.as_ref().map_or(0, |io| io.as_ref() as *const _ as usize).hash(state);
|
||||||
self.application_io.as_ref().map_or(0, |io| io.as_ref() as *const _ as usize).hash(state);
|
|
||||||
(self.node_graph_message_sender.as_ref() as *const dyn NodeGraphUpdateSender).hash(state);
|
|
||||||
(self.editor_preferences.as_ref() as *const dyn GetEditorPreferences).hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io> PartialEq for EditorApi<Io> {
|
impl<T> PartialEq for ApplicationIoValue<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.font_cache == other.font_cache
|
self.0.as_ref().map_or(0, |io| addr_of!(io) as usize) == other.0.as_ref().map_or(0, |io| addr_of!(io) as usize)
|
||||||
&& self.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize) == other.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize)
|
|
||||||
&& std::ptr::eq(self.node_graph_message_sender.as_ref() as *const _, other.node_graph_message_sender.as_ref() as *const _)
|
|
||||||
&& std::ptr::eq(self.editor_preferences.as_ref() as *const _, other.editor_preferences.as_ref() as *const _)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Debug for EditorApi<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("EditorApi").field("font_cache", &self.font_cache).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: StaticTypeSized> StaticType for EditorApi<T> {
|
|
||||||
type Static = EditorApi<T::Static>;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Ctx, ExtractAnimationTime, ExtractTime};
|
use crate::{Ctx, ExtractAnimationTime, ExtractRealTime};
|
||||||
|
|
||||||
const DAY: f64 = 1000. * 3600. * 24.;
|
const DAY: f64 = 1000. * 3600. * 24.;
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ pub enum AnimationTimeMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Animation"))]
|
#[node_macro::node(category("Animation"))]
|
||||||
fn real_time(ctx: impl Ctx + ExtractTime, _primary: (), mode: RealTimeMode) -> f64 {
|
fn real_time(ctx: impl Ctx + ExtractRealTime, _primary: (), mode: RealTimeMode) -> f64 {
|
||||||
let time = ctx.try_time().unwrap_or_default();
|
let time = ctx.try_real_time().unwrap_or_default();
|
||||||
// TODO: Implement proper conversion using and existing time implementation
|
// TODO: Implement proper conversion using and existing time implementation
|
||||||
match mode {
|
match mode {
|
||||||
RealTimeMode::Utc => time,
|
RealTimeMode::Utc => time,
|
||||||
|
|||||||
+312
-82
@@ -1,13 +1,13 @@
|
|||||||
|
use glam::{DAffine2, UVec2};
|
||||||
|
|
||||||
use crate::transform::Footprint;
|
use crate::transform::Footprint;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::panic::Location;
|
use std::panic::Location;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait Ctx: Clone + Send {}
|
pub trait Ctx: Clone + Send {}
|
||||||
|
|
||||||
pub trait ExtractFootprint {
|
pub trait ExtractFootprint {
|
||||||
#[track_caller]
|
|
||||||
fn try_footprint(&self) -> Option<&Footprint>;
|
fn try_footprint(&self) -> Option<&Footprint>;
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn footprint(&self) -> &Footprint {
|
fn footprint(&self) -> &Footprint {
|
||||||
@@ -18,8 +18,12 @@ pub trait ExtractFootprint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ExtractTime {
|
pub trait ExtractDownstreamTransform {
|
||||||
fn try_time(&self) -> Option<f64>;
|
fn try_downstream_transform(&self) -> Option<&DAffine2>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExtractRealTime {
|
||||||
|
fn try_real_time(&self) -> Option<f64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ExtractAnimationTime {
|
pub trait ExtractAnimationTime {
|
||||||
@@ -42,9 +46,32 @@ pub trait CloneVarArgs: ExtractVarArgs {
|
|||||||
fn arc_clone(&self) -> Option<Arc<dyn ExtractVarArgs + Send + Sync>>;
|
fn arc_clone(&self) -> Option<Arc<dyn ExtractVarArgs + Send + Sync>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ExtractAll: ExtractFootprint + ExtractIndex + ExtractTime + ExtractAnimationTime + ExtractVarArgs {}
|
pub trait ExtractAll: ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs {}
|
||||||
|
|
||||||
impl<T: ?Sized + ExtractFootprint + ExtractIndex + ExtractTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
|
impl<T: ?Sized + ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum ContextDependency {
|
||||||
|
ExtractFootprint,
|
||||||
|
// Can be used by cull nodes to check if the final output would be outside the footprint viewport
|
||||||
|
ExtractDownstreamTransform,
|
||||||
|
ExtractRealTime,
|
||||||
|
ExtractAnimationTime,
|
||||||
|
ExtractIndex,
|
||||||
|
ExtractVarArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_context_dependencies() -> Vec<ContextDependency> {
|
||||||
|
vec![
|
||||||
|
ContextDependency::ExtractFootprint,
|
||||||
|
// Can be used by cull nodes to check if the final output would be outside the footprint viewport
|
||||||
|
ContextDependency::ExtractDownstreamTransform,
|
||||||
|
ContextDependency::ExtractRealTime,
|
||||||
|
ContextDependency::ExtractAnimationTime,
|
||||||
|
ContextDependency::ExtractIndex,
|
||||||
|
ContextDependency::ExtractVarArgs,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum VarArgsResult {
|
pub enum VarArgsResult {
|
||||||
@@ -72,17 +99,30 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Option<T> {
|
|||||||
fn try_footprint(&self) -> Option<&Footprint> {
|
fn try_footprint(&self) -> Option<&Footprint> {
|
||||||
self.as_ref().and_then(|x| x.try_footprint())
|
self.as_ref().and_then(|x| x.try_footprint())
|
||||||
}
|
}
|
||||||
#[track_caller]
|
}
|
||||||
fn footprint(&self) -> &Footprint {
|
|
||||||
self.try_footprint().unwrap_or_else(|| {
|
impl ExtractDownstreamTransform for () {
|
||||||
log::warn!("trying to extract footprint from context None {} ", Location::caller());
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
&Footprint::DEFAULT
|
log::error!("tried to extract downstream transform form (), {}", Location::caller());
|
||||||
})
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ExtractTime + Sync> ExtractTime for Option<T> {
|
|
||||||
fn try_time(&self) -> Option<f64> {
|
impl<T: ExtractDownstreamTransform + Ctx + Sync + Send> ExtractDownstreamTransform for &T {
|
||||||
self.as_ref().and_then(|x| x.try_time())
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
|
(*self).try_downstream_transform()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Option<T> {
|
||||||
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
|
self.as_ref().and_then(|x| x.try_downstream_transform())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExtractRealTime + Sync> ExtractRealTime for Option<T> {
|
||||||
|
fn try_real_time(&self) -> Option<f64> {
|
||||||
|
self.as_ref().and_then(|x| x.try_real_time())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Option<T> {
|
impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Option<T> {
|
||||||
@@ -111,9 +151,16 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Arc<T> {
|
|||||||
(**self).try_footprint()
|
(**self).try_footprint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ExtractTime + Sync> ExtractTime for Arc<T> {
|
|
||||||
fn try_time(&self) -> Option<f64> {
|
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Arc<T> {
|
||||||
(**self).try_time()
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
|
(**self).try_downstream_transform()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExtractRealTime + Sync> ExtractRealTime for Arc<T> {
|
||||||
|
fn try_real_time(&self) -> Option<f64> {
|
||||||
|
(**self).try_real_time()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Arc<T> {
|
impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Arc<T> {
|
||||||
@@ -156,43 +203,22 @@ impl<T: CloneVarArgs + Sync> CloneVarArgs for Arc<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ctx for ContextImpl<'_> {}
|
|
||||||
impl Ctx for Arc<OwnedContextImpl> {}
|
impl Ctx for Arc<OwnedContextImpl> {}
|
||||||
|
|
||||||
impl ExtractFootprint for ContextImpl<'_> {
|
|
||||||
fn try_footprint(&self) -> Option<&Footprint> {
|
|
||||||
self.footprint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ExtractTime for ContextImpl<'_> {
|
|
||||||
fn try_time(&self) -> Option<f64> {
|
|
||||||
self.time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ExtractIndex for ContextImpl<'_> {
|
|
||||||
fn try_index(&self) -> Option<Vec<usize>> {
|
|
||||||
self.index.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ExtractVarArgs for ContextImpl<'_> {
|
|
||||||
fn vararg(&self, index: usize) -> Result<DynRef<'_>, VarArgsResult> {
|
|
||||||
let Some(inner) = self.varargs else { return Err(VarArgsResult::NoVarArgs) };
|
|
||||||
inner.get(index).ok_or(VarArgsResult::IndexOutOfBounds).copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn varargs_len(&self) -> Result<usize, VarArgsResult> {
|
|
||||||
let Some(inner) = self.varargs else { return Err(VarArgsResult::NoVarArgs) };
|
|
||||||
Ok(inner.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtractFootprint for OwnedContextImpl {
|
impl ExtractFootprint for OwnedContextImpl {
|
||||||
fn try_footprint(&self) -> Option<&Footprint> {
|
fn try_footprint(&self) -> Option<&Footprint> {
|
||||||
self.footprint.as_ref()
|
self.footprint.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ExtractTime for OwnedContextImpl {
|
|
||||||
fn try_time(&self) -> Option<f64> {
|
impl ExtractDownstreamTransform for OwnedContextImpl {
|
||||||
|
fn try_downstream_transform(&self) -> Option<&DAffine2> {
|
||||||
|
self.downstream_transform.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtractRealTime for OwnedContextImpl {
|
||||||
|
fn try_real_time(&self) -> Option<f64> {
|
||||||
self.real_time
|
self.real_time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,20 +260,26 @@ impl CloneVarArgs for Arc<OwnedContextImpl> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lifetime isnt necessary?
|
|
||||||
pub type Context<'a> = Option<Arc<OwnedContextImpl>>;
|
pub type Context<'a> = Option<Arc<OwnedContextImpl>>;
|
||||||
type DynRef<'a> = &'a (dyn Any + Send + Sync);
|
type DynRef<'a> = &'a (dyn Any + Send + Sync);
|
||||||
type DynBox = Box<dyn Any + Send + Sync>;
|
type DynBox = Box<dyn Any + Send + Sync>;
|
||||||
|
|
||||||
#[derive(dyn_any::DynAny)]
|
#[derive(dyn_any::DynAny)]
|
||||||
pub struct OwnedContextImpl {
|
pub struct OwnedContextImpl {
|
||||||
|
// The footprint represents the document to viewport render metadata
|
||||||
footprint: Option<Footprint>,
|
footprint: Option<Footprint>,
|
||||||
varargs: Option<Arc<[DynBox]>>,
|
// The transform node does not modify the document to viewport, it instead modifies this,
|
||||||
parent: Option<Arc<dyn ExtractVarArgs + Sync + Send>>,
|
// which can be used to transform the evaluated data from the node and check if it is within the
|
||||||
// This could be converted into a single enum to save extra bytes
|
// document to viewport transform.
|
||||||
index: Option<Vec<usize>>,
|
downstream_transform: Option<DAffine2>,
|
||||||
|
index: Option<usize>,
|
||||||
real_time: Option<f64>,
|
real_time: Option<f64>,
|
||||||
animation_time: Option<f64>,
|
animation_time: Option<f64>,
|
||||||
|
|
||||||
|
// varargs: Option<(Vec<String>, Arc<[DynBox]>)>,
|
||||||
|
varargs: Option<Arc<[DynBox]>>,
|
||||||
|
|
||||||
|
parent: Option<Arc<dyn ExtractVarArgs + Sync + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for OwnedContextImpl {
|
impl std::fmt::Debug for OwnedContextImpl {
|
||||||
@@ -285,8 +317,9 @@ impl OwnedContextImpl {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn from<T: ExtractAll + CloneVarArgs>(value: T) -> Self {
|
pub fn from<T: ExtractAll + CloneVarArgs>(value: T) -> Self {
|
||||||
let footprint = value.try_footprint().copied();
|
let footprint = value.try_footprint().copied();
|
||||||
|
let downstream_transform = value.try_downstream_transform().copied();
|
||||||
let index = value.try_index();
|
let index = value.try_index();
|
||||||
let time = value.try_time();
|
let time = value.try_real_time();
|
||||||
let frame_time = value.try_animation_time();
|
let frame_time = value.try_animation_time();
|
||||||
let parent = match value.varargs_len() {
|
let parent = match value.varargs_len() {
|
||||||
Ok(x) if x > 0 => value.arc_clone(),
|
Ok(x) if x > 0 => value.arc_clone(),
|
||||||
@@ -294,21 +327,40 @@ impl OwnedContextImpl {
|
|||||||
};
|
};
|
||||||
OwnedContextImpl {
|
OwnedContextImpl {
|
||||||
footprint,
|
footprint,
|
||||||
varargs: None,
|
downstream_transform,
|
||||||
parent,
|
|
||||||
index,
|
index,
|
||||||
real_time: time,
|
real_time: time,
|
||||||
animation_time: frame_time,
|
animation_time: frame_time,
|
||||||
|
varargs: None,
|
||||||
|
parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn empty() -> Self {
|
pub const fn empty() -> Self {
|
||||||
OwnedContextImpl {
|
OwnedContextImpl {
|
||||||
footprint: None,
|
footprint: None,
|
||||||
varargs: None,
|
downstream_transform: None,
|
||||||
parent: None,
|
|
||||||
index: None,
|
index: None,
|
||||||
real_time: None,
|
real_time: None,
|
||||||
animation_time: None,
|
animation_time: None,
|
||||||
|
varargs: None,
|
||||||
|
parent: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nullify(&mut self, nullify: &Vec<ContextDependency>) {
|
||||||
|
for context_dependency in nullify {
|
||||||
|
match context_dependency {
|
||||||
|
ContextDependency::ExtractFootprint => self.footprint = None,
|
||||||
|
ContextDependency::ExtractDownstreamTransform => self.downstream_transform = None,
|
||||||
|
ContextDependency::ExtractRealTime => self.real_time = None,
|
||||||
|
ContextDependency::ExtractAnimationTime => self.animation_time = None,
|
||||||
|
ContextDependency::ExtractIndex => self.index = None,
|
||||||
|
ContextDependency::ExtractVarArgs => {
|
||||||
|
self.varargs = None;
|
||||||
|
self.parent = None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,10 +369,31 @@ impl OwnedContextImpl {
|
|||||||
pub fn set_footprint(&mut self, footprint: Footprint) {
|
pub fn set_footprint(&mut self, footprint: Footprint) {
|
||||||
self.footprint = Some(footprint);
|
self.footprint = Some(footprint);
|
||||||
}
|
}
|
||||||
|
pub fn set_downstream_transform(&mut self, transform: DAffine2) {
|
||||||
|
self.downstream_transform = Some(transform);
|
||||||
|
}
|
||||||
|
pub fn try_apply_downstream_transform(&mut self, transform: DAffine2) {
|
||||||
|
if let Some(downstream_transform) = self.downstream_transform {
|
||||||
|
self.downstream_transform = Some(downstream_transform * transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_real_time(&mut self, time: f64) {
|
||||||
|
self.real_time = Some(time);
|
||||||
|
}
|
||||||
|
pub fn set_animation_time(&mut self, animation_time: f64) {
|
||||||
|
self.animation_time = Some(animation_time);
|
||||||
|
}
|
||||||
|
pub fn set_index(&mut self, index: usize) {
|
||||||
|
self.index = Some(index);
|
||||||
|
}
|
||||||
pub fn with_footprint(mut self, footprint: Footprint) -> Self {
|
pub fn with_footprint(mut self, footprint: Footprint) -> Self {
|
||||||
self.footprint = Some(footprint);
|
self.footprint = Some(footprint);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub fn with_downstream_transform(mut self, downstream_transform: DAffine2) -> Self {
|
||||||
|
self.downstream_transform = Some(downstream_transform);
|
||||||
|
self
|
||||||
|
}
|
||||||
pub fn with_real_time(mut self, time: f64) -> Self {
|
pub fn with_real_time(mut self, time: f64) -> Self {
|
||||||
self.real_time = Some(time);
|
self.real_time = Some(time);
|
||||||
self
|
self
|
||||||
@@ -329,11 +402,6 @@ impl OwnedContextImpl {
|
|||||||
self.animation_time = Some(animation_time);
|
self.animation_time = Some(animation_time);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn with_vararg(mut self, value: Box<dyn Any + Send + Sync>) -> Self {
|
|
||||||
assert!(self.varargs.is_none_or(|value| value.is_empty()));
|
|
||||||
self.varargs = Some(Arc::new([value]));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn with_index(mut self, index: usize) -> Self {
|
pub fn with_index(mut self, index: usize) -> Self {
|
||||||
if let Some(current_index) = &mut self.index {
|
if let Some(current_index) = &mut self.index {
|
||||||
current_index.push(index);
|
current_index.push(index);
|
||||||
@@ -345,31 +413,193 @@ impl OwnedContextImpl {
|
|||||||
pub fn into_context(self) -> Option<Arc<Self>> {
|
pub fn into_context(self) -> Option<Arc<Self>> {
|
||||||
Some(Arc::new(self))
|
Some(Arc::new(self))
|
||||||
}
|
}
|
||||||
|
pub fn add_vararg(mut self, _variable_name: String, value: Box<dyn Any + Send + Sync>) -> Self {
|
||||||
|
assert!(self.varargs.is_none_or(|value| value.is_empty()));
|
||||||
|
// self.varargs = Some((vec![variable_name], Arc::new([value])));
|
||||||
|
self.varargs = Some(Arc::new([value]));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn set_varargs(&mut self, var_args: (Vec<String>, Arc<[DynBox]>)) {
|
||||||
|
self.varargs = Some(var_args.1)
|
||||||
|
}
|
||||||
|
pub fn with_vararg(mut self, var_args: (impl Into<String>, DynBox)) -> Self {
|
||||||
|
self.varargs = Some(Arc::new([var_args.1]));
|
||||||
|
self
|
||||||
|
}
|
||||||
pub fn erase_parent(mut self) -> Self {
|
pub fn erase_parent(mut self) -> Self {
|
||||||
self.parent = None;
|
self.parent = None;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, dyn_any::DynAny)]
|
// #[derive(Default, Clone, Copy, dyn_any::DynAny)]
|
||||||
pub struct ContextImpl<'a> {
|
// pub struct ContextImpl<'a> {
|
||||||
pub(crate) footprint: Option<&'a Footprint>,
|
// pub(crate) footprint: Option<&'a Footprint>,
|
||||||
varargs: Option<&'a [DynRef<'a>]>,
|
// varargs: Option<&'a [DynRef<'a>]>,
|
||||||
// This could be converted into a single enum to save extra bytes
|
// // This could be converted into a single enum to save extra bytes
|
||||||
index: Option<Vec<usize>>,
|
// index: Option<usize>,
|
||||||
time: Option<f64>,
|
// time: Option<f64>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> ContextImpl<'a> {
|
||||||
|
// pub fn with_footprint<'f>(&self, new_footprint: &'f Footprint, varargs: Option<&'f impl Borrow<[DynRef<'f>]>>) -> ContextImpl<'f>
|
||||||
|
// where
|
||||||
|
// 'a: 'f,
|
||||||
|
// {
|
||||||
|
// ContextImpl {
|
||||||
|
// footprint: Some(new_footprint),
|
||||||
|
// varargs: varargs.map(|x| x.borrow()),
|
||||||
|
// ..*self
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[node_macro::node(category("Context Getter"))]
|
||||||
|
fn get_footprint(ctx: impl Ctx + ExtractFootprint) -> Option<Footprint> {
|
||||||
|
ctx.try_footprint().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContextImpl<'a> {
|
#[node_macro::node(category("Context Getter"))]
|
||||||
pub fn with_footprint<'f>(&self, new_footprint: &'f Footprint, varargs: Option<&'f impl Borrow<[DynRef<'f>]>>) -> ContextImpl<'f>
|
fn get_document_to_viewport(ctx: impl Ctx + ExtractFootprint) -> Option<DAffine2> {
|
||||||
where
|
ctx.try_footprint().map(|footprint| footprint.transform.clone())
|
||||||
'a: 'f,
|
}
|
||||||
{
|
|
||||||
ContextImpl {
|
#[node_macro::node(category("Context Getter"))]
|
||||||
footprint: Some(new_footprint),
|
fn get_resolution(ctx: impl Ctx + ExtractFootprint) -> Option<UVec2> {
|
||||||
varargs: varargs.map(|x| x.borrow()),
|
ctx.try_footprint().map(|footprint| footprint.resolution.clone())
|
||||||
index: self.index.clone(),
|
}
|
||||||
..*self
|
|
||||||
}
|
#[node_macro::node(category("Context Getter"))]
|
||||||
}
|
fn get_downstream_transform(ctx: impl Ctx + ExtractDownstreamTransform) -> Option<DAffine2> {
|
||||||
|
ctx.try_downstream_transform().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Context Getter"))]
|
||||||
|
fn get_real_time(ctx: impl Ctx + ExtractRealTime) -> Option<f64> {
|
||||||
|
ctx.try_real_time()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Context Getter"))]
|
||||||
|
fn get_animation_time(ctx: impl Ctx + ExtractAnimationTime) -> Option<f64> {
|
||||||
|
ctx.try_animation_time()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Context Getter"))]
|
||||||
|
fn get_index(ctx: impl Ctx + ExtractIndex) -> Option<u32> {
|
||||||
|
ctx.try_index().map(|index| index as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// async fn loop_node<T: Default>(
|
||||||
|
// ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||||
|
// #[implementations(
|
||||||
|
// Context -> Option<u32>,
|
||||||
|
// )]
|
||||||
|
// return_if_some: impl Node<Context<'static>, Output = Option<T>>,
|
||||||
|
// #[implementations(
|
||||||
|
// Context -> (),
|
||||||
|
// )]
|
||||||
|
// run_if_none: impl Node<Context<'static>, Output = ()>,
|
||||||
|
// ) -> T {
|
||||||
|
// let mut context = OwnedContextImpl::from(ctx.clone());
|
||||||
|
// context.arc_mutex = Some(Arc::new(Mutex::new(None)));
|
||||||
|
// loop {
|
||||||
|
// if let Some(return_value) = return_if_some.eval(context.clone().into_context()).await {
|
||||||
|
// return return_value;
|
||||||
|
// }
|
||||||
|
// run_if_none.eval(context.clone().into_context()).await;
|
||||||
|
// let Some(context_after_loop) = context.arc_mutex.unwrap().lock().unwrap().take() else {
|
||||||
|
// log::error!("Loop context was not set, breaking loop to avoid infinite loop");
|
||||||
|
// return T::default();
|
||||||
|
// };
|
||||||
|
// context = context_after_loop;
|
||||||
|
// context.arc_mutex = Some(Arc::new(Mutex::new(None)));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// async fn update_loop_node_context(ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync) -> () {
|
||||||
|
// let mut context = OwnedContextImpl::from(ctx.clone());
|
||||||
|
// let context_after_loop = OwnedContextImpl::from(ctx.clone());
|
||||||
|
// if let Some(arc_mutex) = context.arc_mutex.as_ref() {
|
||||||
|
// *arc_mutex.lock().unwrap() = Some(context_after_loop);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[node_macro::node(category("Loop"))]
|
||||||
|
async fn set_index<T: 'n + 'static>(
|
||||||
|
ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync,
|
||||||
|
#[expose]
|
||||||
|
#[implementations(
|
||||||
|
Context -> u32,
|
||||||
|
Context -> (),
|
||||||
|
)]
|
||||||
|
input: impl Node<Context<'static>, Output = T>,
|
||||||
|
number: u32,
|
||||||
|
) -> T {
|
||||||
|
let mut new_context = OwnedContextImpl::from(ctx);
|
||||||
|
new_context.index = Some(number.try_into().unwrap());
|
||||||
|
input.eval(new_context.into_context()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// fn create_arc_mutex(_ctx: impl Ctx) -> Arc<Mutex<Option<OwnedContextImpl>>> {
|
||||||
|
// Arc::new(Mutex::new(0))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// fn get_arc_mutex(ctx: impl Ctx + ExtractArcMutex) -> Option<Arc<Mutex<Option<OwnedContextImpl>>>> {
|
||||||
|
// ctx.try_arc_mutex()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// async fn set_arc_mutex<T: 'n + 'static>(
|
||||||
|
// ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync,
|
||||||
|
// // Auto generate for each tagged value type
|
||||||
|
// #[expose]
|
||||||
|
// #[implementations(
|
||||||
|
// Context -> u32,
|
||||||
|
// )]
|
||||||
|
// input: impl Node<Context<'static>, Output = T>,
|
||||||
|
// arc_mutex: Arc<Mutex<Option<OwnedContextImpl>>>,
|
||||||
|
// ) -> T {
|
||||||
|
// let mut new_context = OwnedContextImpl::from(ctx);
|
||||||
|
// new_context.arc_mutex = Some(arc_mutex);
|
||||||
|
// input.eval(new_context.into_context()).await
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // TODO: Discard node + return () for loop if none branch
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// fn set_arc_mutex_value<T>(_ctx: impl Ctx, #[implementations(Arc<Mutex<u32>>)] arc_mutex: Arc<Mutex<T>>, #[implementations(u32)] value: T) -> () {
|
||||||
|
// let mut guard = arc_mutex.lock().unwrap(); // lock the mutex
|
||||||
|
// *guard = value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// fn get_context(ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync) -> OwnedContextImpl {
|
||||||
|
// OwnedContextImpl::from(ctx)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Loop"))]
|
||||||
|
// fn discard(_ctx: impl Ctx, _input: u32) -> () {}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Debug"))]
|
||||||
|
fn is_none<T>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, Option<u32>, Option<u64>, Option<String>)] input: Option<T>) -> bool {
|
||||||
|
input.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[node_macro::node(category("Debug"))]
|
||||||
|
// fn unwrap<T>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, Option<u32>, Option<u64>, Option<String>, Option<Vec<u32>>)] input: Option<T>) -> T {
|
||||||
|
// input.unwrap()
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[node_macro::node(category("Debug"))]
|
||||||
|
fn to_option<T>(_: impl Ctx, boolean: bool, #[implementations(u32)] input: T) -> Option<T> {
|
||||||
|
boolean.then(|| input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Debug"))]
|
||||||
|
fn to_usize(_: impl Ctx, u32: u32) -> usize {
|
||||||
|
u32.try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ pub trait Node<'i, Input> {
|
|||||||
|
|
||||||
/// Get the call argument or output data for the monitor node on the next evaluation after set_introspect_input
|
/// Get the call argument or output data for the monitor node on the next evaluation after set_introspect_input
|
||||||
/// Also returns a boolean of whether the node was evaluated
|
/// Also returns a boolean of whether the node was evaluated
|
||||||
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
|
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,88 @@ use std::ops::Deref;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
/// Caches the output of a given Node and acts as a proxy
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MonitorMemoNode<T, CachedNode> {
|
||||||
|
// Introspection cache, uses the hash of the nullified context with default var args
|
||||||
|
// cache: Arc<Mutex<std::collections::HashMap<u64, Arc<T>>>>,
|
||||||
|
// Return value cache,
|
||||||
|
cache: Arc<Mutex<Option<(u64, Arc<T>)>>>,
|
||||||
|
node: CachedNode,
|
||||||
|
changed_since_last_eval: Arc<Mutex<bool>>,
|
||||||
|
}
|
||||||
|
impl<'i, I: Hash + 'i + std::fmt::Debug, T: 'static + Clone + Send + Sync, CachedNode: 'i> Node<'i, I> for MonitorMemoNode<T, CachedNode>
|
||||||
|
where
|
||||||
|
CachedNode: for<'any_input> Node<'any_input, I>,
|
||||||
|
for<'a> <CachedNode as Node<'a, I>>::Output: Future<Output = T> + WasmNotSend,
|
||||||
|
{
|
||||||
|
// TODO: This should return a reference to the cached cached_value
|
||||||
|
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD
|
||||||
|
type Output = DynFuture<'i, T>;
|
||||||
|
// fn eval(&'i self, input: I) -> Self::Output {
|
||||||
|
// let mut hasher = DefaultHasher::new();
|
||||||
|
// input.hash(&mut hasher);
|
||||||
|
// let hash = hasher.finish();
|
||||||
|
|
||||||
|
// if let Some(data) = self.cache.lock().unwrap().get(&hash) {
|
||||||
|
// let cloned_data = (**data).clone();
|
||||||
|
// Box::pin(async move { cloned_data })
|
||||||
|
// } else {
|
||||||
|
// let fut = self.node.eval(input);
|
||||||
|
// let cache = self.cache.clone();
|
||||||
|
// Box::pin(async move {
|
||||||
|
// let value = fut.await;
|
||||||
|
// cache.lock().unwrap().insert(hash, Arc::new(value.clone()));
|
||||||
|
// value
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
// let mut hasher = DefaultHasher::new();
|
||||||
|
// OwnedContextImpl::default().into_context().hash(&mut hasher);
|
||||||
|
// let hash = hasher.finish();
|
||||||
|
// self.cache.lock().unwrap().get(&hash).map(|data| (*data).clone() as Arc<dyn std::any::Any + Send + Sync>)
|
||||||
|
// }
|
||||||
|
fn eval(&'i self, input: I) -> Self::Output {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
input.hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
|
||||||
|
if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) {
|
||||||
|
let cloned_data = (*data).clone();
|
||||||
|
Box::pin(async move { cloned_data })
|
||||||
|
} else {
|
||||||
|
let fut = self.node.eval(input);
|
||||||
|
let cache = self.cache.clone();
|
||||||
|
*self.changed_since_last_eval.lock().unwrap() = true;
|
||||||
|
Box::pin(async move {
|
||||||
|
let value = fut.await;
|
||||||
|
*cache.lock().unwrap() = Some((hash, Arc::new(value.clone())));
|
||||||
|
value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
if *self.changed_since_last_eval.lock().unwrap() {
|
||||||
|
*self.changed_since_last_eval.lock().unwrap() = false;
|
||||||
|
Some(self.cache.lock().unwrap().as_ref().expect("Cached data should always be evaluated before introspection").1.clone() as Arc<dyn std::any::Any + Send + Sync>)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, CachedNode> MonitorMemoNode<T, CachedNode> {
|
||||||
|
pub fn new(node: CachedNode) -> MonitorMemoNode<T, CachedNode> {
|
||||||
|
MonitorMemoNode {
|
||||||
|
cache: Default::default(),
|
||||||
|
node,
|
||||||
|
changed_since_last_eval: Arc::new(Mutex::new(true)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Caches the output of a given Node and acts as a proxy
|
/// Caches the output of a given Node and acts as a proxy
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MemoNode<T, CachedNode> {
|
pub struct MemoNode<T, CachedNode> {
|
||||||
@@ -107,7 +189,7 @@ pub mod impure_memo {
|
|||||||
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode");
|
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum IntrospectMode {
|
pub enum IntrospectMode {
|
||||||
Input,
|
Input,
|
||||||
Data,
|
Data,
|
||||||
@@ -117,8 +199,8 @@ pub enum IntrospectMode {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MonitorNode<I, O, N> {
|
pub struct MonitorNode<I, O, N> {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
input: Arc<Mutex<Option<Box<I>>>>,
|
input: Arc<Mutex<Option<Arc<I>>>>,
|
||||||
output: Arc<Mutex<Option<Box<O>>>>,
|
output: Arc<Mutex<Option<Arc<O>>>>,
|
||||||
// Gets set to true by the editor when before evaluating the network, then reset when the monitor node is evaluated
|
// Gets set to true by the editor when before evaluating the network, then reset when the monitor node is evaluated
|
||||||
introspect_input: Arc<Mutex<bool>>,
|
introspect_input: Arc<Mutex<bool>>,
|
||||||
introspect_output: Arc<Mutex<bool>>,
|
introspect_output: Arc<Mutex<bool>>,
|
||||||
@@ -137,12 +219,12 @@ where
|
|||||||
let output = self.node.eval(input.clone()).await;
|
let output = self.node.eval(input.clone()).await;
|
||||||
let mut introspect_input = self.introspect_input.lock().unwrap();
|
let mut introspect_input = self.introspect_input.lock().unwrap();
|
||||||
if *introspect_input {
|
if *introspect_input {
|
||||||
*self.input.lock().unwrap() = Some(Box::new(input));
|
*self.input.lock().unwrap() = Some(Arc::new(input));
|
||||||
*introspect_input = false;
|
*introspect_input = false;
|
||||||
}
|
}
|
||||||
let mut introspect_output = self.introspect_output.lock().unwrap();
|
let mut introspect_output = self.introspect_output.lock().unwrap();
|
||||||
if *introspect_output {
|
if *introspect_output {
|
||||||
*self.output.lock().unwrap() = Some(Box::new(output.clone()));
|
*self.output.lock().unwrap() = Some(Arc::new(output.clone()));
|
||||||
*introspect_output = false;
|
*introspect_output = false;
|
||||||
}
|
}
|
||||||
output
|
output
|
||||||
@@ -150,10 +232,10 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// After introspecting, the input/output get set to None because the Arc is moved to the editor where it can be directly accessed.
|
// After introspecting, the input/output get set to None because the Arc is moved to the editor where it can be directly accessed.
|
||||||
fn introspect(&self, introspect_mode: IntrospectMode) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
fn introspect(&self, introspect_mode: IntrospectMode) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
match introspect_mode {
|
match introspect_mode {
|
||||||
IntrospectMode::Input => self.input.lock().unwrap().take().map(|input| input as Box<dyn std::any::Any + Send + Sync>),
|
IntrospectMode::Input => self.input.lock().unwrap().take().map(|input| input as Arc<dyn std::any::Any + Send + Sync>),
|
||||||
IntrospectMode::Data => self.output.lock().unwrap().take().map(|output| output as Box<dyn std::any::Any + Send + Sync>),
|
IntrospectMode::Data => self.output.lock().unwrap().take().map(|output| output as Arc<dyn std::any::Any + Send + Sync>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ pub static NODE_REGISTRY: NodeRegistry = LazyLock::new(|| Mutex::new(HashMap::ne
|
|||||||
|
|
||||||
pub static NODE_METADATA: LazyLock<Mutex<HashMap<ProtoNodeIdentifier, NodeMetadata>>> = LazyLock::new(|| Mutex::new(HashMap::new()));
|
pub static NODE_METADATA: LazyLock<Mutex<HashMap<ProtoNodeIdentifier, NodeMetadata>>> = LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
pub static NODE_CONTEXT_DEPENDENCY: LazyLock<Mutex<HashMap<String, Vec<crate::ContextDependency>>>> = LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub type DynFuture<'n, T> = Pin<Box<dyn Future<Output = T> + 'n + Send>>;
|
pub type DynFuture<'n, T> = Pin<Box<dyn Future<Output = T> + 'n + Send>>;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -132,7 +134,7 @@ pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
|
|||||||
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
|
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
|
||||||
|
|
||||||
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
||||||
pub type MonitorConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
|
pub type CacheConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NodeContainer {
|
pub struct NodeContainer {
|
||||||
@@ -277,17 +279,24 @@ where
|
|||||||
type Output = FutureAny<'input>;
|
type Output = FutureAny<'input>;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eval(&'input self, input: Any<'input>) -> Self::Output {
|
fn eval(&'input self, input: Any<'input>) -> Self::Output {
|
||||||
let node_name = std::any::type_name::<N>();
|
|
||||||
let output = |input| {
|
let output = |input| {
|
||||||
let result = self.node.eval(input);
|
let result = self.node.eval(input);
|
||||||
async move { Box::new(result.await) as Any<'input> }
|
async move { Box::new(result.await) as Any<'input> }
|
||||||
};
|
};
|
||||||
match dyn_any::downcast(input) {
|
match dyn_any::downcast(input) {
|
||||||
Ok(input) => Box::pin(output(*input)),
|
Ok(input) => Box::pin(output(*input)),
|
||||||
Err(e) => panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name),
|
Err(e) => panic!("DynAnyNode Input, {0} in:\n{1}", e, std::any::type_name::<N>()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn introspect(&self, introspect_mode: crate::IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
self.node.introspect(introspect_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_introspect(&self, introspect_mode: crate::IntrospectMode) {
|
||||||
|
self.node.set_introspect(introspect_mode);
|
||||||
|
}
|
||||||
|
|
||||||
fn reset(&self) {
|
fn reset(&self) {
|
||||||
self.node.reset();
|
self.node.reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::registry::Node;
|
use crate::Node;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
/// This is how we can generically define composition of two nodes.
|
/// This is how we can generically define composition of two nodes.
|
||||||
|
|||||||
@@ -91,5 +91,5 @@ pub type SNI = NodeId;
|
|||||||
// An input of a compiled protonode, used to reference thumbnails, which are stored on a per input basis
|
// An input of a compiled protonode, used to reference thumbnails, which are stored on a per input basis
|
||||||
pub type CompiledProtonodeInput = (NodeId, usize);
|
pub type CompiledProtonodeInput = (NodeId, usize);
|
||||||
|
|
||||||
// Path to the protonode in the document network
|
// Path to the protonode in the wrapped network (document network prefixed with NodeId(0))
|
||||||
pub type ProtonodePath = Box<[NodeId]>;
|
pub type ProtonodePath = Vec<NodeId>;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + '
|
|||||||
let mut iteration = async |index, point| {
|
let mut iteration = async |index, point| {
|
||||||
let transformed_point = transform.transform_point2(point);
|
let transformed_point = transform.transform_point2(point);
|
||||||
|
|
||||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(Box::new(transformed_point));
|
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(("Transformed point", Box::new(transformed_point)));
|
||||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||||
|
|
||||||
for mut instanced in generated_instance.instance_iter() {
|
for mut instanced in generated_instance.instance_iter() {
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
use crate::document::value::TaggedValue;
|
use crate::document::value::TaggedValue;
|
||||||
use crate::proto::{ConstructionArgs, NodeConstructionArgs, OriginalLocation, ProtoNode};
|
use crate::proto::{ConstructionArgs, NodeConstructionArgs, NodeValueArgs, ProtoNetwork, ProtoNode, UpstreamInputMetadata};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use graphene_core::memo::MemoHashGuard;
|
use graphene_core::memo::MemoHashGuard;
|
||||||
|
use graphene_core::registry::NODE_CONTEXT_DEPENDENCY;
|
||||||
pub use graphene_core::uuid::generate_uuid;
|
pub use graphene_core::uuid::generate_uuid;
|
||||||
use graphene_core::uuid::{CompiledProtonodeInput, NodeId, ProtonodePath, SNI};
|
use graphene_core::uuid::{CompiledProtonodeInput, NodeId, ProtonodePath, SNI};
|
||||||
use graphene_core::{Context, Cow, MemoHash, ProtoNodeIdentifier, Type};
|
use graphene_core::{Context, Cow, MemoHash, ProtoNodeIdentifier, Type};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// Utility function for providing a default boolean value to serde.
|
/// Utility function for providing a default boolean value to serde.
|
||||||
@@ -509,94 +510,170 @@ impl NodeNetwork {
|
|||||||
|
|
||||||
/// Functions for compiling the network
|
/// Functions for compiling the network
|
||||||
impl NodeNetwork {
|
impl NodeNetwork {
|
||||||
// Returns a topologically sorted vec of protonodes, as well as metadata extracted during compilation
|
// Returns a topologically sorted vec of vec of protonodes, as well as metadata extracted during compilation
|
||||||
|
// The first index represents the greatest distance to the export
|
||||||
// Compiles a network with one export where any scope injections are added the top level network, and the network to run is implemented as a DocumentNodeImplementation::Network
|
// Compiles a network with one export where any scope injections are added the top level network, and the network to run is implemented as a DocumentNodeImplementation::Network
|
||||||
// The traversal input is the node which calls the network to be flattened. If it is None, then start from the export.
|
// The traversal input is the node which calls the network to be flattened. If it is None, then start from the export.
|
||||||
// Every value protonode stores the connector which directly called it, which is used to map the value input to the protonode caller.
|
// Every value protonode stores the connector which directly called it, which is used to map the value input to the protonode caller.
|
||||||
// Every value input connector is mapped to its caller, and every protonode is mapped to its caller. If there are multiple, then they are compared to ensure it is the same between compilations
|
// Every value input connector is mapped to its caller, and every protonode is mapped to its caller. If there are multiple, then they are compared to ensure it is the same between compilations
|
||||||
pub fn flatten(&mut self) -> Result<(Vec<ProtoNode>, Vec<(AbsoluteInputConnector, CompiledProtonodeInput)>, Vec<(ProtonodePath, CompiledProtonodeInput)>), String> {
|
pub fn flatten(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
ProtoNetwork,
|
||||||
|
Vec<(Vec<AbsoluteInputConnector>, CompiledProtonodeInput)>,
|
||||||
|
Vec<(Vec<ProtonodePath>, CompiledProtonodeInput)>,
|
||||||
|
),
|
||||||
|
String,
|
||||||
|
> {
|
||||||
// These three arrays are stored in parallel
|
// These three arrays are stored in parallel
|
||||||
let mut protonetwork = Vec::new();
|
let mut protonetwork = Vec::new();
|
||||||
let mut value_connectors = Vec::new();
|
|
||||||
let mut protonode_paths = Vec::new();
|
|
||||||
let mut calling_protonodes = HashMap::new();
|
|
||||||
|
|
||||||
// This function creates a flattened network with populated original location fields but unmapped inputs
|
// This function creates a topologically flattened network with populated original location fields but unmapped inputs
|
||||||
// The input to flattened protonode hashmap is used to map the inputs
|
// The input to flattened protonode hashmap is used to map the inputs
|
||||||
self.traverse_input(
|
let mut protonode_indices = HashMap::new();
|
||||||
&mut protonetwork,
|
self.traverse_input(&mut protonetwork, &mut HashMap::new(), &mut protonode_indices, AbsoluteInputConnector::traversal_start(), None);
|
||||||
&mut value_connectors,
|
|
||||||
&mut protonode_paths,
|
|
||||||
&mut calling_protonodes,
|
|
||||||
&mut HashMap::new(),
|
|
||||||
AbsoluteInputConnector::traversal_start(),
|
|
||||||
(0, 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut generated_snis = HashSet::new();
|
// If a node with the same sni is reached, then its original location metadata must be added to the one at the higher vec index
|
||||||
|
// The index will always be a ProtonodeEntry::Protonode
|
||||||
|
let mut generated_snis_to_index = HashMap::new();
|
||||||
// Generate SNI's. This gets called after all node inputs are replaced with their indices
|
// Generate SNI's. This gets called after all node inputs are replaced with their indices
|
||||||
for protonode_index in (0..protonetwork.len()).rev() {
|
for protonode_index in 0..protonetwork.len() {
|
||||||
let protonode = protonetwork.get_mut(protonode_index).unwrap();
|
let ProtonodeEntry::Protonode(protonode) = protonetwork.get_mut(protonode_index).unwrap() else {
|
||||||
if let ConstructionArgs::Nodes(NodeConstructionArgs { inputs: input_snis, .. }) = &protonode.construction_args {
|
panic!("No protonode can be deduplicated during flattening");
|
||||||
for input_sni in input_snis {
|
};
|
||||||
assert_ne!(
|
// Generate context dependencies. If None, then it is a value node and does not require nullification
|
||||||
*input_sni,
|
let mut protonode_context_dependencies = None;
|
||||||
NodeId(0),
|
if let ConstructionArgs::Nodes(NodeConstructionArgs { inputs, context_dependencies, .. }) = &mut protonode.construction_args {
|
||||||
"All inputs should be mapped to a stable node index, and the calling nodes inputs should be updated"
|
for upstream_metadata in inputs.iter() {
|
||||||
);
|
let Some(upstream_metadata) = upstream_metadata else {
|
||||||
}
|
panic!("All inputs should be when the upstream SNI was generated");
|
||||||
}
|
};
|
||||||
|
for upstream_dependency in upstream_metadata.context_dependencies.iter().flatten() {
|
||||||
use std::hash::Hasher;
|
if !context_dependencies.contains(upstream_dependency) {
|
||||||
let mut hasher = rustc_hash::FxHasher::default();
|
context_dependencies.push(upstream_dependency.clone());
|
||||||
protonode.construction_args.hash(&mut hasher);
|
}
|
||||||
let mut stable_node_id = NodeId(hasher.finish());
|
|
||||||
// The stable node index must be unique for every protonode. If it has the same hash as another protonode, continue hashing itself
|
|
||||||
// For example two cache nodes connected to a Context getter node have two cache different values, even though the stable node id is the same.
|
|
||||||
while !generated_snis.insert(stable_node_id) {
|
|
||||||
stable_node_id.hash(&mut hasher);
|
|
||||||
stable_node_id = NodeId(hasher.finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
protonode.stable_node_id = stable_node_id;
|
|
||||||
for (calling_node_index, input_index) in calling_protonodes.get(&protonode_index).unwrap() {
|
|
||||||
match &mut protonetwork.get_mut(*calling_node_index).unwrap().construction_args {
|
|
||||||
ConstructionArgs::Nodes(nodes) => {
|
|
||||||
*nodes.inputs.get_mut(*input_index).unwrap() = stable_node_id;
|
|
||||||
}
|
}
|
||||||
// TODO: Implement for extract
|
}
|
||||||
_ => unreachable!(),
|
// The context_dependencies are now the union of all inputs and the dependencies of the protonode. Set the dependencies of each input to the difference, which represents the data to nullify
|
||||||
|
for upstream_metadata in inputs.iter_mut() {
|
||||||
|
let Some(upstream_metadata) = upstream_metadata else {
|
||||||
|
panic!("All inputs should be when the upstream SNI was generated");
|
||||||
|
};
|
||||||
|
match upstream_metadata.context_dependencies.as_ref() {
|
||||||
|
Some(upstream_dependencies) => {
|
||||||
|
upstream_metadata.context_dependencies = Some(
|
||||||
|
context_dependencies
|
||||||
|
.iter()
|
||||||
|
.filter(|protonode_dependency| !upstream_dependencies.contains(protonode_dependency))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// If none then the upstream node is a Value node, so do not nullify the context
|
||||||
|
None => upstream_metadata.context_dependencies = Some(Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protonode_context_dependencies = Some(context_dependencies.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
protonode.generate_stable_node_id();
|
||||||
|
let current_stable_node_id = protonode.stable_node_id;
|
||||||
|
|
||||||
|
// If the stable node id is the same as a previous node, then deduplicate
|
||||||
|
let callers = if let Some(upstream_index) = generated_snis_to_index.get(&protonode.stable_node_id) {
|
||||||
|
let ProtonodeEntry::Protonode(deduplicated_protonode) = std::mem::replace(&mut protonetwork[protonode_index], ProtonodeEntry::Deduplicated(*upstream_index)) else {
|
||||||
|
panic!("Reached protonode must not be deduplicated");
|
||||||
|
};
|
||||||
|
let ProtonodeEntry::Protonode(upstream_protonode) = &mut protonetwork[*upstream_index] else {
|
||||||
|
panic!("Upstream protonode must not be deduplicated");
|
||||||
|
};
|
||||||
|
match deduplicated_protonode.construction_args {
|
||||||
|
ConstructionArgs::Value(node_value_args) => {
|
||||||
|
let ConstructionArgs::Value(upstream_value_args) = &mut upstream_protonode.construction_args else {
|
||||||
|
panic!("Upstream protonode must match current protonode construction args");
|
||||||
|
};
|
||||||
|
upstream_value_args.connector_paths.extend(node_value_args.connector_paths);
|
||||||
|
}
|
||||||
|
ConstructionArgs::Nodes(node_construction_args) => {
|
||||||
|
let ConstructionArgs::Nodes(upstream_value_args) = &mut upstream_protonode.construction_args else {
|
||||||
|
panic!("Upstream protonode must match current protonode construction args");
|
||||||
|
};
|
||||||
|
upstream_value_args.node_paths.extend(node_construction_args.node_paths);
|
||||||
|
// The dependencies of the deduplicated node and the upstream node are the same because all inputs are the same
|
||||||
|
}
|
||||||
|
ConstructionArgs::Inline(_) => todo!(),
|
||||||
|
}
|
||||||
|
// Set the caller of the upstream node to be the minimum of all deduplicated nodes and itself
|
||||||
|
upstream_protonode.caller = deduplicated_protonode.callers.iter().chain(upstream_protonode.caller.iter()).min().cloned();
|
||||||
|
deduplicated_protonode.callers
|
||||||
|
} else {
|
||||||
|
generated_snis_to_index.insert(protonode.stable_node_id, protonode_index);
|
||||||
|
protonode.caller = protonode.callers.iter().min().cloned();
|
||||||
|
std::mem::take(&mut protonode.callers)
|
||||||
|
};
|
||||||
|
|
||||||
|
// This runs for all protonodes
|
||||||
|
for (caller_path, input_index) in callers {
|
||||||
|
let caller_index = protonode_indices[&caller_path];
|
||||||
|
let ProtonodeEntry::Protonode(caller_protonode) = &mut protonetwork[caller_index] else {
|
||||||
|
panic!("Downstream caller cannot be deduplicated");
|
||||||
|
};
|
||||||
|
match &mut caller_protonode.construction_args {
|
||||||
|
ConstructionArgs::Nodes(nodes) => {
|
||||||
|
assert!(caller_index > protonode_index, "Caller index must be higher than current index");
|
||||||
|
let input_metadata: &mut Option<UpstreamInputMetadata> = &mut nodes.inputs[input_index];
|
||||||
|
if input_metadata.is_none() {
|
||||||
|
*input_metadata = Some(UpstreamInputMetadata {
|
||||||
|
input_sni: current_stable_node_id,
|
||||||
|
context_dependencies: protonode_context_dependencies.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Value node cannot be a caller
|
||||||
|
ConstructionArgs::Value(_) => unreachable!(),
|
||||||
|
ConstructionArgs::Inline(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do another traversal now that the caller SNI have been generated to collect metadata for the editor
|
// Do another traversal now that the metadata has been accumulated after deduplication
|
||||||
|
// This includes the caller of all absolute value connections which have a NodeInput::Value, as well as the caller for each protonode
|
||||||
let mut value_connector_callers = Vec::new();
|
let mut value_connector_callers = Vec::new();
|
||||||
let mut protonode_callers = Vec::new();
|
let mut protonode_callers = Vec::new();
|
||||||
|
// Collect caller ids into a separate vec so that the pronetwork can be mutably iterated over to take the connector/node paths rather than cloning
|
||||||
|
let calling_protonode_ids = protonetwork
|
||||||
|
.iter()
|
||||||
|
.map(|entry| match entry {
|
||||||
|
ProtonodeEntry::Protonode(proto_node) => proto_node.stable_node_id,
|
||||||
|
ProtonodeEntry::Deduplicated(upstream_protonode_index) => {
|
||||||
|
let ProtonodeEntry::Protonode(proto_node) = &protonetwork[*upstream_protonode_index] else {
|
||||||
|
panic!("Upstream protonode index must not be dedeuplicated");
|
||||||
|
};
|
||||||
|
proto_node.stable_node_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for (protonode_index, (value_connector, protonode_path)) in value_connectors.iter_mut().zip(protonode_paths.iter_mut()).enumerate().rev() {
|
for protonode_entry in &mut protonetwork {
|
||||||
let callers = calling_protonodes.get(&protonode_index).unwrap();
|
if let ProtonodeEntry::Protonode(protonode) = protonode_entry {
|
||||||
|
if let Some((caller_path, caller_input_index)) = protonode.caller.as_ref() {
|
||||||
let &(min_protonode_index, input_index) = callers.iter().min().unwrap();
|
let caller_index = protonode_indices[caller_path];
|
||||||
|
match &mut protonode.construction_args {
|
||||||
let protonode_id = protonetwork[min_protonode_index].stable_node_id;
|
ConstructionArgs::Value(node_value_args) => {
|
||||||
|
value_connector_callers.push((std::mem::take(&mut node_value_args.connector_paths), (calling_protonode_ids[caller_index], *caller_input_index)))
|
||||||
if let Some(value_connector) = value_connector.take() {
|
}
|
||||||
value_connector_callers.push((value_connector, (protonode_id, input_index)));
|
ConstructionArgs::Nodes(node_construction_args) => {
|
||||||
}
|
protonode_callers.push((std::mem::take(&mut node_construction_args.node_paths), (calling_protonode_ids[caller_index], *caller_input_index)))
|
||||||
|
}
|
||||||
if let Some(protonode_path) = protonode_path.take() {
|
ConstructionArgs::Inline(_) => todo!(),
|
||||||
protonode_callers.push((protonode_path, (protonode_id, input_index)));
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut existing_ids = HashSet::new();
|
log::debug!("protonetwork: {:?}", protonetwork);
|
||||||
// Value nodes can be deduplicated if they share the same hash, since they do not depend on the input
|
Ok((ProtoNetwork::from_vec(protonetwork), value_connector_callers, protonode_callers))
|
||||||
let protonetwork = protonetwork
|
|
||||||
.into_iter()
|
|
||||||
.filter(|protonode| !(matches!(protonode.construction_args, ConstructionArgs::Value(_)) && !existing_ids.insert(protonode.stable_node_id)))
|
|
||||||
.collect();
|
|
||||||
Ok((protonetwork, value_connector_callers, protonode_callers))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_input_from_absolute_connector(&mut self, traversal_input: &AbsoluteInputConnector) -> Option<&mut NodeInput> {
|
fn get_input_from_absolute_connector(&mut self, traversal_input: &AbsoluteInputConnector) -> Option<&mut NodeInput> {
|
||||||
@@ -632,39 +709,32 @@ impl NodeNetwork {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Performs a recursive graph traversal starting from all protonode inputs and the root export until reaching the next protonode or value input.
|
|
||||||
// Automatically inserts value nodes by moving the value from the current network
|
// Performs a recursive graph traversal starting from the root export across all node inputs
|
||||||
|
// Inserts values into the protonetwork by moving the value from the current network
|
||||||
//
|
//
|
||||||
// protonetwork - The topologically sorted flattened protonetwork. The caller of each protonode is at a lower index. The output of the network is the first protonode
|
// protonetwork - The topologically sorted flattened protonetwork. The caller of each protonode is at a lower index. The output of the network is the first protonode
|
||||||
//
|
//
|
||||||
// calling protonodes - anytime a protonode is reached, the caller is added as a value with (caller protonetwork index, caller input index).
|
// calling protonodes - anytime a protonode is reached, the caller is added as a value with (caller protonetwork index, caller input index).
|
||||||
// This is necessary so the calling protonodes input can be looked up and mapped when generating SNI's
|
// This is necessary so the calling protonodes input can be looked up and mapped when generating SNI's
|
||||||
|
// None indicates that the caller is the traversal start, which is skipped
|
||||||
//
|
//
|
||||||
// Protonode indices - mapping of protonode path to its index in the protonetwork, updated when inserting a protonode
|
// Protonode indices - mapping of protonode path to its index in the protonetwork, updated when inserting a protonode
|
||||||
//
|
//
|
||||||
// Traversal input - current connector to traverse over. added to downstream_calling_inputs every time the function is called.
|
// Traversal input - current connector to traverse over. added to downstream_calling_inputs every time the function is called.
|
||||||
//
|
//
|
||||||
// downstream_calling_inputs - tracks all inputs reached during traversal
|
|
||||||
//
|
|
||||||
// any_input_to_downstream_protonode_input - used by the runtime/javascript to get the calling protonode input from any input connector.
|
|
||||||
// When a protonode is reached, each input connector in downstream_calling_inputs, is looked up in `any_input_to_downstream_protonode_input`. If there is an entry,
|
|
||||||
// Then the paths are compared, and the greater one is chosen using stable ordering.
|
|
||||||
// This is to ensure a constant mapping, since an export for instance can have multiple calling nodes in the parent network
|
|
||||||
//
|
|
||||||
// any_input_to_upstream_protonode - used by the runtime to get the node to evaluate for any given input connector.
|
|
||||||
// Each input connector is inserted into any_input_to_upstream_protonode with the value being the path to the reached protonode.
|
|
||||||
// It doesnt matter if its overwritten since it must have previously pointed to the same protonode anyways
|
|
||||||
//
|
//
|
||||||
pub fn traverse_input(
|
pub fn traverse_input(
|
||||||
&mut self,
|
&mut self,
|
||||||
protonetwork: &mut Vec<ProtoNode>, // Flattened node id to protonode, stable node ids can only be generated once the network is fully flattened, since it runs in reverse
|
protonetwork: &mut Vec<ProtonodeEntry>, // None represents a deduplicated value node
|
||||||
value_connector: &mut Vec<Option<AbsoluteInputConnector>>,
|
// Every time a value input is reached, it is added to a mapping so if it reached again, it can be moved to the end of the protonetwork
|
||||||
protonode_path: &mut Vec<Option<ProtonodePath>>,
|
value_protonode_indices: &mut HashMap<AbsoluteInputConnector, usize>,
|
||||||
calling_protonodes: &mut HashMap<usize, Vec<(usize, usize)>>, // A mapping of protonode path to all (flattened network indices, their input index) that called the protonode, used during SNI generation to remap inputs
|
// Every time a protonode is reached, is it added to a mapping so if it reached again, it can be moved to the end of the protonetwork
|
||||||
protonode_indices: &mut HashMap<Vec<SNI>, usize>, // Mapping of protonode path to its index in the flattened protonetwork
|
protonode_indices: &mut HashMap<ProtonodePath, usize>,
|
||||||
|
// The original location of the current traversal
|
||||||
traversal_input: AbsoluteInputConnector,
|
traversal_input: AbsoluteInputConnector,
|
||||||
// Protonode index, input index
|
// The protnode input which started the traversal. None if it is called from the root export
|
||||||
traversal_start: (usize, usize),
|
traversal_start: Option<(ProtonodePath, usize)>,
|
||||||
) {
|
) {
|
||||||
let network_path = &traversal_input.network_path;
|
let network_path = &traversal_input.network_path;
|
||||||
|
|
||||||
@@ -730,90 +800,111 @@ impl NodeNetwork {
|
|||||||
network_path: upstream_node_path.clone(),
|
network_path: upstream_node_path.clone(),
|
||||||
connector: InputConnector::Export(output_index),
|
connector: InputConnector::Export(output_index),
|
||||||
};
|
};
|
||||||
self.traverse_input(protonetwork, value_connector, protonode_path, calling_protonodes, protonode_indices, traversal_input, traversal_start);
|
self.traverse_input(protonetwork, value_protonode_indices, protonode_indices, traversal_input, traversal_start);
|
||||||
}
|
}
|
||||||
DocumentNodeImplementation::ProtoNode(protonode_id) => {
|
DocumentNodeImplementation::ProtoNode(protonode_id) => {
|
||||||
// Only insert the protonode if it has not previously been inserted
|
// Check if the protonode has already been reached
|
||||||
// Do not insert the protonode into the proto network or traverse over inputs if its already visited
|
let reached_protonode = match protonode_indices.get(&upstream_node_path) {
|
||||||
let reached_protonode_index = match protonode_indices.get(&upstream_node_path) {
|
// The protonode has already been inserted, add the caller and node path to its metadata
|
||||||
// The protonode has already been inserted, return its index
|
Some(previous_protonode_index) => {
|
||||||
Some(reached_protonode_index) => *reached_protonode_index,
|
let ProtonodeEntry::Protonode(protonode) = &mut protonetwork[*previous_protonode_index] else {
|
||||||
// Insert the protonode and traverse over inputs
|
panic!("Previously inserted protonode must exist at mapped protonode index");
|
||||||
None => {
|
|
||||||
let construction_args = ConstructionArgs::Nodes(NodeConstructionArgs {
|
|
||||||
identifier: protonode_id.clone(),
|
|
||||||
inputs: vec![NodeId(0); upstream_document_node.inputs.len()],
|
|
||||||
});
|
|
||||||
let protonode = ProtoNode {
|
|
||||||
construction_args,
|
|
||||||
// All protonodes take Context by default
|
|
||||||
input: concrete!(Context),
|
|
||||||
original_location: OriginalLocation {
|
|
||||||
protonode_path: upstream_node_path.clone().into(),
|
|
||||||
send_types_to_editor: true,
|
|
||||||
},
|
|
||||||
stable_node_id: NodeId(0),
|
|
||||||
};
|
};
|
||||||
let new_protonode_index = protonetwork.len();
|
protonode
|
||||||
protonode_indices.insert(upstream_node_path.clone(), new_protonode_index);
|
}
|
||||||
protonetwork.push(protonode);
|
// Construct the protonode and traverse over inputs
|
||||||
value_connector.push(None);
|
None => {
|
||||||
protonode_path.push(Some(upstream_node_path.into_boxed_slice()));
|
let number_of_inputs = upstream_document_node.inputs.len();
|
||||||
// Iterate over all upstream inputs, which will map the inputs to the index of the connected protonode
|
let identifier = protonode_id.clone();
|
||||||
for input_index in 0..upstream_document_node.inputs.len() {
|
for input_index in 0..upstream_document_node.inputs.len() {
|
||||||
self.traverse_input(
|
self.traverse_input(
|
||||||
protonetwork,
|
protonetwork,
|
||||||
value_connector,
|
value_protonode_indices,
|
||||||
protonode_path,
|
|
||||||
calling_protonodes,
|
|
||||||
protonode_indices,
|
protonode_indices,
|
||||||
AbsoluteInputConnector {
|
AbsoluteInputConnector {
|
||||||
network_path: network_path.clone(),
|
network_path: network_path.clone(),
|
||||||
connector: InputConnector::node(upstream_node_id, input_index),
|
connector: InputConnector::node(upstream_node_id, input_index),
|
||||||
},
|
},
|
||||||
(new_protonode_index, input_index),
|
Some((upstream_node_path.clone(), input_index)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
new_protonode_index
|
let context_dependencies = NODE_CONTEXT_DEPENDENCY.lock().unwrap().get(identifier.name.as_ref()).cloned().unwrap_or_default();
|
||||||
|
let construction_args = ConstructionArgs::Nodes(NodeConstructionArgs {
|
||||||
|
identifier,
|
||||||
|
inputs: vec![None; number_of_inputs],
|
||||||
|
context_dependencies,
|
||||||
|
node_paths: Vec::new(),
|
||||||
|
});
|
||||||
|
let protonode = ProtoNode {
|
||||||
|
construction_args,
|
||||||
|
// All protonodes take Context by default
|
||||||
|
input: concrete!(Context),
|
||||||
|
stable_node_id: NodeId(0),
|
||||||
|
callers: Vec::new(),
|
||||||
|
caller: None,
|
||||||
|
};
|
||||||
|
let new_protonode_index = protonetwork.len();
|
||||||
|
protonetwork.push(ProtonodeEntry::Protonode(protonode));
|
||||||
|
protonode_indices.insert(upstream_node_path.clone(), new_protonode_index);
|
||||||
|
let ProtonodeEntry::Protonode(protonode) = &mut protonetwork[new_protonode_index] else {
|
||||||
|
panic!("Inserted protonode must exist at new_protonode_index");
|
||||||
|
};
|
||||||
|
protonode
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
calling_protonodes.entry(reached_protonode_index).or_insert_with(Vec::new).push(traversal_start);
|
// Only add the traversal start if it is not the root export
|
||||||
|
if let Some(traversal_start) = traversal_start {
|
||||||
|
reached_protonode.callers.push(traversal_start);
|
||||||
|
}
|
||||||
|
let ConstructionArgs::Nodes(args) = &mut reached_protonode.construction_args else {
|
||||||
|
panic!("Reached protonode must have Nodes construction args");
|
||||||
|
};
|
||||||
|
args.node_paths.push(upstream_node_path);
|
||||||
}
|
}
|
||||||
DocumentNodeImplementation::Extract => todo!(),
|
DocumentNodeImplementation::Extract => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeInput::Value { tagged_value, .. } => {
|
NodeInput::Value { tagged_value, .. } => {
|
||||||
// Deduplication of value nodes based on their tagged value, since they do not depend on the Context
|
// Check if the protonode has already been reached
|
||||||
//
|
let reached_protonode = match value_protonode_indices.get(&traversal_input) {
|
||||||
use std::hash::Hasher;
|
// The protonode has already been inserted, add the caller and node path to its metadata
|
||||||
let mut hasher = rustc_hash::FxHasher::default();
|
Some(previous_protonode_index) => {
|
||||||
tagged_value.hash(&mut hasher);
|
let ProtonodeEntry::Protonode(protonode) = &mut protonetwork[*previous_protonode_index] else {
|
||||||
let value_node_path = vec![NodeId(hasher.finish())];
|
panic!("Previously inserted protonode must exist at mapped protonode index");
|
||||||
|
};
|
||||||
// Only insert the value protonode if it has not previously been inserted
|
protonode
|
||||||
let value_protonode_index = match protonode_indices.get(&value_node_path) {
|
}
|
||||||
// The value input has already been inserted, return it the existing value nodes index
|
|
||||||
Some(value_protonode_index) => *value_protonode_index,
|
|
||||||
// Insert the protonode and traverse over inputs
|
// Insert the protonode and traverse over inputs
|
||||||
None => {
|
None => {
|
||||||
let protonode = ProtoNode {
|
let value_protonode = ProtoNode {
|
||||||
construction_args: ConstructionArgs::Value(std::mem::replace(tagged_value, TaggedValue::None.into())),
|
construction_args: ConstructionArgs::Value(NodeValueArgs {
|
||||||
|
value: std::mem::replace(tagged_value, TaggedValue::None.into()),
|
||||||
|
connector_paths: Vec::new(),
|
||||||
|
}),
|
||||||
input: concrete!(Context), // Could be ()
|
input: concrete!(Context), // Could be ()
|
||||||
original_location: OriginalLocation {
|
|
||||||
protonode_path: Vec::new().into(),
|
|
||||||
send_types_to_editor: false,
|
|
||||||
},
|
|
||||||
stable_node_id: NodeId(0),
|
stable_node_id: NodeId(0),
|
||||||
|
callers: Vec::new(),
|
||||||
|
caller: None,
|
||||||
};
|
};
|
||||||
let new_protonode_index = protonetwork.len();
|
let new_protonode_index = protonetwork.len();
|
||||||
protonode_indices.insert(value_node_path.clone(), new_protonode_index);
|
protonetwork.push(ProtonodeEntry::Protonode(value_protonode));
|
||||||
protonetwork.push(protonode);
|
value_protonode_indices.insert(traversal_input.clone(), new_protonode_index);
|
||||||
value_connector.push(Some(traversal_input));
|
|
||||||
protonode_path.push(None);
|
let ProtonodeEntry::Protonode(protonode) = &mut protonetwork[new_protonode_index] else {
|
||||||
new_protonode_index
|
panic!("Previously inserted protonode must exist at mapped protonode index");
|
||||||
|
};
|
||||||
|
protonode
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
calling_protonodes.entry(value_protonode_index).or_insert_with(Vec::new).push(traversal_start);
|
|
||||||
|
// Only add the traversal start if it is not the root export
|
||||||
|
if let Some(traversal_start) = traversal_start {
|
||||||
|
reached_protonode.callers.push(traversal_start);
|
||||||
|
}
|
||||||
|
let ConstructionArgs::Value(args) = &mut reached_protonode.construction_args else {
|
||||||
|
panic!("Reached protonode must have Nodes construction args");
|
||||||
|
};
|
||||||
|
args.connector_paths.push(traversal_input);
|
||||||
}
|
}
|
||||||
// Continue traversal
|
// Continue traversal
|
||||||
NodeInput::Network { import_index, .. } => {
|
NodeInput::Network { import_index, .. } => {
|
||||||
@@ -823,35 +914,14 @@ impl NodeNetwork {
|
|||||||
network_path: encapsulating_network_path,
|
network_path: encapsulating_network_path,
|
||||||
connector: InputConnector::node(node_id, *import_index),
|
connector: InputConnector::node(node_id, *import_index),
|
||||||
};
|
};
|
||||||
self.traverse_input(protonetwork, value_connector, protonode_path, calling_protonodes, protonode_indices, traversal_input, traversal_start);
|
self.traverse_input(protonetwork, value_protonode_indices, protonode_indices, traversal_input, traversal_start);
|
||||||
}
|
}
|
||||||
NodeInput::Scope(_cow) => unreachable!(),
|
NodeInput::Scope(_) => unreachable!(),
|
||||||
NodeInput::Reflection(_document_node_metadata) => unreachable!(),
|
NodeInput::Reflection(_) => unreachable!(),
|
||||||
NodeInput::Inline(_inline_rust) => todo!(),
|
NodeInput::Inline(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn collect_downstream_metadata(
|
|
||||||
// reached_protonode_index: usize,
|
|
||||||
// calling_protonodes: &mut HashMap<usize, Vec<(usize, usize)>>,
|
|
||||||
// protonode_indices: &mut HashMap<Vec<SNI>, usize>,
|
|
||||||
// downstream_calling_inputs: Vec<AbsoluteInputConnector>,
|
|
||||||
// ) {
|
|
||||||
// // Map the first downstream calling node input (which is traversed for every node input) to the reached protonode
|
|
||||||
// let downstream_protonode_caller = downstream_calling_inputs[0].clone();
|
|
||||||
|
|
||||||
// match &downstream_protonode_caller.connector {
|
|
||||||
// InputConnector::Node { node_id, input_index } => {
|
|
||||||
// // The calling protonode has already been added to the flattened network, so it can be looked up by index and the reached node can be mapped to it
|
|
||||||
// let mut calling_protonode_path = downstream_protonode_caller.network_path.clone();
|
|
||||||
// calling_protonode_path.push(*node_id);
|
|
||||||
// let calling_protonode_index = protonode_indices[&calling_protonode_path];
|
|
||||||
|
|
||||||
// }
|
|
||||||
// InputConnector::Export(_) => {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Converts the `DocumentNode`s with a `DocumentNodeImplementation::Extract` into a `ClonedNode` that returns
|
/// Converts the `DocumentNode`s with a `DocumentNodeImplementation::Extract` into a `ClonedNode` that returns
|
||||||
/// the `DocumentNode` specified by the single `NodeInput::Node`.
|
/// the `DocumentNode` specified by the single `NodeInput::Node`.
|
||||||
/// The referenced node is removed from the network, and any `NodeInput::Node`s used by the referenced node are replaced with a generically typed network input.
|
/// The referenced node is removed from the network, and any `NodeInput::Node`s used by the referenced node are replaced with a generically typed network input.
|
||||||
@@ -898,11 +968,17 @@ impl NodeNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
pub enum ProtonodeEntry {
|
||||||
|
Protonode(ProtoNode),
|
||||||
|
// If deduplicated, then any upstream node which this node previously called needs to map to the new protonode
|
||||||
|
Deduplicated(usize),
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct CompilationMetadata {
|
pub struct CompilationMetadata {
|
||||||
// Stored for every value input in the compiled network
|
// Stored for every value input in the compiled network
|
||||||
pub protonode_callers_for_value: Vec<(AbsoluteInputConnector, CompiledProtonodeInput)>,
|
pub protonode_caller_for_values: Vec<(Vec<AbsoluteInputConnector>, CompiledProtonodeInput)>,
|
||||||
// Stored for every protonode in the compiled network
|
// Stored for every protonode in the compiled network
|
||||||
pub protonode_callers_for_node: Vec<(ProtonodePath, CompiledProtonodeInput)>,
|
pub protonode_caller_for_nodes: Vec<(Vec<ProtonodePath>, CompiledProtonodeInput)>,
|
||||||
pub types_to_add: Vec<(SNI, Vec<Type>)>,
|
pub types_to_add: Vec<(SNI, Vec<Type>)>,
|
||||||
pub types_to_remove: Vec<(SNI, usize)>,
|
pub types_to_remove: Vec<(SNI, usize)>,
|
||||||
}
|
}
|
||||||
@@ -970,7 +1046,7 @@ pub struct AbsoluteOutputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an output connector
|
/// Represents an output connector
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub enum OutputConnector {
|
pub enum OutputConnector {
|
||||||
#[serde(rename = "node")]
|
#[serde(rename = "node")]
|
||||||
Node {
|
Node {
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
use super::DocumentNode;
|
use super::DocumentNode;
|
||||||
use crate::proto::{Any as DAny, FutureAny};
|
use crate::proto::{Any as DAny, FutureAny};
|
||||||
use crate::wasm_application_io::WasmEditorApi;
|
use crate::wasm_application_io::WasmApplicationIoValue;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
pub use dyn_any::StaticType;
|
pub use dyn_any::StaticType;
|
||||||
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
|
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
|
||||||
use graphene_application_io::SurfaceFrame;
|
use graphene_application_io::SurfaceFrame;
|
||||||
use graphene_brush::brush_cache::BrushCache;
|
use graphene_brush::brush_cache::BrushCache;
|
||||||
use graphene_brush::brush_stroke::BrushStroke;
|
use graphene_brush::brush_stroke::BrushStroke;
|
||||||
use graphene_core::raster_types::CPU;
|
use graphene_core::raster_types::{CPU, GPU};
|
||||||
use graphene_core::transform::ReferencePoint;
|
use graphene_core::transform::ReferencePoint;
|
||||||
use graphene_core::uuid::NodeId;
|
use graphene_core::uuid::NodeId;
|
||||||
use graphene_core::vector::style::Fill;
|
use graphene_core::vector::style::Fill;
|
||||||
use graphene_core::{Color, MemoHash, Node, Type};
|
use graphene_core::{Color, MemoHash, Node, Type};
|
||||||
use graphene_svg_renderer::RenderMetadata;
|
use graphene_svg_renderer::{GraphicElementRendered, RenderMetadata};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -32,10 +32,9 @@ macro_rules! tagged_value {
|
|||||||
$( $(#[$meta] ) *$identifier( $ty ), )*
|
$( $(#[$meta] ) *$identifier( $ty ), )*
|
||||||
RenderOutput(RenderOutput),
|
RenderOutput(RenderOutput),
|
||||||
SurfaceFrame(SurfaceFrame),
|
SurfaceFrame(SurfaceFrame),
|
||||||
#[serde(skip)]
|
|
||||||
EditorApi(Arc<WasmEditorApi>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// We must manually implement hashing because some values are floats and so do not reproducibly hash (see FakeHash below)
|
// We must manually implement hashing because some values are floats and so do not reproducibly hash (see FakeHash below)
|
||||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||||
impl Hash for TaggedValue {
|
impl Hash for TaggedValue {
|
||||||
@@ -46,7 +45,6 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(x) => {x.hash(state)}),*
|
$( Self::$identifier(x) => {x.hash(state)}),*
|
||||||
Self::RenderOutput(x) => x.hash(state),
|
Self::RenderOutput(x) => x.hash(state),
|
||||||
Self::SurfaceFrame(x) => x.hash(state),
|
Self::SurfaceFrame(x) => x.hash(state),
|
||||||
Self::EditorApi(x) => x.hash(state),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +56,6 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(x) => Box::new(x), )*
|
$( Self::$identifier(x) => Box::new(x), )*
|
||||||
Self::RenderOutput(x) => Box::new(x),
|
Self::RenderOutput(x) => Box::new(x),
|
||||||
Self::SurfaceFrame(x) => Box::new(x),
|
Self::SurfaceFrame(x) => Box::new(x),
|
||||||
Self::EditorApi(x) => Box::new(x),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Converts to a Arc<dyn Any + Send + Sync + 'static>
|
/// Converts to a Arc<dyn Any + Send + Sync + 'static>
|
||||||
@@ -68,7 +65,6 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(x) => Arc::new(x), )*
|
$( Self::$identifier(x) => Arc::new(x), )*
|
||||||
Self::RenderOutput(x) => Arc::new(x),
|
Self::RenderOutput(x) => Arc::new(x),
|
||||||
Self::SurfaceFrame(x) => Arc::new(x),
|
Self::SurfaceFrame(x) => Arc::new(x),
|
||||||
Self::EditorApi(x) => Arc::new(x),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates a graphene_core::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
|
/// Creates a graphene_core::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
|
||||||
@@ -78,7 +74,6 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(_) => concrete!($ty), )*
|
$( Self::$identifier(_) => concrete!($ty), )*
|
||||||
Self::RenderOutput(_) => concrete!(RenderOutput),
|
Self::RenderOutput(_) => concrete!(RenderOutput),
|
||||||
Self::SurfaceFrame(_) => concrete!(SurfaceFrame),
|
Self::SurfaceFrame(_) => concrete!(SurfaceFrame),
|
||||||
Self::EditorApi(_) => concrete!(&WasmEditorApi)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Attempts to downcast the dynamic type to a tagged value
|
/// Attempts to downcast the dynamic type to a tagged value
|
||||||
@@ -115,7 +110,6 @@ macro_rules! tagged_value {
|
|||||||
$(TaggedValue::$identifier(value) => {any.downcast_ref::<$ty>().map_or(false, |v| v==value)}, )*
|
$(TaggedValue::$identifier(value) => {any.downcast_ref::<$ty>().map_or(false, |v| v==value)}, )*
|
||||||
TaggedValue::RenderOutput(value) => any.downcast_ref::<RenderOutput>().map_or(false, |v| v==value),
|
TaggedValue::RenderOutput(value) => any.downcast_ref::<RenderOutput>().map_or(false, |v| v==value),
|
||||||
TaggedValue::SurfaceFrame(value) => any.downcast_ref::<SurfaceFrame>().map_or(false, |v| v==value),
|
TaggedValue::SurfaceFrame(value) => any.downcast_ref::<SurfaceFrame>().map_or(false, |v| v==value),
|
||||||
TaggedValue::EditorApi(value) => any.downcast_ref::<Arc<WasmEditorApi>>().map_or(false, |v| v==value),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_type(input: &Type) -> Option<Self> {
|
pub fn from_type(input: &Type) -> Option<Self> {
|
||||||
@@ -258,6 +252,9 @@ tagged_value! {
|
|||||||
ReferencePoint(graphene_core::transform::ReferencePoint),
|
ReferencePoint(graphene_core::transform::ReferencePoint),
|
||||||
CentroidType(graphene_core::vector::misc::CentroidType),
|
CentroidType(graphene_core::vector::misc::CentroidType),
|
||||||
BooleanOperation(graphene_path_bool::BooleanOperation),
|
BooleanOperation(graphene_path_bool::BooleanOperation),
|
||||||
|
EditorMetadata(EditorMetadata),
|
||||||
|
#[serde(skip)]
|
||||||
|
ApplicationIo(Arc<WasmApplicationIoValue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaggedValue {
|
impl TaggedValue {
|
||||||
@@ -382,18 +379,6 @@ impl TaggedValue {
|
|||||||
_ => panic!("Passed value is not of type u32"),
|
_ => panic!("Passed value is not of type u32"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_renderable<'a>(value: &'a TaggedValue) -> Option<&'a dyn graphene_svg_renderer::GraphicElementRendered> {
|
|
||||||
match value {
|
|
||||||
TaggedValue::VectorData(v) => Some(v),
|
|
||||||
TaggedValue::RasterData(r) => Some(r),
|
|
||||||
TaggedValue::GraphicElement(e) => Some(e),
|
|
||||||
TaggedValue::GraphicGroup(g) => Some(g),
|
|
||||||
TaggedValue::ArtboardGroup(a) => Some(a),
|
|
||||||
TaggedValue::Artboard(a) => Some(a),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for TaggedValue {
|
impl Display for TaggedValue {
|
||||||
@@ -441,6 +426,8 @@ impl<T: AsRef<U> + Sync + Send, U: Sync + Send> UpcastAsRefNode<T, U> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct RenderOutput {
|
pub struct RenderOutput {
|
||||||
pub data: RenderOutputType,
|
pub data: RenderOutputType,
|
||||||
@@ -460,6 +447,47 @@ impl Hash for RenderOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for RenderOutput {
|
||||||
|
fn default() -> Self {
|
||||||
|
RenderOutput {
|
||||||
|
data: RenderOutputType::Image(Vec::new()),
|
||||||
|
metadata: RenderMetadata::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// We hash the floats and so-forth despite it not being reproducible because all inputs to the node graph must be hashed otherwise the graph execution breaks (so sorry about this hack)
|
/// We hash the floats and so-forth despite it not being reproducible because all inputs to the node graph must be hashed otherwise the graph execution breaks (so sorry about this hack)
|
||||||
trait FakeHash {
|
trait FakeHash {
|
||||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H);
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H);
|
||||||
@@ -509,3 +537,47 @@ mod fake_hash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! thumbnail_render {
|
||||||
|
( $( $ty:ty ),* $(,)? ) => {
|
||||||
|
pub fn render_thumbnail_if_change(new_value: &Arc<dyn std::any::Any + Send + Sync>, old_value: Option<&Arc<dyn std::any::Any + Send + Sync>>) -> ThumbnailRenderResult {
|
||||||
|
$(
|
||||||
|
if let Some(new_value) = new_value.downcast_ref::<$ty>() {
|
||||||
|
match old_value {
|
||||||
|
None => return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail()),
|
||||||
|
Some(old_value) => {
|
||||||
|
if let Some(old_value) = old_value.downcast_ref::<$ty>() {
|
||||||
|
match new_value == old_value {
|
||||||
|
true => return ThumbnailRenderResult::NoChange,
|
||||||
|
false => return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
return ThumbnailRenderResult::ClearThumbnail;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnail_render! {
|
||||||
|
graphene_core::GraphicGroupTable,
|
||||||
|
graphene_core::vector::VectorDataTable,
|
||||||
|
graphene_core::Artboard,
|
||||||
|
graphene_core::ArtboardGroupTable,
|
||||||
|
graphene_core::raster_types::RasterDataTable<CPU>,
|
||||||
|
graphene_core::raster_types::RasterDataTable<GPU>,
|
||||||
|
graphene_core::GraphicElement,
|
||||||
|
Option<Color>,
|
||||||
|
Vec<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ThumbnailRenderResult {
|
||||||
|
NoChange,
|
||||||
|
// Cleared if there is an error or the data could not be rendered
|
||||||
|
ClearThumbnail,
|
||||||
|
UpdateThumbnail(String),
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::document::{InlineRust, value};
|
use crate::document::{AbsoluteInputConnector, InlineRust, ProtonodeEntry, value};
|
||||||
pub use graphene_core::registry::*;
|
pub use graphene_core::registry::*;
|
||||||
use graphene_core::uuid::{NodeId, ProtonodePath, SNI};
|
use graphene_core::uuid::{NodeId, ProtonodePath, SNI};
|
||||||
use graphene_core::*;
|
use graphene_core::*;
|
||||||
@@ -6,18 +6,43 @@ use std::borrow::Cow;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
// #[derive(Debug, Default, PartialEq, Clone, Hash, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Default)]
|
||||||
// /// A list of [`ProtoNode`]s, which is an intermediate step between the [`crate::document::NodeNetwork`] and the `BorrowTree` containing a single flattened network.
|
/// A list of [`ProtoNode`]s, which is an intermediate step between the [`crate::document::NodeNetwork`] and the `BorrowTree` containing a single flattened network.
|
||||||
// pub struct ProtoNetwork {
|
pub struct ProtoNetwork {
|
||||||
// // TODO: remove this since it seems to be unused?
|
/// A list of nodes stored in a Vec to allow for sorting.
|
||||||
// // Should a proto Network even allow inputs? Don't think so
|
nodes: Vec<ProtonodeEntry>,
|
||||||
// pub inputs: Vec<NodeId>,
|
/// The most downstream node in the protonetwork
|
||||||
// /// The node ID that provides the output. This node is then responsible for calling the rest of the graph.
|
pub output: NodeId,
|
||||||
// pub output: NodeId,
|
}
|
||||||
// /// A list of nodes stored in a Vec to allow for sorting.
|
|
||||||
// pub nodes: Vec<(NodeId, ProtoNode)>,
|
impl ProtoNetwork {
|
||||||
// }
|
pub fn from_vec(nodes: Vec<ProtonodeEntry>) -> Self {
|
||||||
|
let last_entry = nodes.last().expect("Cannot compile empty protonetwork");
|
||||||
|
let output = match last_entry {
|
||||||
|
ProtonodeEntry::Protonode(proto_node) => proto_node.stable_node_id,
|
||||||
|
ProtonodeEntry::Deduplicated(deduplicated_index) => {
|
||||||
|
let ProtonodeEntry::Protonode(protonode) = &nodes[*deduplicated_index] else {
|
||||||
|
panic!("Deduplicated protonode must point to valid protonode");
|
||||||
|
};
|
||||||
|
protonode.stable_node_id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ProtoNetwork { nodes, output }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes(&self) -> impl Iterator<Item = &ProtoNode> {
|
||||||
|
self.nodes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry| if let ProtonodeEntry::Protonode(protonode) = entry { Some(protonode) } else { None })
|
||||||
|
}
|
||||||
|
pub fn into_nodes(self) -> impl Iterator<Item = ProtoNode> {
|
||||||
|
self.nodes
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|entry| if let ProtonodeEntry::Protonode(protonode) = entry { Some(protonode) } else { None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// impl core::fmt::Display for ProtoNetwork {
|
// impl core::fmt::Display for ProtoNetwork {
|
||||||
// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
@@ -69,59 +94,57 @@ use std::hash::Hash;
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct UpstreamInputMetadata {
|
||||||
|
pub input_sni: SNI,
|
||||||
|
// Context dependencies are accumulated during compilation, then replaced with whatever needs to be nullified
|
||||||
|
// If None, then the upstream node is a value node, so replace with an empty vec
|
||||||
|
pub context_dependencies: Option<Vec<ContextDependency>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct NodeConstructionArgs {
|
pub struct NodeConstructionArgs {
|
||||||
// Used to get the constructor from the function in `node_registry.rs`.
|
// Used to get the constructor from the function in `node_registry.rs`.
|
||||||
pub identifier: ProtoNodeIdentifier,
|
pub identifier: ProtoNodeIdentifier,
|
||||||
/// A list of stable node ids used as inputs to the constructor
|
/// A list of stable node ids used as inputs to the constructor
|
||||||
pub inputs: Vec<SNI>,
|
// A node is dependent on whatever is marked in its implementation, as well as all inputs
|
||||||
|
// If a node is dependent on more than its input, then a context nullification node is placed on the input
|
||||||
|
// Starts as None, and is populated during stable node id generation
|
||||||
|
pub inputs: Vec<Option<UpstreamInputMetadata>>,
|
||||||
|
// The union of all input context dependencies and the nodes context dependency. Used to generate the context nullification for the editor entry point
|
||||||
|
pub context_dependencies: Vec<ContextDependency>,
|
||||||
|
// Stores the path of document nodes which correspond to it
|
||||||
|
pub node_paths: Vec<ProtonodePath>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NodeValueArgs {
|
||||||
|
/// A value of a type that is known, allowing serialization (serde::Deserialize is not object safe)
|
||||||
|
/// Also stores its caller inputs, which is used to map the rendered thumbnail to the wire input
|
||||||
|
pub value: MemoHash<value::TaggedValue>,
|
||||||
|
// Stores all absolute input connectors which correspond to this value.
|
||||||
|
pub connector_paths: Vec<AbsoluteInputConnector>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
/// Defines the arguments used to construct the boxed node struct. This is used to call the constructor function in the `node_registry.rs` file - which is hidden behind a wall of macros.
|
/// Defines the arguments used to construct the boxed node struct. This is used to call the constructor function in the `node_registry.rs` file - which is hidden behind a wall of macros.
|
||||||
pub enum ConstructionArgs {
|
pub enum ConstructionArgs {
|
||||||
/// A value of a type that is known, allowing serialization (serde::Deserialize is not object safe)
|
Value(NodeValueArgs),
|
||||||
Value(MemoHash<value::TaggedValue>),
|
|
||||||
Nodes(NodeConstructionArgs),
|
Nodes(NodeConstructionArgs),
|
||||||
/// Used for GPU computation to work around the limitations of rust-gpu.
|
/// Used for GPU computation to work around the limitations of rust-gpu.
|
||||||
Inline(InlineRust),
|
Inline(InlineRust),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for ConstructionArgs {}
|
// impl ConstructionArgs {
|
||||||
|
// // TODO: what? Used in the gpu_compiler crate for something.
|
||||||
impl Hash for ConstructionArgs {
|
// pub fn new_function_args(&self) -> Vec<String> {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
// match self {
|
||||||
core::mem::discriminant(self).hash(state);
|
// ConstructionArgs::Nodes(nodes) => nodes.inputs.iter().map(|n| format!("n{:0x}", n.0)).collect(),
|
||||||
match self {
|
// ConstructionArgs::Value(value) => vec![value.to_primitive_string()],
|
||||||
Self::Nodes(nodes) => {
|
// ConstructionArgs::Inline(inline) => vec![inline.expr.clone()],
|
||||||
for node in &nodes.inputs {
|
// }
|
||||||
node.hash(state);
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
Self::Value(value) => value.hash(state),
|
|
||||||
Self::Inline(inline) => inline.hash(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConstructionArgs {
|
|
||||||
// TODO: what? Used in the gpu_compiler crate for something.
|
|
||||||
pub fn new_function_args(&self) -> Vec<String> {
|
|
||||||
match self {
|
|
||||||
ConstructionArgs::Nodes(nodes) => nodes.inputs.iter().map(|n| format!("n{:0x}", n.0)).collect(),
|
|
||||||
ConstructionArgs::Value(value) => vec![value.to_primitive_string()],
|
|
||||||
ConstructionArgs::Inline(inline) => vec![inline.expr.clone()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct OriginalLocation {
|
|
||||||
/// The original location to the document node - e.g. [grandparent_id, parent_id, node_id].
|
|
||||||
pub protonode_path: ProtonodePath,
|
|
||||||
// // Types should not be sent for autogenerated nodes or value nodes, which are not visible and inserted during compilation
|
|
||||||
pub send_types_to_editor: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// A proto node is an intermediate step between the `DocumentNode` and the boxed struct that actually runs the node (found in the [`BorrowTree`]).
|
/// A proto node is an intermediate step between the `DocumentNode` and the boxed struct that actually runs the node (found in the [`BorrowTree`]).
|
||||||
@@ -130,43 +153,61 @@ pub struct OriginalLocation {
|
|||||||
pub struct ProtoNode {
|
pub struct ProtoNode {
|
||||||
pub construction_args: ConstructionArgs,
|
pub construction_args: ConstructionArgs,
|
||||||
pub input: Type,
|
pub input: Type,
|
||||||
pub original_location: OriginalLocation,
|
|
||||||
pub stable_node_id: SNI,
|
pub stable_node_id: SNI,
|
||||||
|
// Each protonode stores the path and input index of the protonodes which called it
|
||||||
|
pub callers: Vec<(ProtonodePath, usize)>,
|
||||||
|
// Each protonode will finally store a single caller (the minimum of all callers), used by the editor
|
||||||
|
pub caller: Option<(ProtonodePath, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProtoNode {
|
impl Default for ProtoNode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(0).into()),
|
construction_args: ConstructionArgs::Value(NodeValueArgs {
|
||||||
|
value: value::TaggedValue::U32(0).into(),
|
||||||
|
connector_paths: Vec::new(),
|
||||||
|
}),
|
||||||
input: concrete!(Context),
|
input: concrete!(Context),
|
||||||
original_location: Default::default(),
|
|
||||||
stable_node_id: NodeId(0),
|
stable_node_id: NodeId(0),
|
||||||
|
callers: Vec::new(),
|
||||||
|
caller: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProtoNode {
|
impl ProtoNode {
|
||||||
/// Construct a new [`ProtoNode`] with the specified construction args and a `ClonedNode` implementation.
|
/// Construct a new [`ProtoNode`] with the specified construction args and a `ClonedNode` implementation.
|
||||||
pub fn value(value: ConstructionArgs, path: Vec<NodeId>, stable_node_id: SNI) -> Self {
|
pub fn value(value: ConstructionArgs, stable_node_id: SNI) -> Self {
|
||||||
let inputs_exposed = match &value {
|
|
||||||
ConstructionArgs::Nodes(nodes) => nodes.inputs.len() + 1,
|
|
||||||
_ => 2,
|
|
||||||
};
|
|
||||||
Self {
|
Self {
|
||||||
construction_args: value,
|
construction_args: value,
|
||||||
input: concrete!(Context),
|
input: concrete!(Context),
|
||||||
original_location: OriginalLocation {
|
|
||||||
protonode_path: path.into(),
|
|
||||||
send_types_to_editor: false,
|
|
||||||
},
|
|
||||||
stable_node_id,
|
stable_node_id,
|
||||||
|
callers: Vec::new(),
|
||||||
|
caller: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hashes the inputs and implementation of non value nodes, and the value for value nodes
|
||||||
|
pub fn generate_stable_node_id(&mut self) {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
let mut hasher = rustc_hash::FxHasher::default();
|
||||||
|
match &self.construction_args {
|
||||||
|
ConstructionArgs::Nodes(nodes) => {
|
||||||
|
for upstream_input in &nodes.inputs {
|
||||||
|
upstream_input.as_ref().unwrap().input_sni.hash(&mut hasher);
|
||||||
|
}
|
||||||
|
nodes.identifier.hash(&mut hasher);
|
||||||
|
}
|
||||||
|
ConstructionArgs::Value(value) => value.value.hash(&mut hasher),
|
||||||
|
ConstructionArgs::Inline(_) => todo!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stable_node_id = NodeId(hasher.finish());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum GraphErrorType {
|
pub enum GraphErrorType {
|
||||||
NodeNotFound(NodeId),
|
|
||||||
InputNodeNotFound(NodeId),
|
InputNodeNotFound(NodeId),
|
||||||
UnexpectedGenerics { index: usize, inputs: Vec<Type> },
|
UnexpectedGenerics { index: usize, inputs: Vec<Type> },
|
||||||
NoImplementations,
|
NoImplementations,
|
||||||
@@ -178,7 +219,6 @@ impl Debug for GraphErrorType {
|
|||||||
// TODO: format with the document graph context so the input index is the same as in the graph UI.
|
// TODO: format with the document graph context so the input index is the same as in the graph UI.
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
GraphErrorType::NodeNotFound(id) => write!(f, "Input node {id} is not present in the typing context"),
|
|
||||||
GraphErrorType::InputNodeNotFound(id) => write!(f, "Input node {id} is not present in the typing context"),
|
GraphErrorType::InputNodeNotFound(id) => write!(f, "Input node {id} is not present in the typing context"),
|
||||||
GraphErrorType::UnexpectedGenerics { index, inputs } => write!(f, "Generic inputs should not exist but found at {index}: {inputs:?}"),
|
GraphErrorType::UnexpectedGenerics { index, inputs } => write!(f, "Generic inputs should not exist but found at {index}: {inputs:?}"),
|
||||||
GraphErrorType::NoImplementations => write!(f, "No implementations found"),
|
GraphErrorType::NoImplementations => write!(f, "No implementations found"),
|
||||||
@@ -222,7 +262,7 @@ impl Debug for GraphErrorType {
|
|||||||
}
|
}
|
||||||
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct GraphError {
|
pub struct GraphError {
|
||||||
pub node_path: Vec<NodeId>,
|
pub stable_node_id: SNI,
|
||||||
pub identifier: Cow<'static, str>,
|
pub identifier: Cow<'static, str>,
|
||||||
pub error: GraphErrorType,
|
pub error: GraphErrorType,
|
||||||
}
|
}
|
||||||
@@ -231,11 +271,11 @@ impl GraphError {
|
|||||||
let identifier = match &node.construction_args {
|
let identifier = match &node.construction_args {
|
||||||
ConstructionArgs::Nodes(node_construction_args) => node_construction_args.identifier.name.clone(),
|
ConstructionArgs::Nodes(node_construction_args) => node_construction_args.identifier.name.clone(),
|
||||||
// Values are inserted into upcast nodes
|
// Values are inserted into upcast nodes
|
||||||
ConstructionArgs::Value(memo_hash) => "Value Node".into(),
|
ConstructionArgs::Value(node_value_args) => format!("{:?} Value Node", node_value_args.value.deref().ty()).into(),
|
||||||
ConstructionArgs::Inline(inline_rust) => "Inline".into(),
|
ConstructionArgs::Inline(_) => "Inline".into(),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
node_path: node.original_location.protonode_path.to_vec(),
|
stable_node_id: node.stable_node_id,
|
||||||
identifier,
|
identifier,
|
||||||
error: text.into(),
|
error: text.into(),
|
||||||
}
|
}
|
||||||
@@ -243,11 +283,7 @@ impl GraphError {
|
|||||||
}
|
}
|
||||||
impl Debug for GraphError {
|
impl Debug for GraphError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("NodeGraphError")
|
f.debug_struct("NodeGraphError").field("identifier", &self.identifier.to_string()).field("error", &self.error).finish()
|
||||||
.field("path", &self.node_path.iter().map(|id| id.0).collect::<Vec<_>>())
|
|
||||||
.field("identifier", &self.identifier.to_string())
|
|
||||||
.field("error", &self.error)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub type GraphErrors = Vec<GraphError>;
|
pub type GraphErrors = Vec<GraphError>;
|
||||||
@@ -256,17 +292,17 @@ pub type GraphErrors = Vec<GraphError>;
|
|||||||
#[derive(Default, Clone, dyn_any::DynAny)]
|
#[derive(Default, Clone, dyn_any::DynAny)]
|
||||||
pub struct TypingContext {
|
pub struct TypingContext {
|
||||||
lookup: Cow<'static, HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>>,
|
lookup: Cow<'static, HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>>,
|
||||||
monitor_lookup: Cow<'static, HashMap<Type, MonitorConstructor>>,
|
cache_lookup: Cow<'static, HashMap<Type, CacheConstructor>>,
|
||||||
inferred: HashMap<NodeId, NodeIOTypes>,
|
inferred: HashMap<NodeId, NodeIOTypes>,
|
||||||
constructor: HashMap<NodeId, NodeConstructor>,
|
constructor: HashMap<NodeId, NodeConstructor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypingContext {
|
impl TypingContext {
|
||||||
/// Creates a new `TypingContext` with the given lookup table.
|
/// Creates a new `TypingContext` with the given lookup table.
|
||||||
pub fn new(lookup: &'static HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>, monitor_lookup: &'static HashMap<Type, MonitorConstructor>) -> Self {
|
pub fn new(lookup: &'static HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>, cache_lookup: &'static HashMap<Type, CacheConstructor>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
lookup: Cow::Borrowed(lookup),
|
lookup: Cow::Borrowed(lookup),
|
||||||
monitor_lookup: Cow::Borrowed(monitor_lookup),
|
cache_lookup: Cow::Borrowed(cache_lookup),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,9 +310,9 @@ impl TypingContext {
|
|||||||
/// Updates the `TypingContext` with a given proto network. This will infer the types of the nodes
|
/// Updates the `TypingContext` with a given proto network. This will infer the types of the nodes
|
||||||
/// and store them in the `inferred` field. The proto network has to be topologically sorted
|
/// and store them in the `inferred` field. The proto network has to be topologically sorted
|
||||||
/// and contain fully resolved stable node ids.
|
/// and contain fully resolved stable node ids.
|
||||||
pub fn update(&mut self, network: &Vec<ProtoNode>) -> Result<(), GraphErrors> {
|
pub fn update(&mut self, network: &ProtoNetwork) -> Result<(), GraphErrors> {
|
||||||
// Update types from the most upstream nodes first
|
// Update types from the most upstream nodes first
|
||||||
for node in network.iter().rev() {
|
for node in network.nodes() {
|
||||||
self.infer(node.stable_node_id, node)?;
|
self.infer(node.stable_node_id, node)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -292,9 +328,9 @@ impl TypingContext {
|
|||||||
self.constructor.get(&node_id).copied()
|
self.constructor.get(&node_id).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the monitor node constructor for a given type {
|
// Returns the cache node constructor for a given type {
|
||||||
pub fn monitor_constructor(&self, monitor_type: &Type) -> Option<MonitorConstructor> {
|
pub fn cache_constructor(&self, cache_type: &Type) -> Option<CacheConstructor> {
|
||||||
self.monitor_lookup.get(monitor_type).copied()
|
self.cache_lookup.get(cache_type).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type of a given node id if it exists
|
/// Returns the type of a given node id if it exists
|
||||||
@@ -314,7 +350,7 @@ impl TypingContext {
|
|||||||
ConstructionArgs::Value(ref v) => {
|
ConstructionArgs::Value(ref v) => {
|
||||||
// assert!(matches!(node.input, ProtoNodeInput::None) || matches!(node.input, ProtoNodeInput::ManualComposition(ref x) if x == &concrete!(Context)));
|
// assert!(matches!(node.input, ProtoNodeInput::None) || matches!(node.input, ProtoNodeInput::ManualComposition(ref x) if x == &concrete!(Context)));
|
||||||
// TODO: This should return a reference to the value
|
// TODO: This should return a reference to the value
|
||||||
let types = NodeIOTypes::new(concrete!(Context), Type::Future(Box::new(v.ty())), vec![]);
|
let types = NodeIOTypes::new(concrete!(Context), Type::Future(Box::new(v.value.ty())), vec![]);
|
||||||
self.inferred.insert(node_id, types.clone());
|
self.inferred.insert(node_id, types.clone());
|
||||||
return Ok(types);
|
return Ok(types);
|
||||||
}
|
}
|
||||||
@@ -323,10 +359,11 @@ impl TypingContext {
|
|||||||
let inputs = construction_args
|
let inputs = construction_args
|
||||||
.inputs
|
.inputs
|
||||||
.iter()
|
.iter()
|
||||||
|
.map(|id| id.as_ref().unwrap().input_sni)
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
self.inferred
|
self.inferred
|
||||||
.get(id)
|
.get(&id)
|
||||||
.ok_or_else(|| vec![GraphError::new(node, GraphErrorType::NodeNotFound(*id))])
|
.ok_or_else(|| vec![GraphError::new(node, GraphErrorType::InputNodeNotFound(id))])
|
||||||
.map(|node| node.ty())
|
.map(|node| node.ty())
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Type>, GraphErrors>>()?;
|
.collect::<Result<Vec<Type>, GraphErrors>>()?;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use graphene_application_io::{ApplicationError, ApplicationIo, ResourceFuture, SurfaceHandle, SurfaceId};
|
use graphene_application_io::{ApplicationError, ApplicationIo, ApplicationIoValue, ResourceFuture, SurfaceHandle, SurfaceId};
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use js_sys::{Object, Reflect};
|
use js_sys::{Object, Reflect};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
@@ -56,6 +57,8 @@ unsafe impl Sync for WindowWrapper {}
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
unsafe impl Send for WindowWrapper {}
|
unsafe impl Send for WindowWrapper {}
|
||||||
|
|
||||||
|
pub type WasmApplicationIoValue = ApplicationIoValue<WasmApplicationIo>;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct WasmApplicationIo {
|
pub struct WasmApplicationIo {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -156,20 +159,12 @@ unsafe impl StaticType for WasmApplicationIo {
|
|||||||
type Static = WasmApplicationIo;
|
type Static = WasmApplicationIo;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a WasmEditorApi> for &'a WasmApplicationIo {
|
|
||||||
fn from(editor_api: &'a WasmEditorApi) -> Self {
|
|
||||||
editor_api.application_io.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
impl<'a> From<&'a WasmApplicationIo> for &'a WgpuExecutor {
|
impl<'a> From<&'a WasmApplicationIo> for &'a WgpuExecutor {
|
||||||
fn from(app_io: &'a WasmApplicationIo) -> Self {
|
fn from(app_io: &'a WasmApplicationIo) -> Self {
|
||||||
app_io.gpu_executor.as_ref().unwrap()
|
app_io.gpu_executor.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type WasmEditorApi = graphene_application_io::EditorApi<WasmApplicationIo>;
|
|
||||||
|
|
||||||
impl ApplicationIo for WasmApplicationIo {
|
impl ApplicationIo for WasmApplicationIo {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
type Surface = HtmlCanvasElement;
|
type Surface = HtmlCanvasElement;
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use fern::colors::{Color, ColoredLevelConfig};
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
|
use graph_craft::document::value::EditorMetadata;
|
||||||
use graph_craft::document::*;
|
use graph_craft::document::*;
|
||||||
use graph_craft::graphene_compiler::{Compiler, Executor};
|
use graph_craft::graphene_compiler::{Compiler, Executor};
|
||||||
use graph_craft::proto::{ProtoNetwork, ProtoNode};
|
use graph_craft::proto::{ProtoNetwork, ProtoNode};
|
||||||
use graph_craft::util::load_network;
|
use graph_craft::util::load_network;
|
||||||
use graph_craft::wasm_application_io::EditorPreferences;
|
use graph_craft::wasm_application_io::{EditorPreferences, WasmApplicationIoValue};
|
||||||
use graphene_core::text::FontCache;
|
use graphene_core::text::FontCache;
|
||||||
use graphene_std::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
|
use graphene_std::application_io::{ApplicationIo, ApplicationIoValue, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
|
||||||
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
|
use graphene_std::wasm_application_io::WasmApplicationIo;
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
use interpreted_executor::util::wrap_network_in_scope;
|
use interpreted_executor::util::wrap_network_in_scope;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@@ -92,14 +93,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
use_vello: true,
|
use_vello: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let editor_api = Arc::new(WasmEditorApi {
|
let application_io = Arc::new(ApplicationIoValue(Some(Arc::new(application_io))));
|
||||||
font_cache: FontCache::default(),
|
|
||||||
application_io: Some(application_io.into()),
|
|
||||||
node_graph_message_sender: Box::new(UpdateLogger {}),
|
|
||||||
editor_preferences: Box::new(preferences),
|
|
||||||
});
|
|
||||||
|
|
||||||
let proto_graph = compile_graph(document_string, editor_api)?;
|
let proto_graph = compile_graph(document_string, application_io)?;
|
||||||
|
|
||||||
match app.command {
|
match app.command {
|
||||||
Command::Compile { print_proto, .. } => {
|
Command::Compile { print_proto, .. } => {
|
||||||
@@ -180,17 +176,16 @@ fn fix_nodes(network: &mut NodeNetwork) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn compile_graph(document_string: String, editor_api: Arc<WasmEditorApi>) -> Result<Vec<ProtoNode>, Box<dyn Error>> {
|
fn compile_graph(document_string: String, application_io: Arc<WasmApplicationIoValue>) -> Result<ProtoNetwork, Box<dyn Error>> {
|
||||||
let mut network = load_network(&document_string);
|
let mut network = load_network(&document_string);
|
||||||
fix_nodes(&mut network);
|
fix_nodes(&mut network);
|
||||||
|
|
||||||
let substitutions = preprocessor::generate_node_substitutions();
|
let substitutions: std::collections::HashMap<String, DocumentNode> = preprocessor::generate_node_substitutions();
|
||||||
preprocessor::expand_network(&mut network, &substitutions);
|
preprocessor::expand_network(&mut network, &substitutions);
|
||||||
|
|
||||||
let mut wrapped_network = wrap_network_in_scope(network.clone(), editor_api);
|
let mut wrapped_network = wrap_network_in_scope(network, Arc::new(FontCache::default()), EditorMetadata::default(), application_io);
|
||||||
|
|
||||||
let compiler = Compiler {};
|
wrapped_network.flatten().map(|result| result.0).map_err(|x| x.into())
|
||||||
wrapped_network.flatten().map(|result|result.0).map_err(|x| x.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
|
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
|
use glam::DAffine2;
|
||||||
pub use graph_craft::proto::{Any, NodeContainer, TypeErasedBox, TypeErasedNode};
|
pub use graph_craft::proto::{Any, NodeContainer, TypeErasedBox, TypeErasedNode};
|
||||||
use graph_craft::proto::{DynFuture, FutureAny, SharedNodeContainer};
|
use graph_craft::proto::{DynFuture, FutureAny, SharedNodeContainer};
|
||||||
|
use graphene_core::Context;
|
||||||
|
use graphene_core::ContextDependency;
|
||||||
use graphene_core::NodeIO;
|
use graphene_core::NodeIO;
|
||||||
|
use graphene_core::OwnedContextImpl;
|
||||||
use graphene_core::WasmNotSend;
|
use graphene_core::WasmNotSend;
|
||||||
pub use graphene_core::registry::{DowncastBothNode, DynAnyNode, FutureWrapperNode, PanicNode};
|
pub use graphene_core::registry::{DowncastBothNode, DynAnyNode, FutureWrapperNode, PanicNode};
|
||||||
|
use graphene_core::transform::Footprint;
|
||||||
pub use graphene_core::{Node, generic, ops};
|
pub use graphene_core::{Node, generic, ops};
|
||||||
|
|
||||||
pub trait IntoTypeErasedNode<'n> {
|
pub trait IntoTypeErasedNode<'n> {
|
||||||
@@ -46,3 +51,115 @@ pub fn input_node<O: StaticType>(n: SharedNodeContainer) -> DowncastBothNode<(),
|
|||||||
pub fn downcast_node<I: StaticType, O: StaticType>(n: SharedNodeContainer) -> DowncastBothNode<I, O> {
|
pub fn downcast_node<I: StaticType, O: StaticType>(n: SharedNodeContainer) -> DowncastBothNode<I, O> {
|
||||||
DowncastBothNode::new(n)
|
DowncastBothNode::new(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EditorContextToContext {
|
||||||
|
first: SharedNodeContainer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> Node<'i, Any<'i>> for EditorContextToContext {
|
||||||
|
type Output = DynFuture<'i, Any<'i>>;
|
||||||
|
fn eval(&'i self, input: Any<'i>) -> Self::Output {
|
||||||
|
Box::pin(async move {
|
||||||
|
let editor_context = dyn_any::downcast::<EditorContext>(input).unwrap();
|
||||||
|
log::debug!("evaluating with context: {:?}", editor_context.to_context());
|
||||||
|
self.first.eval(Box::new(editor_context.to_context())).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditorContextToContext {
|
||||||
|
pub const fn new(first: SharedNodeContainer) -> Self {
|
||||||
|
EditorContextToContext { first }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct EditorContext {
|
||||||
|
pub footprint: Option<Footprint>,
|
||||||
|
pub downstream_transform: Option<DAffine2>,
|
||||||
|
pub real_time: Option<f64>,
|
||||||
|
pub animation_time: Option<f64>,
|
||||||
|
pub index: Option<usize>,
|
||||||
|
// #[serde(skip)]
|
||||||
|
// pub editor_var_args: Option<(Vec<String>, Vec<Arc<Box<[dyn std::any::Any + 'static + std::panic::UnwindSafe]>>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl StaticType for EditorContext {
|
||||||
|
type Static = EditorContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Default for EditorContext {
|
||||||
|
// fn default() -> Self {
|
||||||
|
// EditorContext {
|
||||||
|
// footprint: None,
|
||||||
|
// downstream_transform: None,
|
||||||
|
// real_time: None,
|
||||||
|
// animation_time: None,
|
||||||
|
// index: None,
|
||||||
|
// // editor_var_args: None,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl EditorContext {
|
||||||
|
pub fn to_context(&self) -> Context {
|
||||||
|
let mut context = OwnedContextImpl::default();
|
||||||
|
if let Some(footprint) = self.footprint {
|
||||||
|
context.set_footprint(footprint);
|
||||||
|
}
|
||||||
|
if let Some(footprint) = self.footprint {
|
||||||
|
context.set_footprint(footprint);
|
||||||
|
}
|
||||||
|
// if let Some(downstream_transform) = self.downstream_transform {
|
||||||
|
// context.set_downstream_transform(downstream_transform);
|
||||||
|
// }
|
||||||
|
if let Some(real_time) = self.real_time {
|
||||||
|
context.set_real_time(real_time);
|
||||||
|
}
|
||||||
|
if let Some(animation_time) = self.animation_time {
|
||||||
|
context.set_animation_time(animation_time);
|
||||||
|
}
|
||||||
|
if let Some(index) = self.index {
|
||||||
|
context.set_index(index);
|
||||||
|
}
|
||||||
|
// if let Some(editor_var_args) = self.editor_var_args {
|
||||||
|
// let (variable_names, values)
|
||||||
|
// context.set_varargs((variable_names, values))
|
||||||
|
// }
|
||||||
|
context.into_context()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NullificationNode {
|
||||||
|
first: SharedNodeContainer,
|
||||||
|
nullify: Vec<ContextDependency>,
|
||||||
|
}
|
||||||
|
impl<'i> Node<'i, Any<'i>> for NullificationNode {
|
||||||
|
type Output = DynFuture<'i, Any<'i>>;
|
||||||
|
|
||||||
|
fn eval(&'i self, input: Any<'i>) -> Self::Output {
|
||||||
|
let new_input = match dyn_any::try_downcast::<Context>(input) {
|
||||||
|
Ok(context) => match *context {
|
||||||
|
Some(context) => {
|
||||||
|
log::debug!("Nullifying inputs: {:?}", self.nullify);
|
||||||
|
let mut new_context = OwnedContextImpl::from(context);
|
||||||
|
new_context.nullify(&self.nullify);
|
||||||
|
Box::new(new_context.into_context()) as Any<'i>
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let none: Context = None;
|
||||||
|
Box::new(none) as Any<'i>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(other_input) => other_input,
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::pin(async move { self.first.eval(new_input).await })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NullificationNode {
|
||||||
|
pub fn new(first: SharedNodeContainer, nullify: Vec<ContextDependency>) -> Self {
|
||||||
|
Self { first, nullify }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use crate::vector::VectorDataTable;
|
use crate::vector::{VectorData, VectorDataTable};
|
||||||
use graph_craft::wasm_application_io::WasmEditorApi;
|
|
||||||
use graphene_core::Ctx;
|
use graphene_core::Ctx;
|
||||||
pub use graphene_core::text::*;
|
pub use graphene_core::text::*;
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
fn text<'i: 'n>(
|
fn text<'i: 'n>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
editor: &'i WasmEditorApi,
|
font_cache: std::sync::Arc<FontCache>,
|
||||||
text: String,
|
text: String,
|
||||||
font_name: Font,
|
font_name: Font,
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
@@ -41,7 +40,7 @@ fn text<'i: 'n>(
|
|||||||
tilt,
|
tilt,
|
||||||
};
|
};
|
||||||
|
|
||||||
let font_data = editor.font_cache.get(&font_name).map(|f| load_font(f));
|
let font_data = font_cache.get(&font_name).map(|f| load_font(f));
|
||||||
|
|
||||||
to_path(&text, font_data, typesetting, per_glyph_instances)
|
to_path(&text, font_data, typesetting, per_glyph_instances)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
use graph_craft::document::value::RenderOutput;
|
|
||||||
pub use graph_craft::document::value::RenderOutputType;
|
pub use graph_craft::document::value::RenderOutputType;
|
||||||
|
use graph_craft::document::value::{EditorMetadata, RenderOutput};
|
||||||
pub use graph_craft::wasm_application_io::*;
|
pub use graph_craft::wasm_application_io::*;
|
||||||
use graphene_application_io::{ApplicationIo, ExportFormat, RenderConfig};
|
use graphene_application_io::ApplicationIo;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use graphene_core::instances::Instances;
|
use graphene_core::instances::Instances;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use graphene_core::math::bbox::Bbox;
|
use graphene_core::math::bbox::Bbox;
|
||||||
use graphene_core::raster::image::Image;
|
use graphene_core::raster::image::Image;
|
||||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
use graphene_core::raster_types::{CPU, GPU, Raster, RasterDataTable};
|
||||||
use graphene_core::transform::Footprint;
|
use graphene_core::transform::Footprint;
|
||||||
use graphene_core::vector::VectorDataTable;
|
use graphene_core::vector::VectorDataTable;
|
||||||
use graphene_core::{Color, Context, Ctx, ExtractFootprint, GraphicGroupTable, OwnedContextImpl, WasmNotSend};
|
use graphene_core::{Color, Context, Ctx, ExtractFootprint, GraphicGroupTable, WasmNotSend};
|
||||||
use graphene_svg_renderer::RenderMetadata;
|
use graphene_svg_renderer::RenderMetadata;
|
||||||
use graphene_svg_renderer::{GraphicElementRendered, RenderParams, RenderSvgSegmentList, SvgRender, format_transform_matrix};
|
use graphene_svg_renderer::{GraphicElementRendered, RenderParams, RenderSvgSegmentList, SvgRender, format_transform_matrix};
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@ use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
|
|||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
#[node_macro::node(category("Debug: GPU"))]
|
#[node_macro::node(category("Debug: GPU"))]
|
||||||
async fn create_surface<'a: 'n>(_: impl Ctx, editor: &'a WasmEditorApi) -> Arc<WasmSurfaceHandle> {
|
async fn create_surface<'a: 'n>(_: impl Ctx, application_io: WasmApplicationIoValue) -> Arc<WasmSurfaceHandle> {
|
||||||
Arc::new(editor.application_io.as_ref().unwrap().create_window())
|
Arc::new(application_io.0.as_ref().unwrap().create_window())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fix and reenable in order to get the 'Draw Canvas' node working again.
|
// TODO: Fix and reenable in order to get the 'Draw Canvas' node working again.
|
||||||
@@ -59,20 +59,20 @@ async fn create_surface<'a: 'n>(_: impl Ctx, editor: &'a WasmEditorApi) -> Arc<W
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[node_macro::node(category("Web Request"))]
|
// #[node_macro::node(category("Web Request"))]
|
||||||
async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[scope("editor-api")] editor: &'a WasmEditorApi, #[name("URL")] url: String) -> Arc<[u8]> {
|
// async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[scope("editor-api")] editor: &'a WasmEditorApi, #[name("URL")] url: String) -> Arc<[u8]> {
|
||||||
let Some(api) = editor.application_io.as_ref() else {
|
// let Some(api) = editor.application_io.as_ref() else {
|
||||||
return Arc::from(include_bytes!("../../graph-craft/src/null.png").to_vec());
|
// return Arc::from(include_bytes!("../../graph-craft/src/null.png").to_vec());
|
||||||
};
|
// };
|
||||||
let Ok(data) = api.load_resource(url) else {
|
// let Ok(data) = api.load_resource(url) else {
|
||||||
return Arc::from(include_bytes!("../../graph-craft/src/null.png").to_vec());
|
// return Arc::from(include_bytes!("../../graph-craft/src/null.png").to_vec());
|
||||||
};
|
// };
|
||||||
let Ok(data) = data.await else {
|
// let Ok(data) = data.await else {
|
||||||
return Arc::from(include_bytes!("../../graph-craft/src/null.png").to_vec());
|
// return Arc::from(include_bytes!("../../graph-craft/src/null.png").to_vec());
|
||||||
};
|
// };
|
||||||
|
|
||||||
data
|
// data
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[node_macro::node(category("Web Request"))]
|
#[node_macro::node(category("Web Request"))]
|
||||||
fn decode_image(_: impl Ctx, data: Arc<[u8]>) -> RasterDataTable<CPU> {
|
fn decode_image(_: impl Ctx, data: Arc<[u8]>) -> RasterDataTable<CPU> {
|
||||||
@@ -118,16 +118,16 @@ fn render_svg(data: impl GraphicElementRendered, mut render: SvgRender, render_p
|
|||||||
#[cfg(feature = "vello")]
|
#[cfg(feature = "vello")]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
|
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
|
||||||
async fn render_canvas(
|
async fn render_canvas(
|
||||||
render_config: RenderConfig,
|
footprint: Footprint,
|
||||||
|
hide_artboards: bool,
|
||||||
data: impl GraphicElementRendered,
|
data: impl GraphicElementRendered,
|
||||||
editor: &WasmEditorApi,
|
application_io: Arc<WasmApplicationIoValue>,
|
||||||
surface_handle: wgpu_executor::WgpuSurface,
|
surface_handle: wgpu_executor::WgpuSurface,
|
||||||
render_params: RenderParams,
|
render_params: RenderParams,
|
||||||
) -> RenderOutputType {
|
) -> RenderOutputType {
|
||||||
use graphene_application_io::SurfaceFrame;
|
use graphene_application_io::SurfaceFrame;
|
||||||
|
|
||||||
let footprint = render_config.viewport;
|
let Some(exec) = application_io.0.as_ref().unwrap().gpu_executor() else {
|
||||||
let Some(exec) = editor.application_io.as_ref().unwrap().gpu_executor() else {
|
|
||||||
unreachable!("Attempted to render with Vello when no GPU executor is available");
|
unreachable!("Attempted to render with Vello when no GPU executor is available");
|
||||||
};
|
};
|
||||||
use vello::*;
|
use vello::*;
|
||||||
@@ -142,7 +142,7 @@ async fn render_canvas(
|
|||||||
scene.append(&child, Some(kurbo::Affine::new(footprint.transform.to_cols_array())));
|
scene.append(&child, Some(kurbo::Affine::new(footprint.transform.to_cols_array())));
|
||||||
|
|
||||||
let mut background = Color::from_rgb8_srgb(0x22, 0x22, 0x22);
|
let mut background = Color::from_rgb8_srgb(0x22, 0x22, 0x22);
|
||||||
if !data.contains_artboard() && !render_config.hide_artboards {
|
if !data.contains_artboard() && !hide_artboards {
|
||||||
background = Color::WHITE;
|
background = Color::WHITE;
|
||||||
}
|
}
|
||||||
exec.render_vello_scene(&scene, &surface_handle, footprint.resolution.x, footprint.resolution.y, &context, background)
|
exec.render_vello_scene(&scene, &surface_handle, footprint.resolution.x, footprint.resolution.y, &context, background)
|
||||||
@@ -151,7 +151,7 @@ async fn render_canvas(
|
|||||||
|
|
||||||
let frame = SurfaceFrame {
|
let frame = SurfaceFrame {
|
||||||
surface_id: surface_handle.window_id,
|
surface_id: surface_handle.window_id,
|
||||||
resolution: render_config.viewport.resolution,
|
resolution: footprint.resolution,
|
||||||
transform: glam::DAffine2::IDENTITY,
|
transform: glam::DAffine2::IDENTITY,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -230,73 +230,61 @@ where
|
|||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
|
async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
|
||||||
render_config: RenderConfig,
|
context: impl Ctx + ExtractFootprint,
|
||||||
editor_api: impl Node<Context<'static>, Output = &'a WasmEditorApi>,
|
editor_metadata: EditorMetadata,
|
||||||
|
application_io: Arc<WasmApplicationIoValue>,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
Context -> VectorDataTable,
|
VectorDataTable,
|
||||||
Context -> RasterDataTable<CPU>,
|
RasterDataTable<CPU>,
|
||||||
Context -> GraphicGroupTable,
|
RasterDataTable<GPU>,
|
||||||
Context -> graphene_core::Artboard,
|
GraphicGroupTable,
|
||||||
Context -> graphene_core::ArtboardGroupTable,
|
graphene_core::Artboard,
|
||||||
Context -> Option<Color>,
|
graphene_core::ArtboardGroupTable,
|
||||||
Context -> Vec<Color>,
|
Option<Color>,
|
||||||
Context -> bool,
|
Vec<Color>,
|
||||||
Context -> f32,
|
bool,
|
||||||
Context -> f64,
|
f32,
|
||||||
Context -> String,
|
f64,
|
||||||
|
String,
|
||||||
)]
|
)]
|
||||||
data: impl Node<Context<'static>, Output = T>,
|
data: T,
|
||||||
_surface_handle: impl Node<Context<'static>, Output = Option<wgpu_executor::WgpuSurface>>,
|
_surface_handle: impl Node<Context<'static>, Output = Option<wgpu_executor::WgpuSurface>>,
|
||||||
) -> RenderOutput {
|
) -> RenderOutput {
|
||||||
let footprint = render_config.viewport;
|
let Some(footprint) = context.try_footprint().copied() else {
|
||||||
let ctx = OwnedContextImpl::default()
|
log::error!("Footprint must be Some when rendering");
|
||||||
.with_footprint(footprint)
|
return RenderOutput::default();
|
||||||
.with_real_time(render_config.time.time)
|
};
|
||||||
.with_animation_time(render_config.time.animation_time.as_secs_f64())
|
|
||||||
.into_context();
|
|
||||||
ctx.footprint();
|
|
||||||
|
|
||||||
let RenderConfig { hide_artboards, for_export, .. } = render_config;
|
|
||||||
let render_params = RenderParams {
|
let render_params = RenderParams {
|
||||||
view_mode: render_config.view_mode,
|
view_mode: editor_metadata.view_mode,
|
||||||
culling_bounds: None,
|
culling_bounds: None,
|
||||||
thumbnail: false,
|
thumbnail: false,
|
||||||
hide_artboards,
|
hide_artboards: editor_metadata.hide_artboards,
|
||||||
for_export,
|
for_export: editor_metadata.for_export,
|
||||||
for_mask: false,
|
for_mask: false,
|
||||||
alignment_parent_transform: None,
|
alignment_parent_transform: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = data.eval(ctx.clone()).await;
|
|
||||||
let editor_api = editor_api.eval(None).await;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "vello", not(test)))]
|
#[cfg(all(feature = "vello", not(test)))]
|
||||||
let surface_handle = _surface_handle.eval(None).await;
|
let surface_handle = _surface_handle.eval(None).await;
|
||||||
|
|
||||||
let use_vello = editor_api.editor_preferences.use_vello();
|
let use_vello = editor_metadata.use_vello;
|
||||||
#[cfg(all(feature = "vello", not(test)))]
|
#[cfg(all(feature = "vello", not(test)))]
|
||||||
let use_vello = use_vello && surface_handle.is_some();
|
let use_vello = use_vello && surface_handle.is_some();
|
||||||
|
|
||||||
let mut metadata = RenderMetadata::default();
|
let mut metadata = RenderMetadata::default();
|
||||||
data.collect_metadata(&mut metadata, footprint, None);
|
data.collect_metadata(&mut metadata, footprint, None);
|
||||||
|
|
||||||
let output_format = render_config.export_format;
|
let data = if use_vello {
|
||||||
let data = match output_format {
|
#[cfg(all(feature = "vello", not(test)))]
|
||||||
ExportFormat::Svg => render_svg(data, SvgRender::new(), render_params, footprint),
|
return RenderOutput {
|
||||||
ExportFormat::Canvas => {
|
data: render_canvas(footprint, editor_metadata.hide_artboards, data, application_io, surface_handle.unwrap(), render_params).await,
|
||||||
if use_vello && editor_api.application_io.as_ref().unwrap().gpu_executor().is_some() {
|
metadata,
|
||||||
#[cfg(all(feature = "vello", not(test)))]
|
};
|
||||||
return RenderOutput {
|
#[cfg(any(not(feature = "vello"), test))]
|
||||||
data: render_canvas(render_config, data, editor_api, surface_handle.unwrap(), render_params).await,
|
render_svg(data, SvgRender::new(), render_params, footprint)
|
||||||
metadata,
|
} else {
|
||||||
};
|
render_svg(data, SvgRender::new(), render_params, footprint)
|
||||||
#[cfg(any(not(feature = "vello"), test))]
|
|
||||||
render_svg(data, SvgRender::new(), render_params, footprint)
|
|
||||||
} else {
|
|
||||||
render_svg(data, SvgRender::new(), render_params, footprint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => todo!("Non-SVG render output for {output_format:?}"),
|
|
||||||
};
|
};
|
||||||
RenderOutput { data, metadata }
|
RenderOutput { data, metadata }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,30 @@ pub trait GraphicElementRendered: BoundingBox + RenderComplexity {
|
|||||||
#[cfg(feature = "vello")]
|
#[cfg(feature = "vello")]
|
||||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams);
|
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams);
|
||||||
|
|
||||||
|
fn render_thumbnail(&self) -> String {
|
||||||
|
let bounds = self.bounding_box(DAffine2::IDENTITY, true);
|
||||||
|
|
||||||
|
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();
|
||||||
|
self.render_svg(&mut render, &render_params);
|
||||||
|
|
||||||
|
// Give the SVG a viewbox and outer <svg>...</svg> wrapper tag
|
||||||
|
// let [min, max] = bounds.unwrap_or_default();
|
||||||
|
// render.format_svg(min, max);
|
||||||
|
|
||||||
|
render.svg.to_svg_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection.
|
/// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection.
|
||||||
fn add_upstream_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
|
fn add_upstream_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use interpreted_executor::dynamic_executor::DynamicExecutor;
|
|||||||
|
|
||||||
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
||||||
let mut network = load_from_name(name);
|
let mut network = load_from_name(name);
|
||||||
let proto_network = network.flatten().unwrap();
|
let proto_network = network.flatten().unwrap().0;
|
||||||
let executor = block_on(DynamicExecutor::new(proto_network.0)).unwrap();
|
let executor = block_on(DynamicExecutor::new(proto_network.0)).unwrap();
|
||||||
(executor, proto_network)
|
(executor, proto_network)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
use crate::node_registry::{MONITOR_NODES, NODE_REGISTRY};
|
use crate::node_registry::{CACHE_NODES, NODE_REGISTRY};
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::DAffine2;
|
use graph_craft::document::ProtonodeEntry;
|
||||||
use graph_craft::document::value::{TaggedValue, UpcastAsRefNode, UpcastNode};
|
use graph_craft::document::value::{TaggedValue, UpcastNode};
|
||||||
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext, downcast_node};
|
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNetwork, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext, UpstreamInputMetadata};
|
||||||
use graph_craft::proto::{GraphErrorType, GraphErrors};
|
use graph_craft::proto::{GraphErrorType, GraphErrors};
|
||||||
use graph_craft::{Type, concrete};
|
use graph_craft::{Type, concrete};
|
||||||
use graphene_std::application_io::{ExportFormat, RenderConfig, TimingInformation};
|
use graphene_std::any::{EditorContext, EditorContextToContext, NullificationNode};
|
||||||
use graphene_std::memo::{IntrospectMode, MonitorNode};
|
use graphene_std::memo::IntrospectMode;
|
||||||
use graphene_std::transform::Footprint;
|
|
||||||
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
|
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
|
||||||
use graphene_std::{NodeIOTypes, OwnedContextImpl};
|
use graphene_std::{Context, MemoHash};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::ptr::null;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
||||||
@@ -32,25 +32,24 @@ impl Default for DynamicExecutor {
|
|||||||
Self {
|
Self {
|
||||||
output: None,
|
output: None,
|
||||||
tree: Default::default(),
|
tree: Default::default(),
|
||||||
typing_context: TypingContext::new(&NODE_REGISTRY, &MONITOR_NODES),
|
typing_context: TypingContext::new(&NODE_REGISTRY, &CACHE_NODES),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicExecutor {
|
impl DynamicExecutor {
|
||||||
pub async fn new(proto_network: Vec<ProtoNode>) -> Result<Self, GraphErrors> {
|
pub async fn new(proto_network: ProtoNetwork) -> Result<Self, GraphErrors> {
|
||||||
let mut typing_context = TypingContext::default();
|
let mut typing_context = TypingContext::default();
|
||||||
typing_context.update(&proto_network)?;
|
typing_context.update(&proto_network)?;
|
||||||
let output = proto_network.get(0).map(|protonode| protonode.stable_node_id);
|
let output = Some(proto_network.output);
|
||||||
let tree = BorrowTree::new(proto_network, &typing_context).await?;
|
let tree = BorrowTree::new(proto_network, &typing_context).await?;
|
||||||
|
|
||||||
Ok(Self { tree, output, typing_context })
|
Ok(Self { tree, output, typing_context })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
||||||
#[cfg_attr(debug_assertions, inline(never))]
|
#[cfg_attr(debug_assertions, inline(never))]
|
||||||
pub async fn update(mut self, proto_network: Vec<ProtoNode>) -> Result<(Vec<(SNI, Vec<Type>)>, Vec<(SNI, usize)>), GraphErrors> {
|
pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<(Vec<(SNI, Vec<Type>)>, Vec<(SNI, usize)>), GraphErrors> {
|
||||||
self.output = proto_network.get(0).map(|protonode| protonode.stable_node_id);
|
self.output = Some(proto_network.output);
|
||||||
self.typing_context.update(&proto_network)?;
|
self.typing_context.update(&proto_network)?;
|
||||||
// A protonode id can change while having the same document path, and the path can change while having the same stable node id.
|
// A protonode id can change while having the same document path, and the path can change while having the same stable node id.
|
||||||
// Either way, the mapping of paths to ids and ids to paths has to be kept in sync.
|
// Either way, the mapping of paths to ids and ids to paths has to be kept in sync.
|
||||||
@@ -58,12 +57,9 @@ impl DynamicExecutor {
|
|||||||
let (add, orphaned_proto_nodes) = self.tree.update(proto_network, &self.typing_context).await?;
|
let (add, orphaned_proto_nodes) = self.tree.update(proto_network, &self.typing_context).await?;
|
||||||
let mut remove = Vec::new();
|
let mut remove = Vec::new();
|
||||||
for sni in orphaned_proto_nodes {
|
for sni in orphaned_proto_nodes {
|
||||||
let Some(types) = self.typing_context.type_of(sni) else {
|
if let Some(number_of_inputs) = self.tree.free_node(&sni) {
|
||||||
log::error!("Could not get type for protonode {sni} when removing");
|
remove.push((sni, number_of_inputs));
|
||||||
continue;
|
}
|
||||||
};
|
|
||||||
remove.push((sni, types.inputs.len()));
|
|
||||||
self.tree.free_node(&sni, types.inputs.len());
|
|
||||||
self.typing_context.remove_inference(&sni);
|
self.typing_context.remove_inference(&sni);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +67,6 @@ impl DynamicExecutor {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|sni| {
|
.filter_map(|sni| {
|
||||||
let Some(types) = self.typing_context.type_of(sni) else {
|
let Some(types) = self.typing_context.type_of(sni) else {
|
||||||
log::debug!("Could not get type for added node: {sni}");
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
Some((sni, types.inputs.clone()))
|
Some((sni, types.inputs.clone()))
|
||||||
@@ -82,24 +77,27 @@ impl DynamicExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Intospect the value for that specific protonode input, returning for example the cached value for a monitor node.
|
/// Intospect the value for that specific protonode input, returning for example the cached value for a monitor node.
|
||||||
pub fn introspect(&self, protonode_input: CompiledProtonodeInput, introspect_mode: IntrospectMode) -> Result<Box<dyn std::any::Any + Send + Sync>, IntrospectError> {
|
pub fn introspect(&self, protonode_input: CompiledProtonodeInput, introspect_mode: IntrospectMode) -> Result<Option<Arc<dyn std::any::Any + Send + Sync>>, IntrospectError> {
|
||||||
let node = self.get_monitor_node_container(protonode_input)?;
|
let node = self.get_introspect_node_container(protonode_input)?;
|
||||||
node.introspect(introspect_mode).ok_or(IntrospectError::IntrospectNotImplemented)
|
Ok(node.introspect(introspect_mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_introspect(&self, protonode_input: CompiledProtonodeInput, introspect_mode: IntrospectMode) {
|
pub fn set_introspect(&self, protonode_input: CompiledProtonodeInput, introspect_mode: IntrospectMode) {
|
||||||
let Ok(node) = self.get_monitor_node_container(protonode_input) else {
|
let Ok(node) = self.get_introspect_node_container(protonode_input) else {
|
||||||
log::error!("Could not get monitor node for input: {:?}", protonode_input);
|
log::error!("Could not get monitor node for input: {:?}", protonode_input);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
node.set_introspect(introspect_mode);
|
node.set_introspect(introspect_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_monitor_node_container(&self, protonode_input: CompiledProtonodeInput) -> Result<SharedNodeContainer, IntrospectError> {
|
pub fn get_introspect_node_container(&self, protonode_input: CompiledProtonodeInput) -> Result<SharedNodeContainer, IntrospectError> {
|
||||||
// The SNI of the monitor nodes are the ids of the protonode + input index
|
// The SNI of the monitor nodes are the ids of the protonode + input index
|
||||||
let monitor_node_id = NodeId(protonode_input.0.0 + protonode_input.1 as u64 + 1);
|
let inserted_node = self.tree.nodes.get(&protonode_input.0).ok_or(IntrospectError::ProtoNodeNotFound(protonode_input))?;
|
||||||
let inserted_node = self.tree.nodes.get(&monitor_node_id).ok_or(IntrospectError::ProtoNodeNotFound(monitor_node_id))?;
|
let node = inserted_node
|
||||||
Ok(inserted_node.clone())
|
.input_introspection_entrypoints
|
||||||
|
.get(protonode_input.1)
|
||||||
|
.ok_or(IntrospectError::InputIndexOutOfBounds(protonode_input))?;
|
||||||
|
Ok(node.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_type(&self) -> Option<Type> {
|
pub fn input_type(&self) -> Option<Type> {
|
||||||
@@ -118,15 +116,38 @@ impl DynamicExecutor {
|
|||||||
self.output.and_then(|output| self.typing_context.type_of(output).map(|node_io| node_io.return_value.clone()))
|
self.output.and_then(|output| self.typing_context.type_of(output).map(|node_io| node_io.return_value.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute<I>(&self, input: I) -> LocalFuture<'_, Result<TaggedValue, Box<dyn Error>>>
|
// If node to evaluate is None then the most downstream node is used
|
||||||
|
pub async fn evaluate_from_node(&self, editor_context: EditorContext, node_to_evaluate: Option<SNI>) -> Result<TaggedValue, String> {
|
||||||
|
let node_to_evaluate: NodeId = node_to_evaluate
|
||||||
|
.or_else(|| self.output)
|
||||||
|
.ok_or("Could not find output node when evaluating network. Has the network been compiled?")?;
|
||||||
|
let input_type = self
|
||||||
|
.typing_context
|
||||||
|
.type_of(node_to_evaluate)
|
||||||
|
.map(|node_io| node_io.call_argument.clone())
|
||||||
|
.ok_or("Could not get input type of network to execute".to_string())?;
|
||||||
|
// A node to convert the EditorContext to the Context is automatically inserted for each node at id-1
|
||||||
|
let result = match input_type {
|
||||||
|
t if t == concrete!(Context) => self.execute(editor_context, node_to_evaluate).await.map_err(|e| e.to_string()),
|
||||||
|
t if t == concrete!(()) => (&self).execute((), node_to_evaluate).await.map_err(|e| e.to_string()),
|
||||||
|
t => Err(format!("Invalid input type {t:?}")),
|
||||||
|
};
|
||||||
|
let result = match result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute<I>(&self, input: I, protonode_id: SNI) -> LocalFuture<'_, Result<TaggedValue, Box<dyn Error>>>
|
||||||
where
|
where
|
||||||
I: dyn_any::StaticType + 'static + Send + Sync + std::panic::UnwindSafe,
|
I: dyn_any::StaticType + 'static + Send + Sync + std::panic::UnwindSafe,
|
||||||
{
|
{
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
let output_node = self.output.ok_or("Could not execute network before compilation")?;
|
|
||||||
|
|
||||||
let result = self.tree.eval_tagged_value(output_node, input);
|
let result = self.tree.eval_tagged_value(protonode_id, input);
|
||||||
let wrapped_result = std::panic::AssertUnwindSafe(result).catch_unwind().await;
|
let wrapped_result = std::panic::AssertUnwindSafe(result).catch_unwind().await;
|
||||||
|
|
||||||
match wrapped_result {
|
match wrapped_result {
|
||||||
@@ -138,95 +159,14 @@ impl DynamicExecutor {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// If node to evaluate is None then the most downstream node is used
|
|
||||||
// pub async fn evaluate_from_node(&self, editor_context: EditorContext, node_to_evaluate: Option<SNI>) -> Result<TaggedValue, String> {
|
|
||||||
// let node_to_evaluate: NodeId = node_to_evaluate
|
|
||||||
// .or_else(|| self.output)
|
|
||||||
// .ok_or("Could not find output node when evaluating network. Has the network been compiled?")?;
|
|
||||||
// let input_type = self
|
|
||||||
// .typing_context
|
|
||||||
// .type_of(node_to_evaluate)
|
|
||||||
// .map(|node_io| node_io.call_argument.clone())
|
|
||||||
// .ok_or("Could not get input type of network to execute".to_string())?;
|
|
||||||
// let result = match input_type {
|
|
||||||
// t if t == concrete!(EditorContext) => self.execute(editor_context, node_to_evaluate).await.map_err(|e| e.to_string()),
|
|
||||||
// t if t == concrete!(()) => (&self).execute((), node_to_evaluate).await.map_err(|e| e.to_string()),
|
|
||||||
// t => Err(format!("Invalid input type {t:?}")),
|
|
||||||
// };
|
|
||||||
// let result = match result {
|
|
||||||
// Ok(value) => value,
|
|
||||||
// Err(e) => return Err(e),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok(result)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct EditorContext {
|
|
||||||
// pub footprint: Option<Footprint>,
|
|
||||||
// pub downstream_transform: Option<DAffine2>,
|
|
||||||
// pub real_time: Option<f64>,
|
|
||||||
// pub animation_time: Option<f64>,
|
|
||||||
// pub index: Option<usize>,
|
|
||||||
// pub editor_var_args: Option<(Vec<String>, Vec<Arc<Box<[dyn std::any::Any + 'static + std::panic::UnwindSafe]>>>)>,
|
|
||||||
|
|
||||||
// TODO: Temporarily used to execute with RenderConfig as call argument, will be removed once these fields can be passed
|
|
||||||
// As a scope input to the reworked render node. This will allow the Editor Context to be used to evaluate any node
|
|
||||||
pub render_config: RenderConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl StaticType for EditorContext {
|
|
||||||
type Static = EditorContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl Default for EditorContext {
|
|
||||||
// fn default() -> Self {
|
|
||||||
// EditorContext {
|
|
||||||
// footprint: None,
|
|
||||||
// downstream_transform: None,
|
|
||||||
// real_time: None,
|
|
||||||
// animation_time: None,
|
|
||||||
// index: None,
|
|
||||||
// // editor_var_args: None,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl EditorContext {
|
|
||||||
// pub fn to_context(&self) -> graphene_std::Context {
|
|
||||||
// let mut context = OwnedContextImpl::default();
|
|
||||||
// if let Some(footprint) = self.footprint {
|
|
||||||
// context.set_footprint(footprint);
|
|
||||||
// }
|
|
||||||
// if let Some(footprint) = self.footprint {
|
|
||||||
// context.set_footprint(footprint);
|
|
||||||
// }
|
|
||||||
// if let Some(downstream_transform) = self.downstream_transform {
|
|
||||||
// context.set_downstream_transform(downstream_transform);
|
|
||||||
// }
|
|
||||||
// if let Some(real_time) = self.real_time {
|
|
||||||
// context.set_real_time(real_time);
|
|
||||||
// }
|
|
||||||
// if let Some(animation_time) = self.animation_time {
|
|
||||||
// context.set_animation_time(animation_time);
|
|
||||||
// }
|
|
||||||
// if let Some(index) = self.index {
|
|
||||||
// context.set_index(index);
|
|
||||||
// }
|
|
||||||
// // if let Some(editor_var_args) = self.editor_var_args {
|
|
||||||
// // let (variable_names, values)
|
|
||||||
// // context.set_varargs((variable_names, values))
|
|
||||||
// // }
|
|
||||||
// context.into_context()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum IntrospectError {
|
pub enum IntrospectError {
|
||||||
PathNotFound(Vec<NodeId>),
|
PathNotFound(Vec<NodeId>),
|
||||||
ProtoNodeNotFound(SNI),
|
ProtoNodeNotFound(CompiledProtonodeInput),
|
||||||
|
InputIndexOutOfBounds(CompiledProtonodeInput),
|
||||||
|
InvalidInputType(CompiledProtonodeInput),
|
||||||
NoData,
|
NoData,
|
||||||
RuntimeNotReady,
|
RuntimeNotReady,
|
||||||
IntrospectNotImplemented,
|
IntrospectNotImplemented,
|
||||||
@@ -236,14 +176,33 @@ impl std::fmt::Display for IntrospectError {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
IntrospectError::PathNotFound(path) => write!(f, "Path not found: {:?}", path),
|
IntrospectError::PathNotFound(path) => write!(f, "Path not found: {:?}", path),
|
||||||
IntrospectError::ProtoNodeNotFound(id) => write!(f, "ProtoNode not found: {:?}", id),
|
IntrospectError::ProtoNodeNotFound(input) => write!(f, "ProtoNode not found: {:?}", input),
|
||||||
IntrospectError::NoData => write!(f, "No data found for this node"),
|
IntrospectError::NoData => write!(f, "No data found for this node"),
|
||||||
IntrospectError::RuntimeNotReady => write!(f, "Node runtime is not ready"),
|
IntrospectError::RuntimeNotReady => write!(f, "Node runtime is not ready"),
|
||||||
IntrospectError::IntrospectNotImplemented => write!(f, "Intospect not implemented"),
|
IntrospectError::IntrospectNotImplemented => write!(f, "Intospect not implemented"),
|
||||||
|
IntrospectError::InputIndexOutOfBounds(input) => write!(f, "Invalid input index: {:?}", input),
|
||||||
|
IntrospectError::InvalidInputType(input) => write!(f, "Invalid input type: {:?}", input),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct InsertedProtonode {
|
||||||
|
// If the inserted protonode is a value node, then do not clear types when removing
|
||||||
|
is_value: bool,
|
||||||
|
// Either the value node, cache node, or protonode if output is not clone
|
||||||
|
cached_protonode: SharedNodeContainer,
|
||||||
|
// Value nodes are the entry points, since they can be directly evaluated
|
||||||
|
// Nodes with cloneable outputs have a cache, then editor entry point
|
||||||
|
// Nodes without cloneable outputs just have an editor entry point connected to their output
|
||||||
|
output_editor_entrypoint: SharedNodeContainer,
|
||||||
|
// Nodes with inputs store references to the entry points of the upstream node
|
||||||
|
// This is used to generate thumbnails
|
||||||
|
input_thumbnail_entrypoints: Vec<SharedNodeContainer>,
|
||||||
|
// They also store references to the upstream cache/value node, used for introspection
|
||||||
|
input_introspection_entrypoints: Vec<SharedNodeContainer>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A store of dynamically typed nodes and their associated source map.
|
/// A store of dynamically typed nodes and their associated source map.
|
||||||
///
|
///
|
||||||
/// [`BorrowTree`] maintains two main data structures:
|
/// [`BorrowTree`] maintains two main data structures:
|
||||||
@@ -264,51 +223,54 @@ impl std::fmt::Display for IntrospectError {
|
|||||||
/// A store of the dynamically typed nodes and also the source map.
|
/// A store of the dynamically typed nodes and also the source map.
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct BorrowTree {
|
pub struct BorrowTree {
|
||||||
// A hashmap of node IDs and dynamically typed nodes, as well as the number of inserted monitor nodes
|
// A hashmap of node IDs to dynamically typed proto nodes, as well as the auto inserted MonitorCache nodes, and editor entry point
|
||||||
nodes: HashMap<SNI, SharedNodeContainer>,
|
nodes: HashMap<SNI, InsertedProtonode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowTree {
|
impl BorrowTree {
|
||||||
pub async fn new(proto_network: Vec<ProtoNode>, typing_context: &TypingContext) -> Result<BorrowTree, GraphErrors> {
|
pub async fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<BorrowTree, GraphErrors> {
|
||||||
let mut nodes = BorrowTree::default();
|
let mut nodes = BorrowTree::default();
|
||||||
for node in proto_network {
|
for node in proto_network.into_nodes() {
|
||||||
nodes.push_node(node, typing_context).await?
|
nodes.push_node(node, typing_context).await?
|
||||||
}
|
}
|
||||||
Ok(nodes)
|
Ok(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes new nodes into the tree and returns a vec of document nodes that had their types changed, and a vec of all nodes that were removed (including auto inserted value nodes)
|
/// Pushes new nodes into the tree and returns a vec of document nodes that had their types changed, and a vec of all nodes that were removed (including auto inserted value nodes)
|
||||||
pub async fn update(&mut self, proto_network: Vec<ProtoNode>, typing_context: &TypingContext) -> Result<(Vec<SNI>, HashSet<SNI>), GraphErrors> {
|
pub async fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<(Vec<SNI>, HashSet<SNI>), GraphErrors> {
|
||||||
let mut old_nodes = self.nodes.keys().copied().into_iter().collect::<HashSet<_>>();
|
let mut old_nodes = self.nodes.keys().copied().into_iter().collect::<HashSet<_>>();
|
||||||
// List of all document node paths that need to be updated, which occurs if their path changes or type changes
|
// List of all document node paths that need to be updated, which occurs if their path changes or type changes
|
||||||
let mut nodes_with_new_type = Vec::new();
|
let mut nodes_with_new_type = Vec::new();
|
||||||
for node in proto_network {
|
for node in proto_network.into_nodes() {
|
||||||
let sni = node.stable_node_id;
|
let sni = node.stable_node_id;
|
||||||
old_nodes.remove(&sni);
|
old_nodes.remove(&sni);
|
||||||
let sni = node.stable_node_id;
|
|
||||||
if !self.nodes.contains_key(&sni) {
|
if !self.nodes.contains_key(&sni) {
|
||||||
if node.original_location.send_types_to_editor {
|
// Do not send types for auto inserted value nodes
|
||||||
|
if matches!(node.construction_args, ConstructionArgs::Nodes(_)) {
|
||||||
nodes_with_new_type.push(sni)
|
nodes_with_new_type.push(sni)
|
||||||
}
|
}
|
||||||
self.push_node(node, typing_context);
|
self.push_node(node, typing_context).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((nodes_with_new_type, old_nodes))
|
Ok((nodes_with_new_type, old_nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_deps(&self, nodes: &[SNI]) -> Vec<SharedNodeContainer> {
|
fn node_deps(&self, input_metadata: &Vec<Option<UpstreamInputMetadata>>) -> Vec<&InsertedProtonode> {
|
||||||
nodes.iter().map(|node| self.nodes.get(node).unwrap().clone()).collect()
|
input_metadata
|
||||||
|
.iter()
|
||||||
|
.map(|input_metadata| self.nodes.get(&input_metadata.as_ref().expect("input should be mapped during SNI generation").input_sni).unwrap())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the output node of the [`BorrowTree`].
|
/// Evaluate any node in the borrow tree
|
||||||
pub async fn eval<'i, I, O>(&'i self, id: NodeId, input: I) -> Option<O>
|
pub async fn eval<'i, I, O>(&'i self, id: NodeId, input: I) -> Option<O>
|
||||||
where
|
where
|
||||||
I: StaticType + 'i + Send + Sync,
|
I: StaticType + 'i + Send + Sync,
|
||||||
O: StaticType + 'i,
|
O: StaticType + 'i,
|
||||||
{
|
{
|
||||||
let node = self.nodes.get(&id).cloned()?;
|
let node = self.nodes.get(&id)?;
|
||||||
let output = node.eval(Box::new(input));
|
let output = node.output_editor_entrypoint.eval(Box::new(input));
|
||||||
dyn_any::downcast::<O>(output.await).ok().map(|o| *o)
|
dyn_any::downcast::<O>(output.await).ok().map(|o| *o)
|
||||||
}
|
}
|
||||||
/// Evaluate the output node of the [`BorrowTree`] and cast it to a tagged value.
|
/// Evaluate the output node of the [`BorrowTree`] and cast it to a tagged value.
|
||||||
@@ -317,8 +279,8 @@ impl BorrowTree {
|
|||||||
where
|
where
|
||||||
I: StaticType + 'static + Send + Sync,
|
I: StaticType + 'static + Send + Sync,
|
||||||
{
|
{
|
||||||
let inserted_node = self.nodes.get(&id).cloned().ok_or("Output node not found in executor")?;
|
let inserted_node = self.nodes.get(&id).ok_or("Output node not found in executor")?;
|
||||||
let output = inserted_node.eval(Box::new(input));
|
let output = inserted_node.output_editor_entrypoint.eval(Box::new(input));
|
||||||
TaggedValue::try_from_any(output.await)
|
TaggedValue::try_from_any(output.await)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,12 +339,9 @@ impl BorrowTree {
|
|||||||
/// - Removes the node from `nodes` HashMap.
|
/// - Removes the node from `nodes` HashMap.
|
||||||
/// - If the node is the primary node for its path in the `source_map`, it's also removed from there.
|
/// - If the node is the primary node for its path in the `source_map`, it's also removed from there.
|
||||||
/// - Returns `None` if the node is not found in the `nodes` HashMap.
|
/// - Returns `None` if the node is not found in the `nodes` HashMap.
|
||||||
pub fn free_node(&mut self, id: &SNI, inputs: usize) {
|
pub fn free_node(&mut self, id: &SNI) -> Option<usize> {
|
||||||
self.nodes.remove(&id);
|
let removed_node = self.nodes.remove(&id).expect(&format!("Could not remove node: {:?}", id));
|
||||||
// Also remove all corresponding monitor nodes
|
removed_node.is_value.then_some(removed_node.input_thumbnail_entrypoints.len())
|
||||||
for monitor_index in 1..=inputs {
|
|
||||||
self.nodes.remove(&NodeId(id.0 + monitor_index as u64));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a new node into the [`BorrowTree`], calling the constructor function from `node_registry.rs`.
|
/// Inserts a new node into the [`BorrowTree`], calling the constructor function from `node_registry.rs`.
|
||||||
@@ -400,40 +359,115 @@ impl BorrowTree {
|
|||||||
/// - `Nodes`: Constructs a node using other nodes as dependencies.
|
/// - `Nodes`: Constructs a node using other nodes as dependencies.
|
||||||
/// - Uses the constructor function from the `typing_context` for `Nodes` construction arguments.
|
/// - Uses the constructor function from the `typing_context` for `Nodes` construction arguments.
|
||||||
/// - Returns an error if no constructor is found for the given node ID.
|
/// - Returns an error if no constructor is found for the given node ID.
|
||||||
|
/// Thumbnails is a mapping of the protonode input to the rendered thumbnail through the monitor cache node
|
||||||
async fn push_node(&mut self, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), GraphErrors> {
|
async fn push_node(&mut self, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), GraphErrors> {
|
||||||
let sni = proto_node.stable_node_id;
|
let sni = proto_node.stable_node_id;
|
||||||
// Move the value into the upcast node instead of cloning it
|
// Move the value into the upcast node instead of cloning it
|
||||||
match proto_node.construction_args {
|
match proto_node.construction_args {
|
||||||
ConstructionArgs::Value(value) => {
|
ConstructionArgs::Value(value_args) => {
|
||||||
// The constructor for nodes with value construction args (value nodes) is not called.
|
// The constructor for nodes with value construction args (value nodes) is not called.
|
||||||
// It is not necessary to clone the Arc for the wasm editor api, since the value node is deduplicated and only called once.
|
// let node = if let TaggedValue::ApplicationIo(api) = &*value {
|
||||||
// It is cloned whenever it is evaluated
|
// let editor_api = UpcastAsRefNode::new(api.clone());
|
||||||
let upcasted = UpcastNode::new(value);
|
// let node = Box::new(editor_api) as TypeErasedBox<'_>;
|
||||||
|
// NodeContainer::new(node)
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
let upcasted = UpcastNode::new(value_args.value);
|
||||||
let node = Box::new(upcasted) as TypeErasedBox<'_>;
|
let node = Box::new(upcasted) as TypeErasedBox<'_>;
|
||||||
self.nodes.insert(sni, NodeContainer::new(node));
|
let value_node = NodeContainer::new(node);
|
||||||
|
|
||||||
|
let inserted_protonode = InsertedProtonode {
|
||||||
|
is_value: true,
|
||||||
|
cached_protonode: value_node.clone(),
|
||||||
|
output_editor_entrypoint: value_node,
|
||||||
|
input_thumbnail_entrypoints: Vec::new(),
|
||||||
|
input_introspection_entrypoints: Vec::new(),
|
||||||
|
};
|
||||||
|
self.nodes.insert(sni, inserted_protonode);
|
||||||
}
|
}
|
||||||
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
||||||
ConstructionArgs::Nodes(ref node_construction_args) => {
|
ConstructionArgs::Nodes(node_construction_args) => {
|
||||||
let construction_nodes = self.node_deps(&node_construction_args.inputs);
|
let construction_nodes = self.node_deps(&node_construction_args.inputs);
|
||||||
|
|
||||||
let types = typing_context.type_of(sni).ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
let input_thumbnail_entrypoints = construction_nodes
|
||||||
let monitor_nodes = construction_nodes
|
.iter()
|
||||||
.into_iter()
|
.map(|inserted_protonode| inserted_protonode.output_editor_entrypoint.clone())
|
||||||
.enumerate()
|
.collect::<Vec<_>>();
|
||||||
.map(|(input_index, construction_node)| {
|
let input_introspection_entrypoints = construction_nodes.iter().map(|inserted_protonode| inserted_protonode.cached_protonode.clone()).collect::<Vec<_>>();
|
||||||
let input_type = types.inputs.get(input_index).unwrap(); //.ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
|
||||||
let monitor_constructor = typing_context.monitor_constructor(input_type).unwrap(); // .ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
|
||||||
let monitor = monitor_constructor(construction_node);
|
|
||||||
let monitor_node_container = NodeContainer::new(monitor);
|
|
||||||
self.nodes.insert(NodeId(sni.0 + input_index as u64 + 1), monitor_node_container.clone());
|
|
||||||
monitor_node_container
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let constructor = typing_context.constructor(sni).ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
// Insert nullification if necessary
|
||||||
let node = constructor(monitor_nodes).await;
|
let protonode_inputs = construction_nodes
|
||||||
let node = NodeContainer::new(node);
|
.iter()
|
||||||
self.nodes.insert(sni, node);
|
.zip(node_construction_args.inputs.into_iter())
|
||||||
|
.map(|(inserted_protonode, input_metadata)| {
|
||||||
|
let previous_input = inserted_protonode.cached_protonode.clone();
|
||||||
|
let input_context_dependencies = input_metadata.unwrap().context_dependencies.unwrap();
|
||||||
|
let protonode_input = if !input_context_dependencies.is_empty() {
|
||||||
|
let nullification_node = NullificationNode::new(previous_input, input_context_dependencies);
|
||||||
|
let node = Box::new(nullification_node) as TypeErasedBox<'_>;
|
||||||
|
NodeContainer::new(node)
|
||||||
|
} else {
|
||||||
|
previous_input
|
||||||
|
};
|
||||||
|
protonode_input
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let constructor = typing_context.constructor(sni).ok_or_else(|| {
|
||||||
|
vec![GraphError {
|
||||||
|
stable_node_id: sni,
|
||||||
|
identifier: node_construction_args.identifier.name.clone(),
|
||||||
|
error: GraphErrorType::NoConstructor,
|
||||||
|
}]
|
||||||
|
})?;
|
||||||
|
let node = constructor(protonode_inputs).await;
|
||||||
|
let protonode = NodeContainer::new(node);
|
||||||
|
|
||||||
|
let types = typing_context.type_of(sni).ok_or_else(|| {
|
||||||
|
vec![GraphError {
|
||||||
|
stable_node_id: sni,
|
||||||
|
identifier: node_construction_args.identifier.name,
|
||||||
|
error: GraphErrorType::NoConstructor,
|
||||||
|
}]
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Insert cache nodes on the output if possible
|
||||||
|
let cached_protonode = if let Some(cache_constructor) = typing_context.cache_constructor(&types.return_value.nested_type()) {
|
||||||
|
let cache = cache_constructor(protonode);
|
||||||
|
let cache_node_container = NodeContainer::new(cache);
|
||||||
|
cache_node_container
|
||||||
|
} else {
|
||||||
|
protonode
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the call argument is Context, insert a conversion node between EditorContext to Context so that it can be evaluated
|
||||||
|
// Also insert the nullification node to whatever the protonode is not dependent on
|
||||||
|
let mut editor_entrypoint_input = cached_protonode.clone();
|
||||||
|
if types.call_argument == concrete!(Context) {
|
||||||
|
let nullify = graphene_std::all_context_dependencies()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|dependency| !node_construction_args.context_dependencies.contains(dependency))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !nullify.is_empty() {
|
||||||
|
let nullification_node = NullificationNode::new(cached_protonode.clone(), nullify);
|
||||||
|
let node = Box::new(nullification_node) as TypeErasedBox<'_>;
|
||||||
|
editor_entrypoint_input = NodeContainer::new(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let editor_entry_point = EditorContextToContext::new(editor_entrypoint_input);
|
||||||
|
let node = Box::new(editor_entry_point) as TypeErasedBox;
|
||||||
|
let output_editor_entrypoint = NodeContainer::new(node);
|
||||||
|
|
||||||
|
let inserted_protonode = InsertedProtonode {
|
||||||
|
is_value: false,
|
||||||
|
cached_protonode,
|
||||||
|
output_editor_entrypoint,
|
||||||
|
input_thumbnail_entrypoints,
|
||||||
|
input_introspection_entrypoints,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.nodes.insert(sni, inserted_protonode);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -449,7 +483,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn push_node_sync() {
|
fn push_node_sync() {
|
||||||
let mut tree = BorrowTree::default();
|
let mut tree = BorrowTree::default();
|
||||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), vec![], NodeId(0));
|
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), NodeId(0));
|
||||||
let context = TypingContext::default();
|
let context = TypingContext::default();
|
||||||
let future = tree.push_node(val_1_protonode, &context);
|
let future = tree.push_node(val_1_protonode, &context);
|
||||||
futures::executor::block_on(future).unwrap();
|
futures::executor::block_on(future).unwrap();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::{DVec2, IVec2, UVec2};
|
use glam::{DVec2, IVec2, UVec2};
|
||||||
use graph_craft::document::value::RenderOutput;
|
use graph_craft::document::value::RenderOutput;
|
||||||
use graph_craft::proto::{MonitorConstructor, NodeConstructor, TypeErasedBox};
|
use graph_craft::proto::{CacheConstructor, NodeConstructor, TypeErasedBox};
|
||||||
use graphene_core::raster::color::Color;
|
use graphene_core::raster::color::Color;
|
||||||
use graphene_core::raster::*;
|
use graphene_core::raster::*;
|
||||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
||||||
@@ -17,8 +17,8 @@ use graphene_std::any::DowncastBothNode;
|
|||||||
use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
||||||
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use graphene_std::wasm_application_io::{WasmEditorApi, WasmSurfaceHandle};
|
use graphene_std::wasm_application_io::{WasmApplicationIoValue, WasmSurfaceHandle};
|
||||||
use node_registry_macros::{async_node, convert_node, into_node, monitor_node};
|
use node_registry_macros::{async_node, cache_node, convert_node, into_node};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
@@ -119,22 +119,22 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => RasterDataTable<GPU>]),
|
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => RasterDataTable<GPU>]),
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RasterDataTable<GPU>]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RasterDataTable<GPU>]),
|
||||||
#[cfg(feature = "gpu")]
|
// #[cfg(feature = "gpu")]
|
||||||
into_node!(from: &WasmEditorApi, to: &WgpuExecutor),
|
// into_node!(from: &WasmApplicationIoValue, to: &WgpuExecutor),
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
(
|
(
|
||||||
ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)),
|
ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)),
|
||||||
|args| {
|
|args| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let editor_api: DowncastBothNode<Context, &WasmEditorApi> = DowncastBothNode::new(args[0].clone());
|
let editor_api: DowncastBothNode<Context, Arc<WasmApplicationIoValue>> = DowncastBothNode::new(args[0].clone());
|
||||||
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(editor_api);
|
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(editor_api);
|
||||||
let any: DynAnyNode<Context, _, _> = DynAnyNode::new(node);
|
let any: DynAnyNode<Context, _, _> = DynAnyNode::new(node);
|
||||||
Box::new(any) as TypeErasedBox
|
Box::new(any) as TypeErasedBox
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(graphene_std::any::PanicNode::<Context, dyn_any::DynFuture<'static, &WasmEditorApi>>::new());
|
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(graphene_std::any::PanicNode::<Context, dyn_any::DynFuture<'static, Arc<WasmApplicationIoValue>>>::new());
|
||||||
let params = vec![fn_type_fut!(Context, &WasmEditorApi)];
|
let params = vec![fn_type_fut!(Context, Arc<WasmApplicationIoValue>)];
|
||||||
let mut node_io = <wgpu_executor::CreateGpuSurfaceNode<_> as NodeIO<'_, Context>>::to_async_node_io(&node, params);
|
let mut node_io = <wgpu_executor::CreateGpuSurfaceNode<_> as NodeIO<'_, Context>>::to_async_node_io(&node, params);
|
||||||
node_io.call_argument = concrete!(<Context as StaticType>::Static);
|
node_io.call_argument = concrete!(<Context as StaticType>::Static);
|
||||||
node_io
|
node_io
|
||||||
@@ -192,51 +192,51 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
|
|
||||||
pub static NODE_REGISTRY: Lazy<HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>> = Lazy::new(|| node_registry());
|
pub static NODE_REGISTRY: Lazy<HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>> = Lazy::new(|| node_registry());
|
||||||
|
|
||||||
fn monitor_nodes() -> HashMap<Type, MonitorConstructor> {
|
fn cache_nodes() -> HashMap<Type, CacheConstructor> {
|
||||||
let nodes: Vec<(Type, MonitorConstructor)> = vec![
|
let nodes: Vec<(Type, CacheConstructor)> = vec![
|
||||||
monitor_node!(ImageTexture),
|
cache_node!(ImageTexture),
|
||||||
monitor_node!(VectorDataTable),
|
cache_node!(VectorDataTable),
|
||||||
monitor_node!(GraphicGroupTable),
|
cache_node!(GraphicGroupTable),
|
||||||
monitor_node!(GraphicElement),
|
cache_node!(GraphicElement),
|
||||||
monitor_node!(Artboard),
|
cache_node!(Artboard),
|
||||||
monitor_node!(RasterDataTable<CPU>),
|
cache_node!(RasterDataTable<CPU>),
|
||||||
monitor_node!(RasterDataTable<GPU>),
|
cache_node!(RasterDataTable<GPU>),
|
||||||
monitor_node!(graphene_core::instances::Instances<Artboard>),
|
cache_node!(graphene_core::instances::Instances<Artboard>),
|
||||||
monitor_node!(String),
|
cache_node!(String),
|
||||||
monitor_node!(IVec2),
|
cache_node!(IVec2),
|
||||||
monitor_node!(DVec2),
|
cache_node!(DVec2),
|
||||||
monitor_node!(bool),
|
cache_node!(bool),
|
||||||
monitor_node!(f64),
|
cache_node!(f64),
|
||||||
monitor_node!(u32),
|
cache_node!(u32),
|
||||||
monitor_node!(u64),
|
cache_node!(u64),
|
||||||
monitor_node!(()),
|
cache_node!(()),
|
||||||
monitor_node!(Vec<f64>),
|
cache_node!(Vec<f64>),
|
||||||
monitor_node!(BlendMode),
|
cache_node!(BlendMode),
|
||||||
monitor_node!(graphene_std::transform::ReferencePoint),
|
cache_node!(graphene_std::transform::ReferencePoint),
|
||||||
monitor_node!(graphene_path_bool::BooleanOperation),
|
cache_node!(graphene_path_bool::BooleanOperation),
|
||||||
monitor_node!(Option<Color>),
|
cache_node!(Option<Color>),
|
||||||
monitor_node!(graphene_core::vector::style::Fill),
|
cache_node!(graphene_core::vector::style::Fill),
|
||||||
monitor_node!(graphene_core::vector::style::StrokeCap),
|
cache_node!(graphene_core::vector::style::StrokeCap),
|
||||||
monitor_node!(graphene_core::vector::style::StrokeJoin),
|
cache_node!(graphene_core::vector::style::StrokeJoin),
|
||||||
monitor_node!(graphene_core::vector::style::PaintOrder),
|
cache_node!(graphene_core::vector::style::PaintOrder),
|
||||||
monitor_node!(graphene_core::vector::style::StrokeAlign),
|
cache_node!(graphene_core::vector::style::StrokeAlign),
|
||||||
monitor_node!(graphene_core::vector::style::Stroke),
|
cache_node!(graphene_core::vector::style::Stroke),
|
||||||
monitor_node!(graphene_core::vector::style::Gradient),
|
cache_node!(graphene_core::vector::style::Gradient),
|
||||||
monitor_node!(graphene_core::vector::style::GradientStops),
|
cache_node!(graphene_core::vector::style::GradientStops),
|
||||||
monitor_node!(Vec<graphene_core::uuid::NodeId>),
|
cache_node!(Vec<graphene_core::uuid::NodeId>),
|
||||||
monitor_node!(Color),
|
cache_node!(Color),
|
||||||
monitor_node!(Box<graphene_core::vector::VectorModification>),
|
cache_node!(Box<graphene_core::vector::VectorModification>),
|
||||||
monitor_node!(graphene_std::vector::misc::CentroidType),
|
cache_node!(graphene_std::vector::misc::CentroidType),
|
||||||
monitor_node!(graphene_std::vector::misc::PointSpacingType),
|
cache_node!(graphene_std::vector::misc::PointSpacingType),
|
||||||
];
|
];
|
||||||
let mut monitor_nodes = HashMap::new();
|
let mut cache_nodes = HashMap::new();
|
||||||
for (monitor_type, constructor) in nodes {
|
for (cache_type, constructor) in nodes {
|
||||||
monitor_nodes.insert(monitor_type, constructor);
|
cache_nodes.insert(cache_type, constructor);
|
||||||
}
|
}
|
||||||
monitor_nodes
|
cache_nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static MONITOR_NODES: Lazy<HashMap<Type, MonitorConstructor>> = Lazy::new(|| monitor_nodes());
|
pub static CACHE_NODES: Lazy<HashMap<Type, CacheConstructor>> = Lazy::new(|| cache_nodes());
|
||||||
|
|
||||||
mod node_registry_macros {
|
mod node_registry_macros {
|
||||||
macro_rules! async_node {
|
macro_rules! async_node {
|
||||||
@@ -331,10 +331,10 @@ mod node_registry_macros {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! monitor_node {
|
macro_rules! cache_node {
|
||||||
($type:ty) => {
|
($type:ty) => {
|
||||||
(concrete!($type), |arg| {
|
(concrete!($type), |arg| {
|
||||||
let node = <graphene_core::memo::MonitorNode<graphene_std::Context, _, _>>::new(graphene_std::registry::downcast_node::<graphene_std::Context, $type>(arg));
|
let node = <graphene_core::memo::MonitorMemoNode<_, _>>::new(graphene_std::registry::downcast_node::<graphene_std::Context, $type>(arg));
|
||||||
let any: DynAnyNode<_, _, _> = graphene_std::any::DynAnyNode::new(node);
|
let any: DynAnyNode<_, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
Box::new(any) as TypeErasedBox
|
Box::new(any) as TypeErasedBox
|
||||||
})
|
})
|
||||||
@@ -342,7 +342,7 @@ mod node_registry_macros {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use async_node;
|
pub(crate) use async_node;
|
||||||
|
pub(crate) use cache_node;
|
||||||
pub(crate) use convert_node;
|
pub(crate) use convert_node;
|
||||||
pub(crate) use into_node;
|
pub(crate) use into_node;
|
||||||
pub(crate) use monitor_node;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
use graph_craft::ProtoNodeIdentifier;
|
use graph_craft::ProtoNodeIdentifier;
|
||||||
use graph_craft::concrete;
|
use graph_craft::concrete;
|
||||||
|
use graph_craft::document::value::EditorMetadata;
|
||||||
|
use graph_craft::document::value::RenderOutput;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
||||||
use graph_craft::generic;
|
use graph_craft::generic;
|
||||||
use graph_craft::wasm_application_io::WasmEditorApi;
|
use graph_craft::wasm_application_io::WasmApplicationIo;
|
||||||
use graphene_std::Context;
|
use graphene_std::Context;
|
||||||
|
use graphene_std::application_io::ApplicationIoValue;
|
||||||
|
use graphene_std::text::FontCache;
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEditorApi>) -> NodeNetwork {
|
pub fn wrap_network_in_scope(network: NodeNetwork, font_cache: Arc<FontCache>, editor_metadata: EditorMetadata, application_io: Arc<WasmApplicationIo>) -> NodeNetwork {
|
||||||
let inner_network = DocumentNode {
|
let inner_network = DocumentNode {
|
||||||
implementation: DocumentNodeImplementation::Network(network),
|
implementation: DocumentNodeImplementation::Network(network),
|
||||||
inputs: vec![],
|
inputs: vec![],
|
||||||
@@ -16,12 +20,12 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
|
|||||||
};
|
};
|
||||||
|
|
||||||
let render_node = DocumentNode {
|
let render_node = DocumentNode {
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(2), 0)],
|
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
exports: vec![NodeInput::node(NodeId(2), 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![NodeInput::scope("editor-api")],
|
inputs: vec![NodeInput::scope("application-io")],
|
||||||
manual_composition: Some(concrete!(Context)),
|
manual_composition: Some(concrete!(Context)),
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("wgpu_executor::CreateGpuSurfaceNode")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("wgpu_executor::CreateGpuSurfaceNode")),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
@@ -35,9 +39,10 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
|
|||||||
},
|
},
|
||||||
// TODO: Add conversion step
|
// TODO: Add conversion step
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
manual_composition: Some(concrete!(graphene_std::application_io::RenderConfig)),
|
manual_composition: Some(concrete!(Context)),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::scope("editor-api"),
|
NodeInput::scope("editor-metadata"),
|
||||||
|
NodeInput::scope("application-io"),
|
||||||
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Context)), Box::new(generic!(T))), 0),
|
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Context)), Box::new(generic!(T))), 0),
|
||||||
NodeInput::node(NodeId(1), 0),
|
NodeInput::node(NodeId(1), 0),
|
||||||
],
|
],
|
||||||
@@ -58,9 +63,16 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
|
|||||||
let nodes = vec![inner_network, render_node];
|
let nodes = vec![inner_network, render_node];
|
||||||
|
|
||||||
NodeNetwork {
|
NodeNetwork {
|
||||||
|
// exports: vec![NodeInput::value(TaggedValue::RenderOutput(RenderOutput::default()), true)],
|
||||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||||
nodes: nodes.into_iter().enumerate().map(|(id, node)| (NodeId(id as u64), node)).collect(),
|
nodes: nodes.into_iter().enumerate().map(|(id, node)| (NodeId(id as u64), node)).collect(),
|
||||||
scope_injections: [("editor-api".to_string(), TaggedValue::EditorApi(editor_api))].into_iter().collect(),
|
scope_injections: [
|
||||||
|
("font-cache".to_string(), TaggedValue::FontCache(font_cache)),
|
||||||
|
("editor-metadata".to_string(), TaggedValue::EditorMetadata(editor_metadata)),
|
||||||
|
("application-io".to_string(), TaggedValue::ApplicationIo(Arc::new(ApplicationIoValue(Some(application_io))))),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
// TODO(TrueDoctor): check if it makes sense to set `generated` to `true`
|
// TODO(TrueDoctor): check if it makes sense to set `generated` to `true`
|
||||||
generated: false,
|
generated: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::sync::atomic::AtomicU64;
|
|||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::token::Comma;
|
use syn::token::Comma;
|
||||||
use syn::{Error, Ident, PatIdent, Token, WhereClause, WherePredicate, parse_quote};
|
use syn::{Error, Ident, PatIdent, Token, TypeParamBound, WhereClause, WherePredicate, parse_quote};
|
||||||
static NODE_ID: AtomicU64 = AtomicU64::new(0);
|
static NODE_ID: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStream2> {
|
pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStream2> {
|
||||||
@@ -346,6 +346,8 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||||||
let properties = &attributes.properties_string.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
|
let properties = &attributes.properties_string.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
|
||||||
|
|
||||||
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, &graphene_core, &identifier);
|
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, &graphene_core, &identifier);
|
||||||
|
|
||||||
|
let context_dependencies = input.context_dependency.clone();
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
/// Underlying implementation for [#struct_name]
|
/// Underlying implementation for [#struct_name]
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -373,10 +375,10 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||||||
mod #mod_name {
|
mod #mod_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
use #graphene_core as gcore;
|
use #graphene_core as gcore;
|
||||||
use gcore::{Node, NodeIOTypes, concrete, fn_type, fn_type_fut, future, ProtoNodeIdentifier, WasmNotSync, NodeIO};
|
use gcore::{Node, NodeIOTypes, concrete, fn_type, fn_type_fut, future, ProtoNodeIdentifier, WasmNotSync, NodeIO, ContextDependency};
|
||||||
use gcore::value::ClonedNode;
|
use gcore::value::ClonedNode;
|
||||||
use gcore::ops::TypeNode;
|
use gcore::ops::TypeNode;
|
||||||
use gcore::registry::{NodeMetadata, FieldMetadata, NODE_REGISTRY, NODE_METADATA, DynAnyNode, DowncastBothNode, DynFuture, TypeErasedBox, PanicNode, RegistryValueSource, RegistryWidgetOverride};
|
use gcore::registry::{NodeMetadata, FieldMetadata, NODE_REGISTRY, NODE_METADATA, NODE_CONTEXT_DEPENDENCY, DynAnyNode, DowncastBothNode, DynFuture, TypeErasedBox, PanicNode, RegistryValueSource, RegistryWidgetOverride};
|
||||||
use gcore::ctor::ctor;
|
use gcore::ctor::ctor;
|
||||||
|
|
||||||
// Use the types specified in the implementation
|
// Use the types specified in the implementation
|
||||||
@@ -429,6 +431,17 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||||||
};
|
};
|
||||||
NODE_METADATA.lock().unwrap().insert(#identifier(), metadata);
|
NODE_METADATA.lock().unwrap().insert(#identifier(), metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), ctor)]
|
||||||
|
fn register_context_dependency() {
|
||||||
|
let mut context_dependency = NODE_CONTEXT_DEPENDENCY.lock().unwrap();
|
||||||
|
context_dependency.insert(
|
||||||
|
#identifier,
|
||||||
|
vec![
|
||||||
|
#(ContextDependency::#context_dependencies,)*
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -602,7 +615,6 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let registry_name = format_ident!("__node_registry_{}_{}", NODE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst), struct_name);
|
let registry_name = format_ident!("__node_registry_{}_{}", NODE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst), struct_name);
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), ctor)]
|
#[cfg_attr(not(target_arch = "wasm32"), ctor)]
|
||||||
@@ -615,11 +627,13 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn #registry_name() {
|
extern "C" fn #registry_name() {
|
||||||
register_node();
|
register_node();
|
||||||
register_metadata();
|
register_metadata();
|
||||||
|
register_context_dependency();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ pub(crate) struct Input {
|
|||||||
pub(crate) pat_ident: PatIdent,
|
pub(crate) pat_ident: PatIdent,
|
||||||
pub(crate) ty: Type,
|
pub(crate) ty: Type,
|
||||||
pub(crate) implementations: Punctuated<Type, Comma>,
|
pub(crate) implementations: Punctuated<Type, Comma>,
|
||||||
|
pub(crate) context_dependency: Vec<proc_macro2::TokenStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Implementation {
|
impl Parse for Implementation {
|
||||||
@@ -350,6 +351,7 @@ fn parse_inputs(inputs: &Punctuated<FnArg, Comma>) -> syn::Result<(Input, Vec<Pa
|
|||||||
pat_ident,
|
pat_ident,
|
||||||
ty: (**ty).clone(),
|
ty: (**ty).clone(),
|
||||||
implementations,
|
implementations,
|
||||||
|
context_dependency: Vec::new(),
|
||||||
});
|
});
|
||||||
} else if let Pat::Ident(pat_ident) = &**pat {
|
} else if let Pat::Ident(pat_ident) = &**pat {
|
||||||
let field = parse_field(pat_ident.clone(), (**ty).clone(), attrs).map_err(|e| Error::new_spanned(pat_ident, format!("Failed to parse argument '{}': {}", pat_ident.ident, e)))?;
|
let field = parse_field(pat_ident.clone(), (**ty).clone(), attrs).map_err(|e| Error::new_spanned(pat_ident, format!("Failed to parse argument '{}': {}", pat_ident.ident, e)))?;
|
||||||
@@ -630,6 +632,24 @@ pub fn new_node_fn(attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
|
|||||||
impl ParsedNodeFn {
|
impl ParsedNodeFn {
|
||||||
fn replace_impl_trait_in_input(&mut self) {
|
fn replace_impl_trait_in_input(&mut self) {
|
||||||
if let Type::ImplTrait(impl_trait) = self.input.ty.clone() {
|
if let Type::ImplTrait(impl_trait) = self.input.ty.clone() {
|
||||||
|
let mut dependency_tokens = Vec::new();
|
||||||
|
for bound in &impl_trait.bounds {
|
||||||
|
if let syn::TypeParamBound::Trait(trait_bound) = bound {
|
||||||
|
if let Some(ident) = trait_bound.path.get_ident() {
|
||||||
|
match ident.to_string().as_str() {
|
||||||
|
"ExtractFootprint" => dependency_tokens.push(quote::quote! {ExtractFootprint}),
|
||||||
|
"ExtractDownstreamTransform" => dependency_tokens.push(quote::quote! {ExtractDownstreamTransform}),
|
||||||
|
"ExtractRealTime" => dependency_tokens.push(quote::quote! {ExtractRealTime}),
|
||||||
|
"ExtractAnimationTime" => dependency_tokens.push(quote::quote! {ExtractAnimationTime}),
|
||||||
|
"ExtractIndex" => dependency_tokens.push(quote::quote! {ExtractIndex}),
|
||||||
|
"ExtractVarArgs" => dependency_tokens.push(quote::quote! {ExtractVarArgs}),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.input.context_dependency = dependency_tokens;
|
||||||
|
|
||||||
let ident = Ident::new("_Input", impl_trait.span());
|
let ident = Ident::new("_Input", impl_trait.span());
|
||||||
let mut bounds = impl_trait.bounds;
|
let mut bounds = impl_trait.bounds;
|
||||||
bounds.push(parse_quote!('n));
|
bounds.push(parse_quote!('n));
|
||||||
@@ -768,6 +788,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("a"),
|
pat_ident: pat_ident("a"),
|
||||||
ty: parse_quote!(f64),
|
ty: parse_quote!(f64),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(f64),
|
output_type: parse_quote!(f64),
|
||||||
is_async: false,
|
is_async: false,
|
||||||
@@ -829,6 +850,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("footprint"),
|
pat_ident: pat_ident("footprint"),
|
||||||
ty: parse_quote!(Footprint),
|
ty: parse_quote!(Footprint),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(T),
|
output_type: parse_quote!(T),
|
||||||
is_async: false,
|
is_async: false,
|
||||||
@@ -901,6 +923,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("_"),
|
pat_ident: pat_ident("_"),
|
||||||
ty: parse_quote!(impl Ctx),
|
ty: parse_quote!(impl Ctx),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(VectorData),
|
output_type: parse_quote!(VectorData),
|
||||||
is_async: false,
|
is_async: false,
|
||||||
@@ -958,6 +981,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("image"),
|
pat_ident: pat_ident("image"),
|
||||||
ty: parse_quote!(RasterDataTable<P>),
|
ty: parse_quote!(RasterDataTable<P>),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(RasterDataTable<P>),
|
output_type: parse_quote!(RasterDataTable<P>),
|
||||||
is_async: false,
|
is_async: false,
|
||||||
@@ -1027,6 +1051,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("a"),
|
pat_ident: pat_ident("a"),
|
||||||
ty: parse_quote!(f64),
|
ty: parse_quote!(f64),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(f64),
|
output_type: parse_quote!(f64),
|
||||||
is_async: false,
|
is_async: false,
|
||||||
@@ -1084,6 +1109,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("api"),
|
pat_ident: pat_ident("api"),
|
||||||
ty: parse_quote!(&WasmEditorApi),
|
ty: parse_quote!(&WasmEditorApi),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(RasterDataTable<CPU>),
|
output_type: parse_quote!(RasterDataTable<CPU>),
|
||||||
is_async: true,
|
is_async: true,
|
||||||
@@ -1141,6 +1167,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("input"),
|
pat_ident: pat_ident("input"),
|
||||||
ty: parse_quote!(i32),
|
ty: parse_quote!(i32),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
context_dependency: Vec::new(),
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(i32),
|
output_type: parse_quote!(i32),
|
||||||
is_async: false,
|
is_async: false,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use anyhow::Result;
|
|||||||
pub use context::Context;
|
pub use context::Context;
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::UVec2;
|
use glam::UVec2;
|
||||||
use graphene_application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
use graphene_application_io::{ApplicationIo, ApplicationIoValue, SurfaceHandle};
|
||||||
use graphene_core::{Color, Ctx};
|
use graphene_core::{Color, Ctx};
|
||||||
pub use graphene_svg_renderer::RenderContext;
|
pub use graphene_svg_renderer::RenderContext;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -23,9 +23,9 @@ impl std::fmt::Debug for WgpuExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<&'a EditorApi<T>> for &'a WgpuExecutor {
|
impl<'a, Io: ApplicationIo<Executor = WgpuExecutor>> From<&'a Arc<ApplicationIoValue<Io>>> for &'a WgpuExecutor {
|
||||||
fn from(editor_api: &'a EditorApi<T>) -> Self {
|
fn from(application_io: &'a Arc<ApplicationIoValue<Io>>) -> Self {
|
||||||
editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap()
|
application_io.0.as_ref().unwrap().gpu_executor().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,8 +153,8 @@ impl WgpuExecutor {
|
|||||||
pub type WindowHandle = Arc<SurfaceHandle<Window>>;
|
pub type WindowHandle = Arc<SurfaceHandle<Window>>;
|
||||||
|
|
||||||
#[node_macro::node(skip_impl)]
|
#[node_macro::node(skip_impl)]
|
||||||
fn create_gpu_surface<'a: 'n, Io: ApplicationIo<Executor = WgpuExecutor, Surface = Window> + 'a + Send + Sync>(_: impl Ctx + 'a, editor_api: &'a EditorApi<Io>) -> Option<WgpuSurface> {
|
fn create_gpu_surface<'a: 'n, Io: ApplicationIo<Executor = WgpuExecutor, Surface = Window> + 'a + Send + Sync>(_: impl Ctx + 'a, application_io: Arc<ApplicationIoValue<Io>>) -> Option<WgpuSurface> {
|
||||||
let canvas = editor_api.application_io.as_ref()?.window()?;
|
let canvas = application_io.0.as_ref()?.window()?;
|
||||||
let executor = editor_api.application_io.as_ref()?.gpu_executor()?;
|
let executor = application_io.0.as_ref()?.gpu_executor()?;
|
||||||
Some(Arc::new(executor.create_surface(canvas).ok()?))
|
Some(Arc::new(executor.create_surface(canvas).ok()?))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user