Skip to content

Commit 023cf1b

Browse files
authored
Fix the 'Flatten Vector' node losing child layer transforms for the editor tools (#4116)
1 parent d106bed commit 023cf1b

1 file changed

Lines changed: 29 additions & 2 deletions

File tree

node-graph/nodes/graphic/src/graphic.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use core_types::bounds::{BoundingBox, RenderBoundingBox};
22
use core_types::registry::types::{Angle, SignedInteger};
33
use core_types::table::{AttributeColumnDyn, AttributeValueDyn, Table, TableDyn, TableRow};
44
use core_types::uuid::NodeId;
5-
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, AnyHash, BlendMode, CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
5+
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, AnyHash, BlendMode, CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
66
use glam::{DAffine2, DVec2};
77
use graphic_types::graphic::{Graphic, IntoGraphicTable};
88
use graphic_types::{Artboard, Vector};
@@ -613,7 +613,34 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten
613613
/// Converts a `Table<Graphic>` into a `Table<Vector>` by deeply flattening any vector content it contains, and discarding any non-vector content.
614614
#[node_macro::node(category("Vector"))]
615615
pub async fn flatten_vector<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: T) -> Table<Vector> {
616-
content.into_flattened_table()
616+
let graphic_table = content.into_graphic_table();
617+
let mut output: Table<Vector> = graphic_table.clone().into_flattened_table();
618+
619+
// TODO: Replace this snapshot hack with per-layer metadata driven by each layer's Monitor node.
620+
// TODO: Flattening here erases the upstream `Table<Graphic>` hierarchy that editor metadata collection walks
621+
// TODO: to populate `upstream_footprints` / `local_transforms` / `click_targets` per child layer. As a workaround
622+
// TODO: we stash the pre-flattened table on the output so `Table<Vector>::collect_metadata` can recurse into it,
623+
// TODO: which conflates render output with editor metadata and forces the pre-compensation dance below.
624+
// TODO: The cleaner fix is to drive each layer's metadata from its own Monitor's captured `(Context, Table<Graphic>)`,
625+
// TODO: at which point this attribute (and the equivalents in Boolean Operation, Solidify Stroke, Flatten Path,
626+
// TODO: Morph, Rasterize) become unnecessary.
627+
if !output.is_empty() {
628+
// Item 0 carries a composed transform inherited from the flattened input, but the merged_layers
629+
// already holds the original transforms; pre-compensate by item 0's inverse so the renderer's
630+
// `upstream_footprint *= item_0_transform` recursion cancels out and leaves the originals intact.
631+
let mut graphic_table = graphic_table;
632+
let item_0_transform: DAffine2 = output.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
633+
if item_0_transform.matrix2.determinant().abs() > f64::EPSILON {
634+
let inverse = item_0_transform.inverse();
635+
for transform in graphic_table.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
636+
*transform = inverse * *transform;
637+
}
638+
}
639+
640+
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_table);
641+
}
642+
643+
output
617644
}
618645

619646
/// Converts a `Table<Graphic>` into a `Table<Raster>` by deeply flattening any raster content it contains, and discarding any non-raster content.

0 commit comments

Comments
 (0)