Skip to content

Commit 9f65ee1

Browse files
Add front box prop + box step-taps (#2)
- props.ts: new `prop box` — a low platform placed IN FRONT of the figure with a `box` top anchor (chair/wall sit behind, bar overhead, box in front). - box-step-taps.movit: alternating foot taps onto the box top. Verified both feet land on the anchor (0.11 m) through the real rig. - vocab/SPEC: register the `box` prop. - coverage-gap.md: record why a true step-up and triceps dips stay deferred — they need the body to translate vertically (rise onto box / lower into dip), and the rig has no authorable root-height channel yet. Reframed step-up as step-taps. 136 tests pass; build green. Claude-Session: https://claude.ai/code/session_018WCktDrKYZpJjCb2apbqWp Co-authored-by: Claude <noreply@anthropic.com>
1 parent 49e0927 commit 9f65ee1

6 files changed

Lines changed: 51 additions & 9 deletions

File tree

docs/coverage-gap.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,15 @@ Ordered by gap size × how cleanly the engine renders it:
5151
These ~10 movements roughly **double our chest/core/back coverage** — all on
5252
today's engine, no new primitives required.
5353

54-
**Deferred (need a front-facing prop we don't have):** `step-up` and
55-
`triceps-dips`. Our only box/bench prop (the chair) is placed *behind* the figure
56-
for sit-back moves, so a step-up's lead foot and a dip's hands land in empty space.
57-
These return once we add a front-placed box / dip-station prop (and anchor-aware
58-
ground-lock) — a clean roadmap item rather than a forced, low-fidelity render.
54+
**Front box prop (added):** a `prop box` placed in front of the figure now powers
55+
`box-step-taps` (the lead foot taps the box top — verified landing on the anchor).
56+
57+
**Still deferred:** a true `step-up` and `triceps-dips`. Beyond prop placement,
58+
both need the figure's whole body to **translate vertically** (rise onto the box /
59+
lower into the dip), and the rig has no authorable root-height channel yet — the
60+
pelvis is fixed and only ground-lock nudges it. So a step-up reads as a step-*tap*
61+
and a dip can't lower. These return with a root-translation primitive (a `lift` /
62+
body-height channel) — a clean roadmap item, not a forced low-fidelity render.
5963

6064
## Metadata gap (drives the catalogue work)
6165

packages/movit-language/src/vocab.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const EFFECTORS = ["hands", "feet"];
1919

2020
/** Reach effectors (friendly aliases) and the scene props that supply anchors. */
2121
export const REACH_EFFECTORS = ["hand_left", "hand_right", "foot_left", "foot_right"];
22-
export const PROPS = ["chair", "wall", "bar"];
22+
export const PROPS = ["chair", "wall", "bar", "box"];
2323

2424
/** Top-level directives (excluding the `movit` header keyword). */
2525
export const TOP_KEYWORDS = ["rig", "prop", "pose", "step", "repeat"];
@@ -31,7 +31,7 @@ export const CHILD_KEYWORDS = ["ground-lock", "reach", "cue"];
3131
export const KEYWORD_DOCS: Record<string, string> = {
3232
movit: 'Document header — `movit <kind> "<name>"`.',
3333
rig: "Selects the rig (currently `humanoid`).",
34-
prop: "Adds a scene object — `prop chair | wall | bar`. Supplies reach anchors.",
34+
prop: "Adds a scene object — `prop chair | wall | bar | box`. Supplies reach anchors.",
3535
pose: "Sets the starting pose — `pose start = standing | neutral | plank | supine | prone | seated`.",
3636
start: "Used in `pose start = <pose>`.",
3737
step: 'A movement phase — `step "<name>" <Ns> <easing>:`.',

packages/movit-render/src/props.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ export function buildProps(types: string[], material?: THREE.Material): PropScen
5353
wall.position.set(0, 1.3, -0.34);
5454
group.add(wall);
5555
anchors.set("wall", new THREE.Vector3(0, 0.9, -0.29));
56+
} else if (type === "box") {
57+
// A low step/plateau placed IN FRONT of the figure (+Z) — the lead foot
58+
// steps forward and up onto it. Top surface at ~0.30 m; `box` anchor sits
59+
// on top where the foot lands.
60+
const topH = 0.3;
61+
const plat = box(0.5, topH, 0.42, mat);
62+
plat.position.set(0, topH / 2, 0.32);
63+
group.add(plat);
64+
anchors.set("box", new THREE.Vector3(0, topH, 0.3));
5665
}
5766
}
5867

playground/src/presets.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import forwardLunge from "../../spec/examples/forward-lunge.movit?raw";
8383
import calfRaise from "../../spec/examples/single-leg-calf-raise.movit?raw";
8484
import jumpingJacks from "../../spec/examples/jumping-jacks.movit?raw";
8585
import quadStretch from "../../spec/examples/standing-quad-stretch.movit?raw";
86+
import boxStepTaps from "../../spec/examples/box-step-taps.movit?raw";
8687

8788
export type Difficulty = "Beginner" | "Intermediate" | "Advanced";
8889

@@ -124,6 +125,7 @@ export const PRESETS: Preset[] = [
124125
{ id: "forward-lunge", label: "Forward lunge", domain: "Fitness", bodyPart: "Upper legs", target: "Quadriceps", equipment: "Body weight", difficulty: "Intermediate", source: forwardLunge },
125126
{ id: "calf-raise", label: "Single-leg calf raise", domain: "Fitness", bodyPart: "Lower legs", target: "Calves", equipment: "Body weight", difficulty: "Intermediate", source: calfRaise },
126127
{ id: "jumping-jacks", label: "Jumping jacks", domain: "Warm-up", bodyPart: "Full body", target: "Full body", equipment: "Body weight", difficulty: "Beginner", source: jumpingJacks },
128+
{ id: "box-step-taps", label: "Box step taps", domain: "Warm-up", bodyPart: "Upper legs", target: "Hip flexors", equipment: "Box", difficulty: "Beginner", source: boxStepTaps },
127129
{ id: "quad-stretch", label: "Standing quad stretch", domain: "Mobility", bodyPart: "Upper legs", target: "Quadriceps", equipment: "Body weight", difficulty: "Beginner", source: quadStretch },
128130

129131
// --- Education / anatomy: single-joint ROM demos ---

spec/SPEC.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,9 @@ research §5.1 normative tables. Selected ceilings (degrees):
126126
foot_left|foot_right`) to a world **target** via Cyclic Coordinate Descent
127127
(CCD) over the arm/leg chain. A target is a body landmark bone (e.g.
128128
`ankle_left`), the keyword `floor`, or a prop anchor (`bar`, `seat`, `wall`).
129-
5. **Props**`prop chair|wall|bar` adds a scene object at a fixed default
130-
placement; its named anchors become reach targets.
129+
5. **Props**`prop chair|wall|bar|box` adds a scene object at a fixed default
130+
placement (chair/wall behind, bar overhead, box in front); its named anchors
131+
become reach targets.
131132
6. **Looping** — the timeline loops base → phases → base; `repeat` is the rep
132133
count surfaced to the UI.
133134

spec/examples/box-step-taps.movit

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
movit exercise "Box step taps"
2+
rig humanoid
3+
prop box
4+
pose start = standing
5+
6+
step "Right tap" 0.6s ease-out:
7+
hip_right: flex 70
8+
knee_right: flex 90
9+
cue "Tap the right foot lightly on top of the box"
10+
11+
step "Right down" 0.5s ease-in:
12+
hip_right: flex 0
13+
knee_right: flex 0
14+
cue "Return the right foot to the floor"
15+
16+
step "Left tap" 0.6s ease-out:
17+
hip_left: flex 70
18+
knee_left: flex 90
19+
cue "Tap the left foot on the box"
20+
21+
step "Left down" 0.5s ease-in:
22+
hip_left: flex 0
23+
knee_left: flex 0
24+
cue "Back to the floor — keep a light, quick rhythm"
25+
26+
repeat 6

0 commit comments

Comments
 (0)