@rig-c/pose 0.2.0 → 0.2.1
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 +10 -0
- package/package.json +1 -1
- package/src/rigc.ts +15 -6
- package/src/run.ts +11 -7
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ Part of the rig-c toolchain: [core](https://github.com/rig-c/core) compiles and
|
|
|
8
8
|
|
|
9
9
|
<!-- demo -->
|
|
10
10
|
|
|
11
|
+
## Try it
|
|
12
|
+
|
|
13
|
+
[`examples/wave`](examples/wave) 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
|
+
|
|
15
|
+
```sh
|
|
16
|
+
rig-pose retarget --rig examples/wave/rig --map examples/wave/map.json --pose examples/wave/pose.json --out wave.motion.json --build wave-build
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
11
21
|
## What it does
|
|
12
22
|
|
|
13
23
|
```sh
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/pose",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Puts motion from a body-18 pose sequence onto a rig built with rig-c: bone keys in rig-c's motion format, checked through rig-c's gate before anything is written. A CLI for agents that cannot see the rig.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/rigc.ts
CHANGED
|
@@ -100,18 +100,27 @@ export function gate(rig: string, motion: Record<string, unknown>, dir: string,
|
|
|
100
100
|
mkdirSync(dir, { recursive: true });
|
|
101
101
|
const motionPath = join(dir, 'motion.json');
|
|
102
102
|
writeFileSync(motionPath, `${JSON.stringify(motion, null, 2)}\n`);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
return buildInto(rig, motionPath, join(dir, 'build'), profile, join(dir, 'build-report.json'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* `rigc build` of `rig` with the motion spec at `motionPath` into `out`, under
|
|
108
|
+
* rigc's gate (it writes nothing when red). rigc records each source image's
|
|
109
|
+
* path relative to `out` (`images[].page` in the model document), so a build is
|
|
110
|
+
* only right where it was built: the run gates in a scratch directory, and the
|
|
111
|
+
* build `--build` keeps is built again, by rigc, where it will live (#18) —
|
|
112
|
+
* never copied out, which would carry a path climbing from the scratch
|
|
113
|
+
* directory to the filesystem root. --copy-images puts the pages beside it.
|
|
114
|
+
*/
|
|
115
|
+
export function buildInto(rig: string, motionPath: string, out: string, profile: string, report?: string): Gate {
|
|
116
|
+
const run = runRigc(['build', '--rig', rig, '--motion', motionPath, '--out', out, '--profile', profile, '--copy-images', ...(report === undefined ? [] : ['--report', report])]);
|
|
108
117
|
const lines = run.out.split('\n');
|
|
109
118
|
return {
|
|
110
119
|
run,
|
|
111
120
|
green: run.status === 0,
|
|
112
121
|
summary: lines.find((l) => /assertions: \d+ measured/.test(l))?.replace(/^[\s.]+/, '') ?? null,
|
|
113
122
|
fails: lines.filter((l) => /^\s*FAIL\s/.test(l) || l.startsWith('rigc compile error')).map((l) => l.trim()),
|
|
114
|
-
build,
|
|
123
|
+
build: out,
|
|
115
124
|
};
|
|
116
125
|
}
|
|
117
126
|
|
package/src/run.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { cpSync, existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
|
-
import { dirname, join } from 'node:path';
|
|
8
|
+
import { dirname, join, resolve } from 'node:path';
|
|
9
9
|
import { readMap, resolveMap } from './map.ts';
|
|
10
10
|
import { convertCoco17, personTrack, readPoseSequence } from './pose.ts';
|
|
11
11
|
import { lineOf, Problems, Refusal, type Problem } from './problems.ts';
|
|
12
12
|
import { bendLine, flipLine, holdLine, retarget, type RetargetOptions } from './retarget.ts';
|
|
13
|
-
import { baseMotionOf, folds, gate, readBuild, resolveRig, restOf } from './rigc.ts';
|
|
13
|
+
import { baseMotionOf, buildInto, folds, gate, readBuild, resolveRig, restOf } from './rigc.ts';
|
|
14
14
|
import { landed, measureLanding, measurePosture, poseFrames, postureLines, ANGLE_TOLERANCE, ORIGIN_TOLERANCE } from './verify.ts';
|
|
15
15
|
|
|
16
16
|
export interface RetargetArgs extends RetargetOptions {
|
|
@@ -109,17 +109,21 @@ export function runRetarget(a: RetargetArgs, log: (line: string) => void): RunRe
|
|
|
109
109
|
|
|
110
110
|
const dts = r.times.slice(1).map((t, i) => t - r.times[i]!).sort((x, y) => x - y);
|
|
111
111
|
const fps = dts.length === 0 ? 12 : Math.max(1, Math.min(60, Math.round(1 / dts[dts.length >> 1]!)));
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// A rig that draws nothing (bones only) has nothing to fold, and rigc's render refuses it.
|
|
113
|
+
const f = doc.slots.length === 0 ? { run: { args: [], status: 0, out: '' }, meshes: [], frames: '' } : folds(g1.build, a.name, join(scratch, 'folds'), fps);
|
|
114
|
+
if (doc.slots.length === 0) say(' FOLD the rig has no slots, so it draws nothing to fold; not measured');
|
|
115
|
+
else if (f.meshes === null) say(` FOLD rigc render --folds did not read (exit ${f.run.status}); folds not measured`);
|
|
114
116
|
else if (f.meshes.length === 0) say(` FOLD rigc render --folds at ${fps} fps: no mesh folds`);
|
|
115
117
|
else for (const m of f.meshes) say(` FOLD mesh "${m.attachment}" (slot "${m.slot}") folds on ${m.times.length} frame(s) at ${fps} fps: t=${spans(m.times, 1 / fps)}`);
|
|
116
118
|
|
|
117
|
-
writeFileSync(a.out, `${JSON.stringify(motion, null, 2)}\n`);
|
|
118
119
|
if (a.build !== undefined) {
|
|
120
|
+
// Built again where it will live, not copied: rigc's page paths are relative to the build (#18).
|
|
119
121
|
rmSync(a.build, { recursive: true, force: true });
|
|
120
|
-
|
|
122
|
+
const kept = buildInto(input.rig, join(scratch, 'with', 'motion.json'), resolve(a.build), a.profile);
|
|
123
|
+
if (!kept.green) refuse([{ code: 'RIG_GATE', text: `rigc build into --build ${a.build} is red where the same build was green in scratch (exit ${kept.run.status}); nothing written` }, ...kept.fails.map((x) => ({ code: 'RIGC', text: x }))]);
|
|
121
124
|
}
|
|
122
|
-
|
|
125
|
+
writeFileSync(a.out, `${JSON.stringify(motion, null, 2)}\n`);
|
|
126
|
+
if (a.render !== undefined && f.meshes !== null && f.frames !== '') {
|
|
123
127
|
rmSync(a.render, { recursive: true, force: true });
|
|
124
128
|
cpSync(f.frames, a.render, { recursive: true });
|
|
125
129
|
}
|