@rig-c/pose 0.20.2 → 0.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rig-c/pose",
3
- "version": "0.20.2",
3
+ "version": "0.20.3",
4
4
  "description": "Puts motion from a body-18 pose sequence onto a rig built with rig-c: bone keys in rig-c's motion format, checked through rig-c's gate before anything is written. A CLI for agents that cannot see the rig.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/legs.ts CHANGED
@@ -683,6 +683,23 @@ function placeHip(feet: Feet, offset: (i: number, s: Side) => Pt, travel: (i: nu
683
683
  const tracked = new Array<Side | null>(n).fill(null);
684
684
  for (const t of feet.tracked ?? []) for (let i = t.frames[0]; i <= t.frames[1]; i++) tracked[i] = t.side;
685
685
  const trkX = trackFn ?? follow;
686
+ // #142: on a run (two or more flights), each support span's push-off ankle lift and side-to-side
687
+ // straight-length asymmetry would otherwise swing floorY - offset(i, s)[1] far past the pose's
688
+ // own bounce and ratchet the un-floored picture pass per cycle. Keep the support spans' mean floor
689
+ // shift across both support spans and flights, holding each grounded ankle on floorY via blends.
690
+ const isRun = feet.jumps.length >= 2 && feet.supports.length > 0;
691
+ let runShift = 0;
692
+ if (isRun && floorY !== null) {
693
+ const t0 = travel(0)[1];
694
+ const shifts: number[] = [];
695
+ for (let i = 0; i < n; i++) {
696
+ const s = feet.support[i] ?? null;
697
+ if (s === null) continue;
698
+ shifts.push(floorY - offset(i, s)[1] - (start[1] + travel(i)[1] - t0));
699
+ }
700
+ if (shifts.length > 0) runShift = shifts.reduce((a, b) => a + b, 0) / shifts.length;
701
+ }
702
+ const flightShift = runShift;
686
703
  /** How the hip is derived on a frame: held over a foot's anchor, tracking the lower foot on the floor (#119), or carried by the pose's travel from a frame's hip (and for a jump against `floorY`, carried so the landing frame meets the floor and frames inside the flight never go below it, #103). */
687
704
  type Mode =
688
705
  | { kind: 'plant'; side: Side; anchor: Pt; from: number }
@@ -693,6 +710,13 @@ function placeHip(feet: Feet, offset: (i: number, s: Side) => Pt, travel: (i: nu
693
710
  const o = offset(i, mode.side);
694
711
  // In place (#92) the anchor's x follows the supporting ankle's own track from the frame it planted.
695
712
  const dx = follow === undefined ? 0 : follow(i, mode.side) - follow(mode.from, mode.side);
713
+ if (isRun) {
714
+ const yPose = start[1] + travel(i)[1] - travel(0)[1] + (floorY === null ? 0 : runShift);
715
+ if (floorY === null) return [mode.anchor[0] + dx - o[0], yPose];
716
+ const d = -o[1];
717
+ const y = d > 0 ? Math.min(Math.max(yPose, mode.anchor[1] + STANCE_BOUNDS[0] * d), mode.anchor[1] + STANCE_BOUNDS[1] * d) : yPose;
718
+ return [mode.anchor[0] + dx - o[0], y];
719
+ }
696
720
  return [mode.anchor[0] + dx - o[0], mode.anchor[1] - o[1]];
697
721
  }
698
722
  if (mode.kind === 'track') {
@@ -703,6 +727,13 @@ function placeHip(feet: Feet, offset: (i: number, s: Side) => Pt, travel: (i: nu
703
727
  }
704
728
  const a = travel(i);
705
729
  const b = travel(mode.from);
730
+ if (isRun) {
731
+ let y = start[1] + a[1] - travel(0)[1] + (floorY === null ? 0 : flightShift);
732
+ if (mode.end !== undefined && floorY !== null && i <= mode.end) {
733
+ y = Math.max(y, floorY - Math.min(offset(i, 'r')[1], offset(i, 'l')[1]));
734
+ }
735
+ return [mode.at[0] + a[0] - b[0], y];
736
+ }
706
737
  let y = mode.at[1] + a[1] - b[1];
707
738
  if (mode.carry !== undefined && mode.land !== undefined && mode.end !== undefined && floorY !== null) {
708
739
  if (mode.land < n && i <= mode.land && mode.land > mode.from) {
@@ -756,7 +787,7 @@ function placeHip(feet: Feet, offset: (i: number, s: Side) => Pt, travel: (i: nu
756
787
  // The new support is held where it is on this frame, under the hip the old derivation gives here.
757
788
  const here = of(mode!, i);
758
789
  const o = offset(i, s);
759
- from = mode;
790
+ from = isRun ? null : mode;
760
791
  mode = plant(i, s, [here[0] + o[0], here[1] + o[1]]);
761
792
  blendAt = i;
762
793
  }
@@ -784,6 +815,10 @@ function placeHip(feet: Feet, offset: (i: number, s: Side) => Pt, travel: (i: nu
784
815
  } else {
785
816
  from = null;
786
817
  hip.push(to);
818
+ const m = mode!;
819
+ if (isRun && floorY !== null && m.kind === 'plant' && Math.abs(to[1] - (m.anchor[1] - offset(i, m.side)[1])) > BLEND_HOLD_WITHIN) {
820
+ blends.push({ frame: i, side: m.side, y: m.anchor[1] });
821
+ }
787
822
  }
788
823
  }
789
824
  return { hip, anchors, blends };
package/src/retarget.ts CHANGED
@@ -775,7 +775,16 @@ export function retarget(rest: Rest, map: JointMap, frames: ReadonlyArray<Partia
775
775
  const ankleSeries = { r: series.get('r_ankle')!, l: series.get('l_ankle')! };
776
776
  const lower = stanceRangeOf(ankleSeries, { r: series.get('r_hip')!, l: series.get('l_hip')! });
777
777
  blendHold = holdBlend(planted.blends, n, posedFeet, lower);
778
+ const holdAnchorX = feet!.jumps.length >= 2 ? blendHold.stretch.map((b, i) => (b ? posedFeet(i).ankle[b.side][0] : 0)) : null;
778
779
  stretchChain(blendHold.stretch);
780
+ if (holdAnchorX !== null) {
781
+ for (let i = 0; i < n; i++) {
782
+ const b = blendHold.stretch[i];
783
+ if (!b) continue;
784
+ hip[i]![0] += holdAnchorX[i]! - posedFeet(i).ankle[b.side][0];
785
+ travel[i]![0] = hip[i]![0] - tb.worldX;
786
+ }
787
+ }
779
788
  // --stance (#60): with the hip placed, a free ankle within the tolerance of the floor has its leg
780
789
  // stretched (every bone of its chain's scalex, about the chain's root) until it reaches it.
781
790
  if (opts.stance !== undefined && opts.stance !== 'off') {