mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Adopt the cascading "appearance" attribute in place of Vector::stroke and the "fill"/"paint" attributes (#4433)
* Add the appearance model types and attribute constants * Dual-write the appearance attribute alongside the fill/stroke pair in all paint-writing nodes * Read paint from the appearance attribute in the renderer, analysis, metadata, and editor, cascading from ancestors * Retire the fill/stroke attribute pair and the Vector stroke field in favor of the appearance attribute * Replace the Stroke node's paint order input with the relative chain order of the Fill and Stroke nodes * Code review fixes * Re-save demo art * Stamp coverages in place and fuse the renderer's per-item appearance reads into single walks * Treat an empty appearance as the undeclared state so padded rows inherit instead of blocking the cascade * Update demo art * Treat padded appearance rows as undeclared in the boolean flatten's group recursion
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use core_types::list::{Item, List, NodeIdPath};
|
||||
use core_types::list::{ATTR_APPEARANCE, Item, List, NodeIdPath};
|
||||
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphic_types::Appearance;
|
||||
use graphic_types::graphic::bake_paint_transforms;
|
||||
use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath};
|
||||
use graphic_types::vector_types::vector::PointId;
|
||||
@@ -45,7 +46,15 @@ async fn boolean_operation<I: graphic_types::IntoGraphicList>(
|
||||
|
||||
let result_vector = result_vector_list.element_mut(0).unwrap();
|
||||
Vector::transform(result_vector, transform);
|
||||
result_vector.set_stroke_transform(DAffine2::IDENTITY);
|
||||
|
||||
// The geometry is baked into identity space, so a copied stroke authoring space would be stale
|
||||
if let Some(appearance) = result_vector_list.attribute::<Appearance>(ATTR_APPEARANCE, 0) {
|
||||
let mut appearance = appearance.clone();
|
||||
for coverage in appearance.0.iter_element_values_mut() {
|
||||
coverage.0.remove_attribute::<DAffine2>(ATTR_TRANSFORM);
|
||||
}
|
||||
result_vector_list.set_attribute(ATTR_APPEARANCE, 0, appearance);
|
||||
}
|
||||
|
||||
// Snapshot the input layers as the `editor:merged_layers` attribute so the renderer can recurse into them
|
||||
// for editor click-target preservation.
|
||||
@@ -142,12 +151,7 @@ fn boolean_operation_on_vector_list(vector: &List<Vector>, boolean_operation: Bo
|
||||
|
||||
bake_paint_transforms(&mut attributes, copy_from_transform);
|
||||
|
||||
let copy_from = vector.element(index).unwrap();
|
||||
let element = Vector {
|
||||
stroke: copy_from.stroke.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
Item::from_parts(element, attributes)
|
||||
Item::from_parts(Vector::default(), attributes)
|
||||
} else {
|
||||
Item::<Vector>::default()
|
||||
};
|
||||
@@ -191,6 +195,7 @@ fn flatten_vector(graphic_list: &List<Graphic>) -> List<Vector> {
|
||||
let parent_opacity: f64 = graphic_list.attribute_cloned_or(ATTR_OPACITY, index, 1.);
|
||||
let parent_fill: f64 = graphic_list.attribute_cloned_or(ATTR_OPACITY_FILL, index, 1.);
|
||||
let layer_path: NodeIdPath = graphic_list.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
let parent_appearance = graphic_list.attribute::<Appearance>(ATTR_APPEARANCE, index).and_then(Appearance::declared).cloned();
|
||||
|
||||
let compose_parent = |mut item: Item<Vector>| {
|
||||
if parent_has_transform || item.attribute::<DAffine2>(ATTR_TRANSFORM).is_some() {
|
||||
@@ -208,6 +213,12 @@ fn flatten_vector(graphic_list: &List<Graphic>) -> List<Vector> {
|
||||
if parent_has_layer_path {
|
||||
item.set_attribute(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
|
||||
}
|
||||
// Appearance cascades into each child whose own is undeclared, since a declared child wins wholesale
|
||||
if let Some(appearance) = &parent_appearance
|
||||
&& item.attribute::<Appearance>(ATTR_APPEARANCE).and_then(Appearance::declared).is_none()
|
||||
{
|
||||
item.set_attribute(ATTR_APPEARANCE, appearance.clone());
|
||||
}
|
||||
|
||||
item
|
||||
};
|
||||
@@ -231,6 +242,13 @@ fn flatten_vector(graphic_list: &List<Graphic>) -> List<Vector> {
|
||||
*fill *= parent_fill;
|
||||
}
|
||||
}
|
||||
if let Some(appearance) = &parent_appearance {
|
||||
for value in graphic.iter_attribute_values_mut_or_default::<Appearance>(ATTR_APPEARANCE) {
|
||||
if value.is_empty() {
|
||||
*value = appearance.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unioning each group lets it enter the outer operation as one region, which is why this cannot defer
|
||||
// to `flatten_graphic_list`. Splicing children in as sibling operands makes them subtract from each other.
|
||||
|
||||
Reference in New Issue
Block a user