Change flipping to use the joint bounding box of the selection (#323)
* Change flipping to use the joint bounding box * Fix minor untested changes * Replace unwrap with question mark
This commit is contained in:
parent
e02250e8c6
commit
6bb8357dbb
|
|
@ -58,7 +58,7 @@ pub enum DocumentMessage {
|
|||
WheelCanvasZoom,
|
||||
SetCanvasRotation(f64),
|
||||
NudgeSelectedLayers(f64, f64),
|
||||
FlipLayer(Vec<LayerId>, bool, bool),
|
||||
FlipSelectedLayers(FlipAxis),
|
||||
AlignSelectedLayers(AlignAxis, AlignAggregate),
|
||||
DragLayer(Vec<LayerId>, DVec2),
|
||||
MoveSelectedLayersTo { path: Vec<LayerId>, insert_index: isize },
|
||||
|
|
@ -66,6 +66,12 @@ pub enum DocumentMessage {
|
|||
SetLayerTranslation(Vec<LayerId>, Option<f64>, Option<f64>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum FlipAxis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum AlignAxis {
|
||||
X,
|
||||
|
|
@ -652,13 +658,52 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
|||
}
|
||||
}
|
||||
}
|
||||
FlipLayer(path, flip_horizontal, flip_vertical) => {
|
||||
if let Ok(layer) = self.active_document_mut().document.layer_mut(&path) {
|
||||
let scale = DVec2::new(if flip_horizontal { -1. } else { 1. }, if flip_vertical { -1. } else { 1. });
|
||||
FlipSelectedLayers(axis) => {
|
||||
// TODO: Handle folder nested transforms with the transforms API
|
||||
let selected_paths = self.selected_layers_sorted();
|
||||
if selected_paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let selected_layers = selected_paths.iter().filter_map(|path| {
|
||||
let layer = self.active_document().document.layer(path).ok()?;
|
||||
// TODO: Refactor with `reduce` and `merge_bounding_boxes` once the latter is added
|
||||
let (min, max) = {
|
||||
let bounding_box = layer.bounding_box(layer.transform, layer.style)?;
|
||||
match axis {
|
||||
FlipAxis::X => (bounding_box[0].x, bounding_box[1].x),
|
||||
FlipAxis::Y => (bounding_box[0].y, bounding_box[1].y),
|
||||
}
|
||||
};
|
||||
Some((path.clone(), (min, max)))
|
||||
});
|
||||
|
||||
let (min, max) = selected_layers
|
||||
.clone()
|
||||
.map(|(_, extrema)| extrema)
|
||||
.reduce(|(min_a, max_a), (min_b, max_b)| (min_a.min(min_b), max_a.max(max_b)))
|
||||
.unwrap();
|
||||
let middle = (min + max) / 2.;
|
||||
|
||||
for (path, _) in selected_layers {
|
||||
let layer = self.active_document().document.layer(&path).unwrap();
|
||||
let mut transform = layer.transform;
|
||||
let scale = match axis {
|
||||
FlipAxis::X => DVec2::new(-1., 1.),
|
||||
FlipAxis::Y => DVec2::new(1., -1.),
|
||||
};
|
||||
transform = transform * DAffine2::from_scale(scale);
|
||||
|
||||
let coord = match axis {
|
||||
FlipAxis::X => &mut transform.translation.x,
|
||||
FlipAxis::Y => &mut transform.translation.y,
|
||||
};
|
||||
*coord = *coord - 2. * (*coord - middle);
|
||||
|
||||
responses.push_back(
|
||||
DocumentOperation::SetLayerTransform {
|
||||
path,
|
||||
transform: (layer.transform * DAffine2::from_scale(scale)).to_cols_array(),
|
||||
transform: transform.to_cols_array(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ mod document_message_handler;
|
|||
pub use document_file::{Document, LayerData};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use document_message_handler::{AlignAggregate, AlignAxis, DocumentMessage, DocumentMessageDiscriminant, DocumentMessageHandler};
|
||||
pub use document_message_handler::{AlignAggregate, AlignAxis, DocumentMessage, DocumentMessageDiscriminant, DocumentMessageHandler, FlipAxis};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
|||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{
|
||||
consts::SELECTION_TOLERANCE,
|
||||
document::{AlignAggregate, AlignAxis, Document},
|
||||
document::{AlignAggregate, AlignAxis, Document, FlipAxis},
|
||||
message_prelude::*,
|
||||
};
|
||||
|
||||
|
|
@ -173,18 +173,12 @@ impl Fsm for SelectToolFsmState {
|
|||
self
|
||||
}
|
||||
(_, FlipHorizontal) => {
|
||||
let selected_layers = document.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone()));
|
||||
for path in selected_layers {
|
||||
responses.push_back(DocumentMessage::FlipLayer(path, true, false).into());
|
||||
}
|
||||
responses.push_back(DocumentMessage::FlipSelectedLayers(FlipAxis::X).into());
|
||||
|
||||
self
|
||||
}
|
||||
(_, FlipVertical) => {
|
||||
let selected_layers = document.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone()));
|
||||
for path in selected_layers {
|
||||
responses.push_back(DocumentMessage::FlipLayer(path, false, true).into());
|
||||
}
|
||||
responses.push_back(DocumentMessage::FlipSelectedLayers(FlipAxis::Y).into());
|
||||
|
||||
self
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue