Skip to content

fix(camera): repair and refactor the camera protocol stack#134

Open
gold-silver-copper wants to merge 25 commits into
orhun:mainfrom
gold-silver-copper:fix/camera-protocol-stack
Open

fix(camera): repair and refactor the camera protocol stack#134
gold-silver-copper wants to merge 25 commits into
orhun:mainfrom
gold-silver-copper:fix/camera-protocol-stack

Conversation

@gold-silver-copper

@gold-silver-copper gold-silver-copper commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Parent stack

Stacked on #118; review and merge #118 first. Once #118 is merged, this PR will
contain only the architectural and corrective follow-up.

This PR is stacked on top of #118. It preserves PR #118's current head
(22d9bbad5dcc6ae83aa37e143599b5f011f9aaef) and all 13 original commits in
its ancestry, then merges current orhun/ratty:main without rebasing or
rewriting those commits.

What changed

  • fixes the perspective regression with a positive near plane, finite/clamped
    projection values, independent orthographic scale and perspective FOV,
    explicit orthographic/perspective markers, and a tested activation/clear-owner
    matrix
  • replaces the camera slot/resource copy loop with one persistent
    TerminalCameraSlots source of truth, transient mouse interaction state,
    queued RGP updates, activation messages, and focused ordered systems
  • moves yaw, pitch, roll, pan, dolly, scale, and FOV synchronization onto the
    active Bevy camera while avoiding writes to inactive projections
  • removes camera mutation from the unrelated instance-brightness system and
    keeps camera-only changes outside the direct Vello terminal redraw/material
    pipeline introduced by Render terminal texture with direct Vello path #107
  • corrects the flat/3D inline object and cursor paths after integrating current
    main
  • defines the RGP camera contract completely: slots 0..9, partial updates,
    immediate set=1 activation, finite numeric validation, pz dolly,
    rx/ry/rz = pitch/yaw/roll in degrees, and independent valid scale/FOV
    handling
  • adds configurable Ctrl+Alt+Shift+0..9 camera-slot bindings without
    colliding with the existing Ctrl+Alt+0 font reset
  • updates the protocol documentation, README controls, and big_rat example

Review follow-up

  • preserves the complete pre-Mobius mode and pose independently for every camera slot, including the documented minimum orthographic scale
  • makes protocol updates deterministically cancel active Mobius transitions while applying partial updates to both the live and saved source poses
  • unifies orthographic, perspective, cursor, RGP, terminal, and Kitty placement on the same warped surface geometry
  • keeps Kitty planes synchronized through Mobius transitions, emits the final morph frame without idle mesh uploads, and renders the strip double-sided
  • adds focused regression coverage for source restoration, protocol/transition ownership, surface alignment, Kitty geometry, scheduling, and material culling
  • widens the interactive wheel-zoom range to the protocol minimum scale (0.01, previously 0.1) and turns the 4.0 interactive cap into a one-way ratchet, so larger application-set scales are preserved instead of being snapped down by the first wheel tick

Root cause

PR #118's conflict resolution combined a perspective projection with a
negative near plane and indirect camera identification. A single zoom value was
also interpreted as both orthographic scale and perspective FOV, so an ordinary
orthographic zoom-out became a nearly-pi perspective FOV and reduced the plane
to a sliver. The implementation copied presets into live resources in keyboard
handling, copied user changes back from the brightness system, and inverted
several flat/3D visibility branches while adapting to the #107/#121 rendering
changes. The result was invalid perspective state, ambiguous clear ownership,
and camera state that depended on unrelated system execution.

Architecture

Before this follow-up, PTY parsing mutated slots, keyboard handling copied a
slot into live camera resources, mouse input mutated those copies, and the
brightness system wrote the copies back. Mode and pose ownership therefore
depended on unrelated system order.

After this follow-up, TerminalCameraSlots is the only persistent source of
mode and pose. Protocol updates and activation are queued, mouse drag state is
transient, Mobius transitions are explicit and slot-aware, and a final camera
system synchronizes only changed active values to marked Bevy entities. This is
necessary alongside #107 because camera changes must remain separate from its
dirty-gated Vello texture upload and material preparation path.

Protocol decisions

The c command has not shipped, so this PR favors a coherent contract over its
WIP behavior. Rotations are degrees, all three translation/rotation axes are
implemented, orthographic scale and perspective FOV persist independently,
and malformed commands are consumed but ignored safely. This intentionally
changes the WIP axis/unit behavior from #118; there is no released compatibility
surface to preserve.

Verification

Passed locally:

  • cargo fmt --all -- --check
  • cargo check --all-targets
  • cargo clippy --all-targets -- -D warnings
  • cargo test --all-targets (49 tests passed)
  • cargo fmt --manifest-path widget/Cargo.toml --all -- --check
  • cargo check --manifest-path widget/Cargo.toml --all-targets
  • cargo clippy --manifest-path widget/Cargo.toml --all-targets -- -D warnings
  • cargo test --manifest-path widget/Cargo.toml --all-targets
  • ancestry: git merge-base --is-ancestor refs/remotes/origin/pr-118 HEAD
  • full diff: git diff --check origin/main...HEAD

Manual smoke test

Passed using the real Ratty and big_rat binaries under Xvfb with Mesa's
llvmpipe Vulkan adapter. The smoke run
completed successfully and uploaded 16 screenshots plus renderer logs and pixel
statistics.

  • visually inspected RGP transitions through Ortho, Persp, Mobius, Flat, and
    back to Ortho; the terminal and inline model remained rendered in every mode
  • visually inspected Ratty's keyboard bindings for perspective, Mobius enter,
    Mobius exit, orthographic, and flat transitions
  • applied an application perspective pose, then exercised mouse orbit, pan, and
    wheel zoom; the resulting camera framing changed without losing the terminal
  • resized the window and changed/reset font size in Ortho, Persp, Mobius, and
    Flat; captures changed from 960x620 to 1100x700 and remained nonblank
  • registered and rendered the animated SpinyMouse.glb RGP object in flat and
    3D modes; logs also confirm the embedded cursor model loaded, and captures
    show it on the 3D terminal surface
  • typed a brightness update into big_rat; the visible value changed from
    1.0 to 1.1, exercising PTY output, terminal texture redraw, and RGP
    material refresh
  • all captures contained 3,983-8,284 colors with nonzero deviation; no render
    panic or GPU loss occurred, and Ratty exited cleanly after the demo quit

This validates the software Vulkan path. Native Metal/DX12 driver behavior is
still left to normal platform testing, but no camera-specific blocker remains.

Related work

Update (2026-07-19): degree-based fov field

Commit 40c0066 replaces the dual-purpose scale field with a split contract,
since the c command has not shipped and this is the last cheap moment to fix
the wire format:

  • scale now always means orthographic projection scale (minimum 0.01).
  • A new fov field carries the vertical perspective field of view in
    degrees
    , valid strictly inside the equivalent of 0.05 to pi - 0.05
    radians (about 2.87 to 177.13 degrees). Degrees are converted to radians
    at the parse boundary; internal storage and the Bevy projection wiring are
    unchanged.
  • Every angle on the wire (rx, ry, rz, fov) is now degrees — one unit
    rule, no exceptions.
  • One command may set scale and fov together regardless of the current or
    requested type, mirroring the independent per-slot storage.
  • Error semantics mirror the existing rules: out-of-range numeric scale or
    fov is ignored while the rest of a valid command still applies; non-finite
    or valueless fov rejects the whole command.

The protocol documentation, the big_rat perspective preset (scale=0.85
radians → fov=50 degrees), and the regression tests were updated to match.

A fresh GPU smoke run
on this commit passed with all 16 captures healthy (4,391-8,567 colors, nonzero
deviation, no panics); the perspective capture now exercises the degree-based
fov=50 preset end to end.

Commit c3cef5d follows up with documentation polish: the degree-to-radian
parse boundary is now documented on every struct field and constant it touches,
the protocol doc gained a perspective example, camera steps in the example
session, and an explicit note that out-of-range scale/fov values do not
block set=1 activation, and cargo doc is warning-free after replacing
rustdoc links to private items with plain code spans.

@gold-silver-copper
gold-silver-copper marked this pull request as ready for review July 19, 2026 01:04
@gold-silver-copper
gold-silver-copper marked this pull request as draft July 19, 2026 06:01
- compose the camera orbit as YXZ so pitch stays independent of yaw at
  every orbit angle, matching the inverse of the old plane rotation
- carry morph, pose, and roll continuity through interrupted Mobius
  transitions and let the toggle turn an exit back into an enter
- animate Mobius entry on slot activation and on protocol mode changes,
  honoring stored preset scales exactly instead of the keyboard zoom
  floor, and keep the settled strip when switching between Mobius slots
- respect the protocol scale range in mouse-wheel zoom and reset camera
  drag state when the window loses focus
- render Kitty inline planes with NoFrustumCulling and stop re-uploading
  terminal meshes every frame in settled Mobius mode
- reject value-less camera protocol fields atomically, fix stale camera
  rustdoc, and order sync_inline_objects after the camera Transition set
- reset selection drags and forwarded mouse-button state on window focus
  loss alongside camera drags, so no held-button state survives a
  release delivered to another window
- hand an in-flight Mobius strip animation to the newly activated slot
  continuously instead of snapping it to the settled strip
- shrink a resumed transition's morph phase in proportion to the
  remaining distance, keeping morph velocity continuous
- pass the Mobius enter source as TerminalMobiusSource and the zoom
  floor as a named enum instead of a raw f32, and document why the
  keyboard floor cannot reach protocol or activation enters
- pin the keyboard enter-finish zoom floor write-back and Mobius wheel
  zoom routing with tests, and correct the orbit test tolerance comment
- treat a re-enter toward identical targets as a no-op so repeated slot
  activations cannot reset the transition clock and hold input gating
  open indefinitely
- let a resumed morph advance during the camera phase instead of
  re-freezing for the hold window on every hand-off
- synthesize the release events the PTY application is still owed when
  the window loses focus mid-drag, and clear the motion dedup cell
- make apply_wheel_zoom the single owner of the Mobius-animating wheel
  gate, correct the zoom-floor comments (the floor is recomputed from
  the begin_enter argument on every call; stop() only controls start
  continuity), and drop the dead enter_morph_progress accessor
@gold-silver-copper

gold-silver-copper commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Manual test plan

Since the automated verification covered fmt/clippy/tests plus a headless smoke run on Mesa's software Vulkan renderer, this checklist focuses on what that run couldn't cover: native GPU drivers (Metal/Vulkan), interactive feel, and real keyboard handling.

Conventions

  • <repo> = a ratty checkout with this PR applied.
  • Keys are written Ctrl+Alt+…. On macOS, Ctrl = Control, Alt = Option. Digit bindings use physical top-row keys (Digit0Digit9), not the numpad.
  • Mouse in any 3D mode: left-drag = orbit, right-drag = pan, wheel = zoom (ortho scale in Ortho/Mobius, FOV in Persp). Camera mouse input is intentionally ignored while a Mobius morph is animating.
  • All shell commands run inside the ratty window unless stated otherwise, and work with GNU and BSD coreutils.

Stage 0 — Build and launch

cd <repo>
cargo run --release

✅ Launches on the native GPU backend (Metal on macOS, Vulkan on Linux) without panics.

Stage 1 — The original regression (ortho scale vs. perspective FOV)

  1. Ctrl+Alt+Enter → orthographic 3D.
  2. Wheel down ~15 clicks — zoom far out.
  3. Ctrl+Alt+P → perspective. ✅ Normal framing, not a collapsed sliver (the bug reused the big ortho scale as a near-π FOV).
  4. Wheel down ~5 clicks in perspective (widens FOV), then Ctrl+Alt+Enter. ✅ Ortho returns at step 2's zoom, unaffected.
  5. Ctrl+Alt+P. ✅ Perspective returns with step 4's FOV. The two values persist independently.
  6. Ctrl+Alt+Enter twice → back to flat 2D.

Stage 2 — Keyboard matrix

  1. Ctrl+= three times, then Ctrl+Alt+0. ✅ Font resets.
  2. Ctrl+Alt+Shift+0. ✅ Slot 0 activates; the font must not reset and nothing types into the shell.
  3. Ctrl+Alt+0 again. ✅ Font reset still works — the two bindings on the same digit don't shadow each other.
  4. Ctrl+Alt+Shift+1Ctrl+Alt+Shift+3. ✅ Chords register cleanly (slots are still defaults; real poses come in Stage 4).
  5. In ortho (Ctrl+Alt+Enter): Ctrl+Alt+Up ×5, Ctrl+Alt+Down ×5. ✅ Warp changes as before.
  6. Untouched bindings: select text, Ctrl+Alt+C, Ctrl+Alt+V; Alt+PageUp / Alt+PageDown.
  7. Back-compat alias: in a custom config, rename ToggleOrtho3DMode back to Toggle3DMode, relaunch, press Ctrl+Alt+Enter. ✅ Still toggles. Revert.
  8. On non-US layouts, repeat steps 1–3 — Shift+digit is where layouts misbehave.

Stage 3 — Mouse feel, zoom floor, and the ratchet

In ortho mode:

  1. Left-drag slowly right, then up. ✅ Smooth orbit; no jump on button-down, no drift after release.
  2. Right-drag in a circle. ✅ Pans with the cursor; stops cleanly on release.
  3. Wheel down to the outward limit, then wheel up the same amount. ✅ Fully recoverable; you never lose the terminal (wheel-out cap is scale 4.0).
  4. Ctrl+Alt+P, repeat 1–3 in perspective (wheel = FOV). ✅ Pan speed feels proportionate at different zooms.
  5. Ratchet — set a scale beyond the wheel cap:
    printf '\033_ratty;g;c;id=0;set=1;type=Ortho;scale=6.0\033\\'
    Wheel up one click. ✅ Zooms in slightly from 6.0 — must not snap to 4.0. Continue wheeling up. ✅ Smooth return through 4.0.
  6. Floor — wheel in as far as it goes. ✅ Stops at scale 0.01; no flip or degenerate view.
  7. Focus loss mid-drag: hold a left-drag, switch away and back (Cmd+Tab on macOS, Alt+Tab on Linux). ✅ Drag is released; moving the mouse afterward doesn't keep orbiting.

Stage 4 — Slots and per-slot persistence

  1. Program three slots without activating (set=0):
    printf '\033_ratty;g;c;id=1;set=0;type=Ortho;scale=1.5;rx=15;ry=30\033\\'
    printf '\033_ratty;g;c;id=2;set=0;type=Persp;fov=70;pz=30;ry=-25\033\\'
    printf '\033_ratty;g;c;id=3;set=0;type=Flat\033\\'
    ✅ No visible change yet.
  2. Ctrl+Alt+Shift+1 ✅ tilted ortho; Ctrl+Alt+Shift+2 ✅ dollied perspective; Ctrl+Alt+Shift+3 ✅ flat.
  3. On slot 1, left-drag to a distinctive orbit. Switch to slot 2, then back to slot 1. ✅ Slot 1 shows the hand-modified pose (mouse edits persist into the slot); slot 2 is untouched.
  4. Partial update:
    printf '\033_ratty;g;c;id=1;px=0.4\033\\'
    Ctrl+Alt+Shift+1. ✅ Same pose, panned right — rotation/scale retained.
  5. Pre-Mobius restore: on slot 1, Ctrl+Alt+M, then Ctrl+Alt+M again. ✅ Returns to slot 1's exact ortho pose, not a default.

Stage 5 — Transition stress

  1. Ctrl+Alt+M, and mid-morph press Ctrl+Alt+M again; repeat rapidly ~10×. ✅ No stall or frozen half-morphed geometry (this PR's head commit fixes exactly this).
  2. Mash Ctrl+Alt+M / Ctrl+Alt+Enter / Ctrl+Alt+P / Ctrl+Alt+M / Ctrl+Alt+Enter with ~0.2 s gaps. ✅ Never stuck in an unrecoverable mode; text renders throughout.
  3. Start a morph (Ctrl+Alt+M), immediately run:
    printf '\033_ratty;g;c;id=0;set=1;type=Persp;fov=60\033\\'
    ✅ Morph cancels deterministically; lands in perspective.
  4. During a morph, wiggle the mouse with the left button held. ✅ Ignored, and no queued jump when the morph ends.

Stage 6 — Protocol contract and malformed input

  1. Support query — the reply must advertise camera=1:
    printf '\033_ratty;g;s\033\\'
  2. Malformed cases — after each: camera unmoved, no panic, no stray characters in the terminal:
    printf '\033_ratty;g;c\033\\'                          # missing required id
    printf '\033_ratty;g;c;id=12;set=1;type=Persp\033\\'   # slot out of range
    printf '\033_ratty;g;c;id=1;set=7\033\\'               # invalid set
    printf '\033_ratty;g;c;id=1;type=Bogus\033\\'          # unknown type
    printf '\033_ratty;g;c;id=1;rx=nan;py=inf\033\\'       # non-finite numbers
    printf '\033_ratty;g;c;id=1;pz=\033\\'                 # field with no value
  3. Spec subtlety — out-of-range scale is ignored but the rest still applies:
    printf '\033_ratty;g;c;id=0;set=1;type=Ortho;scale=0.001;px=0.5\033\\'
    ✅ Pans right; scale unchanged.
  4. Range edges: ortho scale=0.01 works; perspective fov=3 and fov=177 (degrees) work, fov=2.8 and fov=177.2 are ignored (must stay strictly inside the equivalent of 0.05 to π − 0.05 radians).
  5. Mixed independence: printf '\033_ratty;g;c;id=0;scale=2.0;fov=50\033\\' in any mode updates both stored projection values — switch between Ortho and Persp to confirm each shows its own.

Stage 6b — Degree-based fov contract (commit 40c0066)

These verify the wire-format change that split perspective FOV out of scale
and moved every protocol angle to degrees.

  1. Degrees, not radians — the discriminator. Activate perspective with a small FOV:
    printf '\033_ratty;g;c;id=0;set=1;type=Persp;fov=3\033\\'
    3 degrees is an extreme telephoto: the terminal appears hugely magnified with almost no perspective distortion. If the value were still interpreted in radians (3 rad ≈ 172°), you'd see the opposite — a tiny fisheye-distorted plane. One glance settles which unit is live.
  2. Intuitive scaling. Step through:
    printf '\033_ratty;g;c;id=0;fov=50\033\\'    # normal framing
    printf '\033_ratty;g;c;id=0;fov=120\033\\'   # wide angle, strong perspective
    ✅ Bigger degrees = wider view, monotonically, no jumps.
  3. scale no longer means FOV. While still in perspective:
    printf '\033_ratty;g;c;id=0;scale=2.0\033\\'
    ✅ The perspective view does not change at all. Press Ctrl+Alt+Enter (ortho). ✅ The stored ortho scale 2.0 is now visible — the command updated the other projection value, silently and correctly.
  4. Range edges in degrees (derived from the internal 0.05..π−0.05 radian clamp):
    printf '\033_ratty;g;c;id=0;set=1;type=Persp;fov=2.8\033\\'    # below min — ignored
    printf '\033_ratty;g;c;id=0;fov=3\033\\'                       # min side — applies
    printf '\033_ratty;g;c;id=0;fov=177\033\\'                     # max side — applies
    printf '\033_ratty;g;c;id=0;fov=177.2\033\\'                   # above max — ignored
    ✅ The two out-of-range commands leave the view exactly as the previous in-range value set it.
  5. Field-level vs command-level rejection. These are different by design:
    printf '\033_ratty;g;c;id=0;fov=999;px=0.5\033\\'   # out-of-range fov: px STILL applies
    printf '\033_ratty;g;c;id=0;fov=;px=0.5\033\\'      # valueless fov: whole command ignored
    printf '\033_ratty;g;c;id=0;fov=nan;px=-0.5\033\\'  # non-finite fov: whole command ignored
    ✅ Only the first command pans the camera.
  6. Both projections in one command. From flat mode:
    printf '\033_ratty;g;c;id=0;scale=1.5;fov=100\033\\'
    Press Ctrl+Alt+Enter (ortho shows scale 1.5), then Ctrl+Alt+P (perspective shows 100°). ✅ One partial update fed both stored values regardless of the mode it was sent from.
  7. Rotation degrees sanity. printf '\033_ratty;g;c;id=0;set=1;type=Ortho;ry=180\033\\' ✅ terminal seen from behind (mirrored); ry=360 ✅ identical to ry=0.
  8. Migrated preset. In big_rat, press v to reach Persp — the preset now sends fov=50 instead of the old scale=0.85 radians (≈48.7°). ✅ Framing is visually equivalent to the pre-change preset, not noticeably wider or tighter.

Stage 7 — big_rat: state ownership and the RGP object path

  1. Inside ratty:
    cd <repo>/widget && cargo run --example big_rat
    ✅ Animated SpinyMouse model renders.
  2. Press v repeatedly — cycles Flat → Ortho → Persp → Mobius via set=1 protocol commands. ✅ Terminal text and model render in every mode.
  3. On Persp, left-drag to a distinctive orbit, then press ] three times (brightness 1.0 → 1.3). ✅ Brightness changes and the camera does not move one pixel — direct test that the brightness system no longer writes camera state.
  4. [ lowers brightness, a toggles animation, 16 rotate the model, +/- scale it, arrows move it. ✅ None of these disturb the camera pose.
  5. r to reset, q to quit. ✅ Clean shell; no leftover objects or camera weirdness.

Stage 8 — Kitty image through the morph

Display an 8×4-cell random-noise image (noise makes warp distortion visible; works with GNU and BSD tools):

printf '\033_Gf=24,s=32,v=32,c=8,r=4,a=T;%s\033\\' \
  "$(head -c 3072 /dev/urandom | base64 | tr -d '\n')"

Or a solid white square (LC_ALL=C is required — without it, a UTF-8 locale makes tr emit two bytes per pixel byte, and ratty correctly drops the size-mismatched transfer):

printf '\033_Gf=24,s=32,v=32,c=8,r=4,a=T;%s\033\\' \
  "$(head -c 3072 /dev/zero | LC_ALL=C tr '\0' '\377' | base64 | tr -d '\n')"
  1. ✅ Image appears at the cursor spanning 8×4 cells.
  2. Ctrl+Alt+Enter, then Ctrl+Alt+M. ✅ Image stays glued to the surface through the whole morph, not just at the end.
  3. In Mobius, left-drag until you view the back of the strip. ✅ Renders from behind (now double-sided) — back-face culling is a classic software-vs-native-driver divergence, so this needs eyes on real hardware.
  4. Ctrl+Alt+M to exit. ✅ Image correctly placed in flat mode.

Stage 9 — Resize, font, and idle cost

  1. In each mode (Ctrl+Alt+Enter / Ctrl+Alt+P / Ctrl+Alt+M / flat): resize the window, then Ctrl+= twice and Ctrl+Alt+0. ✅ No blank frames or stale-size rendering.
  2. Idle check: enter Mobius, let the morph finish, hands off, watch CPU/GPU for ~30 s (Activity Monitor on macOS, htop / nvtop on Linux). ✅ Usage settles to pre-PR idle level — no ongoing mesh uploads, no camera-triggered texture re-uploads. Repeat after a camera drag ends in ortho.

The c command previously overloaded scale as orthographic scale in
Ortho/Mobius and vertical FOV in radians in Persp, while rotations were
degrees. Since the camera contract has not shipped, fix the wire format
before it becomes compatibility surface:

- scale now always means orthographic projection scale (min 0.01)
- new fov field carries vertical perspective FOV in degrees, converted
  to radians at the parse boundary and validated against the existing
  0.05..pi-0.05 clamp; internal storage stays radians
- every angle on the wire (rx, ry, rz, fov) is degrees
- one command may set scale and fov together regardless of type,
  mirroring the independent per-slot storage
- out-of-range numeric scale/fov is ignored field-level, other fields
  still apply; non-finite or valueless fov rejects the whole command
- document the degree-to-radian parse boundary on RgpCameraSettings,
  TerminalCameraUpdate, and the FOV clamp constants so no call site can
  mistake which unit a field holds
- state in the protocol doc that an out-of-range scale/fov does not
  block set=1 activation, add a perspective example command, and extend
  the example session with camera control
- note the preset unit conventions in the big_rat example
- replace rustdoc links to private items with plain code spans; cargo
  doc is now warning-free
@gold-silver-copper

Copy link
Copy Markdown
Collaborator Author

I verified all the behavior manually and seems to be good to goo

@gold-silver-copper
gold-silver-copper marked this pull request as ready for review July 20, 2026 02:48
@gold-silver-copper

Copy link
Copy Markdown
Collaborator Author

Two follow-ups from re-reading the #118 conversation against this PR — neither is a blocker, but both deserve an explicit decision on the record rather than passing silently.

Ctrl+Alt+O was dropped

#118's description promised Ctrl+Alt+O for the orthographic view alongside Ctrl+Alt+P for perspective. This PR keeps only Ctrl+Alt+P and routes orthographic through the pre-existing Ctrl+Alt+Enter toggle (renamed ToggleOrtho3DMode, with Toggle3DMode kept as a config alias). That continuity choice seems right — existing users already know Ctrl+Alt+Enter — but it was never discussed on either PR, so flagging it here to make it a decision instead of an accident. If anyone prefers the original proposal, adding Ctrl+Alt+O as a second binding for the same action is a one-line config default.

Roll is now enabled, but the reason it was disabled still holds

In #118's inline review, the roll input from the protocol was commented out on purpose:

ratty's camera doesn't currently have the capability to be rolled by the user, it has 2 axis rotation. If I allowed to actually change the roll, the user would have no intuitive way to correct it afterwards with the mouse

This PR implements rz fully (degrees → pose.roll → camera quaternion), which resolves the review comment about commented-out code — but mouse orbit still only drives yaw and pitch. So the original concern is now live: an application that sets rz=45 leaves the user with no interactive way to undo the roll. The slot system softens this considerably (a default slot is one Ctrl+Alt+Shift+N away, and the app can always send rz=0), so shipping as-is seems fine, but it's worth agreeing that's the intended answer.

If we want to close the gap properly later, two small candidates:

  • a roll gesture (e.g. Shift+left-drag horizontal) in the 3D modes, or
  • a "reset active slot pose" keybinding that restores the slot's defaults without touching the others.

Either works as a separate follow-up PR; neither needs to hold this one.

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.

3 participants