spine-rigc 0.20.3 → 0.22.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/cli.ts +7 -1
- package/docs/AUTHORING.md +275 -43
- package/docs/FACE.md +53 -7
- package/docs/SPEC_COVERAGE.md +9 -0
- package/package.json +2 -1
- package/src/compile.ts +618 -171
- package/src/keys.ts +117 -0
- package/src/motion.ts +143 -15
- package/src/rig.ts +445 -117
- package/src/types.ts +97 -8
- package/src/validate.ts +106 -1
- package/tools/editor_roundtrip.ts +99 -12
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/cli.ts
CHANGED
|
@@ -2411,8 +2411,14 @@ function cmdExplain(flags: Record<string, string>): void {
|
|
|
2411
2411
|
}
|
|
2412
2412
|
|
|
2413
2413
|
console.log('\nslots (array order IS the draw order)');
|
|
2414
|
+
// The DEFAULT skin's placeholders, resolved by name. It was `skins[0]` until
|
|
2415
|
+
// issue #541, which is the same thing only because rigc pins `default` at
|
|
2416
|
+
// index 0 — a property of the emitter that this report should not be quietly
|
|
2417
|
+
// relying on. Reading it by name means a change to the skins ORDER cannot turn
|
|
2418
|
+
// this line into a report about some other skin.
|
|
2419
|
+
const defaultSkin = result.skeleton.skins.find((skin) => skin.name === 'default');
|
|
2414
2420
|
for (const s of result.skeleton.slots) {
|
|
2415
|
-
const atts = Object.keys(
|
|
2421
|
+
const atts = Object.keys(defaultSkin?.attachments[s.name] ?? {});
|
|
2416
2422
|
console.log(
|
|
2417
2423
|
` ${s.name.padEnd(12)} bone=${s.bone.padEnd(12)} setup=${(s.attachment ?? 'null').padEnd(22)} color=${s.color ?? 'ffffffff'} attachments=[${atts.join(', ')}]`,
|
|
2418
2424
|
);
|
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` |
|
|
@@ -540,6 +540,15 @@ in the skeleton and the size in the atlas cannot drift apart. The **region name
|
|
|
540
540
|
PNG's basename**; when your placeholder name differs from it, rigc writes a `path`
|
|
541
541
|
so the attachment still joins to the region.
|
|
542
542
|
|
|
543
|
+
**Every** attachment that names an `image` is measured, whichever skin it sits in.
|
|
544
|
+
The atlas holds **one region per file**, so two skins filling one placeholder from
|
|
545
|
+
two files put two regions in it and each attachment draws its own (§3.4.2), while
|
|
546
|
+
two attachments naming the same file share the one region that file made. What
|
|
547
|
+
cannot be reconciled is two *different* files whose basenames collide — only one of
|
|
548
|
+
them can be region `patch` — so rigc refuses the build and names both paths rather
|
|
549
|
+
than letting one of them silently draw the other's pixels
|
|
550
|
+
([#555](https://github.com/firejune/rigc/issues/555)).
|
|
551
|
+
|
|
543
552
|
**R6 — A key carries `ease` or `curve`, never both.** A named easing says "this
|
|
544
553
|
shape, wherever it is used" and is the recommended path. `curve` is the escape
|
|
545
554
|
hatch: the absolute `(time, value)` control points, verbatim, for when every key
|
|
@@ -566,7 +575,8 @@ behind it writes literal `x`/`y` instead.
|
|
|
566
575
|
|
|
567
576
|
**R10 — The `animations` object is keyed in the editor's order, not in yours, and
|
|
568
577
|
names that have no one order are refused.** Declare animations in whatever order
|
|
569
|
-
reads best; the emit keys them
|
|
578
|
+
reads best; the emit keys them the way the Spine editor does — **natural and
|
|
579
|
+
case-insensitive**. This is the one place rigc
|
|
570
580
|
reorders anything you wrote, and it is not cosmetic: a `slider`'s animation is a
|
|
571
581
|
**name** in JSON and an **ordinal** in the format's binary half, so an editor that
|
|
572
582
|
re-sorts the object repoints every slider whose animation moved index — silently,
|
|
@@ -575,25 +585,68 @@ in a file that still parses and still gates green (§3.5.2,
|
|
|
575
585
|
animation's own body is byte-identical either way, and every other collection is
|
|
576
586
|
emitted in the order you gave it.
|
|
577
587
|
|
|
578
|
-
⚠️ **
|
|
579
|
-
case-insensitive
|
|
588
|
+
⚠️ **What is measured about that comparator, and what is not.** The editor sorts
|
|
589
|
+
natural and case-insensitive — measured, two rigs, one axis each:
|
|
580
590
|
`Turn, sweep, wave` came back `sweep, Turn, wave`, and `turn10, turn2, zoom` came
|
|
581
591
|
back `turn2, turn10, zoom` ([#539](https://github.com/firejune/rigc/issues/539)).
|
|
582
|
-
|
|
583
|
-
**
|
|
584
|
-
|
|
592
|
+
But "natural and case-insensitive" is a **family** of comparators, not one, and
|
|
593
|
+
**four** of its choices have never been measured. rigc emits the order every
|
|
594
|
+
member of that family agrees on, and **refuses the sets where one of the four
|
|
595
|
+
would decide**, naming the pair. So the rule you have to hold is about *names*,
|
|
596
|
+
and it is four things:
|
|
585
597
|
|
|
586
598
|
| Do not let two animation names differ | Because | Instead |
|
|
587
599
|
| --- | --- | --- |
|
|
588
|
-
| by **case**
|
|
589
|
-
| by a **number
|
|
600
|
+
| 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 |
|
|
601
|
+
| 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 |
|
|
602
|
+
| 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
603
|
| 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
604
|
|
|
592
|
-
⭐ **Capitals and
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
605
|
+
⭐ **Capitals and numbered series are not what is refused** — only pairs one of
|
|
606
|
+
those four decides. `Sweep, Turn, Wave, Zoom02, Zoom10` builds, and so does
|
|
607
|
+
`shot1 … shot12`: every member of the family puts each of those sets in one
|
|
608
|
+
order, and that order is what rigc emits. A numbered series that crosses 9 → 10 is
|
|
609
|
+
keyed **1, 2, … 9, 10, 11, 12**, which is what the editor does with it — and is
|
|
610
|
+
not what a codepoint sort does.
|
|
611
|
+
|
|
612
|
+
✅ **This list had two more rows before
|
|
613
|
+
[#543](https://github.com/firejune/rigc/issues/543), and both were artefacts of
|
|
614
|
+
the emit rather than facts about the editor.** rigc used to key `animations`
|
|
615
|
+
**codepoint-ascending** and refuse every pair codepoint and the editor could order
|
|
616
|
+
differently — which refused a pair that folds the other way (`Turn` against
|
|
617
|
+
`sweep`) and a pair of digit runs of unequal width (`turn10` against `turn2`).
|
|
618
|
+
Those are the only two name sets anybody has ever put through the editor and read
|
|
619
|
+
back, so the tool was refusing precisely the pairs it knew the most about, and its
|
|
620
|
+
only repair was *rename* — the one repair a transcription cannot take. Emitting a
|
|
621
|
+
member of the family instead moves no byte on any set the old rule accepted; it
|
|
622
|
+
just stops refusing the ones it did.
|
|
623
|
+
|
|
624
|
+
**R11 — The `skins` array is written with `default` first and the rest in the
|
|
625
|
+
editor's order, and skin names that have no one order are refused.** The same rule
|
|
626
|
+
as R10, in the collection that was believed exempt from it. A skin is a **name** in
|
|
627
|
+
the JSON half of the format and an **ordinal** in the binary half —
|
|
628
|
+
`skins[readInt()]` for an attachment timeline, `skins[skinIndex]` for a linked mesh
|
|
629
|
+
— so an editor that writes the array in another order repoints every such
|
|
630
|
+
reference, silently, in a file that still parses. Measured: a rig built
|
|
631
|
+
`default, zulu, mike, alpha` exported `default, alpha, mike, zulu`
|
|
632
|
+
([#541](https://github.com/firejune/rigc/issues/541)).
|
|
633
|
+
|
|
634
|
+
⚠️ **The refusal here is wider than R10's, and deliberately.** R10 can be narrow
|
|
635
|
+
because #539 measured two animation-name pairs and thereby *refuted* a codepoint
|
|
636
|
+
sort. The skins measurement refutes nothing — `alpha, mike, zulu` is the answer
|
|
637
|
+
codepoint, folding and natural order all give — so the editor's skin comparator is
|
|
638
|
+
**not established**, and rigc refuses any pair those candidates could disagree
|
|
639
|
+
about. In practice that is R10's four rows plus two more: a pair a case fold
|
|
640
|
+
reverses (`Zulu` against `mike`) and two digit runs of unequal width (`mike10`
|
|
641
|
+
against `mike2`). Both of those *build* as animation names and are refused as skin
|
|
642
|
+
names, and the two refusals say which is which.
|
|
643
|
+
|
|
644
|
+
**R12 — A placeholder that more than one skin fills gets a per-skin attachment
|
|
645
|
+
`name`.** rigc writes `"name": "<skin>/<placeholder>"` on each of those entries,
|
|
646
|
+
and restates `path` beside it so the texture still resolves where it did. You do
|
|
647
|
+
not author this and there is nothing to do about it — but it is visible in the
|
|
648
|
+
emitted file, so §3.4.2 says what it is and why. A placeholder only one skin fills
|
|
649
|
+
is emitted exactly as before.
|
|
597
650
|
|
|
598
651
|
---
|
|
599
652
|
|
|
@@ -692,7 +745,7 @@ the default `type`:
|
|
|
692
745
|
| `type` | `"region"`, or omit |
|
|
693
746
|
| `image` | **rigc extension.** A PNG relative to the rig's `images` directory; rigc measures it (R5) |
|
|
694
747
|
| `width`, `height` | required by the format — give them, or give an `image` |
|
|
695
|
-
| `path` | the atlas region to resolve; defaults to the attachment's own name. rigc sets it for you when the PNG basename differs from the placeholder |
|
|
748
|
+
| `path` | the atlas region to resolve; defaults to the attachment's own name. rigc sets it for you when the PNG basename differs from the placeholder, and whenever it composes a `name` because more than one skin fills this placeholder (§3.4.2) |
|
|
696
749
|
| `x`, `y` | offset from the bone, in the bone's local space |
|
|
697
750
|
| `rotation` | degrees; cancels a rotated bone for a plate authored screen-upright |
|
|
698
751
|
| `scaleX`, `scaleY`, `color` | as Spine |
|
|
@@ -1355,6 +1408,70 @@ beside them is refused rather than ignored (an ignored slot is an attachment tha
|
|
|
1355
1408
|
vanishes), and a rig with a *slot* of one of those names is refused too, because
|
|
1356
1409
|
there the two forms are genuinely ambiguous. Rename the slot.
|
|
1357
1410
|
|
|
1411
|
+
#### 3.4.2 Two skins, one placeholder — the `name` rigc writes for you
|
|
1412
|
+
|
|
1413
|
+
Two skins putting different art under one placeholder is what a skin is *for*, and
|
|
1414
|
+
it is the one shape rigc emitted wrongly until
|
|
1415
|
+
[#541](https://github.com/firejune/rigc/issues/541). Without a `name` field an
|
|
1416
|
+
attachment's name **is** its placeholder (`SkeletonJson.ts:526`), so four skins
|
|
1417
|
+
filling `patch` are four different attachments all called `patch`. spine-core never
|
|
1418
|
+
notices — its skin table is keyed by placeholder, so the two never meet — and the
|
|
1419
|
+
whole gate is green. The Spine editor refuses the import outright:
|
|
1420
|
+
|
|
1421
|
+
```
|
|
1422
|
+
ERROR: Unable to import skeleton.
|
|
1423
|
+
[error] Error reading skeleton: skins
|
|
1424
|
+
Cause: [error] Error reading attachment: patch (MOw)
|
|
1425
|
+
Cause: [error] Multiple attachments have the same name: patch patch
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
⇒ rigc now writes each of those entries a name of its own, composed from the two
|
|
1429
|
+
names you already gave it:
|
|
1430
|
+
|
|
1431
|
+
```json
|
|
1432
|
+
"skins": {
|
|
1433
|
+
"default": { "patch": { "patch": { "image": "patch_a.png" } } },
|
|
1434
|
+
"zulu": { "patch": { "patch": { "image": "patch_a.png", "x": 4 } } }
|
|
1435
|
+
}
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
emits
|
|
1439
|
+
|
|
1440
|
+
```json
|
|
1441
|
+
{ "name": "default/patch", "path": "patch", "width": 64, "height": 64 }
|
|
1442
|
+
{ "name": "zulu/patch", "path": "patch", "width": 64, "height": 64, "x": 4 }
|
|
1443
|
+
```
|
|
1444
|
+
|
|
1445
|
+
Three things to know about it and nothing to author:
|
|
1446
|
+
|
|
1447
|
+
- **`path` is restated, and it has to be.** `path` defaults to the attachment's
|
|
1448
|
+
**name**, not to its placeholder, so an entry given a name and no path would
|
|
1449
|
+
resolve its texture at `zulu/patch` and find no such region. `A00_ROUNDTRIP_PARSE`
|
|
1450
|
+
says so in the parser's own words if it is ever dropped.
|
|
1451
|
+
- **Only contested placeholders are touched.** One skin filling a placeholder, or
|
|
1452
|
+
two skins filling a slot under *different* placeholders, emit exactly what they
|
|
1453
|
+
always did — every rig in this repository is byte-identical across the change.
|
|
1454
|
+
- **A composed name that collides is a compile error, not a surprise.** If some
|
|
1455
|
+
other placeholder in the same slot is literally called `zulu/patch`, rigc refuses
|
|
1456
|
+
and names both sites rather than emitting two attachments with one name again.
|
|
1457
|
+
(`/` is the separator because it appears in **0** of the 160 placeholder names and
|
|
1458
|
+
159 atlas region names in `examples/` and `gallery/`, where `-` appears in 85 and
|
|
1459
|
+
`_` in 37.)
|
|
1460
|
+
- **Each skin's art is measured and atlased on its own.** The example above points
|
|
1461
|
+
both skins at one PNG, so there is one region; point them at two and there are
|
|
1462
|
+
two, each attachment's `path` resolving to the file that attachment named and its
|
|
1463
|
+
`width`/`height` measured off that file. Until
|
|
1464
|
+
[#555](https://github.com/firejune/rigc/issues/555) only the first skin's PNG was
|
|
1465
|
+
ever opened, and the second skin's art reached neither the atlas nor the
|
|
1466
|
+
measurement — so name the two files **distinctly**, because the region name is
|
|
1467
|
+
the basename and `a/patch.png` beside `b/patch.png` is refused (R5).
|
|
1468
|
+
|
|
1469
|
+
⚠️ **The uniqueness scope is the slot, not the skeleton.** `spineboy-pro.json`,
|
|
1470
|
+
which the editor wrote, gives the name `head` to a region in slot `head` and to a
|
|
1471
|
+
bounding box in slot `head-bb`, and reuses `hoverglow-small` across eight slots. So
|
|
1472
|
+
a name shared between slots is normal and rigc leaves it alone; what #541 refused
|
|
1473
|
+
was one slot holding two.
|
|
1474
|
+
|
|
1358
1475
|
### 3.5 `constraints` — 4.3's single typed array
|
|
1359
1476
|
|
|
1360
1477
|
Spine 4.3 folds every constraint into one `constraints` array with a `type`
|
|
@@ -1377,6 +1494,14 @@ carrying here:
|
|
|
1377
1494
|
- A physics constraint's five components all default to 0, so one that names none of
|
|
1378
1495
|
them parses cleanly and does nothing at all. rigc refuses it up front, and `A23`
|
|
1379
1496
|
catches it from the other side.
|
|
1497
|
+
- An **ik** and a **physics** constraint both carry `ScaleYMode` under the key
|
|
1498
|
+
`scaleY`, spelled `"none"`, `"uniform"` or `"volume"`. It is an enum resolved by
|
|
1499
|
+
`Utils.enumValue`, so only the first letter's case is free and an unrecognised
|
|
1500
|
+
name is assigned as `undefined` with no error; rigc checks it, like the three
|
|
1501
|
+
path modes below. ⚠️ `src/rig.ts` called the physics one **`scaleYMode`** until
|
|
1502
|
+
issue #545 — the runtime's field name rather than the format's key — and nothing
|
|
1503
|
+
read it, so a spec that wrote `scaleYMode` set no mode and said nothing. A rig
|
|
1504
|
+
that still writes it is now refused by name, with `scaleY` beside it.
|
|
1380
1505
|
|
|
1381
1506
|
Every constraint may also carry `skin: true`, which makes it run only under the skin
|
|
1382
1507
|
that lists it — see §3.4.1, and note that the flag alone does nothing.
|
|
@@ -1482,25 +1607,32 @@ animation now stands at the position. `gallery/look` went into a licensed editor
|
|
|
1482
1607
|
`sweep, tilt, turn` with **`yaw -> "sweep"`**: a file that parses, gates green and
|
|
1483
1608
|
applies the wrong animation. ⭐ Its second slider is what named the mechanism
|
|
1484
1609
|
rather than a second casualty — `tilt` survived because it sat at index 1 in both
|
|
1485
|
-
orderings. rigc now emits animations
|
|
1610
|
+
orderings. rigc now emits animations in the editor's own order so its re-sort
|
|
1486
1611
|
moves no index ([#535](https://github.com/firejune/rigc/issues/535)); on the same
|
|
1487
1612
|
rig through the same editor that restored `yaw -> "turn"` and took the
|
|
1488
1613
|
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
|
-
|
|
1614
|
+
0.3035 / 0.0769 / 0.0588. (`look`'s three names are ones a codepoint sort orders
|
|
1615
|
+
identically, which is what rigc emitted when that trip was measured.)
|
|
1616
|
+
|
|
1617
|
+
⚠️ The editor's comparator is natural and case-insensitive
|
|
1618
|
+
([#539](https://github.com/firejune/rigc/issues/539)), and four of its choices are
|
|
1619
|
+
unmeasured — so the emit is that family's order for names none of the four
|
|
1620
|
+
decides, and the rest are a compile error. **R10** has the four shapes to avoid.
|
|
1621
|
+
|
|
1622
|
+
✅ **What that repair does not reach is a compile error now, not a hazard.** This
|
|
1623
|
+
paragraph used to say that names a codepoint sort and a friendlier one disagree
|
|
1624
|
+
about — `Turn` / `turn`, `turn2` / `turn10` — were where *the hazard returns*, and
|
|
1625
|
+
that nobody had round-tripped such a pair. Somebody has: `Turn, sweep, wave` came
|
|
1626
|
+
back `sweep, Turn, wave` and `turn10, turn2, zoom` came back `turn2, turn10, zoom`
|
|
1627
|
+
([#539](https://github.com/firejune/rigc/issues/539)). ⇒ rigc no longer leaves
|
|
1628
|
+
that to naming discipline — and since
|
|
1629
|
+
[#543](https://github.com/firejune/rigc/issues/543) it does better than refusing
|
|
1630
|
+
those two, because they are the two sets the editor's answer is **known** for:
|
|
1631
|
+
both are emitted in the order it returned. What is still a compile error is the
|
|
1632
|
+
set whose order turns on one of the four unmeasured choices, printed with both
|
|
1633
|
+
names, which of them decides it, and the rename that settles it. What changed is
|
|
1634
|
+
the price of forgetting: a build that stops, rather than a slider that silently
|
|
1635
|
+
applies the wrong animation.
|
|
1504
1636
|
|
|
1505
1637
|
⚠️ **The fields of the model you did not choose are refused, not ignored.** The
|
|
1506
1638
|
parser reads `time` only in the bone-less branch and `property`/`from`/`to`/`scale`/
|
|
@@ -1770,7 +1902,8 @@ is not an array. Every field is optional and each is the payload a firing
|
|
|
1770
1902
|
|
|
1771
1903
|
Optional with one exception, and only meaningful for rigc's own formations:
|
|
1772
1904
|
`meshSlots` and `meshTriangles` (the two halves of the mesh budget `A13` measures
|
|
1773
|
-
against), `axisBone`, `massBone`, `detached`, `deformMayFold`.
|
|
1905
|
+
against), `axisBone`, `massBone`, `detached`, `deformMayFold`, `editorRoundTrip`.
|
|
1906
|
+
Nothing in skeleton
|
|
1774
1907
|
JSON records that a
|
|
1775
1908
|
bone carries a cut's axis or that a parentage is forbidden, so the rig spec says it
|
|
1776
1909
|
and the validator's archetype assertions read it. **An assertion whose field is
|
|
@@ -1808,6 +1941,33 @@ and the entry is gone. ⇒ An exemption whose `why` reads *"known defect, see
|
|
|
1808
1941
|
against a fix, not a fix — and the thing that made it repayable was A39
|
|
1809
1942
|
measuring the ceiling the art could actually take.
|
|
1810
1943
|
|
|
1944
|
+
🎬 **`editorRoundTrip` is the one field here that names a CONSUMER rather than a
|
|
1945
|
+
shape.** Write `"editorRoundTrip": true` when this rig is authored to come back
|
|
1946
|
+
out of the Spine editor — imported, hand-edited, exported — and
|
|
1947
|
+
`A41_PHYSICS_SURVIVES_EDITOR_ROUND_TRIP` refuses a physics constraint driving a
|
|
1948
|
+
component that editor cannot hold. `true` is the only accepted value; a `false`
|
|
1949
|
+
would be a key nothing reads ([#545](https://github.com/firejune/rigc/issues/545)).
|
|
1950
|
+
|
|
1951
|
+
⚠️ **Leaving it out is not a weaker gate, and this is the part worth reading.**
|
|
1952
|
+
rigc's output is not wrong here: a physics constraint driving `rotate` is valid
|
|
1953
|
+
Spine 4.3 that every runtime plays — a cowlick, a tail, an ear — so refusing it by
|
|
1954
|
+
default would be refusing correct data on behalf of a pipeline nobody declared.
|
|
1955
|
+
What a rig that says nothing gets instead is the **SKIP**, and the SKIP names the
|
|
1956
|
+
constraint and the component:
|
|
1957
|
+
|
|
1958
|
+
```
|
|
1959
|
+
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)
|
|
1960
|
+
```
|
|
1961
|
+
|
|
1962
|
+
📏 **Measured, not inferred** ([#540](https://github.com/firejune/rigc/issues/540)):
|
|
1963
|
+
three rigs, twelve constraints, predictions written before the round trip. A lone
|
|
1964
|
+
`y` came back and `x` + `y` together came back — the rule is membership, not arity
|
|
1965
|
+
— while a lone `rotate`, a lone `scaleX` and a lone `shearX` each came back driving
|
|
1966
|
+
**no component at all**, and neither `scaleY` mode rescues `scaleX`. So the fix for
|
|
1967
|
+
a refusal is to drive the constraint in `x`/`y`, or to drop the declaration if this
|
|
1968
|
+
rig never goes near the editor. ⇒ `A23_PHYSICS_CONSTRAINT_EFFECTIVE` is the same
|
|
1969
|
+
loss seen from the far side: it is what fires on the file the editor hands **back**.
|
|
1970
|
+
|
|
1811
1971
|
🚫 **Do not reach for it to cover a part you have faded out.** A key whose slot
|
|
1812
1972
|
draws no pixels at that key's own time is already passed over — `A39` measures
|
|
1813
1973
|
that and says so (§4.11, and the `skipped` line in the `DEFORM` block). Declaring
|
|
@@ -3220,6 +3380,38 @@ compiled, and ask only what the one file in front of them can answer. Every
|
|
|
3220
3380
|
one per message. (The rig spec's parser predates the convention and its messages
|
|
3221
3381
|
are prose, so they sit in the second table with everything else.)
|
|
3222
3382
|
|
|
3383
|
+
🚨 **A key neither format has is refused by name, in both files.** Not a row in
|
|
3384
|
+
the table below, because it is not about one field: every object in a rig spec and
|
|
3385
|
+
in a motion spec is checked against the keys its shape actually owns, and a key
|
|
3386
|
+
outside that set stops the build. Issue #545 is why — before it, such a key was
|
|
3387
|
+
never looked at, never mentioned and never emitted, and the build exited 0 with
|
|
3388
|
+
every assertion green. Four keys planted into one physics constraint all vanished,
|
|
3389
|
+
and no line of output named any of them.
|
|
3390
|
+
|
|
3391
|
+
```
|
|
3392
|
+
rigc compile error: rig.json: constraint "ctl" (physics) has 4 keys this compiler
|
|
3393
|
+
does not read: "scaleYMode" (did you mean "scaleY", "scaleX"?), "scale" (did you
|
|
3394
|
+
mean "scaleX", "scaleY"?), "wobble", "ROTATE" (did you mean "rotate"?). Nothing
|
|
3395
|
+
reads such a key, so it would be dropped from the emitted skeleton in silence —
|
|
3396
|
+
fix the spelling or remove it. Known here: bone, damping, dampingGlobal, fps, …
|
|
3397
|
+
```
|
|
3398
|
+
|
|
3399
|
+
Read it as a **repair**, not a rule: every stray key on that object is named at
|
|
3400
|
+
once, the closest known spellings come with it (the search is case-insensitive, so
|
|
3401
|
+
a real key in the wrong case leads the list), and the shape's whole key set is
|
|
3402
|
+
printed after. What it will *not* do is guess — a key four edits from anything gets
|
|
3403
|
+
no suggestion, only the set.
|
|
3404
|
+
|
|
3405
|
+
⚠️ There is **no forward-compatibility escape**, and no `note` field except where
|
|
3406
|
+
one is listed: a rig spec's root, a motion spec's root, an animation, and a
|
|
3407
|
+
`physics` tuning entry. Prose anywhere else has to go in a document, because a key
|
|
3408
|
+
the compiler tolerates is a key it cannot distinguish from one you meant it to
|
|
3409
|
+
read. (The **cut manifest** is deliberately outside this: it is the record of the
|
|
3410
|
+
pipeline that produced the art as much as an input, it carries fields the compiler
|
|
3411
|
+
states outright that it does not read — `roi` — and the fixtures in this repository
|
|
3412
|
+
already give it `note` and `archetype`. It has no shape parse at all, which is the
|
|
3413
|
+
same hole issue #307 closed for the motion spec.)
|
|
3414
|
+
|
|
3223
3415
|
| Key | Refused when it is not | Why the shape matters |
|
|
3224
3416
|
| --- | --- | --- |
|
|
3225
3417
|
| 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 |
|
|
@@ -3273,6 +3465,8 @@ or the key's position in its own track. These are the frequent ones, verbatim:
|
|
|
3273
3465
|
| `the triangles do not tile the outline: …` / `the triangles' outline is not one closed loop: …` | §3.4 — a doubled triangle, an unused vertex, a pinch or a hole in `triangles` |
|
|
3274
3466
|
| `image "X.png" is not on disk at …` | fix the name, or point `--images` at the right directory |
|
|
3275
3467
|
| `duplicate region name "X"` | two PNGs share a basename; one part, one page, one name |
|
|
3468
|
+
| `"b/X.png" and the art already atlased as region "X" are two different files — … Rename one of the PNGs.` | R5 — the region name is the basename, so only one of the two can hold it. Rename a file (not a placeholder: the placeholder is free to repeat) |
|
|
3469
|
+
| `the image "X.png" was never added to the atlas, so there is no region "X" …` | nothing in the spec — every image an attachment names is measured, so this says rigc skipped one. Report it ([#555](https://github.com/firejune/rigc/issues/555)) |
|
|
3276
3470
|
| `motion spec names archetype "A" but the rig spec at … is called "B"` | make `archetype` equal the rig's `name` |
|
|
3277
3471
|
| `animation "A" declares duration Ns but its last key is at Ms` | R7 — fix whichever of the two you meant |
|
|
3278
3472
|
| `animation "A" slot "X" attachment: key at Ns is Ms past the declared duration Ds` | §4.5 — the key is past the end of the animation and nothing will sample it. Move the key onto `duration`, or raise `duration` |
|
|
@@ -3333,7 +3527,9 @@ or the key's position in its own track. These are the frequent ones, verbatim:
|
|
|
3333
3527
|
| `skin "S": uses the long form … and also has a key "X"` | §3.4.1 — move the slot inside `attachments` |
|
|
3334
3528
|
| `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
3529
|
| `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: … "
|
|
3530
|
+
| `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)) |
|
|
3531
|
+
| `N pair(s) of skin names have no one order: … "Zulu" / "mike" (case) — folded to one case "Zulu" and "mike" order the other way round, so whether the editor folds SKIN names decides this pair` | **R11** — rename until no pair is left. The same shape as the row above with a **wider** family: #539 measured the editor's comparator for animation names and thereby ruled codepoint out, and nothing has ruled anything out for skin names, so a pair the candidates could disagree about is refused even where the animation rule would emit it. `Zulu`/`mike` and `mike10`/`mike2` build as animation names and are refused as skin names ([#541](https://github.com/firejune/rigc/issues/541)) |
|
|
3532
|
+
| `N attachment name collision(s): a placeholder that more than one skin fills is emitted with the name "<skin>/<placeholder>" … slot "patch": skin "default" placeholder "zulu/patch" and skin "zulu" placeholder "patch" would both be named "zulu/patch"` | **R12** — rename the placeholder or the skin. rigc composes an attachment name for every placeholder more than one skin fills (§3.4.2), and this fires when the composed name is one another entry in the same slot already answers to. Both sites are named; either rename ends it |
|
|
3337
3533
|
|
|
3338
3534
|
### 5.2 Assertions — the gate
|
|
3339
3535
|
|
|
@@ -3402,6 +3598,7 @@ The report prints one line per assertion:
|
|
|
3402
3598
|
| `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
3599
|
| `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
3600
|
| `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 |
|
|
3601
|
+
| `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
3602
|
|
|
3406
3603
|
`both ◑` marks a mixed assertion: its validity half always runs and its policy
|
|
3407
3604
|
clauses are gated by profile.
|
|
@@ -3426,6 +3623,7 @@ says so, because a deferral without its reason is a wall rather than a work item
|
|
|
3426
3623
|
| 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
3624
|
| 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
3625
|
| 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 |
|
|
3626
|
+
| 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
3627
|
|
|
3430
3628
|
Two more limits that are not errors but will shape what you can attempt:
|
|
3431
3629
|
|
|
@@ -4878,12 +5076,17 @@ low figure as a miss — say in the log that the art did not carry them.
|
|
|
4878
5076
|
`default`"* and *"bones are ordered so that the parent always comes before a child
|
|
4879
5077
|
bone"* — [JSON format](http://esotericsoftware.com/spine-json-format). §3.4.
|
|
4880
5078
|
|
|
4881
|
-
🔬 **The editor re-keys every name-keyed OBJECT and leaves
|
|
4882
|
-
Read off its export of a rigc build (Spine 4.3.26,
|
|
4883
|
-
`animations` object, a skin's 24 `attachments` slot keys and
|
|
4884
|
-
and 2 bone-timeline keys all came back sorted, while the 30
|
|
4885
|
-
and 3 `constraints` — arrays — came back in the build's own
|
|
4886
|
-
element, and each slider kept its place among them.
|
|
5079
|
+
🔬 **The editor re-keys every name-keyed OBJECT, and leaves `bones`, `slots` and
|
|
5080
|
+
`constraints` alone.** Read off its export of a rigc build (Spine 4.3.26,
|
|
5081
|
+
`gallery/look`): the `animations` object, a skin's 24 `attachments` slot keys and
|
|
5082
|
+
two animations' 16 and 2 bone-timeline keys all came back sorted, while the 30
|
|
5083
|
+
`bones`, 24 `slots` and 3 `constraints` — arrays — came back in the build's own
|
|
5084
|
+
order, element for element, and each slider kept its place among them.
|
|
5085
|
+
|
|
5086
|
+
🚨 **Not every array: `skins` it re-sorts.** That sentence read *"leaves every
|
|
5087
|
+
ARRAY alone"* for two releases, on those three arrays and nothing else, and
|
|
5088
|
+
`skins` is the one it was wrong about — see the paragraph at the end of this
|
|
5089
|
+
section.
|
|
4887
5090
|
|
|
4888
5091
|
⚠️ **The order it sorts them into is natural and case-insensitive, not
|
|
4889
5092
|
codepoint.** This paragraph said codepoint until
|
|
@@ -4900,10 +5103,13 @@ one key, deform blocks counted at each of their three levels;
|
|
|
4900
5103
|
⇒ in rigc: only `animations` is emitted sorted (R10), because it is the one
|
|
4901
5104
|
object measured here whose ORDER is also an index space — every reference into
|
|
4902
5105
|
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
|
-
|
|
5106
|
+
they are re-keyed. rigc emits **that comparator's own order** and refuses the name
|
|
5107
|
+
sets on which its leading-zero, case-tie, digit-against-word or separator
|
|
5108
|
+
behaviour — the four choices still unmeasured — would decide a pair. Sorting the
|
|
5109
|
+
105 collections that way reproduces **105 of 105**, the three codepoint cannot
|
|
5110
|
+
included, and refuses none of them; the codepoint rule that stood until
|
|
5111
|
+
[#543](https://github.com/firejune/rigc/issues/543) reproduced 102 and refused
|
|
5112
|
+
those same 3.
|
|
4907
5113
|
|
|
4908
5114
|
✅ **`events` is re-keyed too, and the references into it survive it.** The same
|
|
4909
5115
|
session measured it: `zebra, mike, alpha` came back `alpha, mike, zebra`, and the
|
|
@@ -4911,6 +5117,32 @@ firings still resolved **by name** — `0.3 -> mike`, `0.6 -> alpha`, payloads
|
|
|
4911
5117
|
intact (#539). So the editor treats `events` and `animations` differently, and
|
|
4912
5118
|
rigc emits events in the order you declare them.
|
|
4913
5119
|
|
|
5120
|
+
🚨 **`skins` is re-sorted, with `default` pinned first — measured, and it is the
|
|
5121
|
+
first array measured to move.** A four-skin rig built `default, zulu, mike, alpha`
|
|
5122
|
+
exported `default, alpha, mike, zulu`
|
|
5123
|
+
([#541](https://github.com/firejune/rigc/issues/541)), and the deform timelines
|
|
5124
|
+
came back keyed `mike, zulu` rather than `zulu, mike` with it. Note `alpha` sorts
|
|
5125
|
+
before `default` under every candidate comparator and still came back second: the
|
|
5126
|
+
default skin is **pinned**, not sorted. It matters for the same reason `animations`
|
|
5127
|
+
does — `skins` carries ordinals in the binary half, `skins[readInt()]` for an
|
|
5128
|
+
attachment timeline and `skins[skinIndex]` for a linked mesh — so this is the
|
|
5129
|
+
`animations` defect (#535) in the collection nobody had checked. ⇒ in rigc: R11.
|
|
5130
|
+
|
|
5131
|
+
⚠️ **What that measurement does *not* settle is which comparator.** `alpha, mike,
|
|
5132
|
+
zulu` is the order codepoint, case-folding and natural order all produce, so unlike
|
|
5133
|
+
the animations case nothing here refutes anything, and rigc refuses any skin-name
|
|
5134
|
+
pair the candidates could disagree about (R11).
|
|
5135
|
+
|
|
5136
|
+
✅ **The two readings this replaces.** A pull request once called `skins` *measured
|
|
5137
|
+
preserved*, on the strength of a one-skin rig where a one-element array comes back
|
|
5138
|
+
in order whatever the editor does to it;
|
|
5139
|
+
[#544](https://github.com/firejune/rigc/issues/544) corrected that to *unmeasured*,
|
|
5140
|
+
and added that it could not be measured because the editor refused a four-skin rig
|
|
5141
|
+
on import without printing a word. Both readings were the same generalisation — *an
|
|
5142
|
+
editor does not move arrays* — off the three arrays that were measured, and the
|
|
5143
|
+
"without a word" was rigc's own harness discarding the editor's stderr, which had
|
|
5144
|
+
named the cause all along (§3.4.2).
|
|
5145
|
+
|
|
4914
5146
|
### 10.2 Draw order
|
|
4915
5147
|
|
|
4916
5148
|
📗 **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
|
|