mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add invalid type
This commit is contained in:
@@ -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] });
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,6 +16,7 @@ pub enum FrontendGraphDataType {
|
||||
Color,
|
||||
Gradient,
|
||||
Typography,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl FrontendGraphDataType {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)],
|
||||
|
||||
Reference in New Issue
Block a user