From 54b63c3eb5782ba98c99067fef87e3b05cec71bf Mon Sep 17 00:00:00 2001 From: Boutillier Date: Mon, 12 Sep 2022 10:46:11 +0200 Subject: [PATCH] Fix crashes related to 0-scale shapes (#777) * Fix crashes when dragging the bounding box/transform cage of a 0-scale shape. * Fix crashes when dragging the pivot point of a 0-scale shape * Fix rotation computation on DAffine2 when scale.x is 0, avoids Nan display * remove remaining log::info that I introduced in earlier commit * Fix crash when updating the scale of a transform that was already 0. * Fix NumberInput behaviour when the requested value changed is does not happen. * Fix rotation computation when Scale X and Scale Y are both 0. Display 0. This also fixes crashes when modifying the rotation in such case --- .../properties_panel/utility_functions.rs | 30 +++++++++++++++---- .../tool/common_functionality/pivot.rs | 9 ++++-- .../transformation_cage.rs | 8 ++++- .../components/widgets/inputs/NumberInput.vue | 5 ++-- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs b/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs index 516bd996..e63d5240 100644 --- a/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs +++ b/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs @@ -964,7 +964,12 @@ impl DAffine2Utils for DAffine2 { } fn update_scale_x(self, new_width: f64) -> Self { - self * DAffine2::from_scale((new_width / self.scale_x(), 1.).into()) + let scale_x = self.scale_x(); + if scale_x != 0. { + self * DAffine2::from_scale((new_width / scale_x, 1.).into()) + } else { + self + } } fn scale_y(&self) -> f64 { @@ -972,7 +977,12 @@ impl DAffine2Utils for DAffine2 { } fn update_scale_y(self, new_height: f64) -> Self { - self * DAffine2::from_scale((1., new_height / self.scale_y()).into()) + let scale_y = self.scale_y(); + if scale_y != 0. { + self * DAffine2::from_scale((1., new_height / scale_y).into()) + } else { + self + } } fn x(&self) -> f64 { @@ -994,9 +1004,19 @@ impl DAffine2Utils for DAffine2 { } fn rotation(&self) -> f64 { - let cos = self.matrix2.col(0).x / self.scale_x(); - let sin = self.matrix2.col(0).y / self.scale_x(); - sin.atan2(cos) + if self.scale_x() != 0. { + let cos = self.matrix2.col(0).x / self.scale_x(); + let sin = self.matrix2.col(0).y / self.scale_x(); + sin.atan2(cos) + } else if self.scale_y() != 0. { + let sin = -self.matrix2.col(1).x / self.scale_y(); + let cos = self.matrix2.col(1).y / self.scale_y(); + sin.atan2(cos) + } else { + // Rotation information does not exists anymore in the matrix + // return 0 for user experience. + 0. + } } fn update_rotation(self, new_rotation: f64) -> Self { diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index 5cf4c697..14433e32 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -158,9 +158,12 @@ impl Pivot { for layer_path in document.selected_visible_layers() { if let Ok(layer) = document.graphene_document.layer(layer_path) { let transform = Self::get_layer_pivot_transform(layer_path, layer, document, font_cache); - let pivot = transform.inverse().transform_point2(position).into(); - let layer_path = layer_path.to_owned(); - responses.push_back(Operation::SetPivot { layer_path, pivot }.into()); + let pivot = transform.inverse().transform_point2(position); + // Only update the pivot when computed position is finite. Infinite can happen when scale is 0. + if pivot.is_finite() { + let layer_path = layer_path.to_owned(); + responses.push_back(Operation::SetPivot { layer_path, pivot: pivot.into() }.into()); + } } } } diff --git a/editor/src/messages/tool/common_functionality/transformation_cage.rs b/editor/src/messages/tool/common_functionality/transformation_cage.rs index 0f914519..dcf4e48b 100644 --- a/editor/src/messages/tool/common_functionality/transformation_cage.rs +++ b/editor/src/messages/tool/common_functionality/transformation_cage.rs @@ -130,7 +130,13 @@ 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 enlargement_factor = size / (self.bounds[1] - self.bounds[0]); + let mut enlargement_factor = size / (self.bounds[1] - self.bounds[0]); + if enlargement_factor.x.is_nan() { + enlargement_factor.x = 0.; + } + if enlargement_factor.y.is_nan() { + enlargement_factor.y = 0.; + } let mut pivot = (self.bounds[0] * enlargement_factor - position) / (enlargement_factor - DVec2::splat(1.)); if pivot.x.is_nan() { pivot.x = 0.; diff --git a/frontend/src/components/widgets/inputs/NumberInput.vue b/frontend/src/components/widgets/inputs/NumberInput.vue index e6ac0846..2d801a8d 100644 --- a/frontend/src/components/widgets/inputs/NumberInput.vue +++ b/frontend/src/components/widgets/inputs/NumberInput.vue @@ -183,9 +183,10 @@ export default defineComponent({ if (typeof this.min === "number" && !Number.isNaN(this.min) && cleaned !== undefined) cleaned = Math.max(cleaned, this.min); if (typeof this.max === "number" && !Number.isNaN(this.max) && cleaned !== undefined) cleaned = Math.min(cleaned, this.max); - if (newValue !== undefined) this.$emit("update:value", cleaned); + // Required as the call to update:value can, not change the value + this.text = this.displayText(this.value); - this.text = this.displayText(cleaned); + if (newValue !== undefined) this.$emit("update:value", cleaned); }, displayText(value: number | undefined): string { if (value === undefined) return "-";