mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-08 00:05:00 +00:00
Improve instancing nodes (make them output group data, add 'Instance Repeat', fix Flatten Vector Elements click targets, and more) (#2610)
* Improve instancing nodes (make them output group data, add 'Instance Repeat', fix Flatten Vector Elements click targets, and more) * Fix test? * Fix more tests? * Fix moar test?? * Clean up instance method naming
This commit is contained in:
parent
a29802de36
commit
ac9fb2b02d
33 changed files with 811 additions and 529 deletions
2
demo-artwork/changing-seasons.graphite
generated
2
demo-artwork/changing-seasons.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/procedural-string-lights.graphite
generated
2
demo-artwork/procedural-string-lights.graphite
generated
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@ use graph_craft::document::value::*;
|
|||
use graph_craft::document::*;
|
||||
use graphene_core::raster::brush_cache::BrushCache;
|
||||
use graphene_core::raster::image::ImageFrameTable;
|
||||
use graphene_core::raster::{Color, RedGreenBlue, RedGreenBlueAlpha};
|
||||
use graphene_core::raster::{CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, NoiseType, RedGreenBlue, RedGreenBlueAlpha};
|
||||
use graphene_core::text::{Font, TypesettingConfig};
|
||||
use graphene_core::transform::Footprint;
|
||||
use graphene_core::vector::VectorDataTable;
|
||||
|
@ -128,6 +128,87 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
description: Cow::Borrowed("Passes-through the input value without changing it. This is useful for rerouting wires for organization purposes."),
|
||||
properties: Some("identity_properties"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Cache",
|
||||
category: "General",
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::network(generic!(T), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode")),
|
||||
manual_composition: Some(generic!(T)),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::transform::FreezeRealTimeNode")),
|
||||
manual_composition: Some(generic!(T)),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::node(NodeId(1), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::transform::BoundlessFootprintNode")),
|
||||
manual_composition: Some(generic!(T)),
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}),
|
||||
inputs: vec![NodeInput::value(TaggedValue::None, true)],
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
network_metadata: Some(NodeNetworkMetadata {
|
||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||
node_metadata: [
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Memoize".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Freeze Real Time".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Boundless Footprint".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
.collect(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
input_properties: vec![("Data", "TODO").into()],
|
||||
output_names: vec!["Data".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
// TODO: Auto-generate this from its proto node macro
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Monitor",
|
||||
|
@ -716,6 +797,87 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
description: Cow::Borrowed("Rasterizes the given vector data"),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Noise Pattern",
|
||||
category: "Raster",
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
manual_composition: Some(concrete!(Context)),
|
||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::raster::NoisePatternNode")),
|
||||
inputs: vec![
|
||||
NodeInput::value(TaggedValue::None, false),
|
||||
NodeInput::value(TaggedValue::Bool(true), false),
|
||||
NodeInput::value(TaggedValue::U32(0), false),
|
||||
NodeInput::value(TaggedValue::F64(10.), false),
|
||||
NodeInput::value(TaggedValue::NoiseType(NoiseType::default()), false),
|
||||
NodeInput::value(TaggedValue::DomainWarpType(DomainWarpType::default()), false),
|
||||
NodeInput::value(TaggedValue::F64(100.), false),
|
||||
NodeInput::value(TaggedValue::FractalType(FractalType::default()), false),
|
||||
NodeInput::value(TaggedValue::U32(3), false),
|
||||
NodeInput::value(TaggedValue::F64(2.), false),
|
||||
NodeInput::value(TaggedValue::F64(0.5), false),
|
||||
NodeInput::value(TaggedValue::F64(0.), false), // 0-1 range
|
||||
NodeInput::value(TaggedValue::F64(2.), false),
|
||||
NodeInput::value(TaggedValue::CellularDistanceFunction(CellularDistanceFunction::default()), false),
|
||||
NodeInput::value(TaggedValue::CellularReturnType(CellularReturnType::default()), false),
|
||||
NodeInput::value(TaggedValue::F64(1.), false),
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
input_properties: vec![
|
||||
("Spacer", "TODO").into(),
|
||||
("Clip", "TODO").into(),
|
||||
("Seed", "TODO").into(),
|
||||
PropertiesRow::with_override("Scale", "TODO", WidgetOverride::Custom("noise_properties_scale".to_string())),
|
||||
PropertiesRow::with_override("Noise Type", "TODO", WidgetOverride::Custom("noise_properties_noise_type".to_string())),
|
||||
PropertiesRow::with_override("Domain Warp Type", "TODO", WidgetOverride::Custom("noise_properties_domain_warp_type".to_string())),
|
||||
PropertiesRow::with_override("Domain Warp Amplitude", "TODO", WidgetOverride::Custom("noise_properties_domain_warp_amplitude".to_string())),
|
||||
PropertiesRow::with_override("Fractal Type", "TODO", WidgetOverride::Custom("noise_properties_fractal_type".to_string())),
|
||||
PropertiesRow::with_override("Fractal Octaves", "TODO", WidgetOverride::Custom("noise_properties_fractal_octaves".to_string())),
|
||||
PropertiesRow::with_override("Fractal Lacunarity", "TODO", WidgetOverride::Custom("noise_properties_fractal_lacunarity".to_string())),
|
||||
PropertiesRow::with_override("Fractal Gain", "TODO", WidgetOverride::Custom("noise_properties_fractal_gain".to_string())),
|
||||
PropertiesRow::with_override("Fractal Weighted Strength", "TODO", WidgetOverride::Custom("noise_properties_fractal_weighted_strength".to_string())),
|
||||
PropertiesRow::with_override("Fractal Ping Pong Strength", "TODO", WidgetOverride::Custom("noise_properties_ping_pong_strength".to_string())),
|
||||
PropertiesRow::with_override("Cellular Distance Function", "TODO", WidgetOverride::Custom("noise_properties_cellular_distance_function".to_string())),
|
||||
PropertiesRow::with_override("Cellular Return Type", "TODO", WidgetOverride::Custom("noise_properties_cellular_return_type".to_string())),
|
||||
PropertiesRow::with_override("Cellular Jitter", "TODO", WidgetOverride::Custom("noise_properties_cellular_jitter".to_string())),
|
||||
],
|
||||
output_names: vec!["Image".to_string()],
|
||||
network_metadata: Some(NodeNetworkMetadata {
|
||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||
node_metadata: [
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Noise Pattern".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeMetadata {
|
||||
persistent_metadata: DocumentNodePersistentMetadata {
|
||||
display_name: "Cull".to_string(),
|
||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
.collect(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Generates different noise patterns."),
|
||||
properties: None,
|
||||
},
|
||||
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
|
||||
// TODO: Auto-generate this from its proto node macro
|
||||
DocumentNodeDefinition {
|
||||
|
@ -3559,7 +3721,7 @@ impl DocumentNodeDefinition {
|
|||
};
|
||||
nested_node_metadata.persistent_metadata.input_properties.resize_with(input_length, PropertiesRow::default);
|
||||
|
||||
//Recurse over all sub nodes if the current node is a network implementation
|
||||
// Recurse over all sub-nodes if the current node is a network implementation
|
||||
let mut current_path = path.clone();
|
||||
current_path.push(current_node);
|
||||
let DocumentNodeImplementation::Network(template_network) = &node_template.document_node.implementation else {
|
||||
|
|
|
@ -54,7 +54,11 @@ pub fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphData
|
|||
ParameterExposeButton::new()
|
||||
.exposed(exposed)
|
||||
.data_type(data_type)
|
||||
.tooltip("Expose this parameter as a node input in the graph")
|
||||
.tooltip(if exposed {
|
||||
"Stop exposing this parameter as a node input in the graph"
|
||||
} else {
|
||||
"Expose this parameter as a node input in the graph"
|
||||
})
|
||||
.on_update(move |_parameter| {
|
||||
Message::Batched(Box::new([
|
||||
NodeGraphMessage::ExposeInput {
|
||||
|
|
|
@ -1141,7 +1141,7 @@ impl NodeNetworkInterface {
|
|||
|
||||
pub fn input_description<'a>(&'a self, node_id: NodeId, index: usize, network_path: &[NodeId]) -> Option<&'a str> {
|
||||
let Some(input_row) = self.input_properties_row(&node_id, index, network_path) else {
|
||||
log::error!("Could not get node_metadata in input_description");
|
||||
log::error!("Could not get input_row in input_description");
|
||||
return None;
|
||||
};
|
||||
let description = input_row.input_description.as_str();
|
||||
|
@ -1162,7 +1162,7 @@ impl NodeNetworkInterface {
|
|||
|
||||
pub fn input_metadata(&self, node_id: &NodeId, index: usize, field: &str, network_path: &[NodeId]) -> Option<&Value> {
|
||||
let Some(input_row) = self.input_properties_row(node_id, index, network_path) else {
|
||||
log::error!("Could not get node_metadata in get_input_metadata");
|
||||
log::error!("Could not get input_row in get_input_metadata");
|
||||
return None;
|
||||
};
|
||||
input_row.input_data.get(field)
|
||||
|
@ -3781,6 +3781,7 @@ impl NodeNetworkInterface {
|
|||
self.unload_stack_dependents(network_path);
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts
|
||||
pub fn replace_implementation(&mut self, node_id: &NodeId, network_path: &[NodeId], implementation: DocumentNodeImplementation) {
|
||||
let Some(network) = self.network_mut(network_path) else {
|
||||
|
|
|
@ -862,6 +862,21 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
document.network_interface.set_input(&InputConnector::node(*node_id, i + 1), input.clone(), network_path);
|
||||
}
|
||||
}
|
||||
|
||||
if reference == "Instance on Points" && inputs_count == 2 {
|
||||
let node_definition = resolve_document_node_type(reference).unwrap();
|
||||
let new_node_template = node_definition.default_node_template();
|
||||
let document_node = new_node_template.document_node;
|
||||
document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone());
|
||||
document
|
||||
.network_interface
|
||||
.replace_implementation_metadata(node_id, network_path, new_node_template.persistent_node_metadata);
|
||||
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
|
||||
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
|
|
|
@ -258,7 +258,7 @@ impl<T: InstanceLayout> InstanceLayout for Instances<T> {
|
|||
}
|
||||
|
||||
let mut rows = self
|
||||
.instances()
|
||||
.instance_ref_iter()
|
||||
.enumerate()
|
||||
.map(|(index, instance)| {
|
||||
vec![
|
||||
|
|
|
@ -293,7 +293,7 @@ impl NodeRuntime {
|
|||
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
|
||||
// Insert the vector modify if we are dealing with vector data
|
||||
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<Context, VectorDataTable>>() {
|
||||
self.vector_modify.insert(parent_network_node_id, record.output.one_instance().instance.clone());
|
||||
self.vector_modify.insert(parent_network_node_id, record.output.one_instance_ref().instance.clone());
|
||||
} else {
|
||||
log::warn!("failed to downcast monitor node output {parent_network_node_id:?}");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::application_io::{ImageTexture, TextureFrameTable};
|
||||
use crate::instances::Instances;
|
||||
use crate::instances::{Instance, Instances};
|
||||
use crate::raster::BlendMode;
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::transform::TransformMut;
|
||||
|
@ -67,10 +67,12 @@ pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||
EitherFormat::OldGraphicGroup(old) => {
|
||||
let mut graphic_group_table = GraphicGroupTable::empty();
|
||||
for (graphic_element, source_node_id) in old.elements {
|
||||
let last = graphic_group_table.push(graphic_element);
|
||||
*last.source_node_id = source_node_id;
|
||||
*last.transform = old.transform;
|
||||
*last.alpha_blending = old.alpha_blending;
|
||||
graphic_group_table.push(Instance {
|
||||
instance: graphic_element,
|
||||
transform: old.transform,
|
||||
alpha_blending: old.alpha_blending,
|
||||
source_node_id,
|
||||
});
|
||||
}
|
||||
graphic_group_table
|
||||
}
|
||||
|
@ -78,12 +80,14 @@ pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||
// Try to deserialize as either table format
|
||||
if let Ok(old_table) = serde_json::from_value::<OldGraphicGroupTable>(value.clone()) {
|
||||
let mut graphic_group_table = GraphicGroupTable::empty();
|
||||
for instance in old_table.instances() {
|
||||
for instance in old_table.instance_ref_iter() {
|
||||
for (graphic_element, source_node_id) in &instance.instance.elements {
|
||||
let new_row = graphic_group_table.push(graphic_element.clone());
|
||||
*new_row.source_node_id = *source_node_id;
|
||||
*new_row.transform = *instance.transform;
|
||||
*new_row.alpha_blending = *instance.alpha_blending;
|
||||
graphic_group_table.push(Instance {
|
||||
instance: graphic_element.clone(),
|
||||
transform: *instance.transform,
|
||||
alpha_blending: *instance.alpha_blending,
|
||||
source_node_id: *source_node_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
graphic_group_table
|
||||
|
@ -276,8 +280,12 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||
EitherFormat::ArtboardGroup(artboard_group) => {
|
||||
let mut table = ArtboardGroupTable::empty();
|
||||
for (artboard, source_node_id) in artboard_group.artboards {
|
||||
let pushed = table.push(artboard);
|
||||
*pushed.source_node_id = source_node_id;
|
||||
table.push(Instance {
|
||||
instance: artboard,
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
source_node_id,
|
||||
});
|
||||
}
|
||||
table
|
||||
}
|
||||
|
@ -290,38 +298,18 @@ pub type ArtboardGroupTable = Instances<Artboard>;
|
|||
#[node_macro::node(category(""))]
|
||||
async fn layer(_: impl Ctx, mut stack: GraphicGroupTable, element: GraphicElement, node_path: Vec<NodeId>) -> GraphicGroupTable {
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
let pushed = stack.push(element);
|
||||
*pushed.source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
stack.push(Instance {
|
||||
instance: element,
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
source_node_id,
|
||||
});
|
||||
|
||||
stack
|
||||
}
|
||||
|
||||
// // TODO: Once we have nicely working spreadsheet tables, test this and make it nicely user-facing and move it from "Debug" to "General"
|
||||
// #[node_macro::node(category("Debug"))]
|
||||
// async fn concatenate<T: Clone>(
|
||||
// _: impl Ctx,
|
||||
// #[implementations(
|
||||
// GraphicGroupTable,
|
||||
// VectorDataTable,
|
||||
// ImageFrameTable<Color>,
|
||||
// TextureFrameTable,
|
||||
// )]
|
||||
// from: Instances<T>,
|
||||
// #[expose]
|
||||
// #[implementations(
|
||||
// GraphicGroupTable,
|
||||
// VectorDataTable,
|
||||
// ImageFrameTable<Color>,
|
||||
// TextureFrameTable,
|
||||
// )]
|
||||
// mut to: Instances<T>,
|
||||
// ) -> Instances<T> {
|
||||
// for instance in from.instances() {
|
||||
// to.push_instance(instance);
|
||||
// }
|
||||
// to
|
||||
// }
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn to_element<Data: Into<GraphicElement> + 'n>(
|
||||
_: impl Ctx,
|
||||
|
@ -354,7 +342,7 @@ async fn to_group<Data: Into<GraphicGroupTable> + 'n>(
|
|||
async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: bool) -> GraphicGroupTable {
|
||||
// TODO: Avoid mutable reference, instead return a new GraphicGroupTable?
|
||||
fn flatten_group(output_group_table: &mut GraphicGroupTable, current_group_table: GraphicGroupTable, fully_flatten: bool, recursion_depth: usize) {
|
||||
for current_instance in current_group_table.instances() {
|
||||
for current_instance in current_group_table.instance_ref_iter() {
|
||||
let current_element = current_instance.instance.clone();
|
||||
let reference = *current_instance.source_node_id;
|
||||
|
||||
|
@ -364,7 +352,7 @@ async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: boo
|
|||
// If we're allowed to recurse, flatten any GraphicGroups we encounter
|
||||
GraphicElement::GraphicGroup(mut current_element) if recurse => {
|
||||
// Apply the parent group's transform to all child elements
|
||||
for graphic_element in current_element.instances_mut() {
|
||||
for graphic_element in current_element.instance_mut_iter() {
|
||||
*graphic_element.transform = *current_instance.transform * *graphic_element.transform;
|
||||
}
|
||||
|
||||
|
@ -372,10 +360,12 @@ async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: boo
|
|||
}
|
||||
// Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten
|
||||
_ => {
|
||||
let pushed = output_group_table.push(current_element);
|
||||
*pushed.source_node_id = reference;
|
||||
// Apply the parent group's transform to the leaf element
|
||||
*pushed.transform = *current_instance.transform * *pushed.transform;
|
||||
output_group_table.push(Instance {
|
||||
instance: current_element,
|
||||
transform: *current_instance.transform,
|
||||
alpha_blending: *current_instance.alpha_blending,
|
||||
source_node_id: reference,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,8 +417,12 @@ async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artb
|
|||
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
let pushed = artboards.push(artboard);
|
||||
*pushed.source_node_id = encapsulating_node_id;
|
||||
artboards.push(Instance {
|
||||
instance: artboard,
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
source_node_id: encapsulating_node_id,
|
||||
});
|
||||
|
||||
artboards
|
||||
}
|
||||
|
|
|
@ -299,7 +299,7 @@ pub trait GraphicElementRendered {
|
|||
|
||||
impl GraphicElementRendered for GraphicGroupTable {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
render.parent_tag(
|
||||
"g",
|
||||
|attributes| {
|
||||
|
@ -325,12 +325,12 @@ impl GraphicElementRendered for GraphicGroupTable {
|
|||
|
||||
#[cfg(feature = "vello")]
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let transform = transform * *instance.transform;
|
||||
let alpha_blending = *instance.alpha_blending;
|
||||
|
||||
let mut layer = false;
|
||||
if let Some(bounds) = self.instances().filter_map(|element| element.instance.bounding_box(transform)).reduce(Quad::combine_bounds) {
|
||||
if let Some(bounds) = self.instance_ref_iter().filter_map(|element| element.instance.bounding_box(transform)).reduce(Quad::combine_bounds) {
|
||||
let blend_mode = match render_params.view_mode {
|
||||
ViewMode::Outline => peniko::Mix::Normal,
|
||||
_ => alpha_blending.blend_mode.into(),
|
||||
|
@ -356,13 +356,13 @@ impl GraphicElementRendered for GraphicGroupTable {
|
|||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.instances()
|
||||
self.instance_ref_iter()
|
||||
.filter_map(|element| element.instance.bounding_box(transform * *element.transform))
|
||||
.reduce(Quad::combine_bounds)
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
if let Some(element_id) = instance.source_node_id {
|
||||
let mut footprint = footprint;
|
||||
footprint.transform *= *instance.transform;
|
||||
|
@ -374,7 +374,7 @@ impl GraphicElementRendered for GraphicGroupTable {
|
|||
if let Some(graphic_group_id) = element_id {
|
||||
let mut all_upstream_click_targets = Vec::new();
|
||||
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let mut new_click_targets = Vec::new();
|
||||
instance.instance.add_upstream_click_targets(&mut new_click_targets);
|
||||
|
||||
|
@ -390,7 +390,7 @@ impl GraphicElementRendered for GraphicGroupTable {
|
|||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let mut new_click_targets = Vec::new();
|
||||
|
||||
instance.instance.add_upstream_click_targets(&mut new_click_targets);
|
||||
|
@ -404,11 +404,11 @@ impl GraphicElementRendered for GraphicGroupTable {
|
|||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
self.instances().any(|instance| instance.instance.contains_artboard())
|
||||
self.instance_ref_iter().any(|instance| instance.instance.contains_artboard())
|
||||
}
|
||||
|
||||
fn new_ids_from_hash(&mut self, _reference: Option<NodeId>) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.instance.new_ids_from_hash(*instance.source_node_id);
|
||||
}
|
||||
}
|
||||
|
@ -420,7 +420,7 @@ impl GraphicElementRendered for GraphicGroupTable {
|
|||
|
||||
impl GraphicElementRendered for VectorDataTable {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let multiplied_transform = render.transform * *instance.transform;
|
||||
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
||||
let has_real_stroke = instance.instance.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||
|
@ -469,7 +469,7 @@ impl GraphicElementRendered for VectorDataTable {
|
|||
use vello::kurbo::{Cap, Join};
|
||||
use vello::peniko;
|
||||
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let multiplied_transform = parent_transform * *instance.transform;
|
||||
let has_real_stroke = instance.instance.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform.matrix2.determinant() != 0.);
|
||||
|
@ -614,7 +614,7 @@ impl GraphicElementRendered for VectorDataTable {
|
|||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.instances()
|
||||
self.instance_ref_iter()
|
||||
.flat_map(|instance| {
|
||||
let stroke_width = instance.instance.style.stroke().map(|s| s.weight()).unwrap_or_default();
|
||||
|
||||
|
@ -633,7 +633,7 @@ impl GraphicElementRendered for VectorDataTable {
|
|||
fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option<NodeId>) {
|
||||
let instance_transform = self.transform();
|
||||
|
||||
for instance in self.instances().map(|instance| instance.instance) {
|
||||
for instance in self.instance_ref_iter().map(|instance| instance.instance) {
|
||||
if let Some(element_id) = element_id {
|
||||
let stroke_width = instance.style.stroke().as_ref().map_or(0., Stroke::weight);
|
||||
let filled = instance.style.fill() != &Fill::None;
|
||||
|
@ -661,7 +661,7 @@ impl GraphicElementRendered for VectorDataTable {
|
|||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let stroke_width = instance.instance.style.stroke().as_ref().map_or(0., Stroke::weight);
|
||||
let filled = instance.instance.style.fill() != &Fill::None;
|
||||
let fill = |mut subpath: bezier_rs::Subpath<_>| {
|
||||
|
@ -679,7 +679,7 @@ impl GraphicElementRendered for VectorDataTable {
|
|||
}
|
||||
|
||||
fn new_ids_from_hash(&mut self, reference: Option<NodeId>) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.instance.vector_new_ids_from_hash(reference.map(|id| id.0).unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
@ -796,42 +796,42 @@ impl GraphicElementRendered for Artboard {
|
|||
|
||||
impl GraphicElementRendered for ArtboardGroupTable {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for artboard in self.instances() {
|
||||
for artboard in self.instance_ref_iter() {
|
||||
artboard.instance.render_svg(render, render_params);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
instance.instance.render_to_vello(scene, transform, context, render_params);
|
||||
}
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.instances().filter_map(|instance| instance.instance.bounding_box(transform)).reduce(Quad::combine_bounds)
|
||||
self.instance_ref_iter().filter_map(|instance| instance.instance.bounding_box(transform)).reduce(Quad::combine_bounds)
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
instance.instance.collect_metadata(metadata, footprint, *instance.source_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
instance.instance.add_upstream_click_targets(click_targets);
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
self.instances().count() > 0
|
||||
self.instance_ref_iter().count() > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicElementRendered for ImageFrameTable<Color> {
|
||||
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let transform = *instance.transform * render.transform;
|
||||
|
||||
let image = &instance.instance;
|
||||
|
@ -870,7 +870,7 @@ impl GraphicElementRendered for ImageFrameTable<Color> {
|
|||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _: &mut RenderContext, _render_params: &RenderParams) {
|
||||
use vello::peniko;
|
||||
|
||||
for instance in self.instances() {
|
||||
for instance in self.instance_ref_iter() {
|
||||
let image = &instance.instance;
|
||||
if image.data.is_empty() {
|
||||
return;
|
||||
|
@ -883,7 +883,7 @@ impl GraphicElementRendered for ImageFrameTable<Color> {
|
|||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.instances()
|
||||
self.instance_ref_iter()
|
||||
.flat_map(|instance| {
|
||||
let transform = transform * *instance.transform;
|
||||
(transform.matrix2.determinant() != 0.).then(|| (transform * Quad::from_box([DVec2::ZERO, DVec2::ONE])).bounding_box())
|
||||
|
@ -939,7 +939,7 @@ impl GraphicElementRendered for RasterFrame {
|
|||
|
||||
match self {
|
||||
RasterFrame::ImageFrame(image) => {
|
||||
for instance in image.instances() {
|
||||
for instance in image.instance_ref_iter() {
|
||||
let image = &instance.instance;
|
||||
if image.data.is_empty() {
|
||||
return;
|
||||
|
@ -951,7 +951,7 @@ impl GraphicElementRendered for RasterFrame {
|
|||
}
|
||||
}
|
||||
RasterFrame::TextureFrame(image_texture) => {
|
||||
for instance in image_texture.instances() {
|
||||
for instance in image_texture.instance_ref_iter() {
|
||||
let image =
|
||||
vello::peniko::Image::new(vec![].into(), peniko::Format::Rgba8, instance.instance.texture.width(), instance.instance.texture.height()).with_extend(peniko::Extend::Repeat);
|
||||
|
||||
|
|
|
@ -40,39 +40,15 @@ impl<T> Instances<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, instance: T) -> InstanceMut<T> {
|
||||
self.instance.push(instance);
|
||||
self.transform.push(DAffine2::IDENTITY);
|
||||
self.alpha_blending.push(AlphaBlending::default());
|
||||
self.source_node_id.push(None);
|
||||
|
||||
InstanceMut {
|
||||
instance: self.instance.last_mut().expect("Shouldn't be empty"),
|
||||
transform: self.transform.last_mut().expect("Shouldn't be empty"),
|
||||
alpha_blending: self.alpha_blending.last_mut().expect("Shouldn't be empty"),
|
||||
source_node_id: self.source_node_id.last_mut().expect("Shouldn't be empty"),
|
||||
}
|
||||
pub fn push(&mut self, instance: Instance<T>) {
|
||||
self.instance.push(instance.instance);
|
||||
self.transform.push(instance.transform);
|
||||
self.alpha_blending.push(instance.alpha_blending);
|
||||
self.source_node_id.push(instance.source_node_id);
|
||||
}
|
||||
|
||||
pub fn push_instance(&mut self, instance: Instance<T>) -> InstanceMut<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.instance.push(instance.instance.clone());
|
||||
self.transform.push(*instance.transform);
|
||||
self.alpha_blending.push(*instance.alpha_blending);
|
||||
self.source_node_id.push(*instance.source_node_id);
|
||||
|
||||
InstanceMut {
|
||||
instance: self.instance.last_mut().expect("Shouldn't be empty"),
|
||||
transform: self.transform.last_mut().expect("Shouldn't be empty"),
|
||||
alpha_blending: self.alpha_blending.last_mut().expect("Shouldn't be empty"),
|
||||
source_node_id: self.source_node_id.last_mut().expect("Shouldn't be empty"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one_instance(&self) -> Instance<T> {
|
||||
Instance {
|
||||
pub fn one_instance_ref(&self) -> InstanceRef<T> {
|
||||
InstanceRef {
|
||||
instance: self.instance.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
transform: self.transform.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
alpha_blending: self.alpha_blending.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
|
@ -91,12 +67,12 @@ impl<T> Instances<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn instances(&self) -> impl DoubleEndedIterator<Item = Instance<T>> {
|
||||
pub fn instance_iter(self) -> impl DoubleEndedIterator<Item = Instance<T>> {
|
||||
self.instance
|
||||
.iter()
|
||||
.zip(self.transform.iter())
|
||||
.zip(self.alpha_blending.iter())
|
||||
.zip(self.source_node_id.iter())
|
||||
.into_iter()
|
||||
.zip(self.transform)
|
||||
.zip(self.alpha_blending)
|
||||
.zip(self.source_node_id)
|
||||
.map(|(((instance, transform), alpha_blending), source_node_id)| Instance {
|
||||
instance,
|
||||
transform,
|
||||
|
@ -105,7 +81,21 @@ impl<T> Instances<T> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn instances_mut(&mut self) -> impl DoubleEndedIterator<Item = InstanceMut<T>> {
|
||||
pub fn instance_ref_iter(&self) -> impl DoubleEndedIterator<Item = InstanceRef<T>> {
|
||||
self.instance
|
||||
.iter()
|
||||
.zip(self.transform.iter())
|
||||
.zip(self.alpha_blending.iter())
|
||||
.zip(self.source_node_id.iter())
|
||||
.map(|(((instance, transform), alpha_blending), source_node_id)| InstanceRef {
|
||||
instance,
|
||||
transform,
|
||||
alpha_blending,
|
||||
source_node_id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn instance_mut_iter(&mut self) -> impl DoubleEndedIterator<Item = InstanceMut<T>> {
|
||||
self.instance
|
||||
.iter_mut()
|
||||
.zip(self.transform.iter_mut())
|
||||
|
@ -119,12 +109,12 @@ impl<T> Instances<T> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Option<Instance<T>> {
|
||||
pub fn get(&self, index: usize) -> Option<InstanceRef<T>> {
|
||||
if index >= self.instance.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Instance {
|
||||
Some(InstanceRef {
|
||||
instance: &self.instance[index],
|
||||
transform: &self.transform[index],
|
||||
alpha_blending: &self.alpha_blending[index],
|
||||
|
@ -199,12 +189,13 @@ fn one_source_node_id_default() -> Vec<Option<NodeId>> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Instance<'a, T> {
|
||||
pub struct InstanceRef<'a, T> {
|
||||
pub instance: &'a T,
|
||||
pub transform: &'a DAffine2,
|
||||
pub alpha_blending: &'a AlphaBlending,
|
||||
pub source_node_id: &'a Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstanceMut<'a, T> {
|
||||
pub instance: &'a mut T,
|
||||
|
@ -213,10 +204,32 @@ pub struct InstanceMut<'a, T> {
|
|||
pub source_node_id: &'a mut Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Instance<T> {
|
||||
pub instance: T,
|
||||
pub transform: DAffine2,
|
||||
pub alpha_blending: AlphaBlending,
|
||||
pub source_node_id: Option<NodeId>,
|
||||
}
|
||||
|
||||
impl<T> Instance<T> {
|
||||
pub fn to_graphic_element<U>(self) -> Instance<U>
|
||||
where
|
||||
T: Into<U>,
|
||||
{
|
||||
Instance {
|
||||
instance: self.instance.into(),
|
||||
transform: self.transform,
|
||||
alpha_blending: self.alpha_blending,
|
||||
source_node_id: self.source_node_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VECTOR DATA TABLE
|
||||
impl Transform for VectorDataTable {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.one_instance().transform
|
||||
*self.one_instance_ref().transform
|
||||
}
|
||||
}
|
||||
impl TransformMut for VectorDataTable {
|
||||
|
@ -228,7 +241,7 @@ impl TransformMut for VectorDataTable {
|
|||
// TEXTURE FRAME TABLE
|
||||
impl Transform for TextureFrameTable {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.one_instance().transform
|
||||
*self.one_instance_ref().transform
|
||||
}
|
||||
}
|
||||
impl TransformMut for TextureFrameTable {
|
||||
|
@ -243,7 +256,7 @@ where
|
|||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.one_instance().transform
|
||||
*self.one_instance_ref().transform
|
||||
}
|
||||
}
|
||||
impl<P: Pixel> TransformMut for ImageFrameTable<P>
|
||||
|
|
|
@ -365,6 +365,30 @@ fn not_equals<U: core::cmp::PartialEq<T>, T>(
|
|||
other_value != value
|
||||
}
|
||||
|
||||
/// The less-than operation (<) compares two values and returns true if the first value is less than the second, or false if it is not.
|
||||
/// If enabled with "Or Equal", the less-than-or-equal operation (<=) will be used instead.
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn less_than<T: core::cmp::PartialOrd<T>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] other_value: T,
|
||||
or_equal: bool,
|
||||
) -> bool {
|
||||
if or_equal { value <= other_value } else { value < other_value }
|
||||
}
|
||||
|
||||
/// The greater-than operation (>) compares two values and returns true if the first value is greater than the second, or false if it is not.
|
||||
/// If enabled with "Or Equal", the greater-than-or-equal operation (>=) will be used instead.
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn greater_than<T: core::cmp::PartialOrd<T>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] other_value: T,
|
||||
or_equal: bool,
|
||||
) -> bool {
|
||||
if or_equal { value >= other_value } else { value > other_value }
|
||||
}
|
||||
|
||||
/// The logical or operation (||) returns true if either of the two inputs are true, or false if both are false.
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn logical_or(_: impl Ctx, value: bool, other_value: bool) -> bool {
|
||||
|
|
|
@ -300,21 +300,21 @@ trait SetBlendMode {
|
|||
|
||||
impl SetBlendMode for VectorDataTable {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.blend_mode = blend_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for GraphicGroupTable {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.blend_mode = blend_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for ImageFrameTable<Color> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.blend_mode = blend_mode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -598,7 +598,7 @@ impl Blend<Color> for ImageFrameTable<Color> {
|
|||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result = self.clone();
|
||||
|
||||
for (over, under) in result.instances_mut().zip(under.instances()) {
|
||||
for (over, under) in result.instance_mut_iter().zip(under.instance_ref_iter()) {
|
||||
let data = over.instance.data.iter().zip(under.instance.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
|
||||
*over.instance = Image {
|
||||
|
@ -731,7 +731,7 @@ where
|
|||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn adjust(&mut self, map_fn: impl Fn(&P) -> P) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
for c in instance.instance.data.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
}
|
||||
|
@ -1360,14 +1360,14 @@ impl MultiplyAlpha for Color {
|
|||
}
|
||||
impl MultiplyAlpha for VectorDataTable {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.opacity *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for GraphicGroupTable {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.opacity *= factor as f32;
|
||||
}
|
||||
}
|
||||
|
@ -1377,7 +1377,7 @@ where
|
|||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instances_mut() {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.opacity *= factor as f32;
|
||||
}
|
||||
}
|
||||
|
@ -1577,7 +1577,7 @@ mod test {
|
|||
let opacity = 100_f64;
|
||||
|
||||
let result = super::color_overlay((), ImageFrameTable::new(image.clone()), overlay_color, BlendMode::Multiply, opacity);
|
||||
let result = result.instances().next().unwrap().instance;
|
||||
let result = result.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
|
||||
assert_eq!(result.data[0], Color::from_rgbaf32_unchecked(0., image_color.g(), 0., image_color.a()));
|
||||
|
|
|
@ -31,7 +31,7 @@ struct BrushCacheImpl {
|
|||
impl BrushCacheImpl {
|
||||
fn compute_brush_plan(&mut self, mut background: ImageFrameTable<Color>, input: &[BrushStroke]) -> BrushPlan {
|
||||
// Do background invalidation.
|
||||
if background.one_instance().instance != self.background.one_instance().instance {
|
||||
if background.one_instance_ref().instance != self.background.one_instance_ref().instance {
|
||||
self.background = background.clone();
|
||||
return BrushPlan {
|
||||
strokes: input.to_vec(),
|
||||
|
|
|
@ -232,7 +232,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
fn from(element: GraphicElement) -> Self {
|
||||
match element {
|
||||
GraphicElement::RasterFrame(crate::RasterFrame::ImageFrame(image)) => Self {
|
||||
image: image.one_instance().instance.clone(),
|
||||
image: image.one_instance_ref().instance.clone(),
|
||||
},
|
||||
_ => panic!("Expected Image, found {:?}", element),
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
*image_frame_table.one_instance_mut().alpha_blending = alpha_blending;
|
||||
image_frame_table
|
||||
}
|
||||
FormatVersions::ImageFrame(image_frame) => ImageFrameTable::new(image_frame.one_instance().instance.image.clone()),
|
||||
FormatVersions::ImageFrame(image_frame) => ImageFrameTable::new(image_frame.one_instance_ref().instance.image.clone()),
|
||||
FormatVersions::ImageFrameTable(image_frame_table) => image_frame_table,
|
||||
})
|
||||
}
|
||||
|
@ -315,8 +315,8 @@ where
|
|||
// TODO: Improve sampling logic
|
||||
#[inline(always)]
|
||||
fn sample(&self, pos: DVec2, area: DVec2) -> Option<Self::Pixel> {
|
||||
let image_transform = self.one_instance().transform;
|
||||
let image = self.one_instance().instance;
|
||||
let image_transform = self.one_instance_ref().transform;
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
let image_size = DVec2::new(image.width() as f64, image.height() as f64);
|
||||
let pos = (DAffine2::from_scale(image_size) * image_transform.inverse()).transform_point2(pos);
|
||||
|
@ -333,19 +333,19 @@ where
|
|||
type Pixel = P;
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
let image = self.one_instance().instance;
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
image.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
let image = self.one_instance().instance;
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
image.height()
|
||||
}
|
||||
|
||||
fn get_pixel(&self, x: u32, y: u32) -> Option<Self::Pixel> {
|
||||
let image = self.one_instance().instance;
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
image.get_pixel(x, y)
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ async fn transform<T: 'n + 'static>(
|
|||
|
||||
let mut transform_target = transform_target.eval(ctx.into_context()).await;
|
||||
|
||||
for data_transform in transform_target.instances_mut() {
|
||||
for data_transform in transform_target.instance_mut_iter() {
|
||||
*data_transform.transform = matrix * *data_transform.transform;
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ fn replace_transform<Data, TransformInput: Transform>(
|
|||
#[implementations(VectorDataTable, ImageFrameTable<Color>, GraphicGroupTable)] mut data: Instances<Data>,
|
||||
#[implementations(DAffine2)] transform: TransformInput,
|
||||
) -> Instances<Data> {
|
||||
for data_transform in data.instances_mut() {
|
||||
for data_transform in data.instance_mut_iter() {
|
||||
*data_transform.transform = transform.transform();
|
||||
}
|
||||
data
|
||||
|
|
|
@ -1,39 +1,74 @@
|
|||
use crate::instances::Instance;
|
||||
use crate::vector::{VectorData, VectorDataTable};
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, OwnedContextImpl};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use crate::instances::{InstanceRef, Instances};
|
||||
use crate::raster::Color;
|
||||
use crate::raster::image::ImageFrameTable;
|
||||
use crate::transform::TransformMut;
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, GraphicElement, GraphicGroupTable, OwnedContextImpl};
|
||||
use glam::DVec2;
|
||||
|
||||
#[node_macro::node(name("Instance on Points"), category("Vector: Shape"), path(graphene_core::vector))]
|
||||
async fn instance_on_points(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_on_points<T: Into<GraphicElement> + Default + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx,
|
||||
points: VectorDataTable,
|
||||
#[implementations(Context -> VectorDataTable)] instance_node: impl Node<'n, Context<'static>, Output = VectorDataTable>,
|
||||
) -> VectorDataTable {
|
||||
let mut result = VectorDataTable::empty();
|
||||
#[implementations(Context -> GraphicGroupTable, Context -> VectorDataTable, Context -> ImageFrameTable<Color>)] instance: impl Node<'n, Context<'static>, Output = Instances<T>>,
|
||||
reverse: bool,
|
||||
) -> GraphicGroupTable {
|
||||
let mut result_table = GraphicGroupTable::empty();
|
||||
|
||||
for Instance { instance: points, transform, .. } in points.instances() {
|
||||
for (index, &point) in points.point_domain.positions().iter().enumerate() {
|
||||
for InstanceRef { instance: points, transform, .. } in points.instance_ref_iter() {
|
||||
let mut iteration = async |index, point| {
|
||||
let transformed_point = transform.transform_point2(point);
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(Box::new(transformed_point));
|
||||
let instanced = instance_node.eval(new_ctx.into_context()).await;
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
|
||||
for instanced in instanced.instances() {
|
||||
let instanced = result.push_instance(instanced);
|
||||
*instanced.transform *= DAffine2::from_translation(transformed_point);
|
||||
for mut instanced in generated_instance.instance_iter() {
|
||||
instanced.transform.translate(transformed_point);
|
||||
result_table.push(instanced.to_graphic_element());
|
||||
}
|
||||
};
|
||||
|
||||
let range = points.point_domain.positions().iter().enumerate();
|
||||
if reverse {
|
||||
for (index, &point) in range.rev() {
|
||||
iteration(index, point).await;
|
||||
}
|
||||
} else {
|
||||
for (index, &point) in range {
|
||||
iteration(index, point).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove once we support empty tables, currently this is here to avoid crashing
|
||||
if result.is_empty() {
|
||||
return VectorDataTable::new(VectorData::empty());
|
||||
}
|
||||
|
||||
result
|
||||
result_table
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Attributes"), path(graphene_core::vector))]
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_repeat<T: Into<GraphicElement> + Default + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(Context -> GraphicGroupTable, Context -> VectorDataTable, Context -> ImageFrameTable<Color>)] instance: impl Node<'n, Context<'static>, Output = Instances<T>>,
|
||||
#[default(1)] count: u64,
|
||||
reverse: bool,
|
||||
) -> GraphicGroupTable {
|
||||
let count = count.max(1) as usize;
|
||||
|
||||
let mut result_table = GraphicGroupTable::empty();
|
||||
|
||||
for index in 0..count {
|
||||
let index = if reverse { count - index - 1 } else { index };
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index);
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
|
||||
for instanced in generated_instance.instance_iter() {
|
||||
result_table.push(instanced.to_graphic_element());
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_position(ctx: impl Ctx + ExtractVarArgs) -> DVec2 {
|
||||
match ctx.vararg(0).map(|dynamic| dynamic.downcast_ref::<DVec2>()) {
|
||||
Ok(Some(position)) => return *position,
|
||||
|
@ -43,7 +78,7 @@ async fn instance_position(ctx: impl Ctx + ExtractVarArgs) -> DVec2 {
|
|||
Default::default()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Attributes"), path(graphene_core::vector))]
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_index(ctx: impl Ctx + ExtractIndex) -> f64 {
|
||||
match ctx.try_index() {
|
||||
Some(index) => return index as f64,
|
||||
|
@ -87,10 +122,17 @@ mod test {
|
|||
|
||||
let positions = [DVec2::new(40., 20.), DVec2::ONE, DVec2::new(-42., 9.), DVec2::new(10., 345.)];
|
||||
let points = VectorDataTable::new(VectorData::from_subpath(Subpath::from_anchors_linear(positions, false)));
|
||||
let repeated = super::instance_on_points(owned, points, &rect).await;
|
||||
let repeated = super::instance_on_points(owned, points, &rect, false).await;
|
||||
assert_eq!(repeated.len(), positions.len());
|
||||
for (position, instanced) in positions.into_iter().zip(repeated.instances()) {
|
||||
let bounds = instanced.instance.bounding_box_with_transform(*instanced.transform).unwrap();
|
||||
for (position, instanced) in positions.into_iter().zip(repeated.instance_ref_iter()) {
|
||||
let bounds = instanced
|
||||
.instance
|
||||
.as_vector_data()
|
||||
.unwrap()
|
||||
.one_instance_ref()
|
||||
.instance
|
||||
.bounding_box_with_transform(*instanced.transform)
|
||||
.unwrap();
|
||||
assert!(position.abs_diff_eq((bounds[0] + bounds[1]) / 2., 1e-10));
|
||||
assert_eq!((bounds[1] - bounds[0]).x, position.y);
|
||||
}
|
||||
|
|
|
@ -253,9 +253,9 @@ fn isometric_grid_test() {
|
|||
|
||||
// Works properly
|
||||
let grid = grid((), (), GridType::Isometric, 10., (30., 30.).into(), 5, 5);
|
||||
assert_eq!(grid.one_instance().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.one_instance().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.one_instance().instance.segment_bezier_iter() {
|
||||
assert_eq!(grid.one_instance_ref().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.one_instance_ref().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.one_instance_ref().instance.segment_bezier_iter() {
|
||||
assert_eq!(bezier.handles, bezier_rs::BezierHandles::Linear);
|
||||
assert!(
|
||||
((bezier.start - bezier.end).length() - 10.).abs() < 1e-5,
|
||||
|
@ -268,9 +268,9 @@ fn isometric_grid_test() {
|
|||
#[test]
|
||||
fn skew_isometric_grid_test() {
|
||||
let grid = grid((), (), GridType::Isometric, 10., (40., 30.).into(), 5, 5);
|
||||
assert_eq!(grid.one_instance().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.one_instance().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.one_instance().instance.segment_bezier_iter() {
|
||||
assert_eq!(grid.one_instance_ref().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.one_instance_ref().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.one_instance_ref().instance.segment_bezier_iter() {
|
||||
assert_eq!(bezier.handles, bezier_rs::BezierHandles::Linear);
|
||||
let vector = bezier.start - bezier.end;
|
||||
let angle = (vector.angle_to(DVec2::X).to_degrees() + 180.) % 180.;
|
||||
|
|
|
@ -433,29 +433,29 @@ impl VectorData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn concat(&mut self, other: &Self, transform: DAffine2, node_id: u64) {
|
||||
let point_map = other
|
||||
pub fn concat(&mut self, additional: &Self, transform_of_additional: DAffine2, collision_hash_seed: u64) {
|
||||
let point_map = additional
|
||||
.point_domain
|
||||
.ids()
|
||||
.iter()
|
||||
.filter(|id| self.point_domain.ids().contains(id))
|
||||
.map(|&old| (old, old.generate_from_hash(node_id)))
|
||||
.map(|&old| (old, old.generate_from_hash(collision_hash_seed)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let segment_map = other
|
||||
let segment_map = additional
|
||||
.segment_domain
|
||||
.ids()
|
||||
.iter()
|
||||
.filter(|id| self.segment_domain.ids().contains(id))
|
||||
.map(|&old| (old, old.generate_from_hash(node_id)))
|
||||
.map(|&old| (old, old.generate_from_hash(collision_hash_seed)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let region_map = other
|
||||
let region_map = additional
|
||||
.region_domain
|
||||
.ids()
|
||||
.iter()
|
||||
.filter(|id| self.region_domain.ids().contains(id))
|
||||
.map(|&old| (old, old.generate_from_hash(node_id)))
|
||||
.map(|&old| (old, old.generate_from_hash(collision_hash_seed)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let id_map = IdMap {
|
||||
|
@ -465,14 +465,14 @@ impl VectorData {
|
|||
region_map,
|
||||
};
|
||||
|
||||
self.point_domain.concat(&other.point_domain, transform, &id_map);
|
||||
self.segment_domain.concat(&other.segment_domain, transform, &id_map);
|
||||
self.region_domain.concat(&other.region_domain, transform, &id_map);
|
||||
self.point_domain.concat(&additional.point_domain, transform_of_additional, &id_map);
|
||||
self.segment_domain.concat(&additional.segment_domain, transform_of_additional, &id_map);
|
||||
self.region_domain.concat(&additional.region_domain, transform_of_additional, &id_map);
|
||||
|
||||
// TODO: properly deal with fills such as gradients
|
||||
self.style = other.style.clone();
|
||||
self.style = additional.style.clone();
|
||||
|
||||
self.colinear_manipulators.extend(other.colinear_manipulators.iter().copied());
|
||||
self.colinear_manipulators.extend(additional.colinear_manipulators.iter().copied());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -181,6 +181,14 @@ impl PointDomain {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.id.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.id.is_empty()
|
||||
}
|
||||
|
||||
/// Iterate over point IDs and positions
|
||||
pub fn iter(&self) -> impl Iterator<Item = (PointId, DVec2)> + '_ {
|
||||
self.ids().iter().copied().zip(self.positions().iter().copied())
|
||||
|
|
|
@ -424,7 +424,7 @@ impl core::hash::Hash for VectorModification {
|
|||
/// A node that applies a procedural modification to some [`VectorData`].
|
||||
#[node_macro::node(category(""))]
|
||||
async fn path_modify(_ctx: impl Ctx, mut vector_data: VectorDataTable, modification: Box<VectorModification>) -> VectorDataTable {
|
||||
let vector_data_transform = *vector_data.one_instance().transform;
|
||||
let vector_data_transform = *vector_data.one_instance_ref().transform;
|
||||
let vector_data = vector_data.one_instance_mut().instance;
|
||||
|
||||
modification.apply(vector_data);
|
||||
|
|
|
@ -2,7 +2,8 @@ use super::algorithms::offset_subpath::offset_subpath;
|
|||
use super::misc::CentroidType;
|
||||
use super::style::{Fill, Gradient, GradientStops, Stroke};
|
||||
use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable};
|
||||
use crate::instances::{InstanceMut, Instances};
|
||||
use crate::instances::{Instance, InstanceMut, Instances};
|
||||
use crate::raster::image::ImageFrameTable;
|
||||
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, SeedValue};
|
||||
use crate::renderer::GraphicElementRendered;
|
||||
use crate::transform::{Footprint, Transform, TransformMut};
|
||||
|
@ -11,8 +12,10 @@ use crate::vector::style::{LineCap, LineJoin};
|
|||
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicElement, GraphicGroupTable, OwnedContextImpl};
|
||||
use bezier_rs::{Join, ManipulatorGroup, Subpath, SubpathTValue, TValue};
|
||||
use core::f64::consts::PI;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
||||
/// Implemented for types that can be converted to an iterator of vector data.
|
||||
/// Used for the fill and stroke node so they can be used on VectorData or GraphicGroup
|
||||
|
@ -23,15 +26,15 @@ trait VectorDataTableIterMut {
|
|||
impl VectorDataTableIterMut for GraphicGroupTable {
|
||||
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<VectorData>> {
|
||||
// Grab only the direct children
|
||||
self.instances_mut()
|
||||
self.instance_mut_iter()
|
||||
.filter_map(|element| element.instance.as_vector_data_mut())
|
||||
.flat_map(move |vector_data| vector_data.instances_mut())
|
||||
.flat_map(move |vector_data| vector_data.instance_mut_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl VectorDataTableIterMut for VectorDataTable {
|
||||
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<VectorData>> {
|
||||
self.instances_mut()
|
||||
self.instance_mut_iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +201,7 @@ where
|
|||
async fn repeat<I: 'n + Send>(
|
||||
_: impl Ctx,
|
||||
// TODO: Implement other GraphicElementRendered types.
|
||||
#[implementations(VectorDataTable, GraphicGroupTable)] instance: Instances<I>,
|
||||
#[implementations(GraphicGroupTable, VectorDataTable, ImageFrameTable<Color>)] instance: Instances<I>,
|
||||
#[default(100., 100.)]
|
||||
// TODO: When using a custom Properties panel layout in document_node_definitions.rs and this default is set, the widget weirdly doesn't show up in the Properties panel. Investigation is needed.
|
||||
direction: DVec2,
|
||||
|
@ -221,13 +224,14 @@ where
|
|||
for index in 0..instances {
|
||||
let angle = index as f64 * angle / total;
|
||||
let translation = index as f64 * direction / total;
|
||||
let modification = DAffine2::from_translation(center) * DAffine2::from_angle(angle) * DAffine2::from_translation(translation) * DAffine2::from_translation(-center);
|
||||
let transform = DAffine2::from_translation(center) * DAffine2::from_angle(angle) * DAffine2::from_translation(translation) * DAffine2::from_translation(-center);
|
||||
|
||||
let mut new_graphic_element = instance.to_graphic_element().clone();
|
||||
new_graphic_element.new_ids_from_hash(Some(crate::uuid::NodeId(index as u64)));
|
||||
|
||||
let new_instance = result_table.push(new_graphic_element);
|
||||
*new_instance.transform = modification;
|
||||
result_table.push(Instance {
|
||||
instance: instance.to_graphic_element().clone(),
|
||||
transform,
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id: None,
|
||||
});
|
||||
}
|
||||
|
||||
result_table
|
||||
|
@ -237,7 +241,7 @@ where
|
|||
async fn circular_repeat<I: 'n + Send>(
|
||||
_: impl Ctx,
|
||||
// TODO: Implement other GraphicElementRendered types.
|
||||
#[implementations(VectorDataTable, GraphicGroupTable)] instance: Instances<I>,
|
||||
#[implementations(GraphicGroupTable, VectorDataTable, ImageFrameTable<Color>)] instance: Instances<I>,
|
||||
angle_offset: Angle,
|
||||
#[default(5)] radius: f64,
|
||||
#[default(5)] instances: IntegerCount,
|
||||
|
@ -256,13 +260,14 @@ where
|
|||
|
||||
for index in 0..instances {
|
||||
let rotation = DAffine2::from_angle((std::f64::consts::TAU / instances as f64) * index as f64 + angle_offset.to_radians());
|
||||
let modification = DAffine2::from_translation(center) * rotation * DAffine2::from_translation(base_transform);
|
||||
let transform = DAffine2::from_translation(center) * rotation * DAffine2::from_translation(base_transform);
|
||||
|
||||
let mut new_graphic_element = instance.to_graphic_element().clone();
|
||||
new_graphic_element.new_ids_from_hash(Some(crate::uuid::NodeId(index as u64)));
|
||||
|
||||
let new_instance = result_table.push(new_graphic_element);
|
||||
*new_instance.transform = modification;
|
||||
result_table.push(Instance {
|
||||
instance: instance.to_graphic_element().clone(),
|
||||
transform,
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id: None,
|
||||
});
|
||||
}
|
||||
|
||||
result_table
|
||||
|
@ -274,7 +279,7 @@ async fn copy_to_points<I: 'n + Send>(
|
|||
points: VectorDataTable,
|
||||
#[expose]
|
||||
/// Artwork to be copied and placed at each point.
|
||||
#[implementations(VectorDataTable, GraphicGroupTable)]
|
||||
#[implementations(GraphicGroupTable, VectorDataTable, ImageFrameTable<Color>)]
|
||||
instance: Instances<I>,
|
||||
/// Minimum range of randomized sizes given to each instance.
|
||||
#[default(1)]
|
||||
|
@ -301,7 +306,7 @@ where
|
|||
Instances<I>: GraphicElementRendered,
|
||||
{
|
||||
let points_transform = points.transform();
|
||||
let points_list = points.instances().flat_map(|element| element.instance.point_domain.positions());
|
||||
let points_list = points.instance_ref_iter().flat_map(|element| element.instance.point_domain.positions());
|
||||
|
||||
let random_scale_difference = random_scale_max - random_scale_min;
|
||||
|
||||
|
@ -316,7 +321,7 @@ where
|
|||
|
||||
let mut result_table = GraphicGroupTable::default();
|
||||
|
||||
for (index, &point) in points_list.into_iter().enumerate() {
|
||||
for &point in points_list.into_iter() {
|
||||
let center_transform = DAffine2::from_translation(instance_center);
|
||||
|
||||
let translation = points_transform.transform_point2(point);
|
||||
|
@ -342,11 +347,14 @@ where
|
|||
random_scale_min
|
||||
};
|
||||
|
||||
let mut new_graphic_element = instance.to_graphic_element().clone();
|
||||
new_graphic_element.new_ids_from_hash(Some(crate::uuid::NodeId(index as u64)));
|
||||
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(scale), rotation, translation) * center_transform;
|
||||
|
||||
let new_instance = result_table.push(new_graphic_element);
|
||||
*new_instance.transform = DAffine2::from_scale_angle_translation(DVec2::splat(scale), rotation, translation) * center_transform;
|
||||
result_table.push(Instance {
|
||||
instance: instance.to_graphic_element().clone(),
|
||||
transform,
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id: None,
|
||||
});
|
||||
}
|
||||
|
||||
result_table
|
||||
|
@ -355,7 +363,7 @@ where
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
async fn mirror<I: 'n + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(VectorDataTable, GraphicGroupTable)] instance: Instances<I>,
|
||||
#[implementations(GraphicGroupTable, VectorDataTable, ImageFrameTable<Color>)] instance: Instances<I>,
|
||||
#[default(0., 0.)] center: DVec2,
|
||||
#[range((-90., 90.))] angle: Angle,
|
||||
#[default(true)] keep_original: bool,
|
||||
|
@ -382,20 +390,25 @@ where
|
|||
);
|
||||
|
||||
// Apply reflection around the center point
|
||||
let modification = DAffine2::from_translation(mirror_center) * reflection * DAffine2::from_translation(-mirror_center);
|
||||
let transform = DAffine2::from_translation(mirror_center) * reflection * DAffine2::from_translation(-mirror_center);
|
||||
|
||||
// Add original instance depending on the keep_original flag
|
||||
if keep_original {
|
||||
result_table.push(instance.to_graphic_element());
|
||||
result_table.push(Instance {
|
||||
instance: instance.to_graphic_element().clone(),
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Create and add mirrored instance
|
||||
let mut mirrored_element = instance.to_graphic_element();
|
||||
mirrored_element.new_ids_from_hash(None);
|
||||
|
||||
// Apply the transformation to the mirrored instance
|
||||
let mirrored_instance = result_table.push(mirrored_element);
|
||||
*mirrored_instance.transform = modification;
|
||||
result_table.push(Instance {
|
||||
instance: instance.to_graphic_element(),
|
||||
transform,
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id: None,
|
||||
});
|
||||
|
||||
result_table
|
||||
}
|
||||
|
@ -417,7 +430,7 @@ async fn round_corners(
|
|||
) -> VectorDataTable {
|
||||
let source_transform = source.transform();
|
||||
let source_transform_inverse = source_transform.inverse();
|
||||
let source = source.one_instance().instance;
|
||||
let source = source.one_instance_ref().instance;
|
||||
let upstream_graphics_group = source.upstream_graphic_group.clone();
|
||||
|
||||
// Flip the roundness to help with user intuition
|
||||
|
@ -517,7 +530,7 @@ async fn spatial_merge_by_distance(
|
|||
distance: f64,
|
||||
) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
let point_count = vector_data.point_domain.positions().len();
|
||||
|
||||
// Find min x and y for grid cell normalization
|
||||
|
@ -638,10 +651,10 @@ async fn spatial_merge_by_distance(
|
|||
#[node_macro::node(category("Debug"), path(graphene_core::vector))]
|
||||
async fn box_warp(_: impl Ctx, vector_data: VectorDataTable, #[expose] rectangle: VectorDataTable) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance.clone();
|
||||
let vector_data = vector_data.one_instance_ref().instance.clone();
|
||||
|
||||
let target_transform = rectangle.transform();
|
||||
let target = rectangle.one_instance().instance;
|
||||
let target = rectangle.one_instance_ref().instance;
|
||||
|
||||
// Get the bounding box of the source vector data
|
||||
let source_bbox = vector_data.bounding_box_with_transform(vector_data_transform).unwrap_or([DVec2::ZERO, DVec2::ONE]);
|
||||
|
@ -727,7 +740,7 @@ async fn remove_handles(
|
|||
max_handle_distance: f64,
|
||||
) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let mut vector_data = vector_data.one_instance().instance.clone();
|
||||
let mut vector_data = vector_data.one_instance_ref().instance.clone();
|
||||
|
||||
for (_, handles, start, end) in vector_data.segment_domain.handles_mut() {
|
||||
// Only convert to linear if handles are within the threshold distance
|
||||
|
@ -773,7 +786,7 @@ async fn generate_handles(
|
|||
curvature: f64,
|
||||
) -> VectorDataTable {
|
||||
let source_transform = source.transform();
|
||||
let source = source.one_instance().instance;
|
||||
let source = source.one_instance_ref().instance;
|
||||
|
||||
let mut result = VectorData::empty();
|
||||
result.style = source.style.clone();
|
||||
|
@ -955,7 +968,7 @@ async fn generate_handles(
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
async fn bounding_box(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let mut result = vector_data
|
||||
.bounding_box()
|
||||
|
@ -972,7 +985,7 @@ async fn bounding_box(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTa
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
async fn dimensions(_: impl Ctx, vector_data: VectorDataTable) -> DVec2 {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
vector_data
|
||||
.bounding_box_with_transform(vector_data_transform)
|
||||
.map(|[top_left, bottom_right]| bottom_right - top_left)
|
||||
|
@ -982,7 +995,7 @@ async fn dimensions(_: impl Ctx, vector_data: VectorDataTable) -> DVec2 {
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector), properties("offset_path_properties"))]
|
||||
async fn offset_path(_: impl Ctx, vector_data: VectorDataTable, distance: f64, line_join: LineJoin, #[default(4.)] miter_limit: f64) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let subpaths = vector_data.stroke_bezier_paths();
|
||||
let mut result = VectorData::empty();
|
||||
|
@ -1018,7 +1031,7 @@ async fn offset_path(_: impl Ctx, vector_data: VectorDataTable, distance: f64, l
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
async fn solidify_stroke(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let stroke = vector_data.style.stroke().clone().unwrap_or_default();
|
||||
let bezpaths = vector_data.stroke_bezpath_iter();
|
||||
|
@ -1072,15 +1085,20 @@ async fn flatten_vector_elements(_: impl Ctx, graphic_group_input: GraphicGroupT
|
|||
// a flatten vector elements connected to an if else node, another connection from the cache directly
|
||||
// To the if else node, and another connection from the cache to a matches type node connected to the if else node.
|
||||
fn flatten_group(graphic_group_table: &GraphicGroupTable, output: &mut InstanceMut<VectorData>) {
|
||||
for current_element in graphic_group_table.instances() {
|
||||
for (group_index, current_element) in graphic_group_table.instance_ref_iter().enumerate() {
|
||||
match current_element.instance {
|
||||
GraphicElement::VectorData(vector_data_table) => {
|
||||
// Loop through every row of the VectorDataTable and concatenate each instance's subpath into the output VectorData instance.
|
||||
for vector_data_instance in vector_data_table.instances() {
|
||||
for (vector_index, vector_data_instance) in vector_data_table.instance_ref_iter().enumerate() {
|
||||
let other = vector_data_instance.instance;
|
||||
let transform = *current_element.transform * *vector_data_instance.transform;
|
||||
let node_id = current_element.source_node_id.map(|node_id| node_id.0).unwrap_or_default();
|
||||
output.instance.concat(other, transform, node_id);
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
(group_index, vector_index, node_id).hash(&mut hasher);
|
||||
let collision_hash_seed = hasher.finish();
|
||||
|
||||
output.instance.concat(other, transform, collision_hash_seed);
|
||||
|
||||
// Use the last encountered style as the output style
|
||||
output.instance.style = vector_data_instance.instance.style.clone();
|
||||
|
@ -1088,7 +1106,7 @@ async fn flatten_vector_elements(_: impl Ctx, graphic_group_input: GraphicGroupT
|
|||
}
|
||||
GraphicElement::GraphicGroup(graphic_group) => {
|
||||
let mut graphic_group = graphic_group.clone();
|
||||
for instance in graphic_group.instances_mut() {
|
||||
for instance in graphic_group.instance_mut_iter() {
|
||||
*instance.transform = *current_element.transform * *instance.transform;
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1118,7 @@ async fn flatten_vector_elements(_: impl Ctx, graphic_group_input: GraphicGroupT
|
|||
}
|
||||
|
||||
let mut output_table = VectorDataTable::default();
|
||||
let Some(mut output) = output_table.instances_mut().next() else { return output_table };
|
||||
let Some(mut output) = output_table.instance_mut_iter().next() else { return output_table };
|
||||
|
||||
flatten_group(&graphic_group_input, &mut output);
|
||||
|
||||
|
@ -1113,7 +1131,7 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
|
|||
let spacing = spacing.max(0.01);
|
||||
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
// Create an iterator over the bezier segments with enumeration and peeking capability.
|
||||
let mut bezier = vector_data.segment_bezier_iter().enumerate().peekable();
|
||||
|
@ -1274,7 +1292,7 @@ async fn position_on_path(
|
|||
let euclidian = !parameterized_distance;
|
||||
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let subpaths_count = vector_data.stroke_bezier_paths().count() as f64;
|
||||
let progress = progress.clamp(0., subpaths_count);
|
||||
|
@ -1306,7 +1324,7 @@ async fn tangent_on_path(
|
|||
let euclidian = !parameterized_distance;
|
||||
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let subpaths_count = vector_data.stroke_bezier_paths().count() as f64;
|
||||
let progress = progress.clamp(0., subpaths_count);
|
||||
|
@ -1340,7 +1358,7 @@ async fn poisson_disk_points(
|
|||
seed: SeedValue,
|
||||
) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||
let mut result = VectorData::empty();
|
||||
|
@ -1391,7 +1409,7 @@ async fn poisson_disk_points(
|
|||
#[node_macro::node(category(""), path(graphene_core::vector))]
|
||||
async fn subpath_segment_lengths(_: impl Ctx, vector_data: VectorDataTable) -> Vec<f64> {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
vector_data
|
||||
.segment_bezier_iter()
|
||||
|
@ -1447,7 +1465,7 @@ async fn spline(_: impl Ctx, mut vector_data: VectorDataTable) -> VectorDataTabl
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)] amount: f64, seed: SeedValue) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let mut vector_data = vector_data.one_instance().instance.clone();
|
||||
let mut vector_data = vector_data.one_instance_ref().instance.clone();
|
||||
|
||||
let inverse_transform = (vector_data_transform.matrix2.determinant() != 0.).then(|| vector_data_transform.inverse()).unwrap_or_default();
|
||||
|
||||
|
@ -1500,14 +1518,14 @@ async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)]
|
|||
async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDataTable, #[default(0.5)] time: Fraction, #[min(0.)] start_index: IntegerCount) -> VectorDataTable {
|
||||
let time = time.clamp(0., 1.);
|
||||
|
||||
let source_alpha_blending = source.one_instance().alpha_blending;
|
||||
let target_alpha_blending = target.one_instance().alpha_blending;
|
||||
let source_alpha_blending = source.one_instance_ref().alpha_blending;
|
||||
let target_alpha_blending = target.one_instance_ref().alpha_blending;
|
||||
|
||||
let source_transform = source.transform();
|
||||
let target_transform = target.transform();
|
||||
|
||||
let source = source.one_instance().instance;
|
||||
let target = target.one_instance().instance;
|
||||
let source = source.one_instance_ref().instance;
|
||||
let target = target.one_instance_ref().instance;
|
||||
|
||||
let mut result = VectorDataTable::default();
|
||||
|
||||
|
@ -1699,7 +1717,7 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2,
|
|||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
fn bevel(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable {
|
||||
let source_transform = source.transform();
|
||||
let source = source.one_instance().instance;
|
||||
let source = source.one_instance_ref().instance;
|
||||
|
||||
let mut result = VectorDataTable::new(bevel_algorithm(source.clone(), source_transform, distance));
|
||||
*result.transform_mut() = source_transform;
|
||||
|
@ -1709,7 +1727,7 @@ fn bevel(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length)
|
|||
#[node_macro::node(name("Merge by Distance"), category("Vector"), path(graphene_core::vector))]
|
||||
fn merge_by_distance(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable {
|
||||
let source_transform = source.transform();
|
||||
let mut source = source.one_instance().instance.clone();
|
||||
let mut source = source.one_instance_ref().instance.clone();
|
||||
|
||||
source.merge_by_distance(distance);
|
||||
|
||||
|
@ -1725,7 +1743,7 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<
|
|||
let vector_data = vector_data.eval(new_ctx).await;
|
||||
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
let mut area = 0.;
|
||||
let scale = vector_data_transform.decompose_scale();
|
||||
|
@ -1742,7 +1760,7 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl N
|
|||
let vector_data = vector_data.eval(new_ctx).await;
|
||||
|
||||
let vector_data_transform = vector_data.transform();
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
if centroid_type == CentroidType::Area {
|
||||
let mut area = 0.;
|
||||
|
@ -1814,7 +1832,7 @@ mod test {
|
|||
let instances = 3;
|
||||
let repeated = super::repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
||||
let vector_data = super::flatten_vector_elements(Footprint::default(), repeated).await;
|
||||
let vector_data = vector_data.instances().next().unwrap().instance;
|
||||
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(vector_data.region_bezier_paths().count(), 3);
|
||||
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
||||
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
||||
|
@ -1826,7 +1844,7 @@ mod test {
|
|||
let instances = 8;
|
||||
let repeated = super::repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
||||
let vector_data = super::flatten_vector_elements(Footprint::default(), repeated).await;
|
||||
let vector_data = vector_data.instances().next().unwrap().instance;
|
||||
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
||||
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
||||
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
||||
|
@ -1836,7 +1854,7 @@ mod test {
|
|||
async fn circular_repeat() {
|
||||
let repeated = super::circular_repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)), 45., 4., 8).await;
|
||||
let vector_data = super::flatten_vector_elements(Footprint::default(), repeated).await;
|
||||
let vector_data = vector_data.instances().next().unwrap().instance;
|
||||
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
||||
|
||||
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
||||
|
@ -1851,7 +1869,7 @@ mod test {
|
|||
#[tokio::test]
|
||||
async fn bounding_box() {
|
||||
let bounding_box = super::bounding_box((), vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE))).await;
|
||||
let bounding_box = bounding_box.instances().next().unwrap().instance;
|
||||
let bounding_box = bounding_box.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(bounding_box.region_bezier_paths().count(), 1);
|
||||
let subpath = bounding_box.region_bezier_paths().next().unwrap().1;
|
||||
assert_eq!(&subpath.anchors()[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]);
|
||||
|
@ -1865,7 +1883,7 @@ mod test {
|
|||
}
|
||||
.eval(Footprint::default())
|
||||
.await;
|
||||
let bounding_box = bounding_box.instances().next().unwrap().instance;
|
||||
let bounding_box = bounding_box.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(bounding_box.region_bezier_paths().count(), 1);
|
||||
let subpath = bounding_box.region_bezier_paths().next().unwrap().1;
|
||||
let expected_bounding_box = [DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.)];
|
||||
|
@ -1882,7 +1900,7 @@ mod test {
|
|||
|
||||
let copy_to_points = super::copy_to_points(Footprint::default(), vector_node(points), vector_node(instance), 1., 1., 0., 0, 0., 0).await;
|
||||
let flatten_vector_elements = super::flatten_vector_elements(Footprint::default(), copy_to_points).await;
|
||||
let flattened_copy_to_points = flatten_vector_elements.instances().next().unwrap().instance;
|
||||
let flattened_copy_to_points = flatten_vector_elements.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
assert_eq!(flattened_copy_to_points.region_bezier_paths().count(), expected_points.len());
|
||||
|
||||
|
@ -1898,7 +1916,7 @@ mod test {
|
|||
async fn sample_points() {
|
||||
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
||||
let sample_points = super::sample_points(Footprint::default(), vector_node(path), 30., 0., 0., false, vec![100.]).await;
|
||||
let sample_points = sample_points.instances().next().unwrap().instance;
|
||||
let sample_points = sample_points.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(sample_points.point_domain.positions().len(), 4);
|
||||
for (pos, expected) in sample_points.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
||||
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
||||
|
@ -1908,7 +1926,7 @@ mod test {
|
|||
async fn adaptive_spacing() {
|
||||
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
||||
let sample_points = super::sample_points(Footprint::default(), vector_node(path), 18., 45., 10., true, vec![100.]).await;
|
||||
let sample_points = sample_points.instances().next().unwrap().instance;
|
||||
let sample_points = sample_points.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(sample_points.point_domain.positions().len(), 4);
|
||||
for (pos, expected) in sample_points.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
||||
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
||||
|
@ -1923,7 +1941,7 @@ mod test {
|
|||
0,
|
||||
)
|
||||
.await;
|
||||
let sample_points = sample_points.instances().next().unwrap().instance;
|
||||
let sample_points = sample_points.instance_ref_iter().next().unwrap().instance;
|
||||
assert!(
|
||||
(20..=40).contains(&sample_points.point_domain.positions().len()),
|
||||
"actual len {}",
|
||||
|
@ -1942,7 +1960,7 @@ mod test {
|
|||
#[tokio::test]
|
||||
async fn spline() {
|
||||
let spline = super::spline(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.))).await;
|
||||
let spline = spline.instances().next().unwrap().instance;
|
||||
let spline = spline.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(spline.stroke_bezier_paths().count(), 1);
|
||||
assert_eq!(spline.point_domain.positions(), &[DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]);
|
||||
}
|
||||
|
@ -1951,7 +1969,7 @@ mod test {
|
|||
let source = Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.);
|
||||
let target = Subpath::new_ellipse(DVec2::NEG_ONE * 100., DVec2::ZERO);
|
||||
let sample_points = super::morph(Footprint::default(), vector_node(source), vector_node(target), 0.5, 0).await;
|
||||
let sample_points = sample_points.instances().next().unwrap().instance;
|
||||
let sample_points = sample_points.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(
|
||||
&sample_points.point_domain.positions()[..4],
|
||||
vec![DVec2::new(-25., -50.), DVec2::new(50., -25.), DVec2::new(25., 50.), DVec2::new(-50., 25.)]
|
||||
|
@ -1974,7 +1992,7 @@ mod test {
|
|||
async fn bevel_rect() {
|
||||
let source = Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.);
|
||||
let beveled = super::bevel(Footprint::default(), vector_node(source), 5.);
|
||||
let beveled = beveled.instances().next().unwrap().instance;
|
||||
let beveled = beveled.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 8);
|
||||
assert_eq!(beveled.segment_domain.ids().len(), 8);
|
||||
|
@ -1997,7 +2015,7 @@ mod test {
|
|||
let curve = Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::new(10., 0.), DVec2::new(10., 100.), DVec2::X * 100.);
|
||||
let source = Subpath::from_beziers(&[Bezier::from_linear_dvec2(DVec2::X * -100., DVec2::ZERO), curve], false);
|
||||
let beveled = super::bevel((), vector_node(source), 5.);
|
||||
let beveled = beveled.instances().next().unwrap().instance;
|
||||
let beveled = beveled.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
||||
|
@ -2021,7 +2039,7 @@ mod test {
|
|||
*vector_data_table.one_instance_mut().transform = DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.));
|
||||
|
||||
let beveled = super::bevel((), VectorDataTable::new(vector_data), 5.);
|
||||
let beveled = beveled.instances().next().unwrap().instance;
|
||||
let beveled = beveled.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
||||
|
@ -2039,7 +2057,7 @@ mod test {
|
|||
async fn bevel_too_high() {
|
||||
let source = Subpath::from_anchors([DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)], false);
|
||||
let beveled = super::bevel(Footprint::default(), vector_node(source), 999.);
|
||||
let beveled = beveled.instances().next().unwrap().instance;
|
||||
let beveled = beveled.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
||||
|
@ -2060,7 +2078,7 @@ mod test {
|
|||
let point = Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::ZERO, DVec2::ZERO);
|
||||
let source = Subpath::from_beziers(&[Bezier::from_linear_dvec2(DVec2::X * -100., DVec2::ZERO), point, curve], false);
|
||||
let beveled = super::bevel(Footprint::default(), vector_node(source), 5.);
|
||||
let beveled = beveled.instances().next().unwrap().instance;
|
||||
let beveled = beveled.instance_ref_iter().next().unwrap().instance;
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
||||
|
|
|
@ -15,7 +15,7 @@ use graphene_core::{Ctx, GraphicElement, Node};
|
|||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
fn vector_points(_: impl Ctx, vector_data: VectorDataTable) -> Vec<DVec2> {
|
||||
let vector_data = vector_data.one_instance().instance;
|
||||
let vector_data = vector_data.one_instance_ref().instance;
|
||||
|
||||
vector_data.point_domain.positions().to_vec()
|
||||
}
|
||||
|
@ -96,8 +96,8 @@ where
|
|||
return target;
|
||||
}
|
||||
|
||||
let target_width = target.one_instance().instance.width;
|
||||
let target_height = target.one_instance().instance.height;
|
||||
let target_width = target.one_instance_ref().instance.width;
|
||||
let target_height = target.one_instance_ref().instance.height;
|
||||
let target_size = DVec2::new(target_width as f64, target_height as f64);
|
||||
|
||||
let texture_size = DVec2::new(texture.width as f64, texture.height as f64);
|
||||
|
@ -122,7 +122,7 @@ where
|
|||
let max_y = (blit_area_offset.y + blit_area_dimensions.y).saturating_sub(1);
|
||||
let max_x = (blit_area_offset.x + blit_area_dimensions.x).saturating_sub(1);
|
||||
assert!(texture_index(max_x, max_y) < texture.data.len());
|
||||
assert!(target_index(max_x, max_y) < target.one_instance().instance.data.len());
|
||||
assert!(target_index(max_x, max_y) < target.one_instance_ref().instance.data.len());
|
||||
|
||||
for y in blit_area_offset.y..blit_area_offset.y + blit_area_dimensions.y {
|
||||
for x in blit_area_offset.x..blit_area_offset.x + blit_area_dimensions.x {
|
||||
|
@ -143,7 +143,7 @@ pub async fn create_brush_texture(brush_style: &BrushStyle) -> Image<Color> {
|
|||
let blank_texture = empty_image((), transform, Color::TRANSPARENT);
|
||||
let image = crate::raster::blend_image_closure(stamp, blank_texture, |a, b| blend_colors(a, b, BlendMode::Normal, 1.));
|
||||
|
||||
image.one_instance().instance.clone()
|
||||
image.one_instance_ref().instance.clone()
|
||||
}
|
||||
|
||||
macro_rules! inline_blend_funcs {
|
||||
|
@ -220,7 +220,7 @@ async fn brush(_: impl Ctx, image_frame_table: ImageFrameTable<Color>, bounds: I
|
|||
let mut background_bounds = bbox.to_transform();
|
||||
|
||||
// If the bounds are empty (no size on images or det(transform) = 0), keep the target bounds
|
||||
let bounds_empty = bounds.instances().all(|bounds| bounds.instance.width() == 0 || bounds.instance.height() == 0);
|
||||
let bounds_empty = bounds.instance_ref_iter().all(|bounds| bounds.instance.width() == 0 || bounds.instance.height() == 0);
|
||||
if bounds.transform().matrix2.determinant() != 0. && !bounds_empty {
|
||||
background_bounds = bounds.transform();
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ use std::cmp::{max, min};
|
|||
#[node_macro::node(category("Raster"))]
|
||||
async fn dehaze(_: impl Ctx, image_frame: ImageFrameTable<Color>, strength: Percentage) -> ImageFrameTable<Color> {
|
||||
let image_frame_transform = image_frame.transform();
|
||||
let image_frame_alpha_blending = image_frame.one_instance().alpha_blending;
|
||||
let image_frame_alpha_blending = image_frame.one_instance_ref().alpha_blending;
|
||||
|
||||
let image = image_frame.one_instance().instance;
|
||||
let image = image_frame.one_instance_ref().instance;
|
||||
|
||||
// Prepare the image data for processing
|
||||
let image_data = bytemuck::cast_vec(image.data.clone());
|
||||
|
|
|
@ -62,7 +62,7 @@ impl Clone for ComputePass {
|
|||
#[node_macro::old_node_impl(MapGpuNode)]
|
||||
async fn map_gpu<'a: 'input>(image: ImageFrameTable<Color>, node: DocumentNode, editor_api: &'a graphene_core::application_io::EditorApi<WasmApplicationIo>) -> ImageFrameTable<Color> {
|
||||
let image_frame_table = ℑ
|
||||
let image = image.one_instance().instance;
|
||||
let image = image.one_instance_ref().instance;
|
||||
|
||||
log::debug!("Executing gpu node");
|
||||
let executor = &editor_api.application_io.as_ref().and_then(|io| io.gpu_executor()).unwrap();
|
||||
|
@ -113,7 +113,7 @@ async fn map_gpu<'a: 'input>(image: ImageFrameTable<Color>, node: DocumentNode,
|
|||
};
|
||||
let mut result = ImageFrameTable::new(new_image);
|
||||
*result.transform_mut() = image_frame_table.transform();
|
||||
*result.one_instance_mut().alpha_blending = *image_frame_table.one_instance().alpha_blending;
|
||||
*result.one_instance_mut().alpha_blending = *image_frame_table.one_instance_ref().alpha_blending;
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ where
|
|||
GraphicElement: From<Image<T>>,
|
||||
T::Static: Pixel,
|
||||
{
|
||||
let image = image.one_instance().instance;
|
||||
let image = image.one_instance_ref().instance;
|
||||
|
||||
let compiler = graph_craft::graphene_compiler::Compiler {};
|
||||
let inner_network = NodeNetwork::value_network(node);
|
||||
|
@ -278,10 +278,10 @@ async fn blend_gpu_image(_: impl Ctx, foreground: ImageFrameTable<Color>, backgr
|
|||
let foreground_transform = foreground.transform();
|
||||
let background_transform = background.transform();
|
||||
|
||||
let background_alpha_blending = background.one_instance().alpha_blending;
|
||||
let background_alpha_blending = background.one_instance_ref().alpha_blending;
|
||||
|
||||
let foreground = foreground.one_instance().instance;
|
||||
let background = background.one_instance().instance;
|
||||
let foreground = foreground.one_instance_ref().instance;
|
||||
let background = background.one_instance_ref().instance;
|
||||
|
||||
let foreground_size = DVec2::new(foreground.width as f64, foreground.height as f64);
|
||||
let background_size = DVec2::new(background.width as f64, background.height as f64);
|
||||
|
|
|
@ -16,7 +16,7 @@ async fn image_color_palette(
|
|||
let mut histogram: Vec<usize> = vec![0; (bins + 1.) as usize];
|
||||
let mut colors: Vec<Vec<Color>> = vec![vec![]; (bins + 1.) as usize];
|
||||
|
||||
let image = image.one_instance().instance;
|
||||
let image = image.one_instance_ref().instance;
|
||||
|
||||
for pixel in image.data.iter() {
|
||||
let r = pixel.r() * GRID;
|
||||
|
|
|
@ -29,9 +29,9 @@ impl From<std::io::Error> for Error {
|
|||
#[node_macro::node(category("Debug: Raster"))]
|
||||
fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFrameTable<Color>) -> ImageFrameTable<Color> {
|
||||
let image_frame_transform = image_frame.transform();
|
||||
let image_frame_alpha_blending = image_frame.one_instance().alpha_blending;
|
||||
let image_frame_alpha_blending = image_frame.one_instance_ref().alpha_blending;
|
||||
|
||||
let image = image_frame.one_instance().instance;
|
||||
let image = image_frame.one_instance_ref().instance;
|
||||
|
||||
// Resize the image using the image crate
|
||||
let data = bytemuck::cast_vec(image.data.clone());
|
||||
|
@ -325,7 +325,7 @@ fn extend_image_to_bounds(image: ImageFrameTable<Color>, bounds: DAffine2) -> Im
|
|||
return image;
|
||||
}
|
||||
|
||||
let image_instance = image.one_instance().instance;
|
||||
let image_instance = image.one_instance_ref().instance;
|
||||
if image_instance.width == 0 || image_instance.height == 0 {
|
||||
return empty_image((), bounds, Color::TRANSPARENT);
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ fn extend_image_to_bounds(image: ImageFrameTable<Color>, bounds: DAffine2) -> Im
|
|||
|
||||
let mut result = ImageFrameTable::new(new_img);
|
||||
*result.transform_mut() = new_texture_to_layer_space;
|
||||
*result.one_instance_mut().alpha_blending = *image.one_instance().alpha_blending;
|
||||
*result.one_instance_mut().alpha_blending = *image.one_instance_ref().alpha_blending;
|
||||
|
||||
result
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@ use std::ops::Mul;
|
|||
async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, operation: BooleanOperation) -> VectorDataTable {
|
||||
fn flatten_vector_data(graphic_group_table: &GraphicGroupTable) -> Vec<VectorDataTable> {
|
||||
graphic_group_table
|
||||
.instances()
|
||||
.instance_ref_iter()
|
||||
.map(|element| match element.instance.clone() {
|
||||
GraphicElement::VectorData(mut vector_data) => {
|
||||
// Apply the parent group's transform to each element of vector data
|
||||
for sub_vector_data in vector_data.instances_mut() {
|
||||
for sub_vector_data in vector_data.instance_mut_iter() {
|
||||
*sub_vector_data.transform = *element.transform * *sub_vector_data.transform;
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,12 @@ async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, opera
|
|||
// Apply the parent group's transform to each element of raster data
|
||||
match &mut image {
|
||||
graphene_core::RasterFrame::ImageFrame(image) => {
|
||||
for instance in image.instances_mut() {
|
||||
for instance in image.instance_mut_iter() {
|
||||
*instance.transform = *element.transform * *instance.transform;
|
||||
}
|
||||
}
|
||||
graphene_core::RasterFrame::TextureFrame(image) => {
|
||||
for instance in image.instances_mut() {
|
||||
for instance in image.instance_mut_iter() {
|
||||
*instance.transform = *element.transform * *instance.transform;
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, opera
|
|||
}
|
||||
GraphicElement::GraphicGroup(mut graphic_group) => {
|
||||
// Apply the parent group's transform to each element of inner group
|
||||
for sub_element in graphic_group.instances_mut() {
|
||||
for sub_element in graphic_group.instance_mut_iter() {
|
||||
*sub_element.transform = *element.transform * *sub_element.transform;
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, opera
|
|||
let result = result.one_instance_mut().instance;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance().instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
||||
|
@ -105,7 +105,7 @@ async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, opera
|
|||
let result_vector_data = result_vector_data_table.one_instance_mut().instance;
|
||||
|
||||
let upper_path_string = to_path(result_vector_data, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance().instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_union(upper_path_string, lower_path_string) };
|
||||
|
@ -136,7 +136,7 @@ async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, opera
|
|||
let result = result.one_instance_mut().instance;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance().instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
|
@ -160,12 +160,12 @@ async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, opera
|
|||
// Find where all vector data intersect at least once
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let all_other_vector_data = boolean_operation_on_vector_data(&vector_data_table.iter().filter(|v| v != &lower_vector_data).cloned().collect::<Vec<_>>(), BooleanOperation::Union);
|
||||
let all_other_vector_data_instance = all_other_vector_data.one_instance();
|
||||
let all_other_vector_data_instance = all_other_vector_data.one_instance_ref();
|
||||
|
||||
let transform_of_lower_into_space_of_upper = all_other_vector_data.transform().inverse() * lower_vector_data.transform();
|
||||
|
||||
let upper_path_string = to_path(all_other_vector_data_instance.instance, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance().instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_intersection_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
|
|
|
@ -187,7 +187,7 @@ where
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
for instance in data.instances_mut() {
|
||||
for instance in data.instance_mut_iter() {
|
||||
*instance.transform = DAffine2::from_translation(-aabb.start) * *instance.transform;
|
||||
}
|
||||
data.render_svg(&mut render, &render_params);
|
||||
|
|
|
@ -17,6 +17,7 @@ use graphene_std::GraphicElement;
|
|||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode};
|
||||
use graphene_std::application_io::ImageTexture;
|
||||
use graphene_std::wasm_application_io::*;
|
||||
use node_registry_macros::{async_node, into_node};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
@ -24,109 +25,27 @@ use std::sync::Arc;
|
|||
use wgpu_executor::{ShaderInputFrame, WgpuExecutor};
|
||||
use wgpu_executor::{WgpuSurface, WindowHandle};
|
||||
|
||||
macro_rules! async_node {
|
||||
// TODO: we currently need to annotate the type here because the compiler would otherwise (correctly)
|
||||
// TODO: assign a Pin<Box<dyn Future<Output=T>>> type to the node, which is not what we want for now.
|
||||
//
|
||||
// This `params` variant of the macro wraps the normal `fn_params` variant and is used as a shorthand for writing `T` instead of `() => T`
|
||||
($path:ty, input: $input:ty, params: [$($type:ty),*]) => {
|
||||
async_node!($path, input: $input, fn_params: [ $(() => $type),*])
|
||||
};
|
||||
($path:ty, input: $input:ty, fn_params: [$($arg:ty => $type:ty),*]) => {
|
||||
(
|
||||
ProtoNodeIdentifier::new(stringify!($path)),
|
||||
|mut args| {
|
||||
Box::pin(async move {
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::downcast_node::<$arg, $type>(args.pop().expect("Not enough arguments provided to construct node"))),*);
|
||||
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = <$path>::new($(
|
||||
graphene_std::any::PanicNode::<$arg, core::pin::Pin<Box<dyn core::future::Future<Output = $type> + Send>>>::new()
|
||||
),*);
|
||||
let params = vec![$(fn_type_fut!($arg, $type)),*];
|
||||
let mut node_io = NodeIO::<'_, $input>::to_async_node_io(&node, params);
|
||||
node_io.call_argument = concrete!(<$input as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! into_node {
|
||||
(from: $from:ty, to: $to:ty) => {
|
||||
(
|
||||
ProtoNodeIdentifier::new(concat!["graphene_core::ops::IntoNode<", stringify!($to), ">"]),
|
||||
|mut args| {
|
||||
Box::pin(async move {
|
||||
args.reverse();
|
||||
let node = graphene_core::ops::IntoNode::<$to>::new();
|
||||
let any: DynAnyNode<$from, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = graphene_core::ops::IntoNode::<$to>::new();
|
||||
let mut node_io = NodeIO::<'_, $from>::to_async_node_io(&node, vec![]);
|
||||
node_io.call_argument = future!(<$from as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: turn into hashmap
|
||||
fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
|
||||
let node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"),
|
||||
// |_| Box::pin(async move { FutureWrapperNode::new(IdentityNode::new()).into_type_erased() }),
|
||||
// NodeIOTypes::new(generic!(I), generic!(I), vec![]),
|
||||
// ),
|
||||
// async_node!(graphene_core::ops::IntoNode<ImageFrameTable<SRGBA8>>, input: ImageFrameTable<Color>, params: []),
|
||||
// async_node!(graphene_core::ops::IntoNode<ImageFrameTable<Color>>, input: ImageFrameTable<SRGBA8>, params: []),
|
||||
into_node!(from: f64, to: f64),
|
||||
into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable),
|
||||
into_node!(from: f64,to: f64),
|
||||
into_node!(from: u32,to: f64),
|
||||
into_node!(from: u8,to: u32),
|
||||
into_node!(from: ImageFrameTable<Color>,to: GraphicGroupTable),
|
||||
into_node!(from: VectorDataTable,to: GraphicGroupTable),
|
||||
#[cfg(feature = "gpu")]
|
||||
into_node!(from: &WasmEditorApi,to: &WgpuExecutor),
|
||||
into_node!(from: VectorDataTable,to: GraphicElement),
|
||||
into_node!(from: ImageFrameTable<Color>,to: GraphicElement),
|
||||
into_node!(from: GraphicGroupTable,to: GraphicElement),
|
||||
into_node!(from: VectorDataTable,to: GraphicGroupTable),
|
||||
into_node!(from: ImageFrameTable<Color>,to: GraphicGroupTable),
|
||||
into_node!(from: f64, to: f64),
|
||||
into_node!(from: u32, to: f64),
|
||||
into_node!(from: u8, to: u32),
|
||||
into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable),
|
||||
into_node!(from: VectorDataTable, to: GraphicGroupTable),
|
||||
into_node!(from: VectorDataTable, to: GraphicElement),
|
||||
into_node!(from: ImageFrameTable<Color>, to: GraphicElement),
|
||||
into_node!(from: GraphicGroupTable, to: GraphicElement),
|
||||
into_node!(from: VectorDataTable, to: GraphicGroupTable),
|
||||
into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageFrameTable<Color>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => GraphicElement]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Artboard]),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)),
|
||||
|args| {
|
||||
Box::pin(async move {
|
||||
let editor_api: DowncastBothNode<Context, &WasmEditorApi> = DowncastBothNode::new(args[0].clone());
|
||||
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(editor_api);
|
||||
let any: DynAnyNode<Context, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(graphene_std::any::PanicNode::<Context, dyn_any::DynFuture<'static, &WasmEditorApi>>::new());
|
||||
let params = vec![fn_type_fut!(Context, &WasmEditorApi)];
|
||||
let mut node_io = <wgpu_executor::CreateGpuSurfaceNode<_> as NodeIO<'_, Context>>::to_async_node_io(&node, params);
|
||||
node_io.call_argument = concrete!(<Context as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::RasterFrame]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::instances::Instances<Artboard>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => String]),
|
||||
|
@ -135,6 +54,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => bool]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => f64]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => u32]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => u64]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ()]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<f64>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BlendMode]),
|
||||
|
@ -149,40 +69,27 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<graphene_core::uuid::NodeId>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::Color]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Box<graphene_core::vector::VectorModification>]),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
ProtoNodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode"),
|
||||
|args| {
|
||||
Box::pin(async move {
|
||||
let document_node: DowncastBothNode<(), graph_craft::document::DocumentNode> = DowncastBothNode::new(args[0].clone());
|
||||
let editor_api: DowncastBothNode<(), &WasmEditorApi> = DowncastBothNode::new(args[1].clone());
|
||||
let node = graphene_std::gpu_nodes::MapGpuNode::new(document_node, editor_api);
|
||||
let any: DynAnyNode<ImageFrameTable<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
any.into_type_erased()
|
||||
})
|
||||
},
|
||||
NodeIOTypes::new(
|
||||
concrete!(ImageFrameTable<Color>),
|
||||
concrete!(ImageFrameTable<Color>),
|
||||
vec![fn_type!(graph_craft::document::DocumentNode), fn_type!(WasmEditorApi)],
|
||||
),
|
||||
),
|
||||
(
|
||||
ProtoNodeIdentifier::new("graphene_core::structural::ComposeNode"),
|
||||
|args| {
|
||||
Box::pin(async move {
|
||||
let node = ComposeTypeErased::new(args[0].clone(), args[1].clone());
|
||||
node.into_type_erased()
|
||||
})
|
||||
},
|
||||
// This is how we can generically define composition of two nodes.
|
||||
// See further details in the code definition for the `struct ComposeNode<First, Second, I> { ... }` struct.
|
||||
NodeIOTypes::new(
|
||||
generic!(T),
|
||||
generic!(U),
|
||||
vec![Type::Fn(Box::new(generic!(T)), Box::new(generic!(V))), Type::Fn(Box::new(generic!(V)), Box::new(generic!(U)))],
|
||||
),
|
||||
),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => ImageFrameTable<Color>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec<DVec2>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Arc<WasmSurfaceHandle>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WindowHandle]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option<wgpu_executor::WgpuSurface>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => wgpu_executor::WindowHandle]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::SurfaceFrame]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: UVec2, fn_params: [UVec2 => graphene_std::SurfaceFrame]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f64]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => String]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicElement]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => WgpuSurface]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
|
||||
// Filters
|
||||
// TODO: Move these filters to the new node macro and put them in `graphene_core::raster::adjustments`, then add them to the document upgrade script which moves many of the adjustment nodes from `graphene_core::raster` to `graphene_core::raster::adjustments`
|
||||
(
|
||||
|
@ -214,118 +121,77 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
},
|
||||
NodeIOTypes::new(concrete!(ImageFrameTable<Color>), concrete!(ImageFrameTable<Color>), vec![fn_type!(f64), fn_type!(f64), fn_type!(bool)]),
|
||||
),
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_core::raster::CurvesNode"),
|
||||
// |args| {
|
||||
// use graphene_core::raster::curve::Curve;
|
||||
// use graphene_core::raster::GenerateCurvesNode;
|
||||
// let curve: DowncastBothNode<(), Curve> = DowncastBothNode::new(args[0].clone());
|
||||
// Box::pin(async move {
|
||||
// let curve = ClonedNode::new(curve.eval(()).await);
|
||||
|
||||
// let generate_curves_node = GenerateCurvesNode::new(curve, ClonedNode::new(0_f32));
|
||||
// let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_curves_node.eval(())));
|
||||
// let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node);
|
||||
// let any: DynAnyNode<ImageFrameTable<Luma>, _, _> = graphene_std::any::DynAnyNode::new(map_image_frame_node);
|
||||
// any.into_type_erased()
|
||||
// })
|
||||
// },
|
||||
// NodeIOTypes::new(concrete!(ImageFrameTable<Luma>), concrete!(ImageFrameTable<Luma>), vec![fn_type!(graphene_core::raster::curve::Curve)]),
|
||||
// ),
|
||||
// TODO: Use channel split and merge for this instead of using LuminanceMut for the whole color.
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_core::raster::CurvesNode"),
|
||||
// |args| {
|
||||
// use graphene_core::raster::curve::Curve;
|
||||
// use graphene_core::raster::GenerateCurvesNode;
|
||||
// let curve: DowncastBothNode<(), Curve> = DowncastBothNode::new(args[0].clone());
|
||||
// Box::pin(async move {
|
||||
// let curve = ValueNode::new(ClonedNode::new(curve.eval(()).await));
|
||||
|
||||
// let generate_curves_node = GenerateCurvesNode::new(FutureWrapperNode::new(curve), FutureWrapperNode::new(ClonedNode::new(0_f32)));
|
||||
// let map_image_frame_node = graphene_std::raster::MapImageNode::new(FutureWrapperNode::new(ValueNode::new(generate_curves_node.eval(()))));
|
||||
// let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node);
|
||||
// let any: DynAnyNode<ImageFrameTable<Color>, _, _> = graphene_std::any::DynAnyNode::new(map_image_frame_node);
|
||||
// any.into_type_erased()
|
||||
// })
|
||||
// },
|
||||
// NodeIOTypes::new(
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// vec![fn_type!(graphene_core::raster::curve::Curve)],
|
||||
// ),
|
||||
// ),
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode"),
|
||||
// |args: Vec<graph_craft::proto::SharedNodeContainer>| {
|
||||
// Box::pin(async move {
|
||||
// use graphene_std::raster::ImaginateNode;
|
||||
// macro_rules! instantiate_imaginate_node {
|
||||
// ($($i:expr,)*) => { ImaginateNode::new($(graphene_std::any::input_node(args[$i].clone()),)* ) };
|
||||
// }
|
||||
// let node: ImaginateNode<Color, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _> = instantiate_imaginate_node!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,);
|
||||
// let any = graphene_std::any::DynAnyNode::new(node);
|
||||
// any.into_type_erased()
|
||||
// })
|
||||
// },
|
||||
// NodeIOTypes::new(
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// vec![
|
||||
// fn_type!(&WasmEditorApi),
|
||||
// fn_type!(ImaginateController),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(Option<DVec2>),
|
||||
// fn_type!(u32),
|
||||
// fn_type!(ImaginateSamplingMethod),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(String),
|
||||
// fn_type!(String),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(ImaginateMaskStartingFill),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(u64),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => ImageFrameTable<Color>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec<DVec2>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Arc<WasmSurfaceHandle>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WindowHandle]),
|
||||
(
|
||||
ProtoNodeIdentifier::new("graphene_core::structural::ComposeNode"),
|
||||
|args| {
|
||||
Box::pin(async move {
|
||||
let node = ComposeTypeErased::new(args[0].clone(), args[1].clone());
|
||||
node.into_type_erased()
|
||||
})
|
||||
},
|
||||
// This is how we can generically define composition of two nodes.
|
||||
// See further details in the code definition for the `struct ComposeNode<First, Second, I> { ... }` struct.
|
||||
NodeIOTypes::new(
|
||||
generic!(T),
|
||||
generic!(U),
|
||||
vec![Type::Fn(Box::new(generic!(T)), Box::new(generic!(V))), Type::Fn(Box::new(generic!(V)), Box::new(generic!(U)))],
|
||||
),
|
||||
),
|
||||
#[cfg(feature = "gpu")]
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => ShaderInputFrame]),
|
||||
#[cfg(feature = "gpu")]
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => wgpu_executor::WgpuSurface]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option<wgpu_executor::WgpuSurface>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => wgpu_executor::WindowHandle]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::SurfaceFrame]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: UVec2, fn_params: [UVec2 => graphene_std::SurfaceFrame]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f64]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => String]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicElement]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||
#[cfg(feature = "gpu")]
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => ShaderInputFrame]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => WgpuSurface]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
|
||||
#[cfg(feature = "gpu")]
|
||||
into_node!(from: &WasmEditorApi, to: &WgpuExecutor),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
ProtoNodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode"),
|
||||
|args| {
|
||||
Box::pin(async move {
|
||||
let document_node: DowncastBothNode<(), graph_craft::document::DocumentNode> = DowncastBothNode::new(args[0].clone());
|
||||
let editor_api: DowncastBothNode<(), &WasmEditorApi> = DowncastBothNode::new(args[1].clone());
|
||||
let node = graphene_std::gpu_nodes::MapGpuNode::new(document_node, editor_api);
|
||||
let any: DynAnyNode<ImageFrameTable<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
any.into_type_erased()
|
||||
})
|
||||
},
|
||||
NodeIOTypes::new(
|
||||
concrete!(ImageFrameTable<Color>),
|
||||
concrete!(ImageFrameTable<Color>),
|
||||
vec![fn_type!(graph_craft::document::DocumentNode), fn_type!(WasmEditorApi)],
|
||||
),
|
||||
),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)),
|
||||
|args| {
|
||||
Box::pin(async move {
|
||||
let editor_api: DowncastBothNode<Context, &WasmEditorApi> = DowncastBothNode::new(args[0].clone());
|
||||
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(editor_api);
|
||||
let any: DynAnyNode<Context, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(graphene_std::any::PanicNode::<Context, dyn_any::DynFuture<'static, &WasmEditorApi>>::new());
|
||||
let params = vec![fn_type_fut!(Context, &WasmEditorApi)];
|
||||
let mut node_io = <wgpu_executor::CreateGpuSurfaceNode<_> as NodeIO<'_, Context>>::to_async_node_io(&node, params);
|
||||
node_io.call_argument = concrete!(<Context as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
let mut map: HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
|
||||
|
||||
for (id, entry) in graphene_core::registry::NODE_REGISTRY.lock().unwrap().iter() {
|
||||
for (constructor, types) in entry.iter() {
|
||||
map.entry(id.clone().into()).or_default().insert(types.clone(), *constructor);
|
||||
}
|
||||
}
|
||||
|
||||
for (id, c, types) in node_types.into_iter() {
|
||||
// TODO: this is a hack to remove the newline from the node new_name
|
||||
// This occurs for the ChannelMixerNode presumably because of the long name.
|
||||
|
@ -340,12 +206,67 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
let nid = ProtoNodeIdentifier { name: Cow::Owned(new_name) };
|
||||
map.entry(nid).or_default().insert(types.clone(), c);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
pub static NODE_REGISTRY: Lazy<HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>> = Lazy::new(|| node_registry());
|
||||
|
||||
#[cfg(test)]
|
||||
mod protograph_testing {
|
||||
// TODO: add tests testing the node registry
|
||||
mod node_registry_macros {
|
||||
macro_rules! async_node {
|
||||
// TODO: we currently need to annotate the type here because the compiler would otherwise (correctly)
|
||||
// TODO: assign a Pin<Box<dyn Future<Output=T>>> type to the node, which is not what we want for now.
|
||||
//
|
||||
// This `params` variant of the macro wraps the normal `fn_params` variant and is used as a shorthand for writing `T` instead of `() => T`
|
||||
($path:ty, input: $input:ty, params: [$($type:ty),*]) => {
|
||||
async_node!($path, input: $input, fn_params: [ $(() => $type),*])
|
||||
};
|
||||
($path:ty, input: $input:ty, fn_params: [$($arg:ty => $type:ty),*]) => {
|
||||
(
|
||||
ProtoNodeIdentifier::new(stringify!($path)),
|
||||
|mut args| {
|
||||
Box::pin(async move {
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::downcast_node::<$arg, $type>(args.pop().expect("Not enough arguments provided to construct node"))),*);
|
||||
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = <$path>::new($(
|
||||
graphene_std::any::PanicNode::<$arg, core::pin::Pin<Box<dyn core::future::Future<Output = $type> + Send>>>::new()
|
||||
),*);
|
||||
let params = vec![$(fn_type_fut!($arg, $type)),*];
|
||||
let mut node_io = NodeIO::<'_, $input>::to_async_node_io(&node, params);
|
||||
node_io.call_argument = concrete!(<$input as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! into_node {
|
||||
(from: $from:ty, to: $to:ty) => {
|
||||
(
|
||||
ProtoNodeIdentifier::new(concat!["graphene_core::ops::IntoNode<", stringify!($to), ">"]),
|
||||
|mut args| {
|
||||
Box::pin(async move {
|
||||
args.reverse();
|
||||
let node = graphene_core::ops::IntoNode::<$to>::new();
|
||||
let any: DynAnyNode<$from, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = graphene_core::ops::IntoNode::<$to>::new();
|
||||
let mut node_io = NodeIO::<'_, $from>::to_async_node_io(&node, vec![]);
|
||||
node_io.call_argument = future!(<$from as StaticType>::Static);
|
||||
node_io
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use async_node;
|
||||
pub(crate) use into_node;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
//! This has all been copied out of node_registry.rs to avoid leaving many lines of commented out code in that file. It's left here instead for future reference.
|
||||
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_core::raster::CurvesNode"),
|
||||
// |args| {
|
||||
// use graphene_core::raster::curve::Curve;
|
||||
// use graphene_core::raster::GenerateCurvesNode;
|
||||
// let curve: DowncastBothNode<(), Curve> = DowncastBothNode::new(args[0].clone());
|
||||
// Box::pin(async move {
|
||||
// let curve = ClonedNode::new(curve.eval(()).await);
|
||||
|
||||
// let generate_curves_node = GenerateCurvesNode::new(curve, ClonedNode::new(0_f32));
|
||||
// let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_curves_node.eval(())));
|
||||
// let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node);
|
||||
// let any: DynAnyNode<ImageFrameTable<Luma>, _, _> = graphene_std::any::DynAnyNode::new(map_image_frame_node);
|
||||
// any.into_type_erased()
|
||||
// })
|
||||
// },
|
||||
// NodeIOTypes::new(concrete!(ImageFrameTable<Luma>), concrete!(ImageFrameTable<Luma>), vec![fn_type!(graphene_core::raster::curve::Curve)]),
|
||||
// ),
|
||||
// TODO: Use channel split and merge for this instead of using LuminanceMut for the whole color.
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_core::raster::CurvesNode"),
|
||||
// |args| {
|
||||
// use graphene_core::raster::curve::Curve;
|
||||
// use graphene_core::raster::GenerateCurvesNode;
|
||||
// let curve: DowncastBothNode<(), Curve> = DowncastBothNode::new(args[0].clone());
|
||||
// Box::pin(async move {
|
||||
// let curve = ValueNode::new(ClonedNode::new(curve.eval(()).await));
|
||||
|
||||
// let generate_curves_node = GenerateCurvesNode::new(FutureWrapperNode::new(curve), FutureWrapperNode::new(ClonedNode::new(0_f32)));
|
||||
// let map_image_frame_node = graphene_std::raster::MapImageNode::new(FutureWrapperNode::new(ValueNode::new(generate_curves_node.eval(()))));
|
||||
// let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node);
|
||||
// let any: DynAnyNode<ImageFrameTable<Color>, _, _> = graphene_std::any::DynAnyNode::new(map_image_frame_node);
|
||||
// any.into_type_erased()
|
||||
// })
|
||||
// },
|
||||
// NodeIOTypes::new(
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// vec![fn_type!(graphene_core::raster::curve::Curve)],
|
||||
// ),
|
||||
// ),
|
||||
// (
|
||||
// ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode"),
|
||||
// |args: Vec<graph_craft::proto::SharedNodeContainer>| {
|
||||
// Box::pin(async move {
|
||||
// use graphene_std::raster::ImaginateNode;
|
||||
// macro_rules! instantiate_imaginate_node {
|
||||
// ($($i:expr,)*) => { ImaginateNode::new($(graphene_std::any::input_node(args[$i].clone()),)* ) };
|
||||
// }
|
||||
// let node: ImaginateNode<Color, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _> = instantiate_imaginate_node!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,);
|
||||
// let any = graphene_std::any::DynAnyNode::new(node);
|
||||
// any.into_type_erased()
|
||||
// })
|
||||
// },
|
||||
// NodeIOTypes::new(
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// concrete!(ImageFrameTable<Color>),
|
||||
// vec![
|
||||
// fn_type!(&WasmEditorApi),
|
||||
// fn_type!(ImaginateController),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(Option<DVec2>),
|
||||
// fn_type!(u32),
|
||||
// fn_type!(ImaginateSamplingMethod),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(String),
|
||||
// fn_type!(String),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(f64),
|
||||
// fn_type!(ImaginateMaskStartingFill),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(bool),
|
||||
// fn_type!(u64),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
|
@ -913,7 +913,7 @@ async fn render_texture<'a: 'n>(
|
|||
async fn upload_texture<'a: 'n>(_: impl ExtractFootprint + Ctx, input: ImageFrameTable<Color>, executor: &'a WgpuExecutor) -> ImageTexture {
|
||||
// let new_data: Vec<RGBA16F> = input.image.data.into_iter().map(|c| c.into()).collect();
|
||||
|
||||
let input = input.one_instance().instance;
|
||||
let input = input.one_instance_ref().instance;
|
||||
let new_data: Vec<SRGBA8> = input.data.iter().map(|x| (*x).into()).collect();
|
||||
let new_image = Image {
|
||||
width: input.width,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue