spine-rigc 0.4.0 β 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +21 -0
- package/README.md +336 -223
- package/bin/rigc.cjs +40 -0
- package/cli.ts +558 -49
- package/docs/AUTHORING.md +538 -23
- package/docs/PROMPTING.md +106 -0
- package/docs/SPEC_COVERAGE.md +16 -14
- package/package.json +4 -2
- package/src/compile.ts +189 -142
- package/src/emit.ts +88 -0
- package/src/json-position.ts +253 -0
- package/src/png.ts +72 -7
- package/src/preview.ts +243 -0
- package/src/render.ts +58 -0
- package/src/types.ts +8 -0
- package/src/validate.ts +58 -13
- package/tools/plate.ts +164 -21
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="assets/banner.svg" alt="rigc - Rig compiler for Spine" width="100%" />
|
|
2
|
+
<img src="https://raw.githubusercontent.com/firejune/rigc/main/assets/banner.svg" alt="rigc - Rig compiler for Spine" width="100%" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
@@ -53,6 +53,252 @@ weights. Every one of those loads clean, plays, and is wrong. rigc's answer is t
|
|
|
53
53
|
make the failure legible: compile from a spec, round-trip through the real parser,
|
|
54
54
|
run a list of named assertions, and **write nothing unless all of them are green.**
|
|
55
55
|
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
π¦ **rigc measures loose PNGs directly β one atlas page per image.** It is not an
|
|
59
|
+
atlas packer: packing several regions onto one page is tracked as
|
|
60
|
+
[issue #4](https://github.com/firejune/rigc/issues/4), not something the tool does
|
|
61
|
+
today.
|
|
62
|
+
|
|
63
|
+
rigc runs on [Bun](https://bun.sh). The package ships its TypeScript sources and
|
|
64
|
+
Bun runs them, so there is no build step and no `dist/` that can drift from the
|
|
65
|
+
repository it was cut from.
|
|
66
|
+
|
|
67
|
+
**The npm package is `spine-rigc`; the command it installs is `rigc`.** npm
|
|
68
|
+
refuses the name `rigc` as too similar to packages that already exist, so the
|
|
69
|
+
project, this repository and the executable keep their name and only the
|
|
70
|
+
registry entry is spelled out.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
bunx spine-rigc --help # run it without installing
|
|
74
|
+
bun add -g spine-rigc # or install the command
|
|
75
|
+
bun add -d spine-rigc # or pin it in a project
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`npx spine-rigc` works too, as long as Bun is on `PATH` β the executable is a
|
|
79
|
+
Bun script, and npm only writes the shim that calls it.
|
|
80
|
+
|
|
81
|
+
Installed, the command is `rigc`. The examples below spell it `bun cli.ts`
|
|
82
|
+
because they are written from a clone of this repository (`bun install`, then run
|
|
83
|
+
the CLI in place); the two are interchangeable β `rigc build β¦` is
|
|
84
|
+
`bun cli.ts build β¦`.
|
|
85
|
+
|
|
86
|
+
Two commands are repository workflows rather than package ones: `bench` and
|
|
87
|
+
`check` measure against Spine's official example projects and the reference
|
|
88
|
+
frames rendered from them, which are fetched rather than redistributed (see
|
|
89
|
+
[NOTICE.md](NOTICE.md)). They need a clone and `bun run fetch-examples`, and say
|
|
90
|
+
so by name when the corpus is absent.
|
|
91
|
+
|
|
92
|
+
## First rig in ten minutes
|
|
93
|
+
|
|
94
|
+
A whole rig, end to end, in a scratch directory: three tiny plates, two JSON
|
|
95
|
+
files, one `build`, one `validate`. No clone, no art pipeline, nothing fetched.
|
|
96
|
+
|
|
97
|
+
π« **Every value below is invented for this section** β a doll that exists
|
|
98
|
+
nowhere else in this repository. That is [AUTHORING.md](docs/AUTHORING.md) Β§3's
|
|
99
|
+
rule applied here: no example value in these documents is copied out of a
|
|
100
|
+
reference export, so nothing you read in a quickstart is an answer to anything
|
|
101
|
+
[the ladder](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) measures.
|
|
102
|
+
|
|
103
|
+
**1. Install the command.**
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
bun add -g spine-rigc # installs `rigc`
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or skip the install and prefix every command below with `bunx `, e.g.
|
|
110
|
+
`bunx spine-rigc build β¦`.
|
|
111
|
+
|
|
112
|
+
**2. Make a directory and three plates.** rigc measures PNGs rather than trusting
|
|
113
|
+
a number you typed (R5), so the art has to exist. These three are solid colours a
|
|
114
|
+
few dozen pixels across β a hull, a mast and a lamp:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
mkdir -p buoy/images && cd buoy
|
|
118
|
+
bun -e '
|
|
119
|
+
const parts = {
|
|
120
|
+
"images/hull.png": "iVBORw0KGgoAAAANSUhEUgAAADgAAAAMCAYAAAA3bX6lAAAAKElEQVR42mOI8bL6P5wxw6gHRz046sFRD456cNSDox4c9eCoBwcrBgDSZ+mdl2OiDgAAAABJRU5ErkJggg==",
|
|
121
|
+
"images/mast.png": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAA0CAYAAAC3t3ldAAAAH0lEQVR42mO4dunIf3yYYVTBqIJRBaMKRhWMKhgcCgBGJo4s9YnopgAAAABJRU5ErkJggg==",
|
|
122
|
+
"images/lamp.png": "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAHElEQVR42mP4v8HhPzUww6hBowaNGjRq0HAzCADvdrVmFPbc+QAAAABJRU5ErkJggg=="
|
|
123
|
+
};
|
|
124
|
+
for (const [p, b] of Object.entries(parts)) await Bun.write(p, Buffer.from(b, "base64"));
|
|
125
|
+
'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**3. The rig spec β `buoy.rig.json`.** Structure only: bones, the slots array in
|
|
129
|
+
draw order, and one skin mapping each slot to a plate.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"spec": "rigc-rig/1",
|
|
134
|
+
"name": "buoy",
|
|
135
|
+
"images": "images",
|
|
136
|
+
"skeleton": { "width": 200, "height": 200 },
|
|
137
|
+
"bones": [
|
|
138
|
+
{ "name": "root" },
|
|
139
|
+
{ "name": "hull", "parent": "root", "x": 0, "y": 0 },
|
|
140
|
+
{ "name": "mast", "parent": "hull", "x": 0, "y": 4 },
|
|
141
|
+
{ "name": "lamp", "parent": "mast", "x": 0, "y": 52 }
|
|
142
|
+
],
|
|
143
|
+
"slots": [
|
|
144
|
+
{ "name": "mast", "bone": "mast", "attachment": "mast" },
|
|
145
|
+
{ "name": "hull", "bone": "hull", "attachment": "hull" },
|
|
146
|
+
{ "name": "lamp", "bone": "lamp", "attachment": "lamp" }
|
|
147
|
+
],
|
|
148
|
+
"skins": {
|
|
149
|
+
"default": {
|
|
150
|
+
"mast": { "mast": { "image": "mast.png", "y": 26 } },
|
|
151
|
+
"hull": { "hull": { "image": "hull.png" } },
|
|
152
|
+
"lamp": { "lamp": { "image": "lamp.png" } }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Three things in there are worth naming, because each is a rule rather than a
|
|
159
|
+
style: the **slots array is the setup draw order** (R4) β index 0 is furthest
|
|
160
|
+
back, so the mast is behind the hull; the attachment carries an **`image`
|
|
161
|
+
instead of a `width`/`height`** (R5), which is what makes the size in the
|
|
162
|
+
skeleton and the size in the atlas incapable of drifting apart; and the mast's
|
|
163
|
+
`"y": 26` offsets the plate *within* its slot so the bone sits at the mast's foot
|
|
164
|
+
rather than its middle.
|
|
165
|
+
|
|
166
|
+
**4. The motion spec β `buoy.motion.json`.** Time only, aimed at the rig by name:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"spec": "rigc-motion/1",
|
|
171
|
+
"archetype": "buoy",
|
|
172
|
+
"cut": "buoy",
|
|
173
|
+
"easings": { "swing": [0.42, 0, 0.58, 1] },
|
|
174
|
+
"animations": {
|
|
175
|
+
"bob": {
|
|
176
|
+
"duration": 2,
|
|
177
|
+
"loop": true,
|
|
178
|
+
"tracks": [
|
|
179
|
+
{
|
|
180
|
+
"bone": "hull",
|
|
181
|
+
"property": "translatey",
|
|
182
|
+
"keys": [
|
|
183
|
+
{ "t": 0, "v": [0], "ease": "swing" },
|
|
184
|
+
{ "t": 0.5, "v": [5], "ease": "swing" },
|
|
185
|
+
{ "t": 1.5, "v": [-5], "ease": "swing" },
|
|
186
|
+
{ "t": 2, "v": [0] }
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"bone": "mast",
|
|
191
|
+
"property": "rotate",
|
|
192
|
+
"keys": [
|
|
193
|
+
{ "t": 0, "v": [-6], "ease": "swing" },
|
|
194
|
+
{ "t": 1, "v": [6], "ease": "swing" },
|
|
195
|
+
{ "t": 2, "v": [-6] }
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`archetype` must equal the rig's `name`. `duration` is declared and then checked
|
|
205
|
+
against what actually compiled (R7). The **last key of each track carries no
|
|
206
|
+
easing** β there is nothing after it to ease towards, and saying otherwise is a
|
|
207
|
+
compile error.
|
|
208
|
+
|
|
209
|
+
**5. Build, then re-gate what it wrote.**
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
rigc build --rig buoy.rig.json --motion buoy.motion.json --images images --out spine
|
|
213
|
+
rigc validate spine
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`build` prints every assertion by name, then the shape of what it emitted, then
|
|
217
|
+
the two files:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
.. pages=3 regions=3 bones=4 slots=3 animations=1 version=4.3.13 regionAttachments=3 meshAttachments=0 physicsConstraints=0 rig=buoy profile=spine
|
|
221
|
+
rigc: wrote β¦/buoy/spine/skeleton.json
|
|
222
|
+
rigc: wrote β¦/buoy/spine/skeleton.atlas
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`profile=spine` is the rulebook that judged it: *is this valid Spine 4.3 that any
|
|
226
|
+
runtime plays correctly?* That is the default, and the [Profiles](#profiles--wrong-versus-not-how-we-do-it-here)
|
|
227
|
+
section below is where the other one lives. `validate` then re-reads those
|
|
228
|
+
artifacts from disk and ends `rigc: green`. That is a rig. `spine/skeleton.json`
|
|
229
|
+
is Spine 4.3 skeleton data β it loads in a Spine runtime and it imports into the
|
|
230
|
+
Spine editor.
|
|
231
|
+
|
|
232
|
+
**Try breaking it**, because the validator's messages are the interface here and
|
|
233
|
+
they are worth meeting once on purpose. With `spine/` built, rename
|
|
234
|
+
`images/hull.png` to `images/raft.png` and re-run `rigc validate spine`:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
FAIL A17_ATLAS_PAGE_FILES_EXIST: page "../images/hull.png" is not on disk at β¦/images/hull.png
|
|
238
|
+
rigc: 1 assertion(s) failed
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Put the name back and it is green again. The same gate runs inside `build`, and
|
|
242
|
+
a FAIL there stops it **before it writes** β a red build leaves no half-built
|
|
243
|
+
artifact on disk to mistake for a result, and there is no flag that changes that.
|
|
244
|
+
|
|
245
|
+
**6. See what you built.**
|
|
246
|
+
|
|
247
|
+
π¨ **Green is a claim about validity and about nothing else.** A rig whose head
|
|
248
|
+
sits visibly off its torso passes every assertion, loads in `spine-core` and steps
|
|
249
|
+
numerically clean β the offsets are the ones your spec asked for, and no
|
|
250
|
+
assertion can know you did not mean them. The only remedy is looking, and both
|
|
251
|
+
commands below need nothing you do not already have: no reference frames, no
|
|
252
|
+
second package, no server.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
rigc render --candidate spine # PNG frames + a contact sheet, in render/
|
|
256
|
+
rigc preview --candidate spine # one .html file that plays it: preview.html
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**`render`** samples every animation at 12 fps and writes `render/<animation>/f0000.pngβ¦`
|
|
260
|
+
with a `contact.png` beside them β every frame of the shot as one labelled grid,
|
|
261
|
+
which is the picture to open first, because spacing is a comparison *across*
|
|
262
|
+
frames. It draws with rigc's own rasteriser (the one `check` measures with), so
|
|
263
|
+
it needs no browser and no network, and the `frames.json` it leaves beside the
|
|
264
|
+
directories makes the result a frame set like any other β the world box every
|
|
265
|
+
frame is a picture of. `--animation <name>` narrows it to one, `--fps` and
|
|
266
|
+
`--max` change the rate and the frame size.
|
|
267
|
+
|
|
268
|
+
**`preview`** writes a single self-contained `.html`: your skeleton, your atlas
|
|
269
|
+
and every page's PNG bytes are embedded in it as data URIs, and it plays them in
|
|
270
|
+
the **official [Spine Web Player](https://esotericsoftware.com/spine-player)**.
|
|
271
|
+
Double-click it, or attach it to a message β the file carries the whole artifact.
|
|
272
|
+
It is also the strongest interop statement in this repository: a rig that plays
|
|
273
|
+
there has been played by Esoteric Software's own runtime rather than by ours.
|
|
274
|
+
|
|
275
|
+
> βοΈ The player itself is **referenced, not embedded** β the page loads it from
|
|
276
|
+
> unpkg, so the first open needs a network, and rigc redistributes nothing
|
|
277
|
+
> Esoteric Software owns (see [NOTICE.md](NOTICE.md)). Everything the player
|
|
278
|
+
> draws is inside your file.
|
|
279
|
+
|
|
280
|
+
**Where to go next.**
|
|
281
|
+
|
|
282
|
+
- π **[docs/AUTHORING.md](docs/AUTHORING.md)** is the real guide β both files
|
|
283
|
+
field by field, the emission rules, every named failure mapped to the file that
|
|
284
|
+
has to change, and Β§8βΒ§9 for reproducing a shot you were given as pictures. It
|
|
285
|
+
ships inside the npm package too, at
|
|
286
|
+
`node_modules/spine-rigc/docs/AUTHORING.md`.
|
|
287
|
+
- `rigc explain --rig buoy.rig.json --motion buoy.motion.json --out spine` prints
|
|
288
|
+
the compiled rig as a table β every bone with its resolved parent, the slots in
|
|
289
|
+
draw order, every timeline key by key β and writes nothing. It is what to reach
|
|
290
|
+
for when a rig compiles and still looks wrong.
|
|
291
|
+
- π¨ **A green gate does not mean the animation is right**, and no assertion
|
|
292
|
+
could. If you have reference pictures of the shot,
|
|
293
|
+
`rigc check --candidate spine --frames <dir>` is the half of the loop that can
|
|
294
|
+
see a wrong animation β AUTHORING.md Β§9.
|
|
295
|
+
- [docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) is the benchmark: the same job, from a brief
|
|
296
|
+
and rendered frames, scored. [docs/PILOT.md](https://github.com/firejune/rigc/blob/main/docs/PILOT.md) is how to run an
|
|
297
|
+
agent through it and score what comes back.
|
|
298
|
+
- π€ **Handing the authoring to an AI agent?**
|
|
299
|
+
[docs/PROMPTING.md](docs/PROMPTING.md) is the operator's page β the six prompt
|
|
300
|
+
clauses a measured pilot run paid for, and what you can leave unsaid.
|
|
301
|
+
|
|
56
302
|
## The yardstick
|
|
57
303
|
|
|
58
304
|
The measure of whether this works is **Spine's own official example projects** β
|
|
@@ -193,16 +439,31 @@ the scale for a run.
|
|
|
193
439
|
|
|
194
440
|
There is no pass mark **in the tool**, for the same reason `diff` has none. The
|
|
195
441
|
ladder's pass definition and its thresholds are a document read by a person over
|
|
196
|
-
the whole table β [docs/
|
|
442
|
+
the whole table β [docs/GATE.md](https://github.com/firejune/rigc/blob/main/docs/GATE.md) states the clauses and
|
|
443
|
+
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md)'s *Operating rules* derives them β and not an
|
|
197
444
|
exit code either command could produce.
|
|
198
445
|
|
|
199
446
|
### Benchmark ladder β the rungs, and where they stand
|
|
200
447
|
|
|
201
|
-
**
|
|
448
|
+
π **The ladder is complete, 2026-08-28.** All eight numbered rungs and the
|
|
449
|
+
spineboy graduation exam are cleared under gate v2.1 and hold under **v2.2**, every clause PASS or SKIP:
|
|
450
|
+
worst attributable slot drift **5.55 px** against a 6.0 px bar, and **0 of 124**
|
|
451
|
+
frame-change disagreements. Recompiling the same spec in a different session
|
|
452
|
+
reproduced every field of the measurement record **to the digit**. The rungs stay
|
|
453
|
+
in place as regression gates.
|
|
454
|
+
|
|
455
|
+
β οΈ **What that certifies, stated exactly.** That **the tool, the guide and the
|
|
456
|
+
protocol reach the bar across a bounded series of honest attempts, each residual
|
|
457
|
+
diagnosed and fixed** β spineboy took five, and the last inherited its
|
|
458
|
+
predecessor's specs under the run protocol's inheritance clause. It is **not**
|
|
459
|
+
that an agent authors a spineboy-scale rig from the brief alone in one run: the
|
|
460
|
+
ladder has not demonstrated that, and each row records which of the two it is.
|
|
461
|
+
|
|
462
|
+
**[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) is the live ledger**: the rung order
|
|
202
463
|
(blockers β rung 3 first β 1 Β· 2 Β· 4 Β· 5 β 6 β 8 β 7 β spineboy), what each
|
|
203
464
|
rung gates on, how a rung is scored, the honesty rule that keeps the reference
|
|
204
465
|
export away from the authoring agent, the operating rules β what a pass is, and
|
|
205
|
-
the numbered thresholds of the current gate (**gate v2
|
|
466
|
+
the numbered thresholds of the current gate (**gate v2.2**, stated in [docs/GATE.md](https://github.com/firejune/rigc/blob/main/docs/GATE.md)) that decide one β and a status table. Run
|
|
206
467
|
one with:
|
|
207
468
|
|
|
208
469
|
```bash
|
|
@@ -225,9 +486,40 @@ that every example declares; and **B3**, every example ships a **packed** atlas
|
|
|
225
486
|
page) against rigc's one-part-per-page model, which `A06` enforced unconditionally. **B1 and B2 are
|
|
226
487
|
closed**; B3's validator half is (the packed-atlas clauses live behind `--profile`, above) and its
|
|
227
488
|
emitter half β no packer, no atlas importer β is not. Ordered gap list in Part 4 of that document;
|
|
228
|
-
live status, and B1's proof, in [docs/LADDER.md](docs/LADDER.md).
|
|
489
|
+
live status, and B1's proof, in [docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
|
490
|
+
|
|
491
|
+
## Looking at a rig β `rigc render` and `rigc preview`
|
|
492
|
+
|
|
493
|
+
The validator cannot see a wrong pose and says so honestly; `check` can, and needs
|
|
494
|
+
reference frames a first user does not have. That left looking as the one thing
|
|
495
|
+
the package could not do, and these two commands are it. Both take a compiled
|
|
496
|
+
artifact β the directory `build --out` wrote β and neither needs a reference, a
|
|
497
|
+
clone or a server:
|
|
229
498
|
|
|
230
|
-
|
|
499
|
+
```bash
|
|
500
|
+
rigc render --candidate spine [--animation <name>] [--fps 12] [--max 256] [--out render/]
|
|
501
|
+
rigc preview --candidate spine [--animation <name>] [--out preview.html]
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`render` writes `render/<animation>/f0000.pngβ¦` plus a `contact.png` grid of every
|
|
505
|
+
frame and a `frames.json` sidecar describing the world box they are pictures of β
|
|
506
|
+
the same frame-set shape `bench/render_reference.ts` writes and `rigc check`
|
|
507
|
+
reads, drawn by the same rasteriser, so the output is a frame set rather than a
|
|
508
|
+
pile of images. `preview` writes one self-contained `.html` that plays the
|
|
509
|
+
artifact in the official Spine Web Player, with the skeleton, the atlas and every
|
|
510
|
+
page embedded as data URIs; the player is loaded from unpkg rather than copied, so
|
|
511
|
+
the first open needs a network and rigc redistributes nothing Esoteric Software
|
|
512
|
+
owns ([NOTICE.md](NOTICE.md)).
|
|
513
|
+
|
|
514
|
+
They complement each other rather than overlap. `render` is offline, deterministic
|
|
515
|
+
and measurable β its pixels are the ones `check` reports on. `preview` is the
|
|
516
|
+
interop proof: what plays there was played by Esoteric's own runtime, not by ours.
|
|
517
|
+
|
|
518
|
+
## Run viewer β watching a *run* instead of reading it
|
|
519
|
+
|
|
520
|
+
π **This is the ladder's instrument, not the way to look at your own rig** β that
|
|
521
|
+
is the section above. The viewer is reference-bound and repository-bound, and it
|
|
522
|
+
deliberately never ships.
|
|
231
523
|
|
|
232
524
|
`check.txt` says a candidate's worst frame is f0012 at 56 MAE. The viewer shows
|
|
233
525
|
you f0012.
|
|
@@ -342,8 +634,15 @@ So `validate` and `build` take a `--profile`:
|
|
|
342
634
|
|
|
343
635
|
| Profile | Runs | For |
|
|
344
636
|
| --- | --- | --- |
|
|
345
|
-
| `spine
|
|
346
|
-
| `spine` |
|
|
637
|
+
| `spine` | the 20 validity rules | **the default.** Is this valid Spine 4.3 that any runtime plays correctly? |
|
|
638
|
+
| `spine-html` | all 34 | Opt-in. Is this a rig *this* project can ship? |
|
|
639
|
+
|
|
640
|
+
`spine` is the default because it is the question this package's output answers:
|
|
641
|
+
the artifact imports into the Spine editor and plays in any 4.3 runtime, and
|
|
642
|
+
that is what the 20 validity rules are about. The other 14 are somebody's policy
|
|
643
|
+
β one renderer's, one canvas budget's, one compiler's own formations' β and a
|
|
644
|
+
rig arriving from anywhere else has no stake in them. Ask for them with
|
|
645
|
+
`--profile spine-html` when you want them.
|
|
347
646
|
|
|
348
647
|
The **Profile** column below says which is which β `both` = validity, `renderer` and
|
|
349
648
|
`archetype` = `spine-html` only, and **`both β`** = a mixed assertion whose validity
|
|
@@ -374,7 +673,7 @@ the renderer policy*.
|
|
|
374
673
|
| `A16_SKELETON_VERSION_4_3` | both | the `skeleton.spine` version label is on the 4.3 line (the parser never checks it) |
|
|
375
674
|
| `A17_ATLAS_PAGE_FILES_EXIST` | both | every page the atlas declares is a file on disk |
|
|
376
675
|
| `A18_DETERMINISTIC_EMIT` | both | a second, independent compile of the same inputs is byte-identical. SKIPs when re-gating artifacts already on disk |
|
|
377
|
-
| `A19_OVERLAY_PNGS_HAVE_ALPHA` | renderer | every overlay
|
|
676
|
+
| `A19_OVERLAY_PNGS_HAVE_ALPHA` | renderer | every overlay part image can be transparent somewhere β an alpha channel (colour type 4 or 6) **or** a `tRNS` chunk, which is where indexed and greyscale PNGs keep theirs. Only the base plate β identified structurally as the region covering the stage β may be opaque |
|
|
378
677
|
| `A20_MESH_WEIGHTS_COHERENT` | both β | every weighted vertex has at least one bone, no negative weight, bone indices in range, and each vertex's weights sum to 1. `spine-html` also requires that a mesh be weighted at all and that no binding sit at weight 0 |
|
|
379
678
|
| `A21_MESH_RIM_PINNED` | archetype | a ring mesh's rim vertices are pinned to the anchor bone and its hull is a real ring; a ribbon's entry row stays put. **SKIPs on authored geometry** β rigc did not place its rim |
|
|
380
679
|
| `A22_MESH_UVS_IN_UNIT_RANGE` | both | every UV lies inside its region |
|
|
@@ -390,208 +689,6 @@ the renderer policy*.
|
|
|
390
689
|
| `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 |
|
|
391
690
|
| `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 |
|
|
392
691
|
|
|
393
|
-
## Install
|
|
394
|
-
|
|
395
|
-
rigc runs on [Bun](https://bun.sh). The package ships its TypeScript sources and
|
|
396
|
-
Bun runs them, so there is no build step and no `dist/` that can drift from the
|
|
397
|
-
repository it was cut from.
|
|
398
|
-
|
|
399
|
-
**The npm package is `spine-rigc`; the command it installs is `rigc`.** npm
|
|
400
|
-
refuses the name `rigc` as too similar to packages that already exist, so the
|
|
401
|
-
project, this repository and the executable keep their name and only the
|
|
402
|
-
registry entry is spelled out.
|
|
403
|
-
|
|
404
|
-
```bash
|
|
405
|
-
bunx spine-rigc --help # run it without installing
|
|
406
|
-
bun add -g spine-rigc # or install the command
|
|
407
|
-
bun add -d spine-rigc # or pin it in a project
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
`npx spine-rigc` works too, as long as Bun is on `PATH` β the executable is a
|
|
411
|
-
Bun script, and npm only writes the shim that calls it.
|
|
412
|
-
|
|
413
|
-
Installed, the command is `rigc`. The examples below spell it `bun cli.ts`
|
|
414
|
-
because they are written from a clone of this repository (`bun install`, then run
|
|
415
|
-
the CLI in place); the two are interchangeable β `rigc build β¦` is
|
|
416
|
-
`bun cli.ts build β¦`.
|
|
417
|
-
|
|
418
|
-
Two commands are repository workflows rather than package ones: `bench` and
|
|
419
|
-
`check` measure against Spine's official example projects and the reference
|
|
420
|
-
frames rendered from them, which are fetched rather than redistributed (see
|
|
421
|
-
[NOTICE.md](NOTICE.md)). They need a clone and `bun run fetch-examples`, and say
|
|
422
|
-
so by name when the corpus is absent.
|
|
423
|
-
|
|
424
|
-
## First rig in ten minutes
|
|
425
|
-
|
|
426
|
-
A whole rig, end to end, in a scratch directory: three tiny plates, two JSON
|
|
427
|
-
files, one `build`, one `validate`. No clone, no art pipeline, nothing fetched.
|
|
428
|
-
|
|
429
|
-
π« **Every value below is invented for this section** β a doll that exists
|
|
430
|
-
nowhere else in this repository. That is [AUTHORING.md](docs/AUTHORING.md) Β§3's
|
|
431
|
-
rule applied here: no example value in these documents is copied out of a
|
|
432
|
-
reference export, so nothing you read in a quickstart is an answer to anything
|
|
433
|
-
[the ladder](docs/LADDER.md) measures.
|
|
434
|
-
|
|
435
|
-
**1. Install the command.**
|
|
436
|
-
|
|
437
|
-
```bash
|
|
438
|
-
bun add -g spine-rigc # installs `rigc`
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
Or skip the install and prefix every command below with `bunx `, e.g.
|
|
442
|
-
`bunx spine-rigc build β¦`.
|
|
443
|
-
|
|
444
|
-
**2. Make a directory and three plates.** rigc measures PNGs rather than trusting
|
|
445
|
-
a number you typed (R5), so the art has to exist. These three are solid colours a
|
|
446
|
-
few dozen pixels across β a hull, a mast and a lamp:
|
|
447
|
-
|
|
448
|
-
```bash
|
|
449
|
-
mkdir -p buoy/images && cd buoy
|
|
450
|
-
bun -e '
|
|
451
|
-
const parts = {
|
|
452
|
-
"images/hull.png": "iVBORw0KGgoAAAANSUhEUgAAADgAAAAMCAYAAAA3bX6lAAAAKElEQVR42mOI8bL6P5wxw6gHRz046sFRD456cNSDox4c9eCoBwcrBgDSZ+mdl2OiDgAAAABJRU5ErkJggg==",
|
|
453
|
-
"images/mast.png": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAA0CAYAAAC3t3ldAAAAH0lEQVR42mO4dunIf3yYYVTBqIJRBaMKRhWMKhgcCgBGJo4s9YnopgAAAABJRU5ErkJggg==",
|
|
454
|
-
"images/lamp.png": "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAHElEQVR42mP4v8HhPzUww6hBowaNGjRq0HAzCADvdrVmFPbc+QAAAABJRU5ErkJggg=="
|
|
455
|
-
};
|
|
456
|
-
for (const [p, b] of Object.entries(parts)) await Bun.write(p, Buffer.from(b, "base64"));
|
|
457
|
-
'
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
**3. The rig spec β `buoy.rig.json`.** Structure only: bones, the slots array in
|
|
461
|
-
draw order, and one skin mapping each slot to a plate.
|
|
462
|
-
|
|
463
|
-
```json
|
|
464
|
-
{
|
|
465
|
-
"spec": "rigc-rig/1",
|
|
466
|
-
"name": "buoy",
|
|
467
|
-
"images": "images",
|
|
468
|
-
"skeleton": { "width": 200, "height": 200 },
|
|
469
|
-
"bones": [
|
|
470
|
-
{ "name": "root" },
|
|
471
|
-
{ "name": "hull", "parent": "root", "x": 0, "y": 0 },
|
|
472
|
-
{ "name": "mast", "parent": "hull", "x": 0, "y": 4 },
|
|
473
|
-
{ "name": "lamp", "parent": "mast", "x": 0, "y": 52 }
|
|
474
|
-
],
|
|
475
|
-
"slots": [
|
|
476
|
-
{ "name": "mast", "bone": "mast", "attachment": "mast" },
|
|
477
|
-
{ "name": "hull", "bone": "hull", "attachment": "hull" },
|
|
478
|
-
{ "name": "lamp", "bone": "lamp", "attachment": "lamp" }
|
|
479
|
-
],
|
|
480
|
-
"skins": {
|
|
481
|
-
"default": {
|
|
482
|
-
"mast": { "mast": { "image": "mast.png", "y": 26 } },
|
|
483
|
-
"hull": { "hull": { "image": "hull.png" } },
|
|
484
|
-
"lamp": { "lamp": { "image": "lamp.png" } }
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
Three things in there are worth naming, because each is a rule rather than a
|
|
491
|
-
style: the **slots array is the setup draw order** (R4) β index 0 is furthest
|
|
492
|
-
back, so the mast is behind the hull; the attachment carries an **`image`
|
|
493
|
-
instead of a `width`/`height`** (R5), which is what makes the size in the
|
|
494
|
-
skeleton and the size in the atlas incapable of drifting apart; and the mast's
|
|
495
|
-
`"y": 26` offsets the plate *within* its slot so the bone sits at the mast's foot
|
|
496
|
-
rather than its middle.
|
|
497
|
-
|
|
498
|
-
**4. The motion spec β `buoy.motion.json`.** Time only, aimed at the rig by name:
|
|
499
|
-
|
|
500
|
-
```json
|
|
501
|
-
{
|
|
502
|
-
"spec": "rigc-motion/1",
|
|
503
|
-
"archetype": "buoy",
|
|
504
|
-
"cut": "buoy",
|
|
505
|
-
"easings": { "swing": [0.42, 0, 0.58, 1] },
|
|
506
|
-
"animations": {
|
|
507
|
-
"bob": {
|
|
508
|
-
"duration": 2,
|
|
509
|
-
"loop": true,
|
|
510
|
-
"tracks": [
|
|
511
|
-
{
|
|
512
|
-
"bone": "hull",
|
|
513
|
-
"property": "translatey",
|
|
514
|
-
"keys": [
|
|
515
|
-
{ "t": 0, "v": [0], "ease": "swing" },
|
|
516
|
-
{ "t": 0.5, "v": [5], "ease": "swing" },
|
|
517
|
-
{ "t": 1.5, "v": [-5], "ease": "swing" },
|
|
518
|
-
{ "t": 2, "v": [0] }
|
|
519
|
-
]
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
"bone": "mast",
|
|
523
|
-
"property": "rotate",
|
|
524
|
-
"keys": [
|
|
525
|
-
{ "t": 0, "v": [-6], "ease": "swing" },
|
|
526
|
-
{ "t": 1, "v": [6], "ease": "swing" },
|
|
527
|
-
{ "t": 2, "v": [-6] }
|
|
528
|
-
]
|
|
529
|
-
}
|
|
530
|
-
]
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
`archetype` must equal the rig's `name`. `duration` is declared and then checked
|
|
537
|
-
against what actually compiled (R7). The **last key of each track carries no
|
|
538
|
-
easing** β there is nothing after it to ease towards, and saying otherwise is a
|
|
539
|
-
compile error.
|
|
540
|
-
|
|
541
|
-
**5. Build, then re-gate what it wrote.**
|
|
542
|
-
|
|
543
|
-
```bash
|
|
544
|
-
rigc build --rig buoy.rig.json --motion buoy.motion.json --images images --out spine
|
|
545
|
-
rigc validate spine
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
`build` prints every assertion by name, then the shape of what it emitted, then
|
|
549
|
-
the two files:
|
|
550
|
-
|
|
551
|
-
```
|
|
552
|
-
.. pages=3 regions=3 bones=4 slots=3 animations=1 version=4.3.13 regionAttachments=3 meshAttachments=0 physicsConstraints=0 rig=buoy profile=spine-html
|
|
553
|
-
rigc: wrote β¦/buoy/spine/skeleton.json
|
|
554
|
-
rigc: wrote β¦/buoy/spine/skeleton.atlas
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
and `validate` re-reads those artifacts from disk and ends `rigc: green`. That is
|
|
558
|
-
a rig. `spine/skeleton.json` is Spine 4.3 skeleton data β it loads in a Spine
|
|
559
|
-
runtime and it imports into the Spine editor.
|
|
560
|
-
|
|
561
|
-
**Try breaking it**, because the validator's messages are the interface here and
|
|
562
|
-
they are worth meeting once on purpose. Rename `images/hull.png` to
|
|
563
|
-
`images/raft.png`, point the spec's `image` at the new name, and build again:
|
|
564
|
-
|
|
565
|
-
```
|
|
566
|
-
FAIL A08_REGION_NAMES_MATCH_ATTACHMENTS: attachment "hull" resolves to region "raft"; v0 requires them identical
|
|
567
|
-
rigc: 1 assertion(s) failed β nothing written
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
Nothing was written. A red run leaves no half-built artifact on disk to mistake
|
|
571
|
-
for a result, and there is no flag that changes that.
|
|
572
|
-
|
|
573
|
-
**Where to go next.**
|
|
574
|
-
|
|
575
|
-
- π **[docs/AUTHORING.md](docs/AUTHORING.md)** is the real guide β both files
|
|
576
|
-
field by field, the emission rules, every named failure mapped to the file that
|
|
577
|
-
has to change, and Β§8βΒ§9 for reproducing a shot you were given as pictures. It
|
|
578
|
-
ships inside the npm package too, at
|
|
579
|
-
`node_modules/spine-rigc/docs/AUTHORING.md`.
|
|
580
|
-
- `rigc explain --rig buoy.rig.json --motion buoy.motion.json --out spine` prints
|
|
581
|
-
the compiled rig as a table β every bone with its resolved parent, the slots in
|
|
582
|
-
draw order, every timeline key by key β and writes nothing. It is what to reach
|
|
583
|
-
for when a rig compiles and still looks wrong.
|
|
584
|
-
- π¨ **A green gate does not mean the animation is right**, and no assertion
|
|
585
|
-
could. If you have reference pictures of the shot,
|
|
586
|
-
`rigc check --candidate spine --frames <dir>` is the half of the loop that can
|
|
587
|
-
see a wrong animation β AUTHORING.md Β§9.
|
|
588
|
-
- [docs/LADDER.md](docs/LADDER.md) is the benchmark: the same job, from a brief
|
|
589
|
-
and rendered frames, scored. [docs/PILOT.md](docs/PILOT.md) is how to run an
|
|
590
|
-
agent through it and score what comes back.
|
|
591
|
-
- π€ **Handing the authoring to an AI agent?**
|
|
592
|
-
[docs/PROMPTING.md](docs/PROMPTING.md) is the operator's page β the six prompt
|
|
593
|
-
clauses a measured pilot run paid for, and what you can leave unsaid.
|
|
594
|
-
|
|
595
692
|
## Usage
|
|
596
693
|
|
|
597
694
|
π **Writing a spec? Read [docs/AUTHORING.md](docs/AUTHORING.md) first.** It is the
|
|
@@ -615,6 +712,11 @@ bun cli.ts build \
|
|
|
615
712
|
[--manifest path/to/manifest.json] [--images path/to/images]
|
|
616
713
|
```
|
|
617
714
|
|
|
715
|
+
By default, atlas page paths point back at the source art wherever it lives β
|
|
716
|
+
often outside `--out` β so add `--copy-images` when `spine/` itself needs to be
|
|
717
|
+
self-contained (zipped, committed, or handed off on its own): it copies every
|
|
718
|
+
referenced page PNG into `--out` and rewrites the atlas to match.
|
|
719
|
+
|
|
618
720
|
β¦or register cuts in a `cuts.json` and build them by name. Every path in the table
|
|
619
721
|
resolves **relative to the `cuts.json` file itself**, so the table lives with the
|
|
620
722
|
project that owns the art:
|
|
@@ -640,17 +742,24 @@ commands:
|
|
|
640
742
|
```bash
|
|
641
743
|
bun cli.ts explain --cut my_cut --cuts path/to/cuts.json # the compiled rig as a table
|
|
642
744
|
bun cli.ts validate path/to/spine # re-gate artifacts already on disk
|
|
643
|
-
bun cli.ts validate --profile spine path/to/
|
|
745
|
+
bun cli.ts validate --profile spine-html path/to/spine # β¦and this project's policy too (see Profiles)
|
|
644
746
|
bun cli.ts diff candidate.json reference.json # structural comparison
|
|
645
747
|
bun cli.ts check --candidate path/to/spine \
|
|
646
748
|
--frames bench/reference/3-timing-and-spacing # against pictures
|
|
647
749
|
bun cli.ts bench 3 --candidate path/to/spine # one rung of the ladder
|
|
750
|
+
bun cli.ts render --candidate path/to/spine # PNG frames + a contact sheet
|
|
751
|
+
bun cli.ts preview --candidate path/to/spine # one .html that plays it
|
|
648
752
|
```
|
|
649
753
|
|
|
650
754
|
`validate` on a bare directory checks what it can see. Adding `--cut`/`--cuts` lets
|
|
651
755
|
it re-derive the declared durations and the structural expectations too, and the
|
|
652
|
-
report says which it had. `build` and `validate` both
|
|
653
|
-
the
|
|
756
|
+
report says which it had. `build` and `validate` both default to `--profile spine`,
|
|
757
|
+
the 20 validity rules; `--profile spine-html` adds this project's renderer and
|
|
758
|
+
archetype policy on top.
|
|
759
|
+
|
|
760
|
+
`render` and `preview` are the two that need no reference at all β see
|
|
761
|
+
[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.
|
|
654
763
|
|
|
655
764
|
## Checks
|
|
656
765
|
|
|
@@ -661,7 +770,7 @@ bun run selftest # the validator's own negative controls (next section)
|
|
|
661
770
|
```
|
|
662
771
|
|
|
663
772
|
All three run on every push and pull request β
|
|
664
|
-
[`.github/workflows/ci.yml`](.github/workflows/ci.yml). Bun runs the sources
|
|
773
|
+
[`.github/workflows/ci.yml`](https://github.com/firejune/rigc/blob/main/.github/workflows/ci.yml). Bun runs the sources
|
|
665
774
|
directly, so the first two are not on the path of anything; they exist because a
|
|
666
775
|
convention nothing checks is a convention. `tsconfig.json` is
|
|
667
776
|
`strict: false` with `strictNullChecks: true` and says in place why the rest is
|
|
@@ -681,7 +790,7 @@ for each. Two further edits are *tolerance* controls the gate must let through,
|
|
|
681
790
|
because a widened assertion can fail by firing too often as easily as by firing too
|
|
682
791
|
rarely.
|
|
683
792
|
|
|
684
|
-
**The rigs it breaks are generated.** [`fixtures/public.ts`](fixtures/public.ts)
|
|
793
|
+
**The rigs it breaks are generated.** [`fixtures/public.ts`](https://github.com/firejune/rigc/blob/main/fixtures/public.ts)
|
|
685
794
|
writes three synthetic cuts into a temp directory on every run, and between them
|
|
686
795
|
they carry every structure the assertions have an opinion about β region
|
|
687
796
|
attachments, attachment swaps, rgba fades, a ring mesh on a control bone, a ribbon
|
|
@@ -747,7 +856,7 @@ nothing substantive executed exits 2 rather than printing green.
|
|
|
747
856
|
|
|
748
857
|
```
|
|
749
858
|
tsconfig.json type-check config (noEmit); eslint.config.js β the no-any gate
|
|
750
|
-
cli.ts build / validate / explain / diff / check / bench
|
|
859
|
+
cli.ts build / validate / explain / diff / check / bench / render / preview
|
|
751
860
|
selftest.ts the validator's own negative controls, and diff's and check's
|
|
752
861
|
fixtures/ public.ts β the three synthetic cuts the selftest breaks
|
|
753
862
|
src/
|
|
@@ -755,14 +864,17 @@ src/
|
|
|
755
864
|
rig.ts the rig spec β `spec: "rigc-rig/1"`, the skeleton as data
|
|
756
865
|
validate.ts spine-core round trip + the 34 assertions
|
|
757
866
|
diff.ts structural comparison of two skeletons, one ratio per measure
|
|
758
|
-
render.ts the rasteriser (regions + meshes), shared by the reference renderer
|
|
867
|
+
render.ts the rasteriser (regions + meshes), shared by the reference renderer,
|
|
868
|
+
`rigc render` and check
|
|
869
|
+
preview.ts the single-file HTML player page β the artifact embedded as data
|
|
870
|
+
URIs, played by the official Spine Web Player (referenced, not vendored)
|
|
759
871
|
check.ts a candidate against rendered frames β pixels and per-slot drift,
|
|
760
872
|
and it never opens the reference skeleton
|
|
761
873
|
ladder.ts which example is which rung, and which file in it is the reference
|
|
762
874
|
timelines.ts the 4.3 timeline catalogue and its walker (shared, pure JSON)
|
|
763
875
|
mesh.ts ring and ribbon mesh builders, weighted-vertex encoding
|
|
764
876
|
transform.ts crop pixels (y down) <-> Spine world (y up), world transforms
|
|
765
|
-
png.ts PNG header reader (size
|
|
877
|
+
png.ts PNG header reader (size, colour type, tRNS; no pixel decode)
|
|
766
878
|
errors.ts CompileError, and NotImplementedError for what the format holds
|
|
767
879
|
and the emitter does not write
|
|
768
880
|
types.ts manifest, motion spec, and emitted-JSON shapes
|
|
@@ -779,7 +891,8 @@ viewer/ the run viewer β dev server only, no build (see above)
|
|
|
779
891
|
vite.config.ts /api/inventory and /repo/<path>, and the build refusal
|
|
780
892
|
inventory.ts what is under bench/runs, resolved to URLs
|
|
781
893
|
main.ts the two panes, the transport, the report
|
|
782
|
-
docs/ AUTHORING.md (how to author a rig),
|
|
894
|
+
docs/ AUTHORING.md (how to author a rig), GATE.md (the clause statements
|
|
895
|
+
a candidate is graded against), LADDER.md (live rung status),
|
|
783
896
|
SPEC_COVERAGE.md (format survey),
|
|
784
897
|
feature_matrix.{csv,json}
|
|
785
898
|
.github/ workflows/ β ci.yml (the gates) and release.yml (release-please)
|
|
@@ -792,14 +905,14 @@ CONTRIBUTING.md how to propose a change; RELEASING.md β how a version is cut
|
|
|
792
905
|
| --- | --- |
|
|
793
906
|
| `measure_contact_depth.ts` | measures a cut's contact depth from its plates, with the two-sided proof it has to satisfy. Both slot names are required: which plate is the mass and which is the occluder is a fact about one cut, and a default would measure the wrong pair and still print a number |
|
|
794
907
|
| `contact.ts` | plate-vs-plate overlap measurement β the largest advance that keeps two footprints disjoint |
|
|
795
|
-
| `plate.ts` / `png_probe.mjs` | minimal PNG read/write and decode |
|
|
908
|
+
| `plate.ts` / `png_probe.mjs` | minimal PNG read/write and decode. The writer emits colour type 6 only; the reader takes every colour type and bit depth PNG allows except interlaced, expanding indexed palettes (`PLTE` + `tRNS`) and greyscale to RGBA β because the gate accepts that art, so the renderer has to as well (issue #226) |
|
|
796
909
|
| `font5x7.ts` | bitmap labels for diagnostic images and generated plates |
|
|
797
910
|
|
|
798
911
|
## Contributing
|
|
799
912
|
|
|
800
|
-
Issues are the ledger; see [CONTRIBUTING.md](CONTRIBUTING.md) for what a change
|
|
913
|
+
Issues are the ledger; see [CONTRIBUTING.md](https://github.com/firejune/rigc/blob/main/CONTRIBUTING.md) for what a change
|
|
801
914
|
has to clear before it lands. Releases are cut by release-please β
|
|
802
|
-
[RELEASING.md](RELEASING.md).
|
|
915
|
+
[RELEASING.md](https://github.com/firejune/rigc/blob/main/RELEASING.md).
|
|
803
916
|
|
|
804
917
|
## Licence
|
|
805
918
|
|