Path tool points manipulation: nudging, drag axis snapping, and G/R/S (#1068)

* issue820: implemented nudging points

* nudge points triggered only when the mouse is moved after selecting one point

* issue 820 // bullet 1 done

* grab working / rotate not

* rotate works (sensitive)

* G/R/S and Shift/Drag working

* Cargo formatted // implemented Hypercubes comments

* Refactored G/R/S - need to fix fast transform

* Finished refactored G/R/S

* Typed Angle needs further touch up

* Cargo formatted

* Dealt with dangerous unwraps

* Cargo fmt (again) - unwraps fixed

* Cleaned up

* cargo fmt

* Ready for Review

* Ready for Review- cargo fmt

* Code review fixes

* Remove duplicate constant for nudging

* Fix consts.rs spacing

* Apply suggestions from code review

Added suggestions

Co-authored-by: Keavon Chambers <keavon@keavon.com>

* Added typo/grammar suggestions

* Nits

---------

Co-authored-by: Shiro <shiro@damedane.local>
Co-authored-by: hypercube <0hypercube@gmail.com>
Co-authored-by: 0HyperCube <78500760+0HyperCube@users.noreply.github.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
OllieDolan
2023-04-16 15:56:52 -07:00
committed by Keavon Chambers
co-authored by Keavon Chambers Shiro hypercube 0HyperCube
parent b83f2c24f1
commit 974a37f127
6 changed files with 294 additions and 50 deletions
@@ -24,7 +24,7 @@ pub struct PathTool {
#[remain::sorted]
#[impl_message(Message, ToolMessage, Path)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
pub enum PathToolMessage {
// Standard messages
#[remain::unsorted]
@@ -43,6 +43,10 @@ pub enum PathToolMessage {
shift_mirror_distance: Key,
},
InsertPoint,
NudgeSelectedPoints {
delta_x: f64,
delta_y: f64,
},
PointerMove {
alt_mirror_angle: Key,
shift_mirror_distance: Key,
@@ -77,6 +81,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
InsertPoint,
DragStart,
Delete,
NudgeSelectedPoints,
),
Dragging => actions!(PathToolMessageDiscriminant;
InsertPoint,
@@ -108,7 +113,6 @@ enum PathToolFsmState {
#[derive(Default)]
struct PathToolData {
snap_manager: SnapManager,
drag_start_pos: DVec2,
previous_mouse_position: DVec2,
alt_debounce: bool,
@@ -203,6 +207,7 @@ impl Fsm for PathToolFsmState {
let include_handles: Vec<_> = selected_layers.iter().map(|x| x.as_slice()).collect();
tool_data.snap_manager.add_all_document_handles(document, input, &include_handles, &[], &selected_points.points);
tool_data.drag_start_pos = input.mouse.position;
tool_data.previous_mouse_position = input.mouse.position - selected_points.offset;
@@ -241,9 +246,11 @@ impl Fsm for PathToolFsmState {
responses.push_back(DocumentMessage::DeselectAllLayers.into());
}
}
PathToolFsmState::Ready
}
}
// Dragging
(
PathToolFsmState::Dragging,
@@ -282,6 +289,7 @@ impl Fsm for PathToolFsmState {
tool_data.previous_mouse_position = snapped_position;
PathToolFsmState::Dragging
}
// Mouse up
(_, PathToolMessage::DragStop { shift_mirror_distance }) => {
let nearest_point = shape_editor
@@ -335,6 +343,10 @@ impl Fsm for PathToolFsmState {
shift_mirror_distance: _,
},
) => self,
(_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => {
shape_editor.move_selected_points(&document.document_legacy, (delta_x, delta_y).into(), true, responses);
PathToolFsmState::Ready
}
}
} else {
self
@@ -346,8 +358,8 @@ impl Fsm for PathToolFsmState {
PathToolFsmState::Ready => HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Select Point"), HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus()]),
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected (coming soon)"), HintInfo::keys([Key::Shift], "10x").prepend_plus()]),
HintGroup(vec![HintInfo::keys([Key::KeyG, Key::KeyR, Key::KeyS], "Grab/Rotate/Scale Selected (coming soon)")]),
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "10x").prepend_plus()]),
HintGroup(vec![HintInfo::keys([Key::KeyG, Key::KeyR, Key::KeyS], "Grab/Rotate/Scale Selected")]),
]),
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
HintInfo::keys([Key::Alt], "Split/Align Handles (Toggle)"),
@@ -539,7 +539,15 @@ impl Fsm for SelectToolFsmState {
let document = &document.document_legacy;
let selected = &tool_data.layers_dragging.iter().collect::<Vec<_>>();
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, selected, responses, document);
let mut selected = Selected::new(
&mut bounds.original_transforms,
&mut bounds.center_of_transformation,
selected,
responses,
document,
None,
&ToolType::Select,
);
bounds.center_of_transformation = selected.mean_average_of_pivots(render_data);
}
@@ -549,7 +557,15 @@ impl Fsm for SelectToolFsmState {
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
let selected = selected.iter().collect::<Vec<_>>();
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, &selected, responses, &document.document_legacy);
let mut selected = Selected::new(
&mut bounds.original_transforms,
&mut bounds.center_of_transformation,
&selected,
responses,
&document.document_legacy,
None,
&ToolType::Select,
);
bounds.center_of_transformation = selected.mean_average_of_pivots(render_data);
}
@@ -650,10 +666,10 @@ impl Fsm for SelectToolFsmState {
let snapped_mouse_position = tool_data.snap_manager.snap_position(responses, document, mouse_position);
let (position, size) = movement.new_size(snapped_mouse_position, bounds.transform, center, bounds.center_of_transformation, axis_align);
let (delta, mut pivot) = movement.bounds_to_scale_transform(position, size);
let (delta, mut _pivot) = movement.bounds_to_scale_transform(position, size);
let selected = &tool_data.layers_dragging.iter().collect::<Vec<_>>();
let mut selected = Selected::new(&mut bounds.original_transforms, &mut pivot, selected, responses, &document.document_legacy);
let mut selected = Selected::new(&mut bounds.original_transforms, &mut _pivot, selected, responses, &document.document_legacy, None, &ToolType::Select);
selected.update_transforms(delta);
}
@@ -679,7 +695,15 @@ impl Fsm for SelectToolFsmState {
let delta = DAffine2::from_angle(snapped_angle);
let selected = tool_data.layers_dragging.iter().collect::<Vec<_>>();
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, &selected, responses, &document.document_legacy);
let mut selected = Selected::new(
&mut bounds.original_transforms,
&mut bounds.center_of_transformation,
&selected,
responses,
&document.document_legacy,
None,
&ToolType::Select,
);
selected.update_transforms(delta);
}
@@ -872,6 +896,8 @@ impl Fsm for SelectToolFsmState {
&selected,
responses,
&document.document_legacy,
None,
&ToolType::Select,
);
selected.revert_operation();
@@ -6,6 +6,7 @@ use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::utility_types::{ToolData, ToolType};
use document_legacy::layers::style::RenderData;
use graphene_core::vector::ManipulatorPointId;
use glam::DVec2;
@@ -43,16 +44,48 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, ipp, render_data, tool_data, shape_editor): TransformData) {
use TransformLayerMessage::*;
// TODO: Transform individual points when using the path tool.
let _using_path_tool = tool_data.active_tool_type == ToolType::Path;
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
let selected_layers = document.layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then_some(layer_path)).collect::<Vec<_>>();
let mut selected = Selected::new(&mut self.original_transforms, &mut self.pivot, &selected_layers, responses, &document.document_legacy);
let mut selected = Selected::new(
&mut self.original_transforms,
&mut self.pivot,
&selected_layers,
responses,
&document.document_legacy,
Some(shape_editor),
&tool_data.active_tool_type,
);
let mut begin_operation = |operation: TransformOperation, typing: &mut Typing, mouse_position: &mut DVec2, start_mouse: &mut DVec2| {
if operation != TransformOperation::None {
selected.revert_operation();
typing.clear();
}
if using_path_tool {
if let Ok(layer) = document.document_legacy.layer(&selected_layers[0]) {
if let Some(vector_data) = layer.as_vector_data() {
*selected.original_transforms = OriginalTransforms::default();
let viewspace = &mut document.document_legacy.generate_transform_relative_to_viewport(&selected_layers[0]).ok().unwrap_or_default();
let mut point_count: usize = 0;
let count_point = |position| {
point_count += 1;
position
};
let get_location = |point: &ManipulatorPointId| {
vector_data
.manipulator_from_id(point.group)
.and_then(|manipulator_group| point.manipulator_type.get_position(manipulator_group))
.map(|position| viewspace.transform_point2(position))
};
let points = shape_editor.selected_points();
*selected.pivot = points.filter_map(get_location).map(count_point).sum::<DVec2>() / point_count as f64;
}
}
} else {
*selected.pivot = selected.mean_average_of_pivots(render_data);
}
@@ -65,7 +98,8 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
#[remain::sorted]
match message {
ApplyTransformOperation => {
self.original_transforms.clear();
selected.original_transforms.clear();
self.typing.clear();
self.transform_operation = TransformOperation::None;
@@ -90,6 +124,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation = TransformOperation::Grabbing(Default::default());
selected.original_transforms.clear();
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
BeginRotate => {
@@ -106,6 +141,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation = TransformOperation::Rotating(Default::default());
selected.original_transforms.clear();
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
BeginScale => {
@@ -122,6 +158,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation = TransformOperation::Scaling(Default::default());
selected.original_transforms.clear();
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
CancelTransformOperation => {
@@ -163,15 +200,12 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint);
}
TransformOperation::Rotating(rotation) => {
let selected_pivot = selected.mean_average_of_pivots(render_data);
let angle = {
let start_offset = self.mouse_position - selected_pivot;
let end_offset = ipp.mouse.position - selected_pivot;
start_offset.angle_between(end_offset)
};
let start_offset = *selected.pivot - self.mouse_position;
let end_offset = *selected.pivot - ipp.mouse.position;
let angle = start_offset.angle_between(end_offset);
let change = if self.slow { angle / SLOWING_DIVISOR } else { angle };
self.transform_operation = TransformOperation::Rotating(rotation.increment_amount(change));
self.transform_operation.apply_transform_operation(&mut selected, self.snap, Axis::Both);
}