Improve previewing node data (#1446)
* Improve preview * Improve contrast * Restructure in order to duplicate code * Code review nits --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
c823016316
commit
e0ac073805
|
|
@ -60,7 +60,7 @@ impl Default for Document {
|
||||||
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true), NodeInput::Network(concrete!(WasmEditorApi))],
|
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true), NodeInput::Network(concrete!(WasmEditorApi))],
|
||||||
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
inputs: vec![3, 0],
|
inputs: vec![3, 0],
|
||||||
outputs: vec![NodeOutput::new(4, 0)],
|
outputs: vec![NodeOutput::new(3, 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "EditorApi".to_string(),
|
name: "EditorApi".to_string(),
|
||||||
|
|
@ -82,16 +82,14 @@ impl Default for Document {
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNode {
|
|
||||||
name: "Conversion".to_string(),
|
|
||||||
inputs: vec![NodeInput::Network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T))))],
|
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IntoNode<_, GraphicGroup>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "RenderNode".to_string(),
|
name: "RenderNode".to_string(),
|
||||||
inputs: vec![NodeInput::node(0, 0), NodeInput::node(3, 0), NodeInput::node(2, 0)],
|
inputs: vec![
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _>")),
|
NodeInput::node(0, 0),
|
||||||
|
NodeInput::Network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T)))),
|
||||||
|
NodeInput::node(2, 0),
|
||||||
|
],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _, _>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -290,7 +290,7 @@ impl core::fmt::Display for LayerNodeIdentifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayerNodeIdentifier {
|
impl LayerNodeIdentifier {
|
||||||
const ROOT: Self = LayerNodeIdentifier::new_unchecked(0);
|
pub const ROOT: Self = LayerNodeIdentifier::new_unchecked(0);
|
||||||
|
|
||||||
/// Construct a [`LayerNodeIdentifier`] without checking if it is a layer node
|
/// Construct a [`LayerNodeIdentifier`] without checking if it is a layer node
|
||||||
pub const fn new_unchecked(node_id: NodeId) -> Self {
|
pub const fn new_unchecked(node_id: NodeId) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView);
|
responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
}
|
}
|
||||||
FitViewportToSelection => {
|
FitViewportToSelection => {
|
||||||
if let Some(bounds) = selection_bounds {
|
if let Some(bounds) = selection_bounds {
|
||||||
|
|
@ -214,7 +214,7 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||||
}
|
}
|
||||||
SetCanvasRotation { angle_radians } => {
|
SetCanvasRotation { angle_radians } => {
|
||||||
self.tilt = angle_radians;
|
self.tilt = angle_radians;
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +224,7 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView);
|
responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
}
|
}
|
||||||
TransformCanvasEnd { abort_transform } => {
|
TransformCanvasEnd { abort_transform } => {
|
||||||
if abort_transform {
|
if abort_transform {
|
||||||
|
|
@ -235,12 +235,12 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||||
}
|
}
|
||||||
TransformOperation::Pan { pre_commit_pan, .. } => {
|
TransformOperation::Pan { pre_commit_pan, .. } => {
|
||||||
self.pan = pre_commit_pan;
|
self.pan = pre_commit_pan;
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
}
|
}
|
||||||
TransformOperation::Zoom { pre_commit_zoom, .. } => {
|
TransformOperation::Zoom { pre_commit_zoom, .. } => {
|
||||||
self.zoom = pre_commit_zoom;
|
self.zoom = pre_commit_zoom;
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +264,7 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||||
self.pan += transformed_delta;
|
self.pan += transformed_delta;
|
||||||
responses.add(BroadcastEvent::CanvasTransformed);
|
responses.add(BroadcastEvent::CanvasTransformed);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
}
|
}
|
||||||
TranslateCanvasBegin => {
|
TranslateCanvasBegin => {
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
||||||
|
|
@ -281,7 +281,7 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||||
|
|
||||||
self.pan += transformed_delta;
|
self.pan += transformed_delta;
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
self.create_document_transform(responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
||||||
}
|
}
|
||||||
WheelCanvasTranslate { use_y_as_x } => {
|
WheelCanvasTranslate { use_y_as_x } => {
|
||||||
let delta = match use_y_as_x {
|
let delta = match use_y_as_x {
|
||||||
|
|
@ -381,21 +381,21 @@ impl NavigationMessageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_offset_transform(&self, offset: DVec2) -> DAffine2 {
|
pub fn calculate_offset_transform(&self, viewport_center: DVec2) -> DAffine2 {
|
||||||
// Try to avoid fractional coordinates to reduce anti aliasing.
|
// Try to avoid fractional coordinates to reduce anti aliasing.
|
||||||
let scale = self.snapped_scale();
|
let scale = self.snapped_scale();
|
||||||
let rounded_pan = ((self.pan + offset) * scale).round() / scale - offset;
|
let rounded_pan = ((self.pan + viewport_center) * scale).round() / scale - viewport_center;
|
||||||
|
|
||||||
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
|
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
|
||||||
let offset_transform = DAffine2::from_translation(offset);
|
let offset_transform = DAffine2::from_translation(viewport_center);
|
||||||
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
|
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
|
||||||
let angle_transform = DAffine2::from_angle(self.snapped_angle());
|
let angle_transform = DAffine2::from_angle(self.snapped_angle());
|
||||||
let translation_transform = DAffine2::from_translation(rounded_pan);
|
let translation_transform = DAffine2::from_translation(rounded_pan);
|
||||||
scale_transform * offset_transform * angle_transform * translation_transform
|
scale_transform * offset_transform * angle_transform * offset_transform.inverse() * translation_transform
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_document_transform(&self, responses: &mut VecDeque<Message>) {
|
fn create_document_transform(&self, viewport_center: DVec2, responses: &mut VecDeque<Message>) {
|
||||||
let transform = self.calculate_offset_transform(DVec2::ZERO);
|
let transform = self.calculate_offset_transform(viewport_center);
|
||||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,11 @@ impl<'a> ModifyInputsContext<'a> {
|
||||||
|
|
||||||
// Update the document metadata structure
|
// Update the document metadata structure
|
||||||
if let Some(new_id) = new_id {
|
if let Some(new_id) = new_id {
|
||||||
let parent = LayerNodeIdentifier::new(output_node_id, self.network);
|
let parent = if self.network.nodes.get(&output_node_id).is_some_and(|node| node.name == "Layer") {
|
||||||
|
LayerNodeIdentifier::new(output_node_id, self.network)
|
||||||
|
} else {
|
||||||
|
LayerNodeIdentifier::ROOT
|
||||||
|
};
|
||||||
let new_child = LayerNodeIdentifier::new(new_id, self.network);
|
let new_child = LayerNodeIdentifier::new(new_id, self.network);
|
||||||
parent.push_front_child(self.document_metadata, new_child);
|
parent.push_front_child(self.document_metadata, new_child);
|
||||||
self.responses.add(DocumentMessage::DocumentStructureChanged);
|
self.responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
|
|
@ -562,25 +566,25 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewArtboard { id, artboard } => {
|
GraphOperationMessage::NewArtboard { id, artboard } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
||||||
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) {
|
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) {
|
||||||
modify_inputs.insert_artboard(artboard, layer);
|
modify_inputs.insert_artboard(artboard, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewBitmapLayer { id, image_frame } => {
|
GraphOperationMessage::NewBitmapLayer { id, image_frame } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
||||||
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) {
|
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) {
|
||||||
modify_inputs.insert_image_data(image_frame, layer);
|
modify_inputs.insert_image_data(image_frame, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewVectorLayer { id, subpaths } => {
|
GraphOperationMessage::NewVectorLayer { id, subpaths } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
||||||
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) {
|
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) {
|
||||||
modify_inputs.insert_vector_data(subpaths, layer);
|
modify_inputs.insert_vector_data(subpaths, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewTextLayer { id, text, font, size } => {
|
GraphOperationMessage::NewTextLayer { id, text, font, size } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
||||||
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) {
|
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) {
|
||||||
modify_inputs.insert_text(text, font, size, layer);
|
modify_inputs.insert_text(text, font, size, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -475,7 +475,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||||
DocumentNodeType {
|
DocumentNodeType {
|
||||||
name: "End Scope",
|
name: "End Scope",
|
||||||
category: "Ignore",
|
category: "Ignore",
|
||||||
identifier: NodeImplementation::proto("graphene_core::memo::EndLetNode<_>"),
|
identifier: NodeImplementation::proto("graphene_core::memo::EndLetNode<_, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Scope",
|
name: "Scope",
|
||||||
|
|
|
||||||
|
|
@ -546,7 +546,7 @@ fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Option<Color>
|
||||||
move |_: &IconButton| {
|
move |_: &IconButton| {
|
||||||
let mut new_positions = positions.clone();
|
let mut new_positions = positions.clone();
|
||||||
|
|
||||||
// Blend linearly between the two colours.
|
// Blend linearly between the two colors.
|
||||||
let get_color = |index: usize| match (new_positions[index].1, new_positions.get(index + 1).and_then(|x| x.1)) {
|
let get_color = |index: usize| match (new_positions[index].1, new_positions.get(index + 1).and_then(|x| x.1)) {
|
||||||
(Some(a), Some(b)) => Color::from_rgbaf32((a.r() + b.r()) / 2., (a.g() + b.g()) / 2., (a.b() + b.b()) / 2., ((a.a() + b.a()) / 2.).clamp(0., 1.)),
|
(Some(a), Some(b)) => Color::from_rgbaf32((a.r() + b.r()) / 2., (a.g() + b.g()) / 2., (a.b() + b.b()) / 2., ((a.a() + b.a()) / 2.).clamp(0., 1.)),
|
||||||
(Some(v), _) | (_, Some(v)) => Some(v),
|
(Some(v), _) | (_, Some(v)) => Some(v),
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ use document_legacy::layers::layer_info::{LayerDataType, LayerDataTypeDiscrimina
|
||||||
use document_legacy::{LayerId, Operation};
|
use document_legacy::{LayerId, Operation};
|
||||||
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
|
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput};
|
||||||
use graph_craft::graphene_compiler::Compiler;
|
use graph_craft::graphene_compiler::Compiler;
|
||||||
use graph_craft::imaginate_input::ImaginatePreferences;
|
use graph_craft::imaginate_input::ImaginatePreferences;
|
||||||
use graph_craft::{concrete, Type};
|
use graph_craft::{concrete, Type};
|
||||||
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
|
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
|
||||||
use graphene_core::raster::{Image, ImageFrame};
|
use graphene_core::raster::{Image, ImageFrame};
|
||||||
use graphene_core::renderer::{ClickTarget, SvgSegment, SvgSegmentList};
|
use graphene_core::renderer::{ClickTarget, GraphicElementRendered, SvgSegment, SvgSegmentList};
|
||||||
use graphene_core::text::FontCache;
|
use graphene_core::text::FontCache;
|
||||||
use graphene_core::transform::{Footprint, Transform};
|
use graphene_core::transform::{Footprint, Transform};
|
||||||
use graphene_core::vector::style::ViewMode;
|
use graphene_core::vector::style::ViewMode;
|
||||||
|
|
@ -82,6 +82,7 @@ pub(crate) struct GenerationResponse {
|
||||||
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
||||||
new_transforms: HashMap<LayerNodeIdentifier, DAffine2>,
|
new_transforms: HashMap<LayerNodeIdentifier, DAffine2>,
|
||||||
new_upstream_transforms: HashMap<NodeId, DAffine2>,
|
new_upstream_transforms: HashMap<NodeId, DAffine2>,
|
||||||
|
transform: DAffine2,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NodeGraphUpdate {
|
enum NodeGraphUpdate {
|
||||||
|
|
@ -160,6 +161,7 @@ impl NodeRuntime {
|
||||||
new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(),
|
new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(),
|
||||||
new_transforms: self.transforms.clone().into_iter().map(|(id, transform)| (LayerNodeIdentifier::new_unchecked(id), transform)).collect(),
|
new_transforms: self.transforms.clone().into_iter().map(|(id, transform)| (LayerNodeIdentifier::new_unchecked(id), transform)).collect(),
|
||||||
new_upstream_transforms: self.upstream_transforms.clone(),
|
new_upstream_transforms: self.upstream_transforms.clone(),
|
||||||
|
transform,
|
||||||
};
|
};
|
||||||
self.sender.send_generation_response(response);
|
self.sender.send_generation_response(response);
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +169,7 @@ impl NodeRuntime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_network<'a>(&'a mut self, path: &[LayerId], graph: NodeNetwork, transform: DAffine2, viewport_resolution: UVec2) -> (Result<TaggedValue, String>, MonitorNodes) {
|
async fn execute_network<'a>(&'a mut self, path: &[LayerId], mut graph: NodeNetwork, transform: DAffine2, viewport_resolution: UVec2) -> (Result<TaggedValue, String>, MonitorNodes) {
|
||||||
if self.wasm_io.is_none() {
|
if self.wasm_io.is_none() {
|
||||||
self.wasm_io = Some(WasmApplicationIo::new().await);
|
self.wasm_io = Some(WasmApplicationIo::new().await);
|
||||||
}
|
}
|
||||||
|
|
@ -512,6 +514,7 @@ impl NodeGraphExecutor {
|
||||||
new_click_targets,
|
new_click_targets,
|
||||||
new_transforms,
|
new_transforms,
|
||||||
new_upstream_transforms,
|
new_upstream_transforms,
|
||||||
|
transform,
|
||||||
}) => {
|
}) => {
|
||||||
self.thumbnails = new_thumbnails;
|
self.thumbnails = new_thumbnails;
|
||||||
document.metadata.update_transforms(new_transforms, new_upstream_transforms);
|
document.metadata.update_transforms(new_transforms, new_upstream_transforms);
|
||||||
|
|
@ -519,7 +522,7 @@ impl NodeGraphExecutor {
|
||||||
let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {e:?}"))?;
|
let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {e:?}"))?;
|
||||||
let execution_context = self.futures.remove(&generation_id).ok_or_else(|| "Invalid generation ID".to_string())?;
|
let execution_context = self.futures.remove(&generation_id).ok_or_else(|| "Invalid generation ID".to_string())?;
|
||||||
responses.extend(updates);
|
responses.extend(updates);
|
||||||
self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), responses, execution_context.document_id)?;
|
self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), transform, responses, execution_context.document_id)?;
|
||||||
responses.add(DocumentMessage::LayerChanged {
|
responses.add(DocumentMessage::LayerChanged {
|
||||||
affected_layer_path: execution_context.layer_path,
|
affected_layer_path: execution_context.layer_path,
|
||||||
});
|
});
|
||||||
|
|
@ -537,56 +540,40 @@ impl NodeGraphExecutor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>, document_id: u64) -> Result<(), String> {
|
fn render(render_object: impl GraphicElementRendered, transform: DAffine2, responses: &mut VecDeque<Message>) {
|
||||||
|
use graphene_core::renderer::{ImageRenderMode, RenderParams, SvgRender};
|
||||||
|
|
||||||
|
// Setup rendering
|
||||||
|
let mut render = SvgRender::new();
|
||||||
|
let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::BlobUrl, None, false);
|
||||||
|
|
||||||
|
// Render SVG
|
||||||
|
render_object.render_svg(&mut render, &render_params);
|
||||||
|
|
||||||
|
// Concatenate the defs and the SVG into one string
|
||||||
|
render.wrap_with_transform(transform);
|
||||||
|
let svg = render.svg.to_string();
|
||||||
|
|
||||||
|
// Send to frontend
|
||||||
|
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec<LayerId>, transform: DAffine2, responses: &mut VecDeque<Message>, document_id: u64) -> Result<(), String> {
|
||||||
self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty()));
|
self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty()));
|
||||||
match node_graph_output {
|
match node_graph_output {
|
||||||
TaggedValue::VectorData(vector_data) => {
|
|
||||||
// Update the cached vector data on the layer
|
|
||||||
let transform = vector_data.transform.to_cols_array();
|
|
||||||
responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform });
|
|
||||||
responses.add(Operation::SetVectorData { path: layer_path, vector_data });
|
|
||||||
}
|
|
||||||
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform }) => {
|
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform }) => {
|
||||||
let transform = transform.to_cols_array();
|
let transform = transform.to_cols_array();
|
||||||
responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform });
|
responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform });
|
||||||
responses.add(Operation::SetSurface { path: layer_path, surface_id });
|
responses.add(Operation::SetSurface { path: layer_path, surface_id });
|
||||||
}
|
}
|
||||||
TaggedValue::ImageFrame(ImageFrame { image, transform }) => {
|
|
||||||
// Don't update the frame's transform if the new transform is DAffine2::ZERO.
|
|
||||||
let transform = (!transform.abs_diff_eq(DAffine2::ZERO, f64::EPSILON)).then_some(transform.to_cols_array());
|
|
||||||
|
|
||||||
// If no image was generated, clear the frame
|
|
||||||
if image.width == 0 || image.height == 0 {
|
|
||||||
responses.add(DocumentMessage::FrameClear);
|
|
||||||
|
|
||||||
// Update the transform based on the graph output
|
|
||||||
if let Some(transform) = transform {
|
|
||||||
responses.add(Operation::SetLayerTransform { path: layer_path, transform });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Update the image data
|
|
||||||
let image_data = vec![Self::to_frontend_image_data(image, transform, &layer_path, None, None)?];
|
|
||||||
responses.add(FrontendMessage::UpdateImageData { document_id, image_data });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TaggedValue::Artboard(artboard) => {
|
|
||||||
warn!("Rendered graph produced artboard (which is not currently rendered): {artboard:#?}");
|
|
||||||
return Err("Artboard (see console)".to_string());
|
|
||||||
}
|
|
||||||
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::Svg(svg)) => {
|
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::Svg(svg)) => {
|
||||||
// Send to frontend
|
// Send to frontend
|
||||||
//log::debug!("svg: {svg}");
|
|
||||||
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
responses.add(DocumentMessage::RenderScrollbars);
|
||||||
//responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
|
||||||
|
|
||||||
//return Err("Graphic group (see console)".to_string());
|
|
||||||
}
|
}
|
||||||
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::CanvasFrame(frame)) => {
|
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::CanvasFrame(frame)) => {
|
||||||
// Send to frontend
|
// Send to frontend
|
||||||
//log::debug!("svg: {svg}");
|
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
responses.add(DocumentMessage::RenderScrollbars);
|
||||||
//responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
|
||||||
let matrix = frame
|
let matrix = frame
|
||||||
.transform
|
.transform
|
||||||
.to_cols_array()
|
.to_cols_array()
|
||||||
|
|
@ -600,30 +587,14 @@ impl NodeGraphExecutor {
|
||||||
1920, 1080, matrix, frame.surface_id.0
|
1920, 1080, matrix, frame.surface_id.0
|
||||||
);
|
);
|
||||||
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
||||||
|
|
||||||
//return Err("Graphic group (see console)".to_string());
|
|
||||||
}
|
|
||||||
TaggedValue::GraphicGroup(graphic_group) => {
|
|
||||||
use graphene_core::renderer::{GraphicElementRendered, RenderParams, SvgRender};
|
|
||||||
|
|
||||||
// Setup rendering
|
|
||||||
let mut render = SvgRender::new();
|
|
||||||
let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::BlobUrl, None, false);
|
|
||||||
|
|
||||||
// Render svg
|
|
||||||
graphic_group.render_svg(&mut render, &render_params);
|
|
||||||
|
|
||||||
// Conctenate the defs and the svg into one string
|
|
||||||
let mut svg = "<defs>".to_string();
|
|
||||||
svg.push_str(&render.svg_defs);
|
|
||||||
svg.push_str("</defs>");
|
|
||||||
use std::fmt::Write;
|
|
||||||
write!(svg, "{}", render.svg).unwrap();
|
|
||||||
|
|
||||||
// Send to frontend
|
|
||||||
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
|
||||||
}
|
}
|
||||||
|
TaggedValue::Bool(render_object) => Self::render(render_object, transform, responses),
|
||||||
|
TaggedValue::String(render_object) => Self::render(render_object, transform, responses),
|
||||||
|
TaggedValue::F32(render_object) => Self::render(render_object, transform, responses),
|
||||||
|
TaggedValue::F64(render_object) => Self::render(render_object, transform, responses),
|
||||||
|
TaggedValue::OptionalColor(render_object) => Self::render(render_object, transform, responses),
|
||||||
|
TaggedValue::VectorData(render_object) => Self::render(render_object, transform, responses),
|
||||||
|
TaggedValue::ImageFrame(render_object) => Self::render(render_object, transform, responses),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(format!("Invalid node graph output type: {node_graph_output:#?}"));
|
return Err(format!("Invalid node graph output type: {node_graph_output:#?}"));
|
||||||
}
|
}
|
||||||
|
|
@ -643,6 +614,6 @@ impl NodeGraphExecutor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
warn!("Recieved blob url for invalid segment")
|
warn!("Received blob url for invalid segment")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Purpose of Nodes
|
## Purpose of Nodes
|
||||||
|
|
||||||
Graphite is an image editor which is centred around a node based editing workflow, which allows operations to be visually connected in a graph. This is flexible as it allows all operations to be viewed or modified at any time without losing original data. The node system has been designed to be as general as possible with all data types being representable and a broad selection of nodes for a variety of use cases being planned.
|
Graphite is an image editor which is centered around a node based editing workflow, which allows operations to be visually connected in a graph. This is flexible as it allows all operations to be viewed or modified at any time without losing original data. The node system has been designed to be as general as possible with all data types being representable and a broad selection of nodes for a variety of use cases being planned.
|
||||||
|
|
||||||
## The Document Graph
|
## The Document Graph
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,18 @@ impl<'a, T> AsRef<EditorApi<'a, T>> for EditorApi<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Required for the EndLetNode
|
||||||
|
impl<'a, IO> From<EditorApi<'a, IO>> for Footprint {
|
||||||
|
fn from(value: EditorApi<'a, IO>) -> Self {
|
||||||
|
value.render_config.viewport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required for the EndLetNode
|
||||||
|
impl<'a, IO> From<EditorApi<'a, IO>> for () {
|
||||||
|
fn from(_value: EditorApi<'a, IO>) -> Self {}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct ExtractImageFrame;
|
pub struct ExtractImageFrame;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,15 @@ impl SvgRender {
|
||||||
self.svg.push("</svg>");
|
self.svg.push("</svg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wraps the SVG with `<svg><g transform="...">`, which allows for rotation
|
||||||
|
pub fn wrap_with_transform(&mut self, transform: DAffine2) {
|
||||||
|
let defs = &self.svg_defs;
|
||||||
|
|
||||||
|
let svg_header = format!(r#"<svg xmlns="http://www.w3.org/2000/svg"><defs>{defs}</defs><g transform="{}">"#, format_transform_matrix(transform));
|
||||||
|
self.svg.insert(0, svg_header.into());
|
||||||
|
self.svg.push("</g></svg>");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn leaf_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs)) {
|
pub fn leaf_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs)) {
|
||||||
self.indent();
|
self.indent();
|
||||||
self.svg.push("<");
|
self.svg.push("<");
|
||||||
|
|
@ -97,6 +106,11 @@ impl SvgRender {
|
||||||
self.svg.push("/>");
|
self.svg.push("/>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn leaf_node(&mut self, content: impl Into<SvgSegment>) {
|
||||||
|
self.indent();
|
||||||
|
self.svg.push(content);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parent_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs), inner: impl FnOnce(&mut Self)) {
|
pub fn parent_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs), inner: impl FnOnce(&mut Self)) {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
self.indent();
|
self.indent();
|
||||||
|
|
@ -359,6 +373,55 @@ impl GraphicElementRendered for GraphicElementData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to stop rust complaining about upstream traits adding display implementations to `Option<Color>`. This would not be an issue as we control that crate.
|
||||||
|
trait Primitive: core::fmt::Display {}
|
||||||
|
impl Primitive for String {}
|
||||||
|
impl Primitive for bool {}
|
||||||
|
impl Primitive for f32 {}
|
||||||
|
impl Primitive for f64 {}
|
||||||
|
|
||||||
|
fn text_attributes(attributes: &mut SvgRenderAttrs) {
|
||||||
|
attributes.push("fill", "white");
|
||||||
|
attributes.push("y", "30");
|
||||||
|
attributes.push("font-size", "30");
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Primitive> GraphicElementRendered for T {
|
||||||
|
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
|
||||||
|
render.parent_tag("text", text_attributes, |render| render.leaf_node(format!("{self}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphicElementRendered for Option<Color> {
|
||||||
|
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
|
||||||
|
let Some(color) = self else {
|
||||||
|
render.parent_tag("text", |_| {}, |render| render.leaf_node("Empty color"));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render.leaf_tag("rect", |attributes| {
|
||||||
|
attributes.push("width", "100");
|
||||||
|
attributes.push("height", "100");
|
||||||
|
attributes.push("y", "40");
|
||||||
|
attributes.push("fill", format!("#{}", color.rgba_hex()));
|
||||||
|
});
|
||||||
|
let color_info = format!("{:?} #{} {:?}", color, color.rgba_hex(), color.to_rgba8_srgb());
|
||||||
|
render.parent_tag("text", text_attributes, |render| render.leaf_node(color_info))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
|
||||||
|
}
|
||||||
|
|
||||||
/// A segment of an svg string to allow for embedding blob urls
|
/// A segment of an svg string to allow for embedding blob urls
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum SvgSegment {
|
pub enum SvgSegment {
|
||||||
|
|
|
||||||
|
|
@ -111,23 +111,24 @@ impl<T> LetNode<T> {
|
||||||
|
|
||||||
/// Caches the output of a given Node and acts as a proxy
|
/// Caches the output of a given Node and acts as a proxy
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
pub struct EndLetNode<Input> {
|
pub struct EndLetNode<Input, Parameter> {
|
||||||
input: Input,
|
input: Input,
|
||||||
|
paramenter: PhantomData<Parameter>,
|
||||||
}
|
}
|
||||||
impl<'i, T: 'i, Input> Node<'i, T> for EndLetNode<Input>
|
impl<'i, T: 'i, Parameter: 'i + From<T>, Input> Node<'i, T> for EndLetNode<Input, Parameter>
|
||||||
where
|
where
|
||||||
Input: Node<'i, ()>,
|
Input: Node<'i, Parameter>,
|
||||||
{
|
{
|
||||||
type Output = <Input>::Output;
|
type Output = <Input>::Output;
|
||||||
fn eval(&'i self, _: T) -> Self::Output {
|
fn eval(&'i self, t: T) -> Self::Output {
|
||||||
let result = self.input.eval(());
|
let result = self.input.eval(Parameter::from(t));
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Input> EndLetNode<Input> {
|
impl<Input, Parameter> EndLetNode<Input, Parameter> {
|
||||||
pub const fn new(input: Input) -> EndLetNode<Input> {
|
pub const fn new(input: Input) -> EndLetNode<Input, Parameter> {
|
||||||
EndLetNode { input }
|
EndLetNode { input, paramenter: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ pub struct GenerateBrightnessContrastMapperNode<Brightness, Contrast> {
|
||||||
contrast: Contrast,
|
contrast: Contrast,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Replace this node implementation with one that reuses the more generalized Curves adjustment node.
|
||||||
|
// TODO: It will be necessary to ensure the tests below are faithfully translated in a way that ensures identical results.
|
||||||
#[node_macro::node_fn(GenerateBrightnessContrastMapperNode)]
|
#[node_macro::node_fn(GenerateBrightnessContrastMapperNode)]
|
||||||
fn brightness_contrast_node(_primary: (), brightness: f32, contrast: f32) -> BrightnessContrastMapperNode {
|
fn brightness_contrast_node(_primary: (), brightness: f32, contrast: f32) -> BrightnessContrastMapperNode {
|
||||||
// Brightness LUT
|
// Brightness LUT
|
||||||
|
|
|
||||||
|
|
@ -127,12 +127,12 @@ impl Gradient {
|
||||||
|
|
||||||
// Compute the color of the inserted stop
|
// Compute the color of the inserted stop
|
||||||
let get_color = |index: usize, time: f64| match (self.positions[index].1, self.positions.get(index + 1).and_then(|x| x.1)) {
|
let get_color = |index: usize, time: f64| match (self.positions[index].1, self.positions.get(index + 1).and_then(|x| x.1)) {
|
||||||
// Lerp between the nearest colours if applicable
|
// Lerp between the nearest colors if applicable
|
||||||
(Some(a), Some(b)) => a.lerp(
|
(Some(a), Some(b)) => a.lerp(
|
||||||
b,
|
b,
|
||||||
((time - self.positions[index].0) / self.positions.get(index + 1).map(|end| end.0 - self.positions[index].0).unwrap_or_default()) as f32,
|
((time - self.positions[index].0) / self.positions.get(index + 1).map(|end| end.0 - self.positions[index].0).unwrap_or_default()) as f32,
|
||||||
),
|
),
|
||||||
// Use the start or the end colour if applicable
|
// Use the start or the end color if applicable
|
||||||
(Some(v), _) | (_, Some(v)) => v,
|
(Some(v), _) | (_, Some(v)) => v,
|
||||||
_ => Color::WHITE,
|
_ => Color::WHITE,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1019,18 +1019,18 @@ impl NodeNetwork {
|
||||||
// We filter out the newly inserted empty stack in case `resolve_empty_stacks` runs multiple times.
|
// We filter out the newly inserted empty stack in case `resolve_empty_stacks` runs multiple times.
|
||||||
for node in self.nodes.values_mut().filter(|node| node.name != EMPTY_STACK) {
|
for node in self.nodes.values_mut().filter(|node| node.name != EMPTY_STACK) {
|
||||||
for input in &mut node.inputs {
|
for input in &mut node.inputs {
|
||||||
if matches!(
|
if let NodeInput::Value {
|
||||||
input,
|
tagged_value: TaggedValue::GraphicGroup(graphic_group),
|
||||||
NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::GraphicGroup(GraphicGroup::EMPTY),
|
|
||||||
..
|
..
|
||||||
}
|
} = input
|
||||||
) {
|
{
|
||||||
|
if *graphic_group == GraphicGroup::EMPTY {
|
||||||
*input = NodeInput::node(new_id, 0);
|
*input = NodeInput::node(new_id, 0);
|
||||||
used = true;
|
used = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only insert the node if necessary.
|
// Only insert the node if necessary.
|
||||||
if used {
|
if used {
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
||||||
inner_network,
|
inner_network,
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "End Scope".to_string(),
|
name: "End Scope".to_string(),
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_>"),
|
implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_, _>"),
|
||||||
inputs: vec![NodeInput::node(0, 0), NodeInput::node(1, 0)],
|
inputs: vec![NodeInput::node(0, 0), NodeInput::node(1, 0)],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ graphene-core = { path = "../gcore", features = [
|
||||||
"alloc",
|
"alloc",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
dyn-any = { path = "../../libraries/dyn-any", features = ["derive"] }
|
dyn-any = { path = "../../libraries/dyn-any", features = ["derive"] }
|
||||||
graph-craft = { path = "../graph-craft" }
|
graph-craft = { path = "../graph-craft", features = ["serde"] }
|
||||||
vulkan-executor = { path = "../vulkan-executor", optional = true }
|
vulkan-executor = { path = "../vulkan-executor", optional = true }
|
||||||
wgpu-executor = { path = "../wgpu-executor", optional = true, version = "0.1" }
|
wgpu-executor = { path = "../wgpu-executor", optional = true, version = "0.1" }
|
||||||
gpu-executor = { path = "../gpu-executor", optional = true }
|
gpu-executor = { path = "../gpu-executor", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use graphene_core::{Color, GraphicGroup};
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use js_sys::{Object, Reflect};
|
use js_sys::{Object, Reflect};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
|
|
@ -288,35 +289,28 @@ fn decode_image_node<'a: 'input>(data: Arc<[u8]>) -> ImageFrame<Color> {
|
||||||
}
|
}
|
||||||
pub use graph_craft::document::value::RenderOutput;
|
pub use graph_craft::document::value::RenderOutput;
|
||||||
|
|
||||||
pub struct RenderNode<Data, Surface> {
|
pub struct RenderNode<Data, Surface, Parameter> {
|
||||||
data: Data,
|
data: Data,
|
||||||
surface_handle: Surface,
|
surface_handle: Surface,
|
||||||
|
parameter: PhantomData<Parameter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(RenderNode)]
|
fn render_svg(data: impl GraphicElementRendered, mut render: SvgRender, render_params: RenderParams, footprint: Footprint) -> RenderOutput {
|
||||||
async fn render_node<'a: 'input, F: Future<Output = GraphicGroup>>(
|
|
||||||
editor: WasmEditorApi<'a>,
|
|
||||||
data: impl Node<'input, Footprint, Output = F>,
|
|
||||||
surface_handle: Arc<SurfaceHandle<HtmlCanvasElement>>,
|
|
||||||
) -> RenderOutput {
|
|
||||||
let footprint = editor.render_config.viewport;
|
|
||||||
let data = self.data.eval(footprint).await;
|
|
||||||
let mut render = SvgRender::new();
|
|
||||||
let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false);
|
|
||||||
let output_format = editor.render_config.export_format;
|
|
||||||
let resolution = footprint.resolution;
|
|
||||||
|
|
||||||
match output_format {
|
|
||||||
ExportFormat::Svg => {
|
|
||||||
data.render_svg(&mut render, &render_params);
|
data.render_svg(&mut render, &render_params);
|
||||||
// TODO: reenable once we switch to full node graph
|
render.wrap_with_transform(footprint.transform);
|
||||||
let min = footprint.transform.inverse().transform_point2((0., 0.).into());
|
|
||||||
let max = footprint.transform.inverse().transform_point2(resolution.as_dvec2());
|
|
||||||
render.format_svg(min, max);
|
|
||||||
RenderOutput::Svg(render.svg.to_string())
|
RenderOutput::Svg(render.svg.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "resvg", feature = "vello"))]
|
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||||
ExportFormat::Canvas => {
|
fn render_canvas(
|
||||||
|
data: impl GraphicElementRendered,
|
||||||
|
mut render: SvgRender,
|
||||||
|
render_params: RenderParams,
|
||||||
|
footprint: Footprint,
|
||||||
|
editor: WasmEditorApi<'_>,
|
||||||
|
surface_handle: Arc<SurfaceHandle<HtmlCanvasElement>>,
|
||||||
|
) -> RenderOutput {
|
||||||
|
let resolution = footprint.resolution;
|
||||||
data.render_svg(&mut render, &render_params);
|
data.render_svg(&mut render, &render_params);
|
||||||
// TODO: reenable once we switch to full node graph
|
// TODO: reenable once we switch to full node graph
|
||||||
let min = footprint.transform.inverse().transform_point2((0., 0.).into());
|
let min = footprint.transform.inverse().transform_point2((0., 0.).into());
|
||||||
|
|
@ -359,6 +353,68 @@ async fn render_node<'a: 'input, F: Future<Output = GraphicGroup>>(
|
||||||
};
|
};
|
||||||
RenderOutput::CanvasFrame(frame.into())
|
RenderOutput::CanvasFrame(frame.into())
|
||||||
}
|
}
|
||||||
_ => todo!("Non svg render output for {output_format:?}"),
|
|
||||||
|
// Render with the data node taking in Footprint.
|
||||||
|
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>>
|
||||||
|
for RenderNode<Data, Surface, Footprint>
|
||||||
|
where
|
||||||
|
Data: Node<'input, Footprint, Output = F>,
|
||||||
|
Surface: Node<'input, (), Output = SurfaceFuture>,
|
||||||
|
SurfaceFuture: core::future::Future<Output = Arc<SurfaceHandle<HtmlCanvasElement>>>,
|
||||||
|
{
|
||||||
|
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output {
|
||||||
|
Box::pin(async move {
|
||||||
|
let footprint = editor.render_config.viewport;
|
||||||
|
let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false);
|
||||||
|
|
||||||
|
let output_format = editor.render_config.export_format;
|
||||||
|
match output_format {
|
||||||
|
ExportFormat::Svg => render_svg(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint),
|
||||||
|
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||||
|
ExportFormat::Canvas => render_canvas(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint, editor, self.surface_handle.eval(()).await),
|
||||||
|
_ => todo!("Non-SVG render output for {output_format:?}"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render with the data node taking in ().
|
||||||
|
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>>
|
||||||
|
for RenderNode<Data, Surface, ()>
|
||||||
|
where
|
||||||
|
Data: Node<'input, (), Output = F>,
|
||||||
|
Surface: Node<'input, (), Output = SurfaceFuture>,
|
||||||
|
SurfaceFuture: core::future::Future<Output = Arc<SurfaceHandle<HtmlCanvasElement>>>,
|
||||||
|
{
|
||||||
|
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
|
||||||
|
#[inline]
|
||||||
|
fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output {
|
||||||
|
Box::pin(async move {
|
||||||
|
use graphene_core::renderer::ImageRenderMode;
|
||||||
|
|
||||||
|
let footprint = editor.render_config.viewport;
|
||||||
|
let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::Base64, None, false);
|
||||||
|
|
||||||
|
let output_format = editor.render_config.export_format;
|
||||||
|
match output_format {
|
||||||
|
ExportFormat::Svg => render_svg(self.data.eval(()).await, SvgRender::new(), render_params, footprint),
|
||||||
|
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||||
|
ExportFormat::Canvas => render_canvas(self.data.eval(()).await, SvgRender::new(), render_params, footprint, editor, self.surface_handle.eval(()).await),
|
||||||
|
_ => todo!("Non-SVG render output for {output_format:?}"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[automatically_derived]
|
||||||
|
impl<Data, Surface, Parameter> RenderNode<Data, Surface, Parameter> {
|
||||||
|
pub const fn new(data: Data, surface_handle: Surface) -> Self {
|
||||||
|
Self {
|
||||||
|
data,
|
||||||
|
surface_handle,
|
||||||
|
parameter: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use graphene_core::value::{ClonedNode, CopiedNode, ValueNode};
|
||||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||||
use graphene_core::vector::VectorData;
|
use graphene_core::vector::VectorData;
|
||||||
use graphene_core::{application_io::SurfaceHandle, SurfaceFrame, WasmSurfaceHandleFrame};
|
use graphene_core::{application_io::SurfaceHandle, SurfaceFrame, WasmSurfaceHandleFrame};
|
||||||
use graphene_core::{concrete, generic, GraphicGroup};
|
use graphene_core::{concrete, generic, Artboard, GraphicGroup};
|
||||||
use graphene_core::{fn_type, raster::*};
|
use graphene_core::{fn_type, raster::*};
|
||||||
use graphene_core::{Cow, NodeIdentifier, Type};
|
use graphene_core::{Cow, NodeIdentifier, Type};
|
||||||
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
||||||
|
|
@ -276,7 +276,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
||||||
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: ImageFrame<Color>, output: GraphicGroup, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: ImageFrame<Color>, output: GraphicGroup, params: []),
|
||||||
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: VectorData, output: GraphicGroup, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: VectorData, output: GraphicGroup, params: []),
|
||||||
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: GraphicGroup, output: GraphicGroup, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: GraphicGroup, output: GraphicGroup, params: []),
|
||||||
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: graphene_core::Artboard, output: GraphicGroup, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: Artboard, output: GraphicGroup, params: []),
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: WasmEditorApi, output: &WgpuExecutor, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: WasmEditorApi, output: &WgpuExecutor, params: []),
|
||||||
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||||
|
|
@ -546,28 +546,35 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
||||||
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f32, f32, f32]),
|
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f32, f32, f32]),
|
||||||
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
|
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
|
||||||
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
|
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
|
||||||
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||||
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: VectorData, params: [VectorData]),
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: VectorData, params: [VectorData]),
|
||||||
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: RenderOutput, params: [RenderOutput]),
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [RenderOutput]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [f32]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [f64]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [bool]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [String]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [Option<Color>]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData]),
|
||||||
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>]),
|
||||||
async_node!(
|
async_node!(
|
||||||
graphene_core::memo::EndLetNode<_>,
|
graphene_core::memo::EndLetNode<_, _>,
|
||||||
input: WasmEditorApi,
|
input: WasmEditorApi,
|
||||||
output: graphene_core::GraphicGroup,
|
output: GraphicGroup,
|
||||||
params: [graphene_core::GraphicGroup]
|
params: [GraphicGroup]
|
||||||
),
|
),
|
||||||
async_node!(
|
async_node!(
|
||||||
graphene_core::memo::EndLetNode<_>,
|
graphene_core::memo::EndLetNode<_, _>,
|
||||||
input: WasmEditorApi,
|
input: WasmEditorApi,
|
||||||
output: graphene_core::Artboard,
|
output: Artboard,
|
||||||
params: [graphene_core::Artboard]
|
params: [Artboard]
|
||||||
),
|
),
|
||||||
async_node!(
|
async_node!(
|
||||||
graphene_core::memo::EndLetNode<_>,
|
graphene_core::memo::EndLetNode<_, _>,
|
||||||
input: WasmEditorApi,
|
input: WasmEditorApi,
|
||||||
output: WasmSurfaceHandleFrame,
|
output: WasmSurfaceHandleFrame,
|
||||||
params: [WasmSurfaceHandleFrame]
|
params: [WasmSurfaceHandleFrame]
|
||||||
),
|
),
|
||||||
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: SurfaceFrame, params: [SurfaceFrame]),
|
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: SurfaceFrame, params: [SurfaceFrame]),
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
NodeIdentifier::new("graphene_core::memo::RefNode<_, _>"),
|
NodeIdentifier::new("graphene_core::memo::RefNode<_, _>"),
|
||||||
|
|
@ -639,7 +646,19 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
||||||
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
||||||
register_node!(graphene_core::quantization::DeQuantizeNode<_>, input: PackedPixel, params: [QuantizationChannels]),
|
register_node!(graphene_core::quantization::DeQuantizeNode<_>, input: PackedPixel, params: [QuantizationChannels]),
|
||||||
register_node!(graphene_core::ops::CloneNode<_>, input: &QuantizationChannels, params: []),
|
register_node!(graphene_core::ops::CloneNode<_>, input: &QuantizationChannels, params: []),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Artboard, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => ImageFrame<Color>, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => VectorData, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => Artboard, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => bool, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f32, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f64, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => String, () => Arc<WasmSurfaceHandle>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => Option<Color>, () => Arc<WasmSurfaceHandle>]),
|
||||||
//register_node!(graphene_core::transform::TranformNode<_, _, _, _, _, _>, input: , output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
|
//register_node!(graphene_core::transform::TranformNode<_, _, _, _, _, _>, input: , output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
|
|
@ -740,34 +759,32 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
||||||
args.reverse();
|
args.reverse();
|
||||||
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<VectorData>(args.pop().expect("Not enough arguments provided to construct node")));
|
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<VectorData>(args.pop().expect("Not enough arguments provided to construct node")));
|
||||||
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
|
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>)> + '_>
|
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>> + '_>
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let node = <graphene_core::transform::CullNode<_>>::new((graphene_std::any::PanicNode::<(), VectorData>::new()));
|
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::PanicNode::<(), VectorData>::new());
|
||||||
let params = vec![fn_type!((), VectorData)];
|
let params = vec![fn_type!((), VectorData)];
|
||||||
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
|
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
|
||||||
node_io.input = concrete!(<Footprint as StaticType>::Static);
|
node_io.input = concrete!(<Footprint as StaticType>::Static);
|
||||||
node_io
|
node_io
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [graphene_core::Artboard]),
|
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [Artboard]),
|
||||||
vec![(
|
vec![(
|
||||||
NodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
NodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
||||||
|args| {
|
|args| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut args = args.clone();
|
let mut args = args.clone();
|
||||||
args.reverse();
|
args.reverse();
|
||||||
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<graphene_core::GraphicGroup>(
|
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<GraphicGroup>(args.pop().expect("Not enough arguments provided to construct node")));
|
||||||
args.pop().expect("Not enough arguments provided to construct node"),
|
|
||||||
));
|
|
||||||
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
|
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>)> + '_>
|
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>> + '_>
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let node = <graphene_core::transform::CullNode<_>>::new((graphene_std::any::PanicNode::<(), graphene_core::GraphicGroup>::new()));
|
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::PanicNode::<(), GraphicGroup>::new());
|
||||||
let params = vec![fn_type!((), graphene_core::GraphicGroup)];
|
let params = vec![fn_type!((), GraphicGroup)];
|
||||||
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
|
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
|
||||||
node_io.input = concrete!(<Footprint as StaticType>::Static);
|
node_io.input = concrete!(<Footprint as StaticType>::Static);
|
||||||
node_io
|
node_io
|
||||||
|
|
@ -792,12 +809,12 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
||||||
register_node!(graphene_core::text::TextGenerator<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
|
register_node!(graphene_core::text::TextGenerator<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
|
||||||
register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []),
|
register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []),
|
||||||
register_node!(graphene_core::ExtractImageFrame, input: WasmEditorApi, params: []),
|
register_node!(graphene_core::ExtractImageFrame, input: WasmEditorApi, params: []),
|
||||||
async_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => graphene_core::GraphicElementData, () => String, () => BlendMode, () => f32, () => bool, () => bool, () => bool, Footprint => graphene_core::GraphicGroup]),
|
async_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => graphene_core::GraphicElementData, () => String, () => BlendMode, () => f32, () => bool, () => bool, () => bool, Footprint => GraphicGroup]),
|
||||||
register_node!(graphene_core::ToGraphicElementData, input: graphene_core::vector::VectorData, params: []),
|
register_node!(graphene_core::ToGraphicElementData, input: graphene_core::vector::VectorData, params: []),
|
||||||
register_node!(graphene_core::ToGraphicElementData, input: ImageFrame<Color>, params: []),
|
register_node!(graphene_core::ToGraphicElementData, input: ImageFrame<Color>, params: []),
|
||||||
register_node!(graphene_core::ToGraphicElementData, input: graphene_core::GraphicGroup, params: []),
|
register_node!(graphene_core::ToGraphicElementData, input: GraphicGroup, params: []),
|
||||||
register_node!(graphene_core::ToGraphicElementData, input: graphene_core::Artboard, params: []),
|
register_node!(graphene_core::ToGraphicElementData, input: Artboard, params: []),
|
||||||
register_node!(graphene_core::ConstructArtboardNode<_, _, _, _>, input: graphene_core::GraphicGroup, params: [glam::IVec2, glam::IVec2, Color, bool]),
|
register_node!(graphene_core::ConstructArtboardNode<_, _, _, _>, input: GraphicGroup, params: [glam::IVec2, glam::IVec2, Color, bool]),
|
||||||
];
|
];
|
||||||
let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
|
let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
|
||||||
for (id, c, types) in node_types.into_iter().flatten() {
|
for (id, c, types) in node_types.into_iter().flatten() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue