mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 14:58:05 +08:00
Implement node composition and Cache node
This commit is contained in:
38
node-graph/src/iter.rs
Normal file
38
node-graph/src/iter.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
#[derive(Clone)]
|
||||
pub struct InsertAfterNth<A>
|
||||
where
|
||||
A: Iterator,
|
||||
{
|
||||
n: usize,
|
||||
iter: A,
|
||||
value: Option<A::Item>,
|
||||
}
|
||||
|
||||
impl<A> Iterator for InsertAfterNth<A>
|
||||
where
|
||||
A: Iterator,
|
||||
{
|
||||
type Item = A::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.n {
|
||||
1.. => {
|
||||
self.n -= 1;
|
||||
self.iter.next()
|
||||
}
|
||||
0 if self.value.is_some() => self.value.take(),
|
||||
_ => self.iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_after_nth<A>(n: usize, iter: A, value: A::Item) -> InsertAfterNth<A>
|
||||
where
|
||||
A: Iterator,
|
||||
{
|
||||
InsertAfterNth {
|
||||
n,
|
||||
iter,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
@@ -1,131 +1,29 @@
|
||||
use std::{any::Any, iter::Sum, ops::Add};
|
||||
#![deny(rust_2018_idioms)]
|
||||
use std::any::Any;
|
||||
|
||||
pub struct InsertAfterNth<A>
|
||||
where
|
||||
A: Iterator,
|
||||
{
|
||||
n: usize,
|
||||
iter: A,
|
||||
value: Option<A::Item>,
|
||||
}
|
||||
mod iter;
|
||||
mod nodes;
|
||||
use iter::insert_after_nth;
|
||||
use nodes::*;
|
||||
|
||||
impl<A> Iterator for InsertAfterNth<A>
|
||||
where
|
||||
A: Iterator,
|
||||
{
|
||||
type Item = A::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.n {
|
||||
1.. => {
|
||||
self.n -= 1;
|
||||
self.iter.next()
|
||||
}
|
||||
0 if self.value.is_some() => self.value.take(),
|
||||
_ => self.iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_after_nth<A>(n: usize, iter: A, value: A::Item) -> InsertAfterNth<A>
|
||||
where
|
||||
A: Iterator,
|
||||
{
|
||||
InsertAfterNth {
|
||||
n,
|
||||
iter,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
trait Node<O> {
|
||||
fn eval<'a>(&'a self, input: impl Iterator<Item = &'a dyn Any>) -> O;
|
||||
pub trait Node<'n, OUT> {
|
||||
fn eval(&'n self, input: impl Iterator<Item = &'n dyn Any> + Clone) -> OUT;
|
||||
// fn source code
|
||||
// positon
|
||||
}
|
||||
|
||||
struct IntNode;
|
||||
impl Node<u32> for IntNode {
|
||||
fn eval<'a>(&'a self, _input: impl Iterator<Item = &'a dyn Any>) -> u32 {
|
||||
42
|
||||
}
|
||||
}
|
||||
|
||||
struct AddNode;
|
||||
impl<T: Sum + 'static + Copy> Node<T> for AddNode {
|
||||
fn eval<'a>(&'a self, input: impl Iterator<Item = &'a dyn Any>) -> T {
|
||||
input
|
||||
.take(2)
|
||||
.map(|x| *(x.downcast_ref::<T>().unwrap()))
|
||||
.sum::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
struct CurryNthArgNode<'a, T: Node<O>, A, O, const N: usize> {
|
||||
node: &'a T,
|
||||
arg: A,
|
||||
_phantom_data: std::marker::PhantomData<O>,
|
||||
}
|
||||
impl<'a, T: Node<O>, A: 'static, O, const N: usize> Node<O> for CurryNthArgNode<'a, T, A, O, N> {
|
||||
fn eval<'b>(&'b self, input: impl Iterator<Item = &'b dyn Any>) -> O {
|
||||
self.node
|
||||
.eval(insert_after_nth(N, input, &self.arg as &dyn Any))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Node<O>, A: 'static, O, const N: usize> CurryNthArgNode<'a, T, A, O, N> {
|
||||
fn new(node: &'a T, arg: A) -> Self {
|
||||
CurryNthArgNode::<'a, T, A, O, N> {
|
||||
node,
|
||||
arg,
|
||||
_phantom_data: std::marker::PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ComposeNode<'a, L, R, B>
|
||||
where
|
||||
L: Node<B>,
|
||||
{
|
||||
first: &'a L,
|
||||
second: &'a R,
|
||||
_phantom_data: std::marker::PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<'a, B: 'static, L, R, O> Node<O> for ComposeNode<'a, L, R, B>
|
||||
where
|
||||
L: Node<B>,
|
||||
R: Node<O>,
|
||||
{
|
||||
fn eval<'b>(&'b self, input: impl Iterator<Item = &'b dyn Any>) -> O {
|
||||
let curry = CurryNthArgNode::<'a, R, B, O, 0> {
|
||||
node: self.second,
|
||||
arg: self.first.eval(input),
|
||||
_phantom_data: std::marker::PhantomData::default(),
|
||||
};
|
||||
let result: O = curry.eval([].into_iter());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L, R, B: 'static> ComposeNode<'a, L, R, B>
|
||||
where
|
||||
L: Node<B>,
|
||||
{
|
||||
fn new(first: &'a L, second: &'a R) -> Self {
|
||||
ComposeNode::<'a, L, R, B> {
|
||||
first,
|
||||
second,
|
||||
_phantom_data: std::marker::PhantomData::default(),
|
||||
}
|
||||
}
|
||||
trait After<'n, OUT, SECOND: Node<'n, OUT>> {
|
||||
fn after<INTERMEDIATE, FIRST: Node<'n, INTERMEDIATE>>(
|
||||
&'n self,
|
||||
first: &'n FIRST,
|
||||
) -> ComposeNode<'n, FIRST, SECOND, INTERMEDIATE>;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let int = IntNode;
|
||||
let curry: CurryNthArgNode<_, u32, u32, 0> =
|
||||
CurryNthArgNode::new(&AddNode, int.eval(std::iter::empty()));
|
||||
let composition = ComposeNode::new(&curry, &curry);
|
||||
let curry: CurryNthArgNode<_, u32, _, 0> = CurryNthArgNode::new(&composition, 10);
|
||||
println!("{}", curry.eval(std::iter::empty()))
|
||||
use std::iter;
|
||||
let int = IntNode::<32>;
|
||||
let curry: CurryNthArgNode<'_, _, _, u32, u32, 0> = CurryNthArgNode::new(&AddNode, &int);
|
||||
let composition = curry.after(&curry);
|
||||
let n = ValueNode::new(10_u32);
|
||||
let curry: CurryNthArgNode<'_, _, _, u32, _, 0> = CurryNthArgNode::new(&composition, &n);
|
||||
println!("{}", curry.eval(iter::empty()))
|
||||
}
|
||||
|
||||
200
node-graph/src/nodes.rs
Normal file
200
node-graph/src/nodes.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
use std::{
|
||||
any::Any, collections::hash_map::DefaultHasher, hash::Hasher, iter, iter::Sum,
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use crate::{insert_after_nth, After, Node};
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
pub struct IntNode<const N: u32>;
|
||||
impl<'n, const N: u32> Node<'n, u32> for IntNode<N> {
|
||||
fn eval(&'n self, _input: impl Iterator<Item = &'n dyn Any>) -> u32 {
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ValueNode<T>(T);
|
||||
impl<'n, T> Node<'n, &'n T> for ValueNode<T> {
|
||||
fn eval(&'n self, _input: impl Iterator<Item = &'n dyn Any>) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl<'n, T: Copy> Node<'n, T> for ValueNode<T> {
|
||||
fn eval(&'n self, _input: impl Iterator<Item = &'n dyn Any>) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ValueNode<T> {
|
||||
pub fn new(value: T) -> ValueNode<T> {
|
||||
ValueNode(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddNode;
|
||||
impl<'n, T: Sum + 'static + Copy> Node<'n, T> for AddNode {
|
||||
fn eval(&'n self, input: impl Iterator<Item = &'n dyn Any>) -> T {
|
||||
input.map(|x| *(x.downcast_ref::<T>().unwrap())).sum::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
pub struct CacheNode<'n, NODE: Node<'n, OUT>, OUT: Clone> {
|
||||
node: &'n NODE,
|
||||
cache: OnceCell<OUT>,
|
||||
}
|
||||
impl<'n, NODE: Node<'n, OUT>, OUT: Clone> Node<'n, &'n OUT> for CacheNode<'n, NODE, OUT> {
|
||||
fn eval(&'n self, input: impl Iterator<Item = &'n dyn Any> + Clone) -> &'n OUT {
|
||||
self.cache.get_or_init(|| self.node.eval(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, NODE: Node<'n, OUT>, OUT: Clone> CacheNode<'n, NODE, OUT> {
|
||||
fn clear(&'n mut self) {
|
||||
self.cache = OnceCell::new();
|
||||
}
|
||||
fn new(node: &'n NODE) -> CacheNode<'n, NODE, OUT> {
|
||||
CacheNode {
|
||||
node,
|
||||
cache: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
/// Automatically resets if it receives different input
|
||||
pub struct SmartCacheNode<'n, NODE: Node<'n, OUT>, OUT: Clone> {
|
||||
node: &'n NODE,
|
||||
map: dashmap::DashMap<u64, CacheNode<'n, NODE, OUT>>,
|
||||
}
|
||||
impl<'n, NODE: for<'a> Node<'a, OUT>, OUT: Clone> Node<'n, &'n CacheNode<'n, NODE, OUT>>
|
||||
for SmartCacheNode<'n, NODE, OUT>
|
||||
{
|
||||
fn eval(
|
||||
&'n self,
|
||||
input: impl Iterator<Item = &'n dyn Any> + Clone,
|
||||
) -> &'n CacheNode<'n, NODE, OUT> {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
input.clone().for_each(|value| unsafe {
|
||||
hasher.write(std::slice::from_raw_parts(
|
||||
value as *const dyn Any as *const u8,
|
||||
std::mem::size_of_val(value),
|
||||
))
|
||||
});
|
||||
let hash = hasher.finish();
|
||||
self.map.entry(hash).or_insert(CacheNode::new(self.node));
|
||||
fn map<'a, 'c, 'd, N, OUT: Clone>(
|
||||
_key: &'a u64,
|
||||
node: &'c CacheNode<'d, N, OUT>,
|
||||
) -> &'c CacheNode<'b, N, OUT>
|
||||
where
|
||||
N: for<'b> Node<'b, OUT>,
|
||||
{
|
||||
node
|
||||
}
|
||||
let foo: Option<&CacheNode<'n, NODE, OUT>> = self.map.view(&hash, map);
|
||||
foo.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, NODE: Node<'n, OUT>, OUT: Clone> SmartCacheNode<'n, NODE, OUT> {
|
||||
fn clear(&'n mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
fn new(node: &'n NODE) -> SmartCacheNode<'n, NODE, OUT> {
|
||||
SmartCacheNode {
|
||||
node,
|
||||
map: dashmap::DashMap::new(),
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
pub struct CurryNthArgNode<
|
||||
'n,
|
||||
CurryNode: Node<'n, OUT>,
|
||||
ArgNode: Node<'n, ARG>,
|
||||
ARG: Clone,
|
||||
OUT,
|
||||
const NTH: usize,
|
||||
> {
|
||||
node: &'n CurryNode,
|
||||
arg: CacheNode<'n, ArgNode, ARG>,
|
||||
_phantom_out: std::marker::PhantomData<OUT>,
|
||||
_phantom_arg: std::marker::PhantomData<ARG>,
|
||||
}
|
||||
impl<
|
||||
'n,
|
||||
CurryNode: Node<'n, OUT>,
|
||||
ArgNode: Node<'n, ARG>,
|
||||
ARG: 'static + Clone,
|
||||
OUT,
|
||||
const NTH: usize,
|
||||
> Node<'n, OUT> for CurryNthArgNode<'n, CurryNode, ArgNode, ARG, OUT, NTH>
|
||||
{
|
||||
fn eval(&'n self, input: impl Iterator<Item = &'n dyn Any> + Clone) -> OUT {
|
||||
let arg = self.arg.eval(iter::empty());
|
||||
let arg: &dyn Any = arg as &dyn Any;
|
||||
self.node.eval(insert_after_nth(NTH, input, arg))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, CurryNode: Node<'n, Out>, ArgNode: Node<'n, Arg>, Arg: Clone, Out, const Nth: usize>
|
||||
CurryNthArgNode<'n, CurryNode, ArgNode, Arg, Out, Nth>
|
||||
{
|
||||
pub fn new(node: &'n CurryNode, arg: &'n ArgNode) -> Self {
|
||||
CurryNthArgNode::<'n, CurryNode, ArgNode, Arg, Out, Nth> {
|
||||
node,
|
||||
arg: CacheNode::new(arg),
|
||||
_phantom_out: PhantomData::default(),
|
||||
_phantom_arg: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ComposeNode<'n, FIRST, SECOND, INTERMEDIATE>
|
||||
where
|
||||
FIRST: Node<'n, INTERMEDIATE>,
|
||||
{
|
||||
first: &'n FIRST,
|
||||
second: &'n SECOND,
|
||||
_phantom_data: PhantomData<INTERMEDIATE>,
|
||||
}
|
||||
|
||||
impl<'n, FIRST, SECOND, OUT: 'n, INTERMEDIATE: 'static + Clone> Node<'n, OUT>
|
||||
for ComposeNode<'n, FIRST, SECOND, INTERMEDIATE>
|
||||
where
|
||||
FIRST: Node<'n, INTERMEDIATE>,
|
||||
SECOND: Node<'n, OUT>,
|
||||
{
|
||||
fn eval(&'n self, input: impl Iterator<Item = &'n dyn Any> + Clone) -> OUT {
|
||||
let curry = CurryNthArgNode::<'_, _, _, _, _, 0>::new(self.second, self.first);
|
||||
CurryNthArgNode::<'_, _, _, _, _, 0>::new(curry, ValueNode::new(input)).eval(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, FIRST, SECOND, INTERMEDIATE: 'static> ComposeNode<'n, FIRST, SECOND, INTERMEDIATE>
|
||||
where
|
||||
FIRST: Node<'n, INTERMEDIATE>,
|
||||
{
|
||||
pub fn new(first: &'n FIRST, second: &'n SECOND) -> Self {
|
||||
ComposeNode::<'n, FIRST, SECOND, INTERMEDIATE> {
|
||||
first,
|
||||
second,
|
||||
_phantom_data: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, OUT, SECOND: Node<'n, OUT>> After<'n, OUT, SECOND> for SECOND {
|
||||
fn after<INTERMEDIATE, FIRST: Node<'n, INTERMEDIATE>>(
|
||||
&'n self,
|
||||
first: &'n FIRST,
|
||||
) -> ComposeNode<'n, FIRST, SECOND, INTERMEDIATE> {
|
||||
ComposeNode::<'n, FIRST, SECOND, INTERMEDIATE> {
|
||||
first,
|
||||
second: self,
|
||||
_phantom_data: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user