Skip to content

Commit 6826ebd

Browse files
committed
feat(eval,docs): capability probe and gap analysis for complex-teaching 540 movement (#106)
1 parent 5566bb8 commit 6826ebd

2 files changed

Lines changed: 233 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Capability Probe: Complex-Teaching Use Case (540 Movement)
2+
3+
**Status:** Completed research & capability audit (Issue #106 / Workstream 7 of #91)
4+
**Reference Video:** [Tutorial / Performance Reference: 540 Kick](https://www.youtube.com/watch?v=aBy_qAqRaxo)
5+
**Scope:** Kinematic breakdown, expressibility audit, capability gap analysis, and runtime recommendations for teaching complex rotational aerial movements in Posecode.
6+
7+
---
8+
9+
## 1. Executive Summary & Objective
10+
11+
The **540 Kick** (cheat 540 / inside crescent to landing on the kicking leg) is an iconic movement in martial arts tricking, wushu, and contemporary performance. It combines:
12+
1. High-velocity rotational momentum (540° body rotation).
13+
2. Vertical ballistic launch and free flight.
14+
3. Asymmetric aerial leg shapes (chambering the non-kicking leg while whipping the kicking leg).
15+
4. A single-leg landing *on the kicking leg itself* (the same leg that executed the kick), requiring an airborne scissor switch.
16+
17+
### Primary Question
18+
> *Can Posecode represent this movement for a high-value teaching visualization (slow playback, scrub diagnostics, 3D angle inspection)? What works today, and what architectural capabilities are missing?*
19+
20+
---
21+
22+
## 2. Semantic Kinematic Decomposition
23+
24+
A full 540 kick decomposes into eight distinct semantic phases:
25+
26+
```
27+
[1. Cheat Prep] ──► [2. Takeoff] ──► [3. Flight Launch] ──► [4. Inversion/Chamber]
28+
(Step & dip) (Left leg plant) (Leave ground) (Non-kicking chamber)
29+
│
30+
[8. Recovery] ◄── [7. Landing] ◄── [6. Scissor Prep] ◄── [5. Crescent Apex]
31+
(Step out) (Right foot plant) (Switch in air) (Right leg kick 360°)
32+
```
33+
34+
### Phase-by-Phase Breakdown
35+
36+
| Phase | Duration | Body Facing | Contacts | Kinematic Signature |
37+
| :--- | :--- | :--- | :--- | :--- |
38+
| **1. Cheat Prep** | ~0.5s | 0° → 90° | Both feet grounded | Curved approach step, torso dips to build rotational angular momentum. |
39+
| **2. Takeoff Plant** | ~0.25s | 90° → 180° | `pin: foot_left floor` | Left stance foot plants firmly; right leg swings forward and up; arms lift overhead. |
40+
| **3. Takeoff Launch** | ~0.15s | 180° | Airborne transition | Explosive extension of left leg; body launches vertically into free flight. |
41+
| **4. Mid-air Inversion** | ~0.25s | 180° → 270° | **None (Free flight)** | Torso tilts ~30° off-axis; left non-kicking knee chambers to chest to accelerate spin. |
42+
| **5. Crescent Apex** | ~0.25s | 270° → 360° | **None (Free flight)** | Right leg whips across target line (hip abduction 80°, knee extended, foot plantarflexed). Peak height ~0.8m. |
43+
| **6. Scissor Switch** | ~0.2s | 360° → 450° | **None (Free flight)** | Right leg drives downward toward floor; left leg extends backward/upward to prepare landing clearance. |
44+
| **7. Single-leg Landing** | ~0.3s | 450° → 540° | `pin: foot_right floor` | Right foot touches down; right knee flexes 45° to absorb shock; left leg airborne. |
45+
| **8. Recovery** | ~0.5s | 540° (180°) | `ground-lock: feet` | Left foot touches down; torso straightens into stable martial arts stance. |
46+
47+
---
48+
49+
## 3. Expressibility Audit: Current Grammar vs. Gaps
50+
51+
### What Works Today in Posecode (v0.4)
52+
1. **Multi-Turn Yaw (`turn: 540`):**
53+
- The parser, AST, and timeline engine fully support large yaw rotations (`turn: 540`).
54+
- The loop boundary correctly understands full rotations ($540^\circ \equiv 180^\circ$ relative to origin).
55+
2. **Individual Limb Kinematics:**
56+
- Hip flexion/abduction, knee flexion, and arm swings for the crescent kick and chambering fit cleanly within configured ROM limits.
57+
3. **Grounded Contacts & Pins:**
58+
- Phase 1 & 2 (`pin: foot_left floor`) and Phase 7 (`pin: foot_right floor`) accurately translate root to stance feet.
59+
4. **Teaching Tools (#22 Shipped):**
60+
- 0.25x / 0.5x slow-motion playback.
61+
- Interactive timeline scrubber with millisecond precision.
62+
- Full 3D orbit camera controls allowing top-down, side-angle, and front-angle inspection of leg clearance.
63+
64+
---
65+
66+
### Critical Architectural Gaps
67+
68+
#### Gap 1: Free Flight Bounding-Box Drop
69+
- **The Problem:** In Posecode's grounding system (`packages/posecode-render/src/index.ts`), every frame executes a bounding-box drop that lowers the character so the lowest vertex rests on the floor ($y = 0$).
70+
- **The Impact on 540:** During free flight (Phases 3–6), when the athlete is 0.8m in the air, the lowest foot is dragged down to the floor. The character appears to crawl or slide along the floor while rotating, destroying the visual physics of a jump.
71+
- **Language Requirement:** A mechanism to declare a phase as `airborne` with a root elevation offset or ballistic trajectory.
72+
73+
#### Gap 2: Tilted Gyroscopic Rotational Axis
74+
- **The Problem:** In Posecode, `turn: <deg>` strictly applies a rotation about the global vertical Y-axis ($R_y$).
75+
- **The Impact on 540:** Real tricking kicks rotate about an inclined cone or gyroscopic axis (torso dipped ~30°–45° during spin). Authoring this currently requires severe lateral spine flexion (`spine: abduct 35`) on every phase to simulate lean while the root remains vertical.
76+
- **Language Requirement:** Generalized 3D turn syntax, e.g. `turn: 540 tilt 30`.
77+
78+
#### Gap 3: Ballistic Dynamics vs. Linear Hermite Splines
79+
- **The Problem:** Jump timing is parabolic ($y(t) = v_0 t - \frac{1}{2}gt^2$). Keyframed cubic splines often produce floaty or inconsistent vertical apex profiles.
80+
81+
---
82+
83+
## 4. Minimum Language & Runtime Proposals
84+
85+
To generalize across all jumping, acrobatic, and dance movements (e.g. ballet *grand jeté*, wushu *butterfly twist*, gymnastics *back tuck*), we propose two clean orthogonal extensions:
86+
87+
### Proposal A: Elevation Channel (`elevation: <meters>`)
88+
Add an optional root elevation property to steps:
89+
```posecode
90+
step "Apex Crescent" 0.25s flow:
91+
elevation: 0.85
92+
turn: 360
93+
hip_right: abduct 85
94+
knee_right: extend 0
95+
cue "Apex of jump: whip right leg through high crescent kick"
96+
```
97+
- **Runtime Effect:** Bypasses floor bounding-box drop. The character root Y translates to the specified height above the floor plane.
98+
99+
### Proposal B: Ballistic Flight Helper
100+
Alternatively, declare takeoff and landing phases, allowing the runtime to compute the parabolic ballistic trajectory:
101+
```posecode
102+
step "Apex Crescent" 0.25s ballistic:
103+
apex: 0.85
104+
turn: 360
105+
```
106+
107+
---
108+
109+
## 5. Evaluation of Teaching Utility
110+
111+
When evaluated against the YouTube reference tutorial:
112+
- **Frame-by-frame scrubbing** provides unmatched clarity for teaching the *scissor switch*—the most common failure point for students (landing on the wrong leg).
113+
- **Multi-angle perspective** allows students to see how the cheat step sets up hip rotation from above (floor guide cardinal markings).
114+
- **Conclusion:** Once `elevation:` or free-flight support is implemented, Posecode will be a premier tool for tricking and martial arts choreography education.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { describe, it, expect } from "vitest";
2+
import { parse } from "posecode-parser";
3+
import { probeMovement } from "../src/index.js";
4+
import { kneeFlexionDeg, heightOf } from "../src/metrics.js";
5+
6+
const PROBE_540 = `posecode exercise "540 Kick Capability Probe"
7+
rig humanoid
8+
pose start = standing
9+
10+
step "Cheat prep" 0.45s flow:
11+
turn: 90
12+
hip_left: flex 25
13+
knee_left: flex 35
14+
hip_right: flex 15
15+
knee_right: flex 25
16+
ground-lock: feet
17+
cue "Cheat step and torso dip to generate rotational momentum"
18+
19+
step "Takeoff plant" 0.25s flow:
20+
turn: 180
21+
hip_left: flex 35
22+
knee_left: flex 45
23+
hip_right: flex 40
24+
knee_right: flex 30
25+
shoulder_right: flex 120
26+
shoulder_left: flex 120
27+
pin: foot_left floor
28+
cue "Plant left foot explosively, swing right leg and arms upward"
29+
30+
step "Inversion and chamber" 0.25s flow:
31+
turn: 270
32+
knee_left: flex 95
33+
hip_left: flex 85
34+
hip_right: abduct 45
35+
cue "Launch into air, chamber non-kicking left knee tightly to chest"
36+
37+
step "Apex crescent kick" 0.25s flow:
38+
turn: 360
39+
hip_right: abduct 45
40+
hip_right: flex 70
41+
knee_right: extend 0
42+
ankle_right: plantarflex 35
43+
knee_left: flex 90
44+
cue "Apex of jump: whip right leg through high crescent kick"
45+
46+
step "Scissor switch" 0.20s flow:
47+
turn: 450
48+
hip_right: flex 35
49+
knee_right: flex 25
50+
hip_left: extend 10
51+
knee_left: flex 45
52+
cue "Switch legs mid-air: right leg reaches down to receive landing"
53+
54+
step "Single-leg landing" 0.35s settle:
55+
turn: 540
56+
hip_right: flex 40
57+
knee_right: flex 45
58+
ankle_right: dorsiflex 12
59+
hip_left: flex 30
60+
knee_left: flex 75
61+
pin: foot_right floor
62+
cue "Land cleanly on the kicking right leg, absorbing shock through the knee"
63+
64+
step "Recovery" 0.50s settle:
65+
turn: 540
66+
knees: flex 15
67+
ground-lock: feet
68+
cue "Step down with left foot into balanced guard stance"
69+
`;
70+
71+
describe("540 kick capability probe (Issue #106)", () => {
72+
it("parses the complete 540 movement sequence with zero errors and zero ROM warnings", () => {
73+
const { ir, errors, warnings } = parse(PROBE_540);
74+
expect(errors).toHaveLength(0);
75+
expect(warnings).toHaveLength(0);
76+
expect(ir?.phases).toHaveLength(7);
77+
});
78+
79+
it("verifies multi-turn yaw (540°) and single-leg landing semantics", () => {
80+
const probe = probeMovement(PROBE_540);
81+
expect(probe.ok).toBe(true);
82+
expect(probe.phases).toHaveLength(7);
83+
84+
const takeoff = probe.phases.find((p) => p.name === "Takeoff plant")!;
85+
const chamber = probe.phases.find((p) => p.name === "Inversion and chamber")!;
86+
const apex = probe.phases.find((p) => p.name === "Apex crescent kick")!;
87+
const landing = probe.phases.find((p) => p.name === "Single-leg landing")!;
88+
89+
// Takeoff plants on left foot
90+
expect(takeoff.pins.some((p) => p.effector === "foot_left")).toBe(true);
91+
92+
// Mid-air chamber has non-kicking left knee deeply bent
93+
expect(kneeFlexionDeg(chamber, "left")).toBeGreaterThan(80);
94+
95+
// Apex crescent kick has kicking leg extended
96+
expect(kneeFlexionDeg(apex, "right")).toBeLessThan(5);
97+
98+
// Single-leg landing lands on the KICKING right foot (the core 540 signature)
99+
expect(landing.pins.some((p) => p.effector === "foot_right")).toBe(true);
100+
expect(kneeFlexionDeg(landing, "right")).toBeGreaterThan(35);
101+
102+
// Yaw reaches 540 degrees (3π radians)
103+
expect(Math.abs(landing.rootYaw - 3 * Math.PI)).toBeLessThan(0.05);
104+
});
105+
106+
it("identifies the free-flight grounding gap: lowest joint drops to floor in absence of elevation channel", () => {
107+
const probe = probeMovement(PROBE_540);
108+
const apex = probe.phases.find((p) => p.name === "Apex crescent kick")!;
109+
110+
// In physical reality, apex height is ~0.8m above floor.
111+
// In current Posecode runtime (Gap 1), bounding-box drop forces the lowest foot near 0.
112+
const lowestFootY = Math.min(
113+
heightOf(apex, "ankle_left"),
114+
heightOf(apex, "ankle_right"),
115+
);
116+
// Proves that lowest point sits on the floor rather than in ballistic flight
117+
expect(lowestFootY).toBeLessThan(0.35);
118+
});
119+
});

0 commit comments

Comments
 (0)