Skip to content

Commit ed83b84

Browse files
authored
Merge branch 'master' into merge-spline-path
2 parents c1ab56f + 4275eaf commit ed83b84

File tree

14 files changed

+350
-215
lines changed

14 files changed

+350
-215
lines changed

editor/src/messages/input_mapper/input_mappings.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::messages::input_mapper::utility_types::misc::{KeyMappingEntries, Mapp
88
use crate::messages::portfolio::document::node_graph::utility_types::Direction;
99
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
1010
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
11+
use crate::messages::portfolio::document::utility_types::transformation::TransformType;
1112
use crate::messages::prelude::*;
1213
use crate::messages::tool::tool_messages::brush_tool::BrushToolMessageOptionsUpdate;
1314
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
@@ -82,8 +83,8 @@ pub fn input_mappings() -> Mapping {
8283
entry!(KeyDown(ArrowLeft); action_dispatch=NodeGraphMessage::ShiftSelectedNodes { direction: Direction::Left, rubber_band: false }),
8384
//
8485
// TransformLayerMessage
85-
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
86-
entry!(KeyDown(MouseLeft); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
86+
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation { final_transform: true }),
87+
entry!(KeyDown(MouseLeft); action_dispatch=TransformLayerMessage::ApplyTransformOperation { final_transform: true }),
8788
entry!(KeyDown(MouseRight); action_dispatch=TransformLayerMessage::CancelTransformOperation),
8889
entry!(KeyDown(Escape); action_dispatch=TransformLayerMessage::CancelTransformOperation),
8990
entry!(KeyDown(KeyX); action_dispatch=TransformLayerMessage::ConstrainX),
@@ -255,8 +256,8 @@ pub fn input_mappings() -> Mapping {
255256
entry!(PointerMove; refresh_keys=[Control, Alt, Shift, KeyC], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control, colinear: KeyC, move_anchor_with_handles: Space }),
256257
entry!(KeyDown(MouseLeft); action_dispatch=PenToolMessage::DragStart { append_to_selected: Shift }),
257258
entry!(KeyUp(MouseLeft); action_dispatch=PenToolMessage::DragStop),
258-
entry!(KeyDown(MouseRight); action_dispatch=PenToolMessage::Confirm),
259-
entry!(KeyDown(Escape); action_dispatch=PenToolMessage::Confirm),
259+
entry!(KeyDown(MouseRight); action_dispatch=PenToolMessage::Abort),
260+
entry!(KeyDown(Escape); action_dispatch=PenToolMessage::Abort),
260261
entry!(KeyDown(Enter); action_dispatch=PenToolMessage::Confirm),
261262
entry!(KeyDown(Delete); action_dispatch=PenToolMessage::RemovePreviousHandle),
262263
entry!(KeyDown(Backspace); action_dispatch=PenToolMessage::RemovePreviousHandle),
@@ -365,9 +366,9 @@ pub fn input_mappings() -> Mapping {
365366
entry!(KeyDown(ArrowRight); action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: NUDGE_AMOUNT, delta_y: 0., resize: Alt, resize_opposite_corner: Control }),
366367
//
367368
// TransformLayerMessage
368-
entry!(KeyDown(KeyG); action_dispatch=TransformLayerMessage::BeginGrab),
369-
entry!(KeyDown(KeyR); action_dispatch=TransformLayerMessage::BeginRotate),
370-
entry!(KeyDown(KeyS); action_dispatch=TransformLayerMessage::BeginScale),
369+
entry!(KeyDown(KeyG); action_dispatch=TransformLayerMessage::BeginGRS { transform_type: TransformType::Grab }),
370+
entry!(KeyDown(KeyR); action_dispatch=TransformLayerMessage::BeginGRS { transform_type: TransformType::Rotate }),
371+
entry!(KeyDown(KeyS); action_dispatch=TransformLayerMessage::BeginGRS { transform_type: TransformType::Scale }),
371372
entry!(KeyDown(Digit0); action_dispatch=TransformLayerMessage::TypeDigit { digit: 0 }),
372373
entry!(KeyDown(Digit1); action_dispatch=TransformLayerMessage::TypeDigit { digit: 1 }),
373374
entry!(KeyDown(Digit2); action_dispatch=TransformLayerMessage::TypeDigit { digit: 2 }),

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

+47-9
Original file line numberDiff line numberDiff line change
@@ -2252,15 +2252,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
22522252
..Default::default()
22532253
}),
22542254
),
2255-
PropertiesRow::with_override(
2256-
"Skew",
2257-
WidgetOverride::Vec2(Vec2InputSettings {
2258-
x: "X".to_string(),
2259-
y: "Y".to_string(),
2260-
unit: "°".to_string(),
2261-
..Default::default()
2262-
}),
2263-
),
2255+
PropertiesRow::with_override("Skew", WidgetOverride::Custom("transform_skew".to_string())),
22642256
PropertiesRow::with_override("Pivot", WidgetOverride::Hidden),
22652257
],
22662258
output_names: vec!["Data".to_string()],
@@ -3360,6 +3352,52 @@ fn static_input_properties() -> InputProperties {
33603352
Ok(vec![LayoutGroup::Row { widgets }])
33613353
}),
33623354
);
3355+
// Skew has a custom override that maps to degrees
3356+
map.insert(
3357+
"transform_skew".to_string(),
3358+
Box::new(|node_id, index, context| {
3359+
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
3360+
3361+
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, super::utility_types::FrontendGraphDataType::Number, true);
3362+
3363+
let Some(input) = document_node.inputs.get(index) else {
3364+
return Err("Input not found in transform skew input override".to_string());
3365+
};
3366+
if let Some(&TaggedValue::DVec2(val)) = input.as_non_exposed_value() {
3367+
let to_skew = |input: &NumberInput| input.value.unwrap().to_radians().tan();
3368+
widgets.extend_from_slice(&[
3369+
Separator::new(SeparatorType::Unrelated).widget_holder(),
3370+
NumberInput::new(Some(val.x.atan().to_degrees()))
3371+
.label("X")
3372+
.unit("°")
3373+
.min(-89.9)
3374+
.max(89.9)
3375+
.on_update(node_properties::update_value(
3376+
move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(to_skew(input), val.y)),
3377+
node_id,
3378+
index,
3379+
))
3380+
.on_commit(node_properties::commit_value)
3381+
.widget_holder(),
3382+
Separator::new(SeparatorType::Related).widget_holder(),
3383+
NumberInput::new(Some(val.y.atan().to_degrees()))
3384+
.label("Y")
3385+
.unit("°")
3386+
.min(-89.9)
3387+
.max(89.9)
3388+
.on_update(node_properties::update_value(
3389+
move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(val.x, to_skew(input))),
3390+
node_id,
3391+
index,
3392+
))
3393+
.on_commit(node_properties::commit_value)
3394+
.widget_holder(),
3395+
]);
3396+
}
3397+
3398+
Ok(vec![LayoutGroup::Row { widgets }])
3399+
}),
3400+
);
33633401
map.insert(
33643402
"text_area".to_string(),
33653403
Box::new(|node_id, index, context| {

editor/src/messages/portfolio/document/utility_types/transformation.rs

+16
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,22 @@ pub enum TransformOperation {
299299
Scaling(Scale),
300300
}
301301

302+
#[derive(Debug, Clone, PartialEq, Copy, serde::Serialize, serde::Deserialize)]
303+
pub enum TransformType {
304+
Grab,
305+
Rotate,
306+
Scale,
307+
}
308+
309+
impl TransformType {
310+
pub fn equivalent_to(&self, operation: TransformOperation) -> bool {
311+
matches!(
312+
(operation, self),
313+
(TransformOperation::Scaling(_), TransformType::Scale) | (TransformOperation::Grabbing(_), TransformType::Grab) | (TransformOperation::Rotating(_), TransformType::Rotate)
314+
)
315+
}
316+
}
317+
302318
impl TransformOperation {
303319
#[allow(clippy::too_many_arguments)]
304320
pub fn apply_transform_operation(&self, selected: &mut Selected, increment_mode: bool, local: bool, quad: Quad, transform: DAffine2, pivot: DVec2, local_transform: DAffine2) {

editor/src/messages/tool/common_functionality/resize.rs

+24-27
Original file line numberDiff line numberDiff line change
@@ -42,73 +42,70 @@ impl Resize {
4242
self.layer.take();
4343
return None;
4444
}
45-
Some(self.calculate_points_ignore_layer(document, input, center, lock_ratio))
45+
Some(self.calculate_points_ignore_layer(document, input, center, lock_ratio, false))
4646
}
4747

4848
/// Compute the drag start and end based on the current mouse position. Ignores the state of the layer.
4949
/// If you want to only draw whilst a layer exists, use [`Resize::calculate_points`].
50-
pub fn calculate_points_ignore_layer(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key, lock_ratio: Key) -> [DVec2; 2] {
51-
let start = self.drag_start;
50+
pub fn calculate_points_ignore_layer(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key, lock_ratio: Key, in_document: bool) -> [DVec2; 2] {
51+
let start = self.viewport_drag_start(document);
5252
let mouse = input.mouse.position;
5353
let document_to_viewport = document.navigation_handler.calculate_offset_transform(input.viewport_bounds.center(), &document.document_ptz);
5454
let document_mouse = document_to_viewport.inverse().transform_point2(mouse);
55-
let mut document_points = [start, document_mouse];
56-
55+
let mut points_viewport = [start, mouse];
5756
let ignore = if let Some(layer) = self.layer { vec![layer] } else { vec![] };
5857
let ratio = input.keyboard.get(lock_ratio as usize);
5958
let center = input.keyboard.get(center as usize);
60-
6159
let snap_data = SnapData::ignore(document, input, &ignore);
6260
let config = SnapTypeConfiguration::default();
6361
if ratio {
64-
let size = document_points[1] - document_points[0];
65-
let size = size.abs().max(size.abs().yx()) * size.signum();
66-
document_points[1] = document_points[0] + size;
67-
let end = document_points[1];
62+
let viewport_size = points_viewport[1] - points_viewport[0];
63+
let raw_size = if in_document { document_to_viewport.inverse() } else { DAffine2::IDENTITY }.transform_vector2(viewport_size);
64+
let adjusted_size = raw_size.abs().max(raw_size.abs().yx()) * raw_size.signum();
65+
let size = if in_document { document_to_viewport.transform_vector2(adjusted_size) } else { adjusted_size };
66+
points_viewport[1] = points_viewport[0] + size;
67+
68+
let end_document = document_to_viewport.inverse().transform_point2(points_viewport[1]);
6869
let constraint = SnapConstraint::Line {
6970
origin: self.drag_start,
70-
direction: end - self.drag_start,
71+
direction: end_document - self.drag_start,
7172
};
7273
if center {
73-
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end), constraint, config);
74-
let far = SnapCandidatePoint::handle(2. * self.drag_start - end);
74+
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, config);
75+
let far = SnapCandidatePoint::handle(2. * self.drag_start - end_document);
7576
let snapped_far = self.snap_manager.constrained_snap(&snap_data, &far, constraint, config);
7677
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
77-
document_points[0] = best.snapped_point_document;
78-
document_points[1] = self.drag_start * 2. - best.snapped_point_document;
78+
points_viewport[0] = document_to_viewport.transform_point2(best.snapped_point_document);
79+
points_viewport[1] = document_to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
7980
self.snap_manager.update_indicator(best);
8081
} else {
81-
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end), constraint, config);
82-
document_points[1] = snapped.snapped_point_document;
82+
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, config);
83+
points_viewport[1] = document_to_viewport.transform_point2(snapped.snapped_point_document);
8384
self.snap_manager.update_indicator(snapped);
8485
}
8586
} else if center {
8687
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
8788
let opposite = 2. * self.drag_start - document_mouse;
8889
let snapped_far = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(opposite), config);
8990
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
90-
document_points[0] = best.snapped_point_document;
91-
document_points[1] = self.drag_start * 2. - best.snapped_point_document;
91+
points_viewport[0] = document_to_viewport.transform_point2(best.snapped_point_document);
92+
points_viewport[1] = document_to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
9293
self.snap_manager.update_indicator(best);
9394
} else {
9495
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
95-
document_points[1] = snapped.snapped_point_document;
96+
points_viewport[1] = document_to_viewport.transform_point2(snapped.snapped_point_document);
9697
self.snap_manager.update_indicator(snapped);
9798
}
9899

99-
document_points
100+
points_viewport
100101
}
101102

102103
pub fn calculate_transform(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key, lock_ratio: Key, skip_rerender: bool) -> Option<Message> {
103-
let viewport_points = self.calculate_points(document, input, center, lock_ratio).map(|points| {
104-
let document_to_viewport = document.metadata().document_to_viewport;
105-
[document_to_viewport.transform_point2(points[0]), document_to_viewport.transform_point2(points[1])]
106-
})?;
107-
104+
let points_viewport = self.calculate_points(document, input, center, lock_ratio)?;
108105
Some(
109106
GraphOperationMessage::TransformSet {
110107
layer: self.layer?,
111-
transform: DAffine2::from_scale_angle_translation(viewport_points[1] - viewport_points[0], 0., viewport_points[0]),
108+
transform: DAffine2::from_scale_angle_translation(points_viewport[1] - points_viewport[0], 0., points_viewport[0]),
112109
transform_in: TransformIn::Viewport,
113110
skip_rerender,
114111
}

editor/src/messages/tool/common_functionality/transformation_cage.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,22 @@ impl SelectedEdges {
206206
}
207207
let snapped_bounds = bounds_to_doc.inverse().transform_point2(snapped.snapped_point_document);
208208

209-
let mut scale_factor = (snapped_bounds - pivot) / (updated - pivot);
209+
let new_from_pivot = snapped_bounds - pivot; // The new vector from the snapped point to the pivot
210+
let original_from_pivot = updated - pivot; // The original vector from the point to the pivot
211+
let mut scale_factor = new_from_pivot / original_from_pivot;
212+
213+
// Constrain should always scale by the same factor in x and y
214+
if constrain {
215+
// When the point is on the pivot, we simply copy the other axis.
216+
if original_from_pivot.x.abs() < 1e-5 {
217+
scale_factor.x = scale_factor.y;
218+
} else if original_from_pivot.y.abs() < 1e-5 {
219+
scale_factor.y = scale_factor.x;
220+
}
221+
222+
debug_assert!((scale_factor.x - scale_factor.y).abs() < 1e-5);
223+
}
224+
210225
if !(self.left || self.right || constrain) {
211226
scale_factor.x = 1.
212227
}

editor/src/messages/tool/tool_messages/artboard_tool.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,9 @@ impl Fsm for ArtboardToolFsmState {
316316
ArtboardToolFsmState::Dragging
317317
}
318318
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
319-
let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, center, constrain_axis_or_aspect);
319+
let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, center, constrain_axis_or_aspect, true);
320+
let viewport_to_document = document.metadata().document_to_viewport.inverse();
321+
let [start, end] = [start, end].map(|point| viewport_to_document.transform_point2(point));
320322
if let Some(artboard) = tool_data.selected_artboard {
321323
assert_ne!(artboard, LayerNodeIdentifier::ROOT_PARENT, "Selected artboard cannot be ROOT_PARENT");
322324

editor/src/messages/tool/tool_messages/ellipse_tool.rs

+11-18
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl Fsm for EllipseToolFsmState {
236236
responses.add(GraphOperationMessage::TransformSet {
237237
layer,
238238
transform: DAffine2::from_translation((start + end) / 2.),
239-
transform_in: TransformIn::Local,
239+
transform_in: TransformIn::Viewport,
240240
skip_rerender: false,
241241
});
242242
}
@@ -400,17 +400,14 @@ mod test_ellipse {
400400
let ellipse = get_ellipse(&mut editor).await;
401401
assert_eq!(ellipse.len(), 1);
402402
println!("{ellipse:?}");
403-
// TODO: re-enable after https://github.com/GraphiteEditor/Graphite/issues/2370
404-
// assert_eq!(ellipse[0].radius_x, 5.);
405-
// assert_eq!(ellipse[0].radius_y, 5.);
403+
assert_eq!(ellipse[0].radius_x, 5.);
404+
assert_eq!(ellipse[0].radius_y, 5.);
406405

407-
// assert!(ellipse[0]
408-
// .transform
409-
// .abs_diff_eq(DAffine2::from_angle_translation(-f64::consts::FRAC_PI_4, DVec2::X * f64::consts::FRAC_1_SQRT_2 * 10.), 0.001));
410-
411-
float_eq!(ellipse[0].radius_x, 11. / core::f64::consts::SQRT_2 / 2.);
412-
float_eq!(ellipse[0].radius_y, 11. / core::f64::consts::SQRT_2 / 2.);
413-
assert!(ellipse[0].transform.abs_diff_eq(DAffine2::from_translation(DVec2::splat(11. / core::f64::consts::SQRT_2 / 2.)), 0.001));
406+
assert!(
407+
ellipse[0]
408+
.transform
409+
.abs_diff_eq(DAffine2::from_angle_translation(-f64::consts::FRAC_PI_4, DVec2::X * f64::consts::FRAC_1_SQRT_2 * 10.), 0.001)
410+
);
414411
}
415412

416413
#[tokio::test]
@@ -427,13 +424,9 @@ mod test_ellipse {
427424

428425
let ellipse = get_ellipse(&mut editor).await;
429426
assert_eq!(ellipse.len(), 1);
430-
// TODO: re-enable after https://github.com/GraphiteEditor/Graphite/issues/2370
431-
// assert_eq!(ellipse[0].radius_x, 10.);
432-
// assert_eq!(ellipse[0].radius_y, 10.);
433-
// assert!(ellipse[0].transform.abs_diff_eq(DAffine2::from_angle(-f64::consts::FRAC_PI_4), 0.001));
434-
float_eq!(ellipse[0].radius_x, 11. / core::f64::consts::SQRT_2);
435-
float_eq!(ellipse[0].radius_y, 11. / core::f64::consts::SQRT_2);
436-
assert!(ellipse[0].transform.abs_diff_eq(DAffine2::IDENTITY, 0.001));
427+
assert_eq!(ellipse[0].radius_x, 10.);
428+
assert_eq!(ellipse[0].radius_y, 10.);
429+
assert!(ellipse[0].transform.abs_diff_eq(DAffine2::from_angle(-f64::consts::FRAC_PI_4), 0.001));
437430
}
438431

439432
#[tokio::test]

0 commit comments

Comments
 (0)