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

4.9 KiB
Raw Blame History

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.