Refactor document node type lookup function to fix performance degradation over time (#1878)

* Refactor document_node_types function

* Fix node introspection

* Implement diff based type updates

* Fix missing monitor nodes

* Improve docs and fix warings

* Fix wrongful removal of node paths

* Remove code examples for non pub methodsü

* Code review

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Dennis Kobert
2024-08-08 17:37:28 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 06a409f1c5
commit 0dfddd529b
25 changed files with 460 additions and 282 deletions
@@ -6,7 +6,7 @@ use crate::messages::prelude::*;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
#[impl_message(Message, DocumentMessage, NodeGraph)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -151,7 +151,7 @@ pub enum NodeGraphMessage {
UpdateNewNodeGraph,
UpdateTypes {
#[serde(skip)]
resolved_types: ResolvedDocumentNodeTypes,
resolved_types: ResolvedDocumentNodeTypesDelta,
#[serde(skip)]
node_graph_errors: GraphErrors,
},
@@ -11,7 +11,7 @@ use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, Source};
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
use graphene_core::*;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
@@ -74,7 +74,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
match message {
// TODO: automatically remove broadcast messages.
NodeGraphMessage::AddNodes { nodes, new_ids } => {
let Some(new_layer_id) = new_ids.get(&NodeId(0)).cloned().or_else(|| nodes.get(0).map(|(node_id, _)| *node_id)) else {
let Some(new_layer_id) = new_ids.get(&NodeId(0)).cloned().or_else(|| nodes.first().map(|(node_id, _)| *node_id)) else {
log::error!("No nodes to add in AddNodes");
return;
};
@@ -723,7 +723,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
return;
};
// TODO: Cache all wire locations if this is a performance issue
let mut overlapping_wires = Self::collect_wires(network_interface, selection_network_path)
let overlapping_wires = Self::collect_wires(network_interface, selection_network_path)
.into_iter()
.filter(|frontend_wire| {
// Prevent inserting on a link that is connected upstream to the selected node
@@ -798,7 +798,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
let overlapping_wire = if network_interface.is_layer(&selected_node_id, selection_network_path) {
if stack_wires.len() == 1 {
stack_wires.first()
} else if stack_wires.len() == 0 && node_wires.len() == 1 {
} else if stack_wires.is_empty() && node_wires.len() == 1 {
node_wires.first()
} else {
None
@@ -1240,7 +1240,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
}
NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => {
network_interface.resolved_types = resolved_types;
for (path, node_type) in resolved_types.add {
network_interface.resolved_types.types.insert(path.to_vec(), node_type);
}
for path in resolved_types.remove {
network_interface.resolved_types.types.remove(&path.to_vec());
}
self.node_graph_errors = node_graph_errors;
}
NodeGraphMessage::UpdateActionButtons => {
@@ -1573,11 +1578,11 @@ impl NodeGraphMessageHandler {
let frontend_graph_inputs = node.inputs.iter().enumerate().map(|(index, _)| {
// Convert the index in all inputs to the index in only the exposed inputs
// TODO: Only display input type if potential inputs in node_registry are all the same type
let input_type = network_interface.resolved_types.inputs.get(&Source { node: node_id_path.clone(), index }).cloned();
let node_types = network_interface.resolved_types.types.get(node_id_path.as_slice());
// TODO: Should display the color of the "most commonly relevant" (we'd need some sort of precedence) data type it allows given the current generic form that's constrained by the other present connections.
let frontend_data_type = if let Some(ref input_type) = input_type {
FrontendGraphDataType::with_type(input_type)
let frontend_data_type = if let Some(node_types) = node_types {
FrontendGraphDataType::with_type(&node_types.inputs[index])
} else {
FrontendGraphDataType::General
};
@@ -1592,7 +1597,7 @@ impl NodeGraphMessageHandler {
FrontendGraphInput {
data_type: frontend_data_type,
name: input_name,
resolved_type: input_type.map(|input| format!("{input:?}")),
resolved_type: node_types.map(|types| format!("{:?}", types.inputs[index])),
connected_to: None,
}
});
@@ -1801,21 +1806,45 @@ impl NodeGraphMessageHandler {
}
}
/// 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 get_output_types(node: &DocumentNode, resolved_types: &ResolvedDocumentNodeTypes, node_id_path: &[NodeId]) -> Vec<Option<Type>> {
let mut output_types = Vec::new();
let primary_output_type = resolved_types
.outputs
.get(&Source {
node: node_id_path.to_owned(),
index: 0,
})
.cloned();
output_types.push(primary_output_type);
let primary_output_type = resolved_types.types.get(node_id_path).map(|ty| ty.output.clone());
// If the node is not a protonode, get types by traversing across exports until a proto node is reached.
if let graph_craft::document::DocumentNodeImplementation::Network(internal_network) = &node.implementation {
for export in internal_network.exports.iter().skip(1) {
for export in internal_network.exports.iter() {
let mut current_export = export;
let mut current_network = internal_network;
let mut current_path = node_id_path.to_owned();
@@ -1833,25 +1862,23 @@ impl NodeGraphMessageHandler {
}
}
let output_type: Option<Type> = if let NodeInput::Node { output_index, .. } = current_export {
// Current export is pointing to a proto node where type can be derived
assert_eq!(*output_index, 0, "Output index for a proto node should always be 0");
resolved_types.outputs.get(&Source { node: current_path.clone(), index: 0 }).cloned()
} else if let NodeInput::Value { tagged_value, .. } = current_export {
Some(tagged_value.ty())
} else if let NodeInput::Network { import_index, .. } = current_export {
resolved_types
.outputs
.get(&Source {
node: node_id_path.to_owned(),
index: *import_index,
})
.cloned()
} else {
None
let output_type: Option<Type> = match current_export {
NodeInput::Node { output_index, .. } => {
// Current export is pointing to a proto node where type can be derived
assert_eq!(*output_index, 0, "Output index for a proto node should always be 0");
resolved_types.types.get(&current_path).map(|ty| ty.output.clone())
}
NodeInput::Value { tagged_value, .. } => Some(tagged_value.ty()),
NodeInput::Network { import_type, .. } => Some(import_type.clone()),
_ => None,
};
output_types.push(output_type);
}
} else {
if primary_output_type.is_none() {
log::warn!("no output type found for {:?} {:?}", node_id_path, &node.implementation);
}
output_types.push(primary_output_type);
}
output_types
}
@@ -6,7 +6,7 @@ use crate::messages::portfolio::document::node_graph::utility_types::{FrontendCl
use crate::messages::prelude::NodeGraphMessageHandler;
use bezier_rs::Subpath;
use graph_craft::document::{value::TaggedValue, DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork, Source};
use graph_craft::document::{value::TaggedValue, DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork};
use graph_craft::{concrete, Type};
use graphene_std::renderer::{ClickTarget, Quad};
use graphene_std::vector::{PointId, VectorModificationType};
@@ -386,11 +386,10 @@ impl NodeNetworkInterface {
// TODO: Store types for all document nodes, not just the compiled proto nodes, which currently skips isolated nodes
let node_type_from_compiled_network = if let Some(node_id) = input_connector.node_id() {
let node_id_path = [network_path, &[node_id]].concat().clone();
let input_type = self.resolved_types.inputs.get(&graph_craft::document::Source {
node: node_id_path,
index: input_connector.input_index(),
});
input_type.cloned()
self.resolved_types
.types
.get(node_id_path.as_slice())
.map(|node_types| node_types.inputs[input_connector.input_index()].clone())
} else if let Some(encapsulating_node) = self.encapsulating_node(network_path) {
let output_types = NodeGraphMessageHandler::get_output_types(encapsulating_node, &self.resolved_types, network_path);
output_types.get(input_connector.input_index()).map_or_else(
@@ -502,14 +501,7 @@ impl NodeNetworkInterface {
if !network_path.is_empty() {
// TODO: https://github.com/GraphiteEditor/Graphite/issues/1767
// TODO: Non exposed inputs are not added to the inputs_source_map, fix `pub fn document_node_types(&self) -> ResolvedDocumentNodeTypes`
let input_type = self
.resolved_types
.inputs
.get(&Source {
node: network_path.to_vec(),
index: *import_index,
})
.cloned();
let input_type = self.resolved_types.types.get(network_path).map(|nt| nt.inputs[*import_index].clone());
let frontend_data_type = if let Some(input_type) = input_type.clone() {
FrontendGraphDataType::with_type(&input_type)
@@ -2823,8 +2815,8 @@ impl NodeNetworkInterface {
pub fn set_to_node_or_layer(&mut self, node_id: &NodeId, network_path: &[NodeId], is_layer: bool) {
// If a layer is set to a node, set upstream nodes to absolute position, and upstream siblings to absolute position
let child_id = { self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).skip(1).next() };
let upstream_sibling_id = { self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::PrimaryFlow).skip(1).next() };
let child_id = { self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).nth(1) };
let upstream_sibling_id = { self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::PrimaryFlow).nth(1) };
match (self.is_layer(node_id, network_path), is_layer) {
(true, false) => {
if let Some(child_id) = child_id {
@@ -2865,7 +2857,7 @@ impl NodeNetworkInterface {
.and_then(|outward_wires| {
outward_wires
.get(&OutputConnector::node(*node_id, 0))
.and_then(|outward_wires| outward_wires.get(0))
.and_then(|outward_wires| outward_wires.first())
.and_then(|downstream_connector| if downstream_connector.input_index() == 0 { downstream_connector.node_id() } else { None })
})
.is_some_and(|downstream_node_id| self.is_layer(&downstream_node_id, network_path));
@@ -16,6 +16,7 @@ use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graphene_core::text::Font;
use graphene_std::vector::style::{Fill, FillType, Gradient};
use interpreted_executor::dynamic_executor::IntrospectError;
use std::sync::Arc;
use std::vec;
@@ -749,7 +750,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
}
impl PortfolioMessageHandler {
pub async fn introspect_node(&self, node_path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
pub async fn introspect_node(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any>, IntrospectError> {
self.executor.introspect_node(node_path).await
}
@@ -108,6 +108,7 @@ pub fn get_blend_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
/// - Set by an Opacity node with an exposed parameter value driven by another node
/// - Already factored into the pixel alpha channel of an image
/// - The default value of 100% if no Opacity node is present, but this function returns None in that case
///
/// With those limitations in mind, the intention of this function is to show just the value already present in an upstream Opacity node so that value can be directly edited.
pub fn get_opacity(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Opacity")?;
@@ -406,9 +406,11 @@ fn assert_boxes_in_order(rectangles: &VecDeque<Rect>, index: usize) {
#[test]
fn dist_snap_point_right() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.right = [2., 10., 15., 20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
dist_snapper.left = [-2.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
right: [2., 10., 15., 20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
left: [-2.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0.5, 0.), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -422,9 +424,11 @@ fn dist_snap_point_right() {
#[test]
fn dist_snap_point_right_left() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.right = [2., 10., 15., 20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
dist_snapper.left = [-2., -10., -15., -20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
right: [2., 10., 15., 20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
left: [-2., -10., -15., -20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0.5, 0.), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -439,8 +443,10 @@ fn dist_snap_point_right_left() {
#[test]
fn dist_snap_point_left() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.left = [-2., -10., -15., -20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
left: [-2., -10., -15., -20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0.5, 0.), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -454,9 +460,11 @@ fn dist_snap_point_left() {
#[test]
fn dist_snap_point_left_right() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.left = [-2., -10., -15., -20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
dist_snapper.right = [2., 10., 15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
left: [-2., -10., -15., -20.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
right: [2., 10., 15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0.5, 0.), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -470,9 +478,11 @@ fn dist_snap_point_left_right() {
#[test]
fn dist_snap_point_center_x() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.left = [-10., -15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
dist_snapper.right = [10., 15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
left: [-10., -15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
right: [10., 15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0.5, 0.), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -488,9 +498,11 @@ fn dist_snap_point_center_x() {
#[test]
fn dist_snap_point_down() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.down = [2., 10., 15., 20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
dist_snapper.up = [-2.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
down: [2., 10., 15., 20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
up: [-2.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0., 0.5), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -504,9 +516,11 @@ fn dist_snap_point_down() {
#[test]
fn dist_snap_point_down_up() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.down = [2., 10., 15., 20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
dist_snapper.up = [-2., -10., -15., -20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
down: [2., 10., 15., 20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
up: [-2., -10., -15., -20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0., 0.5), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -521,8 +535,10 @@ fn dist_snap_point_down_up() {
#[test]
fn dist_snap_point_up() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.up = [-2., -10., -15., -20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
up: [-2., -10., -15., -20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0., 0.5), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -536,9 +552,11 @@ fn dist_snap_point_up() {
#[test]
fn dist_snap_point_up_down() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.up = [-2., -10., -15., -20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
dist_snapper.down = [2., 10., 15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
up: [-2., -10., -15., -20.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
down: [2., 10., 15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0., 0.5), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -552,9 +570,11 @@ fn dist_snap_point_up_down() {
#[test]
fn dist_snap_point_center_y() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.up = [-10., -15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
dist_snapper.down = [10., 15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
up: [-10., -15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
down: [10., 15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
..Default::default()
};
let source = Rect::from_square(DVec2::new(0., 0.5), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
@@ -568,11 +588,12 @@ fn dist_snap_point_center_y() {
#[test]
fn dist_snap_point_center_xy() {
let mut dist_snapper = DistributionSnapper::default();
dist_snapper.up = [-10., -15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
dist_snapper.down = [10., 15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec();
dist_snapper.left = [-12., -15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
dist_snapper.right = [12., 15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec();
let dist_snapper = DistributionSnapper {
up: [-10., -15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
down: [10., 15.].map(|y| Rect::from_square(DVec2::new(0., y), 2.)).to_vec(),
left: [-12., -15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
right: [12., 15.].map(|x| Rect::from_square(DVec2::new(x, 0.), 2.)).to_vec(),
};
let source = Rect::from_square(DVec2::new(0.3, 0.4), 2.);
let snap_results = &mut SnapResults::default();
dist_snapper.snap_bbox_points(1., &SnapCandidatePoint::default(), snap_results, SnapConstraint::None, source);
+33 -36
View File
@@ -22,7 +22,7 @@ use graphene_core::vector::VectorData;
use graphene_core::{Color, GraphicElement, SurfaceFrame};
use graphene_std::renderer::format_transform_matrix;
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::{DynamicExecutor, ResolvedDocumentNodeTypes};
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
use glam::{DAffine2, DVec2, UVec2};
use once_cell::sync::Lazy;
@@ -43,7 +43,6 @@ pub struct NodeRuntime {
editor_api: Arc<WasmEditorApi>,
node_graph_errors: GraphErrors,
resolved_types: ResolvedDocumentNodeTypes,
monitor_nodes: Vec<Vec<NodeId>>,
// TODO: Remove, it doesn't need to be persisted anymore
@@ -91,8 +90,7 @@ pub struct ExecutionResponse {
}
pub struct CompilationResponse {
result: Result<(), String>,
resolved_types: ResolvedDocumentNodeTypes,
result: Result<ResolvedDocumentNodeTypesDelta, String>,
node_graph_errors: GraphErrors,
}
@@ -143,7 +141,6 @@ impl NodeRuntime {
.into(),
node_graph_errors: Vec::new(),
resolved_types: ResolvedDocumentNodeTypes::default(),
monitor_nodes: Vec::new(),
thumbnail_renders: Default::default(),
@@ -214,7 +211,6 @@ impl NodeRuntime {
self.update_thumbnails = true;
self.sender.send_generation_response(CompilationResponse {
result,
resolved_types: self.resolved_types.clone(),
node_graph_errors: self.node_graph_errors.clone(),
});
}
@@ -240,13 +236,8 @@ impl NodeRuntime {
}
}
async fn update_network(&mut self, graph: NodeNetwork) -> Result<(), String> {
async fn update_network(&mut self, graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, String> {
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
self.monitor_nodes = scoped_network
.recursive_nodes()
.filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>"))
.map(|(_, node)| node.original_location.path.clone().unwrap_or_default())
.collect::<Vec<_>>();
// We assume only one output
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
@@ -255,14 +246,18 @@ impl NodeRuntime {
Ok(network) => network,
Err(e) => return Err(e),
};
self.monitor_nodes = proto_network
.nodes
.iter()
.filter(|(_, node)| node.identifier == "graphene_core::memo::MonitorNode<_, _, _>".into())
.map(|(_, node)| node.original_location.path.clone().unwrap_or_default())
.collect::<Vec<_>>();
assert_ne!(proto_network.nodes.len(), 0, "No proto nodes exist?");
if let Err(e) = self.executor.update(proto_network).await {
self.node_graph_errors = e;
}
self.resolved_types = self.executor.document_node_types();
Ok(())
self.executor.update(proto_network).await.map_err(|e| {
self.node_graph_errors = e.clone();
format!("{e:?}")
})
}
async fn execute_network(&mut self, render_config: RenderConfig) -> Result<TaggedValue, String> {
@@ -296,10 +291,10 @@ impl NodeRuntime {
};
// Extract the monitor node's stored `GraphicElement` data.
let Some(introspected_data) = self.executor.introspect(monitor_node_path).flatten() else {
let Ok(introspected_data) = self.executor.introspect(monitor_node_path) else {
// TODO: Fix the root of the issue causing the spam of this warning (this at least temporarily disables it in release builds)
#[cfg(debug_assertions)]
warn!("Failed to introspect monitor node {:?}", self.executor.introspect(monitor_node_path));
warn!("Failed to introspect monitor node {}", self.executor.introspect(monitor_node_path).unwrap_err());
continue;
};
@@ -377,12 +372,12 @@ impl NodeRuntime {
}
}
pub async fn introspect_node(path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
pub async fn introspect_node(path: &[NodeId]) -> Result<Arc<dyn std::any::Any>, IntrospectError> {
let runtime = NODE_RUNTIME.lock();
if let Some(ref mut runtime) = runtime.as_ref() {
return runtime.executor.introspect(path).flatten();
return runtime.executor.introspect(path);
}
None
Err(IntrospectError::RuntimeNotReady)
}
pub async fn run_node_graph() -> bool {
@@ -436,7 +431,7 @@ impl NodeGraphExecutor {
execution_id
}
pub async fn introspect_node(&self, path: &[NodeId]) -> Option<Arc<dyn std::any::Any>> {
pub async fn introspect_node(&self, path: &[NodeId]) -> Result<Arc<dyn std::any::Any>, IntrospectError> {
introspect_node(path).await
}
@@ -462,7 +457,7 @@ impl NodeGraphExecutor {
return None;
};
let introspection_node = find_node(wrapped_network)?;
let introspection = futures::executor::block_on(self.introspect_node(&[node_path, &[introspection_node]].concat()))?;
let introspection = futures::executor::block_on(self.introspect_node(&[node_path, &[introspection_node]].concat())).ok()?;
let Some(downcasted): Option<&T> = <dyn std::any::Any>::downcast_ref(introspection.as_ref()) else {
log::warn!("Failed to downcast type for introspection");
return None;
@@ -609,20 +604,22 @@ impl NodeGraphExecutor {
}
}
NodeGraphUpdate::CompilationResponse(execution_response) => {
let CompilationResponse {
resolved_types,
node_graph_errors,
result,
} = execution_response;
if let Err(e) = result {
// Clear the click targets while the graph is in an un-renderable state
document.network_interface.document_metadata_mut().update_from_monitor(HashMap::new(), HashMap::new());
log::trace!("{e}");
let CompilationResponse { node_graph_errors, result } = execution_response;
let type_delta = match result {
Err(e) => {
// Clear the click targets while the graph is in an un-renderable state
document.network_interface.document_metadata_mut().update_from_monitor(HashMap::new(), HashMap::new());
log::trace!("{e}");
return Err("Node graph evaluation failed".to_string());
return Err("Node graph evaluation failed".to_string());
}
Ok(result) => result,
};
responses.add(NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors });
responses.add(NodeGraphMessage::UpdateTypes {
resolved_types: type_delta,
node_graph_errors,
});
responses.add(NodeGraphMessage::SendGraph);
}
NodeGraphUpdate::NodeGraphUpdateMessage(NodeGraphUpdateMessage::ImaginateStatusUpdate) => {