mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Deprecate automatic composition (#3088)
* Make manual_compositon non optional and rename to call_argument * Fix clippy warnings * Remove automatic composition compiler infrastructure * Implement document migration * Fix tests * Fix compilation on web * Fix doble number test * Remove extra parens * Cleanup * Update demo artwork * Remove last compose node mention * Remove last mention of manual composition
This commit is contained in:
@@ -21,7 +21,6 @@ pub mod raster;
|
||||
pub mod raster_types;
|
||||
pub mod registry;
|
||||
pub mod render_complexity;
|
||||
pub mod structural;
|
||||
pub mod subpath;
|
||||
pub mod table;
|
||||
pub mod text;
|
||||
|
||||
@@ -8,6 +8,21 @@ use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
///
|
||||
/// ```text
|
||||
/// ┌───────────────┐ ┌───────────────┐
|
||||
/// │ │◄───┤ │◄─── EVAL (START)
|
||||
/// │ CacheNode │ │ F │
|
||||
/// │ ├───►│ │───► RESULT (END)
|
||||
/// ┌───────────────┐ ├───────────────┤ └───────────────┘
|
||||
/// │ │◄───┤ │
|
||||
/// │ G │ │ Cached Data │
|
||||
/// │ ├───►│ │
|
||||
/// └───────────────┘ └───────────────┘
|
||||
/// ```
|
||||
///
|
||||
/// The call from `F` directly reaches the `CacheNode` and the `CacheNode` can decide whether to call `G.eval(input_from_f)`
|
||||
/// in the event of a cache miss or just return the cached data in the event of a cache hit.
|
||||
#[derive(Default)]
|
||||
pub struct MemoNode<T, CachedNode> {
|
||||
cache: Arc<Mutex<Option<(u64, T)>>>,
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
use crate::Node;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// This is how we can generically define composition of two nodes.
|
||||
/// This is done generically as shown: <https://files.keavon.com/-/SurprisedGaseousAnhinga/capture.png>
|
||||
/// A concrete example: <https://files.keavon.com/-/ExcitableGoldRay/capture.png>
|
||||
/// And showing the direction of data flow: <https://files.keavon.com/-/SoreShimmeringElephantseal/capture.png>
|
||||
/// ```text
|
||||
/// ┌────────────────┐
|
||||
/// T │ │ U
|
||||
/// ───────────►│ Compose Node ├───────────►
|
||||
/// │ │
|
||||
/// └────┬───────────┤
|
||||
/// ┌──────────┐ │ │
|
||||
/// │ │ T -> V │ │
|
||||
/// │ First ├─────────────►│ │
|
||||
/// │ │ │ │
|
||||
/// └──────────┘ │ │
|
||||
/// ┌──────────┐ │ │
|
||||
/// │ │ V -> U │ │
|
||||
/// │ Second ├─────────────►│ │
|
||||
/// │ │ └───────────┘
|
||||
/// └──────────┘
|
||||
/// ```
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ComposeNode<First, Second, I> {
|
||||
first: First,
|
||||
second: Second,
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<'i, Input: 'i, First, Second> Node<'i, Input> for ComposeNode<First, Second, Input>
|
||||
where
|
||||
First: Node<'i, Input>,
|
||||
Second: Node<'i, <First as Node<'i, Input>>::Output> + 'i,
|
||||
{
|
||||
type Output = <Second as Node<'i, <First as Node<'i, Input>>::Output>>::Output;
|
||||
fn eval(&'i self, input: Input) -> Self::Output {
|
||||
let arg = self.first.eval(input);
|
||||
let second = &self.second;
|
||||
second.eval(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<First, Second, Input> ComposeNode<First, Second, Input> {
|
||||
pub const fn new(first: First, second: Second) -> Self {
|
||||
ComposeNode::<First, Second, Input> { first, second, phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AsyncComposeNode<First, Second, I> {
|
||||
first: First,
|
||||
second: Second,
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<'i, Input: 'static, First, Second> Node<'i, Input> for AsyncComposeNode<First, Second, Input>
|
||||
where
|
||||
First: Node<'i, Input>,
|
||||
First::Output: Future,
|
||||
Second: Node<'i, <<First as Node<'i, Input>>::Output as Future>::Output> + 'i,
|
||||
{
|
||||
type Output = std::pin::Pin<Box<dyn Future<Output = <Second as Node<'i, <<First as Node<'i, Input>>::Output as Future>::Output>>::Output> + 'i>>;
|
||||
fn eval(&'i self, input: Input) -> Self::Output {
|
||||
Box::pin(async move {
|
||||
let arg = self.first.eval(input).await;
|
||||
self.second.eval(arg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, First, Second, Input: 'i> AsyncComposeNode<First, Second, Input>
|
||||
where
|
||||
First: Node<'i, Input>,
|
||||
First::Output: Future,
|
||||
Second: Node<'i, <<First as Node<'i, Input>>::Output as Future>::Output> + 'i,
|
||||
{
|
||||
pub const fn new(first: First, second: Second) -> Self {
|
||||
AsyncComposeNode::<First, Second, Input> { first, second, phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Then<'i, Input: 'i>: Sized {
|
||||
fn then<Second>(self, second: Second) -> ComposeNode<Self, Second, Input>
|
||||
where
|
||||
Self: Node<'i, Input>,
|
||||
Second: Node<'i, <Self as Node<'i, Input>>::Output>,
|
||||
{
|
||||
ComposeNode::new(self, second)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, First: Node<'i, Input>, Input: 'i> Then<'i, Input> for First {}
|
||||
|
||||
pub trait AndThen<'i, Input: 'i>: Sized {
|
||||
fn and_then<Second>(self, second: Second) -> AsyncComposeNode<Self, Second, Input>
|
||||
where
|
||||
Self: Node<'i, Input>,
|
||||
Self::Output: Future,
|
||||
Second: Node<'i, <<Self as Node<'i, Input>>::Output as Future>::Output> + 'i,
|
||||
{
|
||||
AsyncComposeNode::new(self, second)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, First: Node<'i, Input>, Input: 'i> AndThen<'i, Input> for First {}
|
||||
|
||||
pub struct ConsNode<I: From<()>, Root>(pub Root, PhantomData<I>);
|
||||
|
||||
impl<'i, Root, Input: 'i, I: 'i + From<()>> Node<'i, Input> for ConsNode<I, Root>
|
||||
where
|
||||
Root: Node<'i, I>,
|
||||
{
|
||||
type Output = (Input, Root::Output);
|
||||
fn eval(&'i self, input: Input) -> Self::Output {
|
||||
let arg = self.0.eval(I::from(()));
|
||||
(input, arg)
|
||||
}
|
||||
}
|
||||
impl<'i, Root: Node<'i, I>, I: 'i + From<()>> ConsNode<I, Root> {
|
||||
pub fn new(root: Root) -> Self {
|
||||
ConsNode(root, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::generic::FnNode;
|
||||
use crate::value::ValueNode;
|
||||
|
||||
#[test]
|
||||
fn compose() {
|
||||
let value = ValueNode::new(4u32);
|
||||
let compose = value.then(FnNode::new(|x| x));
|
||||
assert_eq!(compose.eval(()), &4u32);
|
||||
let type_erased = &compose as &dyn Node<'_, (), Output = &'_ u32>;
|
||||
assert_eq!(type_erased.eval(()), &4u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ref_eval() {
|
||||
let value = ValueNode::new(5);
|
||||
|
||||
assert_eq!(value.eval(()), &5);
|
||||
let id = FnNode::new(|x| x);
|
||||
|
||||
let compose = ComposeNode::new(&value, &id);
|
||||
|
||||
assert_eq!(compose.eval(()), &5);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,6 @@ pub enum Type {
|
||||
/// A wrapper around the Rust type id for any concrete Rust type. Allows us to do equality comparisons, like checking if a String == a String.
|
||||
Concrete(TypeDescriptor),
|
||||
/// Runtime type information for a function. Given some input, gives some output.
|
||||
/// See the example and explanation in the `ComposeNode` implementation within the node registry for more info.
|
||||
Fn(Box<Type>, Box<Type>),
|
||||
/// Represents a future which promises to return the inner type.
|
||||
Future(Box<Type>),
|
||||
|
||||
Reference in New Issue
Block a user