mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Split Fill's optional transform into a has-transform toggle and add the DashPattern and BoxCorners value types
This commit is contained in:
committed by
Dennis Kobert
parent
a373fad0ca
commit
1bac1d694f
@@ -492,9 +492,14 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
.and_then(|node| node.inputs.get(graphene_std::vector::fill::TransformInput::INDEX))
|
||||
.is_some_and(|input| input.as_value().is_some());
|
||||
if transform_is_value {
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::HasTransformInput::INDEX),
|
||||
NodeInput::value(TaggedValue::Bool(true), false),
|
||||
true,
|
||||
);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::TransformInput::INDEX),
|
||||
NodeInput::value(TaggedValue::OptionalDAffine2(Some(transform)), false),
|
||||
NodeInput::value(TaggedValue::DAffine2(transform), false),
|
||||
true,
|
||||
);
|
||||
}
|
||||
@@ -733,8 +738,8 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.join_miter_limit), false), false);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintOrderInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::PaintOrder(stroke.paint_order), false), false);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashLengthsInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64Array(stroke.dash_lengths), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashPatternInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::DashPattern(stroke.dash_lengths.into()), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashOffsetInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,12 @@ use graphene_std::text::{Font, TextAlign};
|
||||
use graphene_std::text_nodes::StringCapitalization;
|
||||
use graphene_std::transform::{Footprint, ReferencePoint, ScaleType, Transform};
|
||||
use graphene_std::vector::misc::BooleanOperation;
|
||||
use graphene_std::vector::misc::{ArcType, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType};
|
||||
use graphene_std::vector::style::{FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation};
|
||||
use graphene_std::vector::misc::{
|
||||
ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
|
||||
};
|
||||
use graphene_std::vector::style::{
|
||||
DashPattern, FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation,
|
||||
};
|
||||
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
|
||||
|
||||
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
|
||||
@@ -861,6 +865,32 @@ pub fn array_of_number_widget(parameter_widgets_info: ParameterWidgetsInfo, text
|
||||
widgets
|
||||
}
|
||||
|
||||
pub fn dash_pattern_widget(parameter_widgets_info: ParameterWidgetsInfo, text_input: TextInput) -> Vec<WidgetInstance> {
|
||||
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
|
||||
|
||||
let mut widgets = start_widgets(parameter_widgets_info);
|
||||
|
||||
let Some(document_node) = document_node else { return Vec::new() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return vec![];
|
||||
};
|
||||
if let Some(TaggedValue::DashPattern(pattern)) = &input.as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
text_input
|
||||
.value(pattern.0.iter_element_values().map(|length| length.to_string()).collect::<Vec<_>>().join(", "))
|
||||
.on_update(optionally_update_value(
|
||||
move |input: &TextInput| Some(TaggedValue::DashPattern(DashPattern::from(input.value.as_str()))),
|
||||
node_id,
|
||||
index,
|
||||
))
|
||||
.widget_instance(),
|
||||
])
|
||||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetInstance>, Option<Vec<WidgetInstance>>) {
|
||||
pub fn assign_font_message(node_id: NodeId, font: Font) -> Message {
|
||||
let resource_id = ResourceId::new();
|
||||
@@ -2205,16 +2235,11 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return vec![];
|
||||
};
|
||||
let uniform_val = match input.as_non_exposed_value() {
|
||||
Some(TaggedValue::F64(x)) => *x,
|
||||
Some(TaggedValue::F64Array(values)) => values.first().copied().unwrap_or(0.),
|
||||
_ => 0.,
|
||||
};
|
||||
let individual_val = match input.as_non_exposed_value() {
|
||||
Some(&TaggedValue::F64(x)) => vec![x; 4],
|
||||
Some(TaggedValue::F64Array(values)) => values.clone(),
|
||||
_ => vec![0.; 4],
|
||||
let corner_values = match input.as_non_exposed_value() {
|
||||
Some(TaggedValue::BoxCorners(corners)) => corners.to_corner_values(),
|
||||
_ => [0.; 4],
|
||||
};
|
||||
let uniform_val = corner_values[0];
|
||||
|
||||
// Uniform/individual radio input widget
|
||||
let uniform = RadioEntryData::new("Uniform")
|
||||
@@ -2230,13 +2255,12 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: CornerRadiusInput::INDEX,
|
||||
value: Box::new(TaggedValue::F64(uniform_val)),
|
||||
value: Box::new(TaggedValue::BoxCorners(BoxCorners::from(uniform_val))),
|
||||
}
|
||||
.into(),
|
||||
]),
|
||||
})
|
||||
.on_commit(commit_value);
|
||||
let individual_val_for_switch = individual_val.clone();
|
||||
let individual = RadioEntryData::new("Individual")
|
||||
.label("Individual")
|
||||
.on_update(move |_| Message::Batched {
|
||||
@@ -2250,7 +2274,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: CornerRadiusInput::INDEX,
|
||||
value: Box::new(TaggedValue::F64Array(individual_val_for_switch.clone())),
|
||||
value: Box::new(TaggedValue::BoxCorners(BoxCorners::from(corner_values.to_vec()))),
|
||||
}
|
||||
.into(),
|
||||
]),
|
||||
@@ -2261,24 +2285,23 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
||||
|
||||
// Radius value input widget
|
||||
let input_widget = if is_individual {
|
||||
let from_string = |string: &str| {
|
||||
string
|
||||
.split(&[',', ' '])
|
||||
.filter(|x| !x.is_empty())
|
||||
.map(str::parse::<f64>)
|
||||
.collect::<Result<Vec<f64>, _>>()
|
||||
.ok()
|
||||
.map(|values| TaggedValue::F64Array(values.into_iter().take(4).collect()))
|
||||
};
|
||||
TextInput::default()
|
||||
.value(individual_val.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", "))
|
||||
.on_update(optionally_update_value(move |x: &TextInput| from_string(&x.value), node_id, CornerRadiusInput::INDEX))
|
||||
.value(corner_values.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", "))
|
||||
.on_update(optionally_update_value(
|
||||
move |x: &TextInput| Some(TaggedValue::BoxCorners(BoxCorners::from(x.value.as_str()))),
|
||||
node_id,
|
||||
CornerRadiusInput::INDEX,
|
||||
))
|
||||
.widget_instance()
|
||||
} else {
|
||||
NumberInput::default()
|
||||
.value(Some(uniform_val))
|
||||
.unit(" px")
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, CornerRadiusInput::INDEX))
|
||||
.on_update(update_value(
|
||||
move |x: &NumberInput| TaggedValue::BoxCorners(BoxCorners::from(x.value.unwrap())),
|
||||
node_id,
|
||||
CornerRadiusInput::INDEX,
|
||||
))
|
||||
.on_commit(commit_value)
|
||||
.widget_instance()
|
||||
};
|
||||
@@ -2675,7 +2698,22 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
} else {
|
||||
"Swap the start and end points of the gradient line."
|
||||
})
|
||||
.on_update(update_value(move |_| TaggedValue::OptionalDAffine2(Some(new_transform)), node_id, TransformInput::INDEX))
|
||||
.on_update(move |_| Message::Batched {
|
||||
messages: Box::new([
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: HasTransformInput::INDEX,
|
||||
value: Box::new(TaggedValue::Bool(true)),
|
||||
}
|
||||
.into(),
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: TransformInput::INDEX,
|
||||
value: Box::new(TaggedValue::DAffine2(new_transform)),
|
||||
}
|
||||
.into(),
|
||||
]),
|
||||
})
|
||||
.widget_instance();
|
||||
spread_methods_row.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
spread_methods_row.push(reverse_direction_button);
|
||||
@@ -2719,8 +2757,8 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
|
||||
_ => &StrokeJoin::Miter,
|
||||
};
|
||||
|
||||
let has_dash_lengths = match &document_node.inputs[DashLengthsInput::INDEX].as_value() {
|
||||
Some(TaggedValue::F64Array(values)) => values.is_empty(),
|
||||
let has_dash_lengths = match &document_node.inputs[DashPatternInput::INDEX].as_value() {
|
||||
Some(TaggedValue::DashPattern(pattern)) => pattern.0.is_empty(),
|
||||
_ => true,
|
||||
};
|
||||
let miter_limit_disabled = join_value != &StrokeJoin::Miter;
|
||||
@@ -2746,7 +2784,7 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
|
||||
.for_socket(ParameterWidgetsInfo::new(node_id, PaintOrderInput::INDEX, true, context))
|
||||
.property_row();
|
||||
let disabled_number_input = NumberInput::default().unit(" px").disabled(has_dash_lengths);
|
||||
let dash_lengths = array_of_number_widget(ParameterWidgetsInfo::new(node_id, DashLengthsInput::INDEX, true, context), TextInput::default().centered(true));
|
||||
let dash_lengths = dash_pattern_widget(ParameterWidgetsInfo::new(node_id, DashPatternInput::INDEX, true, context), TextInput::default().centered(true));
|
||||
let number_input = disabled_number_input;
|
||||
let dash_offset = number_widget(ParameterWidgetsInfo::new(node_id, DashOffsetInput::INDEX, true, context), number_input);
|
||||
|
||||
|
||||
@@ -765,3 +765,46 @@ async fn none_fill_survives_document_reopen() {
|
||||
let reopened_paint = fill_paint_value(editor.active_document());
|
||||
assert!(reopened_paint.is_no_paint(), "a none fill should survive reopening, but the stored paint became {reopened_paint:?}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn legacy_four_input_fill_migrates_to_the_split_transform_shape() {
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::NodeInputDecleration as _;
|
||||
|
||||
// A minimal master-era document: a 4-input Fill (content, fill: wired, backup color, backup gradient) fed by another node
|
||||
const LEGACY_DOCUMENT: &str = r#"{"network_interface":{"network":{"exports":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"nodes":[[1,{"inputs":[{"Value":{"tagged_value":{"GraphicGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Node":{"node_id":2,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"Gradient":{"stops":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]],"gradient_type":"Linear","start":[0.0,0.5],"end":[1.0,0.5],"transform":[1.0,0.0,0.0,1.0,0.0,0.0]}},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::vector::FillNode"}},"visible":true,"skip_deduplication":false}],[2,{"inputs":[{"Value":{"tagged_value":"None","exposed":false}},{"Value":{"tagged_value":{"GradientStops":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}},{"Value":{"tagged_value":{"F64":0.5},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::ops::SampleGradientNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]},"network_metadata":{"persistent_metadata":{"node_metadata":[[1,{"persistent_metadata":{"reference":"Fill","display_name":"","input_properties":[{"input_data":{"input_name":"Vector Data"},"widget_override":null},{"input_data":{"input_name":"Fill"},"widget_override":null},{"input_data":{"input_name":"Backup Color"},"widget_override":null},{"input_data":{"input_name":"Backup Gradient"},"widget_override":null}],"output_names":["Future<Instances<VectorData>>"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}],[2,{"persistent_metadata":{"reference":"Sample Gradient","display_name":"","input_properties":[{"input_data":{"input_name":"Primary"},"widget_override":null},{"input_data":{"input_name":"Gradient"},"widget_override":null},{"input_data":{"input_name":"Position"},"widget_override":null}],"output_names":["Future<Color>"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-20,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}},"collapsed":[],"name":"legacy_fill.graphite","commit_hash":"0000000000000000000000000000000000000000","document_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"document_mode":"DesignMode","view_mode":"Normal","overlays_visibility_settings":{"all":true,"artboard_name":true,"compass_rose":true,"quick_measurement":true,"transform_measurement":true,"transform_cage":true,"hover_outline":true,"selection_outline":true,"pivot":true,"path":true,"anchors":true,"handles":true},"rulers_visible":true,"snapping_state":{"snapping_enabled":true,"grid_snapping":false,"artboards":true,"tolerance":8.0,"bounding_box":{"center_point":true,"corner_point":true,"edge_midpoint":true,"align_with_edges":true,"distribute_evenly":true},"path":{"anchor_point":true,"line_midpoint":true,"along_path":true,"normal_to_path":true,"tangent_to_path":true,"path_intersection_point":true,"align_with_anchor_point":true,"perpendicular_from_endpoint":true},"grid":{"origin":[0.0,0.0],"grid_type":{"Rectangular":{"spacing":[1.0,1.0]}},"grid_color":{"red":0.6,"green":0.6,"blue":0.6,"alpha":1.0},"dot_display":false}},"graph_view_overlay_open":false,"graph_fade_artwork_percentage":80.0}"#;
|
||||
|
||||
// Deserializing alone must succeed, so a failure below is attributable to the migrations
|
||||
DocumentMessageHandler::deserialize_document(LEGACY_DOCUMENT).expect("the legacy document should deserialize");
|
||||
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor
|
||||
.handle_message(PortfolioMessage::OpenDocumentFile {
|
||||
document_name: None,
|
||||
document_path: None,
|
||||
document_serialized_content: LEGACY_DOCUMENT.to_string(),
|
||||
})
|
||||
.await;
|
||||
|
||||
let document = editor.active_document();
|
||||
let (network_path, node_id) = find_fill_node(document);
|
||||
let network = document.network_interface.nested_network(&network_path).expect("the found network path should resolve");
|
||||
let inputs = &network.nodes[&node_id].inputs;
|
||||
|
||||
assert_eq!(inputs.len(), 8, "the legacy Fill should upgrade to the 8-input shape");
|
||||
let paint = &inputs[graphene_std::vector::fill::FillInput::INDEX];
|
||||
assert!(
|
||||
matches!(paint, graph_craft::document::NodeInput::Node { .. }),
|
||||
"the wired legacy fill should keep its connection, but became {paint:?}"
|
||||
);
|
||||
let has_transform = inputs[graphene_std::vector::fill::HasTransformInput::INDEX].as_value();
|
||||
assert!(
|
||||
matches!(has_transform, Some(TaggedValue::Bool(_))),
|
||||
"the has-transform input should hold a bool, but became {has_transform:?}"
|
||||
);
|
||||
let transform = inputs[graphene_std::vector::fill::TransformInput::INDEX].as_value();
|
||||
assert!(
|
||||
matches!(transform, Some(TaggedValue::DAffine2(_))),
|
||||
"the transform input should hold a matrix, but became {transform:?}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ use graphene_std::text::{TextAlign, TypesettingConfig};
|
||||
use graphene_std::transform::ScaleType;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::vector::graphic_types;
|
||||
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
||||
use graphene_std::vector::misc::BoxCorners;
|
||||
use graphene_std::vector::style::{DashPattern, PaintOrder, StrokeAlign};
|
||||
use std::collections::HashMap;
|
||||
use std::f64::consts::PI;
|
||||
use std::ops::Range;
|
||||
@@ -1376,6 +1377,29 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a legacy stroke dash input (a `List<f64>`, single `f64`, or comma/space separated `String`) to the `DashPattern` value type.
|
||||
fn migrate_dash_input(input: &NodeInput) -> Option<NodeInput> {
|
||||
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
||||
let pattern = match &*tagged_value.clone().into_inner() {
|
||||
TaggedValue::F64Array(lengths) => DashPattern::from(lengths.clone()),
|
||||
TaggedValue::F64(length) => DashPattern::from(*length),
|
||||
TaggedValue::String(text) => DashPattern::from(text.as_str()),
|
||||
_ => return None,
|
||||
};
|
||||
Some(NodeInput::value(TaggedValue::DashPattern(pattern), *exposed))
|
||||
}
|
||||
|
||||
/// Converts a legacy rectangle corner radius input (a single `f64` or a `List<f64>` of up to four values) to the `BoxCorners` value type.
|
||||
fn migrate_corner_radius_input(input: &NodeInput) -> Option<NodeInput> {
|
||||
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
||||
let corners = match &*tagged_value.clone().into_inner() {
|
||||
TaggedValue::F64Array(values) => BoxCorners::from(values.clone()),
|
||||
TaggedValue::F64(value) => BoxCorners::from(*value),
|
||||
_ => return None,
|
||||
};
|
||||
Some(NodeInput::value(TaggedValue::BoxCorners(corners), *exposed))
|
||||
}
|
||||
|
||||
fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> {
|
||||
// Must run before the reset block below: a node referencing a removed catalog entry would otherwise abort
|
||||
// `migrate_node` via the `?` on `resolve_document_node_type`, preventing subsequent migration blocks from running.
|
||||
@@ -1640,7 +1664,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
.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
|
||||
// Gradient metadata (4, 5, 6, 7): applies only to a literal gradient, solids/none keep the template defaults
|
||||
if let graphic_types::migrations::legacy::LegacyFill::Gradient(gradient) = old_fill {
|
||||
document.network_interface.set_input(
|
||||
&InputConnector::node(*node_id, 4),
|
||||
@@ -1653,16 +1677,19 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
network_path,
|
||||
);
|
||||
|
||||
let transform = if gradient.absolute {
|
||||
Some(gradient.transform * gradient.to_transform())
|
||||
if gradient.absolute {
|
||||
let transform = gradient.transform * gradient.to_transform();
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 7), NodeInput::value(TaggedValue::DAffine2(transform), false), network_path);
|
||||
} else {
|
||||
// Baking a legacy bounding-box-relative gradient is deferred until the measurement pre-pass can supply the paint target's bounds
|
||||
// Baking a legacy bounding-box-relative gradient is deferred until the measurement pre-pass can supply the paint
|
||||
// target's bounds, so the template's unbaked `_has_transform = false` stands until the bake lands
|
||||
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.
|
||||
@@ -1697,19 +1724,54 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
network_path,
|
||||
);
|
||||
|
||||
let transform = if g.absolute {
|
||||
Some(g.transform * g.to_transform())
|
||||
if g.absolute {
|
||||
let transform = g.transform * g.to_transform();
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 7), NodeInput::value(TaggedValue::DAffine2(transform), false), network_path);
|
||||
} 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;
|
||||
inputs_count = 8;
|
||||
}
|
||||
|
||||
// Fill split its `Option<DAffine2>` placement into a `_has_transform` bool immediately before the `_transform` matrix
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER) && inputs_count == 7 {
|
||||
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
||||
|
||||
for (index, input) in old_inputs.iter().enumerate().take(6) {
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path);
|
||||
}
|
||||
|
||||
match old_inputs.get(6).and_then(|input| input.as_value()) {
|
||||
Some(TaggedValue::LegacyOptionalDAffine2(value)) => {
|
||||
let has_transform = value.is_some();
|
||||
let transform = value.unwrap_or(glam::DAffine2::IDENTITY);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(has_transform), false), network_path);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 7), NodeInput::value(TaggedValue::DAffine2(transform), false), network_path);
|
||||
}
|
||||
// A wired (or otherwise non-value) transform keeps its connection and is treated as present
|
||||
_ => {
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
||||
let transform_input = old_inputs.get(6).cloned().unwrap_or_else(|| NodeInput::value(TaggedValue::DAffine2(glam::DAffine2::IDENTITY), false));
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 7), transform_input, network_path);
|
||||
}
|
||||
}
|
||||
|
||||
inputs_count = 8;
|
||||
}
|
||||
|
||||
// Upgrade Stroke node to reorder parameters and add "Align" and "Paint Order" (#2644)
|
||||
@@ -1728,7 +1790,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[6].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 6), old_inputs[7].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 7), paint_order_input, network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 8), old_inputs[3].clone(), network_path);
|
||||
let dash_input = migrate_dash_input(&old_inputs[3]).unwrap_or_else(|| old_inputs[3].clone());
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 8), dash_input, network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 9), old_inputs[4].clone(), network_path);
|
||||
}
|
||||
|
||||
@@ -1783,6 +1846,36 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
}
|
||||
}
|
||||
|
||||
// The stroke dash sequence became the `DashPattern` value type; convert any already-shaped stroke that still stores a legacy dash input
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER)
|
||||
&& let Some(dash_input) = node.inputs.get(graphene_std::vector::stroke::DashPatternInput::INDEX)
|
||||
&& let Some(migrated) = migrate_dash_input(dash_input)
|
||||
{
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, graphene_std::vector::stroke::DashPatternInput::INDEX), migrated, network_path);
|
||||
}
|
||||
|
||||
// The rectangle's corner radius became the `BoxCorners` value type and its hidden individual-radii toggle moved after the
|
||||
// user-visible inputs. A legacy rectangle stores that toggle (a plain `bool`) at index 3, where the new shape stores the corner
|
||||
// radius, so a `bool` value there identifies the old input order: [width, height, individual, corner_radius, clamped].
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::rectangle::IDENTIFIER)
|
||||
&& let Some(toggle_input) = node.inputs.get(3)
|
||||
&& matches!(toggle_input.as_value(), Some(TaggedValue::Bool(_)))
|
||||
{
|
||||
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
||||
|
||||
let corner_radius = migrate_corner_radius_input(&old_inputs[4]).unwrap_or_else(|| old_inputs[4].clone());
|
||||
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 3), corner_radius, network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
|
||||
}
|
||||
|
||||
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
|
||||
if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_std::text::TextNode")) && inputs_count == 8 {
|
||||
let mut template: NodeTemplate = legacy_text_node_template()?;
|
||||
|
||||
@@ -569,8 +569,8 @@ pub fn get_stroke_options(layer: LayerNodeIdentifier, network_interface: &NodeNe
|
||||
Some(TaggedValue::PaintOrder(value)) => *value,
|
||||
_ => PaintOrder::default(),
|
||||
};
|
||||
let dash_lengths = match read(graphene_std::vector::stroke::DashLengthsInput::INDEX) {
|
||||
Some(TaggedValue::F64Array(value)) => value.clone(),
|
||||
let dash_lengths = match read(graphene_std::vector::stroke::DashPatternInput::INDEX) {
|
||||
Some(TaggedValue::DashPattern(value)) => value.0.iter_element_values().copied().collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let dash_offset = match read(graphene_std::vector::stroke::DashOffsetInput::INDEX) {
|
||||
@@ -648,9 +648,11 @@ pub fn read_fill_node_gradient(fill_node: &DocumentNode, bounding_box: impl FnOn
|
||||
Some(&TaggedValue::GradientSpreadMethod(value)) => value,
|
||||
_ => GradientSpreadMethod::default(),
|
||||
};
|
||||
let has_transform = matches!(fill_node.inputs.get(fill::HasTransformInput::INDEX).and_then(|input| input.as_value()), Some(&TaggedValue::Bool(true)));
|
||||
let transform_input = fill_node.inputs.get(fill::TransformInput::INDEX).and_then(|input| input.as_value());
|
||||
let transform = match transform_input {
|
||||
Some(&TaggedValue::OptionalDAffine2(value)) => value.unwrap_or_else(|| initial_gradient_transform_for_bounding_box(bounding_box())),
|
||||
let transform = match (has_transform, transform_input) {
|
||||
(true, Some(&TaggedValue::DAffine2(value))) => value,
|
||||
(false, _) => initial_gradient_transform_for_bounding_box(bounding_box()),
|
||||
_ => DAffine2::IDENTITY,
|
||||
};
|
||||
|
||||
@@ -803,10 +805,10 @@ pub fn set_fill_for_selected_layers(fill_choice: FillChoice, document: &Document
|
||||
Some(TaggedValue::GradientSpreadMethod(value)) => *value,
|
||||
_ => GradientSpreadMethod::default(),
|
||||
};
|
||||
let transform = match read(graphene_std::vector::fill::TransformInput::INDEX) {
|
||||
Some(TaggedValue::OptionalDAffine2(value)) => {
|
||||
value.unwrap_or_else(|| initial_gradient_transform_for_bounding_box(document.network_interface.document_metadata().nonzero_bounding_box(layer)))
|
||||
}
|
||||
let has_transform = matches!(read(graphene_std::vector::fill::HasTransformInput::INDEX), Some(TaggedValue::Bool(true)));
|
||||
let transform = match (has_transform, read(graphene_std::vector::fill::TransformInput::INDEX)) {
|
||||
(true, Some(TaggedValue::DAffine2(value))) => *value,
|
||||
(false, _) => initial_gradient_transform_for_bounding_box(document.network_interface.document_metadata().nonzero_bounding_box(layer)),
|
||||
_ => DAffine2::IDENTITY,
|
||||
};
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ pub fn apply_paint_order(drawing: &mut DrawingToolState, order: PaintOrder, docu
|
||||
|
||||
pub fn apply_dash_lengths(drawing: &mut DrawingToolState, lengths: Vec<f64>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
drawing.dash_lengths = Some(lengths.clone());
|
||||
set_stroke_input_for_selected(document, graphene_std::vector::stroke::DashLengthsInput::INDEX, TaggedValue::F64Array(lengths), responses);
|
||||
set_stroke_input_for_selected(document, graphene_std::vector::stroke::DashPatternInput::INDEX, TaggedValue::DashPattern(lengths.into()), responses);
|
||||
}
|
||||
|
||||
pub fn apply_dash_offset(drawing: &mut DrawingToolState, offset: f64, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
|
||||
@@ -2067,8 +2067,9 @@ mod test_gradient {
|
||||
_ => GradientSpreadMethod::default(),
|
||||
};
|
||||
|
||||
let has_transform = matches!(fill_node.inputs.get(fill::HasTransformInput::INDEX).and_then(|input| input.as_value()), Some(&TaggedValue::Bool(true)));
|
||||
let local_transform = match fill_node.inputs.get(fill::TransformInput::INDEX).and_then(|input| input.as_value()) {
|
||||
Some(&TaggedValue::OptionalDAffine2(Some(value))) => value,
|
||||
Some(&TaggedValue::DAffine2(value)) if has_transform => value,
|
||||
_ => DAffine2::IDENTITY,
|
||||
};
|
||||
|
||||
|
||||
@@ -625,10 +625,14 @@ impl NodeGraphExecutor {
|
||||
if fill_transform_unbaked(document, &network_path, fill_node_id) {
|
||||
let absolute_gradient = gradient.to_absolute(bounding_box, item_transform);
|
||||
let gradient_transform = absolute_gradient.transform * absolute_gradient.to_transform();
|
||||
let input = InputConnector::node(fill_node_id, graphene_std::vector::fill::TransformInput::INDEX);
|
||||
let has_transform_input = InputConnector::node(fill_node_id, graphene_std::vector::fill::HasTransformInput::INDEX);
|
||||
let transform_input = InputConnector::node(fill_node_id, graphene_std::vector::fill::TransformInput::INDEX);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&input, NodeInput::value(TaggedValue::OptionalDAffine2(Some(gradient_transform)), false), &network_path);
|
||||
.set_input(&has_transform_input, NodeInput::value(TaggedValue::Bool(true), false), &network_path);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&transform_input, NodeInput::value(TaggedValue::DAffine2(gradient_transform), false), &network_path);
|
||||
}
|
||||
|
||||
// The transform is settled, so its entry no longer needs to persist for a retry on the next open
|
||||
@@ -840,8 +844,8 @@ fn fill_transform_unbaked(document: &DocumentMessageHandler, network_path: &[Nod
|
||||
};
|
||||
let Some(node) = network.nodes.get(&fill_node_id) else { return false };
|
||||
matches!(
|
||||
node.inputs.get(graphene_std::vector::fill::TransformInput::INDEX).and_then(|input| input.as_value()),
|
||||
Some(TaggedValue::OptionalDAffine2(None))
|
||||
node.inputs.get(graphene_std::vector::fill::HasTransformInput::INDEX).and_then(|input| input.as_value()),
|
||||
Some(TaggedValue::Bool(false))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user