@rig-c/core 2.81.0 → 2.82.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 +2 -2
- package/README.md +2 -2
- package/docs/AUTHORING.md +28 -3
- package/docs/FACE.md +3 -3
- package/package.json +1 -1
- package/skills/rigc-face/SKILL.md +2 -2
- package/skills/rigc-ingest/SKILL.md +2 -2
- package/skills/rigc-motion/SKILL.md +2 -2
- package/skills/rigc-rigging/SKILL.md +2 -2
- package/src/cli/core_commands.ts +25 -1
- package/src/cli/shared.ts +4 -3
package/NOTICE.md
CHANGED
|
@@ -8,8 +8,8 @@ Copyright (c) 2013-2025 Esoteric Software LLC, licensed under the
|
|
|
8
8
|
[Spine Runtimes License Agreement](https://esotericsoftware.com/spine-runtimes-license),
|
|
9
9
|
as a **development dependency**: a clone and CI install it, and every Spine file
|
|
10
10
|
`rigc build` writes there is read back through it before it is written. **The
|
|
11
|
-
published package does not carry it** — an install of
|
|
12
|
-
`spine-rigc`, the same files under the
|
|
11
|
+
published package does not carry it** — an install of `@rig-c/core` (or of `rig-c` or
|
|
12
|
+
`spine-rigc`, the same product files under the convenience or legacy name) has no
|
|
13
13
|
Spine runtime in it unless one is installed beside it, and then the same `rigc`
|
|
14
14
|
uses it.
|
|
15
15
|
|
package/README.md
CHANGED
|
@@ -223,7 +223,7 @@ candidate chooser (`loadCandidate`, `throughPoser`), setup and animation sampler
|
|
|
223
223
|
`rasterisePiece`, `renderFrame`), with their argument/result types and named
|
|
224
224
|
failures. It loads without the optional Spine runtime; a Spine input requires
|
|
225
225
|
explicitly loading `rig-c/render`, whose eager runtime dependency is unchanged.
|
|
226
|
-
The promised scope and
|
|
226
|
+
The promised scope and the first release (2.69.0) are in
|
|
227
227
|
[RELEASING.md, The import surface](https://github.com/rig-c/core/blob/main/RELEASING.md#the-import-surface);
|
|
228
228
|
[AUTHORING §0.5](docs/AUTHORING.md#05-reading-a-build-from-code) shows the call chain.
|
|
229
229
|
|
|
@@ -647,7 +647,7 @@ commands take it and what its default is.
|
|
|
647
647
|
| `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. a skeleton that declares no stage is carried as declaring none, `--stage x,y,w,h` adds a box to one — 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 |
|
|
648
648
|
| `split --rig … --motion … --slot <name> --out <dir>` | the answer to a mesh refused because its triangles are **several pieces that share no vertex** — two legs after the triangles bridging them were deleted. It reads the two specs and writes them back into an absent or empty `--out` with one mesh per piece: piece 1 stays in the slot, piece k gets a new slot `<slot>_<k>` after it, each with the original's uvs and weights unchanged, its triangles renumbered outline first where the hull rule needs it, and every deform key re-sliced to the new indices — plus `split.json`, where every original vertex and triangle went. One piece is said so and nothing is written; what it cannot carry is refused by name ([AUTHORING §3.4.11](docs/AUTHORING.md)) |
|
|
649
649
|
| `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 |
|
|
650
|
-
| `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/`, one directory per set: `<anim>@<fps>fps/` rather than `<anim>/` at any rate but the default — `--fps 24` writes `<anim>@24fps/`, and the default 12 writes `<anim>/`. `--hide <slot,…>` or `--slot <slot,…>` draws part of the rig on the **same grid** as the whole, so the two frames overlay and the difference is the part; `frames.json` records the subset and `check` refuses such a set as a reference. `--geometry` adds a `geometry.json` per set: every frame's bone world transforms and skinned attachment vertices, with the rest geometry, on the frames' own grid. `--folds` adds a `folds.json` per set and prints one line per mesh with a triangle turned over against the setup pose: the frames, the worst frame's count and which triangles, their world box and their setup-pose area. `--pass-through <cloth>:<leg>` adds a `pass-through.json` per set and prints the frames where a cloth drawn over a leg has its side inside the leg's drawn outline, with the depth and the world box — a reading, not a gate. `--seam <slot>:<slot>` adds a `seam.json` per set and prints, per pair, the frames where two slots that meet open background inside the joint (a gap) or show the overlap band of one past the other (a ghost), with the worst frame's cell count and world box; `ghostA` and `ghostB` partition the same ghost by which named pair member drew it, each with its own frames, spans and worst; `gapOpen` and `gapCovered` partition `gap` by zero versus positive rendered alpha from the whole skeleton, including faint and clipped drawings ([AUTHORING §8.8](docs/AUTHORING.md#88-where-two-slots-stop-meeting--render---seam)) — a reading, not a gate; the exit status never moves. `--overlaps` adds an `overlaps.json` per set and prints one `OVERLAP "<under>" under "<over>"` line per pair of slots where the one drawn later covers the earlier one and the motion moves it — the frames, and the most pixels covered and the frame of them — with an outline plate per covered frame; the render reports what was drawn, and the draw-order key is the author's remedy — a reading, not a gate. `--extremes` adds `extremes.json` and a native six-panel `extremes.png` (setup, geometry-pair min/max and three premultiplied RGBA diffs with a legend), plus one `EXTREMES` line per set. Per-channel minima/maxima and the farthest original-vertex geometry pair are distinct readings; valid channel extrema survive a geometry/image SKIP, with channel refusals reported separately; still or uncomparable geometry reports a named SKIP without changing render exit status. Look at min, max and diff before a person does ([AUTHORING §8.11](docs/AUTHORING.md#811-min-max-and-diff-before-a-person-looks--render---extremes)). `--angle-range <bone>:<relative-to>` prints how far one bone turns against another over each set: the lowest and highest angle and the frames they fall on — a reading, not a gate. `--viewport <x,y,w,h>` or `--viewport-from <frames.json>` draws over a given box instead of a fitted one, so two builds land on one pixel grid; a slot that reaches past it is named by set and frame, and `frames.json` records it. `--environment <schedule.json>` draws the rig under the wind and gravity its physics answers, in time — the same frames on the core and on spine-core; an inspection of the response the rig states, not a scene. `--sheet [--sheet-format json-hash] [--page-edges pot\|free]` also packs each set's straight-alpha transparent frames into `sheet.png` + `sheet.json` (TexturePacker JSON Hash with a shared world-origin `anchor` and `2`-px clamped edge extrusion, verified through `pixi.js@8.22.0` and `phaser@3.90.0` by `tools/sheet_readers.ts`) and `sheet-manifest.json` (`rigc-sheet/1`), leaving diagnostic `f####.png` and `contact.png` unchanged ([AUTHORING §8.10](docs/AUTHORING.md#810-sprite-sheets-from-a-render--render---sheet)) — a self-contained sprite object per animation set; when animations play and how a scene is composed remains the consumer's |
|
|
650
|
+
| `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/`, one directory per set: `<anim>@<fps>fps/` rather than `<anim>/` at any rate but the default — `--fps 24` writes `<anim>@24fps/`, and the default 12 writes `<anim>/`. `--hide <slot,…>` or `--slot <slot,…>` draws part of the rig on the **same grid** as the whole, so the two frames overlay and the difference is the part; `frames.json` records the subset and `check` refuses such a set as a reference. `--geometry` adds a `geometry.json` per set: every frame's bone world transforms and skinned attachment vertices, with the rest geometry, on the frames' own grid. `--folds` adds a `folds.json` per set and prints one line per mesh with a triangle turned over against the setup pose: the frames, the worst frame's count and which triangles, their world box and their setup-pose area. `--pass-through <cloth>:<leg>` adds a `pass-through.json` per set and prints the frames where a cloth drawn over a leg has its side inside the leg's drawn outline, with the depth and the world box — a reading, not a gate. `--seam <slot>:<slot>` adds a `seam.json` per set and prints, per pair, the frames where two slots that meet open background inside the joint (a gap) or show the overlap band of one past the other (a ghost), with the worst frame's cell count and world box; `ghostA` and `ghostB` partition the same ghost by which named pair member drew it, each with its own frames, spans and worst; `gapOpen` and `gapCovered` partition `gap` by zero versus positive rendered alpha from the whole skeleton, including faint and clipped drawings ([AUTHORING §8.8](docs/AUTHORING.md#88-where-two-slots-stop-meeting--render---seam)) — a reading, not a gate; the exit status never moves. `--overlaps` adds an `overlaps.json` per set and prints one `OVERLAP "<under>" under "<over>"` line per pair of slots where the one drawn later covers the earlier one and the motion moves it — the frames, and the most pixels covered and the frame of them — with an outline plate per covered frame; the render reports what was drawn, and the draw-order key is the author's remedy — a reading, not a gate. `--extremes` adds `extremes.json` and a native six-panel `extremes.png` (setup, geometry-pair min/max and three premultiplied RGBA diffs with a legend), plus one `EXTREMES` line per set. Per-channel minima/maxima and the farthest original-vertex geometry pair are distinct readings; valid channel extrema survive a geometry/image SKIP, with channel refusals reported separately; still or uncomparable geometry reports a named SKIP without changing render exit status. `--extremes-only` writes the same `extremes.json` and readable `extremes.png` bytes while skipping ordinary frame rasterization/encoding, `contact.png` and `frames.json`; it implies `--extremes` and takes no other diagnostic or sheet output flags. Look at min, max and diff before a person does ([AUTHORING §8.11](docs/AUTHORING.md#811-min-max-and-diff-before-a-person-looks--render---extremes)). `--angle-range <bone>:<relative-to>` prints how far one bone turns against another over each set: the lowest and highest angle and the frames they fall on — a reading, not a gate. `--viewport <x,y,w,h>` or `--viewport-from <frames.json>` draws over a given box instead of a fitted one, so two builds land on one pixel grid; a slot that reaches past it is named by set and frame, and `frames.json` records it. `--environment <schedule.json>` draws the rig under the wind and gravity its physics answers, in time — the same frames on the core and on spine-core; an inspection of the response the rig states, not a scene. `--sheet [--sheet-format json-hash] [--page-edges pot\|free]` also packs each set's straight-alpha transparent frames into `sheet.png` + `sheet.json` (TexturePacker JSON Hash with a shared world-origin `anchor` and `2`-px clamped edge extrusion, verified through `pixi.js@8.22.0` and `phaser@3.90.0` by `tools/sheet_readers.ts`) and `sheet-manifest.json` (`rigc-sheet/1`), leaving diagnostic `f####.png` and `contact.png` unchanged ([AUTHORING §8.10](docs/AUTHORING.md#810-sprite-sheets-from-a-render--render---sheet)) — a self-contained sprite object per animation set; when animations play and how a scene is composed remains the consumer's |
|
|
651
651
|
| `preview --candidate <dir>` | one self-contained `.html` that plays it, headed by the line `validate <dir>` prints for it and the rigc version — a refused candidate is still previewed, and its header says so in the gate's words. Repeat `--candidate` for one page with a pane per candidate, in the order given; a green `build` ends by naming this command for its own `--out` |
|
|
652
652
|
| `vote --candidate a --candidate b` | one `.html` that asks a human which; `vote --record <file>` checks the answer into `votes.jsonl` |
|
|
653
653
|
| `pose --images <dir> --frame <png>` | reads part placements **out of** a picture |
|
package/docs/AUTHORING.md
CHANGED
|
@@ -280,8 +280,14 @@ What the flags mean:
|
|
|
280
280
|
| `--report` | `build` and `repack`: also write the gate's report as a JSON document (`build-report/1`) to this file — every gate's `PASS`, `SKIP` and `FAIL` rows, its summary figures and stats line, which supplier judged it, and every `pack:` line's figures. Written when the command writes `--out` and when a gate is red, before the exit; a file already at the path is removed first, so a compile error or a refused repack leaves none. Never inside `--out`, refused by name. The printed lines are byte-identical with it and without it — **§5.3** |
|
|
281
281
|
| `--candidate` | `check`, `bench`, `render`, `inspect`, `preview`, `chainfit` and `vote` only: a **compiled** artifact — the directory `build --out` wrote, or a `skeleton.json` path. A directory holding no `skeleton.json`, or a path with nothing at it, is refused `nothing at <path>`, exit 2, nothing written, by every command that takes a build — `validate <dir>` and a directory given to `diff` included (`RC27`). `--atlas <path>` names the atlas when it does not sit beside the skeleton. On `render` and `check` a rigc build whose `skeleton.model.json` is `rigc-compiled/3` or `/2` needs no atlas at all: with no `--atlas` and no atlas beside it, it is drawn from the document's `pages`, the same frames, geometry and figures as with it, and the `atlas` line says `— not there`. An atlas that is there is still read, and held to `pages`: one that differs (edited after the build) is drawn through spine-core, and the `poser` line names the first difference. A build drawn through an atlas that is not there — a `rigc-compiled/1` document, or a build the core refuses — is refused `nothing at <the atlas>: … is drawn through its atlas (<why>)`, exit 2, nothing written; a Spine export without its atlas is refused `nothing at <the atlas>` as it always was. A directory whose files are not one build — a `skeleton.json` or a `skeleton.atlas` from another build beside this one's `skeleton.model.json` — is drawn through spine-core where the runtime can load the pair, the `poser` line naming the two digests or the atlas's first difference; where it cannot, it is refused `<skeleton> does not load against <the atlas>: spine-core draws this pair (<the poser line's why>) and could not resolve it — "<the runtime's own words>". <dir> is not one build: …`, exit 2, nothing written (`RC18`) — another build's skeleton beside this build's atlas typically ends `"Region not found in atlas: <region> (attachment: <name>)"`. Rebuild, or put the build's own three files back beside each other. A Spine export beside an atlas it was not exported with is refused in the same words, ending `The skeleton and the atlas are not one pair`. An atlas copied from another build's directory usually stops earlier, at its pages — their paths are relative to the directory it was written in — and is refused naming the first one: `nothing at <the page>: <the atlas> names it as a page, and spine-core draws this pair through that atlas (<why>). … <dir> is not one build: …`, exit 2, nothing written (`RC18`); an export whose atlas names a page that is not there is refused the same way, ending `and the page has to be there`. A build moved or copied whole away from the directory it was built in stops at the same place, on either poser, and is refused naming the page and the file that names it: `nothing at <the page>: <the skeleton.model.json> names it as a page, and the core draws this build from it. A page path is relative to the build's directory, so a build moved or copied away from the directory it was built in names pages that are not here — build it again where it is, or build it with --copy-images, which writes its pages beside it` (under `--poser spine`, `<the atlas> names it as a page, and spine-core draws this pair through that atlas (--poser spine)` with the same ending), exit 2, nothing written (`RC21`); only where the core refused the files does a page refusal say the directory is not one build. `bench --bones` and `rigc bonedist` — on its `--candidate` and on its `--reference` — refuse a pair the runtime cannot load in the words above with `spine-core poses this pair (bonedist's candidate: …)` or `(bonedist's reference: …)`, exit 2, nothing written (`RC20`); they read no page image, so a build whose pages are elsewhere is measured as it is in place. A `skeleton.json` that is not JSON is refused `rigc: cannot read <the file>: <the parser's words> (line N, column N)`, exit 2, by `render`, `check`, `bench` and `bonedist` as by every other command that reads one (`RC22`). With the atlas gone, `check` on a `rigc-compiled/3` build reads its pages' `scale:` lines off the document, so its texture note and `check.json`'s `textureFrom.candidateScales` are what they are with the atlas; on a `/2` build its texture note says whether the atlas declares a `scale:` line is not read (that document does not state one), and `candidateScales` is `null` (`RC13`). **`vote` and `preview` take it more than once**: `vote` 2–4 times, one per pane, labelled A, B, C, D in the order given; `preview` any number of times, one pane per candidate in the order given, each headed by its path and its gate line, and the same skeleton twice (a directory and its `skeleton.json` are one) is refused by name. `--atlas` goes with one candidate only. Everywhere else a repeat is a typo and is refused |
|
|
282
282
|
| `--animation` | `render`, `preview` and `vote` only: which animation to show. The default is **every** one for `render`, the **first** for `preview` (each candidate's own first, with several), and for `vote` the first of candidate A. A name the skeleton does not have is refused, with the ones it does have listed — and for `vote` and a several-candidate `preview`, so is a name that only *some* candidates have, naming the one that lacks it |
|
|
283
|
+
| `--slot` | On `render`: draw only these comma-separated slot names, in draw order, on the **same grid** as the whole rig (the viewport still fits every slot). Default: all slots. Unknown names or art available only in another skin are refused by name. `frames.json` records the subset, and `check` refuses it as a reference. `split` uses this flag to name the mesh to separate — **§3.4.11** |
|
|
284
|
+
| `--hide` | On `render`: draw every slot except these comma-separated names, on the same grid and with the same subset record and refusals as `--slot`. Default: hide none. Refused with `--slot` |
|
|
285
|
+
| `--fps` | On `render`: frames per second to sample (default `12`, finite and at least `1`); a non-default rate writes `<anim>@<fps>fps/`, while `12` writes `<anim>/`. `bonedist` samples both skeletons at this rate (default `12`); `inspect` uses it for the `--at` grid (default `12`, **§13**); `check` uses it only for a frame set with no `frames.json` sidecar (**§9**) |
|
|
286
|
+
| `--max` | `render` and `inspect`: the longest side of the frame in pixels (default `256`, finite and at least `16`). On `render`, refused with `--viewport-from`, which already fixes the pixel size — **§8.6**, **§13** |
|
|
283
287
|
| `--show`, `--dim`, `--at`, `--zoom` | `inspect` only — what it draws, whether the art is faded, which times of `--animation` it poses and the bone, slot or folds it frames to; §13 says each, with the refusals |
|
|
284
288
|
| `--geometry` | `render` only: also write a `geometry.json` into each frame directory — per frame, every bone's world transform and every slot's region or mesh vertices in world units after skinning, plus each attachment's rest geometry and topology, on the frames' own grid and viewport. Every other file is byte for byte what the render writes without it. Refused with `--slot`/`--hide`, because the geometry is the whole pose whatever is drawn — **§8.2** |
|
|
289
|
+
| `--extremes-only` | `render` only: imply `--extremes`, but write only `extremes.json` and readable `extremes.png` with the same bytes. Pose the same grid; rasterize only the setup/min/max pictures used by the plate. No ordinary frame PNGs, contact sheet or `frames.json`. Takes no `--sheet`, `--sheet-format`, `--page-edges`, `--geometry`, `--folds`, `--pass-through`, `--seam`, `--overlaps` or `--angle-range`; drop the named flag or use `--extremes` instead — **§8.11** |
|
|
290
|
+
| `--extremes` | `render` only: add `extremes.json` and an `EXTREMES` line per set; a moving set with comparable geometry and readable images also gets a native six-panel `extremes.png`: setup, geometry-pair min/max and three premultiplied RGBA diffs with a legend. Per-channel extrema are independent of that geometry pair; valid channels survive geometry/image SKIPs, and channel refusals are named separately. Still or uncomparable geometry is a named SKIP, with no invented pair or PNG; observation failures do not change a successful render's exit status. Supports `--slot`/`--hide` on both posers, on the whole rig's viewport. Without the flag, no extremes observations are added — **§8.11** |
|
|
285
291
|
| `--folds` | `render` only: also read which mesh triangles **fold** — turn over against the setup pose — and write a `folds.json` into each frame directory: per mesh, the frames with a folded triangle, the worst frame's count and which triangles, by index, with their world box and setup-pose area. One printed line per mesh that folds; a triangle degenerate or inverted at the setup pose is said once and never counted. A reading, not a gate. Every other file is byte for byte what the render writes without it, `geometry.json` included. Refused with `--slot`/`--hide`, for `--geometry`'s reason — **§8.3** |
|
|
286
292
|
| `--pass-through` | `render` only: `<cloth>:<leg>[,<cloth>:<leg>…]` — also read where each cloth slot **passes through** its leg slot, and write a `pass-through.json` into each frame directory: the frames where the cloth draws over the leg and its drawn side lies inside the leg's drawn outline by 2 steps or more, each with the depth and the world box. One printed line per pair, and one per frame that passes through. The first name must draw over the second for a frame to be read — the declared order, never both. A reading, not a gate. Every other file is byte for byte what the render writes without it. An unknown slot, a slot that draws on no frame, a malformed list and a pair named twice are refused by name, and so is `--slot`/`--hide` beside it — **§8.5** |
|
|
287
293
|
| `--seam` | `render` only: `<slot>:<slot>[,<slot>:<slot>…]` — also read, for each pair, where the two drawings **stop meeting**, and write a `seam.json` into each frame directory: per pair, the frames with a **gap** (background inside the joint, where the two covered together at the setup pose) and the frames with a **ghost** (the overlap band of one showing where, from its partner's point of view, nothing was drawn), each with its cell count, area and world box, and as runs of frames with their times. One printed line per pair; a pair whose drawings share no cell at the setup pose is a **SKIP**. A reading, not a gate: the exit status never moves. Every other file is byte for byte what the render writes without it. An unknown slot, a slot with itself, a slot that draws on no frame, a malformed list and a pair named twice in either order are refused by name, and so is `--slot`/`--hide` beside it — **§8.8** |
|
|
@@ -1646,9 +1652,9 @@ written`, followed by `rigc repack: nothing was written to <out>; the work direc
|
|
|
1646
1652
|
### 0.5 Reading a build from code
|
|
1647
1653
|
|
|
1648
1654
|
The named `rig-c/render-shared` entry promises the official renderer reads for
|
|
1649
|
-
checked consumers (issue #1561)
|
|
1650
|
-
|
|
1651
|
-
|
|
1655
|
+
checked consumers (issue #1561), available since 2.69.0; pin
|
|
1656
|
+
`>=2.69.0 <3.0.0`. It requires Bun and the candidate's real files, but imports
|
|
1657
|
+
no optional Spine runtime. The existing
|
|
1652
1658
|
`rig-c/render` entry still eagerly imports that runtime.
|
|
1653
1659
|
|
|
1654
1660
|
```ts
|
|
@@ -9473,6 +9479,25 @@ bounds plus its `2`-px border on each side exceed `2048` (`w + 4 > 2048` or
|
|
|
9473
9479
|
bun cli.ts render --candidate path/to/build --extremes [--animation turn] [--fps 12] [--max 256]
|
|
9474
9480
|
```
|
|
9475
9481
|
|
|
9482
|
+
Use `--extremes-only` instead when only those observations are needed:
|
|
9483
|
+
|
|
9484
|
+
```sh
|
|
9485
|
+
bun cli.ts render --candidate path/to/build --extremes-only --out extremes/
|
|
9486
|
+
```
|
|
9487
|
+
|
|
9488
|
+
It implies `--extremes` and writes the same `extremes.json` and readable
|
|
9489
|
+
`extremes.png` bytes, sampling the same poses and fitting the same viewport.
|
|
9490
|
+
Only the setup/min/max pictures needed by the plate are rasterized; ordinary
|
|
9491
|
+
frame PNGs, the contact sheet and `frames.json` are not produced. A still or
|
|
9492
|
+
refused set writes its JSON SKIP with no PNG. Skin, subset, environment, fps,
|
|
9493
|
+
animation and viewport options keep their meanings. Other diagnostic and sheet
|
|
9494
|
+
output flags listed in the `--extremes-only` row above are refused together
|
|
9495
|
+
rather than silently ignored. Use `--extremes` when those outputs are needed.
|
|
9496
|
+
Selected set directories are replaced just as in an ordinary render; a prior
|
|
9497
|
+
root `frames.json` is removed because it would advertise frames no longer there.
|
|
9498
|
+
Unselected set directories are left alone. Use a dedicated output root when
|
|
9499
|
+
retaining an ordinary render beside these observations.
|
|
9500
|
+
|
|
9476
9501
|
Look at **min · max · diff before a person does**. A baked line carried by a
|
|
9477
9502
|
moving part can be obvious in the difference image while a seam metric misses
|
|
9478
9503
|
it. This flag makes the picture; it does not classify tears or ghosts and does
|
package/docs/FACE.md
CHANGED
|
@@ -545,9 +545,9 @@ renderer must never idle-skip a mesh. Keying the pivot one link up satisfies it,
|
|
|
545
545
|
and **the rig that satisfies the assertion is the better rig anyway.**
|
|
546
546
|
|
|
547
547
|
⚠️ **A15 assumes the meshes are mostly static, and that assumption is the rule's
|
|
548
|
-
whole premise.** The
|
|
549
|
-
face whose `idle` breathes through one pivot
|
|
550
|
-
nothing keys the meshes' own bones. The rule reads bone **names**: keying `headroll`
|
|
548
|
+
whole premise.** The `@rig-c/play` renderer (the `rig-play` profile's player) skips
|
|
549
|
+
redrawing a mesh nothing moved, so a face whose `idle` breathes through one pivot
|
|
550
|
+
keeps every mesh under it cheap only if nothing keys the meshes' own bones. The rule reads bone **names**: keying `headroll`
|
|
551
551
|
passes it, and `head`'s mesh still moves, because a child's world transform is
|
|
552
552
|
composed from its parent's — so pivoting is right here for the anatomical reason
|
|
553
553
|
above, not because it stops a redraw. A **painting rig** is the genre where the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.82.0",
|
|
4
4
|
"description": "AI-authored 2D rigging and animation, verified before it is written. rigc compiles a rig spec into skeleton data in the Spine 4.3 format, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: rigc-face
|
|
3
3
|
description: Author a face on plain Spine data with rigc — a blink, a gaze shift, a breathing portrait and a head turn a few degrees off axis, built from deform timelines and per-part parallax. Use when the request is a talking or living portrait, a standing character, an expression or a head turn, such as "rig this face", "make the portrait blink and look around" or "turn the head". Not for Live2D file conversion, cutting a face illustration into parts, or VTuber-style real-time face tracking.
|
|
4
4
|
license: MIT
|
|
5
|
-
compatibility: Requires Bun 1.4 or later and the npm package rig-c.
|
|
5
|
+
compatibility: Requires Bun 1.4 or later and the npm package @rig-c/core (rig-c is the convenience name).
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Face — a turn, a gaze and a blink
|
|
@@ -63,7 +63,7 @@ differential audit and the three limits it does not lift.
|
|
|
63
63
|
rather than this closed form, and [INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) if the head
|
|
64
64
|
arrived as a compiled skeleton.
|
|
65
65
|
|
|
66
|
-
Every guide linked here is in the installed package at `node_modules
|
|
66
|
+
Every guide linked here is in the installed package at `node_modules/@rig-c/core/docs/`,
|
|
67
67
|
which is the copy that matches the rigc you run; the links go to the repository's
|
|
68
68
|
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
69
69
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: rigc-ingest
|
|
3
3
|
description: Work with a Spine skeleton.json somebody else authored — exported from the Spine editor or another tool — using rigc. Read and validate it, understand a complaint rigc raised about it, decompile it into rigc specs with `rigc ingest`, normalise, re-pivot or rename it, and extend it with an animation it does not have. Use when the input is an existing skeleton.json with its .atlas and page images rather than loose part PNGs. Not for Live2D file conversion or runtime tracking.
|
|
4
4
|
license: MIT
|
|
5
|
-
compatibility: Requires Bun 1.4 or later and the npm package rig-c.
|
|
5
|
+
compatibility: Requires Bun 1.4 or later and the npm package @rig-c/core (rig-c is the convenience name).
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Ingest — a skeleton you did not author
|
|
@@ -81,7 +81,7 @@ thing to reach for; transcription by hand is what you fall back on for a constru
|
|
|
81
81
|
3. Then [RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md) for why the re-pivot edit has the shape
|
|
82
82
|
it has, and [MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md) for the animation you are adding.
|
|
83
83
|
|
|
84
|
-
Every guide linked here is in the installed package at `node_modules
|
|
84
|
+
Every guide linked here is in the installed package at `node_modules/@rig-c/core/docs/`,
|
|
85
85
|
which is the copy that matches the rigc you run; the links go to the repository's
|
|
86
86
|
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
87
87
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: rigc-motion
|
|
3
3
|
description: Author a Spine animation with rigc from key poses — an idle, a loop, a move from one picture to another — with timing and spacing, ease in and out, anticipation, arcs, overlap, follow-through, squash and stretch, and candidate variants a person can choose between. Use when the request is a movement on an existing or planned rig, in the animator's words too: "animate this rig", "make it breathe", "go from pose A to pose B", "make it feel heavier", "the cape should follow through". Not for Live2D, separating an image into parts, or real-time tracking.
|
|
4
4
|
license: MIT
|
|
5
|
-
compatibility: Requires Bun 1.4 or later and the npm package rig-c.
|
|
5
|
+
compatibility: Requires Bun 1.4 or later and the npm package @rig-c/core (rig-c is the convenience name).
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Motion — what goes between two poses
|
|
@@ -54,7 +54,7 @@ thing that judges a movement is a person's eye through `rigc vote` — MOTION §
|
|
|
54
54
|
head turn; [INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) if the rig arrived as a compiled
|
|
55
55
|
skeleton rather than loose parts.
|
|
56
56
|
|
|
57
|
-
Every guide linked here is in the installed package at `node_modules
|
|
57
|
+
Every guide linked here is in the installed package at `node_modules/@rig-c/core/docs/`,
|
|
58
58
|
which is the copy that matches the rigc you run; the links go to the repository's
|
|
59
59
|
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
60
60
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: rigc-rigging
|
|
3
3
|
description: Decide a Spine rig's hierarchy with rigc — how many bones, where each pivot sits, what hangs off what, offsets, chains and what a chain can reach, siblings versus chains, constraints as structure — and which of those decisions the reference frames can check. Use when the request is a skeleton from loose part PNGs, such as "rig these parts", "make a Spine skeleton" or "where do the joints go", before any motion is authored. Not for Live2D, cutting an illustration into parts, or VTuber-style tracking.
|
|
4
4
|
license: MIT
|
|
5
|
-
compatibility: Requires Bun 1.4 or later and the npm package rig-c.
|
|
5
|
+
compatibility: Requires Bun 1.4 or later and the npm package @rig-c/core (rig-c is the convenience name).
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Rigging — the hierarchy itself
|
|
@@ -52,7 +52,7 @@ are named in RIGGING §11 — neither as a pass bar.
|
|
|
52
52
|
[INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) if the skeleton was handed to you already
|
|
53
53
|
compiled.
|
|
54
54
|
|
|
55
|
-
Every guide linked here is in the installed package at `node_modules
|
|
55
|
+
Every guide linked here is in the installed package at `node_modules/@rig-c/core/docs/`,
|
|
56
56
|
which is the copy that matches the rigc you run; the links go to the repository's
|
|
57
57
|
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
58
58
|
|
package/src/cli/core_commands.ts
CHANGED
|
@@ -658,6 +658,14 @@ export function cmdRender(flags: Record<string, string>): void {
|
|
|
658
658
|
const maxSide = readPositiveNumber(flags, 'max', 256, 16);
|
|
659
659
|
// A box given rather than fitted (issue #1414), read before a line is printed, so a malformed one is refused first.
|
|
660
660
|
const given = readGivenViewport(flags);
|
|
661
|
+
const extremesOnly = flags['extremes-only'] !== undefined;
|
|
662
|
+
if (extremesOnly) {
|
|
663
|
+
for (const flag of ['sheet', 'sheet-format', 'page-edges', 'geometry', 'folds', 'pass-through', 'seam', 'overlaps', 'angle-range']) {
|
|
664
|
+
if (flags[flag] !== undefined) {
|
|
665
|
+
throw new UsageError(`--extremes-only takes no --${flag}: it writes only extremes.json and, when readable, extremes.png. Drop --${flag}, or use --extremes for ordinary frames and additional outputs`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
661
669
|
const wantsSheet = flags.sheet !== undefined;
|
|
662
670
|
if (flags['sheet-format'] !== undefined && !wantsSheet) {
|
|
663
671
|
throw new UsageError('--sheet-format takes --sheet: pass --sheet alongside --sheet-format <format>, or drop --sheet-format');
|
|
@@ -672,7 +680,7 @@ export function cmdRender(flags: Record<string, string>): void {
|
|
|
672
680
|
const sheetPageEdges = wantsSheet ? readPageEdges(flags) : undefined;
|
|
673
681
|
const outRoot = resolve(flags.out ?? 'render');
|
|
674
682
|
const geometry = flags.geometry !== undefined;
|
|
675
|
-
const extremes = flags.extremes !== undefined;
|
|
683
|
+
const extremes = extremesOnly || flags.extremes !== undefined;
|
|
676
684
|
// The fold reading (issue #1383) is its own flag rather than a part of --geometry, so a caller that asked for
|
|
677
685
|
// neither, or for --geometry alone, gets the bytes it always got: geometry.json is the pose, and a reading derived
|
|
678
686
|
// from it is a second file beside it, written only when asked for.
|
|
@@ -1063,6 +1071,22 @@ export function cmdRender(flags: Record<string, string>): void {
|
|
|
1063
1071
|
}
|
|
1064
1072
|
|
|
1065
1073
|
mkdirSync(outRoot, { recursive: true });
|
|
1074
|
+
if (extremesOnly) {
|
|
1075
|
+
// Reuse the same posed grid and plates; selecting extremes needs no ordinary pixels.
|
|
1076
|
+
// Clear only the selected sets, just as the ordinary render does. No frames.json
|
|
1077
|
+
// is written: this output is an observation, not a frame set consumable by check.
|
|
1078
|
+
for (const [name, extreme] of extremeReadings) {
|
|
1079
|
+
const dir = join(outRoot, frameSetDir(name, fps));
|
|
1080
|
+
if (existsSync(dir)) rmSync(dir, { recursive: true });
|
|
1081
|
+
mkdirSync(dir, { recursive: true });
|
|
1082
|
+
writeFileSync(join(dir, 'extremes.json'), extremesText(extreme.reading));
|
|
1083
|
+
if (extreme.plate !== null) extreme.plate.writePng(join(dir, 'extremes.png'));
|
|
1084
|
+
console.log(` .. ${extremesLine(extreme.reading)}`);
|
|
1085
|
+
}
|
|
1086
|
+
// A prior ordinary render's index would falsely advertise now-removed frames.
|
|
1087
|
+
rmSync(join(outRoot, FRAMES_SIDECAR), { force: true });
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1066
1090
|
const sets: FrameSet[] = [];
|
|
1067
1091
|
// A mesh's setup faults are a property of the mesh, not of an animation: said once, the first time a set shows it.
|
|
1068
1092
|
const faultsSaid = new Set<string>();
|
package/src/cli/shared.ts
CHANGED
|
@@ -152,7 +152,7 @@ export { PACKAGE_ROOT, readPackageMeta, readVersion };
|
|
|
152
152
|
* needs a value` (issue #328). `CLI10`/`CLI11` in `selftest.ts` now hold the two
|
|
153
153
|
* halves together by reading `--help` rather than by naming a flag.
|
|
154
154
|
*/
|
|
155
|
-
const BOOLEAN_FLAGS = new Set(['all', 'all-frames', 'all-bones', 'help', 'copy-images', 'again', 'pack', 'copy', 'geometry', 'folds', 'extremes', 'overlaps', 'sheet', 'accept-skeleton-differences', 'keep-pages', 'dim']);
|
|
155
|
+
const BOOLEAN_FLAGS = new Set(['all', 'all-frames', 'all-bones', 'help', 'copy-images', 'again', 'pack', 'copy', 'geometry', 'folds', 'extremes', 'extremes-only', 'overlaps', 'sheet', 'accept-skeleton-differences', 'keep-pages', 'dim']);
|
|
156
156
|
|
|
157
157
|
/**
|
|
158
158
|
* The flags a command is allowed to spell more than once.
|
|
@@ -1066,6 +1066,7 @@ const FLAG_MEANINGS: Record<string, string> = {
|
|
|
1066
1066
|
'Each frame with a moving pair also gets an outline plate, overlaps-f<NNNN>.png: every slot in a moving pair as its contour in its own ' +
|
|
1067
1067
|
'colour, dashed where a slot drawn later covers it, named at its top. The render reports what was drawn; who should be in front is the ' +
|
|
1068
1068
|
"author's, and the draw-order key in the motion spec is the remedy. A reading, not a gate. Not with --slot/--hide",
|
|
1069
|
+
'extremes-only': 'write only extremes.json and readable extremes.png per set, with the same posed grid and bytes as --extremes; no ordinary frame PNGs, contact.png or frames.json. Implies --extremes; takes no --sheet, --sheet-format, --page-edges, --geometry, --folds, --pass-through, --seam, --overlaps or --angle-range',
|
|
1069
1070
|
extremes: 'also write extremes.json and a native six-panel extremes.png per set: setup, geometry-pair min/max and premultiplied RGBA diffs, with a legend. Per-channel extrema are recorded separately and valid channels survive geometry/image SKIPs; channel refusals are named separately. Still geometry reports SKIP; a reading, not a gate. Attachment identity/count changes record a named SKIP without changing render exit; supports --slot/--hide on both posers',
|
|
1070
1071
|
seam:
|
|
1071
1072
|
`also read, for each <slot>:<slot> pair, where the two drawings stop meeting, and write ${SEAM_FILE} into each frame directory: ` +
|
|
@@ -1757,7 +1758,7 @@ export const COMMANDS: CommandDoc[] = [
|
|
|
1757
1758
|
runtime: false,
|
|
1758
1759
|
spineFormat: false,
|
|
1759
1760
|
usage: [
|
|
1760
|
-
'rigc render --candidate <dir | skeleton.json> [--animation <name>] [--skin <name>] [--fps 12] [--max 256] [--geometry] [--folds] [--extremes] [--poser core|spine] [--out render/]',
|
|
1761
|
+
'rigc render --candidate <dir | skeleton.json> [--animation <name>] [--skin <name>] [--fps 12] [--max 256] [--geometry] [--folds] [--extremes | --extremes-only] [--poser core|spine] [--out render/]',
|
|
1761
1762
|
'rigc render … --slot <name[,name…]> | --hide <name[,name…]> (a subset of the slots, on the whole rig\'s grid)',
|
|
1762
1763
|
'rigc render … --environment <schedule.json> (the wind and gravity the physics answers, in time)',
|
|
1763
1764
|
'rigc render … --pass-through <cloth>:<leg>[,…] (the frames where a cloth drawn over a leg passes through it)',
|
|
@@ -1767,7 +1768,7 @@ export const COMMANDS: CommandDoc[] = [
|
|
|
1767
1768
|
'rigc render … --viewport <x,y,w,h> | --viewport-from <frames.json> (a box given, not fitted: two renders on one pixel grid)',
|
|
1768
1769
|
'rigc render … --sheet [--sheet-format json-hash] [--page-edges pot|free] (deterministic sprite sheets + JSON hash table + sheet-manifest.json per set)',
|
|
1769
1770
|
],
|
|
1770
|
-
flags: ['candidate', 'atlas', 'animation', 'skin', 'slot', 'hide', 'fps', 'max', 'viewport', 'viewport-from', 'geometry', 'folds', 'extremes', 'pass-through', 'seam', 'overlaps', 'angle-range', 'sheet', 'sheet-format', 'page-edges', 'environment', 'poser', 'out'],
|
|
1771
|
+
flags: ['candidate', 'atlas', 'animation', 'skin', 'slot', 'hide', 'fps', 'max', 'viewport', 'viewport-from', 'geometry', 'folds', 'extremes', 'extremes-only', 'pass-through', 'seam', 'overlaps', 'angle-range', 'sheet', 'sheet-format', 'page-edges', 'environment', 'poser', 'out'],
|
|
1771
1772
|
overrides: {
|
|
1772
1773
|
'page-edges': {
|
|
1773
1774
|
meaning:
|