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-10-17 11:02:07 -07:00
committed by Keavon Chambers
parent 239ca698e5
commit d82f133514
33 changed files with 836 additions and 305 deletions
+57 -3
View File
@@ -8,6 +8,8 @@ use crate::layers::style::RenderData;
use crate::{DocumentError, DocumentResponse, Operation};
use glam::{DAffine2, DVec2};
use graphene_core::transform::Footprint;
use graphene_std::wasm_application_io::WasmEditorApi;
use serde::{Deserialize, Serialize};
use std::cmp::max;
use std::collections::hash_map::DefaultHasher;
@@ -15,6 +17,10 @@ use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::vec;
use graph_craft::document::{DocumentNode, NodeOutput};
use graph_craft::document::{DocumentNodeImplementation, NodeId};
use graphene_core::{concrete, generic, NodeIdentifier};
/// A number that identifies a layer.
/// This does not technically need to be unique globally, only within a folder.
pub type LayerId = u64;
@@ -51,12 +57,54 @@ impl Default for Document {
let mut network = NodeNetwork::default();
let node = graph_craft::document::DocumentNode {
name: "Output".into(),
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true)],
implementation: graph_craft::document::DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true), NodeInput::Network(concrete!(WasmEditorApi))],
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
inputs: vec![3, 0],
outputs: vec![NodeOutput::new(4, 0)],
nodes: [
DocumentNode {
name: "EditorApi".to_string(),
inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
..Default::default()
},
DocumentNode {
name: "Create Canvas".to_string(),
inputs: vec![NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
name: "Cache".to_string(),
manual_composition: Some(concrete!(())),
inputs: vec![NodeInput::node(1, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
..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 {
name: "RenderNode".to_string(),
inputs: vec![NodeInput::node(0, 0), NodeInput::node(3, 0), NodeInput::node(2, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _>")),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
}),
metadata: graph_craft::document::DocumentNodeMetadata::position((8, 4)),
..Default::default()
};
network.push_node(node, false);
network.push_node(node);
network
},
metadata: Default::default(),
@@ -886,6 +934,12 @@ impl Document {
}
Some(Vec::new())
}
Operation::SetSvg { path, svg } => {
if let LayerDataType::Layer(layer) = &mut self.layer_mut(&path)?.data {
layer.cached_output_data = CachedOutputData::Svg(svg);
}
Some(Vec::new())
}
Operation::TransformLayerInScope { path, transform, scope } => {
let transform = DAffine2::from_cols_array(&transform);
let scope = DAffine2::from_cols_array(&scope);
+1 -1
View File
@@ -92,7 +92,7 @@ impl DocumentMetadata {
}
/// Find all of the layers that were clicked on from a viewport space location
pub fn click_xray<'a>(&'a self, viewport_location: DVec2) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
pub fn click_xray(&self, viewport_location: DVec2) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
let point = self.document_to_viewport.inverse().transform_point2(viewport_location);
self.root()
.decendants(self)
@@ -17,6 +17,7 @@ pub enum CachedOutputData {
BlobURL(String),
VectorPath(Box<VectorData>),
SurfaceId(SurfaceId),
Svg(String),
}
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
@@ -91,6 +92,7 @@ impl LayerData for LayerLayer {
id
);
}
CachedOutputData::Svg(new_svg) => svg.push_str(new_svg),
_ => {
// Render a dotted blue outline if there is no image or vector data
let _ = write!(
+4
View File
@@ -80,6 +80,10 @@ pub enum Operation {
path: Vec<LayerId>,
surface_id: graphene_core::SurfaceId,
},
SetSvg {
path: Vec<LayerId>,
svg: String,
},
TransformLayerInScope {
path: Vec<LayerId>,
transform: [f64; 6],