mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Move source_node_id to "editor:layer" and Vector<Upstream> to "editor:merged_layers", backed by a new 'Write Attribute' node (#4061)
* Fix click target propagation with the Rasterize node * Add the 'Write Attribute' node * Remove tag_layer in favor of the new Write Attribute node, prune redundant attribute writes * Replace the Vector<Upstream> type argument with the "editor:merged_layers" attribute
This commit is contained in:
@@ -17,8 +17,8 @@ async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Bo
|
||||
|
||||
// Update the source node id
|
||||
let this_node_path = node_path.iter().rev().nth(1).copied();
|
||||
let existing: Option<NodeId> = vector.attribute_cloned_or_default("source_node_id", 0);
|
||||
vector.set_attribute("source_node_id", 0, existing.or(this_node_path));
|
||||
let existing: Option<NodeId> = vector.attribute_cloned_or_default("editor:layer", 0);
|
||||
vector.set_attribute("editor:layer", 0, existing.or(this_node_path));
|
||||
|
||||
if vector.len() > 1 {
|
||||
warn!("The path modify ran on {} vector rows. Only the first can be modified.", vector.len());
|
||||
|
||||
@@ -354,8 +354,6 @@ async fn round_corners(
|
||||
let attributes = source.clone_row_attributes(index);
|
||||
let source = source.element(index).unwrap();
|
||||
|
||||
let upstream_nested_layers = source.upstream_data.clone();
|
||||
|
||||
// Flip the roundness to help with user intuition
|
||||
let roundness = 1. - roundness;
|
||||
// Convert 0-100 to 0-0.5
|
||||
@@ -439,8 +437,6 @@ async fn round_corners(
|
||||
result.append_bezpath(rounded_subpath);
|
||||
}
|
||||
|
||||
result.upstream_data = upstream_nested_layers;
|
||||
|
||||
TableRow::from_parts(result, attributes)
|
||||
})
|
||||
.collect()
|
||||
@@ -1300,7 +1296,7 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||
// Concatenate every vector element's subpaths into the single output compound path
|
||||
for index in 0..flattened.len() {
|
||||
let Some(element) = flattened.element(index) else { continue };
|
||||
let node_id: Option<NodeId> = flattened.attribute_cloned_or_default("source_node_id", index);
|
||||
let node_id: Option<NodeId> = flattened.attribute_cloned_or_default("editor:layer", index);
|
||||
let node_id = node_id.map(|node_id| node_id.0).unwrap_or_default();
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
@@ -1317,13 +1313,13 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||
// Preserve a reference to the original upstream graphic table so the renderer can recurse into it
|
||||
// when collecting metadata, exposing the original child layers' click targets to editor tools.
|
||||
// This is the same mechanism Boolean Operation uses to keep its inputs editable after the merge.
|
||||
output.upstream_data = Some(graphic_table);
|
||||
output_table.set_attribute("editor:merged_layers", 0, graphic_table);
|
||||
|
||||
// Adopt the last input row's source_node_id so the editor can also bucket clicks under a contributing child layer
|
||||
// Adopt the last input row's layer so the editor can also bucket clicks under a contributing child layer
|
||||
if !flattened.is_empty() {
|
||||
let primary = flattened.len() - 1;
|
||||
let source_node_id: Option<NodeId> = flattened.attribute_cloned_or_default("source_node_id", primary);
|
||||
output_table.set_attribute("source_node_id", 0, source_node_id);
|
||||
let layer: Option<NodeId> = flattened.attribute_cloned_or_default("editor:layer", primary);
|
||||
output_table.set_attribute("editor:layer", 0, layer);
|
||||
}
|
||||
|
||||
output_table
|
||||
@@ -1351,7 +1347,6 @@ async fn sample_polyline(
|
||||
region_domain: Default::default(),
|
||||
colinear_manipulators: Default::default(),
|
||||
style: std::mem::take(&mut row.element_mut().style),
|
||||
upstream_data: std::mem::take(&mut row.element_mut().upstream_data),
|
||||
};
|
||||
// Transfer the stroke transform from the input vector content to the result.
|
||||
result.style.set_stroke_transform(row.attribute_cloned_or_default("transform"));
|
||||
@@ -1441,7 +1436,6 @@ async fn simplify(
|
||||
|
||||
let mut result = Vector {
|
||||
style: std::mem::take(&mut row.element_mut().style),
|
||||
upstream_data: std::mem::take(&mut row.element_mut().upstream_data),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -1538,7 +1532,6 @@ async fn decimate(
|
||||
|
||||
let mut result = Vector {
|
||||
style: std::mem::take(&mut row.element_mut().style),
|
||||
upstream_data: std::mem::take(&mut row.element_mut().upstream_data),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -2382,13 +2375,13 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
trs * skew
|
||||
};
|
||||
|
||||
// Pre-compensate upstream_data transforms so that when collect_metadata applies
|
||||
// Pre-compensate merged_layers transforms so that when collect_metadata applies
|
||||
// the row transform (which will be group_transform * lerped_transform after the
|
||||
// pipeline's Transform node runs), the lerped_transform cancels out and children
|
||||
// get the correct footprint: parent * group_transform * child_transform.
|
||||
// Only pre-compensate if the lerped transform is invertible (non-zero determinant).
|
||||
// A zero determinant can occur when interpolated scale passes through zero (e.g., flipped axes),
|
||||
// in which case we skip pre-compensation to avoid propagating NaN through upstream_data transforms.
|
||||
// in which case we skip pre-compensation to avoid propagating NaN through merged_layers transforms.
|
||||
if lerped_transform.matrix2.determinant().abs() > f64::EPSILON {
|
||||
let lerped_inverse = lerped_transform.inverse();
|
||||
for transform in graphic_table_content.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
||||
@@ -2404,21 +2397,15 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
|
||||
let mut attributes = content.clone_row_attributes(endpoint_index);
|
||||
attributes.insert("transform", lerped_transform);
|
||||
attributes.insert("editor:merged_layers", graphic_table_content);
|
||||
|
||||
return Table::new_from_row(TableRow::from_parts(
|
||||
Vector {
|
||||
upstream_data: Some(graphic_table_content),
|
||||
..endpoint_element.clone()
|
||||
},
|
||||
attributes,
|
||||
));
|
||||
return Table::new_from_row(TableRow::from_parts(endpoint_element.clone(), attributes));
|
||||
}
|
||||
|
||||
let mut vector = Vector {
|
||||
upstream_data: Some(graphic_table_content),
|
||||
style: source_element.style.lerp(&target_element.style, time),
|
||||
..Default::default()
|
||||
};
|
||||
vector.style = source_element.style.lerp(&target_element.style, time);
|
||||
|
||||
// Work directly with manipulator groups, bypassing the BezPath intermediate representation.
|
||||
// This avoids the full Vector → BezPath → interpolate → BezPath → Vector roundtrip each frame.
|
||||
@@ -2566,13 +2553,14 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
// The result is a synthesis of source and target, so adopt whichever endpoint the result is closer to as
|
||||
// the click-target identity (so the editor can route clicks back to one of the contributing layers)
|
||||
let primary_index = if time < 0.5 { source_index } else { target_index };
|
||||
let source_node_id: Option<NodeId> = content.attribute_cloned_or_default("source_node_id", primary_index);
|
||||
let layer: Option<NodeId> = content.attribute_cloned_or_default("editor:layer", primary_index);
|
||||
|
||||
Table::new_from_row(
|
||||
TableRow::new_from_element(vector)
|
||||
.with_attribute("transform", lerped_transform)
|
||||
.with_attribute("alpha_blending", vector_alpha_blending)
|
||||
.with_attribute("source_node_id", source_node_id),
|
||||
.with_attribute("editor:layer", layer)
|
||||
.with_attribute("editor:merged_layers", graphic_table_content),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3047,7 +3035,6 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use core_types::AlphaBlending;
|
||||
use core_types::Node;
|
||||
use kurbo::{CubicBez, Ellipse, Point, Rect};
|
||||
use std::future::Future;
|
||||
@@ -3073,10 +3060,7 @@ mod test {
|
||||
fn create_vector_row(bezpath: BezPath, transform: DAffine2) -> TableRow<Vector> {
|
||||
let mut row = Vector::default();
|
||||
row.append_bezpath(bezpath);
|
||||
TableRow::new_from_element(row)
|
||||
.with_attribute("transform", transform)
|
||||
.with_attribute("alpha_blending", AlphaBlending::default())
|
||||
.with_attribute("source_node_id", None::<core_types::uuid::NodeId>)
|
||||
TableRow::new_from_element(row).with_attribute("transform", transform)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user