spine-rigc 1.0.1 → 1.0.2
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 +34 -55
- package/docs/AUTHORING.md +725 -1160
- package/docs/FACE.md +205 -371
- package/docs/INGEST.md +151 -306
- package/docs/MOTION.md +11 -27
- package/docs/PROMPTING.md +1 -1
- package/docs/RIGGING.md +10 -26
- package/docs/SPEC_COVERAGE.md +27 -871
- package/package.json +1 -1
- package/src/atlas.ts +9 -8
- package/src/compile.ts +1 -1
- package/src/generation.ts +2 -1
- package/src/ladder.ts +1 -1
- package/src/rig.ts +4 -4
- package/src/validate.ts +20 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Rig compiler for Spine — declarative rig specs in, Spine 4.3 skeleton data out, verified by a spine-core round-trip. Built so AI agents can author rigs and check their own work; the output imports into the Spine editor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/atlas.ts
CHANGED
|
@@ -431,11 +431,12 @@ export function rewritePageNames(parsed: ParsedAtlas, rename: (name: string, ind
|
|
|
431
431
|
*
|
|
432
432
|
* This is what `--atlas-in` re-emits (issue #803). A pack's blank lines are its
|
|
433
433
|
* packer's, not its content: a 3.8-era packer begins every file with one, and
|
|
434
|
-
* `A07_ATLAS_TEXT_SHAPE` — which checks the text rigc writes —
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
434
|
+
* `A07_ATLAS_TEXT_SHAPE` — which checks the text rigc writes — refuses a blank
|
|
435
|
+
* line before the first page block, two side by side, and one after the last
|
|
436
|
+
* page block, each by its own sentence. What makes dropping them safe is what
|
|
437
|
+
* they mean to the reader that owns the format: in `TextureAtlas` a run of
|
|
438
|
+
* blank lines ends a page block exactly as one blank line does, and a run before
|
|
439
|
+
* the first page is read as nothing. Measured through the runtime, `"\n" + text`, `"\n\n" + text`
|
|
439
440
|
* and `text` load to the same pages and regions (`PKR63`, `PKR64`).
|
|
440
441
|
*
|
|
441
442
|
* ⚠️ One reading is NOT the same, and it moves toward rigc's: the runtime's
|
|
@@ -523,9 +524,9 @@ export const PACK_NO_ROTATE = 0;
|
|
|
523
524
|
* the trailing newline was appended unconditionally. That one byte contradicts
|
|
524
525
|
* the paragraph above it: a blank line is the separator that sits BETWEEN page
|
|
525
526
|
* blocks, so a file consisting of one is a separator with nothing on either side.
|
|
526
|
-
* `A07_ATLAS_TEXT_SHAPE`
|
|
527
|
-
*
|
|
528
|
-
*
|
|
527
|
+
* `A07_ATLAS_TEXT_SHAPE` reads a file with no non-blank line as having no page
|
|
528
|
+
* block and reports SKIP, and refuses a blank line that trails a page block as
|
|
529
|
+
* `the file ends with a blank line`.
|
|
529
530
|
*
|
|
530
531
|
* The runtime cannot tell the two apart — `new TextureAtlas('')`,
|
|
531
532
|
* `new TextureAtlas('\n')` and `new TextureAtlas('\n\n')` all come back with
|
package/src/compile.ts
CHANGED
|
@@ -3869,7 +3869,7 @@ function deferredAttachmentRefusal(type: string, where: string, how: string): st
|
|
|
3869
3869
|
return (
|
|
3870
3870
|
`${where}: this attachment is a "${type}"${how} — ${DEFERRED_ATTACHMENTS[type]}. ` +
|
|
3871
3871
|
`rigc does not emit it yet, deliberately: it emits ${EMITTED_ATTACHMENT_TYPES.join(', ')}, and a point ` +
|
|
3872
|
-
'appears nowhere in the benchmark corpus
|
|
3872
|
+
'appears nowhere in the benchmark corpus, so it is not on the ' +
|
|
3873
3873
|
"ladder's critical path. docs/SPEC_COVERAGE.md part 1-6 is the row this sentence reads " +
|
|
3874
3874
|
'from, and it is what an implementation would have to carry.'
|
|
3875
3875
|
);
|
package/src/generation.ts
CHANGED
|
@@ -115,7 +115,8 @@ export function spineGeneration(version: string): SpineGeneration | null {
|
|
|
115
115
|
export const TOPLEVEL_CONSTRAINT_ARRAYS: readonly string[] = ['ik', 'transform', 'path', 'physics', 'slider'];
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* The bone key 4.
|
|
118
|
+
* The bone key 4.0 and 4.1 spelled `transform`, renamed `inherit` in 4.2 and
|
|
119
|
+
* spelled that way by 4.3.
|
|
119
120
|
*
|
|
120
121
|
* The old key does not throw and does not warn — it is an unknown field, so the
|
|
121
122
|
* bone falls back to Normal inheritance (#706 row 6, and
|
package/src/ladder.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface Rung {
|
|
|
34
34
|
id: string;
|
|
35
35
|
/** Directory under `examples/`. */
|
|
36
36
|
example: string;
|
|
37
|
-
/** One line on what this rung is testing, from
|
|
37
|
+
/** One line on what this rung is testing, from docs/SURVEY_2026-08-22.md part 4-1. */
|
|
38
38
|
gates: string;
|
|
39
39
|
skeletons: RungSkeleton[];
|
|
40
40
|
}
|
package/src/rig.ts
CHANGED
|
@@ -989,10 +989,10 @@ export interface RigPathAttachment extends RigVertexGeometry {
|
|
|
989
989
|
* an unknown `type` and drop the attachment without a word
|
|
990
990
|
* (`SkeletonJson.ts:653`).
|
|
991
991
|
*
|
|
992
|
-
* 🚧 It appears nowhere in the benchmark corpus
|
|
993
|
-
*
|
|
994
|
-
*
|
|
995
|
-
*
|
|
992
|
+
* 🚧 It appears nowhere in the benchmark corpus, so it is not on the ladder's
|
|
993
|
+
* critical path — which is the reason it is deferred rather than an oversight.
|
|
994
|
+
* `linkedmesh` stood here beside it until issue #691; `RigLinkedMeshAttachment`
|
|
995
|
+
* is the shape that replaced it.
|
|
996
996
|
*/
|
|
997
997
|
export interface RigUnimplementedAttachment {
|
|
998
998
|
type: 'point';
|
package/src/validate.ts
CHANGED
|
@@ -1205,22 +1205,27 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
1205
1205
|
// for want of a SUBJECT only when its reason is one of those constants.
|
|
1206
1206
|
return skip('A07_ATLAS_TEXT_SHAPE', SKIP_NO_ATLAS_PAGE);
|
|
1207
1207
|
}
|
|
1208
|
-
// A blank line
|
|
1209
|
-
//
|
|
1210
|
-
//
|
|
1211
|
-
//
|
|
1212
|
-
//
|
|
1213
|
-
//
|
|
1214
|
-
//
|
|
1215
|
-
//
|
|
1208
|
+
// A blank line at either END of the file is named as that end (issues #803,
|
|
1209
|
+
// #810), and a run of them is one finding stating its length. They are taken
|
|
1210
|
+
// off both ends before the walk below, so the walk sees the page blocks and
|
|
1211
|
+
// the blank lines between them and nothing else: a blank line after the last
|
|
1212
|
+
// region is not a page block, and "consecutive blank lines" and "the last
|
|
1213
|
+
// page block declares no region" are about blocks. `TextureAtlas` reads a
|
|
1214
|
+
// blank at either end as nothing (`TextureAtlas.js:98-100` skips the leading
|
|
1215
|
+
// run, `:116-118` ends a page block on each blank line), and rigc writes
|
|
1216
|
+
// neither (`writeAtlasText`, and `--atlas-in` through `canonicalAtlasShape`),
|
|
1217
|
+
// so a file that has one was written by something else and is refused.
|
|
1216
1218
|
let leading = 0;
|
|
1217
1219
|
while (leading < atlasLines.length && atlasLines[leading].trim().length === 0) leading++;
|
|
1218
1220
|
if (leading > 0) {
|
|
1219
1221
|
fail('A07_ATLAS_TEXT_SHAPE', `line 1: the file begins with ${leading === 1 ? 'a blank line' : `${leading} blank lines`}`);
|
|
1220
1222
|
}
|
|
1223
|
+
let trailing = 0;
|
|
1224
|
+
while (trailing < atlasLines.length - leading && atlasLines[atlasLines.length - 1 - trailing].trim().length === 0) trailing++;
|
|
1225
|
+
const blockEnd = atlasLines.length - trailing;
|
|
1221
1226
|
let expectPage = true;
|
|
1222
1227
|
let sawRegionForPage = false;
|
|
1223
|
-
for (let i = leading; i <
|
|
1228
|
+
for (let i = leading; i < blockEnd; i++) {
|
|
1224
1229
|
const line = atlasLines[i];
|
|
1225
1230
|
if (line.trim().length === 0) {
|
|
1226
1231
|
if (expectPage) fail('A07_ATLAS_TEXT_SHAPE', `line ${i + 1}: consecutive blank lines`);
|
|
@@ -1242,8 +1247,9 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
1242
1247
|
}
|
|
1243
1248
|
sawRegionForPage = true;
|
|
1244
1249
|
}
|
|
1245
|
-
if (!sawRegionForPage
|
|
1246
|
-
|
|
1250
|
+
if (!sawRegionForPage) fail('A07_ATLAS_TEXT_SHAPE', 'the last page block declares no region');
|
|
1251
|
+
if (trailing > 0) {
|
|
1252
|
+
fail('A07_ATLAS_TEXT_SHAPE', `line ${blockEnd + 1}: the file ends with ${trailing === 1 ? 'a blank line' : `${trailing} blank lines`}`);
|
|
1247
1253
|
}
|
|
1248
1254
|
});
|
|
1249
1255
|
|
|
@@ -1904,14 +1910,14 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
1904
1910
|
});
|
|
1905
1911
|
|
|
1906
1912
|
// --- A02: no bone.transform key ------------------------------------------
|
|
1907
|
-
// 4.
|
|
1908
|
-
// Normal inheritance (case 6b). The key itself is `generation.ts`'s, for
|
|
1913
|
+
// 4.2 renamed it to `inherit`, and 4.3 kept that spelling; the old key loads
|
|
1914
|
+
// and silently falls back to Normal inheritance (case 6b). The key itself is `generation.ts`'s, for
|
|
1909
1915
|
// A01's reason.
|
|
1910
1916
|
check('A02_NO_BONE_TRANSFORM_KEY', () => {
|
|
1911
1917
|
const bones = Array.isArray(raw?.bones) ? (raw.bones as unknown[]) : [];
|
|
1912
1918
|
for (const bone of bones) {
|
|
1913
1919
|
if (isObj(bone) && LEGACY_BONE_INHERIT_KEY in bone) {
|
|
1914
|
-
fail('A02_NO_BONE_TRANSFORM_KEY', `bone "${String(bone.name)}" uses 4.
|
|
1920
|
+
fail('A02_NO_BONE_TRANSFORM_KEY', `bone "${String(bone.name)}" uses "transform", the key 4.0 and 4.1 spelled; 4.2 and 4.3 spell it "inherit"`);
|
|
1915
1921
|
}
|
|
1916
1922
|
}
|
|
1917
1923
|
});
|