spine-rigc 0.24.0 → 0.25.1
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 +12 -4
- package/cli.ts +24 -8
- package/docs/AUTHORING.md +50 -12
- package/docs/FACE.md +9 -0
- package/docs/INGEST.md +69 -10
- package/docs/SPEC_COVERAGE.md +1 -1
- package/package.json +1 -1
- package/skills/ingest/SKILL.md +6 -4
- package/src/diff.ts +265 -6
- package/src/ingest.ts +112 -8
- package/src/validate.ts +268 -0
- package/tools/editor_roundtrip.ts +186 -8
package/README.md
CHANGED
|
@@ -503,7 +503,7 @@ commands take it and what its default is.
|
|
|
503
503
|
| `build … --pack` | the same build with every part arranged onto **shared** atlas pages, written into `--out` — losslessly, and gated a second time as the pair that ships. `--page-size` and `--padding` tune it |
|
|
504
504
|
| `build … --atlas-in <file.atlas>` | the same build with every part resolved to a **region of an existing pack** instead of a loose PNG; a name the atlas lacks, a size the spec disagrees with or a rectangle off its page is refused by name |
|
|
505
505
|
| `validate <dir>` | re-gates artifacts already on disk |
|
|
506
|
-
| `ingest <skeleton.json> --out <dir>` | `build` run backwards: reads a Spine 4.3 skeleton and writes the rig spec and motion spec that **rebuild it**, plus a findings report naming everything it could not carry. `--stage x,y,w,h` supplies the
|
|
506
|
+
| `ingest <skeleton.json> --out <dir>` | `build` run backwards: reads a Spine 4.3 skeleton and writes the rig spec and motion spec that **rebuild it**, plus a findings report naming everything it could not carry. `--stage x,y,w,h` supplies the stage for a skeleton that declares none — and is refused, rather than ignored, beside one that declares a box — and `--images <dir>` writes the spec's own images directory — the opposite direction from `build --images`, which overrides it — so the rebuild carries no flag at all |
|
|
507
507
|
| `explain --rig … --motion …` | the compiled rig as a table — every bone with its resolved parent, the slots in draw order, every timeline key by key. Writes nothing. What to reach for when a rig compiles and still looks wrong |
|
|
508
508
|
| `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/` |
|
|
509
509
|
| `preview --candidate <dir>` | one self-contained `.html` that plays it |
|
|
@@ -539,7 +539,7 @@ rebuild it. It is the only command that runs against `build`'s direction, and th
|
|
|
539
539
|
only one whose contract is an equality rather than a rulebook:
|
|
540
540
|
|
|
541
541
|
```bash
|
|
542
|
-
rigc ingest hero.json --out specs/ --
|
|
542
|
+
rigc ingest hero.json --out specs/ --images parts/
|
|
543
543
|
rigc build --rig specs/rig.json --motion specs/motion.json --out build/
|
|
544
544
|
rigc diff build/skeleton.json hero.json
|
|
545
545
|
```
|
|
@@ -580,7 +580,10 @@ One thing it drops on purpose and says so: a path attachment's `lengths`, which
|
|
|
580
580
|
- **The stage.** `skeleton.width`/`height`: rigc always writes one, and a skeleton that
|
|
581
581
|
carries none is refused by name unless `--stage x,y,w,h` supplies it. It is not
|
|
582
582
|
derivable — posing the rig gives the *animated* extent, which is a different number
|
|
583
|
-
from the setup box.
|
|
583
|
+
from the setup box. ⛔ **And `--stage` beside a box the file already states is refused
|
|
584
|
+
too**, for the opposite reason: two sources for one value, where the file is the record
|
|
585
|
+
of what was measured. It used to be read after the box and therefore never
|
|
586
|
+
([#626](https://github.com/firejune/rigc/issues/626)). ⚠️ This said *"an editor export carries none"* until #594 measured
|
|
584
587
|
it: **all twelve exports in the example corpus carry a stage** and none of them needs
|
|
585
588
|
the flag. It is still the value that costs least to get wrong, because `diff` reports
|
|
586
589
|
the box and gates nothing on it.
|
|
@@ -613,7 +616,12 @@ export, every `diff` measure that moved, `check`'s mean MAE and worst drift per
|
|
|
613
616
|
animation **for each skin the build declares** — one render-and-check block per
|
|
614
617
|
skin, with a per-skin roll-up under them, because a rig's contested art lives in
|
|
615
618
|
its named skins and a single un-skinned check draws none of it — and a
|
|
616
|
-
field-by-field list of what the editor rewrote.
|
|
619
|
+
field-by-field list of what the editor rewrote. Every step quotes what its child
|
|
620
|
+
said when that child did not do what it was for, the renderers included; a skin
|
|
621
|
+
**neither** side can draw — a hit-box rig, say — is a **SKIP** naming that, not a
|
|
622
|
+
red, because `check` had nothing to compare and `diff` and `validate` have
|
|
623
|
+
already measured the rig. One side drawing where the other does not is the
|
|
624
|
+
divergence the trip exists to find and stays a failure. On its first
|
|
617
625
|
run it found three emitter defects — [#368](https://github.com/firejune/rigc/issues/368),
|
|
618
626
|
[#369](https://github.com/firejune/rigc/issues/369),
|
|
619
627
|
[#370](https://github.com/firejune/rigc/issues/370) — and then showed that a
|
package/cli.ts
CHANGED
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
type DeformSpan,
|
|
68
68
|
} from './src/deformmeasure.ts';
|
|
69
69
|
import { diffLines, diffSkeletons, reportedFigures, sectionFigures, type DiffReport } from './src/diff.ts';
|
|
70
|
-
import { ingest, type IngestFindingKind, type IngestStage } from './src/ingest.ts';
|
|
70
|
+
import { ingest, IngestError, type IngestFindingKind, type IngestStage } from './src/ingest.ts';
|
|
71
71
|
import { copyAtlasImages } from './src/emit.ts';
|
|
72
72
|
import { DEFAULT_PADDING, DEFAULT_PAGE_SIZE, packAtlas } from './src/atlas.ts';
|
|
73
73
|
import { parseJsonWithPosition } from './src/json-position.ts';
|
|
@@ -2961,9 +2961,13 @@ const FLAG_MEANINGS: Record<string, string> = {
|
|
|
2961
2961
|
'--images <dir>` on every rebuild), `none` states width/height only for `build --atlas-in <pack>` to ' +
|
|
2962
2962
|
'resolve (default: loose)',
|
|
2963
2963
|
stage:
|
|
2964
|
-
|
|
2965
|
-
'
|
|
2966
|
-
"caller's value and
|
|
2964
|
+
'the setup bounding box — `skeleton.x,y,width,height` — for a skeleton that declares none. It cannot be ' +
|
|
2965
|
+
'derived: posing the rig gives the ANIMATED extent, which is a different number from the setup box, so this ' +
|
|
2966
|
+
"is the caller's value, and without it the missing stage is reported as a blocker. ⚠️ An editor export MAY " +
|
|
2967
|
+
'carry none; every editor export measured for this project carries one and ingest reads it straight through, ' +
|
|
2968
|
+
'so the flag is for a file that really has none rather than for editor exports as a class. ⛔ Beside a ' +
|
|
2969
|
+
'skeleton that already declares a box it is REFUSED rather than ignored: two sources for one value, and the ' +
|
|
2970
|
+
'file is the record of what was measured',
|
|
2967
2971
|
help: "show this command's flags and exit",
|
|
2968
2972
|
};
|
|
2969
2973
|
|
|
@@ -3308,13 +3312,15 @@ const USAGE = [
|
|
|
3308
3312
|
'ingest runs build backwards: it reads a Spine 4.3 skeleton.json and writes the rig',
|
|
3309
3313
|
'spec and motion spec that rebuild it, so an existing skeleton becomes a starting',
|
|
3310
3314
|
'point instead of something to retype:',
|
|
3311
|
-
' rigc ingest hero.json --out specs/ --
|
|
3315
|
+
' rigc ingest hero.json --out specs/ --images parts/ rig.json + motion.json',
|
|
3312
3316
|
'The contract is an equality, not a rulebook: build(ingest(x)) is x, byte for byte.',
|
|
3313
3317
|
'It reads the skeleton and nothing else — no .spine project, no binary .skel, no',
|
|
3314
3318
|
'atlas — so two things are the caller\'s and are refused rather than guessed: the',
|
|
3315
|
-
'setup stage (--stage
|
|
3316
|
-
'
|
|
3317
|
-
'
|
|
3319
|
+
'setup stage (--stage, only when the skeleton itself declares none — beside a box the',
|
|
3320
|
+
'file states, the flag is refused rather than ignored) and how the spec',
|
|
3321
|
+
'reaches the art (--art). --images <dir> is the third and the only optional one: it',
|
|
3322
|
+
'WRITES the rig spec\'s own images directory, relative to --out, so the rebuild needs',
|
|
3323
|
+
'no flag.',
|
|
3318
3324
|
'Everything the spec format cannot hold is printed as a named finding and',
|
|
3319
3325
|
'exits non-zero, with both files still written, because a spec plus a list of what',
|
|
3320
3326
|
'is missing from it beats no spec at all.',
|
|
@@ -3428,5 +3434,15 @@ try {
|
|
|
3428
3434
|
console.error(`rigc chainfit: ${err.message}`);
|
|
3429
3435
|
process.exit(2);
|
|
3430
3436
|
}
|
|
3437
|
+
// A usage error in kind — an option that contradicts the file it was given —
|
|
3438
|
+
// and exit 2 for that reason rather than 1: nothing was compiled and nothing
|
|
3439
|
+
// was written, so it is the invocation that has to change (issue #626). It is
|
|
3440
|
+
// raised in `src/ingest.ts` rather than here because the library caller who
|
|
3441
|
+
// passes the same contradiction deserves the same refusal, and one rule in one
|
|
3442
|
+
// place is what stops the two from drifting apart.
|
|
3443
|
+
if (err instanceof IngestError) {
|
|
3444
|
+
console.error(`rigc ingest: ${err.message}`);
|
|
3445
|
+
process.exit(2);
|
|
3446
|
+
}
|
|
3431
3447
|
throw err;
|
|
3432
3448
|
}
|
package/docs/AUTHORING.md
CHANGED
|
@@ -362,7 +362,7 @@ Everything above starts from two spec files you wrote. `rigc ingest` starts from
|
|
|
362
362
|
an existing rig is a starting point instead of 250 KB of arrays to retype.
|
|
363
363
|
|
|
364
364
|
```bash
|
|
365
|
-
bun cli.ts ingest hero.json --out specs/ --
|
|
365
|
+
bun cli.ts ingest hero.json --out specs/ --images parts/
|
|
366
366
|
# .. out /abs/path/specs
|
|
367
367
|
# .. art loose
|
|
368
368
|
# .. images ../parts/ (the rig spec's own, from /abs/path/specs)
|
|
@@ -397,7 +397,7 @@ repository builds on every run.
|
|
|
397
397
|
| `--art loose` (default) | name an `image` per attachment — `<path or placeholder>.png` — so the rebuild resolves loose PNGs and rigc measures them |
|
|
398
398
|
| `--art none` | state `width`/`height` only, so the rebuild is `build --atlas-in <pack.atlas>` and every part resolves out of the pack |
|
|
399
399
|
| `--images <dir>` | **write** the rig spec's own `images` directory, spelled relative to `--out`, so the rebuild is a plain `build --rig … --motion … --out …`. Without it the field is left out and every `image` resolves against `--out` itself, which holds the specs and no art — so every rebuild has to repeat `build --images <dir>`. Refused together with `--art none`, which writes no `image` for it to be the base of |
|
|
400
|
-
| `--stage x,y,w,h` | the setup bounding box
|
|
400
|
+
| `--stage x,y,w,h` | the setup bounding box, **for a skeleton that declares none**. An editor export *may* be one; every export under `examples/` carries a box and `ingest` reads it straight through — so passing the flag at one of them is **refused**, naming both boxes, rather than silently doing nothing ([#626](https://github.com/firejune/rigc/issues/626)) |
|
|
401
401
|
| `--name <n>` | the rig spec's `name`, which the motion spec's `archetype` must equal (default: the file's basename) |
|
|
402
402
|
|
|
403
403
|
⚠️ **`ingest --images` and `build --images` point opposite ways.** `build --images`
|
|
@@ -407,13 +407,36 @@ they name the same field — and `ingest` spells the value with the same functio
|
|
|
407
407
|
`build` spells `skeleton.images` with, so a spec and the skeleton it came from say
|
|
408
408
|
where the parts are in one convention.
|
|
409
409
|
|
|
410
|
-
🚨 **The stage is the
|
|
411
|
-
`skeleton.width`/`height
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
410
|
+
🚨 **The stage is one of the two values `ingest` will not guess.** A skeleton JSON
|
|
411
|
+
*need not* carry `skeleton.width`/`height`, and when it does not rigc cannot derive
|
|
412
|
+
one — posing the rig gives the *animated* extent, which is a different number from the
|
|
413
|
+
editor's setup box. So a file that declares none is a **blocker**, named, unless
|
|
414
|
+
`--stage x,y,w,h` supplies it; supply it from the project the file came from, or from
|
|
415
|
+
the editor's own canvas.
|
|
416
|
+
|
|
417
|
+
⛔ **The flag is refused beside a box the file states.** Two sources for one value, and
|
|
418
|
+
the file is the one that was measured — so `ingest` names both boxes and stops rather
|
|
419
|
+
than writing one of them and saying nothing. Drop the flag, or correct `skeleton` in the
|
|
420
|
+
source if its box is wrong ([#626](https://github.com/firejune/rigc/issues/626)). What
|
|
421
|
+
it does **not** do is refuse an *omitted origin*: inside a declared extent an omitted
|
|
422
|
+
`x`/`y` is `0` — the reading `build` emits and `diff` compares
|
|
423
|
+
([#620](https://github.com/firejune/rigc/issues/620)) — so the written spec states it
|
|
424
|
+
and a `LOSS HEADER_ORIGIN` line says the source omitted it and that the rebuild will
|
|
425
|
+
spell it ([#622](https://github.com/firejune/rigc/issues/622)).
|
|
426
|
+
|
|
427
|
+
⚠️ **This said an editor export "never" carries one until
|
|
428
|
+
[#594](https://github.com/firejune/rigc/issues/594) measured the corpus.** All twelve
|
|
429
|
+
exports under `examples/` declare `x`, `y`, `width` and `height`, `ingest` takes the
|
|
430
|
+
early return on every one of them, and not one needs the flag. What an editor export
|
|
431
|
+
*may* do is carry none: a rigc build that declares no stage
|
|
432
|
+
([#578](https://github.com/firejune/rigc/issues/578)) came back from a Spine 4.3.26
|
|
433
|
+
round trip with a header of `hash`, `spine`, `images`, `audio` and **no box at all** —
|
|
434
|
+
the editor preserves the absence rather than inventing a stage
|
|
435
|
+
([#616](https://github.com/firejune/rigc/issues/616)). So `--stage` is for a file that
|
|
436
|
+
really has none, and this repository's corpus holds no example of one. It is still the
|
|
437
|
+
value that costs least to get wrong: `diff` reports the box as `stage_present` and
|
|
438
|
+
`stage_box` ([#578](https://github.com/firejune/rigc/issues/578)) and both are
|
|
439
|
+
`(reported)`, so nothing on the ladder consults them.
|
|
417
440
|
|
|
418
441
|
⚠️ **The duration is a convention, and it is recorded as one.** Skeleton JSON has no
|
|
419
442
|
duration field. The largest key time is the only derivable answer and it is what a
|
|
@@ -430,7 +453,7 @@ the first:
|
|
|
430
453
|
| --- | --- |
|
|
431
454
|
| `BLOCK` | the spec format cannot say it, so the rebuild will **not** be the file that was read — `linkedmesh`, `point`, an attachment `sequence`, an unknown field on a bone, slot or constraint, a timeline family the motion spec has no track for. The command exits non-zero **and still writes both specs**, because a spec plus a list of what is missing from it beats no spec |
|
|
432
455
|
| `JUDGE` | the skeleton cannot answer and somebody has to: the stage, and each animation's duration |
|
|
433
|
-
| `LOSS` | the skeleton
|
|
456
|
+
| `LOSS` | the skeleton's spelling and rigc's differ, on purpose, and the line says how. A path attachment's `lengths` is the one that matters — it is `PathConstraint`'s own four-sample measurement rather than an arc length (#560), so a transcribed one would freeze whatever produced the source. The header ones are cheaper: `HEADER_BOOKKEEPING` for a field the spec has no home for, `HEADER_REDERIVED` for the version string, `HEADER_ORIGIN` for an origin the source left to the format and the rebuild writes out (#622) |
|
|
434
457
|
|
|
435
458
|
📝 **Do not delete the `note`.** Both written specs carry one saying the file is
|
|
436
459
|
decompiled and naming the skeleton it came from. A decompiled spec is
|
|
@@ -822,7 +845,17 @@ Three readings stay apart, and the middle one is the point of the other two:
|
|
|
822
845
|
reports **SKIP** on one, because there is no full frame for a mesh to span. And
|
|
823
846
|
`rigc diff` reports it — `skeleton.stage_present` and `skeleton.stage_box`, in the
|
|
824
847
|
header block at the top of the report — so a stage somebody invented now reads
|
|
825
|
-
below 1.000 against a source that has none.
|
|
848
|
+
below 1.000 against a source that has none.
|
|
849
|
+
|
|
850
|
+
⭐ **A stage at `0,0` is not a stage-less one, and an editor export spells it by
|
|
851
|
+
saying nothing.** The editor omits a header field that is at its default, so a
|
|
852
|
+
skeleton whose box sits at the origin exports as a `width` and a `height` with no
|
|
853
|
+
`x`/`y`; `stage_box` reads that omission as the `0` it means, and its line says so
|
|
854
|
+
— *the extent as stated, an omitted origin as the 0 it means*. So `4/4` on a build
|
|
855
|
+
of yours against an export of that same build is the right answer rather than a
|
|
856
|
+
tolerance, and an origin that really did move still reads below 1.000. The extent
|
|
857
|
+
is the half that is read exactly as stated: omit a `width` and you have declared no
|
|
858
|
+
stage, which `stage_present` is the measure of. Both are reported and gate nothing, for
|
|
826
859
|
the reason every reported measure is: no reading of the rendered frames could have
|
|
827
860
|
decided a setup-pose bounding box. The measure inventory that says so lives in
|
|
828
861
|
[BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md), which
|
|
@@ -927,7 +960,12 @@ pointing into it is not.
|
|
|
927
960
|
⚠️ **`rigc render` still refuses such a build**, by name and before it draws
|
|
928
961
|
anything: `… posed no drawable attachment in any animation or in its setup pose —
|
|
929
962
|
there is nothing to draw`. That is the honest division — the rig is valid Spine
|
|
930
|
-
data, and there is no picture of it.
|
|
963
|
+
data, and there is no picture of it. `rigc check` says the same thing one step
|
|
964
|
+
on, since it has no frames to compare; `tools/editor_roundtrip.ts` quotes both
|
|
965
|
+
renderers and reports its step 5 as a **SKIP** naming that, and the round trip
|
|
966
|
+
comes back green on the strength of `validate` and `diff`
|
|
967
|
+
([#621](https://github.com/firejune/rigc/issues/621)). A skin only **one** side
|
|
968
|
+
can draw is the other case entirely, and stays red.
|
|
931
969
|
|
|
932
970
|
**Region attachment** ([Spine: region attachments](http://esotericsoftware.com/spine-regions)),
|
|
933
971
|
the default `type`:
|
package/docs/FACE.md
CHANGED
|
@@ -1403,6 +1403,15 @@ shift through the vertex's coordinate instead of its index, so the next
|
|
|
1403
1403
|
renumbering cannot move it. A `vertices` run is positional by format — that is
|
|
1404
1404
|
`fromVertex`'s whole job — and a *generator* of one has no reason to be.
|
|
1405
1405
|
|
|
1406
|
+
🔒 **And that repair is now measured rather than asserted.** `bun run selftest`
|
|
1407
|
+
runs this script again against a rig whose vertices have been renumbered every
|
|
1408
|
+
way the compiler accepts — the outline rotated along its own walk, the outline
|
|
1409
|
+
reflected, the interior reordered — and requires the run it writes to come back
|
|
1410
|
+
as the same geometry once the renumbering is undone, while the run's own
|
|
1411
|
+
positions have visibly moved. Nothing here declares that property: the subject
|
|
1412
|
+
is any script on any page whose product is a `vertices` run, read off the
|
|
1413
|
+
product.
|
|
1414
|
+
|
|
1406
1415
|
**What comes back from both:**
|
|
1407
1416
|
|
|
1408
1417
|
| | good | (a) one band inverted | (b) mesh folded |
|
package/docs/INGEST.md
CHANGED
|
@@ -253,7 +253,7 @@ rigc diff
|
|
|
253
253
|
|
|
254
254
|
skeleton (reported) (no mean) over 2 measures — the stage, which no reading of the frames could decide
|
|
255
255
|
1.000 stage_present 1/1 both sides declare a setup-pose stage, or neither does — …
|
|
256
|
-
1.000 stage_box 4/4 the stage is the same box (x, y, width, height,
|
|
256
|
+
1.000 stage_box 4/4 the stage is the same box (x, y, width, height — the extent as stated, an omitted origin as the 0 it means) — …
|
|
257
257
|
|
|
258
258
|
bones mean 1.000 over 8 measures
|
|
259
259
|
1.000 count 3/3 how many bones
|
|
@@ -288,12 +288,31 @@ units and every one of the 49 measures still reads **1.000**. ⇒ Never take a g
|
|
|
288
288
|
`diff` as evidence that a geometric edit did not land, and never take it as evidence
|
|
289
289
|
that one did.
|
|
290
290
|
|
|
291
|
+
⚠️ **The corpus gate has a value-level measure and this command does not expose it**
|
|
292
|
+
(§2.3, and `docs/BENCHMARK.md`'s *The nine value measures*). The reason is an input
|
|
293
|
+
rather than a policy: comparing values means reading both files through `spine-core`,
|
|
294
|
+
and a skeleton whose attachments carry a `sequence` cannot be parsed without the atlas
|
|
295
|
+
that resolves it — so the measure takes two skeletons **and two packs**, which
|
|
296
|
+
`rigc diff <a.json> <b.json>` does not have. The sentence above is about this command
|
|
297
|
+
and stays true of it.
|
|
298
|
+
|
|
291
299
|
⚠️ **The one exception is the skeleton's own declared box**, and it is an exception to
|
|
292
300
|
the sentence and not to the rule: `skeleton.stage_box` compares four world numbers,
|
|
293
|
-
but they are numbers an exporter *
|
|
301
|
+
but they are numbers an exporter *declared in the header* rather than a pose anything
|
|
294
302
|
measured, and the block they sit in gates nothing. Moving a pivot does not move them
|
|
295
303
|
either.
|
|
296
304
|
|
|
305
|
+
⭐ **Declared is not the same as written down, and for the origin it is the
|
|
306
|
+
difference between a green round trip and a false finding**
|
|
307
|
+
([#620](https://github.com/firejune/rigc/issues/620)). The editor omits a header
|
|
308
|
+
field at its default, so a stage sitting at `0,0` exports as a `width` and a
|
|
309
|
+
`height` and no `x`/`y` at all — there is no other spelling for it. The measure
|
|
310
|
+
reads that omission as the `0` it means, which is why a rigc build whose stage is at
|
|
311
|
+
the origin and its own export of that build read `stage_box` **4/4**; reading the
|
|
312
|
+
four "exactly as stated" scored the same box **2/4**. The extent is still read
|
|
313
|
+
exactly as stated: it is what decides whether there is a stage at all, so a missing
|
|
314
|
+
`width` is an absent stage rather than a stage of width zero.
|
|
315
|
+
|
|
297
316
|
⛔ **And its ratios are not a score.** [`src/diff.ts`](../src/diff.ts) says so in the
|
|
298
317
|
type itself (*"Unweighted mean of the measures below. NOT a quality score"*), and the
|
|
299
318
|
report repeats it at the foot. It measures *agreement with a particular reference*,
|
|
@@ -437,16 +456,21 @@ a file rigc did not write.
|
|
|
437
456
|
them** ([#594](https://github.com/firejune/rigc/issues/594)). Every
|
|
438
457
|
`examples/*/export/*.json` is ingested with `--art none`, rebuilt through the pack
|
|
439
458
|
beside it, and `diff`ed against the file it was read from: **12 of 12 come back with 0
|
|
440
|
-
blockers and 1.000 on all 49 ratio-bearing measures and all 5 reported ones
|
|
459
|
+
blockers and 1.000 on all 49 ratio-bearing measures and all 5 reported ones** — and,
|
|
460
|
+
since [#615](https://github.com/firejune/rigc/issues/615), on all **nine value
|
|
461
|
+
measures** too, over **193,927** compared values. Byte
|
|
441
462
|
identity is not the claim there and the reason is the input, not the round trip — §2.3
|
|
442
|
-
has the three kinds of difference, measured
|
|
463
|
+
has the three kinds of difference, measured, and what the value measures do and do not
|
|
464
|
+
reach. ⚠️ Which pack is "the one beside it" is
|
|
443
465
|
resolved rather than guessed, for §0.2's reason: `spineboy/export` holds two, and
|
|
444
466
|
`spineboy-run.atlas` covers neither skeleton in it.
|
|
445
467
|
|
|
446
468
|
**What it will not do is invent.** Everything the spec format cannot hold is a
|
|
447
469
|
finding with a code — `BLOCK` for a construct the rebuild will be missing, `JUDGE`
|
|
448
|
-
for the two values a skeleton does not carry, `LOSS`
|
|
449
|
-
|
|
470
|
+
for the two values a skeleton does not carry, `LOSS` wherever the source's spelling and
|
|
471
|
+
rigc's differ on purpose (a number rigc re-derives, a field the spec has no home for, or
|
|
472
|
+
a default the source left to the format and the rebuild writes out). A blocker exits
|
|
473
|
+
non-zero and still writes both files.
|
|
450
474
|
|
|
451
475
|
**Two values are not in a skeleton**, so `ingest` asks rather than guesses:
|
|
452
476
|
|
|
@@ -461,7 +485,11 @@ re-derives on purpose. A blocker exits non-zero and still writes both files.
|
|
|
461
485
|
reports it as two measures of its own (`stage_present`, `stage_box`, since
|
|
462
486
|
[#578](https://github.com/firejune/rigc/issues/578)) and they are `(reported)`, so
|
|
463
487
|
nothing on the ladder reads them and an absurd box is green nearly everywhere. The
|
|
464
|
-
corpus half of the selftest's `IG` suite is the one gate that does read them
|
|
488
|
+
corpus half of the selftest's `IG` suite is the one gate that does read them. ⛔ **The
|
|
489
|
+
flag is refused beside a box the file states** — two sources for one value, both named,
|
|
490
|
+
and the file is the record of what was measured
|
|
491
|
+
([#626](https://github.com/firejune/rigc/issues/626)). It used to be read only *after*
|
|
492
|
+
the file's box, so `--stage` at any of the twelve did nothing and said nothing;
|
|
465
493
|
- **each animation's duration** — the format has no such field. The largest key time
|
|
466
494
|
is used, stated in the motion spec's `note`, and recorded as a finding per
|
|
467
495
|
animation. Edit it if you know the real number.
|
|
@@ -598,15 +626,46 @@ rebuild against its source produces differences of exactly three kinds, in every
|
|
|
598
626
|
| Kind | Example, candidate vs reference | Why |
|
|
599
627
|
| --- | --- | --- |
|
|
600
628
|
| **header bookkeeping**, 3 per file | `skeleton.hash: undefined vs "VFWbaK2UoCM"`, `skeleton.audio: undefined vs null`, `skeleton.spine: "4.3.13" vs "4.3.75-beta"` | the rig spec has no field for `hash` or `audio`, and the version is the runtime rigc links. `ingest` reports all three as findings — `HEADER_BOOKKEEPING` and `HEADER_REDERIVED` |
|
|
601
|
-
| **an omitted default written out** | `…rotate[0].time: 0 vs undefined` | the editor omits a zero `time`; rigc writes it. AUTHORING §10.5's *do not imitate the exporter's omissions*, from the other side |
|
|
629
|
+
| **an omitted default written out** | `…rotate[0].time: 0 vs undefined` | the editor omits a zero `time`; rigc writes it. AUTHORING §10.5's *do not imitate the exporter's omissions*, from the other side. The header has one of these too and it is the one `ingest` now names: a stage at the origin is written `width`/`height` with no `x`/`y`, and the rebuild spells both — `LOSS HEADER_ORIGIN`, with the box unchanged ([#622](https://github.com/firejune/rigc/issues/622)). No file in this corpus takes that branch: all twelve declare an origin away from 0 |
|
|
602
630
|
| **the emitted precision** | `…curve[0]: 0.066667 vs 0.06666667`, `uvs[0]: 0 vs 2.554152e-7` | rigc emits six decimals |
|
|
603
631
|
|
|
604
632
|
⇒ **So the corpus gate is `diff` at 1.000 rather than a byte comparison**, and it is
|
|
605
633
|
worth being exact about what that does and does not cover. `diff` compares structure —
|
|
606
634
|
counts, names, parentage, order, timeline kinds, key counts, curve kinds — and **not
|
|
607
635
|
the values inside the keys**, which is why the precision row above is invisible to it.
|
|
608
|
-
On rigc's own rigs byte identity covers both; on a foreign export
|
|
609
|
-
|
|
636
|
+
On rigc's own rigs byte identity covers both; on a foreign export it used to be
|
|
637
|
+
`check` (pixels) or nothing, depending on what you render.
|
|
638
|
+
|
|
639
|
+
⭐ **The values are gated now, and by a second measure rather than by `diff`**
|
|
640
|
+
([issue #615](https://github.com/firejune/rigc/issues/615)). Structure at 1.000 is
|
|
641
|
+
silent about the numbers inside it: a decompiler that halved every rotation, dropped
|
|
642
|
+
every bone's `length` or mirrored every vertex would read 1.000 on all 49 measures and
|
|
643
|
+
on every `(reported)` one. So the corpus round trip also compares **value by value**,
|
|
644
|
+
with the format's defaults taken from the parser rather than from a table — both files
|
|
645
|
+
are read through `spine-core` and the parsed forms are compared path by path, under a
|
|
646
|
+
tolerance that is the sum of rigc's own 1e-6 emitted grid and one float32 step of the
|
|
647
|
+
runtime's storage. Nine measures, printed on `IG16`'s own line and gated there — here
|
|
648
|
+
is the `6-arcs` export's, wrapped to fit this page:
|
|
649
|
+
|
|
650
|
+
```
|
|
651
|
+
values: 9/9 measure(s) at 1.000 over 13865 compared value(s); skeleton 1.000 ·
|
|
652
|
+
bones 1.000 · slots 1.000 · attachments 1.000 · constraints 1.000 · events 1.000 ·
|
|
653
|
+
key_times 1.000 · key_values 1.000 · curves 1.000
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Over the whole corpus that is **193,927 values** compared, and the twelve read 1.000
|
|
657
|
+
on all nine.
|
|
658
|
+
|
|
659
|
+
`docs/BENCHMARK.md`'s *The nine value measures* is the full statement. What it still
|
|
660
|
+
does **not** cover, in the same breath:
|
|
661
|
+
|
|
662
|
+
| Still uncovered | Why |
|
|
663
|
+
| --- | --- |
|
|
664
|
+
| `version` and `hash` | the rig spec has no field for either, and `ingest` reports both as findings — the header row above, unchanged |
|
|
665
|
+
| anything below one float32 step | the parser stores frames, curves and vertices in a `Float32Array`, so a difference it cannot represent is invisible to any reading of the parsed form |
|
|
666
|
+
| a Bezier's handles *as written* | the parser samples them into the curve, so a moved handle arrives as moved samples rather than as the handle it was |
|
|
667
|
+
| how the file is **spelled** | field order, an omitted default written out, six decimals against eight — the second and third rows of the table above are values that agree, and this measure says so |
|
|
668
|
+
| how it **looks** | that is `check`, and `--texture-from` is how its figure is attributed |
|
|
610
669
|
|
|
611
670
|
The geometric row needs a real number, because a naive reading of `check` makes an
|
|
612
671
|
exact transcription look wrong. Here is the 3-timing transcription against frames
|
package/docs/SPEC_COVERAGE.md
CHANGED
|
@@ -1052,7 +1052,7 @@ Nothing structurally new (attachment timeline, blend, inherit all arrived by run
|
|
|
1052
1052
|
| (a) | **Transform constraints** — 🔴 first appearance (4). Full 4.3 `source` + `properties{from→to}` model (§1.4), which is the least-documented constraint in the format. ✅ Expressible and round-trips exactly; `RigTransformConstraint` already carried the 4.3 shape |
|
|
1053
1053
|
| (a) | **Weighted meshes from authored geometry** — 🔴 first appearance. ~~rigc can emit weighted meshes, but only from `buildRingMesh`/`buildRibbonMesh`. An arbitrary 40-vertex/38-triangle mesh cannot be expressed~~ ⚠️ **This was wrong.** `RigMeshAttachment` takes authored `uvs`/`triangles`/`vertices`/`hull`, and `buildRigMesh` copies them verbatim. Both of 6-arcs' meshes round-trip to 1e-5 |
|
|
1054
1054
|
| (a) | Mesh **`edges`** key ~~(rigc emits `hull` but not `edges`)~~ ✅ emitted from `RigMeshAttachment.edges` and byte-identical to the reference. 🚨 But **nothing measures it** — deleting `edges` from the rig still scores 1.000 on all nine attachment measures, so this rung's own gating feature is invisible to `bench` (issue #46) |
|
|
1055
|
-
| (b) | Mesh geometry as data — vertices, triangles, uvs, per-vertex bone weights — instead of a generator name plus a polygon. ✅ Present. ~~🚨 But the weights bind bones by **index into the emitted bone array**, not by name — inserting a bone rebinds every vertex with the gate still green (issue #45)~~ ✅ **Fixed.** Weights bind **by name** (`weights: [[{ bone, x, y, weight }, …], …]`) and the compiler resolves them at emit; an unknown name is a `CompileError` (selftest `
|
|
1055
|
+
| (b) | Mesh geometry as data — vertices, triangles, uvs, per-vertex bone weights — instead of a generator name plus a polygon. ✅ Present. ~~🚨 But the weights bind bones by **index into the emitted bone array**, not by name — inserting a bone rebinds every vertex with the gate still green (issue #45)~~ ✅ **Fixed.** Weights bind **by name** (`weights: [[{ bone, x, y, weight }, …], …]`) and the compiler resolves them at emit; an unknown name is a `CompileError` (selftest `RF08`). Spine's index run survives behind an explicit `"boneIndexing": "raw"`, whose cost — silence — `MR07` still measures |
|
|
1056
1056
|
| (c) | **A20** (unweighted forbidden) is satisfied here. ~~Under `--profile spine-html` its extra clause fires 11 times instead — the editor writes zero-weight bindings and that profile forbids them~~ ✅ **Fixed with #44**: both of A20's policy clauses are statements about what a rigc *generator* produces, so neither applies to authored geometry. Its coherence clauses — present, in range, summing to 1 — still do, in every profile |
|
|
1057
1057
|
| (c) | **A21_MESH_RIM_PINNED** and **A28** encode ring/ribbon topology and will fire on an arbitrary mesh. ~~✅ **Confirmed**: A21 fires 40 times on the `tail` mesh under the default profile, because `meshKinds` has no entry for an authored mesh and the lookup falls back to `'ring'`~~ ✅ **Fixed** (issue #44): `meshKinds` has a third state, `authored`, and both assertions SKIP on one with that as the reason. The whole transcription is green under the default profile; selftest `MR08` holds it |
|
|
1058
1058
|
| (c) | A13's mesh budget (≤4 slots, ≤80 tris) is **satisfied** at this rung (2 slots, max 38 tris) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"description": "Rig compiler for Spine — declarative rig specs in, Spine 4.3 skeleton data out, verified by a spine-core round-trip. Built so AI agents can author rigs and check their own work; the output imports into the Spine editor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/skills/ingest/SKILL.md
CHANGED
|
@@ -29,14 +29,16 @@ not do for you.
|
|
|
29
29
|
## Start here: `rigc ingest`
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
rigc ingest hero.json --out specs/
|
|
32
|
+
rigc ingest hero.json --out specs/
|
|
33
33
|
rigc build --rig specs/rig.json --motion specs/motion.json --images parts/ --out build/
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
It reads the skeleton — **only** the skeleton — and writes the rig spec and motion
|
|
37
|
-
spec that rebuild it, byte for byte. Two values
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
spec that rebuild it, byte for byte. Two values it refuses rather than guessing: the
|
|
38
|
+
**stage** (`--stage`, for a skeleton that declares none — an editor export *may* be
|
|
39
|
+
one, though every one in the example corpus carries a box, and passing the flag at a
|
|
40
|
+
file that declares a box is refused rather than ignored) and each animation's
|
|
41
|
+
**duration** (the largest key time, recorded as a finding).
|
|
40
42
|
Read `findings.json`: a `BLOCK` line means the rebuild will be missing something and
|
|
41
43
|
the command exits non-zero. Keep the `note` both specs carry. INGEST §2.0.
|
|
42
44
|
|