From fa7116133bd3e5d2ca95aacfa20d3e1e67966f8e Mon Sep 17 00:00:00 2001 From: 0HyperCube <78500760+0HyperCube@users.noreply.github.com> Date: Thu, 27 Oct 2022 00:14:32 +0100 Subject: [PATCH] Better checks for invalid transforms in the transform cage (#831) * Better checks for invalid transform * Fix resize cursor * Do not allow resizing with no width/height * Fix pivots for 0 width/height layers --- .../tool/common_functionality/pivot.rs | 10 +++++- .../transformation_cage.rs | 35 +++++++++++++------ graphene/src/layers/layer_info.rs | 11 +++++- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index 14433e32..259b5e53 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -41,7 +41,15 @@ impl Default for Pivot { impl Pivot { /// Calculates the transform that gets from normalized pivot to viewspace. fn get_layer_pivot_transform(layer_path: &[LayerId], layer: &graphene::layers::layer_info::Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> DAffine2 { - let [min, max] = layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]); + let [mut min, max] = layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]); + + // If the layer bounds are 0 in either axis then set them to one (to avoid div 0) + if (max.x - min.x) < f64::EPSILON * 1000. { + min.x = max.x - 1.; + } + if (max.y - min.y) < f64::EPSILON * 1000. { + min.y = max.y - 1.; + } let bounds_transform = DAffine2::from_translation(min) * DAffine2::from_scale(max - min); let layer_transform = document.graphene_document.multiply_transforms(layer_path).unwrap_or(DAffine2::IDENTITY); layer_transform * bounds_transform diff --git a/editor/src/messages/tool/common_functionality/transformation_cage.rs b/editor/src/messages/tool/common_functionality/transformation_cage.rs index dcf4e48b..084557da 100644 --- a/editor/src/messages/tool/common_functionality/transformation_cage.rs +++ b/editor/src/messages/tool/common_functionality/transformation_cage.rs @@ -130,18 +130,19 @@ impl SelectedEdges { /// Calculates the required scaling to resize the bounding box pub fn bounds_to_scale_transform(&self, position: DVec2, size: DVec2) -> (DAffine2, DVec2) { - let mut enlargement_factor = size / (self.bounds[1] - self.bounds[0]); - if enlargement_factor.x.is_nan() { - enlargement_factor.x = 0.; + let old_size = self.bounds[1] - self.bounds[0]; + let mut enlargement_factor = size / old_size; + if !enlargement_factor.x.is_finite() || old_size.x.abs() < f64::EPSILON * 1000. { + enlargement_factor.x = 1.; } - if enlargement_factor.y.is_nan() { - enlargement_factor.y = 0.; + if !enlargement_factor.y.is_finite() || old_size.y.abs() < f64::EPSILON * 1000. { + enlargement_factor.y = 1.; } let mut pivot = (self.bounds[0] * enlargement_factor - position) / (enlargement_factor - DVec2::splat(1.)); - if pivot.x.is_nan() { + if !pivot.x.is_finite() { pivot.x = 0.; } - if pivot.y.is_nan() { + if !pivot.y.is_finite() { pivot.y = 0.; } (DAffine2::from_scale(enlargement_factor), pivot) @@ -278,6 +279,8 @@ impl BoundingBoxOverlays { let mut bottom = (max.y - cursor.y).abs() < select_threshold; let mut left = (cursor.x - min.x).abs() < select_threshold; let mut right = (max.x - cursor.x).abs() < select_threshold; + + // Prioritise single axis transformations on very small bounds if cursor.y - min.y + max.y - cursor.y < select_threshold * 2. && (left || right) { top = false; bottom = false; @@ -287,6 +290,16 @@ impl BoundingBoxOverlays { right = false; } + // On bounds with no width/height, disallow transformation in the relevant axis + if (max.x - min.x) < f64::EPSILON * 1000. { + left = false; + right = false; + } + if (max.y - min.y) < f64::EPSILON * 1000. { + top = false; + bottom = false; + } + if top || bottom || left || right { return Some((top, bottom, left, right)); } @@ -313,10 +326,10 @@ impl BoundingBoxOverlays { pub fn get_cursor(&self, input: &InputPreprocessorMessageHandler, rotate: bool) -> MouseCursorIcon { if let Some(directions) = self.check_selected_edges(input.mouse.position) { match directions { - (true, false, false, false) | (false, true, false, false) => MouseCursorIcon::NSResize, - (false, false, true, false) | (false, false, false, true) => MouseCursorIcon::EWResize, - (true, false, true, false) | (false, true, false, true) => MouseCursorIcon::NWSEResize, - (true, false, false, true) | (false, true, true, false) => MouseCursorIcon::NESWResize, + (true, _, false, false) | (_, true, false, false) => MouseCursorIcon::NSResize, + (false, false, true, _) | (false, false, _, true) => MouseCursorIcon::EWResize, + (true, _, true, _) | (_, true, _, true) => MouseCursorIcon::NWSEResize, + (true, _, _, true) | (_, true, true, _) => MouseCursorIcon::NESWResize, _ => MouseCursorIcon::Default, } } else if rotate && self.check_rotate(input.mouse.position) { diff --git a/graphene/src/layers/layer_info.rs b/graphene/src/layers/layer_info.rs index bf46bf79..b9329692 100644 --- a/graphene/src/layers/layer_info.rs +++ b/graphene/src/layers/layer_info.rs @@ -374,7 +374,16 @@ impl Layer { } pub fn layerspace_pivot(&self, font_cache: &FontCache) -> DVec2 { - let [min, max] = self.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]); + let [mut min, max] = self.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]); + + // If the layer bounds are 0 in either axis then set them to one (to avoid div 0) + if (max.x - min.x) < f64::EPSILON * 1000. { + min.x = max.x - 1.; + } + if (max.y - min.y) < f64::EPSILON * 1000. { + min.y = max.y - 1.; + } + self.pivot * (max - min) + min }