spine-rigc 2.11.1 → 2.13.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/NOTICE.md +2 -2
- package/README.md +19 -8
- package/docs/AUTHORING.md +294 -10
- package/docs/INGEST.md +18 -1
- package/package.json +1 -1
- package/skills/rigc/SKILL.md +10 -2
- package/src/assertions/bodies/a50.ts +97 -0
- package/src/assertions/facts/stage_box.ts +65 -0
- package/src/assertions/kinds.ts +1 -0
- package/src/assertions/model/index.ts +7 -0
- package/src/assertions/model/stage_box.ts +51 -0
- package/src/atlas.ts +6 -1
- package/src/cli/core_commands.ts +11 -2
- package/src/cli/repack.ts +408 -0
- package/src/cli/shared.ts +96 -3
- package/src/cli/spine_commands.ts +2 -0
- package/src/compile.ts +119 -3
- package/src/core/index.ts +16 -2
- package/src/ingest.ts +154 -8
- package/src/model.ts +21 -2
- package/src/repack.ts +495 -0
- package/src/rig.ts +80 -2
- package/src/validate.ts +61 -0
package/NOTICE.md
CHANGED
|
@@ -38,8 +38,8 @@ Software's terms, not a licence term of this project:
|
|
|
38
38
|
development dependency for its own gate, and so does an install with
|
|
39
39
|
`spine-core` added beside it. Validation is not optional on either: the gate
|
|
40
40
|
runs through the runtime where it is installed and through rigc's own validator
|
|
41
|
-
where it is not, and the two are held to the same verdicts in CI on
|
|
42
|
-
|
|
41
|
+
where it is not, and the two are held to the same verdicts in CI on 50 of the
|
|
42
|
+
51 assertions — the official parser's own parse runs only where the runtime is.
|
|
43
43
|
|
|
44
44
|
> **If a product integrates a Spine Runtime to play rigc's output, that integration
|
|
45
45
|
> is subject to the terms above** — rigc neither creates those terms nor removes
|
package/README.md
CHANGED
|
@@ -61,10 +61,15 @@ it cuts both ways:
|
|
|
61
61
|
is a development dependency: this repository uses it to hold rigc's own validator
|
|
62
62
|
to the round trip's verdicts — in CI, on the public example recipes and every
|
|
63
63
|
selftest call, and on a private corpus of production rigs measured before each
|
|
64
|
-
release. Of the
|
|
64
|
+
release. Of the 51 assertions, 50 run on both; `A00_ROUNDTRIP_PARSE`, the official
|
|
65
65
|
parser's own parse, runs only where the runtime is installed and is reported as a
|
|
66
|
-
SKIP elsewhere.
|
|
67
|
-
|
|
66
|
+
SKIP elsewhere. There, in the parse's place, the report also carries the model
|
|
67
|
+
side's own two parse rules, `A00_MODEL_READ` and `A00_MODEL_REGIONS_ON_PAGES`,
|
|
68
|
+
which no round-trip rule matches and the registry does not hold — so its summary
|
|
69
|
+
totals 53 on the core entry, and its last line counts 44 rules on the model side
|
|
70
|
+
([AUTHORING §5.2](docs/AUTHORING.md) has a row for each). Install it beside rigc
|
|
71
|
+
(`bun add -d @esotericsoftware/spine-core`) and the same `rigc` runs the round
|
|
72
|
+
trip on every build. The
|
|
68
73
|
[Spine Runtimes License Agreement](https://esotericsoftware.com/spine-runtimes-license)
|
|
69
74
|
applies to that dependency wherever it is installed.
|
|
70
75
|
|
|
@@ -557,6 +562,7 @@ commands take it and what its default is.
|
|
|
557
562
|
| `build … --pack` | the same build with every part arranged onto **shared** atlas pages, written into `--out` — losslessly, and gated a second time as the pair that ships. `--page-size`, `--padding`, `--page-edges` and `--pack-shape` tune it — `--pack-shape polygon` lets a neighbour sit inside a mesh's rectangle where its hull is not, and with `--page-edges free` costs 12–56 times `rect`'s pack (3–11 s against 0.2–0.3 s on 90–144 parts over two to four pages; past 5 s from about 60 parts, a second or so below 45 on one page — AUTHORING §0.1) |
|
|
558
563
|
| `build … --atlas-in <file.atlas>` | the same build with every part resolved to a **region of an existing pack** instead of a loose PNG; a name the atlas lacks, a size the spec disagrees with or a rectangle off its page is refused by name |
|
|
559
564
|
| `validate <dir>` | re-gates artifacts already on disk |
|
|
565
|
+
| `repack <dir> --out <dir>` | a packed build **repacked from its own output** — `skeleton.json`, `skeleton.atlas` and the pages, with no parts kept — under the packing flags `build --pack` takes, so a packer improvement reaches a build whose parts are gone. Every region is lifted off its page, the skeleton read back with `ingest`, and `build --pack` run over the lifted parts with the same gate; it writes only after every region is shown pixel-identical to the input's, the skeleton byte-identical and the gate green, and refuses by name an atlas it cannot lift exactly. A skeleton the rebuild does not reproduce byte for byte — a build written before 2.2.0, whose header box was its stage — is refused naming every differing path, and written, every path printed, only under `--accept-skeleton-differences`. `--stage-box <slot>` reads the stage from the box a build carries for it (`skeleton.stageBox`), the one way the three files alone carry the stage exactly ([AUTHORING §0.4](docs/AUTHORING.md)) |
|
|
560
566
|
| `ingest <skeleton.json> --out <dir>` | `build` run backwards: reads a Spine 4.3 skeleton and writes the rig spec and motion spec that **rebuild it**, plus a findings report naming everything it could not carry. a skeleton that declares no stage is carried as declaring none, `--stage x,y,w,h` adds a box to one — and is refused, rather than ignored, beside one that declares a box — and `--images <dir>` writes the spec's own images directory — the opposite direction from `build --images`, which overrides it — so the rebuild carries no flag at all |
|
|
561
567
|
| `explain --rig … --motion …` | the compiled rig as a table — every bone with its resolved parent, the slots in draw order, every timeline key by key. Writes nothing. What to reach for when a rig compiles and still looks wrong |
|
|
562
568
|
| `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/`. `--hide <slot,…>` or `--slot <slot,…>` draws part of the rig on the **same grid** as the whole, so the two frames overlay and the difference is the part; `frames.json` records the subset and `check` refuses such a set as a reference. `--geometry` adds a `geometry.json` per set: every frame's bone world transforms and skinned attachment vertices, with the rest geometry, on the frames' own grid |
|
|
@@ -575,9 +581,9 @@ first three work on any reference you have, and `bench` is a repository workflow
|
|
|
575
581
|
and `bun run fetch-examples`. The reasoning behind them is in
|
|
576
582
|
[the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
|
|
577
583
|
|
|
578
|
-
`build` and `validate` both default to `--profile spine` — the
|
|
584
|
+
`build` and `validate` both default to `--profile spine` — the 35 validity rules, which
|
|
579
585
|
ask *is this valid Spine 4.3 that any runtime plays correctly?* `--profile spine-html`
|
|
580
|
-
adds all
|
|
586
|
+
adds all 51: the other 16 are one renderer's policy and one canvas budget's, and they
|
|
581
587
|
fire on perfectly correct editor-produced Spine data, which is why they are opt-in.
|
|
582
588
|
⇒ **That reason is about foreign data and does not carry to a rig you are authoring
|
|
583
589
|
yourself: author under `--profile spine-html` and read the extra 16 as findings, and
|
|
@@ -649,7 +655,12 @@ specs are still written — a spec plus a list of what is missing from it beats
|
|
|
649
655
|
`diff` reports the box and gates nothing on it. 🔁 The box an export's header carries
|
|
650
656
|
is its setup-pose bounding box, and it becomes the rebuild's *stage*; the rebuild's own
|
|
651
657
|
header is computed again, because since issue #907 `build` writes the setup-pose
|
|
652
|
-
bounding box there — never the stage — held to spine-core's `getBounds`.
|
|
658
|
+
bounding box there — never the stage — held to spine-core's `getBounds`. 📦 A rig
|
|
659
|
+
that has to carry its stage in the Spine files asks for it as a bounding box
|
|
660
|
+
(`skeleton.stageBox`, written by `build` from the stage and held by
|
|
661
|
+
`A50_STAGE_BOX_IS_THE_STAGE`), and `ingest --stage-box <slot>` reads that box back as
|
|
662
|
+
the rebuild's stage — [AUTHORING §3.1](docs/AUTHORING.md) has the field and the
|
|
663
|
+
paragraph for a consumer of the files.
|
|
653
664
|
- **An animation's duration.** The format has no such field. The largest key time is
|
|
654
665
|
the only derivable answer and it is what a runtime plays to; it is wrong for an
|
|
655
666
|
animation that holds its last pose past its last key, so it is recorded as a finding
|
|
@@ -728,7 +739,7 @@ letting `A17` blame the editor for the harness's own doing.
|
|
|
728
739
|
| 📥 **[docs/INGEST.md](docs/INGEST.md)** | **working with a skeleton you did not author.** What every command can and cannot do with a foreign `skeleton.json`, reading it with the toolchain, transcription as the route that makes it yours, what each validator complaint means on an export, and the re-pivot/rename/extend recipes. Ships in the package too |
|
|
729
740
|
| 🤖 **[docs/PROMPTING.md](docs/PROMPTING.md)** | **handing the authoring to an AI agent** — the prompt clauses a measured pilot run paid for, and what you can leave unsaid. Ships in the package too |
|
|
730
741
|
| 🔬 **[docs/SPEC_COVERAGE.md](docs/SPEC_COVERAGE.md)** | **the Spine 4.3 format, field by field.** Every key the 4.3 JSON parser and atlas reader take, what each defaults to, and the parser line it comes from — the rows rigc's refusals cite by part number. Ships in the package too |
|
|
731
|
-
| 🎓 **[the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md)** | **why you can trust the output.** The yardstick, `diff` and `check` and what neither can see, the eight-rung ladder and the spineboy graduation exam, the run viewer, the
|
|
742
|
+
| 🎓 **[the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md)** | **why you can trust the output.** The yardstick, `diff` and `check` and what neither can see, the eight-rung ladder and the spineboy graduation exam, the run viewer, the 51 named assertions with their profiles, and the selftest that has watched every one of them fire. Repository material — it is not in the npm package |
|
|
732
743
|
| 📋 [LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) · [GATE.md](https://github.com/firejune/rigc/blob/main/docs/GATE.md) · [PILOT.md](https://github.com/firejune/rigc/blob/main/docs/PILOT.md) | the live rung ledger, the clause statements a candidate is graded against, and how to run an agent through the ladder and score what comes back |
|
|
733
744
|
| 🔬 [SURVEY_2026-08-22.md](https://github.com/firejune/rigc/blob/main/docs/SURVEY_2026-08-22.md) | the survey the ladder was climbed by: rigc and the official examples as measured on 2026-08-22, and the gap list ordered by rung. A dated record, not kept current. Repository material |
|
|
734
745
|
| 🧬 [GENERATIONS.md](https://github.com/firejune/rigc/blob/main/docs/GENERATIONS.md) | **Spine data from another generation.** Why a 3.8–4.2 file read as 4.3 fails in silence, the policy that follows (detect from `skeleton.spine`, never guess, play on the matching runtime), what `A16` and `ingest` do with such a file, and the editor as the migration path. Repository material |
|
|
@@ -778,7 +789,7 @@ quality."* All six, with their verdicts, are in
|
|
|
778
789
|
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
|
779
790
|
|
|
780
791
|
The whole dossier — the yardstick, `diff` and `check` and what neither of them can
|
|
781
|
-
see, every rung, the run viewer, the
|
|
792
|
+
see, every rung, the run viewer, the 51 assertions and the selftest behind them — is
|
|
782
793
|
[docs/BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
|
|
783
794
|
Live rung status is
|
|
784
795
|
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
package/docs/AUTHORING.md
CHANGED
|
@@ -155,9 +155,11 @@ That entry's `build` takes the same flags and writes the same files, byte for by
|
|
|
155
155
|
`skeleton.json`, `skeleton.atlas`, `skeleton.model.json`, and the pages `--pack` or
|
|
156
156
|
`--copy-images` put in `--out`.
|
|
157
157
|
Only the gate differs, because the round trip is spine-core's: it runs the model
|
|
158
|
-
side over the document
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
side over the document — its own two parse rules first, `A00_MODEL_READ` and
|
|
159
|
+
`A00_MODEL_REGIONS_ON_PAGES`, which stand where spine-core's parse stands and are
|
|
160
|
+
not in the registry, then the registry's rules that read the model — and the round
|
|
161
|
+
trip's own rules restated over the text the emitter wrote (A01, A02, A05, A07, A16,
|
|
162
|
+
A31, A35, and A18 over a second compile's three files), and it does not run `A00_ROUNDTRIP_PARSE`, which reports **SKIP**
|
|
161
163
|
naming spine-core. Its report ends on a line saying which rules ran there and
|
|
162
164
|
which did not, and it writes only when none of them fails (§5.2).
|
|
163
165
|
|
|
@@ -219,19 +221,19 @@ What the flags mean:
|
|
|
219
221
|
| --- | --- |
|
|
220
222
|
| `--rig` | the rig spec — skeleton structure |
|
|
221
223
|
| `--motion` | the motion spec — time |
|
|
222
|
-
| `--out` | directory for `skeleton.json` + `skeleton.atlas`, and on `build` `skeleton.model.json` beside them; atlas page paths and `skeleton.images` are written relative to it. `skeleton.model.json` is rigc's own record of the compiled rig (`rigc-compiled/3`: the stage the header declares, or `null`; the bones, slots, skins, constraints, events and animations the Spine files were emitted from, in the spec's order, and `editorOrder`, the order `skeleton.json` lists the skins, each skin's slot keys and the animations in; the rest of what posing them takes; then `pages`, every page of the `skeleton.atlas` written beside it, its `pma` and its `scale:` line (`null` where it states none), and where each region sits on it — after `--pack` the packed pages, after `--copy-images` the copies' names), written after the same gate as the pair and ending in `spine.sha256`, the digest of the `skeleton.json` written beside it. A `rigc-compiled/2` document (rigc 1.7) states no stage, editor order, `pma` or `scale`, and is still read: `render` and `check` then read those off `skeleton.json` and the atlas, and the `poser` line names both files. A `rigc-compiled/1` document (rigc 1.6) has no `pages` either and is still read: the core then takes the placement from the atlas beside it, the `poser` line says so, and with no atlas there `render` and `check` refuse it naming the file. A `rigc-compiled/2` or `/3` build is rendered and checked with its `skeleton.atlas` gone (`--candidate` below). It is what rigc's own posing core (`src/core/`) poses: `render` and `check` — and `bench --frames`, which folds `check` in — pose a build through it, and `--poser` below says when they do not. On `check` it is a directory of **pictures** instead: one per frame the table lists (every compared frame under `--all-frames`), reference · candidate · difference · overlay at the comparison grid's native size, as `<dir>/<set>/f####.png` beside a `frames.json` that says what they are pictures of. Each `<dir>/<set>/` is cleared first; a file at `<dir>`, or a directory that is or holds `--frames`, is refused by name — **§9.2.1** |
|
|
224
|
+
| `--out` | directory for `skeleton.json` + `skeleton.atlas`, and on `build` `skeleton.model.json` beside them; atlas page paths and `skeleton.images` are written relative to it. `skeleton.model.json` is rigc's own record of the compiled rig (`rigc-compiled/3`: the stage the header declares, or `null`; the bones, slots, skins, constraints, events and animations the Spine files were emitted from, in the spec's order, and `editorOrder`, the order `skeleton.json` lists the skins, each skin's slot keys and the animations in; the rest of what posing them takes; then `pages`, every page of the `skeleton.atlas` written beside it, its `pma` and its `scale:` line (`null` where it states none), and where each region sits on it — after `--pack` the packed pages, after `--copy-images` the copies' names), written after the same gate as the pair and ending in `spine.sha256`, the digest of the `skeleton.json` written beside it. A `rigc-compiled/2` document (rigc 1.7) states no stage, editor order, `pma` or `scale`, and is still read: `render` and `check` then read those off `skeleton.json` and the atlas, and the `poser` line names both files. A `rigc-compiled/1` document (rigc 1.6) has no `pages` either and is still read: the core then takes the placement from the atlas beside it, the `poser` line says so, and with no atlas there `render` and `check` refuse it naming the file. A `rigc-compiled/2` or `/3` build is rendered and checked with its `skeleton.atlas` gone (`--candidate` below). It is what rigc's own posing core (`src/core/`) poses: `render` and `check` — and `bench --frames`, which folds `check` in — pose a build through it, and `--poser` below says when they do not. On `check` it is a directory of **pictures** instead: one per frame the table lists (every compared frame under `--all-frames`), reference · candidate · difference · overlay at the comparison grid's native size, as `<dir>/<set>/f####.png` beside a `frames.json` that says what they are pictures of. Each `<dir>/<set>/` is cleared first; a file at `<dir>`, or a directory that is or holds `--frames`, is refused by name — **§9.2.1**. On `repack` it is the directory the repacked build is written into — the same files `build --pack` writes — and it has to be absent or empty — **§0.4** |
|
|
223
225
|
| `--copy-images` | `build` only: also copies every page **the emitted atlas names** into `--out` and rewrites the atlas to the copies, so the directory is self-contained enough to zip or commit on its own, and points `skeleton.images` at `--out` itself so the editor's import finds the parts beside the skeleton (§3.1 says why it is spelled `../<out>/` and not `./`). Under `--atlas-in` those pages are the pack's, not one per part (**§0.2**). Without it, page paths point at the source art |
|
|
224
226
|
| `--pack` | `build` only: arrange every part onto **shared** atlas page(s), written into `--out` as real PNGs, instead of one page per part. Lossless — nothing is resampled, trimmed or rotated. The default is one page per part — **§0.1** |
|
|
225
|
-
| `--page-size` | `build --pack` only: the largest page edge (default `2048`). A ceiling, not the size: page edges are powers of two and the one written is the smallest that holds the pack — **§0.1** |
|
|
226
|
-
| `--padding` | `build --pack` only: the gutter each region reserves on every side (default `2`), filled by extending the region's own edge pixels outwards. `0` is not a legal-but-tight choice, it is bleed — **§0.1** |
|
|
227
|
-
| `--page-edges` | `build --pack` only: `pot` (default) or `free`. `pot` keeps both page edges powers of two; `free` sizes the page to the parts — a smaller page, at the cost of region attachments sampling within one least significant bit of the loose build instead of exactly. Any other value is refused by name — **§0.1** |
|
|
228
|
-
| `--pack-shape` | `build --pack` only: `rect` (default) or `polygon`. `rect` keeps every region's cell apart; `polygon` packs a region that only meshes draw by its emitted hull, so a neighbour may sit inside its rectangle where the hull is not, with the padding kept between footprints. A region attachment's footprint stays its rectangle. Any other value is refused by name, and so is the flag without `--pack` — **§0.1** |
|
|
227
|
+
| `--page-size` | `build --pack` only: the largest page edge (default `2048`). A ceiling, not the size: page edges are powers of two and the one written is the smallest that holds the pack — **§0.1**. `repack` takes it with the same meaning — **§0.4** |
|
|
228
|
+
| `--padding` | `build --pack` only: the gutter each region reserves on every side (default `2`), filled by extending the region's own edge pixels outwards. `0` is not a legal-but-tight choice, it is bleed — **§0.1**. `repack` takes it with the same meaning — **§0.4** |
|
|
229
|
+
| `--page-edges` | `build --pack` only: `pot` (default) or `free`. `pot` keeps both page edges powers of two; `free` sizes the page to the parts — a smaller page, at the cost of region attachments sampling within one least significant bit of the loose build instead of exactly. Any other value is refused by name — **§0.1**. `repack` takes it with the same meaning — **§0.4** |
|
|
230
|
+
| `--pack-shape` | `build --pack` only: `rect` (default) or `polygon`. `rect` keeps every region's cell apart; `polygon` packs a region that only meshes draw by its emitted hull, so a neighbour may sit inside its rectangle where the hull is not, with the padding kept between footprints. A region attachment's footprint stays its rectangle. Any other value is refused by name, and so is the flag without `--pack` — **§0.1**. `repack` takes it with the same meaning, and always packs — **§0.4** |
|
|
229
231
|
| `--atlas-in` | `build` and `explain`: resolve every part against the **regions of a pre-packed `.atlas`** instead of against loose PNGs. Region geometry is read from the file and sizes are descaled by the page's `scale:`; `build` re-emits the atlas into `--out`, re-anchored, and `explain` writes nothing and poses through it — **§0.2**. On `explain` it is the flag that makes a **size-only** spec readable at all (`ingest --art none`), because posing resolves every attachment against an atlas; without it that pair is refused by name rather than thrown through (§5.1) |
|
|
230
232
|
| `--images` | where the rig spec's `image` names resolve (overrides the rig's own `images` field, and is relative to your working directory). For `pose` it is the directory of **loose part PNGs to place** — every `.png` in it is a part, in name order. For `chainfit` it is only where each attachment's image name **resolves**: the candidate decides what the parts are, so extra PNGs are unused and a missing name is refused by name (§12.3) |
|
|
231
233
|
| `--manifest` | a cut manifest. Only for a rig with **measured art** behind it; a foreign skeleton has none |
|
|
232
234
|
| `--cut` | `build`, `explain` and `validate`: look up a named cut in `--cuts <cuts.json>`, **instead of** `--rig`/`--motion`/`--out` — the two spellings are one build stated two ways and are refused together. A `cuts.json` is `{ "<name>": { "rig": …, "motion": …, "out": …, "manifest"?: … } }`, every path in it relative to the table's own file, so the table lives with the project that owns the art |
|
|
233
235
|
| `--cuts` | the `cuts.json` `--cut` names. Required beside it — `--cut` alone is refused, with no guess at where the table lives |
|
|
234
|
-
| `--profile` | `spine` = the
|
|
236
|
+
| `--profile` | `spine` = the 35 validity rules (**the default**) · `spine-html` = all 51, opt-in |
|
|
235
237
|
| `--candidate` | `check`, `bench`, `render`, `preview`, `chainfit` and `vote` only: a **compiled** artifact — the directory `build --out` wrote, or a `skeleton.json` path. A directory holding no `skeleton.json`, or a path with nothing at it, is refused `nothing at <path>`, exit 2, nothing written, by every command that takes a build — `validate <dir>` and a directory given to `diff` included (`RC27`). `--atlas <path>` names the atlas when it does not sit beside the skeleton. On `render` and `check` a rigc build whose `skeleton.model.json` is `rigc-compiled/3` or `/2` needs no atlas at all: with no `--atlas` and no atlas beside it, it is drawn from the document's `pages`, the same frames, geometry and figures as with it, and the `atlas` line says `— not there`. An atlas that is there is still read, and held to `pages`: one that differs (edited after the build) is drawn through spine-core, and the `poser` line names the first difference. A build drawn through an atlas that is not there — a `rigc-compiled/1` document, or a build the core refuses — is refused `nothing at <the atlas>: … is drawn through its atlas (<why>)`, exit 2, nothing written; a Spine export without its atlas is refused `nothing at <the atlas>` as it always was. A directory whose files are not one build — a `skeleton.json` or a `skeleton.atlas` from another build beside this one's `skeleton.model.json` — is drawn through spine-core where the runtime can load the pair, the `poser` line naming the two digests or the atlas's first difference; where it cannot, it is refused `<skeleton> does not load against <the atlas>: spine-core draws this pair (<the poser line's why>) and could not resolve it — "<the runtime's own words>". <dir> is not one build: …`, exit 2, nothing written (`RC18`) — another build's skeleton beside this build's atlas typically ends `"Region not found in atlas: <region> (attachment: <name>)"`. Rebuild, or put the build's own three files back beside each other. A Spine export beside an atlas it was not exported with is refused in the same words, ending `The skeleton and the atlas are not one pair`. An atlas copied from another build's directory usually stops earlier, at its pages — their paths are relative to the directory it was written in — and is refused naming the first one: `nothing at <the page>: <the atlas> names it as a page, and spine-core draws this pair through that atlas (<why>). … <dir> is not one build: …`, exit 2, nothing written (`RC18`); an export whose atlas names a page that is not there is refused the same way, ending `and the page has to be there`. A build moved or copied whole away from the directory it was built in stops at the same place, on either poser, and is refused naming the page and the file that names it: `nothing at <the page>: <the skeleton.model.json> names it as a page, and the core draws this build from it. A page path is relative to the build's directory, so a build moved or copied away from the directory it was built in names pages that are not here — build it again where it is, or build it with --copy-images, which writes its pages beside it` (under `--poser spine`, `<the atlas> names it as a page, and spine-core draws this pair through that atlas (--poser spine)` with the same ending), exit 2, nothing written (`RC21`); only where the core refused the files does a page refusal say the directory is not one build. `bench --bones` and `rigc bonedist` — on its `--candidate` and on its `--reference` — refuse a pair the runtime cannot load in the words above with `spine-core poses this pair (bonedist's candidate: …)` or `(bonedist's reference: …)`, exit 2, nothing written (`RC20`); they read no page image, so a build whose pages are elsewhere is measured as it is in place. A `skeleton.json` that is not JSON is refused `rigc: cannot read <the file>: <the parser's words> (line N, column N)`, exit 2, by `render`, `check`, `bench` and `bonedist` as by every other command that reads one (`RC22`). With the atlas gone, `check` on a `rigc-compiled/3` build reads its pages' `scale:` lines off the document, so its texture note and `check.json`'s `textureFrom.candidateScales` are what they are with the atlas; on a `/2` build its texture note says whether the atlas declares a `scale:` line is not read (that document does not state one), and `candidateScales` is `null` (`RC13`). **`vote` and `preview` take it more than once**: `vote` 2–4 times, one per pane, labelled A, B, C, D in the order given; `preview` any number of times, one pane per candidate in the order given, each headed by its path and its gate line, and the same skeleton twice (a directory and its `skeleton.json` are one) is refused by name. `--atlas` goes with one candidate only. Everywhere else a repeat is a typo and is refused |
|
|
236
238
|
| `--animation` | `render`, `preview` and `vote` only: which animation to show. The default is **every** one for `render`, the **first** for `preview` (each candidate's own first, with several), and for `vote` the first of candidate A. A name the skeleton does not have is refused, with the ones it does have listed — and for `vote` and a several-candidate `preview`, so is a name that only *some* candidates have, naming the one that lacks it |
|
|
237
239
|
| `--geometry` | `render` only: also write a `geometry.json` into each frame directory — per frame, every bone's world transform and every slot's region or mesh vertices in world units after skinning, plus each attachment's rest geometry and topology, on the frames' own grid and viewport. Every other file is byte for byte what the render writes without it. Refused with `--slot`/`--hide`, because the geometry is the whole pose whatever is drawn — **§8.2** |
|
|
@@ -962,6 +964,7 @@ repository builds on every run.
|
|
|
962
964
|
| `--art none` | state `width`/`height` only, so the rebuild is `build --atlas-in <pack.atlas>` and every part resolves out of the pack |
|
|
963
965
|
| `--images <dir>` | **write** the rig spec's own `images` directory, spelled relative to `--out`, so the rebuild is a plain `build --rig … --motion … --out …`. Without it the field is left out and every `image` resolves against `--out` itself, which holds the specs and no art — so every rebuild has to repeat `build --images <dir>`. Refused together with `--art none`, which writes no `image` for it to be the base of |
|
|
964
966
|
| `--stage x,y,w,h` | a stage to **add** to a skeleton whose header carries no box (a header's box — its setup-pose bounding box — is otherwise read as the rebuild's stage) — without it the absence is carried as `"width": null, "height": null`. An editor export *may* be such a file; every export under `examples/` carries a box and `ingest` reads it straight through — so passing the flag at one of them is **refused**, naming both boxes, rather than silently doing nothing |
|
|
967
|
+
| `--stage-box <slot>` | read the stage from the bounding box in that slot — what `build` writes for a rig that states `skeleton.stageBox` (§3.1) — in place of the header's box, which is the setup-pose bounding box. The rebuilt spec asks for the same box (`skeleton.stageBox`) rather than transcribing it, so a rigc build that carries one rebuilds byte for byte from `skeleton.json` and its atlas alone. Only a box `build` could write back is read; everything else in the slot is refused by name (below). Without the flag no slot is read as the stage because of its name |
|
|
965
968
|
| `--name <n>` | the rig spec's `name`, which the motion spec's `archetype` must equal (default: the file's basename) |
|
|
966
969
|
|
|
967
970
|
⚠️ **`ingest --images` and `build --images` point opposite ways.** `build --images`
|
|
@@ -998,6 +1001,28 @@ editor's command-line import and export carried a header box through **verbatim*
|
|
|
998
1001
|
a forged `0, 0, 1, 1` came back as `width 1, height 1` — so the editor does not
|
|
999
1002
|
recompute it on a round trip either; whatever box a file states travels.)
|
|
1000
1003
|
|
|
1004
|
+
📦 **`--stage-box <slot>`: the stage a rig carried in its Spine files** (issue #1168). A
|
|
1005
|
+
build whose rig asked for `skeleton.stageBox` carries its stage as a bounding box, and
|
|
1006
|
+
a caller holding only `skeleton.json`, the atlas and its pages names the slot. The
|
|
1007
|
+
box's four corners are the rebuild's stage — read in place of the header's box, and
|
|
1008
|
+
printed as `stage read from the bounding box in slot "…" (--stage-box)` — and the
|
|
1009
|
+
spec states `skeleton.stageBox` instead of an attachment, so `build` writes the box
|
|
1010
|
+
again from the stage. Refused by name, as an `IngestError` before anything is written:
|
|
1011
|
+
|
|
1012
|
+
| what the slot holds | the refusal names |
|
|
1013
|
+
| --- | --- |
|
|
1014
|
+
| no such slot | *the skeleton has no slot "…"; it declares [...]* |
|
|
1015
|
+
| a slot on a bone below the root, or on a root that states a setup transform | *which is not the root* / *which states rotation 90* |
|
|
1016
|
+
| no attachment, several, or one in a skin other than `default` | *the default skin holds no attachment …* / *is also filled by skin(s) …* |
|
|
1017
|
+
| an attachment that is not a bounding box, states another `name`, or is not four unweighted vertices | *is a "clipping", not a bounding box* / *states vertexCount … the stage is four unweighted corners* |
|
|
1018
|
+
| four vertices that are not one axis-aligned rectangle of positive size | *which is not the four corners of one axis-aligned rectangle of positive size* |
|
|
1019
|
+
| `--stage` beside it, or a model document beside the skeleton stating another stage | *two sources for one value* / *two statements of one stage that disagree* |
|
|
1020
|
+
|
|
1021
|
+
A box `build` spells differently — its corners in another order, the `color` the
|
|
1022
|
+
editor writes on every box it exports — is read, and is the lossy
|
|
1023
|
+
`STAGE_BOX_REWRITTEN` ([INGEST.md](INGEST.md) §2.0): the rebuild carries the same
|
|
1024
|
+
rectangle, spelled `build`'s way.
|
|
1025
|
+
|
|
1001
1026
|
⛔ **The flag is refused beside a box the file states.** Two sources for one value, and
|
|
1002
1027
|
the file is the one that was measured — so `ingest` names both boxes and stops rather
|
|
1003
1028
|
than writing one of them and saying nothing. Drop the flag, or correct `skeleton` in the
|
|
@@ -1250,6 +1275,195 @@ bun cli.ts pose --images path/to/parts --frame poseA.png [--out pose.json]
|
|
|
1250
1275
|
`both-unacceptable` is the one that means *propose again* rather than *adopt
|
|
1251
1276
|
either* — check for it before you treat a ballot as settled.
|
|
1252
1277
|
|
|
1278
|
+
### 0.4 Repacking a build whose parts were not kept — `repack`
|
|
1279
|
+
|
|
1280
|
+
A build directory holds `skeleton.json`, `skeleton.atlas`, the pages and
|
|
1281
|
+
`skeleton.model.json`, and a project that keeps only those — no loose part PNGs —
|
|
1282
|
+
could not take up a packer change (`--page-edges free`, `--pack-shape polygon`)
|
|
1283
|
+
without regenerating its parts upstream. `rigc repack` packs such a build again,
|
|
1284
|
+
from its own output:
|
|
1285
|
+
|
|
1286
|
+
```bash
|
|
1287
|
+
bun cli.ts repack spine/ --out spine.free/ --page-edges free --pack-shape polygon
|
|
1288
|
+
# .. lift 10 region(s) off 1 page(s), each named once: 0 turned on its page, 0 trimmed of whitespace — …
|
|
1289
|
+
# .. stage 640 x 700 at 0,0 — read from the skeleton.model.json beside it (its spine.sha256 is this skeleton's). …
|
|
1290
|
+
# .. check (a) regions: 10 of 10 pixel-identical, by name, to the same region lifted off the input's pages — 7 over the whole rectangle, 3 that only meshes draw over the footprint …
|
|
1291
|
+
# .. check (b) skeleton.json: byte-identical to the input's — the pack owns none of it
|
|
1292
|
+
# .. check (c) gate: green — the report above, on the compile and on the packed pair
|
|
1293
|
+
# rigc: wrote /abs/path/spine.free/skeleton.png
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
It is three steps, run in process through the tree's own functions: every region
|
|
1297
|
+
is **lifted** off its page by its atlas bounds (`extractRegion`, the inverse of
|
|
1298
|
+
what the runtime samples — `rotate:` and `offsets:` included), the skeleton is
|
|
1299
|
+
**read back** with `ingest --art loose` (an `image` per attachment, named after the
|
|
1300
|
+
region it draws, which is the file the lift wrote), and **`build --pack`** runs over
|
|
1301
|
+
the two specs and the lifted parts — the same body, the same gate, and on each entry
|
|
1302
|
+
that entry's gate (§0's paragraph on the two entries). They run in a work directory
|
|
1303
|
+
under the system temp directory that the command removes when it ends, green or
|
|
1304
|
+
refused or a gate that exits; only the build is copied into `--out`, so the output is
|
|
1305
|
+
the same kind of directory as the input and can itself be repacked. The lifted parts
|
|
1306
|
+
are not kept: the output's pages hold every one of them, and `repack` is the command
|
|
1307
|
+
that lifts them.
|
|
1308
|
+
|
|
1309
|
+
| flag | what it decides |
|
|
1310
|
+
| --- | --- |
|
|
1311
|
+
| `<dir \| skeleton.json>` | the build to repack — a directory holding `skeleton.json` and `skeleton.atlas`, or a `skeleton.json` path |
|
|
1312
|
+
| `--out <dir>` | where the repacked build is written: `skeleton.json`, `skeleton.atlas`, `skeleton.model.json` and the pages. **Absent or empty**, and refused otherwise (exit 2): a page an earlier pack wrote and this one does not would stay beside the new atlas with nothing naming it — and the input's own directory is never empty |
|
|
1313
|
+
| `--atlas <path>` | the atlas, when it is not `skeleton.atlas` beside the skeleton |
|
|
1314
|
+
| `--page-size`, `--padding`, `--page-edges`, `--pack-shape` | `build --pack`'s, with the same defaults and the same meaning (§0.1). `repack` always packs |
|
|
1315
|
+
| `--profile` | the gate's profile, as on `build` |
|
|
1316
|
+
| `--stage x,y,w,h` | handed to `ingest` as its own `--stage` (§0.3) — a box for a skeleton that states none, refused beside one that does |
|
|
1317
|
+
| `--stage-box <slot>` | handed to `ingest` as its own `--stage-box` (§0.3): the stage read from the bounding box a build writes for a rig that states `skeleton.stageBox` — the exact way to carry the stage through a repack of the three files alone (*The stage*, below). `ingest`'s refusals reach the caller in its own words |
|
|
1318
|
+
| `--accept-skeleton-differences` | write the rebuild even where its `skeleton.json` differs from the input's, with **every** differing path printed and both values (default: refused naming them, check (b) below). Nothing else relaxes: check (a) and the gate hold exactly as without it. Beside a skeleton that comes back byte-identical, check (b)'s line says the flag accepted nothing |
|
|
1319
|
+
|
|
1320
|
+
Every other flag `build` takes is refused by name, with the reason `repack` does not
|
|
1321
|
+
take it (`--rig`, `--motion`, `--images`, `--manifest`, `--copy-images`, `--pack`,
|
|
1322
|
+
`--atlas-in`, `--cut`, `--cuts`); any other flag is refused listing the ones it takes.
|
|
1323
|
+
|
|
1324
|
+
**Nothing reaches `--out` until three things are shown**, each on its own line:
|
|
1325
|
+
|
|
1326
|
+
- **(a) every region** lifted off the **new** pages is pixel-identical, by name, to
|
|
1327
|
+
the same region lifted off the **input's**. Under `--pack-shape rect` that is the
|
|
1328
|
+
whole rectangle. Under `polygon` a region that only meshes draw owns its
|
|
1329
|
+
**footprint** on the page — its hull and triangles, dilated by the padding — and a
|
|
1330
|
+
neighbour may sit in the rest of its rectangle by design (§0.1), so that region is
|
|
1331
|
+
compared over the footprint the packer kept as its own (`packFootprints` and
|
|
1332
|
+
`footprintCell`, the packer's own two functions). Measured: a whole-rectangle
|
|
1333
|
+
comparison refused `gallery/flex` repacked `--page-edges free --pack-shape polygon`
|
|
1334
|
+
on 4 of its 20 regions, each one a neighbour's texels in a mesh's empty corner.
|
|
1335
|
+
The lift carries those texels, so a later `rect` repack of a polygon build keeps
|
|
1336
|
+
them where nothing samples them: `gallery/nod` taken `pot`/`rect` → `free`/`polygon`
|
|
1337
|
+
→ `pot`/`rect` comes back with its original `skeleton.json` and `skeleton.atlas`,
|
|
1338
|
+
and a page that differs only in one mesh-only region outside its footprint.
|
|
1339
|
+
A region the input atlas has and the rebuild does not is named here too — a
|
|
1340
|
+
rebuild packs the regions this skeleton's attachments draw and no other, so an
|
|
1341
|
+
atlas several skeletons share comes back short;
|
|
1342
|
+
- **(b) `skeleton.json`** is byte-identical to the input's. A repack is entitled to
|
|
1343
|
+
change **nothing** in the skeleton: attachments name their regions and the atlas
|
|
1344
|
+
alone says where a region sits, so the pack owns the atlas and the pages and none
|
|
1345
|
+
of the skeleton. Measured on every repack below, the rebuilt skeleton was the
|
|
1346
|
+
input's bytes. A difference is refused naming each path with both values — it is
|
|
1347
|
+
something other than the pack — and the refusal names
|
|
1348
|
+
`--accept-skeleton-differences`, which writes the rebuild anyway and prints every
|
|
1349
|
+
differing path as `N place(s) differ from the input, accepted by
|
|
1350
|
+
--accept-skeleton-differences`. The flag is general on purpose: the command can say
|
|
1351
|
+
**where** two skeletons differ and never **why**, so a switch for one cause (the
|
|
1352
|
+
header box below) would be a guess about the cause wearing a flag's name;
|
|
1353
|
+
- **(c) the gate** is green — `build --pack`'s two reports, on the compile and on the
|
|
1354
|
+
packed pair, printed above the three lines.
|
|
1355
|
+
|
|
1356
|
+
Under the settings the input was packed with, a fourth line says the atlas and every
|
|
1357
|
+
page came back byte-identical to the input's — **40 of 40** such repacks below. That
|
|
1358
|
+
is a measurement, not the guarantee: under other settings the pages differ by design,
|
|
1359
|
+
and what is held is (a), (b) and (c). `skeleton.model.json` is the rebuild's own
|
|
1360
|
+
record — of a rig read back out of the skeleton, its `rig.archetype` the skeleton
|
|
1361
|
+
file's basename as `ingest` names it, its `pages` the new pack — and is not the
|
|
1362
|
+
input's bytes; its `spine.sha256` names the `skeleton.json` beside it, as every
|
|
1363
|
+
build's does.
|
|
1364
|
+
|
|
1365
|
+
**What it was measured on.** The seven gallery rigs, each built `--pack` under
|
|
1366
|
+
`pot`/`free` × `rect`/`polygon` and under `--page-size 1024` (two pages for two of
|
|
1367
|
+
them; two more do not fit a 1024 page and are not built), each repacked under its own
|
|
1368
|
+
settings and under `free` + `polygon`; each built without `--pack` (one page per part,
|
|
1369
|
+
pages outside the directory) and repacked; and each `pot`/`rect` build reduced to its
|
|
1370
|
+
three kinds of file, with no `skeleton.model.json`. **80 of 80 repacked green**,
|
|
1371
|
+
every region pixel-identical, every skeleton the input's bytes, at about 1.4 s a rig.
|
|
1372
|
+
|
|
1373
|
+
**What it accepts and what it refuses.** Measured over those 80 inputs and the twelve
|
|
1374
|
+
editor exports under `examples/` — each repacked directly, and each first rebuilt by
|
|
1375
|
+
rigc through `ingest --art none` and `build --atlas-in` (the shape a rigc build that
|
|
1376
|
+
carries an editor's pack has) — and, for a shape refused for one reason, with that
|
|
1377
|
+
refusal set aside to see whether the lift alone was exact:
|
|
1378
|
+
|
|
1379
|
+
| atlas shape | occurs in | lifts exactly | repack |
|
|
1380
|
+
| --- | --- | --- | --- |
|
|
1381
|
+
| rigc's own pack, any `--page-edges`/`--pack-shape`, one page or several | the 80 gallery inputs | yes | **accepted** |
|
|
1382
|
+
| one part per page, pages outside the build directory (`build` without `--pack`) | the gallery, built unpacked | yes | **accepted** |
|
|
1383
|
+
| a region turned on its page (`rotate: 90`, `270`; `180` read the same way) | three editor exports, once each | yes — the turned regions of `5-squash-and-stretch` (90) and `7-anticipation` (270) came back identical, and the selftest lays a whole rig turned and trimmed (`RPK09`) | **accepted**: the pack writes it unturned |
|
|
1384
|
+
| a region trimmed of transparent border (`offsets:`) | one editor atlas (`spineboy-run`, refused for the reasons below) | yes — `RPK09` | **accepted**: lifted at its full drawing size, transparent where trimmed |
|
|
1385
|
+
| a region two attachments share | three editor exports | yes — lifted once | **accepted** |
|
|
1386
|
+
| a page `scale:` other than 1 | all twelve editor exports (0.4, 0.5) | yes — every region the skeleton draws came back identical | **refused**: rigc's packer writes every page at scale 1, so the repacked atlas would state texels coarser than the drawings as the drawings' own size |
|
|
1387
|
+
| `pma: true` | `spineboy-run` | — | **refused**: rigc's packer writes `pma: false`, and the same bytes would be blended as straight colour |
|
|
1388
|
+
| a region the atlas names twice | `spineboy-run` (a numbered series) | — | **refused**: one part per name, and the runtime draws the first of two |
|
|
1389
|
+
| `index:` on a region | `spineboy-run` | — | **refused**: rigc's packer writes no `index` line |
|
|
1390
|
+
| `filter` other than `Linear, Linear`; `format`; `repeat`; `split`/`pad`; a `rotate:` value outside `0/90/180/270/true/false`; any other line | none here | — | **refused**: the repacked atlas would drop or contradict it |
|
|
1391
|
+
| regions no attachment of this skeleton draws | three editor atlases each serving two skeletons | yes | **refused by (a)**: they do not come back |
|
|
1392
|
+
| an editor's `skeleton.json` itself | the twelve exports, repacked directly | — | **refused by (b)** were it reached: the rebuild writes rigc's `spine` version, drops `hash`, and computes the header box again (§0.3) |
|
|
1393
|
+
|
|
1394
|
+
⚠️ **Why the loose-plate route was refused by `A08` on editor exports, and why this
|
|
1395
|
+
one is not.** Rebuilding a skeleton from loose parts after `ingest --art none` — the
|
|
1396
|
+
spec an `--atlas-in` build is made from — fails `A08_REGION_NAMES_MATCH_ATTACHMENTS`
|
|
1397
|
+
on every attachment: a `none` spec states each attachment's size and names **no
|
|
1398
|
+
`image`**, so `--images <dir>` resolves nothing (`0 part page(s)`) and no region exists
|
|
1399
|
+
for any attachment to join. Measured on `3-timing-and-spacing`: both attachments
|
|
1400
|
+
refused, exactly that way. It is the spec's art mode, not a shape of the atlas —
|
|
1401
|
+
not the region names, not stripped whitespace, not the page scale. `repack` reads
|
|
1402
|
+
the skeleton with `--art loose`, which names an `image` per attachment after the
|
|
1403
|
+
region it draws; the twelve exports rebuilt that way from their lifted regions all
|
|
1404
|
+
built green.
|
|
1405
|
+
|
|
1406
|
+
**A build written before 2.2.0.** Until 2.2.0 `build` wrote the rig's **stage** into
|
|
1407
|
+
the header's `x`, `y`, `width`, `height`; since #907/#1117 it writes the setup-pose
|
|
1408
|
+
bounding box there. Such a build repacks with every region identical and is refused by
|
|
1409
|
+
(b) on those four fields and nothing else — measured on a consumer's 35 production
|
|
1410
|
+
builds kept as the three kinds of file: (a) held on all 35, (b) refused 34 (the one
|
|
1411
|
+
that passed had been rebuilt with 2.10.1), every refusal the same four paths, for
|
|
1412
|
+
example `skeleton.width: 832 in the input, 321.00003 in the rebuild`. With
|
|
1413
|
+
`--accept-skeleton-differences` the repack is written, the four paths are printed as
|
|
1414
|
+
accepted, and the output's header is the setup-pose box, as any build of this rigc's
|
|
1415
|
+
has. This is the one case where `ingest`'s header rule gives the true stage **by
|
|
1416
|
+
construction**: the header it reads *is* the stage the build was made with, so the
|
|
1417
|
+
stage line's box is the rig's own, and `skeleton.model.json` carries it from then on.
|
|
1418
|
+
⚠️ After the repack the header is no longer the stage, and a consumer that read the
|
|
1419
|
+
header as its stage must stop: read `skeleton.model.json`'s `stage` — or the stage
|
|
1420
|
+
box #1168 adds to the build — instead. The selftest builds this shape from a gallery
|
|
1421
|
+
rig's own document (`RPK13`–`RPK15`).
|
|
1422
|
+
|
|
1423
|
+
**The stage.** `ingest`'s rules decide it, and the `stage` line says which applied.
|
|
1424
|
+
A repack learns the stage one of three ways, and each is exact in its own case:
|
|
1425
|
+
|
|
1426
|
+
- **`--stage-box <slot>`** — the bounding box a build writes for a rig that states
|
|
1427
|
+
`skeleton.stageBox` (§3.1, issue #1168), read by `ingest --stage-box`. Exact for
|
|
1428
|
+
any build that carries one, from the three files alone: the rebuilt spec asks for
|
|
1429
|
+
the same box, the skeleton comes back byte for byte, and `skeleton.model.json`'s
|
|
1430
|
+
`stage` is the declared one (`RPK17`). `ingest`'s refusals reach the caller as
|
|
1431
|
+
`rigc repack: ingest refused the skeleton: …` (exit 2, nothing written) — a slot that
|
|
1432
|
+
is not such a box, `--stage` beside it, or a `skeleton.model.json` beside the build
|
|
1433
|
+
that states another stage (`RPK19`);
|
|
1434
|
+
- **the `skeleton.model.json` beside the skeleton**, when its `spine.sha256` is the
|
|
1435
|
+
skeleton's digest. Exact for any rigc build that kept its document;
|
|
1436
|
+
- **the header's box**, when neither is there. On a build written before 2.2.0 that box
|
|
1437
|
+
is the stage itself (above), so this is exact there by construction; on a rigc build
|
|
1438
|
+
since 2.2.0 it is the **setup-pose bounding box**, not the stage, and nothing in the
|
|
1439
|
+
skeleton says which — `gallery/nod`'s `640 x 700` stage reads back as `640 x 725`
|
|
1440
|
+
without its document. The line says so, and names `--stage-box`.
|
|
1441
|
+
|
|
1442
|
+
⚠️ A build that carries a stage box, repacked **without** `--stage-box` and without its
|
|
1443
|
+
document, is written with its skeleton byte for byte — the box is transcribed as an
|
|
1444
|
+
ordinary bounding-box attachment and written back the same — but the rebuilt
|
|
1445
|
+
`skeleton.model.json`'s stage is the header's setup-pose box and carries no box
|
|
1446
|
+
(`RPK18`). Check (b) cannot see that, because the stage is not in the skeleton's bytes
|
|
1447
|
+
either way; the stage line is where it shows. Pass `--stage-box <slot>` for such a
|
|
1448
|
+
build. (`--stage` adds a box only to a skeleton that states none, which is `ingest`'s
|
|
1449
|
+
rule.)
|
|
1450
|
+
|
|
1451
|
+
**What each refusal says, and what to change:**
|
|
1452
|
+
|
|
1453
|
+
| `rigc repack: …` | exit | change |
|
|
1454
|
+
| --- | --- | --- |
|
|
1455
|
+
| `<atlas> cannot be lifted exactly — N reason(s), and nothing was written:` then one line per reason (`page … is not on disk`, `page … declares size … and … is …`, `page … states scale: …`, `… pma: true`, `region … is named twice`, `regions … differ only in case`, `region … states index: …`, `region … occupies … which leaves the … page`, `region … keeps … which does not fit inside it`, `region … is not a relative file name …`) | 1 | the atlas or its pages: put the page back, or repack the build a rigc pack wrote — a foreign pack with those shapes is rebuilt from its parts, not repacked |
|
|
1456
|
+
| `the atlas and skeleton.model.json beside it disagree — region "…" x: … in the atlas, … in the document` | 1 | one of the two was edited after the build wrote them: restore the atlas, or drop the document if the atlas is the record you mean |
|
|
1457
|
+
| `the repack lost something, so nothing was written to <out>:` then `(a) …` region lines | 1 | a region that does not come back, or a texel apart: the input atlas carries regions this skeleton does not draw (repack the build that has only its own), or the lift was not what the pack drew from |
|
|
1458
|
+
| the same, then `(b) skeleton.json: N place(s) differ from the input, which the pack does not own:` and the paths | 1 | the skeleton is not what this rigc writes from what it states. Read the paths: if the rebuild is the skeleton you want — a build written before 2.2.0, whose four header fields are its stage, is the measured case — pass `--accept-skeleton-differences`; otherwise rebuild it yourself (`ingest`, then `build`) |
|
|
1459
|
+
| `ingest found N blocker(s) …` / `ingest refused the skeleton: …` | 1 / 2 | the skeleton cannot be read back exactly (§0.3's findings); `--stage` beside a stated box is `ingest`'s refusal |
|
|
1460
|
+
| `--out <dir> is not empty …` / `is a file …` | 2 | name a new directory |
|
|
1461
|
+
| `repack takes no --<flag>: …` | 2 | drop the flag |
|
|
1462
|
+
|
|
1463
|
+
A red gate is `build`'s own report and its `rigc: N assertion(s) failed — nothing
|
|
1464
|
+
written`, followed by `rigc repack: nothing was written to <out>; the work directory
|
|
1465
|
+
… was removed`.
|
|
1466
|
+
|
|
1253
1467
|
---
|
|
1254
1468
|
|
|
1255
1469
|
## 1. The two files
|
|
@@ -1567,6 +1781,7 @@ that is a defect in this guide: report it.
|
|
|
1567
1781
|
| `referenceScale` | 4.2+ physics/scale reference: a physics constraint's `wind` and `gravity` act over it | parser default 100. Carried as stated — never rounded — and the header leaves it out at exactly 100. `skeleton.model.json` always states it, as `referenceScale` after `spec`: the number stated, or 100 when none is — the value the Spine file is read as — and rigc's posing core steps wind and gravity over it (issue #958) |
|
|
1568
1782
|
| `images` | where the editor's import looks for the part PNGs, as a path from the skeleton file | **written for you**: under `--copy-images` the `--out` directory itself, spelled `../<its basename>/` (a literal `./` is dropped by the editor on import; a named directory is kept and every part is found — measured on 4.3.23); otherwise the relative path from `--out` to the one directory the spec names every part PNG in (the rig's images directory, or the manifest's plates). A declared value is carried through verbatim — and overridden by `--copy-images`, which moved the parts. Parts spread over several directories have no single true path, so nothing is written |
|
|
1569
1783
|
| `audio` | nonessential: where the editor looks for the skeleton's audio files, as a path from the skeleton file — a string, or `null` for none | **not written unless you state it.** rigc has no audio to point at, so this is a value a spec states or does not; stated, it is carried verbatim, `null` included, because `null` is what an editor export writes when no audio folder is set (all twelve under `examples/` do) and `ingest` carries it from there. Anything but a string or `null` is a compile error naming the value |
|
|
1784
|
+
| `stageBox` | **rigc's own field**: carry the stage in the Spine files as a bounding-box attachment — `{ "slot": …, "attachment": … }`, see *The stage in the shipped files* below | absent: no box, and no byte of any build moves. Both names required; the numbers are never yours to write |
|
|
1570
1785
|
|
|
1571
1786
|
`spine` and `hash` are not yours to write: rigc emits its own version label
|
|
1572
1787
|
(`A16` re-checks it is on the 4.3 line) and inventing a hash would claim an export
|
|
@@ -1605,6 +1820,62 @@ figure's area on a figure that fills a quarter of its crop. Now:
|
|
|
1605
1820
|
|
|
1606
1821
|
You do not author the box and there is no field for it: it is a measurement of the rig.
|
|
1607
1822
|
|
|
1823
|
+
📦 **For a consumer of the Spine files: the header is not the stage.** Up to 2.1.3
|
|
1824
|
+
`skeleton.json`'s header `x`/`y`/`width`/`height` held the rig's stage; from 2.2.0 they
|
|
1825
|
+
hold the setup-pose bounding box, as the format defines them — a reader that fits a
|
|
1826
|
+
rig by taking the header for the stage is mis-sized in silence (one consumer's rig
|
|
1827
|
+
stood 1.108 times larger and shifted up, with no gate failing). The stage is in the
|
|
1828
|
+
rig spec and in `skeleton.model.json`'s `stage`, and neither ships with a Spine
|
|
1829
|
+
runtime's three files. A rig that has to carry its stage there asks for it:
|
|
1830
|
+
|
|
1831
|
+
```json
|
|
1832
|
+
"skeleton": { "width": 832, "height": 1216, "stageBox": { "slot": "stage", "attachment": "stage" } },
|
|
1833
|
+
"slots": [ …, { "name": "stage", "bone": "root", "attachment": "stage" } ]
|
|
1834
|
+
```
|
|
1835
|
+
|
|
1836
|
+
and `build` writes a `boundingbox` attachment named `stage` in the `default` skin
|
|
1837
|
+
under slot `stage`, whose four vertices are the stage's corners in Spine world —
|
|
1838
|
+
`(x, y)`, `(x + width, y)`, `(x + width, y + height)`, `(x, y + height)`, bottom-left
|
|
1839
|
+
first and counter-clockwise, y up. The consumer reads it with the runtime it already
|
|
1840
|
+
has: the slot's attachment by name (`skeletonData.defaultSkin.getAttachment(slot.index,
|
|
1841
|
+
"stage")` in spine-core), whose `vertices` are those four corners, or the slot's
|
|
1842
|
+
bounding box at the setup pose (`SkeletonBounds`), and fits the rig to that. A key
|
|
1843
|
+
the format does not define would be read by no runtime and dropped by the editor; a
|
|
1844
|
+
bounding box is returned by every runtime by slot and name, in JSON and in binary,
|
|
1845
|
+
and comes back from the editor with the same vertices (measured: a build carrying one
|
|
1846
|
+
imported into Spine 4.3.26 and exported again, the box `[0, 0, 640, 0, 640, 700, 0,
|
|
1847
|
+
700]` came back as it went, the editor adding only its own `color`).
|
|
1848
|
+
|
|
1849
|
+
- **The numbers are the stage's**, never typed: the field names where the box goes,
|
|
1850
|
+
`build` writes what is in it. Absent, no build writes one and no byte moves.
|
|
1851
|
+
- **The slot is yours to declare** in `slots` — where it stands is its place in the
|
|
1852
|
+
draw order, and a bounding box draws nothing — and **nothing else may fill it**: the
|
|
1853
|
+
box is its slot's one attachment. Its setup pose is stated the way any slot's is
|
|
1854
|
+
(the slot's `attachment`, or the motion spec's `setup`); name the box there and a
|
|
1855
|
+
reader of the slot's current attachment sees it at setup.
|
|
1856
|
+
- **The slot hangs on the root, and the root states no setup transform** — no `x`,
|
|
1857
|
+
`y`, `rotation`, `scale`, `shear`. The vertices are then the stage's own numbers,
|
|
1858
|
+
readable off the attachment without posing anything. A bone below the root is
|
|
1859
|
+
refused because the runtime spells an unrotated frame's `b` as cos 90° at its pi,
|
|
1860
|
+
−2.3e-8, once per level: on the root that residue is the one the header's own box
|
|
1861
|
+
already carries (gallery/nod's header `x` is `-0.000016`, 700 × −2.3e-8), and the
|
|
1862
|
+
gate holds the posed box through exactly that frame; below it, it compounds.
|
|
1863
|
+
- **The claim is the setup pose's.** An animation that keys the root moves the box
|
|
1864
|
+
with everything else on it; the stage is the attachment's vertices, or the box read
|
|
1865
|
+
at setup.
|
|
1866
|
+
- **The header box does not move**: a bounding box is not counted in it (above).
|
|
1867
|
+
- **`A50_STAGE_BOX_IS_THE_STAGE`** holds the box to the stage on both suppliers — the
|
|
1868
|
+
stored vertices exactly, and the posed box at setup on the header's grid — and
|
|
1869
|
+
SKIPs a rig that does not ask (§5.2). `ingest --stage-box <slot>` reads it back as
|
|
1870
|
+
the stage (§0.3).
|
|
1871
|
+
|
|
1872
|
+
Refused at compile, each naming `skeleton.stageBox`: a slot `slots` does not declare
|
|
1873
|
+
(*slot "…" is not one the rig declares*); a slot something else fills (*is also
|
|
1874
|
+
filled by …*); a slot on a bone below the root (*which is not the root*); a root that
|
|
1875
|
+
states a setup transform (*states a setup transform (rotation 90)*); a stage declared
|
|
1876
|
+
absent (*declares no stage (width: null, height: null) — a box around nothing*); a
|
|
1877
|
+
missing name (*states no "attachment"*).
|
|
1878
|
+
|
|
1608
1879
|
⭐ **A skeleton may declare no stage, and saying so is not the same as saying
|
|
1609
1880
|
nothing.** Write the pair as `null`:
|
|
1610
1881
|
|
|
@@ -6089,6 +6360,16 @@ you can reproduce by counting rows:
|
|
|
6089
6360
|
assertions and not `FAIL` lines: one assertion that finds six wrong vertices
|
|
6090
6361
|
prints six rows and is one failure here.
|
|
6091
6362
|
|
|
6363
|
+
🔢 **On the entry without the runtime the report has two rows the registry does not.**
|
|
6364
|
+
`cli_core.ts build` (and `repack`) prints the model side's own parse rules,
|
|
6365
|
+
`A00_MODEL_READ` and `A00_MODEL_REGIONS_ON_PAGES` — the first two rows of the
|
|
6366
|
+
table below — which stand where `A00_ROUNDTRIP_PARSE`'s parse stands and have no
|
|
6367
|
+
round-trip counterpart, so they are not in the registry. That is the whole of the
|
|
6368
|
+
difference between the two totals: `<N>` is 53 on the core entry and 51 on `cli.ts build`,
|
|
6369
|
+
and the core report's last line states the split — 44 rules on the model side,
|
|
6370
|
+
which are the 42 registry assertions the model side runs and those two; the 8 restated over the emitted text;
|
|
6371
|
+
and `A00_ROUNDTRIP_PARSE`, which it reports as a SKIP.
|
|
6372
|
+
|
|
6092
6373
|
🚨 **When `A00_ROUNDTRIP_PARSE` fails, read the report as a report about A00 and
|
|
6093
6374
|
nothing else.** Most of the rules read the skeleton or the atlas that A00 loads,
|
|
6094
6375
|
and with no parse there is nothing for them to look at — so they report `SKIP`
|
|
@@ -6099,7 +6380,9 @@ Fix A00 and run it again.
|
|
|
6099
6380
|
|
|
6100
6381
|
| Assertion | Profile | What tripped it, and where to fix it |
|
|
6101
6382
|
| --- | --- | --- |
|
|
6102
|
-
| `
|
|
6383
|
+
| `A00_MODEL_READ` | both | **Printed only by `cli_core.ts build` and `repack`**, where it runs before every other rule and its line leads the lines of its verdict; `cli.ts` never prints it, because there the parse is `A00_ROUNDTRIP_PARSE`'s. rigc's own reader of the model document (`readModel`) refused the `skeleton.model.json` this build compiled — the model side's parse, and like A00 the row to read first: when it fails, `A00_MODEL_REGIONS_ON_PAGES` **SKIPs** naming it and every registry rule of the model side **SKIPs** with *the model side read no document to measure*. The detail is the reader's own sentence, naming each path it refused, the value there and what it must be: `the model document: 1 problem(s): skins[0] "default".attachments["torso"]["torso"]: width is "wide", not a finite number`, `spec is "rigc-compiled/9", not "rigc-compiled/3" (…)`, `section "rig" is missing`, or `… in the rig section the model side reads its declarations from: rig.archetype is 7, not a string`. ⚠️ **No file of yours reaches it, written or edited**: the document it reads is the one the compiler just wrote in the same process, before anything is on disk, and `validate` on this entry refuses a rigc build rather than reading one (`RC36`). So a FAIL here is the compiler writing a document its own reader refuses — a rigc fault, whatever the spec says. Report it with the rig spec, the motion spec, the manifest and art if the build used them, and the command line. Not one of the registry's assertions: it is one of the two rows that make a core-entry summary total two more than `cli.ts`'s (§5.2 above) |
|
|
6384
|
+
| `A00_MODEL_REGIONS_ON_PAGES` | both | **Printed only by `cli_core.ts build` and `repack`**, run second, after `A00_MODEL_READ`. A record of the document that draws through an atlas region — a region, a mesh or a linked mesh, every frame of a `sequence` by its numbered name — resolves through a region no page of the document holds, or states that the build had no rectangle for it: `skin "default" slot "torso" placeholder "torso": resolves through the region "torso_typo", which no page of the document holds`, beside `…: the region record's atlas is null — the build had no rectangle for "torso_typo", and a runtime loading it finds no region`. **The rig spec reaches this one**: an attachment with no `image`, its `width` and `height` given, whose `path` names a region the build atlases no art for. `A08_REGION_NAMES_MATCH_ATTACHMENTS` fails on the same record with the attachment's own name, and the round trip refuses the same build at A08 and `A00_ROUNDTRIP_PARSE`. Change the attachment's `path` in the rig spec to a region the build has — a PNG under `images`, or a region of the `--atlas-in` pack — or give it the `image` it draws. Two more shapes it prints are documents a build of this rigc does not write, so on `build` they are a rigc fault, reported as for `A00_MODEL_READ`: `the document is rigc-compiled/1, which states no pages, …` and `the model document's sequences: … start is "x", not a whole number of at least 0`. **SKIP** when `A00_MODEL_READ` refused the document (it names that row), and when no record draws through a region — a rig with no region, mesh or linked-mesh attachment. Not one of the registry's assertions, for the reason the row above gives |
|
|
6385
|
+
| `A00_ROUNDTRIP_PARSE` | both | `spine-core` could not parse the skeleton or the atlas. Almost everything else in the report is downstream of this one — fix it first. When it fails, every rule that reads the loaded skeleton or the loaded atlas reports **SKIP** saying so by name, so the row count stays at the full registry and the summary's *measured* figure tells you how little was actually asked. ⚠️ **Two rules run before it and can be upstream of it**: `A31_DRAW_ORDER_OFFSETS_RESOLVE`, because a bad draw-order key makes the loader spin rather than return, and `A08_REGION_NAMES_MATCH_ATTACHMENTS`, because a `path` naming no region makes it throw. The round trip is still attempted either way; when the loader refuses a path A08 has already refused, this row **defers** to A08 by name instead of restating the miss in the parser's poorer words Under `cli_core.ts build`, which links none of spine-core, it is a **SKIP** naming spine-core — the one rule of the registry that entry does not run. The entry that links spine-core runs it on `build` and `validate`: installed, the same `rigc` once `@esotericsoftware/spine-core` is installed beside the package; from a clone, `bun cli.ts` (`RC28`). |
|
|
6103
6386
|
| `A01_NO_LEGACY_TOPLEVEL_CONSTRAINT_ARRAYS` | both | a 4.1/4.2-shaped `ik`/`transform`/`path`/`physics`/`slider` array. rigc emits the 4.3 `constraints` array, so this normally means hand-edited JSON Under `cli_core.ts build` the same clause runs over the text the emitter wrote and prints this line in the same words (`RC28`). |
|
|
6104
6387
|
| `A02_NO_BONE_TRANSFORM_KEY` | both | `bone "…" uses "transform", the key 4.0 and 4.1 spelled; 4.2 and 4.3 spell it "inherit"` — rename it `inherit` in the rig spec Under `cli_core.ts build` the same clause runs over the text the emitter wrote and prints this line in the same words (`RC28`). |
|
|
6105
6388
|
| `A03_REGION_WIDTH_HEIGHT_FINITE` | both | a region loaded `NaN` or a non-positive size — the attachment has no `image` and no `width`/`height`. **SKIP** when the skeleton carries no region attachment |
|
|
@@ -6149,6 +6432,7 @@ Fix A00 and run it again.
|
|
|
6149
6432
|
| `A47_IK_CONSTRAINT_NOT_MUTED_THROUGHOUT` | both | an ik constraint resting at `mix` 0 that no animation keys **away from 0** (§4.9, §4.12). `IkConstraint.update` returns on `mix === 0`, so it sits in the update cache and moves nothing. The keys are read the way `A23`/`A36`/`A37` read theirs — every value the loaded timeline poses on its `mix` channel, Bezier samples included — so a timeline keying 0 only is no rescue: [measured] it poses every bone exactly where the same rig with no constraint does. Live is the runtime's `!== 0`, so a negative mix is not refused. `ik constraint "C" has mix 0 at setup and none of the 1 animation keys its mix above 0; update() returns on mix 0, so "upper" never reaches for "goal" — rest it above 0, or key its mix above 0 in an animation, or declare that the consumer drives its mix, in the rig spec as invariants.consumerDrivenMix: [{ "constraint": "C", "type": "ik", "why": … }]`. A constraint the rig spec declares in `invariants.consumerDrivenMix` (§3.7) is not measured, and one the file also switches on is refused: `ik constraint "C" is declared in the rig spec as invariants.consumerDrivenMix, and the file already switches it on — … — so the declaration exempts nothing; drop the entry`. **SKIP** when the skeleton declares no ik constraint, and **SKIP by name** when every ik constraint it declares is declared consumer-driven: `every ik constraint here is declared in the rig spec as invariants.consumerDrivenMix, so its mix is the consumer's to set and nothing in this file shows it moving — "C" (why: …): it rests muted and none of the 1 animation keys its mix above 0`. With a live one beside it the rule measures that one and the stats line names the declared (`ikConsumerDriven`) |
|
|
6150
6433
|
| `A48_TRANSFORM_CONSTRAINT_NOT_MUTED_THROUGHOUT` | both | a transform constraint none of whose mixes **for a property it drives** is away from 0 at setup or on any value an animation poses (§4.10, §4.12), or one whose `properties` name no `to` at all. A property is applied only when its own mix `!== 0`, and a key that omits a mix reads it as 1, so the six-mix early return of `TransformConstraint.update` would take a key of `mixRotate: 0` alone as a rescue — [measured] that key, and one keying `mixX` 1 on a rotate-only constraint, pose every bone exactly where no constraint does. A negative mix runs, and five transforms in the editor's example exports rest at −1. `transform constraint "C" drives rotate and has mixRotate 0 at setup, and none of the 1 animation keys its mix above 0; a mix is read only for a property the constraint drives, and update() skips each one at 0, so nothing ever moves "follower" — rest mixRotate above 0, or key its mix above 0 in an animation, or declare that the consumer drives its mix, in the rig spec as invariants.consumerDrivenMix: [{ "constraint": "C", "type": "transform", "why": … }]`. A constraint the rig spec declares in `invariants.consumerDrivenMix` (§3.7) is not measured, and one the file also switches on is refused as `A47`'s is; one that drives no property is refused with its own sentence declared or not, since no mix it carries is read by anybody. **SKIP** when the skeleton declares no transform constraint, and **SKIP by name** when every transform constraint it declares is declared consumer-driven: `every transform constraint here is declared in the rig spec as invariants.consumerDrivenMix, so its mix is the consumer's to set and nothing in this file shows it moving — "C" (why: …): it rests muted and none of the 1 animation keys its mix above 0`. With a live one beside it the stats line names the declared (`transformConsumerDriven`) |
|
|
6151
6434
|
| `A49_PACKED_FOOTPRINTS_DO_NOT_OVERLAP` | renderer | two regions on one page whose rectangles overlap **and** whose footprints overlap — so one drawing samples texels that are the other's. A region's **footprint** is what samples it: a region a mesh names (its `path`, else its name, every frame of a `sequence` included) draws the mesh's hull loop and its triangles, its UVs placed on the page as `MeshAttachment.computeUVs` places them (a **linked mesh** draws its source's); a region a region attachment names, or that nothing names, draws its whole rectangle — and so does a mesh region whose UVs leave 0..1, whose hull or triangles do not index its vertices, or that is turned by `rotate:`, and every region when a `sequence` cannot be read. For two rectangles that is exactly the clause `A06` held until issue #1099 — an editor alias, a sequence frame, two rectangles moved over the same texels are refused as they were — and it accepts what `--pack-shape polygon` writes: a neighbour inside a mesh's rectangle where the hull is not (§0.1). **Touching is not overlapping**: interiors are compared, every comparison `FOOTPRINT_SLACK` (1e-6 texels) in favour of apart. `regions "ear_l" (341,696 112x420) and "mouth" (332,709 96x48) overlap on page "skeleton.png", and so do their footprints — "ear_l" draws the hull of mesh "ear_l" (skin "default", slot "ear_l"), "mouth" draws its whole rectangle, which region attachment "mouth" (skin "default", slot "mouth") draws; what they draw overlaps by 1916.5512 texel(s) of area inside 347,709 81x48. One drawing samples texels that are the other's: move one region clear of the other's footprint, or pack the page again` — `gallery/nod`'s polygon pack with `mouth` moved onto a triangle of the `ear_l` mesh (`PK88`). Nothing about the **padding** is read — the atlas and the model document do not state it — and the `2 · padding` the packer keeps is the packer's own (`PK81`). A renderer rule because two regions over the same texels is what an editor's packer writes for an alias or a sequence: 49 pairs on four of the ten atlases in `examples/`. **SKIP** when the atlas declares no page or no region, and when no page carries two regions — every unpacked build |
|
|
6435
|
+
| `A50_STAGE_BOX_IS_THE_STAGE` | both | the stage box a rig asked for (`skeleton.stageBox`, §3.1) is not the stage the model document states. Read on the data a consumer reads and on the pose it reads: the slot carries, in the `default` skin under the asked name, an unweighted bounding box of four vertices whose **stored** vertices are the stage's corners exactly — `(x, y)`, `(x + w, y)`, `(x + w, y + h)`, `(x, y + h)`, as the float32 the runtime loads — and whose **world** vertices at the setup pose (constraints applied, no physics, no skin set) are those corners as the runtime poses them on an unrotated root, compared on the header's 1e-6 grid at float32. `the stage box (slot "stage", attachment "stage"): vertex 1 is stored as (65, 0) and the stage's corner is (64, 0) — the stage 0,0 64x48, bottom-left first and counter-clockwise`; a root moved at setup reads *vertex 0 poses at (1, 0) at the setup pose and the stage's corner poses at (0, 0) on an unrotated root, on the header's grid — something moves the box's bone at setup (a constraint, or a pose the root does not state)*; a slot the skeleton lacks, no attachment of that name, another type, or a weighted box are each named too. Through spine-core it reads `findSlot`, `defaultSkin.getAttachment` and `computeWorldVertices` on a fresh skeleton after `setupPose()` and `updateWorldTransform(Physics.none)`; on the model side the document's record and rigc's core poser, held to spine-core's poses at tolerance 0 (`PG06`, `PG07`). **SKIP** where nothing asks: *nothing asks for a stage box: no rigc-compiled/3 model document beside this skeleton states one …* — a rig without the field, and any skeleton with no `/3` document beside it, an export among them |
|
|
6152
6436
|
|
|
6153
6437
|
`both ◑` marks a mixed assertion: its validity half always runs and its policy
|
|
6154
6438
|
clauses are gated by profile.
|
package/docs/INGEST.md
CHANGED
|
@@ -525,6 +525,20 @@ at the policy rather than implying the file was read.
|
|
|
525
525
|
`(reported)`, so no score reads them and an absurd box is green nearly everywhere.
|
|
526
526
|
⛔ **The flag is refused beside a box the file states** — two sources for one value,
|
|
527
527
|
both named, and the file is the record of what was measured;
|
|
528
|
+
📦 **`--stage-box <slot>` reads the stage a rig carried in its Spine files** (issue
|
|
529
|
+
#1168): a rig whose spec states `skeleton.stageBox` ([AUTHORING §3.1](AUTHORING.md))
|
|
530
|
+
ships its stage as a bounding box in that slot, and a caller who has only
|
|
531
|
+
`skeleton.json`, the atlas and its pages names the slot. The box's four corners are
|
|
532
|
+
read as the stage in place of the header's box, and the rebuilt spec asks for the same
|
|
533
|
+
box rather than transcribing it, so `build` writes it again from the stage — a rigc
|
|
534
|
+
build carrying one rebuilds byte for byte. Only a box `build` could write back is read:
|
|
535
|
+
a slot the skeleton lacks, a slot on a bone other than an unmoved root, a slot holding
|
|
536
|
+
anything but one bounding box in the `default` skin, a box not four unweighted corners
|
|
537
|
+
of one axis-aligned rectangle — each is refused by name, as is `--stage` beside the
|
|
538
|
+
flag, and a model document beside the skeleton stating another stage. A box `build`
|
|
539
|
+
spells differently (its corners in another order, the editor's `color`) is the lossy
|
|
540
|
+
`STAGE_BOX_REWRITTEN`. Without the flag no slot is read as the stage because of its
|
|
541
|
+
name, and the box is transcribed as the attachment it is;
|
|
528
542
|
- **each animation's duration** — the format has no such field. The largest key time
|
|
529
543
|
is used, stated in the motion spec's `note`, and recorded as a finding per
|
|
530
544
|
animation. Edit it if you know the real number.
|
|
@@ -571,7 +585,9 @@ under, whether it changes the exit code, what it means, and what to do about it.
|
|
|
571
585
|
rather than rejected — so an exit of 1 means *a blocker was recorded*, and both specs
|
|
572
586
|
are on disk either way. (The one thing `ingest` does refuse outright is an option that
|
|
573
587
|
contradicts the file, which is not a finding: `--stage` beside a box the skeleton
|
|
574
|
-
declares
|
|
588
|
+
declares — and, under `--stage-box <slot>`, a slot that holds no box `build` could
|
|
589
|
+
write back, `--stage` beside it, or a model document beside it stating another
|
|
590
|
+
stage (§2.0's stage box, below).)
|
|
575
591
|
|
|
576
592
|
| code | gutter | exit | what it means | what to do |
|
|
577
593
|
| --- | --- | --- | --- | --- |
|
|
@@ -602,6 +618,7 @@ declares.)
|
|
|
602
618
|
| `SLOT_FIELD` | `BLOCK` | 1 | as `BONE_FIELD`, on a slot | as `BONE_FIELD` |
|
|
603
619
|
| `SLOT_TIMELINE` | `BLOCK` | 1 | a slot timeline the motion spec has no track for. The spec has a track for all six the format has, so what still reaches this line is a name **outside** the format — `sequence` written on a slot rather than an attachment is the likeliest — and the detail says so: the runtime's own reader throws `Invalid timeline type for a slot` on it, so no player loads that file either. The detail names the tracks the spec does have and, when the format has any it lacks, those too, **both read off the tables**. `rgb` and `alpha` are carried under their own names and on their own key times, never folded into one `rgba` — that would state each channel at the other's key times, a value nobody keyed | fix the timeline's name, or accept that the rebuild plays nothing there |
|
|
604
620
|
| `SPEC_REFUSED` | `BLOCK` | 1 | the specs were written and **rigc's own parser refuses one of them** — the detail carries that refusal word for word, after the file and the spec it is about. It is the one finding that is not about a single construct: it is whatever `parseRigSpec` or `parseMotionSpec` names, from a shape the format holds and the spec cannot say (a constraint that is `skinRequired` under no skin) to a defect in this decompiler | read the quoted sentence against the skeleton: it names the object. Both specs are on disk for exactly that, and `build` will refuse them until the shape has a spelling — [AUTHORING §5.1](AUTHORING.md) is the list of what a parser says |
|
|
621
|
+
| `STAGE_BOX_REWRITTEN` | `LOSS` | 0 | under `--stage-box <slot>`: the bounding box read as the stage states its corners in another order than `build` writes them (bottom-left first, counter-clockwise), or a `color` — the editor writes one on every box it exports. The rebuilt spec asks for the box (`skeleton.stageBox`), and `build` writes it from the stage, so the rebuild carries the same rectangle spelled `build`'s way. The detail names what changes | nothing. The stage is the same four numbers; only the box's spelling moves |
|
|
605
622
|
| `TIMELINE_FIELD` | `BLOCK` | 1 | a key field on a bone, path, physics or slider timeline that is not part of that timeline's shape. On an `inherit` key that includes a `curve`: the parser reads `time` and `inherit` there and nothing else, and `build` refuses a curve on that track by name | check the spelling; the field is dropped from the rebuilt key |
|
|
606
623
|
| `TIMELINE_KEY_RESTATED` | `LOSS` | 0 | an editor omits a channel that equals the parser's default, and the motion spec's `v` is positional, so the omission is written out at that default **in the spec** — and the line is printed only where the **file** will carry it too. The emitter leaves a channel out wherever it is the one the parser reads without it ([AUTHORING §10.6c](AUTHORING.md)), so on every key kind with a row the rebuild is the source's own text and nothing is said — measured on the twelve exports, 0 tracks print it. What still prints it is a timeline whose keys have no row (`shearx`, `alpha`, path `spacing`, …), and an `inherit` key: one that omits the mode is written as `normal` — the parser's default — and one spelled with a capital first letter (`NoScale`) as the editor's `noScale`, the same mode either way | nothing. The same values the runtime reads, spelled out — a larger file and the same animation |
|
|
607
624
|
| `TRANSFORM_KEY_FIELD` | `BLOCK` | 1 | as `IK_KEY_FIELD`, on a `transform` timeline | as `IK_KEY_FIELD` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "AI-authored Spine 2D rigging and animation, verified before it is written. rigc compiles a rig spec into Spine 4.3 skeleton data, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Output imports into the Spine editor. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|