Lay groundwork for adaptive resolution system (#1395)

* Make transform node accept footprint as input and pass it along to its input

use f32 instead of f64 and add default to document node definition

* Add cull node

* Fix types for Transform and Cull Nodes

* Add render config struct

* Add Render Node skeleton

* Add Render Node to node_registry

* Make types macro use macro hygiene

* Place Render Node as output

* Start making DownresNode footprint aware

* Correctly calculate footprint in Transform Node

* Add cropping and resizing to downres node

* Fix Output node declaration

* Fix image transform

* Fix Vector Data rendering

* Add concept of ImageRenderMode

* Take base image size into account when calculating the final image size

* Supply viewport transform to the node graph

* Start adapting document graph to resolution agnosticism

* Make document node short circuting not shift the input index

* Apply clippy lints
This commit is contained in:
Dennis Kobert
2023-09-06 12:39:21 +02:00
committed by Keavon Chambers
parent 239ca698e5
commit d82f133514
33 changed files with 836 additions and 305 deletions

View File

@@ -13,12 +13,12 @@ use graph_craft::document::value::TaggedValue;
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
use graph_craft::graphene_compiler::Compiler;
use graph_craft::imaginate_input::ImaginatePreferences;
use graph_craft::{concrete, Type, TypeDescriptor};
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender};
use graph_craft::{concrete, Type};
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_core::raster::{Image, ImageFrame};
use graphene_core::renderer::{ClickTarget, SvgSegment, SvgSegmentList};
use graphene_core::text::FontCache;
use graphene_core::transform::Transform;
use graphene_core::transform::{Footprint, Transform};
use graphene_core::vector::style::ViewMode;
use graphene_core::{Color, SurfaceFrame, SurfaceId};
@@ -26,7 +26,6 @@ use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::DynamicExecutor;
use glam::{DAffine2, DVec2};
use std::borrow::Cow;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::mpsc::{Receiver, Sender};
@@ -72,6 +71,7 @@ pub(crate) struct GenerationRequest {
graph: NodeNetwork,
path: Vec<LayerId>,
image_frame: Option<ImageFrame<Color>>,
transform: DAffine2,
}
pub(crate) struct GenerationResponse {
@@ -140,6 +140,7 @@ impl NodeRuntime {
generation_id,
graph,
image_frame,
transform,
path,
..
}) => {
@@ -151,7 +152,7 @@ impl NodeRuntime {
.map(|node| node.path.clone().unwrap_or_default())
.collect();
let result = self.execute_network(&path, network, image_frame).await;
let result = self.execute_network(&path, network, image_frame, transform).await;
let mut responses = VecDeque::new();
self.update_thumbnails(&path, monitor_nodes, &mut responses);
let response = GenerationResponse {
@@ -168,7 +169,7 @@ impl NodeRuntime {
}
}
async fn execute_network<'a>(&'a mut self, path: &[LayerId], scoped_network: NodeNetwork, image_frame: Option<ImageFrame<Color>>) -> Result<TaggedValue, String> {
async fn execute_network<'a>(&'a mut self, path: &[LayerId], scoped_network: NodeNetwork, image_frame: Option<ImageFrame<Color>>, transform: DAffine2) -> Result<TaggedValue, String> {
if self.wasm_io.is_none() {
self.wasm_io = Some(WasmApplicationIo::new().await);
}
@@ -179,6 +180,10 @@ impl NodeRuntime {
application_io: self.wasm_io.as_ref().unwrap(),
node_graph_message_sender: &self.sender,
imaginate_preferences: &self.imaginate_preferences,
render_config: RenderConfig {
viewport: Footprint { transform, ..Default::default() },
..Default::default()
},
};
// We assume only one output
@@ -197,7 +202,8 @@ impl NodeRuntime {
let result = match self.executor.input_type() {
Some(t) if t == concrete!(WasmEditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()),
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
_ => Err("Invalid input type".to_string()),
Some(t) => Err(format!("Invalid input type {:?}", t)),
_ => Err("No input type".to_string()),
}?;
if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
@@ -231,7 +237,7 @@ impl NodeRuntime {
};
use graphene_core::renderer::*;
let bounds = graphic_element_data.bounding_box(DAffine2::IDENTITY);
let render_params = RenderParams::new(ViewMode::Normal, bounds, true);
let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::BlobUrl, bounds, true);
let mut render = SvgRender::new();
graphic_element_data.render_svg(&mut render, &render_params);
let [min, max] = bounds.unwrap_or_default();
@@ -339,13 +345,14 @@ impl Default for NodeGraphExecutor {
impl NodeGraphExecutor {
/// Execute the network by flattening it and creating a borrow stack.
fn queue_execution(&self, network: NodeNetwork, image_frame: Option<ImageFrame<Color>>, layer_path: Vec<LayerId>) -> u64 {
fn queue_execution(&self, network: NodeNetwork, image_frame: Option<ImageFrame<Color>>, layer_path: Vec<LayerId>, transform: DAffine2) -> u64 {
let generation_id = generate_uuid();
let request = GenerationRequest {
path: layer_path,
graph: network,
image_frame,
generation_id,
transform,
};
self.sender.send(NodeRuntimeMessage::GenerationRequest(request)).expect("Failed to send generation request");
@@ -452,9 +459,10 @@ impl NodeGraphExecutor {
// Construct the input image frame
let transform = DAffine2::IDENTITY;
let image_frame = ImageFrame { image, transform };
let document_transform = document.document_legacy.metadata.document_to_viewport;
// Execute the node graph
let generation_id = self.queue_execution(network, Some(image_frame), layer_path.clone());
let generation_id = self.queue_execution(network, Some(image_frame), layer_path.clone(), document_transform);
self.futures.insert(generation_id, ExecutionContext { layer_path, document_id });
@@ -533,12 +541,21 @@ impl NodeGraphExecutor {
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)) => {
// Send to frontend
log::debug!("svg: {svg}");
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
responses.add(DocumentMessage::RenderScrollbars);
//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, None, false);
let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::BlobUrl, None, false);
// Render svg
graphic_group.render_svg(&mut render, &render_params);