Skip to content

Commit 55d7bda

Browse files
committed
Fix after AI review
1 parent 60f80cf commit 55d7bda

8 files changed

Lines changed: 184 additions & 292 deletions

File tree

editor/src/messages/input_mapper/input_mappings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping {
186186
// MeshGradientToolMessage
187187
entry!(DoubleClick(MouseButton::Left); action_dispatch=MeshGradientToolMessage::DoubleClick),
188188
entry!(KeyDown(MouseLeft); action_dispatch=MeshGradientToolMessage::PointerDown),
189-
entry!(PointerMove; refresh_keys=[Shift, Control], action_dispatch=MeshGradientToolMessage::PointerMove { constrain_axis: Shift, lock_angle: Control }),
189+
entry!(PointerMove; refresh_keys=[Shift], action_dispatch=MeshGradientToolMessage::PointerMove { constrain_axis: Shift }),
190190
entry!(KeyUp(MouseLeft); action_dispatch=MeshGradientToolMessage::PointerUp),
191191
entry!(KeyDown(Delete); action_dispatch=MeshGradientToolMessage::DeleteEdge),
192192
entry!(KeyDown(Backspace); action_dispatch=MeshGradientToolMessage::DeleteEdge),

editor/src/messages/tool/common_functionality/graph_modification_utils.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -520,13 +520,7 @@ pub fn gradient_orientation_rightward(transform: glam::DAffine2) -> bool {
520520

521521
/// Try to find a "Mesh Gradient Value" node that is connected to a "Fill" node, or to a layer directly.
522522
pub fn get_upstream_mesh_gradient_value_node_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
523-
let target_input = gradient_chain_target_input(layer, network_interface);
524-
let walk_from = network_interface.upstream_output_connector(&target_input, &[])?.node_id()?;
525-
526-
network_interface
527-
.upstream_flow_back_from_nodes(vec![walk_from], &[], FlowType::HorizontalFlow)
528-
.take_while(|node_id| !network_interface.is_layer(node_id, &[]))
529-
.find(|node_id| network_interface.reference(node_id, &[]).as_ref() == Some(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::mesh_gradient_value::IDENTIFIER)))
523+
get_upstream_paint_value_node_id(layer, network_interface, graphene_std::math_nodes::mesh_gradient_value::IDENTIFIER)
530524
}
531525

532526
/// Get the current fill of a layer from the closest "Fill" node.

editor/src/messages/tool/tool_messages/mesh_gradient_tool.rs

Lines changed: 55 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,25 @@ pub struct MeshGradientTool {
2626
options: MeshGradientOptions,
2727
}
2828

29-
#[derive(Default)]
3029
pub struct MeshGradientOptions {
3130
space: GradientSpace,
3231
interpolation: GradientInterpolation,
3332
}
3433

34+
impl Default for MeshGradientOptions {
35+
fn default() -> Self {
36+
let MeshGradientSurface {
37+
gradient_space,
38+
gradient_interpolation,
39+
..
40+
} = MeshGradientSurface::default();
41+
Self {
42+
space: gradient_space,
43+
interpolation: gradient_interpolation,
44+
}
45+
}
46+
}
47+
3548
#[impl_message(Message, ToolMessage, MeshGradient)]
3649
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
3750
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -40,15 +53,13 @@ pub enum MeshGradientToolMessage {
4053
Abort,
4154
Overlays { context: OverlayContext },
4255
SelectionChanged,
43-
WorkingColorChanged,
4456

4557
// Tool-specific messages
4658
DeleteEdge,
4759
DoubleClick,
48-
InsertStop,
4960
PointerDown,
50-
PointerMove { constrain_axis: Key, lock_angle: Key },
51-
PointerOutsideViewport { constrain_axis: Key, lock_angle: Key },
61+
PointerMove { constrain_axis: Key },
62+
PointerOutsideViewport { constrain_axis: Key },
5263
PointerUp,
5364
StartTransactionForColorStop,
5465
CommitTransactionForColorStop,
@@ -86,9 +97,10 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Mesh
8697
MeshGradientOptionsUpdate::Interpolation(interpolation) => self.options.interpolation = interpolation,
8798
}
8899

89-
apply_mesh_gradient_options(context, responses, |surface| {
90-
surface.gradient_space = self.options.space;
91-
surface.gradient_interpolation = self.options.interpolation;
100+
// Write back only the setting that actually changed, so a layer whose other setting differs keeps it
101+
apply_mesh_gradient_options(context, responses, |surface| match &options {
102+
MeshGradientOptionsUpdate::Space(space) => surface.gradient_space = *space,
103+
MeshGradientOptionsUpdate::Interpolation(interpolation) => surface.gradient_interpolation = *interpolation,
92104
});
93105
self.refresh_options(responses);
94106
}
@@ -327,64 +339,7 @@ fn resolve_mesh_gradient_source(layer: LayerNodeIdentifier, network_interface: &
327339
}
328340
}
329341

330-
fn approximate_valid_region_bounds(initial_position: DVec2, [min, max]: [DVec2; 2], mut is_valid: impl FnMut(DVec2) -> bool) -> Option<[DVec2; 2]> {
331-
const SUBDIVISIONS: usize = 12;
332-
333-
let mut x_samples = (0..=SUBDIVISIONS).map(|index| min.x + (max.x - min.x) * index as f64 / SUBDIVISIONS as f64).collect::<Vec<_>>();
334-
let mut y_samples = (0..=SUBDIVISIONS).map(|index| min.y + (max.y - min.y) * index as f64 / SUBDIVISIONS as f64).collect::<Vec<_>>();
335-
x_samples.push(initial_position.x);
336-
y_samples.push(initial_position.y);
337-
x_samples.sort_by(f64::total_cmp);
338-
y_samples.sort_by(f64::total_cmp);
339-
x_samples.dedup();
340-
y_samples.dedup();
341-
342-
let columns = x_samples.len();
343-
let rows = y_samples.len();
344-
let seed_column = x_samples.iter().position(|&x| x == initial_position.x)?;
345-
let seed_row = y_samples.iter().position(|&y| y == initial_position.y)?;
346-
let seed_index = seed_row * columns + seed_column;
347-
348-
let valid_samples = y_samples.iter().flat_map(|&y| x_samples.iter().map(move |&x| DVec2::new(x, y))).map(&mut is_valid).collect::<Vec<_>>();
349-
350-
if !valid_samples[seed_index] {
351-
return None;
352-
}
353-
354-
let mut visited = vec![false; rows * columns];
355-
let mut queue = VecDeque::from([seed_index]);
356-
let mut bounds_min = initial_position;
357-
let mut bounds_max = initial_position;
358-
359-
while let Some(index) = queue.pop_front() {
360-
if visited[index] || !valid_samples[index] {
361-
continue;
362-
}
363-
visited[index] = true;
364-
365-
let row = index / columns;
366-
let column = index % columns;
367-
let position = DVec2::new(x_samples[column], y_samples[row]);
368-
bounds_min = bounds_min.min(position);
369-
bounds_max = bounds_max.max(position);
370-
371-
if row > 0 {
372-
queue.push_back(index - columns);
373-
}
374-
if row + 1 < rows {
375-
queue.push_back(index + columns);
376-
}
377-
if column > 0 {
378-
queue.push_back(index - 1);
379-
}
380-
if column + 1 < columns {
381-
queue.push_back(index + 1);
382-
}
383-
}
384-
385-
Some([bounds_min, bounds_max])
386-
}
387-
342+
/// Walks back from `target` toward `valid_region_center` for the furthest position that keeps the mesh free of foldovers.
388343
fn constrain_to_valid_region(target: DVec2, valid_region_center: DVec2, candidate: impl Fn(DVec2) -> Option<MeshGradient>) -> Option<MeshGradient> {
389344
candidate(target).or_else(|| {
390345
const BINARY_SEARCH_ITERATIONS: usize = 12;
@@ -435,7 +390,6 @@ impl ToolTransition for MeshGradientTool {
435390
EventToMessageMap {
436391
tool_abort: Some(MeshGradientToolMessage::Abort.into()),
437392
selection_changed: Some(MeshGradientToolMessage::SelectionChanged.into()),
438-
working_color_changed: Some(MeshGradientToolMessage::WorkingColorChanged.into()),
439393
overlay_provider: Some(|context| MeshGradientToolMessage::Overlays { context }.into()),
440394
..Default::default()
441395
}
@@ -615,9 +569,12 @@ impl Fsm for MeshGradientToolFsmState {
615569

616570
(_state @ MeshGradientToolFsmState::Ready { .. }, MeshGradientToolMessage::DeleteEdge) => {
617571
let Some(selected_mesh) = tool_data.selected_mesh.as_mut() else { return self };
618-
if let MeshGradientTarget::Segment { segment_id, .. } = selected_mesh.target {
619-
selected_mesh.surface.mesh.remove_edge(segment_id);
620-
};
572+
let MeshGradientTarget::Segment { segment_id, .. } = selected_mesh.target else { return self };
573+
let mut mesh = selected_mesh.surface.mesh.clone();
574+
if mesh.remove_edge(segment_id).is_none() {
575+
return self;
576+
}
577+
selected_mesh.surface.mesh = mesh;
621578

622579
responses.add(DocumentMessage::StartTransaction);
623580
selected_mesh.update_gradient_in_graph(responses);
@@ -721,18 +678,6 @@ impl Fsm for MeshGradientToolFsmState {
721678

722679
if distance_squared < tolerance_squared {
723680
responses.add(DocumentMessage::StartTransaction);
724-
let valid_region_center = gradient
725-
.geometry()
726-
.bounding_box()
727-
.and_then(|bounds| {
728-
approximate_valid_region_bounds(corner.position, bounds, |position| {
729-
let mut candidate = gradient.clone();
730-
candidate.set_corner_position(corner.index, position).is_some()
731-
&& candidate.patches().all(|patch| patch.is_some_and(|patch| patch.sampled_no_foldover()))
732-
})
733-
})
734-
.map(|[min, max]| min.midpoint(max))
735-
.unwrap_or(corner.position);
736681

737682
tool_data.selected_mesh = Some(SelectedMeshGradient {
738683
layer,
@@ -744,7 +689,7 @@ impl Fsm for MeshGradientToolFsmState {
744689
corner_index: corner.index,
745690
initial_mouse: local_mouse,
746691
initial_corner: corner.position,
747-
valid_region_center,
692+
valid_region_center: corner.position,
748693
},
749694
});
750695

@@ -782,37 +727,27 @@ impl Fsm for MeshGradientToolFsmState {
782727
consider_handle(HandleId::end(segment_id), handle_end, bezier.end, None);
783728
}
784729
}
730+
}
785731

786-
if let Some((handle_id, initial_handle, _)) = closest_handle {
787-
responses.add(DocumentMessage::StartTransaction);
788-
let valid_region_center = gradient
789-
.geometry()
790-
.bounding_box()
791-
.and_then(|bounds| {
792-
approximate_valid_region_bounds(initial_handle, bounds, |position| {
793-
let mut candidate = gradient.clone();
794-
candidate.set_handle_position(handle_id, position).is_some() && candidate.patches().all(|patch| patch.is_some_and(|patch| patch.sampled_no_foldover()))
795-
})
796-
})
797-
.map(|[min, max]| min.midpoint(max))
798-
.unwrap_or(initial_handle);
799-
800-
tool_data.selected_mesh = Some(SelectedMeshGradient {
801-
layer,
802-
mesh_index: index,
803-
surface: mesh_gradient_surface(meshes, index, gradient),
804-
mesh_to_document,
805-
source,
806-
target: MeshGradientTarget::Handle {
807-
handle_id,
808-
initial_mouse: local_mouse,
809-
initial_handle,
810-
valid_region_center,
811-
},
812-
});
732+
// Resolved only after every segment has been offered, so the nearest-wins comparison spans the whole mesh
733+
if let Some((handle_id, initial_handle, _)) = closest_handle {
734+
responses.add(DocumentMessage::StartTransaction);
735+
736+
tool_data.selected_mesh = Some(SelectedMeshGradient {
737+
layer,
738+
mesh_index: index,
739+
surface: mesh_gradient_surface(meshes, index, gradient),
740+
mesh_to_document,
741+
source,
742+
target: MeshGradientTarget::Handle {
743+
handle_id,
744+
initial_mouse: local_mouse,
745+
initial_handle,
746+
valid_region_center: initial_handle,
747+
},
748+
});
813749

814-
return MeshGradientToolFsmState::Dragging;
815-
}
750+
return MeshGradientToolFsmState::Dragging;
816751
}
817752

818753
for edge in gradient.edges() {
@@ -826,31 +761,11 @@ impl Fsm for MeshGradientToolFsmState {
826761

827762
let handles = match (points.p1, points.p2) {
828763
(Some(p1), Some(p2)) => [p1, p2],
829-
(Some(p1), None) | (None, Some(p1)) => [p1, points.p3],
764+
(Some(control), None) | (None, Some(control)) => [points.p0 + (control - points.p0) * 2. / 3., points.p3 + (control - points.p3) * 2. / 3.],
830765
(None, None) => [points.p0 + (points.p3 - points.p0) / 3., points.p3 + (points.p0 - points.p3) / 3.],
831766
};
832767

833768
responses.add(DocumentMessage::StartTransaction);
834-
let valid_region_center = gradient
835-
.geometry()
836-
.bounding_box()
837-
.and_then(|bounds| {
838-
approximate_valid_region_bounds(local_mouse, bounds, |position| {
839-
let delta = position - local_mouse;
840-
let mut candidate = gradient.clone();
841-
candidate
842-
.set_edge_handles(
843-
edge.segment_id,
844-
BezierHandles::Cubic {
845-
handle_start: handles[0] + delta,
846-
handle_end: handles[1] + delta,
847-
},
848-
)
849-
.is_some() && candidate.patches().all(|patch| patch.is_some_and(|patch| patch.sampled_no_foldover()))
850-
})
851-
})
852-
.map(|[min, max]| min.midpoint(max))
853-
.unwrap_or(local_mouse);
854769

855770
tool_data.selected_mesh = Some(SelectedMeshGradient {
856771
layer,
@@ -862,7 +777,7 @@ impl Fsm for MeshGradientToolFsmState {
862777
segment_id: edge.segment_id,
863778
initial_mouse: local_mouse,
864779
initial_handles: handles,
865-
valid_region_center,
780+
valid_region_center: local_mouse,
866781
},
867782
});
868783

@@ -904,7 +819,7 @@ impl Fsm for MeshGradientToolFsmState {
904819

905820
self
906821
}
907-
(MeshGradientToolFsmState::Dragging, MeshGradientToolMessage::PointerMove { constrain_axis, lock_angle }) => {
822+
(MeshGradientToolFsmState::Dragging, MeshGradientToolMessage::PointerMove { constrain_axis }) => {
908823
let MeshGradientToolData {
909824
selected_mesh,
910825
snap_manager,
@@ -1032,8 +947,8 @@ impl Fsm for MeshGradientToolFsmState {
1032947

1033948
// Auto-panning
1034949
let messages = [
1035-
MeshGradientToolMessage::PointerOutsideViewport { constrain_axis, lock_angle }.into(),
1036-
MeshGradientToolMessage::PointerMove { constrain_axis, lock_angle }.into(),
950+
MeshGradientToolMessage::PointerOutsideViewport { constrain_axis }.into(),
951+
MeshGradientToolMessage::PointerMove { constrain_axis }.into(),
1037952
];
1038953
auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
1039954

@@ -1074,10 +989,10 @@ impl Fsm for MeshGradientToolFsmState {
1074989

1075990
MeshGradientToolFsmState::Dragging
1076991
}
1077-
(state, MeshGradientToolMessage::PointerOutsideViewport { constrain_axis, lock_angle }) => {
992+
(state, MeshGradientToolMessage::PointerOutsideViewport { constrain_axis }) => {
1078993
let messages = [
1079-
MeshGradientToolMessage::PointerOutsideViewport { constrain_axis, lock_angle }.into(),
1080-
MeshGradientToolMessage::PointerMove { constrain_axis, lock_angle }.into(),
994+
MeshGradientToolMessage::PointerOutsideViewport { constrain_axis }.into(),
995+
MeshGradientToolMessage::PointerMove { constrain_axis }.into(),
1081996
];
1082997
tool_data.auto_panning.stop(&messages, responses);
1083998

0 commit comments

Comments
 (0)