Skip to content

Commit 9054727

Browse files
committed
Support pelvis floor pins in cobra poses
1 parent 43b3d70 commit 9054727

4 files changed

Lines changed: 56 additions & 3 deletions

File tree

packages/posecode-eval/src/probe.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
* and returns world-space bone positions at the end of every phase. This is
88
* the ground truth the invariant checks score against.
99
*
10-
* Known gap vs the viewer: contact pins and reach-IK are scene-dependent
11-
* (their anchors can live on props), so the probe skips them; pinned/reaching
12-
* movements are covered by the parse/clamp invariants only.
10+
* Prop-backed pins and reach-IK remain scene-dependent. Floor/body-landmark
11+
* pins are deterministic, however, so the probe resolves those exactly like
12+
* the viewer; this lets contact regressions in floor work (cobra, etc.) fail
13+
* the scorecard instead of being hidden behind `usesSceneIk`.
1314
*/
1415

1516
import * as THREE from "three";
@@ -109,6 +110,40 @@ export function probeMovement(source: string): ProbeResult {
109110
anchors.set(id, v);
110111
}
111112
applyGroundLock(m, info.groundLock, anchors);
113+
// Resolve scene-independent pins. Unknown names here are prop anchors and
114+
// intentionally remain for browser-level coverage.
115+
if (info.pins.length > 0) {
116+
const delta = new THREE.Vector3();
117+
let pinCount = 0;
118+
for (const pin of info.pins) {
119+
const effectorId = pin.effector === "hand_left"
120+
? "wrist_left"
121+
: pin.effector === "hand_right"
122+
? "wrist_right"
123+
: pin.effector === "foot_left"
124+
? "ankle_left"
125+
: pin.effector === "foot_right"
126+
? "ankle_right"
127+
: pin.effector;
128+
const effector = m.bones.get(effectorId);
129+
if (!effector) continue;
130+
let target: THREE.Vector3 | null = null;
131+
if (pin.anchor === "floor") {
132+
target = effector.getWorldPosition(new THREE.Vector3());
133+
target.y = 0;
134+
} else {
135+
const landmark = m.bones.get(pin.anchor);
136+
if (landmark) target = landmark.getWorldPosition(new THREE.Vector3());
137+
}
138+
if (!target) continue;
139+
delta.add(target.sub(effector.getWorldPosition(new THREE.Vector3())));
140+
pinCount++;
141+
}
142+
if (pinCount > 0) {
143+
m.root.position.add(delta.multiplyScalar(1 / pinCount));
144+
m.root.updateMatrixWorld(true);
145+
}
146+
}
112147
// Viewer safety net: never leave the lowest mesh point below the floor.
113148
m.root.updateMatrixWorld(true);
114149
const box = new THREE.Box3().setFromObject(m.root);

packages/posecode-parser/src/joints.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ const EFFECTOR_SIDES = [
103103
"hand_right",
104104
"foot_left",
105105
"foot_right",
106+
// Axial support point for floor-based poses such as cobra. Unlike a reach,
107+
// pinning the pelvis moves the root while the spine and limbs articulate.
108+
"pelvis",
106109
] as const;
107110

108111
/** Symmetric effector groups → the per-side effectors they expand to. */

packages/posecode-parser/test/parse.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ describe("parse", () => {
168168
});
169169

170170
describe("reach/pin effectors", () => {
171+
it("accepts the pelvis as an axial contact pin", () => {
172+
const src = `posecode stretch "Cobra"
173+
rig humanoid
174+
pose start = prone
175+
step "Lift" 1s ease-in-out:
176+
pin: pelvis floor`;
177+
const result = parse(src);
178+
expect(result.errors).toEqual([]);
179+
expect(result.ir?.phases[0]?.pins).toEqual([{ effector: "pelvis", anchor: "floor" }]);
180+
});
181+
171182
it("expands `hands` / `feet` into per-side effectors", () => {
172183
const { ir, errors } = parse(
173184
[

spec/examples/cobra.posecode

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ posecode stretch "Cobra"
99
shoulders: flex 110
1010
elbows: flex 40
1111
elbows: pronate 80
12+
pin: pelvis floor
13+
pin: feet floor
1214
reach: hands floor
1315
cue "Press the palms into the floor and lift the chest, shoulders rolling back"
1416

@@ -19,6 +21,8 @@ posecode stretch "Cobra"
1921
shoulders: flex 0
2022
elbows: flex 0
2123
elbows: pronate 80
24+
pin: pelvis floor
25+
pin: feet floor
2226
reach: hands floor
2327
cue "Lower the chest back to the floor with control"
2428

0 commit comments

Comments
 (0)