mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Correctly apply transforms to vector data and strokes (#1977)
* Fix adding a layer to a transformed group * Fix assorted transform issues * Default stroke transform * Fix bench * Transform gradient * Gradient fix * Add gradient reversal buttons to Fill node in the Properties panel --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
2fa8773092
commit
dd4a97b09f
19 changed files with 152 additions and 59 deletions
|
|
@ -266,7 +266,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
|
|||
|
||||
let bounds_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||
apply_usvg_fill(path.fill(), modify_inputs, transform * usvg_transform(node.abs_transform()), bounds_transform);
|
||||
apply_usvg_stroke(path.stroke(), modify_inputs);
|
||||
apply_usvg_stroke(path.stroke(), modify_inputs, transform * usvg_transform(node.abs_transform()));
|
||||
}
|
||||
usvg::Node::Image(_image) => {
|
||||
warn!("Skip image")
|
||||
|
|
@ -279,7 +279,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_usvg_stroke(stroke: Option<&usvg::Stroke>, modify_inputs: &mut ModifyInputsContext) {
|
||||
fn apply_usvg_stroke(stroke: Option<&usvg::Stroke>, modify_inputs: &mut ModifyInputsContext, transform: DAffine2) {
|
||||
if let Some(stroke) = stroke {
|
||||
if let usvg::Paint::Color(color) = &stroke.paint() {
|
||||
modify_inputs.stroke_set(Stroke {
|
||||
|
|
@ -299,6 +299,7 @@ fn apply_usvg_stroke(stroke: Option<&usvg::Stroke>, modify_inputs: &mut ModifyIn
|
|||
usvg::LineJoin::Bevel => LineJoin::Bevel,
|
||||
},
|
||||
line_join_miter_limit: stroke.miterlimit().get() as f64,
|
||||
transform,
|
||||
})
|
||||
} else {
|
||||
warn!("Skip non-solid stroke")
|
||||
|
|
|
|||
|
|
@ -2597,7 +2597,28 @@ pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
|||
|
||||
let fill_type_switch = {
|
||||
let mut row = vec![TextLabel::new("").widget_holder()];
|
||||
add_blank_assist(&mut row);
|
||||
match fill {
|
||||
Fill::Solid(_) | Fill::None => add_blank_assist(&mut row),
|
||||
Fill::Gradient(gradient) => {
|
||||
let reverse_button = IconButton::new("Reverse", 24)
|
||||
.tooltip("Reverse the gradient color stops")
|
||||
.on_update(update_value(
|
||||
{
|
||||
let gradient = gradient.clone();
|
||||
move |_| {
|
||||
let mut gradient = gradient.clone();
|
||||
gradient.stops = gradient.stops.reversed();
|
||||
TaggedValue::Fill(Fill::Gradient(gradient))
|
||||
}
|
||||
},
|
||||
node_id,
|
||||
fill_index,
|
||||
))
|
||||
.widget_holder();
|
||||
row.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
row.push(reverse_button);
|
||||
}
|
||||
}
|
||||
|
||||
let entries = vec![
|
||||
RadioEntryData::new("solid")
|
||||
|
|
@ -2619,9 +2640,35 @@ pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
|||
};
|
||||
widgets.push(fill_type_switch);
|
||||
|
||||
if let Fill::Gradient(gradient) = fill {
|
||||
if let Fill::Gradient(gradient) = fill.clone() {
|
||||
let mut row = vec![TextLabel::new("").widget_holder()];
|
||||
add_blank_assist(&mut row);
|
||||
match gradient.gradient_type {
|
||||
GradientType::Linear => add_blank_assist(&mut row),
|
||||
GradientType::Radial => {
|
||||
let orientation = if (gradient.end.x - gradient.start.x).abs() > f64::EPSILON * 1e6 {
|
||||
gradient.end.x > gradient.start.x
|
||||
} else {
|
||||
(gradient.start.x + gradient.start.y) < (gradient.end.x + gradient.end.y)
|
||||
};
|
||||
let reverse_radial_gradient_button = IconButton::new(if orientation { "ReverseRadialGradientToRight" } else { "ReverseRadialGradientToLeft" }, 24)
|
||||
.tooltip("Reverse which end the gradient radiates from")
|
||||
.on_update(update_value(
|
||||
{
|
||||
let gradient = gradient.clone();
|
||||
move |_| {
|
||||
let mut gradient = gradient.clone();
|
||||
std::mem::swap(&mut gradient.start, &mut gradient.end);
|
||||
TaggedValue::Fill(Fill::Gradient(gradient))
|
||||
}
|
||||
},
|
||||
node_id,
|
||||
fill_index,
|
||||
))
|
||||
.widget_holder();
|
||||
row.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
row.push(reverse_radial_gradient_button);
|
||||
}
|
||||
}
|
||||
|
||||
let new_gradient1 = gradient.clone();
|
||||
let new_gradient2 = gradient.clone();
|
||||
|
|
|
|||
|
|
@ -206,16 +206,15 @@ impl Fsm for EllipseToolFsmState {
|
|||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
shape_data.layer = Some(layer);
|
||||
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
});
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
shape_data.layer = Some(layer);
|
||||
|
||||
EllipseToolFsmState::Drawing
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ impl Fsm for FreehandToolFsmState {
|
|||
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
let transform = document.metadata().transform_to_viewport(layer);
|
||||
let transform = document.metadata().transform_to_viewport(parent);
|
||||
let position = transform.inverse().transform_point2(input.mouse.position);
|
||||
|
||||
extend_path_with_next_segment(tool_data, position, responses);
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ impl LayoutHolder for GradientTool {
|
|||
let gradient_type = RadioInput::new(vec![
|
||||
RadioEntryData::new("linear")
|
||||
.label("Linear")
|
||||
.tooltip("Linear Gradient")
|
||||
.tooltip("Linear gradient")
|
||||
.on_update(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Linear)).into()),
|
||||
RadioEntryData::new("radial")
|
||||
.label("Radial")
|
||||
.tooltip("Radial Gradient")
|
||||
.tooltip("Radial gradient")
|
||||
.on_update(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Radial)).into()),
|
||||
])
|
||||
.selected_index(Some((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32))
|
||||
|
|
|
|||
|
|
@ -188,15 +188,14 @@ impl Fsm for LineToolFsmState {
|
|||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
});
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
tool_data.layer = Some(layer);
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
|
|
|||
|
|
@ -265,16 +265,15 @@ impl Fsm for PolygonToolFsmState {
|
|||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
polygon_data.layer = Some(layer);
|
||||
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
});
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
polygon_data.layer = Some(layer);
|
||||
|
||||
PolygonToolFsmState::Drawing
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,16 +212,15 @@ impl Fsm for RectangleToolFsmState {
|
|||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
shape_data.layer = Some(layer);
|
||||
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
});
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
shape_data.layer = Some(layer);
|
||||
|
||||
RectangleToolFsmState::Drawing
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ impl NodeRuntime {
|
|||
|
||||
for monitor_node_path in &self.monitor_nodes {
|
||||
// The monitor nodes are located within a document node, and are thus children in that network, so this gets the parent document node's ID
|
||||
let Some(parent_network_node_id) = monitor_node_path.get(monitor_node_path.len() - 2).copied() else {
|
||||
let Some(parent_network_node_id) = monitor_node_path.len().checked_sub(2).and_then(|index| monitor_node_path.get(index)).copied() else {
|
||||
warn!("Monitor node has invalid node id");
|
||||
|
||||
continue;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue