Skip to content

feat: move shapes with the arrow keys the way the camera sees them - #93

Open
FutuRiata wants to merge 1 commit into
Formsmith746:mainfrom
FutuRiata:upstream-fix/camera-relative-nudge
Open

feat: move shapes with the arrow keys the way the camera sees them#93
FutuRiata wants to merge 1 commit into
Formsmith746:mainfrom
FutuRiata:upstream-fix/camera-relative-nudge

Conversation

@FutuRiata

Copy link
Copy Markdown

Arrow keys step along the world axes no matter where the camera stands. Orbit round to the Left view, press , and the shape walks away from you into the screen. The axis is correct; the key is not the one the user pressed.

This makes an arrow move the selection towards the workplane axis closest to the direction the arrow points on screen — the camera yaw rounded to the nearest 90°, the way Tinkercad does it.

Why this is a fix, not a preference

Nothing changes in the two views people spend most of their time in.

  • Front view maps to the same axes as before: right → +X, down → +Z.
  • The default home view does too. Its yaw is exactly 45°, halfway between two axes, and the tie deliberately rounds to the axis the editor already used. A user who never orbits never sees a difference.

The step stays on an axis. No diagonals, ever — only which axis changes. Grid snapping, typed coordinates and the 1 mm / 5 mm step are all untouched. There is a test that walks the yaw in 3° increments through two full turns and asserts every result moves along exactly one axis by exactly the whole step.

Ctrl + ↑/↓ is untouched. Lift is along world Y, and height does not depend on where you stand, so it must not turn with the camera.

Corners worth calling out

  • Top and Bottom. On the pole there is no horizontal offset to read a yaw from, and atan2(0, 0) returns 0. That happens to be the right answer: THREE.Matrix4.lookAt breaks the same degenerate tie towards the same axes, so Top view keeps screen-right = +X and screen-down = +Z. Verified against the matrix three.js actually builds, not just derived on paper.
  • Below the horizon the view is mirrored, so screen-down is flipped. Screen-right is not — it never depends on the pitch.
  • Orthographic camera shares position, target and up with the perspective one, so it needs nothing extra. Covered by tests anyway.
  • A workplane laid on a face keeps its own axes. Turning those with the camera would send arrows off the face the workplane was drawn on, so the camera only steers the flat base plane.

How the camera gets out of the viewport

The viewport did not expose the camera at all — no ref, no event, no prop. It now reports yaw and pitch through an optional onCameraOrientationChange, computed by the very helper syncViewCube already used (extracted, not duplicated) and emitted from the same throttled branch of the render loop. The editor parks it in a ref, so a moving camera causes no renders. With the callback absent the editor keeps the Front-view mapping, i.e. exactly today's behaviour.

Tests

tests/unit/screenAlignedNudge.test.ts, 26 cases over the pure axis-choice function:

  • four compass views, both poles, negative yaw, a full turn either way
  • the 45° ties on both sides of zero (44.9 / 45 / 45.1, 135 / 135.1, −45 / −44.9)
  • the below-horizon mirror, the shift step, the workplane-on-a-face passthrough
  • and a block that compares every answer against the screen axes three.js really builds for that camera — perspective and orthographic — asserting the chosen axis is the closest one and never more than 45° off

npm test 290 passed / 47 files, npm run typecheck clean, npm run build clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TAoEuBEJyJtkEGapsp6G22

Arrow keys step along the world axes no matter where the camera stands.
Orbit round to the Left view, press Right, and the shape walks away from
you into the screen. The axis is correct; the key is not the one you
pressed.

This picks the workplane axis closest to the direction the arrow points
on screen, which is the camera yaw rounded to the nearest 90 degrees.
The step stays on an axis: no diagonals, so grid snapping and typed
coordinates are untouched. Only which axis changes.

Nothing changes in the two views people spend most of their time in.
The Front view maps to the same axes as before, and so does the default
home view: its yaw is exactly 45, halfway between two axes, and the tie
rounds to the axis the editor already used. A user who never orbits
never sees a difference.

Details worth knowing:

- Ctrl + Up/Down still lifts along world Y. Height does not depend on
  where you stand, so it must not turn.
- On the pole, straight down or straight up, there is no horizontal
  offset to read a yaw from and atan2(0, 0) returns 0. That is the right
  answer: THREE.Matrix4.lookAt breaks the same tie towards the same axes,
  so Top view keeps screen right = +X and screen down = +Z.
- Below the horizon the view is mirrored, so screen down is flipped.
  Right is not: it never depends on the pitch.
- The orthographic camera shares position, target and up with the
  perspective one, so it needs nothing extra.
- A workplane laid on a face keeps its own axes. Turning those with the
  camera would send arrows off the face the workplane was drawn on.

The viewport did not expose the camera at all, so it now reports yaw and
pitch through an optional onCameraOrientationChange, computed by the
helper the view cube already used. The editor parks it in a ref, so a
moving camera causes no renders. Without the callback the editor keeps
the Front view mapping, which is exactly today's behaviour.

The axis choice is a pure function in lib, covered by 26 tests: the four
compass views, both poles, the 45 degree ties, negative yaw, a full turn,
the below-horizon mirror, and a check that every result matches the
screen axes three.js really builds for that camera, orthographic included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAoEuBEJyJtkEGapsp6G22
@FutuRiata FutuRiata changed the title Move shapes with the arrow keys the way the camera sees them feat: move shapes with the arrow keys the way the camera sees them Sep 6, 2026
mhaeu pushed a commit to mhaeu/SketchForge-3D that referenced this pull request Sep 9, 2026
Arrow keys step along the world axes no matter where the camera stands.
Orbit round to the Left view, press Right, and the shape walks away from
you into the screen. The axis is correct; the key is not the one you
pressed.

This picks the workplane axis closest to the direction the arrow points
on screen, which is the camera yaw rounded to the nearest 90 degrees.
The step stays on an axis: no diagonals, so grid snapping and typed
coordinates are untouched. Only which axis changes.

Nothing changes in the two views people spend most of their time in.
The Front view maps to the same axes as before, and so does the default
home view: its yaw is exactly 45, halfway between two axes, and the tie
rounds to the axis the editor already used. A user who never orbits
never sees a difference.

Details worth knowing:

- Ctrl + Up/Down still lifts along world Y. Height does not depend on
  where you stand, so it must not turn.
- On the pole, straight down or straight up, there is no horizontal
  offset to read a yaw from and atan2(0, 0) returns 0. That is the right
  answer: THREE.Matrix4.lookAt breaks the same tie towards the same axes,
  so Top view keeps screen right = +X and screen down = +Z.
- Below the horizon the view is mirrored, so screen down is flipped.
  Right is not: it never depends on the pitch.
- The orthographic camera shares position, target and up with the
  perspective one, so it needs nothing extra.
- A workplane laid on a face keeps its own axes. Turning those with the
  camera would send arrows off the face the workplane was drawn on.

The viewport did not expose the camera at all, so it now reports yaw and
pitch through an optional onCameraOrientationChange, computed by the
helper the view cube already used. The editor parks it in a ref, so a
moving camera causes no renders. Without the callback the editor keeps
the Front view mapping, which is exactly today's behaviour.

The axis choice is a pure function in lib, covered by 26 tests: the four
compass views, both poles, the 45 degree ties, negative yaw, a full turn,
the below-horizon mirror, and a check that every result matches the
screen axes three.js really builds for that camera, orthographic included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAoEuBEJyJtkEGapsp6G22
Applied-from: Formsmith746#93 (upstream nicht gemergt)
Applied-by: Claude Opus 5 <noreply@anthropic.com>
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