Add invalid type

This commit is contained in:
Adam
2025-11-17 01:31:59 -08:00
parent ced44310bf
commit 9bfa58b442
10 changed files with 165 additions and 140 deletions

View File

@@ -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<GraphOperationMessage, GraphOperationMessageContext<'_>> 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] });

View File

@@ -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<DocumentNodeDefinition>) -> Vec<DocumentNodeDefinition> {
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<DocumentNodeDefinition>) -> 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<DocumentNodeDefinition>) -> 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<DocumentNodeDefinition>) -> 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<DocumentNodeDefinition>) -> Vec
custom.push(node);
}
custom.iter().for_each(|def| log::debug!("{:?}", def.identifier));
custom
}

View File

@@ -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<NodeId>,
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<NodeGraphMessage, NodeGraphMessageContext<'a>> 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<NodeGraphMessage, NodeGraphMessageContext<'a>> 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

View File

@@ -16,6 +16,7 @@ pub enum FrontendGraphDataType {
Color,
Gradient,
Typography,
Invalid,
}
impl FrontendGraphDataType {

View File

@@ -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<WirePath> {
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,

View File

@@ -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<Vec<NodeId>, 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<String> {
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<String> {
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<Vec<Type>, 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<Vec<Type>, 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::<Vec<_>>())
}
/// 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<Type> {
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)
}),
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<std::cmp::Ordering> {
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 {

View File

@@ -51,6 +51,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
},
..Default::default()
},
// Keep this in sync with the protonode in valid_input_types
DocumentNode {
call_argument: concrete!(Context),
inputs: vec![NodeInput::scope("editor-api"), NodeInput::node(NodeId(2), 0), NodeInput::node(NodeId(1), 0)],
@@ -61,7 +62,6 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
},
..Default::default()
},
// Keep this in sync with the protonode id in valid_input_types for the document network export
DocumentNode {
call_argument: concrete!(graphene_std::application_io::RenderConfig),
inputs: vec![NodeInput::node(NodeId(3), 0)],