Skip to content

G1: Shift rotation and reflect honor Constrain Angle preference - #3046

Open
nrq6993-a11y wants to merge 2 commits into
storytold:mainfrom
nrq6993-a11y:stream/vc-feat-g1-constrain-angle-transform
Open

nrq6993-a11y wants to merge 2 commits into
storytold:mainfrom
nrq6993-a11y:stream/vc-feat-g1-constrain-angle-transform

Conversation

@nrq6993-a11y

@nrq6993-a11y nrq6993-a11y commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

G1: Shift rotation and reflect honor Constrain Angle preference

Summary

With Shift, bounding-box rotation (Selection tool, Free Transform) and Rotate-tool drags now snap the angle turned, as Illustrator does: the rotation lands on a multiple of 45° offset by Preferences › General › Constrain Angle (for example 30°, −15°, −60° with a 30° Constrain Angle), wherever the drag starts. Before, Shift snapped the pointer's direction, so a drag that did not start on a 45° line turned by an odd angle. Reflect-tool axis drags with Shift snap the axis direction to 45° steps from the same Constrain Angle, matching pen segments and direction handles. Part of gap G1 (rotation snap).

Note: touches the same Shift-rotate helper in tools/src/bbox.rs as #1298.

Plan

  • rotate_for_drag takes base_deg and, with Shift, rounds the turned angle to base_deg + k·45°
  • Pass cx.constrain_angle from Selection, Free Transform and Transform tools
  • Reflect tool Shift axis uses constrain_angle_from with the same base (axis direction, as in Illustrator)
  • Unit tests: snapping from a drag that starts off a snap line, across the ±180° seam, with and without a Constrain Angle; updated matrix_for call sites

Tests

  • cargo test -p vectorcraft-tools --lib — pass (440 passed, incl. the new off-snap-line test)
  • cargo fmt -p vectorcraft-tools -- --check — pass
  • cargo clippy -p vectorcraft-tools --lib --tests -- -D warnings — pass

Part of gap G1

Status: Ready on stream/vc-feat-g1-constrain-angle-transform (commit 978f9000).

Summary by CodeRabbit

  • Improvements
    • Rotation and reflection snapping now follow the configured angle constraint. When using Shift, rotation and reflection snap to 45° steps relative to the angle set in Preferences › General › Constrain Angle.
    • Rotation previews respect the configured constraint angle, keeping the displayed rotation aligned with the selected snapping preference. Rotation can also snap relative to the drag’s starting angle, including when the drag begins between snap points.

Rotate, Free Transform, and bounding-box drags snap pointer direction in
45° steps from Preferences › General › Constrain Angle; Reflect matches.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ee516308-b313-4079-9cad-00de9818d077
📥 Commits

Reviewing files that changed from the base of the PR and between b9fd541 and 978f900.

📒 Files selected for processing: 1
  • crates/tools/src/bbox.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

📝 Walkthrough

Rotation and reflection drag constraints now use the configured angle preference. Rotation snapping uses 45° steps relative to a supplied base angle. Selection, free transform, and transform tools pass the preference into their rotation calculations.

Changes: Angle-constrained transforms

Layer / File(s) Summary
Relative rotation snapping
crates/tools/src/bbox.rs
rotate_for_drag snaps pointer direction to 45° steps relative to base_deg and returns the rotation angle in degrees. Tests cover nonzero base angles, free rotation, off-line drag starts, and crossing the ±180° seam.
Preference wiring for transform drags
crates/tools/src/select.rs, crates/tools/src/xform/free.rs, crates/tools/src/xform/transform.rs
Selection and free-transform rotation previews pass the configured angle preference. TransformTool::matrix_for uses it for rotation and Shift-constrained reflection. Tests cover a 30° preference and pass explicit values to other transform tests. The module documentation describes the preference used for Shift constraints.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: echelon

Merge Risk: ⚪ Minimal · up to 978f9

Shift rotation snapping now follows the configured Constrain Angle preference. No actionable merge-blocking risk was found in the supplied changes.

### Architecture Summary

Architecture risk: 🔵 Low · up to 978f9

The change affects 1 system.

Changed systems: crates

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — crates (service) was modified; 4 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in crates/tools/src/select.rs: Rotation dragging now supplies the configured constrain_angle value to rotate_for_drag, in addition to the existing Shift constraint.
  • observed — Modified behavior in crates/tools/src/xform/free.rs: The rotation preview now supplies cx.constrain_angle to rotate_for_drag; the previous call omitted this argument.
  • observed — Modified behavior in crates/tools/src/xform/transform.rs: The module documentation now specifies that Shift constrains rotation and reflection to 45° steps using the General preference, while retaining uniform-scale behavior and describing Alt-at-release copying.
  • observed — Modified behavior in crates/tools/src/xform/transform.rs: matrix_for adds a constrain_angle parameter. Rotate drag calculation now receives it, and Shift-constrained reflection uses constrain_angle_from with that value instead of fixed 45° angle steps.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 78.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly and concisely describes the main change: Shift rotation and reflection now honor the Constrain Angle preference.
Description check Passed The description provides a clear summary, rationale, implementation plan, and test results. It omits the required Checklist section, including the repository checklist items, but the main required cha…
Full details: Description check

Explanation

The description provides a clear summary, rationale, implementation plan, and test results. It omits the required Checklist section, including the repository checklist items, but the main required change and testing information are complete.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

…ngle

Like Illustrator, Shift now constrains the rotation itself (base + k·45°)
rather than the pointer direction, so a drag that starts off a snap line
still turns by a snapped angle. Reflect axis snapping is unchanged.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant