Adds WorkoutActivityType.warmUp (HealthKit .preparationAndRecovery) and
.stretching (.flexibility), and retags the six starter splits that were all
mislabeled as Functional Strength:
- Warm-Up: Upper Body Warm-Up, Lower Body Warm-Up, Morning Wake-Up
- Stretching: Morning Mobility, Full Body Stretch, Evening Stretch
The split editor's activity picker surfaces them automatically (CaseIterable).
Older app versions decode the new raw values as the default type — additive and
not schema-gated, so no quarantine.
The orbit was always a real camera orbit — figure and props share one
rigid rotation — but a bar's screen-space angle authored the wrong 3D
rod: the default horizontal encoded a rod along the body axis, so
barbells hovered fixed on screen and vanished at the head-on view
where they should span widest. Line props now take "axis": "z" (both
renderers in lockstep, fixture-pinned): the world left-right direction
projects through the camera pitch like the floor quad — end-on plates
in profile, full span face-on, swinging with the hands in between.
Applied to the ten cross-body bars; vertical handles were already
orbit-invariant.
Goblet Squat's hand pins sat so close to the shoulders that the
two-bone IK was degenerate, flipping between a chicken-wing and an
elbow-behind solve; re-pinned level with the shoulders so the elbows
tuck straight down through the whole rep.
Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
Abdominal's pinned hands used elbow -40 as the IK plane hint, drawing
the arms hyperextended (user-reported). Flipping the hint bends the
elbows the natural way while the hands stay on the handles. Same class
of fix for the milder cases: Arm Curl and Shoulder Press elbows and
Calfs knees clamp to -8, Side Plank's raised arm to the -70 ROM cap.
The whole library now passes render.py --strict with zero warnings,
making it a valid verification gate. Fixtures regenerated; 48 tests
green.
Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
Scene shapes, cable anchors, bar angles, pad perpendiculars, and roller
offsets all resolve in the authored view exactly as before, then rotate
about the world-vertical axis through the root anchor - the same
resolve-then-rotate pattern as the figure's pins and the mat - so at the
authored yaw every exercise renders bit-identically to today, and under
an orbiting camera the equipment turns with the figure while staying
welded to its hands and feet. Scene lines gain an optional depth plane
(z) and slab extrusion (depth) so seats, backrests, and platforms keep
form edge-on; the rect shape is retired (re-authored as slab lines).
All 14 machines' props re-authored with depths and verified at eight
orbit angles. The fixture snapshots move into the pipeline as
render.py --fixtures and now cover orbit-presentation samples with
resolved prop primitives for a spread of prop flavors; the in-app
renderer resolves props in MotionSolver (lockstep with resolve_props)
and the view just draws primitives.
Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
The floor rectangle was screen-locked, which broke the illusion the
moment the camera orbited. It is now a world-space quad on the ground
plane, sized to each motion's projected footprint across its key frames
and rotated through the same camera as the figure - a long rectangle in
profile, a parallelogram mid-orbit, end-on when face-on. Both renderers
in lockstep; fixtures unaffected (the mat is a pure addition).
Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
The default camera pitches down 10 degrees, so the floor reads as a
plane (drawn as a rectangle) and near/far contacts straddle it.
Elevation is pure presentation - IK pins solve in the flat authored
view and the posed body tilts, the same pattern as the orbit, so
authored canvas targets never go out of reach. The leg-extension
roller moves up onto the shin above the ankle and the leg-curl roller
tucks under the heel. Fixtures and reference test values regenerated
for the pitched camera.
Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
Shoulder and pelvis widths grow to human-like proportions per profile
(shoulders wider than hips for neutral/male, reversed for female) and
are now drawn — bars across the attach points that read near-full-width
face-on and as a shoulder/hip nub in profile, so limbs visibly hang
from a torso instead of a point. Orbiting no longer re-solves IK pins
in the rotated view (pins are canvas targets in the authored camera):
the pose resolves first and the posed body rotates, which fixes hands
sticking to stale screen points mid-orbit (Cat-Cow, Bird Dog, Plank).
Leg Extension and Leg Curl swap their ankle bars for a machine roller
disc — a new `roller` prop riding the shin's press side. Fixtures
regenerated; both renderers updated in lockstep.
Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
The library's planar world-angle rig becomes a genuine 3D anatomical
model: skeleton.json holds bone-length profiles (real shoulder/pelvis
widths, feet, neutral/female/male) and per-joint ROM; motions pose
joints with anatomical angles (flexion/abduction/rotation from neutral
standing) under a per-exercise orthographic camera, resolved by
kinematics.py (3D FK, analytic two-bone IK with anatomical write-back)
and validated against physiological ranges. All 20 sagittal motions
were migrated by planar decomposition with 0.00 px golden parity against
the old renderer — relabeled to true anatomy, since shading is now
near-dark/far-light by camera depth rather than by limb suffix — and
the face-on machines are re-authored honestly: Abductor/Adductor with
real hip abduction (the foreshortened "frontal" profile is retired) and
Rotary with genuine spine axial rotation. Figures gain articulated
feet; profiles swap without touching a single motion script; --orbit
sweeps the camera 360° while a motion loops.
The in-app SwiftUI renderer (iOS + watch) is ported to the same model
and consumes the exported motions verbatim; figure-fixtures.json pins
its geometry to the Python pipeline within 0.5 px across every
exercise, key frame, tween, and orbit sample. Also makes the watch
bridge logger nonisolated for the newer SDK's stricter isolation
checking.
Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
Fourteen Planet Fitness machine exercises join the rig library, each with
authored motion, info page, and schematic equipment via the new props layer
(scene shapes, cables, bars, pads) rendered in lockstep by render.py and the
in-app figure renderer. Abductor and Adductor are authored face-on with the
real seated machine motion — knees-bent legs swinging apart/together against
knee pads — replacing the earlier middle-split depiction. The watch target
now bundles the figure renderer and motion rigs too.
Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7