mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 08:48:12 +08:00
Refactor the 'Fill' and 'Stroke' nodes to set "fill" and "stroke" attributes for paints (#4257)
* Allow using any graphics type for fill * Adapt gradient/fill property panels and tools to handle List<T> * Make the initial gradient transform covers the target's bounding box * Introduce AnyGraphicListDyn to avoid combinatorial explosion * Allow using any graphics type for stroke paint * Add FIll node migration * Add `for_each_vector_list_mut` instead of `set_paint_attribute` * Adapt paint flow to read and write attributes instead of legacy Fill/Stroke.color * Fix responsibilities of paint related node input setters * Fix Morph by storing List<Graphic> for attributes rather than the concrete types Store fill/stroke paints as List<Graphic> so Color/Gradient transitions do not hit set_attribute_value_dyn's type-mismatch fallback to default paint. * Preserve paint attributes in vector editing ops * Enhance the clarity between direct and chained fill gradients * Update demo arts * Consolidate Fill node gradient appearance inputs * Fix after the cubic review * Revert "Consolidate Fill node gradient appearance inputs" This reverts commit 9622feb20196e2c4da99e98ca95dcc2e34c2c98e. * Replace AnyGraphicListDyn with generic paint connectors on the Fill and Stroke nodes * Canonicalize paint attribute storage to List<Graphic> with a single write helper * Fix Solidify Stroke missing fills stored in the legacy style * Fix Solidify Stroke producing invisible strokes for legacy-only stroke colors * Fix the initial gradient transform ignoring the bounding box's vertical extent * Step paint at the morph midpoint instead of dropping it for unmixable pairings * Remove migration-stage comments * Clarify the initial gradient transform helper's doc and name * Correct the bake_paint_transforms doc and prune dead tolerance arms * Delete the unused Gradient::lerp * Fix a comment typo * Thread network paths through the legacy gradient bake so nested fills migrate * Restore the legacy fill fallback in Expand Fill and Stroke * Read gradient stops from the node's own input in the Fill properties panel * Use the transform input constant instead of a hardcoded index * Remove the unreachable wired color fallback in the Fill properties solid branch * Narrow the fill overlay redraw check to actual fill inputs * Coalesce the fill setter's graph runs into a single dispatch * Position the Gradient Value node inserted for gradient stops * Bake backup gradient placement during migration * Persist pending gradient bakes so unfinished migrations retry on reopen * Migrate the backup gradient's type and spread method * Harden the gradient-migration pass against document switches and stale bakes * Keep the Fill properties UI for layerless and nested Fill nodes * Leave a wired gradient transform input connected instead of overwriting it * Refuse to start a gradient chain ahead of existing layer content * Decode a Fill node's gradient through one shared reader * Nudge a degenerate bounding box so the Fill gradient transform stays invertible * Broadcast Fill and Stroke paint with a single attribute-column pass * Fix Morph stepping the target's stroke in near the source instead of the target * Tidy conventions: clippy get_first, comment periods, sentence-case test messages * Reattach the gradient orientation doc to its function * Re-save demo artwork --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
f82b0a8fca
commit
9f9899cfd0
@@ -5,7 +5,6 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector};
|
||||
use crate::messages::prelude::DocumentMessageHandler;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use glam::{DVec2, IVec2};
|
||||
use graph_craft::application_io::resource::{DataSource, Resource, ResourceHash, ResourceId};
|
||||
use graph_craft::descriptor;
|
||||
@@ -15,7 +14,7 @@ use graphene_std::ProtoNodeIdentifier;
|
||||
use graphene_std::text::{TextAlign, TypesettingConfig};
|
||||
use graphene_std::transform::ScaleType;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
||||
use graphene_std::vector::style::{Fill, PaintOrder, StrokeAlign};
|
||||
use std::collections::HashMap;
|
||||
use std::f64::consts::PI;
|
||||
use std::ops::Range;
|
||||
@@ -1122,9 +1121,6 @@ pub fn document_migration_replace_resources_referenced_by_hash(document_serializ
|
||||
pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) {
|
||||
document.network_interface.migrate_path_modify_node();
|
||||
|
||||
// Legacy `Fill::Gradient`s are converted to absolute by the deferred migration pre-pass that measures each fill's geometry
|
||||
document.pending_gradient_migration = !graph_modification_utils::legacy_gradient_fill_nodes(&document.network_interface).is_empty();
|
||||
|
||||
let network = document.network_interface.document_network().clone();
|
||||
|
||||
// Apply string and node replacements to each node
|
||||
@@ -1575,6 +1571,96 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
inputs_count = 5;
|
||||
}
|
||||
|
||||
// Upgrade the legacy 4-input Fill node (content, fill: Fill, _backup_color, _backup_gradient: Gradient) to the
|
||||
// value-model 7-input shape (content, fill: generic paint list, _backup_color, _backup_gradient, _gradient_type, _spread_method, _transform).
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::fill::IDENTIFIER) && inputs_count == 4 {
|
||||
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
||||
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
||||
|
||||
// Content: no change
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
||||
|
||||
// Fill: a literal Fill value is decomposed, and a wired input (`List<GradientStops> / List<Color>`) is kept as-is
|
||||
match old_inputs[1].as_value() {
|
||||
Some(TaggedValue::Fill(old_fill)) => {
|
||||
let exposed = old_inputs[1].is_exposed();
|
||||
let fill_value = match old_fill {
|
||||
Fill::None => TaggedValue::Color(None),
|
||||
Fill::Solid(color) => TaggedValue::Color(Some(*color)),
|
||||
Fill::Gradient(gradient) => TaggedValue::Gradient(gradient.stops.clone()),
|
||||
};
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(fill_value, exposed), network_path);
|
||||
|
||||
// Gradient metadata (4, 5, 6): applies only to a literal gradient, solids/none keep the template defaults
|
||||
if let Fill::Gradient(gradient) = old_fill {
|
||||
document.network_interface.set_input(
|
||||
&InputConnector::node(*node_id, 4),
|
||||
NodeInput::value(TaggedValue::GradientType(gradient.gradient_type), false),
|
||||
network_path,
|
||||
);
|
||||
document.network_interface.set_input(
|
||||
&InputConnector::node(*node_id, 5),
|
||||
NodeInput::value(TaggedValue::GradientSpreadMethod(gradient.spread_method), false),
|
||||
network_path,
|
||||
);
|
||||
|
||||
let transform = if gradient.absolute {
|
||||
Some(gradient.transform * gradient.to_transform())
|
||||
} else {
|
||||
// Baking a legacy bounding-box-relative gradient is deferred until the measurement pre-pass can supply the paint target's bounds
|
||||
document.pending_gradient_bbox_bake.push((network_path.to_vec(), *node_id, gradient.clone()));
|
||||
None
|
||||
};
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::OptionalDAffine2(transform), false), network_path);
|
||||
}
|
||||
}
|
||||
// Wired/exposed fill keeps the connection.
|
||||
// The generic paint connector accepts `List<Color>`/`List<GradientStops>` sources directly, and there were no other nodes which can generate output type that implements `From` for `Fill`.
|
||||
_ => {
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Color backup: no change
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
||||
|
||||
// Gradient backup: extract stops
|
||||
if let Some(TaggedValue::FillGradient(g)) = old_inputs[3].as_value() {
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Gradient(g.stops.clone()), false), network_path);
|
||||
|
||||
// A solid/no-fill node leaves the gradient metadata inputs unused, so seed them from the backup gradient for a later Solid -> Gradient toggle to restore
|
||||
if matches!(old_inputs[1].as_value(), Some(TaggedValue::Fill(Fill::None | Fill::Solid(_)))) {
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 4), NodeInput::value(TaggedValue::GradientType(g.gradient_type), false), network_path);
|
||||
document.network_interface.set_input(
|
||||
&InputConnector::node(*node_id, 5),
|
||||
NodeInput::value(TaggedValue::GradientSpreadMethod(g.spread_method), false),
|
||||
network_path,
|
||||
);
|
||||
|
||||
let transform = if g.absolute {
|
||||
Some(g.transform * g.to_transform())
|
||||
} else {
|
||||
document.pending_gradient_bbox_bake.push((network_path.to_vec(), *node_id, g.clone()));
|
||||
None
|
||||
};
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::OptionalDAffine2(transform), false), network_path);
|
||||
}
|
||||
}
|
||||
|
||||
inputs_count = 7;
|
||||
}
|
||||
|
||||
// Upgrade Stroke node to reorder parameters and add "Align" and "Paint Order" (#2644)
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER) && inputs_count == 8 {
|
||||
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
||||
|
||||
Reference in New Issue
Block a user