Skip to content

Commit b7fe38c

Browse files
TrueDoctor0HyperCubeKeavon
authored
Fix how transforms work with footprints and remove a redundant transforms field (#1484)
* Prune unused thumbnails in node graph executor * Fix transform downcasting failure for GraphicElementData * Remove more warnings * Revert upstream transform calculation change * Use footprint to calculate layer transforms * Fix artboards * Move artwork with artboard * Remove Keavon's warnings * Prevent misordered FrontendMessages failing to reach JS handlers --------- Co-authored-by: 0hypercube <[email protected]> Co-authored-by: Keavon Chambers <[email protected]>
1 parent 1093aab commit b7fe38c

File tree

14 files changed

+116
-112
lines changed

14 files changed

+116
-112
lines changed

demo-artwork/just-a-potted-cactus-v2.graphite

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

demo-artwork/valley-of-spires-v2.graphite

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

document-legacy/src/document_metadata.rs

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use glam::{DAffine2, DVec2};
22
use graphene_core::renderer::ClickTarget;
3+
use graphene_core::transform::Footprint;
34
use std::collections::{HashMap, HashSet};
45
use std::num::NonZeroU64;
56

@@ -9,8 +10,7 @@ use graphene_core::renderer::Quad;
910

1011
#[derive(Debug, Clone)]
1112
pub struct DocumentMetadata {
12-
transforms: HashMap<LayerNodeIdentifier, DAffine2>,
13-
upstream_transforms: HashMap<NodeId, DAffine2>,
13+
upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
1414
structure: HashMap<LayerNodeIdentifier, NodeRelations>,
1515
artboards: HashSet<LayerNodeIdentifier>,
1616
folders: HashSet<LayerNodeIdentifier>,
@@ -23,7 +23,6 @@ pub struct DocumentMetadata {
2323
impl Default for DocumentMetadata {
2424
fn default() -> Self {
2525
Self {
26-
transforms: HashMap::new(),
2726
upstream_transforms: HashMap::new(),
2827
click_targets: HashMap::new(),
2928
structure: HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]),
@@ -207,7 +206,6 @@ impl DocumentMetadata {
207206

208207
self.selected_nodes.retain(|node| graph.nodes.contains_key(node));
209208
self.upstream_transforms.retain(|node, _| graph.nodes.contains_key(node));
210-
self.transforms.retain(|layer, _| self.structure.contains_key(layer));
211209
self.click_targets.retain(|layer, _| self.structure.contains_key(layer));
212210
}
213211
}
@@ -222,38 +220,29 @@ fn sibling_below<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a
222220
// transforms
223221
impl DocumentMetadata {
224222
/// Update the cached transforms of the layers
225-
pub fn update_transforms(&mut self, mut new_transforms: HashMap<LayerNodeIdentifier, DAffine2>, new_upstream_transforms: HashMap<NodeId, DAffine2>) {
226-
let mut stack = vec![(LayerNodeIdentifier::ROOT, DAffine2::IDENTITY)];
227-
while let Some((layer, transform)) = stack.pop() {
228-
for child in layer.children(self) {
229-
let Some(new_transform) = new_transforms.get_mut(&child) else { continue };
230-
*new_transform = transform * *new_transform;
231-
232-
stack.push((child, *new_transform));
233-
}
234-
}
235-
self.transforms = new_transforms;
223+
pub fn update_transforms(&mut self, new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>) {
236224
self.upstream_transforms = new_upstream_transforms;
237225
}
238226

239227
/// Access the cached transformation to document space from layer space
240228
pub fn transform_to_document(&self, layer: LayerNodeIdentifier) -> DAffine2 {
241-
self.transforms.get(&layer).copied().unwrap_or_else(|| {
242-
warn!("Tried to access transform of bad layer {layer:?}");
243-
DAffine2::IDENTITY
244-
})
245-
}
246-
247-
pub fn local_transform(&self, layer: LayerNodeIdentifier) -> DAffine2 {
248-
self.transform_to_document(layer.parent(self).unwrap_or_default()).inverse() * self.transform_to_document(layer)
229+
self.document_to_viewport.inverse() * self.transform_to_viewport(layer)
249230
}
250231

251232
pub fn transform_to_viewport(&self, layer: LayerNodeIdentifier) -> DAffine2 {
252-
self.document_to_viewport * self.transform_to_document(layer)
233+
self.upstream_transforms
234+
.get(&layer.to_node())
235+
.copied()
236+
.map(|(footprint, transform)| footprint.transform * transform)
237+
.unwrap_or(DAffine2::IDENTITY)
253238
}
254239

255240
pub fn upstream_transform(&self, node_id: NodeId) -> DAffine2 {
256-
self.upstream_transforms.get(&node_id).copied().unwrap_or(DAffine2::IDENTITY)
241+
self.upstream_transforms.get(&node_id).copied().map(|(_, transform)| transform).unwrap_or(DAffine2::IDENTITY)
242+
}
243+
244+
pub fn downstream_transform_to_viewport(&self, node_id: NodeId) -> DAffine2 {
245+
self.upstream_transforms.get(&node_id).copied().map(|(footprint, _)| footprint.transform).unwrap_or(DAffine2::IDENTITY)
257246
}
258247
}
259248

editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -576,23 +576,11 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
576576
skip_rerender,
577577
} => {
578578
let layer_identifier = LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network);
579-
let parent_transform = document
580-
.metadata
581-
.transform_to_viewport(layer_identifier.parent(&document.metadata).unwrap_or(LayerNodeIdentifier::ROOT));
579+
let parent_transform = document.metadata.downstream_transform_to_viewport(layer_identifier.to_node());
582580
let bounds = LayerBounds::new(document, &layer);
583581
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&layer, document, node_graph, responses) {
584582
modify_inputs.transform_change(transform, transform_in, parent_transform, bounds, skip_rerender);
585583
}
586-
587-
let transform = transform.to_cols_array();
588-
responses.add(match transform_in {
589-
TransformIn::Local => Operation::TransformLayer { path: layer, transform },
590-
TransformIn::Scope { scope } => {
591-
let scope = scope.to_cols_array();
592-
Operation::TransformLayerInScope { path: layer, transform, scope }
593-
}
594-
TransformIn::Viewport => Operation::TransformLayerInViewport { path: layer, transform },
595-
});
596584
}
597585
GraphOperationMessage::TransformSet {
598586
layer,
@@ -601,9 +589,7 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
601589
skip_rerender,
602590
} => {
603591
let layer_identifier = LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network);
604-
let parent_transform = document
605-
.metadata
606-
.transform_to_viewport(layer_identifier.parent(&document.metadata).unwrap_or(LayerNodeIdentifier::ROOT));
592+
let parent_transform = document.metadata.downstream_transform_to_viewport(layer_identifier.to_node());
607593

608594
let current_transform = Some(document.metadata.transform_to_viewport(layer_identifier));
609595
let bounds = LayerBounds::new(document, &layer);

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ fn static_nodes() -> Vec<DocumentNodeBlueprint> {
258258
DocumentNodeBlueprint {
259259
name: "Artboard",
260260
category: "General",
261-
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_, _, _, _>"),
261+
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_, _, _, _, _>"),
262262
inputs: vec![
263263
DocumentInputType::value("Graphic Group", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
264264
DocumentInputType::value("Location", TaggedValue::IVec2(glam::IVec2::ZERO), false),
@@ -268,6 +268,7 @@ fn static_nodes() -> Vec<DocumentNodeBlueprint> {
268268
],
269269
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Artboard)],
270270
properties: node_properties::artboard_properties,
271+
manual_composition: Some(concrete!(Footprint)),
271272
..Default::default()
272273
},
273274
DocumentNodeBlueprint {

editor/src/messages/portfolio/document/utility_types/transformation.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl OriginalTransforms {
3434
match self {
3535
OriginalTransforms::Layer(layer_map) => {
3636
for &layer in selected {
37-
layer_map.entry(layer).or_insert_with(|| document.metadata.local_transform(layer));
37+
layer_map.entry(layer).or_insert_with(|| document.metadata.upstream_transform(layer.to_node()));
3838
}
3939
}
4040
OriginalTransforms::Path(path_map) => {
@@ -368,8 +368,7 @@ impl<'a> Selected<'a> {
368368

369369
fn transform_layer(document: &Document, layer: LayerNodeIdentifier, original_transform: Option<&DAffine2>, transformation: DAffine2, responses: &mut VecDeque<Message>) {
370370
let Some(&original_transform) = original_transform else { return };
371-
let parent = layer.parent(&document.metadata);
372-
let to = parent.map(|parent| document.metadata.transform_to_viewport(parent)).unwrap_or(document.metadata.document_to_viewport);
371+
let to = document.metadata.downstream_transform_to_viewport(layer.to_node());
373372
let new = to.inverse() * transformation * to * original_transform;
374373
responses.add(GraphOperationMessage::TransformSet {
375374
layer: layer.to_path(),

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,6 @@ impl PortfolioMessageHandler {
691691

692692
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) {
693693
let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else {
694-
warn!("Polling node graph with no document");
695694
return;
696695
};
697696

editor/src/node_graph_executor.rs

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ use graph_craft::graphene_compiler::Compiler;
1414
use graph_craft::imaginate_input::ImaginatePreferences;
1515
use graph_craft::{concrete, Type};
1616
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
17+
use graphene_core::memo::IORecord;
1718
use graphene_core::raster::{Image, ImageFrame};
1819
use graphene_core::renderer::{ClickTarget, GraphicElementRendered, SvgSegment, SvgSegmentList};
1920
use graphene_core::text::FontCache;
2021
use graphene_core::transform::{Footprint, Transform};
2122
use graphene_core::vector::style::ViewMode;
2223
use graphene_core::vector::VectorData;
2324

24-
use graphene_core::{Color, SurfaceFrame, SurfaceId};
25+
use graphene_core::{Color, GraphicElementData, SurfaceFrame, SurfaceId};
2526
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
2627
use interpreted_executor::dynamic_executor::DynamicExecutor;
2728

@@ -56,10 +57,10 @@ pub struct NodeRuntime {
5657
imaginate_preferences: ImaginatePreferences,
5758
pub(crate) thumbnails: HashMap<NodeId, SvgSegmentList>,
5859
pub(crate) click_targets: HashMap<NodeId, Vec<ClickTarget>>,
59-
pub(crate) transforms: HashMap<NodeId, DAffine2>,
60-
pub(crate) upstream_transforms: HashMap<NodeId, DAffine2>,
60+
pub(crate) upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
6161
graph_hash: Option<u64>,
6262
canvas_cache: HashMap<Vec<LayerId>, SurfaceId>,
63+
monitor_nodes: Vec<Vec<NodeId>>,
6364
}
6465

6566
enum NodeRuntimeMessage {
@@ -81,8 +82,7 @@ pub(crate) struct GenerationResponse {
8182
updates: VecDeque<Message>,
8283
new_thumbnails: HashMap<NodeId, SvgSegmentList>,
8384
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
84-
new_transforms: HashMap<LayerNodeIdentifier, DAffine2>,
85-
new_upstream_transforms: HashMap<NodeId, DAffine2>,
85+
new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
8686
transform: DAffine2,
8787
}
8888

@@ -109,8 +109,6 @@ thread_local! {
109109
pub(crate) static NODE_RUNTIME: Rc<RefCell<Option<NodeRuntime>>> = Rc::new(RefCell::new(None));
110110
}
111111

112-
type MonitorNodes = Vec<Vec<NodeId>>;
113-
114112
impl NodeRuntime {
115113
fn new(receiver: Receiver<NodeRuntimeMessage>, sender: Sender<NodeGraphUpdate>) -> Self {
116114
let executor = DynamicExecutor::default();
@@ -124,9 +122,9 @@ impl NodeRuntime {
124122
wasm_io: None,
125123
canvas_cache: HashMap::new(),
126124
click_targets: HashMap::new(),
127-
transforms: HashMap::new(),
128125
graph_hash: None,
129126
upstream_transforms: HashMap::new(),
127+
monitor_nodes: Vec::new(),
130128
}
131129
}
132130
pub async fn run(&mut self) {
@@ -151,19 +149,18 @@ impl NodeRuntime {
151149
..
152150
}) => {
153151
let transform = render_config.viewport.transform;
154-
let (result, monitor_nodes) = self.execute_network(&path, graph, render_config).await;
152+
let result = self.execute_network(&path, graph, render_config).await;
155153
let mut responses = VecDeque::new();
156-
if let Some(ref monitor_nodes) = monitor_nodes {
157-
self.update_thumbnails(&path, monitor_nodes, &mut responses);
158-
self.update_upstream_transforms(monitor_nodes);
159-
}
154+
155+
self.update_thumbnails(&path, &mut responses);
156+
self.update_upstream_transforms();
157+
160158
let response = GenerationResponse {
161159
generation_id,
162160
result,
163161
updates: responses,
164162
new_thumbnails: self.thumbnails.clone(),
165163
new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(),
166-
new_transforms: self.transforms.clone().into_iter().map(|(id, transform)| (LayerNodeIdentifier::new_unchecked(id), transform)).collect(),
167164
new_upstream_transforms: self.upstream_transforms.clone(),
168165
transform,
169166
};
@@ -173,7 +170,7 @@ impl NodeRuntime {
173170
}
174171
}
175172

176-
async fn execute_network<'a>(&'a mut self, path: &[LayerId], graph: NodeNetwork, render_config: RenderConfig) -> (Result<TaggedValue, String>, Option<MonitorNodes>) {
173+
async fn execute_network<'a>(&'a mut self, path: &[LayerId], graph: NodeNetwork, render_config: RenderConfig) -> Result<TaggedValue, String> {
177174
if self.wasm_io.is_none() {
178175
self.wasm_io = Some(WasmApplicationIo::new().await);
179176
}
@@ -200,12 +197,10 @@ impl NodeRuntime {
200197
self.graph_hash = None;
201198
}
202199

203-
let mut cached_monitor_nodes = None;
204-
205200
if self.graph_hash.is_none() {
206201
let scoped_network = wrap_network_in_scope(graph, font_hash_code);
207202

208-
let monitor_nodes = scoped_network
203+
self.monitor_nodes = scoped_network
209204
.recursive_nodes()
210205
.filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>"))
211206
.map(|(_, node)| node.path.clone().unwrap_or_default())
@@ -216,16 +211,15 @@ impl NodeRuntime {
216211
let c = Compiler {};
217212
let proto_network = match c.compile_single(scoped_network) {
218213
Ok(network) => network,
219-
Err(e) => return (Err(e), Some(monitor_nodes)),
214+
Err(e) => return Err(e),
220215
};
221216

222217
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
223218
if let Err(e) = self.executor.update(proto_network).await {
224219
error!("Failed to update executor:\n{e}");
225-
return (Err(e), Some(monitor_nodes));
220+
return Err(e);
226221
}
227222

228-
cached_monitor_nodes = Some(monitor_nodes);
229223
self.graph_hash = Some(hash_code);
230224
}
231225

@@ -239,7 +233,7 @@ impl NodeRuntime {
239233
};
240234
let result = match result {
241235
Ok(value) => value,
242-
Err(e) => return (Err(e), cached_monitor_nodes),
236+
Err(e) => return Err(e),
243237
};
244238

245239
if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
@@ -252,13 +246,14 @@ impl NodeRuntime {
252246
}
253247
}
254248
}
255-
(Ok(result), cached_monitor_nodes)
249+
Ok(result)
256250
}
257251

258252
/// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI.
259-
pub fn update_thumbnails(&mut self, layer_path: &[LayerId], monitor_nodes: &[Vec<u64>], responses: &mut VecDeque<Message>) {
253+
pub fn update_thumbnails(&mut self, layer_path: &[LayerId], responses: &mut VecDeque<Message>) {
260254
let mut image_data: Vec<_> = Vec::new();
261-
for node_path in monitor_nodes {
255+
self.thumbnails.retain(|id, _| self.monitor_nodes.iter().any(|node_path| node_path.contains(id)));
256+
for node_path in &self.monitor_nodes {
262257
let Some(node_id) = node_path.get(node_path.len() - 2).copied() else {
263258
warn!("Monitor node has invalid node id");
264259
continue;
@@ -268,8 +263,7 @@ impl NodeRuntime {
268263
continue;
269264
};
270265

271-
let Some(io_data) = value.downcast_ref::<graphene_core::memo::IORecord<Footprint, graphene_core::GraphicElementData>>() else {
272-
warn!("Failed to downcast thumbnail to graphic element data");
266+
let Some(io_data) = value.downcast_ref::<IORecord<Footprint, graphene_core::GraphicElementData>>() else {
273267
continue;
274268
};
275269
let graphic_element_data = &io_data.output;
@@ -283,10 +277,9 @@ impl NodeRuntime {
283277

284278
let click_targets = self.click_targets.entry(node_id).or_default();
285279
click_targets.clear();
280+
// Add the graphic element data's click targets to the click targets vector
286281
graphic_element_data.add_click_targets(click_targets);
287282

288-
self.transforms.insert(node_id, graphic_element_data.transform());
289-
290283
let old_thumbnail = self.thumbnails.entry(node_id).or_default();
291284
if *old_thumbnail != render.svg {
292285
responses.add(FrontendMessage::UpdateNodeThumbnail {
@@ -305,8 +298,8 @@ impl NodeRuntime {
305298
}
306299
}
307300

308-
pub fn update_upstream_transforms(&mut self, monitor_nodes: &[Vec<u64>]) {
309-
for node_path in monitor_nodes {
301+
pub fn update_upstream_transforms(&mut self) {
302+
for node_path in &self.monitor_nodes {
310303
let Some(node_id) = node_path.get(node_path.len() - 2).copied() else {
311304
warn!("Monitor node has invalid node id");
312305
continue;
@@ -315,14 +308,16 @@ impl NodeRuntime {
315308
warn!("Failed to introspect monitor node for upstream transforms");
316309
continue;
317310
};
318-
let Some(transform) = value
319-
.downcast_ref::<graphene_core::memo::IORecord<Footprint, VectorData>>()
320-
.map(|vector_data| vector_data.output.transform())
321-
.or_else(|| {
322-
value
323-
.downcast_ref::<graphene_core::memo::IORecord<Footprint, ImageFrame<Color>>>()
324-
.map(|image| image.output.transform())
325-
})
311+
312+
fn try_downcast<T: Transform + 'static>(value: &dyn std::any::Any) -> Option<(Footprint, DAffine2)> {
313+
let io_data = value.downcast_ref::<IORecord<Footprint, T>>()?;
314+
let transform = io_data.output.transform();
315+
Some((io_data.input, transform))
316+
}
317+
318+
let Some(transform) = try_downcast::<VectorData>(value.as_ref())
319+
.or_else(|| try_downcast::<ImageFrame<Color>>(value.as_ref()))
320+
.or_else(|| try_downcast::<GraphicElementData>(value.as_ref()))
326321
else {
327322
warn!("Failed to downcast transform input");
328323
continue;
@@ -527,7 +522,6 @@ impl NodeGraphExecutor {
527522
updates,
528523
new_thumbnails,
529524
new_click_targets,
530-
new_transforms,
531525
new_upstream_transforms,
532526
transform,
533527
}) => {
@@ -566,7 +560,7 @@ impl NodeGraphExecutor {
566560
});
567561
}
568562
self.thumbnails = new_thumbnails;
569-
document.metadata.update_transforms(new_transforms, new_upstream_transforms);
563+
document.metadata.update_transforms(new_upstream_transforms);
570564
document.metadata.update_click_targets(new_click_targets);
571565
let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {e:?}"))?;
572566
let execution_context = self.futures.remove(&generation_id).ok_or_else(|| "Invalid generation ID".to_string())?;

0 commit comments

Comments
 (0)