Files
workouts/Exercise Library/TODO-orbit.md
T

91 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Handoff: complete camera rotation (slow orbit) for all exercises
Repo: `~/Documents/indie/Workouts`. Read `Exercise Library/SYSTEM.md` first
(format spec + visual language), then `Exercise Library/kinematics.py` and
`render.py` (reference renderer), and `Workouts/ExerciseFigure/`
(`MotionSolver.swift`, `ExerciseFigureView.swift`, `ExerciseMotion.swift`
the Swift port, kept in lockstep with Python; shared by the iOS and watch
targets).
## Current state
The figure rig is an anatomical 3D skeleton with a per-exercise orthographic
camera (`camera: {yaw, pitch}`, default pitch 10°). In the app,
`FigureAnimation` (ExerciseFigureView.swift) slowly orbits the camera (360°
per 24 s, `orbitPeriod`) — but only when `props.isEmpty && hide.isEmpty`,
i.e. the 8 bodyweight moves. The 14 machine exercises stay at their authored
view because their equipment can't rotate. The goal: **every exercise
orbits.**
## What blocks it, in order of work
1. **`scene` props are 2D canvas billboards** (seats, masts, benches —
`line`/`rect`/`circle` in canvas coords). They need world-space 3D form.
Follow the exercise-mat pattern (`geo["floor"]` in `render.py` /
`FigureGeometry.floor` in Swift — a quad built in model space relative to
the root anchor, rotated through `Rx(pitch)·Ry(yaw)`, then projected):
extend the scene-shape schema so each shape carries depth — suggest an
optional `"z"` (depth plane, default 0) plus `"depth"` (extrusion
half-width) so a seat rect becomes a slab and a mast line a post; author
full 3D endpoints only where needed. Convert authored canvas coords to
model space by subtracting the motion's root anchor (same as the mat).
Re-author all 14 machines' props minimally — schematic silhouettes, not
scale models; check each at yaw 0 (must look identical to today), ±45°,
and 90°.
2. **`cable`** `from` anchors and **`bar`** fixed world angles are
authored-view constructs: make both world-space (the anchor gets a depth,
the bar becomes a segment defined in the authored view and rotated like
the mat — it must stay welded to the hands).
3. **`roller`** and **`pad`/`dumbbell`** perpendiculars are computed in
screen space per frame — under orbit compute them in the flat authored
view, then rotate (the resolve-then-rotate invariant below).
4. **`hide` lists** name limbs occluded in one authored view — wrong under
orbit. Only Side Plank uses one; re-author it with genuine 3D (root
`roll`, real abduction) instead, then delete the `hide` mechanism if
nothing else uses it.
5. Relax the `orbits` gate in `FigureAnimation` to always-on once 14 hold
up visually.
## Invariants — do not break
- **Resolve flat, then rotate.** IK pins and prop attachment logic operate
in the *authored* view (yaw as authored, pitch 0); the *posed result* is
rotated for presentation. Never re-solve pins or recompute screen-space
perpendiculars in the rotated view — that class of bug ("hands stuck
mid-orbit") was already fixed twice.
- **Both renderers in lockstep.** Every geometry change lands in
`render.py`/`kinematics.py` *and* the Swift port, same conventions and
constants. `WorkoutsTests/Fixtures/figure-fixtures.json` pins Swift to
Python (0.5 px); regenerate it from the Python pipeline whenever projected
geometry changes (per exercise/keyframe snapshot: order, shade, spine,
head, nose, limb chains, plus tween + orbit samples — extend the orbit
samples to a machine exercise once props rotate).
- **Visual parity at the authored view**: rendering any exercise at its
authored yaw must look the same as today. Verify with
`python3 render.py --sheet` before/after, and orbit strips
(`--orbit <name>`, or a multi-yaw contact sheet) for each converted
machine.
## Verification workflow
`python3 render.py` + `--sheet`/`--orbit` and eyeball the PNGs;
`python3 render.py --export` (verbatim copy into
`Workouts/Resources/ExerciseMotions/`); `xcodegen generate` if project.yml
changes; `xcodebuild test -scheme Workouts -destination 'platform=iOS
Simulator,name=iPhone 17 Pro' -only-testing:WorkoutsTests` (48 tests must
stay green, plus whatever you add); `xcodebuild build -scheme "Workouts
Watch App" -destination 'generic/platform=watchOS Simulator'`. Update
`SYSTEM.md` (props section) and add one end-user CHANGELOG.md entry per the
app-changelog skill (one sentence, no bullets, top of the July 2026 block —
e.g. machine form guides now rotate too). Commit per feature with the
repo's existing message style and push. Delete this file in the final
commit.
## Judgment calls you're empowered to make
Exact 3D prop schema shape (keep it minimal and hand-authorable),
per-machine depth values, and whether a couple of visually-degenerate
machines (e.g. cable stacks) look better with a reduced orbit sweep (±40°
pendulum instead of full 360°) — if so, make sweep style a per-motion
`camera` option with full-360 the default.