diff --git a/ROADMAP.md b/ROADMAP.md index b28366c..d0a1df3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -39,7 +39,9 @@ These are the unlocks, roughly in order of leverage: 3. ~~**Scene props with contact anchors**~~: **shipped (starter set).** `prop chair|wall|bar` adds a scene object with named anchors (`seat`, `wall`, `bar`). Powers `sit-to-stand`, `box-squat`, `wall-sit`, `dead-hang`, `hanging-knee-raise`. - Next: more props (bench, rings, bands), load cues, anchor-aware ground-lock. + Bar and dip-bar contacts now resolve to independent left/right anchors with + terminal wrist orientation; mocap is contact-corrected after blending. + Next: more props (bench, rings, bands), load cues, arbitrary surface shapes. 4. ~~**Lying & seated base poses**~~: **shipped.** `supine | prone | seated` start poses (grounded by a bounding-box drop). Powers `glute-bridge`, `dead-bug`, `cobra`, `seated-forward-fold`. Next: quadruped + chair-seated. @@ -51,8 +53,9 @@ These are the unlocks, roughly in order of leverage: both absolute + carried across phases and returning home on the loop wrap. Powers `pirouette`, `box-step`, `grapevine`, `waltz-box`, `chasse`, `walk-cycle`, `quarter-turns`: pirouettes, traveling combos, and gait. - Standing poses only. Next: footstep-locked travel (true gait), motion - aliveness (velocity-continuous flow + weight shift). + Standing poses only. Floor-contacting soles are orientation-locked and the + visible mocap rig is re-planted after blending. Next: a larger curated clip + library, explicit gait phase metadata, and motion matching/inertialization. 7. **Two-person + collision**: partner stretches, assisted rehab, contact sports (still deferred in the spec). diff --git a/packages/posecode-eval/src/checks.ts b/packages/posecode-eval/src/checks.ts index 180601a..ac2ff99 100644 --- a/packages/posecode-eval/src/checks.ts +++ b/packages/posecode-eval/src/checks.ts @@ -8,6 +8,7 @@ import type { PhasePose, ProbeResult } from "./probe.js"; import { balanceOverflow, + barGripError, distanceBetween, feetCenterSkateDistance, footIsSupported, @@ -17,8 +18,10 @@ import { kneeFlexionDeg, lowestPoint, palmFloorAngleDeg, + palmBarAngleDeg, phaseMaxLandmarkSpeed, segmentTiltDeg, + soleUpAngleDeg, spineCurlDeg, torsoPitchDeg, } from "./metrics.js"; @@ -244,6 +247,26 @@ export const MOVEMENT_CHECKS: MovementChecks[] = [ (v) => v > 0.9, "pelvis > 0.9m", ), + phaseCheck("left-sole-flat", "Descend", (p) => soleUpAngleDeg(p, "left"), (v) => v < 2, "< 2°"), + phaseCheck("right-sole-flat", "Descend", (p) => soleUpAngleDeg(p, "right"), (v) => v < 2, "< 2°"), + ], + }, + { + movement: "pull-up", + checks: [ + phaseCheck("left-grip-position", "Hang", (p) => barGripError(p, "left"), (v) => v < 0.12, "< 0.12m"), + phaseCheck("right-grip-position", "Hang", (p) => barGripError(p, "right"), (v) => v < 0.12, "< 0.12m"), + phaseCheck("left-palm-wrap", "Hang", (p) => palmBarAngleDeg(p, "left"), (v) => v < 5, "< 5°"), + phaseCheck("right-palm-wrap", "Hang", (p) => palmBarAngleDeg(p, "right"), (v) => v < 5, "< 5°"), + phaseCheck("left-grip-held", "Pull up", (p) => barGripError(p, "left"), (v) => v < 0.12, "< 0.12m"), + phaseCheck("right-grip-held", "Pull up", (p) => barGripError(p, "right"), (v) => v < 0.12, "< 0.12m"), + ], + }, + { + movement: "walk-cycle", + checks: [ + phaseCheck("left-stance-flat", "Step right", (p) => soleUpAngleDeg(p, "left"), (v) => v < 2, "< 2°"), + phaseCheck("right-stance-flat", "Step left", (p) => soleUpAngleDeg(p, "right"), (v) => v < 2, "< 2°"), ], }, { diff --git a/packages/posecode-eval/src/metrics.ts b/packages/posecode-eval/src/metrics.ts index 1582de4..4617db2 100644 --- a/packages/posecode-eval/src/metrics.ts +++ b/packages/posecode-eval/src/metrics.ts @@ -92,6 +92,28 @@ export function palmFloorAngleDeg(pose: PhasePose, side: "left" | "right"): numb return angleBetweenDeg(rotateByQuat(side === "left" ? [1, 0, 0] : [-1, 0, 0], q), [0, -1, 0]); } +/** Angle between the sole's local up axis and world up (0 = foot flat). */ +export function soleUpAngleDeg(pose: PhasePose, side: "left" | "right"): number { + const q = pose.boneQuaternions.get(`ankle_${side}`); + if (!q) return 180; + return angleBetweenDeg(rotateByQuat([0, 1, 0], q), [0, 1, 0]); +} + +/** Overhand bar grip: angle between the palm face normal and character-forward. */ +export function palmBarAngleDeg(pose: PhasePose, side: "left" | "right"): number { + const q = pose.boneQuaternions.get(`wrist_${side}`); + if (!q) return 180; + const localNormal: Vec3 = side === "left" ? [1, 0, 0] : [-1, 0, 0]; + return angleBetweenDeg(rotateByQuat(localNormal, q), [0, 0, 1]); +} + +/** Distance from a wrist to its side-specific pull-up-bar grip anchor. */ +export function barGripError(pose: PhasePose, side: "left" | "right"): number { + const wrist = bone(pose, `wrist_${side}`); + const anchor: Vec3 = [side === "left" ? 0.24 : -0.24, 2.255, 0.025]; + return norm(sub(wrist, anchor)); +} + const MASS_WEIGHTS: ReadonlyArray = [ ["pelvis", 0.22], ["spine", 0.13], ["chest", 0.2], ["head", 0.08], ["hip_left", 0.07], ["hip_right", 0.07], ["knee_left", 0.05], ["knee_right", 0.05], diff --git a/packages/posecode-eval/src/probe.ts b/packages/posecode-eval/src/probe.ts index 89c2a3f..4252f5e 100644 --- a/packages/posecode-eval/src/probe.ts +++ b/packages/posecode-eval/src/probe.ts @@ -16,7 +16,9 @@ import * as THREE from "three"; import { parse, type Easing, type ParseError, type PinTarget, type ReachTarget, type Warning } from "posecode-parser"; import { applyGroundLock, + alignBarGrips, alignFloorPalms, + alignFloorSoles, buildMannequin, buildProps, buildTimeline, @@ -122,6 +124,7 @@ export function probeMovement(source: string): ProbeResult { v.z += info.rootOffset.z; anchors.set(id, v); } + alignFloorSoles(m, info.groundLock, info.reaches, info.pins); applyGroundLock(m, info.groundLock, anchors); // Resolve scene-independent pins. Unknown names here are prop anchors and // intentionally remain for browser-level coverage. @@ -144,9 +147,17 @@ export function probeMovement(source: string): ProbeResult { if (pin.anchor === "floor") { target = effector.getWorldPosition(new THREE.Vector3()); target.y = 0; - } else if (propScene.anchors.has(pin.anchor)) { - target = propScene.anchors.get(pin.anchor)!.clone(); } else { + const side = effectorId.endsWith("_left") + ? "left" + : effectorId.endsWith("_right") + ? "right" + : null; + const propTarget = (side ? propScene.anchors.get(`${pin.anchor}.${side}`) : undefined) + ?? propScene.anchors.get(pin.anchor); + if (propTarget) target = propTarget.clone(); + } + if (!target && pin.anchor !== "floor") { const landmark = m.bones.get(pin.anchor); if (landmark) target = landmark.getWorldPosition(new THREE.Vector3()); } @@ -160,6 +171,7 @@ export function probeMovement(source: string): ProbeResult { } } alignFloorPalms(m, info.reaches, info.pins); + alignBarGrips(m, info.reaches, info.pins); // Viewer safety net: never leave the lowest mesh point below the floor. m.root.updateMatrixWorld(true); const box = new THREE.Box3().setFromObject(m.root); diff --git a/packages/posecode-render/src/character.ts b/packages/posecode-render/src/character.ts index 7635d8b..efe9c0f 100644 --- a/packages/posecode-render/src/character.ts +++ b/packages/posecode-render/src/character.ts @@ -71,6 +71,8 @@ export interface Character { proportions: Proportions; /** Copy the driver's current pose onto the character skeleton. */ sync(driver: Mannequin): void; + /** Restore solved terminal contacts after a mocap layer has overwritten them. */ + correctContacts(driver: Mannequin, boneIds: readonly string[]): void; /** * The character's first skinned mesh, the retarget target for mocap clips * (see clips.ts). Null on bare skeletons, which then can't play clips. @@ -283,6 +285,7 @@ export function rigCharacter(charScene: THREE.Object3D): Character { // ---- Capture rest state for the per-frame retarget. ---- const mapped: MappedBone[] = []; const mappedByNode = new Map(); + const mappedById = new Map(); for (const [driverId] of Object.entries(BONE_MAP)) { const node = bone(driverId); const mb: MappedBone = { @@ -293,6 +296,7 @@ export function rigCharacter(charScene: THREE.Object3D): Character { }; mapped.push(mb); mappedByNode.set(node, mb); + mappedById.set(driverId, mb); } // Distal phalanges: capture rest locals + the curl axis expressed in each // phalanx's rest-local frame (the driver curls fingers as a single bone; the @@ -379,6 +383,34 @@ export function rigCharacter(charScene: THREE.Object3D): Character { group.updateMatrixWorld(true); } + function correctContacts(driver: Mannequin, boneIds: readonly string[]): void { + const ids = [...new Set(boneIds)].filter((id) => mappedById.has(id) && driver.bones.has(id)); + if (ids.length === 0) return; + + group.updateMatrixWorld(true); + const delta = new THREE.Vector3(); + const driverPos = new THREE.Vector3(); + const charPos = new THREE.Vector3(); + for (const id of ids) { + driver.bones.get(id)!.getWorldPosition(driverPos); + mappedById.get(id)!.node.getWorldPosition(charPos); + delta.add(driverPos).sub(charPos); + } + group.position.add(delta.multiplyScalar(1 / ids.length)); + group.updateMatrixWorld(true); + + for (const id of ids) { + const mb = mappedById.get(id)!; + if (!mb.node.parent) continue; + driver.bones.get(id)!.getWorldQuaternion(TMP_Q); + const desiredWorld = TMP_Q2.copy(TMP_Q).multiply(mb.restWorld); + mb.node.parent.getWorldQuaternion(TMP_Q); + mb.node.quaternion.copy(TMP_Q.invert().multiply(desiredWorld)); + mb.node.updateMatrixWorld(true); + } + group.updateMatrixWorld(true); + } + // Surface for the optional mocap-clip layer (clips.ts): the retarget target // mesh and the set of bones sync() rewrites each frame. let skinnedMesh: THREE.SkinnedMesh | null = null; @@ -394,6 +426,7 @@ export function rigCharacter(charScene: THREE.Object3D): Character { group, proportions, sync, + correctContacts, skinnedMesh, drivenNodes, dispose() { diff --git a/packages/posecode-render/src/clips.ts b/packages/posecode-render/src/clips.ts index 731ff80..68646e0 100644 --- a/packages/posecode-render/src/clips.ts +++ b/packages/posecode-render/src/clips.ts @@ -3,7 +3,7 @@ * walk) on the skinned character instead of — or crossfaded with — the * procedural DSL keyframes. * - * Pipeline: `loadClipSource` fetches an FBX/GLB and picks its longest + * Pipeline: `loadClipSource` fetches an FBX/GLB and picks its strongest moving * AnimationClip; `retargetMocapClip` bakes it onto the character's skeleton * with SkeletonUtils.retargetClip (both rigs follow Mixamo naming, so bones * pair up by suffix); `createClipLayer` plays the result through a @@ -29,12 +29,51 @@ function plainName(name: string): string { export interface ClipSource { /** The loaded asset's scene root (holds the source skeleton). */ root: THREE.Object3D; - /** The longest animation found in the asset. */ + /** The most motion-rich animation found in the asset. */ clip: THREE.AnimationClip; } /** - * Load a mocap asset (.fbx or .glb/.gltf) and pick its longest clip. Rejects + * Prefer the take with real changing bone tracks over long bind-pose/default + * takes commonly embedded beside a Mixamo animation in FBX exports. + */ +export function selectMotionClip(animations: readonly THREE.AnimationClip[]): THREE.AnimationClip | null { + let best: THREE.AnimationClip | null = null; + let bestScore = -Infinity; + for (const clip of animations) { + let movingTracks = 0; + let motion = 0; + for (const track of clip.tracks) { + const frames = track.times.length; + const stride = frames > 0 ? track.values.length / frames : 0; + if (frames < 2 || stride < 1) continue; + let trackMotion = 0; + for (let frame = 1; frame < frames; frame++) { + let deltaSq = 0; + for (let component = 0; component < stride; component++) { + const a = track.values[(frame - 1) * stride + component]!; + const b = track.values[frame * stride + component]!; + deltaSq += (b - a) * (b - a); + } + trackMotion += Math.sqrt(deltaSq); + } + trackMotion /= frames - 1; + if (trackMotion > 1e-5) { + movingTracks++; + motion += Math.min(trackMotion, 10); + } + } + const score = movingTracks * 100 + motion + Math.min(clip.duration, 10) * 0.001; + if (score > bestScore) { + best = clip; + bestScore = score; + } + } + return best; +} + +/** + * Load a mocap asset (.fbx or .glb/.gltf) and pick its most motion-rich clip. Rejects * when the asset has no animations; callers treat any rejection as "keep the * procedural path". */ @@ -51,7 +90,7 @@ export async function loadClipSource(url: string): Promise { root = gltf.scene; animations = gltf.animations; } - const clip = [...animations].sort((a, b) => b.duration - a.duration)[0]; + const clip = selectMotionClip(animations); if (!clip) throw new Error(`clip asset has no animations: ${url}`); return { root, clip }; } diff --git a/packages/posecode-render/src/contacts.ts b/packages/posecode-render/src/contacts.ts index 9a5d231..9a93fb4 100644 --- a/packages/posecode-render/src/contacts.ts +++ b/packages/posecode-render/src/contacts.ts @@ -4,21 +4,41 @@ import type { PinTarget, ReachTarget } from "posecode-parser"; import type { Mannequin } from "./mannequin.js"; const DOWN = new THREE.Vector3(0, -1, 0); +const UP = new THREE.Vector3(0, 1, 0); +const FORWARD = new THREE.Vector3(0, 0, 1); -/** Rotate contacting wrists so the palm face normal points into the floor. */ -export function alignFloorPalms( - m: Mannequin, +function contactSides( reaches: readonly ReachTarget[], pins: readonly PinTarget[], -): void { + target: (name: string) => boolean, + kind: "hand" | "foot", +): Set<"left" | "right"> { const sides = new Set<"left" | "right">(); - const collect = (effector: string, target: string) => { - if (target !== "floor") return; - if (effector === "hands" || effector === "hand_left") sides.add("left"); - if (effector === "hands" || effector === "hand_right") sides.add("right"); + const collect = (effector: string, name: string) => { + if (!target(name)) return; + const group = kind === "hand" ? "hands" : "feet"; + if (effector === group || effector === `${kind}_left`) sides.add("left"); + if (effector === group || effector === `${kind}_right`) sides.add("right"); }; reaches.forEach((r) => collect(r.effector, r.target)); pins.forEach((p) => collect(p.effector, p.anchor)); + return sides; +} + +/** Set a bone's world orientation while preserving the rest of its chain. */ +function setWorldQuaternion(node: THREE.Object3D, desiredWorld: THREE.Quaternion): void { + if (!node.parent) return; + const parentWorld = node.parent.getWorldQuaternion(new THREE.Quaternion()); + node.quaternion.copy(parentWorld.invert().multiply(desiredWorld)); +} + +/** Rotate contacting wrists so the palm face normal points into the floor. */ +export function alignFloorPalms( + m: Mannequin, + reaches: readonly ReachTarget[], + pins: readonly PinTarget[], +): void { + const sides = contactSides(reaches, pins, (target) => target === "floor", "hand"); for (const side of sides) { const wrist = m.bones.get(`wrist_${side}`); @@ -30,8 +50,68 @@ export function alignFloorPalms( const current = localNormal.applyQuaternion(world).normalize(); const correction = new THREE.Quaternion().setFromUnitVectors(current, DOWN); const desiredWorld = correction.multiply(world); - const parentWorld = wrist.parent.getWorldQuaternion(new THREE.Quaternion()); - wrist.quaternion.copy(parentWorld.invert().multiply(desiredWorld)); + setWorldQuaternion(wrist, desiredWorld); + } + if (sides.size > 0) m.root.updateMatrixWorld(true); +} + +/** + * Keep contacting feet flat and facing with the body. The ankle joint remains + * in place, so hip/knee motion and weight shift are preserved; only the sole's + * terminal orientation is corrected. + */ +export function alignFloorSoles( + m: Mannequin, + groundLock: readonly string[], + reaches: readonly ReachTarget[] = [], + pins: readonly PinTarget[] = [], +): void { + const sides = contactSides(reaches, pins, (target) => target === "floor", "foot"); + if (groundLock.includes("feet")) { + sides.add("left"); + sides.add("right"); + } + if (sides.size === 0) return; + + const rootForward = FORWARD.clone().applyQuaternion( + m.root.getWorldQuaternion(new THREE.Quaternion()), + ); + rootForward.y = 0; + if (rootForward.lengthSq() < 1e-8) rootForward.copy(FORWARD); + rootForward.normalize(); + const worldX = UP.clone().cross(rootForward).normalize(); + const desiredWorld = new THREE.Quaternion().setFromRotationMatrix( + new THREE.Matrix4().makeBasis(worldX, UP, rootForward), + ); + for (const side of sides) { + const ankle = m.bones.get(`ankle_${side}`); + if (ankle) setWorldQuaternion(ankle, desiredWorld); + } + m.root.updateMatrixWorld(true); +} + +/** + * Orient bar-contacting wrists as an overhand grip: fingers point up toward + * the bar and the palm faces away from the body. Finger curl remains authored + * independently, so this composes with grip strength / release animation. + */ +export function alignBarGrips( + m: Mannequin, + reaches: readonly ReachTarget[], + pins: readonly PinTarget[], +): void { + const sides = contactSides(reaches, pins, (target) => target === "bar", "hand"); + for (const side of sides) { + const wrist = m.bones.get(`wrist_${side}`); + if (!wrist) continue; + // Local palm normal is mirrored X; local -Y follows wrist→fingers. + const worldX = side === "left" ? FORWARD.clone() : FORWARD.clone().negate(); + const worldY = UP.clone().negate(); + const worldZ = worldX.clone().cross(worldY).normalize(); + const desiredWorld = new THREE.Quaternion().setFromRotationMatrix( + new THREE.Matrix4().makeBasis(worldX, worldY, worldZ), + ); + setWorldQuaternion(wrist, desiredWorld); } if (sides.size > 0) m.root.updateMatrixWorld(true); } diff --git a/packages/posecode-render/src/index.ts b/packages/posecode-render/src/index.ts index d9d61fe..5fc2527 100644 --- a/packages/posecode-render/src/index.ts +++ b/packages/posecode-render/src/index.ts @@ -17,17 +17,18 @@ import { buildMannequin, type Mannequin } from "./mannequin.js"; import { applyGroundLock as applyGroundLockTo, groundFigure as groundFigureOf } from "./groundlock.js"; import { buildTimeline, type BuiltTimeline, type PhaseSegment } from "./timeline.js"; import { solveCCD, type JointLimits } from "./ik.js"; -import { buildProps, type PropScene } from "./props.js"; +import { buildProps, syncPropAttachments, type PropScene } from "./props.js"; import { loadCharacter, type Character } from "./character.js"; import { loadClipSource, + selectMotionClip, retargetMocapClip, createClipLayer, type ClipLayer, type ClipSource, } from "./clips.js"; import { depenetrate } from "./depenetrate.js"; -import { alignFloorPalms } from "./contacts.js"; +import { alignBarGrips, alignFloorPalms, alignFloorSoles } from "./contacts.js"; const DEG = Math.PI / 180; @@ -363,6 +364,17 @@ export function createViewer( foot_right: "ankle_right", }; + function contactBoneIds(groundLock: readonly string[], pins: readonly PinTarget[]): string[] { + const ids = new Set(); + for (const group of groundLock) { + if (group === "hands") { ids.add("wrist_left"); ids.add("wrist_right"); } + if (group === "forearms") { ids.add("elbow_left"); ids.add("elbow_right"); } + if (group === "feet") { ids.add("ankle_left"); ids.add("ankle_right"); } + } + for (const pin of pins) ids.add(EFFECTOR_BONE[pin.effector] ?? pin.effector); + return [...ids]; + } + /** * The rotatable joint chain (proximal → distal) that moves an effector, with * each joint's ROM expressed as local Euler limits for the constrained solve. @@ -427,7 +439,13 @@ export function createViewer( p.y = Number.isFinite(box.min.y) ? Math.max(0, p.y - box.min.y) : 0; return p; } - const anchor = propAnchors.get(target); + const side = effector.name.endsWith("_left") + ? "left" + : effector.name.endsWith("_right") + ? "right" + : null; + const anchor = (side ? propAnchors.get(`${target}.${side}`) : undefined) + ?? propAnchors.get(target); if (anchor) return anchor.clone(); const bone = mannequin.bones.get(target); if (bone) return bone.getWorldPosition(new THREE.Vector3()); @@ -510,8 +528,10 @@ export function createViewer( } function frame(): void { + let activeContactBones: string[] = []; if (timeline) { const info = timeline.sample(time, mannequin.bones); + activeContactBones = contactBoneIds(info.groundLock, info.pins); // Life layer rides on wall-clock time (not timeline time) so the figure // keeps breathing and blinking while paused or scrubbing. applyLife(performance.now() / 1000); @@ -533,6 +553,7 @@ export function createViewer( // Self-collision: nudge limbs out of the body BEFORE contact solving so // ground-lock and pins see the corrected pose (same order as load()). depenetrate(mannequin); + alignFloorSoles(mannequin, info.groundLock, info.reaches, info.pins); applyGroundLockTo(mannequin, info.groundLock, frameAnchors(info.rootYaw, info.rootOffset)); applyPins(info.pins); // Reach-IK BEFORE the floor safety clamp. When authored FK pushes a @@ -544,6 +565,7 @@ export function createViewer( // floor/landmark targets resolve against. applyReaches(info.reaches); alignFloorPalms(mannequin, info.reaches, info.pins); + alignBarGrips(mannequin, info.reaches, info.pins); // Safety net: nothing above ever intentionally pushes part of the body // below the floor, so clamp the root up whenever the lowest point dips // below y=0, a no-op whenever the pose is legitimately grounded or @@ -574,8 +596,15 @@ export function createViewer( const gap = clipTargetWeight - clipWeight; clipWeight += Math.sign(gap) * Math.min(Math.abs(gap), step); clipLayer.apply(time, clipWeight); - if (clipWeight > 0) character.group.updateMatrixWorld(true); + if (clipWeight > 0) { + character.group.updateMatrixWorld(true); + // Mocap is deliberately layered after procedural posing. Re-apply the + // final contact positions/orientations so the visible mesh cannot skate + // away from feet/hands the driver already solved. + character.correctContacts(mannequin, activeContactBones); + } } + if (propScene?.attachments.length) syncPropAttachments(propScene, mannequin.bones); frameDt = 0; if (easeCamera) { controls.target.lerp(desiredTarget, 0.07); @@ -774,15 +803,16 @@ export { applyGroundLock, groundFigure } from "./groundlock.js"; export type { Mannequin, Proportions, CollisionRadii } from "./mannequin.js"; export { buildTimeline } from "./timeline.js"; export { solveCCD, type IkChain, type JointLimits } from "./ik.js"; -export { buildProps, type PropScene } from "./props.js"; +export { buildProps, syncPropAttachments, type PropScene, type PropAttachment } from "./props.js"; export { loadCharacter, rigCharacter, type Character } from "./character.js"; export { loadClipSource, + selectMotionClip, retargetMocapClip, createClipLayer, type ClipLayer, type ClipSource, } from "./clips.js"; export { depenetrate } from "./depenetrate.js"; -export { alignFloorPalms } from "./contacts.js"; +export { alignBarGrips, alignFloorPalms, alignFloorSoles } from "./contacts.js"; export type { PhaseSegment } from "./timeline.js"; diff --git a/packages/posecode-render/src/props.ts b/packages/posecode-render/src/props.ts index bad7815..ff7170c 100644 --- a/packages/posecode-render/src/props.ts +++ b/packages/posecode-render/src/props.ts @@ -18,6 +18,15 @@ export interface PropScene { group: THREE.Group; /** Anchor name → world-space contact point, merged into reach/ground-lock. */ anchors: Map; + /** Props rigidly attached to a driver bone (weapon/tool sockets). */ + attachments: PropAttachment[]; +} + +export interface PropAttachment { + object: THREE.Object3D; + bone: string; + offset: THREE.Vector3; + rotation: THREE.Quaternion; } /** Build the declared props (`chair | wall | bar | box | dip-bars`). Unknown types are ignored. */ @@ -25,6 +34,7 @@ export function buildProps(types: string[], material?: THREE.Material): PropScen const group = new THREE.Group(); group.name = "posecode-props"; const anchors = new Map(); + const attachments: PropAttachment[] = []; const mat = material ?? new THREE.MeshStandardMaterial({ color: 0x6b7280, roughness: 0.8, metalness: 0.05 }); @@ -54,7 +64,15 @@ export function buildProps(types: string[], material?: THREE.Material): PropScen post.position.set(x, barH / 2, 0); group.add(post); } - anchors.set("bar", new THREE.Vector3(0, barH, 0)); + // The wrist joint belongs slightly below and in front of the cylinder; + // placing the joint at the bar centre made the fingers close as a fist + // above the rail instead of wrapping around it. + const gripY = barH - 0.045; + const gripZ = 0.025; + const gripHalfSpan = 0.24; + anchors.set("bar", new THREE.Vector3(0, gripY, gripZ)); + anchors.set("bar.left", new THREE.Vector3(gripHalfSpan, gripY, gripZ)); + anchors.set("bar.right", new THREE.Vector3(-gripHalfSpan, gripY, gripZ)); } else if (type === "wall") { const wall = box(2.2, 2.6, 0.1, mat); wall.position.set(0, 1.3, -0.34); @@ -87,6 +105,8 @@ export function buildProps(types: string[], material?: THREE.Material): PropScen } } anchors.set("bars", new THREE.Vector3(0, railH, 0)); + anchors.set("bars.left", new THREE.Vector3(halfSpan, railH, 0)); + anchors.set("bars.right", new THREE.Vector3(-halfSpan, railH, 0)); } else if (type === "box") { // A low step/plateau placed IN FRONT of the figure (+Z): the lead foot // steps forward and up onto it. Top surface at ~0.30 m; `box` anchor sits @@ -96,10 +116,54 @@ export function buildProps(types: string[], material?: THREE.Material): PropScen plat.position.set(0, topH / 2, 0.32); group.add(plat); anchors.set("box", new THREE.Vector3(0, topH, 0.3)); + } else if (type === "sword") { + const weapon = new THREE.Group(); + const grip = new THREE.Mesh(new THREE.CylinderGeometry(0.018, 0.018, 0.16, 10), mat); + const guard = box(0.16, 0.025, 0.035, mat); + guard.position.y = -0.09; + const blade = box(0.045, 0.72, 0.012, mat); + blade.position.y = -0.46; + weapon.add(grip, guard, blade); + group.add(weapon); + attachments.push({ + object: weapon, + bone: "wrist_right", + offset: new THREE.Vector3(0, -0.075, 0), + rotation: new THREE.Quaternion(), + }); + } else if (type === "gun") { + const weapon = new THREE.Group(); + const body = box(0.055, 0.09, 0.28, mat); + body.position.z = 0.12; + const handle = box(0.05, 0.16, 0.07, mat); + handle.position.set(0, -0.1, 0.02); + weapon.add(body, handle); + group.add(weapon); + attachments.push({ + object: weapon, + bone: "wrist_right", + offset: new THREE.Vector3(0, -0.035, 0.04), + rotation: new THREE.Quaternion(), + }); } } - return { group, anchors }; + return { group, anchors, attachments }; +} + +/** Follow final solved driver-bone transforms with held props. */ +export function syncPropAttachments(scene: PropScene, bones: Map): void { + const p = new THREE.Vector3(); + const q = new THREE.Quaternion(); + for (const attachment of scene.attachments) { + const bone = bones.get(attachment.bone); + if (!bone) continue; + bone.getWorldPosition(p); + bone.getWorldQuaternion(q); + attachment.object.position.copy(attachment.offset).applyQuaternion(q).add(p); + attachment.object.quaternion.copy(q).multiply(attachment.rotation); + } + scene.group.updateMatrixWorld(true); } function box(w: number, h: number, d: number, mat: THREE.Material): THREE.Mesh { diff --git a/packages/posecode-render/src/timeline.ts b/packages/posecode-render/src/timeline.ts index 6d9585d..120bfd7 100644 --- a/packages/posecode-render/src/timeline.ts +++ b/packages/posecode-render/src/timeline.ts @@ -73,9 +73,12 @@ function eulerToQuat([x, y, z]: EulerDegTuple): THREE.Quaternion { const EASE: Record number> = { linear: (t) => t, - "ease-in": (t) => t * t, - "ease-out": (t) => 1 - (1 - t) * (1 - t), - "ease-in-out": (t) => t * t * (3 - 2 * t), + // Cubic one-sided eases reduce the acceleration discontinuity of the old + // quadratic curves. Smootherstep is C2-continuous at both endpoints, which + // removes the visible "step" as a phase changes direction or contact mode. + "ease-in": (t) => t * t * t, + "ease-out": (t) => 1 - (1 - t) * (1 - t) * (1 - t), + "ease-in-out": (t) => t * t * t * (t * (t * 6 - 15) + 10), }; export function buildTimeline(ir: PosecodeIR): BuiltTimeline { diff --git a/packages/posecode-render/test/character.test.ts b/packages/posecode-render/test/character.test.ts index c81ce98..c3719b4 100644 --- a/packages/posecode-render/test/character.test.ts +++ b/packages/posecode-render/test/character.test.ts @@ -159,4 +159,24 @@ describe("character retargeting", () => { // The ankle rides well above the sole (character feet, not the default shoe). expect(p.soleDrop).toBeGreaterThan(0.05); }); + + it("restores visible contacts after a mocap pose overwrites them", () => { + const char = rigCharacter(makeTposeSkeleton()); + const driver = buildMannequin(undefined, char.proportions); + driver.root.position.set(0.2, 0.1, -0.15); + driver.bones.get("shoulder_left")!.rotation.x = -120 * DEG; + driver.root.updateMatrixWorld(true); + char.sync(driver); + + const hand = char.group.getObjectByName("mixamorigLeftHand")!; + const solvedWorld = hand.getWorldQuaternion(new THREE.Quaternion()); + char.group.position.add(new THREE.Vector3(0.3, -0.2, 0.25)); + hand.rotation.set(0.7, -0.4, 0.2); + char.group.updateMatrixWorld(true); + + char.correctContacts(driver, ["wrist_left"]); + expect(jointGap(driver, char, "wrist_left", "LeftHand")).toBeLessThan(1e-3); + const correctedWorld = hand.getWorldQuaternion(new THREE.Quaternion()); + expect(correctedWorld.angleTo(solvedWorld)).toBeLessThan(1e-3); + }); }); diff --git a/packages/posecode-render/test/clips.test.ts b/packages/posecode-render/test/clips.test.ts index e99c666..b103463 100644 --- a/packages/posecode-render/test/clips.test.ts +++ b/packages/posecode-render/test/clips.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "vitest"; import * as THREE from "three"; -import { retargetMocapClip, createClipLayer } from "../src/clips.js"; +import { retargetMocapClip, createClipLayer, selectMotionClip } from "../src/clips.js"; const DEG = Math.PI / 180; @@ -98,6 +98,14 @@ function track(clip: THREE.AnimationClip, name: string): THREE.KeyframeTrack | u } describe("retargetMocapClip", () => { + it("chooses a moving take over a longer embedded bind-pose take", () => { + const staticTake = new THREE.AnimationClip("Take 001", 8, [ + new THREE.VectorKeyframeTrack("mixamorigHips.position", [0, 8], [0, 1, 0, 0, 1, 0]), + ]); + const movingTake = makeSourceClip(); + expect(selectMotionClip([staticTake, movingTake])?.name).toBe("walk"); + }); + it("emits mixer-ready .bones[] tracks for bones the source animates", () => { const { clip } = retargeted(); expect(track(clip, ".bones[mixamorigLeftArm].quaternion")).toBeDefined(); diff --git a/packages/posecode-render/test/render.test.ts b/packages/posecode-render/test/render.test.ts index 8eca00a..5992888 100644 --- a/packages/posecode-render/test/render.test.ts +++ b/packages/posecode-render/test/render.test.ts @@ -4,8 +4,9 @@ import { buildMannequin } from "../src/mannequin.js"; import { buildTimeline } from "../src/timeline.js"; import { solveCCD } from "../src/ik.js"; import { poseFor } from "../src/poses.js"; -import { buildProps } from "../src/props.js"; +import { buildProps, syncPropAttachments } from "../src/props.js"; import { applyGroundLock, groundFigure } from "../src/groundlock.js"; +import { alignBarGrips, alignFloorSoles } from "../src/contacts.js"; import { parse, eulerRomFor } from "posecode-parser"; const DEG = Math.PI / 180; @@ -398,11 +399,59 @@ describe("props", () => { const { anchors, group } = buildProps(["chair", "bar", "wall"]); expect(anchors.has("seat")).toBe(true); expect(anchors.has("bar")).toBe(true); + expect(anchors.has("bar.left")).toBe(true); + expect(anchors.has("bar.right")).toBe(true); + expect(anchors.get("bar.left")!.x).toBeGreaterThan(anchors.get("bar.right")!.x); expect(anchors.has("wall")).toBe(true); expect(anchors.get("bar")!.y).toBeGreaterThan(1.5); // overhead expect(group.children.length).toBeGreaterThan(0); }); + it("attaches held sword and gun props to the solved wrist socket", () => { + const props = buildProps(["sword", "gun"]); + const m = buildMannequin(); + m.root.position.set(0.4, 0.2, -0.3); + m.root.updateMatrixWorld(true); + syncPropAttachments(props, m.bones); + expect(props.attachments).toHaveLength(2); + const wrist = m.bones.get("wrist_right")!.getWorldPosition(new THREE.Vector3()); + for (const attachment of props.attachments) { + expect(attachment.object.position.distanceTo(wrist)).toBeLessThan(0.2); + } + }); + +}); + +describe("oriented contacts", () => { + it("keeps grounded soles flat and facing with the figure", () => { + const m = buildMannequin(); + m.bones.get("hip_left")!.rotation.x = -70 * DEG; + m.bones.get("knee_left")!.rotation.x = 90 * DEG; + m.root.rotation.y = 35 * DEG; + m.root.updateMatrixWorld(true); + alignFloorSoles(m, ["feet"]); + const q = m.bones.get("ankle_left")!.getWorldQuaternion(new THREE.Quaternion()); + const up = new THREE.Vector3(0, 1, 0).applyQuaternion(q); + expect(up.y).toBeGreaterThan(0.999); + }); + + it("orients both palms into a stable overhand bar grip", () => { + const m = buildMannequin(); + m.root.updateMatrixWorld(true); + const pins = [ + { effector: "hand_left", anchor: "bar" }, + { effector: "hand_right", anchor: "bar" }, + ]; + alignBarGrips(m, [], pins); + for (const side of ["left", "right"] as const) { + const q = m.bones.get(`wrist_${side}`)!.getWorldQuaternion(new THREE.Quaternion()); + const localNormal = side === "left" + ? new THREE.Vector3(1, 0, 0) + : new THREE.Vector3(-1, 0, 0); + const normal = localNormal.applyQuaternion(q); + expect(normal.z).toBeGreaterThan(0.999); + } + }); }); describe("ccd ik", () => { diff --git a/playground/public/moves/bent-over-row.html b/playground/public/moves/bent-over-row.html index 04bff67..4156639 100644 --- a/playground/public/moves/bent-over-row.html +++ b/playground/public/moves/bent-over-row.html @@ -161,29 +161,35 @@

The .posecode source

step "Set the hinge" 1.6s ease-in-out: pelvis: hinge 70 knees: flex 20 - shoulders: extend 55 + ankles: plantarflex 20 + shoulders: flex 65 elbows: flex 10 + elbows: pronate 80 neck: extend 12 ground-lock: feet cue "Hinge to a flat back, let the arms hang straight down" step "Row" 0.9s ease-out: - shoulders: extend 15 + shoulders: extend 10 elbows: flex 95 + elbows: pronate 80 ground-lock: feet cue "Drive the elbows up past the ribs, squeeze the shoulder blades" step "Lower" 1s ease-in: - shoulders: extend 55 + shoulders: flex 65 elbows: flex 10 + elbows: pronate 80 ground-lock: feet cue "Lower the bar under control, keep the back flat" step "Stand" 1s ease-out: pelvis: hinge 0 knees: flex 0 - shoulders: extend 0 + ankles: plantarflex 0 + shoulders: flex 0 elbows: flex 0 + elbows: pronate 0 neck: extend 0 ground-lock: feet cue "Stand up tall between sets" diff --git a/playground/public/moves/biceps.html b/playground/public/moves/biceps.html index a3907f6..fe39aaf 100644 --- a/playground/public/moves/biceps.html +++ b/playground/public/moves/biceps.html @@ -152,10 +152,12 @@

The .posecode source

step "Curl" 1.1s ease-out: elbows: flex 135 + elbows: supinate 80 cue "Curl up, keep the elbows tucked at your sides" step "Lower" 1.4s ease-in: elbows: flex 15 + elbows: supinate 80 cue "Lower under control: don't swing" repeat 10 diff --git a/playground/public/moves/bicycle-crunch.html b/playground/public/moves/bicycle-crunch.html index e33a9ee..a0511aa 100644 --- a/playground/public/moves/bicycle-crunch.html +++ b/playground/public/moves/bicycle-crunch.html @@ -163,28 +163,34 @@

The .posecode source

knees: flex 90 shoulders: flex 100 elbows: flex 90 - spine: flex 12 + spine: flex 25 + chest: flex 15 neck: flex 20 cue "Hands by the ears, knees over the hips, shoulders lifted" step "Right to left" 0.8s ease-in-out: - spine: rotate-out 20 + spine: rotate-in 30 + chest: rotate-in 20 hip_left: flex 110 knee_left: flex 90 - hip_right: flex 25 - knee_right: flex 20 + hip_right: flex 15 + knee_right: flex 5 + reach: elbow_right knee_left cue "Right elbow toward the left knee; the right leg extends long" step "Left to right" 0.8s ease-in-out: - spine: rotate-in 20 + spine: rotate-out 30 + chest: rotate-out 20 hip_right: flex 110 knee_right: flex 90 - hip_left: flex 25 - knee_left: flex 20 + hip_left: flex 15 + knee_left: flex 5 + reach: elbow_left knee_right cue "Switch: left elbow toward the right knee" step "Center" 0.6s ease-out: spine: rotate-in 0 + chest: rotate-in 0 hip_right: flex 90 knee_right: flex 90 hip_left: flex 90 diff --git a/playground/public/moves/box-squat.html b/playground/public/moves/box-squat.html index a453158..73666c8 100644 --- a/playground/public/moves/box-squat.html +++ b/playground/public/moves/box-squat.html @@ -154,8 +154,9 @@

The .posecode source

step "Sit back" 1.6s ease-in-out: hips: flex 85 knees: flex 90 - ankles: dorsiflex 12 - spine: flex 10 + ankles: dorsiflex 15 + pelvis: hinge 20 + spine: flex 0 shoulders: flex 70 ground-lock: feet cue "Push the hips back and sit lightly onto the box" @@ -163,7 +164,8 @@

The .posecode source

step "Stand" 1.4s ease-out: hips: flex 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 + pelvis: hinge 0 spine: flex 0 shoulders: flex 0 ground-lock: feet diff --git a/playground/public/moves/box-step-taps.html b/playground/public/moves/box-step-taps.html index e333881..0176232 100644 --- a/playground/public/moves/box-step-taps.html +++ b/playground/public/moves/box-step-taps.html @@ -162,21 +162,29 @@

The .posecode source

step "Right tap" 0.6s ease-out: hip_right: flex 70 knee_right: flex 90 + ankle_right: plantarflex 50 + pin: foot_left floor cue "Tap the right foot lightly on top of the box" step "Right down" 0.5s ease-in: hip_right: flex 0 knee_right: flex 0 + ankle_right: plantarflex 0 + ground-lock: feet cue "Return the right foot to the floor" step "Left tap" 0.6s ease-out: hip_left: flex 70 knee_left: flex 90 + ankle_left: plantarflex 50 + pin: foot_right floor cue "Tap the left foot on the box" step "Left down" 0.5s ease-in: hip_left: flex 0 knee_left: flex 0 + ankle_left: plantarflex 0 + ground-lock: feet cue "Back to the floor: keep a light, quick rhythm" repeat 6 diff --git a/playground/public/moves/box-step.html b/playground/public/moves/box-step.html index 86e74bb..70ea919 100644 --- a/playground/public/moves/box-step.html +++ b/playground/public/moves/box-step.html @@ -161,15 +161,17 @@

The .posecode source

step "Step forward-right" 0.9s ease-in-out: hip_right: flex 35 knee_right: flex 40 + ankle_right: plantarflex 40 shoulder_left: flex 25 shoulder_right: extend 15 travel: -0.35 0.35 - ground-lock: feet + pin: foot_left floor cue "Step the right foot forward and out to the corner" step "Close the feet" 0.7s ease-in-out: hip_right: flex 0 knee_right: flex 0 + ankle_right: plantarflex 0 shoulders: flex 0 travel: -0.35 0.35 ground-lock: feet @@ -178,15 +180,17 @@

The .posecode source

step "Step back-left" 0.9s ease-in-out: hip_left: flex 35 knee_left: flex 40 + ankle_left: plantarflex 40 shoulder_right: flex 25 shoulder_left: extend 15 travel: 0 0 - ground-lock: feet + pin: foot_right floor cue "Step the left foot back to the start on the diagonal" step "Close home" 0.7s ease-in-out: hip_left: flex 0 knee_left: flex 0 + ankle_left: plantarflex 0 shoulders: flex 0 travel: 0 0 ground-lock: feet diff --git a/playground/public/moves/calf-raise.html b/playground/public/moves/calf-raise.html index 60f0ea1..0f5c56f 100644 --- a/playground/public/moves/calf-raise.html +++ b/playground/public/moves/calf-raise.html @@ -152,12 +152,14 @@

The .posecode source

step "Rise" 1.1s ease-out: ankle_right: plantarflex 40 - knee_left: flex 90 + knee_left: flex 60 + pin: foot_right floor cue "Balance on the right foot and rise onto the ball of the foot" step "Lower" 1.3s ease-in: ankle_right: plantarflex 0 - knee_left: flex 90 + knee_left: flex 60 + pin: foot_right floor cue "Lower the right heel slowly under control" repeat 10 diff --git a/playground/public/moves/chair.html b/playground/public/moves/chair.html index cc0ec57..98257c7 100644 --- a/playground/public/moves/chair.html +++ b/playground/public/moves/chair.html @@ -153,18 +153,20 @@

The .posecode source

step "Sink" 3s ease-in-out: hips: flex 55 knees: flex 70 - ankles: dorsiflex 12 + ankles: dorsiflex 15 shoulders: flex 170 - spine: flex 10 + pelvis: hinge 15 + spine: extend 5 ground-lock: feet cue "Sit the hips back and down, reach the arms overhead" step "Rise" 2s ease-out: hips: flex 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 shoulders: flex 0 - spine: flex 0 + pelvis: hinge 0 + spine: extend 0 ground-lock: feet cue "Press through the feet to stand, arms float down" diff --git a/playground/public/moves/chasse.html b/playground/public/moves/chasse.html index 8dba1fb..68a6d99 100644 --- a/playground/public/moves/chasse.html +++ b/playground/public/moves/chasse.html @@ -161,16 +161,19 @@

The .posecode source

step "Reach & push" 0.8s ease-out: hip_right: flex 30 knee_right: flex 25 + ankle_right: plantarflex 25 ankle_left: plantarflex 20 shoulders: abduct 60 elbows: flex 18 travel: 0.5 0 - ground-lock: feet + pin: foot_left floor + reach: foot_right floor cue "Reach the lead foot out and push off to travel sideways" step "Gallop close" 0.6s ease-in: hip_right: flex 0 knee_right: flex 0 + ankle_right: plantarflex 0 ankle_left: plantarflex 0 travel: 0.9 0 ground-lock: feet @@ -179,14 +182,17 @@

The .posecode source

step "Reach & push back" 0.8s ease-out: hip_left: flex 30 knee_left: flex 25 + ankle_left: plantarflex 25 ankle_right: plantarflex 20 travel: 0.4 0 - ground-lock: feet + pin: foot_right floor + reach: foot_left floor cue "Change direction and travel back the other way" step "Gallop home" 0.6s ease-in: hip_left: flex 0 knee_left: flex 0 + ankle_left: plantarflex 0 ankle_right: plantarflex 0 shoulders: abduct 0 elbows: flex 0 diff --git a/playground/public/moves/cobra.html b/playground/public/moves/cobra.html index e47268c..01aa2b3 100644 --- a/playground/public/moves/cobra.html +++ b/playground/public/moves/cobra.html @@ -151,11 +151,14 @@

The .posecode source

pose start = prone step "Lift" 2.5s ease-in-out: - spine: extend 30 + spine: extend 25 chest: extend 15 - neck: extend 25 - shoulders: flex 50 - elbows: flex 25 + neck: extend 20 + shoulders: flex 110 + elbows: flex 40 + elbows: pronate 80 + pin: pelvis floor + pin: feet floor reach: hands floor cue "Press the palms into the floor and lift the chest, shoulders rolling back" @@ -165,6 +168,9 @@

The .posecode source

neck: extend 0 shoulders: flex 0 elbows: flex 0 + elbows: pronate 80 + pin: pelvis floor + pin: feet floor reach: hands floor cue "Lower the chest back to the floor with control" diff --git a/playground/public/moves/crunch.html b/playground/public/moves/crunch.html index df42895..8e2570c 100644 --- a/playground/public/moves/crunch.html +++ b/playground/public/moves/crunch.html @@ -8,11 +8,11 @@ - + - + @@ -98,7 +98,7 @@ footer.site-footer p{font-size:12.5px;color:var(--muted);margin:0 0 6px;max-width:70ch} @media(max-width:600px){.wrap{padding:0 18px}} - + @@ -133,6 +133,10 @@

Crunch

How to do it

    +
  1. + Set up0.5s · ease-in-out +   +
  2. Curl up1s · ease-out Curl the shoulders off the floor, ribs toward the hips @@ -150,22 +154,33 @@

    The .posecode source

    rig humanoid pose start = supine + step "Set up" 0.5s ease-in-out: + hips: flex 45 + knees: flex 90 + ankles: plantarflex 50 + shoulders: flex 45 + pin: feet floor + step "Curl up" 1s ease-out: + hips: flex 45 knees: flex 90 + ankles: plantarflex 50 + shoulders: flex 45 spine: flex 30 chest: flex 20 - neck: flex 20 - shoulders: flex 45 - ground-lock: feet + neck: flex 15 + pin: feet floor cue "Curl the shoulders off the floor, ribs toward the hips" step "Lower" 1.2s ease-in: - knees: flex 0 + hips: flex 45 + knees: flex 90 + ankles: plantarflex 50 + shoulders: flex 45 spine: flex 0 chest: flex 0 neck: flex 0 - shoulders: flex 0 - ground-lock: feet + pin: feet floor cue "Lower the upper back down with control" repeat 12 diff --git a/playground/public/moves/dance-phrase.html b/playground/public/moves/dance-phrase.html index 0b8732f..5e27425 100644 --- a/playground/public/moves/dance-phrase.html +++ b/playground/public/moves/dance-phrase.html @@ -169,7 +169,7 @@

    The .posecode source

    step "3-4 - demi-plié" 2s ease-in-out: hips: flex 18 knees: flex 50 - ankles: dorsiflex 12 + ankles: plantarflex 50 shoulders: flex 28 elbows: flex 30 ground-lock: feet diff --git a/playground/public/moves/dead-bug.html b/playground/public/moves/dead-bug.html index 42c9854..ce37106 100644 --- a/playground/public/moves/dead-bug.html +++ b/playground/public/moves/dead-bug.html @@ -166,8 +166,8 @@

    The .posecode source

    step "Extend right arm & left leg" 1.2s ease-in-out: shoulder_right: flex 150 - hip_left: flex 25 - knee_left: flex 30 + hip_left: flex 20 + knee_left: flex 5 cue "Lower the right arm overhead and the left leg toward the floor" step "Switch" 1.2s ease-in-out: @@ -175,8 +175,8 @@

    The .posecode source

    hip_left: flex 90 knee_left: flex 90 shoulder_left: flex 150 - hip_right: flex 25 - knee_right: flex 30 + hip_right: flex 20 + knee_right: flex 5 cue "Return and switch: left arm and right leg reach out" step "Return" 1s ease-out: diff --git a/playground/public/moves/dead-hang.html b/playground/public/moves/dead-hang.html index 6199490..48904a5 100644 --- a/playground/public/moves/dead-hang.html +++ b/playground/public/moves/dead-hang.html @@ -158,18 +158,24 @@

    The .posecode source

    step "Reach" 0.8s ease-out: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 ground-lock: feet cue "Reach up and grip the bar" step "Hang" 3s ease-in-out: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 pin: hands bar cue "Relax into a long, straight-arm hang" step "Down" 1.5s ease-out: shoulders: flex 0 elbows: flex 0 + elbows: pronate 0 + fingers: flex 0 ground-lock: feet cue "Drop down off the bar and shake out the arms" diff --git a/playground/public/moves/deadlift.html b/playground/public/moves/deadlift.html index 5b173d0..480f1b9 100644 --- a/playground/public/moves/deadlift.html +++ b/playground/public/moves/deadlift.html @@ -153,7 +153,9 @@

    The .posecode source

    step "Lower" 1.8s ease-in-out: pelvis: hinge 75 knees: flex 25 - shoulders: extend 60 + ankles: plantarflex 0 + shoulders: flex 70 + elbows: pronate 80 neck: extend 12 ground-lock: feet cue "Push the hips back and hinge with a flat back: let the arms hang to the bar" @@ -161,7 +163,9 @@

    The .posecode source

    step "Lift" 1.4s ease-out: pelvis: hinge 0 knees: flex 0 - shoulders: extend 0 + ankles: plantarflex 0 + shoulders: flex 0 + elbows: pronate 80 neck: extend 0 ground-lock: feet cue "Drive the hips forward to stand tall, bar close to the body" diff --git a/playground/public/moves/demi-plie.html b/playground/public/moves/demi-plie.html index 61956f7..4a6d1e0 100644 --- a/playground/public/moves/demi-plie.html +++ b/playground/public/moves/demi-plie.html @@ -154,7 +154,7 @@

    The .posecode source

    hips: flex 20 hips: rotate-out 30 knees: flex 55 - ankles: dorsiflex 12 + ankles: dorsiflex 15 shoulders: abduct 70 elbows: flex 15 spine: extend 4 @@ -165,7 +165,7 @@

    The .posecode source

    hips: flex 0 hips: rotate-out 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 shoulders: abduct 0 elbows: flex 0 spine: flex 0 diff --git a/playground/public/moves/fold.html b/playground/public/moves/fold.html index 72a1c2e..80ecdfb 100644 --- a/playground/public/moves/fold.html +++ b/playground/public/moves/fold.html @@ -156,6 +156,7 @@

    The .posecode source

    neck: flex 20 shoulders: flex 95 knees: flex 10 + ankles: plantarflex 10 ground-lock: feet cue "Drop the chin and round down one vertebra at a time" @@ -165,6 +166,7 @@

    The .posecode source

    neck: flex 0 shoulders: flex 0 knees: flex 0 + ankles: plantarflex 0 ground-lock: feet cue "Stack the spine back up to standing" diff --git a/playground/public/moves/forward-lunge.html b/playground/public/moves/forward-lunge.html index 4e0f041..7b3f2ef 100644 --- a/playground/public/moves/forward-lunge.html +++ b/playground/public/moves/forward-lunge.html @@ -154,10 +154,12 @@

    The .posecode source

    hip_right: flex 45 knee_right: flex 95 hip_left: extend 15 - knee_left: flex 45 - ankle_right: dorsiflex 14 + knee_left: flex 80 + ankle_right: plantarflex 50 spine: extend 4 - ground-lock: feet + travel: 0 0.3 + pin: foot_left floor + reach: foot_right floor cue "Step forward and lower the back knee toward the floor" step "Drive up" 1.4s ease-out: @@ -165,8 +167,9 @@

    The .posecode source

    knee_right: flex 0 hip_left: extend 0 knee_left: flex 0 - ankle_right: dorsiflex 0 + ankle_right: plantarflex 0 spine: flex 0 + travel: 0 0 ground-lock: feet cue "Push through the front heel to stand tall" diff --git a/playground/public/moves/front-kick.html b/playground/public/moves/front-kick.html index 6c1a23d..c9ee838 100644 --- a/playground/public/moves/front-kick.html +++ b/playground/public/moves/front-kick.html @@ -161,21 +161,25 @@

    The .posecode source

    step "Chamber" 0.5s ease-in: hip_right: flex 95 knee_right: flex 90 + pin: foot_left floor cue "Drive the right knee up to chamber the kick" step "Extend" 0.35s ease-out: hip_right: flex 100 knee_right: flex 5 + pin: foot_left floor cue "Snap the lower leg out: strike with the ball of the foot" step "Re-chamber" 0.45s ease-in: hip_right: flex 95 knee_right: flex 90 + pin: foot_left floor cue "Snap the foot back to chamber" step "Return" 0.6s ease-out: hip_right: flex 0 knee_right: flex 0 + ground-lock: feet cue "Set the foot back down to a fighting stance" repeat 5 diff --git a/playground/public/moves/glute-bridge.html b/playground/public/moves/glute-bridge.html index d7c6843..00af6aa 100644 --- a/playground/public/moves/glute-bridge.html +++ b/playground/public/moves/glute-bridge.html @@ -152,14 +152,21 @@

    The .posecode source

    step "Bridge up" 1.6s ease-out: knees: flex 90 - hips: extend 15 + ankles: plantarflex 50 + hips: extend 20 spine: extend 10 + shoulders: rotate-in 70 + elbows: pronate 80 + reach: hands floor ground-lock: feet cue "Press through the feet and lift the hips toward the ceiling" step "Lower" 1.6s ease-in: hips: extend 0 spine: flex 0 + shoulders: rotate-in 70 + elbows: pronate 80 + reach: hands floor ground-lock: feet cue "Lower the hips back to the floor, one vertebra at a time" diff --git a/playground/public/moves/good-morning.html b/playground/public/moves/good-morning.html index d1148fb..2cf81ed 100644 --- a/playground/public/moves/good-morning.html +++ b/playground/public/moves/good-morning.html @@ -153,6 +153,7 @@

    The .posecode source

    step "Hinge" 2s ease-in-out: pelvis: hinge 80 knees: flex 15 + ankles: plantarflex 15 shoulders: abduct 45 elbows: flex 110 neck: extend 10 @@ -162,8 +163,9 @@

    The .posecode source

    step "Stand" 1.8s ease-out: pelvis: hinge 0 knees: flex 0 - shoulders: abduct 0 - elbows: flex 0 + ankles: plantarflex 0 + shoulders: abduct 45 + elbows: flex 110 neck: extend 0 ground-lock: feet cue "Squeeze the glutes to return to standing tall" diff --git a/playground/public/moves/grapevine.html b/playground/public/moves/grapevine.html index 1348013..7be0a65 100644 --- a/playground/public/moves/grapevine.html +++ b/playground/public/moves/grapevine.html @@ -162,23 +162,28 @@

    The .posecode source

    hip_left: abduct 30 shoulders: abduct 40 travel: 0.4 0 - ground-lock: feet + pin: foot_right floor + reach: foot_left floor cue "Step the leading foot out to the side" step "Cross behind" 0.7s ease-in-out: hip_left: abduct 0 hip_right: rotate-in 20 knee_right: flex 25 + ankle_right: plantarflex 25 travel: 0.8 0 - ground-lock: feet + pin: foot_left floor + reach: foot_right floor cue "Cross the trailing foot behind and keep travelling" step "Step out again" 0.7s ease-in-out: hip_right: rotate-in 0 knee_right: flex 0 + ankle_right: plantarflex 0 hip_left: abduct 30 travel: 1.2 0 - ground-lock: feet + pin: foot_right floor + reach: foot_left floor cue "Step out to the side once more" step "Return home" 1s ease-in-out: diff --git a/playground/public/moves/hamstring-curl.html b/playground/public/moves/hamstring-curl.html index 6f98884..6ac2006 100644 --- a/playground/public/moves/hamstring-curl.html +++ b/playground/public/moves/hamstring-curl.html @@ -152,10 +152,12 @@

    The .posecode source

    step "Curl" 1.4s ease-out: knee_right: flex 95 + pin: foot_left floor cue "Bend the right knee, drawing the heel toward the glute" step "Lower" 1.6s ease-in: knee_right: flex 0 + pin: foot_left floor cue "Lower the foot back to the floor" repeat 10 diff --git a/playground/public/moves/hanging-knee-raise.html b/playground/public/moves/hanging-knee-raise.html index ffd78d4..c5fc50a 100644 --- a/playground/public/moves/hanging-knee-raise.html +++ b/playground/public/moves/hanging-knee-raise.html @@ -166,12 +166,16 @@

    The .posecode source

    step "Reach" 0.8s ease-out: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 ground-lock: feet cue "Reach up and grip the bar" step "Grip" 0.7s ease-in-out: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 pin: hand_left bar pin: hand_right bar cue "Hang from the bar overhead, arms long, body still" @@ -181,6 +185,8 @@

    The .posecode source

    knees: flex 90 shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 pin: hand_left bar pin: hand_right bar cue "Draw both knees up toward the chest" @@ -190,6 +196,8 @@

    The .posecode source

    knees: flex 0 shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 pin: hand_left bar pin: hand_right bar cue "Lower the legs under control, staying in a steady hang" @@ -197,6 +205,8 @@

    The .posecode source

    step "Release" 0.8s ease-in: shoulders: flex 0 elbows: flex 0 + elbows: pronate 0 + fingers: flex 0 ground-lock: feet cue "Drop off the bar and rest" diff --git a/playground/public/moves/high-knee-march.html b/playground/public/moves/high-knee-march.html index 69dc957..8dd6389 100644 --- a/playground/public/moves/high-knee-march.html +++ b/playground/public/moves/high-knee-march.html @@ -158,6 +158,7 @@

    The .posecode source

    hip_right: flex 90 knee_right: flex 90 shoulder_left: flex 40 + pin: foot_left floor cue "Drive the right knee up to hip height, opposite arm swings" step "Switch" 0.7s ease-in-out: @@ -167,12 +168,14 @@

    The .posecode source

    hip_left: flex 90 knee_left: flex 90 shoulder_right: flex 40 + pin: foot_right floor cue "Plant and drive the left knee up" step "Down" 0.6s ease-out: hip_left: flex 0 knee_left: flex 0 shoulder_right: flex 0 + ground-lock: feet cue "Return to a tall, ready stance" repeat 6 diff --git a/playground/public/moves/hip-abduction.html b/playground/public/moves/hip-abduction.html index 09a4cc4..baea747 100644 --- a/playground/public/moves/hip-abduction.html +++ b/playground/public/moves/hip-abduction.html @@ -152,10 +152,12 @@

    The .posecode source

    step "Lift" 1.6s ease-in-out: hip_right: abduct 40 + pin: foot_left floor cue "Lift the right leg out to the side, keep the torso tall" step "Lower" 1.6s ease-in-out: hip_right: abduct 0 + ground-lock: feet cue "Lower the leg back to the midline" repeat 10 diff --git a/playground/public/moves/hip-flexion.html b/playground/public/moves/hip-flexion.html index cf51b6a..b2c227e 100644 --- a/playground/public/moves/hip-flexion.html +++ b/playground/public/moves/hip-flexion.html @@ -152,10 +152,12 @@

    The .posecode source

    step "Lift" 2.5s ease-in-out: hip_right: flex 90 + pin: foot_left floor cue "Raise the straight right leg forward: sagittal-plane hip flexion" step "Lower" 2.5s ease-in-out: hip_right: flex 0 + ground-lock: feet cue "Lower the leg back under the hip" repeat 4 diff --git a/playground/public/moves/horse-stance.html b/playground/public/moves/horse-stance.html index cff86b1..9f4784d 100644 --- a/playground/public/moves/horse-stance.html +++ b/playground/public/moves/horse-stance.html @@ -154,7 +154,7 @@

    The .posecode source

    hips: flex 30 hips: abduct 25 knees: flex 90 - ankles: dorsiflex 10 + ankles: dorsiflex 15 spine: extend 5 shoulders: flex 80 elbows: flex 90 @@ -165,7 +165,7 @@

    The .posecode source

    hips: flex 0 hips: abduct 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 spine: flex 0 shoulders: flex 0 elbows: flex 0 diff --git a/playground/public/moves/jab-cross.html b/playground/public/moves/jab-cross.html index 186b1ee..3701b01 100644 --- a/playground/public/moves/jab-cross.html +++ b/playground/public/moves/jab-cross.html @@ -161,25 +161,37 @@

    The .posecode source

    step "Jab" 0.4s ease-out: shoulder_left: flex 85 elbow_left: flex 15 + elbow_left: pronate 80 spine: rotate-out 10 + fingers: flex 80 + ground-lock: feet cue "Snap the lead (left) hand straight out, rotating slightly into it" step "Recoil jab" 0.4s ease-in: shoulder_left: flex 0 elbow_left: flex 90 + elbow_left: pronate 0 spine: rotate-out 0 + fingers: flex 80 + ground-lock: feet cue "Bring the hand back to guard" step "Cross" 0.45s ease-out: shoulder_right: flex 90 elbow_right: flex 10 + elbow_right: pronate 80 spine: rotate-in 35 + fingers: flex 80 + ground-lock: feet cue "Drive the rear (right) hand across, rotating the trunk" step "Recoil cross" 0.45s ease-in: shoulder_right: flex 0 elbow_right: flex 90 + elbow_right: pronate 0 spine: rotate-in 0 + fingers: flex 80 + ground-lock: feet cue "Return to guard, hands high" repeat 4 diff --git a/playground/public/moves/knee-flexion.html b/playground/public/moves/knee-flexion.html index c5f5202..a13bef4 100644 --- a/playground/public/moves/knee-flexion.html +++ b/playground/public/moves/knee-flexion.html @@ -152,10 +152,12 @@

    The .posecode source

    step "Flex" 2.5s ease-in-out: knee_right: flex 130 + pin: foot_left floor cue "Bend the right knee, heel toward the glutes: the knee's single plane" step "Extend" 2.5s ease-in-out: knee_right: flex 0 + pin: foot_left floor cue "Lower the shin back to standing" repeat 4 diff --git a/playground/public/moves/mountain-climber.html b/playground/public/moves/mountain-climber.html index e69a044..0936a1b 100644 --- a/playground/public/moves/mountain-climber.html +++ b/playground/public/moves/mountain-climber.html @@ -157,7 +157,9 @@

    The .posecode source

    step "Right knee in" 0.5s ease-out: hip_right: flex 60 knee_right: flex 75 - ground-lock: hands, feet + elbows: pronate 80 + pin: foot_left floor + pin: hands floor cue "Drive the right knee toward the chest" step "Switch" 0.5s ease-in-out: @@ -165,12 +167,15 @@

    The .posecode source

    knee_right: flex 0 hip_left: flex 60 knee_left: flex 75 - ground-lock: hands, feet + elbows: pronate 80 + pin: foot_right floor + pin: hands floor cue "Switch fast: left knee drives in" step "Out" 0.5s ease-in: hip_left: flex 0 knee_left: flex 0 + elbows: pronate 80 ground-lock: hands, feet cue "Return to a strong plank" diff --git a/playground/public/moves/pirouette.html b/playground/public/moves/pirouette.html index b97f617..d0687cd 100644 --- a/playground/public/moves/pirouette.html +++ b/playground/public/moves/pirouette.html @@ -157,24 +157,30 @@

    The .posecode source

    step "Prep - plié" 1.2s ease-in-out: hips: rotate-out 25 knees: flex 45 - ankles: dorsiflex 10 + ankles: plantarflex 45 shoulders: flex 40 elbows: flex 35 ground-lock: feet cue "Plié to load the turn, arms rounded low in first" step "Spot & spin" 1.4s ease-in-out: - knees: flex 0 - ankles: plantarflex 30 + knee_left: flex 0 + ankle_left: plantarflex 35 + hip_right: flex 55 + hip_right: rotate-out 45 + knee_right: flex 110 + ankle_right: plantarflex 35 shoulders: flex 95 elbows: flex 40 turn: 360 - ground-lock: feet + pin: foot_left floor cue "Push up to relevé, pull the arms in, and spin a full turn" step "Land" 1s ease-out: - ankles: plantarflex 0 + hips: flex 0 hips: rotate-out 0 + knees: flex 0 + ankles: plantarflex 0 shoulders: flex 0 elbows: flex 0 ground-lock: feet diff --git a/playground/public/moves/plank-hold.html b/playground/public/moves/plank-hold.html index 0749cc8..c58ec04 100644 --- a/playground/public/moves/plank-hold.html +++ b/playground/public/moves/plank-hold.html @@ -98,7 +98,7 @@ footer.site-footer p{font-size:12.5px;color:var(--muted);margin:0 0 6px;max-width:70ch} @media(max-width:600px){.wrap{padding:0 18px}} - + @@ -134,12 +134,12 @@

    Plank hold

    How to do it

    1. - Brace3s · ease-in-out - Brace the core: one straight line from head to heels + Brace0.5s · ease-in-out + Lower onto the forearms, elbows directly under the shoulders
    2. Hold3s · linear - Keep breathing, ribs down, glutes engaged + Brace the core: one straight line from head to heels
    @@ -150,16 +150,21 @@

    The .posecode source

    rig humanoid pose start = plank - step "Brace" 3s ease-in-out: - spine: hold neutral - hips: hold neutral - ground-lock: hands, feet - cue "Brace the core: one straight line from head to heels" + step "Brace" 0.5s ease-in-out: + shoulders: flex 90 + elbows: flex 90 + elbows: pronate 80 + ground-lock: forearms, feet + reach: hands floor + cue "Lower onto the forearms, elbows directly under the shoulders" step "Hold" 3s linear: - spine: hold neutral - ground-lock: hands, feet - cue "Keep breathing, ribs down, glutes engaged" + shoulders: flex 90 + elbows: flex 90 + elbows: pronate 80 + ground-lock: forearms, feet + reach: hands floor + cue "Brace the core: one straight line from head to heels" repeat 3 diff --git a/playground/public/moves/pull-up.html b/playground/public/moves/pull-up.html index bcedd9a..b035626 100644 --- a/playground/public/moves/pull-up.html +++ b/playground/public/moves/pull-up.html @@ -166,30 +166,58 @@

    The .posecode source

    step "Reach" 0.8s ease-out: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 20 ground-lock: feet cue "Reach up and grip the bar" step "Hang" 0.7s ease-in-out: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 55 + thumb_left: flex 35 + thumb_right: flex 35 + thumb_left: abduct 20 + thumb_right: abduct 20 pin: hands bar cue "Hang from the bar, arms long, shoulders active" step "Pull up" 1.2s ease-out: - shoulders: flex 150 + shoulders: flex 105 elbows: flex 130 + elbows: pronate 80 + fingers: flex 55 + thumb_left: flex 35 + thumb_right: flex 35 + thumb_left: abduct 20 + thumb_right: abduct 20 + spine: extend 15 + chest: extend 10 + neck: extend 20 pin: hands bar cue "Pull the chest toward the bar, driving the elbows down" step "Lower" 1.4s ease-in: shoulders: flex 175 elbows: flex 5 + elbows: pronate 80 + fingers: flex 55 + thumb_left: flex 35 + thumb_right: flex 35 + thumb_left: abduct 20 + thumb_right: abduct 20 + spine: extend 0 + chest: extend 0 + neck: extend 0 pin: hands bar cue "Lower under control back to a full hang" step "Release" 0.8s ease-in: shoulders: flex 0 elbows: flex 0 + elbows: pronate 0 + fingers: flex 0 ground-lock: feet cue "Drop off the bar and rest" diff --git a/playground/public/moves/quad-stretch.html b/playground/public/moves/quad-stretch.html index 3319741..056cf18 100644 --- a/playground/public/moves/quad-stretch.html +++ b/playground/public/moves/quad-stretch.html @@ -154,11 +154,13 @@

    The .posecode source

    knee_right: flex 144 hip_right: extend 20 reach: hand_right ankle_right + pin: foot_left floor cue "Bend the right knee back and reach the hand for the ankle" step "Release" 2s ease-out: knee_right: flex 0 hip_right: extend 0 + pin: foot_left floor cue "Release the foot and return to standing" repeat 2 diff --git a/playground/public/moves/quarter-turns.html b/playground/public/moves/quarter-turns.html index f66a2c9..5a3414a 100644 --- a/playground/public/moves/quarter-turns.html +++ b/playground/public/moves/quarter-turns.html @@ -161,6 +161,7 @@

    The .posecode source

    step "Face right" 1s ease-in-out: hips: flex 12 knees: flex 20 + ankles: plantarflex 20 shoulders: abduct 30 turn: 90 ground-lock: feet @@ -169,6 +170,7 @@

    The .posecode source

    step "Face back" 1s ease-in-out: knees: flex 0 hips: flex 0 + ankles: plantarflex 0 shoulders: abduct 0 turn: 180 ground-lock: feet @@ -177,6 +179,7 @@

    The .posecode source

    step "Face left" 1s ease-in-out: hips: flex 12 knees: flex 20 + ankles: plantarflex 20 shoulders: abduct 30 turn: 270 ground-lock: feet @@ -185,6 +188,7 @@

    The .posecode source

    step "Face front" 1s ease-in-out: knees: flex 0 hips: flex 0 + ankles: plantarflex 0 shoulders: abduct 0 turn: 360 ground-lock: feet diff --git a/playground/public/moves/seated-forward-fold.html b/playground/public/moves/seated-forward-fold.html index f9d612c..5156e5a 100644 --- a/playground/public/moves/seated-forward-fold.html +++ b/playground/public/moves/seated-forward-fold.html @@ -151,13 +151,15 @@

    The .posecode source

    pose start = seated step "Fold" 3s ease-in-out: - spine: flex 60 - chest: flex 25 - neck: flex 15 + hips: flex 115 + spine: flex 25 + chest: flex 15 + neck: flex 10 shoulders: flex 60 cue "Hinge forward from the hips over the legs, reaching toward the feet" step "Rise" 2.5s ease-in-out: + hips: flex 90 spine: flex 0 chest: flex 0 neck: flex 0 diff --git a/playground/public/moves/sit-to-stand.html b/playground/public/moves/sit-to-stand.html index 9ddc95f..ef54840 100644 --- a/playground/public/moves/sit-to-stand.html +++ b/playground/public/moves/sit-to-stand.html @@ -154,7 +154,8 @@

    The .posecode source

    step "Sit" 2s ease-in-out: hips: flex 90 knees: flex 95 - ankles: dorsiflex 14 + ankles: dorsiflex 15 + pelvis: hinge 15 spine: flex 12 shoulders: flex 60 ground-lock: feet @@ -163,7 +164,8 @@

    The .posecode source

    step "Stand" 1.8s ease-out: hips: flex 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 + pelvis: hinge 0 spine: flex 0 shoulders: flex 0 ground-lock: feet diff --git a/playground/public/moves/squat.html b/playground/public/moves/squat.html index fcf2713..cd74b3e 100644 --- a/playground/public/moves/squat.html +++ b/playground/public/moves/squat.html @@ -153,8 +153,9 @@

    The .posecode source

    step "Descend" 1.6s ease-in-out: hips: flex 80 knees: flex 95 - ankles: dorsiflex 14 - spine: flex 20 + ankles: dorsiflex 15 + pelvis: hinge 25 + spine: flex 0 shoulders: flex 70 neck: extend 10 ground-lock: feet @@ -163,7 +164,8 @@

    The .posecode source

    step "Drive up" 1.2s ease-out: hips: flex 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 + pelvis: hinge 0 spine: flex 0 shoulders: flex 0 neck: extend 0 diff --git a/playground/public/moves/step-up.html b/playground/public/moves/step-up.html index b4c04eb..eefb282 100644 --- a/playground/public/moves/step-up.html +++ b/playground/public/moves/step-up.html @@ -162,11 +162,14 @@

    The .posecode source

    step "Plant the foot" 1s ease-in-out: hip_right: flex 70 knee_right: flex 90 + ankle_right: plantarflex 50 + pin: foot_left floor cue "Plant the right foot up on top of the box" step "Drive up" 1.2s ease-out: hip_right: flex 8 knee_right: flex 8 + ankle_right: plantarflex 8 hip_left: flex 35 knee_left: flex 45 pin: foot_right box @@ -175,6 +178,7 @@

    The .posecode source

    step "Step down" 1.4s ease-in: hip_right: flex 70 knee_right: flex 90 + ankle_right: plantarflex 50 hip_left: flex 0 knee_left: flex 0 pin: foot_right box diff --git a/playground/public/moves/superman.html b/playground/public/moves/superman.html index 4d1bc0b..aaf9459 100644 --- a/playground/public/moves/superman.html +++ b/playground/public/moves/superman.html @@ -151,19 +151,21 @@

    The .posecode source

    pose start = prone step "Lift" 1.5s ease-out: - shoulders: flex 150 + shoulders: abduct 130 spine: extend 20 chest: extend 15 neck: extend 25 hips: extend 18 + pin: pelvis floor cue "Lift the arms, chest, and legs off the floor" step "Lower" 1.5s ease-in: - shoulders: flex 0 + shoulders: abduct 0 spine: flex 0 chest: flex 0 neck: flex 0 hips: extend 0 + pin: pelvis floor cue "Lower everything back down with control" repeat 8 diff --git a/playground/public/moves/supine-leg-raise.html b/playground/public/moves/supine-leg-raise.html index e512f4b..6318144 100644 --- a/playground/public/moves/supine-leg-raise.html +++ b/playground/public/moves/supine-leg-raise.html @@ -153,10 +153,16 @@

    The .posecode source

    step "Raise" 1.2s ease-out: hips: flex 90 knees: flex 0 + shoulders: rotate-in 70 + elbows: pronate 80 + reach: hands floor cue "Lift the straight legs toward vertical, low back pressed down" step "Lower" 1.4s ease-in: hips: flex 20 + shoulders: rotate-in 70 + elbows: pronate 80 + reach: hands floor cue "Lower the legs slowly without arching the back" repeat 10 diff --git a/playground/public/moves/tendu.html b/playground/public/moves/tendu.html index 60fe146..840c571 100644 --- a/playground/public/moves/tendu.html +++ b/playground/public/moves/tendu.html @@ -157,6 +157,7 @@

    The .posecode source

    ankle_right: plantarflex 45 shoulders: abduct 70 elbows: flex 14 + pin: foot_left floor cue "Brush the right foot forward to a fully pointed tendu, leg turned out" step "Close" 1.6s ease-in-out: @@ -165,6 +166,7 @@

    The .posecode source

    ankle_right: plantarflex 0 shoulders: abduct 0 elbows: flex 0 + ground-lock: feet cue "Draw the foot back to first position, heel down" repeat 4 diff --git a/playground/public/moves/touch-toes.html b/playground/public/moves/touch-toes.html index 2deb68e..acc08e0 100644 --- a/playground/public/moves/touch-toes.html +++ b/playground/public/moves/touch-toes.html @@ -151,8 +151,9 @@

    The .posecode source

    pose start = standing step "Fold" 2.5s ease-in-out: - pelvis: hinge 120 - knees: flex 60 + pelvis: hinge 95 + knees: flex 20 + ankles: plantarflex 20 neck: flex 15 reach: hand_left ankle_left reach: hand_right ankle_right @@ -162,6 +163,7 @@

    The .posecode source

    step "Rise" 2s ease-out: pelvis: hinge 0 knees: flex 0 + ankles: plantarflex 0 neck: flex 0 ground-lock: feet cue "Stack the spine back up to standing tall" diff --git a/playground/public/moves/triceps-dips.html b/playground/public/moves/triceps-dips.html index 3148b6e..018e6b4 100644 --- a/playground/public/moves/triceps-dips.html +++ b/playground/public/moves/triceps-dips.html @@ -162,6 +162,8 @@

    The .posecode source

    step "Support" 1s ease-out: shoulders: abduct 5 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 knees: flex 70 ankles: plantarflex 20 pin: hands bars @@ -171,7 +173,10 @@

    The .posecode source

    shoulders: extend 30 shoulders: abduct 5 elbows: flex 90 + elbows: pronate 80 + fingers: flex 80 knees: flex 70 + ankles: plantarflex 20 pin: hands bars cue "Bend the elbows to lower the chest, elbows tracking back" @@ -179,13 +184,18 @@

    The .posecode source

    shoulders: extend 0 shoulders: abduct 5 elbows: flex 5 + elbows: pronate 80 + fingers: flex 80 knees: flex 70 + ankles: plantarflex 20 pin: hands bars cue "Press through the palms to straighten the arms" step "Dismount" 0.8s ease-in: shoulders: abduct 0 elbows: flex 0 + elbows: pronate 0 + fingers: flex 0 knees: flex 0 ankles: plantarflex 0 ground-lock: feet diff --git a/playground/public/moves/twist.html b/playground/public/moves/twist.html index 4ff0e78..c47fda0 100644 --- a/playground/public/moves/twist.html +++ b/playground/public/moves/twist.html @@ -165,6 +165,8 @@

    The .posecode source

    step "Twist left" 3s ease-in-out: spine: rotate-in 40 chest: rotate-in 30 + shoulders: abduct 80 + elbows: flex 90 ground-lock: feet cue "Pass through center and rotate to the other side" diff --git a/playground/public/moves/walk-cycle.html b/playground/public/moves/walk-cycle.html index bd0a248..28285a8 100644 --- a/playground/public/moves/walk-cycle.html +++ b/playground/public/moves/walk-cycle.html @@ -161,6 +161,7 @@

    The .posecode source

    posecode exercise "Walk & turn"
       rig humanoid
       pose start = standing
    +  clip "walk"
     
       step "Step right" 0.7s ease-in-out:
         hip_right: flex 30
    @@ -169,7 +170,7 @@ 

    The .posecode source

    shoulder_left: flex 25 shoulder_right: extend 20 travel: 0 0.4 - ground-lock: feet + pin: foot_left floor cue "Walk forward: right foot leads, opposite arm swings through" step "Step left" 0.7s ease-in-out: @@ -179,7 +180,7 @@

    The .posecode source

    shoulder_right: flex 25 shoulder_left: extend 20 travel: 0 0.8 - ground-lock: feet + pin: foot_right floor cue "Left foot leads, arms swap: keep travelling forward" step "About-face" 1s ease-in-out: @@ -199,7 +200,7 @@

    The .posecode source

    shoulder_right: extend 20 turn: 180 travel: 0 0.4 - ground-lock: feet + pin: foot_left floor cue "Walk back toward the start" step "Arrive & square up" 1s ease-in-out: diff --git a/playground/public/moves/wall-sit.html b/playground/public/moves/wall-sit.html index d8c2fb5..effd21b 100644 --- a/playground/public/moves/wall-sit.html +++ b/playground/public/moves/wall-sit.html @@ -154,7 +154,7 @@

    The .posecode source

    step "Slide down" 2.5s ease-in-out: hips: flex 90 knees: flex 90 - ankles: dorsiflex 12 + ankles: dorsiflex 15 shoulders: flex 80 ground-lock: feet cue "Slide the back down the wall until the thighs are parallel" @@ -162,7 +162,7 @@

    The .posecode source

    step "Hold & rise" 2.5s ease-out: hips: flex 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 shoulders: flex 0 ground-lock: feet cue "Press through the heels and slide back up the wall" diff --git a/playground/public/moves/waltz-box.html b/playground/public/moves/waltz-box.html index 0a1b5e9..8c5ce17 100644 --- a/playground/public/moves/waltz-box.html +++ b/playground/public/moves/waltz-box.html @@ -165,7 +165,7 @@

    The .posecode source

    shoulders: abduct 55 elbows: flex 20 travel: 0 0.4 - ground-lock: feet + pin: foot_left floor cue "Step forward onto the right foot and rise, arms in a soft frame" step "2 - side, lower" 1s ease-in-out: @@ -181,7 +181,7 @@

    The .posecode source

    knee_left: flex 20 ankles: plantarflex 12 travel: -0.4 0 - ground-lock: feet + pin: foot_right floor cue "Step back onto the left foot and rise again" step "4 - close home" 1s ease-in-out: diff --git a/playground/public/sitemap.xml b/playground/public/sitemap.xml index 891d40f..7d17b79 100644 --- a/playground/public/sitemap.xml +++ b/playground/public/sitemap.xml @@ -2,463 +2,463 @@ https://posecode.org/ - 2026-07-08 + 2026-07-11 weekly 1.0 https://posecode.org/play - 2026-07-08 + 2026-07-11 weekly 0.9 https://posecode.org/moves/ - 2026-07-08 + 2026-07-11 weekly 0.8 https://posecode.org/spec.html - 2026-07-08 + 2026-07-11 weekly 0.6 https://posecode.org/llm-guide.html - 2026-07-08 + 2026-07-11 weekly 0.6 https://posecode.org/moves/squat.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/dance-phrase.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/deadlift.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/shoulder-abduction.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/front-kick.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/good-morning.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/chest-opener.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/plank-hold.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/mountain-climber.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/crunch.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/bicycle-crunch.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/supine-leg-raise.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/superman.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/forward-lunge.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/calf-raise.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/jumping-jacks.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/box-step-taps.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/pull-up.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/step-up.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/triceps-dips.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/quad-stretch.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/hip-flexion.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/knee-flexion.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/spine-rotation.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/elbow-forearm.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/heel-raises.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/hamstring-curl.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/hip-abduction.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/shoulder.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/neck.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/posture.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/twist.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/shoulder-rolls.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/neck-side-stretch.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/overhead-reach.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/jab-cross.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/horse-stance.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/bow.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/arm-circles.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/high-knee-march.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/demi-plie.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/releve.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/tendu.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/port-de-bras.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/bent-over-row.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/biceps.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/lateral.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/fold.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/chair.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/sidebend.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/touch-toes.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/cross-body-reach.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/glute-bridge.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/dead-bug.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/cobra.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/seated-forward-fold.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/sit-to-stand.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/box-squat.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/wall-sit.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/dead-hang.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/hanging-knee-raise.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/make-a-fist.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/pinch-grip.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/finger-spell.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/hand-wave.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/pirouette.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/box-step.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/grapevine.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/waltz-box.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/chasse.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/walk-cycle.html - 2026-07-08 + 2026-07-11 weekly 0.5 https://posecode.org/moves/quarter-turns.html - 2026-07-08 + 2026-07-11 weekly 0.5 diff --git a/playground/public/spec.html b/playground/public/spec.html index 4c862f2..f2fedc9 100644 --- a/playground/public/spec.html +++ b/playground/public/spec.html @@ -131,10 +131,11 @@

    1. Grammar

    document   = header { directive } ;
     header     = "posecode" kind STRING ;
     kind       = "exercise" | "stretch" | "posture" ;       (* free-form word *)
    -directive  = rig | prop | pose | step | repeat ;
    +directive  = rig | prop | pose | clip | step | repeat ;
     rig        = "rig" WORD ;
     prop       = "prop" WORD ;                              (* chair|wall|bar|box|dip-bars, repeatable *)
     pose       = "pose" "start" "=" WORD ;                  (* neutral|standing|plank|supine|prone|seated *)
    +clip       = "clip" STRING ;                            (* optional mocap clip; renderer may retarget & blend *)
     repeat     = "repeat" NUMBER ;
     step       = "step" STRING DURATION easing ":" { child } ;
     easing     = "linear" | "ease-in" | "ease-out" | "ease-in-out" ;
    @@ -190,7 +191,7 @@ 

    4. Range of Motion (safety)

    spine903035rotate 45 neck506045rotate 80 -

    > ⚠️ These are general literature values, not medical advice. Consult a > qualified professional for physiotherapy or exercise prescription.

    +

    > These are general literature values, not medical advice. Consult a > qualified professional for physiotherapy or exercise prescription.


    5. Rendering model

    1. Forward kinematics: each phase sets joint angles; the renderer slerps
    @@ -204,11 +205,12 @@

    5. Rendering model

    1. Props: prop chair|wall|bar|box|dip-bars adds a scene object at a

    fixed default placement (chair/wall behind, bar overhead, box in front, dip bars either side); its named anchors (seat, wall, bar, box, bars) become reach/pin targets.

    1. Pins: pin: <effector> <anchor> translates the whole figure so the
    -

    effector sits on the anchor (effectors accept the same hands/feet groups as reach: pin: hands bar pins both). Where ground-lock keeps a foot on the floor and reach moves a limb to a target, a pin moves the body while the contact stays put, so the figure hangs from a bar, pulls up toward it, rises onto a box, or lowers into a dip as the joints work.

    +

    effector sits on the anchor (effectors accept the same hands/feet groups as reach: pin: hands bar pins both). Where ground-lock keeps a foot on the floor and reach moves a limb to a target, a pin moves the body while the contact stays put, so the figure hangs from a bar, pulls up toward it, rises onto a box, or lowers into a dip as the joints work. Symmetric bar contacts resolve to side-specific anchors automatically (bar.left / bar.right, bars.left / bars.right). Contact post-processing also keeps floor-contacting soles level and gives bar-contacting wrists a stable overhand orientation; existing Posecode syntax remains unchanged.

    1. Spatial choreography: turn: <deg> rotates the figure's facing (yaw

    about vertical) and travel: <x> <z> moves it across the floor (world metres from the load spot). Both are absolute targets carried across phases (like joint angles) and both return home on the loop wrap, so a box-step traces a square back to start and a pirouette spins a full turn. They layer under grounding (feet still rest on the floor) and power pirouettes, grapevines, traveling combos, and walk cycles. Standing poses only: combining with lying/seated bases (whose root is already tilted) is out of scope.

    1. Looping: the timeline loops base → phases → base; repeat is the rep

    count surfaced to the UI.

    +

    When a mocap clip is active, the renderer selects the take containing the most actual bone motion (rather than blindly choosing the longest embedded take), retargets and blends it, then restores solved terminal contacts on the visible character. Mocap therefore cannot overwrite a planted sole or pinned grip.

    Start poses: neutral, standing, plank, supine (face-up), prone (face-down), seated (long-sit on the floor).

    IK note: Three.js's bundled CCDIKSolver targets SkinnedMesh; the Posecode mannequin is rigid capsule segments, so Posecode implements CCD directly over the Object3D bone hierarchy (posecode-render/ik.ts) for both ground-lock and reach. Two-person/dual-IK and collision detection remain deferred (research §5.2, §6.2).


    diff --git a/playground/public/squat.posecode b/playground/public/squat.posecode index 79b64ed..97db8f2 100644 --- a/playground/public/squat.posecode +++ b/playground/public/squat.posecode @@ -5,8 +5,9 @@ posecode exercise "Body-weight squat" step "Descend" 1.6s ease-in-out: hips: flex 80 knees: flex 95 - ankles: dorsiflex 14 - spine: flex 20 + ankles: dorsiflex 15 + pelvis: hinge 25 + spine: flex 0 shoulders: flex 70 neck: extend 10 ground-lock: feet @@ -15,7 +16,8 @@ posecode exercise "Body-weight squat" step "Drive up" 1.2s ease-out: hips: flex 0 knees: flex 0 - ankles: dorsiflex 0 + ankles: plantarflex 0 + pelvis: hinge 0 spine: flex 0 shoulders: flex 0 neck: extend 0 diff --git a/spec/SPEC.md b/spec/SPEC.md index 06ac3b3..76910f1 100644 --- a/spec/SPEC.md +++ b/spec/SPEC.md @@ -145,6 +145,10 @@ research §5.1 normative tables. Selected ceilings (degrees): the floor and reach moves a limb to a target, a **pin moves the body** while the contact stays put, so the figure hangs from a bar, pulls up toward it, rises onto a box, or lowers into a dip as the joints work. + Symmetric bar contacts resolve to side-specific anchors automatically + (`bar.left` / `bar.right`, `bars.left` / `bars.right`). Contact post-processing + also keeps floor-contacting soles level and gives bar-contacting wrists a + stable overhand orientation; existing Posecode syntax remains unchanged. 7. **Spatial choreography**: `turn: ` rotates the figure's facing (yaw about vertical) and `travel: ` moves it across the floor (world metres from the load spot). Both are **absolute targets carried across phases** (like @@ -156,6 +160,11 @@ research §5.1 normative tables. Selected ceilings (degrees): 8. **Looping**: the timeline loops base → phases → base; `repeat` is the rep count surfaced to the UI. +When a mocap clip is active, the renderer selects the take containing the most +actual bone motion (rather than blindly choosing the longest embedded take), +retargets and blends it, then restores solved terminal contacts on the visible +character. Mocap therefore cannot overwrite a planted sole or pinned grip. + **Start poses:** `neutral`, `standing`, `plank`, `supine` (face-up), `prone` (face-down), `seated` (long-sit on the floor). diff --git a/spec/examples/box-squat.posecode b/spec/examples/box-squat.posecode index 4179589..53d1668 100644 --- a/spec/examples/box-squat.posecode +++ b/spec/examples/box-squat.posecode @@ -6,7 +6,7 @@ posecode exercise "Box squat" step "Sit back" 1.6s ease-in-out: hips: flex 85 knees: flex 90 - ankles: plantarflex 50 + ankles: dorsiflex 15 pelvis: hinge 20 spine: flex 0 shoulders: flex 70 diff --git a/spec/examples/chair-pose.posecode b/spec/examples/chair-pose.posecode index 3de6ccf..33910cd 100644 --- a/spec/examples/chair-pose.posecode +++ b/spec/examples/chair-pose.posecode @@ -5,7 +5,7 @@ posecode posture "Chair pose" step "Sink" 3s ease-in-out: hips: flex 55 knees: flex 70 - ankles: plantarflex 50 + ankles: dorsiflex 15 shoulders: flex 170 pelvis: hinge 15 spine: extend 5 diff --git a/spec/examples/chair-sit-to-stand.posecode b/spec/examples/chair-sit-to-stand.posecode index cf10f7b..4776193 100644 --- a/spec/examples/chair-sit-to-stand.posecode +++ b/spec/examples/chair-sit-to-stand.posecode @@ -6,7 +6,8 @@ posecode exercise "Sit to stand" step "Sit" 2s ease-in-out: hips: flex 90 knees: flex 95 - ankles: plantarflex 50 + ankles: dorsiflex 15 + pelvis: hinge 15 spine: flex 12 shoulders: flex 60 ground-lock: feet @@ -16,6 +17,7 @@ posecode exercise "Sit to stand" hips: flex 0 knees: flex 0 ankles: plantarflex 0 + pelvis: hinge 0 spine: flex 0 shoulders: flex 0 ground-lock: feet diff --git a/spec/examples/demi-plie.posecode b/spec/examples/demi-plie.posecode index cf88c4a..9c74418 100644 --- a/spec/examples/demi-plie.posecode +++ b/spec/examples/demi-plie.posecode @@ -6,7 +6,7 @@ posecode exercise "Demi-plié" hips: flex 20 hips: rotate-out 30 knees: flex 55 - ankles: plantarflex 50 + ankles: dorsiflex 15 shoulders: abduct 70 elbows: flex 15 spine: extend 4 diff --git a/spec/examples/horse-stance.posecode b/spec/examples/horse-stance.posecode index 673e4a0..9bcc52d 100644 --- a/spec/examples/horse-stance.posecode +++ b/spec/examples/horse-stance.posecode @@ -6,7 +6,7 @@ posecode posture "Horse stance" hips: flex 30 hips: abduct 25 knees: flex 90 - ankles: plantarflex 50 + ankles: dorsiflex 15 spine: extend 5 shoulders: flex 80 elbows: flex 90 diff --git a/spec/examples/pull-up.posecode b/spec/examples/pull-up.posecode index a1af4d5..f5d500e 100644 --- a/spec/examples/pull-up.posecode +++ b/spec/examples/pull-up.posecode @@ -7,7 +7,7 @@ posecode exercise "Pull-up" shoulders: flex 175 elbows: flex 5 elbows: pronate 80 - fingers: flex 80 + fingers: flex 20 ground-lock: feet cue "Reach up and grip the bar" @@ -15,7 +15,11 @@ posecode exercise "Pull-up" shoulders: flex 175 elbows: flex 5 elbows: pronate 80 - fingers: flex 80 + fingers: flex 55 + thumb_left: flex 35 + thumb_right: flex 35 + thumb_left: abduct 20 + thumb_right: abduct 20 pin: hands bar cue "Hang from the bar, arms long, shoulders active" @@ -23,7 +27,11 @@ posecode exercise "Pull-up" shoulders: flex 105 elbows: flex 130 elbows: pronate 80 - fingers: flex 80 + fingers: flex 55 + thumb_left: flex 35 + thumb_right: flex 35 + thumb_left: abduct 20 + thumb_right: abduct 20 spine: extend 15 chest: extend 10 neck: extend 20 @@ -34,7 +42,11 @@ posecode exercise "Pull-up" shoulders: flex 175 elbows: flex 5 elbows: pronate 80 - fingers: flex 80 + fingers: flex 55 + thumb_left: flex 35 + thumb_right: flex 35 + thumb_left: abduct 20 + thumb_right: abduct 20 spine: extend 0 chest: extend 0 neck: extend 0 diff --git a/spec/examples/squat.posecode b/spec/examples/squat.posecode index c328477..97db8f2 100644 --- a/spec/examples/squat.posecode +++ b/spec/examples/squat.posecode @@ -5,7 +5,7 @@ posecode exercise "Body-weight squat" step "Descend" 1.6s ease-in-out: hips: flex 80 knees: flex 95 - ankles: plantarflex 50 + ankles: dorsiflex 15 pelvis: hinge 25 spine: flex 0 shoulders: flex 70 diff --git a/spec/examples/wall-sit.posecode b/spec/examples/wall-sit.posecode index 91d8f0f..7a8d37d 100644 --- a/spec/examples/wall-sit.posecode +++ b/spec/examples/wall-sit.posecode @@ -6,7 +6,7 @@ posecode posture "Wall sit" step "Slide down" 2.5s ease-in-out: hips: flex 90 knees: flex 90 - ankles: plantarflex 50 + ankles: dorsiflex 15 shoulders: flex 80 ground-lock: feet cue "Slide the back down the wall until the thighs are parallel"