spine-rigc 0.20.3 → 0.21.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 +4 -4
- package/docs/AUTHORING.md +148 -36
- package/docs/FACE.md +53 -7
- package/package.json +1 -1
- package/src/compile.ts +168 -141
- package/src/keys.ts +117 -0
- package/src/motion.ts +143 -15
- package/src/rig.ts +445 -117
- package/src/types.ts +62 -8
- package/src/validate.ts +106 -1
package/README.md
CHANGED
|
@@ -508,9 +508,9 @@ work on any frames you have, and `bench` is a repository workflow that needs a c
|
|
|
508
508
|
and `bun run fetch-examples`. The reasoning behind all three is in
|
|
509
509
|
[the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
|
|
510
510
|
|
|
511
|
-
`build` and `validate` both default to `--profile spine` — the
|
|
511
|
+
`build` and `validate` both default to `--profile spine` — the 27 validity rules, which
|
|
512
512
|
ask *is this valid Spine 4.3 that any runtime plays correctly?* `--profile spine-html`
|
|
513
|
-
adds all
|
|
513
|
+
adds all 42: the other 15 are one renderer's policy and one canvas budget's, and they
|
|
514
514
|
fire on perfectly correct editor-produced Spine data, so reach for that profile when
|
|
515
515
|
you are shipping into *that* project rather than to be thorough. A report always names
|
|
516
516
|
the profile it ran and lists what that profile left out.
|
|
@@ -576,7 +576,7 @@ letting `A17` blame the editor for the harness's own doing.
|
|
|
576
576
|
| 📥 **[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 |
|
|
577
577
|
| 🤖 **[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 |
|
|
578
578
|
| 🔬 **[docs/SPEC_COVERAGE.md](docs/SPEC_COVERAGE.md)** | Spine 4.3's full export surface against what rigc emits and what the official examples measurably use, with the ordered gap list |
|
|
579
|
-
| 🎓 **[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
|
|
579
|
+
| 🎓 **[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 42 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 |
|
|
580
580
|
| 📋 [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 |
|
|
581
581
|
| 🗺️ [ROADMAP.md](https://github.com/firejune/rigc/blob/main/ROADMAP.md) | where this is going, and where it has been. What 1.0 has to mean before the number is claimed — conditions rather than a feature list, because direction here comes from what users hit |
|
|
582
582
|
|
|
@@ -632,7 +632,7 @@ quality."* All six, with their verdicts, are in
|
|
|
632
632
|
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
|
633
633
|
|
|
634
634
|
The whole dossier — the yardstick, `diff` and `check` and what neither of them can
|
|
635
|
-
see, every rung, the run viewer, the
|
|
635
|
+
see, every rung, the run viewer, the 42 assertions and the selftest behind them — is
|
|
636
636
|
[docs/BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
|
|
637
637
|
Live rung status is
|
|
638
638
|
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
package/docs/AUTHORING.md
CHANGED
|
@@ -163,7 +163,7 @@ What the flags mean:
|
|
|
163
163
|
| `--atlas-in` | `build` only: 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:`; the atlas is re-emitted into `--out`, re-anchored — **§0.2** |
|
|
164
164
|
| `--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) |
|
|
165
165
|
| `--manifest` | a cut manifest. Only for a rig with **measured art** behind it; a foreign skeleton has none |
|
|
166
|
-
| `--profile` | `spine` = the
|
|
166
|
+
| `--profile` | `spine` = the 27 validity rules (**the default**) · `spine-html` = all 42, opt-in |
|
|
167
167
|
| `--candidate` | `check`, `bench`, `render`, `preview`, `chainfit` and `vote` only: a **compiled** artifact — the directory `build --out` wrote, or a `skeleton.json` path. `--atlas <path>` names the atlas when it does not sit beside the skeleton. **`vote` is the one command that takes it more than once** — repeat it 2–4 times, one per pane, labelled A, B, C, D in the order given; everywhere else a repeat is a typo and is refused |
|
|
168
168
|
| `--animation` | `render`, `preview` and `vote` only: which animation to show. The default is **every** one for `render`, the **first** for `preview`, 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`, so is a name that only *some* candidates have |
|
|
169
169
|
| `--record` | `vote` only: a saved vote to check against its ballot and append to the ledger, instead of writing a ballot. This is the command's second mode; it takes no `--candidate` |
|
|
@@ -566,7 +566,8 @@ behind it writes literal `x`/`y` instead.
|
|
|
566
566
|
|
|
567
567
|
**R10 — The `animations` object is keyed in the editor's order, not in yours, and
|
|
568
568
|
names that have no one order are refused.** Declare animations in whatever order
|
|
569
|
-
reads best; the emit keys them
|
|
569
|
+
reads best; the emit keys them the way the Spine editor does — **natural and
|
|
570
|
+
case-insensitive**. This is the one place rigc
|
|
570
571
|
reorders anything you wrote, and it is not cosmetic: a `slider`'s animation is a
|
|
571
572
|
**name** in JSON and an **ordinal** in the format's binary half, so an editor that
|
|
572
573
|
re-sorts the object repoints every slider whose animation moved index — silently,
|
|
@@ -575,25 +576,41 @@ in a file that still parses and still gates green (§3.5.2,
|
|
|
575
576
|
animation's own body is byte-identical either way, and every other collection is
|
|
576
577
|
emitted in the order you gave it.
|
|
577
578
|
|
|
578
|
-
⚠️ **
|
|
579
|
-
case-insensitive
|
|
579
|
+
⚠️ **What is measured about that comparator, and what is not.** The editor sorts
|
|
580
|
+
natural and case-insensitive — measured, two rigs, one axis each:
|
|
580
581
|
`Turn, sweep, wave` came back `sweep, Turn, wave`, and `turn10, turn2, zoom` came
|
|
581
582
|
back `turn2, turn10, zoom` ([#539](https://github.com/firejune/rigc/issues/539)).
|
|
582
|
-
|
|
583
|
-
**
|
|
584
|
-
|
|
583
|
+
But "natural and case-insensitive" is a **family** of comparators, not one, and
|
|
584
|
+
**four** of its choices have never been measured. rigc emits the order every
|
|
585
|
+
member of that family agrees on, and **refuses the sets where one of the four
|
|
586
|
+
would decide**, naming the pair. So the rule you have to hold is about *names*,
|
|
587
|
+
and it is four things:
|
|
585
588
|
|
|
586
589
|
| Do not let two animation names differ | Because | Instead |
|
|
587
590
|
| --- | --- | --- |
|
|
588
|
-
| by **case**
|
|
589
|
-
| by a **number
|
|
591
|
+
| by **case alone** (`Turn` against `turn`) | they fold together, so only a tie-break separates them, and nobody has measured which way it breaks | pick one case for all of them, or change a letter |
|
|
592
|
+
| by a **number written two ways** (`turn01` against `turn1`) | `01` and `1` are one number twice; shorter-first, longer-first and lexicographic are all real tie-breaks | write the number one way — with leading zeros or without, but not both |
|
|
593
|
+
| by a **digit run against a word** (`1turn` against `turn`) | comparators differ on whether a number sorts before a word | rename so a run of digits is never compared against a word |
|
|
590
594
|
| by a **separator** — anything that is neither a letter nor a digit (`wave_x` against `wavea`, `wave` against `wave-`) | a collator may treat `-` or a space as ignorable, and `_` sits *between* `Z` and `a`, so folding up and folding down order it oppositely | rename so the first character that differs is a letter or a digit |
|
|
591
595
|
|
|
592
|
-
⭐ **Capitals and
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
596
|
+
⭐ **Capitals and numbered series are not what is refused** — only pairs one of
|
|
597
|
+
those four decides. `Sweep, Turn, Wave, Zoom02, Zoom10` builds, and so does
|
|
598
|
+
`shot1 … shot12`: every member of the family puts each of those sets in one
|
|
599
|
+
order, and that order is what rigc emits. A numbered series that crosses 9 → 10 is
|
|
600
|
+
keyed **1, 2, … 9, 10, 11, 12**, which is what the editor does with it — and is
|
|
601
|
+
not what a codepoint sort does.
|
|
602
|
+
|
|
603
|
+
✅ **This list had two more rows before
|
|
604
|
+
[#543](https://github.com/firejune/rigc/issues/543), and both were artefacts of
|
|
605
|
+
the emit rather than facts about the editor.** rigc used to key `animations`
|
|
606
|
+
**codepoint-ascending** and refuse every pair codepoint and the editor could order
|
|
607
|
+
differently — which refused a pair that folds the other way (`Turn` against
|
|
608
|
+
`sweep`) and a pair of digit runs of unequal width (`turn10` against `turn2`).
|
|
609
|
+
Those are the only two name sets anybody has ever put through the editor and read
|
|
610
|
+
back, so the tool was refusing precisely the pairs it knew the most about, and its
|
|
611
|
+
only repair was *rename* — the one repair a transcription cannot take. Emitting a
|
|
612
|
+
member of the family instead moves no byte on any set the old rule accepted; it
|
|
613
|
+
just stops refusing the ones it did.
|
|
597
614
|
|
|
598
615
|
---
|
|
599
616
|
|
|
@@ -1377,6 +1394,14 @@ carrying here:
|
|
|
1377
1394
|
- A physics constraint's five components all default to 0, so one that names none of
|
|
1378
1395
|
them parses cleanly and does nothing at all. rigc refuses it up front, and `A23`
|
|
1379
1396
|
catches it from the other side.
|
|
1397
|
+
- An **ik** and a **physics** constraint both carry `ScaleYMode` under the key
|
|
1398
|
+
`scaleY`, spelled `"none"`, `"uniform"` or `"volume"`. It is an enum resolved by
|
|
1399
|
+
`Utils.enumValue`, so only the first letter's case is free and an unrecognised
|
|
1400
|
+
name is assigned as `undefined` with no error; rigc checks it, like the three
|
|
1401
|
+
path modes below. ⚠️ `src/rig.ts` called the physics one **`scaleYMode`** until
|
|
1402
|
+
issue #545 — the runtime's field name rather than the format's key — and nothing
|
|
1403
|
+
read it, so a spec that wrote `scaleYMode` set no mode and said nothing. A rig
|
|
1404
|
+
that still writes it is now refused by name, with `scaleY` beside it.
|
|
1380
1405
|
|
|
1381
1406
|
Every constraint may also carry `skin: true`, which makes it run only under the skin
|
|
1382
1407
|
that lists it — see §3.4.1, and note that the flag alone does nothing.
|
|
@@ -1482,25 +1507,32 @@ animation now stands at the position. `gallery/look` went into a licensed editor
|
|
|
1482
1507
|
`sweep, tilt, turn` with **`yaw -> "sweep"`**: a file that parses, gates green and
|
|
1483
1508
|
applies the wrong animation. ⭐ Its second slider is what named the mechanism
|
|
1484
1509
|
rather than a second casualty — `tilt` survived because it sat at index 1 in both
|
|
1485
|
-
orderings. rigc now emits animations
|
|
1510
|
+
orderings. rigc now emits animations in the editor's own order so its re-sort
|
|
1486
1511
|
moves no index ([#535](https://github.com/firejune/rigc/issues/535)); on the same
|
|
1487
1512
|
rig through the same editor that restored `yaw -> "turn"` and took the
|
|
1488
1513
|
re-rendered mean absolute error from 10.4655 / 8.4961 / 8.7140 down to
|
|
1489
|
-
0.3035 / 0.0769 / 0.0588.
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
`
|
|
1502
|
-
|
|
1503
|
-
|
|
1514
|
+
0.3035 / 0.0769 / 0.0588. (`look`'s three names are ones a codepoint sort orders
|
|
1515
|
+
identically, which is what rigc emitted when that trip was measured.)
|
|
1516
|
+
|
|
1517
|
+
⚠️ The editor's comparator is natural and case-insensitive
|
|
1518
|
+
([#539](https://github.com/firejune/rigc/issues/539)), and four of its choices are
|
|
1519
|
+
unmeasured — so the emit is that family's order for names none of the four
|
|
1520
|
+
decides, and the rest are a compile error. **R10** has the four shapes to avoid.
|
|
1521
|
+
|
|
1522
|
+
✅ **What that repair does not reach is a compile error now, not a hazard.** This
|
|
1523
|
+
paragraph used to say that names a codepoint sort and a friendlier one disagree
|
|
1524
|
+
about — `Turn` / `turn`, `turn2` / `turn10` — were where *the hazard returns*, and
|
|
1525
|
+
that nobody had round-tripped such a pair. Somebody has: `Turn, sweep, wave` came
|
|
1526
|
+
back `sweep, Turn, wave` and `turn10, turn2, zoom` came back `turn2, turn10, zoom`
|
|
1527
|
+
([#539](https://github.com/firejune/rigc/issues/539)). ⇒ rigc no longer leaves
|
|
1528
|
+
that to naming discipline — and since
|
|
1529
|
+
[#543](https://github.com/firejune/rigc/issues/543) it does better than refusing
|
|
1530
|
+
those two, because they are the two sets the editor's answer is **known** for:
|
|
1531
|
+
both are emitted in the order it returned. What is still a compile error is the
|
|
1532
|
+
set whose order turns on one of the four unmeasured choices, printed with both
|
|
1533
|
+
names, which of them decides it, and the rename that settles it. What changed is
|
|
1534
|
+
the price of forgetting: a build that stops, rather than a slider that silently
|
|
1535
|
+
applies the wrong animation.
|
|
1504
1536
|
|
|
1505
1537
|
⚠️ **The fields of the model you did not choose are refused, not ignored.** The
|
|
1506
1538
|
parser reads `time` only in the bone-less branch and `property`/`from`/`to`/`scale`/
|
|
@@ -1770,7 +1802,8 @@ is not an array. Every field is optional and each is the payload a firing
|
|
|
1770
1802
|
|
|
1771
1803
|
Optional with one exception, and only meaningful for rigc's own formations:
|
|
1772
1804
|
`meshSlots` and `meshTriangles` (the two halves of the mesh budget `A13` measures
|
|
1773
|
-
against), `axisBone`, `massBone`, `detached`, `deformMayFold`.
|
|
1805
|
+
against), `axisBone`, `massBone`, `detached`, `deformMayFold`, `editorRoundTrip`.
|
|
1806
|
+
Nothing in skeleton
|
|
1774
1807
|
JSON records that a
|
|
1775
1808
|
bone carries a cut's axis or that a parentage is forbidden, so the rig spec says it
|
|
1776
1809
|
and the validator's archetype assertions read it. **An assertion whose field is
|
|
@@ -1808,6 +1841,33 @@ and the entry is gone. ⇒ An exemption whose `why` reads *"known defect, see
|
|
|
1808
1841
|
against a fix, not a fix — and the thing that made it repayable was A39
|
|
1809
1842
|
measuring the ceiling the art could actually take.
|
|
1810
1843
|
|
|
1844
|
+
🎬 **`editorRoundTrip` is the one field here that names a CONSUMER rather than a
|
|
1845
|
+
shape.** Write `"editorRoundTrip": true` when this rig is authored to come back
|
|
1846
|
+
out of the Spine editor — imported, hand-edited, exported — and
|
|
1847
|
+
`A41_PHYSICS_SURVIVES_EDITOR_ROUND_TRIP` refuses a physics constraint driving a
|
|
1848
|
+
component that editor cannot hold. `true` is the only accepted value; a `false`
|
|
1849
|
+
would be a key nothing reads ([#545](https://github.com/firejune/rigc/issues/545)).
|
|
1850
|
+
|
|
1851
|
+
⚠️ **Leaving it out is not a weaker gate, and this is the part worth reading.**
|
|
1852
|
+
rigc's output is not wrong here: a physics constraint driving `rotate` is valid
|
|
1853
|
+
Spine 4.3 that every runtime plays — a cowlick, a tail, an ear — so refusing it by
|
|
1854
|
+
default would be refusing correct data on behalf of a pipeline nobody declared.
|
|
1855
|
+
What a rig that says nothing gets instead is the **SKIP**, and the SKIP names the
|
|
1856
|
+
constraint and the component:
|
|
1857
|
+
|
|
1858
|
+
```
|
|
1859
|
+
SKIP A41_PHYSICS_SURVIVES_EDITOR_ROUND_TRIP: the rig "look" does not declare `invariants.editorRoundTrip`, so nothing here is gated against the Spine editor. What is here: physics "whip" drives rotate, and the editor's physics model holds x and y only, so a round trip returns that constraint driving nothing at all (issue #540)
|
|
1860
|
+
```
|
|
1861
|
+
|
|
1862
|
+
📏 **Measured, not inferred** ([#540](https://github.com/firejune/rigc/issues/540)):
|
|
1863
|
+
three rigs, twelve constraints, predictions written before the round trip. A lone
|
|
1864
|
+
`y` came back and `x` + `y` together came back — the rule is membership, not arity
|
|
1865
|
+
— while a lone `rotate`, a lone `scaleX` and a lone `shearX` each came back driving
|
|
1866
|
+
**no component at all**, and neither `scaleY` mode rescues `scaleX`. So the fix for
|
|
1867
|
+
a refusal is to drive the constraint in `x`/`y`, or to drop the declaration if this
|
|
1868
|
+
rig never goes near the editor. ⇒ `A23_PHYSICS_CONSTRAINT_EFFECTIVE` is the same
|
|
1869
|
+
loss seen from the far side: it is what fires on the file the editor hands **back**.
|
|
1870
|
+
|
|
1811
1871
|
🚫 **Do not reach for it to cover a part you have faded out.** A key whose slot
|
|
1812
1872
|
draws no pixels at that key's own time is already passed over — `A39` measures
|
|
1813
1873
|
that and says so (§4.11, and the `skipped` line in the `DEFORM` block). Declaring
|
|
@@ -3220,6 +3280,38 @@ compiled, and ask only what the one file in front of them can answer. Every
|
|
|
3220
3280
|
one per message. (The rig spec's parser predates the convention and its messages
|
|
3221
3281
|
are prose, so they sit in the second table with everything else.)
|
|
3222
3282
|
|
|
3283
|
+
🚨 **A key neither format has is refused by name, in both files.** Not a row in
|
|
3284
|
+
the table below, because it is not about one field: every object in a rig spec and
|
|
3285
|
+
in a motion spec is checked against the keys its shape actually owns, and a key
|
|
3286
|
+
outside that set stops the build. Issue #545 is why — before it, such a key was
|
|
3287
|
+
never looked at, never mentioned and never emitted, and the build exited 0 with
|
|
3288
|
+
every assertion green. Four keys planted into one physics constraint all vanished,
|
|
3289
|
+
and no line of output named any of them.
|
|
3290
|
+
|
|
3291
|
+
```
|
|
3292
|
+
rigc compile error: rig.json: constraint "ctl" (physics) has 4 keys this compiler
|
|
3293
|
+
does not read: "scaleYMode" (did you mean "scaleY", "scaleX"?), "scale" (did you
|
|
3294
|
+
mean "scaleX", "scaleY"?), "wobble", "ROTATE" (did you mean "rotate"?). Nothing
|
|
3295
|
+
reads such a key, so it would be dropped from the emitted skeleton in silence —
|
|
3296
|
+
fix the spelling or remove it. Known here: bone, damping, dampingGlobal, fps, …
|
|
3297
|
+
```
|
|
3298
|
+
|
|
3299
|
+
Read it as a **repair**, not a rule: every stray key on that object is named at
|
|
3300
|
+
once, the closest known spellings come with it (the search is case-insensitive, so
|
|
3301
|
+
a real key in the wrong case leads the list), and the shape's whole key set is
|
|
3302
|
+
printed after. What it will *not* do is guess — a key four edits from anything gets
|
|
3303
|
+
no suggestion, only the set.
|
|
3304
|
+
|
|
3305
|
+
⚠️ There is **no forward-compatibility escape**, and no `note` field except where
|
|
3306
|
+
one is listed: a rig spec's root, a motion spec's root, an animation, and a
|
|
3307
|
+
`physics` tuning entry. Prose anywhere else has to go in a document, because a key
|
|
3308
|
+
the compiler tolerates is a key it cannot distinguish from one you meant it to
|
|
3309
|
+
read. (The **cut manifest** is deliberately outside this: it is the record of the
|
|
3310
|
+
pipeline that produced the art as much as an input, it carries fields the compiler
|
|
3311
|
+
states outright that it does not read — `roi` — and the fixtures in this repository
|
|
3312
|
+
already give it `note` and `archetype`. It has no shape parse at all, which is the
|
|
3313
|
+
same hole issue #307 closed for the motion spec.)
|
|
3314
|
+
|
|
3223
3315
|
| Key | Refused when it is not | Why the shape matters |
|
|
3224
3316
|
| --- | --- | --- |
|
|
3225
3317
|
| the file itself | a JSON object | the version row below would otherwise report a missing `spec` tag in a file that has no fields at all |
|
|
@@ -3333,7 +3425,7 @@ or the key's position in its own track. These are the frequent ones, verbatim:
|
|
|
3333
3425
|
| `skin "S": uses the long form … and also has a key "X"` | §3.4.1 — move the slot inside `attachments` |
|
|
3334
3426
|
| `animation "A" keys "X" as a path constraint, but the rig declares it as a "slider"` | §4.12 — a timeline group resolves by name AND type; use the field named after the constraint's own type |
|
|
3335
3427
|
| `animation "A": "position" is a path constraint timeline, and this track names no constraint` | §4.12 — put the name in `"path"` |
|
|
3336
|
-
| `N pair(s) of animation names have no one order: … "
|
|
3428
|
+
| `N pair(s) of animation names have no one order: … "turn" / "Turn" (case) — they are one name in two cases, and which of them the editor puts first is not measured; rename one of them so they differ by more than letter case` | **R10** — rename until no pair is left. The kind in brackets says which of the editor comparator's four UNMEASURED choices decides the pair: `case` (a pure case tie), `number` (one number written two ways, or a run of digits against a word) or `separator` (make the first character that differs a letter or a digit). rigc keys `animations` in the editor's own comparator — natural and case-insensitive ([#539](https://github.com/firejune/rigc/issues/539), [#543](https://github.com/firejune/rigc/issues/543)) — so a pair that comparator settles is emitted rather than refused, and only the four choices nobody has measured are a compile error; on those, the editor's re-key repoints every slider whose animation moves index ([#535](https://github.com/firejune/rigc/issues/535)) |
|
|
3337
3429
|
|
|
3338
3430
|
### 5.2 Assertions — the gate
|
|
3339
3431
|
|
|
@@ -3402,6 +3494,7 @@ The report prints one line per assertion:
|
|
|
3402
3494
|
| `A38_SKIN_MEMBERS_ARE_SKIN_REQUIRED` | both | a bone or constraint a skin activates that is not `skinRequired` (the list changes nothing), or one that is `skinRequired` and no skin activates (it is never active). Two keys in two places, and only together do they mean "this belongs to that skin" (§3.4.1). **SKIP** when no skin activates anything and nothing is `skinRequired` |
|
|
3403
3495
|
| `A39_DEFORM_KEEPS_TRIANGLE_WINDING` | archetype | a `deform` key reverses a triangle's winding, so the mesh has locally turned inside out and draws its texture backwards there (§4.11). The detail names the animation, the slot, the attachment, the key index and time, and each reversed triangle with its vertex triple and its signed area before and after. Measured at the key's **own** time, deformed against the same posed bones undeformed, so a mirrored slot bone cancels and a wrong *projection* with intact winding is correctly silent. A projection past its fold angle is the usual cause — [FACE.md §4.2](FACE.md) has the closed form. Legitimate art does fold, so declare `invariants.deformMayFold` (§3.7) for a slot that folds on purpose. ⚠️ A key whose slot **draws no pixels at that key's own time** — faded to alpha exactly 0, or showing another attachment — is measured and then passed over, because "draws its texture backwards" is false when nothing of it is drawn; the key is named on the stats line (`deformKeysNotDrawn`) and in the `DEFORM` block, never silently. The bar is **exactly 0**: at alpha 0.5 the fold is still refused and the alpha is in the message. It is per key and per time, so the same slot folding at full alpha in another animation is refused as before. ⚠️ And the **spans between** consecutive keys are scanned too (§4.11.3, issue #403): the runtime interpolates, so a deform inside its fold angle at every key can be past it in between. That refusal is its own sentence — `BETWEEN key 0 (t=0s) and key 1 (t=0.5s), at t=…` — with the time solved for in closed form and then posed and measured like any key, alpha read at that same moment. `deformSpansScanned` says on every green build that the scan ran. ⚠️ And the **frame** it poses in is the one the animation is reached in (§4.11.4, issue #407): on a track when nothing applies it, and otherwise once per **slider**, with that slider's mapping inverted and its bone driven until the runtime selects the key's own time — because a slider picks the time, so the two are one number and posing them independently is a frame that never occurs. The frame is on every `DEFORM` line, on the stats line as `deformFrames`, and in the refusal itself when it is not the track. A key at a time **no dial value selects** is measured in the frame the runtime does land on, left out of `deformKeysMeasured` and named as `deformKeysUnreachable`/`deformUnreachable` — never refused and never silent. **SKIP** when no animation carries a deform timeline, when nothing keyed has triangles, when every mesh keyed is exempt, when every key measured draws no pixels or is unreachable *and no span between them folds where anything is drawn*, or when there is no rig info at all |
|
|
3404
3496
|
| `A40_SLIDERS_COMPOSE_ON_A_SHARED_TARGET` | both | two or more sliders whose animations key the same timeline, where a later one is not `additive` — it writes that property outright at `mix: 1` and every earlier slider on it is dead (§3.5.2). Also fires when the shared timeline **cannot** be additive (a slot colour, an attachment swap, a draw order, a sequence), where `"additive": true` is not the fix and one of the two has to go. The detail names the bone or slot and the property, every slider keying it in `constraints` order with its flag, and which one wins today. Three shapes are deliberately not findings: a slider below `mix: 1` or with its `mix` keyed (the apply is then a lerp from the current pose, not an overwrite), two `skinRequired` sliders no skin activates together, and two sliders on different properties. **SKIP** when fewer than two sliders are at full authority; a PASS means two were compared |
|
|
3497
|
+
| `A41_PHYSICS_SURVIVES_EDITOR_ROUND_TRIP` | both | a physics constraint driving a component the **Spine editor** cannot hold, on a rig that declared `invariants.editorRoundTrip` (§3.7). The editor's physics model holds `x` and `y` only, with no cap on how many at once, so a constraint driving `rotate`, `scaleX` or `shearX` is imported, exported and handed back driving **nothing** — measured over three rigs and twelve constraints with the predictions written first ([#540](https://github.com/firejune/rigc/issues/540)). The detail names the constraint and each component. ⚠️ rigc's own output is correct — every runtime plays a rotation jiggle — so this is opt-in and the default is *not* silence: on a rig that declares nothing it **SKIPs**, and the SKIP names the constraint and the component anyway, so an author learns without having asked. Fix by driving the constraint in `x`/`y`, or by dropping the declaration if the rig never goes near the editor. Disjoint from `A23_PHYSICS_CONSTRAINT_EFFECTIVE` by construction: A23 refuses an **empty** driven set, which is what comes back from the editor, and this refuses a non-empty one that will not survive going in. **SKIP** also when the rig declares the editor and carries no physics constraint at all |
|
|
3405
3498
|
|
|
3406
3499
|
`both ◑` marks a mixed assertion: its validity half always runs and its policy
|
|
3407
3500
|
clauses are gated by profile.
|
|
@@ -3426,6 +3519,7 @@ says so, because a deferral without its reason is a wall rather than a work item
|
|
|
3426
3519
|
| constraint `type` of anything else | `constraint type "X" is not one Spine 4.3 knows. The five are: ik, transform, path, physics, slider.` — all five are emitted, so this is a typo, and a typo is what the parser drops in silence |
|
|
3427
3520
|
| a path attachment's `lengths` | `"lengths" is not authored — rigc measures the setup arc length of each curve off the geometry` (§3.4). Not a deferral: a second copy of a number the vertices already fix |
|
|
3428
3521
|
| a `deform` timeline on a path attachment | `a path attachment does have a vertex array, and rigc does not key it yet` — the format allows it and an animated track is a real idiom, but a deformed path invalidates the `lengths` a `constantSpeed: false` traversal reads. Move the curve by posing the bones its vertices are bound to |
|
|
3522
|
+
| any key neither format has, anywhere in either file | `<object> has a key this compiler does not read: "x" (did you mean "y"?) … Known here: …` (§5.1). Not a deferral either: a key nothing reads is a value you wrote and the emitted skeleton does not contain |
|
|
3429
3523
|
|
|
3430
3524
|
Two more limits that are not errors but will shape what you can attempt:
|
|
3431
3525
|
|
|
@@ -4900,10 +4994,13 @@ one key, deform blocks counted at each of their three levels;
|
|
|
4900
4994
|
⇒ in rigc: only `animations` is emitted sorted (R10), because it is the one
|
|
4901
4995
|
object measured here whose ORDER is also an index space — every reference into
|
|
4902
4996
|
the re-sorted *other* objects is by name on both sides, so nothing moves when
|
|
4903
|
-
they are re-keyed. rigc emits **
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4997
|
+
they are re-keyed. rigc emits **that comparator's own order** and refuses the name
|
|
4998
|
+
sets on which its leading-zero, case-tie, digit-against-word or separator
|
|
4999
|
+
behaviour — the four choices still unmeasured — would decide a pair. Sorting the
|
|
5000
|
+
105 collections that way reproduces **105 of 105**, the three codepoint cannot
|
|
5001
|
+
included, and refuses none of them; the codepoint rule that stood until
|
|
5002
|
+
[#543](https://github.com/firejune/rigc/issues/543) reproduced 102 and refused
|
|
5003
|
+
those same 3.
|
|
4907
5004
|
|
|
4908
5005
|
✅ **`events` is re-keyed too, and the references into it survive it.** The same
|
|
4909
5006
|
session measured it: `zebra, mike, alpha` came back `alpha, mike, zebra`, and the
|
|
@@ -4911,6 +5008,21 @@ firings still resolved **by name** — `0.3 -> mike`, `0.6 -> alpha`, payloads
|
|
|
4911
5008
|
intact (#539). So the editor treats `events` and `animations` differently, and
|
|
4912
5009
|
rigc emits events in the order you declare them.
|
|
4913
5010
|
|
|
5011
|
+
⚠️ **Read *"leaves every ARRAY alone"* above as bones, slots and constraints —
|
|
5012
|
+
`skins` is the array that round trip was not taken over.** `gallery/look` declares
|
|
5013
|
+
one skin, as does every other rig in this repository and all twelve editor exports
|
|
5014
|
+
in `examples/`, and a one-element array comes back in order whatever the editor
|
|
5015
|
+
does to it — so nothing here is
|
|
5016
|
+
evidence about `skins`, and a pull request that once called them *measured
|
|
5017
|
+
preserved* was reading a vacuous result ([#544](https://github.com/firejune/rigc/issues/544)).
|
|
5018
|
+
It matters because `skins` carries ordinals in the binary half too —
|
|
5019
|
+
`skins[readInt()]` for an attachment timeline and a linked mesh's skin index — so
|
|
5020
|
+
a re-order there would repoint them the way the `animations` re-key repoints a
|
|
5021
|
+
slider. ⛔ And it cannot be measured today: the editor refuses a four-skin rig on
|
|
5022
|
+
import without writing a project file or printing a word
|
|
5023
|
+
([#541](https://github.com/firejune/rigc/issues/541)). ⇒ **If you author more than
|
|
5024
|
+
one skin, nothing on this page says the editor survives it.**
|
|
5025
|
+
|
|
4914
5026
|
### 10.2 Draw order
|
|
4915
5027
|
|
|
4916
5028
|
📗 **An overlap change is a draw-order key.** The draw order *"can be keyed"*, and
|
package/docs/FACE.md
CHANGED
|
@@ -1182,7 +1182,10 @@ breaks the moment the two share a target — in the worked example both `turn` a
|
|
|
1182
1182
|
|
|
1183
1183
|
✅ **The editor half, measured.** This paragraph said *unknown* until the round
|
|
1184
1184
|
trip was taken with `tools/editor_roundtrip.ts` on a licensed editor (data
|
|
1185
|
-
version 4.3.26) against a 4.3.13 build of
|
|
1185
|
+
version 4.3.26) against a 4.3.13 build of
|
|
1186
|
+
[`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look) — this
|
|
1187
|
+
subsection's worked case, not the `gallery/portrait` this page names at the top,
|
|
1188
|
+
which declares no constraints at all and so can carry no slider. What it found:
|
|
1186
1189
|
|
|
1187
1190
|
- **Both sliders come back, and the parameter axis survives.** `additive`,
|
|
1188
1191
|
`local`, `bone`, `property`, `from`, `max` and `scale` are identical field for
|
|
@@ -1194,7 +1197,11 @@ version 4.3.26) against a 4.3.13 build of this worked example. What it found:
|
|
|
1194
1197
|
animation is an ordinal in the format, so `yaw -> "turn"` returned as
|
|
1195
1198
|
`yaw -> "sweep"` — the first animation of the sorted list
|
|
1196
1199
|
([#535](https://github.com/firejune/rigc/issues/535)). rigc now emits
|
|
1197
|
-
animations
|
|
1200
|
+
animations in the editor's own order — natural and case-insensitive
|
|
1201
|
+
([#539](https://github.com/firejune/rigc/issues/539),
|
|
1202
|
+
[#543](https://github.com/firejune/rigc/issues/543)); this rig's names are ones
|
|
1203
|
+
a codepoint sort orders identically, which is what it emitted at the time of
|
|
1204
|
+
the round trip below. On the same rig through the same editor that
|
|
1198
1205
|
restored `yaw -> "turn"` and took the re-rendered mean absolute error from
|
|
1199
1206
|
10.4655 / 8.4961 / 8.7140 (`sweep` / `tilt` / `turn`) to
|
|
1200
1207
|
0.3035 / 0.0769 / 0.0588, worst drift 16.535 px to 3.947 px.
|
|
@@ -1202,16 +1209,55 @@ version 4.3.26) against a 4.3.13 build of this worked example. What it found:
|
|
|
1202
1209
|
`rotate: 1` is absent from the export, and an absent `rotate` parses as **0**
|
|
1203
1210
|
(`SkeletonJson`), so the returned file states *drives nothing* rather than
|
|
1204
1211
|
omitting a default — which is why `A23_PHYSICS_CONSTRAINT_EFFECTIVE` refuses it
|
|
1205
|
-
by name. Independent of the ordering defect, and
|
|
1212
|
+
by name. Independent of the ordering defect, and filed as
|
|
1206
1213
|
[#536](https://github.com/firejune/rigc/issues/536).
|
|
1214
|
+
|
|
1215
|
+
✅ **Why, measured since.** It is not elision and not a defect in one field:
|
|
1216
|
+
the editor's physics model holds `x` and `y` and nothing else, with no limit on
|
|
1217
|
+
how many at once. Three rigs, twelve constraints, predictions written before the
|
|
1218
|
+
round trip — a lone `y` came back, `x` and `y` together came back, and a lone
|
|
1219
|
+
`rotate`, a lone `scaleX` and a lone `shearX` each came back as **no components
|
|
1220
|
+
at all**, with every constraint's fixed-point `strength` returning exactly so a
|
|
1221
|
+
silent harness failure could not read as a finding
|
|
1222
|
+
([#540](https://github.com/firejune/rigc/issues/540)). ⇒ **A rotation-driven
|
|
1223
|
+
jiggle does not survive the editor, and no `scaleY` mode substitutes for it.**
|
|
1224
|
+
⚠️ Still open on #536: whether the loss happens at import or at export. The
|
|
1225
|
+
project file's bytes cannot settle it — it carries derived float32s that no
|
|
1226
|
+
input declares — and the answer is invisible to an author either way.
|
|
1227
|
+
|
|
1228
|
+
✅ **And the gate now says so before the trip, not after.** A face rig that is
|
|
1229
|
+
authored to come back out of the editor declares
|
|
1230
|
+
`"invariants": { "editorRoundTrip": true }` (AUTHORING §3.7) and
|
|
1231
|
+
`A41_PHYSICS_SURVIVES_EDITOR_ROUND_TRIP` refuses the constraint by name at build
|
|
1232
|
+
time, with the fix in the message: drive it in `x`/`y`, or drop the declaration.
|
|
1233
|
+
⚠️ A rig that declares nothing is **not** silent either — A41 SKIPs and the skip
|
|
1234
|
+
names the constraint and the component, which is the whole reason the rule is
|
|
1235
|
+
opt-in rather than default-off. rigc's own output was never wrong here: a
|
|
1236
|
+
rotation jiggle is valid Spine 4.3 that every runtime plays, and refusing it for
|
|
1237
|
+
everybody would be refusing correct data on behalf of one consumer. ⇒ A23 and
|
|
1238
|
+
A41 are the same loss from opposite sides of the trip: A41 fires on what goes
|
|
1239
|
+
in, A23 on what comes back.
|
|
1207
1240
|
- 🔸 Unexplained: `diff` reports `animations.curve_kinds` moved on **196 of 200**
|
|
1208
1241
|
keys in every round trip taken, the clean one included. Visually small once the
|
|
1209
1242
|
ordering is fixed — but it is 98% of the keys, and *small* is not *explained*.
|
|
1210
1243
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1244
|
+
✅ **The two things this paragraph said it still did not measure have since been
|
|
1245
|
+
measured, and they came out opposite ways** — [#544](https://github.com/firejune/rigc/issues/544)
|
|
1246
|
+
is the card for having left the sentence standing:
|
|
1247
|
+
|
|
1248
|
+
- **More than one event is safe.** The editor re-keys `events` the way it re-keys
|
|
1249
|
+
`animations` — `zebra, mike, alpha` came back `alpha, mike, zebra` — but every
|
|
1250
|
+
firing resolved **by name**, `0.3 -> mike` and `0.6 -> alpha`, payloads intact
|
|
1251
|
+
([#539](https://github.com/firejune/rigc/issues/539)). The ordinal shape does
|
|
1252
|
+
*not* bite here, and rigc emits events in the order you declare them.
|
|
1253
|
+
- **More than one skin is worse than unmeasured.** A four-skin rig builds green,
|
|
1254
|
+
parses in `spine-core`, and the editor **refuses to import it** — no project
|
|
1255
|
+
file, no message ([#541](https://github.com/firejune/rigc/issues/541)). So there
|
|
1256
|
+
is no export to read, and every figure on this page was taken on a rig carrying
|
|
1257
|
+
exactly one skin (AUTHORING §10.1).
|
|
1258
|
+
|
|
1259
|
+
The runtime half was never in doubt: every figure above came back through
|
|
1260
|
+
`spine-core`.
|
|
1215
1261
|
|
|
1216
1262
|
---
|
|
1217
1263
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
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": {
|