Skip to content

Commit 53caba4

Browse files
authored
Instance tables refactor part 8: Make repeater nodes use pivot not bbox and output instance type not group; rename 'Flatten Vector Elements' to 'Flatten Path' and add 'Flatten Vector' (#2697)
Make repeater nodes use pivot not bbox and output instance type not group; rename 'Flatten Vector Elements' to 'Flatten Path' and add 'Flatten Vector'
1 parent d62891d commit 53caba4

26 files changed

+252
-125
lines changed

demo-artwork/changing-seasons.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/isometric-fountain.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/marbled-mandelbrot.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/painted-dreams.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/parametric-dunescape.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/procedural-string-lights.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/red-dress.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/valley-of-spires.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editor/src/dispatcher.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,14 @@ mod test {
498498
println!("-------------------------------------------------");
499499
println!("Failed test due to receiving a DisplayDialogError while loading a Graphite demo file.");
500500
println!();
501+
println!("NOTE:");
502+
println!("Document upgrading isn't performed in tests like when opening in the actual editor.");
503+
println!("You may need to open and re-save a document in the editor to apply its migrations.");
504+
println!();
501505
println!("DisplayDialogError details:");
502506
println!();
503-
println!("Description: {value}");
507+
println!("Description:");
508+
println!("{value}");
504509
println!("-------------------------------------------------");
505510
println!();
506511

@@ -538,7 +543,9 @@ mod test {
538543
});
539544

540545
// Check if the graph renders
541-
editor.eval_graph().await;
546+
if let Err(e) = editor.eval_graph().await {
547+
print_problem_to_terminal_on_failure(&format!("Failed to evaluate the graph for document '{document_name}':\n{e}"));
548+
}
542549

543550
for response in responses {
544551
// Check for the existence of the file format incompatibility warning dialog after opening the test file

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,17 +289,17 @@ impl<'a> ModifyInputsContext<'a> {
289289
log::error!("Node type {} does not exist in ModifyInputsContext::existing_node_id", reference);
290290
return None;
291291
};
292-
// If inserting a path node, insert a flatten vector elements if the type is a graphic group.
292+
// If inserting a path node, insert a Flatten Path if the type is a graphic group.
293293
// TODO: Allow the path node to operate on Graphic Group data by utilizing the reference for each vector data in a group.
294294
if node_definition.identifier == "Path" {
295295
let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]).0.nested_type().clone();
296296
if layer_input_type == concrete!(GraphicGroupTable) {
297-
let Some(flatten_vector_elements_definition) = resolve_document_node_type("Flatten Vector Elements") else {
298-
log::error!("Flatten Vector Elements does not exist in ModifyInputsContext::existing_node_id");
297+
let Some(flatten_path_definition) = resolve_document_node_type("Flatten Path") else {
298+
log::error!("Flatten Path does not exist in ModifyInputsContext::existing_node_id");
299299
return None;
300300
};
301301
let node_id = NodeId::new();
302-
self.network_interface.insert_node(node_id, flatten_vector_elements_definition.default_node_template(), &[]);
302+
self.network_interface.insert_node(node_id, flatten_path_definition.default_node_template(), &[]);
303303
self.network_interface.move_node_to_chain_start(&node_id, output_layer, &[]);
304304
}
305305
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3805,6 +3805,15 @@ impl NodeNetworkInterface {
38053805
node_metadata.persistent_metadata.network_metadata = metadata.network_metadata;
38063806
}
38073807

3808+
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts
3809+
pub fn replace_reference_name(&mut self, node_id: &NodeId, network_path: &[NodeId], reference_name: String) {
3810+
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
3811+
log::error!("Could not get node metadata in replace_reference_name");
3812+
return;
3813+
};
3814+
node_metadata.persistent_metadata.reference = Some(reference_name);
3815+
}
3816+
38083817
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts
38093818
pub fn set_manual_compostion(&mut self, node_id: &NodeId, network_path: &[NodeId], manual_composition: Option<Type>) {
38103819
let Some(network) = self.network_mut(network_path) else {

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
467467
}
468468
};
469469

470-
const REPLACEMENTS: [(&str, &str); 36] = [
470+
const REPLACEMENTS: [(&str, &str); 37] = [
471471
("graphene_core::AddArtboardNode", "graphene_core::graphic_element::AppendArtboardNode"),
472472
("graphene_core::ConstructArtboardNode", "graphene_core::graphic_element::ToArtboardNode"),
473473
("graphene_core::ToGraphicElementNode", "graphene_core::graphic_element::ToElementNode"),
@@ -507,6 +507,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
507507
("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"),
508508
("graphene_core::transform::CullNode", "graphene_core::ops::IdentityNode"),
509509
("graphene_std::raster::MaskImageNode", "graphene_std::raster::MaskNode"),
510+
("graphene_core::vector::FlattenVectorElementsNode", "graphene_core::vector::FlattenPathNode"),
510511
];
511512
let mut network = document.network_interface.document_network().clone();
512513
network.generate_node_paths(&[]);
@@ -946,6 +947,22 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
946947
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[2].clone(), network_path);
947948
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[3].clone(), network_path);
948949
}
950+
951+
if reference == "Flatten Vector Elements" {
952+
let node_definition = resolve_document_node_type("Flatten Path").unwrap();
953+
let new_node_template = node_definition.default_node_template();
954+
let document_node = new_node_template.document_node;
955+
document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone());
956+
document
957+
.network_interface
958+
.replace_implementation_metadata(node_id, network_path, new_node_template.persistent_node_metadata);
959+
960+
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
961+
962+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
963+
964+
document.network_interface.replace_reference_name(node_id, network_path, "Flatten Path".to_string());
965+
}
949966
}
950967

951968
// TODO: Eventually remove this document upgrade code

editor/src/messages/tool/common_functionality/graph_modification_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
9191
delete_children: false,
9292
});
9393

94-
// Add a flatten vector elements node after the merge
94+
// Add a Flatten Path node after the merge
9595
let flatten_node_id = NodeId::new();
96-
let flatten_node = document_node_definitions::resolve_document_node_type("Flatten Vector Elements")
96+
let flatten_node = document_node_definitions::resolve_document_node_type("Flatten Path")
9797
.expect("Failed to create flatten node")
9898
.default_node_template();
9999
responses.add(NodeGraphMessage::InsertNode {

editor/src/messages/tool/tool_messages/artboard_tool.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,10 @@ mod test_artboard {
563563
pub use crate::test_utils::test_prelude::*;
564564

565565
async fn get_artboards(editor: &mut EditorTestUtils) -> Vec<graphene_core::Artboard> {
566-
let instrumented = editor.eval_graph().await;
566+
let instrumented = match editor.eval_graph().await {
567+
Ok(instrumented) => instrumented,
568+
Err(e) => panic!("Failed to evaluate graph: {}", e),
569+
};
567570
instrumented.grab_all_input::<graphene_core::append_artboard::ArtboardInput>(&editor.runtime).collect()
568571
}
569572

editor/src/messages/tool/tool_messages/ellipse_tool.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,10 @@ mod test_ellipse {
330330
}
331331

332332
async fn get_ellipse(editor: &mut EditorTestUtils) -> Vec<ResolvedEllipse> {
333-
let instrumented = editor.eval_graph().await;
333+
let instrumented = match editor.eval_graph().await {
334+
Ok(instrumented) => instrumented,
335+
Err(e) => panic!("Failed to evaluate graph: {e}"),
336+
};
334337

335338
let document = editor.active_document();
336339
let layers = document.metadata().all_layers();

editor/src/messages/tool/tool_messages/fill_tool.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ mod test_fill {
161161
use graphene_std::vector::style::Fill;
162162

163163
async fn get_fills(editor: &mut EditorTestUtils) -> Vec<Fill> {
164-
let instrumented = editor.eval_graph().await;
164+
let instrumented = match editor.eval_graph().await {
165+
Ok(instrumented) => instrumented,
166+
Err(e) => panic!("Failed to evaluate graph: {e}"),
167+
};
165168

166169
instrumented.grab_all_input::<fill::FillInput<Fill>>(&editor.runtime).collect()
167170
}

editor/src/messages/tool/tool_messages/gradient_tool.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,10 @@ mod test_gradient {
545545
use super::gradient_space_transform;
546546

547547
async fn get_fills(editor: &mut EditorTestUtils) -> Vec<(Fill, DAffine2)> {
548-
let instrumented = editor.eval_graph().await;
548+
let instrumented = match editor.eval_graph().await {
549+
Ok(instrumented) => instrumented,
550+
Err(e) => panic!("Failed to evaluate graph: {}", e),
551+
};
549552

550553
let document = editor.active_document();
551554
let layers = document.metadata().all_layers();

editor/src/messages/tool/tool_messages/spline_tool.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,9 @@ mod test_spline_tool {
670670
editor.handle_message(SplineToolMessage::Confirm).await;
671671

672672
// Evaluate the graph to ensure everything is processed
673-
editor.eval_graph().await;
673+
if let Err(e) = editor.eval_graph().await {
674+
panic!("Graph evaluation failed: {}", e);
675+
}
674676

675677
// Get the layer and vector data
676678
let document = editor.active_document();
@@ -708,7 +710,9 @@ mod test_spline_tool {
708710
editor.handle_message(SplineToolMessage::Confirm).await;
709711

710712
// Evaluating the graph to ensure everything is processed
711-
editor.eval_graph().await;
713+
if let Err(e) = editor.eval_graph().await {
714+
panic!("Graph evaluation failed: {}", e);
715+
}
712716

713717
// Get the layer and vector data
714718
let document = editor.active_document();
@@ -744,7 +748,9 @@ mod test_spline_tool {
744748
editor.handle_message(SplineToolMessage::Confirm).await;
745749

746750
// Evaluating the graph to ensure everything is processed
747-
editor.eval_graph().await;
751+
if let Err(e) = editor.eval_graph().await {
752+
panic!("Graph evaluation failed: {}", e);
753+
}
748754

749755
// Get the layer and vector data
750756
let document = editor.active_document();
@@ -781,7 +787,9 @@ mod test_spline_tool {
781787
editor.click_tool(ToolType::Spline, MouseKeys::LEFT, DVec2::new(150.0, 100.0), ModifierKeys::empty()).await;
782788

783789
editor.handle_message(SplineToolMessage::Confirm).await;
784-
editor.eval_graph().await;
790+
if let Err(e) = editor.eval_graph().await {
791+
panic!("Graph evaluation failed: {}", e);
792+
}
785793

786794
// Get the layer and vector data
787795
let document = editor.active_document();

editor/src/test_utils.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,30 +36,35 @@ impl EditorTestUtils {
3636
Self { editor, runtime }
3737
}
3838

39-
pub fn eval_graph<'a>(&'a mut self) -> impl std::future::Future<Output = Instrumented> + 'a {
39+
pub fn eval_graph<'a>(&'a mut self) -> impl std::future::Future<Output = Result<Instrumented, String>> + 'a {
4040
// An inner function is required since async functions in traits are a bit weird
41-
async fn run<'a>(editor: &'a mut Editor, runtime: &'a mut NodeRuntime) -> Instrumented {
41+
async fn run<'a>(editor: &'a mut Editor, runtime: &'a mut NodeRuntime) -> Result<Instrumented, String> {
4242
let portfolio = &mut editor.dispatcher.message_handlers.portfolio_message_handler;
4343
let exector = &mut portfolio.executor;
4444
let document = portfolio.documents.get_mut(&portfolio.active_document_id.unwrap()).unwrap();
4545

46-
let instrumented = exector.update_node_graph_instrumented(document).expect("update_node_graph_instrumented failed");
46+
let instrumented = match exector.update_node_graph_instrumented(document) {
47+
Ok(instrumented) => instrumented,
48+
Err(e) => return Err(format!("update_node_graph_instrumented failed\n\n{e}")),
49+
};
4750

4851
let viewport_resolution = glam::UVec2::ONE;
49-
exector
50-
.submit_current_node_graph_evaluation(document, viewport_resolution, Default::default())
51-
.expect("submit_current_node_graph_evaluation failed");
52+
if let Err(e) = exector.submit_current_node_graph_evaluation(document, viewport_resolution, Default::default()) {
53+
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
54+
}
5255
runtime.run().await;
5356

5457
let mut messages = VecDeque::new();
55-
editor.poll_node_graph_evaluation(&mut messages).expect("Graph should render");
58+
if let Err(e) = editor.poll_node_graph_evaluation(&mut messages) {
59+
return Err(format!("Graph should render\n\n{e}"));
60+
}
5661
let frontend_messages = messages.into_iter().flat_map(|message| editor.handle_message(message));
5762

5863
for message in frontend_messages {
5964
message.check_node_graph_error();
6065
}
6166

62-
instrumented
67+
Ok(instrumented)
6368
}
6469

6570
run(&mut self.editor, &mut self.runtime)
@@ -69,7 +74,9 @@ impl EditorTestUtils {
6974
self.editor.handle_message(message);
7075

7176
// Required to process any buffered messages
72-
self.eval_graph().await;
77+
if let Err(e) = self.eval_graph().await {
78+
panic!("Failed to evaluate graph: {e}");
79+
}
7380
}
7481

7582
pub async fn new_document(&mut self) {

node-graph/gcore/src/graphic_element.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,49 @@ async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: boo
355355
output
356356
}
357357

358+
#[node_macro::node(category("General"))]
359+
async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTable {
360+
// TODO: Avoid mutable reference, instead return a new GraphicGroupTable?
361+
fn flatten_group(output_group_table: &mut VectorDataTable, current_group_table: GraphicGroupTable) {
362+
for current_instance in current_group_table.instance_ref_iter() {
363+
let current_element = current_instance.instance.clone();
364+
let reference = *current_instance.source_node_id;
365+
366+
match current_element {
367+
// If we're allowed to recurse, flatten any GraphicGroups we encounter
368+
GraphicElement::GraphicGroup(mut current_element) => {
369+
// Apply the parent group's transform to all child elements
370+
for graphic_element in current_element.instance_mut_iter() {
371+
*graphic_element.transform = *current_instance.transform * *graphic_element.transform;
372+
}
373+
374+
flatten_group(output_group_table, current_element);
375+
}
376+
// Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten
377+
GraphicElement::VectorData(vector_instance) => {
378+
for current_element in vector_instance.instance_ref_iter() {
379+
output_group_table.push(Instance {
380+
instance: current_element.instance.clone(),
381+
transform: *current_instance.transform * *current_element.transform,
382+
alpha_blending: AlphaBlending {
383+
opacity: current_instance.alpha_blending.opacity * current_element.alpha_blending.opacity,
384+
blend_mode: current_element.alpha_blending.blend_mode,
385+
},
386+
source_node_id: reference,
387+
});
388+
}
389+
}
390+
_ => {}
391+
}
392+
}
393+
}
394+
395+
let mut output = VectorDataTable::default();
396+
flatten_group(&mut output, group);
397+
398+
output
399+
}
400+
358401
#[node_macro::node(category(""))]
359402
async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
360403
ctx: impl ExtractAll + CloneVarArgs + Ctx,

node-graph/gcore/src/graphic_element/renderer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,10 @@ impl GraphicElementRendered for RasterDataTable<Color> {
921921
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
922922
click_targets.push(ClickTarget::new(subpath, 0.));
923923
}
924+
925+
fn to_graphic_element(&self) -> GraphicElement {
926+
GraphicElement::RasterData(self.clone())
927+
}
924928
}
925929

926930
impl GraphicElementRendered for GraphicElement {

node-graph/gcore/src/instances.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ impl<T> Instances<T> {
3333
self.source_node_id.push(instance.source_node_id);
3434
}
3535

36+
pub fn extend(&mut self, instances: Instances<T>) {
37+
self.instance.extend(instances.instance);
38+
self.transform.extend(instances.transform);
39+
self.alpha_blending.extend(instances.alpha_blending);
40+
self.source_node_id.extend(instances.source_node_id);
41+
}
42+
3643
pub fn instance_iter(self) -> impl DoubleEndedIterator<Item = Instance<T>> {
3744
self.instance
3845
.into_iter()

node-graph/gcore/src/logic.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,26 @@ async fn switch<T, C: Send + 'n + Clone>(
4444
condition: bool,
4545
#[expose]
4646
#[implementations(
47-
Context -> String, Context -> bool, Context -> f64, Context -> u32, Context -> u64, Context -> DVec2, Context -> VectorDataTable, Context -> DAffine2,
47+
Context -> String,
48+
Context -> bool,
49+
Context -> f64,
50+
Context -> u32,
51+
Context -> u64,
52+
Context -> DVec2,
53+
Context -> VectorDataTable,
54+
Context -> DAffine2,
4855
)]
4956
if_true: impl Node<C, Output = T>,
5057
#[expose]
5158
#[implementations(
52-
Context -> String, Context -> bool, Context -> f64, Context -> u32, Context -> u64, Context -> DVec2, Context -> VectorDataTable, Context -> DAffine2,
59+
Context -> String,
60+
Context -> bool,
61+
Context -> f64,
62+
Context -> u32,
63+
Context -> u64,
64+
Context -> DVec2,
65+
Context -> VectorDataTable,
66+
Context -> DAffine2,
5367
)]
5468
if_false: impl Node<C, Output = T>,
5569
) -> T {

0 commit comments

Comments
 (0)