rig-parts 2.3.0 → 2.4.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/cli.ts +2 -0
- package/docs/AUTHORING.md +291 -6
- package/docs/STABILITY.md +59 -3
- package/package.json +2 -2
- package/src/assemble.ts +124 -30
- package/src/boundarycolour.ts +233 -0
- package/src/build.ts +24 -5
- package/src/check.ts +165 -6
- package/src/config.ts +41 -3
- package/src/editreceipt.ts +194 -0
- package/src/motionseampose.ts +3 -1
- package/src/partsource.ts +91 -0
- package/src/requirements.ts +64 -2
- package/src/rig.ts +25 -1
- package/src/setuppainting.ts +18 -3
package/cli.ts
CHANGED
|
@@ -737,6 +737,8 @@ usage:
|
|
|
737
737
|
[--project core|visible] [--alt <name>=<source>:<full>[:<head>]]
|
|
738
738
|
[--requirements <file.json>]
|
|
739
739
|
With cut_material declared, records original source edges bound to emitted parts for check.
|
|
740
|
+
With pinholes.required: true declared, refuses every pinhole of the flat parts
|
|
741
|
+
stack by name, ASSEMBLE_PINHOLES, before writing; otherwise prints its SKIP line.
|
|
740
742
|
Merge the full-body and head-crop See-through runs into rig-space parts:
|
|
741
743
|
<out>/rig/parts/<name>.png (each cropped to its alpha box), <out>/rig/parts.json,
|
|
742
744
|
<out>/render/recomposite_rig.png and <out>/render/${ERROR_MAP_FILE} (the error
|
package/docs/AUTHORING.md
CHANGED
|
@@ -158,6 +158,7 @@ read by no CPU stage).
|
|
|
158
158
|
| `assemble.head_face_underfill` | assemble, `assemble --propose-plan` | **authored**, optional (issue #492): `boolean` (`CONFIG_FIELD_TYPE` on any other). During head-face preparation, when both runs exist and `full:face` has at least `150` opaque run px (`PROPOSE_MIN_PX`), the stage maps `full:face` into the head canvas via `fullLayerToHead` and measures how much of its hole-filled silhouette (`alpha > 8`) is covered by `head:face` (`alpha > 8`). An explicit plan selecting `head:face` is refused when that layer covers less than `75 %` (`HEAD_FACE_COVERAGE_FLOOR`, measured `0.0 %..48.6 %` on hollow/omitted head runs vs `84.8 %..99.6 %` on clean head runs across the 4 public examples and 9-standee corpus), with `SEETHROUGH_HEAD_RUN_HOLLOW`, unless `head_face_underfill: true` is declared. When `head_face_underfill: true` is declared and `head:face` exists (`opaqueRunPx >= 150`), `fullLayerToHead(full:face)` is composited under `head:face` on the head canvas (`alpha = max(headAlpha, fullAlpha)`, RGB blended by `headAlpha / 255`) before `head:face` is placed in rig space (`underfill=<N>px(full:face)` on the `face` part log line and a `face: head_face_underfill composited …` note in `assemble --propose-plan`). The fill restores transparent pixels only and leaves the cut's own edge where it is — a hollow whose edge is not under hair stays visible; follow [Source before scissors](#source-before-scissors) for its source remedy. When `head:face` has fewer than `150` opaque pixels, `SEETHROUGH_HEAD_RUN_HOLLOW` still fires even under `head_face_underfill: true` because the head run omitted the face layer and cannot supply a jawline. The proposer instead selects `full:face` after this refusal when that full-run candidate passes the pixel floor and plausibility guards (issue #863); if it does not, the refusal remains. An assemble plan selecting `full:face` without `head:face` does not prepare the unused head face |
|
|
159
159
|
| `assemble.yield_refused` | assemble | **authored**, optional boolean, default `false` (issue #819; other types: `CONFIG_FIELD_TYPE`). With `true`, an original front projection-core pixel refused at max-channel drift > `DRIFT_LIMIT` (90) can yield when its original source pixel is still opaque. Non-core rim pixels (0 < alpha < `CORE_ALPHA`, 250) on the same 8-connected part body can then yield too (issue #832). Connectivity reads the part support before clearing: the opaque border outside the eroded projection core connects the body to its rim but is not itself newly cleared. A separate rim island without a yielded core seed is unchanged. At every cleared pixel, the actual top-most opaque holder below it (alpha >= 250), after earlier yields, must have colour within 90 of the painting. A deeper matching part cannot bypass an intervening opaque mismatch. This setting also applies to alternate sets. A yielded pixel's alpha becomes 0 and its RGB is not repainted. Accepted own-core pixels in the same connected support within the original yielded core's Gaussian footprint (radius 4 rig pixels), with a matching opaque behind holder, finish to painting RGB while retaining alpha and ownership (issue #841); original refusals, protected samples and replacements are excluded. Away from that footprint projection is unchanged. Merge/ring replacements, samples explicitly kept by `keep_under_occluders`, and pixels outside a derived piece cannot connect a rim or be newly yielded. Front is relative to the matching part behind, so original refusals in another run are eligible even when a different part is the global top holder. The reading proceeds back to front against the current behind pixels; alpha changes do not repaint or create a new matching colour. `yieldReadings` optionally records `finishedProjection` (accepted samples whose RGB changed), and records `yieldedRim` as the rim subset of total `yielded`, skipped core merge/ring/nonopaque/no-match counts and original/final global holders; a yielded pixel also taken by projection closing is omitted from the written `source_px_taken` count. Existing cuts/patches and thresholds are unchanged. The `drift:` line reports `yielded N px` per affected part, even below the drift-body size bar; with the setting off it names `assemble.yield_refused: true` as the conditional remedy. Rebuild and inspect the newly exposed layer in motion before considering any cut; a mismatching or absent behind holder cannot be repaired by this setting. |
|
|
160
160
|
| `assemble.keep_under_occluders` | assemble | **authored**, optional array of unique source part names from `assemble.plan`, default `[]` (issue #825). For each named part, keep its own See-through RGBA where the front-most covering part in the final draw stack has alpha greater than 0 and below `CORE_ALPHA` (250), including a cover in the other run. For an explicitly named part, original alpha 1–8 fringe removed by ghost cleanup also counts as source cover, within its final piece region: those pixels still contributed to the painting. Only original opaque own-layer samples are restored after source projection/closing and protected from later seam re-take; merged/rim, cap, under-fill and authored replacement pixels are excluded. This keeps their existing provenance and makes no holes; fully opaque cover and absent cover keep the old path. Cuts and limb pieces inherit the source part's protection. Unknown names: `CONFIG_NAME_RESOLVES`; duplicates: `CONFIG_PART_UNIQUE`; invalid shape: `CONFIG_FIELD_TYPE`. Inspect the part's own amodal layer first: this handle preserves its existing content and cannot complete a missing pixel — in particular, it does not close a motion opening inside the figure (`figure_openings` / [Openings inside the figure on undeclared layer pairs](#openings-inside-the-figure-on-undeclared-layer-pairs-issue-933)) where the lower layer has no opaque core under an opaque upper layer (measured with every `assemble.plan` part named in `keep_under_occluders`: `0` of `1` pair-animation warnings across `demo`'s eight animations closed, with `hair_back\|handwear_r` on `hop` frame `5` staying at `40` cells over `28` allowed in `38x70` world units at `2.517` world units/cell, while `scarf` reads `CLEAR` (`0` warnings) both with and without `keep_under_occluders`; use `assemble.extend_painting` when the lower part lacks opaque source material under an opaque upper cover). Example: `"keep_under_occluders": ["shin_r"]`. |
|
|
161
|
+
| `assemble.editor_receipts` | assemble, build | **authored**, optional (issue #946): `{receipts: [{file, sha256, from_sha256, editor}], required?, trace_parts?}`. Each receipt records an image edited in an image editor; `file` (relative to the config's directory) must exist and hash to `sha256`, verified at assemble; `from_sha256` and `editor` are the author's declaration, held only to form. With `required: true`, every author-made layer, hidden-art layer, cut `under` and limb `cap` the build reads, alternate sets' runs included, needs a receipt whose `sha256` is its file's. With `trace_parts: true` (issue #947), every part's pixels must trace to a layer of the split or to a receipted image: the receipts are demanded as under `required`, and every shape the config draws through a part — a cut's `polygon`, `seethrough.arm_split`, a limb's `split.line`, a patch's `box`, a painting extension's `band` — is refused (`ASSEMBLE_PART_SOURCE`). Absent, no file is read and no byte moves. See [Editor receipts](#editor-receipts-assembleeditor_receipts-issue-946) and [Part sources](#part-sources-assembleeditor_receiptstrace_parts-issue-947). |
|
|
161
162
|
| `limbs` | assemble (the cut and the caps), rig (through `regions`), check (`LIMB_ONE_BONE`) | **proposed** by `propose --keypoints` (issue #349), then checked: `{<limb>: {segments: [{part, from, bone}, {part, from, bone, joint, range?, cap?, edge?, cap_shape?, cut_band?, hybrid?}, …], split?, layout?, front?}}`, the upper segment first. The rule is *Limbs: cut at their joints* below the table. `part` is a new part (`CONFIG_PART_NAME`, `CONFIG_PART_UNIQUE` against the plan, patches, cuts and every other limb part); `from` a plan part or a cut's piece (`CONFIG_NAME_RESOLVES`) — the layer the segment's pixels come from, which is cut into the limb parts and is not itself made: a `meshes` or `regions` entry for it is `CONFIG_NAME_RESOLVES`, and the segments of one limb that take one part are consecutive (`CONFIG_LIMB_FROM_RUN`); `bone` a declared bone, each segment's the child of the one before (`CONFIG_LIMB_BONES_CHAIN`); the upper segment names no `joint`, `range`, `cap`, `edge`, `cap_shape` or `cut_band` (`CONFIG_KEY_KNOWN`), every other names its `joint` (`CONFIG_FIELD_PRESENT`) — the joint is its bone's origin; `range` is `[min, max]` degrees from rest with `min <= 0 <= max` (`CONFIG_LIMB_RANGE`), which `assemble` writes into the lower part's `parts.json` record and `check` poses the joint at — its two ends and its middle — reading the seam there (`SEAM_OVER_RANGE`, issue #360; rig-c needs no rotation limit); `cap` a PNG path relative to the config's directory (`CONFIG_FIELD_TYPE`); `edge` `"feather"` (issue #360), the one value it takes, and `cap_shape` `"disc"` (issue #360) or `"ball"` (issue #579) (`CONFIG_FIELD_TYPE` on any other) — the cut's geometry at that joint, *Limbs: cut at their joints* below; absent, the cut is hard and the cap the layer's half-disc of half the limb's width; `cut_band` a whole number of px, 1 or more (`CONFIG_FIELD_TYPE`), on a segment that takes the segment above's part — between two layers nothing is cut, so a band there is `CONFIG_KEY_KNOWN` — places the cut within that many px of the joint line (issue #360, *Limbs: cut at their joints*); absent, the cut is the joint line. Each limb part is attached like any part — `regions.<part>`, which must be the segment's `bone` (`CONFIG_LIMB_BONE`), or a mesh, which `check` holds to that bone alone (`LIMB_ONE_BONE`). With `limbs`, `assemble` reads `bones` too (`CONFIG_FIELD_PRESENT` without it). Absent, nothing is cut at a joint and no byte of any output moves. Optional `front` (issue #664), `{parts: [<own segment part>, …], after: "<produced part>"}`, gives each named segment a second slot `<part>_front`, empty at setup, placed after `after`'s slot family, for a gesture set to draw on (`CONFIG_FRONT_SCOPE`, `CONFIG_FRONT_NAME`, `CONFIG_FRONT_LAYOUT`; *Front slots (issue #664)* below) |
|
|
162
163
|
| `bones` | rig; `propose --from-config` | **proposed** by `propose`, then corrected. A single bone `{name, parent, at, tip?}` or a chain `{chain, parent, points, tip, link_inherit?}` 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 with a `tip` is turned the same way, towards its tip, with that distance as its length (issue #239); a tip on the bone's own origin is refused (`RIG_TIP_DISTINCT`). The five face bones `eye_r`, `eye_l`, `brow_r`, `brow_l` and `mouth` (by name, when one has no tip of its own) are turned to the eye line — the line through the origins of `eye_r` and `eye_l` as `bones` writes them, read image-left to image-right, drawn when parts.json holds both an `eyewhite-r` and an `eyewhite-l` part — so the blink's `scaley` squashes across the eye line on a tilted head; move an eye bone and the line moves with it. With one eyewhite or none, or an eye bone missing, the rig stage's `face axis:` line says so and they keep the page's axes. A bone under a face bone keeps the face's axis. With the line, `rig.json` also declares `invariants.axes` — one entry, `"face"`, through `eye_r` and `eye_l`, holding the turned face bones (and their controls) — so rig-c's gate holds the build to the line; the line is the one rigc draws through the same two bones, so the tolerance is only the float floor — the largest gap the emitted (float32) bones have to it, rounded up to 6 places plus one 6-place unit — and the `face axis:` line prints it. With the line the rig stage also adds `head_up` (issue #250): a slotless bone under `head`, at head's origin, turned to the eye line + 90 degrees and as long as the eye midpoint lies from head's origin along that axis — the head's frame, which a rule that needs it reads; `head` keeps its own frame, so no key on it or under it changes meaning, and a config bone of that name is refused (`RIG_HEAD_UP_NAME_FREE`). A face bone with its own `tip` is held too (issue #258), in a second entry, `"face_tips"`, when its tip lies along the line within whole-pixel rounding — a tip written in whole pixels sits at most sqrt(2)/2 px off the point of the line it stands for, so its direction may sit up to asin((sqrt(2)/2) / length) degrees off — with its tolerance measured the same way; a tip further off is the bone's own axis, and the `face axis:` line names it with its gap and that bound. A failure is rig-c's `A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS` line, which names the bone and the fix (rig-c's `docs/AUTHORING.md` §3.7 and §5.2); with no line no entry is written and A51 reports SKIP. Every region on a turned bone is turned back, so the setup pose does not move. Any other single bone keeps world rotation 0; one whose parent is a link is turned back upright. A single bone may declare `inherit` (issue #348): what it takes from its parent's transform, one of rig-c's own mode names exactly as rig-c's table spells them — `normal`, `onlyTranslation`, `noRotationOrReflection`, `noScale`, `noScaleOrReflection` (rig-c's `RigBone.inherit`) — written to that bone in `rig.json` as declared; any other spelling, `"NoScale"` included (the runtime would fold its first letter; nothing here folds or maps), is refused (`CONFIG_BONE_INHERIT`), and a chain takes no `inherit` (`CONFIG_KEY_KNOWN`). A chain may instead declare `link_inherit` (issue #369), an object mapping its own expanded link names to those same modes, for example `"link_inherit": {"leg_r1": "noScale"}`. Only the named link changes; omitted links inherit normally. A key outside this chain is `CONFIG_NAME_RESOLVES`, with this chain's valid link names in the detail; a non-object or non-string mode is `CONFIG_FIELD_TYPE`, and another mode spelling is `CONFIG_BONE_INHERIT`. The mode is forwarded to the expanded link, or to its control under `--idle-keys ctl`, by the same rule as a single bone. This is an authored opt-in: `propose` does not add it. Its setup rotation is the turn from where rig-c's own poser puts the bone with no rotation of its own under that mode, so it still stands upright (or along its tip) at the setup pose. Where the idle gives the bone a `<bone>_ctl`, the mode goes on the control: the control is the bone the parent's rotation reaches, and the bone under it inherits normally, so the idle's keys moved onto the control still turn it. A foot that does not take its shin's rotation (`noRotationOrReflection` or `onlyTranslation`) keeps its shoe level while the leg is posed (`FEET_STAY_LEVEL`, §7); which mode is the author's. `noScale` keeps a shin or shoe's shape under a scaled parent but still takes the parent's rotation: a shoe that must also stay level needs a separate rotation constraint, for example a transform constraint from an unrotated bone. This does not change the proposed feet's existing inheritance or `FEET_STAY_LEVEL` default. An automatic mesh weighted through a bone with a mode other than `normal` gets no derived amplitude (its row names the bone) and no residual envelope (rig-c's `SKINNING_RANGE_UNSUPPORTED`). Absent, `rig.json` carries no `inherit` and no byte moves |
|
|
163
164
|
| `meshes.<part>` | rig | **proposed**, then corrected. Generated modes below; `explicit` transfers an accepted mesh (see “An accepted mesh”). Exactly one mode is allowed. `grid` (the lattice: cell size, px — what `propose` writes: by default one spacing for the figure, round(H / 37) rig px with H the height of every part's box together, a part whose tenth of its short side is already coarser keeping that, and under `propose --grid-rule part` the reference's tenth of the part's short side clamped to 5..36, issue #265), `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 distance-generated modes: `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, less any island a declared `stray` left out) 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. The rule is chosen per mesh, so `RIG_HEAT_BONE_SOURCE` names three remedies: move the segment onto the art, take the bone out of `segments`, or keep this part on the distance rule — the one to take when the bone anchors the part from outside its art (a torso bone under a neck), since the first moves a bone another part placed and the second drops the anchor (issue #303). On a lattice, `RIG_HEAT_ISLAND_SOURCE` on an island that is dust — at most 4 px and at most 0.1 % of the part's art px, `mesh-draft`'s line — names `meshes.<part>.stray` and the figure that leaves it out, with the value declared; an island above that line keeps the segment remedy, a real piece being a real authoring question. In every mode the mesh's `mesh_report.json` row's `bones` names the bones the emitted model binds for the attachment — the sorted set its weights in the written `rig.json` name, so a `turn` twin (`turn_<part>_<bone>`) or a stopped link's `<link>_d` where those rebind it, never the segment bone it stands for — the same list in every mode, `remesh --delta`'s included (issue #536) Optional `gather: false` keeps an exclusively bound non-segment limb rider at its authored draw position (issue #680); its binding and mesh geometry are unchanged. Remove the key for normal gathering; a non-rider is refused as `RIG_GATHER_RIDER`. |
|
|
@@ -732,6 +733,103 @@ identity an executable check on supplied original inputs instead of trusting
|
|
|
732
733
|
that claim or hand-editing `layers.json`. Seed and painting selection remain
|
|
733
734
|
the author's decisions.
|
|
734
735
|
|
|
736
|
+
### Editor receipts (`assemble.editor_receipts`, issue #946)
|
|
737
|
+
|
|
738
|
+
An image edited in code — a layer re-segmented by a script, a patch
|
|
739
|
+
synthesised, a hole inpainted by a scripted pass — reads as art to every gate
|
|
740
|
+
after it. The images this package can know were edited are the ones the input
|
|
741
|
+
itself says were made outside See-through: an author-made layer
|
|
742
|
+
(`author:<label>`), a hidden-art layer (`<layer>:hidden`), a cut's `under` and
|
|
743
|
+
a limb segment's `cap`. Nothing is inferred from pixels. A receipt records
|
|
744
|
+
that such an image was edited in an image editor:
|
|
745
|
+
|
|
746
|
+
```json
|
|
747
|
+
"editor_receipts": {
|
|
748
|
+
"required": true,
|
|
749
|
+
"receipts": [{
|
|
750
|
+
"file": "full/parts/author:hand.png",
|
|
751
|
+
"sha256": "<sha256 of the edited file>",
|
|
752
|
+
"from_sha256": "<sha256 of the bytes the edit started from>",
|
|
753
|
+
"editor": "<the image editor used>"
|
|
754
|
+
}]
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
This object belongs in `assemble`; `receipts` is required (it may be empty),
|
|
759
|
+
and every receipt takes all four fields. What each field is held to:
|
|
760
|
+
|
|
761
|
+
| field | held to | by |
|
|
762
|
+
| --- | --- | --- |
|
|
763
|
+
| `file` | a file at that path, relative to the config's directory; one receipt per file | assemble (`ASSEMBLE_EDITOR_RECEIPT_FILE`), the loader (`CONFIG_EDITOR_RECEIPT`) |
|
|
764
|
+
| `sha256` | the sha256 of that file's bytes, 64 lowercase hex digits | assemble (`ASSEMBLE_EDITOR_RECEIPT_SHA256`), the loader (`CONFIG_FIELD_TYPE`) |
|
|
765
|
+
| `from_sha256` | **declared**: 64 lowercase hex digits, and not the receipt's own `sha256` — an edit that left the bytes unchanged is not an edit | the loader (`CONFIG_FIELD_TYPE`, `CONFIG_EDITOR_RECEIPT`) |
|
|
766
|
+
| `editor` | **declared**: a non-empty string | the loader (`CONFIG_FIELD_TYPE`) |
|
|
767
|
+
|
|
768
|
+
`from_sha256` and `editor` are the author's statement. A file cannot show that
|
|
769
|
+
a person used an editor, and nothing here claims to check it; what the check
|
|
770
|
+
holds is that the statement exists, is well formed, and names the bytes that
|
|
771
|
+
are on disk now. An image changed after its receipt was written no longer
|
|
772
|
+
matches it and is refused.
|
|
773
|
+
|
|
774
|
+
Every declared receipt is verified, with or without `required`. With
|
|
775
|
+
`"required": true`, every edited image the build reads must also carry a
|
|
776
|
+
receipt whose `sha256` is that image's file — the base runs, each alternate
|
|
777
|
+
set's runs, every cut `under` and every limb `cap`. A miss is
|
|
778
|
+
`ASSEMBLE_EDITOR_RECEIPT_MISSING`, naming the image as `parts.json`'s `from`
|
|
779
|
+
names a layer (`full:author:hand`, `alternate set "raised" full:author:hand`)
|
|
780
|
+
or as the config names a file (`config.assemble.cuts[0].under (part "x")`),
|
|
781
|
+
with its path and the sha256 found. The refusals come before anything is
|
|
782
|
+
assembled, and nothing is written. An image is matched to its receipt by
|
|
783
|
+
content, so a layer a wrapper run holds both as `<filename>` and as
|
|
784
|
+
`parts/<name>.png` (byte-identical, `LAYERS_PNG_UNAMBIGUOUS`) needs one
|
|
785
|
+
receipt; a PSD layer's file is the PSD.
|
|
786
|
+
|
|
787
|
+
A See-through layer or the painting overwritten in place carries no mark the
|
|
788
|
+
package can read, so `required` cannot find it; receipt it if it was edited,
|
|
789
|
+
and the receipt is verified like any other. Absent `required` (or `false`), no
|
|
790
|
+
receipt is demanded, and a run with no author-made or hidden-art layer, no
|
|
791
|
+
`under` and no `cap` demands none either. Absent the block, no file is read and
|
|
792
|
+
every output is byte-identical to a build without it.
|
|
793
|
+
|
|
794
|
+
### Part sources (`assemble.editor_receipts.trace_parts`, issue #947)
|
|
795
|
+
|
|
796
|
+
A part carved out of a layer along a line the author drew is not a part the
|
|
797
|
+
split made: its edge runs through the layer's art, and it tears there when the
|
|
798
|
+
piece moves. With `"trace_parts": true` in the `editor_receipts` block, every
|
|
799
|
+
part's pixels must trace to a layer of the See-through split or to an image a
|
|
800
|
+
receipt covers, and nothing else:
|
|
801
|
+
|
|
802
|
+
```json
|
|
803
|
+
"editor_receipts": { "trace_parts": true, "receipts": [] }
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
- Every edited image the build reads needs a receipt, exactly as under
|
|
807
|
+
`required` (`ASSEMBLE_EDITOR_RECEIPT_MISSING`, whose detail then names
|
|
808
|
+
`trace_parts` as the field that demanded it).
|
|
809
|
+
- Every shape the config draws through a part's pixels is refused by
|
|
810
|
+
`ASSEMBLE_PART_SOURCE`, naming the part as `parts.json` names it, where the
|
|
811
|
+
config declares the shape, and the shape found: an `assemble.cuts` entry's
|
|
812
|
+
`polygon` (and each alternate set's own `cuts`), `seethrough.arm_split`'s
|
|
813
|
+
`x` or `line` (its two halves are recorded `full:handwear-r` and
|
|
814
|
+
`full:handwear-l`, as See-through layers are, so only the config shows the
|
|
815
|
+
cut), a limb's `split.line`, an `assemble.patches` entry's `box` of the
|
|
816
|
+
painting and an `assemble.extend_painting` entry's `band`.
|
|
817
|
+
|
|
818
|
+
What stays: a limb's segments are cut at the joint the bones place, not along a
|
|
819
|
+
drawn line, and a limb `split` with `"edge": "painting"` follows the painting's
|
|
820
|
+
stroke; a `take` entry's layer is another run's, held by its identity receipt;
|
|
821
|
+
`extend_below_crop` continues a part from a full-run layer; projection, the seam
|
|
822
|
+
override and the fringe push-back write inside a layer's own footprint. The
|
|
823
|
+
remedy for a refused part is an author layer (`author:<label>`) painted in an
|
|
824
|
+
image editor, with its receipt, or the part taken whole from a layer of the
|
|
825
|
+
split.
|
|
826
|
+
|
|
827
|
+
The refusals come before anything is assembled, every one in one run, and
|
|
828
|
+
nothing is written. Absent `trace_parts` (or `false`), nothing more is demanded
|
|
829
|
+
and no byte moves; a build that passes under it writes the same bytes as
|
|
830
|
+
without it. Of the public examples, every config holds no drawn shape except
|
|
831
|
+
`scarf`, whose four patches `trace_parts` would refuse.
|
|
832
|
+
|
|
735
833
|
### Alternate part layers (`assemble.alternates[i].layers`, issue #666)
|
|
736
834
|
|
|
737
835
|
A gesture run may hold a raised arm only in a layer the author added, while
|
|
@@ -3884,7 +3982,7 @@ and a usage error with `--head-box`, `--cloth` or `--from-config`.
|
|
|
3884
3982
|
| --- | --- | --- | --- |
|
|
3885
3983
|
| `layers` | the table, then one `author-made: layer "author:<label>" — …` line per layer the author made (group `author` in the table; §5, *A layer the author made*), 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*) |
|
|
3886
3984
|
| `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 |
|
|
3887
|
-
| `assemble` | one line per part — its counts (`op=` opaque, `vis=` visible, `src=` taken / candidates, `unproj=` visible but not projected, `drift=<n> (<p>% of core)`, `merged=`, `seam=`; *parts.json*, below). `drift=` is `refused_drift_px` in rig px and its share of the part's candidates, `projected_core_px`, which it is a subset of (issue #314); no bar reads either. On the four public examples the share runs to 94.5 % (`scarf` `hair_back`, 479 of 507) and the count to 26,472 (`scarf` `topwear`, 22.3 %), and the seam override repaints refused top-most pixels afterwards, so a large count says where See-through's colour was not taken, not that the part is unlike the painting: the `drift:` line below is the one that names a body of them another part holds. After the part lines, `provenance:` per part and `provenance total:` report the value partition, followed by `ownership:` per cut for structural moves, independent of RGBA equality; alternate sets get their own `set "<set>"`-prefixed lines ([the two axes](#source-before-scissors)). The author-made/cut/limb/fringe lines described below precede 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), then one `drift: "<part>" refuses <n> px of <run>:<tag> (its colour more than 90 off the painting, max channel), in a body of <px> px at x,y wxh (…); by the plan's draw order "<holder>" holds <px> px of it (…)[, "<holder>" drawn in front of "<part>"][; the painting is within 90 of "<behind>" (<px> px at alpha >= 250, drawn behind "<part>")]; informational: no gate reads this. Swapping the plan order of the parts named here can clear it (when only the front one differs from the painting) or move it to the other part (when both do): swap, rebuild and read this line again; if it moves, removing it needs different input layers. At rest, the seam pass repaints the holder's pixels that pass its rule from the painting` line per part whose drift refusals hold a body (the same opening and bar) that another part holds in the draw order or that a part drawn behind it matches within 90 of the painting (issues #255, #351, #758) — that layer is not what the painting shows there; no gate reads this line, swapping the plan order of the parts named here can clear it (when only the front one differs from the painting) or move it to the other part (when both do) — swap, rebuild and read this line again, and if it moves, removing it needs different input layers — and at rest the seam pass repaints the holder's pixels that pass its rule from the painting (none on the five public paintings), then informational `drift: "<part>" source re-take changed N own-layer px` lines under soft front cover, naming `assemble.keep_under_occluders` and its kept count when enabled ([source ladder](#source-before-scissors)), then, per subset alternate set, one `LINT set "<set>" <part>: <layer part> holds <n> islands (8-connected, alpha above 8)[, and <s> speck(s) under 1 % of the largest (<f> px), each with the island nearest it], kept the one under <joints> (<k> px), dropped <m> (<d> px)` line per limb part cut from a layer of more than one island when the set names one limb side of it (issue #588; issue #608: a joint taken to the nearest island reads `<bone> [x, y] (<e> px off it, within its cap's radius <r>)`, and a part that lost no pixel `dropped none`), then one `LINT set "<set>" <part>: <layer part> holds <limb> and <other limb> in one island (8-connected, alpha above 8), split by the nearest segment line at the set's bones: kept <k> px (<share>), gave <m> px (<share>) to <other limb>; <b> px in the overlap band (nearer one line than the other by 0.5 px or less) went to the limb drawn in front, <bk> kept and <bg> given` line per limb part cut from a layer of one island that a limb the set does not carry also takes (issue #609; k, m and b count the part's pixels above alpha 8, each share of the part's pixels before the split, 3 places) and one `LINT set "<set>" <part>: <n> px under <base part> excluded` line per subset alternate set's limb part and base part drawn over it that the set does not replace which took a pixel (issue #587, `assemble.alternates` above; none on the public examples, which declare no alternate set); and look at `render/recomposite_error_rig.png`. The opt-in `assemble.yield_refused` adds `yielded N px` to each affected part's drift line (including a yielded refusal smaller than the body bar); with it off the line names `assemble.yield_refused: true` as the conditional config remedy before any cut. Between the part lines and the totals, one `author-made: part "<part>" (<run>:author:<label>) — …` line per part from a layer the author made (below, *A layer the author made*), then 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 — with `config.limbs` (issue #349) — one `limb cut: "<part>" opaque <n> = "<segment part>" <px> + …` line per part a limb takes and one `limb cap:` line per joint, its cap's pixels from the cap image, from the layer and missing, and its box (under `cap_shape: "ball"`, issue #579, then the lower half's pixels written, left as painted and missing), followed on a short cap by one `LINT "<part>" cap at [x, y] … short_px left <n>, right <m>: no layer gives them …` line (issue #557) — from the upper segment where it gave a pixel (under `cap_shape: "disc"` always), under `edge: "feather"` the pixels faded, under `cut_band` the line the cut was placed on and the stroke pixels it and the joint line cross (issue #360) (§3, *Limbs: cut at their joints*), 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. Use [Source before scissors](#source-before-scissors) for the source ladder; a missing independently moving structural piece can use a painting patch (below) |
|
|
3985
|
+
| `assemble` | one line per part — its counts (`op=` opaque, `vis=` visible, `src=` taken / candidates, `unproj=` visible but not projected, `drift=<n> (<p>% of core)`, `merged=`, `seam=`; *parts.json*, below). `drift=` is `refused_drift_px` in rig px and its share of the part's candidates, `projected_core_px`, which it is a subset of (issue #314); no bar reads either. On the four public examples the share runs to 94.5 % (`scarf` `hair_back`, 479 of 507) and the count to 26,472 (`scarf` `topwear`, 22.3 %), and the seam override repaints refused top-most pixels afterwards, so a large count says where See-through's colour was not taken, not that the part is unlike the painting: the `drift:` line below is the one that names a body of them another part holds. After the part lines, `provenance:` per part and `provenance total:` report the value partition, followed by `ownership:` per cut for structural moves, independent of RGBA equality; alternate sets get their own `set "<set>"`-prefixed lines ([the two axes](#source-before-scissors)). The author-made/cut/limb/fringe lines described below precede 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), then the `ASSEMBLE_PINHOLES: PASS — 0 pinholes …` or `ASSEMBLE_PINHOLES: SKIP — not measured, not a pass: …` line ([Pinholes at rest](#pinholes-at-rest-issue-944)), then one `drift: "<part>" refuses <n> px of <run>:<tag> (its colour more than 90 off the painting, max channel), in a body of <px> px at x,y wxh (…); by the plan's draw order "<holder>" holds <px> px of it (…)[, "<holder>" drawn in front of "<part>"][; the painting is within 90 of "<behind>" (<px> px at alpha >= 250, drawn behind "<part>")]; informational: no gate reads this. Swapping the plan order of the parts named here can clear it (when only the front one differs from the painting) or move it to the other part (when both do): swap, rebuild and read this line again; if it moves, removing it needs different input layers. At rest, the seam pass repaints the holder's pixels that pass its rule from the painting` line per part whose drift refusals hold a body (the same opening and bar) that another part holds in the draw order or that a part drawn behind it matches within 90 of the painting (issues #255, #351, #758) — that layer is not what the painting shows there; no gate reads this line, swapping the plan order of the parts named here can clear it (when only the front one differs from the painting) or move it to the other part (when both do) — swap, rebuild and read this line again, and if it moves, removing it needs different input layers — and at rest the seam pass repaints the holder's pixels that pass its rule from the painting (none on the five public paintings), then informational `drift: "<part>" source re-take changed N own-layer px` lines under soft front cover, naming `assemble.keep_under_occluders` and its kept count when enabled ([source ladder](#source-before-scissors)), then, per subset alternate set, one `LINT set "<set>" <part>: <layer part> holds <n> islands (8-connected, alpha above 8)[, and <s> speck(s) under 1 % of the largest (<f> px), each with the island nearest it], kept the one under <joints> (<k> px), dropped <m> (<d> px)` line per limb part cut from a layer of more than one island when the set names one limb side of it (issue #588; issue #608: a joint taken to the nearest island reads `<bone> [x, y] (<e> px off it, within its cap's radius <r>)`, and a part that lost no pixel `dropped none`), then one `LINT set "<set>" <part>: <layer part> holds <limb> and <other limb> in one island (8-connected, alpha above 8), split by the nearest segment line at the set's bones: kept <k> px (<share>), gave <m> px (<share>) to <other limb>; <b> px in the overlap band (nearer one line than the other by 0.5 px or less) went to the limb drawn in front, <bk> kept and <bg> given` line per limb part cut from a layer of one island that a limb the set does not carry also takes (issue #609; k, m and b count the part's pixels above alpha 8, each share of the part's pixels before the split, 3 places) and one `LINT set "<set>" <part>: <n> px under <base part> excluded` line per subset alternate set's limb part and base part drawn over it that the set does not replace which took a pixel (issue #587, `assemble.alternates` above; none on the public examples, which declare no alternate set); and look at `render/recomposite_error_rig.png`. The opt-in `assemble.yield_refused` adds `yielded N px` to each affected part's drift line (including a yielded refusal smaller than the body bar); with it off the line names `assemble.yield_refused: true` as the conditional config remedy before any cut. Between the part lines and the totals, one `author-made: part "<part>" (<run>:author:<label>) — …` line per part from a layer the author made (below, *A layer the author made*), then 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 — with `config.limbs` (issue #349) — one `limb cut: "<part>" opaque <n> = "<segment part>" <px> + …` line per part a limb takes and one `limb cap:` line per joint, its cap's pixels from the cap image, from the layer and missing, and its box (under `cap_shape: "ball"`, issue #579, then the lower half's pixels written, left as painted and missing), followed on a short cap by one `LINT "<part>" cap at [x, y] … short_px left <n>, right <m>: no layer gives them …` line (issue #557) — from the upper segment where it gave a pixel (under `cap_shape: "disc"` always), under `edge: "feather"` the pixels faded, under `cut_band` the line the cut was placed on and the stroke pixels it and the joint line cross (issue #360) (§3, *Limbs: cut at their joints*), 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. Use [Source before scissors](#source-before-scissors) for the source ladder; a missing independently moving structural piece can use a painting patch (below) |
|
|
3888
3986
|
| `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 <link> at … runs to … N degrees off the head's down axis …` — a fringe link that does not lean with the head (§3, the fringe in the head's frame): move it along the lock it drives; `LINT <bone> under <parent>: amplitude <a> below the link above's <b> in the keyed chain …` — under `--cloth`, a keyed sibling the recipe left the only keyed child of its parent, which `CHAIN_LAG` would refuse (issue #572, §3 *A keyed sibling left behind*): raise the field it names to `<b>` or more in size; `LINT hairback_… on mesh '<part>' (side <r\|l>): <N> tree(s) for <M> visible hair mass(es) (mass 0 columns x=<minX>..<maxX>, y=<minY>..<maxY>; …)` — back-hair cloth trees whose count does not match the side's visible hair masses (`max(1, floor(w / (4 * grid)) + 1)` per mass) or whose link or tip leaves its mass's column range (issues #445, #461): omit `chains` on `cloth."back hair"` or draft trees per visible hair mass inside its columns; `LINT <a_limb> and <b_limb> on <part> (<from>) cross at [x, y] with no per-limb split: …` — two limbs sharing one layer cross in an X without a per-limb split (issue #446): declare `config.limbs.<covering>.split` (`{ "line": [[x, y], …] }` or `{ "edge": "painting" }`, §3 *Which limb*); `LINT foot_<s> at [x, y], the <s>_ankle joint and leg_<s>1's tip, lies on none of leg_<s>'s art within 1 px along its joint line (the nearest of it on that line is the pixel [x, y], D px from the joint): …` — an ankle keypoint off its leg's art on the line `assemble` sizes the ankle's cap by (issue #517): `propose` drafts every leg without its ankle cut, the shoe layer stays one part, and nothing is moved onto the art; move that ankle keypoint onto the leg's or the shoe's art and re-run `propose` (`(the line crosses none of it)` when no art lies on that line at all); `LINT stop on <chain> (<firstLink> at [x0, y0]) against <against> (at [ax, ay]) places <chain>_touch at [tx, ty], above <firstLink>'s root (rest R deg, \|rest\| + outward B deg): …` — a stop's contact point `<chain>_touch` lies above `links[0]`'s root in rig px or pushes `\|rest\| + outward` past `180°` (issue #447, §2 `stops`): stop against the descending limb bone below `firstLink`, set `links` to start at a link below `<chain>_touch`, or omit the stop on a rising limb; `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 — use another layer/run first; when no separate layer exists, a structural `assemble.cuts` entry separates the independently moving arms ([Source before scissors](#source-before-scissors)); `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 |
|
|
3889
3987
|
| `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 |
|
|
3890
3988
|
| `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` |
|
|
@@ -4634,6 +4732,9 @@ row in this page's `rule` and `line` tables; the selftest holds that (`TY16`).
|
|
|
4634
4732
|
| <a id="RIG_ALT_TIPS_SOURCE"></a>`RIG_ALT_TIPS_SOURCE` | a direct rig caller supplies endpoints without valid source-size normalization provenance | re-run assemble; do not guess dimensions from nominal scale |
|
|
4635
4733
|
| <a id="RIG_ALT_TIPS_MATCH_CONFIG"></a>`RIG_ALT_TIPS_MATCH_CONFIG` | tip presence for a matching declared config set, or keys or values normalized using recorded source dimensions, differs from the parts record | re-run assemble after the config change; source points are never treated as rig points |
|
|
4636
4734
|
| <a id="RIG_ALT_TIP_POINT"></a>`RIG_ALT_TIP_POINT` | a direct rig caller supplies an invalid normalized pair | re-run assemble with a finite source-crop pair |
|
|
4735
|
+
| <a id="RIG_ALT_DRAWN_MATCH_CONFIG"></a>`RIG_ALT_DRAWN_MATCH_CONFIG` | a set present in both config and parts.json has a `drawn` that differs between them (a bone added, removed or changed, or the field present on one side only); rig reads the recorded `drawn` | re-run assemble after changing or removing `drawn` |
|
|
4736
|
+
| <a id="RIG_ALT_PARTS_MATCH_CONFIG"></a>`RIG_ALT_PARTS_MATCH_CONFIG` | a set present in both config and parts.json has a config `parts` that is not the recorded `parts` without the generated hybrid band names assemble appends (order included, or the field present on one side only); rig reads the recorded `parts` | re-run assemble after changing or removing `parts` |
|
|
4737
|
+
| <a id="RIG_ALT_SET_ASSEMBLED"></a>`RIG_ALT_SET_ASSEMBLED` | a set declared in `config.assemble.alternates` has no record in parts.json (added or renamed since assemble, which writes a record for every declared set or refuses); rig builds only recorded sets. A recorded set with no config set is not refused: that is a `--alt` set | re-run assemble after adding or renaming the set |
|
|
4637
4738
|
| <a id="LAYERS_INPUT_KIND"></a>`LAYERS_INPUT_KIND` | the path is neither a directory with `layers.json`, a `layers.json`, nor a `.psd` | the `--full`/`--head` path |
|
|
4638
4739
|
| <a id="LAYERS_MANIFEST_PRESENT"></a><a id="LAYERS_MANIFEST_IS_JSON"></a><a id="LAYERS_FIELD_PRESENT"></a><a id="LAYERS_KEY_KNOWN"></a>`LAYERS_MANIFEST_PRESENT`, `LAYERS_MANIFEST_IS_JSON`, `LAYERS_FIELD_PRESENT`, `LAYERS_KEY_KNOWN` | the wrapper's `layers.json` is missing, unparsable, or not the wrapper's shape | the run's output directory; re-export it |
|
|
4639
4740
|
| <a id="LAYERS_PNG_PRESENT"></a><a id="LAYERS_PNG_UNAMBIGUOUS"></a><a id="LAYERS_PNG_DECODES"></a><a id="LAYERS_PNG_MATCHES_BBOX"></a>`LAYERS_PNG_PRESENT`, `LAYERS_PNG_UNAMBIGUOUS`, `LAYERS_PNG_DECODES`, `LAYERS_PNG_MATCHES_BBOX` | a layer's PNG is missing, found in two places, not a PNG, or not its box's size | the run's files |
|
|
@@ -4716,7 +4817,7 @@ See-through with another seed.
|
|
|
4716
4817
|
| <a id="CONFIG_SET_BASE_ANCHOR"></a>`CONFIG_SET_BASE_ANCHOR` | `assemble.alternates[i].base_anchor` is malformed or names an unknown, hidden, nonregion owner, a nonpatch pair, duplicate ownership, or an axis other than distinct `x`/`y` | declare covered region owners with explicit axes and unique covered painting-patch pairs, or omit the opt-in mode |
|
|
4717
4818
|
| <a id="PARTS_ALT_BASE_ANCHOR"></a>`PARTS_ALT_BASE_ANCHOR` | serialized alternate placement metadata is malformed or names missing/nonpatch records | re-run assemble with the current declaration |
|
|
4718
4819
|
| <a id="RIG_ALT_BASE_ANCHOR"></a>`RIG_ALT_BASE_ANCHOR` | an owner has no matching base/alternate region on the same bone, a pair has no region/frame, or direct input metadata is invalid | correct the named region/patch bindings; mesh placement is not supported by this mode |
|
|
4719
|
-
| <a id="RIG_ALT_BASE_ANCHOR_MATCH_CONFIG"></a>`RIG_ALT_BASE_ANCHOR_MATCH_CONFIG` | config and assembled parts declare different base anchors, including a removed declaration
|
|
4820
|
+
| <a id="RIG_ALT_BASE_ANCHOR_MATCH_CONFIG"></a>`RIG_ALT_BASE_ANCHOR_MATCH_CONFIG` | config and assembled parts declare different base anchors, including a removed declaration; a declared set with no assembled record is [`RIG_ALT_SET_ASSEMBLED`](#RIG_ALT_SET_ASSEMBLED) | re-run assemble after changing or removing the declaration |
|
|
4720
4821
|
| <a id="CONFIG_SET_LAYERS"></a>`CONFIG_SET_LAYERS` | `assemble.alternates[i].layers` names an unknown, uncovered or hidden part, or a painting patch with no run-layer source | name a covered layer-backed output part (a limb segment name, not its consumed source); use `author:<label>` for author art |
|
|
4721
4822
|
| <a id="CONFIG_MOTION_SEAM"></a>`CONFIG_MOTION_SEAM` | motion_seams has a malformed entry, an unknown output part, a self-pair, duplicate/reversed pair or unknown entry field | declare each unordered pair once with two resolved output part names |
|
|
4722
4823
|
| <a id="PARTS_MOTION_SEAM"></a>`PARTS_MOTION_SEAM` | parts.json motion_seams violates the same declaration shape/name/uniqueness contract | reassemble with the declared output part names |
|
|
@@ -4832,6 +4933,11 @@ those settings. Changing a config seed alone cannot change a no-config run.
|
|
|
4832
4933
|
| <a id="ASSEMBLE_TAKE_HIDDEN"></a>`ASSEMBLE_TAKE_HIDDEN` | a replaced tag has hidden companion art | independent hidden-art identity is undeclared; choose a run without that companion |
|
|
4833
4934
|
| <a id="ASSEMBLE_TAKE_UNIQUE"></a>`ASSEMBLE_TAKE_UNIQUE` | repeated donor for one target tag at runtime | declare each target run/tag once |
|
|
4834
4935
|
| <a id="CONFIG_TAKE_UNIQUE"></a>`CONFIG_TAKE_UNIQUE` | config selects the same target run/tag more than once | retain one donor per target tag |
|
|
4936
|
+
| <a id="ASSEMBLE_PART_SOURCE"></a>`ASSEMBLE_PART_SOURCE` | `assemble.editor_receipts.trace_parts` is `true` and a part's pixels are decided by a shape drawn in the config — an `assemble.cuts` (or an alternate set's `cuts`) `polygon`, `seethrough.arm_split`, a limb's `split.line`, an `assemble.patches` `box`, an `assemble.extend_painting` `band`; the object names the part and where the shape is declared, the detail the shape found (issue #947) | `config.json`: remove that entry and paint the piece as an author layer (`author:<label>`) in an image editor with its receipt in `assemble.editor_receipts.receipts`, or take the part whole from a layer of the split (a limb split may use `"edge": "painting"`) |
|
|
4937
|
+
| <a id="ASSEMBLE_EDITOR_RECEIPT_MISSING"></a>`ASSEMBLE_EDITOR_RECEIPT_MISSING` | `assemble.editor_receipts.required` (or `trace_parts`, issue #947) is `true` and an edited image the build reads — an author-made or hidden-art layer of a base or alternate run, a cut `under`, a limb `cap` — has no receipt whose `sha256` is its file's; the detail gives the path and the sha256 found (issue #946) | edit the image in an image editor and add its receipt to `config.assemble.editor_receipts.receipts`; never generate or patch it in code |
|
|
4938
|
+
| <a id="ASSEMBLE_EDITOR_RECEIPT_SHA256"></a>`ASSEMBLE_EDITOR_RECEIPT_SHA256` | a receipt's file hashes to another sha256 than the receipt records; the detail gives both | the receipt's `sha256`, or the file: it changed after the receipt was written, or the receipt names another file |
|
|
4939
|
+
| <a id="ASSEMBLE_EDITOR_RECEIPT_FILE"></a>`ASSEMBLE_EDITOR_RECEIPT_FILE` | a receipt's `file` names no file, relative to the config's directory | that receipt's `file` |
|
|
4940
|
+
| <a id="CONFIG_EDITOR_RECEIPT"></a>`CONFIG_EDITOR_RECEIPT` | a receipt's `from_sha256` equals its own `sha256` (no edit), or two receipts name one `file` | the bytes the edit started from; one receipt per file |
|
|
4835
4941
|
| <a id="PARTS_TAKEN_LAYERS"></a>`PARTS_TAKEN_LAYERS` | a taken-layer receipt is partial, unknown, duplicated, malformed or claims differing pixels | regenerate parts with assemble; never patch source receipts by hand |
|
|
4836
4942
|
| <a id="ASSEMBLE_FIELD_PRESENT"></a>`ASSEMBLE_FIELD_PRESENT` | `seethrough.head_box` is missing | `propose --head-box` proposes it (§3) |
|
|
4837
4943
|
| <a id="ASSEMBLE_RIG_SIZE"></a>`ASSEMBLE_RIG_SIZE` | `rig_scale` makes an empty rig | `assemble.rig_scale` |
|
|
@@ -4850,6 +4956,7 @@ those settings. Changing a config seed alone cannot change a no-config run.
|
|
|
4850
4956
|
| <a id="ASSEMBLE_PAINTING_EXTENSION_CONFLICT"></a>`ASSEMBLE_PAINTING_EXTENSION_CONFLICT` | two declared final parts can reach the same eligible painting pixel without explicit precedence | make their reachable claims disjoint, or declare a unique nonnegative integer `order` on every entry; lower orders claim first, never array order |
|
|
4851
4957
|
| <a id="ASSEMBLE_PAINTING_CANVAS"></a>`ASSEMBLE_PAINTING_CANVAS` | a callable extension stage receives a part outside the rig canvas | pass the final in-canvas assembled stack with its crop offsets |
|
|
4852
4958
|
| <a id="ASSEMBLE_PART_OPAQUE"></a>`ASSEMBLE_PART_OPAQUE` | a part ended with no opaque pixel | drop the entry, or take the tag from the other run |
|
|
4959
|
+
| <a id="ASSEMBLE_PINHOLES"></a>`ASSEMBLE_PINHOLES` | the requirements file declares `pinholes.required: true` and the flat parts stack holds a pinhole: an 8-connected opening (alpha 0 on every part, enclosed by the parts' alpha above 0) whose every pixel the painting shows as figure. One refusal per pinhole, largest first, naming its box, its px and the parts bordering it; nothing is written (issue #944, [Pinholes at rest](#pinholes-at-rest-issue-944)) | `assemble.plan` / `assemble.take` / the head pass: take the pinhole's pixels from a layer that holds them ([Source before scissors](#source-before-scissors)); where no layer holds them, the defect is the input's. Without the declaration the bar reports SKIP |
|
|
4853
4960
|
| <a id="ASSEMBLE_PATCH_BOX_INSIDE"></a>`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 |
|
|
4854
4961
|
| <a id="ASSEMBLE_PATCH_OPAQUE"></a>`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"` |
|
|
4855
4962
|
| <a id="ASSEMBLE_CUT_INSIDE"></a>`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 |
|
|
@@ -5192,6 +5299,9 @@ documents do not ship). Each is an input this package built; none is an author's
|
|
|
5192
5299
|
| <a id="CHECK_SETUP_MATCHES_PAINTING"></a>`CHECK_SETUP_MATCHES_PAINTING` | region, slot, failing-pixel box, slot pixelcount, complete region pixelcount and declared limit | compare the source painting beside the actual setup; follow [Source before scissors](#source-before-scissors), then rerun with the same authored limit |
|
|
5193
5300
|
| <a id="CHECK_SETUP_PAINTING_SOURCE"></a>`CHECK_SETUP_PAINTING_SOURCE` | a declaration without a source painting | supply `--source`; the assembled composite is not an independent reference |
|
|
5194
5301
|
| <a id="CHECK_SETUP_PAINTING_MEASURED"></a>`CHECK_SETUP_PAINTING_MEASURED` | missing native setup/ownership or unsupported clipping | supply observable textured setup geometry; an unavailable instrument never passes |
|
|
5302
|
+
| <a id="CHECK_BOUNDARY_COLOUR_STEP"></a>`CHECK_BOUNDARY_COLOUR_STEP` | the requirements file declares `boundary_colour.max_step_excess` and a part boundary's colour step in the rig's render exceeds the same boundary's step in the rig's own pieces by more than it; the part pair, the frame (`setup` or `idle frame <n> (t <s> s)`), the stage pixel, the excess, the declared allowance and both steps are quoted. Unlike [`CHECK_SETUP_MATCHES_PAINTING`](#CHECK_SETUP_MATCHES_PAINTING), which compares the painting per declared region at the setup pose, this compares against the rig's own flat pieces (`parts.json` placement at setup, each piece's rest colour through its art UVs on every idle frame), so a part drawn over its neighbour's line art is not a failure | the pair's rig image or mesh: `rig/images/<part>.png` (a texture stretched into its padded UVs, or a notch cut out of it), or that part's mesh in `config.json` `meshes` when the mesh cuts a corner off the art; see [Boundary colour step](#boundary-colour-step-issue-942) |
|
|
5303
|
+
| <a id="CHECK_BOUNDARY_COLOUR_UNMEASURED"></a>`CHECK_BOUNDARY_COLOUR_UNMEASURED` | `boundary_colour` is declared and the step cannot be taken: no `parts.json` or no `idle` (refused before anything is built), or a drawn slot has no `parts.json` piece, is clipped, or its rig image is not its parts PNG with one even pad on every side (the slot and frame are named). Never a pass | make every drawn slot a `parts.json` piece, or drop `boundary_colour` from the requirements file (the bar then reports SKIP) |
|
|
5304
|
+
| <a id="SETUP_PAINTING_REQUIRED_LIMIT"></a>`SETUP_PAINTING_REQUIRED_LIMIT` | `setup_painting.required` is `true` and a limit the check needs is absent: `rgb_tolerance`, `regions`, or a region's `max_pixels` (the object names the region). Refused when the requirements file is read, before anything is built (issue #943) | author the named limit; none is derived from the art. Without `required` the same region reports SKIP |
|
|
5195
5305
|
| <a id="CHECK_SOURCE_GRID"></a>`CHECK_SOURCE_GRID` | the setup-pose still and its black-tinted twin (the coverage `SETUP_POSE_VS_SOURCE` reads) did not come back on one grid over one opaque background | a rigc problem; report it |
|
|
5196
5306
|
| <a id="CHECK_RIGC_VERSION"></a>`CHECK_RIGC_VERSION` | `rigc --version` is below 1.4.0, or prints no version: its `render` has no `--geometry`, which `TEXTURE_STRETCH` reads. Refused before anything is built | `bun install` (this package depends on rig-c ^2.20.4), or put a newer `rigc` first on `PATH` |
|
|
5197
5307
|
| <a id="CHECK_RIGC_ENTRY"></a><a id="CHECK_RIGC_ENTRY_READS"></a>`CHECK_RIGC_ENTRY`, `CHECK_RIGC_ENTRY_READS` | `rigc --version` names no entry (a rigc below 2.0.0), or names one in neither launcher form (`entry: cli.ts — @esotericsoftware/spine-core <v> present`, `entry: cli_core.ts — @esotericsoftware/spine-core absent — …`); `check.json`'s `rigc_entry` records the one that gated the build | `bun install` |
|
|
@@ -5210,9 +5320,10 @@ documents do not ship). Each is an input this package built; none is an author's
|
|
|
5210
5320
|
| <a id="CHECK_STRESS_OFF_CHAIN"></a>`CHECK_STRESS_OFF_CHAIN` | a mesh from a See-through `handwear` layer that rides one chain of the idle does not turn rigidly with it: with the chain's first link turned 90°, −90° or 180° on a throwaway copy (physics left out, posed by rigc, issue #347), its worst edge reads above 1.0000 as max(ratio, 1/ratio) against the setup pose. The object names the mesh, the link and the turn; the detail quotes the edge and names each bone the mesh is weighted to off the chain, with how many vertices carry it and the largest weight. Or the turned render shows no mesh on the slot: not read is not a pass | the mesh's `segments`: drop the segment on the bone the line names (a 16 px `chest` stub at the shoulder, as `propose` drafted until issue #347), so the sleeve rides its chain alone. Measured (rigc 2.47.1): `demo`'s sleeves read 2.46 to 254.7 with the stub and 1.0000 without it, and the setup-pose seam does not move (mean 0.147, 0 px over 40, both ways) |
|
|
5211
5321
|
| <a id="CHECK_LIMB_ONE_BONE"></a>`CHECK_LIMB_ONE_BONE` | a part a `config.limbs` segment made is bound in `rig.json` to a bone besides its segment's — a mesh weighted to a second bone, or a slot on another bone — or to none (issue #349); the object names the part, its limb and its bone, the detail every other bone | `regions.<part>` the segment's bone, or a mesh whose `segments` name that bone alone: a bend is a rotation at the joint |
|
|
5212
5322
|
| <a id="CHECK_SEAM_OVER_RANGE"></a>`CHECK_SEAM_OVER_RANGE` | a limb joint with a declared `range` (issue #360), bent to one of its range's ends or its middle, fails the rest-pose seam's bar against the same pose with the joint's own art drawn under the limb — the bend uncovers the joint; the object names the joint, its limb and segment, the part, the bone and the angle. Under `cap_shape: "disc"` (issue #360, unit B) or `"ball"` (issue #598), also any pixel of the disc over 40 at a pose, under the seam's bar or not. Also a joint the line cannot read (its bone has no parent in `rig.json`, no slot shows its limb, or its cap's disc holds no opaque art), and, refused, two renders of the bent copy that rigc did not put on one grid | [Source before scissors](#source-before-scissors) first; a cap that covers the turn: the layer's art within the cap's radius on the upper side of the joint line, or a `cap` image painting what the layer lacks (`limb cap:` names the missing pixels); or a narrower `range` |
|
|
5213
|
-
| <a id="SEAM_OVER_MOTION"></a>`SEAM_OVER_MOTION` | **Existing internal-joint and explicit-seam pairs remain warnings** (issue #493; a seam that opens in motion may be intended). **Proven body-root pairs are a bar** (issue #686): an above-floor opening adds `CHECK_SEAM_OVER_MOTION`, sets `PASS` false and exits 1. Every declared neighbour pair — each limb's two segments at a joint, the upper and the one below (`parts.json`'s limb records, `config.limbs`), and under `--requirements` each `seam`'s `part` and `neighbour`, one pair however many declare it — read in every animation `motion.json` holds, the idle included, by rig-c's seam reading: one `rigc render --seam <a>:<b>,… --fps 12` over the packed build as played (physics on), whose `seam.json` (`rigc-seam/1`, rig-c's AUTHORING §8.8) names per pair and frame a **gap** (background inside the joint) or a **ghost** (one piece's overlap band swung clear of its partner), each frame's cells (`px`, one texel of the coarser drawing — a rig px on a build at scale 1), its cell size and its world box. Printed for existing warning pairs and animations with a frame over the floor: `WARN seam "<a>\|<b>" opens on N frame(s) [<frames>] max <px> px in "<animation>" — over the one-cell floor`, then how many more frames rigc named that were under it. **The floor**: a frame's gap or ghost is reported when it holds more cells than its box is long (its longer side, in cells) — thicker than one cell. A cell is drawn when the renderer paints its centre at half opacity, so an outline that moves by less than a cell flips the cells along it: a line one cell deep, joined or scattered, holds at most as many cells as its box is long, and a hole of any area more. The rule types no number; measured on `examples/wind`'s idle, rigc names 43 openings over its six limb pairs (the two elbows, 1 to 5 cells, boxes up to 17 cells long), every one one cell thick, and on rig-c's `gallery/walk` 13 joint cracks of 1 to 3 cells, all one cell thick. `check.json`'s `SEAM_OVER_MOTION` holds `status` (`WARN`, `CLEAR`, `SKIP`, or `FAIL` for a new root tear), `fps`, `floor`, `pairs`, `animations`, `warnings` and one row per pair and animation (`opens` and `px` over the floor, `max_px`, `under_floor`, rigc's `status`, `drawn` and `banded`); for existing warning pairs it moves no exit status, no `check: PASS` and no bar count — the summary ends `; N warning(s) (SEAM_OVER_MOTION, not a bar)`. **SKIP**, named: no pair declared (no limb segment, no `seam`; then `check.json` has no key), no `parts.json`, no animation, every pair naming a slot `rig.json` lacks (each printed `not read`), or rigc reading no band on any frame of any pair (rigc's own `skip`: the two drawings share no cell at rest) — nothing measured, never a pass | [Source before scissors](#source-before-scissors) first; if the opening is not intended: a cap that covers the turn (`config.limbs`, *Limbs: cut at their joints*; `limb cap:` names the pixels the layer lacks), or a motion that bends that joint less; if it is intended, nothing |
|
|
5323
|
+
| <a id="SEAM_OVER_MOTION"></a>`SEAM_OVER_MOTION` | **Existing internal-joint and explicit-seam pairs remain warnings** (issue #493; a seam that opens in motion may be intended). **Proven body-root pairs are a bar** (issue #686): an above-floor opening adds `CHECK_SEAM_OVER_MOTION`, sets `PASS` false and exits 1. **Under the requirements file's `seam_over_motion.required: true`** (issue #941) every declared pair, of all three classes, is also held to 0 background cells on every frame — [`CHECK_SEAM_OVER_MOTION_REQUIRED`](#CHECK_SEAM_OVER_MOTION_REQUIRED); undeclared, nothing in this row moves. Every declared neighbour pair — each limb's two segments at a joint, the upper and the one below (`parts.json`'s limb records, `config.limbs`), and under `--requirements` each `seam`'s `part` and `neighbour`, one pair however many declare it — read in every animation `motion.json` holds, the idle included, by rig-c's seam reading: one `rigc render --seam <a>:<b>,… --fps 12` over the packed build as played (physics on), whose `seam.json` (`rigc-seam/1`, rig-c's AUTHORING §8.8) names per pair and frame a **gap** (background inside the joint) or a **ghost** (one piece's overlap band swung clear of its partner), each frame's cells (`px`, one texel of the coarser drawing — a rig px on a build at scale 1), its cell size and its world box. Printed for existing warning pairs and animations with a frame over the floor: `WARN seam "<a>\|<b>" opens on N frame(s) [<frames>] max <px> px in "<animation>" — over the one-cell floor`, then how many more frames rigc named that were under it. **The floor**: a frame's gap or ghost is reported when it holds more cells than its box is long (its longer side, in cells) — thicker than one cell. A cell is drawn when the renderer paints its centre at half opacity, so an outline that moves by less than a cell flips the cells along it: a line one cell deep, joined or scattered, holds at most as many cells as its box is long, and a hole of any area more. The rule types no number; measured on `examples/wind`'s idle, rigc names 43 openings over its six limb pairs (the two elbows, 1 to 5 cells, boxes up to 17 cells long), every one one cell thick, and on rig-c's `gallery/walk` 13 joint cracks of 1 to 3 cells, all one cell thick. `check.json`'s `SEAM_OVER_MOTION` holds `status` (`WARN`, `CLEAR`, `SKIP`, or `FAIL` for a new root tear), `fps`, `floor`, `pairs`, `animations`, `warnings` and one row per pair and animation (`opens` and `px` over the floor, `max_px`, `under_floor`, rigc's `status`, `drawn` and `banded`); for existing warning pairs it moves no exit status, no `check: PASS` and no bar count — the summary ends `; N warning(s) (SEAM_OVER_MOTION, not a bar)`. **SKIP**, named: no pair declared (no limb segment, no `seam`; then `check.json` has no key), no `parts.json`, no animation, every pair naming a slot `rig.json` lacks (each printed `not read`), or rigc reading no band on any frame of any pair (rigc's own `skip`: the two drawings share no cell at rest) — nothing measured, never a pass | [Source before scissors](#source-before-scissors) first; if the opening is not intended: a cap that covers the turn (`config.limbs`, *Limbs: cut at their joints*; `limb cap:` names the pixels the layer lacks), or a motion that bends that joint less; if it is intended, nothing |
|
|
5214
5324
|
| <a id="CHECK_MOTION_SEAM_ROOT"></a>`CHECK_MOTION_SEAM_ROOT` | a body-root declaration has missing or ambiguous segment0, bone, parent, slot or default-skin attachment; the refusal names the part and candidates | supply the declared one-to-one hierarchy/slot/provenance binding; no nearest or mesh-influence fallback |
|
|
5215
5325
|
| <a id="CHECK_SEAM_OVER_MOTION"></a>`CHECK_SEAM_OVER_MOTION` | a proven body-root pair has an active animation gapOpen plus UNDER-owned ghost above the unchanged one-cell floor; the refusal names pair, animation, frame, cells, cell size and world box | [Source before scissors](#source-before-scissors) first; repair the declared rest overlap or authored motion; the existing internal-joint warning policy and physics reference worlds are unchanged |
|
|
5326
|
+
| <a id="CHECK_SEAM_OVER_MOTION_REQUIRED"></a>`CHECK_SEAM_OVER_MOTION_REQUIRED` | the requirements file declares `seam_over_motion.required: true` (issue #941; [Background over every frame](#background-over-every-frame-issue-941)) and a declared seam pair — body-root, internal-joint or explicit — shows background on a frame: rigc's `gapOpen` (a gap cell inside the carried setup-overlap band where the whole skeleton composite has alpha 0) above 0 on any frame of any animation `motion.json` holds, every frame at 12 fps, no floor. The refusal names the class, the pair, the animation, the worst frame and its time, the frames that open, the cells found, the cell size and the world box, against 0 required. Also refused under the declaration, since a SKIP is never a pass: `SEAM_OVER_MOTION` itself a SKIP (no pair or no animation to read), a pair naming a slot `rig.json` lacks, or a pair and animation rigc read no band in | the art or the motion, by the file the refusal points at: [Source before scissors](#source-before-scissors) first — the layer that holds the pixels under the joint (`config.assemble`, `extend_painting`); then the joint's cap (`config.limbs`, *Limbs: cut at their joints*), or the keys that pull the pieces apart (`motion.json` through `config.motion` or the animations file `animations_from` names). For a SKIP, declare the pairs (`config.limbs`, `config.motion_seams`, a `--requirements` `seam`) or drop the declaration from the requirements file |
|
|
5216
5327
|
| <a id="CLOTH_SETTLES"></a>`CLOTH_SETTLES` | **a warning, never a refusal** (issue #501; the owner's ruling: cloth at rest while the character stands may be intended). Every physics chain, read by its tip on the idle `check` renders — `rigc render --animation idle --fps 12 --geometry`, physics on, rigc's default environment. A chain is a **physics leaf** — a bone a physics constraint names (`rig.json`'s `constraints` or `motion.json`'s `physics`) with no such bone under it — and the run of physics bones above it; its tip is the leaf's end (origin plus `length` along its world x axis), or its origin when the leaf has no length; its speed between frames is the tip's distance over their time, from the idle's `geometry.json`. A chain **settles** at the first frame from which every speed to the idle's last frame is under the floor, and each one that does — before the loop's length is out — prints `WARN chain "<root>" .. "<tip>" (N physics link(s)) settles at <t> s of the <d> s idle — its tip's end under 0.01694 rig px/s from there to the last frame (fastest on the idle <v> rig px/s)`. **The floor**, 0.01694 rig px/s, is derived, not chosen: half the slowest tip an idle key drives on the public examples (the repository's floor rule, half the weaker figure) — measured with this port's build of each (rig-c 2.60.0), over the 31 chains a keyed bone at or above the leaf drives (`demo` 12, `reference-sample` 1, `scarf` 11, `wind` 7) the slowest frame-to-frame tip speed is `scarf`'s `skirt_l2`, 0.03388 rig px/s; the ten no key drives — `wind`'s skirt, eight trees and the swing, hung from a `hip` its idle never keys, and `reference-sample`'s `hip_jiggle` — fall under it from 1.5 to 2.75 s on a 3.333 s idle (`hip_jiggle` at 0 s: it never moves) and end at 0.0025 rig px/s or less. `check.json`'s `CLOTH_SETTLES` holds `status` (`WARN`, `CLEAR`), `fps`, `frames`, `duration`, `floor_rig_px_per_s`, `floor`, `levers`, `warnings` and one row per chain (`tip`, `root`, `links`, `point` — `end` or `origin` — `settles_at`, null when it does not, `max_rig_px_per_s`, `min_rig_px_per_s`); it moves no exit status, no `check: PASS` and no bar count — the summary's warning clause names it, `; N warning(s) (CLOTH_SETTLES, not a bar)` (with `SEAM_OVER_MOTION` before it, and N their sum, where that read a pair). It reads the idle alone, one loop from rigc's physics reset at frame 0: a chain that settles later than the idle is long is not seen. **SKIP**, named: no physics constraint (then `check.json` has no key), or no idle — nothing measured, never a pass | if the chain should move while the character stands, one of the consumer's two levers: an environment at render (`rigc render --environment`, wind and gravity in time — rig-c's AUTHORING §8.4), or a consumer's animation that moves the bone the chain hangs from. The drafted idle keys no hip, by measurement: a hip turn at the idle's period fails a bar on all five public examples (issue #502, §3 *What moves them*). A `cloth` recipe's skirt hangs from `hip`, which the drafted idle never keys (§3, *Recipe: cloth and hair that answer the world*, *What moves them*, which has a worked schedule for `wind`). If it is intended, nothing |
|
|
5217
5328
|
| <a id="figure_openings"></a>`figure_openings` | **a warning, never a refusal** (issue #933; see [Openings inside the figure on undeclared layer pairs](#openings-inside-the-figure-on-undeclared-layer-pairs-issue-933)). Reads transparent openings inside the pair's own posed hole-filled union (`alpha == 0` inside `fillHoles(a ∪ b)`, excluding cells already transparent at setup) across every undeclared setup-overlapping (`alpha >= 128`) layer pair in `rig.json` draw order in every animation `motion.json` holds at 12 fps (`scale = 1 / cell`). Prints `figure_openings: CLEAR — N undeclared setup-overlapping pair(s) in M animation(s) (<names>) at 12 fps (cell <cell> world units): 0 warning(s), never a bar` when clean, `figure_openings: WARN — …` followed by one `WARN opening "<a>\|<b>" above "<above>" (under "<under>") on K frame(s) [<frames>], worst frame <f> (t = <t>s) in "<animation>": found <found_cells> cell(s), <= <allowed_cells> cell(s) allowed in <w>x<h> world units at <cell> world units/cell — transparent opening inside the figure over motion on an undeclared layer pair (not a bar)` line per opening above the one-cell floor (`found_cells > allowed_cells`), or `figure_openings: SKIP — <reason>` when there is no `parts.json`, no animation, or no undeclared setup-overlapping pair. `check.json`'s `figure_openings` is written whenever the reader ran (`status` `"CLEAR"` or `"WARN"`), holding `status`, `fps`, `cell`, `pairs`, `animations`, `warnings` and `openings` (`pair`, `under`, `above`, `animation`, `frame`, `time`, `opens`, `cell`, `found_cells`, `allowed_cells`, `box`); it moves no exit status, no `check: PASS` and no bar count | if the opening is an unintended tear where a lower layer lacks opaque art under an opaque upper layer, use `assemble.extend_painting` (not `assemble.keep_under_occluders`, which only preserves existing `alpha >= 250` lower pixels under a translucent cover), or adjust mesh weights / motion keys; if it is a deliberate silhouette loop shifting in world space, nothing |
|
|
5218
5329
|
| <a id="CHECK_FOLDS_READ"></a>`CHECK_FOLDS_READ` | the fold render's `frames.json` names no set for an animation, a `folds.json` is missing, not `rigc-folds/1`, another animation's or another rate's, or a folding mesh has no line rigc printed for it | a rigc problem; report it, with `rigc --version` |
|
|
@@ -5239,7 +5350,7 @@ documents do not ship). Each is an input this package built; none is an author's
|
|
|
5239
5350
|
| <a id="BUILD_REQUIREMENTS_CONFLICT"></a>`BUILD_REQUIREMENTS_CONFLICT` | `build --requirements <file>` names a file with other bytes than the one `config.requirements` declares (issue #556): the detail names both, each resolved (the flag against the working directory, the declaration against the config's) with its sha256 — or `no such file`. Refused before assemble writes anything. The same file by both, or a copy of its bytes, is not refused: the declaration is read and recorded | drop the flag to build from the config's file, or point `config.requirements` at the flag's |
|
|
5240
5351
|
| <a id="BUILD_STAGE_REQUEST"></a>`BUILD_STAGE_REQUEST` | an internal build worker was given a missing or malformed request, an unknown stage, or incomplete runner, input, output or scratch paths; the worker refuses before running a stage. This is the internal process protocol, not a public command or option | invoke `rig-parts build` with its documented flags; if that command produces this refusal, report the named request defect rather than constructing or modifying a worker request |
|
|
5241
5352
|
| <a id="REQUIREMENTS_FILE"></a>`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 |
|
|
5242
|
-
| <a id="REQUIREMENTS_FIELD"></a>`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`, `rises` and `rests` when given (each a non-empty list of part names, each once; no part both in `not_swinging` and in `rises` or `rests`), and `may_fold` when given (a non-empty list of `{ "slot", "animation", "why" }`, every one a non-blank string, each pair once; issue #320). `note`, `*_note` (strings) and `x-…` (any value) are read by nothing, as in the config | write the field; the bar is yours |
|
|
5353
|
+
| <a id="REQUIREMENTS_FIELD"></a>`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`, `rises` and `rests` when given (each a non-empty list of part names, each once; no part both in `not_swinging` and in `rises` or `rests`), and `may_fold` when given (a non-empty list of `{ "slot", "animation", "why" }`, every one a non-blank string, each pair once; issue #320), and `pinholes` when given (an object whose one field, `required`, is `true` or `false`; issue #944), `seam_over_motion` when given (the same shape; issue #941), and `boundary_colour` when given (an object whose one field, `max_step_excess`, is an integer from 0 through 255; issue #942). `note`, `*_note` (strings) and `x-…` (any value) are read by nothing, as in the config | write the field; the bar is yours |
|
|
5243
5354
|
| <a id="REQUIREMENTS_RESOLVES"></a>`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`, `rises` or `rests` 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 |
|
|
5244
5355
|
| <a id="REQUIREMENTS_CONSTRAINT_DRIVES"></a>`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 |
|
|
5245
5356
|
| <a id="REQUIREMENTS_TARGET_PARENT"></a>`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 |
|
|
@@ -5847,7 +5958,8 @@ Under `--source <painting.png>`, a second reported line, also never a FAIL
|
|
|
5847
5958
|
| line | reports | bar | `reference-demo` | `reference-sample` | SKIP when |
|
|
5848
5959
|
| --- | --- | --- | --- | --- | --- |
|
|
5849
5960
|
| <a id="SETUP_POSE_VS_SOURCE"></a>`SETUP_POSE_VS_SOURCE` | the setup pose against the painting, by `assemble`'s own `measureRecomposite` over the stage's pixels — `mean_abs` and `within_share` (mean channel, within 8), `error_px` (max channel over 40), `uncovered_error_px` (of those, where the pose has alpha 128 or less), `hole_count` and the largest hole's box — with `painting_px`, `stage_px`, `render_px` and `render_scale` beside them. The painting is resampled onto the stage as assemble resamples it (lanczos3, alpha dropped). rigc renders onto an opaque grey, so the pose over white and its alpha are read off a second still of the same pose with every slot tinted black; both are carried onto the stage with the seam's map turned round (bilinear) | none — status `REPORTED`; assemble applies no bar to the figures it measures, so none is derived here | 3.146 / 93.43 % / 12,399 / 1,732, 518 holes, the largest 61 px at 351,157 6x20 (assemble's recomposite: 2.391 / 95.81 % / 11,050 / 1,564, 453) | 1.095 / 97.30 % / 5,348 / 1,239, 313 holes, the largest 88 px at 479,416 6x29 (assemble's: 0.835 / 97.95 % / 4,512 / 1,185, 259) | not written without `--source` |
|
|
5850
|
-
| <a id="SETUP_MATCHES_PAINTING"></a>`SETUP_MATCHES_PAINTING` | declared region count, native box, limit and actual top contributor rows | PASS/FAIL against authored limits; no limit is SKIP
|
|
5961
|
+
| <a id="SETUP_MATCHES_PAINTING"></a>`SETUP_MATCHES_PAINTING` | declared region count, native box, limit and actual top contributor rows | PASS/FAIL against authored limits; no limit is SKIP, or the refusal `SETUP_PAINTING_REQUIRED_LIMIT` under `setup_painting.required: true`; with `--source`, no declaration is SKIP with its reason and declaration remedy; undeclared regions are not judged |
|
|
5962
|
+
| <a id="BOUNDARY_COLOUR_STEP"></a>`BOUNDARY_COLOUR_STEP` | the worst colour-step excess over the rig's own pieces at any part boundary of the setup and every idle frame, its part pair, frame and stage pixel, and the declared `max_step_excess` | PASS/FAIL against the declared allowance (FAIL is [`CHECK_BOUNDARY_COLOUR_STEP`](#CHECK_BOUNDARY_COLOUR_STEP), an unreadable piece [`CHECK_BOUNDARY_COLOUR_UNMEASURED`](#CHECK_BOUNDARY_COLOUR_UNMEASURED)); no `boundary_colour` in the requirements file is SKIP with its reason and the declaration to add, never a pass |
|
|
5851
5963
|
|
|
5852
5964
|
The figures are assemble's by construction, not a second definition beside them;
|
|
5853
5965
|
what differs is what they are taken of — the pose rigc draws, not the flat stack —
|
|
@@ -6414,6 +6526,18 @@ maximum absolute RGB-channel difference is **strictly greater** than that
|
|
|
6414
6526
|
value. The region passes when its total count is at most its authored
|
|
6415
6527
|
nonnegative integer `max_pixels`. Without that limit the region reports
|
|
6416
6528
|
**SKIP**, including a zero-count region. An undeclared region has no verdict.
|
|
6529
|
+
|
|
6530
|
+
An author for whom a SKIP here is not an acceptable outcome declares it:
|
|
6531
|
+
`"required": true` inside `setup_painting` (issue #943). The field states the
|
|
6532
|
+
requirement, not a kind of rig. With it, every limit must be authored: an
|
|
6533
|
+
absent `rgb_tolerance`, an absent `regions`, or a region without `max_pixels`
|
|
6534
|
+
is refused by name as
|
|
6535
|
+
[`SETUP_PAINTING_REQUIRED_LIMIT`](#SETUP_PAINTING_REQUIRED_LIMIT) when the file
|
|
6536
|
+
is read, naming the field (and the region), before anything is built — the
|
|
6537
|
+
region SKIP above becomes that refusal. A declaration with every limit
|
|
6538
|
+
measures exactly as it does without the field. `required` takes only `true`
|
|
6539
|
+
or `false`; `false` is the same as omitting it, and any other value is
|
|
6540
|
+
`REQUIREMENTS_FIELD`. Without the field, nothing in this section changes.
|
|
6417
6541
|
With `--source` but no `setup_painting` declaration, `SETUP_MATCHES_PAINTING`
|
|
6418
6542
|
is explicitly **SKIP** in the console, `check.json`, the report's skipped-bar
|
|
6419
6543
|
list and `check --json`. Its reason says the comparison was not measured and
|
|
@@ -6456,12 +6580,173 @@ material owner or the cause of the difference. Overlapping region declarations
|
|
|
6456
6580
|
are measured independently. Clipped drawings whose ownership cannot be observed
|
|
6457
6581
|
are refused rather than attributed by a part's bounding box.
|
|
6458
6582
|
|
|
6459
|
-
Invalid limits, repeated names, unknown fields and out-of-stage boxes use the existing `REQUIREMENTS_FIELD` refusal; no value is guessed.
|
|
6583
|
+
Invalid limits, repeated names, unknown fields, a non-boolean `required` and out-of-stage boxes use the existing `REQUIREMENTS_FIELD` refusal; no value is guessed.
|
|
6460
6584
|
|
|
6461
6585
|
This optional declaration is independent of motion requirements and
|
|
6462
6586
|
`fixed_skeleton`; it does not replace their schemas or validation. The named
|
|
6463
6587
|
refusals are in the check/build rule table and the observation in the line table.
|
|
6464
6588
|
|
|
6589
|
+
### Pinholes at rest (issue #944)
|
|
6590
|
+
|
|
6591
|
+
A **pinhole** is an 8-connected opening of `assemble`'s flat parts stack — a
|
|
6592
|
+
pixel no part has any alpha at (alpha 0 on every part) that the union of the
|
|
6593
|
+
parts' alpha above 0, its holes filled, encloses — of which every pixel the
|
|
6594
|
+
painting shows as figure: the rig-sized painting differs from the median colour
|
|
6595
|
+
of its outer 8-pixel ring by more than 40, max channel (`figureSilhouette`'s own
|
|
6596
|
+
test, without its opening and its hole fill; `measurePinholes` in
|
|
6597
|
+
`src/assemble.ts`). An opening with one pixel the painting shows as ground —
|
|
6598
|
+
between arm and body, between the legs, inside a hair loop — is a deliberate gap
|
|
6599
|
+
and not a pinhole. There is no size threshold: one pixel is a pinhole, and the
|
|
6600
|
+
bar is 0. Every opening the setup pose shows on the public examples is one the
|
|
6601
|
+
flat parts stack already has, so the bar sits at `assemble`.
|
|
6602
|
+
|
|
6603
|
+
An author declares the bar in the `spine-parts-requirements/1` file `assemble
|
|
6604
|
+
--requirements` reads (`build` reads it as `--requirements` or
|
|
6605
|
+
`config.requirements`, and forwards the same file to `check`):
|
|
6606
|
+
|
|
6607
|
+
```json
|
|
6608
|
+
{ "spec": "spine-parts-requirements/1", "fps": 12, "requirements": [], "pinholes": { "required": true } }
|
|
6609
|
+
```
|
|
6610
|
+
|
|
6611
|
+
With `required: true`, `assemble` measures the base set's flat parts stack
|
|
6612
|
+
before it writes anything and refuses every pinhole by name,
|
|
6613
|
+
[`ASSEMBLE_PINHOLES`](#ASSEMBLE_PINHOLES): `pinhole 1 of 4 at 481,437 2x6 — is
|
|
6614
|
+
6 px between "sleeves" 11 px, "topwear" 9 px, "bottomwear" 3 px — …`. With none
|
|
6615
|
+
it prints `ASSEMBLE_PINHOLES: PASS — 0 pinholes in the base set's flat parts
|
|
6616
|
+
stack, bar 0` (alternate sets are not measured, and the line names them).
|
|
6617
|
+
Without the block, or with `required: false`, nothing is measured and the line
|
|
6618
|
+
is `ASSEMBLE_PINHOLES: SKIP — not measured, not a pass`, with the declaration
|
|
6619
|
+
remedy; exit codes do not move. `required` is the block's one field and takes
|
|
6620
|
+
only `true` or `false`; any other shape or field is `REQUIREMENTS_FIELD`. `check`
|
|
6621
|
+
reads the block and measures nothing from it.
|
|
6622
|
+
|
|
6623
|
+
The public examples, measured on `bun run fetch-examples` inputs (selftest
|
|
6624
|
+
`AE04` holds both columns to the measurement):
|
|
6625
|
+
|
|
6626
|
+
| example | declared | pinholes (box, px, bordering parts) |
|
|
6627
|
+
| --- | --- | --- |
|
|
6628
|
+
| `demo` | yes, `examples/demo/requirements.json` | 0 |
|
|
6629
|
+
| `scarf` | yes, `examples/scarf/requirements.json` | 0 |
|
|
6630
|
+
| `reference-demo` | no | `334,72 1x1` (1 px, `hairpin`) |
|
|
6631
|
+
| `reference-sample` | no | `481,437 2x6` (6 px, `sleeves`, `topwear`, `bottomwear`), `482,432 1x4` (4 px, `sleeves`, `topwear`), `519,270 2x2` (3 px, `hair_back`, `sleeves`), `514,267 2x1` (2 px, `hair_back`, `sleeves`) |
|
|
6632
|
+
| `wind` | no | `392,245 1x1` (1 px, `neck`, `topwear`) |
|
|
6633
|
+
|
|
6634
|
+
The three undeclared examples keep their pinholes: no move on the source ladder
|
|
6635
|
+
closes them without reshaping the art, and the two `reference-sample` pinholes
|
|
6636
|
+
on the seam line between sleeve and pinafore lie on no layer of either run — only
|
|
6637
|
+
the painting holds those pixels
|
|
6638
|
+
([#956](https://github.com/rig-c/parts/issues/956),
|
|
6639
|
+
[#953](https://github.com/rig-c/parts/issues/953)). Each example's README names
|
|
6640
|
+
its own. `demo` and `scarf` read their declaration from a file `config.json` does
|
|
6641
|
+
not name: as `config.requirements` it would reach `check` too, and `check.json`
|
|
6642
|
+
would gain a requirements block its `expected/` copy does not hold.
|
|
6643
|
+
|
|
6644
|
+
### Background over every frame (issue #941)
|
|
6645
|
+
|
|
6646
|
+
`SEAM_OVER_MOTION` reads every declared seam pair in every animation
|
|
6647
|
+
`motion.json` holds, the idle included: one `rigc render --seam` at 12 fps,
|
|
6648
|
+
which poses frame `i` at `i / 12` s for every `i` from 0 to
|
|
6649
|
+
`round(duration × 12)` — every frame, stride 1, not a sample. What it does with
|
|
6650
|
+
a pair depends on its class. A **body-root** pair (a limb's segment 0 against
|
|
6651
|
+
the body drawing its bone hangs from, proven from hierarchy, slots and
|
|
6652
|
+
provenance) is a bar; an **internal-joint** pair (a limb's two segments at a
|
|
6653
|
+
joint, `config.limbs`) and an **explicit** pair (a `--requirements` `seam`, or
|
|
6654
|
+
`config.motion_seams`) only warn, and only above the one-cell floor (the
|
|
6655
|
+
owner's ruling: a seam that opens in motion may be intended).
|
|
6656
|
+
|
|
6657
|
+
An author who intends none to open declares it in the
|
|
6658
|
+
`spine-parts-requirements/1` file `check --requirements` reads (`build` reads it
|
|
6659
|
+
as `--requirements` or `config.requirements` and forwards it):
|
|
6660
|
+
|
|
6661
|
+
```json
|
|
6662
|
+
{ "spec": "spine-parts-requirements/1", "fps": 12, "requirements": [], "seam_over_motion": { "required": true } }
|
|
6663
|
+
```
|
|
6664
|
+
|
|
6665
|
+
With `required: true`, every declared pair of all three classes is held to **0
|
|
6666
|
+
background cells on every frame it reads**. Background is rigc's `gapOpen`: a
|
|
6667
|
+
gap cell — inside the setup-overlap band each piece carries, drawn by neither —
|
|
6668
|
+
where the whole skeleton composite has alpha 0, so the page shows through. A gap
|
|
6669
|
+
another drawing covers (`gapCovered`) is not background, and a ghost (one
|
|
6670
|
+
piece's band swung clear of its partner) is art, not background; neither
|
|
6671
|
+
counts. There is no floor: one cell on one frame is a refusal,
|
|
6672
|
+
[`CHECK_SEAM_OVER_MOTION_REQUIRED`](#CHECK_SEAM_OVER_MOTION_REQUIRED), naming
|
|
6673
|
+
the class, the pair, the animation, the worst frame and its time, and the cells
|
|
6674
|
+
found against 0 required:
|
|
6675
|
+
|
|
6676
|
+
```text
|
|
6677
|
+
FAIL CHECK_SEAM_OVER_MOTION_REQUIRED: internal-joint seam "upperarm_r|forearm_r" in "tear" at frame 7 (t = 0.5833333333333333s) — background on 1 of 13 frame(s) read [7]: found … px (rigc gapOpen cells of … world units, world box […]) on the worst frame, 0 required; … declares seam_over_motion.required: true, …
|
|
6678
|
+
```
|
|
6679
|
+
|
|
6680
|
+
Check exits 1, `check.json`'s `PASS` is false, and `build` stops at check
|
|
6681
|
+
without an artifact. A line that measured nothing is refused too, since a SKIP
|
|
6682
|
+
is never a pass: no pair declared, no animation, a pair naming a slot `rig.json`
|
|
6683
|
+
lacks, or a pair and animation rigc read no band in. A frame set that is not
|
|
6684
|
+
every frame (a stride, `written` short of `sampled`) or a pair without `gapOpen`
|
|
6685
|
+
is `FRAMES_SIDECAR`, never zero. When nothing opens the console prints
|
|
6686
|
+
`SEAM_OVER_MOTION required: PASS — N declared pair(s) (… body-root, …
|
|
6687
|
+
internal-joint, … explicit), M pair-frame(s) read, every frame of every
|
|
6688
|
+
animation: 0 background px on every frame`, and `check.json`'s
|
|
6689
|
+
`SEAM_OVER_MOTION` gains `required`: `status`, `mode` (`gap-open`), `bar_px`
|
|
6690
|
+
(0), `pairs` (each `pair`, `class`, `frames`, `background_frames`, `max_px`)
|
|
6691
|
+
and `failed`; the bar counts once among the bars measured. Without the block,
|
|
6692
|
+
or with `required: false`, nothing here is read and no exit code, line or byte
|
|
6693
|
+
moves; the warnings and the body-root bar above are unchanged either way.
|
|
6694
|
+
|
|
6695
|
+
**What it does not read.** Frames are the 12 fps grid: an opening that lives
|
|
6696
|
+
between two ticks is not seen, and neither is a pair nobody declared — those
|
|
6697
|
+
are [`figure_openings`](#figure_openings)'s, which still warns. A frame on which
|
|
6698
|
+
one of the two pieces draws nothing is not read (rigc's `drawn`).
|
|
6699
|
+
|
|
6700
|
+
The public examples: `wind` is the one that declares pairs — its four body roots
|
|
6701
|
+
and six limb joints, 41 idle frames each — and holds 0 background cells on all
|
|
6702
|
+
of them (selftest `CH913` re-reads its build through this bar); `demo`,
|
|
6703
|
+
`reference-demo`, `reference-sample` and `scarf` declare no pair, so the
|
|
6704
|
+
declaration on them would be the SKIP refusal. No example declares it, so none
|
|
6705
|
+
of their `expected/` moves.
|
|
6706
|
+
|
|
6707
|
+
### Boundary colour step (issue #942)
|
|
6708
|
+
|
|
6709
|
+
`BOUNDARY_COLOUR_STEP` holds the colour step across every part boundary to the
|
|
6710
|
+
rig's own pieces, on the setup pose and every frame of the idle, drawn by rig-c's
|
|
6711
|
+
renderer (`@rig-c/core/render-shared`) on the gated build. A boundary pair is
|
|
6712
|
+
two 4-adjacent stage pixels drawn by different pieces; its step is the largest
|
|
6713
|
+
RGB difference across it, over white. The reference is never the painting — a
|
|
6714
|
+
part drawn on top legitimately shows its own colour over its neighbour's line
|
|
6715
|
+
art:
|
|
6716
|
+
|
|
6717
|
+
- the setup pose against each piece's `parts/<name>.png` drawn flat at its
|
|
6718
|
+
`parts.json` rectangle, through the same rasteriser (the layer stack
|
|
6719
|
+
recomposited);
|
|
6720
|
+
- each idle frame against the same posed frame with every piece re-seated on
|
|
6721
|
+
its own parts PNG through its own art UVs — the boundary's colours at rest,
|
|
6722
|
+
tracked through the pieces' own UVs.
|
|
6723
|
+
|
|
6724
|
+
The reading is the excess of the render's step over the reference's,
|
|
6725
|
+
`max(0, s_render - s_reference)`, and the bar is its largest value over every
|
|
6726
|
+
pair of every frame. A texture stretched into its padded UVs and a notch cut out
|
|
6727
|
+
of a piece both raise it; a correct overlap does not, because both sides draw the
|
|
6728
|
+
top piece. The author declares the allowance; no number is built in:
|
|
6729
|
+
|
|
6730
|
+
```json
|
|
6731
|
+
{ "spec": "spine-parts-requirements/1", "fps": 12, "requirements": [], "boundary_colour": { "max_step_excess": 12 } }
|
|
6732
|
+
```
|
|
6733
|
+
|
|
6734
|
+
Declared, `check` measures it after rigc's gate, writes the
|
|
6735
|
+
`BOUNDARY_COLOUR_STEP` block to `check.json` and refuses every part pair over the
|
|
6736
|
+
allowance by name, [`CHECK_BOUNDARY_COLOUR_STEP`](#CHECK_BOUNDARY_COLOUR_STEP):
|
|
6737
|
+
`part pair "back|front" — setup, stage pixel (24, 38): step excess 160 >
|
|
6738
|
+
declared max_step_excess 8 …`. A reading that cannot be taken is
|
|
6739
|
+
[`CHECK_BOUNDARY_COLOUR_UNMEASURED`](#CHECK_BOUNDARY_COLOUR_UNMEASURED), never a
|
|
6740
|
+
pass. Undeclared, nothing is measured or written and the console says
|
|
6741
|
+
`BOUNDARY_COLOUR_STEP: SKIP — not measured, not a pass`, with the declaration
|
|
6742
|
+
remedy; exit codes and `check.json` do not move. Alternate sets are not measured.
|
|
6743
|
+
|
|
6744
|
+
The public examples read, clean: `demo` 1, `scarf` 1, `reference-sample` 3,
|
|
6745
|
+
`wind` 4, `reference-demo` 4; the stretch and notch plants read 21 and up on
|
|
6746
|
+
`demo` and `scarf`. Each example declares 12 in its `requirements.json`, a file
|
|
6747
|
+
`config.json` does not name (as for `pinholes`), and the chain suite holds each
|
|
6748
|
+
to it (`CH942`); the suite `boundary-colour` holds the plants.
|
|
6749
|
+
|
|
6465
6750
|
## 8. What one character costs
|
|
6466
6751
|
|
|
6467
6752
|
| step | reference-sample | reference-demo | source |
|