Add G/R/S to the Pen tool to control the outgoing segment handle (#2211)
* more_refactoring_solve_conflict * overlays-target-fix * Code review * select-broken-fix --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
96c57605b7
commit
9e2bda36b0
|
|
@ -260,6 +260,9 @@ pub fn input_mappings() -> Mapping {
|
|||
entry!(KeyDown(Enter); action_dispatch=PenToolMessage::Confirm),
|
||||
entry!(KeyDown(Delete); action_dispatch=PenToolMessage::RemovePreviousHandle),
|
||||
entry!(KeyDown(Backspace); action_dispatch=PenToolMessage::RemovePreviousHandle),
|
||||
entry!(KeyDown(KeyG); action_dispatch=PenToolMessage::GRS { grab: KeyG, rotate: KeyR, scale: KeyS }),
|
||||
entry!(KeyDown(KeyR); action_dispatch=PenToolMessage::GRS { grab: KeyG, rotate: KeyR, scale: KeyS }),
|
||||
entry!(KeyDown(KeyS); action_dispatch=PenToolMessage::GRS { grab: KeyG, rotate: KeyR, scale: KeyS }),
|
||||
//
|
||||
// FreehandToolMessage
|
||||
entry!(PointerMove; action_dispatch=FreehandToolMessage::PointerMove),
|
||||
|
|
|
|||
|
|
@ -384,6 +384,8 @@ pub struct Selected<'a> {
|
|||
pub pivot: &'a mut DVec2,
|
||||
pub shape_editor: Option<&'a ShapeState>,
|
||||
pub tool_type: &'a ToolType,
|
||||
// Only for the Pen tool
|
||||
pub pen_handle: Option<&'a mut DVec2>,
|
||||
}
|
||||
|
||||
impl<'a> Selected<'a> {
|
||||
|
|
@ -396,6 +398,7 @@ impl<'a> Selected<'a> {
|
|||
network_interface: &'a NodeNetworkInterface,
|
||||
shape_editor: Option<&'a ShapeState>,
|
||||
tool_type: &'a ToolType,
|
||||
pen_handle: Option<&'a mut DVec2>,
|
||||
) -> 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())) {
|
||||
|
|
@ -412,6 +415,7 @@ impl<'a> Selected<'a> {
|
|||
pivot,
|
||||
shape_editor,
|
||||
tool_type,
|
||||
pen_handle,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -502,6 +506,13 @@ impl<'a> Selected<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn apply_transform_pen(&mut self, transformation: DAffine2) {
|
||||
if let Some(pen_handle) = &self.pen_handle {
|
||||
let final_position = transformation.transform_point2(**pen_handle);
|
||||
self.responses.add(PenToolMessage::FinalPosition { final_position });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_transformation(&mut self, transformation: DAffine2) {
|
||||
if !self.selected.is_empty() {
|
||||
// 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
|
||||
|
|
@ -523,7 +534,10 @@ impl<'a> Selected<'a> {
|
|||
pub fn update_transforms(&mut self, delta: DAffine2) {
|
||||
let pivot = DAffine2::from_translation(*self.pivot);
|
||||
let transformation = pivot * delta * pivot.inverse();
|
||||
self.apply_transformation(transformation);
|
||||
match self.tool_type {
|
||||
ToolType::Pen => self.apply_transform_pen(transformation),
|
||||
_ => self.apply_transformation(transformation),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn revert_operation(&mut self) {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ pub enum PenToolMessage {
|
|||
UpdateOptions(PenOptionsUpdate),
|
||||
RecalculateLatestPointsPosition,
|
||||
RemovePreviousHandle,
|
||||
GRS { grab: Key, rotate: Key, scale: Key },
|
||||
FinalPosition { final_position: DVec2 },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -71,6 +73,7 @@ enum PenToolFsmState {
|
|||
Ready,
|
||||
DraggingHandle,
|
||||
PlacingAnchor,
|
||||
GRSHandle,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
|
|
@ -162,13 +165,14 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool
|
|||
|
||||
fn actions(&self) -> ActionList {
|
||||
match self.fsm_state {
|
||||
PenToolFsmState::Ready => actions!(PenToolMessageDiscriminant;
|
||||
PenToolFsmState::Ready | PenToolFsmState::GRSHandle => actions!(PenToolMessageDiscriminant;
|
||||
Undo,
|
||||
DragStart,
|
||||
DragStop,
|
||||
Confirm,
|
||||
Abort,
|
||||
PointerMove,
|
||||
FinalPosition
|
||||
),
|
||||
PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor => actions!(PenToolMessageDiscriminant;
|
||||
DragStart,
|
||||
|
|
@ -177,6 +181,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool
|
|||
Confirm,
|
||||
Abort,
|
||||
RemovePreviousHandle,
|
||||
GRS,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -223,6 +228,8 @@ struct PenToolData {
|
|||
modifiers: ModifierState,
|
||||
|
||||
buffering_merged_vector: bool,
|
||||
|
||||
before_grs_pos: DVec2,
|
||||
}
|
||||
impl PenToolData {
|
||||
fn latest_point(&self) -> Option<&LastPoint> {
|
||||
|
|
@ -563,6 +570,72 @@ impl Fsm for PenToolFsmState {
|
|||
|
||||
let ToolMessage::Pen(event) = event else { return self };
|
||||
match (self, event) {
|
||||
(PenToolFsmState::PlacingAnchor | PenToolFsmState::GRSHandle, PenToolMessage::GRS { grab, rotate, scale }) => {
|
||||
let Some(latest) = tool_data.latest_point_mut() else { return PenToolFsmState::PlacingAnchor };
|
||||
let Some(layer) = layer else { return PenToolFsmState::PlacingAnchor };
|
||||
|
||||
if latest.handle_start != latest.pos {
|
||||
let viewport = document.metadata().transform_to_viewport(layer);
|
||||
let last_point = viewport.transform_point2(latest.pos);
|
||||
let handle = viewport.transform_point2(latest.handle_start);
|
||||
|
||||
if input.keyboard.key(grab) {
|
||||
responses.add(TransformLayerMessage::BeginGrabPen { last_point, handle });
|
||||
} else if input.keyboard.key(rotate) {
|
||||
responses.add(TransformLayerMessage::BeginRotatePen { last_point, handle });
|
||||
} else if input.keyboard.key(scale) {
|
||||
responses.add(TransformLayerMessage::BeginScalePen { last_point, handle });
|
||||
}
|
||||
|
||||
tool_data.before_grs_pos = latest.handle_start;
|
||||
}
|
||||
|
||||
PenToolFsmState::GRSHandle
|
||||
}
|
||||
(PenToolFsmState::GRSHandle, PenToolMessage::FinalPosition { final_position: final_pos }) => {
|
||||
let Some(layer) = layer else { return PenToolFsmState::GRSHandle };
|
||||
|
||||
if let Some(latest_pt) = tool_data.latest_point_mut() {
|
||||
let layer_space_to_viewport = document.metadata().transform_to_viewport(layer);
|
||||
let final_pos = layer_space_to_viewport.inverse().transform_point2(final_pos);
|
||||
latest_pt.handle_start = final_pos;
|
||||
}
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
|
||||
PenToolFsmState::GRSHandle
|
||||
}
|
||||
(PenToolFsmState::GRSHandle, PenToolMessage::Confirm) => {
|
||||
tool_data.next_point = input.mouse.position;
|
||||
tool_data.next_handle_start = input.mouse.position;
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
responses.add(PenToolMessage::PointerMove {
|
||||
snap_angle: Key::Control,
|
||||
break_handle: Key::Alt,
|
||||
lock_angle: Key::Shift,
|
||||
});
|
||||
|
||||
PenToolFsmState::PlacingAnchor
|
||||
}
|
||||
(PenToolFsmState::GRSHandle, PenToolMessage::Abort) => {
|
||||
tool_data.next_point = input.mouse.position;
|
||||
tool_data.next_handle_start = input.mouse.position;
|
||||
|
||||
let previous = tool_data.before_grs_pos;
|
||||
if let Some(latest) = tool_data.latest_point_mut() {
|
||||
latest.handle_start = previous;
|
||||
}
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
responses.add(PenToolMessage::PointerMove {
|
||||
snap_angle: Key::Control,
|
||||
break_handle: Key::Alt,
|
||||
lock_angle: Key::Shift,
|
||||
});
|
||||
|
||||
PenToolFsmState::PlacingAnchor
|
||||
}
|
||||
(_, PenToolMessage::SelectionChanged) => {
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
self
|
||||
|
|
@ -814,7 +887,7 @@ impl Fsm for PenToolFsmState {
|
|||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
PenToolFsmState::Ready | PenToolFsmState::GRSHandle => HintData(vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::Lmb, "Draw Path"),
|
||||
// TODO: Only show this if a single layer is selected and it's of a valid type (e.g. a vector path but not raster or artboard)
|
||||
HintInfo::keys([Key::Shift], "Append to Selected Layer").prepend_plus(),
|
||||
|
|
|
|||
|
|
@ -632,6 +632,7 @@ impl Fsm for SelectToolFsmState {
|
|||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
None
|
||||
);
|
||||
bounds.center_of_transformation = selected.mean_average_of_pivots();
|
||||
}
|
||||
|
|
@ -660,6 +661,7 @@ impl Fsm for SelectToolFsmState {
|
|||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
None
|
||||
);
|
||||
|
||||
bounds.center_of_transformation = selected.mean_average_of_pivots();
|
||||
|
|
@ -785,7 +787,16 @@ impl Fsm for SelectToolFsmState {
|
|||
}
|
||||
});
|
||||
let selected = &tool_data.layers_dragging;
|
||||
let mut selected = Selected::new(&mut bounds.original_transforms, &mut pivot, selected, responses, &document.network_interface, None, &ToolType::Select);
|
||||
let mut selected = Selected::new(
|
||||
&mut bounds.original_transforms,
|
||||
&mut pivot,
|
||||
selected,
|
||||
responses,
|
||||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
None,
|
||||
);
|
||||
|
||||
selected.apply_transformation(bounds.original_bound_transform * transformation * bounds.original_bound_transform.inverse());
|
||||
|
||||
|
|
@ -833,6 +844,7 @@ impl Fsm for SelectToolFsmState {
|
|||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
None,
|
||||
);
|
||||
|
||||
selected.update_transforms(delta);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use crate::messages::input_mapper::utility_types::input_keyboard::Key;
|
|||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
#[impl_message(Message, ToolMessage, TransformLayer)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum TransformLayerMessage {
|
||||
|
|
@ -10,6 +12,9 @@ pub enum TransformLayerMessage {
|
|||
BeginGrab,
|
||||
BeginRotate,
|
||||
BeginScale,
|
||||
BeginGrabPen { last_point: DVec2, handle: DVec2 },
|
||||
BeginRotatePen { last_point: DVec2, handle: DVec2 },
|
||||
BeginScalePen { last_point: DVec2, handle: DVec2 },
|
||||
CancelTransformOperation,
|
||||
ConstrainX,
|
||||
ConstrainY,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ use crate::messages::portfolio::document::overlays::utility_types::{OverlayProvi
|
|||
use crate::messages::portfolio::document::utility_types::transformation::{Axis, OriginalTransforms, Selected, TransformOperation, Typing};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||
use crate::messages::tool::tool_messages::tool_prelude::Key;
|
||||
use crate::messages::tool::utility_types::{ToolData, ToolType};
|
||||
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::vector::ManipulatorPointId;
|
||||
use graphene_std::vector::VectorData;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::f64::consts::TAU;
|
||||
|
|
@ -29,6 +31,12 @@ pub struct TransformLayerMessageHandler {
|
|||
|
||||
original_transforms: OriginalTransforms,
|
||||
pivot: DVec2,
|
||||
grab_target: DVec2,
|
||||
|
||||
// Pen tool (outgoing handle GRS manipulation)
|
||||
handle: DVec2,
|
||||
last_point: DVec2,
|
||||
grs_pen_handle: bool,
|
||||
}
|
||||
|
||||
impl TransformLayerMessageHandler {
|
||||
|
|
@ -41,11 +49,37 @@ impl TransformLayerMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
fn calculate_pivot(selected_points: &Vec<&ManipulatorPointId>, vector_data: &VectorData, viewspace: DAffine2, get_location: impl Fn(&ManipulatorPointId) -> Option<DVec2>) -> Option<(DVec2, DVec2)> {
|
||||
let [point] = selected_points.as_slice() else {
|
||||
// Handle the case where there are multiple points
|
||||
let mut point_count = 0;
|
||||
let average_position = selected_points.iter().filter_map(|p| get_location(p)).inspect(|_| point_count += 1).sum::<DVec2>() / point_count as f64;
|
||||
|
||||
return Some((average_position, average_position));
|
||||
};
|
||||
|
||||
match point {
|
||||
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => {
|
||||
// Get the anchor position and transform it to the pivot
|
||||
let pivot_pos = point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position))?;
|
||||
let target = viewspace.transform_point2(point.get_position(vector_data)?);
|
||||
Some((pivot_pos, target))
|
||||
}
|
||||
_ => {
|
||||
// Calculate the average position of all selected points
|
||||
let mut point_count = 0;
|
||||
let average_position = selected_points.iter().filter_map(|p| get_location(p)).inspect(|_| point_count += 1).sum::<DVec2>() / point_count as f64;
|
||||
Some((average_position, average_position))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TransformData<'a> = (&'a DocumentMessageHandler, &'a InputPreprocessorMessageHandler, &'a ToolData, &'a mut ShapeState);
|
||||
impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayerMessageHandler {
|
||||
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, input, tool_data, shape_editor): TransformData) {
|
||||
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
||||
let using_select_tool = tool_data.active_tool_type == ToolType::Select;
|
||||
let using_pen_tool = tool_data.active_tool_type == ToolType::Pen;
|
||||
|
||||
// TODO: Add support for transforming layer not in the document network
|
||||
let selected_layers = document
|
||||
|
|
@ -64,6 +98,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
&document.network_interface,
|
||||
Some(shape_editor),
|
||||
&tool_data.active_tool_type,
|
||||
Some(&mut self.handle),
|
||||
);
|
||||
|
||||
let mut begin_operation = |operation: TransformOperation, typing: &mut Typing, mouse_position: &mut DVec2, start_mouse: &mut DVec2| {
|
||||
|
|
@ -72,27 +107,32 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
typing.clear();
|
||||
}
|
||||
|
||||
if using_path_tool {
|
||||
if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
|
||||
*selected.original_transforms = OriginalTransforms::default();
|
||||
let viewspace = document.metadata().transform_to_viewport(selected_layers[0]);
|
||||
if using_pen_tool {
|
||||
selected.responses.add(PenToolMessage::GRS {
|
||||
grab: Key::KeyG,
|
||||
rotate: Key::KeyR,
|
||||
scale: Key::KeyS,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let mut point_count: usize = 0;
|
||||
let get_location = |point: &&ManipulatorPointId| point.get_position(&vector_data).map(|position| viewspace.transform_point2(position));
|
||||
let points = shape_editor.selected_points();
|
||||
let selected_points: Vec<&ManipulatorPointId> = points.collect();
|
||||
|
||||
if let [point] = selected_points.as_slice() {
|
||||
if let ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) = point {
|
||||
let anchor_position = point.get_anchor_position(&vector_data).unwrap();
|
||||
*selected.pivot = viewspace.transform_point2(anchor_position);
|
||||
} else {
|
||||
*selected.pivot = selected_points.iter().filter_map(get_location).inspect(|_| point_count += 1).sum::<DVec2>() / point_count as f64;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !using_path_tool {
|
||||
*selected.pivot = selected.mean_average_of_pivots();
|
||||
self.grab_target = selected.mean_average_of_pivots();
|
||||
} else if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
|
||||
*selected.original_transforms = OriginalTransforms::default();
|
||||
|
||||
let viewspace = document.metadata().transform_to_viewport(selected_layers[0]);
|
||||
let selected_points = shape_editor.selected_points().collect::<Vec<_>>();
|
||||
|
||||
let get_location = |point: &&ManipulatorPointId| point.get_position(&vector_data).map(|position| viewspace.transform_point2(position));
|
||||
if let Some((new_pivot, grab_target)) = calculate_pivot(&selected_points, &vector_data, viewspace, |point: &ManipulatorPointId| get_location(&point)) {
|
||||
*selected.pivot = new_pivot;
|
||||
|
||||
self.grab_target = grab_target;
|
||||
} else {
|
||||
log::warn!("Failed to calculate pivot.");
|
||||
}
|
||||
}
|
||||
|
||||
*mouse_position = input.mouse.position;
|
||||
|
|
@ -106,18 +146,53 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
match message {
|
||||
TransformLayerMessage::ApplyTransformOperation => {
|
||||
selected.original_transforms.clear();
|
||||
|
||||
self.typing.clear();
|
||||
|
||||
self.transform_operation = TransformOperation::None;
|
||||
|
||||
if using_pen_tool {
|
||||
self.last_point = DVec2::ZERO;
|
||||
self.grs_pen_handle = false;
|
||||
|
||||
selected.pen_handle = None;
|
||||
|
||||
selected.responses.add(PenToolMessage::Confirm);
|
||||
} else {
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
responses.add(ToolMessage::UpdateHints);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
||||
responses.add(OverlaysMessage::RemoveProvider(TRANSFORM_GRS_OVERLAY_PROVIDER));
|
||||
}
|
||||
TransformLayerMessage::BeginGrabPen { last_point, handle } | TransformLayerMessage::BeginRotatePen { last_point, handle } | TransformLayerMessage::BeginScalePen { last_point, handle } => {
|
||||
self.typing.clear();
|
||||
|
||||
self.last_point = last_point;
|
||||
self.handle = handle;
|
||||
self.grs_pen_handle = true;
|
||||
self.mouse_position = input.mouse.position;
|
||||
self.start_mouse = input.mouse.position;
|
||||
|
||||
let top_left = DVec2::new(last_point.x, handle.y);
|
||||
let bottom_right = DVec2::new(handle.x, last_point.y);
|
||||
self.local = false;
|
||||
self.fixed_bbox = Quad::from_box([top_left, bottom_right]);
|
||||
self.grab_target = handle;
|
||||
self.pivot = last_point;
|
||||
self.handle = handle;
|
||||
|
||||
// Operation-specific logic
|
||||
self.transform_operation = match message {
|
||||
TransformLayerMessage::BeginGrabPen { .. } => TransformOperation::Grabbing(Default::default()),
|
||||
TransformLayerMessage::BeginRotatePen { .. } => TransformOperation::Rotating(Default::default()),
|
||||
TransformLayerMessage::BeginScalePen { .. } => TransformOperation::Scaling(Default::default()),
|
||||
_ => unreachable!(), // Safe because the match arms are exhaustive
|
||||
};
|
||||
|
||||
responses.add(OverlaysMessage::AddProvider(TRANSFORM_GRS_OVERLAY_PROVIDER));
|
||||
}
|
||||
TransformLayerMessage::BeginGrab => {
|
||||
if (!using_path_tool && !using_select_tool)
|
||||
if (!using_path_tool && !using_select_tool && !using_pen_tool)
|
||||
|| (using_path_tool && shape_editor.selected_points().next().is_none())
|
||||
|| selected_layers.is_empty()
|
||||
|| matches!(self.transform_operation, TransformOperation::Grabbing(_))
|
||||
|
|
@ -140,7 +215,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
TransformLayerMessage::BeginRotate => {
|
||||
let selected_points: Vec<&ManipulatorPointId> = shape_editor.selected_points().collect();
|
||||
|
||||
if (!using_path_tool && !using_select_tool)
|
||||
if (!using_path_tool && !using_select_tool && !using_pen_tool)
|
||||
|| (using_path_tool && selected_points.is_empty())
|
||||
|| selected_layers.is_empty()
|
||||
|| matches!(self.transform_operation, TransformOperation::Rotating(_))
|
||||
|
|
@ -191,7 +266,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
let selected_points: Vec<&ManipulatorPointId> = shape_editor.selected_points().collect();
|
||||
|
||||
if (using_path_tool && selected_points.is_empty())
|
||||
|| (!using_path_tool && !using_select_tool)
|
||||
|| (!using_path_tool && !using_select_tool && !using_pen_tool)
|
||||
|| selected_layers.is_empty()
|
||||
|| matches!(self.transform_operation, TransformOperation::Scaling(_))
|
||||
{
|
||||
|
|
@ -237,15 +312,23 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
responses.add(OverlaysMessage::AddProvider(TRANSFORM_GRS_OVERLAY_PROVIDER));
|
||||
}
|
||||
TransformLayerMessage::CancelTransformOperation => {
|
||||
selected.revert_operation();
|
||||
|
||||
selected.original_transforms.clear();
|
||||
if using_pen_tool {
|
||||
self.typing.clear();
|
||||
|
||||
self.last_point = DVec2::ZERO;
|
||||
self.transform_operation = TransformOperation::None;
|
||||
self.handle = DVec2::ZERO;
|
||||
|
||||
responses.add(PenToolMessage::Abort);
|
||||
} else {
|
||||
selected.revert_operation();
|
||||
selected.original_transforms.clear();
|
||||
self.typing.clear();
|
||||
self.transform_operation = TransformOperation::None;
|
||||
|
||||
responses.add(DocumentMessage::AbortTransaction);
|
||||
responses.add(ToolMessage::UpdateHints);
|
||||
}
|
||||
|
||||
responses.add(OverlaysMessage::RemoveProvider(TRANSFORM_GRS_OVERLAY_PROVIDER));
|
||||
}
|
||||
|
|
@ -298,7 +381,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
TransformOperation::Grabbing(translation) => {
|
||||
let translation = document_to_viewport.inverse().transform_vector2(translation.to_dvec(document_to_viewport));
|
||||
let vec_to_end = self.mouse_position - self.start_mouse;
|
||||
let quad = Quad::from_box([self.pivot, self.pivot + vec_to_end]).0;
|
||||
let quad = Quad::from_box([self.grab_target, self.grab_target + vec_to_end]).0;
|
||||
let e1 = (self.fixed_bbox.0[1] - self.fixed_bbox.0[0]).normalize();
|
||||
|
||||
if matches!(axis_constraint, Axis::Both | Axis::X) {
|
||||
|
|
@ -374,7 +457,8 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
TransformOperation::Rotating(rotation) => {
|
||||
let angle = rotation.to_f64(self.snap);
|
||||
let quad = self.fixed_bbox.0;
|
||||
let offset_angle = (quad[1] - quad[0]).to_angle();
|
||||
let offset_angle = if self.grs_pen_handle { self.handle - self.last_point } else { quad[1] - quad[0] };
|
||||
let offset_angle = offset_angle.to_angle();
|
||||
let width = viewport_box.max_element();
|
||||
let radius = self.start_mouse.distance(self.pivot);
|
||||
let arc_radius = ANGLE_MEASURE_RADIUS_FACTOR * width;
|
||||
|
|
@ -436,18 +520,13 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
|
||||
(current_frame_dist - previous_frame_dist) / start_transform_dist
|
||||
};
|
||||
let region_negate = (self.start_mouse - *selected.pivot).dot(self.mouse_position - *selected.pivot) < 0.;
|
||||
let change = if self.slow { change / SLOWING_DIVISOR } else { change };
|
||||
let change = change * scale.dragged_factor.signum();
|
||||
|
||||
self.transform_operation = TransformOperation::Scaling(scale.increment_amount(change));
|
||||
if region_negate {
|
||||
let tmp_operation = TransformOperation::Scaling(scale.negate());
|
||||
tmp_operation.apply_transform_operation(&mut selected, self.snap, self.local, self.fixed_bbox, document_to_viewport);
|
||||
} else {
|
||||
|
||||
self.transform_operation
|
||||
.apply_transform_operation(&mut selected, self.snap, self.local, self.fixed_bbox, document_to_viewport);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -470,7 +549,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
}
|
||||
TransformLayerMessage::TypeNegate => {
|
||||
if self.typing.digits.is_empty() {
|
||||
self.transform_operation.negate(&mut selected, self.snap, self.local, self.fixed_bbox, document_to_viewport)
|
||||
self.transform_operation.negate(&mut selected, self.snap, self.local, self.fixed_bbox, document_to_viewport);
|
||||
}
|
||||
self.transform_operation
|
||||
.grs_typed(self.typing.type_negate(), &mut selected, self.snap, self.local, self.fixed_bbox, document_to_viewport)
|
||||
|
|
|
|||
Loading…
Reference in New Issue