mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-24 08:05:04 +00:00
Incremental compilation and stable node IDs (#977)
* Generate stable node ids * checkpoint * Implement borrow tree * Add eval function on borrow tree * Refactor Node trait to fix lifetime issues * Compiler infinite loop * Impl compose pair * Transition to double lifetime on trait * Change node trait to use a generic arg for the input * Start adapting node_macro * Migrate more nodes to new macro * Fix raster tests * Port vector nodes * Make Node trait object safe * Fix FlatMapResultNode * Translate most of gstd * Fix DowncastBothNode * Refactor node trait once again to allow for HRTB for type erased nodes * Start working on type erased nodes * Try getting DowncastBothNode to work * Introduce Upcasting node + work on BorrowTree * Make enough 'static to get the code to compile * Transition DynamicExecutor to use borrow tree * Make Compose Node use HRTB's * Fix MapResultNode * Disable blur test * Add workaround for Composing type erased nodes * Convert more nodes in the node_registry * Convert more of the node_registry * Add update tree fn and hook up to frontend * Fix blur node * Implement CacheNode * Make frontend use graph compiler * Fix document_node_types type declaration for most nodes * Remove unused imports * Move comment down * Reuse nodes via borrow tree * Deprecate trait based value in favor of TaggedValue * Remove unsafe code in buffer creation * Fix blur node * Fix stable node id generation * Fix types for Image adjustment document nodes * Fix Imaginate Node * Remove unused imports * Remove log * Fix off by one error * Remove macro generated imaginate node entry * Create parameterized add node * Fix test case * Remove link from layer_panel.rs * Fix formatting
This commit is contained in:
parent
77e69f4e5b
commit
620540d7cd
36 changed files with 1548 additions and 1869 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2676,6 +2676,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
|||
name = "node-macro"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
|
|
@ -14,11 +14,11 @@ pub struct Color {
|
|||
}
|
||||
|
||||
impl Color {
|
||||
pub const BLACK: Color = Color::from_unsafe(0., 0., 0.);
|
||||
pub const WHITE: Color = Color::from_unsafe(1., 1., 1.);
|
||||
pub const RED: Color = Color::from_unsafe(1., 0., 0.);
|
||||
pub const GREEN: Color = Color::from_unsafe(0., 1., 0.);
|
||||
pub const BLUE: Color = Color::from_unsafe(0., 0., 1.);
|
||||
pub const BLACK: Color = Color::from_uchecked(0., 0., 0.);
|
||||
pub const WHITE: Color = Color::from_uchecked(1., 1., 1.);
|
||||
pub const RED: Color = Color::from_uchecked(1., 0., 0.);
|
||||
pub const GREEN: Color = Color::from_uchecked(0., 1., 0.);
|
||||
pub const BLUE: Color = Color::from_uchecked(0., 0., 1.);
|
||||
pub const TRANSPARENT: Color = Self {
|
||||
red: 0.,
|
||||
green: 0.,
|
||||
|
@ -46,7 +46,7 @@ impl Color {
|
|||
}
|
||||
|
||||
/// Return an opaque `Color` from given `f32` RGB channels.
|
||||
pub const fn from_unsafe(red: f32, green: f32, blue: f32) -> Color {
|
||||
pub const fn from_uchecked(red: f32, green: f32, blue: f32) -> Color {
|
||||
Color { red, green, blue, alpha: 1. }
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ pub const ASYMPTOTIC_EFFECT: f64 = 0.5;
|
|||
pub const SCALE_EFFECT: f64 = 0.5;
|
||||
|
||||
// Colors
|
||||
pub const COLOR_ACCENT: Color = Color::from_unsafe(0x00 as f32 / 255., 0xA8 as f32 / 255., 0xFF as f32 / 255.);
|
||||
pub const COLOR_ACCENT: Color = Color::from_uchecked(0x00 as f32 / 255., 0xA8 as f32 / 255., 0xFF as f32 / 255.);
|
||||
|
||||
// Fonts
|
||||
pub const DEFAULT_FONT_FAMILY: &str = "Merriweather";
|
||||
|
|
|
@ -284,7 +284,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
}
|
||||
ClearLayerTree => {
|
||||
// Send an empty layer tree
|
||||
let data_buffer: RawBuffer = Self::default().serialize_root().into();
|
||||
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
|
||||
responses.push_back(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer }.into());
|
||||
|
||||
// Clear the options bar
|
||||
|
@ -360,7 +360,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
DocumentHistoryBackward => self.undo(responses).unwrap_or_else(|e| warn!("{}", e)),
|
||||
DocumentHistoryForward => self.redo(responses).unwrap_or_else(|e| warn!("{}", e)),
|
||||
DocumentStructureChanged => {
|
||||
let data_buffer: RawBuffer = self.serialize_root().into();
|
||||
let data_buffer: RawBuffer = self.serialize_root().as_slice().into();
|
||||
responses.push_back(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer }.into())
|
||||
}
|
||||
DuplicateSelectedLayers => {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use super::{node_properties, FrontendGraphDataType, FrontendNodeType};
|
||||
use crate::messages::layout::utility_types::layout_widget::LayoutGroup;
|
||||
|
||||
use graph_craft::concrete;
|
||||
use graph_craft::document::value::*;
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::document::{value::*, DocumentNodeImplementation};
|
||||
use graph_craft::imaginate_input::ImaginateSamplingMethod;
|
||||
use graph_craft::proto::{NodeIdentifier, Type};
|
||||
use graph_craft::{concrete, generic};
|
||||
use graphene_core::raster::Image;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
@ -91,8 +91,8 @@ fn document_node_types() -> Vec<DocumentNodeType> {
|
|||
1,
|
||||
DocumentNode {
|
||||
name: "BlurNode".to_string(),
|
||||
inputs: vec![NodeInput::Node(0), NodeInput::Network, NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::raster::BlurNode", &[])),
|
||||
inputs: vec![NodeInput::Node(0), NodeInput::Network, NodeInput::Network, NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::raster::BlurNode", &[concrete!("Image")])),
|
||||
metadata: Default::default(),
|
||||
},
|
||||
),
|
||||
|
@ -118,7 +118,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Identity",
|
||||
category: "General",
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::General,
|
||||
|
@ -130,7 +130,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Image",
|
||||
category: "Ignore",
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]),
|
||||
inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), false)],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
|
||||
|
@ -138,7 +138,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Input",
|
||||
category: "Ignore",
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
|
@ -150,7 +150,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Output",
|
||||
category: "Ignore",
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
|
@ -162,7 +162,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Grayscale",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::GrayscaleNode", &[]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::GrayscaleNode", &[concrete!("Image")]),
|
||||
inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::no_properties,
|
||||
|
@ -171,7 +171,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "GpuImage",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("Image")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType {
|
||||
|
@ -187,7 +187,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "QuantizeImage",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("Image")]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
|
@ -219,7 +219,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Invert RGB",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::InvertRGBNode", &[]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::InvertRGBNode", &[concrete!("Image")]),
|
||||
inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::no_properties,
|
||||
|
@ -227,7 +227,10 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Hue/Saturation",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto(
|
||||
"graphene_std::raster::HueSaturationNode<_, _, _>",
|
||||
&[concrete!("Image"), concrete!("f64"), concrete!("f64"), concrete!("f64")],
|
||||
),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false),
|
||||
|
@ -240,7 +243,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Brightness/Contrast",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::BrightnessContrastNode<_, _>", &[concrete!("Image"), concrete!("f64"), concrete!("f64")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Brightness", TaggedValue::F64(0.), false),
|
||||
|
@ -252,7 +255,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Gamma",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::GammaNode<_>", &[concrete!("Image"), concrete!("f64")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Gamma", TaggedValue::F64(1.), false),
|
||||
|
@ -263,7 +266,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Opacity",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::OpacityNode<_>", &[concrete!("Image"), concrete!("f64")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Factor", TaggedValue::F64(1.), false),
|
||||
|
@ -274,7 +277,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Posterize",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::PosterizeNode<_>", &[concrete!("Image"), concrete!("f64")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Value", TaggedValue::F64(5.), false),
|
||||
|
@ -285,7 +288,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Exposure",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::ExposureNode<_>", &[concrete!("Image"), concrete!("f64")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Value", TaggedValue::F64(0.), false),
|
||||
|
@ -297,7 +300,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Add",
|
||||
category: "Math",
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::AddParameterNode<_>", &[concrete!("f64"), concrete!("f64")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Input", TaggedValue::F64(0.), true),
|
||||
DocumentInputType::new("Addend", TaggedValue::F64(0.), true),
|
||||
|
@ -324,7 +327,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Path Generator",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "Path Data",
|
||||
data_type: FrontendGraphDataType::Subpath,
|
||||
|
@ -363,7 +366,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[
|
|||
pub const IMAGINATE_NODE: DocumentNodeType = DocumentNodeType {
|
||||
name: "Imaginate",
|
||||
category: "Image Synthesis",
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::ImaginateNode", &[concrete!("&TypeErasedNode")]),
|
||||
identifier: NodeImplementation::proto("graphene_std::raster::ImaginateNode<_>", &[concrete!("Image"), concrete!("Option<std::sync::Arc<Image>>")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Input Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Seed", TaggedValue::F64(0.), false),
|
||||
|
|
|
@ -373,7 +373,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
panic!("Invalid status input")
|
||||
};
|
||||
let NodeInput::Value {tagged_value: TaggedValue::RcImage( cached_data),..} = cached_value else {
|
||||
panic!("Invalid cached image input")
|
||||
panic!("Invalid cached image input, recieved {:?}, index: {}", cached_value, cached_index)
|
||||
};
|
||||
let &NodeInput::Value {tagged_value: TaggedValue::F64( percent_complete),..} = complete_value else {
|
||||
panic!("Invalid percent complete input")
|
||||
|
|
|
@ -9,15 +9,9 @@ use serde::{Deserialize, Serialize};
|
|||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)]
|
||||
pub struct RawBuffer(Vec<u8>);
|
||||
|
||||
impl From<Vec<u64>> for RawBuffer {
|
||||
fn from(iter: Vec<u64>) -> Self {
|
||||
// https://github.com/rust-lang/rust-clippy/issues/4484
|
||||
let v_from_raw: Vec<u8> = unsafe {
|
||||
// prepare for an auto-forget of the initial vec:
|
||||
let v_orig: &mut Vec<_> = &mut *std::mem::ManuallyDrop::new(iter);
|
||||
Vec::from_raw_parts(v_orig.as_mut_ptr() as *mut u8, v_orig.len() * 8, v_orig.capacity() * 8)
|
||||
// v_orig is never used again, so no aliasing issue
|
||||
};
|
||||
impl From<&[u64]> for RawBuffer {
|
||||
fn from(iter: &[u64]) -> Self {
|
||||
let v_from_raw: Vec<u8> = iter.iter().flat_map(|x| x.to_ne_bytes()).collect();
|
||||
Self(v_from_raw)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,11 @@ use document_legacy::{LayerId, Operation as DocumentOperation};
|
|||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::document::{NodeInput, NodeNetwork};
|
||||
use graph_craft::executor::Compiler;
|
||||
use graphene_core::raster::Image;
|
||||
|
||||
use glam::DVec2;
|
||||
use interpreted_executor::executor::DynamicExecutor;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
@ -30,6 +32,7 @@ pub struct PortfolioMessageHandler {
|
|||
menu_bar_message_handler: MenuBarMessageHandler,
|
||||
documents: HashMap<u64, DocumentMessageHandler>,
|
||||
document_ids: Vec<u64>,
|
||||
executor: interpreted_executor::executor::DynamicExecutor,
|
||||
active_document_id: Option<u64>,
|
||||
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
|
||||
pub persistent_data: PersistentData,
|
||||
|
@ -680,39 +683,31 @@ impl PortfolioMessageHandler {
|
|||
}
|
||||
|
||||
/// Execute the network by flattening it and creating a borrow stack. Casts the output to the generic `T`.
|
||||
fn execute_network<T: dyn_any::StaticType>(mut network: NodeNetwork, image: Image) -> Result<T, String> {
|
||||
for node_id in network.nodes.keys().copied().collect::<Vec<_>>() {
|
||||
network.flatten(node_id);
|
||||
}
|
||||
|
||||
let mut proto_network = network.into_proto_network();
|
||||
proto_network.reorder_ids();
|
||||
fn execute_network<T: dyn_any::StaticType>(executor: &mut DynamicExecutor, network: NodeNetwork, image: Image) -> Result<T, String> {
|
||||
let c = Compiler {};
|
||||
let proto_network = c.compile(network, true);
|
||||
|
||||
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
|
||||
let stack = borrow_stack::FixedSizeStack::new(proto_network.nodes.len());
|
||||
for (_id, node) in proto_network.nodes {
|
||||
interpreted_executor::node_registry::push_node(node, &stack);
|
||||
}
|
||||
executor.update(proto_network);
|
||||
|
||||
use borrow_stack::BorrowStack;
|
||||
use dyn_any::IntoDynAny;
|
||||
use graphene_core::Node;
|
||||
use graph_craft::executor::Executor;
|
||||
|
||||
let boxed = unsafe { stack.get().last().unwrap().eval(image.into_dyn()) };
|
||||
let boxed = executor.execute(image.into_dyn()).map_err(|e| e.to_string())?;
|
||||
|
||||
dyn_any::downcast::<T>(boxed).map(|v| *v)
|
||||
}
|
||||
|
||||
/// Computes an input for a node in the graph
|
||||
fn compute_input<T: dyn_any::StaticType>(old_network: &NodeNetwork, node_path: &[NodeId], mut input_index: usize, image: Cow<Image>) -> Result<T, String> {
|
||||
fn compute_input<T: dyn_any::StaticType>(executor: &mut DynamicExecutor, old_network: &NodeNetwork, node_path: &[NodeId], mut input_index: usize, image: Cow<Image>) -> Result<T, String> {
|
||||
let mut network = old_network.clone();
|
||||
// Adjust the output of the graph so we find the relevant output
|
||||
'outer: for end in (0..node_path.len()).rev() {
|
||||
let mut inner_network = &mut network;
|
||||
for node_id in node_path.iter().take(end) {
|
||||
inner_network.output = *node_id;
|
||||
for &node_id in &node_path[..end] {
|
||||
inner_network.output = node_id;
|
||||
|
||||
let Some(new_inner) = inner_network.nodes.get_mut(node_id).and_then(|node| node.implementation.get_network_mut()) else {
|
||||
let Some(new_inner) = inner_network.nodes.get_mut(&node_id).and_then(|node| node.implementation.get_network_mut()) else {
|
||||
return Err("Failed to find network".to_string());
|
||||
};
|
||||
inner_network = new_inner;
|
||||
|
@ -730,7 +725,7 @@ impl PortfolioMessageHandler {
|
|||
.0;
|
||||
}
|
||||
// If the input is just a value, return that value
|
||||
NodeInput::Value { tagged_value, .. } => return dyn_any::downcast::<T>(tagged_value.clone().to_value().up_box()).map(|v| *v),
|
||||
NodeInput::Value { tagged_value, .. } => return dyn_any::downcast::<T>(tagged_value.clone().to_any()).map(|v| *v),
|
||||
// If the input is from a node, set the node to be the output (so that is what is evaluated)
|
||||
NodeInput::Node(n) => {
|
||||
inner_network.output = *n;
|
||||
|
@ -739,7 +734,7 @@ impl PortfolioMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
Self::execute_network(network, image.into_owned())
|
||||
Self::execute_network(executor, network, image.into_owned())
|
||||
}
|
||||
|
||||
/// Encodes an image into a format using the image crate
|
||||
|
@ -793,7 +788,7 @@ impl PortfolioMessageHandler {
|
|||
|
||||
let get = |name: &str| IMAGINATE_NODE.inputs.iter().position(|input| input.name == name).unwrap_or_else(|| panic!("Input {name} not found"));
|
||||
|
||||
let resolution: Option<glam::DVec2> = Self::compute_input(&network, &imaginate_node, get("Resolution"), Cow::Borrowed(&image))?;
|
||||
let resolution: Option<glam::DVec2> = Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Resolution"), Cow::Borrowed(&image))?;
|
||||
let resolution = resolution.unwrap_or_else(|| {
|
||||
let transform = document.document_legacy.root.transform.inverse() * document.document_legacy.multiply_transforms(&layer_path).unwrap();
|
||||
let (x, y) = pick_safe_imaginate_resolution((transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length()));
|
||||
|
@ -802,23 +797,23 @@ impl PortfolioMessageHandler {
|
|||
|
||||
let transform = document.document_legacy.root.transform.inverse() * document.document_legacy.multiply_transforms(&layer_path).unwrap();
|
||||
let parameters = ImaginateGenerationParameters {
|
||||
seed: Self::compute_input::<f64>(&network, &imaginate_node, get("Seed"), Cow::Borrowed(&image))? as u64,
|
||||
seed: Self::compute_input::<f64>(&mut self.executor, &network, &imaginate_node, get("Seed"), Cow::Borrowed(&image))? as u64,
|
||||
resolution: resolution.as_uvec2().into(),
|
||||
samples: Self::compute_input::<f64>(&network, &imaginate_node, get("Samples"), Cow::Borrowed(&image))? as u32,
|
||||
sampling_method: Self::compute_input::<ImaginateSamplingMethod>(&network, &imaginate_node, get("Sampling Method"), Cow::Borrowed(&image))?
|
||||
samples: Self::compute_input::<f64>(&mut self.executor, &network, &imaginate_node, get("Samples"), Cow::Borrowed(&image))? as u32,
|
||||
sampling_method: Self::compute_input::<ImaginateSamplingMethod>(&mut self.executor, &network, &imaginate_node, get("Sampling Method"), Cow::Borrowed(&image))?
|
||||
.api_value()
|
||||
.to_string(),
|
||||
text_guidance: Self::compute_input(&network, &imaginate_node, get("Prompt Guidance"), Cow::Borrowed(&image))?,
|
||||
text_prompt: Self::compute_input(&network, &imaginate_node, get("Prompt"), Cow::Borrowed(&image))?,
|
||||
negative_prompt: Self::compute_input(&network, &imaginate_node, get("Negative Prompt"), Cow::Borrowed(&image))?,
|
||||
image_creativity: Some(Self::compute_input::<f64>(&network, &imaginate_node, get("Image Creativity"), Cow::Borrowed(&image))? / 100.),
|
||||
restore_faces: Self::compute_input(&network, &imaginate_node, get("Improve Faces"), Cow::Borrowed(&image))?,
|
||||
tiling: Self::compute_input(&network, &imaginate_node, get("Tiling"), Cow::Borrowed(&image))?,
|
||||
text_guidance: Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Prompt Guidance"), Cow::Borrowed(&image))?,
|
||||
text_prompt: Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Prompt"), Cow::Borrowed(&image))?,
|
||||
negative_prompt: Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Negative Prompt"), Cow::Borrowed(&image))?,
|
||||
image_creativity: Some(Self::compute_input::<f64>(&mut self.executor, &network, &imaginate_node, get("Image Creativity"), Cow::Borrowed(&image))? / 100.),
|
||||
restore_faces: Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Improve Faces"), Cow::Borrowed(&image))?,
|
||||
tiling: Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Tiling"), Cow::Borrowed(&image))?,
|
||||
};
|
||||
let use_base_image = Self::compute_input::<bool>(&network, &imaginate_node, get("Adapt Input Image"), Cow::Borrowed(&image))?;
|
||||
let use_base_image = Self::compute_input::<bool>(&mut self.executor, &network, &imaginate_node, get("Adapt Input Image"), Cow::Borrowed(&image))?;
|
||||
|
||||
let base_image = if use_base_image {
|
||||
let image: Image = Self::compute_input(&network, &imaginate_node, get("Input Image"), Cow::Borrowed(&image))?;
|
||||
let image: Image = Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Input Image"), Cow::Borrowed(&image))?;
|
||||
// Only use if has size
|
||||
if image.width > 0 && image.height > 0 {
|
||||
let (image_data, size) = Self::encode_img(image, Some(resolution), image::ImageOutputFormat::Png)?;
|
||||
|
@ -834,7 +829,7 @@ impl PortfolioMessageHandler {
|
|||
|
||||
let mask_image =
|
||||
if base_image.is_some() {
|
||||
let mask_path: Option<Vec<LayerId>> = Self::compute_input(&network, &imaginate_node, get("Masking Layer"), Cow::Borrowed(&image))?;
|
||||
let mask_path: Option<Vec<LayerId>> = Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Masking Layer"), Cow::Borrowed(&image))?;
|
||||
|
||||
// Calculate the size of the node graph frame
|
||||
let size = DVec2::new(transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());
|
||||
|
@ -864,13 +859,13 @@ impl PortfolioMessageHandler {
|
|||
parameters: Box::new(parameters),
|
||||
base_image: base_image.map(Box::new),
|
||||
mask_image: mask_image.map(Box::new),
|
||||
mask_paint_mode: if Self::compute_input::<bool>(&network, &imaginate_node, get("Inpaint"), Cow::Borrowed(&image))? {
|
||||
mask_paint_mode: if Self::compute_input::<bool>(&mut self.executor, &network, &imaginate_node, get("Inpaint"), Cow::Borrowed(&image))? {
|
||||
ImaginateMaskPaintMode::Inpaint
|
||||
} else {
|
||||
ImaginateMaskPaintMode::Outpaint
|
||||
},
|
||||
mask_blur_px: Self::compute_input::<f64>(&network, &imaginate_node, get("Mask Blur"), Cow::Borrowed(&image))? as u32,
|
||||
imaginate_mask_starting_fill: Self::compute_input(&network, &imaginate_node, get("Mask Starting Fill"), Cow::Borrowed(&image))?,
|
||||
mask_blur_px: Self::compute_input::<f64>(&mut self.executor, &network, &imaginate_node, get("Mask Blur"), Cow::Borrowed(&image))? as u32,
|
||||
imaginate_mask_starting_fill: Self::compute_input(&mut self.executor, &network, &imaginate_node, get("Mask Starting Fill"), Cow::Borrowed(&image))?,
|
||||
hostname: preferences.imaginate_server_hostname.clone(),
|
||||
refresh_frequency: preferences.imaginate_refresh_frequency,
|
||||
document_id,
|
||||
|
@ -880,7 +875,7 @@ impl PortfolioMessageHandler {
|
|||
.into(),
|
||||
);
|
||||
} else {
|
||||
let mut image: Image = Self::execute_network(network, image)?;
|
||||
let mut image: Image = Self::execute_network(&mut self.executor, network, image)?;
|
||||
|
||||
// If no image was generated, use the input image
|
||||
if image.width == 0 || image.height == 0 {
|
||||
|
|
|
@ -2,17 +2,10 @@ use core::marker::PhantomData;
|
|||
|
||||
use crate::Node;
|
||||
pub struct FnNode<T: Fn(I) -> O, I, O>(T, PhantomData<(I, O)>);
|
||||
impl<T: Fn(I) -> O, O, I> Node<I> for FnNode<T, I, O> {
|
||||
type Output = O;
|
||||
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
self.0(input)
|
||||
}
|
||||
}
|
||||
impl<'n, T: Fn(I) -> O, O, I> Node<I> for &'n FnNode<T, I, O> {
|
||||
impl<'i, T: Fn(I) -> O + 'i, O: 'i, I: 'i> Node<'i, I> for FnNode<T, I, O> {
|
||||
type Output = O;
|
||||
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
self.0(input)
|
||||
}
|
||||
}
|
||||
|
@ -23,23 +16,14 @@ impl<T: Fn(I) -> O, I, O> FnNode<T, I, O> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct FnNodeWithState<'n, T: Fn(I, &'n State) -> O, I, O: 'n, State: 'n>(T, State, PhantomData<&'n (O, I)>);
|
||||
impl<'n, T: Fn(I, &State) -> O, I, O: 'n, State: 'n> Node<I> for &'n FnNodeWithState<'n, T, I, O, State> {
|
||||
pub struct FnNodeWithState<'i, T: Fn(I, &'i State) -> O, I, O, State: 'i>(T, State, PhantomData<(&'i O, I)>);
|
||||
impl<'i, I: 'i, O: 'i, State, T: Fn(I, &'i State) -> O + 'i> Node<'i, I> for FnNodeWithState<'i, T, I, O, State> {
|
||||
type Output = O;
|
||||
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
self.0(input, &self.1)
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
(self.0)(input, &self.1)
|
||||
}
|
||||
}
|
||||
impl<'n, T: Fn(I, &State) -> O, I, O: 'n, State: 'n> Node<I> for FnNodeWithState<'n, T, I, O, State> {
|
||||
type Output = O;
|
||||
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
self.0(input, &self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, T: Fn(I, &State) -> O, I, O, State> FnNodeWithState<'n, T, I, O, State> {
|
||||
impl<'i, 's: 'i, I, O, State, T: Fn(I, &'i State) -> O> FnNodeWithState<'i, T, I, O, State> {
|
||||
pub fn new(f: T, state: State) -> Self {
|
||||
FnNodeWithState(f, state, PhantomData)
|
||||
}
|
||||
|
|
|
@ -7,11 +7,6 @@ extern crate alloc;
|
|||
#[cfg(feature = "log")]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "async")]
|
||||
use async_trait::async_trait;
|
||||
|
||||
pub mod generic;
|
||||
pub mod ops;
|
||||
pub mod structural;
|
||||
|
@ -26,100 +21,32 @@ pub mod raster;
|
|||
#[cfg(feature = "alloc")]
|
||||
pub mod vector;
|
||||
|
||||
pub trait Node<T> {
|
||||
type Output;
|
||||
|
||||
fn eval(self, input: T) -> Self::Output;
|
||||
// pub trait Node: for<'n> NodeIO<'n> {
|
||||
pub trait Node<'i, Input: 'i>: 'i {
|
||||
type Output: 'i;
|
||||
fn eval<'s: 'i>(&'s self, input: Input) -> Self::Output;
|
||||
}
|
||||
|
||||
trait Input<I> {
|
||||
unsafe fn input(&self, input: I);
|
||||
}
|
||||
/*impl<'i, I: 'i, O: 'i> Node<'i, I> for &'i dyn for<'n> Node<'n, I, Output = O> {
|
||||
type Output = O;
|
||||
|
||||
pub trait RefNode<T> {
|
||||
type Output;
|
||||
|
||||
fn eval_ref(&self, input: T) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<'n, N: 'n, I> RefNode<I> for &'n N
|
||||
where
|
||||
&'n N: Node<I>,
|
||||
Self: 'n,
|
||||
{
|
||||
type Output = <&'n N as Node<I>>::Output;
|
||||
fn eval_ref(&self, input: I) -> Self::Output {
|
||||
self.eval(input)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsRefNode<'n, T>
|
||||
where
|
||||
&'n Self: Node<T>,
|
||||
Self: 'n,
|
||||
{
|
||||
type Output;
|
||||
fn eval_box(&'n self, input: T) -> <Self>::Output;
|
||||
}
|
||||
|
||||
impl<'n, N: 'n, I> AsRefNode<'n, I> for N
|
||||
where
|
||||
&'n N: Node<I>,
|
||||
N: Node<I>,
|
||||
Self: 'n,
|
||||
{
|
||||
type Output = <&'n N as Node<I>>::Output;
|
||||
fn eval_box(&'n self, input: I) -> <Self>::Output {
|
||||
self.eval(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, T> Node<T> for &'n (dyn AsRefNode<'n, T, Output = T> + 'n) {
|
||||
type Output = T;
|
||||
fn eval(self, input: T) -> Self::Output {
|
||||
self.eval_box(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
pub trait AsyncNode<T> {
|
||||
type Output;
|
||||
|
||||
async fn eval_async(self, input: T) -> Self::Output;
|
||||
}
|
||||
|
||||
/*#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
impl<'n, N: Node<T> + Send + Sync + 'n, T: Send + 'n> AsyncNode<T> for N {
|
||||
type Output = N::Output;
|
||||
|
||||
async fn eval_async(self, input: T) -> Self::Output {
|
||||
Node::eval(self, input)
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
(**self).eval(input)
|
||||
}
|
||||
}*/
|
||||
impl<'i, 'n: 'i, I: 'i, O: 'i> Node<'i, I> for &'n dyn for<'a> Node<'a, I, Output = O> {
|
||||
type Output = O;
|
||||
|
||||
pub trait Cache {
|
||||
fn clear(&mut self);
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
(**self).eval(input)
|
||||
}
|
||||
}
|
||||
use core::pin::Pin;
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<Box<dyn for<'a> Node<'a, I, Output = O> + 'i>> {
|
||||
type Output = O;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<N, I> Node<I> for Box<N>
|
||||
where
|
||||
N: Node<I>,
|
||||
{
|
||||
type Output = <N as Node<I>>::Output;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
(*self).eval(input)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
impl<'n, N, I> Node<I> for &'n Box<N>
|
||||
where
|
||||
&'n N: Node<I>,
|
||||
{
|
||||
type Output = <&'n N as Node<I>>::Output;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
self.as_ref().eval(input)
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
(**self).eval(input)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,36 @@
|
|||
use core::marker::PhantomData;
|
||||
use core::ops::Add;
|
||||
|
||||
use crate::{Node, RefNode};
|
||||
use crate::Node;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct AddNode;
|
||||
impl<'n, L: Add<R, Output = O> + 'n, R, O: 'n> Node<(L, R)> for AddNode {
|
||||
|
||||
impl<'i, L: Add<R, Output = O> + 'i, R: 'i, O: 'i> Node<'i, (L, R)> for AddNode {
|
||||
type Output = <L as Add<R>>::Output;
|
||||
fn eval(self, input: (L, R)) -> Self::Output {
|
||||
input.0 + input.1
|
||||
}
|
||||
}
|
||||
impl<'n, L: Add<R, Output = O> + 'n, R, O: 'n> Node<(L, R)> for &'n AddNode {
|
||||
type Output = <L as Add<R>>::Output;
|
||||
fn eval(self, input: (L, R)) -> Self::Output {
|
||||
input.0 + input.1
|
||||
}
|
||||
}
|
||||
impl<'n, L: Add<R, Output = O> + 'n + Copy, R: Copy, O: 'n> Node<&'n (L, R)> for AddNode {
|
||||
type Output = <L as Add<R>>::Output;
|
||||
fn eval(self, input: &'n (L, R)) -> Self::Output {
|
||||
input.0 + input.1
|
||||
}
|
||||
}
|
||||
impl<'n, L: Add<R, Output = O> + 'n + Copy, R: Copy, O: 'n> Node<&'n (L, R)> for &'n AddNode {
|
||||
type Output = <L as Add<R>>::Output;
|
||||
fn eval(self, input: &'n (L, R)) -> Self::Output {
|
||||
fn eval<'s: 'i>(&'s self, input: (L, R)) -> Self::Output {
|
||||
input.0 + input.1
|
||||
}
|
||||
}
|
||||
|
||||
impl AddNode {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
pub struct AddParameterNode<Second> {
|
||||
second: Second,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(AddParameterNode)]
|
||||
fn flat_map<U, T>(first: U, second: T) -> <U as Add<T>>::Output
|
||||
where
|
||||
U: Add<T>,
|
||||
{
|
||||
first + second
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(feature = "std")]
|
||||
pub mod dynamic {
|
||||
use super::*;
|
||||
|
@ -65,9 +60,9 @@ pub mod dynamic {
|
|||
};
|
||||
}
|
||||
|
||||
impl<'n> Node<(Dynamic<'n>, Dynamic<'n>)> for DynamicAddNode {
|
||||
type Output = Dynamic<'n>;
|
||||
fn eval(self, (left, right): (Dynamic, Dynamic)) -> Self::Output {
|
||||
impl<'i> Node<(Dynamic<'i>, Dynamic<'i>)> for DynamicAddNode {
|
||||
type Output = Dynamic<'i>;
|
||||
fn eval<'s: 'i>(self, (left, right): (Dynamic, Dynamic)) -> Self::Output {
|
||||
resolve_dynamic_types! { AddNode =>
|
||||
(left: usize, right: usize)
|
||||
(left: u8, right: u8)
|
||||
|
@ -85,106 +80,87 @@ pub mod dynamic {
|
|||
(left: f64, right: f64) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CloneNode;
|
||||
impl<'n, O: Clone> Node<&'n O> for CloneNode {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct CloneNode<O>(PhantomData<O>);
|
||||
impl<'i, O: Clone + 'i> Node<'i, &'i O> for CloneNode<O> {
|
||||
type Output = O;
|
||||
fn eval(self, input: &'n O) -> Self::Output {
|
||||
fn eval<'s: 'i>(&'s self, input: &'i O) -> Self::Output {
|
||||
input.clone()
|
||||
}
|
||||
}
|
||||
impl<'n, O: Clone> Node<&'n O> for &CloneNode {
|
||||
type Output = O;
|
||||
fn eval(self, input: &'n O) -> Self::Output {
|
||||
input.clone()
|
||||
impl<O> CloneNode<O> {
|
||||
pub const fn new() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct FstNode;
|
||||
impl<'n, T: 'n, U> Node<(T, U)> for FstNode {
|
||||
type Output = T;
|
||||
fn eval(self, input: (T, U)) -> Self::Output {
|
||||
let (a, _) = input;
|
||||
a
|
||||
impl<'i, L: 'i, R: 'i> Node<'i, (L, R)> for FstNode {
|
||||
type Output = L;
|
||||
fn eval<'s: 'i>(&'s self, input: (L, R)) -> Self::Output {
|
||||
input.0
|
||||
}
|
||||
}
|
||||
impl<'n, T: 'n, U> Node<&'n (T, U)> for FstNode {
|
||||
type Output = &'n T;
|
||||
fn eval(self, input: &'n (T, U)) -> Self::Output {
|
||||
let (a, _) = input;
|
||||
a
|
||||
impl FstNode {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructures a Tuple of two values and returns the first one
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct SndNode;
|
||||
impl<'n, T, U: 'n> Node<(T, U)> for SndNode {
|
||||
type Output = U;
|
||||
fn eval(self, input: (T, U)) -> Self::Output {
|
||||
let (_, b) = input;
|
||||
b
|
||||
impl<'i, L: 'i, R: 'i> Node<'i, (L, R)> for SndNode {
|
||||
type Output = R;
|
||||
fn eval<'s: 'i>(&'s self, input: (L, R)) -> Self::Output {
|
||||
input.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, T, U: 'n> Node<&'n (T, U)> for SndNode {
|
||||
type Output = &'n U;
|
||||
fn eval(self, input: &'n (T, U)) -> Self::Output {
|
||||
let (_, b) = input;
|
||||
b
|
||||
impl SndNode {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructures a Tuple of two values and returns them in reverse order
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct SwapNode;
|
||||
impl<'n, T: 'n, U: 'n> Node<(T, U)> for SwapNode {
|
||||
type Output = (U, T);
|
||||
fn eval(self, input: (T, U)) -> Self::Output {
|
||||
let (a, b) = input;
|
||||
(b, a)
|
||||
impl<'i, L: 'i, R: 'i> Node<'i, (L, R)> for SwapNode {
|
||||
type Output = (R, L);
|
||||
fn eval<'s: 'i>(&'s self, input: (L, R)) -> Self::Output {
|
||||
(input.1, input.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, T, U: 'n> Node<&'n (T, U)> for SwapNode {
|
||||
type Output = (&'n U, &'n T);
|
||||
fn eval(self, input: &'n (T, U)) -> Self::Output {
|
||||
let (a, b) = input;
|
||||
(b, a)
|
||||
impl SwapNode {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a tuple with two instances of the input argument
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct DupNode;
|
||||
impl<'n, T: Clone + 'n> Node<T> for DupNode {
|
||||
type Output = (T, T);
|
||||
fn eval(self, input: T) -> Self::Output {
|
||||
impl<'i, O: Clone + 'i> Node<'i, O> for DupNode {
|
||||
type Output = (O, O);
|
||||
fn eval<'s: 'i>(&'s self, input: O) -> Self::Output {
|
||||
(input.clone(), input)
|
||||
}
|
||||
}
|
||||
impl DupNode {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Input Argument
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct IdNode;
|
||||
impl<T> Node<T> for IdNode {
|
||||
type Output = T;
|
||||
fn eval(self, input: T) -> Self::Output {
|
||||
input
|
||||
}
|
||||
}
|
||||
impl<'n, T> Node<T> for &'n IdNode {
|
||||
type Output = T;
|
||||
fn eval(self, input: T) -> Self::Output {
|
||||
input
|
||||
}
|
||||
}
|
||||
impl<T> RefNode<T> for IdNode {
|
||||
type Output = T;
|
||||
fn eval_ref(&self, input: T) -> Self::Output {
|
||||
impl<'i, O: 'i> Node<'i, O> for IdNode {
|
||||
type Output = O;
|
||||
fn eval<'s: 'i>(&'s self, input: O) -> Self::Output {
|
||||
input
|
||||
}
|
||||
}
|
||||
|
@ -197,77 +173,62 @@ impl IdNode {
|
|||
|
||||
/// Ascribe the node types
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct TypeNode<N, I, O>(pub N, pub PhantomData<(I, O)>);
|
||||
impl<N: Node<I>, I> Node<I> for TypeNode<N, I, N::Output> {
|
||||
type Output = N::Output;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
pub struct TypeNode<N: for<'a> Node<'a, I>, I, O>(pub N, pub PhantomData<(I, O)>);
|
||||
impl<'i, N, I: 'i, O: 'i> Node<'i, I> for TypeNode<N, I, O>
|
||||
where
|
||||
N: for<'n> Node<'n, I, Output = O>,
|
||||
{
|
||||
type Output = O;
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
self.0.eval(input)
|
||||
}
|
||||
}
|
||||
impl<N: Node<I> + Copy, I> Node<I> for &TypeNode<N, I, N::Output> {
|
||||
type Output = N::Output;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
self.0.eval(input)
|
||||
}
|
||||
} /*
|
||||
impl<N: RefNode<I>, I> Node<I> for &TypeNode<N, I, N::Output> {
|
||||
type Output = N::Output;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
self.0.eval_ref(input)
|
||||
}
|
||||
}*/
|
||||
|
||||
impl<N: Node<I>, I> TypeNode<N, I, N::Output> {
|
||||
impl<'i, N: for<'a> Node<'a, I>, I: 'i> TypeNode<N, I, <N as Node<'i, I>>::Output> {
|
||||
pub fn new(node: N) -> Self {
|
||||
Self(node, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Node<I> + Clone, I> Clone for TypeNode<N, I, N::Output> {
|
||||
impl<'i, N: for<'a> Node<'a, I> + Clone, I: 'i> Clone for TypeNode<N, I, <N as Node<'i, I>>::Output> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone(), self.1)
|
||||
}
|
||||
}
|
||||
impl<N: Node<I> + Copy, I> Copy for TypeNode<N, I, N::Output> {}
|
||||
impl<'i, N: for<'a> Node<'a, I> + Copy, I: 'i> Copy for TypeNode<N, I, <N as Node<'i, I>>::Output> {}
|
||||
|
||||
pub struct MapResultNode<MN, I, E>(pub MN, pub PhantomData<(I, E)>);
|
||||
|
||||
impl<MN: Node<I>, I, E> Node<Result<I, E>> for MapResultNode<MN, I, E> {
|
||||
type Output = Result<MN::Output, E>;
|
||||
fn eval(self, input: Result<I, E>) -> Self::Output {
|
||||
input.map(|x| self.0.eval(x))
|
||||
}
|
||||
}
|
||||
impl<'n, MN: Node<I> + Copy, I, E> Node<Result<I, E>> for &'n MapResultNode<MN, I, E> {
|
||||
type Output = Result<MN::Output, E>;
|
||||
fn eval(self, input: Result<I, E>) -> Self::Output {
|
||||
input.map(|x| self.0.eval(x))
|
||||
}
|
||||
/// input.map(|x| self.0.eval(x))
|
||||
pub struct MapResultNode<I, E, Mn> {
|
||||
node: Mn,
|
||||
_i: PhantomData<I>,
|
||||
_e: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<MN, I, E> MapResultNode<MN, I, E> {
|
||||
pub const fn new(mn: MN) -> Self {
|
||||
Self(mn, PhantomData)
|
||||
#[node_macro::node_fn(MapResultNode<_I, _E>)]
|
||||
fn flat_map<_I, _E, N>(input: Result<_I, _E>, node: &'any_input N) -> Result<<N as Node<'input, _I>>::Output, _E>
|
||||
where
|
||||
N: for<'a> Node<'a, _I>,
|
||||
{
|
||||
input.map(|x| node.eval(x))
|
||||
}
|
||||
pub struct FlatMapResultNode<I, O, E, Mn> {
|
||||
node: Mn,
|
||||
_i: PhantomData<I>,
|
||||
_o: PhantomData<O>,
|
||||
_e: PhantomData<E>,
|
||||
}
|
||||
pub struct FlatMapResultNode<MN: Node<I>, I, E>(pub MN, pub PhantomData<(I, E)>);
|
||||
|
||||
impl<'n, MN: Node<I, Output = Result<O, E>>, I, O: 'n, E: 'n> Node<Result<I, E>> for FlatMapResultNode<MN, I, E> {
|
||||
type Output = Result<O, E>;
|
||||
fn eval(self, input: Result<I, E>) -> Self::Output {
|
||||
match input.map(|x| self.0.eval(x)) {
|
||||
#[node_macro::node_fn(FlatMapResultNode<_I, _O, _E>)]
|
||||
fn flat_map<_I, _O, _E, N>(input: Result<_I, _E>, node: &'any_input N) -> Result<_O, _E>
|
||||
where
|
||||
N: for<'a> Node<'a, _I, Output = Result<_O, _E>>,
|
||||
{
|
||||
match input.map(|x| node.eval(x)) {
|
||||
Ok(Ok(x)) => Ok(x),
|
||||
Ok(Err(e)) => Err(e),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MN: Node<I>, I, E> FlatMapResultNode<MN, I, E> {
|
||||
pub const fn new(mn: MN) -> Self {
|
||||
Self(mn, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
@ -277,36 +238,69 @@ mod test {
|
|||
#[test]
|
||||
pub fn dup_node() {
|
||||
let value = ValueNode(4u32);
|
||||
let dup = value.then(DupNode);
|
||||
assert_eq!(dup.eval(()), (4, 4));
|
||||
let dup = ComposeNode::new(value, DupNode::new());
|
||||
assert_eq!(dup.eval(()), (&4, &4));
|
||||
}
|
||||
#[test]
|
||||
pub fn id_node() {
|
||||
let value = ValueNode(4u32).then(IdNode);
|
||||
assert_eq!(value.eval(()), 4);
|
||||
let value = ValueNode(4u32).then(IdNode::new());
|
||||
assert_eq!(value.eval(()), &4);
|
||||
}
|
||||
#[test]
|
||||
pub fn clone_node() {
|
||||
let cloned = (&ValueNode(4u32)).then(CloneNode);
|
||||
let cloned = ValueNode(4u32).then(CloneNode::new());
|
||||
assert_eq!(cloned.eval(()), 4);
|
||||
let type_erased = &CloneNode::new() as &dyn for<'a> Node<'a, &'a u32, Output = u32>;
|
||||
assert_eq!(type_erased.eval(&4), 4);
|
||||
let type_erased = &cloned as &dyn for<'a> Node<'a, (), Output = u32>;
|
||||
assert_eq!(type_erased.eval(()), 4);
|
||||
}
|
||||
#[test]
|
||||
pub fn fst_node() {
|
||||
let fst = ValueNode((4u32, "a")).then(FstNode);
|
||||
let fst = ValueNode((4u32, "a")).then(CloneNode::new()).then(FstNode::new());
|
||||
assert_eq!(fst.eval(()), 4);
|
||||
}
|
||||
#[test]
|
||||
pub fn snd_node() {
|
||||
let fst = ValueNode((4u32, "a")).then(SndNode);
|
||||
let fst = ValueNode((4u32, "a")).then(CloneNode::new()).then(SndNode::new());
|
||||
assert_eq!(fst.eval(()), "a");
|
||||
}
|
||||
#[test]
|
||||
pub fn object_safe() {
|
||||
let fst = ValueNode((4u32, "a")).then(CloneNode::new()).then(SndNode::new());
|
||||
let foo = &fst as &dyn Node<(), Output = &str>;
|
||||
assert_eq!(foo.eval(()), "a");
|
||||
}
|
||||
#[test]
|
||||
pub fn map_result() {
|
||||
let value: ClonedNode<Result<&u32, ()>> = ClonedNode(Ok(&4u32));
|
||||
assert_eq!(value.eval(()), Ok(&4u32));
|
||||
static clone: &CloneNode<u32> = &CloneNode::new();
|
||||
//let type_erased_clone = clone as &dyn for<'a> Node<'a, &'a u32, Output = u32>;
|
||||
let map_result = MapResultNode::new(ValueNode::new(FnNode::new(|x: &u32| x.clone())));
|
||||
//et type_erased = &map_result as &dyn for<'a> Node<'a, Result<&'a u32, ()>, Output = Result<u32, ()>>;
|
||||
assert_eq!(map_result.eval(Ok(&4u32)), Ok(4u32));
|
||||
let fst = value.then(map_result);
|
||||
//let type_erased = &fst as &dyn for<'a> Node<'a, (), Output = Result<u32, ()>>;
|
||||
assert_eq!(fst.eval(()), Ok(4u32));
|
||||
}
|
||||
#[test]
|
||||
pub fn flat_map_result() {
|
||||
let fst = ValueNode(Ok(&4u32)).then(CloneNode::new()); //.then(FlatMapResultNode::new(FnNode::new(|x| Ok(x))));
|
||||
let fn_node: FnNode<_, &u32, Result<&u32, _>> = FnNode::new(|_| Err(8u32));
|
||||
assert_eq!(fn_node.eval(&4u32), Err(8u32));
|
||||
let flat_map = FlatMapResultNode::new(ValueNode::new(fn_node));
|
||||
let fst = fst.then(flat_map);
|
||||
assert_eq!(fst.eval(()), Err(8u32));
|
||||
}
|
||||
#[test]
|
||||
pub fn add_node() {
|
||||
let a = ValueNode(42u32);
|
||||
let b = ValueNode(6u32);
|
||||
let cons_a = ConsNode(a, PhantomData);
|
||||
let cons_a = ConsNode::new(a);
|
||||
let tuple = b.then(cons_a);
|
||||
|
||||
let sum = b.then(cons_a).then(AddNode);
|
||||
let sum = tuple.then(AddNode::new());
|
||||
|
||||
assert_eq!(sum.eval(()), 48);
|
||||
}
|
||||
|
@ -321,7 +315,7 @@ mod test {
|
|||
let fnn = FnNode::new(&swap);
|
||||
let fns = FnNodeWithState::new(int, 42u32);
|
||||
assert_eq!(fnn.eval((1u32, 2u32)), (2, 1));
|
||||
let result: u32 = (&fns).eval(());
|
||||
let result: u32 = fns.eval(());
|
||||
assert_eq!(result, 42);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use core::fmt::Debug;
|
||||
use core::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
use crate::Node;
|
||||
|
||||
|
@ -14,78 +14,62 @@ fn grayscale_color_node(input: Color) -> Color {
|
|||
Color::from_rgbaf32_unchecked(avg, avg, avg, input.a())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MapNode<Iter: Iterator, MapFn: Node<Iter::Item>> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MapNode<MapFn> {
|
||||
map_fn: MapFn,
|
||||
_phantom: core::marker::PhantomData<Iter>,
|
||||
}
|
||||
|
||||
impl<Iter: Iterator, MapFn: Node<Iter::Item> + Clone> Clone for MapNode<Iter, MapFn> {
|
||||
#[node_macro::node_fn(MapNode)]
|
||||
fn map_node<_Iter: Iterator, MapFnNode>(input: _Iter, map_fn: &'any_input MapFnNode) -> MapFnIterator<'input, 'input, _Iter, MapFnNode>
|
||||
where
|
||||
MapFnNode: for<'any_input> Node<'any_input, _Iter::Item>,
|
||||
{
|
||||
MapFnIterator::new(input, map_fn)
|
||||
}
|
||||
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
pub struct MapFnIterator<'i, 's, Iter, MapFn> {
|
||||
iter: Iter,
|
||||
map_fn: &'s MapFn,
|
||||
_phantom: core::marker::PhantomData<&'i &'s ()>,
|
||||
}
|
||||
|
||||
impl<'i, 's: 'i, Iter: Debug, MapFn> Debug for MapFnIterator<'i, 's, Iter, MapFn> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("MapFnIterator").field("iter", &self.iter).field("map_fn", &"MapFn").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, 's: 'i, Iter: Clone, MapFn> Clone for MapFnIterator<'i, 's, Iter, MapFn> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
map_fn: self.map_fn.clone(),
|
||||
_phantom: self._phantom,
|
||||
iter: self.iter.clone(),
|
||||
map_fn: self.map_fn,
|
||||
_phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Iter: Iterator, MapFn: Node<Iter::Item> + Copy> Copy for MapNode<Iter, MapFn> {}
|
||||
impl<'i, 's: 'i, Iter: Copy, MapFn> Copy for MapFnIterator<'i, 's, Iter, MapFn> {}
|
||||
|
||||
impl<Iter: Iterator, MapFn: Node<Iter::Item>> MapNode<Iter, MapFn> {
|
||||
pub fn new(map_fn: MapFn) -> Self {
|
||||
impl<'i, 's: 'i, Iter, MapFn> MapFnIterator<'i, 's, Iter, MapFn> {
|
||||
pub fn new(iter: Iter, map_fn: &'s MapFn) -> Self {
|
||||
Self {
|
||||
iter,
|
||||
map_fn,
|
||||
_phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Iter: Iterator<Item = Item>, MapFn: Node<Item, Output = Out>, Item, Out> Node<Iter> for MapNode<Iter, MapFn> {
|
||||
type Output = MapFnIterator<Iter, MapFn>;
|
||||
|
||||
#[inline]
|
||||
fn eval(self, input: Iter) -> Self::Output {
|
||||
MapFnIterator::new(input, self.map_fn)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Iter: Iterator<Item = Item>, MapFn: Node<Item, Output = Out> + Copy, Item, Out> Node<Iter> for &MapNode<Iter, MapFn> {
|
||||
type Output = MapFnIterator<Iter, MapFn>;
|
||||
|
||||
#[inline]
|
||||
fn eval(self, input: Iter) -> Self::Output {
|
||||
MapFnIterator::new(input, self.map_fn)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
#[derive(Clone)]
|
||||
pub struct MapFnIterator<Iter, MapFn> {
|
||||
iter: Iter,
|
||||
map_fn: MapFn,
|
||||
}
|
||||
|
||||
impl<Iter: Debug, MapFn> Debug for MapFnIterator<Iter, MapFn> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("MapFnIterator").field("iter", &self.iter).field("map_fn", &"MapFn").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Iter: Copy, MapFn: Copy> Copy for MapFnIterator<Iter, MapFn> {}
|
||||
|
||||
impl<Iter, MapFn> MapFnIterator<Iter, MapFn> {
|
||||
pub fn new(iter: Iter, map_fn: MapFn) -> Self {
|
||||
Self { iter, map_fn }
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, I: Iterator, F> Iterator for MapFnIterator<I, F>
|
||||
impl<'i, 's: 'i, I: Iterator + 's, F> Iterator for MapFnIterator<'i, 's, I, F>
|
||||
where
|
||||
F: Node<I::Item, Output = B> + Copy,
|
||||
F: Node<'i, I::Item> + 'i,
|
||||
Self: 'i,
|
||||
{
|
||||
type Item = B;
|
||||
type Item = F::Output;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<B> {
|
||||
fn next(&mut self) -> Option<F::Output> {
|
||||
self.iter.next().map(|x| self.map_fn.eval(x))
|
||||
}
|
||||
|
||||
|
@ -95,19 +79,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct WeightedAvgNode<Iter> {
|
||||
_phantom: core::marker::PhantomData<Iter>,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct WeightedAvgNode {}
|
||||
|
||||
impl<Iter> WeightedAvgNode<Iter> {
|
||||
pub fn new() -> Self {
|
||||
Self { _phantom: core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn weighted_avg_node<Iter: Iterator<Item = (Color, f32)> + Clone>(input: Iter) -> Color {
|
||||
#[node_macro::node_fn(WeightedAvgNode)]
|
||||
fn weighted_avg_node<_Iter: Iterator<Item = (Color, f32)>>(input: _Iter) -> Color
|
||||
where
|
||||
_Iter: Clone,
|
||||
{
|
||||
let total_weight: f32 = input.clone().map(|(_, weight)| weight).sum();
|
||||
let total_r: f32 = input.clone().map(|(color, weight)| color.r() * weight).sum();
|
||||
let total_g: f32 = input.clone().map(|(color, weight)| color.g() * weight).sum();
|
||||
|
@ -116,28 +95,10 @@ fn weighted_avg_node<Iter: Iterator<Item = (Color, f32)> + Clone>(input: Iter) -
|
|||
Color::from_rgbaf32_unchecked(total_r / total_weight, total_g / total_weight, total_b / total_weight, total_a / total_weight)
|
||||
}
|
||||
|
||||
impl<Iter: Iterator<Item = (Color, f32)> + Clone> Node<Iter> for WeightedAvgNode<Iter> {
|
||||
type Output = Color;
|
||||
|
||||
#[inline]
|
||||
fn eval(self, input: Iter) -> Self::Output {
|
||||
weighted_avg_node(input)
|
||||
}
|
||||
}
|
||||
impl<Iter: Iterator<Item = (Color, f32)> + Clone> Node<Iter> for &WeightedAvgNode<Iter> {
|
||||
type Output = Color;
|
||||
|
||||
#[inline]
|
||||
fn eval(self, input: Iter) -> Self::Output {
|
||||
weighted_avg_node(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug)]
|
||||
pub struct GaussianNode<Sigma> {
|
||||
sigma: Sigma,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(GaussianNode)]
|
||||
fn gaussian_node(input: f32, sigma: f64) -> f32 {
|
||||
let sigma = sigma as f32;
|
||||
|
@ -157,42 +118,47 @@ fn distance_node(input: (i32, i32)) -> f32 {
|
|||
pub struct ImageIndexIterNode;
|
||||
|
||||
#[node_macro::node_fn(ImageIndexIterNode)]
|
||||
fn image_index_iter_node(input: ImageSlice<'static>) -> core::ops::Range<u32> {
|
||||
fn image_index_iter_node(input: ImageSlice<'input>) -> core::ops::Range<u32> {
|
||||
0..(input.width * input.height)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct WindowNode<Radius, Image> {
|
||||
#[derive(Debug)]
|
||||
pub struct WindowNode<Radius: for<'i> Node<'i, (), Output = u32>, Image: for<'i> Node<'i, (), Output = ImageSlice<'i>>> {
|
||||
radius: Radius,
|
||||
image: Image,
|
||||
}
|
||||
|
||||
impl<Radius, Image> WindowNode<Radius, Image> {
|
||||
pub fn new(radius: Radius, image: Image) -> Self {
|
||||
impl<'input, S0: 'input, S1: 'input> Node<'input, u32> for WindowNode<S0, S1>
|
||||
where
|
||||
S0: for<'any_input> Node<'any_input, (), Output = u32>,
|
||||
S1: for<'any_input> Node<'any_input, (), Output = ImageSlice<'any_input>>,
|
||||
{
|
||||
type Output = ImageWindowIterator<'input>;
|
||||
#[inline]
|
||||
fn eval<'node: 'input>(&'node self, input: u32) -> Self::Output {
|
||||
let radius = self.radius.eval(());
|
||||
let image = self.image.eval(());
|
||||
{
|
||||
let iter = ImageWindowIterator::new(image, radius, input);
|
||||
iter
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<S0, S1> WindowNode<S0, S1>
|
||||
where
|
||||
S0: for<'any_input> Node<'any_input, (), Output = u32>,
|
||||
S1: for<'any_input> Node<'any_input, (), Output = ImageSlice<'any_input>>,
|
||||
{
|
||||
pub const fn new(radius: S0, image: S1) -> Self {
|
||||
Self { radius, image }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Radius: Node<(), Output = u32>, Image: Node<(), Output = ImageSlice<'a>>> Node<u32> for WindowNode<Radius, Image> {
|
||||
type Output = ImageWindowIterator<'a>;
|
||||
#[inline]
|
||||
fn eval(self, input: u32) -> Self::Output {
|
||||
let radius = self.radius.eval(());
|
||||
let image = self.image.eval(());
|
||||
/*
|
||||
#[node_macro::node_fn(WindowNode)]
|
||||
fn window_node(input: u32, radius: u32, image: ImageSlice<'input>) -> ImageWindowIterator<'input> {
|
||||
let iter = ImageWindowIterator::new(image, radius, input);
|
||||
iter
|
||||
}
|
||||
}
|
||||
impl<'a, 'b: 'a, Radius: Node<(), Output = u32> + Copy, Index: Node<(), Output = ImageSlice<'b>> + Copy> Node<u32> for &'a WindowNode<Radius, Index> {
|
||||
type Output = ImageWindowIterator<'a>;
|
||||
#[inline]
|
||||
fn eval(self, input: u32) -> Self::Output {
|
||||
let radius = self.radius.eval(());
|
||||
let image = self.image.eval(());
|
||||
let iter = ImageWindowIterator::new(image, radius, input);
|
||||
iter
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ImageWindowIterator<'a> {
|
||||
|
@ -245,124 +211,74 @@ impl<'a> Iterator for ImageWindowIterator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MapSndNode<MapFn> {
|
||||
#[derive(Debug)]
|
||||
pub struct MapSndNode<First, Second, MapFn> {
|
||||
map_fn: MapFn,
|
||||
_first: PhantomData<First>,
|
||||
_second: PhantomData<Second>,
|
||||
}
|
||||
|
||||
impl<MapFn> MapSndNode<MapFn> {
|
||||
pub fn new(map_fn: MapFn) -> Self {
|
||||
Self { map_fn }
|
||||
}
|
||||
}
|
||||
|
||||
impl<MapFn: Node<I>, I, F> Node<(F, I)> for MapSndNode<MapFn> {
|
||||
type Output = (F, MapFn::Output);
|
||||
#[inline]
|
||||
fn eval(self, input: (F, I)) -> Self::Output {
|
||||
(input.0, self.map_fn.eval(input.1))
|
||||
}
|
||||
}
|
||||
impl<MapFn: Node<I> + Copy, I, F> Node<(F, I)> for &MapSndNode<MapFn> {
|
||||
type Output = (F, MapFn::Output);
|
||||
#[inline]
|
||||
fn eval(self, input: (F, I)) -> Self::Output {
|
||||
(input.0, self.map_fn.eval(input.1))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BrightenColorNode<N: Node<(), Output = f32>>(N);
|
||||
|
||||
impl<N: Node<(), Output = f32>> Node<Color> for BrightenColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
let brightness = self.0.eval(());
|
||||
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
|
||||
Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a())
|
||||
}
|
||||
}
|
||||
impl<N: Node<(), Output = f32> + Copy> Node<Color> for &BrightenColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
let brightness = self.0.eval(());
|
||||
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
|
||||
Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Node<(), Output = f32> + Copy> BrightenColorNode<N> {
|
||||
pub fn new(node: N) -> Self {
|
||||
Self(node)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GammaColorNode<N: Node<(), Output = f32>>(N);
|
||||
|
||||
impl<N: Node<(), Output = f32>> Node<Color> for GammaColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
let gamma = self.0.eval(());
|
||||
let per_channel = |col: f32| col.powf(gamma);
|
||||
Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a())
|
||||
}
|
||||
}
|
||||
impl<N: Node<(), Output = f32> + Copy> Node<Color> for &GammaColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
let gamma = self.0.eval(());
|
||||
let per_channel = |col: f32| col.powf(gamma);
|
||||
Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Node<(), Output = f32> + Copy> GammaColorNode<N> {
|
||||
pub fn new(node: N) -> Self {
|
||||
Self(node)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
pub struct HueShiftColorNode<N: Node<(), Output = f32>>(N);
|
||||
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl<N: Node<(), Output = f32>> Node<Color> for HueShiftColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
let hue_shift = self.0.eval(());
|
||||
let [hue, saturation, lightness, alpha] = color.to_hsla();
|
||||
Color::from_hsla(hue + hue_shift / 360., saturation, lightness, alpha)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl<N: Node<(), Output = f32> + Copy> Node<Color> for &HueShiftColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
let hue_shift = self.0.eval(());
|
||||
let [hue, saturation, lightness, alpha] = color.to_hsla();
|
||||
Color::from_hsla(hue + hue_shift / 360., saturation, lightness, alpha)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl<N: Node<(), Output = f32> + Copy> HueShiftColorNode<N> {
|
||||
pub fn new(node: N) -> Self {
|
||||
Self(node)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ForEachNode<MN>(pub MN);
|
||||
|
||||
impl<'n, I: Iterator<Item = S>, MN: 'n, S> Node<I> for &'n ForEachNode<MN>
|
||||
#[node_macro::node_fn(MapSndNode< _First, _Second>)]
|
||||
fn map_snd_node<MapFn, _First, _Second>(input: (_First, _Second), map_fn: &'any_input MapFn) -> (_First, <MapFn as Node<'input, _Second>>::Output)
|
||||
where
|
||||
&'n MN: Node<S, Output = ()>,
|
||||
MapFn: for<'any_input> Node<'any_input, _Second>,
|
||||
{
|
||||
type Output = ();
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
input.for_each(|x| (&self.0).eval(x))
|
||||
let (a, b) = input;
|
||||
(a, map_fn.eval(b))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BrightenColorNode<Brightness> {
|
||||
brightness: Brightness,
|
||||
}
|
||||
#[node_macro::node_fn(BrightenColorNode)]
|
||||
fn brighten_color_node(color: Color, brightness: f32) -> Color {
|
||||
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
|
||||
Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GammaColorNode<Gamma> {
|
||||
gamma: Gamma,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(GammaColorNode)]
|
||||
fn gamma_color_node(color: Color, gamma: f32) -> Color {
|
||||
let per_channel = |col: f32| col.powf(gamma);
|
||||
Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a())
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
pub use hue_shift::HueShiftColorNode;
|
||||
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
mod hue_shift {
|
||||
use super::*;
|
||||
#[derive(Debug)]
|
||||
pub struct HueShiftColorNode<Angle> {
|
||||
angle: Angle,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(HueShiftColorNode)]
|
||||
fn hue_shift_color_node(color: Color, angle: f32) -> Color {
|
||||
let hue_shift = angle;
|
||||
let [hue, saturation, lightness, alpha] = color.to_hsla();
|
||||
Color::from_hsla(hue + hue_shift / 360., saturation, lightness, alpha)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ForEachNode<Iter, MapNode> {
|
||||
map_node: MapNode,
|
||||
_iter: PhantomData<Iter>,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(ForEachNode<_Iter>)]
|
||||
fn map_node<_Iter: Iterator, MapNode>(input: _Iter, map_node: &'any_input MapNode) -> ()
|
||||
where
|
||||
MapNode: for<'any_input> Node<'any_input, _Iter::Item, Output = ()> + 'input,
|
||||
{
|
||||
input.for_each(|x| map_node.eval(x));
|
||||
}
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
|
@ -396,47 +312,24 @@ impl<'a> IntoIterator for &'a ImageSlice<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MapImageSliceNode<MapFn>(MapFn);
|
||||
#[derive(Debug)]
|
||||
pub struct ImageDimensionsNode;
|
||||
|
||||
impl<MapFn> MapImageSliceNode<MapFn> {
|
||||
pub fn new(map_fn: MapFn) -> Self {
|
||||
Self(map_fn)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, MapFn: Node<ImageSlice<'a>, Output = Vec<Color>>> Node<ImageSlice<'a>> for MapImageSliceNode<MapFn> {
|
||||
type Output = Image;
|
||||
fn eval(self, image: ImageSlice<'a>) -> Self::Output {
|
||||
let data = self.0.eval(image);
|
||||
Image {
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, MapFn: Copy + Node<ImageSlice<'a>, Output = Vec<Color>>> Node<ImageSlice<'a>> for &MapImageSliceNode<MapFn> {
|
||||
type Output = Image;
|
||||
fn eval(self, image: ImageSlice<'a>) -> Self::Output {
|
||||
let data = self.0.eval(image);
|
||||
Image {
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
data,
|
||||
}
|
||||
}
|
||||
#[node_macro::node_fn(ImageDimensionsNode)]
|
||||
fn dimensions_node(input: ImageSlice<'input>) -> (u32, u32) {
|
||||
(input.width, input.height)
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use image::{CollectNode, Image, ImageRefNode};
|
||||
pub use image::{CollectNode, Image, ImageRefNode, MapImageSliceNode};
|
||||
#[cfg(feature = "alloc")]
|
||||
mod image {
|
||||
use super::{Color, ImageSlice};
|
||||
use crate::Node;
|
||||
use alloc::vec::Vec;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default, specta::Type)]
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default, specta::Type, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Image {
|
||||
pub width: u32,
|
||||
|
@ -477,82 +370,63 @@ mod image {
|
|||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ImageRefNode;
|
||||
|
||||
impl ImageRefNode {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Node<&'a Image> for ImageRefNode {
|
||||
type Output = ImageSlice<'a>;
|
||||
fn eval(self, image: &'a Image) -> Self::Output {
|
||||
#[node_macro::node_fn(ImageRefNode)]
|
||||
fn image_ref_node(image: &'input Image) -> ImageSlice<'input> {
|
||||
image.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Node<&'a Image> for &ImageRefNode {
|
||||
type Output = ImageSlice<'a>;
|
||||
fn eval(self, image: &'a Image) -> Self::Output {
|
||||
image.as_slice()
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CollectNode {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CollectNode;
|
||||
|
||||
use crate::Node;
|
||||
impl<Iter: Iterator> Node<Iter> for CollectNode {
|
||||
type Output = Vec<Iter::Item>;
|
||||
fn eval(self, iter: Iter) -> Self::Output {
|
||||
iter.collect()
|
||||
}
|
||||
}
|
||||
impl<Iter: Iterator> Node<Iter> for &CollectNode {
|
||||
type Output = Vec<Iter::Item>;
|
||||
fn eval(self, iter: Iter) -> Self::Output {
|
||||
iter.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*pub struct MutWrapper<N>(pub N);
|
||||
|
||||
impl<'n, T: Clone, N> Node<&'n mut T> for &'n MutWrapper<N>
|
||||
#[node_macro::node_fn(CollectNode)]
|
||||
fn collect_node<_Iter>(input: _Iter) -> Vec<_Iter::Item>
|
||||
where
|
||||
&'n N: Node<T, Output = T>,
|
||||
_Iter: Iterator,
|
||||
{
|
||||
type Output = ();
|
||||
fn eval(self, value: &'n mut T) {
|
||||
*value = (&self.0).eval(value.clone());
|
||||
input.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MapImageSliceNode<Data> {
|
||||
data: Data,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(MapImageSliceNode)]
|
||||
fn map_node(input: (u32, u32), data: Vec<Color>) -> Image {
|
||||
Image {
|
||||
width: input.0,
|
||||
height: input.1,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
ops::TypeNode,
|
||||
structural::{ComposeNode, Then},
|
||||
value::ValueNode,
|
||||
};
|
||||
use crate::{ops::CloneNode, structural::Then, value::ValueNode, Node};
|
||||
|
||||
use super::*;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[test]
|
||||
fn map_node() {
|
||||
// let array = &mut [Color::from_rgbaf32(1.0, 0.0, 0.0, 1.0).unwrap()];
|
||||
(&GrayscaleColorNode).eval(Color::from_rgbf32_unchecked(1., 0., 0.));
|
||||
GrayscaleColorNode.eval(Color::from_rgbf32_unchecked(1., 0., 0.));
|
||||
/*let map = ForEachNode(MutWrapper(GrayscaleNode));
|
||||
(&map).eval(array.iter_mut());
|
||||
assert_eq!(array[0], Color::from_rgbaf32(0.33333334, 0.33333334, 0.33333334, 1.0).unwrap());*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_node() {
|
||||
let radius = ValueNode::new(1u32);
|
||||
static DATA: &[Color] = &[Color::from_rgbf32_unchecked(1., 0., 0.); 25];
|
||||
let image = ValueNode::<_>::new(ImageSlice { width: 5, height: 5, data: DATA });
|
||||
use alloc::vec;
|
||||
let radius = ValueNode::new(1u32).then(CloneNode::new());
|
||||
let image = ValueNode::<_>::new(Image {
|
||||
width: 5,
|
||||
height: 5,
|
||||
data: vec![Color::from_rgbf32_unchecked(1., 0., 0.); 25],
|
||||
});
|
||||
let image = image.then(ImageRefNode::new());
|
||||
let window = WindowNode::new(radius, image);
|
||||
//let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window);
|
||||
let vec = window.eval(0);
|
||||
assert_eq!(vec.count(), 4);
|
||||
let vec = window.eval(5);
|
||||
|
@ -561,29 +435,50 @@ mod test {
|
|||
assert_eq!(vec.count(), 9);
|
||||
}
|
||||
|
||||
// TODO: I can't be bothered to fix this test rn
|
||||
/*
|
||||
#[test]
|
||||
fn blur_node() {
|
||||
let radius = ValueNode::new(1u32);
|
||||
let sigma = ValueNode::new(3f64);
|
||||
static DATA: &[Color] = &[Color::from_rgbf32_unchecked(1., 0., 0.); 20];
|
||||
let image = ValueNode::<_>::new(ImageSlice { width: 10, height: 2, data: DATA });
|
||||
use alloc::vec;
|
||||
let radius = ValueNode::new(1u32).then(CloneNode::new());
|
||||
let sigma = ValueNode::new(3f64).then(CloneNode::new());
|
||||
let radius = ValueNode::new(1u32).then(CloneNode::new());
|
||||
let image = ValueNode::<_>::new(Image {
|
||||
width: 5,
|
||||
height: 5,
|
||||
data: vec![Color::from_rgbf32_unchecked(1., 0., 0.); 25],
|
||||
});
|
||||
let image = image.then(ImageRefNode::new());
|
||||
let window = WindowNode::new(radius, image);
|
||||
let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window);
|
||||
let pos_to_dist = MapSndNode::new(DistanceNode);
|
||||
let distance = window.then(MapNode::new(pos_to_dist));
|
||||
let map_gaussian = MapSndNode::new(GaussianNode::new(sigma));
|
||||
let map_distances: MapNode<_, MapSndNode<_>> = MapNode::new(map_gaussian);
|
||||
let window: TypeNode<_, u32, ImageWindowIterator<'_>> = TypeNode::new(window);
|
||||
let distance = ValueNode::new(DistanceNode::new());
|
||||
let pos_to_dist = MapSndNode::new(distance);
|
||||
let type_erased = &window as &dyn for<'a> Node<'a, u32, Output = ImageWindowIterator<'a>>;
|
||||
type_erased.eval(0);
|
||||
let map_pos_to_dist = MapNode::new(ValueNode::new(pos_to_dist));
|
||||
|
||||
let type_erased = &map_pos_to_dist as &dyn for<'a> Node<'a, u32, Output = ImageWindowIterator<'a>>;
|
||||
type_erased.eval(0);
|
||||
|
||||
let distance = window.then(map_pos_to_dist);
|
||||
let map_gaussian = MapSndNode::new(ValueNode(GaussianNode::new(sigma)));
|
||||
let map_gaussian: TypeNode<_, (_, f32), (_, f32)> = TypeNode::new(map_gaussian);
|
||||
let map_gaussian = ValueNode(map_gaussian);
|
||||
let map_gaussian: TypeNode<_, (), &_> = TypeNode::new(map_gaussian);
|
||||
let map_distances = MapNode::new(map_gaussian);
|
||||
let map_distances: TypeNode<_, _, MapFnIterator<'_, '_, _, _>> = TypeNode::new(map_distances);
|
||||
let gaussian_iter = distance.then(map_distances);
|
||||
let avg = gaussian_iter.then(WeightedAvgNode::new());
|
||||
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
|
||||
let blur_iter = MapNode::new(avg);
|
||||
let blur_iter = MapNode::new(ValueNode::new(avg));
|
||||
let blur = image.then(ImageIndexIterNode).then(blur_iter);
|
||||
let blur: TypeNode<_, (), MapFnIterator<_, _>> = TypeNode::new(blur);
|
||||
let collect = CollectNode {};
|
||||
let collect = CollectNode::new();
|
||||
let vec = collect.eval(0..10);
|
||||
assert_eq!(vec.len(), 10);
|
||||
let vec = ComposeNode::new(blur, collect);
|
||||
let vec: TypeNode<_, (), Vec<Color>> = TypeNode::new(vec);
|
||||
let _ = blur.eval(());
|
||||
let vec = blur.then(collect);
|
||||
let _image = vec.eval(());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use core::hash::Hash;
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -26,6 +28,16 @@ pub struct Color {
|
|||
alpha: f32,
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl Hash for Color {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.red.to_bits().hash(state);
|
||||
self.green.to_bits().hash(state);
|
||||
self.blue.to_bits().hash(state);
|
||||
self.alpha.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const BLACK: Color = Color::from_rgbf32_unchecked(0., 0., 0.);
|
||||
pub const WHITE: Color = Color::from_rgbf32_unchecked(1., 1., 1.);
|
||||
|
|
|
@ -1,144 +1,92 @@
|
|||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{AsRefNode, Node, RefNode};
|
||||
use crate::Node;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ComposeNode<First, Second, Input> {
|
||||
pub struct ComposeNode<First: for<'i> Node<'i, I>, Second: for<'i> Node<'i, <First as Node<'i, I>>::Output>, I> {
|
||||
first: First,
|
||||
second: Second,
|
||||
_phantom: PhantomData<Input>,
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<Input, Inter, First, Second> Node<Input> for ComposeNode<First, Second, Input>
|
||||
impl<'i, Input: 'i, First, Second> Node<'i, Input> for ComposeNode<First, Second, Input>
|
||||
where
|
||||
First: Node<Input, Output = Inter>,
|
||||
Second: Node<Inter>,
|
||||
First: for<'a> Node<'a, Input> + 'i,
|
||||
Second: for<'a> Node<'a, <First as Node<'a, Input>>::Output> + 'i,
|
||||
{
|
||||
type Output = <Second as Node<Inter>>::Output;
|
||||
|
||||
fn eval(self, input: Input) -> Self::Output {
|
||||
// evaluate the first node with the given input
|
||||
// and then pipe the result from the first computation
|
||||
// into the second node
|
||||
let arg: Inter = self.first.eval(input);
|
||||
type Output = <Second as Node<'i, <First as Node<'i, Input>>::Output>>::Output;
|
||||
fn eval<'s: 'i>(&'s self, input: Input) -> Self::Output {
|
||||
let arg = self.first.eval(input);
|
||||
self.second.eval(arg)
|
||||
}
|
||||
}
|
||||
impl<'n, Input, Inter, First, Second> Node<Input> for &'n ComposeNode<First, Second, Input>
|
||||
|
||||
impl<First, Second, Input> ComposeNode<First, Second, Input>
|
||||
where
|
||||
First: AsRefNode<'n, Input, Output = Inter>,
|
||||
Second: AsRefNode<'n, Inter>,
|
||||
&'n First: Node<Input, Output = Inter>,
|
||||
&'n Second: Node<Inter>,
|
||||
First: for<'a> Node<'a, Input>,
|
||||
Second: for<'a> Node<'a, <First as Node<'a, Input>>::Output>,
|
||||
{
|
||||
type Output = <Second as AsRefNode<'n, Inter>>::Output;
|
||||
|
||||
fn eval(self, input: Input) -> Self::Output {
|
||||
// evaluate the first node with the given input
|
||||
// and then pipe the result from the first computation
|
||||
// into the second node
|
||||
let arg: Inter = (self.first).eval_box(input);
|
||||
(self.second).eval_box(arg)
|
||||
}
|
||||
}
|
||||
impl<Input, Inter, First, Second> RefNode<Input> for ComposeNode<First, Second, Input>
|
||||
where
|
||||
First: RefNode<Input, Output = Inter> + Copy,
|
||||
Second: RefNode<Inter> + Copy,
|
||||
{
|
||||
type Output = <Second as RefNode<Inter>>::Output;
|
||||
|
||||
fn eval_ref(&self, input: Input) -> Self::Output {
|
||||
// evaluate the first node with the given input
|
||||
// and then pipe the result from the first computation
|
||||
// into the second node
|
||||
let arg: Inter = (self.first).eval_ref(input);
|
||||
(self.second).eval_ref(arg)
|
||||
}
|
||||
}
|
||||
impl<Input: 'static, First: 'static, Second: 'static> dyn_any::StaticType for ComposeNode<First, Second, Input> {
|
||||
type Static = ComposeNode<First, Second, Input>;
|
||||
}
|
||||
|
||||
impl<'n, Input, First: 'n, Second: 'n> ComposeNode<First, Second, Input> {
|
||||
pub const fn new(first: First, second: Second) -> Self {
|
||||
ComposeNode::<First, Second, Input> { first, second, _phantom: PhantomData }
|
||||
ComposeNode::<First, Second, Input> { first, second, phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Then<Inter, Input>: Sized {
|
||||
// impl Clone for ComposeNode<First, Second, Input>
|
||||
impl<First, Second, Input> Clone for ComposeNode<First, Second, Input>
|
||||
where
|
||||
First: for<'a> Node<'a, Input> + Clone,
|
||||
Second: for<'a> Node<'a, <First as Node<'a, Input>>::Output> + Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
ComposeNode::<First, Second, Input> {
|
||||
first: self.first.clone(),
|
||||
second: self.second.clone(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Then<'i, Input: 'i>: Sized {
|
||||
fn then<Second>(self, second: Second) -> ComposeNode<Self, Second, Input>
|
||||
where
|
||||
Self: Node<Input, Output = Inter>,
|
||||
Second: Node<Inter>,
|
||||
Self: for<'a> Node<'a, Input>,
|
||||
Second: for<'a> Node<'a, <Self as Node<'a, Input>>::Output>,
|
||||
{
|
||||
ComposeNode::<Self, Second, Input> {
|
||||
first: self,
|
||||
second,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
ComposeNode::new(self, second)
|
||||
}
|
||||
}
|
||||
|
||||
impl<First: Node<Input, Output = Inter>, Inter, Input> Then<Inter, Input> for First {}
|
||||
impl<'i, First: for<'a> Node<'a, Input>, Input: 'i> Then<'i, Input> for First {}
|
||||
|
||||
pub trait ThenRef<Inter, Input>: Sized {
|
||||
fn after<'n, Second: 'n>(&'n self, second: Second) -> ComposeNode<&'n Self, Second, Input>
|
||||
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
|
||||
&'n Self: Node<Input, Output = Inter> + Copy,
|
||||
Second: Node<Inter>,
|
||||
Self: 'n,
|
||||
Root: Node<'i, I>,
|
||||
{
|
||||
ComposeNode::<&'n Self, Second, Input> {
|
||||
first: self,
|
||||
second,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'n, First: 'n, Inter, Input> ThenRef<Inter, Input> for First where &'n First: Node<Input, Output = Inter> {}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub trait ThenBox<Inter, Input> {
|
||||
fn then<'n, Second: 'n>(self, second: Second) -> ComposeNode<Self, Second, Input>
|
||||
where
|
||||
alloc::boxed::Box<Self>: Node<Input, Output = Inter>,
|
||||
Second: Node<Inter> + Copy,
|
||||
Self: Sized,
|
||||
{
|
||||
ComposeNode::<Self, Second, Input> {
|
||||
first: self,
|
||||
second,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
impl<'n, First: 'n, Inter, Input> ThenBox<Inter, Input> for alloc::boxed::Box<First> where &'n alloc::boxed::Box<First>: Node<Input, Output = Inter> {}
|
||||
|
||||
pub struct ConsNode<Root, T: From<()>>(pub Root, pub PhantomData<T>);
|
||||
|
||||
impl<Root, Input, T: From<()>> Node<Input> for ConsNode<Root, T>
|
||||
where
|
||||
Root: Node<T>,
|
||||
{
|
||||
type Output = (Input, <Root as Node<T>>::Output);
|
||||
|
||||
fn eval(self, input: Input) -> Self::Output {
|
||||
let arg = self.0.eval(().into());
|
||||
(input, arg)
|
||||
}
|
||||
}
|
||||
impl<'n, Root: Node<T> + Copy, T: From<()>, Input> Node<Input> for &'n ConsNode<Root, T> {
|
||||
type Output = (Input, Root::Output);
|
||||
|
||||
fn eval(self, input: Input) -> Self::Output {
|
||||
let arg = self.0.eval(().into());
|
||||
fn eval<'s: 'i>(&'s self, input: Input) -> Self::Output {
|
||||
let arg = self.0.eval(I::from(()));
|
||||
(input, arg)
|
||||
}
|
||||
}
|
||||
impl<Root, T: From<()>> ConsNode<Root, T> {
|
||||
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 crate::{ops::IdNode, value::ValueNode};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn compose() {
|
||||
let value = ValueNode::new(4u32);
|
||||
let compose = value.then(IdNode::new());
|
||||
assert_eq!(compose.eval(()), &4u32);
|
||||
let type_erased = &compose as &dyn for<'i> Node<'i, (), Output = &'i u32>;
|
||||
assert_eq!(type_erased.eval(()), &4u32);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,23 @@
|
|||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use crate::Node;
|
||||
|
||||
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct IntNode<const N: u32>;
|
||||
impl<const N: u32> Node<()> for IntNode<N> {
|
||||
|
||||
impl<'i, const N: u32> Node<'i, ()> for IntNode<N> {
|
||||
type Output = u32;
|
||||
fn eval(self, _: ()) -> u32 {
|
||||
fn eval<'s: 'i>(&'s self, _input: ()) -> Self::Output {
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ValueNode<T>(pub T);
|
||||
impl<'n, T: 'n> Node<()> for ValueNode<T> {
|
||||
type Output = T;
|
||||
fn eval(self, _: ()) -> Self::Output {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl<'n, T: 'n> Node<()> for &'n ValueNode<T> {
|
||||
type Output = &'n T;
|
||||
fn eval(self, _: ()) -> Self::Output {
|
||||
|
||||
impl<'i, T: 'i> Node<'i, ()> for ValueNode<T> {
|
||||
type Output = &'i T;
|
||||
fn eval<'s: 'i>(&'s self, _input: ()) -> Self::Output {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -46,58 +40,85 @@ impl<T: Clone> Clone for ValueNode<T> {
|
|||
}
|
||||
impl<T: Clone + Copy> Copy for ValueNode<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClonedNode<T: Clone>(pub T);
|
||||
|
||||
impl<'i, T: Clone + 'i> Node<'i, ()> for ClonedNode<T> {
|
||||
type Output = T;
|
||||
fn eval<'s: 'i>(&'s self, _input: ()) -> Self::Output {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> ClonedNode<T> {
|
||||
pub const fn new(value: T) -> ClonedNode<T> {
|
||||
ClonedNode(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> From<T> for ClonedNode<T> {
|
||||
fn from(value: T) -> Self {
|
||||
ClonedNode::new(value)
|
||||
}
|
||||
}
|
||||
impl<T: Clone + Copy> Copy for ClonedNode<T> {}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DefaultNode<T>(PhantomData<T>);
|
||||
impl<T: Default> Node<()> for DefaultNode<T> {
|
||||
|
||||
impl<'i, T: Default + 'i> Node<'i, ()> for DefaultNode<T> {
|
||||
type Output = T;
|
||||
fn eval(self, _: ()) -> T {
|
||||
fn eval<'s: 'i>(&self, _input: ()) -> Self::Output {
|
||||
T::default()
|
||||
}
|
||||
}
|
||||
impl<'n, T: Default + 'n> Node<()> for &'n DefaultNode<T> {
|
||||
type Output = T;
|
||||
fn eval(self, _: ()) -> T {
|
||||
T::default()
|
||||
|
||||
impl<T> DefaultNode<T> {
|
||||
pub fn new() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
/// Return the unit value
|
||||
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct UnitNode;
|
||||
impl Node<()> for UnitNode {
|
||||
pub struct ForgetNode;
|
||||
|
||||
impl<'i, T: 'i> Node<'i, T> for ForgetNode {
|
||||
type Output = ();
|
||||
fn eval(self, _: ()) -> Self::Output {}
|
||||
}
|
||||
impl<'n> Node<()> for &'n UnitNode {
|
||||
type Output = ();
|
||||
fn eval(self, _: ()) -> Self::Output {}
|
||||
fn eval<'s: 'i>(&self, _input: T) -> Self::Output {}
|
||||
}
|
||||
|
||||
pub struct InputNode<T>(MaybeUninit<T>, AtomicBool);
|
||||
impl<'n, T: 'n> Node<()> for InputNode<T> {
|
||||
type Output = T;
|
||||
fn eval(self, _: ()) -> Self::Output {
|
||||
if self.1.load(core::sync::atomic::Ordering::SeqCst) {
|
||||
unsafe { self.0.assume_init() }
|
||||
} else {
|
||||
panic!("tried to access an input before setting it")
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'n, T: 'n> Node<()> for &'n InputNode<T> {
|
||||
type Output = &'n T;
|
||||
fn eval(self, _: ()) -> Self::Output {
|
||||
if self.1.load(core::sync::atomic::Ordering::SeqCst) {
|
||||
unsafe { self.0.assume_init_ref() }
|
||||
} else {
|
||||
panic!("tried to access an input before setting it")
|
||||
}
|
||||
impl ForgetNode {
|
||||
pub const fn new() -> Self {
|
||||
ForgetNode
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InputNode<T> {
|
||||
pub const fn new() -> InputNode<T> {
|
||||
InputNode(MaybeUninit::uninit(), AtomicBool::new(false))
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_int_node() {
|
||||
let node = IntNode::<5>;
|
||||
assert_eq!(node.eval(()), 5);
|
||||
}
|
||||
#[test]
|
||||
fn test_value_node() {
|
||||
let node = ValueNode::new(5);
|
||||
assert_eq!(node.eval(()), &5);
|
||||
let type_erased = &node as &dyn for<'a> Node<'a, (), Output = &'a i32>;
|
||||
assert_eq!(type_erased.eval(()), &5);
|
||||
}
|
||||
#[test]
|
||||
fn test_default_node() {
|
||||
let node = DefaultNode::<u32>::new();
|
||||
assert_eq!(node.eval(()), 0);
|
||||
}
|
||||
#[test]
|
||||
fn test_unit_node() {
|
||||
let node = ForgetNode::new();
|
||||
assert_eq!(node.eval(()), ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use core::ops::{Index, IndexMut};
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Serialize, Deserialize, specta::Type, Hash)]
|
||||
pub enum ManipulatorType {
|
||||
Anchor,
|
||||
InHandle,
|
||||
|
|
|
@ -35,7 +35,7 @@ fn generate_path(_input: (), path_data: Subpath) -> VectorData {
|
|||
use crate::raster::Image;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlitSubpath<P: Node<(), Output = Subpath>> {
|
||||
pub struct BlitSubpath<P> {
|
||||
path_data: P,
|
||||
}
|
||||
|
||||
|
@ -68,9 +68,10 @@ pub struct TransformSubpathNode<Translation, Rotation, Scale, Shear> {
|
|||
}
|
||||
|
||||
#[node_macro::node_fn(TransformSubpathNode)]
|
||||
fn transform_subpath(mut subpath: Subpath, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2) -> VectorData {
|
||||
fn transform_subpath(subpath: Subpath, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2) -> VectorData {
|
||||
let (sin, cos) = rotate.sin_cos();
|
||||
|
||||
let mut subpath = subpath;
|
||||
subpath.apply_affine(DAffine2::from_cols_array(&[scale.x + cos, shear.y + sin, shear.x - sin, scale.y + cos, translate.x, translate.y]));
|
||||
subpath
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use alloc::vec::Vec;
|
|||
/// The downside is that currently it requires a lot of iteration.
|
||||
|
||||
type ElementId = u64;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, specta::Type)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, specta::Type, Hash)]
|
||||
pub struct IdBackedVec<T> {
|
||||
/// Contained elements
|
||||
elements: Vec<T>,
|
||||
|
|
|
@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize};
|
|||
/// / | \
|
||||
/// "Anchor" "InHandle" "OutHandle" <- These are ManipulatorPoints and the only editable "primitive"
|
||||
/// ```
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Default, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Default, specta::Type, Hash)]
|
||||
pub struct ManipulatorGroup {
|
||||
/// Editable points for the anchor and handles.
|
||||
pub points: [Option<ManipulatorPoint>; 3],
|
||||
|
@ -293,7 +293,7 @@ impl ManipulatorGroup {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ManipulatorGroupEditorState {
|
||||
// Whether the angle between the handles should be maintained
|
||||
pub mirror_angle_between_handles: bool,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use core::hash::Hash;
|
||||
|
||||
use super::consts::ManipulatorType;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -26,6 +28,16 @@ impl Default for ManipulatorPoint {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl Hash for ManipulatorPoint {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.position.to_array().iter().for_each(|x| x.to_bits().hash(state));
|
||||
self.manipulator_type.hash(state);
|
||||
|
||||
self.editor_state.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ManipulatorPoint {
|
||||
/// Initialize a new [ManipulatorPoint].
|
||||
pub fn new(position: glam::DVec2, manipulator_type: ManipulatorType) -> Self {
|
||||
|
@ -60,7 +72,7 @@ impl ManipulatorPoint {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, specta::Type)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, specta::Type, Hash)]
|
||||
pub struct ManipulatorPointEditorState {
|
||||
/// Whether or not this manipulator point can be selected.
|
||||
pub can_be_selected: bool,
|
||||
|
|
|
@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
|
|||
/// [Subpath] represents a single vector path, containing many [ManipulatorGroups].
|
||||
/// For each closed shape we keep a [Subpath] which contains the [ManipulatorGroup]s (handles and anchors) that define that shape.
|
||||
// TODO Add "closed" bool to subpath
|
||||
#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize, DynAny, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize, DynAny, specta::Type, Hash)]
|
||||
pub struct Subpath(IdBackedVec<ManipulatorGroup>);
|
||||
|
||||
impl Subpath {
|
||||
|
|
|
@ -68,7 +68,7 @@ impl DocumentNode {
|
|||
let (input, mut args) = match first {
|
||||
NodeInput::Value { tagged_value, .. } => {
|
||||
assert_eq!(self.inputs.len(), 0);
|
||||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value.to_value()))
|
||||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value))
|
||||
}
|
||||
NodeInput::Node(id) => (ProtoNodeInput::Node(id), ConstructionArgs::Nodes(vec![])),
|
||||
NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])),
|
||||
|
@ -266,7 +266,7 @@ impl NodeNetwork {
|
|||
) {
|
||||
"Value".to_string()
|
||||
} else {
|
||||
format!("Value: {:?}", tagged_value.clone().to_value())
|
||||
format!("Value: {:?}", tagged_value)
|
||||
};
|
||||
let new_id = map_ids(id, gen_id());
|
||||
let value_node = DocumentNode {
|
||||
|
@ -409,7 +409,6 @@ impl NodeNetwork {
|
|||
mod test {
|
||||
use super::*;
|
||||
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use value::IntoValue;
|
||||
|
||||
fn gen_node_id() -> NodeId {
|
||||
static mut NODE_ID: NodeId = 3;
|
||||
|
@ -563,7 +562,7 @@ mod test {
|
|||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
(14, ProtoNode::value(ConstructionArgs::Value(2_u32.into_any()))),
|
||||
(14, ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2)))),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|
@ -602,7 +601,7 @@ mod test {
|
|||
(
|
||||
14,
|
||||
DocumentNode {
|
||||
name: "Value: 2".into(),
|
||||
name: "Value: U32(2)".into(),
|
||||
inputs: vec![NodeInput::Value {
|
||||
tagged_value: value::TaggedValue::U32(2),
|
||||
exposed: false,
|
||||
|
|
|
@ -2,8 +2,11 @@ pub use dyn_any::StaticType;
|
|||
use dyn_any::{DynAny, Upcast};
|
||||
use dyn_clone::DynClone;
|
||||
pub use glam::DVec2;
|
||||
use graphene_core::Node;
|
||||
use std::hash::Hash;
|
||||
pub use std::sync::Arc;
|
||||
|
||||
use crate::executor::Any;
|
||||
pub use crate::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
|
||||
|
||||
/// A type that is known, allowing serialization (serde::Deserialize is not object safe)
|
||||
|
@ -29,9 +32,83 @@ pub enum TaggedValue {
|
|||
LayerPath(Option<Vec<u64>>),
|
||||
}
|
||||
|
||||
impl TaggedValue {
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl Hash for TaggedValue {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::None => 0.hash(state),
|
||||
Self::String(s) => {
|
||||
1.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::U32(u) => {
|
||||
2.hash(state);
|
||||
u.hash(state)
|
||||
}
|
||||
Self::F32(f) => {
|
||||
3.hash(state);
|
||||
f.to_bits().hash(state)
|
||||
}
|
||||
Self::F64(f) => {
|
||||
4.hash(state);
|
||||
f.to_bits().hash(state)
|
||||
}
|
||||
Self::Bool(b) => {
|
||||
5.hash(state);
|
||||
b.hash(state)
|
||||
}
|
||||
Self::DVec2(v) => {
|
||||
6.hash(state);
|
||||
v.to_array().iter().for_each(|x| x.to_bits().hash(state))
|
||||
}
|
||||
Self::OptionalDVec2(None) => 7.hash(state),
|
||||
Self::OptionalDVec2(Some(v)) => {
|
||||
8.hash(state);
|
||||
Self::DVec2(*v).hash(state)
|
||||
}
|
||||
Self::Image(i) => {
|
||||
9.hash(state);
|
||||
i.hash(state)
|
||||
}
|
||||
Self::RcImage(i) => {
|
||||
10.hash(state);
|
||||
i.hash(state)
|
||||
}
|
||||
Self::Color(c) => {
|
||||
11.hash(state);
|
||||
c.hash(state)
|
||||
}
|
||||
Self::Subpath(s) => {
|
||||
12.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::RcSubpath(s) => {
|
||||
13.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::ImaginateSamplingMethod(m) => {
|
||||
14.hash(state);
|
||||
m.hash(state)
|
||||
}
|
||||
Self::ImaginateMaskStartingFill(f) => {
|
||||
15.hash(state);
|
||||
f.hash(state)
|
||||
}
|
||||
Self::ImaginateStatus(s) => {
|
||||
16.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::LayerPath(p) => {
|
||||
17.hash(state);
|
||||
p.hash(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TaggedValue {
|
||||
/// Converts to a Box<dyn DynAny> - this isn't very neat but I'm not sure of a better approach
|
||||
pub fn to_value(self) -> Value {
|
||||
pub fn to_any(self) -> Any<'a> {
|
||||
match self {
|
||||
TaggedValue::None => Box::new(()),
|
||||
TaggedValue::String(x) => Box::new(x),
|
||||
|
@ -54,19 +131,35 @@ impl TaggedValue {
|
|||
}
|
||||
}
|
||||
|
||||
pub type Value = Box<dyn ValueTrait>;
|
||||
pub struct UpcastNode {
|
||||
value: TaggedValue,
|
||||
}
|
||||
impl<'input> Node<'input, Box<dyn DynAny<'input> + 'input>> for UpcastNode {
|
||||
type Output = Box<dyn DynAny<'input> + 'input>;
|
||||
|
||||
pub trait ValueTrait: DynAny<'static> + Upcast<dyn DynAny<'static>> + std::fmt::Debug + DynClone {}
|
||||
fn eval<'s: 'input>(&'s self, _: Box<dyn DynAny<'input> + 'input>) -> Self::Output {
|
||||
self.value.clone().to_any()
|
||||
}
|
||||
}
|
||||
impl UpcastNode {
|
||||
pub fn new(value: TaggedValue) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoValue: Sized + ValueTrait + 'static {
|
||||
fn into_any(self) -> Value {
|
||||
pub type Value<'a> = Box<dyn for<'i> ValueTrait<'i> + 'a>;
|
||||
|
||||
pub trait ValueTrait<'a>: DynAny<'a> + Upcast<dyn DynAny<'a> + 'a> + std::fmt::Debug + DynClone + Sync + Send + 'a {}
|
||||
|
||||
pub trait IntoValue<'a>: Sized + for<'i> ValueTrait<'i> + 'a {
|
||||
fn into_any(self) -> Value<'a> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + StaticType + Upcast<dyn DynAny<'static>> + std::fmt::Debug + PartialEq + Clone> ValueTrait for T {}
|
||||
impl<'a, T: 'a + StaticType + Upcast<dyn DynAny<'a> + 'a> + std::fmt::Debug + PartialEq + Clone + Sync + Send + 'a> ValueTrait<'a> for T {}
|
||||
|
||||
impl<T: 'static + ValueTrait> IntoValue for T {}
|
||||
impl<'a, T: for<'i> ValueTrait<'i> + 'a> IntoValue<'a> for T {}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct Vtable {
|
||||
|
@ -81,7 +174,7 @@ pub(crate) struct TraitObject {
|
|||
pub(crate) vtable: &'static Vtable,
|
||||
}
|
||||
|
||||
impl PartialEq for Box<dyn ValueTrait> {
|
||||
impl<'a> PartialEq for Box<dyn for<'i> ValueTrait<'i> + 'a> {
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.type_id() != other.type_id() {
|
||||
|
@ -96,7 +189,16 @@ impl PartialEq for Box<dyn ValueTrait> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Clone for Value {
|
||||
impl<'a> Hash for Value<'a> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) };
|
||||
let size = self_trait_object.vtable.size;
|
||||
let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) };
|
||||
self_mem.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Clone for Value<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) };
|
||||
let size = self_trait_object.vtable.size;
|
||||
|
@ -116,6 +218,7 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn test_any_src() {
|
||||
assert!(2_u32.into_any() == 2_u32.into_any());
|
||||
assert!(2_u32.into_any() != 3_u32.into_any());
|
||||
|
|
|
@ -9,10 +9,10 @@ pub struct Compiler {}
|
|||
|
||||
impl Compiler {
|
||||
pub fn compile(&self, mut network: NodeNetwork, resolve_inputs: bool) -> ProtoNetwork {
|
||||
let node_count = network.nodes.len();
|
||||
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||
println!("flattening");
|
||||
for id in 0..node_count {
|
||||
network.flatten(id as u64);
|
||||
for id in node_ids {
|
||||
network.flatten(id);
|
||||
}
|
||||
let mut proto_network = network.into_proto_network();
|
||||
if resolve_inputs {
|
||||
|
@ -21,11 +21,12 @@ impl Compiler {
|
|||
}
|
||||
println!("reordering ids");
|
||||
proto_network.reorder_ids();
|
||||
proto_network.generate_stable_node_ids();
|
||||
proto_network
|
||||
}
|
||||
}
|
||||
pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
|
||||
|
||||
pub trait Executor {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>>;
|
||||
fn execute<'a, 's: 'a>(&'s self, input: Any<'a>) -> Result<Any<'a>, Box<dyn Error>>;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,23 @@ pub enum ImaginateStatus {
|
|||
Terminated,
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl core::hash::Hash for ImaginateStatus {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Idle => 0.hash(state),
|
||||
Self::Beginning => 1.hash(state),
|
||||
Self::Uploading(f) => {
|
||||
2.hash(state);
|
||||
f.to_bits().hash(state);
|
||||
}
|
||||
Self::Generating => 3.hash(state),
|
||||
Self::Terminating => 4.hash(state),
|
||||
Self::Terminated => 5.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ImaginateBaseImage {
|
||||
|
@ -31,7 +48,7 @@ pub struct ImaginateMaskImage {
|
|||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, specta::Type)]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, specta::Type, Hash)]
|
||||
pub enum ImaginateMaskPaintMode {
|
||||
#[default]
|
||||
Inpaint,
|
||||
|
@ -39,7 +56,7 @@ pub enum ImaginateMaskPaintMode {
|
|||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, specta::Type)]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, specta::Type, Hash)]
|
||||
pub enum ImaginateMaskStartingFill {
|
||||
#[default]
|
||||
Fill,
|
||||
|
@ -70,7 +87,7 @@ impl std::fmt::Display for ImaginateMaskStartingFill {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, DynAny, specta::Type)]
|
||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, DynAny, specta::Type, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum ImaginateSamplingMethod {
|
||||
#[default]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::document::value;
|
||||
use crate::document::NodeId;
|
||||
|
@ -87,6 +88,7 @@ impl NodeIdentifier {
|
|||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct ProtoNetwork {
|
||||
// Should a proto Network even allow inputs? Don't think so
|
||||
pub inputs: Vec<NodeId>,
|
||||
pub output: NodeId,
|
||||
pub nodes: Vec<(NodeId, ProtoNode)>,
|
||||
|
@ -94,7 +96,7 @@ pub struct ProtoNetwork {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum ConstructionArgs {
|
||||
Value(value::Value),
|
||||
Value(value::TaggedValue),
|
||||
Nodes(Vec<NodeId>),
|
||||
}
|
||||
|
||||
|
@ -108,6 +110,20 @@ impl PartialEq for ConstructionArgs {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for ConstructionArgs {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Nodes(nodes) => {
|
||||
"nodes".hash(state);
|
||||
for node in nodes {
|
||||
node.hash(state);
|
||||
}
|
||||
}
|
||||
Self::Value(value) => value.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstructionArgs {
|
||||
pub fn new_function_args(&self) -> Vec<String> {
|
||||
match self {
|
||||
|
@ -142,6 +158,19 @@ impl ProtoNodeInput {
|
|||
}
|
||||
|
||||
impl ProtoNode {
|
||||
pub fn stable_node_id(&self) -> Option<NodeId> {
|
||||
use std::hash::Hasher;
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
self.identifier.fully_qualified_name().hash(&mut hasher);
|
||||
self.construction_args.hash(&mut hasher);
|
||||
match self.input {
|
||||
ProtoNodeInput::None => "none".hash(&mut hasher),
|
||||
ProtoNodeInput::Network => "network".hash(&mut hasher),
|
||||
ProtoNodeInput::Node(id) => id.hash(&mut hasher),
|
||||
};
|
||||
Some(hasher.finish() as NodeId)
|
||||
}
|
||||
|
||||
pub fn value(value: ConstructionArgs) -> Self {
|
||||
Self {
|
||||
identifier: NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic(Cow::Borrowed("T"))]),
|
||||
|
@ -195,6 +224,24 @@ impl ProtoNetwork {
|
|||
edges
|
||||
}
|
||||
|
||||
pub fn generate_stable_node_ids(&mut self) {
|
||||
for i in 0..self.nodes.len() {
|
||||
self.generate_stable_node_id(i);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_stable_node_id(&mut self, index: usize) -> NodeId {
|
||||
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.nodes[index].0 = sni;
|
||||
sni
|
||||
} else {
|
||||
panic!("failed to generate stable node id for node {:#?}", self.nodes[index].1);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -236,7 +283,7 @@ impl ProtoNetwork {
|
|||
self.nodes.push((
|
||||
compose_node_id,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode", &[generic!("T"), Type::Generic(Cow::Borrowed("U"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]),
|
||||
construction_args: ConstructionArgs::Nodes(vec![input_node, id]),
|
||||
input,
|
||||
},
|
||||
|
@ -330,7 +377,6 @@ impl ProtoNetwork {
|
|||
mod test {
|
||||
use super::*;
|
||||
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use value::IntoValue;
|
||||
|
||||
#[test]
|
||||
fn topological_sort() {
|
||||
|
@ -378,8 +424,30 @@ mod test {
|
|||
assert_eq!(construction_network.nodes[5].1.construction_args, ConstructionArgs::Nodes(vec![3, 4]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stable_node_id_generation() {
|
||||
let mut construction_network = test_network();
|
||||
construction_network.reorder_ids();
|
||||
construction_network.generate_stable_node_ids();
|
||||
construction_network.resolve_inputs();
|
||||
construction_network.generate_stable_node_ids();
|
||||
assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value");
|
||||
let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect();
|
||||
assert_eq!(
|
||||
ids,
|
||||
vec![
|
||||
17495035641492238530,
|
||||
14931179783740213471,
|
||||
2268573767208263092,
|
||||
14616574692620381527,
|
||||
12110007198416821768,
|
||||
11185814750012198757
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn test_network() -> ProtoNetwork {
|
||||
let construction_network = ProtoNetwork {
|
||||
ProtoNetwork {
|
||||
inputs: vec![10],
|
||||
output: 1,
|
||||
nodes: [
|
||||
|
@ -420,13 +488,12 @@ mod test {
|
|||
ProtoNode {
|
||||
identifier: "value".into(),
|
||||
input: ProtoNodeInput::None,
|
||||
construction_args: ConstructionArgs::Value(2_u32.into_any()),
|
||||
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(2)),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
construction_network
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,323 +1,199 @@
|
|||
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
||||
pub use graphene_core::{generic, ops /*, structural*/, Node, RefNode};
|
||||
use std::marker::PhantomData;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
pub use graphene_core::{generic, ops, Node};
|
||||
use std::{marker::PhantomData, pin::Pin};
|
||||
|
||||
pub struct DynAnyNode<N, I: StaticType, O: StaticType, ORef: StaticType>(pub N, pub PhantomData<(I, O, ORef)>);
|
||||
/*impl<'n, I: StaticType, N: RefNode<'n, &'n I, Output = O> + 'n, O: 'n + StaticType> Node<&'n dyn DynAny<'n>> for DynAnyNode<'n, N, I> {
|
||||
type Output = Box<dyn dyn_any::DynAny<'n> + 'n>;
|
||||
fn eval(self, input: &'n dyn DynAny<'n>) -> Self::Output {
|
||||
let output = self.0.eval_ref(dyn_any::downcast_ref(input).expect(fmt_error::<I>().as_str()));
|
||||
Box::new(output)
|
||||
pub struct DynAnyNode<I, O, Node> {
|
||||
node: Node,
|
||||
_i: PhantomData<I>,
|
||||
_o: PhantomData<O>,
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
impl<'n, I: StaticType, N: RefNode<&'n I, Output = O> + Copy + 'n, O: 'n + StaticType> Node<&'n dyn DynAny<'n>> for &'n DynAnyNode<'n, N, I> {
|
||||
type Output = Box<dyn dyn_any::DynAny<'n> + 'n>;
|
||||
fn eval(self, input: &'n dyn DynAny<'n>) -> Self::Output {
|
||||
let output = self.0.eval_ref(dyn_any::downcast_ref(input).unwrap_or_else(|| panic!("{}", fmt_error::<I>())));
|
||||
Box::new(output)
|
||||
}
|
||||
}
|
||||
impl<'n, I: StaticType, N: RefNode<'n, I, Output = O> + 'n, O: 'n + StaticType> Node<Box<dyn DynAny<'n>>> for DynAnyNode<'n, N, I> {
|
||||
type Output = Box<dyn dyn_any::DynAny<'n> + 'n>;
|
||||
fn eval(self, input: Box<dyn DynAny<'n>>) -> Self::Output {
|
||||
let input: Box<I> = dyn_any::downcast(input).unwrap_or_else(|| panic!("{}", fmt_error::<I>()));
|
||||
Box::new(self.0.eval_ref(*input))
|
||||
}
|
||||
}*/
|
||||
impl<'n, I: StaticType, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> Node<Any<'n>> for DynAnyNode<N, I, O, ORef>
|
||||
#[node_macro::node_fn(DynAnyNode<_I, _O>)]
|
||||
fn any_node<_I: StaticType, _O: StaticType, N>(input: Any<'input>, node: &'any_input N) -> Any<'input>
|
||||
where
|
||||
N: Node<I, Output = O>,
|
||||
N: for<'any_input> Node<'any_input, _I, Output = _O>,
|
||||
{
|
||||
type Output = Any<'n>;
|
||||
fn eval(self, input: Any<'n>) -> Self::Output {
|
||||
let node = core::any::type_name::<N>();
|
||||
let input: Box<I> = dyn_any::downcast(input).unwrap_or_else(|_| panic!("DynAnyNode Input in:\n{node}"));
|
||||
Box::new(self.0.eval(*input))
|
||||
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(node.eval(*input))
|
||||
}
|
||||
pub struct DynAnyRefNode<I, O, Node> {
|
||||
node: Node,
|
||||
_i: PhantomData<(I, O)>,
|
||||
}
|
||||
impl<'n, I: StaticType, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> Node<Any<'n>> for &'n DynAnyNode<N, I, O, ORef>
|
||||
impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<'input, Any<'input>> for DynAnyRefNode<_I, _O, N>
|
||||
where
|
||||
&'n N: Node<I, Output = ORef>,
|
||||
N: for<'any_input> Node<'any_input, _I, Output = &'any_input _O>,
|
||||
{
|
||||
type Output = Any<'n>;
|
||||
fn eval(self, input: Any<'n>) -> Self::Output {
|
||||
let node = core::any::type_name::<N>();
|
||||
let input: Box<I> = dyn_any::downcast(input).unwrap_or_else(|_| panic!("DynAnyNode Input in:\n{node}"));
|
||||
Box::new((&self.0).eval_ref(*input))
|
||||
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))
|
||||
}
|
||||
}
|
||||
pub struct TypeErasedNode<'n>(pub Box<dyn AsRefNode<'n, Any<'n>, Output = Any<'n>> + 'n>);
|
||||
impl<'n> Node<Any<'n>> for &'n TypeErasedNode<'n> {
|
||||
type Output = Any<'n>;
|
||||
fn eval(self, input: Any<'n>) -> Self::Output {
|
||||
self.0.eval_box(input)
|
||||
}
|
||||
}
|
||||
impl<'n> Node<Any<'n>> for &'n &'n TypeErasedNode<'n> {
|
||||
type Output = Any<'n>;
|
||||
fn eval(self, input: Any<'n>) -> Self::Output {
|
||||
self.0.eval_box(input)
|
||||
}
|
||||
impl<_I, _O, S0> DynAnyRefNode<_I, _O, S0> {
|
||||
pub const fn new(node: S0) -> Self {
|
||||
Self { node, _i: core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub type TypeErasedNode<'n> = dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync;
|
||||
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>;
|
||||
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>;
|
||||
|
||||
pub trait IntoTypeErasedNode<'n> {
|
||||
fn into_type_erased(self) -> TypeErasedNode<'n>;
|
||||
}
|
||||
|
||||
impl<'n> StaticTypeSized for TypeErasedNode<'n> {
|
||||
type Static = TypeErasedNode<'static>;
|
||||
fn into_type_erased(self) -> TypeErasedPinned<'n>;
|
||||
}
|
||||
|
||||
impl<'n, N: 'n> IntoTypeErasedNode<'n> for N
|
||||
where
|
||||
N: AsRefNode<'n, Any<'n>, Output = Any<'n>>,
|
||||
&'n N: Node<Any<'n>, Output = Any<'n>>,
|
||||
N: for<'i> Node<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n,
|
||||
{
|
||||
fn into_type_erased(self) -> TypeErasedNode<'n> {
|
||||
TypeErasedNode(Box::new(self))
|
||||
fn into_type_erased(self) -> TypeErasedPinned<'n> {
|
||||
Box::pin(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, I: StaticType + 'n, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> DynAnyNode<N, I, O, ORef>
|
||||
where
|
||||
&'n N: Node<I, Output = ORef>,
|
||||
{
|
||||
pub fn new(n: N) -> Self {
|
||||
DynAnyNode(n, PhantomData)
|
||||
pub struct DowncastNode<O, Node> {
|
||||
node: Node,
|
||||
_o: PhantomData<O>,
|
||||
}
|
||||
pub fn into_impl(&'n self) -> impl RefNode<Any<'n>, Output = Any<'n>> {
|
||||
self
|
||||
}
|
||||
/*pub fn as_ref(&'n self) -> &'n AnyNode<'n> {
|
||||
self
|
||||
}
|
||||
pub fn into_ref_box(self) -> Box<dyn RefNode<Box<(dyn DynAny<'n> + 'n)>, Output = Box<(dyn DynAny<'n> + 'n)>> + 'n> {
|
||||
Box::new(self)
|
||||
}*/
|
||||
pub fn as_ref(self: &'n &'n Self) -> &'n (dyn RefNode<Any<'n>, Output = Any<'n>> + 'n) {
|
||||
self
|
||||
}
|
||||
pub fn into_box<'a: 'n>(self) -> TypeErasedNode<'n>
|
||||
where
|
||||
Self: 'a,
|
||||
N: Node<I, Output = O>,
|
||||
{
|
||||
self.into_type_erased()
|
||||
}
|
||||
}
|
||||
impl<'n, I: StaticType + 'n, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> DynAnyNode<&'n N, I, O, ORef>
|
||||
where
|
||||
N: Node<I, Output = ORef>,
|
||||
{
|
||||
pub fn new_from_ref(n: &'n N) -> Self {
|
||||
DynAnyNode(n, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DowncastNode<N, I: StaticType>(pub N, pub PhantomData<I>);
|
||||
impl<N: Copy + Clone, I: StaticType> Clone for DowncastNode<N, I> {
|
||||
impl<N: Clone, O: StaticType> Clone for DowncastNode<O, N> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0, self.1)
|
||||
Self { node: self.node.clone(), _o: self._o }
|
||||
}
|
||||
}
|
||||
impl<N: Copy + Clone, I: StaticType> Copy for DowncastNode<N, I> {}
|
||||
impl<N: Copy, O: StaticType> Copy for DowncastNode<O, N> {}
|
||||
|
||||
impl<'n, N, O: 'n + StaticType> Node<Any<'n>> for DowncastNode<N, O>
|
||||
#[node_macro::node_fn(DowncastNode<_O>)]
|
||||
fn downcast<N, _O: StaticType>(input: Any<'input>, node: &'input N) -> _O
|
||||
where
|
||||
N: Node<Any<'n>, Output = Any<'n>>,
|
||||
N: Node<'input, Any<'input>, Output = Any<'input>>,
|
||||
{
|
||||
type Output = O;
|
||||
fn eval(self, input: Any<'n>) -> Self::Output {
|
||||
let output = self.0.eval(input);
|
||||
*dyn_any::downcast(output).expect("DowncastNode Output")
|
||||
}
|
||||
}
|
||||
impl<'n, N, I: StaticType> DowncastNode<N, I>
|
||||
where
|
||||
N: Node<Any<'n>>,
|
||||
{
|
||||
pub fn new(n: N) -> Self {
|
||||
DowncastNode(n, PhantomData)
|
||||
}
|
||||
let node_name = core::any::type_name::<N>();
|
||||
let out = dyn_any::downcast(node.eval(input)).unwrap_or_else(|e| panic!("DynAnyNode Input {e} in:\n{node_name}"));
|
||||
*out
|
||||
}
|
||||
|
||||
/// Boxes the input and downcasts the output.
|
||||
/// Wraps around a node taking Box<dyn DynAny> and returning Box<dyn DynAny>
|
||||
pub struct DowncastBothNode<N, I: StaticType, O: StaticType>(pub N, pub PhantomData<(I, O)>);
|
||||
impl<N: Copy + Clone, I: StaticType, O: StaticType> Clone for DowncastBothNode<N, I, O> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0, self.1)
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DowncastBothNode<'a, I, O> {
|
||||
node: TypeErasedPinnedRef<'a>,
|
||||
_i: PhantomData<I>,
|
||||
_o: PhantomData<O>,
|
||||
}
|
||||
}
|
||||
impl<N: Copy + Clone, I: StaticType, O: StaticType> Copy for DowncastBothNode<N, I, O> {}
|
||||
|
||||
impl<'n, N, I: 'n + StaticType, O: 'n + StaticType> Node<I> for DowncastBothNode<N, I, O>
|
||||
where
|
||||
N: Node<Any<'n>, Output = Any<'n>>,
|
||||
{
|
||||
impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothNode<'n, I, O> {
|
||||
type Output = O;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
let input = Box::new(input) as Box<dyn DynAny>;
|
||||
let output = self.0.eval(input);
|
||||
*dyn_any::downcast(output).expect("DowncastBothNode Output")
|
||||
}
|
||||
}
|
||||
impl<'n, N, I: 'n + StaticType, O: 'n + StaticType> Node<I> for &DowncastBothNode<N, I, O>
|
||||
where
|
||||
N: Node<Any<'n>, Output = Any<'n>> + Copy,
|
||||
#[inline]
|
||||
fn eval<'node: 'input>(&'node self, input: I) -> Self::Output {
|
||||
{
|
||||
type Output = O;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
let input = Box::new(input) as Box<dyn DynAny>;
|
||||
let output = self.0.eval(input);
|
||||
*dyn_any::downcast(output).expect("DowncastBothNode Output")
|
||||
let input = Box::new(input);
|
||||
let out = dyn_any::downcast(self.node.eval(input)).unwrap_or_else(|e| panic!("DynAnyNode Input {e}"));
|
||||
*out
|
||||
}
|
||||
}
|
||||
impl<'n, N, I: StaticType, O: StaticType> DowncastBothNode<N, I, O>
|
||||
where
|
||||
N: Node<Any<'n>>,
|
||||
}
|
||||
impl<'n, I, O> DowncastBothNode<'n, I, O> {
|
||||
pub const fn new(node: TypeErasedPinnedRef<'n>) -> Self {
|
||||
Self {
|
||||
node,
|
||||
_i: core::marker::PhantomData,
|
||||
_o: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Boxes the input and downcasts the output.
|
||||
/// Wraps around a node taking Box<dyn DynAny> and returning Box<dyn DynAny>
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DowncastBothRefNode<'a, I, O> {
|
||||
node: TypeErasedPinnedRef<'a>,
|
||||
_i: PhantomData<(I, O)>,
|
||||
}
|
||||
impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothRefNode<'n, I, O> {
|
||||
type Output = &'input O;
|
||||
#[inline]
|
||||
fn eval<'node: 'input>(&'node self, input: I) -> Self::Output {
|
||||
{
|
||||
pub fn new(n: N) -> Self {
|
||||
DowncastBothNode(n, PhantomData)
|
||||
let input = Box::new(input);
|
||||
let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input)).unwrap_or_else(|e| panic!("DynAnyNode Input {e}"));
|
||||
*out
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'n, I, O> DowncastBothRefNode<'n, I, O> {
|
||||
pub const fn new(node: TypeErasedPinnedRef<'n>) -> Self {
|
||||
Self { node, _i: core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// If we store a `Box<dyn RefNode>` in the stack then the origional DynAnyNode is dropped (because it is not stored by reference)
|
||||
/// This trait is implemented directly by `DynAnyNode` so this means the borrow stack will hold by value
|
||||
pub trait DynAnyNodeTrait<'n> {
|
||||
fn eval_ref_dispatch(&'n self, input: Any<'n>) -> Any<'n>;
|
||||
}
|
||||
impl<'n, I: StaticType, O: 'n + StaticType, Node: 'n> DynAnyNodeTrait<'n> for DynAnyNode<Node, I, O>
|
||||
where
|
||||
&'n Node: RefNode<I>,
|
||||
{
|
||||
fn eval_ref_dispatch(&'n self, input: Any<'n>) -> Any<'n> {
|
||||
self.eval_ref(input)
|
||||
}
|
||||
}*/
|
||||
|
||||
use graphene_core::ops::dynamic::Dynamic;
|
||||
use graphene_core::AsRefNode;
|
||||
pub struct BoxedComposition<'a, Second> {
|
||||
pub first: Box<dyn Node<(), Output = Dynamic<'a>>>,
|
||||
pub second: Second,
|
||||
pub struct ComposeTypeErased<'a> {
|
||||
first: TypeErasedPinnedRef<'a>,
|
||||
second: TypeErasedPinnedRef<'a>,
|
||||
}
|
||||
|
||||
// I can't see to get this to work
|
||||
// We can't use the existing thing in any as it breaks lifetimes
|
||||
// impl<'a, Second: Node<Dynamic<'a>>> Node<()> for BoxedComposition<'a, Second> {
|
||||
// type Output = <Second as Node<Dynamic<'a>>>::Output;
|
||||
// fn eval(self, input: ()) -> Self::Output {
|
||||
// let x = RefNode::eval_ref(self.first.as_ref(), input);
|
||||
// let arg: Dynamic<'a> = x.eval_ref(input);
|
||||
// (self.second).eval(arg)
|
||||
// }
|
||||
// }
|
||||
|
||||
/*impl<'n: 'static, I: StaticType, N, O: 'n + StaticType> DynAnyNode<'n, N, I>
|
||||
where
|
||||
N: RefNode<I, Output = O> + 'n + Copy,
|
||||
{
|
||||
/*pub fn into_owned_erased(self) -> impl RefNode<Any<'n>, Output = Any<'n>> + 'n {
|
||||
self
|
||||
}*/
|
||||
pub fn as_owned(&'n self) -> &'n (dyn RefNode<Any<'n>, Output = Any<'n>> + 'n) {
|
||||
self
|
||||
impl<'i, 'a: 'i> Node<'i, Any<'i>> for ComposeTypeErased<'a> {
|
||||
type Output = Any<'i>;
|
||||
fn eval<'s: 'i>(&'s self, input: Any<'i>) -> Self::Output {
|
||||
let arg = self.first.eval(input);
|
||||
self.second.eval(arg)
|
||||
}
|
||||
/*pub fn into_owned_box(&self) -> Box<dyn DynNodeOwned<'n>> {
|
||||
Box::new(self)
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
|
||||
impl<'a> ComposeTypeErased<'a> {
|
||||
pub const fn new(first: TypeErasedPinnedRef<'a>, second: TypeErasedPinnedRef<'a>) -> Self {
|
||||
ComposeTypeErased { first, second }
|
||||
}
|
||||
}
|
||||
|
||||
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
|
||||
pub type AnyNode<'n> = dyn RefNode<Any<'n>, Output = Any<'n>>;
|
||||
|
||||
pub trait DynNodeRef<'n>: RefNode<&'n dyn DynAny<'n>, Output = Box<dyn DynAny<'n> + 'n>> + 'n {}
|
||||
impl<'n, N: RefNode<&'n dyn DynAny<'n>, Output = Box<dyn DynAny<'n> + 'n>> + 'n> DynNodeRef<'n> for N {}
|
||||
|
||||
pub trait DynNodeOwned<'n>: RefNode<Any<'n>, Output = Any<'n>> + 'n {}
|
||||
impl<'n, N: RefNode<Any<'n>, Output = Any<'n>> + 'n> DynNodeOwned<'n> for N {}
|
||||
|
||||
/*impl<'n> Node<Box<dyn DynAny<'n>>> for &'n Box<dyn DynNodeOwned<'n>> {
|
||||
type Output = Box<dyn DynAny<'n> + 'n>;
|
||||
fn eval(self, input: Box<dyn DynAny<'n>>) -> Self::Output {
|
||||
(&*self as &dyn Node<Box<dyn DynAny<'n> + 'n>, Output = Box<dyn DynAny<'n> + 'n>>).eval(input)
|
||||
pub fn input_node<O: StaticType>(n: TypeErasedPinnedRef) -> DowncastBothNode<(), O> {
|
||||
DowncastBothNode::new(n)
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use graphene_core::ops::AddNode;
|
||||
use graphene_core::value::ValueNode;
|
||||
/*#[test]
|
||||
pub fn dyn_input_composition() {
|
||||
use graphene_core::structural::After;
|
||||
use graphene_core::structural::ComposeNode;
|
||||
let id: DynAnyNode<_, u32> = DynAnyNode::new(IdNode);
|
||||
let add: DynAnyNode<_, (u32, u32)> = DynAnyNode::new(AddNode);
|
||||
let value: DynAnyNode<_, ()> = DynAnyNode::new(ValueNode((3u32, 4u32)));
|
||||
let id = &id.as_owned();
|
||||
let add = add.as_owned();
|
||||
let value = value.as_owned();
|
||||
use graphene_core::{ops::AddNode, ops::IdNode, value::ValueNode};
|
||||
|
||||
/*let computation = ComposeNode::new(value, add);
|
||||
let computation = value.then(add).then(id);
|
||||
let result: u32 = *dyn_any::downcast(computation.eval(&())).unwrap();*/
|
||||
}*/
|
||||
#[test]
|
||||
#[should_panic]
|
||||
pub fn dyn_input_invalid_eval_panic() {
|
||||
static ADD: &DynAnyNode<AddNode, (u32, u32), u32, u32> = &DynAnyNode(AddNode, PhantomData);
|
||||
|
||||
let add = ADD.as_ref();
|
||||
add.eval_ref(Box::new(&("32", 32u32)));
|
||||
//let add = DynAnyNode::new(AddNode::new()).into_type_erased();
|
||||
//add.eval(Box::new(&("32", 32u32)));
|
||||
let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(AddNode::new()));
|
||||
let type_erased = dyn_any.into_type_erased();
|
||||
let _ref_type_erased = type_erased.as_ref();
|
||||
//let type_erased = Box::pin(dyn_any) as TypeErasedPinned<'_>;
|
||||
type_erased.eval(Box::new(&("32", 32u32)));
|
||||
}
|
||||
/*#[test]
|
||||
pub fn dyn_input_storage() {
|
||||
let mut vec: Vec<Box<dyn DynNodeRef>> = vec![];
|
||||
let id: DynAnyNode<_, u32> = DynAnyNode::new(IdNode);
|
||||
let add: DynAnyNode<_, (u32, u32)> = DynAnyNode::new(AddNode);
|
||||
let value: DynAnyNode<_, ()> = DynAnyNode::new(ValueNode((3u32, 4u32)));
|
||||
|
||||
vec.push(add.into_ref_box());
|
||||
vec.push(id.into_ref_box());
|
||||
vec.push(value.into_ref_box());
|
||||
}*/
|
||||
#[test]
|
||||
pub fn dyn_input_invalid_eval_panic_() {
|
||||
//let add = DynAnyNode::new(AddNode::new()).into_type_erased();
|
||||
//add.eval(Box::new(&("32", 32u32)));
|
||||
let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(AddNode::new()));
|
||||
let type_erased = Box::pin(dyn_any) as TypeErasedPinned<'_>;
|
||||
type_erased.eval(Box::new((4u32, 2u32)));
|
||||
let id_node = IdNode::new();
|
||||
let type_erased_id = Box::pin(id_node) as TypeErasedPinned;
|
||||
let type_erased = ComposeTypeErased::new(type_erased.as_ref(), type_erased_id.as_ref());
|
||||
type_erased.eval(Box::new((4u32, 2u32)));
|
||||
//let downcast: DowncastBothNode<(u32, u32), u32> = DowncastBothNode::new(type_erased.as_ref());
|
||||
//downcast.eval((4u32, 2u32));
|
||||
}
|
||||
|
||||
// TODO: Fix this test
|
||||
/*
|
||||
#[test]
|
||||
pub fn dyn_input_storage_composition() {
|
||||
let mut vec: Vec<&(dyn RefNode<Any, Output = Any>)> = vec![];
|
||||
//let id: DynAnyNode<_, u32> = DynAnyNode::new(IdNode);
|
||||
|
||||
// If we put this until the push in a new scope then it failes to compile due to lifetime errors which I'm struggling to fix.
|
||||
|
||||
let value: &DynAnyNode<ValueNode<(u32, u32)>, (), &(u32, u32), _> = &DynAnyNode(ValueNode((3u32, 4u32)), PhantomData);
|
||||
let add: &DynAnyNode<AddNode, &(u32, u32), u32, _> = &DynAnyNode(AddNode, PhantomData);
|
||||
|
||||
let value_ref = value.as_ref();
|
||||
let add_ref = add.as_ref();
|
||||
vec.push(value_ref);
|
||||
vec.push(add_ref);
|
||||
|
||||
//vec.push(add.as_owned());
|
||||
//vec.push(id.as_owned());
|
||||
//let vec = vec.leak();
|
||||
|
||||
let n_value = vec[0];
|
||||
let n_add = vec[1];
|
||||
//let id = vec[2];
|
||||
|
||||
assert_eq!(*(dyn_any::downcast::<&(u32, u32)>(n_value.eval_ref(Box::new(()))).unwrap()), &(3u32, 4u32));
|
||||
fn compose<'n>(
|
||||
first: &'n (dyn RefNode<Box<(dyn DynAny<'n> + 'n)>, Output = Box<(dyn DynAny<'n> + 'n)>> + 'n),
|
||||
second: &'n (dyn RefNode<Box<(dyn DynAny<'n> + 'n)>, Output = Box<(dyn DynAny<'n> + 'n)>> + 'n),
|
||||
input: Any<'n>,
|
||||
) -> Any<'n> {
|
||||
second.eval_ref(first.eval_ref(input))
|
||||
}
|
||||
let result = compose(n_value, n_add, Box::new(()));
|
||||
assert_eq!(*dyn_any::downcast::<u32>(result).unwrap(), 7u32);
|
||||
//let result: u32 = *dyn_any::downcast(computation.eval(Box::new(()))).unwrap();
|
||||
// todo readd test
|
||||
let node = <graphene_core::ops::IdNode>::new();
|
||||
let any: DynAnyNode<Any<'_>, Any<'_>, _> = DynAnyNode::new(ValueNode::new(node));
|
||||
any.into_type_erased();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -1,38 +1,23 @@
|
|||
use graphene_core::{Cache, Node};
|
||||
use graphene_core::Node;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
#[derive(Default)]
|
||||
pub struct CacheNode<T> {
|
||||
cache: OnceCell<T>,
|
||||
}
|
||||
impl<'n, T> Node<T> for &'n CacheNode<T> {
|
||||
type Output = &'n T;
|
||||
fn eval(self, input: T) -> Self::Output {
|
||||
impl<'i, T: 'i> Node<'i, T> for CacheNode<T> {
|
||||
type Output = &'i T;
|
||||
fn eval<'s: 'i>(&'s self, input: T) -> Self::Output {
|
||||
self.cache.get_or_init(|| {
|
||||
trace!("Creating new cache node");
|
||||
input
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<T> Node<T> for CacheNode<T> {
|
||||
type Output = T;
|
||||
fn eval(self, input: T) -> Self::Output {
|
||||
input
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CacheNode<T> {
|
||||
pub fn new() -> CacheNode<T> {
|
||||
pub const fn new() -> CacheNode<T> {
|
||||
CacheNode { cache: OnceCell::new() }
|
||||
}
|
||||
}
|
||||
impl<T> Default for CacheNode<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
impl<T> Cache for CacheNode<T> {
|
||||
fn clear(&mut self) {
|
||||
self.cache = OnceCell::new();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +1,10 @@
|
|||
use core::marker::PhantomData;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use graphene_core::generic::FnNode;
|
||||
use graphene_core::ops::{FlatMapResultNode, MapResultNode};
|
||||
|
||||
use graphene_core::raster::{Color, Image};
|
||||
use graphene_core::structural::{ComposeNode, ConsNode, Then};
|
||||
use graphene_core::value::ValueNode;
|
||||
use graphene_core::Node;
|
||||
use image::Pixel;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
pub struct MapNode<MN: Node<S>, I: IntoIterator<Item = S>, S>(pub MN, PhantomData<(S, I)>);
|
||||
|
||||
impl<I: IntoIterator<Item = S>, MN: Node<S> + Copy, S> Node<I> for MapNode<MN, I, S> {
|
||||
type Output = Vec<MN::Output>;
|
||||
fn eval(self, input: I) -> Self::Output {
|
||||
input.into_iter().map(|x| self.0.eval(x)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IntoIterator<Item = S>, MN: Node<S>, S> MapNode<MN, I, S> {
|
||||
pub const fn new(mn: MN) -> Self {
|
||||
MapNode(mn, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MapImageNode<MN: Node<Color, Output = Color> + Copy>(pub MN);
|
||||
|
||||
impl<MN: Node<Color, Output = Color> + Copy> Node<Image> for MapImageNode<MN> {
|
||||
type Output = Image;
|
||||
fn eval(self, input: Image) -> Self::Output {
|
||||
Image {
|
||||
width: input.width,
|
||||
height: input.height,
|
||||
data: input.data.iter().map(|x| self.0.eval(*x)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, MN: Node<Color, Output = Color> + Copy> Node<Image> for &'n MapImageNode<MN> {
|
||||
type Output = Image;
|
||||
fn eval(self, input: Image) -> Self::Output {
|
||||
Image {
|
||||
width: input.width,
|
||||
height: input.height,
|
||||
data: input.data.iter().map(|x| self.0.eval(*x)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MN: Node<Color, Output = Color> + Copy> MapImageNode<MN> {
|
||||
pub const fn new(mn: MN) -> Self {
|
||||
MapImageNode(mn)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, DynAny)]
|
||||
pub enum Error {
|
||||
IO(std::io::Error),
|
||||
|
@ -79,39 +30,32 @@ impl FileSystem for StdFs {
|
|||
}
|
||||
type Reader = Box<dyn std::io::Read>;
|
||||
|
||||
pub struct FileNode<P: AsRef<Path>, FS: FileSystem>(PhantomData<(P, FS)>);
|
||||
impl<P: AsRef<Path>, FS: FileSystem> Node<(P, FS)> for FileNode<P, FS> {
|
||||
type Output = Result<Reader, Error>;
|
||||
|
||||
fn eval(self, input: (P, FS)) -> Self::Output {
|
||||
let (path, fs) = input;
|
||||
fs.open(path)
|
||||
pub struct FileNode<FileSystem> {
|
||||
fs: FileSystem,
|
||||
}
|
||||
#[node_macro::node_fn(FileNode)]
|
||||
fn file_node<P: AsRef<Path>, FS: FileSystem>(path: P, fs: FS) -> Result<Reader, Error> {
|
||||
fs.open(path)
|
||||
}
|
||||
|
||||
pub struct BufferNode;
|
||||
impl<Reader: std::io::Read> Node<Reader> for BufferNode {
|
||||
type Output = Result<Vec<u8>, Error>;
|
||||
|
||||
fn eval(self, mut reader: Reader) -> Self::Output {
|
||||
let mut buffer = Vec::new();
|
||||
reader.read_to_end(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
#[node_macro::node_fn(BufferNode)]
|
||||
fn buffer_node<R: std::io::Read>(reader: R) -> Result<Vec<u8>, Error> {
|
||||
Ok(std::io::Read::bytes(reader).collect::<Result<Vec<_>, _>>()?)
|
||||
}
|
||||
|
||||
pub fn file_node<'n, P: AsRef<Path> + 'n>() -> impl Node<P, Output = Result<Vec<u8>, Error>> {
|
||||
let fs = ValueNode(StdFs).clone();
|
||||
let fs = ConsNode::new(fs);
|
||||
let file = fs.then(FileNode(PhantomData));
|
||||
/*
|
||||
pub fn file_node<'i, 's: 'i, P: AsRef<Path> + 'i>() -> impl Node<'i, 's, P, Output = Result<Vec<u8>, Error>> {
|
||||
let fs = ValueNode(StdFs).then(CloneNode::new());
|
||||
let file = FileNode::new(fs);
|
||||
|
||||
file.then(FlatMapResultNode::new(BufferNode))
|
||||
file.then(FlatMapResultNode::new(ValueNode::new(BufferNode)))
|
||||
}
|
||||
|
||||
pub fn image_node<'n, P: AsRef<Path> + 'n>() -> impl Node<P, Output = Result<Image, Error>> {
|
||||
pub fn image_node<'i, 's: 'i, P: AsRef<Path> + 'i>() -> impl Node<'i, 's, P, Output = Result<Image, Error>> {
|
||||
let file = file_node();
|
||||
let image_loader = FnNode::new(|data: Vec<u8>| image::load_from_memory(&data).map_err(Error::Image).map(|image| image.into_rgba32f()));
|
||||
let image: ComposeNode<_, _, P> = file.then(FlatMapResultNode::new(image_loader));
|
||||
let image = file.then(FlatMapResultNode::new(ValueNode::new(image_loader)));
|
||||
let convert_image = FnNode::new(|image: image::ImageBuffer<_, _>| {
|
||||
let data = image
|
||||
.enumerate_pixels()
|
||||
|
@ -130,7 +74,7 @@ pub fn image_node<'n, P: AsRef<Path> + 'n>() -> impl Node<P, Output = Result<Ima
|
|||
image.then(MapResultNode::new(convert_image))
|
||||
}
|
||||
|
||||
pub fn export_image_node<'n>() -> impl Node<(Image, &'n str), Output = Result<(), Error>> {
|
||||
pub fn export_image_node<'i, 's: 'i>() -> impl Node<'i, 's, (Image, &'i str), Output = Result<(), Error>> {
|
||||
FnNode::new(|input: (Image, &str)| {
|
||||
let (image, path) = input;
|
||||
let mut new_image = image::ImageBuffer::new(image.width, image.height);
|
||||
|
@ -143,12 +87,14 @@ pub fn export_image_node<'n>() -> impl Node<(Image, &'n str), Output = Result<()
|
|||
new_image.save(path).map_err(Error::Image)
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GrayscaleNode;
|
||||
|
||||
#[node_macro::node_fn(GrayscaleNode)]
|
||||
fn grayscale_image(mut image: Image) -> Image {
|
||||
fn grayscale_image(image: Image) -> Image {
|
||||
let mut image = image;
|
||||
for pixel in &mut image.data {
|
||||
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
|
||||
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
|
||||
|
@ -156,11 +102,29 @@ fn grayscale_image(mut image: Image) -> Image {
|
|||
image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MapImageNode<MapFn> {
|
||||
map_fn: MapFn,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(MapImageNode)]
|
||||
fn grayscale_image<MapFn>(image: Image, map_fn: &'any_input MapFn) -> Image
|
||||
where
|
||||
MapFn: for<'any_input> Node<'any_input, Color, Output = Color> + 'input,
|
||||
{
|
||||
let mut image = image;
|
||||
for pixel in &mut image.data {
|
||||
*pixel = map_fn.eval(*pixel);
|
||||
}
|
||||
image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InvertRGBNode;
|
||||
|
||||
#[node_macro::node_fn(InvertRGBNode)]
|
||||
fn invert_image(mut image: Image) -> Image {
|
||||
let mut image = image;
|
||||
for pixel in &mut image.data {
|
||||
*pixel = Color::from_rgbaf32_unchecked(1. - pixel.r(), 1. - pixel.g(), 1. - pixel.b(), pixel.a());
|
||||
}
|
||||
|
@ -175,7 +139,8 @@ pub struct HueSaturationNode<Hue, Sat, Lit> {
|
|||
}
|
||||
|
||||
#[node_macro::node_fn(HueSaturationNode)]
|
||||
fn shift_image_hsl(mut image: Image, hue_shift: f64, saturation_shift: f64, lightness_shift: f64) -> Image {
|
||||
fn shift_image_hsl(image: Image, hue_shift: f64, saturation_shift: f64, lightness_shift: f64) -> Image {
|
||||
let mut image = image;
|
||||
let (hue_shift, saturation_shift, lightness_shift) = (hue_shift as f32, saturation_shift as f32, lightness_shift as f32);
|
||||
for pixel in &mut image.data {
|
||||
let [hue, saturation, lightness, alpha] = pixel.to_hsla();
|
||||
|
@ -197,7 +162,8 @@ pub struct BrightnessContrastNode<Brightness, Contrast> {
|
|||
|
||||
// From https://stackoverflow.com/questions/2976274/adjust-bitmap-image-brightness-contrast-using-c
|
||||
#[node_macro::node_fn(BrightnessContrastNode)]
|
||||
fn adjust_image_brightness_and_contrast(mut image: Image, brightness: f64, contrast: f64) -> Image {
|
||||
fn adjust_image_brightness_and_contrast(image: Image, brightness: f64, contrast: f64) -> Image {
|
||||
let mut image = image;
|
||||
let (brightness, contrast) = (brightness as f32, contrast as f32);
|
||||
let factor = (259. * (contrast + 255.)) / (255. * (259. - contrast));
|
||||
let channel = |channel: f32| ((factor * (channel * 255. + brightness - 128.) + 128.) / 255.).clamp(0., 1.);
|
||||
|
@ -215,7 +181,8 @@ pub struct GammaNode<G> {
|
|||
|
||||
// https://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-6-gamma-correction/
|
||||
#[node_macro::node_fn(GammaNode)]
|
||||
fn image_gamma(mut image: Image, gamma: f64) -> Image {
|
||||
fn image_gamma(image: Image, gamma: f64) -> Image {
|
||||
let mut image = image;
|
||||
let inverse_gamma = 1. / gamma;
|
||||
let channel = |channel: f32| channel.powf(inverse_gamma as f32);
|
||||
for pixel in &mut image.data {
|
||||
|
@ -230,7 +197,8 @@ pub struct OpacityNode<O> {
|
|||
}
|
||||
|
||||
#[node_macro::node_fn(OpacityNode)]
|
||||
fn image_opacity(mut image: Image, opacity_multiplier: f64) -> Image {
|
||||
fn image_opacity(image: Image, opacity_multiplier: f64) -> Image {
|
||||
let mut image = image;
|
||||
let opacity_multiplier = opacity_multiplier as f32;
|
||||
for pixel in &mut image.data {
|
||||
*pixel = Color::from_rgbaf32_unchecked(pixel.r(), pixel.g(), pixel.b(), pixel.a() * opacity_multiplier)
|
||||
|
@ -245,7 +213,8 @@ pub struct PosterizeNode<P> {
|
|||
|
||||
// Based on http://www.axiomx.com/posterize.htm
|
||||
#[node_macro::node_fn(PosterizeNode)]
|
||||
fn posterize(mut image: Image, posterize_value: f64) -> Image {
|
||||
fn posterize(image: Image, posterize_value: f64) -> Image {
|
||||
let mut image = image;
|
||||
let posterize_value = posterize_value as f32;
|
||||
let number_of_areas = posterize_value.recip();
|
||||
let size_of_areas = (posterize_value - 1.).recip();
|
||||
|
@ -263,7 +232,8 @@ pub struct ExposureNode<E> {
|
|||
|
||||
// Based on https://stackoverflow.com/questions/12166117/what-is-the-math-behind-exposure-adjustment-on-photoshop
|
||||
#[node_macro::node_fn(ExposureNode)]
|
||||
fn exposure(mut image: Image, exposure: f64) -> Image {
|
||||
fn exposure(image: Image, exposure: f64) -> Image {
|
||||
let mut image = image;
|
||||
let multiplier = 2f32.powf(exposure as f32);
|
||||
let channel = |channel: f32| channel * multiplier;
|
||||
for pixel in &mut image.data {
|
||||
|
@ -286,27 +256,18 @@ fn imaginate(image: Image, cached: Option<std::sync::Arc<graphene_core::raster::
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use graphene_core::raster::color::Color;
|
||||
use graphene_core::raster::GrayscaleColorNode;
|
||||
|
||||
#[test]
|
||||
fn map_node() {
|
||||
let array = [Color::from_rgbaf32(1.0, 0.0, 0.0, 1.0).unwrap()];
|
||||
let map = MapNode(GrayscaleColorNode, PhantomData);
|
||||
let values = map.eval(array.into_iter());
|
||||
assert_eq!(values[0], Color::from_rgbaf32(0.33333334, 0.33333334, 0.33333334, 1.0).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_image() {
|
||||
// TODO: reenable this test
|
||||
/*
|
||||
let image = image_node::<&str>();
|
||||
let gray = MapImageNode::new(GrayscaleColorNode);
|
||||
|
||||
let grayscale_picture = image.then(MapResultNode::new(&gray));
|
||||
let grayscale_picture = image.then(MapResultNode::new(&image));
|
||||
let export = export_image_node();
|
||||
|
||||
let picture = grayscale_picture.eval("test-image-1.png").expect("Failed to load image");
|
||||
export.eval((picture, "test-image-1-result.png")).unwrap();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,170 @@
|
|||
use crate::node_registry::push_node;
|
||||
|
||||
use borrow_stack::{BorrowStack, FixedSizeStack};
|
||||
use graph_craft::executor::Executor;
|
||||
use graph_craft::proto::ProtoNetwork;
|
||||
use graphene_core::Node;
|
||||
use graphene_std::any::{Any, TypeErasedNode};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use dyn_any::StaticType;
|
||||
use graph_craft::document::value::UpcastNode;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::executor::Executor;
|
||||
use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef};
|
||||
|
||||
use crate::node_registry::constrcut_node;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct DynamicExecutor {
|
||||
stack: FixedSizeStack<TypeErasedNode<'static>>,
|
||||
output: NodeId,
|
||||
tree: BorrowTree,
|
||||
}
|
||||
|
||||
impl DynamicExecutor {
|
||||
pub fn new(proto_network: ProtoNetwork) -> Self {
|
||||
assert_eq!(proto_network.inputs.len(), 1);
|
||||
let node_count = proto_network.nodes.len();
|
||||
let stack = FixedSizeStack::new(node_count);
|
||||
for (_id, node) in proto_network.nodes {
|
||||
push_node(node, &stack);
|
||||
let output = proto_network.output;
|
||||
let tree = BorrowTree::new(proto_network);
|
||||
Self { tree, output }
|
||||
}
|
||||
Self { stack }
|
||||
|
||||
pub fn update(&mut self, proto_network: ProtoNetwork) {
|
||||
self.output = proto_network.output;
|
||||
info!("setting output to {}", self.output);
|
||||
self.tree.update(proto_network);
|
||||
}
|
||||
}
|
||||
|
||||
impl Executor for DynamicExecutor {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>> {
|
||||
let result = unsafe { self.stack.get().last().unwrap().eval(input) };
|
||||
Ok(result)
|
||||
fn execute<'a, 's: 'a>(&'s self, input: Any<'a>) -> Result<Any<'a>, Box<dyn Error>> {
|
||||
self.tree.eval_any(self.output, input).ok_or_else(|| "Failed to execute".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeContainer<'n> {
|
||||
node: TypeErasedPinned<'n>,
|
||||
// the dependencies are only kept to ensure that the nodes are not dropped while still in use
|
||||
_dependencies: Vec<Arc<NodeContainer<'static>>>,
|
||||
}
|
||||
|
||||
impl<'a> core::fmt::Debug for NodeContainer<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NodeContainer").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NodeContainer<'a> {
|
||||
/// Return a static reference to the TypeErasedNode
|
||||
/// # Safety
|
||||
/// This is unsafe because the returned reference is only valid as long as the NodeContainer is alive
|
||||
pub unsafe fn erase_lifetime(self) -> NodeContainer<'static> {
|
||||
std::mem::transmute(self)
|
||||
}
|
||||
}
|
||||
impl NodeContainer<'static> {
|
||||
unsafe fn static_ref(&self) -> TypeErasedPinnedRef<'static> {
|
||||
let s = &*(self as *const Self);
|
||||
*(&s.node.as_ref() as *const TypeErasedPinnedRef<'static>)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct BorrowTree {
|
||||
nodes: HashMap<NodeId, Arc<NodeContainer<'static>>>,
|
||||
}
|
||||
|
||||
impl BorrowTree {
|
||||
pub fn new(proto_network: ProtoNetwork) -> Self {
|
||||
let mut nodes = BorrowTree::default();
|
||||
for (id, node) in proto_network.nodes {
|
||||
nodes.push_node(id, node)
|
||||
}
|
||||
nodes
|
||||
}
|
||||
|
||||
/// Pushes new nodes into the tree and return orphaned nodes
|
||||
pub fn update(&mut self, proto_network: ProtoNetwork) -> Vec<NodeId> {
|
||||
let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect();
|
||||
for (id, node) in proto_network.nodes {
|
||||
if !self.nodes.contains_key(&id) {
|
||||
self.push_node(id, node);
|
||||
old_nodes.remove(&id);
|
||||
}
|
||||
}
|
||||
old_nodes.into_iter().collect()
|
||||
}
|
||||
|
||||
fn node_refs(&self, nodes: &[NodeId]) -> Vec<TypeErasedPinnedRef<'static>> {
|
||||
self.node_deps(nodes).into_iter().map(|node| unsafe { node.as_ref().static_ref() }).collect()
|
||||
}
|
||||
fn node_deps(&self, nodes: &[NodeId]) -> Vec<Arc<NodeContainer<'static>>> {
|
||||
nodes.iter().map(|node| self.nodes.get(node).unwrap().clone()).collect()
|
||||
}
|
||||
|
||||
fn store_node(&mut self, node: Arc<NodeContainer<'static>>, id: NodeId) -> Arc<NodeContainer<'static>> {
|
||||
self.nodes.insert(id, node.clone());
|
||||
node
|
||||
}
|
||||
|
||||
pub fn get(&self, id: NodeId) -> Option<Arc<NodeContainer<'static>>> {
|
||||
self.nodes.get(&id).cloned()
|
||||
}
|
||||
|
||||
pub fn eval<'i, I: StaticType + 'i, O: StaticType + 'i>(&self, id: NodeId, input: I) -> Option<O> {
|
||||
let node = self.nodes.get(&id).cloned()?;
|
||||
let output = node.node.eval(Box::new(input));
|
||||
dyn_any::downcast::<O>(output).ok().map(|o| *o)
|
||||
}
|
||||
pub fn eval_any<'i, 's: 'i>(&'s self, id: NodeId, input: Any<'i>) -> Option<Any<'i>> {
|
||||
let node = self.nodes.get(&id)?;
|
||||
Some(node.node.eval(input))
|
||||
}
|
||||
|
||||
pub fn free_node(&mut self, id: NodeId) {
|
||||
self.nodes.remove(&id);
|
||||
}
|
||||
|
||||
pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode) {
|
||||
let ProtoNode { input, construction_args, identifier } = proto_node;
|
||||
|
||||
assert!(
|
||||
!matches!(&input, &ProtoNodeInput::Node(_)),
|
||||
"Only nodes without inputs are supported. Any inputs should already be resolved by placing ComposeNodes {:?}, {:?}, {:?}",
|
||||
identifier,
|
||||
construction_args,
|
||||
input,
|
||||
);
|
||||
|
||||
match construction_args {
|
||||
ConstructionArgs::Value(value) => {
|
||||
let upcasted = UpcastNode::new(value);
|
||||
let node = Box::pin(upcasted) as TypeErasedPinned<'_>;
|
||||
let node = NodeContainer { node, _dependencies: vec![] };
|
||||
let node = unsafe { node.erase_lifetime() };
|
||||
self.store_node(Arc::new(node), id);
|
||||
}
|
||||
ConstructionArgs::Nodes(ids) => {
|
||||
let construction_nodes = self.node_refs(&ids);
|
||||
let node = constrcut_node(identifier, construction_nodes);
|
||||
let node = NodeContainer {
|
||||
node,
|
||||
_dependencies: self.node_deps(&ids),
|
||||
};
|
||||
let node = unsafe { node.erase_lifetime() };
|
||||
self.store_node(Arc::new(node), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn push_node() {
|
||||
let mut tree = BorrowTree::default();
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32)));
|
||||
tree.push_node(0, val_1_protonode);
|
||||
let _node = tree.get(0).unwrap();
|
||||
assert_eq!(tree.eval(0, ()), Some(2u32));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,16 +7,9 @@ pub mod node_registry;
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use graphene_core::value::ValueNode;
|
||||
use graphene_core::{structural::*, RefNode};
|
||||
|
||||
use borrow_stack::BorrowStack;
|
||||
use dyn_any::{downcast, IntoDynAny};
|
||||
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
|
||||
use graphene_std::ops::AddNode;
|
||||
use dyn_any::IntoDynAny;
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn borrow_stack() {
|
||||
let stack = borrow_stack::FixedSizeStack::new(256);
|
||||
|
@ -36,7 +29,7 @@ mod tests {
|
|||
});
|
||||
stack.push_fn(|nodes| {
|
||||
let compose_node = nodes[1].after(&nodes[2]);
|
||||
TypeErasedNode(Box::new(compose_node))
|
||||
TypeErasedNode(Box::pin(compose_node))
|
||||
});
|
||||
|
||||
let result = unsafe { &stack.get()[0] }.eval_ref(().into_dyn());
|
||||
|
@ -48,12 +41,13 @@ mod tests {
|
|||
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
|
||||
let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn());
|
||||
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
|
||||
}
|
||||
}*/
|
||||
|
||||
#[test]
|
||||
fn execute_add() {
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::proto::*;
|
||||
use graph_craft::*;
|
||||
|
||||
fn add_network() -> NodeNetwork {
|
||||
NodeNetwork {
|
||||
|
@ -65,10 +59,7 @@ mod tests {
|
|||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
|
||||
"graphene_core::structural::ConsNode",
|
||||
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
|
||||
)),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode<_, _>", &[concrete!("u32"), concrete!("u32")])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
|
@ -77,10 +68,7 @@ mod tests {
|
|||
DocumentNode {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
|
||||
"graphene_core::ops::AddNode",
|
||||
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
|
||||
)),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("(u32, u32)")])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
|
@ -118,7 +106,7 @@ mod tests {
|
|||
use graph_craft::executor::{Compiler, Executor};
|
||||
|
||||
let compiler = Compiler {};
|
||||
let protograph = compiler.compile(network, false);
|
||||
let protograph = compiler.compile(network, true);
|
||||
|
||||
let exec = DynamicExecutor::new(protograph);
|
||||
|
||||
|
|
|
@ -1,52 +1,168 @@
|
|||
use borrow_stack::FixedSizeStack;
|
||||
use glam::DVec2;
|
||||
use graphene_core::generic::FnNode;
|
||||
use graphene_core::ops::{AddNode, CloneNode, IdNode, TypeNode};
|
||||
use graphene_core::ops::{CloneNode, IdNode, TypeNode};
|
||||
use graphene_core::raster::color::Color;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::structural::{ComposeNode, ConsNode, Then};
|
||||
use graphene_core::value::ValueNode;
|
||||
use graphene_core::vector::subpath::Subpath;
|
||||
use graphene_core::Node;
|
||||
use graphene_std::any::DowncastBothNode;
|
||||
use graphene_std::any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode};
|
||||
use graphene_core::raster::*;
|
||||
use graphene_core::structural::Then;
|
||||
use graphene_core::value::{ForgetNode, ValueNode};
|
||||
|
||||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef};
|
||||
|
||||
use graph_craft::proto::NodeIdentifier;
|
||||
use graph_craft::proto::Type;
|
||||
use graph_craft::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput};
|
||||
|
||||
type NodeConstructor = fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>);
|
||||
type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'static>;
|
||||
|
||||
use graph_craft::{concrete, generic};
|
||||
use graphene_std::memo::CacheNode;
|
||||
|
||||
//TODO: turn into hasmap
|
||||
macro_rules! register_node {
|
||||
($path:ty, input: $input:ty, params: [$($type:ty),*]) => {
|
||||
( {NodeIdentifier::new(stringify!($path), &[concrete!(stringify!($input)), $(concrete!(stringify!($type))),*])},
|
||||
|args| {
|
||||
let mut args = args.clone();
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("not enough arguments provided to construct node"))),*);
|
||||
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
Box::pin(any) as TypeErasedPinned
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//TODO: turn into hashmap
|
||||
static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
(NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
pre_node.into_type_erased()
|
||||
} else {
|
||||
IdNode.into_type_erased()
|
||||
}
|
||||
//register_node!(graphene_core::ops::IdNode, input: Any<'_>, params: []),
|
||||
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()),
|
||||
// TODO: create macro to impl for all types
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [&u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [u32]),
|
||||
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::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]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
|
||||
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::AddParameterNode<_>, input: &f64, params: [&f64]),
|
||||
register_node!(graphene_core::raster::GrayscaleColorNode, input: Color, params: []),
|
||||
register_node!(graphene_core::raster::BrightenColorNode<_>, input: Color, params: [f32]),
|
||||
register_node!(graphene_core::raster::HueShiftColorNode<_>, input: Color, params: [f32]),
|
||||
(NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]), |args| {
|
||||
let node = ComposeTypeErased::new(args[0], args[1]);
|
||||
node.into_type_erased()
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()),
|
||||
register_node!(graphene_std::raster::GrayscaleNode, input: Image, params: []),
|
||||
register_node!(graphene_std::raster::InvertRGBNode, input: Image, params: []),
|
||||
(NodeIdentifier::new("graphene_core::structural::MapImageNode", &[]), |args| {
|
||||
let map_fn: DowncastBothNode<Color, Color> = DowncastBothNode::new(args[0]);
|
||||
let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn));
|
||||
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
}),
|
||||
register_node!(graphene_std::raster::HueSaturationNode<_, _, _>, input: Image, params: [f64, f64, f64]),
|
||||
register_node!(graphene_std::raster::BrightnessContrastNode< _, _>, input: Image, params: [f64, f64]),
|
||||
register_node!(graphene_std::raster::GammaNode<_>, input: Image, params: [f64]),
|
||||
register_node!(graphene_std::raster::OpacityNode<_>, input: Image, params: [f64]),
|
||||
register_node!(graphene_std::raster::PosterizeNode<_>, input: Image, params: [f64]),
|
||||
register_node!(graphene_std::raster::ExposureNode<_>, input: Image, params: [f64]),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>", &[concrete!("Image"), concrete!("Option<std::sync::Arc<Image>>")]),
|
||||
|args| {
|
||||
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image>>>(args[15]);
|
||||
let node = graphene_std::raster::ImaginateNode::new(cached);
|
||||
let any = DynAnyNode::new(ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_core::raster::BlurNode", &[concrete!("Image")]), |args| {
|
||||
let radius = DowncastBothNode::<(), u32>::new(args[0]);
|
||||
let sigma = DowncastBothNode::<(), f64>::new(args[1]);
|
||||
let image = DowncastBothRefNode::<Image, Image>::new(args[2]);
|
||||
let empty_image: ValueNode<Image> = ValueNode::new(Image::empty());
|
||||
let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new()));
|
||||
|
||||
//let image = &image as &dyn for<'a> Node<'a, (), Output = &'a Image>;
|
||||
// dirty hack: we abuse that the cache node will ignore the input if it is
|
||||
// evaluated a second time
|
||||
let image = empty.then(image).then(ImageRefNode::new());
|
||||
|
||||
let window = WindowNode::new(radius, image.clone());
|
||||
let map_gaussian = MapSndNode::new(ValueNode::new(DistanceNode.then(GaussianNode::new(sigma))));
|
||||
let map_distances = MapNode::new(ValueNode::new(map_gaussian));
|
||||
let gaussian_iter = window.then(map_distances);
|
||||
let avg = gaussian_iter.then(WeightedAvgNode::new());
|
||||
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
|
||||
let blur_iter = MapNode::new(ValueNode::new(avg));
|
||||
let pixel_iter = image.clone().then(ImageIndexIterNode::new());
|
||||
let blur = pixel_iter.then(blur_iter);
|
||||
let collect = CollectNode {};
|
||||
let vec = blur.then(collect);
|
||||
let new_image = MapImageSliceNode::new(vec);
|
||||
let dimensions = image.then(ImageDimensionsNode::new());
|
||||
let dimensions: TypeNode<_, (), (u32, u32)> = TypeNode::new(dimensions);
|
||||
let new_image = dimensions.then(new_image);
|
||||
let new_image = ForgetNode::new().then(new_image);
|
||||
let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image));
|
||||
node.into_type_erased()
|
||||
}),
|
||||
//register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []),
|
||||
(NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), |_| {
|
||||
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
|
||||
let any = graphene_std::any::DynAnyRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
}),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]),
|
||||
/*
|
||||
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
|
||||
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |proto_node, stack| {
|
||||
(NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[concrete!("&str")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
pre_node.into_type_erased()
|
||||
} else {
|
||||
IdNode.into_type_erased()
|
||||
}
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
|
||||
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
|
||||
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("Image"), concrete!("&str")]),
|
||||
|proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Add Node constructed with out rhs input node") };
|
||||
let pre_node = nodes.get(node_id).unwrap();
|
||||
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||
|
||||
let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(value_node);
|
||||
let node: DynAnyNode<_, f64, _, _> = DynAnyNode::new(ConsNode::new(input_node).then(graphene_core::ops::AddNode));
|
||||
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
|
@ -56,167 +172,19 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
|||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("u32"), concrete!("u32")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
let node: DynAnyNode<AddNode, (u32, u32), _, _> = DynAnyNode::new(graphene_core::ops::AddNode);
|
||||
let node = (pre_node).then(node);
|
||||
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&u32"), concrete!("&u32")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
let node: DynAnyNode<AddNode, (&u32, &u32), _, _> = DynAnyNode::new(graphene_core::ops::AddNode);
|
||||
let node = (pre_node).then(node);
|
||||
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&u32"), concrete!("u32")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
let node: DynAnyNode<AddNode, (&u32, u32), _, _> = DynAnyNode::new(graphene_core::ops::AddNode);
|
||||
let node = (pre_node).then(node);
|
||||
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("&u32"), concrete!("u32")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
|
||||
let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
|
||||
|
||||
let cons_node = ConsNode::new(DowncastNode::<_, &u32>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, u32, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = match proto_node.input {
|
||||
ProtoNodeInput::Network => node.into_type_erased(),
|
||||
ProtoNodeInput::Node(node_id) => {
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
}
|
||||
ProtoNodeInput::None => unreachable!(),
|
||||
};
|
||||
node
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("u32"), concrete!("u32")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||
|
||||
let cons_node = ConsNode::new(DowncastNode::<_, u32>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, u32, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = match proto_node.input {
|
||||
ProtoNodeInput::Network => node.into_type_erased(),
|
||||
ProtoNodeInput::Node(node_id) => {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
}
|
||||
ProtoNodeInput::None => unreachable!(),
|
||||
};
|
||||
node
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
// TODO: create macro to impl for all types
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("&u32"), concrete!("&u32")]),
|
||||
|proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let pre_node = nodes.get(node_id).unwrap();
|
||||
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||
|
||||
let cons_node = ConsNode::new(DowncastNode::<_, &u32>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, &u32, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_core::any::DowncastNode", &[concrete!("&u32")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
let node = pre_node.then(graphene_core::ops::IdNode);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::value::ValueNode", &[concrete!("Any<'>")]), |proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
if let ConstructionArgs::Value(value) = proto_node.construction_args {
|
||||
let node = FnNode::new(move |_| value.clone().up_box() as Any<'static>);
|
||||
|
||||
node.into_type_erased()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::value::ValueNode", &[generic!("T")]), |proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
if let ConstructionArgs::Value(value) = proto_node.construction_args {
|
||||
let node = FnNode::new(move |_| value.clone().up_box() as Any<'static>);
|
||||
node.into_type_erased()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::raster::GrayscaleColorNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let node = DynAnyNode::new(graphene_core::raster::GrayscaleColorNode);
|
||||
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::raster::BrightenColorNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Brighten Color Node constructed with out brightness input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_core::raster::BrightenColorNode::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::raster::HueShiftColorNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
info!("proto node {:?}", proto_node);
|
||||
stack.push_fn(|nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Hue Shift Color Node constructed with out shift input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_core::raster::HueShiftColorNode::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
|
@ -296,302 +264,19 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
|||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]), |proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let operation_node: DowncastBothNode<_, Color, Color> = DowncastBothNode::new(operation_node);
|
||||
let map_node = DynAnyNode::new(graphene_std::raster::MapImageNode::new(operation_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::GrayscaleNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let node = DynAnyNode::new(graphene_std::raster::GrayscaleNode);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::InvertRGBNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let node = DynAnyNode::new(graphene_std::raster::InvertRGBNode);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("HueSaturationNode Node constructed without inputs") };
|
||||
|
||||
let hue: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let saturation: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let lightness: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::HueSaturationNode::new(hue, saturation, lightness));
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BrightnessContrastNode Node constructed without inputs") };
|
||||
|
||||
let brightness: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let contrast: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::BrightnessContrastNode::new(brightness, contrast));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("GammaNode Node constructed without inputs") };
|
||||
let gamma: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::GammaNode::new(gamma));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("OpacityNode Node constructed without inputs") };
|
||||
let opacity: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::OpacityNode::new(opacity));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Posterize node constructed without inputs") };
|
||||
let value: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::PosterizeNode::new(value));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ExposureNode constructed without inputs") };
|
||||
let value: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::ExposureNode::new(value));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::ImaginateNode", &[concrete!("&TypeErasedNode")]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ImaginateNode constructed without inputs") };
|
||||
let value: DowncastBothNode<_, (), Option<std::sync::Arc<graphene_core::raster::Image>>> = DowncastBothNode::new(nodes.get(construction_nodes[15] as usize).unwrap());
|
||||
|
||||
let node = DynAnyNode::new(graphene_std::raster::ImaginateNode::new(value));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
|
||||
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[concrete!("&str")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
|
||||
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
|
||||
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("Image"), concrete!("&str")]),
|
||||
|proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let pre_node = nodes.get(node_id).unwrap();
|
||||
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||
|
||||
let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), |proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
use graphene_core::raster::*;
|
||||
if let ConstructionArgs::Nodes(_image_args) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let image = nodes.get(node_id).unwrap();
|
||||
let node: DynAnyNode<_, Image, Image, &Image> = DynAnyNode::new(CacheNode::new());
|
||||
|
||||
let node = image.then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), |proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
use graphene_core::raster::*;
|
||||
|
||||
static EMPTY_IMAGE: ValueNode<Image> = ValueNode::new(Image::empty());
|
||||
if let ConstructionArgs::Nodes(blur_args) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let pre_node = nodes.get(node_id).unwrap();
|
||||
let radius = nodes.get(blur_args[0] as usize).unwrap();
|
||||
let sigma = nodes.get(blur_args[1] as usize).unwrap();
|
||||
let radius = DowncastBothNode::<_, (), u32>::new(radius);
|
||||
let sigma = DowncastBothNode::<_, (), f64>::new(sigma);
|
||||
let image = DowncastBothNode::<_, Image, &Image>::new(pre_node);
|
||||
// dirty hack: we abuse that the cache node will ignore the input if it is
|
||||
// evaluated a second time
|
||||
let empty: TypeNode<_, (), Image> = TypeNode::new((&EMPTY_IMAGE).then(CloneNode));
|
||||
let image = empty.then(image).then(ImageRefNode::new());
|
||||
let window = WindowNode::new(radius, image);
|
||||
let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window);
|
||||
let map_gaussian = MapSndNode::new(DistanceNode.then(GaussianNode::new(sigma)));
|
||||
let map_distances = MapNode::new(map_gaussian);
|
||||
let gaussian_iter = window.then(map_distances);
|
||||
let avg = gaussian_iter.then(WeightedAvgNode::new());
|
||||
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
|
||||
let blur_iter = MapNode::new(avg);
|
||||
let blur = ImageIndexIterNode.then(blur_iter);
|
||||
let blur: TypeNode<_, ImageSlice<'_>, MapFnIterator<_, _>> = TypeNode::new(blur);
|
||||
let collect = CollectNode {};
|
||||
let vec = blur.then(collect);
|
||||
let vec: TypeNode<_, ImageSlice<'_>, Vec<Color>> = TypeNode::new(vec);
|
||||
let new_image = MapImageSliceNode::new(vec);
|
||||
let new_image: TypeNode<_, ImageSlice<'_>, Image> = TypeNode::new(new_image);
|
||||
let node: DynAnyNode<_, &Image, Image, Image> = DynAnyNode::new(ImageRefNode.then(new_image));
|
||||
let node = ComposeNode::new(pre_node, node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
|
||||
let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
|
||||
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
<<<<<<< HEAD
|
||||
*/
|
||||
];
|
||||
|
||||
pub fn push_node(proto_node: ProtoNode, stack: &FixedSizeStack<TypeErasedNode<'static>>) {
|
||||
if let Some((_id, f)) = NODE_REGISTRY.iter().find(|(id, _)| *id == proto_node.identifier) {
|
||||
f(proto_node, stack);
|
||||
pub fn constrcut_node<'a>(ident: NodeIdentifier, construction_args: Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'a> {
|
||||
if let Some((_id, f)) = NODE_REGISTRY.iter().find(|(id, _)| *id == ident) {
|
||||
f(construction_args)
|
||||
} else {
|
||||
let other_types = NODE_REGISTRY
|
||||
.iter()
|
||||
.map(|(id, _)| id)
|
||||
.filter(|id| id.name.as_ref() == proto_node.identifier.name.as_ref())
|
||||
.collect::<Vec<_>>();
|
||||
panic!(
|
||||
"NodeImplementation: {:?} not found in Registry. Types for which the node is implemented:\n {:#?}",
|
||||
proto_node.identifier, other_types
|
||||
);
|
||||
let other_types = NODE_REGISTRY.iter().map(|(id, _)| id).filter(|id| id.name.as_ref() == ident.name.as_ref()).collect::<Vec<_>>();
|
||||
panic!("NodeImplementation: {:?} not found in Registry. Types for which the node is implemented:\n {:#?}", ident, other_types);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod protograph_testing {
|
||||
use borrow_stack::BorrowStack;
|
||||
|
@ -602,24 +287,24 @@ mod protograph_testing {
|
|||
fn add_values() {
|
||||
let stack = FixedSizeStack::new(256);
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(2u32)));
|
||||
push_node(val_1_protonode, &stack);
|
||||
constrcut_node(val_1_protonode, &stack);
|
||||
|
||||
let val_2_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(40u32)));
|
||||
push_node(val_2_protonode, &stack);
|
||||
constrcut_node(val_2_protonode, &stack);
|
||||
|
||||
let cons_protonode = ProtoNode {
|
||||
construction_args: ConstructionArgs::Nodes(vec![1]),
|
||||
input: ProtoNodeInput::Node(0),
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("u32"), concrete!("u32")]),
|
||||
};
|
||||
push_node(cons_protonode, &stack);
|
||||
constrcut_node(cons_protonode, &stack);
|
||||
|
||||
let add_protonode = ProtoNode {
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
input: ProtoNodeInput::Node(2),
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("u32"), concrete!("u32")]),
|
||||
};
|
||||
push_node(add_protonode, &stack);
|
||||
constrcut_node(add_protonode, &stack);
|
||||
|
||||
let result = unsafe { stack.get()[3].eval(Box::new(())) };
|
||||
let val = *dyn_any::downcast::<u32>(result).unwrap();
|
||||
|
@ -630,14 +315,14 @@ mod protograph_testing {
|
|||
fn grayscale_color() {
|
||||
let stack = FixedSizeStack::new(256);
|
||||
let val_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(Color::from_rgb8(10, 20, 30))));
|
||||
push_node(val_protonode, &stack);
|
||||
constrcut_node(val_protonode, &stack);
|
||||
|
||||
let grayscale_protonode = ProtoNode {
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
input: ProtoNodeInput::Node(0),
|
||||
identifier: NodeIdentifier::new("graphene_core::raster::GrayscaleColorNode", &[]),
|
||||
};
|
||||
push_node(grayscale_protonode, &stack);
|
||||
constrcut_node(grayscale_protonode, &stack);
|
||||
|
||||
let result = unsafe { stack.get()[1].eval(Box::new(())) };
|
||||
let val = *dyn_any::downcast::<Color>(result).unwrap();
|
||||
|
@ -652,7 +337,7 @@ mod protograph_testing {
|
|||
input: ProtoNodeInput::None,
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]),
|
||||
};
|
||||
push_node(image_protonode, &stack);
|
||||
constrcut_node(image_protonode, &stack);
|
||||
|
||||
let result = unsafe { stack.get()[0].eval(Box::new("../gstd/test-image-1.png")) };
|
||||
let image = *dyn_any::downcast::<Image>(result).unwrap();
|
||||
|
@ -667,24 +352,25 @@ mod protograph_testing {
|
|||
input: ProtoNodeInput::None,
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]),
|
||||
};
|
||||
push_node(image_protonode, &stack);
|
||||
constrcut_node(image_protonode, &stack);
|
||||
|
||||
let grayscale_protonode = ProtoNode {
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
input: ProtoNodeInput::None,
|
||||
identifier: NodeIdentifier::new("graphene_core::raster::GrayscaleColorNode", &[]),
|
||||
};
|
||||
push_node(grayscale_protonode, &stack);
|
||||
constrcut_node(grayscale_protonode, &stack);
|
||||
|
||||
let image_map_protonode = ProtoNode {
|
||||
construction_args: ConstructionArgs::Nodes(vec![1]),
|
||||
input: ProtoNodeInput::Node(0),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]),
|
||||
};
|
||||
push_node(image_map_protonode, &stack);
|
||||
constrcut_node(image_map_protonode, &stack);
|
||||
|
||||
let result = unsafe { stack.get()[2].eval(Box::new("../gstd/test-image-1.png")) };
|
||||
let image = *dyn_any::downcast::<Image>(result).unwrap();
|
||||
assert!(!image.data.iter().any(|c| c.r() != c.b() || c.b() != c.g()));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -17,4 +17,5 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
|
|
|
@ -1,15 +1,45 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{format_ident, ToTokens};
|
||||
use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat, PatIdent, ReturnType};
|
||||
use syn::{
|
||||
parse_macro_input, punctuated::Punctuated, token::Comma, FnArg, GenericParam, Ident, ItemFn, Lifetime, Pat, PatIdent, PathArguments, PredicateType, ReturnType, Token, TraitBound, Type, TypeParam,
|
||||
TypeParamBound, WhereClause, WherePredicate,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let node_name = parse_macro_input!(attr as Ident);
|
||||
//let node_name = parse_macro_input!(attr as Ident);
|
||||
let node = parse_macro_input!(attr as syn::PathSegment);
|
||||
|
||||
let function = parse_macro_input!(item as ItemFn);
|
||||
|
||||
let function_name = &function.sig.ident;
|
||||
let node = &node;
|
||||
let node_name = &node.ident;
|
||||
let mut args = match node.arguments.clone() {
|
||||
PathArguments::AngleBracketed(args) => args
|
||||
.args
|
||||
.into_iter()
|
||||
.map(|arg| match arg {
|
||||
syn::GenericArgument::Type(ty) => ty,
|
||||
_ => panic!("Only types are allowed as arguments"),
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
_ => Default::default(),
|
||||
};
|
||||
let arg_idents = args
|
||||
.iter()
|
||||
.filter(|x| x.to_token_stream().to_string().starts_with('_'))
|
||||
.map(|arg| Ident::new(arg.to_token_stream().to_string().to_lowercase().as_str(), Span::call_site()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut function_inputs = function.sig.inputs.iter().filter_map(|arg| if let FnArg::Typed(typed_arg) = arg { Some(typed_arg) } else { None });
|
||||
|
||||
let mut type_generics = function.sig.generics.params.clone();
|
||||
let mut where_clause = function.sig.generics.where_clause.clone().unwrap_or(WhereClause {
|
||||
where_token: Token),
|
||||
predicates: Default::default(),
|
||||
});
|
||||
|
||||
// Extract primary input as first argument
|
||||
let primary_input = function_inputs.next().expect("Primary input required - set to `()` if not needed.");
|
||||
let Pat::Ident(PatIdent{ident: primary_input_ident,..} ) =&*primary_input.pat else {
|
||||
|
@ -17,9 +47,11 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
};
|
||||
let primary_input_ty = &primary_input.ty;
|
||||
|
||||
let body = function.block;
|
||||
|
||||
// Extract secondary inputs as all other arguments
|
||||
let secondary_inputs = function_inputs.collect::<Vec<_>>();
|
||||
let secondary_idents = secondary_inputs
|
||||
let parameter_inputs = function_inputs.collect::<Vec<_>>();
|
||||
let parameter_idents = parameter_inputs
|
||||
.iter()
|
||||
.map(|input| {
|
||||
let Pat::Ident(PatIdent { ident: primary_input_ident,.. }) = &*input.pat else { panic!("Expected ident for secondary input."); };
|
||||
|
@ -34,50 +66,88 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
quote::quote!(())
|
||||
};
|
||||
|
||||
let struct_generics = (0..parameter_inputs.len())
|
||||
.map(|x| {
|
||||
let ident = format_ident!("S{x}");
|
||||
ident
|
||||
})
|
||||
.collect::<Punctuated<_, Comma>>();
|
||||
let struct_generics_iter = struct_generics.iter();
|
||||
|
||||
for ident in struct_generics.iter() {
|
||||
args.push(Type::Verbatim(quote::quote!(#ident)));
|
||||
}
|
||||
|
||||
// Generics are simply `S0` through to `Sn-1` where n is the number of secondary inputs
|
||||
let generics = (0..secondary_inputs.len()).map(|x| format_ident!("S{x}")).collect::<Vec<_>>();
|
||||
// Bindings for all of the above generics to a node with an input of `()` and an output of the type in the function
|
||||
let where_clause = secondary_inputs
|
||||
let node_generics = struct_generics
|
||||
.iter()
|
||||
.zip(&generics)
|
||||
.cloned()
|
||||
.map(|ident| {
|
||||
GenericParam::Type(TypeParam {
|
||||
attrs: vec![],
|
||||
ident,
|
||||
colon_token: Some(Default::default()),
|
||||
bounds: Punctuated::from_iter([TypeParamBound::Lifetime(Lifetime::new("'input", Span::call_site()))].iter().cloned()),
|
||||
eq_token: None,
|
||||
default: None,
|
||||
})
|
||||
})
|
||||
.collect::<Punctuated<_, Comma>>();
|
||||
type_generics.iter_mut().for_each(|x| {
|
||||
if let GenericParam::Type(t) = x {
|
||||
t.bounds.insert(0, TypeParamBound::Lifetime(Lifetime::new("'input", Span::call_site())));
|
||||
}
|
||||
});
|
||||
let generics = type_generics.into_iter().chain(node_generics.iter().cloned()).collect::<Punctuated<_, Comma>>();
|
||||
// Bindings for all of the above generics to a node with an input of `()` and an output of the type in the function
|
||||
let extra_where_clause = parameter_inputs
|
||||
.iter()
|
||||
.zip(&node_generics)
|
||||
.map(|(ty, name)| {
|
||||
let ty = &ty.ty;
|
||||
quote::quote!(#name: Node<(), Output = #ty>)
|
||||
let GenericParam::Type(generic_ty) = name else { panic!("Expected type generic."); };
|
||||
let ident = &generic_ty.ident;
|
||||
WherePredicate::Type(PredicateType {
|
||||
lifetimes: None,
|
||||
bounded_ty: Type::Verbatim(ident.to_token_stream()),
|
||||
colon_token: Default::default(),
|
||||
bounds: Punctuated::from_iter([TypeParamBound::Trait(TraitBound {
|
||||
paren_token: None,
|
||||
modifier: syn::TraitBoundModifier::None,
|
||||
lifetimes: syn::parse_quote!(for<'any_input>),
|
||||
path: syn::parse_quote!(Node<'any_input, (), Output = #ty>),
|
||||
})]),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
where_clause.predicates.extend(extra_where_clause);
|
||||
|
||||
quote::quote! {
|
||||
#function
|
||||
|
||||
impl <#(#generics),*> Node<#primary_input_ty> for #node_name<#(#generics),*>
|
||||
where
|
||||
#(#where_clause),* {
|
||||
|
||||
impl <'input, #generics> Node<'input, #primary_input_ty> for #node_name<#(#args),*>
|
||||
#where_clause
|
||||
{
|
||||
type Output = #output;
|
||||
fn eval(self, #primary_input_ident: #primary_input_ty) -> #output{
|
||||
#function_name(#primary_input_ident #(, self.#secondary_idents.eval(()))*)
|
||||
#[inline]
|
||||
fn eval<'node: 'input>(&'node self, #primary_input_ident: #primary_input_ty) -> Self::Output {
|
||||
#(
|
||||
let #parameter_idents = self.#parameter_idents.eval(());
|
||||
)*
|
||||
|
||||
#body
|
||||
}
|
||||
}
|
||||
|
||||
impl <#(#generics),*> Node<#primary_input_ty> for &#node_name<#(#generics),*>
|
||||
where
|
||||
#(#where_clause + Copy),* {
|
||||
|
||||
type Output = #output;
|
||||
fn eval(self, #primary_input_ident: #primary_input_ty) -> #output{
|
||||
#function_name(#primary_input_ident #(, self.#secondary_idents.eval(()))*)
|
||||
}
|
||||
}
|
||||
|
||||
impl <#(#generics),*> #node_name<#(#generics),*>
|
||||
where
|
||||
#(#where_clause + Copy),* {
|
||||
pub fn new(#(#secondary_idents: #generics),*) -> Self{
|
||||
impl <#(#args),*> #node_name<#(#args),*>
|
||||
{
|
||||
pub const fn new(#(#parameter_idents: #struct_generics_iter),*) -> Self{
|
||||
Self{
|
||||
#(#secondary_idents),*
|
||||
#(#parameter_idents,)*
|
||||
#(#arg_idents: core::marker::PhantomData,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ impl<I: StaticTypeSized, O> GpuExecutor<I, O> {
|
|||
}
|
||||
|
||||
impl<I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor for GpuExecutor<I, O> {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn std::error::Error>> {
|
||||
fn execute<'i, 's: 'i>(&'s self, input: Any<'i>) -> Result<Any<'i>, Box<dyn std::error::Error>> {
|
||||
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
|
||||
let context = &self.context;
|
||||
let result: Vec<O> = execute_shader(
|
||||
|
|
|
@ -27,7 +27,7 @@ impl<'a, I: StaticTypeSized, O> GpuExecutor<'a, I, O> {
|
|||
}
|
||||
|
||||
impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor for GpuExecutor<'a, I, O> {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn std::error::Error>> {
|
||||
fn execute<'i, 's: 'i>(&'s self, input: Any<'i>) -> Result<Any<'i>, Box<dyn std::error::Error>> {
|
||||
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
|
||||
let context = &self.context;
|
||||
let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), *input, self.entry_point.clone());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue