Repository: GraphiteEditor/Graphite (language: rust)

Issue:
Path tool point nudging should be in document coordinates, not viewport coordinates
If you select a point using the Path tool and nudge it to the right by 10 units with <kbd>Shift</kbd><kbd>Right Arrow</kbd>, starting at 100% viewport zoom but then zooming to 200%, you'll notice it takes two presses of <kbd>Shift</kbd><kbd>Left Arrow</kbd> to return back to its starting place. That's because it currently is moving relative to the viewport coordinates, but it should always work relative to the document coordinates so 10 units to the right means the same thing regardless of the current level of zoom. It also shouldn't be in the layer's local coordinate space, where a shape that has a 0.1 scale factor would take 10 times as many presses to travel the intended distance. The Select tool's nudging can be referenced for this, because it works correctly in document space (not layer-local or viewport space).


Discussion / hints:

Fix patch:
diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs
index 774dff580d..70eca293a7 100644
--- a/editor/src/messages/tool/common_functionality/shape_editor.rs
+++ b/editor/src/messages/tool/common_functionality/shape_editor.rs
@@ -612,15 +612,24 @@ impl ShapeState {
 	}
 
 	/// Move the selected points by dragging the mouse.
-	pub fn move_selected_points(&self, handle_lengths: Option<OpposingHandleLengths>, document: &DocumentMessageHandler, delta: DVec2, equidistant: bool, responses: &mut VecDeque<Message>) {
+	pub fn move_selected_points(
+		&self,
+		handle_lengths: Option<OpposingHandleLengths>,
+		document: &DocumentMessageHandler,
+		delta: DVec2,
+		equidistant: bool,
+		responses: &mut VecDeque<Message>,
+		in_viewport_space: bool,
+	) {
 		for (&layer, state) in &self.selected_shape_state {
-			let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
-				continue;
-			};
+			let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
+
 			let opposing_handles = handle_lengths.as_ref().and_then(|handle_lengths| handle_lengths.get(&layer));
 
-			let transform = document.metadata().transform_to_viewport(layer);
-			let delta = transform.inverse().transform_vector2(delta);
+			let transform_to_viewport_space = document.metadata().transform_to_viewport(layer);
+			let transform_to_document_space = document.metadata().transform_to_document(layer);
+			let delta_transform = if in_viewport_space { transform_to_viewport_space } else { transform_to_document_space };
+			let delta = delta_transform.inverse().transform_vector2(delta);
 
 			for &point in state.selected_points.iter() {
 				let handle = match point {
@@ -654,7 +663,8 @@ impl ShapeState {
 				let new_relative = if equidistant {
 					-(handle_position - anchor_position)
 				} else {
-					let transform = document.metadata().document_to_viewport.inverse() * transform;
+					// TODO: Is this equivalent to `transform_to_document_space`? If changed, the before and after should be tested.
+					let transform = document.metadata().document_to_viewport.inverse() * transform_to_viewport_space;
 					let Some(other_position) = other.to_manipulator_point().get_position(&vector_data) else {
 						continue;
 					};
diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs
index 074af7130b..949b667cdd 100644
--- a/editor/src/messages/tool/tool_messages/path_tool.rs
+++ b/editor/src/messages/tool/tool_messages/path_tool.rs
@@ -472,7 +472,7 @@ impl PathToolData {
 		let previous_mouse = document.metadata().document_to_viewport.transform_point2(self.previous_mouse_position);
 		let snapped_delta = shape_editor.snap(&mut self.snap_manager, &self.snap_cache, document, input, previous_mouse);
 		let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() };
-		shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, responses);
+		shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, responses, true);
 		self.previous_mouse_position += document.metadata().document_to_viewport.inverse().transform_vector2(snapped_delta);
 	}
 }
@@ -786,7 +786,7 @@ impl Fsm for PathToolFsmState {
 			}
 			(_, PathToolMessage::PointerMove { .. }) => self,
 			(_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => {
-				shape_editor.move_selected_points(tool_data.opposing_handle_lengths.take(), document, (delta_x, delta_y).into(), true, responses);
+				shape_editor.move_selected_points(tool_data.opposing_handle_lengths.take(), document, (delta_x, delta_y).into(), true, responses, false);
 
 				PathToolFsmState::Ready
 			}


Test patch: