spine-rigc 2.56.0 → 2.58.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 +3 -1
- package/docs/AUTHORING.md +709 -264
- package/package.json +1 -1
- package/skills/rigc/SKILL.md +7 -2
- package/skills/rigc-face/SKILL.md +3 -1
- package/skills/rigc-ingest/SKILL.md +3 -1
- package/skills/rigc-motion/SKILL.md +3 -1
- package/skills/rigc-rigging/SKILL.md +3 -1
- package/src/atlas.ts +10 -10
- package/src/cli/core_commands.ts +52 -0
- package/src/cli/shared.ts +119 -3
- package/src/compile.ts +356 -334
- package/src/deformgen.ts +28 -28
- package/src/emit_spine.ts +5 -5
- package/src/errors.ts +8 -0
- package/src/guide.ts +4 -9
- package/src/keys.ts +4 -4
- package/src/meshsplit.ts +683 -0
- package/src/model.ts +10 -10
- package/src/motion.ts +6 -6
- package/src/rig.ts +76 -76
- package/src/stop.ts +30 -30
- package/src/trackgen.ts +11 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.58.0",
|
|
4
4
|
"description": "AI-authored 2D rigging and animation, verified before it is written. rigc compiles a rig spec into skeleton data in the Spine 4.3 format, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/skills/rigc/SKILL.md
CHANGED
|
@@ -18,7 +18,9 @@ whole interface, and this skill only says which of them to open.
|
|
|
18
18
|
` (see rigc guide <RULE>)`. Run that command first, before reading anything else:
|
|
19
19
|
it prints that rule's row of the shipped guide and nothing else, and `rigc guide §<n>`
|
|
20
20
|
(and `§<n>.<m>`, `§<n>.<m>.<k>`)
|
|
21
|
-
prints one section of `docs/AUTHORING.md` — a compile error points at
|
|
21
|
+
prints one section of `docs/AUTHORING.md` — a compile error points at its stable `CE_` code row. `rigc guide §5.1.<k>`
|
|
22
|
+
prints a numbered compile-error group of at most 30,000 UTF-8 bytes; only legacy
|
|
23
|
+
uncoded library errors point at `§5.1`.
|
|
22
24
|
|
|
23
25
|
## Non-negotiables
|
|
24
26
|
|
|
@@ -100,7 +102,10 @@ row for each.
|
|
|
100
102
|
parts were not kept, and writes only after every region and the skeleton are
|
|
101
103
|
shown unchanged (`--accept-skeleton-differences` writes a rebuild whose skeleton
|
|
102
104
|
differs, every difference printed; `--stage-box <slot>` reads a build's stage box)
|
|
103
|
-
— AUTHORING §0.4.
|
|
105
|
+
— AUTHORING §0.4. A mesh refused because its triangles are several pieces that
|
|
106
|
+
share no vertex: `rigc split --rig … --motion … --slot <slot> --out <dir>` writes
|
|
107
|
+
both specs back with one mesh per piece and every deform key renumbered — build
|
|
108
|
+
that — AUTHORING §3.4.11.
|
|
104
109
|
7. Every finished unit ends with `rigc preview --candidate <out>`, and the report
|
|
105
110
|
names the `.html` it wrote. The hand-off to a person is part of the work.
|
|
106
111
|
|
|
@@ -16,7 +16,9 @@ it will not do for you.
|
|
|
16
16
|
` (see rigc guide <RULE>)`. Run that command first, before reading anything else:
|
|
17
17
|
it prints that rule's row of the shipped guide and nothing else, and `rigc guide §<n>`
|
|
18
18
|
(and `§<n>.<m>`, `§<n>.<m>.<k>`)
|
|
19
|
-
prints one section of `docs/AUTHORING.md` — a compile error points at
|
|
19
|
+
prints one section of `docs/AUTHORING.md` — a compile error points at its stable `CE_` code row. `rigc guide §5.1.<k>`
|
|
20
|
+
prints a numbered compile-error group of at most 30,000 UTF-8 bytes; only legacy
|
|
21
|
+
uncoded library errors point at `§5.1`.
|
|
20
22
|
|
|
21
23
|
## Non-negotiables
|
|
22
24
|
|
|
@@ -17,7 +17,9 @@ not do for you.
|
|
|
17
17
|
` (see rigc guide <RULE>)`. Run that command first, before reading anything else:
|
|
18
18
|
it prints that rule's row of the shipped guide and nothing else, and `rigc guide §<n>`
|
|
19
19
|
(and `§<n>.<m>`, `§<n>.<m>.<k>`)
|
|
20
|
-
prints one section of `docs/AUTHORING.md` — a compile error points at
|
|
20
|
+
prints one section of `docs/AUTHORING.md` — a compile error points at its stable `CE_` code row. `rigc guide §5.1.<k>`
|
|
21
|
+
prints a numbered compile-error group of at most 30,000 UTF-8 bytes; only legacy
|
|
22
|
+
uncoded library errors point at `§5.1`.
|
|
21
23
|
|
|
22
24
|
## Non-negotiables
|
|
23
25
|
|
|
@@ -17,7 +17,9 @@ not do for you.
|
|
|
17
17
|
` (see rigc guide <RULE>)`. Run that command first, before reading anything else:
|
|
18
18
|
it prints that rule's row of the shipped guide and nothing else, and `rigc guide §<n>`
|
|
19
19
|
(and `§<n>.<m>`, `§<n>.<m>.<k>`)
|
|
20
|
-
prints one section of `docs/AUTHORING.md` — a compile error points at
|
|
20
|
+
prints one section of `docs/AUTHORING.md` — a compile error points at its stable `CE_` code row. `rigc guide §5.1.<k>`
|
|
21
|
+
prints a numbered compile-error group of at most 30,000 UTF-8 bytes; only legacy
|
|
22
|
+
uncoded library errors point at `§5.1`.
|
|
21
23
|
|
|
22
24
|
## Non-negotiables
|
|
23
25
|
|
|
@@ -16,7 +16,9 @@ will not do for you.
|
|
|
16
16
|
` (see rigc guide <RULE>)`. Run that command first, before reading anything else:
|
|
17
17
|
it prints that rule's row of the shipped guide and nothing else, and `rigc guide §<n>`
|
|
18
18
|
(and `§<n>.<m>`, `§<n>.<m>.<k>`)
|
|
19
|
-
prints one section of `docs/AUTHORING.md` — a compile error points at
|
|
19
|
+
prints one section of `docs/AUTHORING.md` — a compile error points at its stable `CE_` code row. `rigc guide §5.1.<k>`
|
|
20
|
+
prints a numbered compile-error group of at most 30,000 UTF-8 bytes; only legacy
|
|
21
|
+
uncoded library errors point at `§5.1`.
|
|
20
22
|
|
|
21
23
|
## Non-negotiables
|
|
22
24
|
|
package/src/atlas.ts
CHANGED
|
@@ -1998,7 +1998,7 @@ function packOnePageByFootprint(
|
|
|
1998
1998
|
if (shapesMeet(shape, put.x, put.y, other.shape, other.x, other.y)) {
|
|
1999
1999
|
throw new CompileError(
|
|
2000
2000
|
`internal: the footprint pass placed a ${shape.width}x${shape.height} cell at ${put.x},${put.y} over ` +
|
|
2001
|
-
`the footprint of the ${other.shape.width}x${other.shape.height} cell at ${other.x},${other.y}`,
|
|
2001
|
+
`the footprint of the ${other.shape.width}x${other.shape.height} cell at ${other.x},${other.y}`, 'CE_ATLAS_INTERNAL_FOOTPRINT_OVERLAP',
|
|
2002
2002
|
);
|
|
2003
2003
|
}
|
|
2004
2004
|
}
|
|
@@ -2313,7 +2313,7 @@ function layoutPack(
|
|
|
2313
2313
|
// Unreachable: every cell was proven to fit an empty page above. Kept as
|
|
2314
2314
|
// a named stop rather than an infinite loop if that ever stops holding.
|
|
2315
2315
|
throw new CompileError(
|
|
2316
|
-
`packing stalled with ${remaining.length} region(s) left and an empty ${maxEdge}x${maxEdge} page`,
|
|
2316
|
+
`packing stalled with ${remaining.length} region(s) left and an empty ${maxEdge}x${maxEdge} page`, 'CE_ATLAS_PACKING_STALLED',
|
|
2317
2317
|
);
|
|
2318
2318
|
}
|
|
2319
2319
|
// ⭐ Then the page is written at the smallest power-of-two pair that holds
|
|
@@ -2337,7 +2337,7 @@ function layoutPack(
|
|
|
2337
2337
|
if (shrunk === null) {
|
|
2338
2338
|
// Unreachable for the same reason as the stall above: these cells were
|
|
2339
2339
|
// just placed on a maxEdge page.
|
|
2340
|
-
throw new CompileError(`page ${pageIndex + 1} of the spill holds ${onPage.length} region(s) that no page fits
|
|
2340
|
+
throw new CompileError(`page ${pageIndex + 1} of the spill holds ${onPage.length} region(s) that no page fits`, 'CE_ATLAS_SPILL_UNPACKABLE');
|
|
2341
2341
|
}
|
|
2342
2342
|
const onThisPage: Placement[] = [];
|
|
2343
2343
|
shrunk.rects.forEach((rect, i) => {
|
|
@@ -2460,19 +2460,19 @@ export function packAtlas(inputs: PackInput[], opts: PackOptions = {}): PackResu
|
|
|
2460
2460
|
const stem = opts.pageStem ?? 'skeleton';
|
|
2461
2461
|
const pageEdges = opts.pageEdges ?? DEFAULT_PAGE_EDGES;
|
|
2462
2462
|
if (!PAGE_EDGES.includes(pageEdges)) {
|
|
2463
|
-
throw new CompileError(`--page-edges ${JSON.stringify(String(opts.pageEdges))}; known values: ${PAGE_EDGES.join(', ')}
|
|
2463
|
+
throw new CompileError(`--page-edges ${JSON.stringify(String(opts.pageEdges))}; known values: ${PAGE_EDGES.join(', ')}`, 'CE_ATLAS_PAGE_EDGES_VALUE');
|
|
2464
2464
|
}
|
|
2465
2465
|
const shape = opts.shape ?? DEFAULT_PACK_SHAPE;
|
|
2466
2466
|
if (!PACK_SHAPES.includes(shape)) {
|
|
2467
|
-
throw new CompileError(`--pack-shape ${JSON.stringify(String(opts.shape))}; known values: ${PACK_SHAPES.join(', ')}
|
|
2467
|
+
throw new CompileError(`--pack-shape ${JSON.stringify(String(opts.shape))}; known values: ${PACK_SHAPES.join(', ')}`, 'CE_ATLAS_PACK_SHAPE_VALUE');
|
|
2468
2468
|
}
|
|
2469
2469
|
if (!Number.isInteger(pageSize) || pageSize < 1) {
|
|
2470
|
-
throw new CompileError(`--page-size must be a positive integer, got ${String(opts.pageSize)}
|
|
2470
|
+
throw new CompileError(`--page-size must be a positive integer, got ${String(opts.pageSize)}`, 'CE_ATLAS_PAGE_SIZE_POSITIVE_INTEGER');
|
|
2471
2471
|
}
|
|
2472
2472
|
if (!Number.isInteger(padding) || padding < 0) {
|
|
2473
|
-
throw new CompileError(`--padding must be a non-negative integer, got ${String(opts.padding)}
|
|
2473
|
+
throw new CompileError(`--padding must be a non-negative integer, got ${String(opts.padding)}`, 'CE_ATLAS_PADDING_NONNEGATIVE_INTEGER');
|
|
2474
2474
|
}
|
|
2475
|
-
if (inputs.length === 0) throw new CompileError('nothing to pack: this compile emitted no images');
|
|
2475
|
+
if (inputs.length === 0) throw new CompileError('nothing to pack: this compile emitted no images', 'CE_ATLAS_IMAGES_REQUIRED');
|
|
2476
2476
|
|
|
2477
2477
|
const maxEdge = floorPowerOfTwo(pageSize);
|
|
2478
2478
|
|
|
@@ -2485,7 +2485,7 @@ export function packAtlas(inputs: PackInput[], opts: PackOptions = {}): PackResu
|
|
|
2485
2485
|
`"${sorted[i].region}" is ${sorted[i].width}x${sorted[i].height} and with --padding ${padding} needs a ` +
|
|
2486
2486
|
`${cells[i].w}x${cells[i].h} cell, which does not fit a ${maxEdge}x${maxEdge} page ` +
|
|
2487
2487
|
`(${sorted[i].absPath}). Raise --page-size, lower --padding, or leave this build unpacked — a packer ` +
|
|
2488
|
-
'cannot split one drawing across two pages.',
|
|
2488
|
+
'cannot split one drawing across two pages.', 'CE_ATLAS_REGION_EXCEEDS_PAGE',
|
|
2489
2489
|
);
|
|
2490
2490
|
}
|
|
2491
2491
|
|
|
@@ -2542,7 +2542,7 @@ export function packAtlas(inputs: PackInput[], opts: PackOptions = {}): PackResu
|
|
|
2542
2542
|
// two reads, or one of the two readers is wrong about it.
|
|
2543
2543
|
throw new CompileError(
|
|
2544
2544
|
`"${place.region}" measured ${input.width}x${input.height} from its PNG header and decodes to ` +
|
|
2545
|
-
`${source.width}x${source.height} (${input.absPath})`,
|
|
2545
|
+
`${source.width}x${source.height} (${input.absPath})`, 'CE_ATLAS_IMAGE_DIMENSIONS_CHANGED',
|
|
2546
2546
|
);
|
|
2547
2547
|
}
|
|
2548
2548
|
extrudeCell(plate, source, place.x - padding, place.y - padding, padding);
|
package/src/cli/core_commands.ts
CHANGED
|
@@ -31,6 +31,8 @@ import { findingLine, ingest, type IngestFinding, IngestSpecRefused, type Ingest
|
|
|
31
31
|
import { PARSER_DEFAULTS, parserReading } from '../keyorder.ts';
|
|
32
32
|
import { MODEL_DOCUMENT_FILE, modelDocument } from '../model.ts';
|
|
33
33
|
import { parseMotionSpec } from '../motion.ts';
|
|
34
|
+
import { splitMesh, SplitError } from '../meshsplit.ts';
|
|
35
|
+
import { parseRigSpec } from '../rig.ts';
|
|
34
36
|
import { type InspectCompare, type InspectFrame, InspectError, inspectCandidate, INSPECT_DEFAULT_FPS, legendText, readBonePatterns, readShow, readTimes, readZoom } from '../inspect.ts';
|
|
35
37
|
import { FOLDS_FILE, foldLine, foldsFileOf, foldsOf, foldsText, setupFaultLine } from '../folds.ts';
|
|
36
38
|
import { AngleRangeError, angleRangeLine, type AngleRangeReading, angleRangesOf, type BonePair, readBonePairs } from '../anglerange.ts';
|
|
@@ -2100,6 +2102,55 @@ export const MODEL_AND_TEXT_GATE: BuildGate = {
|
|
|
2100
2102
|
look: (outDir) => `rigc: look at it: rigc render --candidate ${outDir}`,
|
|
2101
2103
|
};
|
|
2102
2104
|
|
|
2105
|
+
/**
|
|
2106
|
+
* split — one authored mesh's pieces, proposed as one mesh each (issue #1414).
|
|
2107
|
+
*
|
|
2108
|
+
* Reads the two spec files and nothing else: a mesh of several pieces does not
|
|
2109
|
+
* compile, so there is no build to read. Writes the proposal — both specs and
|
|
2110
|
+
* `split.json` — into an `--out` that is absent or empty, which is the whole of
|
|
2111
|
+
* how it never overwrites anything: there is no in-place flag, because the
|
|
2112
|
+
* specs it read are the only statement of what the proposal must reproduce,
|
|
2113
|
+
* and a proposal nobody has built yet is not something to put over them.
|
|
2114
|
+
*/
|
|
2115
|
+
function cmdSplit(flags: Record<string, string>): void {
|
|
2116
|
+
for (const flag of ['rig', 'motion', 'slot', 'out'] as const) {
|
|
2117
|
+
if (flags[flag] === undefined) throw new UsageError(`split needs --${flag}`);
|
|
2118
|
+
}
|
|
2119
|
+
const rigPath = resolve(flags.rig);
|
|
2120
|
+
const motionPath = resolve(flags.motion);
|
|
2121
|
+
const outDir = resolve(flags.out);
|
|
2122
|
+
for (const [flag, path] of [['rig', rigPath], ['motion', motionPath]] as const) {
|
|
2123
|
+
if (!existsSync(path) || !statSync(path).isFile()) throw new SplitError(`--${flag} ${path}: no file there`, 2);
|
|
2124
|
+
}
|
|
2125
|
+
if (existsSync(outDir) && (!statSync(outDir).isDirectory() || readdirSync(outDir).length > 0)) {
|
|
2126
|
+
throw new SplitError(
|
|
2127
|
+
`--out ${outDir} ${statSync(outDir).isDirectory() ? `holds ${readdirSync(outDir).length} entr${readdirSync(outDir).length === 1 ? 'y' : 'ies'}` : 'is a file'}; ` +
|
|
2128
|
+
'the proposal is written into an absent or empty directory only, so it never lands on top of a file — the specs it read included',
|
|
2129
|
+
2,
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
const rig = parseRigSpec(readJsonFile(rigPath), rigPath);
|
|
2133
|
+
const motion = parseMotionSpec(readJsonFile(motionPath), motionPath);
|
|
2134
|
+
// The rig spec's `images` is relative to the rig file, and the proposal is written somewhere else: restate it from
|
|
2135
|
+
// there, so every `image` resolves to the file it resolved to before.
|
|
2136
|
+
const imagesDir = resolve(dirname(rigPath), rig.images ?? '.');
|
|
2137
|
+
const images = relativeImagesPath(outDir, imagesDir);
|
|
2138
|
+
console.log(`rigc split ${rigPath}`);
|
|
2139
|
+
console.log(` .. motion ${motionPath}`);
|
|
2140
|
+
const result = splitMesh({ rig, motion, slot: flags.slot, ...(flags.attachment === undefined ? {} : { attachment: flags.attachment }), ...(flags.skin === undefined ? {} : { skin: flags.skin }), images });
|
|
2141
|
+
if (result.kind === 'whole') {
|
|
2142
|
+
console.log(`rigc split: ${result.line}`);
|
|
2143
|
+
return;
|
|
2144
|
+
}
|
|
2145
|
+
for (const line of result.lines) console.log(` .. ${line}`);
|
|
2146
|
+
console.log(` .. images ${images} (the rig spec's own, restated from ${outDir} — it resolves to ${imagesDir})`);
|
|
2147
|
+
mkdirSync(outDir, { recursive: true });
|
|
2148
|
+
writeJson(join(outDir, 'rig.json'), result.rig);
|
|
2149
|
+
writeJson(join(outDir, 'motion.json'), result.motion);
|
|
2150
|
+
writeJson(join(outDir, 'split.json'), result.record);
|
|
2151
|
+
console.log(`rigc: build it with rigc build --rig ${join(outDir, 'rig.json')} --motion ${join(outDir, 'motion.json')} --out <dir>`);
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2103
2154
|
/**
|
|
2104
2155
|
* The bodies the second entry runs under a name whose full-entry body is the
|
|
2105
2156
|
* runtime's (`runtime.core` in `COMMANDS`, issue #1060) — registered by
|
|
@@ -2121,6 +2172,7 @@ export const CORE_ENTRY_RUNS: Readonly<Record<string, CommandRun>> = {
|
|
|
2121
2172
|
export const CORE_COMMAND_RUNS: Readonly<Record<string, CommandRun>> = {
|
|
2122
2173
|
explain: ({ flags }) => cmdExplain(flags),
|
|
2123
2174
|
ingest: ({ flags, positional }) => cmdIngest(flags, positional),
|
|
2175
|
+
split: ({ flags }) => cmdSplit(flags),
|
|
2124
2176
|
diff: ({ flags, lists, positional }) => cmdDiff(flags, lists, positional),
|
|
2125
2177
|
check: ({ flags }) => cmdCheck(flags),
|
|
2126
2178
|
render: ({ flags }) => cmdRender(flags),
|
package/src/cli/shared.ts
CHANGED
|
@@ -43,7 +43,7 @@ import { readViewportBox, ViewportError } from '../viewportpin.ts';
|
|
|
43
43
|
import { parseJsonWithPosition } from '../json-position.ts';
|
|
44
44
|
import { RUNG_IDS } from '../ladder.ts';
|
|
45
45
|
import { MODEL_DOCUMENT_FILE, MODEL_DOCUMENT_SPEC, modelDocument, spineFileSha256 } from '../model.ts';
|
|
46
|
-
import { compileErrorLine } from '../guide.ts';
|
|
46
|
+
import { compileErrorLine, nearestNames } from '../guide.ts';
|
|
47
47
|
import { PACKAGE_ROOT, readPackageMeta, readVersion } from '../package_meta.ts';
|
|
48
48
|
import { DEFAULT_MAX_RESIDUAL, DEFAULT_SCALE_MAX, DEFAULT_SCALE_MIN } from '../pose.ts';
|
|
49
49
|
import {
|
|
@@ -65,6 +65,7 @@ import { InspectError, DEFAULT_SHOW, DIM_KEEP, INSPECT_DEFAULT_FPS, ON_GRID_FRAM
|
|
|
65
65
|
import { NotAPngError } from '../png.ts';
|
|
66
66
|
import { PoseError } from '../pose.ts';
|
|
67
67
|
import { RepackError } from '../repack.ts';
|
|
68
|
+
import { SplitError } from '../meshsplit.ts';
|
|
68
69
|
import { SpineRuntimeError, SPINE_SIDE_ABSENT } from '../spine_side.ts';
|
|
69
70
|
import type { CompileResult, DroppedState } from '../types.ts';
|
|
70
71
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
@@ -1098,6 +1099,9 @@ const FLAG_MEANINGS: Record<string, string> = {
|
|
|
1098
1099
|
`printed (default ${DEFAULT_MIN_LEVER_PX}); below it the bone is refused \`no-bracket\` naming the measured ` +
|
|
1099
1100
|
'lever, because an angle read across a short lever turns a half-pixel anchor error into several degrees',
|
|
1100
1101
|
animation: 'which animation to show; the default is every one for `render` and the first for `preview`',
|
|
1102
|
+
attachment:
|
|
1103
|
+
"the placeholder the mesh is filed under in its slot, as the rig spec's skin names it. Absent, the slot's one " +
|
|
1104
|
+
'authored mesh in that skin; a slot holding none or several is refused naming them',
|
|
1101
1105
|
skin:
|
|
1102
1106
|
'pose under this skin, by the name the skeleton declares. Without it NO skin is set — every slot resolves ' +
|
|
1103
1107
|
'through the default skin alone, so a slot whose art lives only in a named skin draws nothing. A name the ' +
|
|
@@ -1254,6 +1258,7 @@ const FLAG_VALUES: Record<string, string> = {
|
|
|
1254
1258
|
'anchor-residual': '<0..1>',
|
|
1255
1259
|
'inward-lever': '<px>',
|
|
1256
1260
|
animation: '<name>',
|
|
1261
|
+
attachment: '<name>',
|
|
1257
1262
|
skin: '<name>',
|
|
1258
1263
|
slot: '<name[,name…]>',
|
|
1259
1264
|
hide: '<name[,name…]>',
|
|
@@ -1540,6 +1545,46 @@ export const COMMANDS: CommandDoc[] = [
|
|
|
1540
1545
|
},
|
|
1541
1546
|
},
|
|
1542
1547
|
},
|
|
1548
|
+
{
|
|
1549
|
+
name: 'split',
|
|
1550
|
+
runtime: false,
|
|
1551
|
+
spineFormat: false,
|
|
1552
|
+
usage: ['rigc split --rig <path> --motion <path> --slot <name> [--attachment <name>] [--skin <name>] --out <dir>'],
|
|
1553
|
+
flags: ['rig', 'motion', 'slot', 'attachment', 'skin', 'out'],
|
|
1554
|
+
overrides: {
|
|
1555
|
+
out: {
|
|
1556
|
+
value: '<dir>',
|
|
1557
|
+
meaning:
|
|
1558
|
+
'directory to write the proposal into — rig.json and motion.json, the two specs with the mesh split, and ' +
|
|
1559
|
+
'split.json, where every original vertex and triangle went. Absent or empty, and refused otherwise: the ' +
|
|
1560
|
+
"proposal never lands on top of a file, the author's own specs included",
|
|
1561
|
+
},
|
|
1562
|
+
slot: {
|
|
1563
|
+
value: '<name>',
|
|
1564
|
+
meaning: 'the slot whose mesh to split, as the rig spec declares it. A name it does not declare is refused with every one it does',
|
|
1565
|
+
},
|
|
1566
|
+
skin: {
|
|
1567
|
+
meaning:
|
|
1568
|
+
'the skin the mesh is filed under in the rig spec (default: default). A name the rig spec does not declare ' +
|
|
1569
|
+
'is refused with the ones it does',
|
|
1570
|
+
},
|
|
1571
|
+
},
|
|
1572
|
+
notes: [
|
|
1573
|
+
'split reads the two specs — not a build: a mesh of several pieces does not build —',
|
|
1574
|
+
'and finds the pieces of one authored mesh: triangles that share a vertex are one piece.',
|
|
1575
|
+
'A mesh of one piece is said so on one line, exit 0, and nothing is written. Otherwise it',
|
|
1576
|
+
'writes one mesh per piece: piece 1 (the piece whose first triangle is listed first) stays',
|
|
1577
|
+
'in the slot, piece k gets a new slot <slot>_<k> on the same bone, after it in the draw',
|
|
1578
|
+
"order, under the same placeholder. Each piece carries the original's uvs and weights (or",
|
|
1579
|
+
'vertices) unchanged and its triangles renumbered — outline first where the hull rule',
|
|
1580
|
+
'needs it — and every deform key is re-sliced to the new indices; colour and attachment',
|
|
1581
|
+
'tracks on the slot are copied to the new slots. Nothing is invented: a deform index the',
|
|
1582
|
+
'original run did not cover is written 0, which is what the parser held there. What the',
|
|
1583
|
+
'split cannot carry is refused by name, exit 1 (docs/AUTHORING.md §3.4.11 lists it); a',
|
|
1584
|
+
'name the specs do not have is refused with the names they do, exit 2. Nothing is ever',
|
|
1585
|
+
'written over the specs it read: build the proposal, look at it, then adopt it.',
|
|
1586
|
+
],
|
|
1587
|
+
},
|
|
1543
1588
|
{
|
|
1544
1589
|
name: 'diff',
|
|
1545
1590
|
runtime: false,
|
|
@@ -1945,6 +1990,16 @@ const USAGE_PARAGRAPHS: ReadonlyArray<{ about: readonly string[] | { flag: strin
|
|
|
1945
1990
|
'is missing from it beats no spec at all.',
|
|
1946
1991
|
],
|
|
1947
1992
|
},
|
|
1993
|
+
{
|
|
1994
|
+
about: ['split'],
|
|
1995
|
+
lines: () => [
|
|
1996
|
+
"split answers the refusal a mesh of several pieces meets at compile — the triangles'",
|
|
1997
|
+
'outline is not one closed loop — with one mesh per piece, its uvs, weights, triangles',
|
|
1998
|
+
'and deform keys renumbered, written beside the specs and never over them:',
|
|
1999
|
+
' rigc split --rig rig.json --motion motion.json --slot legs --out split/',
|
|
2000
|
+
'Build what it wrote. See `rigc split --help`.',
|
|
2001
|
+
],
|
|
2002
|
+
},
|
|
1948
2003
|
{
|
|
1949
2004
|
about: ['pose'],
|
|
1950
2005
|
lines: () => [
|
|
@@ -2030,6 +2085,7 @@ export function usageText(docs: readonly CommandDoc[], checkout: string, rules:
|
|
|
2030
2085
|
...docs.flatMap((c) => c.usage.map((u) => ` ${u}`)),
|
|
2031
2086
|
'',
|
|
2032
2087
|
' rigc <command> --help that command\'s own flag table',
|
|
2088
|
+
...HELP_DOC.usage.map((u) => ` ${u}`),
|
|
2033
2089
|
' rigc --version print the installed version (-v works too)',
|
|
2034
2090
|
];
|
|
2035
2091
|
// Read only by the --profile paragraph, which only an entry running a command that takes --profile prints.
|
|
@@ -2783,6 +2839,46 @@ function commandNeedsRuntime(doc: CommandDoc, runs: readonly string[], args: rea
|
|
|
2783
2839
|
);
|
|
2784
2840
|
}
|
|
2785
2841
|
|
|
2842
|
+
/**
|
|
2843
|
+
* `rigc help` documented as a command is (issue #1440): on the usage of both
|
|
2844
|
+
* entries, and its own page for `rigc help --help` and `rigc help help`, as
|
|
2845
|
+
* every listed command answers `--help`. Not a row of `COMMANDS`, because it
|
|
2846
|
+
* has no body of its own: it is the dispatch reading another spelling of
|
|
2847
|
+
* `--help` (`helpArgv`), on every entry alike.
|
|
2848
|
+
*/
|
|
2849
|
+
const HELP_DOC: CommandDoc = {
|
|
2850
|
+
name: 'help',
|
|
2851
|
+
usage: ['rigc help [<command>] the same page as rigc [<command>] --help, and the same exit'],
|
|
2852
|
+
flags: [],
|
|
2853
|
+
runtime: false,
|
|
2854
|
+
spineFormat: false,
|
|
2855
|
+
};
|
|
2856
|
+
|
|
2857
|
+
/**
|
|
2858
|
+
* `rigc help [<command>]` (issue #1440): the command line it stands for, which
|
|
2859
|
+
* the dispatch then runs in its place — `rigc --help` for `help` alone,
|
|
2860
|
+
* `rigc <command> --help` for a command the table documents — or `null` for
|
|
2861
|
+
* `help`'s own page (`help --help`, `help help`). Rewritten rather than
|
|
2862
|
+
* printed here, so the page and the exit code are the ones that spelling gets
|
|
2863
|
+
* on this entry and cannot come apart from them: a command this entry does not
|
|
2864
|
+
* run is refused as `<command> --help` is.
|
|
2865
|
+
*
|
|
2866
|
+
* ⚠️ A name the table does not document is refused by name with the nearest
|
|
2867
|
+
* one, as a `UsageError` — printed and exited as an unknown command is, exit 2
|
|
2868
|
+
* — and so is more than one name, rather than a guess at which was meant.
|
|
2869
|
+
*/
|
|
2870
|
+
export function helpArgv(rest: readonly string[]): string[] | null {
|
|
2871
|
+
if (rest.length === 0) return ['--help'];
|
|
2872
|
+
if (rest.length > 1) {
|
|
2873
|
+
throw new UsageError(`help takes one command name and was given ${rest.length}: ${rest.join(' ')} — \`rigc help <command>\` is \`rigc <command> --help\``);
|
|
2874
|
+
}
|
|
2875
|
+
const [name] = rest;
|
|
2876
|
+
if (name === HELP_DOC.name || name === '--help' || name === '-h') return null;
|
|
2877
|
+
if (KNOWN_COMMANDS.includes(name)) return [name, '--help'];
|
|
2878
|
+
const [nearest] = nearestNames(name, [...KNOWN_COMMANDS, HELP_DOC.name], 1);
|
|
2879
|
+
throw new UsageError(`unknown command: ${name} — the nearest command is ${nearest} (\`rigc help ${nearest}\`)`);
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2786
2882
|
/**
|
|
2787
2883
|
* Run `argv` (`process.argv` without the runtime and the script) through
|
|
2788
2884
|
* `entry`: the usage, `--version`, a command's `--help`, the command, and
|
|
@@ -2797,6 +2893,20 @@ export function runCli(entry: CliEntry, argv: readonly string[]): void {
|
|
|
2797
2893
|
throw new Error(`internal: this entry runs [${known.join(', ')}] by the command table and registers [${registered.join(', ')}]`);
|
|
2798
2894
|
}
|
|
2799
2895
|
const USAGE = usageText(docs, entry.checkout, entry.profileRules);
|
|
2896
|
+
if (argv[0] === 'help') {
|
|
2897
|
+
let target: string[] | null;
|
|
2898
|
+
try {
|
|
2899
|
+
target = helpArgv(argv.slice(1));
|
|
2900
|
+
} catch (err) {
|
|
2901
|
+
refuse(err, 'help', USAGE, entry);
|
|
2902
|
+
}
|
|
2903
|
+
if (target === null) {
|
|
2904
|
+
console.log(commandHelp(HELP_DOC.name, [HELP_DOC]));
|
|
2905
|
+
process.exit(0);
|
|
2906
|
+
}
|
|
2907
|
+
runCli(entry, target);
|
|
2908
|
+
return;
|
|
2909
|
+
}
|
|
2800
2910
|
const [command, ...rest] = argv;
|
|
2801
2911
|
try {
|
|
2802
2912
|
if (command === undefined) {
|
|
@@ -2846,8 +2956,8 @@ function refuse(err: unknown, command: string | undefined, USAGE: string, entry:
|
|
|
2846
2956
|
process.exit(2);
|
|
2847
2957
|
}
|
|
2848
2958
|
if (err instanceof CompileError) {
|
|
2849
|
-
//
|
|
2850
|
-
console.error(compileErrorLine(err.message));
|
|
2959
|
+
// The site's code points at its one AUTHORING row; uncoded library errors keep the legacy section.
|
|
2960
|
+
console.error(compileErrorLine(err.message, err.code));
|
|
2851
2961
|
// The drops the compile recorded before it stopped, in the same line the
|
|
2852
2962
|
// green build prints (issue #671). They were reported from the compile
|
|
2853
2963
|
// RESULT alone, so the run that failed BECAUSE a file was missing was the
|
|
@@ -2913,6 +3023,12 @@ function refuse(err: unknown, command: string | undefined, USAGE: string, entry:
|
|
|
2913
3023
|
console.error(`rigc repack: ${err.message}`);
|
|
2914
3024
|
process.exit(err.status);
|
|
2915
3025
|
}
|
|
3026
|
+
// A split refused (issue #1414): a name the specs do not have (2 — the invocation has to change), or something
|
|
3027
|
+
// the specs hold that the split cannot carry (1). The message names it, and the usage under it would bury that.
|
|
3028
|
+
if (err instanceof SplitError) {
|
|
3029
|
+
console.error(`rigc split: ${err.message}`);
|
|
3030
|
+
process.exit(err.status);
|
|
3031
|
+
}
|
|
2916
3032
|
// A page or frame that is not a PNG rigc can read, from any command that
|
|
2917
3033
|
// opens one (`render`, `check`, `preview`, …) — issue #732. The sentence is
|
|
2918
3034
|
// the one reader's and already names the file, what it is and what rigc
|