@rig-c/pose 0.31.1 → 0.33.0
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/README.md +46 -20
- package/cli.ts +91 -26
- package/package.json +2 -2
- package/src/clean.ts +23 -2
- package/src/legs.ts +93 -0
- package/src/map.ts +35 -4
- package/src/problems.ts +9 -2
- package/src/report.ts +26 -2
- package/src/retarget.ts +461 -27
- package/src/rigc.ts +18 -0
- package/src/run.ts +395 -48
- package/src/seams.ts +241 -0
- package/src/verify.ts +111 -2
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ Part of the rig-c toolchain: [core](https://github.com/rig-c/core) compiles and
|
|
|
13
13
|
[`examples/wave`](https://github.com/rig-c/pose/tree/main/examples/wave) is in the repository, not the npm package: clone the repository, or copy its four files. It runs with nothing else: a bones-only stick rig (no art), a 3-second wave made from joint angles (`make-pose.ts` writes it), and the map that joins them.
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
rig-pose retarget --rig examples/wave/rig --map examples/wave/map.json --
|
|
16
|
+
rig-pose retarget --rig examples/wave/rig --map examples/wave/map.json --sequence examples/wave/pose.json --out wave.motion.json --build wave-build
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
`pose.json` is already cleaned (`make-pose.ts` passes it through `clean`). It prints the gate's verdict and where every mapped bone landed, then writes `wave.motion.json` and the gated build. `rigc preview --candidate wave-build` plays it in the Spine Web Player. A rig with no art shows only its bones.
|
|
19
|
+
`pose.json` is already cleaned (`make-pose.ts` passes it through `clean`). It prints the gate's verdict and where every mapped bone landed, then writes `wave.motion.json`, its report `wave.motion.report.json` and the gated build. `rigc preview --candidate wave-build` plays it in the Spine Web Player. A rig with no art shows only its bones.
|
|
20
20
|
|
|
21
21
|
## What the rig needs
|
|
22
22
|
|
|
@@ -104,10 +104,14 @@ A retarget of the cleaned `dance-a` fixture finds the same jumps (`FEET`) as the
|
|
|
104
104
|
## What it does
|
|
105
105
|
|
|
106
106
|
```sh
|
|
107
|
-
rig-pose retarget --rig out/rig --map map.json --
|
|
107
|
+
rig-pose retarget --rig out/rig --map map.json --sequence dance.pose.json --out motion.json --animation dance
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
**Names** ([#243](https://github.com/rig-c/pose/issues/243)). `--sequence` and `--animation` are the names the rest of the toolchain gives these inputs (rig-c/exam's grader, `rigc render --animation`); `--pose` and `--name`, this command's first names, are the same flags. Both of a pair given with the same value is accepted; with different values the run is refused before anything is read, naming both (`--pose "a.json" and --sequence "b.json" are both given and differ; --sequence is another name for --pose, so give one`). Either spelling writes the same bytes.
|
|
111
|
+
|
|
112
|
+
**Paths** ([#239](https://github.com/rig-c/pose/issues/239)). `--rig` is the directory @rig-c/parts writes, `<out>/rig/`: it reads `rig.json` and the `motion.json` beside it. Naming the `rig.json` itself reads no motion spec, so a rig that needs its own animations — a slider applies one — is red before anything is added, and the `RIG_GATE_BASE` line says the `motion.json` beside it was not read and names the directory form (or `--base-motion`). A directory `--out`, `--report` or `--camera` names that is not there yet is made once the run is green, and a `made directory <dir>` line says so and which paths it holds; a red run makes none, and a file where a directory has to be made is refused (`ARG_PATH`). The report is always written: without `--report` it goes beside the motion as `<out>.report.json` (`motion.json` → `motion.report.json`), and its `wrote` line says it was the default. A report that is already at that path is replaced, and the `wrote` line names when the replaced one was last written (its mtime, ISO 8601), so a second run to the same path is never silent. None of this changes the motion's bytes.
|
|
113
|
+
|
|
114
|
+
For every frame of the pose and every bone the map names, the segment that bone carries (an upper arm: shoulder → elbow) points somewhere in the picture. The bone is turned so its segment points the same way: the picture angle, flipped into rig-c's y-up world, minus the bone's angle at the rig's setup pose (read through rig-c's own poser), less the turn of the mapped bone above it — because a `rotate` key in rig-c adds to the setup rotation and a child turns with its parent. The travel of the pelvis midpoint, scaled by a length measured on both the rig and the pose, moves the hip. When a head chain carries the eye line (`r_eye → l_eye`), the eyes' vertical travel against `neck` in the reference, scaled into the rig's units, keys a vertical `translate` nod channel on the head bone ([#111](https://github.com/rig-c/pose/issues/111)) clamped within `±2%` of the setup neck length (`head − neck`, [#181](https://github.com/rig-c/pose/issues/181)), while on sequences where that clamp acts, the clamped vertical nod (`clampedY`) is coupled in phase into `face.pitch`'s dial (`+2.8°/unit`, mapped through `face.pitch.range`) and the head bone's `rotate` track (`+1.6°/unit`, [#190](https://github.com/rig-c/pose/issues/190), [#205](https://github.com/rig-c/pose/issues/205); gains calibrated against the hand-polished reference `p1`). **How the nod's baseline is read** ([#241](https://github.com/rig-c/pose/issues/241)): on a sequence declared `"travel": "in-place"` it is frame 0, with no window, no clamp and so no coupling ([#242](https://github.com/rig-c/pose/issues/242)); otherwise it is a ±0.5 s running median of the eyes' height against the neck, averaged again over the same window, and the nod is the eyes' height less it. On a sequence `clean --loop` wrote (its `cleaned.params.loop`) and not declared in place, that window **wraps**, the last frame running into the first, as clean's own windows do; cut at the clip's ends instead, the two ends are read against one-sided baselines, and the coupling carries the difference into the head's rotate. On the tests' synthetic loop (#242's walk, 7 cycles of 24 frames, the eyes bobbing 3 px twice a cycle, `travel` deleted), the wrapped window puts cycle 7 on cycle 1 to 0.000° and 0.000 units; cut at the ends it was 1.057° on the rotate and 0.661 units on the nod. `"nod": false` on the eye-line chain ([below](#the-map-rig-pose-map1)) keys no nod and couples none. The `NOD` line says which of these happened. The result is one animation in rig-c's motion format (`rigc-motion/1`): a `rotate` track per mapped bone, a `translate` track on the hip (and on the head when the reference nods), one key per pose frame — and, when the map lists alternate drawings, an `attachment` track per slot they switch (and a `translate` track per bone a set offsets).
|
|
111
115
|
|
|
112
116
|
Nothing is written until every check passes:
|
|
113
117
|
|
|
@@ -115,19 +119,24 @@ Nothing is written until every check passes:
|
|
|
115
119
|
| --- | --- |
|
|
116
120
|
| rig-c's gate is green with the rig as it is, and again with the animation in (`rigc build`) | `RIG_GATE_BASE`, `RIG_GATE`, then rigc's own `FAIL` lines |
|
|
117
121
|
| every mapped bone **lands** on the angle the pose asks for, on every frame — the gated build is posed by rig-c's poser and measured (0.01° / 0.01 units); with `--leg-scale`, every scaled bone's x axis (and the trunk's y axis when trunk foreshortening is keyed, [#172](https://github.com/rig-c/pose/issues/172)) on its asked scale (0.0001 of its setup length), and with either leg flag each ankle where the keys put it (0.01 units + 0.0001 × its setup distance from the hip) | `POSE_LANDS`: the bone, the worst error, the frame |
|
|
118
|
-
| elbows and knees bend only the way they can: at every joint inside a chain, the inner angle stays on the side of the rest pose's | `POSE_BEND_ONE_WAY`: the chain, the joint, the frame, the angle |
|
|
122
|
+
| elbows and knees bend only the way they can: at every joint inside a chain, the inner angle stays on the side of the rest pose's | `POSE_BEND_ONE_WAY`: one line per hinge — the chain, the joint, how many frames in how many runs, the frame range, the worst angle and its frame, and the remedy (`"bend": "free"` for front-facing material, [below](#the-map-rig-pose-map1)); `--verbose` prints the per-frame refusals before it |
|
|
119
123
|
| every point the map needs is good on at least one frame | `POSE_POINT_NEVER_GOOD` |
|
|
120
124
|
| the map resolves against the rig: bones exist, chains descend, aims descend | `MAP_*`, naming both sides |
|
|
121
125
|
|
|
122
126
|
and what the run cannot decide for you it lists:
|
|
123
127
|
|
|
128
|
+
- `CLEAN` — what `rig-pose clean` did to the sequence: its windows, spikes, jitter before → after, and the stillness hold counted — holds, joints, regions and their frames, the joint spans not held, the seams ([#238](https://github.com/rig-c/pose/issues/238)). The per-joint record is the sequence's `cleaned` (`holds`, `unheld`, `seams`), which `--report` writes whole; `--verbose` prints it on the line.
|
|
124
129
|
- `RIGC` — the rig-c version and entry that gated and posed the run (also `rigc` in the report): a later rig-c can change the bytes.
|
|
130
|
+
- `SCALE` — when the map translates a bone: the figure scale, `scale.rig` over the median picture length of `scale.pose`, and ([#240](https://github.com/rig-c/pose/issues/240)) that median as a share of the segment's 95th percentile — under 1, the median frame foreshortens the segment and the factor is that much larger than the segment at its longest gives. Then a `bands` line: the rig-to-picture ratio of the legs (hip → ankle: the rig's at setup over the picture's 95th percentile, to over its median, either side) and of the hips (thigh roots apart at setup over `r_hip → l_hip`'s 95th percentile, to over its median), each `within` or `outside` — within when the rig's length carried into the picture by the factor (rig ÷ factor) lies between the picture's median and 95th percentile give or take one picture px, so a band the picture never widened is not missed by a rounding the picture cannot show (the wave re-cleaned with the hold on reads a factor of 1.00001 against a hip band of exactly 1.00000); an `outside both bands` line when the factor lies outside both — the scale pair sizes the figure as neither its legs nor its hips do; and a `hip travel` line, the translated joint's x and y range, the pose's in px beside the keyed bone's in units, each also in leg lengths (`legs`: the two sides' mean, the picture's 95th percentile and the rig's at setup) and hip widths (`hips`: the picture's 95th percentile, the rig's thigh roots at setup). A reading (`scaleFit` in the report); nothing it prints changes a key. The wave: leg 0.99698–0.99698 and hip 1.00000–1.00000, both within; no `outside both bands` line.
|
|
125
131
|
- `HOLD` — a joint missing, or scored under `--min-score`, holds its last good value; one line per span, with the frames and the reason.
|
|
126
132
|
- `SHORT` — a segment shorter in the picture than `--short` of its median length (with `--short <fraction>`; off by default) has its angle there interpolated, not read; one line per span, with the chain, the segment, the frames and the lowest fraction. Below.
|
|
127
133
|
- `END-ON` — a `SHORT` run lasting at least 0.15 s: the limb points at the camera, a pose the rig cannot draw.
|
|
128
134
|
- `FLIP` — a segment that turns more than `--flip` degrees in one frame (with `--flip <deg>`; off by default) is spread over the frames around the jump; one line per spread, with the chain, the segment, the jump, the frames and h. Below.
|
|
129
|
-
- `BEND` — with `--bends clamp`, a hinge bent the wrong way is straightened on that frame instead of refusing.
|
|
135
|
+
- `BEND` — with `--bends clamp`, a hinge bent the wrong way is straightened on that frame instead of refusing; one line per hinge with the count, the frame range and the worst angle (one per frame with `--verbose`; every frame in the report's `bends`).
|
|
130
136
|
- `FOLD` — `rigc render --folds` over the new animation: every mesh that folds, and when. A reading, not a gate.
|
|
137
|
+
- `SEAM` — rig-c's seam reading (`rigc render --seam`, rig-c ≥ 2.60.0) over the new animation, for the rig's declared neighbour pairs ([#216](https://github.com/rig-c/pose/issues/216)): one line per span of a **gap** (background inside the joint) or a **ghost** (one piece's overlap swung clear of its partner) over the one-cell floor, with the pair, the frames and their times, the rate and the most cells; per pair the frames rigc named that were one cell thick (`under the floor — not reported`), a pair not read and why, or rigc's SKIP (the two drawings share no cell at rest — nothing measured, never a seam that held); then `n span(s) over k pair(s) at <fps> fps — r of d declared pair(s) read`. **The pairs**: a rig spec carries none — `rig.json` holds bones, slots and skins — so they come from @rig-c/parts' `parts.json`, named with `--parts <file or directory>` (its build's `--out`, beside `rig/`): each limb's consecutive segments (a part's `limb` record: `name`, `segment`, and below a joint `joint`), a pair named twice counting once, each part drawn by the slot of its name — the pairs @rig-c/parts' own `check` reads (rig-c/parts#503). A pair whose part has no slot of its name is `not read`, named. Without `--parts`, or with a `parts.json` that holds no limb, the line is `SEAM no neighbour pair declared — <why>; seams: []`, and where the rig's parent directory holds a `parts.json` it says so. **The floor** is @rig-c/parts' so the two tools agree: a frame's gap or ghost counts when it holds more cells than its box is long (its longer side, in cells) — thicker than one cell; a crack one cell deep is the half-opacity edge moving by less than a cell. Its own render, at the rate the folds are read at; it moves no byte of the motion or the build, and never changes the exit or the gate. A malformed `parts.json` is an input and is refused (`PARTS_JSON`).
|
|
138
|
+
- `JITTER` — one line per bone a chain keys (a face or body dial bone excepted), in map order ([#236](https://github.com/rig-c/pose/issues/236)): the mean |second difference| of its local rotation as rig-c's poser posed the gated build (its world matrix in its parent's axes, so the rotate key up to a constant), against the same figure of its map pair's picture angle less the pair of the nearest keyed bone above it, in °/frame², and the ratio. Both are read on the same triples, three consecutive frames on which every pose point the two pairs need is good. A pair whose figure rounds to 0 is still, and gets no ratio. A group's jitter ratio is its bones' together; the line names the bone that moved it. A reading, not a gate.
|
|
139
|
+
- `REACH` — a `reach` chain ([below](#the-map-rig-pose-map1)) whose aim departs from its own pair's angle ([#236](https://github.com/rig-c/pose/issues/236)): why (its origin sits off the anchor's at setup, a lever; and the bones above it the animation turns, or translates other than by the anchor's travel), the largest departure with its frame, the departure's mean |second difference|, and the aim's against the pair's, in °/frame², with the ratio. A reach whose bone sits on the anchor's origin with nothing above it moved is aimed along the pair and prints none. This line also names, with `--raised-by-head` or a `"by-hip"` set, a placed arm out of reach (below); the two read `REACH chain …` and `REACH set …`. A reading, not a gate.
|
|
131
140
|
- `POSTURE` — when the map has a pelvis chain (joints `r_hip`, `l_hip`) and a `translate`: the hip line's tilt range, and the sway — the x-range of mid_hip − mid_shoulder — in rig units and in shoulder widths, the pose's beside the posed rig's. A reading, not a gate. A frame on which either hip is `inferred` is left out of the tilt. When that is every frame, as after `clean --hips-inferred`, the line is `hip tilt not read: hips inferred (declared at clean)` and the report's `posture` has `"tilt": null` with `"tiltWhy"`; on some frames only, the tilt is read over the others and the line counts the frames left out (`posture.hipsInferred`). The source is read from the clean record, or is `source not stated` when the joints carry the state and the record does not. With a `measured` record ([#86](https://github.com/rig-c/pose/issues/86), [#106](https://github.com/rig-c/pose/issues/106)), the read, interpolated, scaled and held frames are the measurement, so the tilt is read on all four, while `unmeasured` frames (`fit.corr < 0.9`) are left out and named. The line adds `tilt measured (<method>) on N frame(s): R read, I interpolated, H held (held: <spans>)` (plus `scaled` and `unmeasured` spans when present). The report's `posture` carries `tiltMeasured`. The sway reads the hips as before. With `--plant-feet`, `POSTURE` also prints one `bounce` line per support span ([#107](https://github.com/rig-c/pose/issues/107), `posture.bounce` in the report): the detrended (centred ±12-frame moving mean removed) vertical standard deviation, rig/reference ratio and correlation for the hip, the neck and `eyes − neck`.
|
|
132
141
|
- `SWITCH` — with `alternates` in the map ([below](#alternate-drawings-alternates)): each span on which a set of alternate drawings shows, its slots, its frames, how far the pose was from the set's key skeleton, how far each bone with a `drawn` angle was turned from it, and where each of its boundaries was moved to (`moved`, [below](#alternate-drawings-alternates)).
|
|
133
142
|
- `UNCOVERED` — with `alternates` in the map: every fold on a frame where no switch took the folding mesh off screen. This is the list of poses still to paint.
|
|
@@ -136,20 +145,30 @@ and what the run cannot decide for you it lists:
|
|
|
136
145
|
- `RAISED` — with `--raised-by-head` ([below](#alternate-drawings-alternates)): one line per span of a set that draws an arm raised, per chain. It gives the frames and the median eye distances (the rig's in units, the reference's in px). It gives the wrist's offset from the head centre in eye distances: the median of the reference's, of where the wrist was placed, of the rig's on the pose's angles, and of the rig's as keyed. It gives the worst |rig − reference| before and after, the worst |after − placed|, and the frames whose upper arm the set's `turn` held. `RAISED none` when the flag is given and no such set showed.
|
|
137
146
|
- `BY-HIP` — with a set's `"place": "by-hip"` ([below](#alternate-drawings-alternates)): the `RAISED` line against the hip, one per span of such a set per chain it places. The unit is the hip width, the offset is from the mid-hip. `BY-HIP none` when the map has such a set and none of its spans showed.
|
|
138
147
|
- `REACH` — with `--raised-by-head`, or a `"by-hip"` set: a placed span whose target was out of the arm's reach, too far or too near. It names the frames, which way, and the shortfall in eye distances (hip widths for `"by-hip"`). The wrist was placed at the reach limit instead. This is not `ART-RANGE`: the rig's proportions were the limit, not the art's range. A too-near frame is placed at the limit like a too-far one, and graded at the placed distance.
|
|
139
|
-
- `LEG-SCALE` — with `--leg-scale` ([below](#the-legs-foreshortening-and-planted-feet)): per leg segment, its straight length, the spread of its picture length over it, the frames clamped at each limit, and (with `--plant-feet`) one `past the floor (support)` line per supporting run that asked under the floor ([#133](https://github.com/rig-c/pose/issues/133), `legScale[].support` in the report).
|
|
148
|
+
- `LEG-SCALE` — with `--leg-scale` ([below](#the-legs-foreshortening-and-planted-feet)): per leg segment, its straight length in px and as a share of `FEET`'s leg length, the spread of its picture length over it, the limits in px, the frames clamped at each limit, where its leg was free on clamped frames how far the clamps on that leg left the free ankle above or below the picture's (in the rig's leg at setup, thigh origin → ankle, and in `FEET`'s px; `legScale[].free` in the report, [#237](https://github.com/rig-c/pose/issues/237)), and (with `--plant-feet`) one `past the floor (support)` line per supporting run that asked under the floor ([#133](https://github.com/rig-c/pose/issues/133), `legScale[].support` in the report).
|
|
140
149
|
- `PIVOT` — when the map translates a bone and has both legs: the translated bone's origin against its thigh roots' midpoint at setup, in units and hip widths — the pivot every hip turn swings the legs about.
|
|
141
|
-
- `FEET` — when the map has both legs: the supports and jumps read off the reference ankles (planted or not), and on the posed build, outside the jumps, the lowest ankle's vertical range and each support span's worst vertical range and horizontal drift of the supporting ankle, in leg lengths.
|
|
142
|
-
- `STANCE` — with `--stance` ([below](#the-legs-foreshortening-and-planted-feet)): one line per run of frames planted both (with the stretch keyed, its largest k), lifted, or past the tolerance below (the pose's height kept, [#114](https://github.com/rig-c/pose/issues/114)), with the free side, the frames and the tolerance; and one per run whose stretch fell past its bounds (`past the bounds`), with the k the floor needed, the bound it had and how far the foot was left from it; and, where the leg's own range set a lower bound below 0.92, one `bound from the leg's own range` line with each side's lowest.
|
|
143
|
-
- `SHEAR` — with `--leg-scale`: every scaled bone and every bone under one, how far its art is sheared and how long its world axes are against setup. A reading.
|
|
150
|
+
- `FEET` — when the map has both legs: one `segment` line per mapped leg segment with its picture length ÷ straight length (min · median · max), the scale `--leg-scale` would ask before its clamp, printed with or without the flag so the minimum can be read before choosing the range ([#237](https://github.com/rig-c/pose/issues/237)); the supports and jumps read off the reference ankles (planted or not), and on the posed build, outside the jumps, the lowest ankle's vertical range and each support span's worst vertical range and horizontal drift of the supporting ankle, in leg lengths.
|
|
151
|
+
- `STANCE` — with `--stance` ([below](#the-legs-foreshortening-and-planted-feet)): a `unit` line first, naming the tolerance's unit (`FEET`'s leg length, each side's median hip → ankle) in px and as a share of the straight leg (the 95th percentile hip → ankle) ([#237](https://github.com/rig-c/pose/issues/237)); one line per run of frames planted both (with the stretch keyed, its largest k), lifted, or past the tolerance below (the pose's height kept, [#114](https://github.com/rig-c/pose/issues/114) — printed only where no `--leg-scale` clamp moved the free ankle; otherwise the frames it moved and how far, [#237](https://github.com/rig-c/pose/issues/237)), with the free side, the frames and the tolerance in leg lengths and px; and one per run whose stretch fell past its bounds (`past the bounds`), with the k the floor needed, the bound it had and how far the foot was left from it; and, where the leg's own range set a lower bound below 0.92, one `bound from the leg's own range` line with each side's lowest.
|
|
152
|
+
- `SHEAR` — with `--leg-scale`: every scaled bone and every bone under one, how far its art is sheared and how long its world axes are against setup. A reading. Where the shear prints above 0.00°, the line names where it comes from ([#240](https://github.com/rig-c/pose/issues/240)): a bone that inherits `normal` from a scaled parent (or one under a scaled bone) takes its parent's unequal axes into its art (`it inherits "normal" from scaled "…"`), and `"inherit": "noScale"` on that bone in the rig spec is the fix — it still turns with its parent, and rig-pose keys it through its parent ([#153](https://github.com/rig-c/pose/issues/153)); `onlyTranslation` removes the shear too and also stops it turning. On the tests' bent knee under a scaled thigh, the shin's 5.27° is 0.00° with `noScale`.
|
|
144
153
|
- `IDLE`, `IDLE-DROPPED`, `IDLE-KEPT-OUT`, `MERGE` — the rig's idle against the new animation, and what `--carry-idle` and `--merge` put back ([below](#the-idle-what-the-retarget-drops)).
|
|
145
|
-
- `REDUCE` — with `--reduce` ([below](#few-keys---reduce)): per bone, its keys before and after and its largest error, with the frame; then the total.
|
|
154
|
+
- `REDUCE` — with `--reduce` ([below](#few-keys---reduce)): per bone, its keys before and after and its largest error, with the frame; then the total. At the end of the run, one `ankle drift allowed` line per ankle ([#240](https://github.com/rig-c/pose/issues/240)): the tolerance its `LAND` line held it to, in units and in its side's rig leg at setup, split into the plant's and the reduction's, and the two-frame slide that allows.
|
|
155
|
+
- `NOD` — with a chain carrying the eye line ([#241](https://github.com/rig-c/pose/issues/241)): the head bone and chain, the keyed translate's y range in units over the frames, the window its baseline was read over (`none (in-place)`, or ±*h* frames `wrapped on the loop` or `cut at the clip's ends`), and outside in place the clamp (±2% of the neck's setup length) with the frames it acted on and the coupling that follows: `NOD bone "head" (chain "head", r_eye → l_eye): translate y -0.400 … +0.400 units over 168 frame(s); window ±12 frame(s) (0.5 s) running median, detrended, wrapped on the loop; clamp ±0.400 units (2% of the neck's setup length) acted on 140 frame(s), so the clamped nod is coupled in phase into bone "head"'s rotate (+1.6°/unit) on every frame`. When no nod is keyed it says why instead: `"nod": false`, a translate on the bone (the map's or a set's offsets), a scaled bone above it, no good neck, no scale, or eyes that do not move against the neck (the example wave).
|
|
146
156
|
- `FACE` — with `face` in the map ([#138](https://github.com/rig-c/pose/issues/138), [#173](https://github.com/rig-c/pose/issues/173)): per-frame yaw and pitch read from `nose` against the eye midpoint (`r_eye + l_eye`) and, when present, the ear midpoint (`r_ear + l_ear`), de-rolled by the eye line, then compressed into each configured axis's `[min, max]` range (`scale` and correlation `r`), and, when `face.yaw.sets` (or `face.sets`) is declared, every yaw-switched set's entered spans, swap count, and any `hides` slot keyed by another track, written as `face` in `--report`.
|
|
147
157
|
- `BODY` — per-frame body yaw read from the camera-normalised shoulder width (`r_shoulder + l_shoulder`) and hip width (`r_hip + l_hip`) projected perpendicular to the trunk against the sequence's standing width (`yaw ≈ acos(w / w_standing)`, [#173](https://github.com/rig-c/pose/issues/173)): its overall and opening (`frames 0–47`) range, every span with `|yaw| ≥ 45°`, and, when `body.yaw` or `body.sets` is declared in the map, its knee-mapped dial keys and every yaw-switched set's entered spans, swap count, and any `hides` slot keyed by another track, written as `body` in `--report`.
|
|
148
158
|
- `TURN` — a line across the body (hips, shoulders, eyes) shorter in the picture than 0.5 of its median length, or reversed, for at least 0.15 s: the body or the head turned. A reading of the pose, made whether or not the map uses the line; it changes no key. The demand for a turned-body drawing, as `END-ON` is for a foreshortened limb.
|
|
149
159
|
|
|
150
|
-
|
|
160
|
+
**How much it prints** ([#238](https://github.com/rig-c/pose/issues/238)). By default the lines above, with the CLEAN line's hold record counted and a hinge's wrong-way frames in one line. `--verbose` prints every line in full, as the run did before #238: the CLEAN line with the record, a `BEND` line per frame, and before each collapsed `POSE_BEND_ONE_WAY` its per-frame refusals. `--summary` prints the run line, one line per measure and the `wrote` lines — the lines two runs are compared on:
|
|
161
|
+
|
|
162
|
+
- `GATE` — the `GATE` line.
|
|
163
|
+
- `FEET` — the posed build's two `FEET` lines' figures: the lowest ankle's vertical range, and the supporting ankle's worst vertical range and horizontal drift over the support spans; `not measured` without both legs in the map.
|
|
164
|
+
- `LIFT` — the `STANCE` runs counted (absorbed runs are inside their neighbours): lifted, planted both and below, each in runs and frames, the highest lifted run, and the runs past the bounds; `not read (no --stance)` without it.
|
|
165
|
+
- `JITTER` — the `JITTER` lines: how many bones, the worst ratio and its bone, the still pairs and the bones not measured.
|
|
166
|
+
- `LAND` — the `LAND` lines: how many, and per kind (aim, origin, scale, ankle) the count, the worst and its bone and frame, and the tolerance.
|
|
151
167
|
|
|
152
|
-
|
|
168
|
+
Each figure is formatted as the full line formats it, from the same record. Refusals print the same in every mode, and the exit code and the gate do not change with it; `--verbose` with `--summary` is a usage error.
|
|
169
|
+
|
|
170
|
+
`--report <file>` writes all of it as JSON (`rig-pose-report/1`): `spec`, `rigc` (`{ version, entry }`: the rig-c that gated it), `rig`, `animation`, `inputs` (consumed rig spec, map and pose files, each with invocation-relative `path` and exact-byte `sha256`; a folder rig resolves to its actual rig.json; these three identities do not claim to bind optional base motion, sets or images), `frames`, `duration`, `frameTimes` (the exact motion-key time for each pose frame; never FPS inferred from duration), `render` (the separate renderer sampling domain, `{ fps, frames }` from frame-set metadata, or `null` when not measured), `cleaned` (the sequence's `cleaned` record, [above](#clean-first-rig-pose-clean)), `options` (every figure the run used, `switchTolerance`, `switchMin`, `switchAt`, `legScale`, `plantFeet`, `stance`, `floor` and `raisedByHead` included), `scale`, `scaleFit` (the `SCALE` reading: `segment`, `leg`, `hip`, `outside`, `travel`; `null` without a scale), `holds`, `short`, `endOn`, `bends`, `flips`, `gate` (rig-c's gate summary, a string), `landings`, `jitter`, `reach`, `posture`, `folds`, `seams`, `seamPairs`, `body` (`standing`, `read`, `over45`, `framesOver45`, and with `body.yaw` or `body.sets` in the map, `yaw` and `sets`), `legScale`, `pivot`, `feet` (`planted`, `legLength`, `straight`, `rigLeg`, `segments`, `supports`, `jumps`, `jumpEvents`, `steps`, `changes`, `lowered`, `floor`, `shifts`, `blend`, `closure`, and the posed `reading`; `straight` is each side's 95th-percentile picture length hip → ankle in px and `rigLeg` each side's rig leg at setup (thigh origin → ankle) in world units, both `null` without both legs; `segments` holds one `{ chain, bone, joints, straight, ratio: { min, median, max }, why? }` per mapped leg segment, read with or without `--leg-scale` (`straight` and `ratio` `null`, and `why` said, where it was not measured), [#237](https://github.com/rig-c/pose/issues/237); a jump is an inclusive `[first, last]` pair of frames; `lowered` holds `{ frames, was, now, cause? }` runs (`cause`, where `now` is `null`: `released` or `higher`), present only with `--plant-feet` on a sequence not declared in place ([#113](https://github.com/rig-c/pose/issues/113)); `floor` is the world y every support span was planted on and `shifts` holds one `{ side, frames, shift }` per support span in world units, `null` and empty without `--plant-feet`; `blend` is `{ bounds, runs: [{ side, frames, k: { min, max } }], over: [{ side, frames, needed, left }] }`, the supporting leg stretched on the blend frames, `null` without `--plant-feet`; `closure` is `{ drift, frames }`, the hip's x drift closed over an in-place loop, `null` unless the sequence is `"in-place"` and `--plant-feet` is given), `stance` (`tolerance`, `release`, `minSeconds`, `bounds`, `runs`: `{ state, side, frames, maxHeight, maxDepth?, k, absorbed?, moved? }` (`state` is `both`, `lifted` or `below`; `maxDepth`, on a `below` run, is the free ankle's largest distance below the supporting one in leg lengths; `moved`, on a `lifted` or `below` run, the frames whose free ankle a `--leg-scale` clamp moved off the pose's height, as `legScale[].free` is written, [#237](https://github.com/rig-c/pose/issues/237)), `over`: `{ side, frames, needed, left }`; `null` without `--stance`), `shear`, `idleDropped`, `idleKeptOut` and `merged` ([below](#the-idle-what-the-retarget-drops)), `reduce` (the tolerances, each row of `REDUCE` and `ankleDrift`, [below](#few-keys---reduce)), and the demand lists. The fields a run is graded by:
|
|
171
|
+
Report event objects with a pose `frame` or inclusive `frames` also carry exact `t` or `times` from `frameTimes`; existing `seconds` remain span lengths, with additive `spanSeconds`. Holds preserve their frame-index `from`/`to`. Existing scan joint/chain identities remain beside map-derived `bone` (a unique mapped target) or `bones` (all actual mapped targets); no joint-name heuristic chooses a target. `feet.jumps` stays an array of original frame pairs; `feet.jumpEvents` adds matching `{ frames, times }` objects. Fold, uncovered and seam rows preserve their discrete `times` and gaps, declare `domain: "render"`, and use the separate root `render` domain, never fabricated nearest pose-frame indices. The run hashes map/pose from the exact buffers passed to their readers, snapshots the resolved rig file before its gate, and rechecks all three before user-output writes. The writer validates coordinates before output writes and never changes motion keys. The switch diagnostic `moved.frame` is a selected-to-keyed pair with signed `frames`/`seconds` displacement; it is preserved as diagnostic data, not reinterpreted as an inclusive interval. An explicitly supplied uniform writer clock requires both positive `fps` and seconds offset `frame0`; canonical pose input does not yet declare FPS, so the retarget producer always writes exact `frameTimes`.
|
|
153
172
|
|
|
154
173
|
|
|
155
174
|
| field | each entry |
|
|
@@ -158,7 +177,11 @@ Report event objects with a pose `frame` or inclusive `frames` also carry exact
|
|
|
158
177
|
| `bends` | `{ chain, joint, frame, t, angle, side, rest }`: a hinge found bent the wrong way (`angle` in degrees, Spine's sense; `side` ±1, the side it may bend) |
|
|
159
178
|
| `endOn` | `{ chain, bone, joints, frames, seconds, minFraction }`: a limb pointing at the camera for ≥ 0.15 s |
|
|
160
179
|
| `landings` | `{ bone, worst, frame, kind, tolerance }`: where rig-c's poser put each bone, against the target. `kind` is `angle` (degrees), `origin` or `foot` (world units), or `scale` (a fraction of the setup length). A run writes only when every `worst` ≤ `tolerance` |
|
|
180
|
+
| `jitter` | `{ bone, chain, joints, against, rig, pair, ratio, triples, reach }`: the `JITTER` line's figures, one per bone a chain keys. `rig` and `pair` are mean \|second difference\| in °/frame², `against` the keyed bone above whose pair the pose's local angle is read against (`null` at the top), `ratio` = `rig` ÷ `pair` (`null` when the pair is still: its figure rounds to 0), `triples` how many three-frame runs both were read over, `reach` whether the chain has `"reach"` |
|
|
181
|
+
| `reach` | `{ chain, bone, joints, anchor, offset: { units, ofLength }, above: [{ bone, property, chain }], departure: { max, frame, t, meanD2 }, aimD2, pairD2, ratio }`: the `REACH` line's figures, one per `reach` chain whose aim departs from its pair; empty when none does. `offset` is the bone's setup distance from the anchor's origin (units, and a fraction of the reached length), `above` the bones over it the animation turns (`rotate`, with the chain that turns it) or translates other than by the anchor's travel (`translate`), `departure` the aim − the pair's angle (largest \|·\| in degrees, its frame, and its mean \|second difference\|), `aimD2` and `pairD2` the two angles' mean \|second difference\| over every frame, `ratio` = `aimD2` ÷ `pairD2` |
|
|
161
182
|
| `folds` | `{ slot, attachment, times }`: every mesh `rigc render --folds` saw fold, at those seconds; `null` when it did not read |
|
|
183
|
+
| `seams` | `{ pair, kind, frames: [first, last], times: [first, last], maxPx, domain: "render" }`: one per consecutive span of one kind (`gap` or `ghost`) over the one-cell floor, per declared pair (`"<a>\|<b>"`), in the pairs' order, gap before ghost; `frames` are the seam render's frame indices (rigc's `f<index>.png`) and `times` rigc's seconds, `maxPx` the most cells on a frame of the span. `[]` when none opens, when no pair is declared, and when none was read — `seamPairs` says which. Always present: the writer refuses a report without it (`REPORT_SEAMS`) |
|
|
184
|
+
| `seamPairs` | `{ pair, declared, status, opens, maxPx, underFloor, reason? }`: one per declared pair. `status` is `read`, `skip` (rigc read no band: nothing measured) or `unread` (not rendered; `reason` says why); `opens` the render frames over the floor, `underFloor` those rigc named that were one cell thick, `declared` where `parts.json` declared it. `[]` when no pair is declared. Always present (`REPORT_SEAMS`) |
|
|
162
185
|
|
|
163
186
|
The demand lists:
|
|
164
187
|
|
|
@@ -202,6 +225,8 @@ A knee bending toward the camera is seen from the front only as a shorter leg, a
|
|
|
202
225
|
|
|
203
226
|
**`--leg-scale <min,max>`** scales each mapped leg segment (two of one side's hip, knee and ankle) along its own x axis — a `scalex` key — by its picture length on the frame over its **straight length**, the larger of the two sides' 95th-percentile picture lengths for that segment over the sequence ([#148](https://github.com/rig-c/pose/issues/148), weighted by the segment's share of the two legs' mean setup bone length when the rig's setup legs differ by more than 0.1 % so an asymmetrically drawn rig places both ankles at the sequence's relative height, [#225](https://github.com/rig-c/pose/issues/225); on a non-`in-place` sequence longer than 25 frames, each frame's segment length is normalised by a continuous rate-limited figure-scale signal `figureScaleOf` derived from posture- and crouch-invariant segment measures — `max(r_thigh + r_shin, l_thigh + l_shin)` plus the `mid_hip → mid_shoulder` trunk segment held at each support span's 95th-percentile straight length and ramped linearly across non-support airborne gaps into a larger incoming support span when the 5-series consensus moves past ±2 %, never vertical `ankle.y − nose.y` — with frame-to-frame step ≤ 1 % and frozen spans ≥ 5 frames held at their median, so neither camera zoom, a forward trunk bend nor a landing crouch reads as the figure changing size or steps the denominator on landing, and `--camera <file>` writes that track as `rig-pose-camera/1` without changing `motion.json`, [#159](https://github.com/rig-c/pose/issues/159), [#161](https://github.com/rig-c/pose/issues/161), [#163](https://github.com/rig-c/pose/issues/163), [#172](https://github.com/rig-c/pose/issues/172), [#226](https://github.com/rig-c/pose/issues/226), [#231](https://github.com/rig-c/pose/issues/231)), clamped to [min, max]. When the camera-normalised trunk (`neck → mid_hip` or `mid_shoulder → mid_hip`) foreshortens or extends by more than 4 % from its standing length, the trunk bone is keyed in `scaley` (for a `+y`-aimed trunk, `scalex` for `+x`) clamped to the same `[min, max]` bounds, carrying the trunk and head's depth bend while keeping the aimed rotation ([#172](https://github.com/rig-c/pose/issues/172)). The bounds are `<min,max>` with `0 < min ≤ 1 ≤ max`. A segment the map aims other than `"x"` is refused (`LEGS_MAP`). `LEG-SCALE` lists each segment's frames clamped at either limit with the unclamped `asked` scale range on each clamped run (`legScale[].clamped` in `--report`, `{ frames, times, limit, asked: { min, max } }`, [#225](https://github.com/rig-c/pose/issues/225)).
|
|
204
227
|
|
|
228
|
+
**Its unit is the segment's own straight length, not a leg length** ([#237](https://github.com/rig-c/pose/issues/237)). `min` and `max` are shares of the segment's 95th-percentile picture length, which `LEG-SCALE` prints in px beside its share of `FEET`'s leg length (the median hip → ankle `--stance` is in); the two are different units and different percentiles. `FEET` prints every mapped segment's picture length ÷ straight (min · median · max) with or without `--leg-scale`, so the fold the clamp would meet can be read first. A free leg clamped at the floor is drawn longer than the picture's, so its ankle hangs lower (or, past the ceiling, higher) than the picture's: on the clamped frames of a free leg `LEG-SCALE` prints how far, against the same leg posed at the picture's scale on every segment, the supporting leg as keyed in both, in the rig's leg at setup (`legScale[].free`: `{ side, frames, count, above, below }`, `above`/`below` as `{ units, legLengths }` or `null`). The card's `0.8,1.05` ([#47](https://github.com/rig-c/pose/issues/47)) was read off a dance whose leg picture length ran 0.86–1.18 of its median; a walk folds its swing leg further (on the tests' synthetic lift, a free leg folded to 0.69 under a 0.85 floor hangs 0.1591 leg lengths below the picture's), so read the `FEET segment` minimum before taking it.
|
|
229
|
+
|
|
205
230
|
rig-c's `scalex` multiplies the bone's x axis, and a child with the `normal` inherit mode takes its parent's whole world matrix. So under a thigh scaled by s, a shin at angle φ in the thigh's axes points at atan2(sin φ, s·cos φ), and is itself scaled by |(s·cos φ, sin φ)|. A child (or its intermediate control bone) with `noScale` or `noScaleOrReflection` ([#153](https://github.com/rig-c/pose/issues/153)) still has its direction carried through the parent's scaled matrix, so its angle is skewed the same way, while rig-c normalises that direction to unit length so the parent's scale is not inherited. The rotate key `turn − turn above` assumes unit scale. A mapped bone under a scaled bone is therefore keyed through its parent's posed matrix: a `rotate` key k and a `scalex` key s' put its world x axis on its target at the scale asked (under `noScale`, s' is the bone's own asked scale s directly). On `dance-a` the unit-scale formula missed the shins by up to 2.4°, their length by 0.16 of setup and the ankles by 38 units; solved, every bone lands. A `normal` child's art is still sheared by its parent's scale, and a bone under it (a foot) is squashed along the shin: `SHEAR` reads both.
|
|
206
231
|
|
|
207
232
|
**`--plant-feet`** places the hip so the supporting foot stays where it took support, through the posed legs at the scale the pose asked for (when a segment asks for a scale below `--leg-scale`'s clamp floor, the clamp does not lift the hip by what it withheld, [#107](https://github.com/rig-c/pose/issues/107); on a supporting leg, the segment's `scalex` follows the asked scale down to the bound #95 derives from the leg's own range so the supporting ankle stays on #85's floor while the free leg keeps the configured clamp, and any frame clamped at #95's bound lifts the hip by the remainder so the ankle never sinks, listed as `LEG-SCALE past the floor (support)`, [#133](https://github.com/rig-c/pose/issues/133)), instead of moving it by the pose's mid_hip travel:
|
|
@@ -209,12 +234,12 @@ rig-c's `scalex` multiplies the bone's x axis, and a child with the `normal` inh
|
|
|
209
234
|
- The supporting foot is the one whose reference ankle is lower in the picture. It changes only when the other ankle has been at least **0.02 leg lengths** lower for at least **0.1 s** (timed as `END-ON` runs are), and once confirmed takes support from the frame it crossed that margin ([#102](https://github.com/rig-c/pose/issues/102)); a leg length is each side's median picture length hip → ankle, and the switch compares against their mean.
|
|
210
235
|
- The new support is held where it is on the frame it takes over, and the hip blends in y from the old derivation to the new over **3 frames** while taking the new derivation's x immediately ([#102](https://github.com/rig-c/pose/issues/102)), so the planted foot does not slide across the blend.
|
|
211
236
|
- A **jump** — both reference ankles **and** mid_hip more than **0.05** leg lengths above their **rolling floor**, the 95th-percentile picture y over ±1.0 s around the frame, for at least **2 frames**, starting from the first immediately preceding push-off frame where both ankles are already above their rolling floor while mid_hip lags ([#142](https://github.com/rig-c/pose/issues/142)) — releases the plant: the hip follows the pose's travel from where it was, and re-plants on landing (blended the same way). At a walking hand-over the front foot has not landed yet while the back ankle lifts its heel and the hip rises 0.051–0.065 leg lengths; when the front foot lands while the back ankle stays above its floor, a back ankle that holds its height in y (< **0.01** leg lengths a frame for **2 frames**) is on a raised heel rather than in flight and is not read as a jump ([#101](https://github.com/rig-c/pose/issues/101)). The body has to lift: an ankle's picture y drifts with depth, and under one floor for the whole sequence the subject in `dance-a` standing still (frames 0–29 and 277–312, mid_hip moving 10 and 14 px) read as jumping, 152–155 of 313 frames in all.
|
|
212
|
-
- **In place** ([#92](https://github.com/rig-c/pose/issues/92)). On a sequence with `"travel": "in-place"`, the supporting foot of a figure that does not travel moves back in the picture while the body stays. Holding it where it planted turns that slide into the body's travel, so the hip walks off and never comes back. Under the declaration each plant's anchor follows its own ankle's picture x, scaled into the rig, from the frame it planted, and keeps its y on the one floor. No step releases a foot, since a foot that follows its track has no plant to drag. Every foot may take support, and the lower one does, by the rules above. The loop is then closed: the hip's x on frame i moves by −drift · i / (n − 1), where drift is its x on the last frame less its x on the first. The last frame's hip x is then the first's, and the loop carries no net travel. The y is the floor's and is not touched. `FEET` prints the drift closed, in place of the steps line, and the report's `feet.closure` carries `{ drift, frames }` (`null` otherwise). On the tests' synthetic in-place loop (40 frames, each supporting ankle sliding back 2 px a frame, the hips still), the hip moves less than 10⁻⁹ px. The same joints without the declaration put it 68 px forward over the loop. With a slide of 0.04 leg lengths a frame, both feet are released as stepping and never re-plant without the declaration, and the supports alternate with it. Jumps are read as before.
|
|
237
|
+
- **In place** ([#92](https://github.com/rig-c/pose/issues/92)). On a sequence with `"travel": "in-place"`, the supporting foot of a figure that does not travel moves back in the picture while the body stays. Holding it where it planted turns that slide into the body's travel, so the hip walks off and never comes back. Under the declaration each plant's anchor follows its own ankle's picture x, scaled into the rig, from the frame it planted, and keeps its y on the one floor. No step releases a foot, since a foot that follows its track has no plant to drag. Every foot may take support, and the lower one does, by the rules above. The loop is then closed: the hip's x on frame i moves by −drift · i / (n − 1), where drift is its x on the last frame less its x on the first. The last frame's hip x is then the first's, and the loop carries no net travel. The y is the floor's and is not touched. `FEET` prints the drift closed, in place of the steps line, and the report's `feet.closure` carries `{ drift, frames }` (`null` otherwise). On the tests' synthetic in-place loop (40 frames, each supporting ankle sliding back 2 px a frame, the hips still), the hip moves less than 10⁻⁹ px. The same joints without the declaration put it 68 px forward over the loop. With a slide of 0.04 leg lengths a frame, both feet are released as stepping and never re-plant without the declaration, and the supports alternate with it. Jumps are read as before. **What planting is and is not needed for** ([#242](https://github.com/rig-c/pose/issues/242)): the declaration is the sequence's, so the support reading (no step releases a foot), the camera-zoom normalisations and the head nod (no detrend window, no clamp) take it with or without `--plant-feet`. Planting is needed only for what acts on a planted hip: the anchor following its ankle's track and the loop closure, since without a plant there is no anchor to follow and the hip is the pose's own travel. Without `--plant-feet` the run says so in a `TRAVEL` line: `sequence declares travel: in-place but --plant-feet is off — its supports and the head nod are read in place as with it; the loop closure is --plant-feet's (it closes the planted hip), so the hip follows the pose's own travel: x +0.00 units from the first frame to the last, not closed` (the figure is the hip's measured x drift; `and the map translates no bone` when nothing is translated). On the tests' synthetic walk in place (7 cycles of 24 frames, the supporting ankle sliding up to 0.09 leg lengths a frame, the eyes bobbing 3 px twice a cycle), without `--plant-feet` the supports are 15 spans covering all 168 frames (the seam splits the first) and the head's rotate key and nod are the same on cycle 1 and cycle 7 to 0.001; reading the declaration only under `--plant-feet`, as before, they were 2 spans covering 3 frames, and the head's cycle 7 differed from cycle 1 by 1.057° and 0.661 units. That head figure came from the nod's detrend window cut at the clip's ends, which [#241](https://github.com/rig-c/pose/issues/241) wraps on a loop, so it no longer reproduces under that reading either.
|
|
213
238
|
- A **step** releases the plant too: while the supporting ankle moves more than **0.03** leg lengths a frame, the other foot takes support if it is planted (not itself released or moving), and otherwise the hip follows the pose's travel. The released ankle may support again once it has moved less than **0.01** leg lengths a frame for **2 frames**. On `dance-a` the plant held the supporting ankle through a real step (frames 176–181, 579 → 718 px) and threw the hip sideways.
|
|
214
239
|
- **Read from the lower ankle** ([#113](https://github.com/rig-c/pose/issues/113), [#119](https://github.com/rig-c/pose/issues/119)). On a sequence not declared `"in-place"`, a foot whose ankle is at least **0.02 leg lengths** higher in the picture than the other's never takes support: not when a plant starts, and not when a step releases the other foot. Before, a step handed support to the only foot free to take it, even a folded, lifted one, and the hip was then placed under a foot in the air. A supporting foot that the other ankle has been that much lower than for **0.1 s** hands support to it when it may take it, as before; when the lower foot may not (it is released and not yet still, or moving), the supporting foot is released, and the hip follows the pose's travel until a foot may take support. On a frame after a jump or a step's release where the only foot free under #113 is the higher one (`cause: "higher"`), the lower foot supports **tracked, not planted** ([#119](https://github.com/rig-c/pose/issues/119)): the hip puts it on #85's floor and follows its scaled ankle track (`FEET tracked`, `feet.tracked`) until it has been still for 2 frames (`< 0.01`), when the normal plant takes over at that position. Steps and jumps are read as before, and under `"in-place"` #92's reading stands. `FEET support read from the lower ankle` names every run of frames whose support differs from the reading before #113, the side before and after (`none`: no foot supports), and for each `none` why: `released`, the supporting foot let go because the lower ankle could not take support (released by a step, or moving), or the only foot free to take support was the higher one. The report's `feet.lowered` holds them as `{ frames, was, now, cause }` (absent in place). On the tests' synthetic step under a lifted leg (the lifted ankle 0.2 leg lengths up, the grounded one stepping 0.05 leg lengths on frame 10 and still from 11), the lifted leg took support on frames 10–11 before; now frames 10–11 track the lower foot on the floor (`feet.tracked`) and the grounded one is planted from 12, when it is still again.
|
|
215
240
|
- **One floor** ([#85](https://github.com/rig-c/pose/issues/85)). A 2D rig has no depth, so its floor is one line, and a planted foot is on it. The picture disagrees: a step toward the camera puts the foot lower in the frame, and a camera residual puts a whole span higher, so a plant held where the picture puts each span leaves the floor stepping between spans. Every support span's supporting ankle is therefore moved in y onto one floor, the hip and everything above with it. A span's *planted height* is the world y (on the rig, posed by rig-c) its supporting ankle would be held at; the floor is the median of the spans' planted heights, or **`--floor <y>`** (world units, y up; only with `--plant-feet`, exit 2 without it). Each span's *shift* is floor − its planted height. A change of support still blends the hip over the 3 frames, now between two spans on the floor, and a planted foot does not leave the floor: on each blend frame the **supporting** leg is stretched along its chain about its root, by the factor `--stance` uses, k = (root y − floor y) / (root y − ankle y), held within **[0.92, 1.08]**, its lower bound on the frame the supporting leg's own range as `--stance`'s ([#95](https://github.com/rig-c/pose/issues/95); where that went below 0.92, a `FEET bound from the leg's own range` line, the report's `feet.blend.range`, and each blend run's `lower`). A frame that needs more is held at the bound, the rest left to the blend, and listed `FEET past the bounds` with the k needed and how far the foot was left from the floor. An ankle already within 0.01 units (the landing check's origin tolerance) is not stretched. The stretched scales are scale targets, so the landing check holds them; `--stance` then stretches the free leg, a different chain, to the supporting ankle. A jump follows the pose's travel from the hip it left, which is on the floor, carried linearly from take-off so the landing frame meets the floor and clamped so no frame inside the flight puts a foot below it ([#103](https://github.com/rig-c/pose/issues/103)), and is held the same way over its first 3 planted frames, with the flight's carry held across the landing blend so it fades out with the blend weights rather than dropping on the first frame after landing ([#147](https://github.com/rig-c/pose/issues/147)). `--stance` is unchanged: it stretches the free leg to the supporting ankle, which is on the floor. On the tests' synthetic stick, three spans 0, 6 and 1 px apart in the picture (0, 9 and 1.5 units on the rig) plant within 0.0001 units of one floor. With the shift forced to 0 they stay 9 units apart; without the stretch, the supporting ankle leaves the floor by 6.75 units on the first blend frame.
|
|
216
241
|
- `FEET` lists the supports, the jumps and the steps (each with the side, its frames, who took support and where it re-planted; in place, the loop's closure instead), and with `--plant-feet` the floor: its world y and where it came from, each span's shift in units and in that side's rig leg at setup (thigh origin → ankle), and the shifts' range, then one line per run of blend frames whose supporting leg was stretched (`blend held on the floor`) and per run `past the bounds`. The report carries them under `feet` (`supports`, `jumps`, `steps`, `changes`, `floor`, `shifts`, `blend`, `closure`).
|
|
217
|
-
- **`--stance <leg lengths>`** (only with `--plant-feet`, which it is refused without; off by default) plants **both** feet when the free one is near the floor ([#60](https://github.com/rig-c/pose/issues/60)). On a frame with a supporting side (so outside every jump), the free ankle's height above the supporting one is read in the picture, in the free side's leg length — the leg length `FEET` uses. The tolerance is a **distance** ([#114](https://github.com/rig-c/pose/issues/114)): within it, above the supporting ankle or below it, the frame is **planted both**: every bone of the free leg's chain has its `scalex` key multiplied by k = (root y − floor y) / (root y − ankle y) on the posed rig, the root being the chain's first bone's origin and the floor the supporting ankle, so the foot reaches the floor. A free ankle within the tolerance below the supporting one is planted too: its k is under 1, since a foot slightly under the floor is the same fault as one floating above it. A free ankle **past the tolerance below** the supporting one is not a stance but a support error (the foot read as supporting is the one in the air), so that frame is never planted both and keeps the pose's height; it is named on a `STANCE past the tolerance below` line with the side, the frames and the largest distance, and read `"below"` in the report's `stance.runs`. Before #114 the gap was signed, so any free ankle below the supporting one counted as within, and a lifted foot read as supporting had the grounded leg shrunk up to it. On the tests' synthetic stance, a free ankle 0.5 leg lengths below is read below and not planted; one 0.03 below is planted with k under 1. k is held within **[0.92, 1.08]**, except that the lower bound comes from the leg's own range in the sequence ([#95](https://github.com/rig-c/pose/issues/95)): per side, r = the leg's picture length hip → ankle on the frame ÷ its straight length (the 95th percentile of that length, as `--leg-scale` reads a segment's), and the frame's lower bound is min(0.92, r), so the rig leg may fold as far as the picture folds it on that frame and never less far than 0.92. The upper bound stays 1.08, since a picture leg does not read longer than straight. A sequence whose legs never fold below 0.92 keeps exactly [0.92, 1.08]. Where the range set a bound below 0.92 on a planted-both frame, one `STANCE bound from the leg's own range` line gives each side's lowest, as does the report's `stance.range`. A frame past its bound is listed `past the bounds` with the bound it had, held at the bound and not forced. Past the tolerance above, the foot is **lifted** and keeps the pose's height. The state is read with **hysteresis** and a **shortest run** ([#87](https://github.com/rig-c/pose/issues/87)), on the distance: a planted foot leaves the stance when its distance from the supporting one goes past the tolerance, and one past it is planted again only under **0.8** of it (both figures are on the `STANCE` line). A change of state takes effect only when its condition holds for **0.1 s** (by the frames' times). A shorter crossing with the other state on both sides of it keeps their state and is listed `absorbed` with its frames and the height that crossed (`absorbed: true` in the report's `stance.runs`, beside the run that took it in). One that runs to the end of a support span is taken, since nothing of the other state follows it. A threshold read frame by frame turned noise at the threshold into a pop: one frame at 0.0708 against a 0.07 threshold dropped the stretch for one frame and the foot jumped up and back. The stretched scales are the scale targets, so the landing check holds them. Measured on `dance-a`, frames 94–175 outside the jumps: the free ankle's height above the supporting one on the posed build went from median 18.3 and p90 25.4 units to 0.0 and 0.0. On `dance-b` (492 frames at 30 fps) the 29 frames labelled by eye as both feet flat, outside the jumps, read 0.0 and 0.0. Of the 6 labelled one foot lifted, 5 stay above the tolerance; the sixth (frame 360) was 0.042 leg lengths up in the picture, within the tolerance, and so planted; without `--stance` it stood 6.5 units up, also under the tolerance (22.6 units there). That is 83 %, under the 90 % the card asked.
|
|
242
|
+
- **`--stance <leg lengths>`** (only with `--plant-feet`, which it is refused without; off by default) plants **both** feet when the free one is near the floor ([#60](https://github.com/rig-c/pose/issues/60)). Its unit is `FEET`'s leg length, each side's **median** picture length hip → ankle — not `--leg-scale`'s straight length, a 95th percentile — and `STANCE`'s `unit` line prints it in px and as a share of the straight leg ([#237](https://github.com/rig-c/pose/issues/237)). The card's `0.07` was measured on a dance, on a span where both shoes stood flat (`dance-a`, below); **keep `--stance` below the lifted-foot threshold the material is graded at**, since a lifted foot within the tolerance is planted: a walk graded with a 0.04 threshold plants its lifted feet under `--stance 0.07`. On a frame with a supporting side (so outside every jump), the free ankle's height above the supporting one is read in the picture, in the free side's leg length — the leg length `FEET` uses. The tolerance is a **distance** ([#114](https://github.com/rig-c/pose/issues/114)): within it, above the supporting ankle or below it, the frame is **planted both**: every bone of the free leg's chain has its `scalex` key multiplied by k = (root y − floor y) / (root y − ankle y) on the posed rig, the root being the chain's first bone's origin and the floor the supporting ankle, so the foot reaches the floor. A free ankle within the tolerance below the supporting one is planted too: its k is under 1, since a foot slightly under the floor is the same fault as one floating above it. A free ankle **past the tolerance below** the supporting one is not a stance but a support error (the foot read as supporting is the one in the air), so that frame is never planted both and keeps the pose's height; it is named on a `STANCE past the tolerance below` line with the side, the frames and the largest distance, and read `"below"` in the report's `stance.runs`. Before #114 the gap was signed, so any free ankle below the supporting one counted as within, and a lifted foot read as supporting had the grounded leg shrunk up to it. On the tests' synthetic stance, a free ankle 0.5 leg lengths below is read below and not planted; one 0.03 below is planted with k under 1. k is held within **[0.92, 1.08]**, except that the lower bound comes from the leg's own range in the sequence ([#95](https://github.com/rig-c/pose/issues/95)): per side, r = the leg's picture length hip → ankle on the frame ÷ its straight length (the 95th percentile of that length, as `--leg-scale` reads a segment's), and the frame's lower bound is min(0.92, r), so the rig leg may fold as far as the picture folds it on that frame and never less far than 0.92. The upper bound stays 1.08, since a picture leg does not read longer than straight. A sequence whose legs never fold below 0.92 keeps exactly [0.92, 1.08]. Where the range set a bound below 0.92 on a planted-both frame, one `STANCE bound from the leg's own range` line gives each side's lowest, as does the report's `stance.range`. A frame past its bound is listed `past the bounds` with the bound it had, held at the bound and not forced. Past the tolerance above, the foot is **lifted** and keeps the pose's height — unless a `--leg-scale` clamp drew that leg at another length on the frame, which moves its ankle: `STANCE` then says on how many of the run's frames the height was kept, and on which frames, and by how much, the clamp moved it (`stance.runs[].moved`, [#237](https://github.com/rig-c/pose/issues/237)). The state is read with **hysteresis** and a **shortest run** ([#87](https://github.com/rig-c/pose/issues/87)), on the distance: a planted foot leaves the stance when its distance from the supporting one goes past the tolerance, and one past it is planted again only under **0.8** of it (both figures are on the `STANCE` line). A change of state takes effect only when its condition holds for **0.1 s** (by the frames' times). A shorter crossing with the other state on both sides of it keeps their state and is listed `absorbed` with its frames and the height that crossed (`absorbed: true` in the report's `stance.runs`, beside the run that took it in). One that runs to the end of a support span is taken, since nothing of the other state follows it. A threshold read frame by frame turned noise at the threshold into a pop: one frame at 0.0708 against a 0.07 threshold dropped the stretch for one frame and the foot jumped up and back. The stretched scales are the scale targets, so the landing check holds them. Measured on `dance-a`, frames 94–175 outside the jumps: the free ankle's height above the supporting one on the posed build went from median 18.3 and p90 25.4 units to 0.0 and 0.0. On `dance-b` (492 frames at 30 fps) the 29 frames labelled by eye as both feet flat, outside the jumps, read 0.0 and 0.0. Of the 6 labelled one foot lifted, 5 stay above the tolerance; the sixth (frame 360) was 0.042 leg lengths up in the picture, within the tolerance, and so planted; without `--stance` it stood 6.5 units up, also under the tolerance (22.6 units there). That is 83 %, under the 90 % the card asked.
|
|
218
243
|
- The map needs one chain ending at each ankle whose last bone is aimed `"x"` (the ankle is that bone's tip), and a `translate` the legs hang from (`LEGS_MAP`). A `range`, or a set's `turn` or `offsets`, on a bone between the hip and an ankle is refused (`LEGS_RANGE`): the plant is derived before the range clamp, and a clamp after it would move the planted foot.
|
|
219
244
|
|
|
220
245
|
Both are checked on the gated build: every scale lands, each ankle lands where the keys put it, and the hip's origin where the plant put it. The figures were fixed in #47 before `dance-a` was measured; the defaults stay off until they pass.
|
|
@@ -256,7 +281,7 @@ The retarget writes one key per pose frame on every mapped bone. That is faithfu
|
|
|
256
281
|
- **It runs last, on the final keys** (rig-c/pose#94): after `--plant-feet`'s plant, its one floor, the blend and `--stance` stretches and the in-place closure. Their keys are reduced like any other, and every landing check is run again on the reduced motion.
|
|
257
282
|
- A scaled bone's `scalex` track is thinned within 5e-5 of its setup length: half of the scale landing check's 1e-4, so a reduced curve cannot fail that check.
|
|
258
283
|
- A mapped bone under a scaled bone (keyed through its parent's posed matrix by `--leg-scale`) is solved again through its parent as reduced. Its rotate is then thinned with the error read as the world aim it lands on, and its `scalex` with the error read as the world length it lands on. Its world error is its own fit error, as down any other chain.
|
|
259
|
-
- Each ankle is held to the plant's tolerance plus the most the reduction may move it: the hip's tolerance (with `--reduce-hip`), plus the leg from the hip to the ankle (each bone's setup offset from its parent and the last bone's length) times the largest scale asked, times the angle tolerance in radians plus 5e-5. The ankle's `LAND` line prints that tolerance.
|
|
284
|
+
- Each ankle is held to the plant's tolerance plus the most the reduction may move it: the hip's tolerance (with `--reduce-hip`), plus the leg from the hip to the ankle (each bone's setup offset from its parent and the last bone's length) times the largest scale asked, times the angle tolerance in radians plus 5e-5. The ankle's `LAND` line prints that tolerance, and at the end of the run `REDUCE ankle drift allowed: bone "…:tip" X units = Y leg lengths on any one frame` prints the same figure, split `(plant … + reduce …)` (`reduce.ankleDrift` in the report, `{ bone, side, plant, reduce, units, legLengths }`, the row's `units` the landing's `tolerance` itself), with the slide between two frames it lets through (2X) — so a slide bar is read against the reduction before it is run, not after ([#240](https://github.com/rig-c/pose/issues/240)). On the wave with `--leg-scale 0.8,1.05 --plant-feet --reduce 0.5 --reduce-hip 0.5`, that is 1.97 units = 0.0132 leg lengths a frame, 0.0263 between two.
|
|
260
285
|
- When `--leg-scale` is active, each trunk bone (`chest` and `head`, not an ancestor of any scaled bone) has its kept knots and Bézier handles smoothed within `deg` of its dense world aim to minimise the mean absolute second difference of its evaluated local angle and its child's compensated local angle, holding any limb children within 0.03° of their fitted world aim (rig-c/pose#116).
|
|
261
286
|
|
|
262
287
|
Exit codes: `0` written, `1` refused (every reason printed, nothing written), `2` bad invocation. `rig-pose --help` has every flag.
|
|
@@ -281,7 +306,7 @@ Exit codes: `0` written, `1` refused (every reason printed, nothing written), `2
|
|
|
281
306
|
- Joints are body-18 (`nose neck r_shoulder r_elbow r_wrist l_shoulder l_elbow l_wrist r_hip r_knee r_ankle l_hip l_knee l_ankle r_eye l_eye r_ear l_ear`); `r`/`l` are the **subject's** sides. `state` is `observed`, `occluded` or `missing`, as in the keypoints file, or `inferred` ([#63](https://github.com/rig-c/pose/issues/63)): a position the detector supplied but did not see. An `inferred` joint carries `at` (one without it is refused, `POSE_JOINT_AT`), and `retarget` keys it as it keys `observed`. An unlisted joint is missing.
|
|
282
307
|
- Two differences from the keypoints file: the unit is `picture-px`, and `at` may lie outside the picture (a hand leaves the frame; the score says how much to believe it).
|
|
283
308
|
- More than one person: `--person <id>`.
|
|
284
|
-
- `travel` (optional, [#92](https://github.com/rig-c/pose/issues/92)) is the clip's frame of reference. Its one value is `"in-place"`: a gait filmed without the figure travelling, such as a treadmill or reel walk, or a loop. `clean --loop` writes it, and it may be written by hand on any sequence. Any other value is refused by name (`POSE_TRAVEL`). Without it the figure's travel in the picture is its travel. It
|
|
309
|
+
- `travel` (optional, [#92](https://github.com/rig-c/pose/issues/92)) is the clip's frame of reference. Its one value is `"in-place"`: a gait filmed without the figure travelling, such as a treadmill or reel walk, or a loop. `clean --loop` writes it, and it may be written by hand on any sequence. Any other value is refused by name (`POSE_TRAVEL`). Without it the figure's travel in the picture is its travel. It is a property of the sequence, not of a flag ([#242](https://github.com/rig-c/pose/issues/242)): with or without `--plant-feet`, retarget reads the supports in place, skips the camera-zoom normalisations (a loop has no zoom, and their rolling windows do not wrap) and reads the head nod without its detrend window or clamp. Only the following foot and the loop closure need `--plant-feet` ([below](#the-legs-foreshortening-and-planted-feet)).
|
|
285
310
|
- `score` is whatever the pose model gives. RTMPose's goes above 1, so `--min-score` is a cut on that model's scale, not a probability.
|
|
286
311
|
|
|
287
312
|
**From COCO-17** — the layout most pose models and datasets use — `rig-pose clean --coco in.json --out pose.json`, which converts and cleans in one step. `rig-pose convert --coco` converts only, and its output still needs `clean`. `retarget --coco` is refused (`POSE_NOT_CLEANED`), naming the `clean --coco` command. The input is `{ "format": "coco17-sequence/1", "width", "height", "source", "frames": [{ "t", "keypoints" }] }`, keypoints in COCO's order as 17 `[x, y, score]` or 51 numbers, or `null` for a frame with nobody. The mapping: COCO's `left_*`/`right_*` → body-18's `l_*`/`r_*` (both are the subject's sides), a score of 0 → `missing`, and body-18's `neck`, which COCO lacks, is the shoulders' midpoint scored the lower of the two (OpenPose's definition).
|
|
@@ -310,11 +335,12 @@ Which bones carry which joints. You write it for your rig:
|
|
|
310
335
|
"scale": { "pose": ["mid_hip", "neck"], "rig": ["hip", "neck"] } } }
|
|
311
336
|
```
|
|
312
337
|
|
|
313
|
-
- A chain's bone *k* carries the segment from joint *k* to joint *k*+1, so it lists one joint more than bones; each bone descends from the one before (a control bone between them is fine).
|
|
338
|
+
- A chain's bone *k* carries the segment from joint *k* to joint *k*+1, so it lists one joint more than bones; each bone descends from the one before (a control bone between them is fine). body-18 has no toe and no hand point, so a leg ends at the ankle and an arm at the wrist: a rig's foot (or hand) bone below them stays out of `bones`, unkeyed, turning with its parent. A chain one joint short whose last joint is an ankle or a wrist is refused (`MAP_CHAIN_JOINTS`) naming that case and the bone to leave out ([#240](https://github.com/rig-c/pose/issues/240)).
|
|
314
339
|
- `aim`, one entry per bone: `"x"` (default) — the bone's own x axis is the segment; right for a limb link @rig-c/parts turned along its limb. A point — the direction from this bone's origin to that point; right for a trunk bone, whose own axis parts leaves at 0°. `"a>b"` — the direction from point a to point b; right for a head, turned by its eye line: `{ "bones": ["head"], "joints": ["r_eye", "l_eye"], "aim": ["eye_r>eye_l"] }`, or for a pelvis, turned by its hip line: `{ "name": "pelvis", "bones": ["hip"], "joints": ["r_hip", "l_hip"], "aim": ["leg_r0>leg_l0"] }`.
|
|
315
340
|
- A **point** of the rig is a bone's origin (`name`), the end of its x axis (`name:tip`), or the midpoint of two of those (`"a+b"`: `"sleeve_r0+sleeve_l0"` is the shoulders' midpoint). An aim's points must be the bone itself or descend from it.
|
|
316
|
-
- `reach: true`, on a one-bone chain from the translated joint (a trunk from `mid_hip`): the bone is aimed from where its origin is posed — the hip moved and turned — at where the pose puts the segment's end relative to mid_hip, scaled. Without it, a tilted pelvis carries the chest's origin sideways and the trunk, parallel to the pose's, misses the shoulders. Its aim is one point, from the bone's own origin: `{ "name": "trunk", "bones": ["chest"], "joints": ["mid_hip", "mid_shoulder"], "aim": ["sleeve_r0+sleeve_l0"], "reach": true }`. The chain must descend from the translated bone.
|
|
317
|
-
- `bend`: every joint inside a chain is a hinge. `"rest"` (default) takes the side it may bend from the rig's rest pose and refuses a rest that is straight (under 0.5°); `"ccw"`/`"cw"` state it; `"free"` skips the check.
|
|
341
|
+
- `reach: true`, on a one-bone chain from the translated joint (a trunk from `mid_hip`): the bone is aimed from where its origin is posed — the hip moved and turned — at where the pose puts the segment's end relative to mid_hip, scaled. Without it, a tilted pelvis carries the chest's origin sideways and the trunk, parallel to the pose's, misses the shoulders. Its aim is one point, from the bone's own origin: `{ "name": "trunk", "bones": ["chest"], "joints": ["mid_hip", "mid_shoulder"], "aim": ["sleeve_r0+sleeve_l0"], "reach": true }`. The chain must descend from the translated bone. **It is not free** ([#236](https://github.com/rig-c/pose/issues/236)): the aim is taken from the bone's posed origin at the pair's end, so an origin partway up the pair is a lever that turns the bone by more than the pair turns — on the example wave's trunk, its origin half way up its 150-unit reach, 2.00× the pair's mean |second difference|, carried into the head and arms that ride it — and a turning hip adds its own turn on top. It pays where a pelvis chain turns the hip, which otherwise carries the chest's origin off the pose's line; on a level hip, the trunk aimed along the pair keeps the pair's jitter and misses the shoulders only by its origin's offset swung through the trunk's lean. `JITTER` and `REACH` print what it cost.
|
|
342
|
+
- `bend`: every joint inside a chain is a hinge. `"rest"` (default) takes the side it may bend from the rig's rest pose and refuses a rest that is straight (under 0.5°); `"ccw"`/`"cw"` state it; `"free"` skips the check. Both bend refusals (`MAP_BEND_SIDE`, `POSE_BEND_ONE_WAY`) name `"free"` on the chain they refuse, and when it is right — the warning below ([#238](https://github.com/rig-c/pose/issues/238)).
|
|
343
|
+
- `nod: false`, on the chain whose joints carry the eye line (`r_eye → l_eye`, [#241](https://github.com/rig-c/pose/issues/241)): the head keys no nod translate, and nothing is coupled into its rotate or the face pitch; the `NOD` line says so. Absent or `true`, the nod is read ([above](#what-it-does)). `nod` on a chain with no eye line, or anything but a boolean, is refused (`MAP_NOD`). It is a key of the chain rather than a flag because the nod is that chain's reading: the map is where a rig's author says what its head does, and a flag would have to name the chain it turns off.
|
|
318
344
|
⚠️ **Seen from the front, an elbow's side is not a joint limit.** An elbow flexes toward the camera, and its picture falls on either side of the upper arm depending on how the arm is turned. On a real 13 s front-facing dance (`dance-a`, 313 frames, RTMPose), the right elbow sat on the "wrong" side of the rig's rest on 165 frames (median 23°), and the left on 200 (median 29°, up to 179° with the hands behind the head). Every one was a real elbow bend. The knees strayed at most 8.8° and 14.7°, which is pose noise around a straight leg. So for front-facing material, arms go `"free"`. Legs on `"rest"` with `--bends clamp` hold only while the knees stay near straight. On `dance-b` (a second dance with deeper knee bends, rig-c/pose#27), that setting straightened the left knee for most of the dance: `leg_l1` was 48.3° off at p90, against 0.0–0.3° with free legs. Leave legs `"free"` unless the dance keeps its knees straight.
|
|
319
345
|
- `mid_hip` and `mid_shoulder` are the midpoints of the two hips and the two shoulders.
|
|
320
346
|
- `translate.scale` is one length measured on both: two points on the rig, two points in the pose (its median over the frames where both are good). Pick a long segment that does not foreshorten much.
|
package/cli.ts
CHANGED
|
@@ -19,7 +19,7 @@ const HELP = `rig-pose ${pkg.version} — motion from a pose sequence onto a rig
|
|
|
19
19
|
|
|
20
20
|
Usage:
|
|
21
21
|
rig-pose clean --pose <pose.json> --out <pose.clean.json> [--loop] [--legs 5] [--rest 7] [--hold off] [--coco] [--person <id>]
|
|
22
|
-
rig-pose retarget --rig <rig> --map <map.json> --
|
|
22
|
+
rig-pose retarget --rig <rig> --map <map.json> --sequence <pose.json> --out <motion.json> [--animation dance] [flags]
|
|
23
23
|
rig-pose convert --coco <coco17.json> --out <pose.json>
|
|
24
24
|
rig-pose compare --reference <pose.json> --render <pose.json> --map <map.json> [flags]
|
|
25
25
|
rig-pose --version | --help
|
|
@@ -61,14 +61,20 @@ clean (required before retarget; rig-c/pose#67)
|
|
|
61
61
|
|
|
62
62
|
retarget
|
|
63
63
|
--rig <path> the rig spec rigc builds from: a rig.json, or a directory holding one
|
|
64
|
-
(with its motion.json beside it — @rig-c/parts writes the pair to <out>/rig/)
|
|
64
|
+
(with its motion.json beside it — @rig-c/parts writes the pair to <out>/rig/); naming
|
|
65
|
+
the rig.json reads no motion spec, so a rig that needs its motion is given as the directory
|
|
65
66
|
--map <map.json> rig-pose-map/1: which bones carry which joints
|
|
66
|
-
--
|
|
67
|
+
--sequence <file> rig-pose-sequence/1 (body-18 per frame) that rig-pose clean wrote; a sequence without
|
|
67
68
|
its "cleaned" record is refused (POSE_NOT_CLEANED), naming the clean command to run
|
|
69
|
+
--pose <file> the same flag under this command's first name (rig-c/pose#243); --sequence is the name
|
|
70
|
+
rig-c/exam's grader uses. Both given, they must name the same file
|
|
68
71
|
--out <motion.json> where the motion spec goes: the rig's own animations plus the new one,
|
|
69
|
-
ready for \`rigc build --rig <rig> --motion <motion.json
|
|
70
|
-
|
|
71
|
-
--
|
|
72
|
+
ready for \`rigc build --rig <rig> --motion <motion.json>\`; a directory it names that is
|
|
73
|
+
not there yet is made once the run is green, and a "made directory" line says so
|
|
74
|
+
--animation <name> the new animation's name (default: dance); refused if the rig has one by that name
|
|
75
|
+
--name <name> the same flag under this command's first name (rig-c/pose#243); --animation is the name
|
|
76
|
+
rigc render uses. Both given, they must be the same name
|
|
77
|
+
--coco read --sequence as coco17-sequence/1: refused (POSE_NOT_CLEANED) — clean it with
|
|
72
78
|
rig-pose clean --coco, then retarget the cleaned file
|
|
73
79
|
--person <id> the person to follow when the sequence holds more than one
|
|
74
80
|
--base-motion <path> the rig's motion spec, when it is not motion.json beside rig.json
|
|
@@ -92,9 +98,14 @@ retarget
|
|
|
92
98
|
--leg-scale <min,max>|off
|
|
93
99
|
each leg segment (two of one side's hip, knee, ankle) is scaled along its own x axis
|
|
94
100
|
(a scalex key) by its picture length over its straight length — the 95th percentile of
|
|
95
|
-
its picture length — clamped to [min, max], listed as LEG-SCALE with the frames
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
its picture length, in px — clamped to [min, max], listed as LEG-SCALE with the frames
|
|
102
|
+
clamped, the limits in px, and on a free leg how far the clamps moved its ankle from the
|
|
103
|
+
picture's; min and max are shares of that segment's own straight length, not of FEET's
|
|
104
|
+
leg length (the median hip → ankle --stance is in). FEET prints every segment's picture
|
|
105
|
+
length ÷ straight (min · median · max) with or without this flag: read the minimum
|
|
106
|
+
before choosing min. A bone under a scaled one is keyed through its parent's scale
|
|
107
|
+
(default: off; 0.8,1.05 is the card's range, rig-c/pose#47, read off a dance whose leg
|
|
108
|
+
picture length ran 0.86–1.18 of its median — not a walk's)
|
|
98
109
|
--plant-feet the hip is placed so the supporting foot stays where it took support: the lower ankle
|
|
99
110
|
in the picture, changing after the other has been 0.02 leg lengths lower for 0.1 s,
|
|
100
111
|
blended over 3 frames; a jump (both ankles and mid_hip 0.05 leg lengths above their
|
|
@@ -111,12 +122,18 @@ retarget
|
|
|
111
122
|
drift from the first frame to the last is spread back over the frames, closing the loop
|
|
112
123
|
--floor <y> with --plant-feet: the floor's world y (rig units, y up) instead of the median
|
|
113
124
|
--stance <leg lengths> with --plant-feet: on a frame outside the jumps, a free ankle within this many
|
|
114
|
-
leg lengths
|
|
125
|
+
leg lengths of the supporting one in the picture, above or below, is planted. The unit
|
|
126
|
+
is FEET's leg length — each side's median picture length hip → ankle, in px on the FEET
|
|
127
|
+
line — not LEG-SCALE's straight length (a 95th percentile); STANCE prints the tolerance
|
|
128
|
+
in px and as a share of the straight leg. Keep it below the lifted-foot threshold the
|
|
129
|
+
material is graded at: a lifted foot under it is planted
|
|
115
130
|
too — its leg's scalex keys stretched by k = (root y − floor y) / (root y − ankle y) until
|
|
116
131
|
the foot reaches the floor, raised or lowered, k within [min(0.92, the leg's own fold on
|
|
117
132
|
the frame), 1.08] (#95; past it, reported, not forced); above the tolerance the foot is
|
|
118
133
|
lifted and keeps the pose's height; below it, it is reported (past the tolerance below,
|
|
119
|
-
#114) and not planted
|
|
134
|
+
#114) and not planted; "the pose's height kept" is printed only on frames no
|
|
135
|
+
--leg-scale clamp moved the free ankle (STANCE lines; default: off; 0.07 is the card's,
|
|
136
|
+
rig-c/pose#60, measured on a dance's flat-footed span — not a walk's)
|
|
120
137
|
--raised-by-head while a set whose arm is drawn raised shows (its "drawn" upper arm within 30° of straight
|
|
121
138
|
up), that arm's wrist goes where the reference's is against the head, in eye distances:
|
|
122
139
|
rig head centre + (reference wrist − reference head centre) × rig eye distance ÷ the
|
|
@@ -149,7 +166,8 @@ retarget
|
|
|
149
166
|
(REDUCE lines; default: off). It runs last, on the final keys: with --leg-scale,
|
|
150
167
|
--plant-feet and --stance, a scaled bone's scalex is thinned within 5e-5 of its setup
|
|
151
168
|
length, a bone under one is re-solved through its parent as reduced, and each ankle
|
|
152
|
-
is held to what the reduced bones above it may move it by (rig-c/pose#94)
|
|
169
|
+
is held to what the reduced bones above it may move it by (rig-c/pose#94), printed
|
|
170
|
+
at the end as "ankle drift allowed" in units and leg lengths (rig-c/pose#240)
|
|
153
171
|
--reduce-hip <units> with --reduce, thin the hip's translate keys too, its world origin within units
|
|
154
172
|
of the dense keys (default: the hip stays dense)
|
|
155
173
|
--switch-tolerance <d> a set of alternate drawings (the map's "alternates") may show on a frame where
|
|
@@ -162,9 +180,24 @@ retarget
|
|
|
162
180
|
--profile spine|spine-html rigc's gate profile (default: spine, rigc's own default)
|
|
163
181
|
--build <dir> keep the gated build (skeleton.json, .atlas, .model.json) here
|
|
164
182
|
--render <dir> keep rigc's render of the animation (frames, contact.png, folds.json) here
|
|
165
|
-
--
|
|
183
|
+
--parts <path> @rig-c/parts' parts.json, or the directory holding it (its build's --out, beside rig/):
|
|
184
|
+
the neighbour pairs the seam reading reads — each limb's consecutive segments. Each
|
|
185
|
+
pair is read by rigc render --seam over the new animation and every span of a gap or
|
|
186
|
+
a ghost thicker than one cell prints a SEAM line and a report seams row; a reading,
|
|
187
|
+
never a gate. Without it no pair is declared (a rig spec carries none): seams: []
|
|
188
|
+
--report <file> write the run's measurements (holds, short, endOn, flips, bends, landings, folds, seams,
|
|
166
189
|
switches, uncovered, turns, range, artRange, raised, byHip,
|
|
167
|
-
idleDropped, idleKeptOut, merged, reduce) as JSON
|
|
190
|
+
idleDropped, idleKeptOut, merged, reduce) as JSON (default: <out>.report.json beside
|
|
191
|
+
the motion, a trailing .json of --out replaced); its wrote line names the mtime of a
|
|
192
|
+
report it replaced
|
|
193
|
+
--verbose print every line in full (rig-c/pose#238): the CLEAN line with the stillness hold's
|
|
194
|
+
per-joint record, one BEND line per frame under --bends clamp, and before a collapsed
|
|
195
|
+
POSE_BEND_ONE_WAY refusal the per-frame refusals it stands for. Without it, the CLEAN line
|
|
196
|
+
counts the holds (the record: --report's cleaned.holds, cleaned.unheld, cleaned.seams)
|
|
197
|
+
and each hinge's wrong-way frames are one line: count, frame range, worst angle
|
|
198
|
+
--summary print only the run line, one line per measure — GATE, FEET (the posed build), LIFT
|
|
199
|
+
(the STANCE runs), JITTER (the worst ratio), LAND (each kind's worst) — and the wrote
|
|
200
|
+
lines: the lines two runs are compared on. Refusals print as without it
|
|
168
201
|
--camera <file> write the sequence's camera track (rig-pose-camera/1: continuous figure scale s,
|
|
169
202
|
raw standing height ratio, pan tx/ty, and composite videoS) as a sidecar JSON file
|
|
170
203
|
without changing motion.json (rig-c/pose#161)
|
|
@@ -211,8 +244,14 @@ Exit codes: 0 written (compare: compared), 1 refused (every reason printed), 2 b
|
|
|
211
244
|
|
|
212
245
|
class Usage extends Error {}
|
|
213
246
|
|
|
214
|
-
|
|
247
|
+
/**
|
|
248
|
+
* Reads `--flag value` pairs and `--switch`es. `aliases` maps a second name to the flag it stands for
|
|
249
|
+
* (rig-c/pose#243): the value is stored under the flag's own name. A flag given twice is refused; a flag
|
|
250
|
+
* and its alias given together are accepted when they agree and refused, naming both, when they differ.
|
|
251
|
+
*/
|
|
252
|
+
function parseFlags(argv: string[], flags: readonly string[], switches: readonly string[], aliases: Readonly<Record<string, string>> = {}): Map<string, string> {
|
|
215
253
|
const out = new Map<string, string>();
|
|
254
|
+
const spelled = new Map<string, string>();
|
|
216
255
|
for (let i = 0; i < argv.length; i++) {
|
|
217
256
|
const a = argv[i]!;
|
|
218
257
|
if (!a.startsWith('--')) throw new Usage(`unexpected argument "${a}"`);
|
|
@@ -221,18 +260,28 @@ function parseFlags(argv: string[], flags: readonly string[], switches: readonly
|
|
|
221
260
|
out.set(name, 'true');
|
|
222
261
|
continue;
|
|
223
262
|
}
|
|
224
|
-
|
|
263
|
+
const flag = aliases[name] ?? name;
|
|
264
|
+
if (!flags.includes(flag)) throw new Usage(`unknown flag ${a}; the flags are ${[...flags, ...Object.keys(aliases), ...switches].map((f) => `--${f}`).join(', ')}`);
|
|
225
265
|
const v = argv[++i];
|
|
226
266
|
if (v === undefined || v.startsWith('--')) throw new Usage(`${a} needs a value`);
|
|
227
|
-
|
|
228
|
-
|
|
267
|
+
const before = spelled.get(flag);
|
|
268
|
+
if (before === a) throw new Usage(`${a} is given twice`);
|
|
269
|
+
if (before !== undefined) {
|
|
270
|
+
const prior = out.get(flag)!;
|
|
271
|
+
if (prior === v) continue;
|
|
272
|
+
// One of the two is the flag's own name and the other its alias: the line names the flag first.
|
|
273
|
+
const [own, ownV, alias, aliasV] = before === `--${flag}` ? [before, prior, a, v] : [a, v, before, prior];
|
|
274
|
+
throw new Usage(`${own} "${ownV}" and ${alias} "${aliasV}" are both given and differ; ${alias} is another name for ${own}, so give one`);
|
|
275
|
+
}
|
|
276
|
+
spelled.set(flag, a);
|
|
277
|
+
out.set(flag, v);
|
|
229
278
|
}
|
|
230
279
|
return out;
|
|
231
280
|
}
|
|
232
281
|
|
|
233
|
-
function need(f: Map<string, string>, name: string): string {
|
|
282
|
+
function need(f: Map<string, string>, name: string, alias?: string): string {
|
|
234
283
|
const v = f.get(name);
|
|
235
|
-
if (v === undefined) throw new Usage(`--${name} is required`);
|
|
284
|
+
if (v === undefined) throw new Usage(alias === undefined ? `--${name} is required` : `--${alias} (or --${name}) is required`);
|
|
236
285
|
return v;
|
|
237
286
|
}
|
|
238
287
|
|
|
@@ -264,8 +313,15 @@ function legScaleOf(f: Map<string, string>): [number, number] | 'off' {
|
|
|
264
313
|
return [parts[0]!, parts[1]!];
|
|
265
314
|
}
|
|
266
315
|
|
|
316
|
+
/**
|
|
317
|
+
* rig-c/pose#243: the names the rest of the toolchain uses for the same inputs — `rigc render --animation`,
|
|
318
|
+
* and the exam grader's `--sequence` — accepted beside this command's own.
|
|
319
|
+
*/
|
|
320
|
+
const RETARGET_ALIASES = { sequence: 'pose', animation: 'name' } as const;
|
|
321
|
+
|
|
267
322
|
function retargetArgs(argv: string[]): RetargetArgs {
|
|
268
|
-
const f = parseFlags(argv, ['rig', 'map', 'pose', 'out', 'name', 'person', 'base-motion', 'images', 'min-score', 'smooth', 'flip', 'short', 'bends', 'bend-tolerance', 'switch-tolerance', 'switch-min', 'switch-at', 'profile', 'build', 'render', 'report', 'camera', 'leg-scale', 'idle', 'merge', 'reduce', 'reduce-hip', 'stance', 'floor'], ['coco', 'plant-feet', 'carry-idle', 'raised-by-head']);
|
|
323
|
+
const f = parseFlags(argv, ['rig', 'map', 'pose', 'out', 'name', 'person', 'base-motion', 'images', 'min-score', 'smooth', 'flip', 'short', 'bends', 'bend-tolerance', 'switch-tolerance', 'switch-min', 'switch-at', 'profile', 'build', 'render', 'parts', 'report', 'camera', 'leg-scale', 'idle', 'merge', 'reduce', 'reduce-hip', 'stance', 'floor'], ['coco', 'plant-feet', 'carry-idle', 'raised-by-head', 'verbose', 'summary'], RETARGET_ALIASES);
|
|
324
|
+
if (f.has('verbose') && f.has('summary')) throw new Usage('--verbose and --summary are both given; --verbose prints every line in full and --summary one line per measure, so give one');
|
|
269
325
|
const bends = f.get('bends') ?? 'refuse';
|
|
270
326
|
if (bends !== 'refuse' && bends !== 'clamp') throw new Usage(`--bends is "${bends}"; it must be refuse or clamp`);
|
|
271
327
|
const switchAt = f.get('switch-at') ?? 'first';
|
|
@@ -273,11 +329,11 @@ function retargetArgs(argv: string[]): RetargetArgs {
|
|
|
273
329
|
const profile = f.get('profile') ?? 'spine';
|
|
274
330
|
if (profile !== 'spine' && profile !== 'spine-html') throw new Usage(`--profile is "${profile}"; it must be spine or spine-html`);
|
|
275
331
|
const name = f.get('name') ?? 'dance';
|
|
276
|
-
if (!/^[A-Za-z0-9_.-]+$/.test(name)) throw new Usage(
|
|
332
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(name)) throw new Usage(`${argv.includes('--name') || !argv.includes('--animation') ? '--name' : '--animation'} is "${name}"; an animation name here is letters, digits, _ . -`);
|
|
277
333
|
return {
|
|
278
334
|
rig: need(f, 'rig'),
|
|
279
335
|
map: need(f, 'map'),
|
|
280
|
-
pose: need(f, 'pose'),
|
|
336
|
+
pose: need(f, 'pose', 'sequence'),
|
|
281
337
|
out: need(f, 'out'),
|
|
282
338
|
name,
|
|
283
339
|
coco: f.has('coco'),
|
|
@@ -301,12 +357,14 @@ function retargetArgs(argv: string[]): RetargetArgs {
|
|
|
301
357
|
profile,
|
|
302
358
|
build: f.get('build'),
|
|
303
359
|
render: f.get('render'),
|
|
360
|
+
parts: f.get('parts'),
|
|
304
361
|
report: f.get('report'),
|
|
305
362
|
camera: f.get('camera'),
|
|
306
363
|
idle: f.get('idle'),
|
|
307
364
|
carryIdle: f.has('carry-idle'),
|
|
308
365
|
merge: f.get('merge'),
|
|
309
366
|
...reduceOf(f),
|
|
367
|
+
output: f.has('verbose') ? 'verbose' : f.has('summary') ? 'summary' : 'default',
|
|
310
368
|
};
|
|
311
369
|
}
|
|
312
370
|
|
|
@@ -433,7 +491,7 @@ function runClean(argv: string[], say: (l: string) => void): void {
|
|
|
433
491
|
if (params.loop) {
|
|
434
492
|
if (c.floor === null) say(' FLOOR no ankle has a position on any frame; not re-pinned');
|
|
435
493
|
else say(` FLOOR the lowest ankle over the cycle: y ${fmt(c.floor.before)} before smoothing, ${fmt(c.floor.after)} after; every joint moved ${c.floor.shift >= 0 ? '+' : ''}${fmt(c.floor.shift, 3)} px in y`);
|
|
436
|
-
say(' TRAVEL "in-place" written (rig-c/pose#92): a loop is one cycle repeated, so the figure does not travel; retarget
|
|
494
|
+
say(' TRAVEL "in-place" written (rig-c/pose#92): a loop is one cycle repeated, so the figure does not travel; retarget reads its supports and head nod in place with or without --plant-feet, and --plant-feet also lets the supporting foot follow its own track and holds the hip\'s x over the loop');
|
|
437
495
|
}
|
|
438
496
|
const jit = (v: number | null) => (v === null ? 'not measured' : `${fmt(v, 3)}`);
|
|
439
497
|
say(` JITTER mean |second difference| of y over ${c.triples} joint-frame triple(s): ${jit(c.record.jitter.before)} → ${jit(c.record.jitter.after)} px/f²${c.record.jitter.before !== null && c.record.jitter.after !== null && c.record.jitter.before > 0 ? ` (${fmt(100 * (1 - c.record.jitter.after / c.record.jitter.before), 1)}% lower)` : ''}`);
|
|
@@ -465,13 +523,17 @@ export function main(argv: string[]): number {
|
|
|
465
523
|
process.stdout.write(`${pkg.version}\n`);
|
|
466
524
|
return 0;
|
|
467
525
|
}
|
|
526
|
+
/** retarget --verbose (rig-c/pose#238): a collapsed refusal prints the per-frame lines it stands for before itself. */
|
|
527
|
+
let verbose = false;
|
|
468
528
|
try {
|
|
469
529
|
if (rest.includes('--help')) {
|
|
470
530
|
process.stdout.write(HELP);
|
|
471
531
|
return 0;
|
|
472
532
|
}
|
|
473
533
|
if (cmd === 'retarget') {
|
|
474
|
-
|
|
534
|
+
const ra = retargetArgs(rest);
|
|
535
|
+
verbose = ra.output === 'verbose';
|
|
536
|
+
runRetarget(ra, (l) => process.stdout.write(`${l}\n`));
|
|
475
537
|
return 0;
|
|
476
538
|
}
|
|
477
539
|
if (cmd === 'compare') {
|
|
@@ -499,7 +561,10 @@ export function main(argv: string[]): number {
|
|
|
499
561
|
return 2;
|
|
500
562
|
}
|
|
501
563
|
if (e instanceof Refusal) {
|
|
502
|
-
for (const p of e.problems)
|
|
564
|
+
for (const p of e.problems) {
|
|
565
|
+
if (verbose) for (const d of p.detail ?? []) process.stderr.write(`FAIL ${lineOf(d)}\n`);
|
|
566
|
+
process.stderr.write(`FAIL ${lineOf(p)}\n`);
|
|
567
|
+
}
|
|
503
568
|
process.stderr.write(`rig-pose ${cmd}: ${e.problems.length} problem(s) — ${cmd === 'compare' ? 'not compared' : 'nothing written'}\n`);
|
|
504
569
|
return 1;
|
|
505
570
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/pose",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
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": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"prepublishOnly": "bun run typecheck && bun run lint && bun run test"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"rig-c": "^2.
|
|
49
|
+
"rig-c": "^2.60.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@esotericsoftware/spine-core": "4.3.13",
|