spine-rigc 0.6.0 β 0.7.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 +92 -11
- package/cli.ts +366 -15
- package/docs/AUTHORING.md +290 -9
- package/package.json +1 -1
- package/src/ballot.ts +869 -0
- package/src/compile.ts +612 -0
- package/src/preview.ts +2 -2
- package/src/types.ts +163 -0
- package/src/validate.ts +229 -1
package/README.md
CHANGED
|
@@ -277,6 +277,22 @@ there has been played by Esoteric Software's own runtime rather than by ours.
|
|
|
277
277
|
> Esoteric Software owns (see [NOTICE.md](NOTICE.md)). Everything the player
|
|
278
278
|
> draws is inside your file.
|
|
279
279
|
|
|
280
|
+
**7. Let someone choose.** Sooner or later you will have two builds that both pass
|
|
281
|
+
the gate and no instrument that can separate them. `vote` puts them in one page
|
|
282
|
+
side by side, labelled `A` and `B` with no paths on screen, and takes an answer
|
|
283
|
+
back:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
rigc vote --candidate spine-a --candidate spine-b # -> ballot.html, open it and pick one
|
|
287
|
+
rigc vote --record vote-<id>.json # -> checks the answer into votes.jsonl
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The voter picks a winner or says "tie / no preference"; the page hands them a
|
|
291
|
+
small JSON file to save; `--record` checks that file against the ballot's own
|
|
292
|
+
hashes and appends one line to an append-only ledger, refusing by name anything
|
|
293
|
+
that does not belong to it. See
|
|
294
|
+
[Letting someone choose](#letting-someone-choose--rigc-vote).
|
|
295
|
+
|
|
280
296
|
**Where to go next.**
|
|
281
297
|
|
|
282
298
|
- π **[docs/AUTHORING.md](docs/AUTHORING.md)** is the real guide β both files
|
|
@@ -515,6 +531,59 @@ They complement each other rather than overlap. `render` is offline, determinist
|
|
|
515
531
|
and measurable β its pixels are the ones `check` reports on. `preview` is the
|
|
516
532
|
interop proof: what plays there was played by Esoteric's own runtime, not by ours.
|
|
517
533
|
|
|
534
|
+
### Letting someone choose β `rigc vote`
|
|
535
|
+
|
|
536
|
+
Sometimes looking is not enough on its own, because there is more than one
|
|
537
|
+
candidate and no instrument that can separate them: a pose fit with two local
|
|
538
|
+
optima that measure the same, a key density that is a matter of taste, a first
|
|
539
|
+
draft with no reference to compare against. `vote` is the deliberate human gate
|
|
540
|
+
for exactly that residue, and only for that residue.
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
rigc vote --candidate spine-a --candidate spine-b [--animation <name>] [--out ballot.html]
|
|
544
|
+
rigc vote --record vote-<id>.json [--ballot ballot.html] [--ledger votes.jsonl] [--again]
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
The first form writes one self-contained `ballot.html`: two to four compiled
|
|
548
|
+
candidates side by side, each in its own official player, looping, with one
|
|
549
|
+
button that restarts them together. The panes are labelled `A`, `B`, `C`, `D` and
|
|
550
|
+
show **no paths** β a voter who can see that `B` came out of `experiments/` is not
|
|
551
|
+
comparing pictures any more β so the pathβlabel mapping lives in a manifest
|
|
552
|
+
embedded in the same file and is never rendered. A voter picks a winner or says
|
|
553
|
+
"tie / no preference", optionally writes a sentence, and copies or downloads a
|
|
554
|
+
small JSON result the page prints the filename for.
|
|
555
|
+
|
|
556
|
+
The second form checks that result against the ballot's own manifest and appends
|
|
557
|
+
it to an append-only JSONL ledger. Nothing is trusted: the result carries a
|
|
558
|
+
content **digest** per candidate, and a result whose digests are not this
|
|
559
|
+
ballot's, whose choice is not on it, or whose reason code contradicts its choice
|
|
560
|
+
is refused by a named rule (`V02_CANDIDATE_DIGESTS_ARE_THE_BALLOTS` and friends)
|
|
561
|
+
with nothing appended. A second vote on one ballot needs `--again`.
|
|
562
|
+
|
|
563
|
+
The loop it is built for, in one line: **the agent compiles N candidates that all
|
|
564
|
+
pass the gate β `rigc vote` writes the ballot β a human opens it, watches, and
|
|
565
|
+
votes β `rigc vote --record` checks the answer into `votes.jsonl` β the agent
|
|
566
|
+
reads the ledger and proceeds.** Compile first, vote last: a candidate reaches a
|
|
567
|
+
ballot only because it already validated green, so the human is never asked to
|
|
568
|
+
read JSON, a diff or a spec.
|
|
569
|
+
|
|
570
|
+
Three properties are worth stating because they are what make the ledger usable
|
|
571
|
+
by the next agent rather than by a reader:
|
|
572
|
+
|
|
573
|
+
- **A tie is a recorded outcome, not a missing one.** The ledger distinguishes a
|
|
574
|
+
ballot with a winner, a ballot the human called a tie, and a ballot nobody
|
|
575
|
+
opened. `both-unacceptable` is the tie that means *propose again*, and it is
|
|
576
|
+
unreachable if ties are not recordable.
|
|
577
|
+
- **The winner is a digest, not a label.** `B` means nothing outside one ballot;
|
|
578
|
+
the digest identifies the same pixels anywhere. Every line also carries its
|
|
579
|
+
`coverage` β which candidates the vote compared β so completeness is
|
|
580
|
+
computable rather than assumed.
|
|
581
|
+
- **Every line carries a reason code** from a closed enumeration, and the
|
|
582
|
+
enumeration is enforced: "tie, because this one is better" is refused.
|
|
583
|
+
|
|
584
|
+
Same player, same posture as `preview`: referenced from a CDN, never vendored,
|
|
585
|
+
and the file contains only your own art ([NOTICE.md](NOTICE.md)).
|
|
586
|
+
|
|
518
587
|
## Run viewer β watching a *run* instead of reading it
|
|
519
588
|
|
|
520
589
|
π **This is the ladder's instrument, not the way to look at your own rig** β that
|
|
@@ -586,8 +655,11 @@ the rest of the repository must not have) and is type-checked on its own with
|
|
|
586
655
|
- A **motion spec** (`MotionSpec`, `spec: "rigc-motion/1"`) owns **time**: the rig
|
|
587
656
|
it was authored against, named easing handles, setup overrides, a physics tuning
|
|
588
657
|
table, and the animations β each with a declared duration, a loop flag, its
|
|
589
|
-
tracks, and
|
|
590
|
-
|
|
658
|
+
tracks, and five timeline families that sit on the animation rather than in `tracks`:
|
|
659
|
+
`drawOrder` and `events`, which name no target at all, and `ik`, `transform`
|
|
660
|
+
and `deform`, whose keys carry named fields instead of one value (an IK mix and
|
|
661
|
+
softness, six transform mixes, a sparse run of vertex offsets) β which is also
|
|
662
|
+
where 4.3 writes each of them.
|
|
591
663
|
|
|
592
664
|
**Outputs β two files per cut**, written to the cut's `out` directory:
|
|
593
665
|
|
|
@@ -613,7 +685,7 @@ model (what is pinned, what may move, how authority falls off), and the
|
|
|
613
685
|
### The validator
|
|
614
686
|
|
|
615
687
|
[`src/validate.ts`](src/validate.ts) parses the emitted artifacts with `spine-core`
|
|
616
|
-
and then runs
|
|
688
|
+
and then runs 36 named assertions over the loaded skeleton. Each one exists because
|
|
617
689
|
the failure it catches is **silent**: the file loads, animates, and lies.
|
|
618
690
|
|
|
619
691
|
Assertions whose data is absent are reported as **SKIP**, never folded into the pass
|
|
@@ -621,7 +693,7 @@ count β an assertion with nothing to check has not checked anything.
|
|
|
621
693
|
|
|
622
694
|
#### Profiles β "wrong" versus "not how we do it here"
|
|
623
695
|
|
|
624
|
-
Not all
|
|
696
|
+
Not all 36 rules are about Spine. Some are about **spine-html**, the renderer this
|
|
625
697
|
compiler was built to feed, and about one project's frame budget; they fire on real,
|
|
626
698
|
correct, editor-produced Spine data, because the official example projects carry
|
|
627
699
|
clipping attachments, unweighted meshes, 116-triangle meshes and packed atlases β
|
|
@@ -634,12 +706,12 @@ So `validate` and `build` take a `--profile`:
|
|
|
634
706
|
|
|
635
707
|
| Profile | Runs | For |
|
|
636
708
|
| --- | --- | --- |
|
|
637
|
-
| `spine` | the
|
|
638
|
-
| `spine-html` | all
|
|
709
|
+
| `spine` | the 22 validity rules | **the default.** Is this valid Spine 4.3 that any runtime plays correctly? |
|
|
710
|
+
| `spine-html` | all 36 | Opt-in. Is this a rig *this* project can ship? |
|
|
639
711
|
|
|
640
712
|
`spine` is the default because it is the question this package's output answers:
|
|
641
713
|
the artifact imports into the Spine editor and plays in any 4.3 runtime, and
|
|
642
|
-
that is what the
|
|
714
|
+
that is what the 22 validity rules are about. The other 14 are somebody's policy
|
|
643
715
|
β one renderer's, one canvas budget's, one compiler's own formations' β and a
|
|
644
716
|
rig arriving from anywhere else has no stake in them. Ask for them with
|
|
645
717
|
`--profile spine-html` when you want them.
|
|
@@ -688,6 +760,8 @@ the renderer policy*.
|
|
|
688
760
|
| `A31_DRAW_ORDER_OFFSETS_RESOLVE` | both | every draw-order key resolves to a real permutation: known slots, one entry per slot, each landing inside the slots array, offsets in ascending slot order. The **only assertion that runs before `A00`** β descending offsets make `readDrawOrder`'s forward-only cursor spin rather than return, so the round trip is refused by name instead of attempted |
|
|
689
761
|
| `A32_EVENT_KEYS_RESOLVE` | both | every event key fires an event the skeleton declares, no key sits earlier in time than the one before it, and `volume`/`balance` appear only on an event with an `audio` path. Only the first of those is loud in the parser; the other two load clean and drop the firing or the value in silence. SKIPs when no animation carries an event timeline |
|
|
690
762
|
| `A33_VERTEX_ATTACHMENT_GEOMETRY` | both | every bounding box and clipping polygon states a `vertexCount` that agrees with its vertex array, its weighted run decodes to that many vertices with bone indices in range, and a clipping `end` names a slot that exists. All three load clean: a missing count reads as zero and empties the polygon, and a missing end slot makes the clip run to the bottom of the draw order. SKIPs when the skeleton carries neither type |
|
|
763
|
+
| `A34_CONSTRAINT_TIMELINE_TARGETS` | both | every `ik` / `transform` timeline names a constraint of that type and carries at least one key. The name-and-type miss is loud in the parser (`IK Constraint not found`) and this one says which constraints the skeleton *does* have; the empty key array is silent β `readAnimation` reads key 0, finds nothing and skips the timeline without a word. SKIPs when no animation carries one |
|
|
764
|
+
| `A35_DEFORM_KEYS_FIT_THE_ATTACHMENT` | both | every deform key's run lands inside the attachment's own deform array, starts on an even index, holds an even count of finite numbers, and names a skin/slot/attachment triple that resolves. The array is one `x, y` pair per **vertex** on an unweighted attachment and one per **bone influence** on a weighted one, so its length is measured from the attachment rather than assumed. An overlong run is the format's quietest defect: `Utils.arrayCopy` into a `Float32Array` drops everything past the end, so part of the mesh deforms and it looks nearly right. SKIPs when no animation carries a deform timeline |
|
|
691
765
|
|
|
692
766
|
## Usage
|
|
693
767
|
|
|
@@ -749,17 +823,21 @@ bun cli.ts check --candidate path/to/spine \
|
|
|
749
823
|
bun cli.ts bench 3 --candidate path/to/spine # one rung of the ladder
|
|
750
824
|
bun cli.ts render --candidate path/to/spine # PNG frames + a contact sheet
|
|
751
825
|
bun cli.ts preview --candidate path/to/spine # one .html that plays it
|
|
826
|
+
bun cli.ts vote --candidate path/to/a --candidate path/to/b # one .html that asks which
|
|
827
|
+
bun cli.ts vote --record vote-<id>.json # check the answer into votes.jsonl
|
|
752
828
|
```
|
|
753
829
|
|
|
754
830
|
`validate` on a bare directory checks what it can see. Adding `--cut`/`--cuts` lets
|
|
755
831
|
it re-derive the declared durations and the structural expectations too, and the
|
|
756
832
|
report says which it had. `build` and `validate` both default to `--profile spine`,
|
|
757
|
-
the
|
|
833
|
+
the 22 validity rules; `--profile spine-html` adds this project's renderer and
|
|
758
834
|
archetype policy on top.
|
|
759
835
|
|
|
760
836
|
`render` and `preview` are the two that need no reference at all β see
|
|
761
837
|
[Looking at a rig](#looking-at-a-rig--rigc-render-and-rigc-preview). Run either
|
|
762
|
-
straight after a green `build`, on the same directory `--out` wrote.
|
|
838
|
+
straight after a green `build`, on the same directory `--out` wrote. `vote` is the
|
|
839
|
+
same idea with more than one candidate in the page and an answer coming back β
|
|
840
|
+
see [Letting someone choose](#letting-someone-choose--rigc-vote).
|
|
763
841
|
|
|
764
842
|
## Checks
|
|
765
843
|
|
|
@@ -856,18 +934,21 @@ nothing substantive executed exits 2 rather than printing green.
|
|
|
856
934
|
|
|
857
935
|
```
|
|
858
936
|
tsconfig.json type-check config (noEmit); eslint.config.js β the no-any gate
|
|
859
|
-
cli.ts build / validate / explain / diff / check / bench / render / preview
|
|
937
|
+
cli.ts build / validate / explain / diff / check / bench / render / preview / vote
|
|
860
938
|
selftest.ts the validator's own negative controls, and diff's and check's
|
|
861
939
|
fixtures/ public.ts β the three synthetic cuts the selftest breaks
|
|
862
940
|
src/
|
|
863
941
|
compile.ts rig + motion spec (+ manifest) -> skeleton JSON + atlas text (pure data assembly)
|
|
864
942
|
rig.ts the rig spec β `spec: "rigc-rig/1"`, the skeleton as data
|
|
865
|
-
validate.ts spine-core round trip + the
|
|
943
|
+
validate.ts spine-core round trip + the 36 assertions
|
|
866
944
|
diff.ts structural comparison of two skeletons, one ratio per measure
|
|
867
945
|
render.ts the rasteriser (regions + meshes), shared by the reference renderer,
|
|
868
946
|
`rigc render` and check
|
|
869
947
|
preview.ts the single-file HTML player page β the artifact embedded as data
|
|
870
948
|
URIs, played by the official Spine Web Player (referenced, not vendored)
|
|
949
|
+
ballot.ts the same page with 2β4 candidates in it and a vote coming back β
|
|
950
|
+
candidate digests, the ballot manifest, and the refusals that
|
|
951
|
+
stand between a saved vote and the ledger
|
|
871
952
|
check.ts a candidate against rendered frames β pixels and per-slot drift,
|
|
872
953
|
and it never opens the reference skeleton
|
|
873
954
|
ladder.ts which example is which rung, and which file in it is the reference
|