diff --git a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs index f40b07d1c9..d22eeda8b1 100644 --- a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs @@ -7,10 +7,12 @@ use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use glam::{DAffine2, DVec2, IVec2}; +use graph_craft::document::value::TaggedValue; use graph_craft::document::{NodeId, NodeInput}; use graphene_std::Color; use graphene_std::renderer::Quad; use graphene_std::renderer::convert_usvg_path::convert_usvg_path; +use graphene_std::table::Table; use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin}; @@ -140,10 +142,16 @@ impl MessageHandler> for skip_rerender: true, }); } + // Set the bottom input of the artboard back to artboard + let bottom_input = NodeInput::value(TaggedValue::Artboard(Table::new()), true); + network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 0), bottom_input, &[]); } else { // We have some non layers (e.g. just a rectangle node). We disconnect the bottom input and connect it to the left input. network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]); network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 1), primary_input, &[]); + // Set the bottom input of the artboard back to artboard + let bottom_input = NodeInput::value(TaggedValue::Artboard(Table::new()), true); + network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 0), bottom_input, &[]); } } responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] }); diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs index f9db802ac5..4be5c13378 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs @@ -6,7 +6,39 @@ use graphene_std::registry::*; use graphene_std::*; use std::collections::HashSet; +/// Traverses a document node template and metadata in parallel to link the protonodes to their reference +fn traverse_node(node: &DocumentNode, node_metadata: &mut DocumentNodePersistentMetadata) { + match &node.implementation { + DocumentNodeImplementation::Network(node_network) => { + for (nested_node_id, nested_node) in node_network.nodes.iter() { + let nested_metadata = node_metadata + .network_metadata + .as_mut() + .expect("Network node must have network metadata") + .persistent_metadata + .node_metadata + .get_mut(nested_node_id) + .expect("Network metadata must have corresponding node id"); + traverse_node(nested_node, &mut nested_metadata.persistent_metadata); + } + } + DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { + if let Some(metadata) = NODE_METADATA.lock().unwrap().get(&proto_node_identifier) { + node_metadata.reference = Some(metadata.display_name.to_string()); + } + } + DocumentNodeImplementation::Extract => {} + } +} + pub(super) fn post_process_nodes(mut custom: Vec) -> Vec { + NODE_METADATA.lock().unwrap().keys().for_each(|key| log::debug!("{key:?}")); + + // Link the protonodes with custom networks to their reference + for node in custom.iter_mut() { + traverse_node(&node.node_template.document_node, &mut node.node_template.persistent_node_metadata); + } + // Remove struct generics for DocumentNodeDefinition { node_template, .. } in custom.iter_mut() { let NodeTemplate { @@ -20,7 +52,6 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec } }; } - let node_registry = NODE_REGISTRY.lock().unwrap(); 'outer: for (id, metadata) in NODE_METADATA.lock().unwrap().iter() { for node in custom.iter() { @@ -32,7 +63,10 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec .. } = node; match implementation { - DocumentNodeImplementation::ProtoNode(name) if name == id => continue 'outer, + DocumentNodeImplementation::ProtoNode(name) if name == id => { + log::debug!("skipping defintion {name}"); + continue 'outer; + } _ => (), } } @@ -55,6 +89,8 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec let output_type = &first_node_io.return_value; let inputs = preprocessor::node_inputs(fields, first_node_io); + log::debug!("generating definition for {display_name:?}"); + let node = DocumentNodeDefinition { identifier: display_name, node_template: NodeTemplate { @@ -91,5 +127,6 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec custom.push(node); } + custom.iter().for_each(|def| log::debug!("{:?}", def.identifier)); custom } 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 750a6c4b53..f64dd130a9 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 @@ -23,7 +23,6 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; use crate::messages::viewport::{Position, Rect}; use glam::{DAffine2, DVec2, IVec2}; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; -use graph_craft::proto::GraphErrors; use graphene_std::math::math_ext::QuadExt; use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath; use graphene_std::*; @@ -51,7 +50,6 @@ pub struct NodeGraphMessageContext<'a> { pub struct NodeGraphMessageHandler { // TODO: Remove network and move to NodeNetworkInterface pub network: Vec, - pub node_graph_errors: GraphErrors, has_selection: bool, widgets: [LayoutGroup; 2], /// Used to add a transaction for the first node move when dragging. @@ -1623,7 +1621,7 @@ impl<'a> MessageHandler> for NodeG if node_bbox[1].x >= document_bbox[0].x && node_bbox[0].x <= document_bbox[1].x && node_bbox[1].y >= document_bbox[0].y && node_bbox[0].y <= document_bbox[1].y { nodes.push(*node_id); } - for error in &self.node_graph_errors { + for error in &network_interface.resolved_types.node_graph_errors { if error.node_path.contains(node_id) { nodes.push(*node_id); } @@ -1995,8 +1993,7 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::SendGraph); } NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => { - network_interface.resolved_types.update(resolved_types); - self.node_graph_errors = node_graph_errors; + network_interface.resolved_types.update(resolved_types, node_graph_errors); } NodeGraphMessage::UpdateActionButtons => { if selection_network_path == breadcrumb_network_path { @@ -2557,13 +2554,14 @@ impl NodeGraphMessageHandler { let locked = network_interface.is_locked(&node_id, breadcrumb_network_path); - let errors = self + let errors = network_interface + .resolved_types .node_graph_errors .iter() .find(|error| error.node_path == node_id_path) .map(|error| format!("{:?}", error.error.clone())) .or_else(|| { - if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) { + if network_interface.resolved_types.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 @@ -2746,7 +2744,6 @@ impl Default for NodeGraphMessageHandler { fn default() -> Self { Self { network: Vec::new(), - node_graph_errors: Vec::new(), has_selection: false, widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: Vec::new() }], drag_start: None, @@ -2778,7 +2775,6 @@ impl Default for NodeGraphMessageHandler { impl PartialEq for NodeGraphMessageHandler { fn eq(&self, other: &Self) -> bool { self.network == other.network - && self.node_graph_errors == other.node_graph_errors && self.has_selection == other.has_selection && self.widgets == other.widgets && self.drag_start == other.drag_start 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 dcab6e9417..7b490f26d1 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -16,6 +16,7 @@ pub enum FrontendGraphDataType { Color, Gradient, Typography, + Invalid, } impl FrontendGraphDataType { 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 35ca465a11..311b08b460 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -8,7 +8,7 @@ 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, FrontendGraphInput, FrontendGraphOutput}; +use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType, 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; @@ -625,7 +625,7 @@ impl NodeNetworkInterface { } 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 resolved_type = input_type.resolved_type_node_string(); let connected_to = self .upstream_output_connector(input_connector, network_path) @@ -656,7 +656,7 @@ impl NodeNetworkInterface { let export_name = if !export_name.is_empty() { export_name - } else if let Some(export_type_name) = input_type.compiled_nested_type_name() { + } else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) { export_type_name } else { format!("Export index {}", export_index) @@ -690,7 +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 { output_type.resolved_type_name() }; + let output_name = if !output_name.is_empty() { output_name } else { output_type.resolved_type_node_string() }; (output_name, String::new()) } OutputConnector::Import(import_index) => { @@ -707,7 +707,7 @@ impl NodeNetworkInterface { let import_name = if !import_name.is_empty() { import_name - } else if let Some(import_type_name) = output_type.compiled_nested_type_name() { + } else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) { import_type_name } else { format!("Import index {}", import_index) @@ -717,7 +717,7 @@ impl NodeNetworkInterface { } }; let data_type = output_type.displayed_type(); - let resolved_type = output_type.resolved_type_name(); + let resolved_type = output_type.resolved_type_node_string(); let mut connected_to = self .outward_wires(network_path) .and_then(|outward_wires| outward_wires.get(output_connector)) @@ -977,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).resolved_type_name() + self.input_type(&InputConnector::node(*node_id, input_index), network_path).resolved_type_node_string() } else { input_metadata.input_name.to_string() }; @@ -2520,7 +2520,10 @@ 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 = self.input_type(input, network_path).displayed_type(); + let data_type = self + .upstream_output_connector(input, network_path) + .map(|output| self.output_type(&output, network_path).displayed_type()) + .unwrap_or(FrontendGraphDataType::General); Some(WirePath { path_string, data_type, 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 index ca58c5e654..c54c767896 100644 --- 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 @@ -2,7 +2,8 @@ use std::collections::{HashMap, HashSet}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput}; -use graph_craft::{ProtoNodeIdentifier, Type, concrete}; +use graph_craft::proto::GraphErrors; +use graph_craft::{Type, concrete}; use graphene_std::uuid::NodeId; use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta}; use interpreted_executor::node_registry::NODE_REGISTRY; @@ -14,37 +15,42 @@ use crate::messages::portfolio::document::utility_types::network_interface::{Inp #[derive(Debug, Default)] pub struct ResolvedDocumentNodeTypes { pub types: HashMap, NodeTypes>, + pub node_graph_errors: GraphErrors, } impl ResolvedDocumentNodeTypes { - pub fn update(&mut self, delta: ResolvedDocumentNodeTypesDelta) { + pub fn update(&mut self, delta: ResolvedDocumentNodeTypesDelta, errors: GraphErrors) { 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()); } + self.node_graph_errors = errors; } } /// 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 + /// A type that has been compiled based on all upstream types. Compiled(Type), - // The type of value inputs + /// 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 + /// When the input/output is not compiled. The Type is from the document node definition, or () if it doesn't exist. Unknown, - + /// When there is a node graph error for the inputs to a node. The Type is from the document node definition, or () if it doesn't exist. + Invalid, + /// When there is an error in the algorithm for determining the input/output type (indicates a bug in the editor). Error(&'static str), } impl TypeSource { /// The reduced set of frontend types for displaying color. pub fn displayed_type(&self) -> FrontendGraphDataType { + if matches!(self, TypeSource::Invalid) { + return FrontendGraphDataType::Invalid; + }; match self.compiled_nested_type() { Some(nested_type) => match TaggedValue::from_type_or_none(nested_type) { TaggedValue::U32(_) @@ -77,29 +83,60 @@ impl TypeSource { } } - /// 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 { + pub fn resolved_type_tooltip_string(&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::Compiled(compiled_type) => format!("Data Type: {:?}", compiled_type.nested_type_name().to_string()), + TypeSource::TaggedValue(value_type) => format!("Data Type: {:?}", value_type.nested_type_name().to_string()), + TypeSource::Unknown => "Unknown Data Type".to_string(), + TypeSource::Invalid => "Invalid Type Combination".to_string(), + TypeSource::Error(_) => "Error Getting Data Type".to_string(), + } + } + + /// The type to display in the node row. + pub fn resolved_type_node_string(&self) -> String { + match self { + TypeSource::Compiled(compiled_type) => compiled_type.nested_type_name().to_string(), + TypeSource::TaggedValue(value_type) => value_type.nested_type_name().to_string(), TypeSource::Unknown => "Unknown".to_string(), + TypeSource::Invalid => "Invalid".to_string(), TypeSource::Error(_) => "Error".to_string(), } } } impl NodeNetworkInterface { + fn input_has_error(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> bool { + match input_connector { + InputConnector::Node { node_id, input_index } => { + let Some(implementation) = self.implementation(node_id, network_path) else { + log::error!("Could not get implementation in input_has_error"); + return false; + }; + let node_path = [network_path, &[*node_id]].concat(); + match implementation { + DocumentNodeImplementation::Network(_) => { + let Some(map) = self.outward_wires(&node_path) else { return false }; + let Some(outward_wires) = map.get(&OutputConnector::Import(*input_index)) else { return false }; + outward_wires.clone().iter().any(|connector| match connector { + InputConnector::Node { node_id, input_index } => self.input_has_error(&InputConnector::node(*node_id, *input_index), &node_path), + InputConnector::Export(_) => false, + }) + } + DocumentNodeImplementation::ProtoNode(_) => self.resolved_types.node_graph_errors.iter().any(|error| error.node_path == node_path), + DocumentNodeImplementation::Extract => false, + } + } + InputConnector::Export(_) => false, + } + } + /// 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 { @@ -109,25 +146,19 @@ impl NodeNetworkInterface { 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()); - } - } - } + let output_connector = OutputConnector::node(*node_id, *output_index); + // First check if there is an error with this node or any protonodes it is connected to + if self.input_has_error(input_connector, network_path) { + return TypeSource::Invalid; } - input_type + self.output_type(&output_connector, network_path) } 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"); + return TypeSource::Error("Could not get type of import in document network since it has no imports"); }; self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) } @@ -142,20 +173,16 @@ impl NodeNetworkInterface { 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.potential_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::Unknown | TypeSource::Invalid => { + let mut ret = concrete!(()); + 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()) { + ret = value.ty(); } } } + ret } TypeSource::Error(e) => { log::error!("Error getting tagged_value_from_input for {input_connector:?} {e}"); @@ -172,18 +199,18 @@ impl NodeNetworkInterface { 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"); + log::error!("Could not get node implementation in potential_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"); + log::error!("Could not get outward wires in potential_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"); + log::error!("Could not get inputs from import in potential_valid_input_types"); return Vec::new(); }; @@ -201,7 +228,7 @@ impl NodeNetworkInterface { } 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"); + log::error!("Protonode {proto_node_identifier:?} not found in registry in potential_valid_input_types"); return Vec::new(); }; let number_of_inputs = self.number_of_inputs(node_id, network_path); @@ -230,13 +257,13 @@ impl NodeNetworkInterface { match input_connector { InputConnector::Node { node_id, input_index } => { let Some(implementation) = self.implementation(node_id, network_path) else { - return Err(format!("Could not get node implementation for {:?} {} in valid_input_types", network_path, *node_id)); + return Err(format!("Could not get node implementation for {:?} {} in complete_valid_input_types", network_path, *node_id)); }; match implementation { DocumentNodeImplementation::Network(_) => self.valid_output_types(&OutputConnector::Import(input_connector.input_index()), &[network_path, &[*node_id]].concat()), DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { - return Err(format!("Protonode {proto_node_identifier:?} not found in registry")); + return Err(format!("Protonode {proto_node_identifier:?} not found in registry in complete_valid_input_types")); }; let valid_output_types = match self.valid_output_types(&OutputConnector::node(*node_id, 0), network_path) { Ok(valid_types) => valid_types, @@ -273,9 +300,8 @@ impl NodeNetworkInterface { Some((encapsulating_node, encapsulating_path)) => self.valid_output_types(&OutputConnector::node(*encapsulating_node, *export_index), encapsulating_path), None => { // Valid types for the export are all types that can be fed into the render node - // TODO: Use ::IDENTIFIER - let render_node = "graphene_std::wasm_application_io::RenderNode"; - let Some(implementations) = NODE_REGISTRY.get(&ProtoNodeIdentifier::new(render_node)) else { + let render_node = graphene_std::render_node::render::IDENTIFIER; + let Some(implementations) = NODE_REGISTRY.get(&render_node) else { return Err(format!("Protonode {render_node:?} not found in registry")); }; Ok(implementations.iter().map(|(types, _)| types.inputs[1].clone()).collect()) @@ -303,19 +329,24 @@ impl NodeNetworkInterface { } 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"); + return TypeSource::Error("Cannot get import type in document network since it has no imports"); }; - self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) + let mut input_type = self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path); + if matches!(input_type, TypeSource::Invalid) { + input_type = TypeSource::Unknown + } + return input_type; } } } - // 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> { + + /// The valid output types are all types that are valid for each downstream connection. + 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()); + return Err("Could not get outward wires in valid_output_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()); + return Err("Could not get inputs from import in valid_output_types".to_string()); }; let intersection = inputs_from_import @@ -330,54 +361,4 @@ impl NodeNetworkInterface { 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().min().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/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index 9a965338a2..2761fdd8f5 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -129,6 +129,8 @@ --color-data-gradient-dim: #6c489b; --color-data-typography: #eea7a7; --color-data-typography-dim: #955252; + --color-data-invalid: var(--color-error-red); + --color-data-invalid-dim: color-mix(in srgb, var(--color-error-red) 75%, black); --color-none: white; --color-none-repeat: no-repeat; diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index 45253cb2f5..ce6d8e9859 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -177,7 +177,7 @@ export type ContextMenuInformation = { contextMenuData: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean }; }; -export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color"; +export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color" | "Invalid"; export class FrontendGraphInput { readonly dataType!: FrontendGraphDataType; diff --git a/node-graph/gcore/src/types.rs b/node-graph/gcore/src/types.rs index b819f15839..010f492ef0 100644 --- a/node-graph/gcore/src/types.rs +++ b/node-graph/gcore/src/types.rs @@ -77,7 +77,7 @@ macro_rules! fn_type_fut { }; } -#[derive(Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)] pub struct NodeIOTypes { pub call_argument: Type, pub return_value: Type, @@ -229,20 +229,8 @@ impl PartialEq for TypeDescriptor { } } -impl Ord for TypeDescriptor { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.name.cmp(&other.name) - } -} - -impl PartialOrd for TypeDescriptor { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - /// Graph runtime type information used for type inference. -#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, specta::Type, serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, specta::Type, serde::Serialize, serde::Deserialize)] pub enum Type { /// A wrapper for some type variable used within the inference system. Resolved at inference time and replaced with a concrete type. Generic(Cow<'static, str>), @@ -364,6 +352,15 @@ impl Type { _ => Cow::Owned(self.to_string()), } } + + pub fn nested_type_name(&self) -> &str { + match self { + Type::Generic(name) => name.as_ref(), + Type::Concrete(type_descriptor) => type_descriptor.name.as_ref(), + Type::Fn(_, nested) => nested.nested_type_name(), + Type::Future(nested) => nested.nested_type_name(), + } + } } fn format_type(ty: &str) -> String { diff --git a/node-graph/interpreted-executor/src/util.rs b/node-graph/interpreted-executor/src/util.rs index 979ce88f16..8de277bbd7 100644 --- a/node-graph/interpreted-executor/src/util.rs +++ b/node-graph/interpreted-executor/src/util.rs @@ -51,6 +51,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc