Skip to content

Commit c3667ca

Browse files
authored
Merge branch 'master' into Radio-button-for-sample-by-count-option
2 parents 5d46a2f + 14b198d commit c3667ca

File tree

39 files changed

+1775
-1007
lines changed

39 files changed

+1775
-1007
lines changed

editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ impl PreferencesDialogMessageHandler {
187187
];
188188

189189
let mut checkbox_id = CheckboxId::default();
190-
let vector_mesh_tooltip = "Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle line joins and fills.";
190+
let vector_mesh_tooltip =
191+
"Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle stroke joins and fills.";
191192
let vector_meshes = vec![
192193
Separator::new(SeparatorType::Unrelated).widget_holder(),
193194
Separator::new(SeparatorType::Unrelated).widget_holder(),

editor/src/messages/portfolio/document/document_message.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ pub enum DocumentMessage {
121121
SelectedLayersReorder {
122122
relative_index_offset: isize,
123123
},
124+
ClipLayer {
125+
id: NodeId,
126+
},
124127
SelectLayer {
125128
id: NodeId,
126129
ctrl: bool,
@@ -142,6 +145,9 @@ pub enum DocumentMessage {
142145
SetOpacityForSelectedLayers {
143146
opacity: f64,
144147
},
148+
SetFillForSelectedLayers {
149+
fill: f64,
150+
},
145151
SetOverlaysVisibility {
146152
visible: bool,
147153
overlays_type: Option<OverlaysType>,

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::{Flo
2020
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
2121
use crate::messages::portfolio::utility_types::PersistentData;
2222
use crate::messages::prelude::*;
23-
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_opacity};
23+
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_fill, get_opacity};
2424
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
2525
use crate::messages::tool::tool_messages::tool_prelude::Key;
2626
use crate::messages::tool::utility_types::ToolType;
@@ -1082,6 +1082,12 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
10821082
DocumentMessage::SelectedLayersReorder { relative_index_offset } => {
10831083
self.selected_layers_reorder(relative_index_offset, responses);
10841084
}
1085+
DocumentMessage::ClipLayer { id } => {
1086+
let layer = LayerNodeIdentifier::new(id, &self.network_interface, &[]);
1087+
1088+
responses.add(DocumentMessage::AddTransaction);
1089+
responses.add(GraphOperationMessage::ClipModeToggle { layer });
1090+
}
10851091
DocumentMessage::SelectLayer { id, ctrl, shift } => {
10861092
let layer = LayerNodeIdentifier::new(id, &self.network_interface, &[]);
10871093

@@ -1176,6 +1182,12 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
11761182
responses.add(GraphOperationMessage::OpacitySet { layer, opacity });
11771183
}
11781184
}
1185+
DocumentMessage::SetFillForSelectedLayers { fill } => {
1186+
let fill = fill.clamp(0., 1.);
1187+
for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) {
1188+
responses.add(GraphOperationMessage::BlendingFillSet { layer, fill });
1189+
}
1190+
}
11791191
DocumentMessage::SetOverlaysVisibility { visible, overlays_type } => {
11801192
let visibility_settings = &mut self.overlays_visibility_settings;
11811193
let overlays_type = match overlays_type {
@@ -2532,38 +2544,47 @@ impl DocumentMessageHandler {
25322544
let selected_layers_except_artboards = selected_nodes.selected_layers_except_artboards(&self.network_interface);
25332545

25342546
// Look up the current opacity and blend mode of the selected layers (if any), and split the iterator into the first tuple and the rest.
2535-
let mut opacity_and_blend_mode = selected_layers_except_artboards.map(|layer| {
2547+
let mut blending_options = selected_layers_except_artboards.map(|layer| {
25362548
(
25372549
get_opacity(layer, &self.network_interface).unwrap_or(100.),
2550+
get_fill(layer, &self.network_interface).unwrap_or(100.),
25382551
get_blend_mode(layer, &self.network_interface).unwrap_or_default(),
25392552
)
25402553
});
2541-
let first_opacity_and_blend_mode = opacity_and_blend_mode.next();
2542-
let result_opacity_and_blend_mode = opacity_and_blend_mode;
2554+
let first_blending_options = blending_options.next();
2555+
let result_blending_options = blending_options;
25432556

25442557
// If there are no selected layers, disable the opacity and blend mode widgets.
2545-
let disabled = first_opacity_and_blend_mode.is_none();
2558+
let disabled = first_blending_options.is_none();
25462559

25472560
// Amongst the selected layers, check if the opacities and blend modes are identical across all layers.
25482561
// The result is setting `option` and `blend_mode` to Some value if all their values are identical, or None if they are not.
25492562
// If identical, we display the value in the widget. If not, we display a dash indicating dissimilarity.
2550-
let (opacity, blend_mode) = first_opacity_and_blend_mode
2551-
.map(|(first_opacity, first_blend_mode)| {
2563+
let (opacity, fill, blend_mode) = first_blending_options
2564+
.map(|(first_opacity, first_fill, first_blend_mode)| {
25522565
let mut opacity_identical = true;
2566+
let mut fill_identical = true;
25532567
let mut blend_mode_identical = true;
25542568

2555-
for (opacity, blend_mode) in result_opacity_and_blend_mode {
2569+
for (opacity, fill, blend_mode) in result_blending_options {
25562570
if (opacity - first_opacity).abs() > (f64::EPSILON * 100.) {
25572571
opacity_identical = false;
25582572
}
2573+
if (fill - first_fill).abs() > (f64::EPSILON * 100.) {
2574+
fill_identical = false;
2575+
}
25592576
if blend_mode != first_blend_mode {
25602577
blend_mode_identical = false;
25612578
}
25622579
}
25632580

2564-
(opacity_identical.then_some(first_opacity), blend_mode_identical.then_some(first_blend_mode))
2581+
(
2582+
opacity_identical.then_some(first_opacity),
2583+
fill_identical.then_some(first_fill),
2584+
blend_mode_identical.then_some(first_blend_mode),
2585+
)
25652586
})
2566-
.unwrap_or((None, None));
2587+
.unwrap_or((None, None, None));
25672588

25682589
let blend_mode_menu_entries = BlendMode::list_svg_subset()
25692590
.iter()
@@ -2622,6 +2643,28 @@ impl DocumentMessageHandler {
26222643
.max_width(100)
26232644
.tooltip("Opacity")
26242645
.widget_holder(),
2646+
Separator::new(SeparatorType::Related).widget_holder(),
2647+
NumberInput::new(fill)
2648+
.label("Fill")
2649+
.unit("%")
2650+
.display_decimal_places(0)
2651+
.disabled(disabled)
2652+
.min(0.)
2653+
.max(100.)
2654+
.range_min(Some(0.))
2655+
.range_max(Some(100.))
2656+
.mode_range()
2657+
.on_update(|number_input: &NumberInput| {
2658+
if let Some(value) = number_input.value {
2659+
DocumentMessage::SetFillForSelectedLayers { fill: value / 100. }.into()
2660+
} else {
2661+
Message::NoOp
2662+
}
2663+
})
2664+
.on_commit(|_| DocumentMessage::AddTransaction.into())
2665+
.max_width(100)
2666+
.tooltip("Fill")
2667+
.widget_holder(),
26252668
];
26262669
let layers_panel_control_bar_left = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
26272670

editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ pub enum GraphOperationMessage {
2121
layer: LayerNodeIdentifier,
2222
fill: Fill,
2323
},
24+
BlendingFillSet {
25+
layer: LayerNodeIdentifier,
26+
fill: f64,
27+
},
2428
OpacitySet {
2529
layer: LayerNodeIdentifier,
2630
opacity: f64,
@@ -29,6 +33,9 @@ pub enum GraphOperationMessage {
2933
layer: LayerNodeIdentifier,
3034
blend_mode: BlendMode,
3135
},
36+
ClipModeToggle {
37+
layer: LayerNodeIdentifier,
38+
},
3239
StrokeSet {
3340
layer: LayerNodeIdentifier,
3441
stroke: Stroke,

editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
55
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector};
66
use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers;
77
use crate::messages::prelude::*;
8+
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
89
use glam::{DAffine2, DVec2, IVec2};
910
use graph_craft::document::{NodeId, NodeInput};
1011
use graphene_core::Color;
1112
use graphene_core::renderer::Quad;
1213
use graphene_core::text::{Font, TypesettingConfig};
13-
use graphene_core::vector::style::{Fill, Gradient, GradientStops, GradientType, LineCap, LineJoin, Stroke};
14+
use graphene_core::vector::style::{Fill, Gradient, GradientStops, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
1415
use graphene_std::vector::convert_usvg_path;
1516

1617
#[derive(Debug, Clone)]
@@ -41,6 +42,11 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
4142
modify_inputs.fill_set(fill);
4243
}
4344
}
45+
GraphOperationMessage::BlendingFillSet { layer, fill } => {
46+
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
47+
modify_inputs.blending_fill_set(fill);
48+
}
49+
}
4450
GraphOperationMessage::OpacitySet { layer, opacity } => {
4551
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
4652
modify_inputs.opacity_set(opacity);
@@ -51,6 +57,12 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
5157
modify_inputs.blend_mode_set(blend_mode);
5258
}
5359
}
60+
GraphOperationMessage::ClipModeToggle { layer } => {
61+
let clip_mode = get_clip_mode(layer, network_interface);
62+
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
63+
modify_inputs.clip_mode_toggle(clip_mode);
64+
}
65+
}
5466
GraphOperationMessage::StrokeSet { layer, stroke } => {
5567
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
5668
modify_inputs.stroke_set(stroke);
@@ -376,18 +388,20 @@ fn apply_usvg_stroke(stroke: &usvg::Stroke, modify_inputs: &mut ModifyInputsCont
376388
weight: stroke.width().get() as f64,
377389
dash_lengths: stroke.dasharray().as_ref().map(|lengths| lengths.iter().map(|&length| length as f64).collect()).unwrap_or_default(),
378390
dash_offset: stroke.dashoffset() as f64,
379-
line_cap: match stroke.linecap() {
380-
usvg::LineCap::Butt => LineCap::Butt,
381-
usvg::LineCap::Round => LineCap::Round,
382-
usvg::LineCap::Square => LineCap::Square,
391+
cap: match stroke.linecap() {
392+
usvg::LineCap::Butt => StrokeCap::Butt,
393+
usvg::LineCap::Round => StrokeCap::Round,
394+
usvg::LineCap::Square => StrokeCap::Square,
383395
},
384-
line_join: match stroke.linejoin() {
385-
usvg::LineJoin::Miter => LineJoin::Miter,
386-
usvg::LineJoin::MiterClip => LineJoin::Miter,
387-
usvg::LineJoin::Round => LineJoin::Round,
388-
usvg::LineJoin::Bevel => LineJoin::Bevel,
396+
join: match stroke.linejoin() {
397+
usvg::LineJoin::Miter => StrokeJoin::Miter,
398+
usvg::LineJoin::MiterClip => StrokeJoin::Miter,
399+
usvg::LineJoin::Round => StrokeJoin::Round,
400+
usvg::LineJoin::Bevel => StrokeJoin::Bevel,
389401
},
390-
line_join_miter_limit: stroke.miterlimit().get() as f64,
402+
join_miter_limit: stroke.miterlimit().get() as f64,
403+
align: StrokeAlign::Center,
404+
paint_order: PaintOrder::StrokeAbove,
391405
transform,
392406
non_scaling: false,
393407
})

editor/src/messages/portfolio/document/graph_operation/transform_utils.rs

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -91,44 +91,48 @@ pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
9191
if let Some(&TaggedValue::DVec2(pivot)) = inputs[5].as_value() { pivot } else { DVec2::splat(0.5) }
9292
}
9393

94-
/// ![](https://files.keavon.com/-/OptimisticSpotlessTinamou/capture.png)
95-
///
96-
/// Source:
97-
/// ```tex
98-
/// \begin{bmatrix}
99-
/// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\
100-
/// S_{x}\sin(\theta)+S_{y}\cos(\theta)H_{y} & S_{x}\sin(\theta)H_{x}+S_{y}\cos(\theta) & T_{y}\\
101-
/// 0 & 0 & 1
102-
/// \end{bmatrix}
103-
/// ```
104-
#[test]
105-
fn derive_transform() {
106-
for shear_x in -10..=10 {
107-
let shear_x = (shear_x as f64) / 2.;
108-
for angle in (0..=360).step_by(15) {
109-
let angle = (angle as f64).to_radians();
110-
for scale_x in 1..10 {
111-
let scale_x = (scale_x as f64) / 5.;
112-
for scale_y in 1..10 {
113-
let scale_y = (scale_y as f64) / 5.;
114-
115-
let shear = DVec2::new(shear_x, 0.);
116-
let scale = DVec2::new(scale_x, scale_y);
117-
let translate = DVec2::new(5666., 644.);
118-
119-
let original_transform = DAffine2::from_cols(
120-
DVec2::new(scale.x * angle.cos() - scale.y * angle.sin() * shear.y, scale.x * angle.sin() + scale.y * angle.cos() * shear.y),
121-
DVec2::new(scale.x * angle.cos() * shear.x - scale.y * angle.sin(), scale.x * angle.sin() * shear.x + scale.y * angle.cos()),
122-
translate,
123-
);
124-
125-
let (new_scale, new_angle, new_translation, new_shear) = compute_scale_angle_translation_shear(original_transform);
126-
let new_transform = DAffine2::from_scale_angle_translation(new_scale, new_angle, new_translation) * DAffine2::from_cols_array(&[1., new_shear.y, new_shear.x, 1., 0., 0.]);
127-
128-
assert!(
129-
new_transform.abs_diff_eq(original_transform, 1e-10),
130-
"original_transform {original_transform} new_transform {new_transform} / scale {scale} new_scale {new_scale} / angle {angle} new_angle {new_angle} / shear {shear} / new_shear {new_shear}",
131-
);
94+
#[cfg(test)]
95+
mod tests {
96+
use super::*;
97+
/// ![](https://files.keavon.com/-/OptimisticSpotlessTinamou/capture.png)
98+
///
99+
/// Source:
100+
/// ```tex
101+
/// \begin{bmatrix}
102+
/// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\
103+
/// S_{x}\sin(\theta)+S_{y}\cos(\theta)H_{y} & S_{x}\sin(\theta)H_{x}+S_{y}\cos(\theta) & T_{y}\\
104+
/// 0 & 0 & 1
105+
/// \end{bmatrix}
106+
/// ```
107+
#[test]
108+
fn derive_transform() {
109+
for shear_x in -10..=10 {
110+
let shear_x = (shear_x as f64) / 2.;
111+
for angle in (0..=360).step_by(15) {
112+
let angle = (angle as f64).to_radians();
113+
for scale_x in 1..10 {
114+
let scale_x = (scale_x as f64) / 5.;
115+
for scale_y in 1..10 {
116+
let scale_y = (scale_y as f64) / 5.;
117+
118+
let shear = DVec2::new(shear_x, 0.);
119+
let scale = DVec2::new(scale_x, scale_y);
120+
let translate = DVec2::new(5666., 644.);
121+
122+
let original_transform = DAffine2::from_cols(
123+
DVec2::new(scale.x * angle.cos() - scale.y * angle.sin() * shear.y, scale.x * angle.sin() + scale.y * angle.cos() * shear.y),
124+
DVec2::new(scale.x * angle.cos() * shear.x - scale.y * angle.sin(), scale.x * angle.sin() * shear.x + scale.y * angle.cos()),
125+
translate,
126+
);
127+
128+
let (new_scale, new_angle, new_translation, new_shear) = compute_scale_angle_translation_shear(original_transform);
129+
let new_transform = DAffine2::from_scale_angle_translation(new_scale, new_angle, new_translation) * DAffine2::from_cols_array(&[1., new_shear.y, new_shear.x, 1., 0., 0.]);
130+
131+
assert!(
132+
new_transform.abs_diff_eq(original_transform, 1e-10),
133+
"original_transform {original_transform} new_transform {new_transform} / scale {scale} new_scale {new_scale} / angle {angle} new_angle {new_angle} / shear {shear} / new_shear {new_shear}",
134+
);
135+
}
132136
}
133137
}
134138
}

0 commit comments

Comments
 (0)