mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-13 02:34:33 +00:00
Transform: Shift-rotating inside a rotated frame lands the page rotation on multiples of 15
Measured in Figma: a layer's rotation field shows its rotation on the page, also inside a rotated frame or group, and Shift snaps that value. A test pins it; the doc comment on shared_rotation_degrees says so. Review item on #2096. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Model-Effort: Opus 5.5 drove and wrote; Sonnet 5.5 measured in Figma
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a1ac22f12f
commit
da81d55a64
@@ -132,9 +132,10 @@ pub fn selection_frame(doc: &Document, sel: &[NodeId]) -> Option<(Affine, f64, f
|
||||
Some((rot * Affine::translate((b.x0, b.y0)), b.width(), b.height()))
|
||||
}
|
||||
|
||||
/// The rotation the Design panel shows for `ids` (degrees, counter-clockwise positive) when they
|
||||
/// all share one, else 0 (the handle box is page-aligned then). Axis-aligned layers give 0, which
|
||||
/// is the same as 90, 180 and 270 for stepping in 15 degrees.
|
||||
/// The rotation `ids` share on the page (degrees, counter-clockwise positive), else 0 (the handle
|
||||
/// box is page-aligned then). Figma's rotation field shows this page rotation, also for a layer
|
||||
/// inside a rotated frame or group, and Shift lands it on multiples of 15 degrees. Axis-aligned
|
||||
/// layers give 0, which is the same as 90, 180 and 270 for stepping in 15 degrees.
|
||||
pub fn shared_rotation_degrees(doc: &Document, ids: &[NodeId]) -> f64 {
|
||||
shared_rotation(doc, ids).map_or(0.0, |a| -a.to_degrees())
|
||||
}
|
||||
|
||||
@@ -403,6 +403,39 @@ fn shift_rotating_lands_on_multiples_of_15_not_steps_from_the_start() {
|
||||
assert!(near(got, 0.0), "-2.27° rounds to 0°: {got}");
|
||||
}
|
||||
|
||||
/// Rotates a 120 × 80 layer inside a frame turned `frame_rot`, the layer at `start` on the page,
|
||||
/// by `turn` from its corner, and returns its rotation on the page.
|
||||
fn rotate_drag_in_frame(frame_rot: f64, start: f64, turn: f64, m: Mods) -> f64 {
|
||||
let mut s = session();
|
||||
let f = id(&ex(&mut s, "frame.create", json!({"x": 0, "y": 100, "width": 400, "height": 400})));
|
||||
set(&mut s, &[f], json!({"rotation": frame_rot}));
|
||||
let r = rect(&mut s, 100.0, 100.0, 120.0, 80.0, Some(f));
|
||||
set(&mut s, &[r], json!({"rotation": start - frame_rot}));
|
||||
let page = |s: &Session| rotation_degrees(s.doc().unwrap().world_transform(r));
|
||||
assert!(near(page(&s), start), "the layer starts at {start}° on the page: {}", page(&s));
|
||||
ex(&mut s, "select.set", json!({"ids": [r]}));
|
||||
let world = s.doc().unwrap().world_transform(r);
|
||||
let (c, corner) = (world * Point::new(60.0, 40.0), world * Point::ZERO);
|
||||
let from = corner + (corner - c) * 0.15;
|
||||
let a = (from - c).atan2() - turn.to_radians();
|
||||
let to = c + figcraft_geom::Vec2::from_angle(a) * 80.0;
|
||||
drag(&mut s, from, to, m);
|
||||
page(&s)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_rotating_inside_a_rotated_frame_lands_the_page_rotation_on_multiples_of_15() {
|
||||
// Measured in Figma (2026-11-10): the rotation field of a layer inside a frame turned 10° shows
|
||||
// its rotation on the page, and Shift lands that on multiples of 15°: from 7°, turns of 40°,
|
||||
// 6° and -10° end at 45°, 15° and 0°; without Shift 6° ends at 13°. Groups behave the same.
|
||||
for (turn, want) in [(40.0, 45.0), (6.0, 15.0), (-10.0, 0.0)] {
|
||||
let got = rotate_drag_in_frame(10.0, 7.0, turn, SHIFT);
|
||||
assert!(near(got, want), "7° on the page turned by {turn}° with Shift: {got}, want {want}");
|
||||
}
|
||||
let got = rotate_drag_in_frame(10.0, 7.0, 6.0, NONE);
|
||||
assert!(near(got, 13.0), "a free turn: {got}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn groups_scale_children_keeping_their_rotation_and_flip() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user