mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 13:28:12 +08:00
Add complete valid types
This commit is contained in:
@@ -671,7 +671,7 @@ impl NodeNetworkInterface {
|
|||||||
resolved_type,
|
resolved_type,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
valid_types: self.valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(),
|
valid_types: self.potential_valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(),
|
||||||
connected_to,
|
connected_to,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+96
-18
@@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
|
|||||||
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput};
|
use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput};
|
||||||
use graph_craft::{Type, concrete};
|
use graph_craft::{ProtoNodeIdentifier, Type, concrete};
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta};
|
use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta};
|
||||||
use interpreted_executor::node_registry::NODE_REGISTRY;
|
use interpreted_executor::node_registry::NODE_REGISTRY;
|
||||||
@@ -43,6 +43,32 @@ pub enum TypeSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TypeSource {
|
impl TypeSource {
|
||||||
|
/// The reduced set of frontend types for displaying color.
|
||||||
|
pub fn displayed_type(&self) -> FrontendGraphDataType {
|
||||||
|
match self.compiled_nested_type() {
|
||||||
|
Some(nested_type) => match TaggedValue::from_type_or_none(nested_type) {
|
||||||
|
TaggedValue::U32(_)
|
||||||
|
| TaggedValue::U64(_)
|
||||||
|
| TaggedValue::F32(_)
|
||||||
|
| TaggedValue::F64(_)
|
||||||
|
| TaggedValue::DVec2(_)
|
||||||
|
| TaggedValue::F64Array4(_)
|
||||||
|
| TaggedValue::VecF64(_)
|
||||||
|
| TaggedValue::VecDVec2(_)
|
||||||
|
| TaggedValue::DAffine2(_) => FrontendGraphDataType::Number,
|
||||||
|
TaggedValue::Artboard(_) => FrontendGraphDataType::Artboard,
|
||||||
|
TaggedValue::Graphic(_) => FrontendGraphDataType::Graphic,
|
||||||
|
TaggedValue::Raster(_) => FrontendGraphDataType::Raster,
|
||||||
|
TaggedValue::Vector(_) => FrontendGraphDataType::Vector,
|
||||||
|
TaggedValue::Color(_) => FrontendGraphDataType::Color,
|
||||||
|
TaggedValue::Gradient(_) | TaggedValue::GradientStops(_) | TaggedValue::GradientTable(_) => FrontendGraphDataType::Gradient,
|
||||||
|
TaggedValue::String(_) => FrontendGraphDataType::Typography,
|
||||||
|
_ => FrontendGraphDataType::General,
|
||||||
|
},
|
||||||
|
None => FrontendGraphDataType::General,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compiled_nested_type(&self) -> Option<&Type> {
|
pub fn compiled_nested_type(&self) -> Option<&Type> {
|
||||||
match self {
|
match self {
|
||||||
TypeSource::Compiled(compiled_type) => Some(compiled_type.nested_type()),
|
TypeSource::Compiled(compiled_type) => Some(compiled_type.nested_type()),
|
||||||
@@ -71,14 +97,6 @@ impl TypeSource {
|
|||||||
TypeSource::Error(_) => "Error".to_string(),
|
TypeSource::Error(_) => "Error".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The reduced set of frontend types for displaying color.
|
|
||||||
pub fn displayed_type(&self) -> FrontendGraphDataType {
|
|
||||||
match self.compiled_nested_type() {
|
|
||||||
Some(nested_type) => FrontendGraphDataType::from_type(nested_type),
|
|
||||||
None => FrontendGraphDataType::General,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeNetworkInterface {
|
impl NodeNetworkInterface {
|
||||||
@@ -126,7 +144,7 @@ impl NodeNetworkInterface {
|
|||||||
TypeSource::TaggedValue(value) => value,
|
TypeSource::TaggedValue(value) => value,
|
||||||
TypeSource::DocumentNodeDefinition(definition) => definition,
|
TypeSource::DocumentNodeDefinition(definition) => definition,
|
||||||
TypeSource::Unknown => {
|
TypeSource::Unknown => {
|
||||||
let mut valid_types = self.valid_input_types(input_connector, network_path);
|
let mut valid_types = self.potential_valid_input_types(input_connector, network_path);
|
||||||
|
|
||||||
match valid_types.pop() {
|
match valid_types.pop() {
|
||||||
Some(valid_type) => valid_type,
|
Some(valid_type) => valid_type,
|
||||||
@@ -147,7 +165,8 @@ impl NodeNetworkInterface {
|
|||||||
TaggedValue::from_type_or_none(&guaranteed_type)
|
TaggedValue::from_type_or_none(&guaranteed_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> {
|
/// A list of all valid input types for this specific node.
|
||||||
|
pub fn potential_valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> {
|
||||||
let InputConnector::Node { node_id, input_index } = input_connector else {
|
let InputConnector::Node { node_id, input_index } = input_connector else {
|
||||||
// An export can have any type connected to it
|
// An export can have any type connected to it
|
||||||
return vec![graph_craft::generic!(T)];
|
return vec![graph_craft::generic!(T)];
|
||||||
@@ -171,7 +190,7 @@ impl NodeNetworkInterface {
|
|||||||
let intersection: HashSet<Type> = inputs_from_import
|
let intersection: HashSet<Type> = inputs_from_import
|
||||||
.clone()
|
.clone()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|input_connector| self.valid_input_types(input_connector, &nested_path).into_iter().collect::<HashSet<_>>())
|
.map(|input_connector| self.potential_valid_input_types(input_connector, &nested_path).into_iter().collect::<HashSet<_>>())
|
||||||
.fold(None, |acc: Option<HashSet<Type>>, set| match acc {
|
.fold(None, |acc: Option<HashSet<Type>>, set| match acc {
|
||||||
Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()),
|
Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()),
|
||||||
None => Some(set),
|
None => Some(set),
|
||||||
@@ -191,10 +210,9 @@ impl NodeNetworkInterface {
|
|||||||
.filter_map(|(node_io, _)| {
|
.filter_map(|(node_io, _)| {
|
||||||
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
|
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
|
||||||
let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path);
|
let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path);
|
||||||
// Value inputs are stored as concrete, so they are compared to the nested type. Node inputs are stored as fn, so they are compared to the entire type.
|
// TODO: Fix type checking for different call arguments
|
||||||
// For example a node input of (Footprint) -> Vector would not be compatible with () -> Vector
|
// For example a node input of (Footprint) -> Vector would not be compatible with a node that is called with () and returns Vector
|
||||||
node_io.inputs.get(iterator_index).map(|ty| ty.nested_type().clone()).as_ref() == input_type.compiled_nested_type()
|
node_io.inputs.get(iterator_index).map(|ty| ty.nested_type()) == input_type.compiled_nested_type()
|
||||||
|| node_io.inputs.get(iterator_index) == input_type.compiled_nested_type()
|
|
||||||
});
|
});
|
||||||
if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None }
|
if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None }
|
||||||
})
|
})
|
||||||
@@ -207,6 +225,66 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs a downstream traversal to ensure input type will work in the full context of the graph.
|
||||||
|
pub fn complete_valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Result<Vec<Type>, String> {
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
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"));
|
||||||
|
};
|
||||||
|
let valid_output_types = match self.valid_output_types(&OutputConnector::node(*node_id, 0), network_path) {
|
||||||
|
Ok(valid_types) => valid_types,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let valid_types = implementations
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(node_io, _)| {
|
||||||
|
if !valid_output_types.iter().any(|output_type| output_type.nested_type() == node_io.return_value.nested_type()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let valid_inputs = (0..node_io.inputs.len()).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
|
||||||
|
let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path);
|
||||||
|
match input_type.compiled_nested_type() {
|
||||||
|
Some(input_type) => node_io.inputs.get(iterator_index).is_some_and(|node_io_input_type| node_io_input_type.nested_type() == input_type),
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if valid_inputs { node_io.inputs.get(*input_index).cloned() } else { None }
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok(valid_types)
|
||||||
|
}
|
||||||
|
DocumentNodeImplementation::Extract => {
|
||||||
|
log::error!("Input types for extract node not supported");
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputConnector::Export(export_index) => {
|
||||||
|
match network_path.split_last() {
|
||||||
|
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 {
|
||||||
|
return Err(format!("Protonode {render_node:?} not found in registry"));
|
||||||
|
};
|
||||||
|
Ok(implementations.iter().map(|(types, _)| types.inputs[1].clone()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> TypeSource {
|
pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> TypeSource {
|
||||||
match output_connector {
|
match output_connector {
|
||||||
OutputConnector::Node { node_id, output_index } => {
|
OutputConnector::Node { node_id, output_index } => {
|
||||||
@@ -243,7 +321,7 @@ impl NodeNetworkInterface {
|
|||||||
let intersection = inputs_from_import
|
let intersection = inputs_from_import
|
||||||
.clone()
|
.clone()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|input_connector| self.valid_input_types(input_connector, &network_path).into_iter().collect::<HashSet<_>>())
|
.map(|input_connector| self.potential_valid_input_types(input_connector, &network_path).into_iter().collect::<HashSet<_>>())
|
||||||
.fold(None, |acc: Option<HashSet<Type>>, set| match acc {
|
.fold(None, |acc: Option<HashSet<Type>>, set| match acc {
|
||||||
Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()),
|
Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()),
|
||||||
None => Some(set),
|
None => Some(set),
|
||||||
@@ -281,7 +359,7 @@ impl NodeNetworkInterface {
|
|||||||
log::error!("Protonode {proto_node_identifier:?} not found in registry");
|
log::error!("Protonode {proto_node_identifier:?} not found in registry");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
implementations.keys().next().and_then(|node_io| node_io.inputs.get(input_connector.input_index())).cloned()
|
implementations.keys().min().and_then(|node_io| node_io.inputs.get(input_connector.input_index())).cloned()
|
||||||
}
|
}
|
||||||
DocumentNodeImplementation::Extract => None,
|
DocumentNodeImplementation::Extract => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ macro_rules! fn_type_fut {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct NodeIOTypes {
|
pub struct NodeIOTypes {
|
||||||
pub call_argument: Type,
|
pub call_argument: Type,
|
||||||
pub return_value: Type,
|
pub return_value: Type,
|
||||||
@@ -229,8 +229,20 @@ 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.
|
/// Graph runtime type information used for type inference.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, specta::Type, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
/// A wrapper for some type variable used within the inference system. Resolved at inference time and replaced with a concrete 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>),
|
Generic(Cow<'static, str>),
|
||||||
|
|||||||
Reference in New Issue
Block a user