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:
Henry Sloan 2021-07-31 18:18:54 -04:00 committed by Keavon Chambers
parent e02250e8c6
commit 6bb8357dbb
3 changed files with 54 additions and 15 deletions

View File

@ -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(),
);

View File

@ -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};

View File

@ -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
}