mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 05:18:19 +00:00
Introduce scopes (#1053)
* Implement let binding * Add lambda inputs * Fix tests * Fix proto network formatting * Generate a template Scoped network by default * Add comment to explain the lambda parameter * Move binding wrapping out of the template * Fix errors cause by image frames
This commit is contained in:
parent
0b813805d2
commit
7254c008f9
12 changed files with 366 additions and 118 deletions
|
@ -82,6 +82,13 @@ pub mod dynamic {
|
|||
}
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct SomeNode;
|
||||
#[node_macro::node_fn(SomeNode)]
|
||||
fn some<T>(input: T) -> Option<T> {
|
||||
Some(input)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct CloneNode<O>(PhantomData<O>);
|
||||
impl<'i, 'n: 'i, O: Clone + 'i> Node<'i, &'n O> for CloneNode<O> {
|
||||
|
|
|
@ -43,17 +43,17 @@ pub struct DocumentNode {
|
|||
}
|
||||
|
||||
impl DocumentNode {
|
||||
pub fn populate_first_network_input(&mut self, node_id: NodeId, output_index: usize, offset: usize) {
|
||||
pub fn populate_first_network_input(&mut self, node_id: NodeId, output_index: usize, offset: usize, lambda: bool) {
|
||||
let input = self
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, input)| matches!(input, NodeInput::Network(_)))
|
||||
.nth(offset)
|
||||
.expect("no network input");
|
||||
.unwrap_or_else(|| panic!("no network input found for {self:#?} and offset: {offset}"));
|
||||
|
||||
let index = input.0;
|
||||
self.inputs[index] = NodeInput::Node { node_id, output_index };
|
||||
self.inputs[index] = NodeInput::Node { node_id, output_index, lambda };
|
||||
}
|
||||
|
||||
fn resolve_proto_node(mut self) -> ProtoNode {
|
||||
|
@ -62,12 +62,12 @@ impl DocumentNode {
|
|||
if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
|
||||
let (input, mut args) = match first {
|
||||
NodeInput::Value { tagged_value, .. } => {
|
||||
assert_eq!(self.inputs.len(), 0);
|
||||
assert_eq!(self.inputs.len(), 0, "{}, {:?}", &self.name, &self.inputs);
|
||||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value))
|
||||
}
|
||||
NodeInput::Node { node_id, output_index } => {
|
||||
NodeInput::Node { node_id, output_index, lambda } => {
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode.");
|
||||
(ProtoNodeInput::Node(node_id), ConstructionArgs::Nodes(vec![]))
|
||||
(ProtoNodeInput::Node(node_id, lambda), ConstructionArgs::Nodes(vec![]))
|
||||
}
|
||||
NodeInput::Network(ty) => (ProtoNodeInput::Network(ty), ConstructionArgs::Nodes(vec![])),
|
||||
};
|
||||
|
@ -81,7 +81,7 @@ impl DocumentNode {
|
|||
|
||||
if let ConstructionArgs::Nodes(nodes) = &mut args {
|
||||
nodes.extend(self.inputs.iter().map(|input| match input {
|
||||
NodeInput::Node { node_id, .. } => *node_id,
|
||||
NodeInput::Node { node_id, lambda, .. } => (*node_id, *lambda),
|
||||
_ => unreachable!(),
|
||||
}));
|
||||
}
|
||||
|
@ -103,11 +103,15 @@ impl DocumentNode {
|
|||
P: Fn(String, usize) -> Option<NodeInput>,
|
||||
{
|
||||
for (index, input) in self.inputs.iter_mut().enumerate() {
|
||||
let &mut NodeInput::Node{node_id: id, output_index} = input else {
|
||||
let &mut NodeInput::Node{node_id: id, output_index, lambda} = input else {
|
||||
continue;
|
||||
};
|
||||
if let Some(&new_id) = new_ids.get(&id) {
|
||||
*input = NodeInput::Node { node_id: new_id, output_index };
|
||||
*input = NodeInput::Node {
|
||||
node_id: new_id,
|
||||
output_index,
|
||||
lambda,
|
||||
};
|
||||
} else if let Some(new_input) = default_input(self.name.clone(), index) {
|
||||
*input = new_input;
|
||||
} else {
|
||||
|
@ -121,21 +125,28 @@ impl DocumentNode {
|
|||
#[derive(Debug, Clone, PartialEq, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum NodeInput {
|
||||
Node { node_id: NodeId, output_index: usize },
|
||||
Node { node_id: NodeId, output_index: usize, lambda: bool },
|
||||
Value { tagged_value: value::TaggedValue, exposed: bool },
|
||||
Network(Type),
|
||||
}
|
||||
|
||||
impl NodeInput {
|
||||
pub const fn node(node_id: NodeId, output_index: usize) -> Self {
|
||||
Self::Node { node_id, output_index }
|
||||
Self::Node { node_id, output_index, lambda: false }
|
||||
}
|
||||
pub const fn lambda(node_id: NodeId, output_index: usize) -> Self {
|
||||
Self::Node { node_id, output_index, lambda: true }
|
||||
}
|
||||
pub const fn value(tagged_value: value::TaggedValue, exposed: bool) -> Self {
|
||||
Self::Value { tagged_value, exposed }
|
||||
}
|
||||
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
|
||||
if let &mut NodeInput::Node { node_id, output_index } = self {
|
||||
*self = NodeInput::Node { node_id: f(node_id), output_index }
|
||||
if let &mut NodeInput::Node { node_id, output_index, lambda } = self {
|
||||
*self = NodeInput::Node {
|
||||
node_id: f(node_id),
|
||||
output_index,
|
||||
lambda,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn is_exposed(&self) -> bool {
|
||||
|
@ -246,7 +257,7 @@ impl NodeNetwork {
|
|||
}
|
||||
|
||||
for input in &mut node.inputs {
|
||||
let &mut NodeInput::Node { node_id, output_index} = input else {
|
||||
let &mut NodeInput::Node { node_id, output_index, .. } = input else {
|
||||
continue;
|
||||
};
|
||||
// Use the initial node when getting the first output
|
||||
|
@ -362,9 +373,9 @@ impl NodeNetwork {
|
|||
for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
|
||||
let offset = network_offsets.entry(network_input).or_insert(0);
|
||||
match document_input {
|
||||
NodeInput::Node { node_id, output_index } => {
|
||||
NodeInput::Node { node_id, output_index, lambda } => {
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(node_id, output_index, *offset);
|
||||
network_input.populate_first_network_input(node_id, output_index, *offset, lambda);
|
||||
}
|
||||
NodeInput::Value { tagged_value, exposed } => {
|
||||
// Skip formatting very large values for seconds in performance speedup
|
||||
|
@ -386,7 +397,7 @@ impl NodeNetwork {
|
|||
assert!(!self.nodes.contains_key(&new_id));
|
||||
self.nodes.insert(new_id, value_node);
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(new_id, 0, *offset);
|
||||
network_input.populate_first_network_input(new_id, 0, *offset, false);
|
||||
}
|
||||
NodeInput::Network(_) => {
|
||||
*network_offsets.get_mut(network_input).unwrap() += 1;
|
||||
|
@ -403,6 +414,7 @@ impl NodeNetwork {
|
|||
.map(|&NodeOutput { node_id, node_output_index }| NodeInput::Node {
|
||||
node_id,
|
||||
output_index: node_output_index,
|
||||
lambda: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -660,7 +672,7 @@ mod test {
|
|||
let reference = ProtoNode {
|
||||
identifier: "graphene_core::structural::ConsNode".into(),
|
||||
input: ProtoNodeInput::Network(concrete!(u32)),
|
||||
construction_args: ConstructionArgs::Nodes(vec![0]),
|
||||
construction_args: ConstructionArgs::Nodes(vec![(0, false)]),
|
||||
};
|
||||
assert_eq!(proto_node, reference);
|
||||
}
|
||||
|
@ -675,7 +687,7 @@ mod test {
|
|||
1,
|
||||
ProtoNode {
|
||||
identifier: "graphene_core::ops::IdNode".into(),
|
||||
input: ProtoNodeInput::Node(11),
|
||||
input: ProtoNodeInput::Node(11, false),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
|
@ -684,14 +696,14 @@ mod test {
|
|||
ProtoNode {
|
||||
identifier: "graphene_core::structural::ConsNode".into(),
|
||||
input: ProtoNodeInput::Network(concrete!(u32)),
|
||||
construction_args: ConstructionArgs::Nodes(vec![14]),
|
||||
construction_args: ConstructionArgs::Nodes(vec![(14, false)]),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
ProtoNode {
|
||||
identifier: "graphene_core::ops::AddNode".into(),
|
||||
input: ProtoNodeInput::Node(10),
|
||||
input: ProtoNodeInput::Node(10, false),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
|
|
|
@ -43,7 +43,7 @@ impl core::fmt::Display for ProtoNetwork {
|
|||
match &node.input {
|
||||
ProtoNodeInput::None => f.write_str("None")?,
|
||||
ProtoNodeInput::Network(ty) => f.write_fmt(format_args!("Network (type = {:?})", ty))?,
|
||||
ProtoNodeInput::Node(_) => f.write_str("Node")?,
|
||||
ProtoNodeInput::Node(_, _) => f.write_str("Node")?,
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
|
||||
|
@ -54,7 +54,7 @@ impl core::fmt::Display for ProtoNetwork {
|
|||
}
|
||||
ConstructionArgs::Nodes(nodes) => {
|
||||
for id in nodes {
|
||||
write_node(f, network, *id, indent + 1)?;
|
||||
write_node(f, network, id.0, indent + 1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,8 @@ impl core::fmt::Display for ProtoNetwork {
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum ConstructionArgs {
|
||||
Value(value::TaggedValue),
|
||||
Nodes(Vec<NodeId>),
|
||||
// the bool indicates whether to treat the node as lambda node
|
||||
Nodes(Vec<(NodeId, bool)>),
|
||||
}
|
||||
|
||||
impl PartialEq for ConstructionArgs {
|
||||
|
@ -101,7 +102,7 @@ impl Hash for ConstructionArgs {
|
|||
impl ConstructionArgs {
|
||||
pub fn new_function_args(&self) -> Vec<String> {
|
||||
match self {
|
||||
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("n{}", n)).collect(),
|
||||
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("n{}", n.0)).collect(),
|
||||
ConstructionArgs::Value(value) => vec![format!("{:?}", value)],
|
||||
}
|
||||
}
|
||||
|
@ -118,13 +119,14 @@ pub struct ProtoNode {
|
|||
pub enum ProtoNodeInput {
|
||||
None,
|
||||
Network(Type),
|
||||
Node(NodeId),
|
||||
// the bool indicates whether to treat the node as lambda node
|
||||
Node(NodeId, bool),
|
||||
}
|
||||
|
||||
impl ProtoNodeInput {
|
||||
pub fn unwrap_node(self) -> NodeId {
|
||||
match self {
|
||||
ProtoNodeInput::Node(id) => id,
|
||||
ProtoNodeInput::Node(id, _) => id,
|
||||
_ => panic!("tried to unwrap id from non node input \n node: {:#?}", self),
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +144,7 @@ impl ProtoNode {
|
|||
"network".hash(&mut hasher);
|
||||
ty.hash(&mut hasher);
|
||||
}
|
||||
ProtoNodeInput::Node(id) => id.hash(&mut hasher),
|
||||
ProtoNodeInput::Node(id, lambda) => (id, lambda).hash(&mut hasher),
|
||||
};
|
||||
Some(hasher.finish() as NodeId)
|
||||
}
|
||||
|
@ -155,16 +157,18 @@ impl ProtoNode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
|
||||
if let ProtoNodeInput::Node(id) = self.input {
|
||||
self.input = ProtoNodeInput::Node(f(id))
|
||||
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId, skip_lambdas: bool) {
|
||||
if let ProtoNodeInput::Node(id, lambda) = self.input {
|
||||
if !(skip_lambdas && lambda) {
|
||||
self.input = ProtoNodeInput::Node(f(id), lambda)
|
||||
}
|
||||
}
|
||||
if let ConstructionArgs::Nodes(ids) = &mut self.construction_args {
|
||||
ids.iter_mut().for_each(|id| *id = f(*id));
|
||||
ids.iter_mut().filter(|(_, lambda)| !(skip_lambdas && *lambda)).for_each(|(id, _)| *id = f(*id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_construction_nodes(&self) -> Vec<NodeId> {
|
||||
pub fn unwrap_construction_nodes(&self) -> Vec<(NodeId, bool)> {
|
||||
match &self.construction_args {
|
||||
ConstructionArgs::Nodes(nodes) => nodes.clone(),
|
||||
_ => panic!("tried to unwrap nodes from non node construction args \n node: {:#?}", self),
|
||||
|
@ -186,12 +190,12 @@ impl ProtoNetwork {
|
|||
pub fn collect_outwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> {
|
||||
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
|
||||
for (id, node) in &self.nodes {
|
||||
if let ProtoNodeInput::Node(ref_id) = &node.input {
|
||||
if let ProtoNodeInput::Node(ref_id, _) = &node.input {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*ref_id).or_default().push(*id)
|
||||
}
|
||||
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
|
||||
for ref_id in ref_nodes {
|
||||
for (ref_id, _) in ref_nodes {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*ref_id).or_default().push(*id)
|
||||
}
|
||||
|
@ -210,7 +214,7 @@ impl ProtoNetwork {
|
|||
let mut lookup = self.nodes.iter().map(|(id, _)| (*id, *id)).collect::<HashMap<_, _>>();
|
||||
if let Some(sni) = self.nodes[index].1.stable_node_id() {
|
||||
lookup.insert(self.nodes[index].0, sni);
|
||||
self.replace_node_references(&lookup);
|
||||
self.replace_node_references(&lookup, false);
|
||||
self.nodes[index].0 = sni;
|
||||
sni
|
||||
} else {
|
||||
|
@ -221,12 +225,12 @@ impl ProtoNetwork {
|
|||
pub fn collect_inwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> {
|
||||
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
|
||||
for (id, node) in &self.nodes {
|
||||
if let ProtoNodeInput::Node(ref_id) = &node.input {
|
||||
if let ProtoNodeInput::Node(ref_id, _) = &node.input {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*id).or_default().push(*ref_id)
|
||||
}
|
||||
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
|
||||
for ref_id in ref_nodes {
|
||||
for (ref_id, _) in ref_nodes {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*id).or_default().push(*ref_id)
|
||||
}
|
||||
|
@ -248,21 +252,22 @@ impl ProtoNetwork {
|
|||
|
||||
let resolved_lookup = resolved.clone();
|
||||
if let Some((input_node, id, input)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| {
|
||||
if let ProtoNodeInput::Node(input_node) = node.input {
|
||||
if let ProtoNodeInput::Node(input_node, false) = node.input {
|
||||
resolved.insert(*id);
|
||||
let pre_node_input = inputs.get(input_node as usize).expect("input node should exist");
|
||||
Some((input_node, *id, pre_node_input.clone()))
|
||||
} else {
|
||||
resolved.insert(*id);
|
||||
None
|
||||
}
|
||||
}) {
|
||||
lookup.insert(id, compose_node_id);
|
||||
self.replace_node_references(&lookup);
|
||||
self.replace_node_references(&lookup, true);
|
||||
self.nodes.push((
|
||||
compose_node_id,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|
||||
construction_args: ConstructionArgs::Nodes(vec![input_node, id]),
|
||||
construction_args: ConstructionArgs::Nodes(vec![(input_node, false), (id, true)]),
|
||||
input,
|
||||
},
|
||||
));
|
||||
|
@ -338,13 +343,13 @@ impl ProtoNetwork {
|
|||
(pos as NodeId, node)
|
||||
})
|
||||
.collect();
|
||||
self.replace_node_references(&lookup);
|
||||
self.replace_node_references(&lookup, false);
|
||||
assert_eq!(order.len(), self.nodes.len());
|
||||
}
|
||||
|
||||
fn replace_node_references(&mut self, lookup: &HashMap<u64, u64>) {
|
||||
fn replace_node_references(&mut self, lookup: &HashMap<u64, u64>, skip_lambdas: bool) {
|
||||
self.nodes.iter_mut().for_each(|(_, node)| {
|
||||
node.map_ids(|id| *lookup.get(&id).expect("node not found in lookup table"));
|
||||
node.map_ids(|id| *lookup.get(&id).expect("node not found in lookup table"), skip_lambdas);
|
||||
});
|
||||
self.inputs = self.inputs.iter().filter_map(|id| lookup.get(id).copied()).collect();
|
||||
self.output = *lookup.get(&self.output).unwrap();
|
||||
|
@ -403,7 +408,7 @@ impl TypingContext {
|
|||
// If the node has nodes as parameters we can infer the types from the node outputs
|
||||
ConstructionArgs::Nodes(ref nodes) => nodes
|
||||
.iter()
|
||||
.map(|id| {
|
||||
.map(|(id, _)| {
|
||||
self.inferred
|
||||
.get(id)
|
||||
.ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context"))
|
||||
|
@ -416,7 +421,7 @@ impl TypingContext {
|
|||
let input = match node.input {
|
||||
ProtoNodeInput::None => concrete!(()),
|
||||
ProtoNodeInput::Network(ref ty) => ty.clone(),
|
||||
ProtoNodeInput::Node(id) => {
|
||||
ProtoNodeInput::Node(id, _) => {
|
||||
let input = self
|
||||
.inferred
|
||||
.get(&id)
|
||||
|
@ -573,7 +578,7 @@ mod test {
|
|||
println!("{:#?}", construction_network);
|
||||
assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value");
|
||||
assert_eq!(construction_network.nodes.len(), 6);
|
||||
assert_eq!(construction_network.nodes[5].1.construction_args, ConstructionArgs::Nodes(vec![3, 4]));
|
||||
assert_eq!(construction_network.nodes[5].1.construction_args, ConstructionArgs::Nodes(vec![(3, false), (4, true)]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -589,11 +594,11 @@ mod test {
|
|||
ids,
|
||||
vec![
|
||||
15907139529964845467,
|
||||
14192092348022507362,
|
||||
14714934190542167928,
|
||||
4518275895314664278,
|
||||
13912679582583718470,
|
||||
3236993912700824422
|
||||
1552706903207877482,
|
||||
15211082859148708110,
|
||||
3361684226823984981,
|
||||
16609475913638361514,
|
||||
5640155373642511298
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -607,7 +612,7 @@ mod test {
|
|||
7,
|
||||
ProtoNode {
|
||||
identifier: "id".into(),
|
||||
input: ProtoNodeInput::Node(11),
|
||||
input: ProtoNodeInput::Node(11, false),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
|
@ -615,7 +620,7 @@ mod test {
|
|||
1,
|
||||
ProtoNode {
|
||||
identifier: "id".into(),
|
||||
input: ProtoNodeInput::Node(11),
|
||||
input: ProtoNodeInput::Node(11, false),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
|
@ -624,14 +629,14 @@ mod test {
|
|||
ProtoNode {
|
||||
identifier: "cons".into(),
|
||||
input: ProtoNodeInput::Network(concrete!(u32)),
|
||||
construction_args: ConstructionArgs::Nodes(vec![14]),
|
||||
construction_args: ConstructionArgs::Nodes(vec![(14, false)]),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
ProtoNode {
|
||||
identifier: "add".into(),
|
||||
input: ProtoNodeInput::Node(10),
|
||||
input: ProtoNodeInput::Node(10, false),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
|
|
|
@ -40,6 +40,28 @@ impl<_I, _O, S0> DynAnyRefNode<_I, _O, S0> {
|
|||
Self { node, _i: core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
pub struct DynAnyInRefNode<I, O, Node> {
|
||||
node: Node,
|
||||
_i: PhantomData<(I, O)>,
|
||||
}
|
||||
impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<'input, Any<'input>> for DynAnyInRefNode<_I, _O, N>
|
||||
where
|
||||
N: for<'any_input> Node<'any_input, &'any_input _I, Output = _O>,
|
||||
{
|
||||
type Output = Any<'input>;
|
||||
fn eval<'node: 'input>(&'node self, input: Any<'input>) -> Self::Output {
|
||||
{
|
||||
let node_name = core::any::type_name::<N>();
|
||||
let input: Box<&_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyNode Input, {e} in:\n{node_name}"));
|
||||
Box::new(self.node.eval(*input))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<_I, _O, S0> DynAnyInRefNode<_I, _O, S0> {
|
||||
pub const fn new(node: S0) -> Self {
|
||||
Self { node, _i: core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoTypeErasedNode<'n> {
|
||||
fn into_type_erased(self) -> TypeErasedPinned<'n>;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use graphene_core::Node;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
|
@ -21,3 +23,71 @@ impl<T> CacheNode<T> {
|
|||
CacheNode { cache: OnceCell::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct LetNode<T> {
|
||||
cache: OnceCell<T>,
|
||||
}
|
||||
impl<'i, T: 'i> Node<'i, Option<T>> for LetNode<T> {
|
||||
type Output = &'i T;
|
||||
fn eval<'s: 'i>(&'s self, input: Option<T>) -> Self::Output {
|
||||
match input {
|
||||
Some(input) => {
|
||||
self.cache.set(input).unwrap_or_else(|_| error!("Let node was set twice but is not mutable"));
|
||||
self.cache.get().unwrap()
|
||||
}
|
||||
None => self.cache.get().expect("Let node was not initialized"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LetNode<T> {
|
||||
pub const fn new() -> LetNode<T> {
|
||||
LetNode { cache: OnceCell::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct EndLetNode<Input> {
|
||||
input: Input,
|
||||
}
|
||||
impl<'i, T: 'i, Input> Node<'i, &'i T> for EndLetNode<Input>
|
||||
where
|
||||
Input: Node<'i, ()>,
|
||||
{
|
||||
type Output = <Input>::Output;
|
||||
fn eval<'s: 'i>(&'s self, _: &'i T) -> Self::Output {
|
||||
self.input.eval(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Input> EndLetNode<Input> {
|
||||
pub const fn new(input: Input) -> EndLetNode<Input> {
|
||||
EndLetNode { input }
|
||||
}
|
||||
}
|
||||
|
||||
pub use graphene_core::ops::SomeNode as InitNode;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct RefNode<T, Let> {
|
||||
let_node: Let,
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
impl<'i, T: 'i, Let> Node<'i, ()> for RefNode<T, Let>
|
||||
where
|
||||
Let: for<'a> Node<'a, Option<T>, Output = &'a T>,
|
||||
{
|
||||
type Output = &'i T;
|
||||
fn eval<'s: 'i>(&'s self, _: ()) -> Self::Output {
|
||||
self.let_node.eval(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Let, T> RefNode<T, Let> {
|
||||
pub const fn new(let_node: Let) -> RefNode<T, Let> {
|
||||
RefNode { let_node, _t: PhantomData }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,13 +131,14 @@ pub struct BlendImageNode<Second, MapFn> {
|
|||
map_fn: MapFn,
|
||||
}
|
||||
|
||||
// TODO: Implement proper blending
|
||||
#[node_macro::node_fn(BlendImageNode)]
|
||||
fn blend_image<MapFn>(image: Image, second: Image, map_fn: &'any_input MapFn) -> Image
|
||||
fn blend_image<MapFn>(image: ImageFrame, second: ImageFrame, map_fn: &'any_input MapFn) -> ImageFrame
|
||||
where
|
||||
MapFn: for<'any_input> Node<'any_input, (Color, Color), Output = Color> + 'input,
|
||||
{
|
||||
let mut image = image;
|
||||
for (pixel, sec_pixel) in &mut image.data.iter_mut().zip(second.data.iter()) {
|
||||
for (pixel, sec_pixel) in &mut image.image.data.iter_mut().zip(second.image.data.iter()) {
|
||||
*pixel = map_fn.eval((*pixel, *sec_pixel));
|
||||
}
|
||||
image
|
||||
|
|
|
@ -152,6 +152,7 @@ impl BorrowTree {
|
|||
self.store_node(Arc::new(node), id);
|
||||
}
|
||||
ConstructionArgs::Nodes(ids) => {
|
||||
let ids: Vec<_> = ids.iter().map(|(id, _)| *id).collect();
|
||||
let construction_nodes = self.node_refs(&ids);
|
||||
let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?;
|
||||
let node = constructor(construction_nodes);
|
||||
|
|
|
@ -10,14 +10,14 @@ use graphene_core::structural::Then;
|
|||
use graphene_core::value::{ClonedNode, ForgetNode, ValueNode};
|
||||
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
||||
|
||||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode, TypeErasedPinnedRef};
|
||||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyInRefNode, DynAnyNode, DynAnyRefNode, IntoTypeErasedNode, TypeErasedPinnedRef};
|
||||
|
||||
use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor};
|
||||
|
||||
use graph_craft::proto::NodeConstructor;
|
||||
|
||||
use graphene_core::{concrete, generic};
|
||||
use graphene_std::memo::CacheNode;
|
||||
use graphene_std::memo::{CacheNode, LetNode};
|
||||
|
||||
use crate::executor::NodeContainer;
|
||||
|
||||
|
@ -122,7 +122,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
|
||||
register_node!(graphene_core::ops::AddNode, input: (u32, &u32), params: []),
|
||||
register_node!(graphene_core::ops::CloneNode<_>, input: &Image, params: []),
|
||||
register_node!(graphene_core::ops::CloneNode<_>, input: &ImageFrame, params: []),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
|
||||
|
@ -131,6 +131,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
|
||||
register_node!(graphene_core::ops::SomeNode, input: ImageFrame, params: []),
|
||||
vec![(
|
||||
NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|
||||
|args| {
|
||||
|
@ -146,19 +147,19 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
NodeIdentifier::new("graphene_core::raster::BlendNode<_, _, _, _>"),
|
||||
|args| {
|
||||
use graphene_core::Node;
|
||||
let image: DowncastBothNode<(), Image> = DowncastBothNode::new(args[0]);
|
||||
let image: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]);
|
||||
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]);
|
||||
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]);
|
||||
let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(blend_mode.eval(())), ClonedNode::new(opacity.eval(())));
|
||||
let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node));
|
||||
let _ = &node as &dyn for<'i> Node<'i, Image, Output = Image>;
|
||||
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
let _ = &node as &dyn for<'i> Node<'i, ImageFrame, Output = ImageFrame>;
|
||||
let any: DynAnyNode<ImageFrame, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(
|
||||
concrete!(Image),
|
||||
concrete!(Image),
|
||||
vec![(concrete!(()), concrete!(Image)), (concrete!(()), concrete!(BlendMode)), (concrete!(()), concrete!(f64))],
|
||||
concrete!(ImageFrame),
|
||||
concrete!(ImageFrame),
|
||||
vec![(concrete!(()), concrete!(ImageFrame)), (concrete!(()), concrete!(BlendMode)), (concrete!(()), concrete!(f64))],
|
||||
),
|
||||
)],
|
||||
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]),
|
||||
|
@ -171,6 +172,35 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
|
||||
vec![
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::memo::LetNode<_>"),
|
||||
|_| {
|
||||
let node: LetNode<ImageFrame> = graphene_std::memo::LetNode::new();
|
||||
let any = graphene_std::any::DynAnyRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(concrete!(Option<ImageFrame>), concrete!(&ImageFrame), vec![]),
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"),
|
||||
|args| {
|
||||
let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]);
|
||||
let node = graphene_std::memo::EndLetNode::new(input);
|
||||
let any: DynAnyInRefNode<ImageFrame, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(generic!(T), concrete!(ImageFrame), vec![(concrete!(()), concrete!(ImageFrame))]),
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::memo::RefNode<_, _>"),
|
||||
|args| {
|
||||
let map_fn: DowncastBothRefNode<Option<ImageFrame>, ImageFrame> = DowncastBothRefNode::new(args[0]);
|
||||
let node = graphene_std::memo::RefNode::new(map_fn);
|
||||
let any = graphene_std::any::DynAnyRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(concrete!(()), concrete!(&ImageFrame), vec![]),
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::MapImageNode"),
|
||||
|args| {
|
||||
|
@ -257,7 +287,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|
||||
|_| {
|
||||
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
|
||||
let any = graphene_std::any::DynAnyRefNode::new(node);
|
||||
let any = DynAnyRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(concrete!(Image), concrete!(&Image), vec![]),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue