rig-parts 1.5.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/cli.ts +10 -2
- package/docs/AUTHORING.md +83 -10
- package/docs/STABILITY.md +1 -1
- package/package.json +1 -1
- package/src/assemble.ts +329 -9
- package/src/build.ts +2 -1
- package/src/check.ts +224 -28
- package/src/instruments.ts +34 -10
- package/src/parts.ts +39 -2
- package/src/propose.ts +97 -39
- package/src/requirements.ts +58 -3
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ config beside the values it replaced: the two outer skirt chains were moved out
|
|
|
37
37
|
against 1.4725; moved, 2.425). One input-stage edit: <code>hair_back</code> is taken
|
|
38
38
|
from the full run, because the twin tails leave the head crop sideways (a plan edit).
|
|
39
39
|
<code>check</code> printed, verbatim:<br/>
|
|
40
|
-
<code>50 assertions:
|
|
40
|
+
<code>50 assertions: 25 measured (25 passed, 0 failed), 25 skipped, 0 not in profile "spine-html"</code><br/>
|
|
41
41
|
<code>loop: idle 49 frame(s) at 12 fps, f0000 vs f0048 (t = 4s): max |d| 0 (0 required)</code><br/>
|
|
42
42
|
<code>seam: setup pose at 654x1216, scale 0.9438: mean |d| 0.147 (<= 1.0), 0 px over 40, 0.00 rig px (the count / s²; <= 50), 0 px over 80 (reported)</code><br/>
|
|
43
43
|
<code>pack: skeleton.png 1051x972, 23 region(s), 96.9% covered, padding 2, page edges free, shape polygon</code><br/>
|
|
@@ -274,7 +274,7 @@ agent skill.
|
|
|
274
274
|
| `comfy seethrough --image --out [--host]` | optional: run the ComfyUI See-through wrapper on one image and write the form `layers` reads |
|
|
275
275
|
| `layers <dir \| layers.json \| file.psd>` | print every layer of a decomposition: draw order, name, tag group, box, size, opaque pixels, depth, and its translucent, background and area figures; a `WARN` line for a layer `--propose-plan` will leave out |
|
|
276
276
|
| `sheet --source <png> --layers <path>… --out <png>` | a labelled contact sheet of the painting and every layer or part |
|
|
277
|
-
| `assemble --propose-plan …` | propose `assemble.plan` and `extend_below_crop` from the two runs, leaving out an implausible layer with a note naming the rule |
|
|
277
|
+
| `assemble --propose-plan …` | propose `assemble.plan` and `extend_below_crop` from the two runs, leaving out an implausible layer with a note naming the rule, and naming in a note any full-run body no planned part covers |
|
|
278
278
|
| `assemble --source --full --head --config --out [--seam] [--project]` | merge the two runs into rig-space parts, `parts.json`, the recomposite and its error map, and list the uncovered holes |
|
|
279
279
|
| `propose --head-box --full <run> --canvas WxH` | propose `seethrough.head_box` from the full run, held inside the painting |
|
|
280
280
|
| `propose --parts --source --out [--compare <config>]` | propose bones, meshes, regions and an idle; draw the overlay; print, per bone, which check read it or that none did and why (its role, its kind of segment), and write `basis.json` beside the proposal — what each bone rests on (a joint, a part's mask, a ratio, other bones) and, with `--compare`, whether the config's bone differs |
|
|
@@ -333,7 +333,9 @@ These are limits of the approach, stated so nobody reads more into a green run:
|
|
|
333
333
|
### What can be built on top
|
|
334
334
|
|
|
335
335
|
This package makes no expression and no jiggle, but the rig it writes is the material for
|
|
336
|
-
both, and the config has a field for each
|
|
336
|
+
both, and the config has a field for each; `examples/demo` and `examples/scarf` carry a worked
|
|
337
|
+
set of each (issue #224: a tilt, two looks, a smile, a startle, a wave or a bow and a hop,
|
|
338
|
+
with physics on the hair, the skirt, the sleeves and the scarf ends). [docs/AUTHORING.md](docs/AUTHORING.md) has a recipe
|
|
337
339
|
for each — a config fragment, the `check` lines it passes with, and its limit:
|
|
338
340
|
|
|
339
341
|
- [**Expressions and other named animations**](docs/AUTHORING.md#recipe-expressions-and-other-named-animations):
|
package/cli.ts
CHANGED
|
@@ -358,7 +358,11 @@ usage:
|
|
|
358
358
|
mean channel; error px: max channel > 40; uncovered: of those, where no
|
|
359
359
|
part has alpha above 128), then the uncovered holes (8-connected) and the
|
|
360
360
|
largest ${HOLES_LISTED}, each \`uncovered hole N: <px> px at x,y wxh (between
|
|
361
|
-
"<part>" <px> px, …)\` — the list parts.json holds under recomposite
|
|
361
|
+
"<part>" <px> px, …)\` — the list parts.json holds under recomposite —
|
|
362
|
+
and one \`uncovered:\` line per full-run layer the plan does not take from
|
|
363
|
+
the full run whose uncovered error holds a body (5x5-opened, 8-connected,
|
|
364
|
+
at least 150 full-run px in rig px), naming the layer, its pixels and the
|
|
365
|
+
rule that left it out.
|
|
362
366
|
Writes nothing unless every check passed.
|
|
363
367
|
--seam defaults to ${DEFAULT_SEAM_RULE}. --project says where a layer takes
|
|
364
368
|
the painting's pixel: core (the reference's) erodes every layer's top-most
|
|
@@ -371,7 +375,11 @@ usage:
|
|
|
371
375
|
Print {plan, extend_below_crop, notes} for config.assemble, from the two
|
|
372
376
|
runs. A layer \`layers\` WARNs about (PLAN_LAYER_TRANSLUCENT,
|
|
373
377
|
PLAN_LAYER_BACKGROUND, PLAN_LAYER_OVERSIZED) is not proposed, and a note
|
|
374
|
-
names it, its figures and the rule.
|
|
378
|
+
names it, its figures and the rule. A full-run layer holding a body no
|
|
379
|
+
planned part covers (a lock the head run's part does not reach and
|
|
380
|
+
extend_below_crop cannot continue) is named in a note with the body, the
|
|
381
|
+
rule that skipped it and the continuation; the plan itself is not
|
|
382
|
+
changed. Reads only config.seethrough.head_box,
|
|
375
383
|
config.seethrough.resolution and config.assemble.rig_scale — the rest of
|
|
376
384
|
the config need not exist yet.
|
|
377
385
|
|
package/docs/AUTHORING.md
CHANGED
|
@@ -961,10 +961,35 @@ recipe below). With exactly this file beside `examples/reference-sample`'s confi
|
|
|
961
961
|
A bone the rig lacks is rigc's refusal at the rig stage, in rigc's words (`RIG_RIGC_GREEN`); a
|
|
962
962
|
fault in the file itself is the loader's (the row above names each one).
|
|
963
963
|
|
|
964
|
+
**Worked sets.** `examples/demo/animations.json` and `examples/scarf/animations.json`
|
|
965
|
+
(issue #224) carry `tilt`, `look_l`, `look_r`, `smile`, `startled`, a gesture (`wave` on
|
|
966
|
+
`demo`, `bow` on `scarf`) and `hop`, each a one-shot that keys only what it needs; the
|
|
967
|
+
examples' READMEs list what each keys and why. Two rules make a clip layer over the idle on
|
|
968
|
+
a player's second track rather than replace it:
|
|
969
|
+
|
|
970
|
+
- **Key the bones the idle leaves alone.** The idle turns and breathes the `*_ctl` bones
|
|
971
|
+
the rig stage writes (`head_ctl`, `chest_ctl`, a chain's `<chain><i>_ctl`) and `neck`; the
|
|
972
|
+
bone under each (`head`, `chest`, `<chain><i>`) is free. A `look_l` that keys `head` alone
|
|
973
|
+
leaves the idle's sway alive under it; one that keys `head_ctl` or `neck` stops it for as
|
|
974
|
+
long as the clip holds the key.
|
|
975
|
+
- **Key no constraint, draw order, deform or attachment.** A player mixing tracks refuses
|
|
976
|
+
those; bones are all a clip needs.
|
|
977
|
+
|
|
978
|
+
**Strands that trail.** A chain whose links lag the body is one `physics` constraint per
|
|
979
|
+
link in `constraints`, on the link bone (`hairback_r0`, not `hairback_r0_ctl`), driving
|
|
980
|
+
`rotate`; the examples soften it down the chain. A single bone with no `length` (a bow, an
|
|
981
|
+
earring, a bun) cannot take a `rotate` drive (rig-c's `A23`) and takes `x`/`y` instead. The
|
|
982
|
+
values that read on the examples, and the ones refused on the way there, are in their
|
|
983
|
+
READMEs.
|
|
984
|
+
|
|
964
985
|
**Limits.** Every slot holds one attachment, the part's own image, so an expression moves,
|
|
965
986
|
turns and scales the face's parts and never swaps one for another: there is no second mouth
|
|
966
|
-
shape to key, and no lip-sync. `check` measures the idle alone: an animation beside it
|
|
967
|
-
passed rigc's gate and nothing else — no seam, stretch or hole bar has read it.
|
|
987
|
+
or eye shape to key, and no lip-sync. `check` measures the idle alone: an animation beside it
|
|
988
|
+
has passed rigc's gate and nothing else — no seam, stretch or hole bar has read it. On the
|
|
989
|
+
examples each clip was read by `check`'s own `TEXTURE_STRETCH` reading applied to the clip's
|
|
990
|
+
`rigc render --geometry` (their READMEs give the figures); that is a measurement, not a
|
|
991
|
+
gate, and it is where an arm raised overhead was found to tear `demo`'s elbow (5.327
|
|
992
|
+
against 1.926544).
|
|
968
993
|
|
|
969
994
|
### Recipe: a soft region driven by physics
|
|
970
995
|
|
|
@@ -1122,8 +1147,14 @@ full loader before its assemble stage writes anything.
|
|
|
1122
1147
|
→ paste `plan` and `extend_below_crop` into `config.assemble`. Config: the
|
|
1123
1148
|
`layers` row, with `head_box`.
|
|
1124
1149
|
Read `notes`: a layer left out by a plausibility rule is named there with its
|
|
1125
|
-
figures and the rule (§6, *Plausibility*)
|
|
1126
|
-
|
|
1150
|
+
figures and the rule (§6, *Plausibility*), and a full-run layer holding a body
|
|
1151
|
+
no planned part covers — long hair that leaves the head crop sideways, or a
|
|
1152
|
+
head-run part that stops above the crop line so `extend_below_crop` continues
|
|
1153
|
+
nothing — is named with the body, the rule that skipped it and the two
|
|
1154
|
+
continuations (take the part from the full run, or add the full-run layer as a
|
|
1155
|
+
second part behind it). The plan is not changed; which continuation fits is
|
|
1156
|
+
yours.
|
|
1157
|
+
7. `rig-parts assemble … --out work` — read its `uncovered hole` and `uncovered:` lines and look at
|
|
1127
1158
|
`work/render/recomposite_error_rig.png` (§5): red is painting no part holds, which
|
|
1128
1159
|
no later stage can see — then
|
|
1129
1160
|
`rig-parts propose --parts work/rig --source inputs/painting.png --out work` →
|
|
@@ -1204,7 +1235,7 @@ proposal in as the steps above say (`RL01`); every step must exit 0.
|
|
|
1204
1235
|
| --- | --- | --- | --- |
|
|
1205
1236
|
| `layers` | the table, then the `WARN` lines | every tag the plan will need has opaque pixels; one `face` in the head run; `0 WARN line(s)` | a run whose layer PNG is not its box's size (`LAYERS_PNG_MATCHES_BBOX`); a `WARN PLAN_LAYER_…` line (§6, *Plausibility*) |
|
|
1206
1237
|
| `sheet` of both runs | the tile list (and the sheet, if you can see) | eyes, irises, lashes and brows as left/right pairs in the head run | a head box that cut off an ornament: move `head_box`, re-run the head crop |
|
|
1207
|
-
| `assemble` | one line per part, the `pixels:` totals (opaque = visible + occluded; taken; visible but not projected), then `recomposite vs source: mean \|d\|, within 8, error px > 40, uncovered error px`, then `uncovered holes (8-connected): N` and the largest five as `uncovered hole K: <px> px at x,y wxh (between "<part>" <px> px, …)
|
|
1238
|
+
| `assemble` | one line per part, the `pixels:` totals (opaque = visible + occluded; taken; visible but not projected), then `recomposite vs source: mean \|d\|, within 8, error px > 40, uncovered error px`, then `uncovered holes (8-connected): N` and the largest five as `uncovered hole K: <px> px at x,y wxh (between "<part>" <px> px, …)`, then one `uncovered: <n> of the <N> uncovered error px lie on the full run's "<tag>", in a body of <px> px at x,y wxh; <the rule that left it out>` line per full-run layer the plan does not take from the full run whose uncovered error holds a body (5x5-opened, 8-connected, at least 150 full-run px in rig px); and look at `render/recomposite_error_rig.png`. Between the part lines and the totals, one `cut: "<part>" opaque <n> = "<part>" <kept> + "<piece>" <taken> …` line per part an `assemble.cuts` entry cut (below; it ends `; under <n> px filled, <m> px left empty` when a cut of that part has `under`), then one `fringe pushed back: "<part>" <px> px, where the painting shows "<holder>" <px> px, …` line per part whose fringe was cleared (below) | on the examples: `reference-sample` 0.83 / 98.0 % / 4,359 / 1,185, 259 holes, the largest 94 px; `reference-demo` 2.34 / 95.8 % / 10,340 / 1,564, 453 holes, the largest 70 px (default rule) — many slivers along part edges, no hole a region could hide | one large hole: part of the figure is in no layer — a plan entry is missing, hair left the head crop sideways (reference-demo's `hair_back` is taken from the full run for that reason), or See-through split one garment into two and left the space between them in neither (a skirt as two trouser legs); the `between` parts say where. When neither run holds it at all, an `assemble.patches` entry cuts it from the painting (below) |
|
|
1208
1239
|
| `propose` | `note:` lines, `LINT` lines, the `coverage` lines, `landmarks.png`, `basis.json` | no LINT line: every chain link lies on its mesh's art, and the hip is below the chest and the figure's top quarter; the `coverage:` summary says how many bones that verdict covers — a `not checked` bone was read by no check (its line says why), and a `ratio` or `fallback` bone in `basis.json` is a guess | a link off the art (a bone on the background) — move it onto the layer; `LINT hip at [x, y] is not below chest at [x, y]: …` or `LINT hip at [x, y] is above 0.25 of the figure height (figure y T..B, so hip y must be at least L): a hip at the shoulders` — move `hip` down to the waist (and `chest` between it and the neck); a `hanging strand … -- no chain proposed` note — that strand hangs stiff until you add a chain down the x and rows it names (§3); a `no blink: …` or `blink without brows: …` note — the idle will not blink (or its brows will not drop), because no part came from an `eyewhite` (or `eyebrow`) layer; the note names the tags looked for; a `no eyewhite part for eye_<s> …` note — the iris and lash parts it names ride `head` and do not blink, because no `eyewhite-<s>` part made that side's eye bone: take the eyewhite from the other run, or keep them on `head`; a `no face part: face box derived from …` note (always the first) — `head`, `neck` and every face-height scale come from a box guessed off the hair and neck (§3): check `head` and `neck` on `landmarks_head.png` and move them; when it adds `the blink shuts the eyes over no face part`, read `BLINK_NO_HOLE` after `check` — an eye with no face under it can open a hole (the `reference-sample` with only its face removed: 587 px). Under `--keypoints`: the `lint rule set:` line first (joints, or screen and why), then the joint notes — `missing: the rule "…" placed bone X instead` is a bone still guessed; `not used — …` names why an arm's joints did not place its sleeve (for a wrist on no part, the nearest part and the `--wrist-snap` that would bind it); a `wrist snap: …` note is a wrist bound past 15 px by the flag — check that chain's tip on `landmarks.png`; a `one handwear part holds both wrists` note is two chains on one mesh — cut the part with `assemble.cuts` to separate them; `LINT chest at … is not between the neck joint …`, `LINT hip at … is not past chest …`, `LINT hip at … is nearer the neck joint …` and `LINT <chain><i> at … does not advance from <s>_shoulder toward <s>_wrist` are the joint-frame lines: move that bone between, past or along the joints they name |
|
|
1209
1240
|
| `compare` | one line per pair (origin, parent, tip, length, direction — or SKIP and why), then the per-row summary, the unmapped bones of each side, `required:`, each side's roles and the `controls:` line | proposal against the config you corrected: the rows you changed and no others. Config against the `rig.json` `build` writes for it: measured on both examples, every origin within 6.19e-7 px (the rig's offsets are written to 6 places) and every chain link's tip and length within 4.99e-4 px (`length` is written to 3), printed to three places as `0.000` (a signed figure as `+0.000` or `-0.000`); `parent … is the right's ancestor at depth 2, <bone>_ctl between` for every keyed mesh bone, no `DIFFERENT` and no `NOT MAPPED` | `parent DIFFERENT: left a, right b` — a bone hangs from another parent than the other side's; `frames not related` — a rig with no stage (`skeleton.width`/`height`), two configs at different `rig_scale`: the distance rows SKIP, so declare the frame in a `--map` file if the author knows it; `FAIL STRUCTURE_REQUIRED_PRESENT` — a bone the map requires is missing through its pairs |
|
|
1210
1241
|
| `rig` (inside `build`) | one line per mesh: vertices, triangles, bones, influences, `cover`; the `bones` line; the `idle keys` line; then rigc's gate lines (with A15's declared SKIP under `--idle-keys direct`); in `rig.json`, each chain link's (or its `_ctl`'s) `length` and `rotation` | `cover 1.00000` on every mesh, both gate summaries `0 failed` (the compile and the packed pages); every link's `length` the distance to the next link and `rotation` its direction (Spine degrees, counter-clockwise, y up, local to the parent), every offset under it — a child bone's `x, y`, a weight's bind `x, y`, a region's `x, y` — in that turned frame, and a region on a link carrying `rotation` that turns it back upright. Nothing moved: `flattenRig` (`src/rig.ts`) turns every offset back and gives the unturned numbers | `RIG_LATTICE_ONE_LOOP`: change that mesh's `grid` |
|
|
@@ -1474,14 +1505,25 @@ immediately behind `X`; `"front"` patches last, then `"front"` pieces). Its coun
|
|
|
1474
1505
|
A part's `from` is `<full|head>:<tag>` — the See-through run and tag, which every
|
|
1475
1506
|
role (`propose`) and every judgement-line region (`check`) is read from — or, for a
|
|
1476
1507
|
patch, `painting:<its own name>`; a cut's piece carries the `<run>:<tag>` of the part
|
|
1477
|
-
it was cut from, so two records may share one
|
|
1508
|
+
it was cut from, so two records may share one, and names that part in `cut_from` (issue
|
|
1509
|
+
#230; written on a piece only). The records are in draw order, so a piece drawn `"back"`
|
|
1510
|
+
or before its base comes first, and only `cut_from` says which of the two the cut made.
|
|
1511
|
+
The reader refuses a `cut_from` that names no part of the file, the piece itself, another
|
|
1512
|
+
piece, or a part on another layer (`PARTS_CUT_FROM_KNOWN`). A `parts.json` written before
|
|
1513
|
+
1.5.6 has no `cut_from` and still reads; `propose` alone refuses it when two of its parts
|
|
1514
|
+
share a layer (`PROPOSE_CUT_FROM_RECORDED`): re-run assemble. **A patch is 100 % source**: every pixel is the
|
|
1478
1515
|
painting's, so it is all visible and all taken — `visible_px`, `projected_core_px`
|
|
1479
1516
|
and `source_px_taken` equal `opaque_px`, and `occluded_px`,
|
|
1480
1517
|
`visible_not_projected_px`, `refused_drift_px` and `merged_px` are 0. The reader
|
|
1481
1518
|
refuses a `painting:` record that says anything else, or that names another part
|
|
1482
1519
|
(`PARTS_FROM_KNOWN`, `PARTS_COUNTS_ADD_UP`). No tag rule reads a patch: `propose`
|
|
1483
1520
|
puts it on the nearest trunk bone as a region with a note, and `check` counts it
|
|
1484
|
-
in no region.
|
|
1521
|
+
in no region. No tag rule reads a cut's piece either (issue #230): its base already took
|
|
1522
|
+
the tag's rule, and a rule that runs once per part of a tag would name the base's bones
|
|
1523
|
+
and chains a second time, so the piece rides the nearest trunk bone as a region with a
|
|
1524
|
+
note saying so — give it a mesh in `meshes` if it should bend. The one exception is the
|
|
1525
|
+
sleeve rule, which names its chains by side and takes a cut sleeve as a blob of its own
|
|
1526
|
+
(the `one handwear part holds both wrists` note, §3).
|
|
1485
1527
|
|
|
1486
1528
|
`visible_px + occluded_px = opaque_px`, and every projected pixel is visible; the stage
|
|
1487
1529
|
refuses (`ASSEMBLE_COUNTS_ADD_UP`) rather than write counts that break either, and the
|
|
@@ -1658,13 +1700,14 @@ See-through with another seed.
|
|
|
1658
1700
|
| `HEADBOX_FACE_PRESENT`, `HEADBOX_RUN_SQUARE`, `HEADBOX_CANVAS_SIZE`, `HEADBOX_FITS_CANVAS` | the full run has no face or is not square; `--canvas` is not a positive size; or the painting cannot hold the proposed box at its size — its shorter side is the bound (shrinking it would cut the head) | `--full`, `--canvas`, or the painting |
|
|
1659
1701
|
| `PROPOSE_SOURCE_PRESENT`, `PROPOSE_PNG_PRESENT`, `PROPOSE_PNG_MATCHES_BOX` | the painting or a part PNG is missing, or a PNG is not its box | `--source`, `--parts` (re-run assemble) |
|
|
1660
1702
|
| `PROPOSE_FACE_PRESENT` | no part comes from a `face` layer, and the face-less fallback cannot derive a face box either: it needs the head run's `neck` and at least one head-run `front hair`/`back hair` part whose top is above the neck's, and the message names the half that is missing. With both, `propose` does not refuse — it derives the box and says so in its first note (§5) | `assemble.plan`: take the face, or the head run's hair and neck |
|
|
1703
|
+
| `PROPOSE_CUT_FROM_RECORDED` | two parts share one layer and none of them records `cut_from`: a `parts.json` an `assemble` before 1.5.6 wrote with `assemble.cuts`. The records are in draw order, so which is the plan part and which the piece cannot be read off the file; the message names the parts and the layer. Every other reader still takes the file | `--parts`: re-run assemble (1.5.6+) to record `cut_from` |
|
|
1661
1704
|
| `PROPOSE_ACCESSORY_BODY` | an accessory has nothing above its pendant rows to hang its bone on | that part's plan entry, or author its bones by hand |
|
|
1662
1705
|
| `KEYPOINTS_FILE_PRESENT`, `KEYPOINTS_IS_JSON`, `KEYPOINTS_KEY_KNOWN`, `KEYPOINTS_FIELD_PRESENT`, `KEYPOINTS_FIELD_TYPE` | the `--keypoints` file is absent, not JSON, has a key this reader does not know, lacks one of `spec`, `space`, `width`, `height`, `source`, `people`, or holds a value of the wrong type (a size that is not a whole positive px count, an empty `source`, no people) | the keypoint file (§3, *A posed figure*) |
|
|
1663
1706
|
| `KEYPOINTS_SPEC_KNOWN`, `KEYPOINTS_SPACE_STATED` | the file is not `spine-parts-keypoints/1`, or states a space other than painting-px, origin top-left, y down | convert the file to that space; it is not read as if it were in it |
|
|
1664
1707
|
| `KEYPOINTS_JOINT_KNOWN`, `KEYPOINTS_JOINT_STATE`, `KEYPOINTS_POSITION_STATED`, `KEYPOINTS_POSITION_INSIDE` | a joint name outside body-18, a state outside observed / occluded / missing, an observed joint with no `at` or a missing one with one, or a position outside the `width`x`height` image | that joint |
|
|
1665
1708
|
| `KEYPOINTS_PERSON_UNIQUE`, `KEYPOINTS_PERSON_CHOSEN` | two people share an id; or the file holds more than one person and no `--person` names one, or `--person` names none of them (the message lists the ids) | `--person <id>` |
|
|
1666
1709
|
| `KEYPOINTS_IMAGE_SIZE` | the file's `width`x`height` is not the `--source` painting's | a file measured on this painting |
|
|
1667
|
-
| `PARTS_*` (`PARTS_FILE_PRESENT`, `PARTS_IS_JSON`, `PARTS_KEY_KNOWN`, `PARTS_FIELD_PRESENT`, `PARTS_FIELD_TYPE`, `PARTS_NAME_UNIQUE`, `PARTS_FROM_KNOWN`, `PARTS_BOX_INSIDE_RIG`, `PARTS_COUNTS_ADD_UP`, `PARTS_HOLES_LARGEST_FIRST`, `PARTS_HOLE_PART_KNOWN`) | the `parts.json` read is not assemble's contract (`PARTS_COUNTS_ADD_UP`: `visible_px + occluded_px` is not `opaque_px`, `visible_not_projected_px` is above `visible_px`, or the `recomposite` block's figures disagree — more uncovered than error pixels, a hole larger than its box, the wrong number of holes listed, listed areas that do not sum to `uncovered_error_px`; the last two codes are the block's order and its border names, §5) | re-run assemble; do not edit `parts.json` |
|
|
1710
|
+
| `PARTS_*` (`PARTS_FILE_PRESENT`, `PARTS_IS_JSON`, `PARTS_KEY_KNOWN`, `PARTS_FIELD_PRESENT`, `PARTS_FIELD_TYPE`, `PARTS_NAME_UNIQUE`, `PARTS_FROM_KNOWN`, `PARTS_CUT_FROM_KNOWN`, `PARTS_BOX_INSIDE_RIG`, `PARTS_COUNTS_ADD_UP`, `PARTS_HOLES_LARGEST_FIRST`, `PARTS_HOLE_PART_KNOWN`) | the `parts.json` read is not assemble's contract (`PARTS_COUNTS_ADD_UP`: `visible_px + occluded_px` is not `opaque_px`, `visible_not_projected_px` is above `visible_px`, or the `recomposite` block's figures disagree — more uncovered than error pixels, a hole larger than its box, the wrong number of holes listed, listed areas that do not sum to `uncovered_error_px`; the last two codes are the block's order and its border names, §5) | re-run assemble; do not edit `parts.json` |
|
|
1668
1711
|
|
|
1669
1712
|
### rig
|
|
1670
1713
|
|
|
@@ -1710,14 +1753,16 @@ See-through with another seed.
|
|
|
1710
1753
|
| `CHECK_PACK_SHAPE` | the pack line disagrees with the `--pack-shape` the build was run with — it ends `, shape rect` under `polygon` or `, shape polygon` under `rect`, or it has no shape clause (the 1.5–2.0 form, read as `rect`) under `polygon` | a rigc older than 2.1.0 on `PATH`: rigc 2.0.3 takes `--pack-shape` without a word, packs by rectangles and prints the form with no shape clause, so this refusal is what catches it — `bun install` (this package depends on rig-c ^2.20.4); otherwise a rigc problem, report it with `rigc --version` |
|
|
1711
1754
|
| `BUILD_ARTIFACT_PRESENT` | the packed build lacks its `.json`, `.atlas` or page | a rigc problem; report it |
|
|
1712
1755
|
| `REQUIREMENTS_FILE` | `--requirements` names no file, or one that does not parse as a JSON object. Refused before anything is built (by `build`, before assemble) | the path of a `spine-parts-requirements/1` file |
|
|
1713
|
-
| `REQUIREMENTS_FIELD` | a field of the file is missing, of the wrong type, or not one the format reads: `spec`, `fps` (no default), `requirements`, each requirement's `name` (letters, digits, underscores; once each), `kind`, `animation` and its kind's fields — every bar among them (`within_px`, `fraction`, `tolerance`, `least_drive` above 0, `within_degrees` from 0 to 180, `lo_degrees` ≤ `hi_degrees`, `within_ratio` ≥ 1) — and each target's `bone`, `animation` and exactly one of `at` and `keys` (times strictly increasing). `note`, `*_note` (strings) and `x-…` (any value) are read by nothing, as in the config | write the field; the bar is yours |
|
|
1714
|
-
| `REQUIREMENTS_RESOLVES` | a bone, an `ik` or `transform` constraint (by name and `constraint_type`, as rig-c resolves it), a slot, a mesh attachment or an animation the file names is not in `rig.json` or `motion.json`, or a seam's part is not in `parts.json` — the detail lists the ones that are; or an animation whose name cannot be a directory. Refused before anything is built | the name as the rig spells it |
|
|
1756
|
+
| `REQUIREMENTS_FIELD` | a field of the file is missing, of the wrong type, or not one the format reads: `spec`, `fps` (no default), `requirements`, each requirement's `name` (letters, digits, underscores; once each), `kind`, `animation` and its kind's fields — every bar among them (`within_px`, `fraction`, `tolerance`, `least_drive` above 0, `within_degrees` from 0 to 180, `lo_degrees` ≤ `hi_degrees`, `within_ratio` ≥ 1) — and each target's `bone`, `animation` and exactly one of `at` and `keys` (times strictly increasing), and `not_planted`, `not_swinging` and `rises` when given (each a non-empty list of part names, each once; no part both in `not_swinging` and in `rises`). `note`, `*_note` (strings) and `x-…` (any value) are read by nothing, as in the config | write the field; the bar is yours |
|
|
1757
|
+
| `REQUIREMENTS_RESOLVES` | a bone, an `ik` or `transform` constraint (by name and `constraint_type`, as rig-c resolves it), a slot, a mesh attachment or an animation the file names is not in `rig.json` or `motion.json`, or a seam's part or a `not_planted`, `not_swinging` or `rises` name is not in `parts.json` — the detail lists the ones that are; or an animation whose name cannot be a directory. Refused before anything is built | the name as the rig spells it |
|
|
1715
1758
|
| `REQUIREMENTS_CONSTRAINT_DRIVES` | a `follow` names a bone its constraint does not constrain, or a property it does not drive: an `ik` drives `rotate` only; a `transform` drives `rotate` when a `to` names `rotate`, `translate` when one names `x` or `y` | the constraint's own bone and property |
|
|
1716
1759
|
| `REQUIREMENTS_TARGET_PARENT` | a scene target's bone is the root or is not parented to it, or the root is not at rest at the origin, or the animation keys the root — a stage point is a bone's position only under a root that stands still at the origin | a bone under the root for the scene to place |
|
|
1717
1760
|
| `REQUIREMENTS_TARGET` | a scene target places one bone twice for one animation, places it for an animation no requirement measures, has a key outside the animation's `[0, duration]`, or sits in a group the animation translates | one placement per bone and animation, inside it, for an animation a requirement reads |
|
|
1718
1761
|
| `CHECK_REQUIREMENTS_FRAMES` | a requirement render did not write every frame rigc sampled, or the as-declared, released and full renders of a follow sampled different frames | a rigc problem; report it |
|
|
1719
1762
|
| `CHECK_REQUIREMENT_MET` | a declared requirement measured outside its bar; the figure, its frame and the bar are quoted | the rig or its motion (§7, *Declared requirements*) — or the bar, if the scene asks less |
|
|
1720
1763
|
| `CHECK_REQUIREMENT_MEASURABLE` | a declared requirement is NOT MEASURABLE: what it asks is undefined in the frames, and the reason is quoted. Not a pass | the declaration (a bone with a length, a target off the bone's origin, a least drive the constraint reaches) or the rig |
|
|
1764
|
+
| `REQUIREMENTS_NOT_PLANTED` | a `not_planted` name is a part `check` does not read as a planted foot — it does not come from a See-through `footwear` layer — or there is no `parts.json` to choose the feet by (issue #231). Refused before anything is built | a `footwear` part's name |
|
|
1765
|
+
| `REQUIREMENTS_TIP_EXCEPTION` | a `not_swinging` or `rises` name is a part `TIP_OVER_ROOT` does not read — not from a See-through `handwear` or `bottomwear` layer — or there is no `parts.json` (refused before anything is built); or a `rises` part is not carried by a chain that rises: the chain's first link to its last, at the setup pose, runs other than up, the detail giving the direction measured (refused after the idle is rendered, before `check.json` is written) (issue #231) | a swinging part's name; for `rises`, a part whose chain does rise |
|
|
1721
1766
|
| `REQUIREMENTS_SEAM` | a `seam` cannot be read against the rig: there is no `parts.json` (a merged rig, §7 *Measuring a rig rig-parts did not assemble*), or the two parts' art does not meet at the setup pose (no 4-adjacent pair). Refused before anything is built | two parts whose art touches, on a rig with its `parts.json` |
|
|
1722
1767
|
|
|
1723
1768
|
### compose
|
|
@@ -2275,6 +2320,34 @@ unit (the stage box maps to the world by a translation and the y flip,
|
|
|
2275
2320
|
}
|
|
2276
2321
|
```
|
|
2277
2322
|
|
|
2323
|
+
**Feet that are not planted** (issue #231). The feet halves of `BREATH_VISIBLE` and
|
|
2324
|
+
`STILL_REGIONS_DARK` assume a figure that stands: every `footwear` part holds still.
|
|
2325
|
+
Whether the figure stands is the scene's knowledge, so a scene where it sits and its
|
|
2326
|
+
legs swing says so here, by part: `"not_planted": ["shoes"]`. Each name must be a
|
|
2327
|
+
`footwear` part in `parts.json` (`REQUIREMENTS_RESOLVES`, `REQUIREMENTS_NOT_PLANTED`).
|
|
2328
|
+
A declared part is left out of both feet halves, and the declaration is the first
|
|
2329
|
+
figure of each line it relaxes — `declared_not_planted` in `BREATH_VISIBLE`, and in
|
|
2330
|
+
`STILL_REGIONS_DARK`'s `feet` — in `check.json` and on the console, so a relaxed bar
|
|
2331
|
+
never reads as a passed one. With every foot declared, the torso is still measured
|
|
2332
|
+
(`feet_parts` `[]`, `feet_heat_max` null) and the feet half reads `unmeasured` with
|
|
2333
|
+
the reason. The field absent, nothing changes: every `footwear` part is a planted foot.
|
|
2334
|
+
|
|
2335
|
+
**Swinging parts that do not hang** (issue #231). `TIP_OVER_ROOT` reads every
|
|
2336
|
+
`handwear` and `bottomwear` part as hanging: the lower half of its box is the tip's.
|
|
2337
|
+
Two declarations, by part, say where that does not hold. `"not_swinging": ["under"]`
|
|
2338
|
+
— a piece cut off and left still — leaves the part out of the bar; its row reads
|
|
2339
|
+
`unmeasured` with the reason. `"rises": ["arm"]` — a forearm raised from the elbow —
|
|
2340
|
+
reads the part's **upper** half as the tip's (its row carries `tip_half: "upper"`),
|
|
2341
|
+
and only after checking the chain that carries it does rise: the part's bound bones
|
|
2342
|
+
walked up to a chain `CHAIN_LAG` reads, its first link's origin to its last at the
|
|
2343
|
+
setup pose, read along its longer axis, must run up, or the declaration is refused
|
|
2344
|
+
naming the direction measured. The line's first figures after `status` are
|
|
2345
|
+
`declared_not_swinging` and `declared_rises`. Each name must be a swinging part in
|
|
2346
|
+
`parts.json`, and no part may be in both lists.
|
|
2347
|
+
|
|
2348
|
+
`fps` and `requirements` are required as always; a file that declares only exceptions
|
|
2349
|
+
is `{ "spec": "spine-parts-requirements/1", "fps": 12, "requirements": [], "not_planted": ["shoes"], "not_swinging": ["under"], "rises": ["arm"] }`.
|
|
2350
|
+
|
|
2278
2351
|
**Four outcomes, never merged** (#87):
|
|
2279
2352
|
|
|
2280
2353
|
| outcome | when | what it does to the run |
|
package/docs/STABILITY.md
CHANGED
|
@@ -155,7 +155,7 @@ every number) are AUTHORING §3's tables, and the loader refuses any other by na
|
|
|
155
155
|
|
|
156
156
|
| file | written by | what is stable |
|
|
157
157
|
| --- | --- | --- |
|
|
158
|
-
| `parts.json`, `parts/<name>.png` | `assemble`, `build` | one record per part: `from`, the box `x`, `y`, `w`, `h` (rig px), the pixel counts `src/parts.ts` documents (`opaque_px`, `visible_px`, `occluded_px`, `projected_core_px`, `source_px_taken`, `visible_not_projected_px`, `refused_drift_px`, `merged_px`, `seam_override_px`, and `fringe_pushed_back` when not empty); `rig_size`, `scale_rig_per_source`, `ghost_px`, `recomposite` |
|
|
158
|
+
| `parts.json`, `parts/<name>.png` | `assemble`, `build` | one record per part: `from`, `cut_from` on a cut's piece only (the plan part it was cut from, issue #230), the box `x`, `y`, `w`, `h` (rig px), the pixel counts `src/parts.ts` documents (`opaque_px`, `visible_px`, `occluded_px`, `projected_core_px`, `source_px_taken`, `visible_not_projected_px`, `refused_drift_px`, `merged_px`, `seam_override_px`, and `fringe_pushed_back` when not empty); `rig_size`, `scale_rig_per_source`, `ghost_px`, `recomposite` |
|
|
159
159
|
| `rig/rig.json`, `rig/motion.json`, `rig/images/` | `rig`, `build`, `compose` | rig-c's rig spec and motion spec, `spec` `rigc-rig/1` and `rigc-motion/1`: the format is rig-c's, and this package writes only what rig-c's parser accepts |
|
|
160
160
|
| `rig/mesh_report.json` | `rig`, `build` | one row per mesh: `part`, `vertices`, `triangles`, `hull`, `bones`, `max_influences`, `mean_influences`, `art_coverage`, then `grid` (a lattice); `mode` "contour" with `params`, `contour`, `regions`; or `mode` "auto" with `settings`, `source`, `result`, `residuals`, `worst_residual`, `worst_region`, `motion_amplitude`, `termination`, `weights`, `regions`, `deformation`, `quality_report` (rig-c's `mesh-quality-report/1` document whole), `motion_report`, and, only when they apply, `stray_cleared` (after `source`, when `source.stray` is declared), `skinning_residual` and `replay` (`AutoMeshReport` in `src/rig.ts`); in every mode, `exponent` when the mesh declares one (after `grid`, or after `mode`); `ribs` when a contour or auto mesh declares them (after `exponent`, or after `mode`), and then `contour.ribs` (`source.contour.ribs` in the automatic mode) |
|
|
161
161
|
| `check/check.json` and the rest of `check/` | `check`, `build`, `compose` | the reference's fields in its order (`gate_spine_html_green`, `rigc_entry`, `pack_mode`, `loop_max_diff`, the seam figures — `seam_mean`, `seam_px_over_40`, `seam_px_over_80` and, from issue #216, `seam_frame_scale`, the frame scale the bar divides the count by), `loop_physics` after `loop_max_diff` when the rig declares a physics constraint, `seam_pairs` after the seam figures when the seam bar fails or `--seam-pairs` asks (each row's picture under `check/seam/`; `--keep-stills` leaves the setup-pose still and its `frames.json` under `check/setup_still/`), then the judgement lines (`BREATH_VISIBLE`, `BLINK_NO_HOLE`, `CHAIN_LAG`, `TIP_OVER_ROOT`, `STILL_REGIONS_DARK`, `TEXTURE_STRETCH`), `RECOMPOSITE_HOLES`, `SETUP_POSE_VS_SOURCE`, `skipped`, a `requirements` block under `--requirements`, and `PASS`; `check/build/skeleton.json`, `.atlas` and `.png` are the packed artifact rig-c writes |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-parts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "AI-authored 2D character parts from one anime painting and its See-through layer decomposition: measured parts, weighted-mesh rig specs, verified through rig-c before anything is written. A CLI for agents that cannot see the image.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|