mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Transformation cage (#502)
* Render corners and edges of selection box * Refactor * Add drag detection * Implement the transform handles * Implement rotation by dragging <40px from bounds * Refine clustered handle behaviour * Add cursors * Add snap angle * Fix MMB drag whilst in select tool * Convert calculate_pivot into a seperate function * rename start_vec to start_offset * Fix typo * Remove Undo transaction on <10px mouse move Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
2a313471d8
commit
599a9d076b
@@ -191,9 +191,9 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
||||
|
||||
let half_viewport = ipp.viewport_bounds.size() / 2.;
|
||||
let rotation = {
|
||||
let start_vec = self.mouse_position - half_viewport;
|
||||
let end_vec = ipp.mouse.position - half_viewport;
|
||||
start_vec.angle_between(end_vec)
|
||||
let start_offset = self.mouse_position - half_viewport;
|
||||
let end_offset = ipp.mouse.position - half_viewport;
|
||||
start_offset.angle_between(end_offset)
|
||||
};
|
||||
|
||||
responses.push_back(SetCanvasRotation { angle_radians: self.tilt + rotation }.into());
|
||||
@@ -329,7 +329,6 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
let mut common = actions!(MovementMessageDiscriminant;
|
||||
MouseMove,
|
||||
TranslateCanvasBegin,
|
||||
RotateCanvasBegin,
|
||||
ZoomCanvasBegin,
|
||||
@@ -345,6 +344,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
||||
|
||||
if self.panning || self.tilting || self.zooming {
|
||||
let transforming = actions!(MovementMessageDiscriminant;
|
||||
MouseMove,
|
||||
TransformCanvasEnd,
|
||||
);
|
||||
common.extend(transforming);
|
||||
|
||||
@@ -36,7 +36,9 @@ impl MessageHandler<TransformLayerMessage, (&mut HashMap<Vec<LayerId>, LayerMeta
|
||||
use TransformLayerMessage::*;
|
||||
|
||||
let (layer_metadata, document, ipp) = data;
|
||||
let mut selected = Selected::new(&mut self.original_transforms, &mut self.pivot, layer_metadata, responses, document);
|
||||
|
||||
let selected_layers = layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then(|| layer_path)).collect::<Vec<_>>();
|
||||
let mut selected = Selected::new(&mut self.original_transforms, &mut self.pivot, &selected_layers, responses, document);
|
||||
|
||||
let mut begin_operation = |operation: TransformOperation, typing: &mut Typing, mouse_position: &mut DVec2, start_mouse: &mut DVec2| {
|
||||
if operation != TransformOperation::None {
|
||||
@@ -128,10 +130,10 @@ impl MessageHandler<TransformLayerMessage, (&mut HashMap<Vec<LayerId>, LayerMeta
|
||||
TransformOperation::Rotating(rotation) => {
|
||||
let selected_pivot = selected.calculate_pivot();
|
||||
let angle = {
|
||||
let start_vec = self.mouse_position - selected_pivot;
|
||||
let end_vec = ipp.mouse.position - selected_pivot;
|
||||
let start_offset = self.mouse_position - selected_pivot;
|
||||
let end_offset = ipp.mouse.position - selected_pivot;
|
||||
|
||||
start_vec.angle_between(end_vec)
|
||||
start_offset.angle_between(end_offset)
|
||||
};
|
||||
|
||||
let change = if self.slow { angle / SLOWING_DIVISOR } else { angle };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use super::layer_panel::LayerMetadata;
|
||||
use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
|
||||
use crate::message_prelude::*;
|
||||
|
||||
@@ -188,25 +187,18 @@ impl TransformOperation {
|
||||
}
|
||||
|
||||
pub struct Selected<'a> {
|
||||
pub selected: Vec<Vec<LayerId>>,
|
||||
pub selected: &'a [&'a Vec<LayerId>],
|
||||
pub responses: &'a mut VecDeque<Message>,
|
||||
pub document: &'a mut Document,
|
||||
pub document: &'a Document,
|
||||
pub original_transforms: &'a mut OriginalTransforms,
|
||||
pub pivot: &'a mut DVec2,
|
||||
}
|
||||
|
||||
impl<'a> Selected<'a> {
|
||||
pub fn new(
|
||||
original_transforms: &'a mut OriginalTransforms,
|
||||
pivot: &'a mut DVec2,
|
||||
layer_metadata: &'a mut HashMap<Vec<LayerId>, LayerMetadata>,
|
||||
responses: &'a mut VecDeque<Message>,
|
||||
document: &'a mut Document,
|
||||
) -> Self {
|
||||
let selected = layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then(|| layer_path.to_owned())).collect();
|
||||
for path in &selected {
|
||||
if !original_transforms.contains_key::<Vec<LayerId>>(path) {
|
||||
original_transforms.insert(path.clone(), document.layer(path).unwrap().transform);
|
||||
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) {
|
||||
original_transforms.insert(path.to_vec(), document.layer(path).unwrap().transform);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
@@ -247,7 +239,7 @@ 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();
|
||||
let original_layer_transforms = *self.original_transforms.get(*layer_path).unwrap();
|
||||
|
||||
let to = self.document.generate_transform_across_scope(parent_folder_path, None).unwrap();
|
||||
let new = to.inverse() * transformation * to * original_layer_transforms;
|
||||
@@ -266,11 +258,11 @@ impl<'a> Selected<'a> {
|
||||
}
|
||||
|
||||
pub fn revert_operation(&mut self) {
|
||||
for path in &self.selected {
|
||||
for path in self.selected {
|
||||
self.responses.push_back(
|
||||
DocumentOperation::SetLayerTransform {
|
||||
path: path.to_vec(),
|
||||
transform: (*self.original_transforms.get(path).unwrap()).to_cols_array(),
|
||||
transform: (*self.original_transforms.get(*path).unwrap()).to_cols_array(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user