From 1f0500023810888279f0892c09c9c33f738a350e Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sun, 21 Jun 2026 12:09:21 -0700 Subject: [PATCH] Exclude viewport zoom from influencing scale in the Transform node set by the drawing tools (#4260) * Keep viewport zoom out of drawing tools' Transform node * Simplify window-aligned transform helpers and fix spline DragStop fallback --- .../tool/common_functionality/resize.rs | 41 +++++++++++++++++++ .../common_functionality/shapes/arc_shape.rs | 17 +++----- .../shapes/circle_shape.rs | 12 ++---- .../shapes/ellipse_shape.rs | 15 +++---- .../common_functionality/shapes/grid_shape.rs | 11 ++--- .../tool/common_functionality/shapes/mod.rs | 1 + .../shapes/polygon_shape.rs | 17 +++----- .../shapes/rectangle_shape.rs | 15 +++---- .../shapes/spiral_shape.rs | 10 +---- .../common_functionality/shapes/star_shape.rs | 17 +++----- .../tool/tool_messages/freehand_tool.rs | 15 ++----- .../messages/tool/tool_messages/pen_tool.rs | 10 +---- .../tool/tool_messages/spline_tool.rs | 17 ++------ .../messages/tool/tool_messages/text_tool.rs | 14 ++----- .../libraries/raster-types/src/image.rs | 13 +++--- 15 files changed, 96 insertions(+), 129 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/resize.rs b/editor/src/messages/tool/common_functionality/resize.rs index 032666ca27..474d774cde 100644 --- a/editor/src/messages/tool/common_functionality/resize.rs +++ b/editor/src/messages/tool/common_functionality/resize.rs @@ -181,3 +181,44 @@ impl Resize { self.layer = None; } } + +/// The viewport zoom factor, taken from the document-to-viewport transform so it accounts for zoom snapping and canvas flipping. +/// Divide a viewport-space measurement by this to convert it to document units. +pub fn viewport_zoom(document: &DocumentMessageHandler) -> f64 { + document.metadata().document_to_viewport.matrix2.determinant().abs().sqrt() +} + +/// The viewport-space transform for a freshly drawn layer placed at `viewport_position`: window-aligned, so it is just the zoom (no tilt or flip). +/// Routing it through [`TransformIn::Viewport`] yields a document-space layer transform that counter-rotates the tilt/flip with the zoom left out. +/// `aspect` is an optional unitless local-frame stretch (e.g. for polygons and stars); pass [`DVec2::ONE`] for none. +pub fn window_aligned_transform(document: &DocumentMessageHandler, viewport_position: DVec2, aspect: DVec2) -> DAffine2 { + DAffine2::from_scale_angle_translation(viewport_zoom(document) * aspect, 0., viewport_position) +} + +/// [`TransformSet`](GraphOperationMessage::TransformSet) applying [`window_aligned_transform`] to a freshly drawn layer, via +/// [`TransformIn::Viewport`] so the parent transform is resolved at execution time (also correct when the placement is deferred until after the graph runs). +pub fn window_aligned_transform_set(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, viewport_position: DVec2, aspect: DVec2) -> Message { + GraphOperationMessage::TransformSet { + layer, + transform: window_aligned_transform(document, viewport_position, aspect), + transform_in: TransformIn::Viewport, + skip_rerender: false, + } + .into() +} + +/// [`TransformSet`](GraphOperationMessage::TransformSet) placing a freshly drawn path-like layer (Pen, Freehand, Spline, Line, Arrow) at +/// `viewport_position` with only a translation: the drawn geometry holds the shape (including tilt), so the Transform node stays a pure document-space offset. +pub fn translation_transform_set(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, viewport_position: DVec2) -> Message { + // Same orientation/scale as the document-to-viewport transform, but translated to land the layer's origin at the drawn viewport position. + let mut transform = document.metadata().document_to_viewport; + transform.translation = viewport_position; + + GraphOperationMessage::TransformSet { + layer, + transform, + transform_in: TransformIn::Viewport, + skip_rerender: false, + } + .into() +} diff --git a/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs b/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs index 7eabb3c619..da10dffe56 100644 --- a/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs @@ -1,6 +1,5 @@ use super::shape_utility::ShapeToolModifierKey; use super::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; @@ -9,7 +8,6 @@ use crate::messages::tool::common_functionality::gizmos::shape_gizmos::sweep_ang use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, arc_outline}; use crate::messages::tool::tool_messages::tool_prelude::*; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; use graphene_std::vector::misc::ArcType; @@ -159,29 +157,26 @@ impl Arc { }; let dimensions = (start - end).abs(); - let mut scale = DVec2::ONE; + let mut aspect = DVec2::ONE; let radius: f64; // We keep the smaller dimension's scale at 1 and scale the other dimension accordingly if dimensions.x > dimensions.y { - scale.x = dimensions.x / dimensions.y; + aspect.x = dimensions.x / dimensions.y; radius = dimensions.y / 2.; } else { - scale.y = dimensions.y / dimensions.x; + aspect.y = dimensions.y / dimensions.x; radius = dimensions.x / 2.; } + let radius = radius / viewport_zoom(document); + responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, 1), input: NodeInput::value(TaggedValue::F64(radius), false), }); - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_scale_angle_translation(scale, 0., start.midpoint(end)), - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(window_aligned_transform_set(document, layer, start.midpoint(end), aspect)); } } } diff --git a/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs b/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs index 836f50f9e6..5f25ac8d23 100644 --- a/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs @@ -1,15 +1,14 @@ -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState}; use crate::messages::tool::common_functionality::graph_modification_utils; +use crate::messages::tool::common_functionality::resize::{viewport_zoom, window_aligned_transform_set}; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, ShapeToolModifierKey}; use crate::messages::tool::tool_messages::shape_tool::ShapeToolData; use crate::messages::tool::tool_messages::tool_prelude::*; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; @@ -101,7 +100,7 @@ impl Circle { return; }; - let dimensions = (start - end).abs(); + let dimensions = ((start - end) / viewport_zoom(document)).abs(); // We keep the smaller dimension's scale at 1 and scale the other dimension accordingly let radius: f64 = if dimensions.x > dimensions.y { dimensions.y / 2. } else { dimensions.x / 2. }; @@ -111,11 +110,6 @@ impl Circle { input: NodeInput::value(TaggedValue::F64(radius), false), }); - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., start.midpoint(end)), - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(window_aligned_transform_set(document, layer, start.midpoint(end), DVec2::ONE)); } } diff --git a/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs b/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs index eb33a08427..511f1ad622 100644 --- a/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs @@ -1,12 +1,10 @@ use super::shape_utility::ShapeToolModifierKey; use super::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::tool_messages::tool_prelude::*; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; use std::collections::VecDeque; @@ -36,20 +34,17 @@ impl Ellipse { return; }; + let radius = ((start - end) / 2. / viewport_zoom(document)).abs(); + responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, 1), - input: NodeInput::value(TaggedValue::F64(((start.x - end.x) / 2.).abs()), false), + input: NodeInput::value(TaggedValue::F64(radius.x), false), }); responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, 2), - input: NodeInput::value(TaggedValue::F64(((start.y - end.y) / 2.).abs()), false), - }); - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_translation(start.midpoint(end)), - transform_in: TransformIn::Viewport, - skip_rerender: false, + input: NodeInput::value(TaggedValue::F64(radius.y), false), }); + responses.add(window_aligned_transform_set(document, layer, start.midpoint(end), DVec2::ONE)); } } } diff --git a/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs b/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs index 7a7f1b0b19..199ecfaddf 100644 --- a/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs @@ -1,6 +1,5 @@ use super::shape_utility::ShapeToolModifierKey; use super::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -10,7 +9,6 @@ use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler; use crate::messages::tool::tool_messages::tool_prelude::*; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; use graphene_std::NodeInputDecleration; @@ -117,6 +115,8 @@ impl Grid { let (translation, dimensions, angle) = calculate_grid_params(start, end, is_isometric, ipp.keyboard.key(center), ipp.keyboard.key(lock_ratio)); + let dimensions = dimensions / viewport_zoom(document); + // Set dimensions/spacing responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, SpacingInput::::INDEX), @@ -132,12 +132,7 @@ impl Grid { } // Set transform - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., translation), - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(window_aligned_transform_set(document, layer, translation, DVec2::ONE)); } } diff --git a/editor/src/messages/tool/common_functionality/shapes/mod.rs b/editor/src/messages/tool/common_functionality/shapes/mod.rs index b005f61a19..4d74b15ba5 100644 --- a/editor/src/messages/tool/common_functionality/shapes/mod.rs +++ b/editor/src/messages/tool/common_functionality/shapes/mod.rs @@ -10,6 +10,7 @@ pub mod shape_utility; pub mod spiral_shape; pub mod star_shape; +pub use super::resize::{viewport_zoom, window_aligned_transform_set}; pub use super::shapes::arrow_shape::Arrow; pub use super::shapes::ellipse_shape::Ellipse; pub use super::shapes::line_shape::{Line, LineEnd}; diff --git a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs index 7b22144a71..5195d6d849 100644 --- a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs @@ -1,6 +1,5 @@ use super::shape_utility::{ShapeToolModifierKey, update_radius_sign}; use super::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type}; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -12,7 +11,6 @@ use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, polygon_outline}; use crate::messages::tool::tool_messages::shape_tool::ShapeOptionsUpdate; use crate::messages::tool::tool_messages::tool_prelude::*; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; use std::collections::VecDeque; @@ -132,16 +130,18 @@ impl Polygon { let dimensions = (start - end).abs(); // We keep the smaller dimension's scale at 1 and scale the other dimension accordingly - let mut scale = DVec2::ONE; + let mut aspect = DVec2::ONE; let radius; if dimensions.x > dimensions.y { - scale.x = dimensions.x / dimensions.y; + aspect.x = dimensions.x / dimensions.y; radius = dimensions.y / 2.; } else { - scale.y = dimensions.y / dimensions.x; + aspect.y = dimensions.y / dimensions.x; radius = dimensions.x / 2.; } + let radius = radius / viewport_zoom(document); + let Some(node_id) = graph_modification_utils::get_polygon_id(layer, &document.network_interface) else { return; }; @@ -151,12 +151,7 @@ impl Polygon { input: NodeInput::value(TaggedValue::F64(radius), false), }); - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_scale_angle_translation(scale, 0., (start + end) / 2.), - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(window_aligned_transform_set(document, layer, start.midpoint(end), aspect)); } } diff --git a/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs b/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs index 787684a8fa..59591b18ee 100644 --- a/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs @@ -1,12 +1,10 @@ use super::shape_utility::ShapeToolModifierKey; use super::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::tool_messages::tool_prelude::*; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; use std::collections::VecDeque; @@ -36,20 +34,17 @@ impl Rectangle { return; }; + let size = ((start - end) / viewport_zoom(document)).abs(); + responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, 1), - input: NodeInput::value(TaggedValue::F64((start.x - end.x).abs()), false), + input: NodeInput::value(TaggedValue::F64(size.x), false), }); responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, 2), - input: NodeInput::value(TaggedValue::F64((start.y - end.y).abs()), false), - }); - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_translation(start.midpoint(end)), - transform_in: TransformIn::Viewport, - skip_rerender: false, + input: NodeInput::value(TaggedValue::F64(size.y), false), }); + responses.add(window_aligned_transform_set(document, layer, start.midpoint(end), DVec2::ONE)); } } } diff --git a/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs b/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs index 99890cb6aa..ed41c86f02 100644 --- a/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs @@ -1,6 +1,5 @@ use super::*; use crate::messages::frontend::utility_types::MouseCursorIcon; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type}; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -135,7 +134,7 @@ impl Spiral { let snapped_viewport_point = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); shape_tool_data.data.snap_manager.update_indicator(snapped); - let dragged_distance = (viewport_drag_start - snapped_viewport_point).length(); + let dragged_distance = (viewport_drag_start - snapped_viewport_point).length() / viewport_zoom(document); let Some(node_id) = graph_modification_utils::get_spiral_id(layer, &document.network_interface) else { return; @@ -155,12 +154,7 @@ impl Spiral { SpiralType::Logarithmic => (dragged_distance).max(0.1), }; - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., viewport_drag_start), - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(window_aligned_transform_set(document, layer, viewport_drag_start, DVec2::ONE)); responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, OuterRadiusInput::INDEX), diff --git a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs index acdde2c286..423c675ebd 100644 --- a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs @@ -1,6 +1,5 @@ use super::shape_utility::{ShapeToolModifierKey, update_radius_sign}; use super::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type}; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -12,7 +11,6 @@ use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, star_outline}; use crate::messages::tool::tool_messages::tool_prelude::*; use core::f64; -use glam::DAffine2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; use std::collections::VecDeque; @@ -137,16 +135,18 @@ impl Star { let dimensions = (start - end).abs(); // We keep the smaller dimension's scale at 1 and scale the other dimension accordingly - let mut scale = DVec2::ONE; + let mut aspect = DVec2::ONE; let radius: f64; if dimensions.x > dimensions.y { - scale.x = dimensions.x / dimensions.y; + aspect.x = dimensions.x / dimensions.y; radius = dimensions.y / 2.; } else { - scale.y = dimensions.y / dimensions.x; + aspect.y = dimensions.y / dimensions.x; radius = dimensions.x / 2.; } + let radius = radius / viewport_zoom(document); + let Some(node_id) = graph_modification_utils::get_star_id(layer, &document.network_interface) else { return; }; @@ -161,12 +161,7 @@ impl Star { input: NodeInput::value(TaggedValue::F64(radius / 2.), false), }); - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_scale_angle_translation(scale, 0., (start + end) / 2.), - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(window_aligned_transform_set(document, layer, start.midpoint(end), aspect)); } } } diff --git a/editor/src/messages/tool/tool_messages/freehand_tool.rs b/editor/src/messages/tool/tool_messages/freehand_tool.rs index d93a1c117a..a58ae056f4 100644 --- a/editor/src/messages/tool/tool_messages/freehand_tool.rs +++ b/editor/src/messages/tool/tool_messages/freehand_tool.rs @@ -1,5 +1,4 @@ use super::tool_prelude::*; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type; use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; @@ -9,6 +8,7 @@ use crate::messages::tool::common_functionality::color_selector::{ sync_drawing_state, }; use crate::messages::tool::common_functionality::graph_modification_utils; +use crate::messages::tool::common_functionality::resize::translation_transform_set; use crate::messages::tool::common_functionality::stroke_options::{StrokeOptionsUpdate, apply_stroke_option, create_stroke_options_popover_widget}; use crate::messages::tool::common_functionality::utility_functions::should_extend; use glam::DVec2; @@ -309,16 +309,7 @@ impl Fsm for FreehandToolFsmState { // Position the layer at the initial mouse position via Transform responses.add(DeferMessage::AfterGraphRun { - messages: vec![ - GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_translation(input.mouse.position), - transform_in: TransformIn::Viewport, - skip_rerender: false, - } - .into(), - NodeGraphMessage::RunDocumentGraph.into(), - ], + messages: vec![translation_transform_set(document, layer, input.mouse.position), NodeGraphMessage::RunDocumentGraph.into()], }); FreehandToolFsmState::Drawing @@ -337,7 +328,7 @@ impl Fsm for FreehandToolFsmState { tool_data.new_layer_viewport_start = None; } let position = if let Some(start) = tool_data.new_layer_viewport_start { - input.mouse.position - start + document.metadata().document_to_viewport.inverse().transform_vector2(input.mouse.position - start) } else { transform.inverse().transform_point2(input.mouse.position) }; diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 3e953818bb..78d7118f8c 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1,7 +1,6 @@ use super::tool_prelude::*; use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_05, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE}; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type; use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; @@ -12,6 +11,7 @@ use crate::messages::tool::common_functionality::color_selector::{ sync_drawing_state, }; use crate::messages::tool::common_functionality::graph_modification_utils::{self, merge_layers}; +use crate::messages::tool::common_functionality::resize::translation_transform_set; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration}; use crate::messages::tool::common_functionality::stroke_options::{StrokeOptionsUpdate, apply_stroke_option, create_stroke_options_popover_widget}; @@ -1373,13 +1373,7 @@ impl PenToolData { // Defer the transform setup and point insertion until after the layer is created responses.add(DeferMessage::AfterGraphRun { messages: vec![ - GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_translation(viewport_vec), - transform_in: TransformIn::Viewport, - skip_rerender: false, - } - .into(), + translation_transform_set(document, layer, viewport_vec), GraphOperationMessage::Vector { layer, modification_type: VectorModificationType::InsertPoint { id, position: DVec2::ZERO }, diff --git a/editor/src/messages/tool/tool_messages/spline_tool.rs b/editor/src/messages/tool/tool_messages/spline_tool.rs index 6e21014345..28d5c815b0 100644 --- a/editor/src/messages/tool/tool_messages/spline_tool.rs +++ b/editor/src/messages/tool/tool_messages/spline_tool.rs @@ -1,7 +1,6 @@ use super::tool_prelude::*; use crate::consts::{DRAG_THRESHOLD, PATH_JOIN_THRESHOLD, SNAP_POINT_TOLERANCE}; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; -use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::{resolve_network_node_type, resolve_proto_node_type}; use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; @@ -12,6 +11,7 @@ use crate::messages::tool::common_functionality::color_selector::{ sync_drawing_state, }; use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points}; +use crate::messages::tool::common_functionality::resize::translation_transform_set; use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint}; use crate::messages::tool::common_functionality::stroke_options::{StrokeOptionsUpdate, apply_stroke_option, create_stroke_options_popover_widget}; use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend}; @@ -414,16 +414,7 @@ impl Fsm for SplineToolFsmState { // Position the layer at the initial mouse position via Transform responses.add(DeferMessage::AfterGraphRun { - messages: vec![ - GraphOperationMessage::TransformSet { - layer, - transform: DAffine2::from_translation(viewport_vec), - transform_in: TransformIn::Viewport, - skip_rerender: false, - } - .into(), - NodeGraphMessage::RunDocumentGraph.into(), - ], + messages: vec![translation_transform_set(document, layer, viewport_vec), NodeGraphMessage::RunDocumentGraph.into()], }); SplineToolFsmState::Drawing @@ -446,7 +437,7 @@ impl Fsm for SplineToolFsmState { // For newly created layers, the deferred TransformSet may not yet be reflected // in the metadata, so compute local position from the known viewport start. tool_data.next_point = if let Some(start) = tool_data.new_layer_viewport_start { - viewport_pos - start + document_to_viewport.inverse().transform_vector2(viewport_pos - start) } else { let transform = document.metadata().transform_to_viewport(layer); transform.inverse().transform_point2(viewport_pos) @@ -483,7 +474,7 @@ impl Fsm for SplineToolFsmState { // For newly created layers, the deferred TransformSet may not yet be reflected // in the metadata, so compute local position from the known viewport start. tool_data.next_point = if let Some(start) = tool_data.new_layer_viewport_start { - viewport_pos - start + document_to_viewport.inverse().transform_vector2(viewport_pos - start) } else { let transform = document.metadata().transform_to_viewport(layer); transform.inverse().transform_point2(viewport_pos) diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index d28bbbb953..29155ce22c 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -12,7 +12,7 @@ use crate::messages::tool::common_functionality::color_selector::{ ToolColorOptions, apply_fill_only_color_pick, apply_fill_only_enabled, refresh_slot_working_color, selection_changed_since_last_sync, solid, sync_fill_only, }; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::resize::Resize; +use crate::messages::tool::common_functionality::resize::{Resize, viewport_zoom, window_aligned_transform}; use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapData}; use crate::messages::tool::common_functionality::transformation_cage::*; use crate::messages::tool::common_functionality::utility_functions::text_bounding_box; @@ -971,17 +971,11 @@ impl Fsm for TextToolFsmState { return TextToolFsmState::Editing; } - // Otherwise create some new text. The drag bounds are in viewport space; map them into document space for the text's - // transform and wrapping size, then compose with document-to-viewport so the editing overlay (a screen-space CSS matrix) carries the zoom. - let document_to_viewport = document.metadata().document_to_viewport; - let viewport_to_document = document_to_viewport.inverse(); - let document_start = viewport_to_document.transform_point2(start); - let document_end = viewport_to_document.transform_point2(end); - - let constraint_size = has_dragged.then_some((document_start - document_end).abs()); + // Otherwise create some new text. The window-aligned transform is in viewport space, so the editing overlay (a screen-space CSS matrix) carries the zoom. + let constraint_size = has_dragged.then_some((start - end).abs() / viewport_zoom(document)); let editing_text = EditingText { text: String::new(), - transform: document_to_viewport * DAffine2::from_translation(document_start), + transform: window_aligned_transform(document, start, DVec2::ONE), typesetting: TypesettingConfig { font_size: tool_options.font_size, letter_spacing: tool_options.letter_spacing, diff --git a/node-graph/libraries/raster-types/src/image.rs b/node-graph/libraries/raster-types/src/image.rs index 8ed636270e..16d4d5955a 100644 --- a/node-graph/libraries/raster-types/src/image.rs +++ b/node-graph/libraries/raster-types/src/image.rs @@ -190,8 +190,7 @@ where let mut last_b_srgb = 0u8; let mut result = vec![0; data.len() * 4]; - let mut i = 0; - for color in data { + for (color, out) in data.iter().zip(result.chunks_exact_mut(4)) { let a = color.a().to_f32(); // Smaller alpha values than this would map to fully transparent // anyway, avoid expensive encoding. @@ -215,13 +214,11 @@ where last_b_srgb = float_to_srgb_u8(b); } - result[i] = last_r_srgb; - result[i + 1] = last_g_srgb; - result[i + 2] = last_b_srgb; - result[i + 3] = (a * 255. + 0.5) as u8; + out[0] = last_r_srgb; + out[1] = last_g_srgb; + out[2] = last_b_srgb; + out[3] = (a * 255. + 0.5) as u8; } - - i += 4; } (result, *width, *height)