Store an appearance's paint as a bare Graphic rather than a single-element List<Graphic> (#4435)

Rank the coverage's paint attribute down from List<Graphic> to Graphic
This commit is contained in:
Keavon Chambers
2026-08-14 22:13:00 -07:00
committed by GitHub
parent 69585b25e3
commit ba7cbd83bc
10 changed files with 215 additions and 84 deletions

View File

@@ -2760,9 +2760,7 @@ impl DocumentMessageHandler {
// A visible stroke needs both renderable geometry (non-zero weight) and paint that draws something
let has_stroke = appearance.is_some_and(|appearance| {
appearance.first_coverage_of(Cover::Stroke).is_some_and(|coverage| coverage.stroke_params().has_renderable_stroke())
&& appearance
.first_paint_of(Cover::Stroke)
.is_some_and(|paint| paint.element(0).is_some_and(|graphic| !graphic.is_fully_transparent()))
&& appearance.first_paint_of(Cover::Stroke).is_some_and(|paint| !paint.is_fully_transparent())
});
// No stroke means there's nothing to solidify. Fill-only layers are already in the desired form, so skip.

View File

@@ -10,7 +10,7 @@ use glam::{DVec2, IVec2};
use graph_craft::application_io::resource::{DataSource, Resource, ResourceHash, ResourceId};
use graph_craft::document::DocumentNode;
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
use graph_craft::{Type, item};
use graph_craft::{Type, item, list};
use graphene_std::Color;
use graphene_std::ParameterRef;
use graphene_std::ProtoNodeIdentifier;
@@ -1894,6 +1894,28 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
.set_input(&InputConnector::node(*node_id, graphene_std::vector::stroke::DashPatternInput), migrated, network_path);
}
// The stored no-paint sentinel was the `List<Graphic>` type default before the paint connectors ranked down to `Item<Graphic>`.
// This must run before the stale-List-default cleanup below, which would otherwise adopt the definition's default paint.
{
let legacy_no_paint = TaggedValue::TypeDefault(list!(graphene_std::Graphic));
let paint_parameters: &[ParameterRef] = &[graphene_std::vector::fill::FillInput.into(), graphene_std::vector::stroke::PaintInput.into()];
for parameter in paint_parameters {
if reference != DefinitionIdentifier::ProtoNode(parameter.node_identifier.clone()) {
continue;
}
let Some(NodeInput::Value { tagged_value, exposed }) = node.inputs.get(parameter.input_index) else {
continue;
};
if **tagged_value == legacy_no_paint {
document.network_interface.set_input(
&InputConnector::node_at_index(*node_id, parameter.input_index),
NodeInput::value(TaggedValue::no_paint(), *exposed),
network_path,
);
}
}
}
// The corner radius became the `BoxCorners` value type; convert any already-shaped rectangle that still stores a legacy corner input
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::rectangle::IDENTIFIER)
&& let Some(corner_input) = node.input(graphene_std::vector::generator_nodes::rectangle::CornerRadiusInput)