@rig-c/core 2.59.0 β 2.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/docs/AUTHORING.md +187 -3
- package/docs/INGEST.md +1 -0
- package/docs/MOTION.md +1 -0
- package/package.json +1 -1
- package/src/cli/core_commands.ts +178 -7
- package/src/cli/shared.ts +80 -6
- package/src/cli/spine_commands.ts +1 -1
- package/src/compile.ts +59 -5
- package/src/passthrough.ts +3 -3
- package/src/render.ts +16 -0
- package/src/render_core.ts +23 -5
- package/src/render_shared.ts +33 -0
- package/src/seam.ts +576 -0
- package/src/stop.ts +33 -3
package/README.md
CHANGED
|
@@ -433,7 +433,10 @@ frames. It draws with rigc's own rasteriser (the one `check` measures with), so
|
|
|
433
433
|
it needs no browser and no network, and the `frames.json` it leaves beside the
|
|
434
434
|
directories makes the result a frame set like any other β the world box every
|
|
435
435
|
frame is a picture of. `--animation <name>` narrows it to one, `--fps` and
|
|
436
|
-
`--max` change the rate and the frame size.
|
|
436
|
+
`--max` change the rate and the frame size. A set drawn at any rate but the
|
|
437
|
+
default is written to `<anim>@<fps>fps/` rather than `<anim>/`, so two rates sit
|
|
438
|
+
side by side: `--fps 24` writes `<anim>@24fps/`, and the default 12 writes
|
|
439
|
+
`<anim>/`. Each set's summary line ends with the directory it wrote.
|
|
437
440
|
|
|
438
441
|
> π¨ **`--skin <name>` if your rig has more than one.** With no `--skin` no skin
|
|
439
442
|
> is set at all, so every slot resolves through the **default** skin alone β and
|
|
@@ -603,7 +606,7 @@ commands take it and what its default is.
|
|
|
603
606
|
| `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 |
|
|
604
607
|
| `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)) |
|
|
605
608
|
| `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 |
|
|
606
|
-
| `render --candidate <dir>` | PNG frames plus a contact sheet, in `render
|
|
609
|
+
| `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 β a reading, not a gate; the exit status never moves. `--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 |
|
|
607
610
|
| `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` |
|
|
608
611
|
| `vote --candidate a --candidate b` | one `.html` that asks a human which; `vote --record <file>` checks the answer into `votes.jsonl` |
|
|
609
612
|
| `pose --images <dir> --frame <png>` | reads part placements **out of** a picture |
|
|
@@ -614,6 +617,7 @@ commands take it and what its default is.
|
|
|
614
617
|
| `bench <rung> --candidate <dir>` | one rung of the benchmark ladder |
|
|
615
618
|
| `guide <RULE>` / `guide Β§<n>[.<m>[.<k>]] [--all]` | the guide's row for the rule a failure line names β every `FAIL` line, `vote --record` refusal, `ingest` finding and coded compile error ends with ` (see rigc guide <RULE>)`; only legacy uncoded library errors point at `Β§5.1` β or a section's heading, own prose and subsection index, read from the installed `docs/AUTHORING.md` when it runs. `--all` includes the subsection bodies; a leaf prints its full text either way. Sizes describe full ledger spans. `--all` with a rule is refused, exit 2; an unknown name is refused with the nearest real ones, exit 1 |
|
|
616
619
|
| `skills install [--dir β¦] [--copy]` | links every agent skill the package ships into `.agents/skills` (or `--dir`) as relative symlinks, or copies them with `--copy` β where Codex, Gemini CLI and Antigravity look. A second run has nothing to do; an entry already there that is not this command's is refused by name and nothing is written |
|
|
620
|
+
| `review <dir> [β¦]` | the human review of a build, handed to the `rig-review` bin of `@rig-c/review` installed beside the package, with every argument untouched and its exit code returned: `rigc review open <dir>` prints a loopback URL to give a person, `check <dir>` validates the folder it would serve, `feedback <dir>` prints what they left, and `rigc review --help` is review's own usage. rigc does not depend on it: without it the command prints `rigc review: @rig-c/review is not installed β npm i -D @rig-c/review` and exits 2 |
|
|
617
621
|
|
|
618
622
|
`diff`, `bonedist`, `check` and `bench` measure against something you were given; the
|
|
619
623
|
first three work on any reference you have, and `bench` is a repository workflow that needs a clone
|
package/docs/AUTHORING.md
CHANGED
|
@@ -141,6 +141,11 @@ bun cli.ts inspect --candidate path/to/spine --show art,bones,names --dim --out
|
|
|
141
141
|
# them, ask a human β the one loop step this toolchain cannot run for you:
|
|
142
142
|
bun cli.ts vote --candidate path/to/spine-a --candidate path/to/spine-b # -> ballot.html
|
|
143
143
|
bun cli.ts vote --record vote-<id>.json # -> votes.jsonl
|
|
144
|
+
|
|
145
|
+
# β¦and when the build is for a person to look at rather than for you to choose
|
|
146
|
+
# between, hand it over: with @rig-c/review installed beside rigc, give the person
|
|
147
|
+
# the loopback URL this prints (rigc review --help is review's own usage):
|
|
148
|
+
bun cli.ts review open path/to/spine
|
|
144
149
|
```
|
|
145
150
|
|
|
146
151
|
`build` compiles, runs the named assertions β through a `@esotericsoftware/spine-core`
|
|
@@ -255,6 +260,7 @@ What the flags mean:
|
|
|
255
260
|
| `--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** |
|
|
256
261
|
| `--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** |
|
|
257
262
|
| `--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** |
|
|
263
|
+
| `--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** |
|
|
258
264
|
| `--angle-range` | `render` only: `<bone>:<relative-to>[,<bone>:<relative-to>β¦]` β also read, for each pair, the range of the first bone's world direction against the second's over each set's frames, and print one line per pair per set: the lowest and the highest angle in degrees, the span, and the frame each was first reached on. A reading, not a gate: nothing is written, and every file is the bytes the render writes without it. An unknown bone (with the bones there are), a bone against itself, a malformed list and a pair named twice are refused by name, and so is a bone with no direction on a frame β **Β§8.7** |
|
|
259
265
|
| `--viewport`, `--viewport-from` | `render`: give the box every frame is drawn over instead of fitting one β `--viewport <x,y,w,h>`, a world box, y up, `x,y` its bottom-left corner, at the pixel size `--max` gives; or `--viewport-from <frames.json \| dir>`, an earlier render's grid exactly (its box, scale and pixel size), so two candidates land on one pixel grid. A slot that reaches past the box is named by set and frame, and `frames.json` records the box was given under `pinned`. The two together are refused, and so is `--max` beside `--viewport-from` β **Β§8.6**. On `check` and `inspect`, `--viewport` pins the box those commands draw over instead |
|
|
260
266
|
| `--environment` | `render` only: the wind and gravity the rig's physics constraints answer, in time β a schedule file `{ "spec": "rigc-environment/1", "steps": [{ "t": <seconds>, "wind"?: [x, y], "gravity"?: [x, y] }, β¦] }`, x right and **y down**, each step holding from its `t` until the next. Both posers set each change before the step that poses the first frame at or after its `t`, and draw the same frames, `geometry.json` and `frames.json` (`RE01`). `frames.json` and `geometry.json` record it, and the framing reads it. A malformed file, a half no physics constraint answers, two steps on one frame and a step after every set's last frame are refused by name, exit 2, nothing written; `check` refuses a frame set drawn under one as a reference. Without it every file is the bytes it always was. An inspection of the response the rig states, not a scene β **Β§8.4** |
|
|
@@ -1130,6 +1136,7 @@ bun cli.ts render --candidate path/to/spine --slot <name,β¦> | --hide <name,
|
|
|
1130
1136
|
bun cli.ts render --candidate path/to/spine --geometry # + geometry.json per set: the numbers the frames were drawn from
|
|
1131
1137
|
bun cli.ts render --candidate path/to/spine --folds # + folds.json per set: which mesh triangles turn over, and when
|
|
1132
1138
|
bun cli.ts render --candidate path/to/spine --pass-through skirt:leg # + pass-through.json: where a cloth goes through a leg
|
|
1139
|
+
bun cli.ts render --candidate path/to/spine --seam thigh:shin # + seam.json: where two slots that meet open a gap or show a ghost
|
|
1133
1140
|
bun cli.ts render --candidate path/to/spine --angle-range tail:hip # how far a bone turns against another, per animation
|
|
1134
1141
|
bun cli.ts render --candidate path/to/spine --viewport-from render/ # an earlier render's grid: two builds, one pixel grid
|
|
1135
1142
|
bun cli.ts preview --candidate path/to/spine [--candidate path/to/another β¦] [--animation β¦] [--out preview.html]
|
|
@@ -1235,7 +1242,11 @@ bun cli.ts pose --images path/to/parts --frame poseA.png [--out pose.json]
|
|
|
1235
1242
|
gate can know you did not mean them. `render` writes
|
|
1236
1243
|
`render/<animation>/f0000.pngβ¦` with a `contact.png` grid of **every** frame
|
|
1237
1244
|
beside them (open that one first β spacing is a comparison across frames) and a
|
|
1238
|
-
`frames.json` sidecar naming the world box they are pictures of.
|
|
1245
|
+
`frames.json` sidecar naming the world box they are pictures of. It also says
|
|
1246
|
+
whether the physics ran: under each set's line, `physics: N constraint(s)
|
|
1247
|
+
stepped at <fps> fps`, and the same two figures on that set in `frames.json` β
|
|
1248
|
+
or, once, `physics: none in this rig` for a rig that declares no physics
|
|
1249
|
+
constraint (Β§8.4). `contact.png`
|
|
1239
1250
|
is for spacing across frames; a defect is read on **one frame at full size**,
|
|
1240
1251
|
because a tile is too small to say which part a pixel belongs to. When a frame
|
|
1241
1252
|
looks wrong, render it again with `--hide <slot>` β the **largest attachment**
|
|
@@ -4518,6 +4529,17 @@ rest; a frame parent scaled unevenly, sheared or reflected, or such a bone
|
|
|
4518
4529
|
between two links; a link below a bone a later stop generates; a `from` bone in
|
|
4519
4530
|
the angles' path.
|
|
4520
4531
|
|
|
4532
|
+
On the first link that bound is |rest| + `outward`, where rest is the angle
|
|
4533
|
+
between the direction the link hangs in and the aim from its root at
|
|
4534
|
+
`<chain>_touch`, so where the point sits decides it as much as `outward` does.
|
|
4535
|
+
The refusal names the link, its root's world position and hang, the point's world
|
|
4536
|
+
position, rest, the end and the sum, and says what to move: `<chain>_touch` to
|
|
4537
|
+
within 180Β° β `outward` of the link's hang direction β below the root, for a link
|
|
4538
|
+
that hangs straight down and `outward` up to 90Β° β or `outward` down to
|
|
4539
|
+
180Β° β |rest|. A leg that rises from behind the cloth, as on a prone figure,
|
|
4540
|
+
runs its local +x up the screen, so `at` at its far end lands above the chain's
|
|
4541
|
+
root; put it at the knee, below the root (`LG34`).
|
|
4542
|
+
|
|
4521
4543
|
The stop decides nothing about *when* β it is what the object can do. Read it
|
|
4522
4544
|
with `render --pass-through <cloth>:<leg>` (Β§8.5) on the motion that swung
|
|
4523
4545
|
through before.
|
|
@@ -6825,6 +6847,14 @@ it had nothing to step (`nanStepping=skipped`). What it does not see is a skin o
|
|
|
6825
6847
|
than the default, which A10 does not pose. The sentence, and where to look for the
|
|
6826
6848
|
number, is under *nothing to draw* in Β§3.4.
|
|
6827
6849
|
|
|
6850
|
+
β¨οΈ **An invocation missing a flag is one line, before any of these.** `build`,
|
|
6851
|
+
`explain` and `validate` given a cut spelled out with `--rig`, `--motion` or
|
|
6852
|
+
`--out` absent print `rigc: <command> needs --<flag> <value> (or --cut <name>
|
|
6853
|
+
--cuts <cuts.json>)` on stderr and exit 2 β the line alone, with no command
|
|
6854
|
+
catalogue under it. An input flag that names a **directory** is a compile error
|
|
6855
|
+
with a remedy in it: `CE_COMPILE_JSON_READ_FAILED` says what the flag wants and
|
|
6856
|
+
names the one file there that reads as it (Β§5.1.2). It is never read as that file.
|
|
6857
|
+
|
|
6828
6858
|
### 5.1 Compile errors β before the gate
|
|
6829
6859
|
|
|
6830
6860
|
A `CompileError` names the object and the field, and nothing is written. Its line
|
|
@@ -6901,7 +6931,7 @@ retain the minimal inputs and report that code rather than inventing author data
|
|
|
6901
6931
|
| <a id="CE_COMPILE_RGBA_CHANNEL_COUNT"></a>`CE_COMPILE_RGBA_CHANNEL_COUNT` | βrgba value needs 4 channels, got <value>β | Supply four rgba channels. |
|
|
6902
6932
|
| <a id="CE_COMPILE_RGBA2_CHANNEL_COUNT"></a>`CE_COMPILE_RGBA2_CHANNEL_COUNT` | βrgba2 value needs 7 channels, got <value>β | Supply seven rgba2 channels. |
|
|
6903
6933
|
| <a id="CE_COMPILE_COLOUR_CHANNEL_COUNT"></a>`CE_COMPILE_COLOUR_CHANNEL_COUNT` | β<value> value needs <value> channel[ / s], got <value>β | Supply the channel count named for this colour shape. |
|
|
6904
|
-
| <a id="CE_COMPILE_JSON_READ_FAILED"></a>`CE_COMPILE_JSON_READ_FAILED` | βcannot read <value>: <value>β<br>Also: β<value> (line <value>, column <value>)β | Fix the named JSON file's path, permissions or JSON syntax. |
|
|
6934
|
+
| <a id="CE_COMPILE_JSON_READ_FAILED"></a>`CE_COMPILE_JSON_READ_FAILED` | βcannot read <value>: <value>β<br>Also: β<value> (line <value>, column <value>)β<br>Also, when `--rig`, `--motion` or `--manifest` names a directory: β<value> names a directory; it wants <value> β <value> exists thereβ Β· ββ¦ β <value> holds no .json fileβ Β· ββ¦ β <value> holds <value> .json file(s) and none reads as one: <value>β Β· ββ¦ β <value> .json files in <value> read as one, so name it: <value>β, a list cut at eight names with β, β¦ <value> in allβ | Fix the named JSON file's path, permissions or JSON syntax. For a directory, name the file the line names β the one `.json` there that reads as that input (a rig spec through the rig spec's parser, a motion spec through the motion spec's; for `--manifest`, any `.json`) β or pick one of the files it lists. A directory is never read as its file: rigc compiles what it was given. |
|
|
6905
6935
|
| <a id="CE_COMPILE_IMAGE_MISSING"></a>`CE_COMPILE_IMAGE_MISSING` | βimage "<value>" is not on disk at <value>β<br>Also: β<value>.pngβ | Supply the PNG at the named images path or correct the image name. |
|
|
6906
6936
|
| <a id="CE_COMPILE_IMAGE_PNG_INVALID"></a>`CE_COMPILE_IMAGE_PNG_INVALID` | β<value> is <value>, not a PNG: its first <value> byte(s) are <value>, where a PNG's first 8 are <value><value>. rigc reads PNG and nothing else β its page-size and alpha readers, its renderer and its region lift all decode PNG, and it links no decoder for any other format β so nothing in this file was measured. Re-export it as PNGβ<br>β<value> is a PNG whose IHDR chunk declares <value> byte(s) of data, where the format's is 13β<br>β<value> is a PNG whose IHDR chunk states its size as <value>x<value>: <value> 0, where the format requires each dimension to be at least 1, so the file describes no texel and nothing in it can be measured or drawn. Re-export itβ<br>β<value> is a PNG whose first chunk is <value> rather than the IHDR the format requires firstβ<br>β<value> is a truncated PNG: it is <value> byte(s) long and <value>, so no reader can decode it. Re-export or re-copy the file wholeβ<br>β<value> is a truncated PNG: it is <value> byte(s) long and ends <value> byte(s) into the 8-byte PNG signature, so no reader can decode it. Re-export or re-copy the file wholeβ<br>β<value> is a truncated PNG: it is <value> byte(s) long and ends after the signature with no IEND chunk, so no reader can decode it. Re-export or re-copy the file wholeβ<br>β<value> is a truncated PNG: it is <value> byte(s) long and its <value> chunk at byte <value> declares <value> byte(s) of data, which with its CRC runs to byte <value>, so no reader can decode it. Re-export or re-copy the file wholeβ<br>β<value> is a truncated PNG: it is <value> byte(s) long and {ends after <value> with no IEND chunk / ends <value> byte(s) into the header of the chunk after <value>}, so no reader can decode it. Re-export or re-copy the file wholeβ<br>β<value> is empty (0 bytes), and a PNG's first 8 bytes are <value>β<br>β[; its name ends in .png, and it is the bytes that decide what a file is / ]β<br>β[its width and its height are / [its width is / its height is]]β<br>βa <value> imageβ<br>βa WebP image (a RIFF/WEBP container whose first chunk is <value>)β<br>βimage "<value>": <value>β<br>βin no image format rigc recognises by its signature (it names <value> when it meets them)β<br>Here the values named in the message include: βKTX2β, βGIFβ, βJPEGβ. | Supply a readable PNG with the required header and dimensions at the named path. |
|
|
6907
6937
|
| <a id="CE_COMPILE_ATLAS_INPUT_MISSING"></a>`CE_COMPILE_ATLAS_INPUT_MISSING` | β--atlas-in names <value>, which is not on diskβ | Correct --atlas-in or supply the atlas file at that path. |
|
|
@@ -7352,7 +7382,7 @@ retain the minimal inputs and report that code rather than inventing author data
|
|
|
7352
7382
|
| <a id="CE_STOP_LINK_UNDRIVEN"></a>`CE_STOP_LINK_UNDRIVEN` | β<stop.who>: links[<k>] "<link>" is written by no constraint of this rig spec β a stop holds a link something swings (a physics, ik, transform or path constraint in "constraints"), and its copy and clamp run right after that. A physics constraint in the motion spec's table is emitted after every rig constraint, past the clamp; declare it hereβ | Declare the swinging physics, ik, transform or path constraint in the rig spec's constraints before its stop clamp. |
|
|
7353
7383
|
| <a id="CE_STOP_LINK_PARENT_CHAIN"></a>`CE_STOP_LINK_PARENT_CHAIN` | β<stop.who>: links[<k>] "<links[k]>" does not hang below "<names.drawn[k - 1]>", the drawn link of links[<k - 1>] β a chain's next link hangs from the drawn link above it, so the clamp that holds that one carries this one; parent it thereβ | Parent each lower link under the preceding generated drawn link. |
|
|
7354
7384
|
| <a id="CE_STOP_AGAINST_MOVES_WITH_STOP"></a>`CE_STOP_AGAINST_MOVES_WITH_STOP` | β<stop.who>: "against" "<stop.against>" hangs below "<under>", which the stop moves β the point would swing with the chain it holds off; name the legβ | Name an against bone outside the subtree moved by the stop. |
|
|
7355
|
-
| <a id="CE_STOP_LOCAL_BOUND_RANGE"></a>`CE_STOP_LOCAL_BOUND_RANGE` | β<stop.who>: "<field><end>" <range[end]> puts <what>'s bound at <value>Β°, past Β±<BOUND>Β° β the clamp reads a local rotation in (-180, 180], so a bound past it is never reached and the range it states is not the one that holdsβ<br>Here the values named in the message include: βinwardβ, βoutwardβ. |
|
|
7385
|
+
| <a id="CE_STOP_LOCAL_BOUND_RANGE"></a>`CE_STOP_LOCAL_BOUND_RANGE` | β<stop.who>: "<field><end>" <range[end]> puts <what>'s bound at <value>Β°, past Β±<BOUND>Β° β the clamp reads a local rotation in (-180, 180], so a bound past it is never reached and the range it states is not the one that holdsβ<br>On the first link it goes on: β. "<stop.links[0]>" hangs at <at.hang>Β° from its root at [<at.root>], and "<stop.names.point>" ("at" [<stop.at>] on "<stop.against>") sits at [<at.point>] β Spine world, y up, at the setup pose β so the aim from that root to the point lies <off>Β° off the hang (rest <rest>Β°): \|rest\| <off>Β° + "<end>" <e>Β° is <sum>Β°β, then one of β, above <BOUND>Β°. Move "<stop.names.point>" to within <BOUND β e>Β° (<BOUND>Β° β "<end>") of the direction "<stop.links[0]>" hangs in from its root, <at.hang>Β°, or state "<end>" at most <BOUND β off>Β°β, β, above <BOUND>Β°, and no point lowers it: \|rest\| is above 0Β° for every point a stop accepts. State "<end>" below <BOUND>Β°β, β, below -<BOUND>Β°. State "<end>" at least <βBOUND β off>Β°β<br>Here the values named in the message include: βinwardβ, βoutwardβ. | On the first link the bound is \|rest\| + the end, where rest is the angle from the link's hang to the aim at `<chain>_touch`, both read from the link's root: move `<chain>_touch` (its `at` on `against`) to within 180Β° β `outward` of the direction the link hangs in from its root β on a link hanging straight down with `outward` at most 90Β°, any point below the root β or lower `outward` to at most 180Β° β \|rest\|. A leg that rises from behind the cloth, as on a prone figure, puts a point at its far end above the root; put it at the knee. On a lower link, bring `lower`'s ends within Β±180Β° of the link's own rest bend. |
|
|
7356
7386
|
| <a id="CE_STOP_DEPENDENCY_ORDER"></a>`CE_STOP_DEPENDENCY_ORDER` | β<who>: "<tip>" hangs below "<name>", which a later stop generates β declare that stop first, so its angles are known when this one is readβ<br>Also: β<value>: stop "<value>"β | Declare the stop generating the parent before the stop depending on it. |
|
|
7357
7387
|
| <a id="CE_STOP_ANCESTOR_MANIFEST_POSITION"></a>`CE_STOP_ANCESTOR_MANIFEST_POSITION` | β<who>: "<name>" takes its place from the manifest ("from"), and a stop's angles are read off the setup pose this rig spec states β state its x, y and rotationβ<br>Also: β<value>: stop "<value>"β | State x, y and rotation for the named setup-pose ancestor instead of from. |
|
|
7358
7388
|
| <a id="CE_STOP_FRAME_PARENT_TRANSFORM"></a>`CE_STOP_FRAME_PARENT_TRANSFORM` | β<who>: "<parent>", where the frame hangs, is scaled unevenly, sheared or reflected at the setup pose β the frame's angles add to its parent's only through a rotation and an even scale, and inward is a turn's senseβ<br>Also: β<value>: stop "<value>"β | Use a frame parent with rotation and even positive scale, without shear or reflection. |
|
|
@@ -8478,6 +8508,34 @@ from its own frame and no earlier (`RE02`); and plants on the core the readings
|
|
|
8478
8508
|
mapping rejects β the schedule ignored, its y read up, each change a frame late or
|
|
8479
8509
|
early β each red against spine-core (`RE03`).
|
|
8480
8510
|
|
|
8511
|
+
**Whether the physics ran.** Every render β with this flag or without β says, under
|
|
8512
|
+
each set's line, how many physics constraints the poser stepped on at least one of
|
|
8513
|
+
its frames, and at what rate:
|
|
8514
|
+
|
|
8515
|
+
```text
|
|
8516
|
+
.. idle 25 frame(s), 2.000s + contact.png -> render/idle
|
|
8517
|
+
.. physics: 3 constraint(s) stepped at 12 fps
|
|
8518
|
+
```
|
|
8519
|
+
|
|
8520
|
+
and `frames.json` carries the same two figures on that set, `"physics": { "stepped":
|
|
8521
|
+
3, "fps": 12 }`. The rate is the set's own: one physics update a frame. The count is
|
|
8522
|
+
read off the posed state, not off the declaration: a constraint counts when, after
|
|
8523
|
+
a step that moved the skeleton's clock, its own clock reads the skeleton's β which
|
|
8524
|
+
the physics update sets and every way of not running leaves behind. So a constraint
|
|
8525
|
+
that is inactive on every frame, or at a mix of 0, is not counted, and the line names
|
|
8526
|
+
it: `physics: 0 constraint(s) stepped at 12 fps β 1 of the 1 declared took none:
|
|
8527
|
+
"iris_settle" (inactive, or a mix of 0, on every frame)`; a set of one pose β an
|
|
8528
|
+
animation of no length, or the setup pose of a skeleton with none β takes no step
|
|
8529
|
+
and says so. A rig that declares no physics constraint prints `physics: none in this
|
|
8530
|
+
rig β it declares no physics constraint, so no frame steps one` once, before the
|
|
8531
|
+
sets, and its `frames.json` carries no `physics` field, as every frame set written
|
|
8532
|
+
before the field existed carries none. This is what tells a reading β a fold count
|
|
8533
|
+
from `--folds`, say (Β§8.3) β taken with the physics from one taken without. Both
|
|
8534
|
+
posers read the same constraints on every frame, and `cli.ts`, `cli.ts --poser
|
|
8535
|
+
spine` and `cli_core.ts` write the same files (`PH01`, `PH02`); the step removed β
|
|
8536
|
+
on the core, or spine-core's `PhysicsConstraint.update` made a no-op β reads 0
|
|
8537
|
+
(`PH03`), and a constraint at mix 0 is named (`PH04`).
|
|
8538
|
+
|
|
8481
8539
|
**The files.** `frames.json` and each `geometry.json` record the schedule as
|
|
8482
8540
|
`environment`, its steps as read, `t`, `wind`, `gravity` in that order. The
|
|
8483
8541
|
framing reads it too: a gust can carry a coat past the box a still rig fills, so
|
|
@@ -8696,6 +8754,131 @@ one scaled to nothing β named with the frame.
|
|
|
8696
8754
|
**A reading, not a gate.** Whether a range is too wide is yours; Β§4.6 says which
|
|
8697
8755
|
values move it.
|
|
8698
8756
|
|
|
8757
|
+
### 8.8 Where two slots stop meeting β `render --seam`
|
|
8758
|
+
|
|
8759
|
+
A cut rig can tear in motion: two pieces painted to overlap at a joint pull apart
|
|
8760
|
+
and the background shows between them, or one swings clear of the piece it was
|
|
8761
|
+
tucked under and the overlap it carries shows where nothing was. Both pass every
|
|
8762
|
+
assertion and every check, and either can be intended β so `--seam` names the
|
|
8763
|
+
frames and decides nothing. On the selftest's probe (`pass_through_probe`,
|
|
8764
|
+
`fixtures/public.ts`: a skirt whose hip swings 30Β° and back over a leg that hangs
|
|
8765
|
+
inside it, and a second copy of the leg on the same bone):
|
|
8766
|
+
|
|
8767
|
+
```bash
|
|
8768
|
+
bun cli.ts render --candidate path/to/spine --animation swing --seam skirt:leg,leg_front:leg [--skin β¦] [--fps 12]
|
|
8769
|
+
# rigc render
|
|
8770
|
+
# .. swing 25 frame(s), 2.000s + contact.png + seam.json (1 of 2 pair(s) open) -> β¦/swing
|
|
8771
|
+
# .. seam "swing": seam "skirt" | "leg" opens on 23 of 25 frame(s) read [1-23] at 12 fps, max 1606 px β gap on 13 frame(s) [6-18], worst frame 12 (t=1.0000s): 616 px (616.00 unitsΒ² at 1.0000 units a px) in world box x [-7.00, 19.00] y [105.99, 196.00] (26.00 Γ 90.00); ghost on 23 frame(s) [1-23], worst frame 12 (t=1.0000s): 1606 px (1606.00 unitsΒ² at 1.0000 units a px) in world box x [-27.00, 41.00] y [104.99, 198.00] (68.00 Γ 93.00)
|
|
8772
|
+
# .. seam "swing": seam "leg_front" | "leg" stays closed on all 25 frame(s) read at 12 fps β no gap, no ghost
|
|
8773
|
+
```
|
|
8774
|
+
|
|
8775
|
+
The skirt swinging off the leg opens background between the leg's right edge and
|
|
8776
|
+
the skirt's swung side (the gap, widest at the top of the swing), and uncovers the
|
|
8777
|
+
top of the leg the skirt hid at rest (the ghost). The two copies of the leg move
|
|
8778
|
+
on one bone and never open. A pair with nothing to read says so β
|
|
8779
|
+
`seam "a" | "b": SKIP β the two drawings share no cell at the setup pose on any of
|
|
8780
|
+
the N frame(s) both draw, so there is no band to read` β and a closed pair says
|
|
8781
|
+
`stays closed on all N frame(s) read`; the two are never the same line.
|
|
8782
|
+
|
|
8783
|
+
**The frame of reference.** A seam is not read in the world: a limb that swings
|
|
8784
|
+
moves both of its pieces from where they were at the setup pose, and that is
|
|
8785
|
+
motion, not a tear. It is read **from each piece's own point of view**. A world
|
|
8786
|
+
point of a frame is carried back into a piece's setup pose by the affine map of
|
|
8787
|
+
that piece's triangle holding it as the frame poses it; outside the piece's mesh,
|
|
8788
|
+
by the map of the triangle it is least outside of (the largest smallest
|
|
8789
|
+
barycentric weight, the lowest index on a tie) β exact for a region and for any
|
|
8790
|
+
mesh one bone moves, the nearest affine piece of a skinned one. Two pieces that
|
|
8791
|
+
move together carry a point back to one place, so a pair moved whole reads nothing.
|
|
8792
|
+
|
|
8793
|
+
**Every number, and where it comes from.** Nothing here is a threshold the rig
|
|
8794
|
+
did not declare; each figure is the posed vertices (`--geometry`'s), the rest
|
|
8795
|
+
table's vertices, and the atlas alpha:
|
|
8796
|
+
|
|
8797
|
+
- **The cell** is one texel of the coarser of the two drawings at the setup pose,
|
|
8798
|
+
in world units β the step `--pass-through` steps by (Β§8.5) β on multiples of it
|
|
8799
|
+
from the world origin. `px` counts cells: on art drawn one texel to a unit, a
|
|
8800
|
+
cell is a world unit and a px is a pixel of the art.
|
|
8801
|
+
- **Drawn** is the renderer painting a cell's centre at **128 of 255** or more β
|
|
8802
|
+
the half-opacity line `--pass-through` draws outlines at, the frame's tint
|
|
8803
|
+
included, the setup pose drawn at the same tint (so a slot faded out is not a
|
|
8804
|
+
tear: it is faded in both). No other alpha enters.
|
|
8805
|
+
- **The band** is the cells both drawings draw at the setup pose β where the two
|
|
8806
|
+
were painted to overlap. It has **no width of its own**: it is the overlap the
|
|
8807
|
+
art states, dilated by nothing. A frame whose band is empty is not read; a pair
|
|
8808
|
+
whose band is empty on every frame both draw is a SKIP.
|
|
8809
|
+
- **gap** β a cell inside the convex hull of the band's cell centres, carried to
|
|
8810
|
+
the frame by both pieces (the region the joint occupies now, as both place it),
|
|
8811
|
+
that neither piece draws at the frame, and that at least one piece, carrying it
|
|
8812
|
+
back, finds drawn by either drawing at the setup pose: the two no longer cover
|
|
8813
|
+
what they covered together. The hull is the joint's own extent, which is what
|
|
8814
|
+
keeps a piece's far end swinging away from reading as a tear; the rest test is
|
|
8815
|
+
what keeps a band that is concave at the setup pose from reading its own notch.
|
|
8816
|
+
- **ghost** β a cell one piece draws at the frame and the other does not, which
|
|
8817
|
+
the drawing piece, carrying it back, lands in the band, and which the other,
|
|
8818
|
+
carrying it back, finds neither drawing at at the setup pose: the overlap swung
|
|
8819
|
+
clear of its partner.
|
|
8820
|
+
|
|
8821
|
+
Draw order does not enter: a cell is covered or bare whichever piece is in front.
|
|
8822
|
+
|
|
8823
|
+
**Its reach, stated.** A piece folded right back over its partner carries the band
|
|
8824
|
+
across the region it vacated, so a hull that wide reads the vacated cells its
|
|
8825
|
+
partner's view still finds drawn. And no size is too small to print: on
|
|
8826
|
+
`gallery/walk`, built as its README says, `--seam thigh_f:shin_f,arm_f:hand_f`
|
|
8827
|
+
reads gaps of 1β2 px on 5 of the 12 frames at the knee and of 1 px on 2 at the
|
|
8828
|
+
wrist β the wrist's, drawn at 20 pixels a unit, is the V where the two outlines
|
|
8829
|
+
meet at an angle, one cell deep. Whether that is a tear is yours.
|
|
8830
|
+
|
|
8831
|
+
**The file.** `seam.json`, `rigc-seam/1`, beside each set's frames β the contract
|
|
8832
|
+
a consumer reads it by. Every unit is in this table; the file's own `reading`
|
|
8833
|
+
block carries the definitions above in words.
|
|
8834
|
+
|
|
8835
|
+
| Field | Type | Unit | Meaning |
|
|
8836
|
+
| --- | --- | --- | --- |
|
|
8837
|
+
| `spec` | string | β | `"rigc-seam/1"` |
|
|
8838
|
+
| `animation` | string or `null` | β | the set's animation; `null` for a skeleton with none (its one frame is the setup pose) |
|
|
8839
|
+
| `skin` | string | β | the skin asked for; **absent** when none was |
|
|
8840
|
+
| `fps` | number | frames per second | the rate the set was sampled at |
|
|
8841
|
+
| `reading` | object | β | `read`, `cell`, `drawn`, `band`, `carried`, `gap`, `ghost`, `box`, `status`: the definitions, in words |
|
|
8842
|
+
| `pairs[]` | array | β | one per pair, in the flag's order |
|
|
8843
|
+
| `pairs[].a`, `pairs[].b` | string | β | the two slots, as the flag names them |
|
|
8844
|
+
| `pairs[].drawn` | integer | frames | frames on which both slots draw |
|
|
8845
|
+
| `pairs[].banded` | integer | frames | of those, frames whose two drawings share a cell at the setup pose β the frames read |
|
|
8846
|
+
| `pairs[].status` | `"read"` or `"skip"` | β | `skip` when `banded` is 0: nothing measured, which is **not** a seam that held |
|
|
8847
|
+
| `pairs[].opens` | integer[] | frame indices | every frame with a gap or a ghost, ascending |
|
|
8848
|
+
| `pairs[].maxPx` | integer | px (cells) | the most cells of either kind on any frame; 0 when none opens |
|
|
8849
|
+
| `pairs[].gap`, `pairs[].ghost` | object | β | one per kind, each `{ frames, spans, worst }` |
|
|
8850
|
+
| `<kind>.frames[]` | hit[] | β | only the frames with a cell of that kind, in frame order |
|
|
8851
|
+
| `<kind>.spans[]` | array | β | `<kind>.frames` as runs of consecutive indices: `{ frames: [first, last], times: [first, last], maxPx }` β frame indices, seconds, px |
|
|
8852
|
+
| `<kind>.worst` | hit or `null` | β | the frame with the most cells, the earliest on a tie; `null` when none |
|
|
8853
|
+
| hit `index` | integer | frame index | the frame β `f<index>.png`, zero-padded |
|
|
8854
|
+
| hit `time` | number | seconds | the frame's time |
|
|
8855
|
+
| hit `px` | integer | px (cells) | cells of that kind on the frame |
|
|
8856
|
+
| hit `cell` | number | world units | a cell's side |
|
|
8857
|
+
| hit `area` | number | world unitsΒ² | `px Γ cellΒ²` |
|
|
8858
|
+
| hit `box` | `[minX, minY, maxX, maxY]` | world units, y up | the counted cells' whole squares, in Spine world |
|
|
8859
|
+
|
|
8860
|
+
A downstream warning is a projection of this file and needs nothing re-measured:
|
|
8861
|
+
one span of one kind is `{ pair: "<a>|<b>", frames: span.frames, times:
|
|
8862
|
+
span.times, maxPx: span.maxPx, kind }`, and one pair over one animation is
|
|
8863
|
+
`opens.length` frame(s) `[opens]`, max `maxPx` px β with `status: "skip"` the case
|
|
8864
|
+
to say SKIP for.
|
|
8865
|
+
|
|
8866
|
+
**Refused, nothing written:** a slot the skeleton does not have (with the slots it
|
|
8867
|
+
does), an entry without exactly one `:`, an empty entry or name, a slot paired with
|
|
8868
|
+
itself (a seam is between two drawings), a pair named twice in either order (the
|
|
8869
|
+
reading is symmetric, so `b:a` is `a:b` again), a slot that draws on no frame of
|
|
8870
|
+
the sets drawn, a slot a clip cuts (its drawing is no longer its attachment's, so
|
|
8871
|
+
it cannot be carried back), and `--slot`/`--hide` beside the flag, for
|
|
8872
|
+
`--pass-through`'s reason.
|
|
8873
|
+
|
|
8874
|
+
**Nothing else moves.** The PNGs, `contact.png`, `frames.json` and `geometry.json`
|
|
8875
|
+
are byte for byte what the render writes without the flag, both posers write the
|
|
8876
|
+
same file, and two runs write the same bytes (the selftest's `seam` suite).
|
|
8877
|
+
|
|
8878
|
+
**A reading, not a gate.** It never changes the exit status, whatever it finds. To
|
|
8879
|
+
look at a frame it names, draw that set over the box it printed:
|
|
8880
|
+
`render --animation <name> --viewport <x,y,w,h> --max 400`.
|
|
8881
|
+
|
|
8699
8882
|
---
|
|
8700
8883
|
|
|
8701
8884
|
## 9. Checking against the frames β `rigc check`
|
|
@@ -11522,6 +11705,7 @@ pixel (`IN09`).
|
|
|
11522
11705
|
| What does this bone move? | `--show art,mesh,weights:<bone> --dim` | the bone's weight over every region and mesh it moves, as a heat: a pixel whose centre lies in a triangle is painted the ramp's colour at the blend of its corners' weights β blue near 0, yellow at 0.5, red at 1 (the stops are in the legend) β and a weight of 0 is not painted, so the art shows where the bone does not reach. A region and an unweighted mesh are moved whole by their slot's bone. The weights are read off `skeleton.json`, the file both posers skin from; a bone that weights nothing paints nothing and the run says so on a line of its own |
|
|
11523
11706
|
| Does this part's edge follow that bone? | `--show art,weights:<bone>,mesh --overlay-slot <part> --zoom slot:<part>` | the part's own triangles and the bone's weight over them alone, over the whole painting. Where slots stack β a neck under the face, the hair, a collar β every mesh crossing the box is drawn and the heat is the union of every slot the bone moves, so the part's edge has no pixel of its own until the others are left out. The legend's `meshes[]` gives the part's weight for the bone at every vertex, so "does the top row follow the head" is read as numbers as well as colour |
|
|
11524
11707
|
| Is this rig's density where its motion is? | `--show art,density --dim` | each region and mesh filled by its **ratio** β its triangles per world unitΒ² over the figure's β on one ramp, log-scaled and centred on 1Γ: grey is as dense as the figure, pink to magenta denser (4Γ, 16Γ), light to dark green sparser (1/4Γ, 1/16Γ), clamped past the end stops (the stops are in the legend). A part's area is its posed mesh's own, the sum of its triangles' areas at the pose drawn, not the image's box; a region is two triangles over its quad. Where parts overlap the pixel is the part drawn last, as in the art, so a part under others is read with `--overlay-slot <part>`. The figure's totals are always over every slot the pose shows β the filter picks what is drawn and listed, never what the figure is β and the legend's `density` gives each slot's attachment, kind, vertices, triangles, hull, area, triangles per area, ratio and the bones it is weighted to, in draw order. A measurement, not a verdict: no bar is drawn, and `A13_MESH_BUDGET` stays the one gate on counts |
|
|
11708
|
+
| Does a joint tear in this animation? | `render --seam <a>:<b> --animation <name>` (Β§8.8) | One line per pair: the frames where background opens inside the joint (gap) or the overlap band of one piece shows past its partner (ghost), with the worst frame's cell count and world box; `seam.json` carries every frame and its time, and the runs of frames. A pair whose drawings never overlap is a SKIP, said by name. A reading, not a verdict β the exit status never moves |
|
|
11525
11709
|
| Does anything fold in this animation? | `render --folds --animation <name>` (Β§8.3), then `inspect --show art,folds,mesh --animation <name> --at <the worst frame's time> --zoom folds` | `render --folds` prints one line per mesh with a triangle turned over against the setup pose: the frames, the worst frame's count, which triangles, their world box at that frame and their setup-pose area. The view fills those triangles at the pose drawn β every pixel whose centre a folded triangle holds β in the colour the legend's `folds` states, and the legend's `folds[]` names, per mesh, the folded triangles by index with the same box and area, and the setup pose's degenerate and inverted ones, which are never counted. A fold is usually a sliver a few units across, invisible in a picture of the whole mesh: `--zoom folds` frames the picture to the folded triangles' box. A thin sliver still needs pixels β raise `--max` when the fill is a line. A reading, not a verdict |
|
|
11526
11710
|
| How is this part triangulated? | `--show art,mesh,hull --zoom slot:<slot>` | each mesh's triangle edges, and over them its hull β vertex 0 to 1 to β¦ to `hull β 1` and back. Regions are not drawn: a region is not a mesh. The legend lists every vertex in world units and in pixels |
|
|
11527
11711
|
| Which is drawn over which? | `--show art,slots` | each slot that shows a region or mesh numbered by its place in the draw order **at the pose drawn** (0 is drawn first), on a white box at the centre of its attachment's box β so a `drawOrder` key (Β§4.7) shows from the first frame `render` draws it on |
|
package/docs/INGEST.md
CHANGED
|
@@ -191,6 +191,7 @@ rigc render
|
|
|
191
191
|
.. atlas β¦/examples/spineboy/export/spineboy.atlas
|
|
192
192
|
.. poser spine-core β no skeleton.model.json beside β¦/examples/spineboy/export/spineboy-pro.json β a Spine export, not a rigc build
|
|
193
193
|
.. 200x186px at 8 fps, 1 set(s) -> β¦/render/sb
|
|
194
|
+
.. physics: none in this rig β it declares no physics constraint, so no frame steps one
|
|
194
195
|
.. walk 9 frame(s), 1.000s + contact.png -> β¦/render/sb/walk@8fps
|
|
195
196
|
rigc: wrote β¦/render/sb/frames.json
|
|
196
197
|
```
|
package/docs/MOTION.md
CHANGED
|
@@ -1067,6 +1067,7 @@ rigc render --candidate spine --fps 24 --max 200
|
|
|
1067
1067
|
|
|
1068
1068
|
```
|
|
1069
1069
|
.. 134x200px at 24 fps, 1 set(s) -> β¦/semaphore/render
|
|
1070
|
+
.. physics: none in this rig β it declares no physics constraint, so no frame steps one
|
|
1070
1071
|
.. raise 14 frame(s), 0.542s + contact.png -> β¦/semaphore/render/raise@24fps
|
|
1071
1072
|
```
|
|
1072
1073
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.61.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": {
|