Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2b25abc
docs: L2 spline-quaternion interpolation design spec
a-baran-orhan Jul 11, 2026
e46679a
docs: L2 implementation plan
a-baran-orhan Jul 11, 2026
c323b01
feat(render): add squad quaternion-spline helper
a-baran-orhan Jul 11, 2026
f69ccf0
feat(parser): timing modes with legacy easing aliases
a-baran-orhan Jul 11, 2026
c7a077b
feat(render): squad spline sampling with per-phase timing modes
a-baran-orhan Jul 11, 2026
7aa9654
feat(language): editor support for timing modes + deprecation hints
a-baran-orhan Jul 11, 2026
1caacc9
chore(eval): use TimingMode type for phase timing
a-baran-orhan Jul 11, 2026
6576d90
feat: demo flow/settle/drive timing on dance-phrase and squat (L2)
a-baran-orhan Jul 11, 2026
be105f0
docs: L3.1 foot-flat correction design spec
a-baran-orhan Jul 11, 2026
d7bae8e
docs: L3.1 foot-flat implementation plan
a-baran-orhan Jul 11, 2026
be3354f
feat(render): levelPlantedFeet — plantigrade foot-flat correction
a-baran-orhan Jul 11, 2026
8f855e4
feat(render): apply foot-flat in the viewer frame loop and on load
a-baran-orhan Jul 11, 2026
1809310
feat: squat rests flat via foot-flat; document ground-lock leveling (…
a-baran-orhan Jul 11, 2026
a588e23
Switch playground and content links to clean /play routes
a-baran-orhan Jul 11, 2026
cd5804c
docs: L3.2 bar-grip system design spec
a-baran-orhan Jul 11, 2026
14793b6
feat(parser): grip directive with two-point side-anchor resolution
a-baran-orhan Jul 11, 2026
9a8f6da
feat(render): bar grip solve — two-point anchors, arm IK, finger wrap
a-baran-orhan Jul 11, 2026
2df04f1
feat(language): editor support for the grip directive
a-baran-orhan Jul 11, 2026
ffec912
feat: pull-up/dead-hang/hanging-knee-raise grip the bar via grip dire…
a-baran-orhan Jul 11, 2026
ce3f90e
docs: L4 secondary-motion design spec (L4.1 relaxed hands)
a-baran-orhan Jul 11, 2026
7734730
feat(render): relaxed resting hand pose (L4.1 secondary motion)
a-baran-orhan Jul 11, 2026
27e0c82
feat(render): contralateral arm swing during locomotion (L4.2)
a-baran-orhan Jul 11, 2026
ae383c7
Merge origin/main into feat/l3-post-ik
a-baran-orhan Jul 11, 2026
618c745
Reconcile merge: keep L2-L4 animation solvers over main's overlapping…
a-baran-orhan Jul 11, 2026
b634ba1
feat(render): look-at head tracking toward active contacts (L4.3)
a-baran-orhan Jul 11, 2026
e89f576
feat(L1): wire 6 Mixamo mocap clips; gitignore FBX binaries; opt-in m…
a-baran-orhan Jul 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ coverage/
.vite/
.vercel
.claude/worktrees/

# Mocap clip binaries: sourced from Mixamo, loaded from storage/CDN, not committed.
playground/public/clips/*.fbx
playground/public/clips/*.glb
playground/public/clips/.DS_Store
813 changes: 813 additions & 0 deletions docs/superpowers/plans/2026-07-11-l2-spline-interpolation.md

Large diffs are not rendered by default.

308 changes: 308 additions & 0 deletions docs/superpowers/plans/2026-07-11-l3-1-foot-flat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
# L3.1 — Foot-Flat Correction Implementation Plan

> **For agentic workers:** Use superpowers:executing-plans to implement task-by-task. Steps use checkbox (`- [ ]`) syntax.

**Goal:** Keep planted soles level with the floor so grounded lower-body moves (squat, lunge, deadlift) rest flat instead of balancing on the toes, without disturbing authored angles, tiptoe moves, or swing feet.

**Architecture:** A new `levelPlantedFeet(m, activeGroundLock)` in `contacts.ts` rotates each ground-locked ankle so its sole normal (local `-Y`) points world-down, weighted by planted-ness and skipped on authored plantarflex. Wired into the viewer `frame()` (before the floor clamp) and `load()`.

**Tech Stack:** TypeScript ESM (`.js` specifiers), Three.js, Vitest.

## Global Constraints

- Immutability: never mutate shared keyframe quaternions; write bone quaternions in place only (matches `alignFloorPalms`).
- `plantarflex` = ankle local Euler **+X** (toe-down); `dorsiflex` = **−X**. Sole-down normal = ankle local `(0,−1,0)`.
- Constants named + exported for tests: `PLANT_FADE = 0.06` (m), `PLANTARFLEX_SKIP = 15 * DEG` (rad).
- Reuse the `alignFloorPalms` idiom (`getWorldQuaternion` → `setFromUnitVectors(current, DOWN)` → back to local via parent inverse).
- Clamp the corrected ankle to its ROM (`eulerRomFor("ankle_left"/"ankle_right")`), widened to admit the authored angle.
- TDD; keep every existing suite green; typecheck clean.
- Test a file: `npx vitest run <path>` from repo root.

---

### Task 1: `levelPlantedFeet` in contacts.ts

**Files:**
- Modify: `packages/posecode-render/src/contacts.ts`
- Test: `packages/posecode-render/test/contacts.test.ts` (create if absent)

**Interfaces:**
- Produces: `levelPlantedFeet(m: Mannequin, activeGroundLock: readonly string[]): void`, plus exported consts `PLANT_FADE`, `PLANTARFLEX_SKIP`.

- [ ] **Step 1: Write the failing tests**

```ts
// packages/posecode-render/test/contacts.test.ts
import { describe, it, expect } from "vitest";
import * as THREE from "three";
import { buildMannequin } from "../src/mannequin.js";
import { levelPlantedFeet } from "../src/contacts.js";

const DEG = Math.PI / 180;

/** World-space sole normal (ankle local -Y) for a foot. */
function soleNormal(m: ReturnType<typeof buildMannequin>, side: "left" | "right") {
const ankle = m.bones.get(`ankle_${side}`)!;
const q = ankle.getWorldQuaternion(new THREE.Quaternion());
return new THREE.Vector3(0, -1, 0).applyQuaternion(q).normalize();
}

describe("levelPlantedFeet", () => {
it("levels a tilted planted foot so the sole faces down", () => {
const m = buildMannequin();
// Tilt the whole leg forward by rotating the knee so the foot pitches.
m.bones.get("knee_left")!.rotation.x = 40 * DEG;
m.root.updateMatrixWorld(true);
levelPlantedFeet(m, ["feet"]);
m.root.updateMatrixWorld(true);
const n = soleNormal(m, "left");
// sole normal points world-down (0,-1,0): dot with DOWN ~ 1
expect(n.dot(new THREE.Vector3(0, -1, 0))).toBeGreaterThan(0.98);
});

it("leaves an authored-plantarflex foot on its toes", () => {
const m = buildMannequin();
m.bones.get("ankle_left")!.rotation.x = 30 * DEG; // plantarflex (toe-down)
m.root.updateMatrixWorld(true);
const before = m.bones.get("ankle_left")!.quaternion.clone();
levelPlantedFeet(m, ["feet"]);
expect(m.bones.get("ankle_left")!.quaternion.angleTo(before)).toBeLessThan(1e-6);
});

it("does not touch a swing foot lifted off the floor", () => {
const m = buildMannequin();
// Lift the foot well above the floor by bending the knee back and raising hip.
m.bones.get("hip_left")!.rotation.x = -60 * DEG;
m.root.position.y = 0.5;
m.root.updateMatrixWorld(true);
const before = m.bones.get("ankle_left")!.quaternion.clone();
levelPlantedFeet(m, ["feet"]);
expect(m.bones.get("ankle_left")!.quaternion.angleTo(before)).toBeLessThan(1e-3);
});
});
```

- [ ] **Step 2: Run to verify failure**

Run: `npx vitest run packages/posecode-render/test/contacts.test.ts`
Expected: FAIL — `levelPlantedFeet` not exported.

- [ ] **Step 3: Implement `levelPlantedFeet`** (append to `contacts.ts`)

```ts
import { eulerRomFor } from "posecode-parser";

const SOLE_LOCAL = new THREE.Vector3(0, -1, 0);
const DEG = Math.PI / 180;
/** Foot mesh-bottom height at/below which the sole is fully leveled (m). */
export const PLANT_FADE = 0.06;
/** Authored plantarflex (ankle local +X) beyond this opts out of leveling (rad). */
export const PLANTARFLEX_SKIP = 15 * DEG;

const FOOT_SIDES: Array<"left" | "right"> = ["left", "right"];
const TMP_EULER = new THREE.Euler();

/**
* Level each ground-locked foot: rotate the ankle so the sole normal points
* world-down (the whole sole rests flat), weighted by how planted the foot is
* and skipped when the ankle is authored into plantarflexion (tiptoe intent).
* Analogue of `alignFloorPalms` for feet.
*/
export function levelPlantedFeet(m: Mannequin, activeGroundLock: readonly string[]): void {
if (!activeGroundLock.includes("feet")) return;
let changed = false;
for (const side of FOOT_SIDES) {
const ankle = m.bones.get(`ankle_${side}`);
if (!ankle?.parent) continue;
// Tiptoe opt-out: authored plantarflex (local +X) beyond the threshold.
TMP_EULER.setFromQuaternion(ankle.quaternion, "XYZ");
if (TMP_EULER.x > PLANTARFLEX_SKIP) continue;
// Planted-ness weight from the foot mesh bottom height.
const box = new THREE.Box3().setFromObject(ankle);
const y = Number.isFinite(box.min.y) ? box.min.y : 0;
const weight = THREE.MathUtils.clamp((PLANT_FADE - y) / PLANT_FADE, 0, 1);
if (weight <= 1e-3) continue;
// Minimal rotation aligning the sole normal to world-down.
const world = ankle.getWorldQuaternion(new THREE.Quaternion());
const current = SOLE_LOCAL.clone().applyQuaternion(world).normalize();
const correction = new THREE.Quaternion().setFromUnitVectors(current, DOWN);
if (weight < 1) correction.slerp(new THREE.Quaternion(), 1 - weight);
const desiredWorld = correction.multiply(world);
const parentWorld = ankle.parent.getWorldQuaternion(new THREE.Quaternion());
const local = parentWorld.invert().multiply(desiredWorld);
// Clamp to ankle ROM, widened to admit the authored angle.
const rom = eulerRomFor(`ankle_${side}`);
if (rom) {
TMP_EULER.setFromQuaternion(local, "XYZ");
const authored = new THREE.Euler().setFromQuaternion(ankle.quaternion, "XYZ");
const cx = THREE.MathUtils.clamp(
TMP_EULER.x,
Math.min(rom.x.min * DEG, authored.x),
Math.max(rom.x.max * DEG, authored.x),
);
const cz = THREE.MathUtils.clamp(
TMP_EULER.z,
Math.min(rom.z.min * DEG, authored.z),
Math.max(rom.z.max * DEG, authored.z),
);
TMP_EULER.set(cx, TMP_EULER.y, cz, "XYZ");
local.setFromEuler(TMP_EULER);
}
ankle.quaternion.copy(local);
changed = true;
}
if (changed) m.root.updateMatrixWorld(true);
}
```

(Note: `DOWN` already exists at the top of `contacts.ts`.)

- [ ] **Step 4: Run to verify pass**

Run: `npx vitest run packages/posecode-render/test/contacts.test.ts`
Expected: PASS (3 tests).

- [ ] **Step 5: Commit**

```bash
git add packages/posecode-render/src/contacts.ts packages/posecode-render/test/contacts.test.ts
git commit -m "feat(render): levelPlantedFeet — plantigrade foot-flat correction"
```

---

### Task 2: Wire into the viewer frame loop + load

**Files:**
- Modify: `packages/posecode-render/src/index.ts`
- Test: `packages/posecode-render/test/render.test.ts` (add end-to-end squat-flat test)

**Interfaces:**
- Consumes: `levelPlantedFeet` (Task 1).

- [ ] **Step 1: Write the failing end-to-end test** (append to `render.test.ts`)

```ts
it("rests a squatting foot flat on the floor (not on the toes)", () => {
const src = [
'posecode exercise "sq"',
" rig humanoid",
" pose start = standing",
' step "Descend" 1s settle:',
" hips: flex 80",
" knees: flex 95",
" pelvis: hinge 25",
" ground-lock: feet",
].join("\n");
const { ir } = parse(src);
const tl = buildTimeline(ir!);
const m = buildMannequin();
tl.sample(1, m.bones);
m.root.updateMatrixWorld(true);
// Simulate the frame-loop contact stages relevant to feet:
groundFigure(m);
applyGroundLock(m, ["feet"]);
levelPlantedFeet(m, ["feet"]);
m.root.updateMatrixWorld(true);
const ankle = m.bones.get("ankle_left")!;
const soleNormal = new THREE.Vector3(0, -1, 0)
.applyQuaternion(ankle.getWorldQuaternion(new THREE.Quaternion()))
.normalize();
expect(soleNormal.dot(new THREE.Vector3(0, -1, 0))).toBeGreaterThan(0.9); // sole ~flat
});
```

Add `levelPlantedFeet` to the render.test.ts import from `../src/contacts.js`.

- [ ] **Step 2: Run to verify failure**

Run: `npx vitest run packages/posecode-render/test/render.test.ts`
Expected: FAIL — `levelPlantedFeet` not imported / sole not level.

- [ ] **Step 3: Wire into `index.ts`**

Import alongside the existing contacts import:

```ts
import { alignFloorPalms, levelPlantedFeet } from "./contacts.js";
```

In `frame()`, immediately after the `alignFloorPalms(mannequin, info.reaches, info.pins);` line and before the floor-clamp bbox block:

```ts
levelPlantedFeet(mannequin, info.groundLock);
```

In `load()`, after `groundFigureOf(mannequin);` and before `captureGroundTargets();`:

```ts
levelPlantedFeet(mannequin, timeline.sample(0, mannequin.bones).groundLock);
```

(If calling `sample` twice is awkward, capture the phase-0 groundLock from `timeline.segments`/IR instead; simplest is to read `ir.phases[0]?.groundLock ?? []`.)

- [ ] **Step 4: Run to verify pass**

Run: `npx vitest run packages/posecode-render`
Expected: PASS, all render tests green.

- [ ] **Step 5: Commit**

```bash
git add packages/posecode-render/src/index.ts packages/posecode-render/test/render.test.ts
git commit -m "feat(render): apply foot-flat in the viewer frame loop and on load"
```

---

### Task 3: Editor discoverability + squat demo fix + full verify

**Files:**
- Modify: `packages/posecode-language/src/vocab.ts` (`ground-lock` doc)
- Modify: `spec/examples/squat.posecode` (drop the spurious plantarflex)

- [ ] **Step 1: Update the ground-lock doc**

```ts
// vocab.ts KEYWORD_DOCS
"ground-lock": "Pins effectors (hands / feet) to the floor for this phase. Planted feet auto-level flat to the floor unless the ankle is plantarflexed (tiptoe).",
```

- [ ] **Step 2: Fix the squat demo**

In `spec/examples/squat.posecode`, remove the `ankles: plantarflex 50` line from the Descend
step (and its `plantarflex 0` in Drive up), letting foot-flat land the sole. Keep everything
else.

- [ ] **Step 3: Language tests + full suite + typecheck**

Run: `npx vitest run` (whole workspace) and `npm run typecheck`.
Expected: all green; the 76 example tests still pass (squat still parses, now flatter).

- [ ] **Step 4: Browser verify**

Start the playground (`preview_start name playground`), open `/play.html#doc=squat` and
`/play.html#doc=releve`. Confirm: squat rests flat on both feet; relevé stays on the balls of
the feet. Check console for errors. Screenshot both.

- [ ] **Step 5: Commit**

```bash
git add packages/posecode-language/src/vocab.ts spec/examples/squat.posecode
git commit -m "feat: squat rests flat via foot-flat; document ground-lock leveling (L3.1)"
```

---

## Self-Review

- Foot-flat mechanism (align sole to down) → Task 1. ✅
- Planted-ness soft blend → Task 1 (`weight`). ✅
- Plantarflex opt-out → Task 1 (`PLANTARFLEX_SKIP`). ✅
- ROM clamp risk mitigation → Task 1 (eulerRomFor widen). ✅
- Frame-loop + load wiring → Task 2. ✅
- DSL discoverability (no new keyword) → Task 3 Step 1. ✅
- Squat demo + relevé opt-out verification → Task 3. ✅
- Tests first each task; existing suites green → all tasks. ✅
- **Placeholder scan:** none. **Type consistency:** `levelPlantedFeet(m, activeGroundLock)`,
`PLANT_FADE`, `PLANTARFLEX_SKIP` used consistently.
Loading
Loading