diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index d9a6905997..53cd56f492 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -17,7 +17,7 @@ use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ}; -use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate, OutputConnector}; +use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::nodes::RawBuffer; use crate::messages::portfolio::utility_types::PanelType; use crate::messages::portfolio::utility_types::PersistentData; @@ -2820,16 +2820,11 @@ impl DocumentMessageHandler { .tooltip("Add an operation to the end of this layer's chain of nodes") .disabled(!has_selection || has_multiple_selection) .popover_layout({ - // Showing only compatible types + // Showing only compatible types for the layer based on the output type of the node upstream from its horizontal input let compatible_type = selected_layer.and_then(|layer| { - let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &self.network_interface); - let node_type = graph_layer.horizontal_layer_flow().nth(1); - if let Some(node_id) = node_type { - let (output_type, _) = self.network_interface.output_type(&OutputConnector::node(node_id, 0), &self.selection_network_path); - Some(format!("type:{}", output_type.nested_type())) - } else { - None - } + self.network_interface + .upstream_output_connector(&InputConnector::node(layer.to_node(), 1), &[]) + .and_then(|upstream_output| self.network_interface.output_type(&upstream_output, &[]).add_node_string()) }); let mut node_chooser = NodeCatalog::new(); diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index 80e005448f..8ecc086249 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -303,8 +303,8 @@ impl<'a> ModifyInputsContext<'a> { // If inserting a 'Path' node, insert a 'Flatten Path' node if the type is `Graphic`. // TODO: Allow the 'Path' node to operate on table data by utilizing the reference (index or ID?) for each row. if node_definition.identifier == "Path" { - let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]).0.nested_type().clone(); - if layer_input_type == concrete!(Table) { + let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]); + if layer_input_type.compiled_nested_type() == Some(&concrete!(Table)) { let Some(flatten_path_definition) = resolve_document_node_type("Flatten Path") else { log::error!("Flatten Path does not exist in ModifyInputsContext::existing_node_id"); return None; diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 379da12328..750a6c4b53 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -10,13 +10,13 @@ use crate::messages::portfolio::document::node_graph::utility_types::{ContextMen use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::network_interface::{ - self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, TypeSource, + self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, }; use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry}; use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; -use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_clip_mode}; +use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed; use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; @@ -881,7 +881,7 @@ impl<'a> MessageHandler> for NodeG }; let Some(output_connector) = output_connector else { return }; self.wire_in_progress_from_connector = network_interface.output_position(&output_connector, selection_network_path); - self.wire_in_progress_type = FrontendGraphDataType::from_type(&network_interface.input_type(clicked_input, breadcrumb_network_path).0); + self.wire_in_progress_type = network_interface.input_type(clicked_input, breadcrumb_network_path).displayed_type(); return; } @@ -891,8 +891,8 @@ impl<'a> MessageHandler> for NodeG self.initial_disconnecting = false; self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path); - let (output_type, source) = &network_interface.output_type(&clicked_output, breadcrumb_network_path); - self.wire_in_progress_type = FrontendGraphDataType::displayed_type(output_type, source); + let output_type = network_interface.output_type(&clicked_output, breadcrumb_network_path); + self.wire_in_progress_type = output_type.displayed_type(); self.update_node_graph_hints(responses); return; @@ -1210,21 +1210,17 @@ impl<'a> MessageHandler> for NodeG } // Get the output types from the network interface - let (output_type, type_source) = network_interface.output_type(&output_connector, selection_network_path); let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { warn!("No network_metadata"); return; }; - let compatible_type = match type_source { - TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None, - _ => Some(format!("type:{}", output_type.nested_type())), - }; - let appear_right_of_mouse = if ipp.mouse.position.x > viewport.size().y() - 173. { -173. } else { 0. }; let appear_above_mouse = if ipp.mouse.position.y > viewport.size().y() - 34. { -34. } else { 0. }; let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x; + let compatible_type = network_interface.output_type(&output_connector, selection_network_path).add_node_string(); + self.context_menu = Some(ContextMenuInformation { context_menu_coordinates: ((point.x + node_graph_shift.x) as i32, (point.y + node_graph_shift.y) as i32), context_menu_data: ContextMenuData::CreateNode { compatible_type }, @@ -1999,12 +1995,7 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::SendGraph); } NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => { - 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()); - } + network_interface.resolved_types.update(resolved_types); self.node_graph_errors = node_graph_errors; } NodeGraphMessage::UpdateActionButtons => { @@ -2118,16 +2109,7 @@ impl NodeGraphMessageHandler { .popover_layout({ // Showing only compatible types let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) { - (true, false, Some(layer)) => { - let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface); - let node_type = graph_layer.horizontal_layer_flow().nth(1); - if let Some(node_id) = node_type { - let (output_type, _) = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); - Some(format!("type:{}", output_type.nested_type())) - } else { - None - } - } + (true, false, Some(layer)) => network_interface.output_type(&OutputConnector::node(layer.to_node(), 1), &[]).add_node_string(), _ => None, }; @@ -2440,17 +2422,10 @@ impl NodeGraphMessageHandler { .icon(Some("Node".to_string())) .tooltip("Add an operation to the end of this layer's chain of nodes") .popover_layout({ - let layer_identifier = LayerNodeIdentifier::new(layer, context.network_interface); - let compatible_type = { - let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer_identifier, context.network_interface); - let node_type = graph_layer.horizontal_layer_flow().nth(1); - if let Some(node_id) = node_type { - let (output_type, _) = context.network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); - Some(format!("type:{}", output_type.nested_type())) - } else { - None - } - }; + let compatible_type = context + .network_interface + .upstream_output_connector(&InputConnector::node(layer, 1), &[]) + .and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string()); let mut node_chooser = NodeCatalog::new(); node_chooser.intial_search = compatible_type.unwrap_or("".to_string()); diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 184df8bf34..7c3706a4b7 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -8,9 +8,9 @@ use crate::messages::prelude::*; use choice::enum_choice; use dyn_any::DynAny; use glam::{DAffine2, DVec2}; -use graph_craft::Type; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput}; +use graph_craft::{Type, concrete}; use graphene_std::NodeInputDecleration; use graphene_std::animation::RealTimeMode; use graphene_std::extract_xy::XY; @@ -85,7 +85,7 @@ pub fn start_widgets(parameter_widgets_info: ParameterWidgetsInfo) -> Vec Vec().for_socket(parameter_info).property_row(); let document_node = match get_document_node(node_id, context) { @@ -1185,7 +1185,7 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp use graphene_std::raster::selective_color::*; let mut default_info = ParameterWidgetsInfo::new(node_id, ColorsInput::INDEX, true, context); - default_info.exposeable = false; + default_info.exposable = false; let colors = enum_choice::().for_socket(default_info).property_row(); let document_node = match get_document_node(node_id, context) { @@ -1574,6 +1574,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper let mut display_decimal_places = None; let mut step = None; let mut unit_suffix = None; + let input_type = match implementation { DocumentNodeImplementation::ProtoNode(proto_node_identifier) => 'early_return: { if let Some(field) = graphene_std::registry::NODE_METADATA @@ -1610,7 +1611,12 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper input_type.clone() } - _ => context.network_interface.input_type(&InputConnector::node(node_id, input_index), context.selection_network_path).0, + _ => context + .network_interface + .input_type(&InputConnector::node(node_id, input_index), context.selection_network_path) + .compiled_nested_type() + .cloned() + .unwrap_or(concrete!(())), }; property_from_type(node_id, input_index, &input_type, number_options, unit_suffix, display_decimal_places, step, context).unwrap_or_else(|value| value) @@ -1989,13 +1995,16 @@ pub struct ParameterWidgetsInfo<'a> { description: String, input_type: FrontendGraphDataType, blank_assist: bool, - exposeable: bool, + exposable: bool, } impl<'a> ParameterWidgetsInfo<'a> { pub fn new(node_id: NodeId, index: usize, blank_assist: bool, context: &'a mut NodePropertiesContext) -> ParameterWidgetsInfo<'a> { let (name, description) = context.network_interface.displayed_input_name_and_description(&node_id, index, context.selection_network_path); - let input_type = FrontendGraphDataType::from_type(&context.network_interface.input_type(&InputConnector::node(node_id, index), context.selection_network_path).0); + let input_type = context + .network_interface + .input_type(&InputConnector::node(node_id, index), context.selection_network_path) + .displayed_type(); let document_node = context.network_interface.document_node(&node_id, context.selection_network_path); ParameterWidgetsInfo { @@ -2006,7 +2015,7 @@ impl<'a> ParameterWidgetsInfo<'a> { description, input_type, blank_assist, - exposeable: true, + exposable: true, } } } diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index c22df85c39..dcab6e9417 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,4 +1,3 @@ -use crate::messages::portfolio::document::utility_types::network_interface::TypeSource; use glam::IVec2; use graph_craft::document::NodeId; use graph_craft::document::value::TaggedValue; @@ -41,13 +40,6 @@ impl FrontendGraphDataType { _ => Self::General, } } - - pub fn displayed_type(input: &Type, type_source: &TypeSource) -> Self { - match type_source { - TypeSource::Error(_) | TypeSource::RandomProtonodeImplementation => Self::General, - _ => Self::from_type(input), - } - } } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 2920cc360e..283925c587 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1,5 +1,6 @@ mod deserialization; mod memo_network; +mod resolved_types; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations}; use super::misc::PTZ; @@ -7,30 +8,27 @@ use super::nodes::SelectedNodes; use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type}; -use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput}; +use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput}; +use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes; use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode; use deserialization::deserialize_node_persistent_metadata; use glam::{DAffine2, DVec2, IVec2}; +use graph_craft::Type; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork}; -use graph_craft::{Type, concrete}; -use graphene_std::Artboard; use graphene_std::ContextDependencies; use graphene_std::math::quad::Quad; use graphene_std::subpath::Subpath; -use graphene_std::table::Table; use graphene_std::transform::Footprint; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::{PointId, Vector, VectorModificationType}; -use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes; -use interpreted_executor::node_registry::NODE_REGISTRY; use kurbo::BezPath; use memo_network::MemoNetwork; use serde_json::{Value, json}; use std::collections::{HashMap, HashSet, VecDeque}; -use std::hash::{DefaultHasher, Hash, Hasher}; +use std::hash::Hash; use std::ops::Deref; /// All network modifications should be done through this API, so the fields cannot be public. However, all fields within this struct can be public since it it not possible to have a public mutable reference. @@ -458,12 +456,12 @@ impl NodeNetworkInterface { *input = NodeInput::Node { node_id: new_id, output_index }; } else { // Disconnect node input if it is not connected to another node in new_ids - let tagged_value = TaggedValue::from_type_or_none(&self.input_type(&InputConnector::node(*node_id, input_index), network_path).0); + let tagged_value = self.tagged_value_from_input(&InputConnector::node(*node_id, input_index), network_path); *input = NodeInput::value(tagged_value, true); } } else if let &mut NodeInput::Import { .. } = input { // Always disconnect network node input - let tagged_value = TaggedValue::from_type_or_none(&self.input_type(&InputConnector::node(*node_id, input_index), network_path).0); + let tagged_value = self.tagged_value_from_input(&InputConnector::node(*node_id, input_index), network_path); *input = NodeInput::value(tagged_value, true); } } @@ -471,8 +469,8 @@ impl NodeNetworkInterface { } /// Try and get the [`DocumentNodeDefinition`] for a node - pub fn get_node_definition(&self, network_path: &[NodeId], node_id: NodeId) -> Option<&DocumentNodeDefinition> { - let metadata = self.node_metadata(&node_id, network_path)?; + pub fn get_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()?) } @@ -493,278 +491,6 @@ impl NodeNetworkInterface { } } - /// Try and get the [`Type`] for any [`InputConnector`] based on the `self.resolved_types`. - fn node_type_from_compiled(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<(Type, TypeSource)> { - let (node_id, input_index) = match *input_connector { - InputConnector::Node { node_id, input_index } => (node_id, input_index), - InputConnector::Export(export_index) => { - let Some((encapsulating_node_id, encapsulating_node_id_path)) = network_path.split_last() else { - // The outermost network export defaults to a Table. - return Some((concrete!(Table), TypeSource::OuterMostExportDefault)); - }; - - let output_type = self.output_type(&OutputConnector::node(*encapsulating_node_id, export_index), encapsulating_node_id_path); - return Some(output_type); - } - }; - let Some(node) = self.document_node(&node_id, network_path) else { - log::error!("Could not get node {node_id} in input_type"); - return None; - }; - // If the input_connector is a NodeInput::Value, return the type of the tagged value. - if let Some(value) = node.inputs.get(input_index).and_then(|input| input.as_value()) { - return Some((value.ty(), TypeSource::TaggedValue)); - } - let node_id_path = [network_path, &[node_id]].concat(); - match &node.implementation { - DocumentNodeImplementation::Network(_nested_network) => { - // Attempt to resolve where this import is within the nested network (it may be connected to the node or directly to an export) - 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(); - - if inputs_using_import.is_some_and(|inputs| inputs.len() > 1) { - warn!("Found multiple inputs using an import. Using the type of the first one."); - } - - if let Some(input_connector) = first_input { - self.node_type_from_compiled(&input_connector, &node_id_path) - } - // Nothing is connected to the import - else { - None - } - } - DocumentNodeImplementation::ProtoNode(_) => { - // Offset the input index by 1 since the proto node also includes the type of the input passed as a call argument. - self.resolved_types - .types - .get(node_id_path.as_slice()) - .and_then(|node_types| node_types.inputs.get(input_index + 1).cloned()) - .map(|node_types| (node_types, TypeSource::Compiled)) - } - DocumentNodeImplementation::Extract => None, - } - } - - /// Guess the type from the node based on a document node default or a random protonode definition. - fn guess_type_from_node(&mut self, network_path: &mut Vec, node_id: NodeId, input_index: usize) -> (Type, TypeSource) { - // Try and get the default value from the document node definition - if let Some(value) = self - .get_node_definition(network_path, node_id) - .and_then(|definition| definition.node_template.document_node.inputs.get(input_index)) - .and_then(|input| input.as_value()) - { - return (value.ty(), TypeSource::DocumentNodeDefault); - } - - let Some(node) = self.document_node(&node_id, network_path) else { - return (concrete!(()), TypeSource::Error("node id {node_id:?} not in network {network_path:?}")); - }; - - let node_id_path = [network_path.as_slice(), &[node_id]].concat(); - match &node.implementation { - DocumentNodeImplementation::ProtoNode(protonode) => { - let Some(node_types) = random_protonode_implementation(protonode) else { - return (concrete!(()), TypeSource::Error("could not resolve protonode")); - }; - - let skip_footprint = 1; - - let Some(input_type) = std::iter::once(node_types.call_argument.clone()).chain(node_types.inputs.clone()).nth(input_index + skip_footprint) else { - // log::warn!("Could not get type for {node_id_path:?}, input: {input_index}"); - return (concrete!(()), TypeSource::Error("could not get the protonode's input")); - }; - - (input_type, 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(); - - if let Some(InputConnector::Node { - node_id: child_id, - input_index: child_input_index, - }) = first_input - { - network_path.push(node_id); - let result = self.guess_type_from_node(network_path, child_id, child_input_index); - network_path.pop(); - return result; - } - - // Input is disconnected - (concrete!(()), TypeSource::Error("disconnected network input")) - } - _ => (concrete!(()), TypeSource::Error("implementation is not network or protonode")), - } - } - - /// Get the [`Type`] for any InputConnector - pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> (Type, TypeSource) { - if let Some(result) = self.node_type_from_compiled(input_connector, network_path) { - return result; - } - - // 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(&mut network_path.to_vec(), node_id, input_connector.input_index()) - } - - pub fn valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec { - let InputConnector::Node { node_id, input_index } = input_connector else { - // An export can have any type connected to it - return vec![graph_craft::generic!(T)]; - }; - let Some(implementation) = self.implementation(node_id, network_path) else { - log::error!("Could not get node implementation in valid_input_types"); - return Vec::new(); - }; - match implementation { - DocumentNodeImplementation::Network(_) => { - let nested_path = [network_path, &[*node_id]].concat(); - let Some(outward_wires) = self.outward_wires(&nested_path) else { - log::error!("Could not get outward wires in valid_input_types"); - return Vec::new(); - }; - let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else { - log::error!("Could not get inputs from import in valid_input_types"); - return Vec::new(); - }; - - let intersection: HashSet = inputs_from_import - .clone() - .iter() - .map(|input_connector| self.valid_input_types(input_connector, &nested_path)) - .map(|vec| vec.into_iter().collect::>()) - .fold(None, |acc: Option>, set| match acc { - Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()), - None => Some(set), - }) - .unwrap_or_default(); - - intersection.into_iter().collect::>() - } - DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { - let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { - log::error!("Protonode {proto_node_identifier:?} not found in registry"); - return Vec::new(); - }; - let number_of_inputs = self.number_of_inputs(node_id, network_path); - implementations - .iter() - .filter_map(|(node_io, _)| { - 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; - // 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) -> Vector would not be compatible with () -> Vector - 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 } - }) - .collect::>() - } - DocumentNodeImplementation::Extract => { - log::error!("Input types for extract node not supported"); - Vec::new() - } - } - } - - /// 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>` 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(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> (Type, TypeSource) { - match output_connector { - OutputConnector::Node { node_id, output_index } => { - 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(&OutputConnector::node(*nested_node_id, *output_index), &[network_path, &[*node_id]].concat()), - NodeInput::Value { tagged_value, .. } => (tagged_value.ty(), TypeSource::TaggedValue), - NodeInput::Import { .. } => { - // 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")), - } - } - OutputConnector::Import(import_index) => { - let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else { - log::error!("Cannot get type of import in document network"); - return (concrete!(()), TypeSource::Error("Cannot get import type in document network")); - }; - self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) - } - } - } - pub fn position(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option { let top_left_position = self .node_click_targets(node_id, network_path) @@ -897,8 +623,10 @@ impl NodeNetworkInterface { if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) { return None; } - let (export_type, source) = self.input_type(input_connector, network_path); - let data_type = FrontendGraphDataType::displayed_type(&export_type, &source); + let input_type = self.input_type(input_connector, network_path); + let data_type = input_type.displayed_type(); + let resolved_type = input_type.resolved_type_name(); + let connected_to = self .upstream_output_connector(input_connector, network_path) .map(|output_connector| match output_connector { @@ -928,8 +656,8 @@ impl NodeNetworkInterface { let export_name = if !export_name.is_empty() { export_name - } else if *export_type.nested_type() != concrete!(()) { - export_type.nested_type().to_string() + } else if let Some(export_type_name) = input_type.compiled_nested_type_name() { + export_type_name } else { format!("Export index {}", export_index) }; @@ -937,11 +665,12 @@ impl NodeNetworkInterface { (export_name, String::new()) } }; + Some(FrontendGraphInput { data_type, + resolved_type, name, description, - resolved_type: format!("{export_type:?}"), valid_types: self.valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(), connected_to, }) @@ -949,7 +678,8 @@ impl NodeNetworkInterface { /// Returns None if there is an error, it is the document network, a hidden primary output or import pub fn frontend_output_from_connector(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option { - let (output_type, type_source) = self.output_type(output_connector, network_path); + let output_type = self.output_type(output_connector, network_path); + let (name, description) = match output_connector { OutputConnector::Node { node_id, output_index } => { // Do not display the primary output port for a node if it is a network node with a hidden primary export @@ -960,14 +690,7 @@ impl NodeNetworkInterface { let node_metadata = self.node_metadata(node_id, network_path)?; let output_name = node_metadata.persistent_metadata.output_names.get(*output_index).cloned().unwrap_or_default(); - let output_name = if !output_name.is_empty() { - output_name - } else if *output_type.nested_type() != concrete!(()) { - output_type.nested_type().to_string() - } else { - format!("Output {}", *output_index + 1) - }; - + let output_name = if !output_name.is_empty() { output_name } else { output_type.resolved_type_name() }; (output_name, String::new()) } OutputConnector::Import(import_index) => { @@ -982,17 +705,19 @@ impl NodeNetworkInterface { }; let (import_name, description) = self.displayed_input_name_and_description(encapsulating_node_id, *import_index, encapsulating_path); - let import_name = if *output_type.nested_type() != concrete!(()) { + let import_name = if !import_name.is_empty() { import_name + } else if let Some(import_type_name) = output_type.compiled_nested_type_name() { + import_type_name } else { - format!("Import index {}", *import_index) + format!("Import index {}", import_index) }; + (import_name, description) } }; - - let data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source); - + let data_type = output_type.displayed_type(); + let resolved_type = output_type.resolved_type_name(); let mut connected_to = self .outward_wires(network_path) .and_then(|outward_wires| outward_wires.get(output_connector)) @@ -1017,8 +742,8 @@ impl NodeNetworkInterface { Some(FrontendGraphOutput { data_type, + resolved_type, name, - resolved_type: format!("{:?}", output_type), description, connected_to, }) @@ -1252,7 +977,7 @@ impl NodeNetworkInterface { }; let description = input_metadata.input_description.to_string(); let name = if input_metadata.input_name.is_empty() { - self.input_type(&InputConnector::node(*node_id, input_index), network_path).0.nested_type().to_string() + self.input_type(&InputConnector::node(*node_id, input_index), network_path).resolved_type_name() } else { input_metadata.input_name.to_string() }; @@ -1288,9 +1013,9 @@ impl NodeNetworkInterface { /// Returns the description of the node, or an empty string if it is not set. pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { - self.get_node_definition(network_path, *node_id) + self.get_node_definition(node_id, network_path) .map(|node_definition| node_definition.description.to_string()) - .filter(|description| description != "TODO") + .filter(|description: &String| description != "TODO") .unwrap_or_default() } @@ -1718,30 +1443,6 @@ impl NodeNetworkInterface { } } -/// Gets the type for a random protonode implementation (used if there is no type from the compiled network) -fn random_protonode_implementation(protonode: &graph_craft::ProtoNodeIdentifier) -> Option<&graphene_std::NodeIOTypes> { - let mut protonode = protonode.clone(); - // TODO: Remove - if let Some((path, _generics)) = protonode.name.split_once('<') { - protonode = path.to_string().to_string().into(); - } - let Some(node_io_hashmap) = NODE_REGISTRY.get(&protonode) else { - log::error!("Could not get hashmap for proto node: {protonode:?}"); - return None; - }; - - let node_types = node_io_hashmap.keys().min_by_key(|node_io_types| { - let mut hasher = DefaultHasher::new(); - node_io_types.hash(&mut hasher); - hasher.finish() - }); - - if node_types.is_none() { - log::error!("Could not get node_types from hashmap"); - }; - node_types -} - // Private mutable getters for use within the network interface impl NodeNetworkInterface { fn network_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetwork> { @@ -2781,7 +2482,7 @@ impl NodeNetworkInterface { let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style); let path_string = vector_wire.to_svg(); - let data_type = FrontendGraphDataType::from_type(&self.input_type(&input, network_path).0); + let data_type = self.input_type(&input, network_path).displayed_type(); let wire_path_update = Some(WirePath { path_string, data_type, @@ -2819,7 +2520,7 @@ impl NodeNetworkInterface { pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option { let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?; let path_string = vector_wire.to_svg(); - let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0); + let data_type = self.input_type(input, network_path).displayed_type(); Some(WirePath { path_string, data_type, @@ -4504,7 +4205,7 @@ impl NodeNetworkInterface { } } - let tagged_value = TaggedValue::from_type_or_none(&self.input_type(input_connector, network_path).0); + let tagged_value = self.tagged_value_from_input(input_connector, network_path); let value_input = NodeInput::value(tagged_value, true); @@ -6124,22 +5825,6 @@ impl Iterator for FlowIter<'_> { } } -// TODO: Refactor to be Unknown, Compiled(Type) for NodeInput::Node, or Value(Type) for NodeInput::Value -/// Represents the source of a resolved type (for debugging). -/// There will be two valid types list. One for the current valid types that will not cause a node graph error, -/// based on the other inputs to that node and returned during compilation. THe other list will be all potential -/// Valid types, based on the protonode implementation/downstream users. -#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum TypeSource { - Compiled, - RandomProtonodeImplementation, - DocumentNodeDefault, - TaggedValue, - OuterMostExportDefault, - - Error(&'static str), -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] pub enum ImportOrExport { Import(usize), diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs new file mode 100644 index 0000000000..695a435c6d --- /dev/null +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs @@ -0,0 +1,305 @@ +use std::collections::{HashMap, HashSet}; + +use graph_craft::document::value::TaggedValue; +use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput}; +use graph_craft::{Type, concrete}; +use graphene_std::uuid::NodeId; +use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta}; +use interpreted_executor::node_registry::NODE_REGISTRY; + +use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; +use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector}; + +// This file contains utility methods for interfacing with the resolved types returned from the compiler +#[derive(Debug, Default)] +pub struct ResolvedDocumentNodeTypes { + pub types: HashMap, NodeTypes>, +} + +impl ResolvedDocumentNodeTypes { + pub fn update(&mut self, delta: ResolvedDocumentNodeTypesDelta) { + for (path, node_type) in delta.add { + self.types.insert(path.to_vec(), node_type); + } + for path in delta.remove { + self.types.remove(&path.to_vec()); + } + } +} + +/// Represents the result of a type query for an input or output connector. +#[derive(Debug, Clone, PartialEq)] +pub enum TypeSource { + // A type that has been compiled based on all upstream types + Compiled(Type), + // The type of value inputs + TaggedValue(Type), + // A type that is guessed from the document node definition + DocumentNodeDefinition(Type), + // When the input is not compiled, the type is unknown and must be guessed from the valid types + Unknown, + + Error(&'static str), +} + +impl TypeSource { + pub fn compiled_nested_type(&self) -> Option<&Type> { + match self { + TypeSource::Compiled(compiled_type) => Some(compiled_type.nested_type()), + TypeSource::TaggedValue(value_type) => Some(value_type.nested_type()), + _ => None, + } + } + + /// If Some, the type should be displayed in the imports/exports, if None it should be replaced with "import/export index _". + pub fn compiled_nested_type_name(&self) -> Option { + self.compiled_nested_type().map(|ty| ty.to_string()) + } + + /// Used when searching for nodes in the add Node popup. + pub fn add_node_string(self) -> Option { + self.compiled_nested_type().map(|ty| format!("type:{}", ty.to_string())) + } + + /// The type to display in the tooltip. + pub fn resolved_type_name(&self) -> String { + match self { + TypeSource::Compiled(compiled_type) => compiled_type.nested_type().to_string(), + TypeSource::TaggedValue(value_type) => value_type.nested_type().to_string(), + TypeSource::DocumentNodeDefinition(_) => "Unknown".to_string(), + TypeSource::Unknown => "Unknown".to_string(), + TypeSource::Error(_) => "Error".to_string(), + } + } + + /// The reduced set of frontend types for displaying color. + pub fn displayed_type(&self) -> FrontendGraphDataType { + match self.compiled_nested_type() { + Some(nested_type) => FrontendGraphDataType::from_type(nested_type), + None => FrontendGraphDataType::General, + } + } +} + +impl NodeNetworkInterface { + /// Get the [`TypeSource`] for any InputConnector. + /// If the input is not compiled, then an Unknown or default from the definition is returned. + pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TypeSource { + let Some(input) = self.input_from_connector(input_connector, network_path) else { + return TypeSource::Error("Could not get input from connector"); + }; + + match input { + NodeInput::Node { node_id, output_index } => { + let input_type = self.output_type(&OutputConnector::node(*node_id, *output_index), network_path); + if input_type == TypeSource::Unknown { + // If we are trying to get the input type of an unknown node, check if it has a reference to its definition and use that input type + if let InputConnector::Node { node_id, input_index } = input_connector { + if let Some(definition) = self.get_node_definition(node_id, network_path) { + if let Some(value) = definition.node_template.document_node.inputs.get(*input_index).and_then(|input| input.as_value()) { + return TypeSource::DocumentNodeDefinition(value.ty()); + } + } + } + } + input_type + } + + NodeInput::Value { tagged_value, .. } => TypeSource::TaggedValue(tagged_value.ty()), + NodeInput::Import { import_index, .. } => { + // Get the input type of the encapsulating node input + let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else { + return TypeSource::Error("Could not get type of import in document network"); + }; + self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) + } + NodeInput::Scope(_) => TypeSource::Compiled(concrete!(())), + NodeInput::Reflection(document_node_metadata) => TypeSource::Compiled(document_node_metadata.ty()), + NodeInput::Inline(_) => TypeSource::Compiled(concrete!(InlineRust)), + } + } + + // Gets the default tagged value for an input. If its not compiled, then it tries to get a valid type. If there are no valid types, then it picks a random implementation + pub fn tagged_value_from_input(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TaggedValue { + let guaranteed_type = match self.input_type(input_connector, network_path) { + TypeSource::Compiled(compiled) => compiled, + TypeSource::TaggedValue(value) => value, + TypeSource::DocumentNodeDefinition(definition) => definition, + TypeSource::Unknown => { + let mut valid_types = self.valid_input_types(input_connector, network_path); + + match valid_types.pop() { + Some(valid_type) => valid_type, + None => { + match self.random_type_for_connector(input_connector, network_path) { + Some(random_type) => random_type, + // If there are no connected protonodes then we give up and return the empty type + None => concrete!(()), + } + } + } + } + TypeSource::Error(e) => { + log::error!("Error getting tagged_value_from_input for {input_connector:?} {e}"); + concrete!(()) + } + }; + TaggedValue::from_type_or_none(&guaranteed_type) + } + + pub fn valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec { + let InputConnector::Node { node_id, input_index } = input_connector else { + // An export can have any type connected to it + return vec![graph_craft::generic!(T)]; + }; + let Some(implementation) = self.implementation(node_id, network_path) else { + log::error!("Could not get node implementation in valid_input_types"); + return Vec::new(); + }; + match implementation { + DocumentNodeImplementation::Network(_) => { + let nested_path = [network_path, &[*node_id]].concat(); + let Some(outward_wires) = self.outward_wires(&nested_path) else { + log::error!("Could not get outward wires in valid_input_types"); + return Vec::new(); + }; + let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else { + log::error!("Could not get inputs from import in valid_input_types"); + return Vec::new(); + }; + + let intersection: HashSet = inputs_from_import + .clone() + .iter() + .map(|input_connector| self.valid_input_types(input_connector, &nested_path).into_iter().collect::>()) + .fold(None, |acc: Option>, set| match acc { + Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()), + None => Some(set), + }) + .unwrap_or_default(); + + intersection.into_iter().collect::>() + } + DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { + let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { + log::error!("Protonode {proto_node_identifier:?} not found in registry"); + return Vec::new(); + }; + let number_of_inputs = self.number_of_inputs(node_id, network_path); + implementations + .iter() + .filter_map(|(node_io, _)| { + 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); + // 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) -> Vector would not be compatible with () -> Vector + node_io.inputs.get(iterator_index).map(|ty| ty.nested_type().clone()).as_ref() == input_type.compiled_nested_type() + || node_io.inputs.get(iterator_index) == input_type.compiled_nested_type() + }); + if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None } + }) + .collect::>() + } + DocumentNodeImplementation::Extract => { + log::error!("Input types for extract node not supported"); + Vec::new() + } + } + } + + pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> TypeSource { + match output_connector { + OutputConnector::Node { node_id, output_index } => { + // First try iterating upstream to the first protonode and try get its compiled type + let Some(implementation) = self.implementation(node_id, network_path) else { + return TypeSource::Error("Could not get implementation"); + }; + match implementation { + DocumentNodeImplementation::Network(_) => self.input_type(&InputConnector::Export(*output_index), &[network_path, &[*node_id]].concat()), + DocumentNodeImplementation::ProtoNode(_) => match self.resolved_types.types.get(&[network_path, &[*node_id]].concat()) { + Some(resolved_type) => TypeSource::Compiled(resolved_type.output.clone()), + None => TypeSource::Unknown, + }, + DocumentNodeImplementation::Extract => TypeSource::Compiled(concrete!(())), + } + } + OutputConnector::Import(import_index) => { + let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else { + return TypeSource::Error("Cannot get import type in document network"); + }; + self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) + } + } + } + // The valid output types are all types that are valid for each downstream connection + pub fn valid_output_types(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Result, String> { + let Some(outward_wires) = self.outward_wires(&network_path) else { + return Err("Could not get outward wires in valid_input_types".to_string()); + }; + let Some(inputs_from_import) = outward_wires.get(output_connector) else { + return Err("Could not get inputs from import in valid_input_types".to_string()); + }; + + let intersection = inputs_from_import + .clone() + .iter() + .map(|input_connector| self.valid_input_types(input_connector, &network_path).into_iter().collect::>()) + .fold(None, |acc: Option>, set| match acc { + Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()), + None => Some(set), + }) + .unwrap_or_default(); + + Ok(intersection.into_iter().collect::>()) + } + + /// Performs a downstream iteration from an input connector to the next protonode and selects a random type from its implementations. + pub fn random_type_for_connector(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option { + match input_connector { + InputConnector::Node { node_id, input_index } => { + let Some(implementation) = self.implementation(node_id, network_path) else { + log::error!("Could not get node {node_id} in random_downstream_protonode_from_connector"); + return None; + }; + match implementation { + DocumentNodeImplementation::Network(_) => { + let Some(outward_wires) = self.outward_wires(&network_path) else { + log::error!("Could not get outward wires in random_downstream_protonode_from_connector"); + return None; + }; + let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else { + log::error!("Could not get inputs from import in valid_input_types"); + return None; + }; + let Some(first_input) = inputs_from_import.first().cloned() else { + return None; + }; + self.random_type_for_connector(&first_input, &[network_path, &[*node_id]].concat()) + } + DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { + let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { + log::error!("Protonode {proto_node_identifier:?} not found in registry"); + return None; + }; + implementations.keys().next().and_then(|node_io| node_io.inputs.get(input_connector.input_index())).cloned() + } + DocumentNodeImplementation::Extract => None, + } + } + InputConnector::Export(export_index) => network_path.split_last().and_then(|(encapsulating_node, encapsulating_path)| { + let Some(outward_wires) = self.outward_wires(&encapsulating_path) else { + log::error!("Could not get outward wires in random_downstream_protonode_from_connector export"); + return None; + }; + let Some(inputs_from_import) = outward_wires.get(&OutputConnector::node(*encapsulating_node, *export_index)) else { + log::error!("Could not get inputs from import in valid_input_types"); + return None; + }; + let Some(first_input) = inputs_from_import.first().cloned() else { + return None; + }; + self.random_type_for_connector(&first_input, encapsulating_path) + }), + } + } +} diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 0c2d87a1f0..c0d1081501 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -484,8 +484,8 @@ impl<'a> NodeGraphLayer<'a> { /// Check if a layer is a raster layer pub fn is_raster_layer(layer: LayerNodeIdentifier, network_interface: &mut NodeNetworkInterface) -> bool { - let layer_input_type = network_interface.input_type(&InputConnector::node(layer.to_node(), 1), &[]).0.nested_type().clone(); + let layer_input_type = network_interface.input_type(&InputConnector::node(layer.to_node(), 1), &[]); - layer_input_type == concrete!(Table>) || layer_input_type == concrete!(Table>) + layer_input_type.compiled_nested_type() == Some(&concrete!(Table>)) || layer_input_type.compiled_nested_type() == Some(&concrete!(Table>)) } } diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index ada904527e..25c96db9fb 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -581,8 +581,8 @@ pub fn make_path_editable_is_allowed(network_interface: &mut NodeNetworkInterfac // Must be a layer of type Table let node_id = NodeGraphLayer::new(first_layer, network_interface).horizontal_layer_flow().nth(1)?; - let (output_type, _) = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); - if output_type.nested_type() != concrete!(Table).nested_type() { + let output_type = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); + if output_type.compiled_nested_type().as_deref() != Some(&concrete!(Table)) { return None; } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index dc6265a2a7..ce57f2eb3c 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -211,6 +211,14 @@ pub enum DocumentNodeMetadata { DocumentNodePath, } +impl DocumentNodeMetadata { + pub fn ty(&self) -> Type { + match self { + DocumentNodeMetadata::DocumentNodePath => concrete!(Vec), + } + } +} + impl NodeInput { pub const fn node(node_id: NodeId, output_index: usize) -> Self { Self::Node { node_id, output_index } diff --git a/node-graph/interpreted-executor/src/dynamic_executor.rs b/node-graph/interpreted-executor/src/dynamic_executor.rs index 2f52214ed7..e6004f5e4c 100644 --- a/node-graph/interpreted-executor/src/dynamic_executor.rs +++ b/node-graph/interpreted-executor/src/dynamic_executor.rs @@ -39,11 +39,6 @@ pub struct NodeTypes { pub output: Type, } -#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -pub struct ResolvedDocumentNodeTypes { - pub types: HashMap, NodeTypes>, -} - type Path = Box<[NodeId]>; #[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)] diff --git a/node-graph/interpreted-executor/src/util.rs b/node-graph/interpreted-executor/src/util.rs index 75278402a4..979ce88f16 100644 --- a/node-graph/interpreted-executor/src/util.rs +++ b/node-graph/interpreted-executor/src/util.rs @@ -61,6 +61,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc