Simplify compilation

This commit is contained in:
Adam
2025-07-16 01:42:39 -07:00
parent f5c6b65fcc
commit 8b665d158c
24 changed files with 863 additions and 1012 deletions
+2 -2
View File
@@ -161,8 +161,8 @@ impl Dispatcher {
Message::EndIntrospectionQueue => {
self.queueing_introspection_messages = false;
}
Message::ProcessIntrospectionQueue(introspected_inputs) => {
let update_message = PortfolioMessage::ProcessIntrospectionResponse { introspected_inputs }.into();
Message::ProcessIntrospectionQueue(introspection_response) => {
let update_message = PortfolioMessage::ProcessIntrospectionResponse { introspection_response }.into();
// Update the state with the render output and introspected inputs
Self::schedule_execution(&mut self.message_queues, true, [update_message]);
@@ -455,73 +455,73 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("Creates a new Artboard which can be used as a working surface."),
properties: None,
},
DocumentNodeDefinition {
identifier: "Load Image",
category: "Web Request",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::Network(NodeNetwork {
exports: vec![NodeInput::node(NodeId(1), 0)],
nodes: [
DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::scope("editor-api"), NodeInput::network(concrete!(String), 1)],
manual_composition: Some(concrete!(Context)),
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::load_resource::IDENTIFIER),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
manual_composition: Some(concrete!(Context)),
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::decode_image::IDENTIFIER),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::value(TaggedValue::String("graphite:null".to_string()), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Empty", "TODO").into(), ("URL", "TODO").into()],
output_names: vec!["Image".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Load Resource".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
..Default::default()
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Decode Image".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
},
..Default::default()
}),
..Default::default()
},
},
description: Cow::Borrowed("Loads an image from a given URL"),
properties: None,
},
// DocumentNodeDefinition {
// identifier: "Load Image",
// category: "Web Request",
// node_template: NodeTemplate {
// document_node: DocumentNode {
// implementation: DocumentNodeImplementation::Network(NodeNetwork {
// exports: vec![NodeInput::node(NodeId(1), 0)],
// nodes: [
// DocumentNode {
// inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::scope("editor-api"), NodeInput::network(concrete!(String), 1)],
// manual_composition: Some(concrete!(Context)),
// implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::load_resource::IDENTIFIER),
// ..Default::default()
// },
// DocumentNode {
// inputs: vec![NodeInput::node(NodeId(0), 0)],
// manual_composition: Some(concrete!(Context)),
// implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::decode_image::IDENTIFIER),
// ..Default::default()
// },
// ]
// .into_iter()
// .enumerate()
// .map(|(id, node)| (NodeId(id as u64), node))
// .collect(),
// ..Default::default()
// }),
// inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::value(TaggedValue::String("graphite:null".to_string()), false)],
// ..Default::default()
// },
// persistent_node_metadata: DocumentNodePersistentMetadata {
// input_metadata: vec![("Empty", "TODO").into(), ("URL", "TODO").into()],
// output_names: vec!["Image".to_string()],
// network_metadata: Some(NodeNetworkMetadata {
// persistent_metadata: NodeNetworkPersistentMetadata {
// node_metadata: [
// DocumentNodeMetadata {
// persistent_metadata: DocumentNodePersistentMetadata {
// display_name: "Load Resource".to_string(),
// node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
// ..Default::default()
// },
// ..Default::default()
// },
// DocumentNodeMetadata {
// persistent_metadata: DocumentNodePersistentMetadata {
// display_name: "Decode Image".to_string(),
// node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
// ..Default::default()
// },
// ..Default::default()
// },
// ]
// .into_iter()
// .enumerate()
// .map(|(id, node)| (NodeId(id as u64), node))
// .collect(),
// ..Default::default()
// },
// ..Default::default()
// }),
// ..Default::default()
// },
// },
// description: Cow::Borrowed("Loads an image from a given URL"),
// properties: None,
// },
#[cfg(feature = "gpu")]
DocumentNodeDefinition {
identifier: "Create Canvas",
@@ -2279,18 +2279,23 @@ impl NodeGraphMessageHandler {
let locked = network_interface.is_locked(&node_id, breadcrumb_network_path);
let errors = None; // TODO: Recursive traversal from export over all protonodes and match metadata with error
// self
// .node_graph_errors
// .iter()
// .find(|error| error.stable_node_id == node_id_path)
// .map(|error| format!("{:?}", error.error.clone()))
// .or_else(|| {
// if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) {
// Some("Node graph type error within this node".to_string())
// } else {
// None
// }
// });
self.node_graph_errors
.iter()
.find(|error| match &error.original_location {
graph_craft::proto::OriginalLocation::Value(_) => false,
graph_craft::proto::OriginalLocation::Node(node_ids) => node_ids == &node_id_path,
})
.map(|error| format!("{:?}", error.error.clone()))
.or_else(|| {
if self.node_graph_errors.iter().any(|error| match &error.original_location {
graph_craft::proto::OriginalLocation::Value(_) => false,
graph_craft::proto::OriginalLocation::Node(node_ids) => node_ids.starts_with(&node_id_path),
}) {
Some("Node graph type error within this node".to_string())
} else {
None
}
});
nodes.push(FrontendNode {
id: node_id,
@@ -13,14 +13,6 @@ pub struct PropertiesPanelMessageHandlerData<'a> {
pub document_name: &'a str,
}
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use graph_craft::document::NodeId;
pub struct PropertiesPanelMessageHandlerData<'a> {
pub network_interface: &'a mut NodeNetworkInterface,
pub selection_network_path: &'a [NodeId],
pub document_name: &'a str,
}
#[derive(Debug, Clone, Default, ExtractField)]
pub struct PropertiesPanelMessageHandler {}
@@ -11,11 +11,13 @@ use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, InputConnector, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork, OutputConnector};
use graph_craft::document::{AbsoluteInputConnector, DocumentNode, DocumentNodeImplementation, InputConnector, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork, OutputConnector};
use graph_craft::proto::OriginalLocation;
use graph_craft::{Type, concrete};
use graphene_std::NodeIOTypes;
use graphene_std::math::quad::Quad;
use graphene_std::transform::Footprint;
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
use graphene_std::uuid::{NodeId, SNI};
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, VectorData, VectorModificationType};
use interpreted_executor::node_registry::NODE_REGISTRY;
@@ -35,10 +37,11 @@ pub struct NodeNetworkInterface {
/// Stores the document network's structural topology. Should automatically kept in sync by the setter methods when changes to the document network are made.
#[serde(skip)]
document_metadata: DocumentMetadata,
/// All input/output types based on the compiled network.
/// All input types based on the compiled network for protonodes.
/// The types for values inputs can be resolved from the tagged value
/// TODO: Move to portfolio message handler
#[serde(skip)]
pub resolved_types: HashMap<SNI, Vec<Type>>,
pub resolved_types: HashMap<SNI, NodeIOTypes>,
#[serde(skip)]
transaction_status: TransactionStatus,
#[serde(skip)]
@@ -490,8 +493,8 @@ impl NodeNetworkInterface {
}
/// Try and get the [`DocumentNodeDefinition`] for a node
pub fn node_definition(&self, node_id: NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeDefinition> {
let metadata = self.node_metadata(&node_id, network_path)?;
pub fn node_definition(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeDefinition> {
let metadata = self.node_metadata(node_id, network_path)?;
resolve_document_node_type(metadata.persistent_metadata.reference.as_ref()?)
}
@@ -512,63 +515,6 @@ impl NodeNetworkInterface {
}
}
pub fn downstream_caller_from_output(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<&CompiledProtonodeInput> {
match output_connector {
OutputConnector::Node { node_id, output_index } => match self.implementation(node_id, network_path)? {
DocumentNodeImplementation::Network(_) => {
let mut nested_path = network_path.to_vec();
nested_path.push(*node_id);
self.downstream_caller_from_input(&InputConnector::Export(*output_index), &nested_path)
}
DocumentNodeImplementation::ProtoNode(_) => self.node_metadata(&node_id, network_path)?.transient_metadata.caller.as_ref(),
DocumentNodeImplementation::Extract => todo!(),
},
OutputConnector::Import(import_index) => {
let mut encapsulating_path = network_path.to_vec();
let node_id = encapsulating_path.pop().expect("No imports in document network");
self.downstream_caller_from_input(&InputConnector::node(node_id, *import_index), &encapsulating_path)
}
}
}
// Returns the path and input index to the protonode which called the input, which has to be the same every time is is called for a given input.
// This has to be done by iterating upstream, since a downstream traversal may lead to an uncompiled branch.
// This requires that value inputs store their caller. Caller input metadata from compilation has to be stored for
pub fn downstream_caller_from_input(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<&CompiledProtonodeInput> {
// Cases: Node/Value input to protonode, Node/Value input to network node
let input = self.input_from_connector(input_connector, network_path)?;
let caller_input = match input {
NodeInput::Node { node_id, output_index, .. } => {
match self.implementation(node_id, network_path)? {
DocumentNodeImplementation::Network(_) => {
// Continue traversal within network
let mut nested_path = network_path.to_vec();
nested_path.push(*node_id);
self.downstream_caller_from_input(&InputConnector::Export(*output_index), &nested_path)
}
DocumentNodeImplementation::ProtoNode(_) => self.node_metadata(node_id, network_path)?.transient_metadata.caller.as_ref(),
// If connected to a protonode, use the data in the node metadata
DocumentNodeImplementation::Extract => todo!(),
}
}
// Can either be an input to a protonode, network node, or export
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Reflection(_) => match input_connector {
InputConnector::Node { node_id, .. } => self.transient_input_metadata(node_id, input_connector.input_index(), network_path)?.caller.as_ref(),
InputConnector::Export(export_index) => self.network_metadata(network_path)?.transient_metadata.callers.get(*export_index)?.as_ref(),
},
NodeInput::Network { import_index, .. } => {
let mut encapsulating_path = network_path.to_vec();
let node_id = encapsulating_path.pop().expect("No imports in document network");
self.downstream_caller_from_input(&InputConnector::node(node_id, *import_index), &encapsulating_path)
}
NodeInput::Inline(_) => None,
};
let Some(caller_input) = caller_input else {
log::error!("Could not get compiled caller input for input: {:?} in network: {:?}", input_connector, network_path);
return None;
};
Some(caller_input)
}
pub fn take_input(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<NodeInput> {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get network in input_from_connector");
@@ -587,131 +533,186 @@ impl NodeNetworkInterface {
input.map(|input| std::mem::replace(input, NodeInput::value(TaggedValue::None, true)))
}
/// Guess the type from the node based on a document node default or a random protonode definition.
fn guess_type_from_node(&mut self, node_id: NodeId, input_index: usize, network_path: &[NodeId]) -> (Type, TypeSource) {
// Try and get the default value from the document node definition
if let Some(value) = self
.node_definition(node_id, network_path)
.and_then(|definition| definition.node_template.document_node.inputs.get(input_index))
.and_then(|input| input.as_value())
{
return (value.ty(), TypeSource::DocumentNodeDefault);
}
/// Guess the type from the node based on the tagged value, document node default, or a random protonode definition.
// fn guess_type_from_uncompiled_input(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
// let Some(input) = self.input_from_connector(input_connector, network_path) else {
// return (concrete!(()), TypeSource::Error("Could not get input from connector"));
// };
let Some(node) = self.document_node(&node_id, network_path) else {
return (concrete!(()), TypeSource::Error("node id {node_id:?} not in network {network_path:?}"));
};
// match input {
// NodeInput::Node { node_id: upstream_node_id, output_index, .. } => {
// let input_index = input_connector.input_index();
// // Try and get the default value from the document node definition
// if let Some(value) = self
// .node_definition(upstream_node_id, network_path)
// .and_then(|definition| definition.node_template.document_node.inputs.get(input_index))
// .and_then(|input| input.as_value())
// {
// return (value.ty(), TypeSource::DocumentNodeDefault);
// }
let mut node_id_path = network_path.to_vec();
node_id_path.push(node_id);
// //Get a random protonode implementation
// let Some(node) = self.document_node(&upstream_node_id, network_path) else {
// return (concrete!(()), TypeSource::Error("node id {node_id:?} not in network {network_path:?}"));
// };
match &node.implementation {
DocumentNodeImplementation::ProtoNode(protonode) => {
let Some(node_types) = random_protonode_implementation(protonode) else {
return (concrete!(()), TypeSource::Error("could not resolve protonode"));
};
// let mut node_id_path = network_path.to_vec();
// node_id_path.push(*upstream_node_id);
let Some(input_type) = node_types.inputs.get(input_index) else {
log::error!("Could not get type");
return (concrete!(()), TypeSource::Error("could not get the protonode's input"));
};
// match &node.implementation {
// DocumentNodeImplementation::ProtoNode(protonode) => {
// let Some(node_types) = random_protonode_implementation(protonode) else {
// return (concrete!(()), TypeSource::Error("could not resolve protonode"));
// };
(input_type.clone(), TypeSource::RandomProtonodeImplementation)
}
DocumentNodeImplementation::Network(_network) => {
// Attempt to resolve where this import is within the nested network
let outwards_wires = self.outward_wires(&node_id_path);
let inputs_using_import = outwards_wires.and_then(|outwards_wires| outwards_wires.get(&OutputConnector::Import(input_index)));
let first_input = inputs_using_import.and_then(|input| input.first()).copied();
// let Some(input_type) = node_types.inputs.get(input_index) else {
// log::error!("Could not get type");
// return (concrete!(()), TypeSource::Error("could not get the protonode's input"));
// };
if let Some(InputConnector::Node {
node_id: child_id,
input_index: child_input_index,
}) = first_input
{
let mut inner_path = network_path.to_vec();
inner_path.push(node_id);
let result = self.guess_type_from_node(child_id, child_input_index, &inner_path);
inner_path.pop();
return result;
}
// Input is disconnected
(concrete!(()), TypeSource::Error("disconnected network input"))
}
_ => (concrete!(()), TypeSource::Error("implementation is not network or protonode")),
}
}
// (input_type.clone(), TypeSource::RandomProtonodeImplementation)
// }
// DocumentNodeImplementation::Network(_) => {
// // TODO: Implement type guessing when
// (concrete!(()), TypeSource::Error("disconnected network input"))
// }
// _ => (concrete!(()), TypeSource::Error("implementation is not network or protonode")),
// }
// }
// // If the current input is a tagged value, then use that
// NodeInput::Value { tagged_value, exposed } => (tagged_value.ty(), TypeSource::TaggedValue),
// NodeInput::Network { import_index, import_type } => {
// // TODO: Implement type guessing for imports
// (concrete!(()), TypeSource::Error("Cannot guess type from import"))
// }
// NodeInput::Scope(cow) => (concrete!(()), TypeSource::Scope),
// NodeInput::Reflection(document_node_metadata) => (concrete!(()), TypeSource::Reflection),
// NodeInput::Inline(inline_rust) => (inline_rust.ty.clone(), TypeSource::Inline),
// }
// }
/// Get the [`Type`] for any InputConnector
pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
if let Some(NodeInput::Value { tagged_value, .. }) = self.input_from_connector(input_connector, network_path) {
return (tagged_value.ty(), TypeSource::TaggedValue);
// Try getting the compiled type
if let Some(node_io) = self.protonode_from_input(input_connector, network_path).and_then(|sni| self.resolved_types.get(&sni)) {
return (node_io.return_value.clone(), TypeSource::Compiled);
}
if let Some(compiled_type) = self
.downstream_caller_from_input(input_connector, network_path)
.and_then(|(sni, input_index)| self.resolved_types.get(sni).and_then(|protonode_input_types| protonode_input_types.get(*input_index)))
{
return (compiled_type.clone(), TypeSource::Compiled);
}
// Resolve types from proto nodes in node_registry
let Some(node_id) = input_connector.node_id() else {
return (concrete!(()), TypeSource::Error("input connector is not a node"));
};
self.guess_type_from_node(node_id, input_connector.input_index(), network_path)
(concrete!(()), TypeSource::Error("Not compiled"))
// self.guess_type_from_uncompiled_input(input_connector, network_path)
}
pub fn output_type(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> (Type, TypeSource) {
if let Some(output_type) = self
.downstream_caller_from_output(output_connector, network_path)
.and_then(|(sni, input_index)| self.resolved_types.get(sni).and_then(|protonode_input_types| protonode_input_types.get(*input_index)))
{
return (output_type.clone(), TypeSource::Compiled);
// Try getting the compiled type
if let Some(node_io) = self.protonode_from_output(output_connector, network_path).and_then(|sni| self.resolved_types.get(&sni)) {
return (node_io.return_value.clone(), TypeSource::Compiled);
}
(concrete!(()), TypeSource::Error("Not compiled"))
(concrete!(()), TypeSource::DocumentNodeDefault)
}
pub fn add_type(&mut self, sni: SNI, input_types: Vec<Type>) {
self.resolved_types.insert(sni, input_types);
// Iterates upstream to whatever protonode this input is connected to
pub fn protonode_from_input(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<SNI> {
match self.input_from_connector(input_connector, network_path)? {
NodeInput::Node { node_id, output_index, .. } => self.protonode_from_output(
&OutputConnector::Node {
node_id: *node_id,
output_index: *output_index,
},
network_path,
),
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Reflection(_) => match input_connector {
InputConnector::Node { node_id, .. } => self.transient_input_metadata(node_id, input_connector.input_index(), network_path)?.sni.clone(),
InputConnector::Export(export_index) => self.network_metadata(network_path)?.transient_metadata.export_stable_node_ids.get(*export_index)?.clone(),
},
NodeInput::Network { import_index, .. } => {
let (encapsulating_node, encapsulating_network) = network_path.split_last().unwrap();
self.protonode_from_input(
&InputConnector::Node {
node_id: *encapsulating_node,
input_index: *import_index,
},
encapsulating_network,
)
}
NodeInput::Inline(_) => None,
}
}
pub fn protonode_from_output(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<SNI> {
match output_connector {
OutputConnector::Node { node_id, output_index } => match self.implementation(node_id, network_path)? {
DocumentNodeImplementation::Network(_) => {
let mut inner_path = network_path.to_vec();
inner_path.push(*node_id);
self.protonode_from_input(&InputConnector::Export(*output_index), &inner_path)
}
DocumentNodeImplementation::ProtoNode(_) => self.node_metadata(node_id, network_path)?.transient_metadata.sni.clone(),
DocumentNodeImplementation::Extract => None,
},
OutputConnector::Import(import_index) => {
let (encapsulating_node, encapsulating_network) = network_path.split_last().unwrap();
self.protonode_from_input(
&InputConnector::Node {
node_id: *encapsulating_node,
input_index: *import_index,
},
encapsulating_network,
)
}
}
}
pub fn update_sni(&mut self, original_location: OriginalLocation, sni: SNI) {
match original_location {
OriginalLocation::Value(AbsoluteInputConnector { network_path, connector }) => {
let (first, network_path) = network_path.split_first().unwrap();
if first != &NodeId(0) {
return;
}
match connector {
InputConnector::Node { node_id, input_index } => {
let Some(metadata) = self.node_metadata_mut(&node_id, network_path) else {
log::error!("node metadata must exist when setting input caller for node {}, input index {}", node_id, input_index);
return;
};
let Some(input_metadata) = metadata.persistent_metadata.input_metadata.get_mut(input_index) else {
log::error!("input metadata must exist when setting input caller for node {}, input index {}", node_id, input_index);
return;
};
input_metadata.transient_metadata.sni = Some(sni);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
network_metadata.transient_metadata.export_stable_node_ids.resize(export_index + 1, None);
network_metadata.transient_metadata.export_stable_node_ids[export_index] = Some(sni);
}
}
}
OriginalLocation::Node(network_path) => {
let (first, node_path) = network_path.split_first().unwrap();
if first != &NodeId(0) {
return;
}
let (node_id, network_path) = node_path.split_last().unwrap();
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
metadata.transient_metadata.sni = Some(sni);
}
}
}
pub fn add_type(&mut self, sni: SNI, compiled_type: NodeIOTypes) {
self.resolved_types.insert(sni, compiled_type);
}
pub fn remove_type(&mut self, sni: SNI) {
self.resolved_types.remove(&sni);
}
pub fn set_node_caller(&mut self, node_id: &NodeId, caller: CompiledProtonodeInput, network_path: &[NodeId]) {
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
metadata.transient_metadata.caller = Some(caller);
}
pub fn set_input_caller(&mut self, input_connector: &InputConnector, caller: CompiledProtonodeInput, network_path: &[NodeId]) {
match input_connector {
InputConnector::Node { node_id, input_index } => {
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("node metadata must exist when setting input caller for node {}, input index {}", node_id, input_index);
return;
};
let Some(input_metadata) = metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
log::error!("input metadata must exist when setting input caller for node {}, input index {}", node_id, input_index);
return;
};
input_metadata.transient_metadata.caller = Some(caller);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
network_metadata.transient_metadata.callers.resize(*export_index + 1, None);
network_metadata.transient_metadata.callers[*export_index] = Some(caller);
}
}
}
pub fn valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> {
let InputConnector::Node { node_id, input_index } = input_connector else {
// An export can have any type connected to it
@@ -755,11 +756,18 @@ impl NodeNetworkInterface {
implementations
.iter()
.filter_map(|(node_io, _)| {
// Check if the node_io is valid based on the other types
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path).0;
let (input_type, type_source) = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path);
// If the other input types have been compiled, then check if the current implementation is valid
if type_source == TypeSource::Compiled {
node_io.inputs.get(iterator_index).map(|ty| ty.nested_type().clone()).as_ref() == Some(&input_type) || node_io.inputs.get(iterator_index) == Some(&input_type)
} else {
// If the other inputs haven't been compiled, then any implementation type is valid
true
}
// Value inputs are stored as concrete, so they are compared to the nested type. Node inputs are stored as fn, so they are compared to the entire type.
// For example a node input of (Footprint) -> VectorData would not be compatible with () -> VectorData
node_io.inputs.get(iterator_index).map(|ty| ty.nested_type().clone()).as_ref() == Some(&input_type) || node_io.inputs.get(iterator_index) == Some(&input_type)
});
if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None }
})
@@ -1155,7 +1163,7 @@ impl NodeNetworkInterface {
/// Returns the description of the node, or an empty string if it is not set.
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
self.node_definition(*node_id, network_path)
self.node_definition(node_id, network_path)
.map(|node_definition| node_definition.description.to_string())
.filter(|description| description != "TODO")
.unwrap_or_default()
@@ -2761,7 +2769,7 @@ impl NodeNetworkInterface {
let mut path_string = String::new();
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
let input_sni = self.downstream_caller_from_input(input, network_path).map(|caller| NodeId(caller.0.0 + caller.1 as u64));
let input_sni = self.protonode_from_input(input, network_path);
Some(WirePath {
path_string,
data_type,
@@ -6056,6 +6064,11 @@ pub enum TypeSource {
TaggedValue,
OuterMostExportDefault,
Scope,
Reflection,
Inline,
Extract,
Error(&'static str),
}
@@ -6304,7 +6317,7 @@ pub struct NodeNetworkTransientMetadata {
pub rounded_network_edge_distance: TransientMetadata<NetworkEdgeDistance>,
// Wires from the exports
pub wires: Vec<TransientMetadata<WirePathUpdate>>,
pub callers: Vec<Option<CompiledProtonodeInput>>,
pub export_stable_node_ids: Vec<Option<SNI>>,
}
#[derive(Debug, Clone)]
@@ -6492,7 +6505,7 @@ impl InputPersistentMetadata {
#[derive(Debug, Clone, Default)]
struct InputTransientMetadata {
wire: TransientMetadata<WirePathUpdate>,
caller: Option<CompiledProtonodeInput>,
sni: Option<SNI>,
}
// TODO: Eventually remove this migration document upgrade code
@@ -6807,7 +6820,7 @@ pub struct DocumentNodeTransientMetadata {
// Metadata that is specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer.
pub node_type_metadata: NodeTypeTransientMetadata,
// Stores the caller input since it will be reached through an upstream traversal, but all data is stored per input.
pub caller: Option<CompiledProtonodeInput>,
pub sni: Option<SNI>,
}
#[derive(Debug, Clone)]
@@ -514,6 +514,7 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
.map(|(node_path, node)| (node_path, node.clone()))
.collect::<Vec<(Vec<NodeId>, graph_craft::document::DocumentNode)>>();
for (node_path, node) in &nodes {
let (node_id, network_path) = node_path.split_last().unwrap();
migrate_node(node_id, node, network_path, document, reset_node_definitions_on_open);
}
}
@@ -9,7 +9,7 @@ use graphene_std::Color;
use graphene_std::raster::Image;
use graphene_std::renderer::RenderMetadata;
use graphene_std::text::Font;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::uuid::{SNI};
#[impl_message(Message, Portfolio)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -31,7 +31,7 @@ pub enum PortfolioMessage {
EvaluateActiveDocument,
// Sends a request to introspect data in the network, and return it to the editor
IntrospectActiveDocument {
inputs_to_introspect: HashSet<CompiledProtonodeInput>,
nodes_to_introspect: HashSet<SNI>,
},
ExportActiveDocument {
file_name: String,
@@ -50,7 +50,7 @@ pub enum PortfolioMessage {
},
ProcessIntrospectionResponse {
#[serde(skip)]
introspected_inputs: IntrospectionResponse,
introspection_response: IntrospectionResponse,
},
RenderThumbnails,
ProcessThumbnails,
@@ -20,14 +20,13 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
use crate::node_graph_executor::{CompilationRequest, ExportConfig, NodeGraphExecutor};
use glam::{DAffine2, DVec2};
use graph_craft::document::value::EditorMetadata;
use graph_craft::document::{AbsoluteInputConnector, InputConnector, NodeInput, OutputConnector};
use graph_craft::document::{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::transform::{Footprint, RenderQuality};
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
use graphene_std::uuid::{NodeId, SNI};
use std::sync::Arc;
#[derive(ExtractField)]
@@ -57,12 +56,11 @@ pub struct PortfolioMessageHandler {
pub spreadsheet: SpreadsheetMessageHandler,
device_pixel_ratio: Option<f64>,
pub reset_node_definitions_on_open: bool,
// Data from the node graph. Data for inputs are set to be collected on each evaluation, and added on the evaluation response
// Data from old nodes get deleted after a compilation
// Always take data after requesting it
pub introspected_data: HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
pub introspected_call_argument: HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
pub previous_thumbnail_data: HashMap<CompiledProtonodeInput, Arc<dyn std::any::Any + Send + Sync>>,
// Data from the node graph, which is populated after an introspection request.
// To access the data, schedule messages with StartIntrospectionQueue [messages] EndIntrospectionQueue
// The data is no longer accessible after EndIntrospectionQueue
pub introspected_data: HashMap<SNI, Option<Arc<dyn std::any::Any + Send + Sync>>>,
pub previous_thumbnail_data: HashMap<SNI, Arc<dyn std::any::Any + Send + Sync>>,
}
#[message_handler_data]
@@ -111,13 +109,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
self.menu_bar_message_handler.process_message(message, responses, ());
}
PortfolioMessage::Spreadsheet(message) => {
self.spreadsheet.process_message(
message,
responses,
SpreadsheetMessageHandlerData {
introspected_data: &self.introspected_data,
},
);
if let Some(document_id) = self.active_document_id {
if let Some(document) = self.documents.get_mut(&document_id) {
self.spreadsheet.process_message(
message,
responses,
SpreadsheetMessageHandlerData {
introspected_data: &self.introspected_data,
network_interface: &document.network_interface,
},
);
}
}
}
PortfolioMessage::Document(message) => {
if let Some(document_id) = self.active_document_id {
@@ -445,11 +448,11 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
document_migration_upgrades(&mut document, reset_node_definitions_on_open);
// Ensure each node has the metadata for its inputs
for (mut path, node) in document.network_interface.document_network().clone().recursive_nodes() {
let node_id = path.pop().unwrap();
document.network_interface.validate_input_metadata(node_id, node, &path);
document.network_interface.validate_display_name_metadata(node_id, &path);
document.network_interface.validate_output_names(node_id, node, &path);
for (node_path, node) in document.network_interface.document_network().clone().recursive_nodes() {
let (node_id, path) = node_path.split_last().unwrap();
document.network_interface.validate_input_metadata(&node_id, node, &path);
document.network_interface.validate_display_name_metadata(&node_id, &path);
document.network_interface.validate_output_names(&node_id, node, &path);
}
// Ensure layers are positioned as stacks if they are upstream siblings of another layer
@@ -788,6 +791,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
transform_to_viewport: true,
},
});
// Also evaluate the document after compilation
responses.add_front(PortfolioMessage::EvaluateActiveDocument);
}
}
PortfolioMessage::ProcessCompilationResponse { compilation_metadata } => {
@@ -795,43 +800,24 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
log::error!("Tried to render non-existent document: {:?}", self.active_document_id);
return;
};
for (value_connectors, caller) in compilation_metadata.protonode_caller_for_values {
for AbsoluteInputConnector { network_path, connector } in value_connectors {
let (first, network_path) = network_path.split_first().unwrap();
if first != &NodeId(0) {
continue;
}
document.network_interface.set_input_caller(&connector, caller, network_path)
}
}
for (protonode_paths, caller) in compilation_metadata.protonode_caller_for_nodes {
for protonode_path in protonode_paths {
let (first, node_path) = protonode_path.split_first().unwrap();
if first != &NodeId(0) {
continue;
}
let (node_id, network_path) = node_path.split_last().expect("Protonode path cannot be empty");
document.network_interface.set_node_caller(node_id, caller, &network_path)
}
for (orignal_location, sni) in compilation_metadata.original_locations {
document.network_interface.update_sni(orignal_location, sni);
}
for (sni, input_types) in compilation_metadata.types_to_add {
document.network_interface.add_type(sni, input_types);
}
let mut cleared_thumbnails = Vec::new();
for (sni, number_of_inputs) in compilation_metadata.types_to_remove {
// Removed saved type of the document node
for sni in compilation_metadata.types_to_remove {
// Removed saved type of the protonode
document.network_interface.remove_type(sni);
// TODO: This does not remove thumbnails for wires to value inputs
// Remove all thumbnails
for input_index in 0..number_of_inputs {
cleared_thumbnails.push(NodeId(sni.0 + input_index as u64 + 1));
}
cleared_thumbnails.push(sni);
}
responses.add(FrontendMessage::UpdateThumbnails {
add: Vec::new(),
clear: cleared_thumbnails,
});
// Always evaluate after a recompile
responses.add(PortfolioMessage::EvaluateActiveDocument);
}
PortfolioMessage::EvaluateActiveDocument => {
let Some(document) = self.active_document_id.and_then(|document_id| self.documents.get(&document_id)) else {
@@ -911,12 +897,14 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let RenderMetadata {
upstream_footprints: footprints,
local_transforms,
first_instance_source_id,
click_targets,
clip_targets,
} = evaluation_metadata;
responses.add(DocumentMessage::UpdateUpstreamTransforms {
upstream_footprints: footprints,
local_transforms,
first_instance_source_id,
});
responses.add(DocumentMessage::UpdateClickTargets { click_targets });
responses.add(DocumentMessage::UpdateClipTargets { clip_targets });
@@ -931,41 +919,35 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// After an evaluation, always render all thumbnails
responses.add(PortfolioMessage::RenderThumbnails);
}
PortfolioMessage::IntrospectActiveDocument { inputs_to_introspect } => {
self.executor.submit_node_graph_introspection(inputs_to_introspect);
PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect } => {
self.executor.submit_node_graph_introspection(nodes_to_introspect);
}
PortfolioMessage::ProcessIntrospectionResponse { introspected_inputs } => {
for (input, mode, data) in introspected_inputs.0.into_iter() {
match mode {
IntrospectMode::Input => {
self.introspected_call_argument.insert(input, data);
}
IntrospectMode::Data => {
self.introspected_data.insert(input, data);
}
}
PortfolioMessage::ProcessIntrospectionResponse { introspection_response } => {
for (protonode, data) in introspection_response.0.into_iter() {
self.introspected_data.insert(protonode, data);
}
}
PortfolioMessage::ClearIntrospectedData => {
self.introspected_call_argument.clear();
self.introspected_data.clear()
}
PortfolioMessage::ClearIntrospectedData => 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();
// All possible inputs, later check if they are connected to any nodes
let mut nodes_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 {
// Get all inputs to render thumbnails for
// Get all protonodes for all connected side layer inputs connected to the export in the document network
for layer in document.network_interface.document_metadata().all_layers() {
let connector = InputConnector::Node {
node_id: layer.to_node(),
input_index: 1,
};
document.network_interface.downstream_caller_from_input(&input, &[])
}) {
inputs_to_render.insert(*caller);
if document.network_interface.input_from_connector(&connector, &[]).is_some_and(|input| input.is_wire()) {
if let Some(compiled_input) = document.network_interface.protonode_from_input(&connector, &[]) {
nodes_to_render.insert(compiled_input);
}
}
}
// Save data for all inputs in the viewed node graph
@@ -973,62 +955,59 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
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 => {}
let mut wire_stack = viewed_network
.exports
.iter()
.enumerate()
.filter_map(|(export_index, export)| export.is_wire().then_some(InputConnector::Export(export_index)))
.collect::<Vec<_>>();
while let Some(input_connector) = wire_stack.pop() {
let Some(input) = document.network_interface.input_from_connector(&input_connector, &document.breadcrumb_network_path) else {
log::error!("Could not get input from connector: {:?}", input_connector);
continue;
};
if let NodeInput::Node { node_id, .. } = 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);
};
}
if let NodeInput::Node { node_id, .. } = input {
let Some(node) = document.network_interface.document_node(node_id, &document.breadcrumb_network_path) else {
log::error!("Could not get node");
continue;
};
for (wire_input_index, _) in node.inputs.iter().enumerate().filter(|(_, input)| input.is_wire()) {
wire_stack.push(InputConnector::Node {
node_id: *node_id,
input_index: wire_input_index,
})
}
}
};
let Some(protonode) = document.network_interface.protonode_from_input(&input_connector, &document.breadcrumb_network_path) else {
// The protonode has not been compiled, so it is not connected to the export
wire_stack = Vec::new();
continue;
};
nodes_to_render.insert(protonode);
}
};
responses.add(PortfolioMessage::IntrospectActiveDocument {
inputs_to_introspect: inputs_to_render,
});
responses.add(PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect: nodes_to_render });
responses.add(Message::StartIntrospectionQueue);
responses.add(PortfolioMessage::ProcessThumbnails);
responses.add(Message::EndIntrospectionQueue);
}
PortfolioMessage::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;
for (thumbnail_node, introspected_data) in self.introspected_data.drain() {
let Some(evaluated_data) = introspected_data else {
// Input was not evaluated, do not change its thumbnail
continue;
};
let previous_thumbnail_data = self.previous_thumbnail_data.get(&thumbnail_input);
let previous_thumbnail_data = self.previous_thumbnail_data.get(&thumbnail_node);
match graph_craft::document::value::render_thumbnail_if_change(&evaluated_data, previous_thumbnail_data) {
graph_craft::document::value::ThumbnailRenderResult::NoChange => return,
graph_craft::document::value::ThumbnailRenderResult::ClearThumbnail => thumbnail_response.clear.push(NodeId(input_node_id)),
graph_craft::document::value::ThumbnailRenderResult::UpdateThumbnail(thumbnail) => thumbnail_response.add.push((NodeId(input_node_id), thumbnail)),
graph_craft::document::value::ThumbnailRenderResult::ClearThumbnail => thumbnail_response.clear.push(thumbnail_node),
graph_craft::document::value::ThumbnailRenderResult::UpdateThumbnail(thumbnail) => thumbnail_response.add.push((thumbnail_node, thumbnail)),
}
self.previous_thumbnail_data.insert(thumbnail_input, evaluated_data);
self.previous_thumbnail_data.insert(thumbnail_node, evaluated_data);
}
responses.add(FrontendMessage::UpdateThumbnails {
add: thumbnail_response.add,
@@ -1,6 +1,5 @@
use crate::messages::prelude::*;
use graph_craft::document::AbsoluteInputConnector;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::uuid::{NodeId, SNI};
/// The spreadsheet UI allows for instance data to be previewed.
#[impl_message(Message, PortfolioMessage, Spreadsheet)]
@@ -8,7 +7,8 @@ use graphene_std::uuid::CompiledProtonodeInput;
pub enum SpreadsheetMessage {
ToggleOpen,
UpdateLayout { inspect_input: InspectInputConnector },
RequestUpdateLayout,
ProcessUpdateLayout { node_to_inspect: NodeId, protonode_id: SNI },
PushToInstancePath { index: usize },
TruncateInstancePath { len: usize },
@@ -23,11 +23,3 @@ pub enum VectorDataDomain {
Segments,
Regions,
}
/// The mapping of input where the data is extracted from to the selected input to display data for
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
// #[cfg_attr(feature = "decouple-execution", derive(serde::Serialize, serde::Deserialize))]
pub struct InspectInputConnector {
pub input_connector: AbsoluteInputConnector,
pub protonode_input: CompiledProtonodeInput,
}
@@ -1,19 +1,23 @@
use super::VectorDataDomain;
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, LayoutTarget, WidgetLayout};
use crate::messages::portfolio::spreadsheet::InspectInputConnector;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::*;
use graph_craft::document::OutputConnector;
use graphene_std::Color;
use graphene_std::GraphicGroupTable;
use graphene_std::instances::Instances;
use graphene_std::raster::Image;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::uuid::{NodeId, SNI};
use graphene_std::vector::{VectorData, VectorDataTable};
use graphene_std::{Artboard, ArtboardGroupTable, GraphicElement};
use std::sync::Arc;
#[derive(ExtractField)]
pub struct SpreadsheetMessageHandlerData<'a> {
pub introspected_data: &'a HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>,
pub introspected_data: &'a HashMap<SNI, Option<Arc<dyn std::any::Any + Send + Sync>>>,
// Network interface of the selected document
pub network_interface: &'a NodeNetworkInterface,
}
/// The spreadsheet UI allows for instance data to be previewed.
@@ -21,50 +25,78 @@ pub struct SpreadsheetMessageHandlerData<'a> {
pub struct SpreadsheetMessageHandler {
/// Sets whether or not the spreadsheet is drawn.
pub spreadsheet_view_open: bool,
inspect_input: Option<InspectInputConnector>,
// Downcasted data is not saved because the spreadsheet is simply a window into the data flowing through the input
// introspected_data: Option<TaggedValue>,
// Path to the document node that is introspected. The protonode is found by traversing from the primary output
inspection_data: Option<Option<Arc<dyn std::any::Any + Send + Sync>>>,
node_to_inspect: Option<NodeId>,
instances_path: Vec<usize>,
viewing_vector_data_domain: VectorDataDomain,
}
#[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) {
let SpreadsheetMessageHandlerData { introspected_data } = data;
let SpreadsheetMessageHandlerData { introspected_data, network_interface } = data;
match message {
SpreadsheetMessage::ToggleOpen => {
self.spreadsheet_view_open = !self.spreadsheet_view_open;
if self.spreadsheet_view_open {
// TODO: This will not get always get data since the input could be cached, and the monitor node would not
// Be run on the evaluation. To solve this, pass in an AbsoluteNodeInput as a parameter to the compilation which tells the compiler
// to generate a random SNI in order to reset any downstream cache
// Run the graph to grab the data
responses.add(PortfolioMessage::EvaluateActiveDocument);
responses.add(SpreadsheetMessage::RequestUpdateLayout);
}
// Update checked UI state for open
responses.add(MenuBarMessage::SendLayout);
self.update_layout(introspected_data, responses);
self.update_layout(responses);
}
// Queued on introspection request, runs on introspection response when the data has been sent back to the editor
SpreadsheetMessage::UpdateLayout { inspect_input } => {
self.inspect_input = Some(inspect_input);
self.update_layout(introspected_data, responses);
}
SpreadsheetMessage::RequestUpdateLayout => {
// Spreadsheet not open, no need to request
if !self.spreadsheet_view_open {
self.node_to_inspect = None;
return;
}
let selected_nodes = network_interface.selected_nodes().0;
// Selected nodes != 1, skipping
if selected_nodes.len() != 1 {
self.node_to_inspect = None;
return;
}
let node_to_inspect = selected_nodes[0];
let Some(protonode_id) = network_interface.protonode_from_output(&OutputConnector::node(node_to_inspect, 0), &[]) else {
return;
};
let mut nodes_to_introspect = HashSet::new();
nodes_to_introspect.insert(protonode_id);
responses.add(PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect });
responses.add(Message::StartIntrospectionQueue);
responses.add(SpreadsheetMessage::ProcessUpdateLayout { node_to_inspect, protonode_id });
responses.add(Message::EndIntrospectionQueue);
self.update_layout(responses);
}
// Runs after the introspection request has returned the Arc back to the editor
SpreadsheetMessage::ProcessUpdateLayout { node_to_inspect, protonode_id } => {
self.node_to_inspect = Some(node_to_inspect);
self.inspection_data = introspected_data.get(&protonode_id).cloned();
}
SpreadsheetMessage::PushToInstancePath { index } => {
self.instances_path.push(index);
self.update_layout(introspected_data, responses);
self.update_layout(responses);
}
SpreadsheetMessage::TruncateInstancePath { len } => {
self.instances_path.truncate(len);
self.update_layout(introspected_data, responses);
self.update_layout(responses);
}
SpreadsheetMessage::ViewVectorDataDomain { domain } => {
self.viewing_vector_data_domain = domain;
self.update_layout(introspected_data, responses);
self.update_layout(responses);
}
}
}
@@ -75,7 +107,7 @@ impl MessageHandler<SpreadsheetMessage, SpreadsheetMessageHandlerData> for Sprea
}
impl SpreadsheetMessageHandler {
fn update_layout(&mut self, introspected_data: &HashMap<CompiledProtonodeInput, Option<Arc<dyn std::any::Any + Send + Sync>>>, responses: &mut VecDeque<Message>) {
fn update_layout(&mut self, responses: &mut VecDeque<Message>) {
responses.add(FrontendMessage::UpdateSpreadsheetState {
// The node is sent when the data is available
node: None,
@@ -90,21 +122,21 @@ impl SpreadsheetMessageHandler {
breadcrumbs: Vec::new(),
vector_data_domain: self.viewing_vector_data_domain,
};
let mut layout = match &self.inspect_input {
Some(inspect_input) => {
match introspected_data.get(&inspect_input.protonode_input) {
let mut layout = match &self.node_to_inspect {
Some(_) => {
match &self.inspection_data {
Some(data) => match data {
Some(instrospected_data) => match generate_layout(instrospected_data, &mut layout_data) {
Some(inspected_data) => match generate_layout(&inspected_data, &mut layout_data) {
Some(layout) => layout,
None => label("The introspected data is not a supported type to be displayed."),
},
None => label("Introspected data is not available for this input. This input may be cached."),
},
// There should always be an entry for each protonode input. If its empty then it was not requested or an error occured
None => label("Error getting introspected data"),
None => label("The output of this node could not be determined"),
}
}
None => label("No input selected to show data for."),
None => label("No node selected to show data for."),
};
if layout_data.breadcrumbs.len() > 1 {
@@ -331,21 +331,21 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Path
responses.add(ToolMessage::UpdateHints);
let pivot_gizmo = self.tool_data.pivot_gizmo();
responses.add(TransformLayerMessage::SetPivotGizmo { pivot_gizmo });
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
self.send_layout(responses, LayoutTarget::ToolOptions);
}
}
PathOptionsUpdate::TogglePivotGizmoType(state) => {
self.tool_data.pivot_gizmo.state.disabled = !state;
responses.add(ToolMessage::UpdateHints);
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
self.send_layout(responses, LayoutTarget::ToolOptions);
}
PathOptionsUpdate::TogglePivotPinned => {
self.tool_data.pivot_gizmo.pivot.pinned = !self.tool_data.pivot_gizmo.pivot.pinned;
responses.add(ToolMessage::UpdateHints);
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
self.send_layout(responses, LayoutTarget::ToolOptions);
}
},
@@ -2407,7 +2407,7 @@ impl Fsm for PathToolFsmState {
tool_data.pivot_gizmo.pivot.set_normalized_position(position.unwrap());
let pivot_gizmo = tool_data.pivot_gizmo();
responses.add(TransformLayerMessage::SetPivotGizmo { pivot_gizmo });
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
self
}
@@ -289,21 +289,21 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Sele
responses.add(ToolMessage::UpdateHints);
let pivot_gizmo = self.tool_data.pivot_gizmo();
responses.add(TransformLayerMessage::SetPivotGizmo { pivot_gizmo });
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
redraw_reference_pivot = true;
}
}
SelectOptionsUpdate::TogglePivotGizmoType(state) => {
self.tool_data.pivot_gizmo.state.disabled = !state;
responses.add(ToolMessage::UpdateHints);
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
redraw_reference_pivot = true;
}
SelectOptionsUpdate::TogglePivotPinned => {
self.tool_data.pivot_gizmo.pivot.pinned = !self.tool_data.pivot_gizmo.pivot.pinned;
responses.add(ToolMessage::UpdateHints);
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
redraw_reference_pivot = true;
}
}
@@ -1255,7 +1255,7 @@ impl Fsm for SelectToolFsmState {
tool_data.pivot_gizmo.pivot.set_viewport_position(snapped_mouse_position);
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
// Auto-panning
let messages = [
@@ -1611,7 +1611,7 @@ impl Fsm for SelectToolFsmState {
let pivot_gizmo = tool_data.pivot_gizmo();
responses.add(TransformLayerMessage::SetPivotGizmo { pivot_gizmo });
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(PortfolioMessage::CompileActiveDocument);
self
}
+3 -36
View File
@@ -7,10 +7,9 @@ use graph_craft::document::value::{EditorMetadata, RenderOutput, TaggedValue};
use graph_craft::document::{CompilationMetadata, DocumentNode, NodeNetwork, generate_uuid};
use graph_craft::proto::GraphErrors;
use graphene_std::any::EditorContext;
use graphene_std::memo::IntrospectMode;
use graphene_std::renderer::format_transform_matrix;
use graphene_std::text::FontCache;
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
use graphene_std::uuid::SNI;
mod runtime_io;
pub use runtime_io::NodeRuntimeIO;
@@ -47,7 +46,7 @@ pub struct EvaluationResponse {
}
#[derive(Debug, Clone, Default)]
pub struct IntrospectionResponse(pub Vec<((NodeId, usize), IntrospectMode, Option<Arc<dyn std::any::Any + Send + Sync>>)>);
pub struct IntrospectionResponse(pub Vec<(SNI, Option<Arc<dyn std::any::Any + Send + Sync>>)>);
impl PartialEq for IntrospectionResponse {
fn eq(&self, _other: &Self) -> bool {
@@ -132,7 +131,7 @@ impl NodeGraphExecutor {
self.futures.insert(evaluation_id, evaluation_context);
}
pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<CompiledProtonodeInput>) {
pub fn submit_node_graph_introspection(&mut self, nodes_to_introspect: HashSet<SNI>) {
if let Err(error) = self.runtime_io.send(GraphRuntimeRequest::IntrospectionRequest(nodes_to_introspect)) {
log::error!("Could not send evaluation request. {:?}", error);
return;
@@ -375,35 +374,3 @@ 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,
}
}
}
+13 -17
View File
@@ -1,10 +1,10 @@
use super::*;
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use glam::DVec2;
use graph_craft::ProtoNodeIdentifier;
use graph_craft::document::NodeNetwork;
use graph_craft::proto::GraphErrors;
use graphene_std::text::FontCache;
use graphene_std::uuid::CompiledProtonodeInput;
use graphene_std::wasm_application_io::WasmApplicationIo;
use interpreted_executor::dynamic_executor::DynamicExecutor;
use interpreted_executor::util::wrap_network_in_scope;
@@ -31,9 +31,6 @@ pub struct NodeRuntime {
/// Mapping of the fully-qualified node paths to their preprocessor substitutions.
substitutions: HashMap<ProtoNodeIdentifier, DocumentNode>,
/// Stored in order to check for changes before sending to the frontend.
thumbnail_render_tagged_values: HashMap<CompiledProtonodeInput, TaggedValue>,
}
/// Messages passed from the editor thread to the node runtime thread.
@@ -49,7 +46,7 @@ pub enum GraphRuntimeRequest {
// ThumbnailRenderRequest(HashSet<CompiledProtonodeInput>),
// Request the data from a list of node inputs. For example, used by vector modify to get the data at the input of every Path node.
// Can also be used by the spreadsheet/introspection system
IntrospectionRequest(HashSet<CompiledProtonodeInput>),
IntrospectionRequest(HashSet<SNI>),
}
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -102,7 +99,7 @@ impl NodeRuntime {
// self.application_io = Some(Arc::new(WasmApplicationIo::new_offscreen().await));
}
// TODO: This deduplication of messages will probably cause issues
// TODO: This deduplication of messages may cause issues
let mut compilation = None;
let mut evaluation = None;
let mut introspection = None;
@@ -143,20 +140,20 @@ impl NodeRuntime {
self.sender.send_evaluation_response(EvaluationResponse { evaluation_id, result });
}
// GraphRuntimeRequest::ThumbnailRenderRequest(_) => {}
GraphRuntimeRequest::IntrospectionRequest(inputs) => {
let mut introspected_inputs = Vec::new();
for protonode_input in inputs {
let introspected_data = match self.executor.introspect(protonode_input, IntrospectMode::Data) {
GraphRuntimeRequest::IntrospectionRequest(nodes) => {
let mut introspected_nodes = Vec::new();
for protonode in nodes {
let introspected_data = match self.executor.introspect(protonode, true) {
Ok(introspected_data) => introspected_data,
Err(e) => {
log::error!("Could not introspect input: {:?}, error: {:?}", protonode_input, e);
log::error!("Could not introspect protonode: {:?}, error: {:?}", protonode, e);
continue;
}
};
introspected_inputs.push((protonode_input, IntrospectMode::Data, introspected_data));
introspected_nodes.push((protonode, introspected_data));
}
self.sender.send_introspection_response(IntrospectionResponse(introspected_inputs));
self.sender.send_introspection_response(IntrospectionResponse(introspected_nodes));
}
}
}
@@ -173,8 +170,8 @@ impl NodeRuntime {
// Modifies the NodeNetwork so the tagged values are removed and the document nodes with protonode implementations have their protonode ids set
// Needs to return a mapping of absolute input connectors to protonode callers, types for protonodes, and callers for protonodes, add/remove delta for resolved types
let (proto_network, protonode_caller_for_values, protonode_caller_for_nodes) = match scoped_network.flatten() {
Ok(network) => network,
let (proto_network, original_locations) = match scoped_network.flatten() {
Ok(result) => result,
Err(e) => {
log::error!("Error compiling network: {e:?}");
return Err(e);
@@ -186,8 +183,7 @@ impl NodeRuntime {
// Used to remove thumbnails from the mapping of SNI to rendered SVG strings on the frontend, which occurs when the SNI is removed
// When native frontend rendering is possible, the strings can just be stored in the network interface for each protonode with the rest of the type metadata
Ok(CompilationMetadata {
protonode_caller_for_values,
protonode_caller_for_nodes,
original_locations,
types_to_add,
types_to_remove,
})