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
This commit is contained in:
Keavon Chambers
2026-06-21 12:09:21 -07:00
committed by GitHub
parent b45cc312dd
commit 1f05000238
15 changed files with 96 additions and 129 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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::<f64>::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));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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