mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
Replace terminology "primary" with "call argument" and "parameter" with "secondary input"
This commit is contained in:
parent
f8c7ada572
commit
c738b4a1f9
15 changed files with 142 additions and 128 deletions
|
@ -2495,17 +2495,17 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
|
||||
let NodeMetadata { display_name, category, fields } = metadata;
|
||||
let Some(implementations) = &node_registry.get(&id) else { continue };
|
||||
let valid_inputs: HashSet<_> = implementations.iter().map(|(_, node_io)| node_io.input.clone()).collect();
|
||||
let valid_inputs: HashSet<_> = implementations.iter().map(|(_, node_io)| node_io.call_argument.clone()).collect();
|
||||
let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() });
|
||||
let mut input_type = &first_node_io.input;
|
||||
let mut input_type = &first_node_io.call_argument;
|
||||
if valid_inputs.len() > 1 {
|
||||
input_type = &const { generic!(T) };
|
||||
}
|
||||
let output_type = &first_node_io.output;
|
||||
let output_type = &first_node_io.return_value;
|
||||
|
||||
let inputs = fields
|
||||
.iter()
|
||||
.zip(first_node_io.parameters.iter())
|
||||
.zip(first_node_io.inputs.iter())
|
||||
.enumerate()
|
||||
.map(|(index, (field, ty))| {
|
||||
let exposed = if index == 0 { *ty != fn_type!(()) } else { field.exposed };
|
||||
|
@ -2526,7 +2526,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
let properties = match properties_overrides.get(id.as_str()) {
|
||||
Some(properties_function) => *properties_function,
|
||||
None => {
|
||||
let field_types: Vec<_> = fields.iter().zip(first_node_io.parameters.iter()).map(|(field, ty)| (field.clone(), ty.clone())).collect();
|
||||
let field_types: Vec<_> = fields.iter().zip(first_node_io.inputs.iter()).map(|(field, ty)| (field.clone(), ty.clone())).collect();
|
||||
let properties = move |document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext| {
|
||||
let rows: Vec<_> = field_types
|
||||
.iter()
|
||||
|
|
|
@ -511,7 +511,7 @@ impl NodeNetworkInterface {
|
|||
}
|
||||
}
|
||||
DocumentNodeImplementation::ProtoNode(_) => {
|
||||
// If a node has manual composition, then offset the input index by 1 since the proto node also includes the type of the parameter passed through manual composition.
|
||||
// If a node has manual composition, then offset the input index by 1 since the proto node also includes the type of the input passed through manual composition.
|
||||
let manual_composition_offset = if node.manual_composition.is_some() { 1 } else { 0 };
|
||||
self.resolved_types
|
||||
.types
|
||||
|
@ -552,7 +552,7 @@ impl NodeNetworkInterface {
|
|||
|
||||
let skip_footprint = if node.manual_composition.is_some() { 1 } else { 0 };
|
||||
|
||||
let Some(input_type) = std::iter::once(node_types.input.clone()).chain(node_types.parameters.clone()).nth(input_index + skip_footprint) else {
|
||||
let Some(input_type) = std::iter::once(node_types.call_argument.clone()).chain(node_types.inputs.clone()).nth(input_index + skip_footprint) else {
|
||||
log::error!("Could not get type");
|
||||
return (concrete!(()), TypeSource::Error("could not get the protonode's input"));
|
||||
};
|
||||
|
@ -678,7 +678,7 @@ impl NodeNetworkInterface {
|
|||
let node_id_path = &[network_path, &[*node_id]].concat();
|
||||
let primary_output_type = self.resolved_types.types.get(node_id_path).map(|ty| (ty.output.clone(), TypeSource::Compiled)).or_else(|| {
|
||||
let node_types = random_protonode_implementation(protonode)?;
|
||||
Some((node_types.output.clone(), TypeSource::RandomProtonodeImplementation))
|
||||
Some((node_types.return_value.clone(), TypeSource::RandomProtonodeImplementation))
|
||||
});
|
||||
|
||||
output_types.push(primary_output_type);
|
||||
|
|
|
@ -105,7 +105,7 @@ pub fn get_blend_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
|
|||
/// Get the current opacity of a layer from the closest Opacity node.
|
||||
/// This may differ from the actual opacity contained within the data type reaching this layer, because that actual opacity may be:
|
||||
/// - Multiplied with additional opacity nodes earlier in the chain
|
||||
/// - Set by an Opacity node with an exposed parameter value driven by another node
|
||||
/// - Set by an Opacity node with an exposed input value driven by another node
|
||||
/// - Already factored into the pixel alpha channel of an image
|
||||
/// - The default value of 100% if no Opacity node is present, but this function returns None in that case
|
||||
///
|
||||
|
|
|
@ -330,7 +330,7 @@ impl Fsm for BrushToolFsmState {
|
|||
|
||||
let layer_document_scale = document.metadata().transform_to_document(parent) * tool_data.transform;
|
||||
|
||||
// TODO: Also scale it based on the input image ('Background' parameter).
|
||||
// TODO: Also scale it based on the input image ('Background' input).
|
||||
// TODO: Resizing the input image results in a different brush size from the chosen diameter.
|
||||
let layer_scale = 0.0001_f64 // Safety against division by zero
|
||||
.max((layer_document_scale.matrix2 * glam::DVec2::X).length())
|
||||
|
|
|
@ -280,20 +280,20 @@
|
|||
editor.handle.createNode(nodeType, $nodeGraph.contextMenuInformation.contextMenuCoordinates.x, $nodeGraph.contextMenuInformation.contextMenuCoordinates.y);
|
||||
}
|
||||
|
||||
function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, parameters: number, primaryOutputExists: boolean, exposedOutputs: number): string {
|
||||
const nodeHeight = Math.max(1 + parameters, 1 + exposedOutputs) * 24;
|
||||
function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, exposedSecondaryInputs: number, primaryOutputExists: boolean, exposedSecondaryOutputs: number): string {
|
||||
const nodeHeight = Math.max(1 + exposedSecondaryInputs, 1 + exposedSecondaryOutputs) * 24;
|
||||
|
||||
const boxes: { x: number; y: number; width: number; height: number }[] = [];
|
||||
|
||||
// Primary input
|
||||
if (primaryInputExists) boxes.push({ x: -8, y: 4, width: 16, height: 16 });
|
||||
// Parameter inputs
|
||||
for (let i = 0; i < parameters; i++) boxes.push({ x: -8, y: 4 + (i + 1) * 24, width: 16, height: 16 });
|
||||
// Secondary inputs
|
||||
for (let i = 0; i < exposedSecondaryInputs; i++) boxes.push({ x: -8, y: 4 + (i + 1) * 24, width: 16, height: 16 });
|
||||
|
||||
// Primary output
|
||||
if (primaryOutputExists) boxes.push({ x: nodeWidth - 8, y: 4, width: 16, height: 16 });
|
||||
// Exposed outputs
|
||||
for (let i = 0; i < exposedOutputs; i++) boxes.push({ x: nodeWidth - 8, y: 4 + (i + 1) * 24, width: 16, height: 16 });
|
||||
for (let i = 0; i < exposedSecondaryOutputs; i++) boxes.push({ x: nodeWidth - 8, y: 4 + (i + 1) * 24, width: 16, height: 16 });
|
||||
|
||||
return borderMask(boxes, nodeWidth, nodeHeight);
|
||||
}
|
||||
|
@ -704,17 +704,17 @@
|
|||
<span class="node-error hover" transition:fade={FADE_TRANSITION} data-node-error>{node.errors}</span>
|
||||
{/if}
|
||||
<!-- Primary row -->
|
||||
<div class="primary" class:in-selected-network={$nodeGraph.inSelectedNetwork} class:no-parameter-section={exposedInputsOutputs.length === 0}>
|
||||
<div class="primary" class:in-selected-network={$nodeGraph.inSelectedNetwork} class:no-secondary-section={exposedInputsOutputs.length === 0}>
|
||||
<IconLabel icon={nodeIcon(node.reference)} />
|
||||
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
|
||||
<TextLabel tooltip={editor.handle.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined}>{node.displayName}</TextLabel>
|
||||
</div>
|
||||
<!-- Parameter rows -->
|
||||
<!-- Secondary rows -->
|
||||
{#if exposedInputsOutputs.length > 0}
|
||||
<div class="parameters" class:in-selected-network={$nodeGraph.inSelectedNetwork}>
|
||||
{#each exposedInputsOutputs as parameter, index}
|
||||
<div class={`parameter expanded ${index < node.exposedInputs.length ? "input" : "output"}`}>
|
||||
<TextLabel tooltip={parameter.name}>{parameter.name}</TextLabel>
|
||||
<div class="secondary" class:in-selected-network={$nodeGraph.inSelectedNetwork}>
|
||||
{#each exposedInputsOutputs as secondary, index}
|
||||
<div class={`secondary-row expanded ${index < node.exposedInputs.length ? "input" : "output"}`}>
|
||||
<TextLabel tooltip={secondary.name}>{secondary.name}</TextLabel>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -740,20 +740,20 @@
|
|||
{/if}
|
||||
</svg>
|
||||
{/if}
|
||||
{#each node.exposedInputs as parameter, index}
|
||||
{#each node.exposedInputs as secondary, index}
|
||||
{#if index < node.exposedInputs.length}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 8 8"
|
||||
class="port"
|
||||
data-port="input"
|
||||
data-datatype={parameter.dataType}
|
||||
style:--data-color={`var(--color-data-${parameter.dataType.toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${parameter.dataType.toLowerCase()}-dim)`}
|
||||
data-datatype={secondary.dataType}
|
||||
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
|
||||
bind:this={inputs[nodeIndex + 1][index + (node.primaryInput ? 1 : 0)]}
|
||||
>
|
||||
<title>{`${dataTypeTooltip(parameter)}\n${inputConnectedToText(parameter)}`}</title>
|
||||
{#if parameter.connectedTo !== undefined}
|
||||
<title>{`${dataTypeTooltip(secondary)}\n${inputConnectedToText(secondary)}`}</title>
|
||||
{#if secondary.connectedTo !== undefined}
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color)" />
|
||||
{:else}
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color-dim)" />
|
||||
|
@ -783,19 +783,19 @@
|
|||
{/if}
|
||||
</svg>
|
||||
{/if}
|
||||
{#each node.exposedOutputs as parameter, outputIndex}
|
||||
{#each node.exposedOutputs as secondary, outputIndex}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 8 8"
|
||||
class="port"
|
||||
data-port="output"
|
||||
data-datatype={parameter.dataType}
|
||||
style:--data-color={`var(--color-data-${parameter.dataType.toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${parameter.dataType.toLowerCase()}-dim)`}
|
||||
data-datatype={secondary.dataType}
|
||||
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
|
||||
bind:this={outputs[nodeIndex + 1][outputIndex + (node.primaryOutput ? 1 : 0)]}
|
||||
>
|
||||
<title>{`${dataTypeTooltip(parameter)}\n${outputConnectedToText(parameter)}`}</title>
|
||||
{#if parameter.connectedTo !== undefined}
|
||||
<title>{`${dataTypeTooltip(secondary)}\n${outputConnectedToText(secondary)}`}</title>
|
||||
{#if secondary.connectedTo !== undefined}
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color)" />
|
||||
{:else}
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color-dim)" />
|
||||
|
@ -1300,7 +1300,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.parameters {
|
||||
.secondary {
|
||||
background: rgba(var(--color-f-white-rgb), 0.1);
|
||||
|
||||
&.in-selected-network {
|
||||
|
@ -1332,7 +1332,7 @@
|
|||
border-radius: 2px 2px 0 0;
|
||||
background: rgba(var(--color-f-white-rgb), 0.05);
|
||||
|
||||
&.no-parameter-section {
|
||||
&.no-secondary-section {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
@ -1347,13 +1347,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.parameters {
|
||||
.secondary {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.parameter {
|
||||
.secondary-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -98,7 +98,7 @@ The `graphene_core::value::CopiedNode` is a node that, when evaluated, copies `1
|
|||
|
||||
## Creating a new node
|
||||
|
||||
Instead of manually implementing the `Node` trait with complex generics, one can use the `node` macro, which can be applied to a function like `opacity`. This will generate the struct, implementation, node_registry entry, doucment node definition and properties panel entries:
|
||||
Instead of manually implementing the `Node` trait with complex generics, one can use the `node` macro, which can be applied to a function like `opacity`. This will generate the struct, implementation, node_registry entry, document node definition and properties panel entries:
|
||||
|
||||
```rs
|
||||
#[node_macro::node(category("Raster: Adjustments"))]
|
||||
|
@ -115,10 +115,10 @@ The macro invocation can be extended with additional attributes. The currently s
|
|||
## Executing a document `NodeNetwork`
|
||||
|
||||
When the document graph is executed, the following steps occur:
|
||||
- The `NodeNetwork` is flattened using `NodeNetwork::flatten`. This involves removing any `DocumentNodeImplementation::Network` - which allow for nested document node networks (not currently exposed in the UI). Instead, all of the inner nodes are moved into a single node graph.
|
||||
- The `NodeNetwork` is converted into a proto-graph, which separates out the primary input from the secondary inputs. The secondary inputs are stored as a list of node ids in the `ConstructionArgs` struct in the `ProtoNode`. Converting a document graph into a proto graph is done with `NodeNetwork::into_proto_networks`.
|
||||
- The `NodeNetwork` is flattened using `NodeNetwork::flatten`. This involves removing any `DocumentNodeImplementation::Network` - which allow for nested document node networks. Instead, all of the inner nodes are moved into a single node graph.
|
||||
- The `NodeNetwork` is converted into a proto-graph. Each node's inputs are stored as a list of node IDs in the `ConstructionArgs` struct in the `ProtoNode`. Converting a document graph into a proto graph is done with `NodeNetwork::into_proto_networks`.
|
||||
- The newly created `ProtoNode`s are then converted into the corresponding constructor functions using the mapping defined in `node-graph/interpreted-executor/src/node_registry.rs`. This is done by `BorrowTree::push_node`.
|
||||
- The constructor functions are run with the `ConstructionArgs` enum. Constructors generally evaluate the result of these secondary inputs e.g. if you have a `Pi` node that is used as the second input to an `Add` node, the `Add` node's constructor will evaluate the `Pi` node. This is visible if you place a log statement in the `Pi` node's implementation.
|
||||
- The constructor functions are run with the `ConstructionArgs` enum. Constructors generally evaluate the result of these inputs, e.g. if you have a `Pi` node that is used as the second input to an `Add` node, the `Add` node's constructor will evaluate the `Pi` node. This is visible if you place a log statement in the `Pi` node's implementation.
|
||||
- The resolved functions are stored in a `BorrowTree`, which allows previous proto-nodes to be referenced as inputs by later nodes. The `BorrowTree` ensures nodes can't be removed while being referenced by other nodes.
|
||||
|
||||
The definition for the constructor of a node that applies the opacity transformation to each pixel of an image:
|
||||
|
@ -141,7 +141,7 @@ The definition for the constructor of a node that applies the opacity transforma
|
|||
any.into_type_erased()
|
||||
})
|
||||
},
|
||||
// Defines the input, output, and parameters (where each parameter is a function taking in some input and returning another input).
|
||||
// Defines the call argument, return value, and inputs.
|
||||
NodeIOTypes::new(concrete!(Image<Color>), concrete!(Image<Color>), vec![fn_type!((), f64))]),
|
||||
),
|
||||
```
|
||||
|
|
|
@ -96,24 +96,24 @@ where
|
|||
core::any::type_name::<Self::Output>()
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
fn to_node_io(&self, parameters: Vec<Type>) -> NodeIOTypes {
|
||||
fn to_node_io(&self, inputs: Vec<Type>) -> NodeIOTypes {
|
||||
NodeIOTypes {
|
||||
input: concrete!(<Input as StaticTypeSized>::Static),
|
||||
output: concrete!(<Self::Output as StaticTypeSized>::Static),
|
||||
parameters,
|
||||
call_argument: concrete!(<Input as StaticTypeSized>::Static),
|
||||
return_value: concrete!(<Self::Output as StaticTypeSized>::Static),
|
||||
inputs,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
fn to_async_node_io(&self, parameters: Vec<Type>) -> NodeIOTypes
|
||||
fn to_async_node_io(&self, inputs: Vec<Type>) -> NodeIOTypes
|
||||
where
|
||||
<Self::Output as Future>::Output: StaticTypeSized,
|
||||
Self::Output: Future,
|
||||
{
|
||||
NodeIOTypes {
|
||||
input: concrete!(<Input as StaticTypeSized>::Static),
|
||||
call_argument: concrete!(<Input as StaticTypeSized>::Static),
|
||||
// TODO return actual future type
|
||||
output: concrete!(<<Self::Output as Future>::Output as StaticTypeSized>::Static),
|
||||
parameters,
|
||||
return_value: concrete!(<<Self::Output as Future>::Output as StaticTypeSized>::Static),
|
||||
inputs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,14 +70,14 @@ macro_rules! fn_type {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct NodeIOTypes {
|
||||
pub input: Type,
|
||||
pub output: Type,
|
||||
pub parameters: Vec<Type>,
|
||||
pub call_argument: Type,
|
||||
pub return_value: Type,
|
||||
pub inputs: Vec<Type>,
|
||||
}
|
||||
|
||||
impl NodeIOTypes {
|
||||
pub const fn new(input: Type, output: Type, parameters: Vec<Type>) -> Self {
|
||||
Self { input, output, parameters }
|
||||
pub const fn new(call_argument: Type, return_value: Type, inputs: Vec<Type>) -> Self {
|
||||
Self { call_argument, return_value, inputs }
|
||||
}
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
|
@ -96,14 +96,14 @@ impl NodeIOTypes {
|
|||
align: 0,
|
||||
};
|
||||
Self {
|
||||
input: Type::Concrete(tds1),
|
||||
output: Type::Concrete(tds2),
|
||||
parameters: Vec::new(),
|
||||
call_argument: Type::Concrete(tds1),
|
||||
return_value: Type::Concrete(tds2),
|
||||
inputs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> Type {
|
||||
Type::Fn(Box::new(self.input.clone()), Box::new(self.output.clone()))
|
||||
Type::Fn(Box::new(self.call_argument.clone()), Box::new(self.return_value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,8 +111,8 @@ impl core::fmt::Debug for NodeIOTypes {
|
|||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.write_fmt(format_args!(
|
||||
"node({}) -> {}",
|
||||
[&self.input].into_iter().chain(&self.parameters).map(|input| input.to_string()).collect::<Vec<_>>().join(", "),
|
||||
self.output
|
||||
[&self.call_argument].into_iter().chain(&self.inputs).map(|input| input.to_string()).collect::<Vec<_>>().join(", "),
|
||||
self.return_value
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,22 +98,34 @@ pub struct DocumentNode {
|
|||
/// - A [`NodeInput::Network`] which specifies that this input is from outside the graph, which is resolved in the graph flattening step in the case of nested networks.
|
||||
///
|
||||
/// In the root network, it is resolved when evaluating the borrow tree.
|
||||
/// Ensure the click target in the encapsulating network is updated when the inputs cause the node shape to change (currently only when exposing/hiding an input) by using network.update_click_target(node_id).
|
||||
/// Ensure the click target in the encapsulating network is updated when the inputs cause the node shape to change (currently only when exposing/hiding an input)
|
||||
/// by using network.update_click_target(node_id).
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_inputs"))]
|
||||
pub inputs: Vec<NodeInput>,
|
||||
/// Manual composition is a way to override the default composition flow of one node into another.
|
||||
/// Manual composition is the methodology by which most nodes are implemented, involving a call argument and upstream inputs.
|
||||
/// By contrast, automatic composition is an alternative way to handle the composition of nodes as they execute in the graph.
|
||||
/// Normally, the program (the compiled graph) builds up its call stack, with each node calling its upstream predecessor to acquire its input data.
|
||||
/// When the document graph becomes the proto graph, that conceptual model changes into a model that's unique to the proto graph.
|
||||
/// Automatic composition allows a document node to be translated into its place in the proto graph differently, such that
|
||||
/// the node doesn't participate in that process of being called with a call argument and calling its upstream predecessor.
|
||||
/// Instead, it is called directly with its input data from the upstream node, skipping the call stack building process.
|
||||
/// The abstraction is provided by the compiler for nodes which opt for automatic composition. It works by inserting a `ComposeNode`
|
||||
/// into the proto graph, which does the job of calling the upstream node and feeding its output into the downstream node's first input.
|
||||
/// That first input is typically used by manual composition nodes as the call argument, but for automatic composition nodes,
|
||||
/// that first input becomes the input data from the upstream node passed in by the `ComposeNode`.
|
||||
///
|
||||
/// Through the usual node composition flow, the upstream node providing the primary input for a node is evaluated before the node itself is run.
|
||||
/// - Abstract example: upstream node `G` is evaluated and its data feeds into the primary input of downstream node `F`,
|
||||
/// Through automatic composition, the upstream node providing the first input for a proto node is evaluated before the proto node itself is run.
|
||||
/// (That first input is usually the call argument when manual composition is used.)
|
||||
/// - Abstract example: upstream node `G` is evaluated and its data feeds into the first input of downstream node `F`,
|
||||
/// just like function composition where function `G` is evaluated and its result is fed into function `F`.
|
||||
/// - Concrete example: a node that takes an image as primary input will get that image data from an upstream node that produces image output data and is evaluated first before being fed downstream.
|
||||
/// - Concrete example: a node that takes an image as its first input will get that image data from an upstream node that produces image output data and is evaluated first before being fed downstream.
|
||||
///
|
||||
/// This is achieved by automatically inserting `ComposeNode`s, which run the first node with the overall input and then feed the resulting output into the second node.
|
||||
/// The `ComposeNode` is basically a function composition operator: the parentheses in `F(G(x))` or circle math operator in `(F ∘ G)(x)`.
|
||||
/// For flexibility, instead of being a language construct, Graphene splits out composition itself as its own low-level node so that behavior can be overridden.
|
||||
/// The `ComposeNode`s are then inserted during the graph rewriting step for nodes that don't opt out with `manual_composition`.
|
||||
/// Instead of node `G` feeding into node `F` feeding as the result back to the caller,
|
||||
/// the graph is rewritten so nodes `G` and `F` both feed as lambdas into the parameters of a `ComposeNode` which calls `F(G(input))` and returns the result to the caller.
|
||||
/// the graph is rewritten so nodes `G` and `F` both feed as lambdas into the inputs of a `ComposeNode` which calls `F(G(input))` and returns the result to the caller.
|
||||
///
|
||||
/// A node's manual composition input represents an input that is not resolved through graph rewriting with a `ComposeNode`,
|
||||
/// and is instead just passed in when evaluating this node within the borrow tree.
|
||||
|
@ -305,15 +317,15 @@ impl DocumentNode {
|
|||
NodeInput::Reflection(_) => unreachable!("Reflection input was not resolved"),
|
||||
}
|
||||
};
|
||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network { .. })), "received non resolved parameter");
|
||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network { .. })), "received non-resolved input");
|
||||
assert!(
|
||||
!self.inputs.iter().any(|input| matches!(input, NodeInput::Value { .. })),
|
||||
"received value as parameter. inputs: {:#?}, construction_args: {:#?}",
|
||||
"received value as input. inputs: {:#?}, construction_args: {:#?}",
|
||||
self.inputs,
|
||||
args
|
||||
);
|
||||
|
||||
// If we have one parameter of the type inline, set it as the construction args
|
||||
// If we have one input of the type inline, set it as the construction args
|
||||
if let &[NodeInput::Inline(ref inline)] = self.inputs.as_slice() {
|
||||
args = ConstructionArgs::Inline(inline.clone());
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ impl core::fmt::Display for ProtoNetwork {
|
|||
f.write_str("{\n")?;
|
||||
|
||||
f.write_str(&"\t".repeat(indent + 1))?;
|
||||
f.write_str("Primary input: ")?;
|
||||
f.write_str("Input: ")?;
|
||||
match &node.input {
|
||||
ProtoNodeInput::None => f.write_str("None")?,
|
||||
ProtoNodeInput::ManualComposition(ty) => f.write_fmt(format_args!("Manual Composition (type = {ty:?})"))?,
|
||||
|
@ -80,11 +80,11 @@ impl core::fmt::Display for ProtoNetwork {
|
|||
pub enum ConstructionArgs {
|
||||
/// A value of a type that is known, allowing serialization (serde::Deserialize is not object safe)
|
||||
Value(MemoHash<value::TaggedValue>),
|
||||
// TODO: use a struct for clearer naming.
|
||||
/// A list of nodes used as inputs to the constructor function in `node_registry.rs`.
|
||||
/// The bool indicates whether to treat the node as lambda node.
|
||||
// TODO: use a struct for clearer naming.
|
||||
Nodes(Vec<(NodeId, bool)>),
|
||||
// TODO: What?
|
||||
/// Used for GPU computation to work around the limitations of rust-gpu.
|
||||
Inline(InlineRust),
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,8 @@ impl ConstructionArgs {
|
|||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
||||
/// A proto node is an intermediate step between the `DocumentNode` and the boxed struct that actually runs the node (found in the [`BorrowTree`]). It has one primary input and several secondary inputs in [`ConstructionArgs`].
|
||||
/// A proto node is an intermediate step between the `DocumentNode` and the boxed struct that actually runs the node (found in the [`BorrowTree`]).
|
||||
/// At different stages in the compilation process, this struct will be transformed into a reduced (more restricted) form acting as a subset of its original form, but that restricted form is still valid in the earlier stage in the compilation process before it was transformed.
|
||||
pub struct ProtoNode {
|
||||
pub construction_args: ConstructionArgs,
|
||||
pub input: ProtoNodeInput,
|
||||
|
@ -157,14 +158,13 @@ impl Default for ProtoNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// A ProtoNodeInput represents the primary input of a node in a ProtoNetwork.
|
||||
/// Similar to [`crate::document::NodeInput`].
|
||||
/// Similar to the document node's [`crate::document::NodeInput`].
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum ProtoNodeInput {
|
||||
/// [`ProtoNode`]s do not require any input, e.g. the value node just takes in [`ConstructionArgs`].
|
||||
None,
|
||||
/// A ManualComposition input represents an input that opts out of being resolved through the default `ComposeNode`, which first runs the previous (upstream) node, then passes that evaluated
|
||||
/// A ManualComposition input represents an input that opts out of being resolved through the `ComposeNode`, which first runs the previous (upstream) node, then passes that evaluated
|
||||
/// result to this node. Instead, ManualComposition lets this node actually consume the provided input instead of passing it to its predecessor.
|
||||
///
|
||||
/// Say we have the network `a -> b -> c` where `c` is the output node and `a` is the input node.
|
||||
|
@ -536,11 +536,11 @@ impl ProtoNetwork {
|
|||
pub enum GraphErrorType {
|
||||
NodeNotFound(NodeId),
|
||||
InputNodeNotFound(NodeId),
|
||||
UnexpectedGenerics { index: usize, parameters: Vec<Type> },
|
||||
UnexpectedGenerics { index: usize, inputs: Vec<Type> },
|
||||
NoImplementations,
|
||||
NoConstructor,
|
||||
InvalidImplementations { parameters: String, error_inputs: Vec<Vec<(usize, (Type, Type))>> },
|
||||
MultipleImplementations { parameters: String, valid: Vec<NodeIOTypes> },
|
||||
InvalidImplementations { inputs: String, error_inputs: Vec<Vec<(usize, (Type, Type))>> },
|
||||
MultipleImplementations { inputs: String, valid: Vec<NodeIOTypes> },
|
||||
}
|
||||
impl core::fmt::Debug for GraphErrorType {
|
||||
// TODO: format with the document graph context so the input index is the same as in the graph UI.
|
||||
|
@ -548,17 +548,17 @@ impl core::fmt::Debug for GraphErrorType {
|
|||
match self {
|
||||
GraphErrorType::NodeNotFound(id) => write!(f, "Input node {id} is not present in the typing context"),
|
||||
GraphErrorType::InputNodeNotFound(id) => write!(f, "Input node {id} is not present in the typing context"),
|
||||
GraphErrorType::UnexpectedGenerics { index, parameters } => write!(f, "Generic parameters should not exist but found at {index}: {parameters:?}"),
|
||||
GraphErrorType::UnexpectedGenerics { index, inputs } => write!(f, "Generic inputs should not exist but found at {index}: {inputs:?}"),
|
||||
GraphErrorType::NoImplementations => write!(f, "No implementations found"),
|
||||
GraphErrorType::NoConstructor => write!(f, "No construct found for node"),
|
||||
GraphErrorType::InvalidImplementations { parameters, error_inputs } => {
|
||||
GraphErrorType::InvalidImplementations { inputs, error_inputs } => {
|
||||
let ordinal = |x: usize| match x.to_string().as_str() {
|
||||
x if x.ends_with('1') && !x.ends_with("11") => format!("{x}st"),
|
||||
x if x.ends_with('2') && !x.ends_with("12") => format!("{x}nd"),
|
||||
x if x.ends_with('3') && !x.ends_with("13") => format!("{x}rd"),
|
||||
x => format!("{x}th"),
|
||||
};
|
||||
let format_index = |index: usize| if index == 0 { "primary".to_string() } else { format!("{} parameter", ordinal(index)) };
|
||||
let format_index = |index: usize| if index == 0 { "primary".to_string() } else { format!("{} secondary", ordinal(index)) };
|
||||
let format_error = |(index, (real, expected)): &(usize, (Type, Type))| format!("• The {} input expected {} but found {}", format_index(*index), expected, real);
|
||||
let format_error_list = |errors: &Vec<(usize, (Type, Type))>| errors.iter().map(format_error).collect::<Vec<_>>().join("\n");
|
||||
let errors = error_inputs.iter().map(format_error_list).collect::<Vec<_>>();
|
||||
|
@ -568,7 +568,7 @@ impl core::fmt::Debug for GraphErrorType {
|
|||
consider using undo to go back and try another way to connect the nodes.\n\
|
||||
\n\
|
||||
No node implementation exists for type:\n\
|
||||
({parameters})\n\
|
||||
({inputs})\n\
|
||||
\n\
|
||||
Caused by{}:\n\
|
||||
{}",
|
||||
|
@ -576,7 +576,7 @@ impl core::fmt::Debug for GraphErrorType {
|
|||
errors.join("\n")
|
||||
)
|
||||
}
|
||||
GraphErrorType::MultipleImplementations { parameters, valid } => write!(f, "Multiple implementations found ({parameters}):\n{valid:#?}"),
|
||||
GraphErrorType::MultipleImplementations { inputs, valid } => write!(f, "Multiple implementations found ({inputs}):\n{valid:#?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -656,8 +656,8 @@ impl TypingContext {
|
|||
return Ok(inferred.clone());
|
||||
}
|
||||
|
||||
let parameters = match node.construction_args {
|
||||
// If the node has a value parameter we can infer the return type from it
|
||||
let inputs = match node.construction_args {
|
||||
// If the node has a value input we can infer the return type from it
|
||||
ConstructionArgs::Value(ref v) => {
|
||||
assert!(matches!(node.input, ProtoNodeInput::None));
|
||||
// TODO: This should return a reference to the value
|
||||
|
@ -665,7 +665,7 @@ impl TypingContext {
|
|||
self.inferred.insert(node_id, types.clone());
|
||||
return Ok(types);
|
||||
}
|
||||
// If the node has nodes as parameters we can infer the types from the node outputs
|
||||
// If the node has nodes as inputs we can infer the types from the node outputs
|
||||
ConstructionArgs::Nodes(ref nodes) => nodes
|
||||
.iter()
|
||||
.map(|(id, _)| {
|
||||
|
@ -684,19 +684,19 @@ impl TypingContext {
|
|||
ProtoNodeInput::ManualComposition(ref ty) => ty.clone(),
|
||||
ProtoNodeInput::Node(id) | ProtoNodeInput::NodeLambda(id) => {
|
||||
let input = self.inferred.get(&id).ok_or_else(|| vec![GraphError::new(node, GraphErrorType::InputNodeNotFound(id))])?;
|
||||
input.output.clone()
|
||||
input.return_value.clone()
|
||||
}
|
||||
};
|
||||
let impls = self.lookup.get(&node.identifier).ok_or_else(|| vec![GraphError::new(node, GraphErrorType::NoImplementations)])?;
|
||||
|
||||
if let Some(index) = parameters.iter().position(|p| {
|
||||
if let Some(index) = inputs.iter().position(|p| {
|
||||
matches!(p,
|
||||
Type::Fn(_, b) if matches!(b.as_ref(), Type::Generic(_)))
|
||||
}) {
|
||||
return Err(vec![GraphError::new(node, GraphErrorType::UnexpectedGenerics { index, parameters })]);
|
||||
return Err(vec![GraphError::new(node, GraphErrorType::UnexpectedGenerics { index, inputs })]);
|
||||
}
|
||||
|
||||
/// Checks if a proposed input to a particular (primary or secondary) input is valid for its type signature.
|
||||
/// Checks if a proposed input to a particular (primary or secondary) input connector is valid for its type signature.
|
||||
/// `from` indicates the value given to a input, `to` indicates the input's allowed type as specified by its type signature.
|
||||
fn valid_subtype(from: &Type, to: &Type) -> bool {
|
||||
match (from, to) {
|
||||
|
@ -721,10 +721,10 @@ impl TypingContext {
|
|||
}
|
||||
}
|
||||
|
||||
// List of all implementations that match the input and parameter types
|
||||
// List of all implementations that match the input types
|
||||
let valid_output_types = impls
|
||||
.keys()
|
||||
.filter(|node_io| valid_subtype(&input, &node_io.input) && parameters.iter().zip(node_io.parameters.iter()).all(|(p1, p2)| valid_subtype(p1, p2)))
|
||||
.filter(|node_io| valid_subtype(&input, &node_io.call_argument) && inputs.iter().zip(node_io.inputs.iter()).all(|(p1, p2)| valid_subtype(p1, p2)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Attempt to substitute generic types with concrete types and save the list of results
|
||||
|
@ -733,12 +733,12 @@ impl TypingContext {
|
|||
.map(|node_io| {
|
||||
collect_generics(node_io)
|
||||
.iter()
|
||||
.try_for_each(|generic| check_generic(node_io, &input, ¶meters, generic).map(|_| ()))
|
||||
.try_for_each(|generic| check_generic(node_io, &input, &inputs, generic).map(|_| ()))
|
||||
.map(|_| {
|
||||
if let Type::Generic(out) = &node_io.output {
|
||||
((*node_io).clone(), check_generic(node_io, &input, ¶meters, out).unwrap())
|
||||
if let Type::Generic(out) = &node_io.return_value {
|
||||
((*node_io).clone(), check_generic(node_io, &input, &inputs, out).unwrap())
|
||||
} else {
|
||||
((*node_io).clone(), node_io.output.clone())
|
||||
((*node_io).clone(), node_io.return_value.clone())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -754,9 +754,9 @@ impl TypingContext {
|
|||
for node_io in impls.keys() {
|
||||
let current_errors = [&input]
|
||||
.into_iter()
|
||||
.chain(¶meters)
|
||||
.chain(&inputs)
|
||||
.cloned()
|
||||
.zip([&node_io.input].into_iter().chain(&node_io.parameters).cloned())
|
||||
.zip([&node_io.call_argument].into_iter().chain(&node_io.inputs).cloned())
|
||||
.enumerate()
|
||||
.filter(|(_, (p1, p2))| !valid_subtype(p1, p2))
|
||||
.map(|(index, ty)| (node.original_location.inputs(index).min_by_key(|s| s.node.len()).map(|s| s.index).unwrap_or(index), ty))
|
||||
|
@ -769,12 +769,12 @@ impl TypingContext {
|
|||
error_inputs.push(current_errors);
|
||||
}
|
||||
}
|
||||
let parameters = [&input].into_iter().chain(¶meters).map(|t| t.to_string()).collect::<Vec<_>>().join(", ");
|
||||
Err(vec![GraphError::new(node, GraphErrorType::InvalidImplementations { parameters, error_inputs })])
|
||||
let inputs = [&input].into_iter().chain(&inputs).map(|t| t.to_string()).collect::<Vec<_>>().join(", ");
|
||||
Err(vec![GraphError::new(node, GraphErrorType::InvalidImplementations { inputs, error_inputs })])
|
||||
}
|
||||
[(org_nio, output)] => {
|
||||
// TODO: Fix unsoundness caused by generic parameters not getting cleaned up
|
||||
let node_io = NodeIOTypes::new(input, (*output).clone(), parameters);
|
||||
let node_io = NodeIOTypes::new(input, (*output).clone(), inputs);
|
||||
|
||||
// Save the inferred type
|
||||
self.inferred.insert(node_id, node_io.clone());
|
||||
|
@ -783,12 +783,12 @@ impl TypingContext {
|
|||
}
|
||||
// If two types are available and one of them accepts () an input, always choose that one
|
||||
[first, second] => {
|
||||
if first.0.input != second.0.input {
|
||||
if first.0.call_argument != second.0.call_argument {
|
||||
for (org_nio, output) in [first, second] {
|
||||
if org_nio.input != concrete!(()) {
|
||||
if org_nio.call_argument != concrete!(()) {
|
||||
continue;
|
||||
}
|
||||
let node_io = NodeIOTypes::new(input, (*output).clone(), parameters);
|
||||
let node_io = NodeIOTypes::new(input, (*output).clone(), inputs);
|
||||
|
||||
// Save the inferred type
|
||||
self.inferred.insert(node_id, node_io.clone());
|
||||
|
@ -796,15 +796,15 @@ impl TypingContext {
|
|||
return Ok(node_io);
|
||||
}
|
||||
}
|
||||
let parameters = [&input].into_iter().chain(¶meters).map(|t| t.to_string()).collect::<Vec<_>>().join(", ");
|
||||
let inputs = [&input].into_iter().chain(&inputs).map(|t| t.to_string()).collect::<Vec<_>>().join(", ");
|
||||
let valid = valid_output_types.into_iter().cloned().collect();
|
||||
Err(vec![GraphError::new(node, GraphErrorType::MultipleImplementations { parameters, valid })])
|
||||
Err(vec![GraphError::new(node, GraphErrorType::MultipleImplementations { inputs, valid })])
|
||||
}
|
||||
|
||||
_ => {
|
||||
let parameters = [&input].into_iter().chain(¶meters).map(|t| t.to_string()).collect::<Vec<_>>().join(", ");
|
||||
let inputs = [&input].into_iter().chain(&inputs).map(|t| t.to_string()).collect::<Vec<_>>().join(", ");
|
||||
let valid = valid_output_types.into_iter().cloned().collect();
|
||||
Err(vec![GraphError::new(node, GraphErrorType::MultipleImplementations { parameters, valid })])
|
||||
Err(vec![GraphError::new(node, GraphErrorType::MultipleImplementations { inputs, valid })])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -812,14 +812,14 @@ impl TypingContext {
|
|||
|
||||
/// Returns a list of all generic types used in the node
|
||||
fn collect_generics(types: &NodeIOTypes) -> Vec<Cow<'static, str>> {
|
||||
let inputs = [&types.input].into_iter().chain(types.parameters.iter().flat_map(|x| x.fn_output()));
|
||||
let inputs = [&types.call_argument].into_iter().chain(types.inputs.iter().flat_map(|x| x.fn_output()));
|
||||
let mut generics = inputs
|
||||
.filter_map(|t| match t {
|
||||
Type::Generic(out) => Some(out.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if let Type::Generic(out) = &types.output {
|
||||
if let Type::Generic(out) = &types.return_value {
|
||||
generics.push(out.clone());
|
||||
}
|
||||
generics.dedup();
|
||||
|
@ -828,9 +828,9 @@ fn collect_generics(types: &NodeIOTypes) -> Vec<Cow<'static, str>> {
|
|||
|
||||
/// Checks if a generic type can be substituted with a concrete type and returns the concrete type
|
||||
fn check_generic(types: &NodeIOTypes, input: &Type, parameters: &[Type], generic: &str) -> Result<Type, String> {
|
||||
let inputs = [(Some(&types.input), Some(input))]
|
||||
let inputs = [(Some(&types.call_argument), Some(input))]
|
||||
.into_iter()
|
||||
.chain(types.parameters.iter().map(|x| x.fn_output()).zip(parameters.iter().map(|x| x.fn_output())));
|
||||
.chain(types.inputs.iter().map(|x| x.fn_output()).zip(parameters.iter().map(|x| x.fn_output())));
|
||||
let concrete_inputs = inputs.filter(|(ni, _)| matches!(ni, Some(Type::Generic(input)) if generic == input));
|
||||
let mut outputs = concrete_inputs.flat_map(|(_, out)| out);
|
||||
let out_ty = outputs
|
||||
|
|
|
@ -33,9 +33,9 @@ async fn compile_gpu<'a: 'n>(_: (), node: &'a DocumentNode, typing_context: Typi
|
|||
.inputs
|
||||
.iter()
|
||||
.map(|id| typing_context.type_of(*id).unwrap())
|
||||
.map(|node_io| node_io.output.clone())
|
||||
.map(|node_io| node_io.return_value.clone())
|
||||
.collect();
|
||||
let output_types = proto_networks.iter().map(|network| typing_context.type_of(network.output).unwrap().output.clone()).collect();
|
||||
let output_types = proto_networks.iter().map(|network| typing_context.type_of(network.output).unwrap().return_value.clone()).collect();
|
||||
|
||||
Ok(compilation_client::compile(proto_networks, input_types, output_types, io).await.unwrap())
|
||||
}
|
||||
|
|
|
@ -100,11 +100,11 @@ impl DynamicExecutor {
|
|||
}
|
||||
|
||||
pub fn input_type(&self) -> Option<Type> {
|
||||
self.typing_context.type_of(self.output).map(|node_io| node_io.input.clone())
|
||||
self.typing_context.type_of(self.output).map(|node_io| node_io.call_argument.clone())
|
||||
}
|
||||
|
||||
pub fn output_type(&self) -> Option<Type> {
|
||||
self.typing_context.type_of(self.output).map(|node_io| node_io.output.clone())
|
||||
self.typing_context.type_of(self.output).map(|node_io| node_io.return_value.clone())
|
||||
}
|
||||
|
||||
pub fn document_node_types<'a>(&'a self, nodes: impl Iterator<Item = Path> + 'a) -> impl Iterator<Item = (Path, NodeTypes)> + 'a {
|
||||
|
@ -327,7 +327,7 @@ impl BorrowTree {
|
|||
log::warn!("did not find type");
|
||||
return false;
|
||||
};
|
||||
let inputs = [&node_io.input].into_iter().chain(&node_io.parameters).cloned().collect();
|
||||
let inputs = [&node_io.call_argument].into_iter().chain(&node_io.inputs).cloned().collect();
|
||||
|
||||
let node_path = &proto_node.original_location.path.as_ref().unwrap_or(const { &vec![] });
|
||||
|
||||
|
@ -337,7 +337,7 @@ impl BorrowTree {
|
|||
id,
|
||||
NodeTypes {
|
||||
inputs,
|
||||
output: node_io.output.clone(),
|
||||
output: node_io.return_value.clone(),
|
||||
},
|
||||
);
|
||||
let modified = *entry != update;
|
||||
|
|
|
@ -60,11 +60,11 @@ macro_rules! register_node {
|
|||
},
|
||||
{
|
||||
let node = <$path>::new($(
|
||||
graphene_std::any::PanicNode::<(), $type>::new()
|
||||
graphene_std::any::PanicNode::<(), $type>::new()
|
||||
),*);
|
||||
let params = vec![$(fn_type!((), $type)),*];
|
||||
let mut node_io = <$path as NodeIO<'_, $input>>::to_node_io(&node, params);
|
||||
node_io.input = concrete!(<$input as StaticType>::Static);
|
||||
node_io.call_argument = concrete!(<$input as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
)
|
||||
|
@ -72,7 +72,7 @@ macro_rules! register_node {
|
|||
}
|
||||
macro_rules! async_node {
|
||||
// TODO: we currently need to annotate the type here because the compiler would otherwise (correctly)
|
||||
// assign a Pin<Box<dyn Future<Output=T>>> type to the node, which is not what we want for now.
|
||||
// TODO: assign a Pin<Box<dyn Future<Output=T>>> type to the node, which is not what we want for now.
|
||||
//
|
||||
// This `params` variant of the macro wraps the normal `fn_params` variant and is used as a shorthand for writing `T` instead of `() => T`
|
||||
($path:ty, input: $input:ty, params: [$($type:ty),*]) => {
|
||||
|
@ -91,13 +91,13 @@ macro_rules! async_node {
|
|||
},
|
||||
{
|
||||
let node = <$path>::new($(
|
||||
graphene_std::any::PanicNode::<$arg, core::pin::Pin<Box<dyn core::future::Future<Output = $type> + Send>>>::new()
|
||||
graphene_std::any::PanicNode::<$arg, core::pin::Pin<Box<dyn core::future::Future<Output = $type> + Send>>>::new()
|
||||
),*);
|
||||
// TODO: Propagate the future type through the node graph
|
||||
// let params = vec![$(Type::Fn(Box::new(concrete!(())), Box::new(Type::Future(Box::new(concrete!($type)))))),*];
|
||||
let params = vec![$(fn_type!($arg, $type)),*];
|
||||
let mut node_io = NodeIO::<'_, $input>::to_async_node_io(&node, params);
|
||||
node_io.input = concrete!(<$input as StaticType>::Static);
|
||||
node_io.call_argument = concrete!(<$input as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// TODO: Deprecate and remove this file
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use proc_macro_error::proc_macro_error;
|
||||
|
@ -51,14 +53,14 @@ mod validation;
|
|||
/// where S1: Node<'input, (), Output = f64>,
|
||||
/// ```
|
||||
///
|
||||
/// for two `f64` parameter (secondary input) types. Since Graphene works by having each function evaluate its upstream node as a lambda that returns output data, these secondary inputs are not directly `f64` values but rather `Node`s that output `f64` values when evaluated (in this case, with an empty input of `()`).
|
||||
/// for two `f64` secondary input types. Since Graphene works by having each function evaluate its upstream node as a lambda that returns output data, these secondary inputs are not directly `f64` values but rather `Node`s that output `f64` values when evaluated (in this case, with an empty input of `()`).
|
||||
/// - Mapping the function's return type to the impl'd `Node` trait's associated type, e.g.:
|
||||
///
|
||||
/// ```ignore
|
||||
/// Output = Color
|
||||
/// ```
|
||||
///
|
||||
/// for a `Color` return (secondary output) type.
|
||||
/// for a `Color` secondary output type.
|
||||
///
|
||||
/// ## `eval()` method generation
|
||||
///
|
||||
|
|
|
@ -37,7 +37,7 @@ Starting from the left, the <img src="https://static.graphite.rs/content/learn/i
|
|||
|
||||
<p><img src="https://static.graphite.rs/content/learn/introduction/features-and-limitations/blue-arch-shape.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width /= 2" alt="" /></p>
|
||||
|
||||
Next, that is fed into the <img src="https://static.graphite.rs/content/learn/introduction/features-and-limitations/circular-repeat-node__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width /= 2" style="vertical-align: middle" alt="Circular Repeat" /> node which has several *parameters* you can modify and get different output data based on your choices, like in these examples:
|
||||
Next, that is fed into the <img src="https://static.graphite.rs/content/learn/introduction/features-and-limitations/circular-repeat-node__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width /= 2" style="vertical-align: middle" alt="Circular Repeat" /> node which has several parameters you can modify and get different output data based on your choices, like in these examples:
|
||||
|
||||
<style class="table-1-style">
|
||||
.table-1-style + table {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue