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:
parent
b83f2c24f1
commit
974a37f127
|
|
@ -4,6 +4,7 @@ use crate::layers::layer_info::Layer;
|
|||
use crate::layers::style::{self, Stroke};
|
||||
use crate::LayerId;
|
||||
|
||||
use graphene_core::vector::SelectedType;
|
||||
use graphene_std::vector::consts::ManipulatorType;
|
||||
use graphene_std::vector::manipulator_group::ManipulatorGroup;
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
|
|
@ -141,7 +142,7 @@ pub enum Operation {
|
|||
MoveManipulatorPoint {
|
||||
layer_path: Vec<LayerId>,
|
||||
id: u64,
|
||||
manipulator_type: ManipulatorType,
|
||||
manipulator_type: SelectedType,
|
||||
position: (f64, f64),
|
||||
},
|
||||
SetManipulatorPoints {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,30 @@ pub fn default_mapping() -> Mapping {
|
|||
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
|
||||
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { shift_mirror_distance: Shift }),
|
||||
entry!(DoubleClick; action_dispatch=PathToolMessage::InsertPoint),
|
||||
entry!(KeyDown(ArrowRight); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: 0. }),
|
||||
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0. }),
|
||||
entry!(KeyDown(ArrowRight); modifiers=[ArrowUp], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowRight); modifiers=[ArrowDown], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowRight); modifiers=[Shift, ArrowUp], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowRight); modifiers=[Shift, ArrowDown], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowUp); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: 0., delta_y: -NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowUp); modifiers=[ArrowLeft], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowUp); modifiers=[ArrowRight], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowLeft], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowRight], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowLeft); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -NUDGE_AMOUNT, delta_y: 0. }),
|
||||
entry!(KeyDown(ArrowLeft); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -BIG_NUDGE_AMOUNT, delta_y: 0. }),
|
||||
entry!(KeyDown(ArrowLeft); modifiers=[ArrowUp], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowLeft); modifiers=[ArrowDown], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowLeft); modifiers=[Shift, ArrowUp], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowLeft); modifiers=[Shift, ArrowDown], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowDown); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: 0., delta_y: NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowDown); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: 0., delta_y: BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowDown); modifiers=[ArrowLeft], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowDown); modifiers=[ArrowRight], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowDown); modifiers=[Shift, ArrowLeft], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: -BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT }),
|
||||
entry!(KeyDown(ArrowDown); modifiers=[Shift, ArrowRight], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT }),
|
||||
//
|
||||
// PenToolMessage
|
||||
entry!(PointerMove; refresh_keys=[Shift, Control], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control}),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,34 @@
|
|||
use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
|
||||
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||
use crate::messages::tool::utility_types::ToolType;
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::layers::style::RenderData;
|
||||
use document_legacy::LayerId;
|
||||
use graphene_core::vector::{ManipulatorPointId, SelectedType};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
pub type OriginalTransforms = HashMap<Vec<LayerId>, DAffine2>;
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum OriginalTransforms {
|
||||
Layer(HashMap<Vec<LayerId>, DAffine2>),
|
||||
Path(HashMap<Vec<LayerId>, Vec<(ManipulatorPointId, DVec2)>>),
|
||||
}
|
||||
impl Default for OriginalTransforms {
|
||||
fn default() -> Self {
|
||||
OriginalTransforms::Path(HashMap::new())
|
||||
}
|
||||
}
|
||||
impl OriginalTransforms {
|
||||
pub fn clear(&mut self) {
|
||||
match self {
|
||||
OriginalTransforms::Layer(layer_map) => layer_map.clear(),
|
||||
OriginalTransforms::Path(path_map) => path_map.clear(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum Axis {
|
||||
|
|
@ -63,6 +83,13 @@ impl Translation {
|
|||
constraint: self.constraint,
|
||||
}
|
||||
}
|
||||
pub fn set_amount(self, change: DVec2) -> Self {
|
||||
Self {
|
||||
dragged_distance: change,
|
||||
typed_distance: None,
|
||||
constraint: self.constraint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Copy)]
|
||||
|
|
@ -90,6 +117,12 @@ impl Rotation {
|
|||
typed_angle: None,
|
||||
}
|
||||
}
|
||||
pub fn set_amount(self, angle: f64) -> Self {
|
||||
Self {
|
||||
dragged_angle: angle,
|
||||
typed_angle: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
|
|
@ -129,6 +162,14 @@ impl Scale {
|
|||
constraint: self.constraint,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_amount(self, change: f64) -> Self {
|
||||
Self {
|
||||
dragged_factor: 1. + change,
|
||||
typed_factor: None,
|
||||
constraint: self.constraint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Copy)]
|
||||
|
|
@ -228,16 +269,74 @@ pub struct Selected<'a> {
|
|||
pub document: &'a Document,
|
||||
pub original_transforms: &'a mut OriginalTransforms,
|
||||
pub pivot: &'a mut DVec2,
|
||||
pub shape_editor: Option<&'a ShapeState>,
|
||||
pub tool_type: &'a ToolType,
|
||||
}
|
||||
|
||||
impl<'a> Selected<'a> {
|
||||
pub fn new(original_transforms: &'a mut OriginalTransforms, pivot: &'a mut DVec2, selected: &'a [&'a Vec<LayerId>], responses: &'a mut VecDeque<Message>, document: &'a Document) -> Self {
|
||||
for path in selected {
|
||||
if !original_transforms.contains_key(*path) {
|
||||
if let Ok(layer) = document.layer(path) {
|
||||
original_transforms.insert(path.to_vec(), layer.transform);
|
||||
} else {
|
||||
warn!("Didn't find a layer for {:?}", path);
|
||||
pub fn new(
|
||||
original_transforms: &'a mut OriginalTransforms,
|
||||
pivot: &'a mut DVec2,
|
||||
selected: &'a [&'a Vec<LayerId>],
|
||||
responses: &'a mut VecDeque<Message>,
|
||||
document: &'a Document,
|
||||
shape_editor: Option<&'a ShapeState>,
|
||||
tool_type: &'a ToolType,
|
||||
) -> Self {
|
||||
// If user is using the Select tool then use the original layer transforms
|
||||
if (*tool_type == ToolType::Select) && (*original_transforms == OriginalTransforms::Path(HashMap::new())) {
|
||||
*original_transforms = OriginalTransforms::Layer(HashMap::new());
|
||||
}
|
||||
|
||||
match original_transforms {
|
||||
OriginalTransforms::Layer(layer_map) => {
|
||||
for layer_path in selected {
|
||||
if !layer_map.contains_key(*layer_path) {
|
||||
if let Ok(layer) = document.layer(layer_path) {
|
||||
layer_map.insert(layer_path.to_vec(), layer.transform);
|
||||
} else {
|
||||
warn!("Didn't find a layer for {:?}", layer_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OriginalTransforms::Path(path_map) => {
|
||||
for path in selected {
|
||||
let Some(shape_editor) = shape_editor else {
|
||||
warn!("No shape editor structure found, which only happens in select tool, which cannot reach this point as we check for ToolType");
|
||||
continue;
|
||||
};
|
||||
// Anchors also move their handles
|
||||
let expand_anchors = |&point: &ManipulatorPointId| {
|
||||
if point.manipulator_type.is_handle() {
|
||||
[Some(point), None, None]
|
||||
} else {
|
||||
[
|
||||
Some(point),
|
||||
Some(ManipulatorPointId::new(point.group, SelectedType::InHandle)),
|
||||
Some(ManipulatorPointId::new(point.group, SelectedType::OutHandle)),
|
||||
]
|
||||
}
|
||||
};
|
||||
let points = shape_editor.selected_points().flat_map(expand_anchors).flatten();
|
||||
if path_map.contains_key(*path) {
|
||||
continue;
|
||||
}
|
||||
let Ok(layer) = document.layer(path) else {
|
||||
warn!("Didn't find a layer for {:?}", path);
|
||||
continue;
|
||||
};
|
||||
let Some(vector_data) = layer.as_vector_data() else {
|
||||
warn!("Didn't find a vectordata for {:?}", layer);
|
||||
continue;
|
||||
};
|
||||
let get_manipulator_point_position = |point_id: ManipulatorPointId| {
|
||||
vector_data
|
||||
.manipulator_from_id(point_id.group)
|
||||
.and_then(|manipulator_group| point_id.manipulator_type.get_position(manipulator_group))
|
||||
.map(|position| (point_id, position))
|
||||
};
|
||||
path_map.insert(path.to_vec(), points.filter_map(get_manipulator_point_position).collect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -247,6 +346,8 @@ impl<'a> Selected<'a> {
|
|||
document,
|
||||
original_transforms,
|
||||
pivot,
|
||||
shape_editor,
|
||||
tool_type,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,33 +379,79 @@ impl<'a> Selected<'a> {
|
|||
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
|
||||
for layer_path in Document::shallowest_unique_layers(self.selected.iter()) {
|
||||
let parent_folder_path = &layer_path[..layer_path.len() - 1];
|
||||
let original_layer_transforms = *self.original_transforms.get(*layer_path).unwrap();
|
||||
if *self.tool_type == ToolType::Select {
|
||||
let original_layer_transforms = match self.original_transforms {
|
||||
OriginalTransforms::Layer(layer_map) => *layer_map.get(*layer_path).unwrap(),
|
||||
OriginalTransforms::Path(_path_map) => {
|
||||
warn!("Found Path variant in original_transforms, returning identity transform for layer {:?}", layer_path);
|
||||
DAffine2::IDENTITY
|
||||
}
|
||||
};
|
||||
let to = self.document.generate_transform_across_scope(parent_folder_path, None).unwrap();
|
||||
let new = to.inverse() * transformation * to * original_layer_transforms;
|
||||
self.responses.add(GraphOperationMessage::TransformSet {
|
||||
layer: layer_path.to_vec(),
|
||||
transform: new,
|
||||
transform_in: TransformIn::Local,
|
||||
skip_rerender: true,
|
||||
});
|
||||
}
|
||||
if *self.tool_type == ToolType::Path {
|
||||
let viewspace = self.document.generate_transform_relative_to_viewport(layer_path).ok().unwrap_or_default();
|
||||
let layerspace_rotation = viewspace.inverse() * transformation;
|
||||
|
||||
let to = self.document.generate_transform_across_scope(parent_folder_path, None).unwrap();
|
||||
let new = to.inverse() * transformation * to * original_layer_transforms;
|
||||
let initial_points = match self.original_transforms {
|
||||
OriginalTransforms::Layer(_layer_map) => {
|
||||
warn!("Found Layer variant in original_transforms when Path wanted, returning identity transform for layer");
|
||||
None
|
||||
}
|
||||
OriginalTransforms::Path(path_map) => path_map.get(*layer_path),
|
||||
};
|
||||
|
||||
self.responses.add(GraphOperationMessage::TransformSet {
|
||||
layer: layer_path.to_vec(),
|
||||
transform: new,
|
||||
transform_in: TransformIn::Local,
|
||||
skip_rerender: true,
|
||||
});
|
||||
let Some(original) = initial_points else {
|
||||
warn!("Initial Points empty, it should not be possible to reach here without points");
|
||||
continue;
|
||||
};
|
||||
for (point_id, position) in original {
|
||||
let viewport_point = viewspace.transform_point2(*position);
|
||||
let new_pos_viewport = layerspace_rotation.transform_point2(viewport_point);
|
||||
let point = *point_id;
|
||||
let position = new_pos_viewport;
|
||||
|
||||
self.responses.add(GraphOperationMessage::Vector {
|
||||
layer: (*layer_path).to_vec(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
});
|
||||
}
|
||||
}
|
||||
self.responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||
}
|
||||
|
||||
self.responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn revert_operation(&mut self) {
|
||||
for layer in self.selected {
|
||||
if let Some(&transform) = self.original_transforms.get(*layer) {
|
||||
// Push front to stop document switching before sending the transform
|
||||
self.responses.add(GraphOperationMessage::TransformSet {
|
||||
layer: layer.to_vec(),
|
||||
transform,
|
||||
transform_in: TransformIn::Local,
|
||||
skip_rerender: false,
|
||||
});
|
||||
for path in self.selected.iter().copied() {
|
||||
let original_transform = &self.original_transforms;
|
||||
match original_transform {
|
||||
OriginalTransforms::Layer(hash) => {
|
||||
let Some(matrix) = hash.get(path) else { continue };
|
||||
self.responses.add(GraphOperationMessage::TransformSet {
|
||||
layer: path.to_vec(),
|
||||
transform: *matrix,
|
||||
transform_in: TransformIn::Local,
|
||||
skip_rerender: false,
|
||||
});
|
||||
}
|
||||
OriginalTransforms::Path(path) => {
|
||||
for (layer_path, points) in path {
|
||||
for &(point, position) in points {
|
||||
self.responses.add(GraphOperationMessage::Vector {
|
||||
layer: (*layer_path).clone(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue