Draw both arms on the supine motions and retire the hide list

Hollow Body Hold, Leg Raises, and Reverse Crunch hid the far arm
because it was never authored - a leftover from the planar rig. Both
arms are now posed, so the far arm reads as the standard light member
behind the near one, the same visual language every other exercise
uses, and the figures stay truthful from any viewpoint. With no
remaining users, the hide mechanism is deleted from both renderers,
the motion schema, and the docs.

Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
This commit is contained in:
2026-07-06 22:19:23 -04:00
parent 81186c51b1
commit b8c3b326c1
25 changed files with 35 additions and 45 deletions
@@ -1,6 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180" fill="none">
<title>Hollow Body Hold</title>
<path d="M 28.0 161.1 L 288.9 161.1 L 288.9 150.7 L 28.0 150.7 Z" fill="none" stroke="#b9bec9" stroke-width="3" stroke-linejoin="round"/>
<path id="arm_l" d="M 105.9 148.1 L 75.9 148.1 L 45.9 148.1" stroke="#a9afba" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_l" d="M 190.9 148.8 L 236.9 148.8 L 276.9 148.8 L 276.9 137.9" stroke="#a9afba" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="girdle" d="M 105.9 148.1 L 100.0 148.0 L 100.0 149.9" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="pelvisBar" d="M 190.9 148.8 L 185.0 148.0 L 185.0 149.2" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -1,6 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180" fill="none">
<title>Hollow Body Hold</title>
<path d="M 28.0 161.1 L 288.9 161.1 L 288.9 150.7 L 28.0 150.7 Z" fill="none" stroke="#b9bec9" stroke-width="3" stroke-linejoin="round"/>
<path id="arm_l" d="M 108.7 129.5 L 81.5 117.0 L 54.3 104.5" stroke="#a9afba" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_l" d="M 190.9 148.8 L 235.9 139.3 L 275.0 131.1 L 272.7 120.5" stroke="#a9afba" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="girdle" d="M 108.7 129.5 L 102.8 129.4 L 102.8 131.3" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="pelvisBar" d="M 190.9 148.8 L 185.0 148.0 L 185.0 149.2" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -2,7 +2,6 @@
"name": "Hollow Body Hold",
"primary": 2,
"working": ["spine"],
"hide": ["arm_l"],
"frames": [
{
"hold": 0.4, "tween": 0.8,
@@ -10,6 +9,7 @@
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": 180, "elbow_r": 0,
"shoulder_l": 180, "elbow_l": 0,
"hip_r": 0, "knee_r": 0,
"hip_l": 0, "knee_l": 0
},
@@ -19,6 +19,7 @@
"spine": [0, 14],
"neck": 20,
"shoulder_r": 175, "elbow_r": 0,
"shoulder_l": 175, "elbow_l": 0,
"hip_r": 18, "knee_r": 0,
"hip_l": 18, "knee_l": 0
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 124 KiB

@@ -1,6 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180" fill="none">
<title>Hollow Body Hold</title>
<path d="M 28.0 161.1 L 288.9 161.1 L 288.9 150.7 L 28.0 150.7 Z" fill="none" stroke="#b9bec9" stroke-width="3" stroke-linejoin="round"/>
<path id="arm_l" d="M 108.7 129.5 L 81.5 117.0 L 54.3 104.5" stroke="#a9afba" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_l" d="M 190.9 148.8 L 235.9 139.3 L 275.0 131.1 L 272.7 120.5" stroke="#a9afba" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="girdle" d="M 108.7 129.5 L 102.8 129.4 L 102.8 131.3" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="pelvisBar" d="M 190.9 148.8 L 185.0 148.0 L 185.0 149.2" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -6,6 +6,7 @@
<path id="pelvisBar" d="M 190.9 148.8 L 185.0 148.0 L 185.0 149.2" stroke="#3a3f4b" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="spine" d="M 185.0 148.0 Q 141.5 148.0 100.0 148.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_l" d="M 190.9 148.8 L 213.9 109.5 L 233.9 75.4 L 224.4 70.0" stroke="#86cfc5" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="arm_r" d="M 100.0 149.9 L 130.0 150.9 L 160.0 152.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_r" d="M 185.0 149.2 L 208.0 110.0 L 228.0 75.9 L 218.5 70.5" stroke="#0d9488" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<circle id="head" cx="80.4" cy="143.9" r="10" fill="white" stroke="#3a3f4b" stroke-width="6"/>
<line id="nose" x1="80.4" y1="133.9" x2="80.4" y2="127.0" stroke="#3a3f4b" stroke-width="4" stroke-linecap="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -7,6 +7,7 @@
<path id="pelvisBar" d="M 190.9 148.8 L 185.0 148.0 L 185.0 149.2" stroke="#3a3f4b" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="spine" d="M 185.0 148.0 Q 141.5 148.0 100.0 148.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_r" d="M 185.0 149.2 L 230.6 142.9 L 270.2 137.4 L 268.6 126.7" stroke="#0d9488" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="arm_r" d="M 100.0 149.9 L 130.0 150.9 L 160.0 152.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<circle id="head" cx="80.4" cy="143.9" r="10" fill="white" stroke="#3a3f4b" stroke-width="6"/>
<line id="nose" x1="80.4" y1="133.9" x2="80.4" y2="127.0" stroke="#3a3f4b" stroke-width="4" stroke-linecap="round"/>
<g font-family="-apple-system, Helvetica, sans-serif" font-size="11" fill="#6b7180">

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+2 -1
View File
@@ -2,13 +2,13 @@
"name": "Leg Raises",
"primary": 1,
"working": ["leg_r", "leg_l"],
"hide": ["arm_r"],
"frames": [
{
"hold": 0.5, "tween": 1.4,
"root": {"pos": [185, 148], "pitch": -90},
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": -2, "elbow_r": 0,
"shoulder_l": -2, "elbow_l": 0,
"hip_r": 60, "knee_r": 0,
"hip_l": 60, "knee_l": 0
@@ -18,6 +18,7 @@
"root": {"pos": [185, 148], "pitch": -90},
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": -2, "elbow_r": 0,
"shoulder_l": -2, "elbow_l": 0,
"hip_r": 8, "knee_r": 0,
"hip_l": 8, "knee_l": 0
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 111 KiB

+1
View File
@@ -6,6 +6,7 @@
<path id="pelvisBar" d="M 190.9 148.8 L 185.0 148.0 L 185.0 149.2" stroke="#3a3f4b" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="spine" d="M 185.0 148.0 Q 141.5 148.0 100.0 148.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_l" d="M 190.9 148.8 L 213.9 109.5 L 233.9 75.4 L 224.4 70.0" stroke="#86cfc5" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="arm_r" d="M 100.0 149.9 L 130.0 150.9 L 160.0 152.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_r" d="M 185.0 149.2 L 208.0 110.0 L 228.0 75.9 L 218.5 70.5" stroke="#0d9488" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<circle id="head" cx="80.4" cy="143.9" r="10" fill="white" stroke="#3a3f4b" stroke-width="6"/>
<line id="nose" x1="80.4" y1="133.9" x2="80.4" y2="127.0" stroke="#3a3f4b" stroke-width="4" stroke-linecap="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -6,6 +6,7 @@
<path id="girdle" d="M 92.9 148.1 L 87.0 148.0 L 87.0 149.9" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="pelvisBar" d="M 177.9 148.8 L 172.0 148.0 L 172.0 149.2" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="spine" d="M 172.0 148.0 Q 128.5 148.0 87.0 148.0" stroke="#0d9488" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="arm_r" d="M 87.0 149.9 L 117.0 150.9 L 147.0 152.0" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_r" d="M 172.0 149.2 L 164.0 104.6 L 201.6 118.1 L 205.4 107.9" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<circle id="head" cx="67.4" cy="143.9" r="10" fill="white" stroke="#3a3f4b" stroke-width="6"/>
<line id="nose" x1="67.4" y1="133.9" x2="67.4" y2="127.0" stroke="#3a3f4b" stroke-width="4" stroke-linecap="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -6,6 +6,7 @@
<path id="girdle" d="M 88.5 143.4 L 82.6 143.4 L 82.6 145.3" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="pelvisBar" d="M 171.9 136.8 L 166.0 136.0 L 166.0 137.2" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="spine" d="M 166.0 136.0 Q 124.6 154.2 82.6 143.4" stroke="#0d9488" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="arm_r" d="M 82.6 145.3 L 112.6 146.3 L 142.6 147.3" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_r" d="M 166.0 137.2 L 146.6 96.2 L 186.0 103.0 L 187.9 92.3" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<circle id="head" cx="62.6" cy="144.0" r="10" fill="white" stroke="#3a3f4b" stroke-width="6"/>
<line id="nose" x1="62.6" y1="134.0" x2="62.6" y2="127.1" stroke="#3a3f4b" stroke-width="4" stroke-linecap="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+2 -1
View File
@@ -2,13 +2,13 @@
"name": "Reverse Crunch",
"primary": 2,
"working": ["spine"],
"hide": ["arm_r"],
"frames": [
{
"hold": 0.5, "tween": 0.7,
"root": {"pos": [172, 148], "pitch": -90},
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": -2, "elbow_r": 0,
"shoulder_l": -2, "elbow_l": 0,
"hip_r": 100, "knee_r": 120,
"hip_l": 100, "knee_l": 120
@@ -18,6 +18,7 @@
"root": {"pos": [166, 136], "pitch": -105},
"spine": [0, 20],
"neck": -7, "head": 2,
"shoulder_r": 3, "elbow_r": 0,
"shoulder_l": 3, "elbow_l": 0,
"hip_r": 100, "knee_r": 125,
"hip_l": 100, "knee_l": 125
Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 128 KiB

@@ -6,6 +6,7 @@
<path id="girdle" d="M 88.5 143.4 L 82.6 143.4 L 82.6 145.3" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="pelvisBar" d="M 171.9 136.8 L 166.0 136.0 L 166.0 137.2" stroke="#0d9488" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="spine" d="M 166.0 136.0 Q 124.6 154.2 82.6 143.4" stroke="#0d9488" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="arm_r" d="M 82.6 145.3 L 112.6 146.3 L 142.6 147.3" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path id="leg_r" d="M 166.0 137.2 L 146.6 96.2 L 186.0 103.0 L 187.9 92.3" stroke="#3a3f4b" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<circle id="head" cx="62.6" cy="144.0" r="10" fill="white" stroke="#3a3f4b" stroke-width="6"/>
<line id="nose" x1="62.6" y1="134.0" x2="62.6" y2="127.1" stroke="#3a3f4b" stroke-width="4" stroke-linecap="round"/>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+4 -5
View File
@@ -45,7 +45,6 @@ never touches a motion script** — proportions are the skeleton's problem.
"primary": 2, // 1-based frame used for visual.svg
"camera": {"yaw": 0}, // 0 = side view (default), 90 = face-on
"working": ["arm_l", "leg_r"], // parts drawn in the accent color
"hide": [], // limbs occluded by convention, not drawn
"frames": [
{
"hold": 0.5, "tween": 0.8, // seconds held / animating to the next frame
@@ -115,8 +114,9 @@ never touches a motion script** — proportions are the skeleton's problem.
shoulder girdle and pelvis drawn as bars across the attach points (near-full
width face-on, a shoulder/hip nub in profile); teal when the trunk is the
working part.
- Canvas 320×180, ground line at y = 152. Limbs listed in `hide` are
occluded by convention in this view and not drawn.
- Canvas 320×180, ground line at y = 152. Every limb is always drawn — a
supine figure's far arm reads as the light far member, never a hidden one,
so any viewpoint (and the orbit) stays truthful.
## The props layer
@@ -172,8 +172,7 @@ authored yaw nothing moves, so the authored look is exact.
and `pad` default to perpendicular to the lower bone (forearm/shin), or
take an explicit `angle`. Under orbit the segment rotates with the scene
and foreshortens naturally. `plateR` puts filled discs on both ends
(dumbbells default to 4.5). A prop whose limb is hidden that frame simply
isn't drawn.
(dumbbells default to 4.5).
- **`roller`** — a machine roller pad seen end-on: a filled disc riding the
limb's lower bone near the joint in `at`, on the `side` (+1/1) of the bone
it presses — a leg extension's instep roller (`side: 1`), a leg curl's
Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 303 KiB

+2 -14
View File
@@ -602,23 +602,18 @@ def prepare(motion, figure="neutral", flip=False, strict=False):
def render_exercise(folder, figure="neutral", flip=False, strict=False):
motion = load_motion(folder)
working = set(motion.get("working", []))
hide = set(motion.get("hide", []))
norms, prof, cam, pitch, props = prepare(motion, figure, flip, strict)
mat = mat_bounds(norms, prof, cam, pitch)
def geometry(nf):
_, geo, order, shade = frame_geometry(nf, prof, cam, flip, pitch, mat)
for limb in hide:
geo.pop(limb, None)
return geo, order, shade
resolved = []
key_cells = []
for nf in norms:
out, geo, order, shade = frame_geometry(nf, prof, cam, flip, pitch, mat)
for limb in hide:
geo.pop(limb, None)
resolved.append(out)
key_cells.append((geo, order, shade, resolve_props(props, geo, nf["root"]["pos"])))
@@ -653,7 +648,6 @@ def render_orbit(folder, figure="neutral"):
motion authors one). Props rotate with the figure about the root anchor."""
motion = load_motion(folder)
working = set(motion.get("working", []))
hide = set(motion.get("hide", []))
norms, prof, cam, pitch, props = prepare(motion, figure)
resolved = [frame_geometry(nf, prof, cam, pitch=pitch)[0] for nf in norms]
mat = mat_bounds(norms, prof, cam, pitch)
@@ -671,8 +665,6 @@ def render_orbit(folder, figure="neutral"):
posed, auth_geo, _, _ = frame_geometry(nf, prof, cam, pitch=pitch)
posed["pins"] = {}
_, geo, order, shade = frame_geometry(posed, prof, cam + off, pitch=pitch, mat=mat)
for limb in hide:
geo.pop(limb, None)
prims = resolve_props(props, geo, nf["root"]["pos"],
prop_rotation(pitch, off), auth_geo)
imgs.append(draw_geo(geo, order, shade, working, colors, font=font, prims=prims))
@@ -686,13 +678,11 @@ def contact_sheet(folders, figure="neutral", out=None):
cells = []
for folder in folders:
motion = load_motion(folder)
working, hide = set(motion.get("working", [])), set(motion.get("hide", []))
working = set(motion.get("working", []))
norms, prof, cam, pitch, props = prepare(motion, figure)
mat = mat_bounds(norms, prof, cam, pitch)
for i, nf in enumerate(norms, start=1):
_, geo, order, shade = frame_geometry(nf, prof, cam, pitch=pitch, mat=mat)
for limb in hide:
geo.pop(limb, None)
prims = resolve_props(props, geo, nf["root"]["pos"])
cells.append((f"{motion['name']} {i}/{len(norms)}",
draw_geo(geo, order, shade, working, PALETTES["default"],
@@ -705,7 +695,7 @@ def demo_sheet(folder):
opens: neutral / female / male profiles (same motion script, different
proportions), the flipped camera, an alternate theme."""
motion = load_motion(folder)
working, hide = set(motion.get("working", [])), set(motion.get("hide", []))
working = set(motion.get("working", []))
idx = motion.get("primary", 1) - 1
font = legend_font()
variants = [("neutral", "neutral", False, "default"),
@@ -718,8 +708,6 @@ def demo_sheet(folder):
norms, prof, cam, pitch, props = prepare(motion, figure, flip)
mat = mat_bounds(norms, prof, cam, pitch)
_, geo, order, shade = frame_geometry(norms[idx], prof, cam, flip, pitch, mat)
for limb in hide:
geo.pop(limb, None)
prims = resolve_props(props, geo, norms[idx]["root"]["pos"])
cells.append((f"{motion['name']}{label}",
draw_geo(geo, order, shade, working, PALETTES[palette],
@@ -22,8 +22,6 @@ struct FigureAnimation {
let timeline: MotionTimeline
/// Parts drawn in the working accent color (`arm_r`, `leg_l`, `spine`, ).
let working: Set<String>
/// Limbs fully occluded in this view never drawn.
let hide: Set<String>
/// Equipment layer (see SYSTEM.md "The props layer").
let props: [MotionProp]
@@ -34,28 +32,20 @@ struct FigureAnimation {
else { return nil }
self.timeline = timeline
self.working = Set(resources.motion.working ?? [])
self.hide = Set(resources.motion.hide ?? [])
self.props = resources.motion.props ?? []
}
/// Seconds per full camera revolution for orbiting motions.
private static let orbitPeriod: Double = 24
/// Prop-free, hide-free motions slowly orbit the camera while animating the
/// bodyweight moves. Scene equipment is a view-locked billboard and a `hide` list
/// describes one authored viewpoint, so machines keep their fixed camera.
var orbits: Bool { props.isEmpty && hide.isEmpty }
/// Prop-free motions slowly orbit the camera while animating the bodyweight
/// moves. Machines keep their fixed camera until their equipment rotates too.
var orbits: Bool { props.isEmpty }
/// Drawable geometry at `time` the figure plus the resolved equipment layers
/// with limbs listed in `hide` dropped so neither they nor any prop riding them
/// are drawn.
/// Drawable geometry at `time` the figure plus the resolved equipment layers.
func geometry(at time: Double) -> FigureGeometry {
let yaw = orbits ? 360 * (time / Self.orbitPeriod) : 0
var geo = timeline.geometry(at: time, yawOffset: yaw, props: props)
for name in hide {
if let limb = FigureLimb(rawValue: name) { geo.limbs[limb] = nil }
}
return geo
return timeline.geometry(at: time, yawOffset: yaw, props: props)
}
}
@@ -235,8 +235,6 @@ struct ExerciseMotion: Codable {
let camera: MotionCamera?
/// Parts (`arm_r`, `leg_l`, `spine`, ) drawn in the working accent color.
let working: [String]?
/// Limbs fully occluded in this view never drawn.
let hide: [String]?
/// Equipment layer: scene shapes and cables behind the figure, joint-attached
/// items (bar/dumbbell/pad) over the limbs. See SYSTEM.md "The props layer".
let props: [MotionProp]?
@@ -2,7 +2,6 @@
"name": "Hollow Body Hold",
"primary": 2,
"working": ["spine"],
"hide": ["arm_l"],
"frames": [
{
"hold": 0.4, "tween": 0.8,
@@ -10,6 +9,7 @@
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": 180, "elbow_r": 0,
"shoulder_l": 180, "elbow_l": 0,
"hip_r": 0, "knee_r": 0,
"hip_l": 0, "knee_l": 0
},
@@ -19,6 +19,7 @@
"spine": [0, 14],
"neck": 20,
"shoulder_r": 175, "elbow_r": 0,
"shoulder_l": 175, "elbow_l": 0,
"hip_r": 18, "knee_r": 0,
"hip_l": 18, "knee_l": 0
}
@@ -2,13 +2,13 @@
"name": "Leg Raises",
"primary": 1,
"working": ["leg_r", "leg_l"],
"hide": ["arm_r"],
"frames": [
{
"hold": 0.5, "tween": 1.4,
"root": {"pos": [185, 148], "pitch": -90},
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": -2, "elbow_r": 0,
"shoulder_l": -2, "elbow_l": 0,
"hip_r": 60, "knee_r": 0,
"hip_l": 60, "knee_l": 0
@@ -18,6 +18,7 @@
"root": {"pos": [185, 148], "pitch": -90},
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": -2, "elbow_r": 0,
"shoulder_l": -2, "elbow_l": 0,
"hip_r": 8, "knee_r": 0,
"hip_l": 8, "knee_l": 0
@@ -2,13 +2,13 @@
"name": "Reverse Crunch",
"primary": 2,
"working": ["spine"],
"hide": ["arm_r"],
"frames": [
{
"hold": 0.5, "tween": 0.7,
"root": {"pos": [172, 148], "pitch": -90},
"spine": [0, 0],
"neck": 12, "head": -12,
"shoulder_r": -2, "elbow_r": 0,
"shoulder_l": -2, "elbow_l": 0,
"hip_r": 100, "knee_r": 120,
"hip_l": 100, "knee_l": 120
@@ -18,6 +18,7 @@
"root": {"pos": [166, 136], "pitch": -105},
"spine": [0, 20],
"neck": -7, "head": 2,
"shoulder_r": 3, "elbow_r": 0,
"shoulder_l": 3, "elbow_l": 0,
"hip_r": 100, "knee_r": 125,
"hip_l": 100, "knee_l": 125
+2 -2
View File
@@ -80,8 +80,8 @@ struct ExerciseMotionTests {
#expect(hypot(releasedHand.x - releasedHand0.x, releasedHand.y - releasedHand0.y) > 1)
}
/// Prop-free bodyweight motions slowly orbit the camera while looping; motions with
/// equipment (view-locked scene shapes) or a hide list keep their authored view.
/// Prop-free bodyweight motions slowly orbit the camera while looping; motions
/// with equipment keep their authored view (until machines orbit too).
@Test func orbitAppliesOnlyToPropFreeMotions() throws {
let birdDog = try #require(FigureAnimation(exerciseName: "Bird Dog"))
#expect(birdDog.orbits)
File diff suppressed because one or more lines are too long