rig-parts 1.2.0 → 1.3.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 +23 -2
- package/cli.ts +10 -4
- package/docs/AUTHORING.md +197 -8
- package/docs/STABILITY.md +6 -3
- package/package.json +1 -1
- package/skills/rig-parts/SKILL.md +22 -4
- package/src/assemble.ts +291 -36
- package/src/automesh.ts +23 -4
- package/src/build.ts +3 -1
- package/src/config.ts +145 -16
- package/src/contour.ts +400 -13
- package/src/propose.ts +24 -1
- package/src/rig.ts +46 -6
- package/src/scene.ts +32 -8
package/README.md
CHANGED
|
@@ -306,8 +306,10 @@ These are limits of the approach, stated so nobody reads more into a green run:
|
|
|
306
306
|
layer that is in front of another in one place and behind it in another is drawn
|
|
307
307
|
right in the still (the painting's own pixels are projected onto it) and wrong once
|
|
308
308
|
it moves.
|
|
309
|
-
- **No expression or lip-sync.** The mouth is one layer; there is no
|
|
310
|
-
and no expression axis
|
|
309
|
+
- **No expression or lip-sync, and no jiggle.** The mouth is one layer; there is no
|
|
310
|
+
mouth-shape set and no expression axis, and nothing here proposes, keys or judges a
|
|
311
|
+
soft region. An expression that moves the face's parts, and a jiggle, can be built on
|
|
312
|
+
the rig it writes ([below](#what-can-be-built-on-top)); lip-sync cannot.
|
|
311
313
|
- **Occluded pixels are See-through's synthesis**, not the artist's. How much of a rig
|
|
312
314
|
that is, is below.
|
|
313
315
|
- **No success rate is claimed.** Ten characters have been measured stage by stage
|
|
@@ -331,6 +333,25 @@ These are limits of the approach, stated so nobody reads more into a green run:
|
|
|
331
333
|
measures is a hanging strand on a headwear or earwear layer: each gets a pendulum
|
|
332
334
|
chain, and a strand it cannot chain is named in a note rather than left stiff.
|
|
333
335
|
|
|
336
|
+
### What can be built on top
|
|
337
|
+
|
|
338
|
+
This package makes no expression and no jiggle, but the rig it writes is the material for
|
|
339
|
+
both, and the config has a field for each. [docs/AUTHORING.md](docs/AUTHORING.md) has a recipe
|
|
340
|
+
for each — a config fragment, the `check` lines it passes with, and its limit:
|
|
341
|
+
|
|
342
|
+
- [**Expressions and other named animations**](docs/AUTHORING.md#recipe-expressions-and-other-named-animations):
|
|
343
|
+
the face's parts are bones; animations written over them in rig-c's motion-spec shape and
|
|
344
|
+
named by `motion.animations_from` are built and gated beside the idle on every build. One
|
|
345
|
+
attachment per slot, so a part moves, turns and scales but is never swapped for another
|
|
346
|
+
image; `check` measures the idle alone.
|
|
347
|
+
- [**A soft region driven by physics**](docs/AUTHORING.md#recipe-a-soft-region-driven-by-physics)
|
|
348
|
+
(a bust, the hips): a bone the config authors, a contour-mode region that hands it the patch,
|
|
349
|
+
and a `physics` constraint on it; `examples/sample` carries one. Under the sample's slow
|
|
350
|
+
4 s idle the bust moves at most 0.066 rig px and the hips not at all: the spring answers
|
|
351
|
+
faster keys on the body. The sample's `hop`, a named animation keying only `hip`, moves the
|
|
352
|
+
bust up to 6.583 and the hips up to 4.432 rig px; the region's `band` caps how far a patch may
|
|
353
|
+
go before the texture-stretch ceiling, and `check` measures the idle alone.
|
|
354
|
+
|
|
334
355
|
### How much of a rig the model painted
|
|
335
356
|
|
|
336
357
|
`parts.json` splits each part's opaque pixels (`opaque_px`) into `visible_px` — no
|
package/cli.ts
CHANGED
|
@@ -326,14 +326,18 @@ usage:
|
|
|
326
326
|
<out>/render/recomposite_rig.png and <out>/render/${ERROR_MAP_FILE} (the error
|
|
327
327
|
map: uncovered error px red, covered error px blue, the rest the painting in
|
|
328
328
|
light grey). Reads config.seethrough.head_box and
|
|
329
|
-
.resolution and config.assemble.rig_scale, .plan, .extend_below_crop
|
|
329
|
+
.resolution and config.assemble.rig_scale, .plan, .extend_below_crop,
|
|
330
330
|
.patches — extra parts cut from the painting itself over a rig-pixel box
|
|
331
331
|
(alpha "silhouette": the painting's figure inside it; "box": all of it),
|
|
332
332
|
drawn "back", "front" or {"before": <plan part>}, recorded in parts.json
|
|
333
|
-
as from "painting:<name>" and counted 100 % source — and
|
|
333
|
+
as from "painting:<name>" and counted 100 % source — and .cuts — a plan
|
|
334
|
+
part's pixels inside a rig-pixel polygon taken into a new part, drawn the
|
|
335
|
+
same way, recorded with the plan part's from — and no rig section:
|
|
334
336
|
bones, meshes, regions and motion need not exist yet, because propose
|
|
335
337
|
drafts them from these parts (AUTHORING §4, the table).
|
|
336
|
-
Prints one line per part,
|
|
338
|
+
Prints one line per part, one \`cut:\` line per part a cut split (its
|
|
339
|
+
opaque pixels = what it kept + what each cut took), the seam override
|
|
340
|
+
counts, the \`pixels:\` totals
|
|
337
341
|
(opaque = visible + occluded; taken from the painting; visible but not
|
|
338
342
|
projected), and \`recomposite vs source\` (mean |d| and % within 8 over the
|
|
339
343
|
mean channel; error px: max channel > 40; uncovered: of those, where no
|
|
@@ -435,7 +439,9 @@ usage:
|
|
|
435
439
|
shared, so a character whose idle keys it, or whose constraint names it,
|
|
436
440
|
is refused. The plate is a region on bone "${PLATE}" under
|
|
437
441
|
root, drawn first, its image at canvas (0, 0). One idle holds every
|
|
438
|
-
character's tracks
|
|
442
|
+
character's tracks; an animation beside a character's idle (its
|
|
443
|
+
config's motion.animations_from) rides along as "<id>:<name>",
|
|
444
|
+
prefixed as the idle is and scheduled by nothing. Refused by name, every problem at once: an id
|
|
439
445
|
repeated, empty or holding ":" (or "/", "\\", a leading "."); a build
|
|
440
446
|
missing a file or whose own check is not green; characters built at
|
|
441
447
|
different rig_scale; a character whose parts' boxes, placed, leave the
|
package/docs/AUTHORING.md
CHANGED
|
@@ -82,9 +82,11 @@ read by no CPU stage).
|
|
|
82
82
|
| `assemble.plan` | assemble; also the list every `meshes`/`regions` key must come from | **proposed** by `assemble --propose-plan`. `[part name, "full" \| "head", See-through tag]`, in draw order back to front. Names are free; roles come from the tag |
|
|
83
83
|
| `assemble.extend_below_crop` | assemble | **proposed** with the plan: `{part, run, tag}` — a head-run part that reaches the bottom of the head crop is continued from a full-run layer, whole connected components |
|
|
84
84
|
| `assemble.patches` | assemble; `meshes`/`regions` keys may name them too | **authored**, optional: `[{name, box, alpha, draw}]` — an extra part cut from the **painting** itself, for figure no See-through layer holds (a hem both runs dropped). `box` is `[x0, y0, x1, y1]` in **rig** pixels, `x1`/`y1` exclusive — the space of `parts.json` and `recomposite_rig.png`, where the hole is found; `alpha` is `"silhouette"` (the painting's figure silhouette inside the box, the one `--seam silhouette` uses) or `"box"` (the whole box); `draw` is `"back"`, `"front"` or `{"before": "<plan part>"}`. A patch is always a region: its bone is `regions.<name>`, in the one place every region's bone lives, and a `meshes` entry for it is refused. See §5 for how to place one |
|
|
85
|
+
| `assemble.cuts` | assemble; `meshes`/`regions` keys may name the new parts too, and must (each is a part like any other) | **authored**, optional (issue #170): `[{from, into, polygon, draw, overlap}]` — a plan part's layer cut by a polygon the author draws: the pixels of plan part `from` whose **centre** lies inside `polygon` (even-odd; a centre exactly on an edge is outside) leave `from` and make the new part `into`, colour and alpha, so every pixel of the layer ends in exactly one of the two. `polygon` is `[[x, y], …]`, at least three points, non-negative integer **rig** pixels — the space of `parts.json` and of the patches' boxes. The file that shows the layer in those coordinates is `assemble`'s own `rig/parts/<from>.png`, placed at its `parts.json` `x`, `y` (the PNG's pixel `(i, j)` is rig pixel `(x + i, y + j)`), or `render/recomposite_rig.png`, the whole rig 1:1; the contact sheets (`sheet`) are scaled and carry no coordinates. `draw` is `"back"`, `"front"` or `{"before": "<plan part>"}`, as for a patch (to keep the setup pose, put it beside `from`: `{"before": "<from>"}`, or before the plan part after it). `from` must be a plan part (`CONFIG_NAME_RESOLVES`; a patch or another cut's piece is refused), `into` a new name (`CONFIG_PART_UNIQUE`), the polygon three or more integer points (`CONFIG_FIELD_TYPE`). The piece's `parts.json` record carries `from`'s `<run>:<tag>` and its own counts, each split from the layer's by where the pixel lies; it is a mesh or a region under its own name, and the coverage rule holds it like a plan part. What the config cannot know the stage refuses: a polygon point past the rig (`ASSEMBLE_CUT_INSIDE`), two cuts of one part sharing a pixel (`ASSEMBLE_CUT_OVERLAP`), a cut that takes none of the layer's opaque pixels or leaves `from` none (`ASSEMBLE_CUT_PIXELS`). `overlap` is required, a non-negative integer of rig pixels with no default (`CONFIG_FIELD_TYPE`): the **band** — the polygon's pixels within `overlap` px of its edge by Chebyshev distance (the polygon's mask less its erosion by a `2 * overlap + 1` square) — stays in `from` as well as going to the piece, so the piece's resampled edge has art under it; the counts stay a strict partition and the band is the base's extra copy, printed on the `cut:` line. At `0` the two abut and `check`'s seam bar judges the edge between them (`CHECK_SEAM_WITHIN_BAR`, §5). A band only covers the edge at rest: a piece that swings well clear of where it was painted shows the hole it leaves, and a thin piece's band shows a ghost of the piece — an authored fill for what lies under a piece is issue #193 |
|
|
85
86
|
| `bones` | rig; `propose --from-config` | **proposed** by `propose`, then corrected. A single bone `{name, parent, at, tip?}` or a chain `{chain, parent, points, tip}` whose links are named `<chain>0 … <chain>n`. Parents come before children. In `rig.json` each link is **turned along its chain** — `rotation` is the direction from its origin to the next link's (the last link's to `tip`) and `length` that distance (issue #73), so a physics constraint added downstream finds a lever; under `--idle-keys ctl` the link's `<link>_ctl` carries the turn and the length and the link sits at local rotation 0 beneath it. A single bone is never turned; one whose parent is a link is turned back upright |
|
|
86
87
|
| `meshes.<part>` | rig | **proposed**, then corrected. Exactly one of `grid` (the lattice: cell size, px — what `propose` writes), `contour` (the outline mode, authored; the next row and "A contour mesh" below) or `auto` (the automatic mode, authored; "An automatic mesh" below) — two, or none, is `CONFIG_MESH_MODE`. In every mode: `r` (added to every distance before weighting: `w = 1/(d + r)²`), `segments` (a chain name, a bone name with a `tip`, or `[bone, [x0,y0], [x1,y1]]`), and, **authored**, optional, `exponent` (issue #161): a number above 0 that replaces the 2 in that rule, `w = 1/(d + r)^exponent` — a larger one hands each vertex more fully to its nearest bone; absent is 2 and changes no byte, a value that is not a number above 0 is `CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`, and a declared one is echoed as `exponent` in the mesh's `mesh_report.json` row; the exponent is the distance rule's, so declared beside `rule: "heat"` it is refused (`CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`). The segment list is the one authored decision about a layer: which bones may pull it. The slot's bone is the first segment's. Optional `rule` (issue #161): `"distance"` — the rule above, and what an absent `rule` means (every byte is the same) — or `"heat"`, bone heat over the part's own silhouette, in every mode: the lattice's art (alpha above 8) with holes filled, or for `contour` and `auto` the silhouette their outline is traced from (that mode's art — alpha above 8 for `contour`, alpha 1 and above for `auto` — less any island a declared `stray` left out, holes filled, grown by its `margin`); each bound bone's sources are the silhouette pixels whose centre lies within 0.75 px of its segment (a pixel two bones reach goes to the nearer, a tie to the first listed), held at 1 for that bone and 0 for the others; the field is solved by row-major Gauss–Seidel over-relaxation to a sweep change under 1e-7 within 20000 sweeps; a vertex reads the nearest silhouette pixel and takes the same cap and floor (and `regions`) as the distance rule; `r` is not read. The mesh's `mesh_report.json` row gains `heat` (the constants, each bone's sources and sweeps, the residual). Any other value is `CONFIG_FIELD_TYPE`; a bound bone with no source pixel is `RIG_HEAT_BONE_SOURCE`, an island no bone has a source in `RIG_HEAT_ISLAND_SOURCE`, a solve past its sweep bound `RIG_HEAT_CONVERGED` — none is weighted 0 |
|
|
87
88
|
| `meshes.<part>.contour` | rig | **authored**, optional (issue #84): `{tolerance, margin, spacing, budget?, stray?, regions?}`. `tolerance` — rig-c's Douglas–Peucker tolerance on the traced outline, px, 0 or more; `margin` — how far the silhouette grows before it is traced, px: 0, or 1 or more (a pixel joins when its centre lies within `margin` of an art pixel's centre; a value above 0 and below 1 adds nothing and is refused, `CONFIG_FIELD_TYPE`); `spacing` — the background interior spacing, px, above 0; `budget` — the most vertices the mesh may have (refused above it, nothing thinned); `stray` — the largest island, in art pixels, that may be left out of the mesh (absent: none is); `regions` — local deformation regions, each `{name, shape, bone, spacing, band}` with `shape` `"circle"` (`cx, cy, r`) or `"polygon"` (`points`, 3 or more): `bone` is the region's control bone (any bone `bones` declares), `spacing` the interior spacing inside the region and its band, `band` the width the bone's weight falls across. Every length and position is **rig px**, like every other in the config; the part image is the rig less `(x − 4, y − 4)` (its box less the pad), a translation, so a length is the same number in both. A region's `cx`, `cy`, `r`, `band` and `points` are multiples of 1/256 px (`CONFIG_FIELD_TYPE` otherwise); region names are unique per mesh (`CONFIG_REGION_NAME_UNIQUE`). The alpha threshold is not a field: art is alpha above 8, the lattice's and `check`'s |
|
|
89
|
+
| `meshes.<part>.ribs` | rig | **authored**, optional (issue #188), in the `contour` and `auto` modes: `{<chain>: {stations}, …}`, each key a chain this mesh's `segments` name. Each link of the chain gets a row of vertices across the part at its origin and at `stations` points evenly between its origin and its end (`stations`, a whole number, 0 or more, required; a chain of L links carries L × (stations + 1) ribs) — so a strand can fold where its chain bends; the rule is "Ribs along a chain" under "A contour mesh". Absent places none and changes no byte. Refused: a lattice mesh with ribs (`CONFIG_RIBS_MODE` — its vertices are its grid's corners), a key that is not one of the mesh's chains (`CONFIG_NAME_RESOLVES`), an object naming no chain or a `stations` that is not a whole number from 0 (`CONFIG_FIELD_TYPE`); at the rig stage `CONTOUR_RIB_ART`, `CONTOUR_RIB_LINK`, `CONTOUR_RIB_CROSSING`, `CONTOUR_RIB_EDGE` (the contour refusals). The mesh's `mesh_report.json` row gains `ribs` — the declaration, the ribs placed and the vertices they hold — and its contour report (the source's, in `auto`) lists each rib's vertices |
|
|
88
90
|
| `regions.<part>` | rig | **proposed**: the bone a rigid part rides. Every plan part is exactly one of a mesh or a region (`CONFIG_PART_ATTACHED`) |
|
|
89
91
|
| `motion.duration` | rig; check (the loop is measured at this time) | proposed as 4 s; a whole number of 1/12 s ticks, because `check` renders at 12 fps |
|
|
90
92
|
| `motion.tracks` | rig | **proposed**, then tuned. Single `{bone, prop, amp, period, phase, base?}` or chain `{chain, amps, period, phase, lag}` — one amplitude per link, link `i` at phase `phase + lag·i`. Every `period` must divide `duration` (`CONFIG_PERIOD_DIVIDES_DURATION`). A bone property is keyed by one track (`CONFIG_BONE_PROPERTY_KEYED_ONCE`): a chain keys `rotate` on every link, the blink's `eyes` group `scaley` and its `brows` group `translatey` on every member, so a single track on any of those is a second track on it — rigc would refuse it one stage later, `animation "idle" has two tracks on eye.scaley`. A chain link is turned along its chain (the `bones` row), so a `translatex`/`translatey` key on a bone whose parent is a turned link, or a `scale`/`shear` key on a turned link, would move or stretch along the link and not along the picture's axis: the rig stage refuses it (`RIG_KEY_FRAME_UNTURNED`); `rotate` is keyed on any bone |
|
|
@@ -364,6 +366,42 @@ contour refusal in a run is listed together; nothing falls back to the lattice:
|
|
|
364
366
|
| `CONTOUR_PART_HAS_ART` | no pixel above alpha 8 | re-run assemble |
|
|
365
367
|
| `CONTOUR_PARAMETER` | a parameter `src/contour.ts` cannot run at: a margin above 0 and below 1 px (it would add no pixel), a spacing whose keep radius (half of it) snaps to 0, a polygon whose band folds it, a part over 32768 px | the named field |
|
|
366
368
|
| `RIG_CONTOUR_REGIONS_OVERLAP` | a vertex two regions both reach (`g > 0`); the detail names the vertex and both regions | move the regions apart or narrow a band |
|
|
369
|
+
| `CONTOUR_RIB_ART` | a rib's point (a link origin or a station) is not on the part's art (its pixel at or below the threshold) or not inside the outline, or a point between its ends falls within 1/256 px of the outline — a rib that leaves the art | move the chain onto the part, or leave the chain out of `ribs` |
|
|
370
|
+
| `CONTOUR_RIB_LINK` | a chain link shorter than the outline's `tolerance` (or of no length), or a joint where the chain turns straight back on itself; the detail names the chain, the link and the tolerance field | fewer, longer links, or a lower `tolerance` |
|
|
371
|
+
| `CONTOUR_RIB_CROSSING` | two ribs cross, touch or share an end (both named, with their ends) | fewer `stations`, or a chain that bends less sharply inside a wide part |
|
|
372
|
+
| `CONTOUR_RIB_EDGE` | a rib's consecutive vertices could not be joined by an edge (no edge across them could be flipped) — a guard: the rule keeps every other point off a rib | report it |
|
|
373
|
+
|
|
374
|
+
**Ribs along a chain** (`meshes.<part>.ribs`, issue #188). A long thin part that hangs from a
|
|
375
|
+
chain — a tie, a strand of hair, a scarf end — bends at the chain's joints, and a mesh with no
|
|
376
|
+
row of vertices across the strand there cannot fold at the joint: the bend is taken up by
|
|
377
|
+
whatever triangles happen to span it. For each chain `ribs` names, `src/contour.ts`
|
|
378
|
+
(`placeRibs`) places, in part-image px:
|
|
379
|
+
|
|
380
|
+
- **where**: on each link k (from `points[k]` to `points[k + 1]`, the last to the tip) a rib at
|
|
381
|
+
its origin and at `points[k] + m/(stations + 1) · (end − origin)`, m = 1..`stations`; the tip
|
|
382
|
+
carries none;
|
|
383
|
+
- **across**: along n = (−d_y, d_x)/|d|, d the link's direction — at a joint between two links
|
|
384
|
+
the sum of their unit directions, the bend's bisector;
|
|
385
|
+
- **clipped to the art**: the point must be on the part's art (its pixel above the mode's
|
|
386
|
+
threshold) and inside the outline; from it the line runs both ways to the first point where it
|
|
387
|
+
meets the outline the mode traced (art, holes filled, grown by `margin`, simplified at
|
|
388
|
+
`tolerance`), and those two points, on the 1/256 px grid, are the rib's ends. Each end joins
|
|
389
|
+
the hull on the edge it lands on (or is that edge's vertex), moved off the edge by at most
|
|
390
|
+
1/512 px; coverage, overshoot and self-intersection then read the outline with them;
|
|
391
|
+
- **between**: the point itself (so the joint is a vertex), and each half from an end to the
|
|
392
|
+
point cut into ⌈half / spacing⌉ equal parts at the mode's background `spacing`
|
|
393
|
+
(`auto.source.spacing` in the automatic mode);
|
|
394
|
+
- **kept a row**: no other interior point is kept within half a spacing of a rib, every
|
|
395
|
+
consecutive pair of a rib is made an edge of the triangulation (edges across it are flipped
|
|
396
|
+
until it is one) and locked, as an outline edge is — so `nonDelaunayEdges` does not count it.
|
|
397
|
+
|
|
398
|
+
Nothing reads a weight: every vertex is weighted as any other at its point. The rib on a strand
|
|
399
|
+
painted inside a wider layer runs across the whole opaque run there, sleeve and body included —
|
|
400
|
+
cutting the strand out is #170's. In the automatic mode every rib vertex is added to
|
|
401
|
+
`protect.vertices` (after the author's, as written) and each rib is sent to rig-c's
|
|
402
|
+
`reduceMesh` as a named line (`lines`, rig-c 2.33.0; `maxDeviation` 0), so the reduction keeps
|
|
403
|
+
every rib vertex and its post-pass flips no rib edge; the result's measurement carries one
|
|
404
|
+
`MQ_LINE_DEVIATION` row per rib.
|
|
367
405
|
|
|
368
406
|
**The window between coverage and overshoot**, measured in #106 (`tools/contour_survey.ts`, five
|
|
369
407
|
generated shapes, margins 0, 1, 1.5, 2, 2.5, 3): the smallest margin that covered every art
|
|
@@ -844,6 +882,123 @@ boundary stays held, and an edge from the region to a vertex further out than it
|
|
|
844
882
|
that crosses the region with no point of it inside to split at — is still stopped by name
|
|
845
883
|
(`AUTO_MESH_ACCEPTED`, the detail naming P16).
|
|
846
884
|
|
|
885
|
+
### Recipe: expressions and other named animations
|
|
886
|
+
|
|
887
|
+
The package proposes no expression and keys nothing but the idle; what it gives is a rig whose
|
|
888
|
+
face is bones (`eye_r`, `eye_l`, `brow_r`, `brow_l`, `mouth` on `sample`) and one door for
|
|
889
|
+
animations you write over them, `motion.animations_from` (the row above, issue #183). Write a
|
|
890
|
+
file in rig-c's motion-spec shape beside the config and name it:
|
|
891
|
+
|
|
892
|
+
```jsonc
|
|
893
|
+
// config.json
|
|
894
|
+
"motion": { "duration": 4.0, "tracks": [ … ], "blink": { … },
|
|
895
|
+
"animations_from": "expressions.json" }
|
|
896
|
+
|
|
897
|
+
// expressions.json — the bones the config declares, keyed as rig-c keys them
|
|
898
|
+
{ "spec": "rigc-motion/1",
|
|
899
|
+
"animations": {
|
|
900
|
+
"surprised": { "duration": 0.5, "tracks": [
|
|
901
|
+
{ "bone": "brow_r", "property": "translatey", "keys": [ { "t": 0, "v": [0] }, { "t": 0.25, "v": [2] }, { "t": 0.5, "v": [0] } ] },
|
|
902
|
+
{ "bone": "brow_l", "property": "translatey", "keys": [ { "t": 0, "v": [0] }, { "t": 0.25, "v": [2] }, { "t": 0.5, "v": [0] } ] },
|
|
903
|
+
{ "bone": "mouth", "property": "scaley", "keys": [ { "t": 0, "v": [1] }, { "t": 0.25, "v": [1.4] }, { "t": 0.5, "v": [1] } ] } ] } } }
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
`build` writes `surprised` into `rig/motion.json` after `idle`, gates it with the rig, and
|
|
907
|
+
`check` names it on a line of its own (`examples/sample` carries one, `animations.json` with `hop`, the
|
|
908
|
+
recipe below). With exactly this file beside `examples/sample`'s config in its place,
|
|
909
|
+
`build` printed (this tree, rig-c as `package.json` pins it):
|
|
910
|
+
|
|
911
|
+
```
|
|
912
|
+
[rig] animations beside the idle, from …/expressions.json as written: surprised
|
|
913
|
+
[check] beside the idle: 1 animation(s), surprised — built and gated by rigc with the rig, not measured: every bar here reads the idle
|
|
914
|
+
[check] check: PASS; 9 of 9 bar(s) measured, 0 skipped
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
A bone the rig lacks is rigc's refusal at the rig stage, in rigc's words (`RIG_RIGC_GREEN`); a
|
|
918
|
+
fault in the file itself is the loader's (the row above names each one).
|
|
919
|
+
|
|
920
|
+
**Limits.** Every slot holds one attachment, the part's own image, so an expression moves,
|
|
921
|
+
turns and scales the face's parts and never swaps one for another: there is no second mouth
|
|
922
|
+
shape to key, and no lip-sync. `check` measures the idle alone: an animation beside it has
|
|
923
|
+
passed rigc's gate and nothing else — no seam, stretch or hole bar has read it.
|
|
924
|
+
|
|
925
|
+
### Recipe: a soft region driven by physics
|
|
926
|
+
|
|
927
|
+
A jiggle (a bust, the hips) is a patch of one part that lags behind the body. The package
|
|
928
|
+
proposes none and judges none; what it gives is the three pieces to build one, all in the
|
|
929
|
+
config. `examples/sample` carries a worked one (issue #183, PR #187), and its config is the
|
|
930
|
+
fragment below, cut to the bust:
|
|
931
|
+
|
|
932
|
+
```jsonc
|
|
933
|
+
"bones": [ …,
|
|
934
|
+
{ "name": "chest_jiggle", "parent": "chest", "at": [419, 320] }, … ],
|
|
935
|
+
"meshes": { "topwear": {
|
|
936
|
+
"contour": { "tolerance": 1, "margin": 1, "spacing": 18, "stray": 4,
|
|
937
|
+
"regions": [ { "name": "bust", "shape": "circle", "cx": 419, "cy": 320, "r": 28,
|
|
938
|
+
"bone": "chest_jiggle", "spacing": 6, "band": 16 } ] },
|
|
939
|
+
"r": 8, "segments": [ ["chest", [419, 223], [419, 437]], ["hip", [419, 437], [419, 557]] ] } },
|
|
940
|
+
"constraints": [
|
|
941
|
+
{ "type": "physics", "name": "bust_phys", "bone": "chest_jiggle",
|
|
942
|
+
"x": 0.5, "y": 1, "inertia": 0.25, "strength": 120, "damping": 0.92, "mass": 1, "mix": 1 } ]
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
- **A bone the config authors**, under the bone the patch rides on. `propose` writes no such
|
|
946
|
+
bone and no constraint; it is yours, and nothing keys it — the constraint moves it.
|
|
947
|
+
- **A region in a `contour` (or `auto`) mesh** that hands the patch to that bone: weight 1
|
|
948
|
+
inside the shape, falling to 0 across `band` (*A contour mesh*, above). A `grid` mesh carries
|
|
949
|
+
no region, so the part moves from the lattice to the outline mode first; `sample` traced
|
|
950
|
+
`topwear` at its former grid spacing (18) and `bottomwear` at 28. Place the shape on the
|
|
951
|
+
patch alone: on `sample` one circle at the hip centre reached the clasped hands, painted
|
|
952
|
+
inside `bottomwear`, so the hips are two side circles (`hip_r`, `hip_l`) on one bone.
|
|
953
|
+
- **A `physics` constraint** on that bone in `constraints`, in rig-c's shape; every field but
|
|
954
|
+
the bone is rigc's to accept.
|
|
955
|
+
|
|
956
|
+
`build` on `sample` as tracked (PR #187; the values above are the ones tuned for its `hop`, issue #183):
|
|
957
|
+
|
|
958
|
+
```
|
|
959
|
+
check: PASS; 9 of 9 bar(s) measured, 0 skipped
|
|
960
|
+
loop physics: 2 physics constraint(s) left out of the loop's frames, reported, not judged — "bust_phys" on bone "chest_jiggle" (rig.json), "hip_phys" on bone "hip_jiggle" (rig.json)
|
|
961
|
+
loop: idle 49 frame(s) at 12 fps, f0000 vs f0048 (t = 4s): max |d| 0 (0 required)
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
The `loop physics:` line is the loop bar saying what it did not read (§7, *loop*): rig-c's
|
|
965
|
+
render resets physics at frame 0 and steps it, so the idle's last frame differs from its first
|
|
966
|
+
by whatever the spring is still doing; the bar is read off the same rig with every physics
|
|
967
|
+
constraint left out, and the constraints left out are listed, reported and not judged
|
|
968
|
+
(`check.json`'s `loop_physics`, PR #184). It is not a measure of the jiggle.
|
|
969
|
+
|
|
970
|
+
**Limits.** The idle drives the spring, and a slow idle barely moves it. Measured on `sample`
|
|
971
|
+
(PR #187, rig-c `render --geometry`, the physics build against the same build with
|
|
972
|
+
`constraints` removed): under its 4 s breath (`chest` `translatey` amplitude 1.3 px), the bust
|
|
973
|
+
moves at most 0.155 rig px (idle frame 3, t 0.25 s); the hips move 0.000 at all 49 frames,
|
|
974
|
+
because the idle keys `chest` and never `hip`, so a child of `hip` receives no motion. A spring
|
|
975
|
+
of strength 100 trails a parent moving at about 2 px/s by the order of speed over its angular
|
|
976
|
+
frequency, about 0.2 px (PR #187, from the definition): no setting makes a 4 s breath read as a
|
|
977
|
+
jiggle (with the values tracked now, `bust_phys` inertia 0.25, strength 120, damping 0.92, the
|
|
978
|
+
idle moves the bust at most 0.066 rig px, idle frame 2). Faster keys on the parent bone — a hop,
|
|
979
|
+
a turn, in a named animation (the recipe above) or a consumer's own — are what the spring
|
|
980
|
+
answers. `sample`'s `animations.json` holds one, `hop` (1 s: `hip` `translate` y down 5, up 24,
|
|
981
|
+
landing at -6 at 0.45 s, settling at 0.55 s; nothing keys the jiggle bones): rendered at 30 fps
|
|
982
|
+
against the same build without its constraints, the bust moves at most 6.583 rig px and the hips
|
|
983
|
+
(`hip_phys` inertia 0.2, strength 160, damping 0.88, mass 1.5) at most 4.432 (frame 13, the fall
|
|
984
|
+
into the landing), and both overshoot after it, 3.38 and 1.38 rig px (frames 21 and 22;
|
|
985
|
+
`docs/evidence/sample-hop.png`). No bar here judges the jiggle, and two limits follow from that:
|
|
986
|
+
|
|
987
|
+
- **The region's `band` caps the displacement.** The patch moves as one piece and the band is
|
|
988
|
+
where the mesh stretches to follow it, so physics values that carry the patch further than
|
|
989
|
+
its band absorbs squeeze or stretch the band's triangles past the texture-stretch ceiling.
|
|
990
|
+
`check` reads the idle alone and does not see it; check's own reading applied to `hop`'s
|
|
991
|
+
geometry with PR #187's values (inertia 0.5, strength 100, damping 0.85, mass 1, a bone
|
|
992
|
+
displacement of 9.85 rig px against bands of 16 and 12) gives
|
|
993
|
+
`CHECK_TEXTURE_STRETCH: mesh "bottomwear" triangle 13 (vertices 5 183 6), edge 6-5, idle frame 13 — the edge is 0.233 times its rest length (max(ratio, 1/ratio) 4.29); <= 1.926544 is required`
|
|
994
|
+
and `topwear` 2.597; with the values tracked now it reads 1.697 (`topwear`) and 1.566
|
|
995
|
+
(`bottomwear`). Tune the constraint against the animation that drives it, not the idle.
|
|
996
|
+
- **An added animation can move the idle's figures.** The render's viewport is fitted over
|
|
997
|
+
every animation, and the idle frames share it, so an animation that travels further than the
|
|
998
|
+
idle redraws the idle at another size: adding `hop` to `sample` narrowed the idle render from
|
|
999
|
+
201 to 198 px wide and moved `check.json`'s `BREATH_VISIBLE` `torso_heat_mean` from 7.8 to
|
|
1000
|
+
7.705, every status unchanged, although `check` reads the idle alone.
|
|
1001
|
+
|
|
847
1002
|
## 4. The command order
|
|
848
1003
|
|
|
849
1004
|
Two steps are external: the See-through runs (by any route; the optional `comfy seethrough` adapter is only a client for a ComfyUI box). Everything else is this tool.
|
|
@@ -978,7 +1133,7 @@ proposal in as the steps above say (`RL01`); every step must exit 0.
|
|
|
978
1133
|
| --- | --- | --- | --- |
|
|
979
1134
|
| `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*) |
|
|
980
1135
|
| `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 |
|
|
981
|
-
| `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, …)`; and look at `render/recomposite_error_rig.png`. Between the part lines and the totals, 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: `sample` 0.83 / 98.0 % / 4,359 / 1,185, 259 holes, the largest 94 px; `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 (the 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) |
|
|
1136
|
+
| `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, …)`; 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), 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: `sample` 0.83 / 98.0 % / 4,359 / 1,185, 259 holes, the largest 94 px; `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 (the 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) |
|
|
982
1137
|
| `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 `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; `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 |
|
|
983
1138
|
| `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 |
|
|
984
1139
|
| `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` |
|
|
@@ -1179,9 +1334,31 @@ leave a resampled edge between them in the render. The patch is config, so every
|
|
|
1179
1334
|
and read `uncovered error px` again: a patch covering `n` of the uncovered error
|
|
1180
1335
|
pixels lowers it by exactly `n` (the selftest's `AS17` plants that and counts it).
|
|
1181
1336
|
|
|
1337
|
+
**Cutting a piece out of a layer.** When one See-through layer holds two things that
|
|
1338
|
+
must move apart — a scarf end painted into the sweater's `topwear`, both arms in one
|
|
1339
|
+
`handwear` — add an `assemble.cuts` entry (§3): open `rig/parts/<part>.png` from an
|
|
1340
|
+
earlier `assemble`, add its `parts.json` `x`, `y` to a pixel's column and row to get
|
|
1341
|
+
rig pixels, and draw the polygon round the piece. The stage prints, between the part
|
|
1342
|
+
lines and the `fringe pushed back` lines, one line per cut part in the `pixels:`
|
|
1343
|
+
line's shape, counted when the cut is made (before the fringe push-back and the seam
|
|
1344
|
+
pass, which the records are counted after):
|
|
1345
|
+
|
|
1346
|
+
cut: "topwear" opaque 134452 = "topwear" 118973 + "scarf" 15479; band 593 px held by both
|
|
1347
|
+
|
|
1348
|
+
Give the piece a `meshes` entry over the bones that should carry it, and take those
|
|
1349
|
+
bones out of the part it left: on the public `scarf`, the scarf end `robe_r` runs down,
|
|
1350
|
+
cut out of `topwear` into its own mesh on `robe_r` (and `robe_r` dropped from `topwear`'s
|
|
1351
|
+
segments) leaves every `topwear` vertex still at wind 6 where the uncut mesh moved
|
|
1352
|
+
one by 101.5 px [observed, `docs/evidence/scarf-cut.png`]. Where the cut runs through
|
|
1353
|
+
opaque art, `overlap` decides the seam: at 0 the pieces abut, each edge is resampled
|
|
1354
|
+
against transparency, and on that cut the seam bar read 170 px over 40 against 50
|
|
1355
|
+
allowed (`CHECK_SEAM_WITHIN_BAR`); at 1 it read 8, at 2 it read 3, at 4 it read 2
|
|
1356
|
+
(the uncut build reads 2) [observed].
|
|
1357
|
+
|
|
1182
1358
|
`parts.json` holds one record per part, in draw order: the plan's, with each patch
|
|
1183
|
-
where its `draw` puts it (`"back"` patches first in their own
|
|
1184
|
-
`{"before": X}` patch
|
|
1359
|
+
and each cut's piece where its `draw` puts it (`"back"` patches first in their own
|
|
1360
|
+
order, then `"back"` pieces; each `{"before": X}` patch, then each such piece,
|
|
1361
|
+
immediately behind `X`; `"front"` patches last, then `"front"` pieces). Its counts:
|
|
1185
1362
|
|
|
1186
1363
|
| field | counts |
|
|
1187
1364
|
| --- | --- |
|
|
@@ -1197,7 +1374,8 @@ where its `draw` puts it (`"back"` patches first in their own order, each
|
|
|
1197
1374
|
|
|
1198
1375
|
A part's `from` is `<full|head>:<tag>` — the See-through run and tag, which every
|
|
1199
1376
|
role (`propose`) and every judgement-line region (`check`) is read from — or, for a
|
|
1200
|
-
patch, `painting:<its own name
|
|
1377
|
+
patch, `painting:<its own name>`; a cut's piece carries the `<run>:<tag>` of the part
|
|
1378
|
+
it was cut from, so two records may share one. **A patch is 100 % source**: every pixel is the
|
|
1201
1379
|
painting's, so it is all visible and all taken — `visible_px`, `projected_core_px`
|
|
1202
1380
|
and `source_px_taken` equal `opaque_px`, and `occluded_px`,
|
|
1203
1381
|
`visible_not_projected_px`, `refused_drift_px` and `merged_px` are 0. The reader
|
|
@@ -1316,10 +1494,11 @@ See-through with another seed.
|
|
|
1316
1494
|
| `CONFIG_FILE_PRESENT`, `CONFIG_IS_JSON` | no such file, or not JSON | `--config` |
|
|
1317
1495
|
| `CONFIG_KEY_KNOWN`, `CONFIG_KEY_RETIRED`, `CONFIG_FIELD_PRESENT`, `CONFIG_FIELD_TYPE` | an unknown key (a retired one says what replaces it), a missing one, a wrong type; a project's own record under a plain key, or an annotation (`note`, `<name>_note`) holding something other than a string | the named field; a record goes under a key beginning `x-` (any JSON, read by nothing), a remark under `note` or `<name>_note` (a string) — §3 |
|
|
1318
1496
|
| `CONFIG_HEAD_BOX_SQUARE` | `seethrough.head_box` is not a non-empty square | `head_box` — take `propose --head-box`'s |
|
|
1319
|
-
| `CONFIG_TAG_KNOWN`, `CONFIG_PART_NAME`, `CONFIG_PART_UNIQUE` | a plan entry names no v3 tag, a part name that is not a file name, or a part or layer twice — a patch named like a plan part or another patch included | `assemble.plan`, `assemble.patches` |
|
|
1320
|
-
| `CONFIG_NAME_RESOLVES` | a parent, segment, region, track, blink member, blink still part or bone, extend part or
|
|
1497
|
+
| `CONFIG_TAG_KNOWN`, `CONFIG_PART_NAME`, `CONFIG_PART_UNIQUE` | a plan entry names no v3 tag, a part name that is not a file name, or a part or layer twice — a patch named like a plan part or another patch, and a cut's `into` named like a plan part, a patch or another cut's, included | `assemble.plan`, `assemble.patches`, `assemble.cuts` |
|
|
1498
|
+
| `CONFIG_NAME_RESOLVES` | a parent, segment, region, track, blink member, blink still part or bone, extend part, patch or cut `draw.before`, or cut `from` names nothing declared above it (a cut's `from` must be a plan part); or a constraint's `bones`, `target`, `source` or `bone` names a bone `bones` does not declare (the detail names the constraint and lists the bones that exist) | the named reference, or the declaration it needs |
|
|
1321
1499
|
| `CONFIG_BONE_UNIQUE` | a bone or chain declared twice, or `root` declared | `bones` |
|
|
1322
|
-
| `CONFIG_PART_ATTACHED` | a plan part with neither or both of a mesh and a region; a patch with a mesh, or with no region | `meshes` / `regions` |
|
|
1500
|
+
| `CONFIG_PART_ATTACHED` | a plan part or a cut's piece with neither or both of a mesh and a region; a patch with a mesh, or with no region | `meshes` / `regions` |
|
|
1501
|
+
| `CONFIG_RIBS_MODE` | `meshes.<part>.ribs` on a lattice (`grid`) mesh: its vertices are its grid's corners, which no row can move (issue #188) | put the part in `contour` or `auto`, or remove `ribs` |
|
|
1323
1502
|
| `CONFIG_PATCH_BOX` | an `assemble.patches` box with `x1 <= x0` or `y1 <= y0` | that patch's `box` (`x1`, `y1` are exclusive) |
|
|
1324
1503
|
| `CONFIG_AMPS_MATCH_CHAIN` | a chain track's `amps` is not one per link | that track's `amps` |
|
|
1325
1504
|
| `CONFIG_PERIOD_DIVIDES_DURATION` | a period that is not a whole fraction of the idle — the loop could not close | that track's `period`, or `motion.duration` |
|
|
@@ -1363,7 +1542,10 @@ See-through with another seed.
|
|
|
1363
1542
|
| `ASSEMBLE_PART_OPAQUE` | a part ended with no opaque pixel | drop the entry, or take the tag from the other run |
|
|
1364
1543
|
| `ASSEMBLE_PATCH_BOX_INSIDE` | a patch's box reaches past the rig (the painting times `rig_scale`; the detail gives the size) | that patch's `box` — it is in rig pixels, not painting pixels |
|
|
1365
1544
|
| `ASSEMBLE_PATCH_OPAQUE` | a patch takes no pixel: a `"silhouette"` patch whose box holds none of the figure | move the box onto the figure, or `"alpha": "box"` |
|
|
1366
|
-
| `
|
|
1545
|
+
| `ASSEMBLE_CUT_INSIDE` | a cut's polygon has a point past the rig (the detail gives the points and the size) | that cut's `polygon` — it is in rig pixels, not painting pixels |
|
|
1546
|
+
| `ASSEMBLE_CUT_OVERLAP` | two cuts of one part share a rig pixel (the detail gives the count and the first, `x,y`) | one of the two polygons: a pixel goes to one part |
|
|
1547
|
+
| `ASSEMBLE_CUT_PIXELS` | a cut takes none of its part's opaque pixels (alpha above 8), or a part's cuts leave it none | draw the polygon over the art; to move the whole layer, rename the plan part instead |
|
|
1548
|
+
| `ASSEMBLE_COUNTS_ADD_UP` | a part's visible and occluded counts do not add up to its opaque pixels, or a projected pixel is not visible, or a cut part's pieces do not add up to it — an assembler bug, not an input problem | report it with the part named; nothing was written |
|
|
1367
1549
|
|
|
1368
1550
|
### propose
|
|
1369
1551
|
|
|
@@ -1842,6 +2024,13 @@ What is composed:
|
|
|
1842
2024
|
duration. Constraints are carried in character order with their
|
|
1843
2025
|
`invariants.detached`; `invariants.idleDrivesMeshes` once, when any character
|
|
1844
2026
|
declares it.
|
|
2027
|
+
- **Every animation beside a character's idle** — what `motion.animations_from`
|
|
2028
|
+
wrote into its `motion.json` (§3) — rides along as `<id>:<name>` after the idle,
|
|
2029
|
+
its tracks' bones, groups and easings prefixed as the idle's are and every other
|
|
2030
|
+
value as written; the scene schedules none of them, they are the consumer's to
|
|
2031
|
+
play. Each is read with the idle's own key lists, so a key compose does not know
|
|
2032
|
+
how to prefix is `SCENE_BUILD_FIELD_KNOWN`, as it is on the idle, and a track of
|
|
2033
|
+
one that keys `root` is `SCENE_ROOT_SHARED` (issue #183).
|
|
1845
2034
|
- **Gated as `rig` gates its own:** `rigc build --profile spine-html --pack`, the
|
|
1846
2035
|
compile and the packed pages on disk, in a scratch directory; `--out` receives
|
|
1847
2036
|
`rig/` (`rig.json`, `motion.json`, `images/`) and `scene.json` only when it is
|
package/docs/STABILITY.md
CHANGED
|
@@ -105,13 +105,16 @@ part name, `[i]` an entry of a list):
|
|
|
105
105
|
- **generation.sampler** — required: `steps`, `cfg`, `sampler`, `scheduler`
|
|
106
106
|
- **generation.control** — required: `skeleton`, `strength`, `end_percent`; optional: `model`
|
|
107
107
|
- **seethrough** — required: `resolution`, `steps`, `seed`, `offload`; optional: `head_box`
|
|
108
|
-
- **assemble** — required: `rig_scale`, `plan` (from plain `assemble` on); optional: `extend_below_crop`, `patches`
|
|
108
|
+
- **assemble** — required: `rig_scale`, `plan` (from plain `assemble` on); optional: `extend_below_crop`, `patches`, `cuts`
|
|
109
109
|
- **assemble.extend_below_crop[i]** — required: `part`, `run`, `tag`
|
|
110
110
|
- **assemble.patches[i]** — required: `name`, `box`, `alpha`, `draw`
|
|
111
111
|
- **assemble.patches[i].draw**, when an object — required: `before`
|
|
112
|
+
- **assemble.cuts[i]** — required: `from`, `into`, `polygon`, `draw`, `overlap`
|
|
113
|
+
- **assemble.cuts[i].draw**, when an object — required: `before`
|
|
112
114
|
- **bones[i]**, a single bone — required: `name`, `parent`, `at`; optional: `tip`
|
|
113
115
|
- **bones[i]**, a chain — required: `chain`, `parent`, `points`, `tip`
|
|
114
|
-
- **meshes.<part>** — exactly one of `grid`, `contour`, `auto`; required in every mode: `r`, `segments`; optional in every mode: `exponent`, `rule`
|
|
116
|
+
- **meshes.<part>** — exactly one of `grid`, `contour`, `auto`; required in every mode: `r`, `segments`; optional in every mode: `exponent`, `rule`; optional with `contour` or `auto`: `ribs`
|
|
117
|
+
- **meshes.<part>.ribs.<chain>** — required: `stations`
|
|
115
118
|
- **meshes.<part>.contour** — required: `tolerance`, `margin`, `spacing`; optional: `budget`, `stray`, `regions`
|
|
116
119
|
- **meshes.<part>.contour.regions[i]** — required: `name`, `shape`, `bone`, `spacing`, `band`, and `cx`, `cy`, `r` for a circle or `points` for a polygon
|
|
117
120
|
- **meshes.<part>.auto** — required: `source`, `sourceBounds`, `targets`, `influences`, `budget`, `minArtSamples`, `motion`; optional: `protect`, `regions`, and the three Stage B opt-ins `boundaryRuns`, `retriangulate`, `removalOrder`
|
|
@@ -152,7 +155,7 @@ every number) are AUTHORING §3's tables, and the loader refuses any other by na
|
|
|
152
155
|
| --- | --- | --- |
|
|
153
156
|
| `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` |
|
|
154
157
|
| `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 |
|
|
155
|
-
| `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`) |
|
|
158
|
+
| `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) |
|
|
156
159
|
| `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), `loop_physics` after `loop_max_diff` when the rig declares a physics constraint, 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 |
|
|
157
160
|
| `proposal.json`, `render/landmarks*.png` | `propose` | the config's four rig sections and `notes` |
|
|
158
161
|
| `basis.json` | `propose` | spec `spine-parts-basis/1` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-parts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rig-parts
|
|
3
|
-
description: Turn one anime character painting and its See-through layer decomposition into a verified 2D character rig (Spine 4.3 skeleton data) — measured parts, weighted meshes, bone chains and a looping idle — with rig-parts, which gates everything through rig-c before it is written. Use for a request to rig or animate a single character painting, to run or read rig-parts layers, sheet, assemble, propose, rig, check, loop or build, or to write or fix a rig-parts config.json. Not for cutting art into parts by hand, for authoring a rig from loose PNGs (that is the rigc skill), for
|
|
3
|
+
description: Turn one anime character painting and its See-through layer decomposition into a verified 2D character rig (Spine 4.3 skeleton data) — measured parts, weighted meshes, bone chains and a looping idle — with rig-parts, which gates everything through rig-c before it is written. Use for a request to rig or animate a single character painting, to run or read rig-parts layers, sheet, assemble, propose, rig, check, loop or build, or to write or fix a rig-parts config.json. Not for cutting art into parts by hand, for authoring a rig from loose PNGs (that is the rigc skill), for lip-sync or swapping a part's image, or for running See-through itself.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later. The tool is the npm package rig-parts (bunx rig-parts, or bun add -d rig-parts); rig-c is installed with it. See-through is run separately.
|
|
6
6
|
---
|
|
@@ -75,6 +75,13 @@ stage writes are the whole interface.
|
|
|
75
75
|
`assemble.patches` entry (a rig-pixel box, `alpha: "silhouette"`, drawn
|
|
76
76
|
`"back"` and reaching under its neighbours, its bone in `regions`) and assemble
|
|
77
77
|
again; never add a part to `rig/` or `parts/` by hand (AUTHORING §5).
|
|
78
|
+
One layer holding two things that must move apart (a scarf end inside
|
|
79
|
+
`topwear`, both arms in one `handwear`): add an `assemble.cuts` entry — a
|
|
80
|
+
polygon in rig pixels read off `work/rig/parts/<part>.png` plus its
|
|
81
|
+
`parts.json` `x`, `y`, a new name, a `draw`, an `overlap` (the band the part
|
|
82
|
+
keeps under the piece's edge; 1 px closed the seam on scarf) — and give the piece its own
|
|
83
|
+
`meshes` entry; the `cut:` line gives the split. At overlap 0 a cut through
|
|
84
|
+
opaque art leaves a seam `check` reads (AUTHORING §5).
|
|
78
85
|
A seated or reclining figure: add `--keypoints <file>` (body-18 joints in
|
|
79
86
|
painting px, each observed, occluded or missing; AUTHORING §3, *A posed
|
|
80
87
|
figure*) so the neck, hip, chest and sleeves follow the pose and LINT reads
|
|
@@ -132,11 +139,13 @@ share is synthesis where the painting was there to be taken.
|
|
|
132
139
|
a GPU or the network.
|
|
133
140
|
- Give a layer two depths: a layer in front of another in one place and behind it in
|
|
134
141
|
another is right in the still and wrong in motion.
|
|
135
|
-
-
|
|
142
|
+
- Propose, key or judge any animation but the looping `idle`, or lip-sync: every slot
|
|
143
|
+
holds one attachment, so nothing swaps a part's image. Expressions and a jiggle are
|
|
144
|
+
yours to build on top (below).
|
|
136
145
|
- Write animated WebP: `loop` writes GIF and APNG.
|
|
137
146
|
- Know what the art does not tag: a swinging element painted inside another layer,
|
|
138
147
|
hair of an unfamiliar shape, whether an accessory swings. Those are yours to add.
|
|
139
|
-
- Promise a success rate.
|
|
148
|
+
- Promise a success rate. Three public characters and eight private ones check green;
|
|
140
149
|
that is all that has been measured.
|
|
141
150
|
|
|
142
151
|
## Where to read next
|
|
@@ -144,6 +153,15 @@ share is synthesis where the painting was there to be taken.
|
|
|
144
153
|
[docs/AUTHORING.md](https://github.com/firejune/rig-parts/blob/main/docs/AUTHORING.md)
|
|
145
154
|
first — every config field, the command order, what to read after each stage, every
|
|
146
155
|
refusal. It is in the installed package at `node_modules/rig-parts/docs/AUTHORING.md`,
|
|
147
|
-
the copy that matches the version you run.
|
|
156
|
+
the copy that matches the version you run. Two recipes there cover what this package
|
|
157
|
+
leaves to you:
|
|
158
|
+
|
|
159
|
+
- An expression or any other named animation: AUTHORING §3, *Recipe: expressions and other
|
|
160
|
+
named animations* (`motion.animations_from`; built and gated with the rig, not measured).
|
|
161
|
+
- A jiggle (a bust, the hips): AUTHORING §3, *Recipe: a soft region driven by physics* (a
|
|
162
|
+
config-only bone, a `contour` region on it, a `physics` constraint; `examples/sample`
|
|
163
|
+
carries one, and its slow idle barely moves it).
|
|
164
|
+
|
|
165
|
+
A `RIG_RIGC_GREEN` or `CHECK_RIGC_GREEN`
|
|
148
166
|
line quotes rig-c's own assertion; rig-c's guide for those is
|
|
149
167
|
`node_modules/rig-c/docs/AUTHORING.md`, and its `rigc` skill covers the compiler.
|