@rig-c/parts 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -3
- package/cli.ts +191 -24
- package/docs/AUTHORING.md +381 -21
- package/docs/MIGRATION.md +52 -0
- package/docs/PAINTING.md +172 -0
- package/docs/STABILITY.md +14 -9
- package/package.json +2 -1
- package/skills/rig-parts/SKILL.md +31 -2
- package/src/assemble.ts +642 -41
- package/src/autoamplitude.ts +23 -3
- package/src/autoenvelope.ts +17 -5
- package/src/build.ts +109 -13
- package/src/check.ts +632 -11
- package/src/config.ts +185 -8
- package/src/inputs.ts +11 -1
- package/src/layers.ts +231 -18
- package/src/limbs.ts +188 -0
- package/src/paintcheck.ts +250 -0
- package/src/parts.ts +167 -6
- package/src/propose.ts +148 -108
- package/src/rig.ts +43 -8
- package/src/tags.ts +47 -0
package/docs/AUTHORING.md
CHANGED
|
@@ -93,15 +93,16 @@ read by no CPU stage).
|
|
|
93
93
|
| `key` | every stage's report | authored; the examples use the directory name |
|
|
94
94
|
| `generation.*` | the optional `comfy` adapter only; no CPU stage | recorded: checkpoint, LoRAs, sampler, prompt parts, `latent`, `seed`, `control` |
|
|
95
95
|
| `generation.control.skeleton` | `comfy paint` — the OpenPose control image, drawn at `latent` | **authored**: a built-in standing pose, `"stand_sides"` or `"stand_clasp"`, or (issue #198) any other string, read as a path relative to the config file's directory to a pose file `{"width": W, "height": H, "points": {"nose": [x, y], "neck": [x, y], …}}` — `width` x `height` is the canvas and must be `generation.latent` (the points are not scaled; `CONFIG_POSE_CANVAS`), and `points` holds the 18 body-18 keypoints of `src/skeleton.ts` by name (`nose`, `neck`, `r_shoulder`, `r_elbow`, `r_wrist`, `l_shoulder`, `l_elbow`, `l_wrist`, `r_hip`, `r_knee`, `r_ankle`, `l_hip`, `l_knee`, `l_ankle`, `r_eye`, `l_eye`, `r_ear`, `l_ear`; `r`/`l` the subject's sides), each `[x, y]` in canvas px, x right, y down, `0 <= x <= W`, `0 <= y <= H`. The image is drawn from the file exactly as from a built-in (same renderer, same size), written as `control_pose-<first 12 hex of its sha256>.png`. A pose file carries no words, so `generation.pose` is required beside it (`CONFIG_FIELD_PRESENT`). The file is read wherever the config is read from a path, every problem at once: no such file — a misspelt built-in lands here too — or one that cannot be read (`CONFIG_FILE_PRESENT`), not JSON (`CONFIG_IS_JSON`), not an object, a size that is not a whole positive px count, a point that is not two numbers (`CONFIG_FIELD_TYPE`), a key besides the three (`CONFIG_KEY_KNOWN`) or written twice (`CONFIG_KEY_UNIQUE`), `points` holding other than the 18 names (`CONFIG_POSE_KEYPOINTS`, with the count found and required), a point off the canvas (`CONFIG_POSE_IN_CANVAS`, naming the point and the canvas size) |
|
|
96
|
-
| `seethrough.resolution` | assemble — each run's canvas must be `resolution` square (`ASSEMBLE_RUN_CANVAS`) | recorded from the See-through runs; 1024 in both examples |
|
|
96
|
+
| `seethrough.resolution` | assemble — each run's canvas must be `resolution` square (`ASSEMBLE_RUN_CANVAS`); with one run (no `--head`, issue #366) the file may be smaller on either side, and is padded to the `resolution` square, centred | recorded from the See-through runs; 1024 in both examples. With one run it is the square the file is read on: at least the file's longer side (`ASSEMBLE_RUN_CANVAS` otherwise), and nothing a part records depends on how much larger |
|
|
97
97
|
| `seethrough.steps`, `.seed`, `.offload` | no CPU stage | recorded from the runs |
|
|
98
|
-
| `seethrough.head_box` | inputs (cuts the head image there; optional at the first call); assemble — where the head run sits on the painting | **proposed** by `propose --head-box` from the full run, square, source pixels, shifted inside the painting when needed; the head run must then be fed exactly that crop |
|
|
99
|
-
| `assemble.rig_scale` | assemble | authored; rig pixels per source pixel. 0.5 in both examples (a 1664x2432 painting makes an 832x1216 rig) |
|
|
98
|
+
| `seethrough.head_box` | inputs (cuts the head image there; optional at the first call); assemble — where the head run sits on the painting | **proposed** by `propose --head-box` from the full run, square, source pixels, shifted inside the painting when needed; the head run must then be fed exactly that crop. With one run (no `--head`, issue #359: an artist's own file, §4 *For artists*) it is left out — there is no crop to place — and a config that sets it is refused (`ASSEMBLE_SINGLE_RUN_HEAD_BOX`) |
|
|
99
|
+
| `assemble.rig_scale` | assemble — the rig is `trunc(w × rig_scale)` x `trunc(h × rig_scale)`, and every rig-px value after it (`bones`, `meshes`, `motion`, `patches`, `cuts`) is in that rig's pixels; `check --source` (`CHECK_SOURCE_SIZE`) and `compose` (`SCENE_RIG_SCALE_AGREES`) read it back off `parts.json` | authored; rig pixels per source pixel. 0.5 in both examples (a 1664x2432 painting makes an 832x1216 rig). **Keep the figure about 1,100 to 1,200 rig px tall** — the `H` of `propose`'s `mesh spacing:` note, the height of the parts' boxes together — so for another painting, about 1,150 over the figure's height in painting px: that is the size the proposer's spacing divisor was calibrated at (H 1106–1185, the `mesh spacing` note says so) and the size of the two examples `check`'s bars are derived from, and the proposer and `check` also hold fixed rig-px values that do not scale with it (the fringe's 10 px step, the trunk segments' 18, 20, 30 and 100 px, the 15 px wrist tolerance, the 10-row pendant floor, the seam bar's 50 rig px over 40). What else it trades, measured on `demo` with the README loop from a fresh config at 0.25, 0.5 and 1 (issue #345, rig-c 2.47.1): the same plan and the same ten chains; every bar but the one the uncorrected `demo` always fails (`bottomwear` `TIP_OVER_ROOT`, the README loop's `RL01`) passing at all three; the atlas page 418x606, 832x1205 and 1825x2047; the figure's mesh spacing 16, 32 and 64, so 830, 868 and 932 triangles; the breath 2.5 % of a torso that scales with it; and the proposed bones, scaled to 0.5, off the 0.5 proposal's by up to 38 px at 0.25 and 22.5 px at 1 (the hairpin's origin; the skirt roots 20 and 10.5) — the fixed-px values above. Changing it after the rig sections are written is not refused: `demo`'s tracked config built at 0.25 gated green and `check` failed only `TIP_OVER_ROOT` on both sleeves; `propose --from-config` names it (54 `LINT` lines, links off the art) — propose again at the new scale. Absent: `CONFIG_FIELD_PRESENT`; 0, negative or not a number: `CONFIG_FIELD_TYPE`; a rig under 1x1: `ASSEMBLE_RIG_SIZE` |
|
|
100
100
|
| `assemble.plan` | assemble; also the list every `meshes`/`regions` key must come from | **proposed** by `assemble --propose-plan`. `[part name, "full" \| "head", See-through tag]`, in draw order back to front. Names are free; roles come from the tag. A layer the author made and added to a run takes `author:<label>` in the tag's place, and has no role (§5, *A layer the author made*) |
|
|
101
101
|
| `assemble.extend_below_crop` | assemble | **proposed** with the plan: `{part, run, tag}` — a head-run part that reaches the bottom of the head crop is continued from a full-run layer, whole connected components |
|
|
102
102
|
| `assemble.patches` | assemble; `meshes`/`regions` keys may name them too | **authored**, optional: `[{name, box, alpha, draw}]` — an extra part cut from the **painting** itself, for figure no See-through layer holds (a hem both runs dropped). `box` is `[x0, y0, x1, y1]` in **rig** pixels, `x1`/`y1` exclusive — the space of `parts.json` and `recomposite_rig.png`, where the hole is found; `alpha` is `"silhouette"` (the painting's figure silhouette inside the box, the one `--seam silhouette` uses) or `"box"` (the whole box); `draw` is `"back"`, `"front"` or `{"before": "<plan part>"}`. A patch is always a region: its bone is `regions.<name>`, in the one place every region's bone lives, and a `meshes` entry for it is refused. See §5 for how to place one |
|
|
103
103
|
| `assemble.cuts` | assemble; `meshes`/`regions` keys may name the new parts too, and must (each is a part like any other) | **authored**, optional (issue #170): `[{from, into, polygon, draw, overlap, under}]`, `under` optional — a plan part's layer cut by a polygon the author draws: the pixels of plan part `from` whose **centre** lies inside `polygon` (even-odd; a centre exactly on an edge is outside) leave `from` and make the new part `into`, colour and alpha, so every pixel of the layer ends in exactly one of the two. `polygon` is `[[x, y], …]`, at least three points, non-negative integer **rig** pixels — the space of `parts.json` and of the patches' boxes. The file that shows the layer in those coordinates is `assemble`'s own `rig/parts/<from>.png`, placed at its `parts.json` `x`, `y` (the PNG's pixel `(i, j)` is rig pixel `(x + i, y + j)`), or `render/recomposite_rig.png`, the whole rig 1:1; the contact sheets (`sheet`) are scaled and carry no coordinates. `draw` is `"back"`, `"front"` or `{"before": "<plan part>"}`, as for a patch (to keep the setup pose, draw the piece immediately in front of `from` — `{"before": "<the plan part after from>"}`, or `"front"` when `from` is the last plan part; `{"before": "<from>"}` draws it behind `from`, which `under` refuses as `ASSEMBLE_UNDER_DRAW` because the fill would hide it). `from` must be a plan part (`CONFIG_NAME_RESOLVES`; a patch or another cut's piece is refused), `into` a new name (`CONFIG_PART_UNIQUE`), the polygon three or more integer points (`CONFIG_FIELD_TYPE`). The piece's `parts.json` record carries `from`'s `<run>:<tag>` and its own counts, each split from the layer's by where the pixel lies; it is a mesh or a region under its own name, and the coverage rule holds it like a plan part. What the config cannot know the stage refuses: a polygon point past the rig (`ASSEMBLE_CUT_INSIDE`), two cuts of one part sharing a pixel (`ASSEMBLE_CUT_OVERLAP`), a cut that takes none of the layer's opaque pixels or leaves `from` none (`ASSEMBLE_CUT_PIXELS`). `overlap` is required, a non-negative integer of rig pixels with no default (`CONFIG_FIELD_TYPE`): the **band** — the polygon's pixels within `overlap` px of its edge by Chebyshev distance (the polygon's mask less its erosion by a `2 * overlap + 1` square) — stays in `from` as well as going to the piece, so the piece's resampled edge has art under it; the counts stay a strict partition and the band is the base's extra copy, printed on the `cut:` line. At `0` the two abut and `check`'s seam bar judges the edge between them (`CHECK_SEAM_WITHIN_BAR`, §5). A band only covers the edge at rest: a piece that swings well clear of where it was painted shows the hole it leaves, and a thin piece's band shows a ghost of the piece. `under` (issue #193), optional, is the author's image of what lies under the piece: a PNG path relative to the config's directory (`CONFIG_FIELD_TYPE` unless a non-empty string), in rig pixels over the polygon's **bounding box** — the least and greatest polygon x and y, so the image is `max x − min x` by `max y − min y` and its pixel `(i, j)` is rig pixel `(min x + i, min y + j)`. Where the cut removed pixels from `from` — the piece's pixels (alpha above 0) less the band — `from` takes the image's pixel, colour and alpha wherever the image's alpha is at least the alpha the cut removed there (**filled**); elsewhere `from` stays empty there (**left empty**): a transparent pixel is the author's statement that nothing lies under it (the background, or a part drawn behind), and a less opaque one is not half-filled. The image is read nowhere else, so its pixels over the band, over the box outside the polygon or where the layer had no art never change `from`. The `cut:` line then ends `; under <n> px filled, <m> px left empty` (`n + m` is every removed pixel), so an unintended hole is a number in the log, not a guess; the filled pixels count in `from`'s `opaque_px` and `occluded_px` (the piece is drawn over them), never as visible or projected. The stage refuses: a file that is not there (`ASSEMBLE_UNDER_PRESENT`) or not a PNG (`ASSEMBLE_UNDER_DECODES`), an image not exactly the box's size (`ASSEMBLE_UNDER_SIZE`, both sizes given), an image that fills none of the removed pixels — all transparent there, or another file (`ASSEMBLE_UNDER_EMPTY`; to leave the whole hole, drop `under`), and a piece drawn behind `from` (`ASSEMBLE_UNDER_DRAW`: the fill would hide it; draw it `"front"` or `{"before": "<the plan part after from>"}`). Producing the image is the author's, by whatever means; absent, `from` keeps only its band |
|
|
104
|
-
| `
|
|
104
|
+
| `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?}, …]}}`, 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` or `cap` (`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`), metadata no stage here reads (the stress readers pose each joint at its ends; rig-c needs no rotation limit); `cap` a PNG path relative to the config's directory (`CONFIG_FIELD_TYPE`). 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 |
|
|
105
|
+
| `bones` | rig; `propose --from-config` | **proposed** by `propose`, then corrected. A single bone `{name, parent, at, tip?}` or a chain `{chain, parent, points, tip}` whose links are named `<chain>0 … <chain>n`. Parents come before children. In `rig.json` each link is **turned along its chain** — `rotation` is the direction from its origin to the next link's (the last link's to `tip`) and `length` that distance (issue #73), so a physics constraint added downstream finds a lever; under `--idle-keys ctl` the link's `<link>_ctl` carries the turn and the length and the link sits at local rotation 0 beneath it. A single bone 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`). 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. 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 |
|
|
105
106
|
| `meshes.<part>` | rig | **proposed**, then corrected. Exactly one of `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 every mode: `r` (added to every distance before weighting: `w = 1/(d + r)²`), `segments` (a chain name, a bone name with a `tip`, or `[bone, [x0,y0], [x1,y1]]`), and, **authored**, optional, `exponent` (issue #161): a number above 0 that replaces the 2 in that rule, `w = 1/(d + r)^exponent` — a larger one hands each vertex more fully to its nearest bone; absent is 2 and changes no byte, a value that is not a number above 0 is `CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`, and a declared one is echoed as `exponent` in the mesh's `mesh_report.json` row; the exponent is the distance rule's, so declared beside `rule: "heat"` it is refused (`CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`). The segment list is the one authored decision about a layer: which bones may pull it. The slot's bone is the first segment's. Optional `rule` (issue #161): `"distance"` — the rule above, and what an absent `rule` means (every byte is the same) — or `"heat"`, bone heat over the part's own silhouette, in every mode: the lattice's art (alpha above 8, 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 |
|
|
106
107
|
| `meshes.<part>.contour` | rig | **authored**, optional (issue #84): `{tolerance, margin, spacing, maxEdge?, budget?, stray?, bridges?, regions?}`. `tolerance` — rig-c's Douglas–Peucker tolerance on the traced outline, px, 0 or more; `margin` — how far the silhouette grows before it is traced, px: 0, or 1 or more (a pixel joins when its centre lies within `margin` of an art pixel's centre; a value above 0 and below 1 adds nothing and is refused, `CONFIG_FIELD_TYPE`); `spacing` — the background interior spacing, px, above 0; `maxEdge` (issue #265) — the longest outline edge, px, above 0: every simplified edge longer than it is cut into `ceil(length / maxEdge)` equal pieces, each cut point on the 1/256 px grid, before any region or rib touches the outline (absent: nothing is cut, and no byte moves; under 2/256 px the rig stage refuses it, `CONTOUR_PARAMETER`, because two cut points could snap together); the row echoes it in `params` and counts the points it added in `contour.outlineCut`, both only when declared; `budget` — the most vertices the mesh may have (refused above it, nothing thinned); `stray` — the largest island, in art pixels, that may be left out of the mesh (absent: none is); `bridges` (issue #326) — `true`, its one value: islands are read in this order — **dust first** (those `stray` leaves out), **then faint islands** (every pixel's alpha under 64, a quarter of full opacity: left out the same way, their pixels kept in the image but not meshed, so not drawn, and named in the row's `contour.bridges.faint` with their px, brightest alpha and box), **then every visible island is meshed and bridged, whatever the bridge costs** — overdraw is reported, never a reason to drop art; the largest island, or a tie for it, is never left out, faint or not: dropping it would draft the part without its art, and a part whose main body is faint is a question about the layer, which the plan and the author own, not one for the mesh — and the pieces are joined into the one outline rig-c takes by bridges — the kept islands, holes filled, grow together by `margin`, so islands closer than about twice the margin are one grown silhouette, traced as one; each silhouette is traced, simplified, capped, refined and triangulated as a one-island mesh is; then, starting from the silhouette with the most art, the nearest pair of a vertex on the joined outline and one on a silhouette still apart whose quad holds (wound as the mesh, crossing no outline, holding no other vertex, one closed outline after) is joined by that quad, two triangles over no art, until none is apart. Coverage and overshoot read the silhouettes' triangles; a bridge's area is in `meshArea` and in the row's `contour.bridges` (`islands`, `units`, and per join `unit`, `span` — its two crossing edges, px — `area`, `vertices`, `pair`; and `faint`, only when an island was left out as faint), and the row's `params` echoes `bridges`, both only when declared; `art_coverage` counts a faint island's pixels as art not covered. Absent, a second island is refused as below and no byte moves; on one island it writes the mesh the undeclared entry writes. Any other value is `CONFIG_FIELD_TYPE`; beside `ribs` it is `CONTOUR_PARAMETER` (a rib runs across one outline); a join no pair makes is `CONTOUR_BRIDGE`. A bridge stretches with whatever pulls its two ends apart, so it belongs on a part whose pieces ride one bone — the only case `mesh-draft` drafts; `regions` — local deformation regions, each `{name, shape, bone, spacing, band}` with `shape` `"circle"` (`cx, cy, r`) or `"polygon"` (`points`, 3 or more): `bone` is the region's control bone (any bone `bones` declares), `spacing` the interior spacing inside the region and its band, `band` the width the bone's weight falls across. Every length and position is **rig px**, like every other in the config; the part image is the rig less `(x − 4, y − 4)` (its box less the pad), a translation, so a length is the same number in both. A region's `cx`, `cy`, `r`, `band` and `points` are multiples of 1/256 px (`CONFIG_FIELD_TYPE` otherwise); region names are unique per mesh (`CONFIG_REGION_NAME_UNIQUE`). The alpha threshold is not a field: art is alpha above 8, the lattice's and `check`'s |
|
|
107
108
|
| `meshes.<part>.ribs` | rig | **authored**, optional (issue #188), in the `contour` and `auto` modes: `{<chain>: {stations}, …}`, each key a chain this mesh's `segments` name. Each link of the chain gets a row of vertices across the part at its origin and at `stations` points evenly between its origin and its end (`stations`, a whole number, 0 or more, required; a chain of L links carries L × (stations + 1) ribs), each rib a row of vertices across the part, kept as a row of locked edges; the rule is "Ribs along a chain" under "A contour mesh". Absent places none and changes no byte. Refused: a lattice mesh with ribs (`CONFIG_RIBS_MODE` — its vertices are its grid's corners), a key that is not one of the mesh's chains (`CONFIG_NAME_RESOLVES`), an object naming no chain or a `stations` that is not a whole number from 0 (`CONFIG_FIELD_TYPE`); at the rig stage `CONTOUR_RIB_ART`, `CONTOUR_RIB_LINK`, `CONTOUR_RIB_CROSSING`, `CONTOUR_RIB_EDGE` (the contour refusals). The mesh's `mesh_report.json` row gains `ribs` — the declaration, the ribs placed and the vertices they hold — and its contour report (the source's, in `auto`) lists each rib's vertices |
|
|
@@ -282,9 +283,9 @@ What a joint with a position places, as given — no ratio is applied over it:
|
|
|
282
283
|
| `neck` | `neck` | the rule: half way from the chin to the neck part's bottom (or 0.12 face heights below the chin), on the eye axis |
|
|
283
284
|
| `hip` | the midpoint of `r_hip` and `l_hip` — a point between two joints, said as that | the hip rule (bottomwear top, the waist, or 0.32 of the figure); one hip alone is said to be unused |
|
|
284
285
|
| `chest` | its rule, half way from neck to hip, taken along the line between the placed ends | the rule as before when neither end is a joint |
|
|
285
|
-
| `sleeve_<s>` | `<s>_shoulder` → `<s>_elbow` (two links), tip at `<s>_wrist`, with the two-blob rule's first two amplitudes
|
|
286
|
+
| `sleeve_<s>` | `<s>_shoulder` → `<s>_elbow` (two links), tip at `<s>_wrist`, with the two-blob rule's first two amplitudes; the arm is a limb (issue #349), `arm_<s>`: its handwear part is cut at the elbow into `upperarm_<s>` on link 0 and `forearm_<s>` on link 1, each a region — no mesh, no chest stub — when every handwear part over 500 px holds one such wrist within 15 px of its art (the larger of the x and y gaps, the LINT's own tolerance), or is bound one by `--wrist-snap` (below) | the sleeve rule places every sleeve chain, and each arm joint's note says why: for a wrist on no part, `… lies on the art of no handwear part within 15 px, so it does not say which sleeve it ends; the nearest is <part>, <d> px from its art by the larger of the x and y gaps (--wrist-snap <d> would bind it)` — `(beyond --wrist-snap <px>)` when the flag was given and fell short, and `the nearest are <a> and <b>, each <d> px …, so no tolerance says which` when two parts tie |
|
|
286
287
|
| `eye_<s>`, `head`, the face box | not moved: they are measured off the parts | the note prints the eye joint's distance from the eye bone, with no bar |
|
|
287
|
-
| `leg_<s>`
|
|
288
|
+
| `leg_<s>` (issue #241), `foot_<s>` | on a `legwear` part over 500 px: `<s>_hip` → `<s>_knee` (two links), tip at `<s>_ankle`, parent `hip` — when every such part holds the ankle of a leg whose three joints are given, within 15 px of its art; a part holding both ankles gets a chain per leg. The leg is a limb (issue #349), `leg_<s>`: the part is cut at the knee into `thigh_<s>` on link 0 and `shin_<s>` on link 1, each a region — no mesh, no `hip` stub (until #349 the thigh was meshed with one, which pinned it to the hip as the chest stub pinned a sleeve); two legs on one part are split between the two limbs by the cut. When every leg's ankle lies within 15 px of the art of exactly one `footwear` part over 500 px, each leg gets `foot_<s>`, a single bone at the ankle under `leg_<s>1` with `inherit` `"noRotationOrReflection"`, and the footwear part is cut between the feet as `foot_<s>`, the limb's third segment; otherwise no foot is drafted and the note gives each ankle's distance to the nearest footwear art. No idle track: the chain is drafted whatever the pose, and whether the legs move is the scene's. A note per limb says what was cut and why | no leg with all three joints: no chain, and the part is the trunk rule's region with the note it always had, byte for byte. A complete leg whose ankle lies on no legwear part, or on two: no chain, and a note `<parts>: no leg chain — <why>; the trunk rule places it` |
|
|
288
289
|
| the trunk parts' segments (issue #243) | on the `neck` (head run), `topwear` and `bottomwear` meshes, each `[bone, from, to]` segment the rule drafts down the screen is laid along neck → hip, the line the chest is placed on — `neck` + 18 px along it to `chest`; a topwear part's `chest` segment from its art's nearest reach toward the neck end to 20 px short of `hip`, and its `hip` segment from there to 100 px past `hip`; a bottomwear part's `chest` segment, when it has one (issue #262: only when its art reaches above `chest`), from 30 px short of `chest` to 20 px short of `hip` — and the bottomwear waist segment runs across that line, 5 px past `hip`, over 0.09 to 0.91 of the art's width across it. The `head` segment on the neck mesh and the one-blob sleeve rule's chest segment are not moved. A note, `trunk segments (<parts>): laid along neck -> hip …`, gives the line's angle | neither end a joint: the vertical draft, byte for byte. Neck and hip on one point: no line, the vertical draft, and a note says so. An upright line over boxes tight on their art writes the vertical draft's bytes |
|
|
289
290
|
|
|
290
291
|
**`--wrist-snap <px>`** (issue #199), with `--keypoints` only, declares a tolerance
|
|
@@ -306,14 +307,12 @@ changes, byte for byte. Choosing the tolerance automatically is not done: the no
|
|
|
306
307
|
for an unbound wrist prints the distance that would have bound it.
|
|
307
308
|
|
|
308
309
|
**One handwear part holding both wrists** — two sleeves and the hands decomposed as
|
|
309
|
-
one piece, not clasped at the midline — gets both
|
|
310
|
-
before, and a note says so rather than leaving two chains to share the part
|
|
311
|
-
silently:
|
|
310
|
+
one piece, not clasped at the midline — gets both arms' limbs on its one part, and a note says so:
|
|
312
311
|
|
|
313
|
-
one handwear part holds both wrists: r_wrist and l_wrist both end on <part>, so
|
|
312
|
+
one handwear part holds both wrists: r_wrist and l_wrist both end on <part>, so limbs arm_r and arm_l both take it, and the cut gives each pixel to the arm whose bones pass nearest it; where the arms touch or cross in its art that split is a line no painting draws — to draw it yourself, cut one sleeve out of <part> with an assemble.cuts entry (a polygon round it) and name the piece as that arm's from
|
|
314
313
|
|
|
315
|
-
|
|
316
|
-
proposer does not.
|
|
314
|
+
An `assemble.cuts` entry (§3; §5, *Cutting a piece out of a layer*) draws the split by hand;
|
|
315
|
+
the proposer does not.
|
|
317
316
|
|
|
318
317
|
Every other joint (`nose`, ears, the knees and ankles no leg chain used; the eyes
|
|
319
318
|
with no eyewhite) is named with its state in one note: no bone is authored from it. Every joint the proposer
|
|
@@ -434,8 +433,73 @@ top edge instead would close the eye upward onto the upper lid and still move wh
|
|
|
434
433
|
above it by (1 − squash) × its height above the pivot, so the cut, not the pivot, is what
|
|
435
434
|
holds the crease.
|
|
436
435
|
|
|
436
|
+
### Limbs: cut at their joints (`limbs`, issue #349)
|
|
437
|
+
|
|
438
|
+
**A limb is separate parts, cut at its joints, each rigid on its own bone.** The upper arm,
|
|
439
|
+
the forearm with the hand, the thigh, the shin and the foot are each a part on one bone with
|
|
440
|
+
weight 1, and a bend is a rotation at the joint, never a deformed mesh. A limb mesh blended
|
|
441
|
+
along a chain and bent hard is what every "grotesque" verdict on a posed rig came from (the
|
|
442
|
+
exam's walk, rig-c/pose's demo); a part on one bone cannot do that, and `check`'s
|
|
443
|
+
`LIMB_ONE_BONE` refuses a limb part bound to a second bone by name. Everything below this
|
|
444
|
+
section about fitting a mesh to motion — the weight cut, the contour and automatic modes, ribs,
|
|
445
|
+
the physics recipe — is for the parts that have no joints: the skirt, the clothing, the hair,
|
|
446
|
+
the torso.
|
|
447
|
+
|
|
448
|
+
`propose --keypoints` drafts the block from the body-18 joints (§3, *A posed figure*): an arm
|
|
449
|
+
whose shoulder, elbow and wrist are given is `arm_<s>`, `upperarm_<s>` on `sleeve_<s>0` and
|
|
450
|
+
`forearm_<s>` on `sleeve_<s>1`, cut at `elbow`; a leg whose hip, knee and ankle are given is
|
|
451
|
+
`leg_<s>`, `thigh_<s>` on `leg_<s>0` and `shin_<s>` on `leg_<s>1`, cut at `knee`, and — when
|
|
452
|
+
every leg's ankle lies on one footwear part — `foot_<s>` on a bone of its own at the ankle under
|
|
453
|
+
the shin, `inherit` `"noRotationOrReflection"`, so the shoe keeps its setup angle while the leg
|
|
454
|
+
turns (`FEET_STAY_LEVEL`, §7). Nothing else is a limb: hair chains and physics sleeves are not,
|
|
455
|
+
and a config without `limbs` builds as it always did. Then `assemble` with the block cuts the
|
|
456
|
+
layers, as follows; every step is arithmetic on the declared bones and the layer's pixels
|
|
457
|
+
(`src/limbs.ts`):
|
|
458
|
+
|
|
459
|
+
- **Which limb.** A part a limb takes (`from`) goes wholly to the limbs that take it. With two —
|
|
460
|
+
See-through's one `legwear` holding both legs — each pixel goes to the limb whose polyline (its
|
|
461
|
+
bones' origins in order, then the last bone's end) passes nearest the pixel's centre, a tie to
|
|
462
|
+
the limb declared first. No polygon is drawn, and each leg's lower part carries its own cap.
|
|
463
|
+
- **The joint line.** Between two segments the joint is the lower bone's origin, and the line
|
|
464
|
+
runs through it perpendicular to the limb's axis there — the sum of the upper bone's and the
|
|
465
|
+
lower bone's unit directions, so on a bent limb the bisector. A pixel whose centre is past the
|
|
466
|
+
line (its dot product with the axis, from the joint, above 0) goes to the lower segment.
|
|
467
|
+
Segments from different parts (the shin's legwear, the foot's footwear) are not cut at all:
|
|
468
|
+
each takes its limb's share of its own part.
|
|
469
|
+
- **The cap.** Each lower segment carries an overlap past its joint: the pixels within half the
|
|
470
|
+
limb's width of the joint, on the upper side of the line — a disc about the limb's width across,
|
|
471
|
+
so a bend shows no gap. The width is read along the joint line: the limb's art (alpha above 8,
|
|
472
|
+
both segments' parts) within half a pixel of the line, the run through the joint (its nearest
|
|
473
|
+
pixel within 1 px of it, neighbours at most 1.5 px apart), first to last plus one pixel, to 3
|
|
474
|
+
places. The tool invents no pixel of it: each is the `cap` image's where that image is not
|
|
475
|
+
transparent, else the layer's where the limb has art there, else **missing**. The line
|
|
476
|
+
`assemble` prints for each joint counts all three and gives the cap's box, `[x, y, w, h]` in
|
|
477
|
+
rig px:
|
|
478
|
+
|
|
479
|
+
limb cap: "arm_r" joint "elbow" at [332, 424], on "forearm_r": a disc of radius 19.9165 px (the limb 39.833 px across the joint line), box [312, 404, 40, 40] — on the upper side of the line 621 px from the layer, 7 px missing — …
|
|
480
|
+
|
|
481
|
+
The missing pixels are where the painting hides the limb or where the disc passes its outline.
|
|
482
|
+
Paint them — and, if the layer's own pixels carry the limb's outline across the cap, the whole
|
|
483
|
+
cap — into a `w`x`h` image whose pixel `(i, j)` is rig pixel `(x + i, y + j)`, and name it in
|
|
484
|
+
the segment's `cap`; its transparent pixels keep the layer's. The record is `parts.json`'s
|
|
485
|
+
`limb.cap` on the lower part. A cap image of another size is `ASSEMBLE_LIMB_CAP_SIZE`, one
|
|
486
|
+
transparent over every cap pixel `ASSEMBLE_LIMB_CAP_EMPTY`.
|
|
487
|
+
- **Draw order.** A limb stands where its upper segment's part stood in the plan, its parts
|
|
488
|
+
lowest first, so the upper is drawn over the lower at every joint: upper arm over forearm,
|
|
489
|
+
thigh over shin, shin over foot. The parts it took leave the order.
|
|
490
|
+
- **What `check` reads.** `LIMB_ONE_BONE` (§7) holds every limb part to its one bone in
|
|
491
|
+
`rig.json`. `TIP_OVER_ROOT` does not read a limb part (it turns at its joint; it does not
|
|
492
|
+
hang), and its row says so.
|
|
493
|
+
|
|
494
|
+
The first character built this way (the exam-01 test rig, issue #349) read, per joint, 5 to 10
|
|
495
|
+
missing cap pixels at the elbows and knees and 468 and 562 at the ankles, where the shoe starts
|
|
496
|
+
below the ankle line.
|
|
497
|
+
|
|
437
498
|
### The weight cut (`meshes.<part>.influences`, issue #284)
|
|
438
499
|
|
|
500
|
+
This section and the mesh sections after it are for parts with no joints; a limb is cut at
|
|
501
|
+
its joints instead (*Limbs: cut at their joints*, above).
|
|
502
|
+
|
|
439
503
|
The distance rule (and bone heat) shares a vertex among the bones its mesh's segments name,
|
|
440
504
|
then cuts the list: the 4 largest kept, any share under 0.03 dropped. Both numbers are the
|
|
441
505
|
reference implementation's (`[:4]` and `>= 0.03`), carried over as port constants. Neither
|
|
@@ -1171,6 +1235,69 @@ right, at +30 moved its hand bone `sleeve_l2` 43.9 px right and 27.6 px up, at
|
|
|
1171
1235
|
51.9 px right, toward the body — so `demo`'s `wave` raises the image's right arm with
|
|
1172
1236
|
`sleeve_l0` at +48 (selftest `RG99` holds the sign on the rig fixture).
|
|
1173
1237
|
|
|
1238
|
+
`demo`'s `wave` in full, as [`examples/demo/animations.json`](https://github.com/rig-c/parts/blob/main/examples/demo/animations.json)
|
|
1239
|
+
holds it (`curve` is rig-c's escape from a named easing, its AUTHORING R6): the arm out to the
|
|
1240
|
+
side and held, a bend further down the arm, the hand rocked ±10°, back down.
|
|
1241
|
+
|
|
1242
|
+
```jsonc
|
|
1243
|
+
"wave": {
|
|
1244
|
+
"duration": 2.21,
|
|
1245
|
+
"loop": false,
|
|
1246
|
+
"tracks": [
|
|
1247
|
+
{
|
|
1248
|
+
"bone": "sleeve_l0",
|
|
1249
|
+
"property": "rotate",
|
|
1250
|
+
"keys": [
|
|
1251
|
+
{"t": 0, "v": [0], "curve": [0.0875, 0, 0.2625, 48]},
|
|
1252
|
+
{"t": 0.35, "v": [48], "curve": [0.69, 48, 1.37, 48]},
|
|
1253
|
+
{"t": 1.71, "v": [48], "curve": [1.835, 48, 2.085, 0]},
|
|
1254
|
+
{"t": 2.21, "v": [0]}
|
|
1255
|
+
]
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
"bone": "sleeve_l1",
|
|
1259
|
+
"property": "rotate",
|
|
1260
|
+
"keys": [
|
|
1261
|
+
{"t": 0, "v": [0], "curve": [0.0875, 0, 0.2625, 20]},
|
|
1262
|
+
{"t": 0.35, "v": [20], "curve": [0.69, 20, 1.37, 20]},
|
|
1263
|
+
{"t": 1.71, "v": [20], "curve": [1.835, 20, 2.085, 0]},
|
|
1264
|
+
{"t": 2.21, "v": [0]}
|
|
1265
|
+
]
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
"bone": "sleeve_l2",
|
|
1269
|
+
"property": "rotate",
|
|
1270
|
+
"keys": [
|
|
1271
|
+
{"t": 0, "v": [0], "curve": [0.0875, 0, 0.2625, 0]},
|
|
1272
|
+
{"t": 0.35, "v": [0], "curve": [0.3925, 0, 0.4775, 10]},
|
|
1273
|
+
{"t": 0.52, "v": [10], "curve": [0.605, 10, 0.775, -10]},
|
|
1274
|
+
{"t": 0.86, "v": [-10], "curve": [0.945, -10, 1.115, 10]},
|
|
1275
|
+
{"t": 1.2, "v": [10], "curve": [1.285, 10, 1.455, 0]},
|
|
1276
|
+
{"t": 1.54, "v": [0], "curve": [1.7075, 0, 2.0425, 0]},
|
|
1277
|
+
{"t": 2.21, "v": [0]}
|
|
1278
|
+
]
|
|
1279
|
+
}
|
|
1280
|
+
]
|
|
1281
|
+
}
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
**Which link bends where.** A sleeve chain the proposer drafts without `--keypoints` (the
|
|
1285
|
+
two-blob rule, `src/propose.ts`) divides the `handwear` part's height in three, not the arm
|
|
1286
|
+
at its joints: its links stand at 0, 1/3 and 2/3 of the way from 15 px below the part's top
|
|
1287
|
+
row to 6 px above its bottom row, and its tip 3 px above the bottom row. On `demo`, `sleeve_l`'s
|
|
1288
|
+
links stand at y 260, 381 and 502, tip 626 (rig px), and on `scarf` at 277, 413 and 550, tip
|
|
1289
|
+
689; on both paintings the elbow lies between links 1 and 2 and the wrist between link 2 and
|
|
1290
|
+
the tip [observed: the chain drawn over the painting and read by eye, issue #345]. So
|
|
1291
|
+
`sleeve_l1` turns the arm part way down the upper arm, and `sleeve_l2` part way down the
|
|
1292
|
+
forearm, hand included: `wave`'s +20 on `sleeve_l1` is that bend, not an elbow's. Place the
|
|
1293
|
+
links on the joints (`bones`, the chain's `points`) before keying a bend that must read as
|
|
1294
|
+
an elbow. Under `--keypoints` the chain is `<s>_shoulder` → `<s>_elbow`, tip at `<s>_wrist`
|
|
1295
|
+
(§3, *A posed figure*): two links, the second the forearm from the elbow, and no `sleeve_l2`,
|
|
1296
|
+
so a clip keying `sleeve_l2` names a bone that rig lacks. `propose --keypoints` on `demo`'s
|
|
1297
|
+
parts, fed its generation control pose doubled to painting px (what was asked of the model,
|
|
1298
|
+
not where the joints ended — read for the chain's shape only), wrote `sleeve_l`
|
|
1299
|
+
`[[492, 272], [550, 440]]`, tip `[626, 590]`, amplitudes `[-0.2, -0.6]`.
|
|
1300
|
+
|
|
1174
1301
|
`build` writes `surprised` into `rig/motion.json` after `idle`, gates it with the rig, and
|
|
1175
1302
|
`check` names it on a line of its own and reads it for folds (`ANIMATIONS_DO_NOT_FOLD`, §7,
|
|
1176
1303
|
issue #320). [`examples/reference-sample`](https://github.com/rig-c/parts/tree/main/examples/reference-sample), in the repository, carries one such file,
|
|
@@ -1181,7 +1308,8 @@ issue #320). [`examples/reference-sample`](https://github.com/rig-c/parts/tree/m
|
|
|
1181
1308
|
[rig] animations beside the idle, from …/animations.json as written: hop
|
|
1182
1309
|
[check] beside the idle: 1 animation(s), hop — built and gated by rigc with the rig; ANIMATIONS_DO_NOT_FOLD reads each for folds, every other bar here reads the idle
|
|
1183
1310
|
[check] ANIMATIONS_DO_NOT_FOLD: PASS — 2 animation(s) read by rigc render --folds at 12 fps, its 2 physics constraint(s) left out (idle, hop): 0 folding mesh(es), 0 not declared in may_fold (<= 0)
|
|
1184
|
-
[check]
|
|
1311
|
+
[check] STRESS_OFF_CHAIN: SKIP — "sleeves": its weights ride 2 chains ("sleeve_r0_ctl", "sleeve_l0_ctl"): turning one stretches the part toward the other by construction; a part riding one chain is read until a limbs block names each limb
|
|
1312
|
+
[check] check: PASS; 10 of 11 bar(s) measured, 1 skipped (STRESS_OFF_CHAIN)
|
|
1185
1313
|
```
|
|
1186
1314
|
|
|
1187
1315
|
A bone the rig lacks is rigc's refusal at the rig stage, in rigc's words (`RIG_RIGC_GREEN`); a
|
|
@@ -1231,6 +1359,19 @@ way there, are in their READMEs, in the repository:
|
|
|
1231
1359
|
[`examples/demo/README.md`](https://github.com/rig-c/parts/blob/main/examples/demo/README.md) and
|
|
1232
1360
|
[`examples/scarf/README.md`](https://github.com/rig-c/parts/blob/main/examples/scarf/README.md).
|
|
1233
1361
|
|
|
1362
|
+
**Under the idle alone** a strand trails by what its constraint's values make of a slow
|
|
1363
|
+
swing, and the soft-region recipe's *Limits* (below) is the same finding for a jiggle: a slow
|
|
1364
|
+
idle barely moves a stiff spring, and faster keys — a hop, a turn, in a named animation — are
|
|
1365
|
+
what it answers. Measured on `demo` (issue #345, rig-c 2.47.1: the idle rendered at 30 fps
|
|
1366
|
+
with `--geometry`, the build against the same build with `constraints` removed, each link's
|
|
1367
|
+
world angle compared frame by frame): the twin tails' last links (`hairback_r2` and
|
|
1368
|
+
`hairback_l2`, inertia 0.8, strength 58.5, the values above) trail their keyed angle by at
|
|
1369
|
+
most 2.753° and 1.720° — `hairback_r2`'s tip 9.96 rig px, where the idle swings that link's
|
|
1370
|
+
world angle through 9.276°, and 2.612° in the idle's second half, so not the physics reset's
|
|
1371
|
+
transient; the bangs' last links (inertia 0.2, strength 130) by at most 0.127°, the skirt's
|
|
1372
|
+
0.073°, the sleeves' 0.054°. A strand that reads stiff under the idle is not broken: key it
|
|
1373
|
+
in a clip, or soften it as the tails are softened.
|
|
1374
|
+
|
|
1234
1375
|
**Limits.** Every slot holds one attachment, the part's own image, so an expression moves,
|
|
1235
1376
|
turns and scales the face's parts and never swaps one for another: there is no second mouth
|
|
1236
1377
|
or eye shape to key, and no lip-sync. `check` reads an animation beside the idle for folds
|
|
@@ -1241,6 +1382,16 @@ examples each clip was read by `check`'s own `TEXTURE_STRETCH` reading applied t
|
|
|
1241
1382
|
gate, and it is where an arm raised overhead was found to tear `demo`'s elbow (5.327
|
|
1242
1383
|
against 1.926544).
|
|
1243
1384
|
|
|
1385
|
+
**A raised arm moves its `handwear` part only.** The proposer meshes each `handwear` part on
|
|
1386
|
+
its sleeve chain and the `topwear` part on `chest` and `hip` alone (§3, *the trunk parts'
|
|
1387
|
+
segments* and the sleeve rule), so arm art See-through left inside `topwear` — a puff
|
|
1388
|
+
sleeve, a shoulder cap — stays with the torso while the arm leaves it. On `demo`, whose puff
|
|
1389
|
+
sleeves are in `handwear`, `wave` (rendered at 30 fps with `--geometry`, physics left out,
|
|
1390
|
+
issue #345) moves `handwear_l`'s vertices up to 443.2 rig px and every other slot's by 0.
|
|
1391
|
+
To make such art follow the arm, cut it out of `topwear` with an `assemble.cuts` entry and
|
|
1392
|
+
give the piece a mesh on the `sleeve_<s>` chain (§5, *Cutting a piece out of a layer*, the move
|
|
1393
|
+
`scarf`'s `robe_r` measured); that cut has not been measured on a sleeve.
|
|
1394
|
+
|
|
1244
1395
|
### Recipe: a soft region driven by physics
|
|
1245
1396
|
|
|
1246
1397
|
A jiggle (a bust, the hips) is a patch of one part that lags behind the body. The package
|
|
@@ -1275,7 +1426,7 @@ fragment below, cut to the bust:
|
|
|
1275
1426
|
`build` on `reference-sample` as tracked (PR #187; the values above are the ones tuned for its `hop`, issue #183):
|
|
1276
1427
|
|
|
1277
1428
|
```
|
|
1278
|
-
check: PASS; 10 of
|
|
1429
|
+
check: PASS; 10 of 11 bar(s) measured, 1 skipped (STRESS_OFF_CHAIN)
|
|
1279
1430
|
loop physics: 2 physics constraint(s) left out of the loop's frames, reported, not judged — "bust_phys" on bone "chest_jiggle" (rig.json), "hip_phys" on bone "hip_jiggle" (rig.json)
|
|
1280
1431
|
loop: idle 49 frame(s) at 12 fps, f0000 vs f0048 (t = 4s) with the rig's 2 physics constraint(s) left out (named on the `loop physics:` line; rendered with them, the ends need not meet — `build --loop --lead-in <n>`): max |d| 0 (0 required)
|
|
1281
1432
|
```
|
|
@@ -1573,17 +1724,147 @@ The selftest runs this order, with the README's flags, on each fetched example f
|
|
|
1573
1724
|
a config holding only `key`, `seethrough` and `assemble.rig_scale`, pasting each
|
|
1574
1725
|
proposal in as the steps above say (`RL01`); every step must exit 0.
|
|
1575
1726
|
|
|
1727
|
+
### For artists: your own layered file (issue #359)
|
|
1728
|
+
|
|
1729
|
+
An artist's layered file is an input in its own right: you paint the figure in
|
|
1730
|
+
layers, the tool reads them, and an agent does the data work. Nothing below asks
|
|
1731
|
+
for See-through.
|
|
1732
|
+
|
|
1733
|
+
**What to paint.** One pixel layer per part, named by its tag. `rig-parts layers
|
|
1734
|
+
--template art.psd --canvas <side>x<side>` writes a PSD holding one empty layer per
|
|
1735
|
+
name the rig reads, bottom first: `back hair`, `neck`, `neckwear`, `topwear`,
|
|
1736
|
+
`handwear-r`, `handwear-l`, `bottomwear`, `legwear`, `footwear`, `tail`, `wings`,
|
|
1737
|
+
`objects`, `headwear`, `face`, `irides-r`, `irides-l`, `eyebrow-r`, `eyebrow-l`,
|
|
1738
|
+
`eyewhite-r`, `eyewhite-l`, `eyelash-r`, `eyelash-l`, `eyewear`, `ears-r`, `ears-l`,
|
|
1739
|
+
`earwear`, `nose`, `mouth`, `front hair` — 29 names: the 24 See-through tags
|
|
1740
|
+
(`src/tags.ts`) less `head`, which upstream never emits and no stage reads, with the
|
|
1741
|
+
six split tags as their halves, `-r` being the character's right (the smaller x).
|
|
1742
|
+
The order is a starting stack, not a rule. Paint the layers the figure has and
|
|
1743
|
+
delete the rest: an empty layer is refused (`PSD_LAYER_HAS_PIXELS`). A layer you
|
|
1744
|
+
need that no tag fits is `author:<label>` (§5, *A layer the author made*).
|
|
1745
|
+
|
|
1746
|
+
- **The face, separated.** `face`, `eyewhite-r` and `eyewhite-l`, each painted, are
|
|
1747
|
+
what lets one file stand for See-through's two runs: the face box comes from
|
|
1748
|
+
`face`, the eye bones and the blink from the eyewhites. Without them the run is
|
|
1749
|
+
refused (`ASSEMBLE_SINGLE_RUN_FACE`, one line per tag missing).
|
|
1750
|
+
- **The stacking order is the draw order**, bottom first. A PSD carries no depth,
|
|
1751
|
+
and none is invented: `layers` prints one `SKIP depth:` line naming the file,
|
|
1752
|
+
and the turn draft (`propose --full`) skips it (`turn: SKIP`).
|
|
1753
|
+
- **Flatten each layer.** Normal blending, 100 % opacity, no clipping layer, no
|
|
1754
|
+
layer mask, 8-bit RGB: each is refused by name (`PSD_LAYER_PLAIN` naming the
|
|
1755
|
+
layer and what it carries, `PSD_IS_RGB8`), because the composite it would make
|
|
1756
|
+
is not one the reader can see. Groups are read through (pass-through or normal
|
|
1757
|
+
blending only). A switched-off layer is refused too, except a hidden-art layer.
|
|
1758
|
+
- **Small pieces.** A layer's pieces under 40 px, and under 1 % of its largest
|
|
1759
|
+
piece, are removed as See-through's specks (`GHOST_MIN_AREA`, `assemble`'s
|
|
1760
|
+
`ghost px removed:` line counts them), and `--propose-plan` leaves out a layer
|
|
1761
|
+
under 150 px (a note names it); both are in run pixels, so paint at the size
|
|
1762
|
+
the rig needs.
|
|
1763
|
+
- **The canvas.** One run is one canvas, of any shape no side of which is above
|
|
1764
|
+
`seethrough.resolution` (`ASSEMBLE_RUN_CANVAS` otherwise: padding cannot shrink a
|
|
1765
|
+
file, so set `resolution` to at least the longer side). The painting — `--source`,
|
|
1766
|
+
the file flattened and saved as an opaque PNG — is the same size
|
|
1767
|
+
(`ASSEMBLE_SINGLE_RUN_SOURCE` otherwise). A file that is not the `resolution`
|
|
1768
|
+
square is padded to it before it is read, the way `inputs` pads a painting for
|
|
1769
|
+
See-through (issue #366): centred, each margin `floor((resolution - side) / 2)`,
|
|
1770
|
+
the odd pixel to the right or the bottom, the pad transparent. The rig holds no
|
|
1771
|
+
pad — it is the file times `rig_scale`, so a part's box over `rig_scale` is its
|
|
1772
|
+
box in the file — and `parts.json` records the pad in `single_run_pad` (§5); a
|
|
1773
|
+
`padded:` line under `one run:` prints the four margins. A 75 x 128 file at
|
|
1774
|
+
resolution 128 is padded left 26, right 27; a square file at the resolution is
|
|
1775
|
+
not padded, and its `parts.json` has no such block.
|
|
1776
|
+
- **Hidden art.** A layer named `<layer>:hidden` — `handwear-r:hidden`, the forearm
|
|
1777
|
+
under the sleeve; `topwear:hidden`, the shirt under a scarf's tail — is that
|
|
1778
|
+
layer's art where something covers it. It is never drawn and never a part, and
|
|
1779
|
+
it may be switched off in the file. `assemble` reads it as the `under` of a cut
|
|
1780
|
+
of that layer's part (§3, `assemble.cuts`) and as the cap of a limb segment that
|
|
1781
|
+
takes that part (§3, *Limbs*), wherever the config names no file for them; a
|
|
1782
|
+
config that names one as well is refused (`ASSEMBLE_HIDDEN_ONE_SOURCE`), since
|
|
1783
|
+
reading either would be a guess. A cut of a part with hidden art is a cut with an
|
|
1784
|
+
`under`, so its piece is drawn in front of the part (`ASSEMBLE_UNDER_DRAW`
|
|
1785
|
+
otherwise), and a cut the hidden art holds nothing under fills 0 and says so. One `hidden art:` line per such layer names
|
|
1786
|
+
what read it, or says nothing did. Its name is the tag plus a suffix, so it
|
|
1787
|
+
needs no config field to say whose it is; a `:hidden` layer whose layer the file
|
|
1788
|
+
does not hold is refused (`LAYERS_HIDDEN_OF_PRESENT`).
|
|
1789
|
+
|
|
1790
|
+
**The commands.** The config holds no `seethrough.head_box`. Leave `--head` out
|
|
1791
|
+
everywhere:
|
|
1792
|
+
|
|
1793
|
+
```sh
|
|
1794
|
+
rig-parts layers art.psd
|
|
1795
|
+
rig-parts assemble --propose-plan --source painting.png --full art.psd --config config.json
|
|
1796
|
+
rig-parts assemble --source painting.png --full art.psd --config config.json --out .
|
|
1797
|
+
rig-parts propose --parts rig --source painting.png --out .
|
|
1798
|
+
rig-parts build --config config.json --source painting.png --full art.psd --out build
|
|
1799
|
+
```
|
|
1800
|
+
|
|
1801
|
+
`--propose-plan` writes every row from the full run, and its first note says the
|
|
1802
|
+
file was read as both runs; the assemble stage refuses a row that takes `"head"`
|
|
1803
|
+
or an `extend_below_crop` entry (`ASSEMBLE_SINGLE_RUN_PLAN`), and prints `one run:`
|
|
1804
|
+
under its first line.
|
|
1805
|
+
|
|
1806
|
+
**What you get back.** `parts/<name>.png` and `parts.json` (where every part came
|
|
1807
|
+
from and what it measured), `rig.json` and `motion.json`, and from `build` the packed
|
|
1808
|
+
atlas — `check/build/skeleton.json`, `.atlas` and `.png` — that a Spine runtime
|
|
1809
|
+
loads, with `check/contact.png` and the idle frames to look at.
|
|
1810
|
+
|
|
1811
|
+
### The correction loop (issue #359)
|
|
1812
|
+
|
|
1813
|
+
A parse can be wrong: a patch painted into the wrong layer, two layers that should
|
|
1814
|
+
be one, a split along a painted edge. An agent cannot edit a PSD, so the loop runs
|
|
1815
|
+
on the wrapper form instead, which any image library edits:
|
|
1816
|
+
|
|
1817
|
+
1. **Export.** `rig-parts layers --export work art.psd` writes `work/layers.json`
|
|
1818
|
+
and one PNG per layer (`<index>_<name>.png`), drawn layers in draw order, then
|
|
1819
|
+
the hidden-art layers; `depth_median` is `null` throughout, which the reader
|
|
1820
|
+
takes as "the manifest's order is the draw order". The export is read back at
|
|
1821
|
+
once and must reproduce the file's reading — layers, boxes, pixels, order — or
|
|
1822
|
+
it is refused (`LAYERS_EXPORT_ROUND_TRIP`).
|
|
1823
|
+
2. **Read.** `rig-parts layers work` (the table and its `WARN` lines),
|
|
1824
|
+
`assemble --full work …` (the part lines, `recomposite vs source`, the
|
|
1825
|
+
`uncovered hole` and `uncovered:` lines) and `render/recomposite_error_rig.png`
|
|
1826
|
+
(uncovered error red, covered error blue). A pixel in the wrong layer shows as
|
|
1827
|
+
error where that layer is drawn behind another part and the painting is
|
|
1828
|
+
near-white there (the seam override leaves near-white alone), or as `drift=`
|
|
1829
|
+
and `seam=` counts on the part drawn over it when it is not: projection and the
|
|
1830
|
+
seam override repaint that part to the painting's colour, so the error map
|
|
1831
|
+
stays clean while the part carries the wrong art.
|
|
1832
|
+
3. **Fix** the PNGs and `layers.json`: move the pixels, and when a layer's art
|
|
1833
|
+
leaves its box, grow the PNG and the box together (`LAYERS_PNG_MATCHES_BBOX`
|
|
1834
|
+
otherwise).
|
|
1835
|
+
4. **Re-run** from step 2 on `work`.
|
|
1836
|
+
|
|
1837
|
+
**A worked example** (the selftest's, `fixtures/artist.ts`): a 128-px file whose
|
|
1838
|
+
right eyewhite was painted half into `back hair` — the 8 x 10 patch at
|
|
1839
|
+
`[52, 22, 60, 32]`, near-white, behind the face. Assembled from the export:
|
|
1840
|
+
|
|
1841
|
+
```
|
|
1842
|
+
recomposite vs source: mean |d|=0.40, within 8: 99.5%, error px > 40: 80, uncovered error px: 0
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
80 covered error pixels, blue on the error map, in one 8 x 10 block; the face's
|
|
1846
|
+
part line says `drift=60`, the block's pixels inside the face's projection core
|
|
1847
|
+
whose colour is more than 90 off the painting's. The fix:
|
|
1848
|
+
in `work/00_back_hair.png` clear the block (it is `back hair`'s pixels
|
|
1849
|
+
`(20..27, 18..27)`, box `[32, 4, 96, 48]`); widen `work/03_eyewhite-r.png` from
|
|
1850
|
+
8 x 10 to 16 x 10, painting the block into its right half; and set that layer's
|
|
1851
|
+
`right` from 52 to 60 in `layers.json`. Re-run: `error px > 40: 0`, the clean
|
|
1852
|
+
file's figure, and every part is the clean file's but `hair_back`, which keeps an
|
|
1853
|
+
80-px hole behind the face where the hair under the eye was never painted — every
|
|
1854
|
+
one of them occluded, so nothing shows it at rest; paint it there if the face will
|
|
1855
|
+
move off it (`AS245`).
|
|
1856
|
+
|
|
1576
1857
|
## 5. After each stage: what to read
|
|
1577
1858
|
|
|
1578
1859
|
| stage | read | green looks like | a common red |
|
|
1579
1860
|
| --- | --- | --- | --- |
|
|
1580
1861
|
| `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*) |
|
|
1581
1862
|
| `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 |
|
|
1582
|
-
| `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. Then 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>"` line per part whose drift refusals hold a body (the same opening and bar) that another part holds in the draw order — that layer is not what the painting shows there (none on the five public paintings); and look at `render/recomposite_error_rig.png`. 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 one `fringe pushed back: "<part>" <px> px, where the painting shows "<holder>" <px> px, …` line per part whose fringe was cleared (below) | on the examples: `reference-sample` 0.83 / 98.0 % / 4,359 / 1,185, 259 holes, the largest 94 px; `reference-demo` 2.34 / 95.8 % / 10,340 / 1,564, 453 holes, the largest 70 px (default rule) — many slivers along part edges, no hole a region could hide | one large hole: part of the figure is in no layer — a plan entry is missing, hair left the head crop sideways (reference-demo's `hair_back` is taken from the full run for that reason), or See-through split one garment into two and left the space between them in neither (a skirt as two trouser legs); the `between` parts say where. When neither run holds it at all, an `assemble.patches` entry cuts it from the painting (below) |
|
|
1863
|
+
| `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. Then 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>"` line per part whose drift refusals hold a body (the same opening and bar) that another part holds in the draw order — that layer is not what the painting shows there (none on the five public paintings); and look at `render/recomposite_error_rig.png`. 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 (§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. When neither run holds it at all, an `assemble.patches` entry cuts it from the painting (below) |
|
|
1583
1864
|
| `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 hip at [x, y] is not below chest at [x, y]: …` or `LINT hip at [x, y] is above 0.25 of the figure height (figure y T..B, so hip y must be at least L): a hip at the shoulders` — move `hip` down to the waist (and `chest` between it and the neck); a `hanging strand … -- no chain proposed` note — that strand hangs stiff until you add a chain down the x and rows it names (§3); a `no blink: …` or `blink without brows: …` note — the idle will not blink (or its brows will not drop), because no part came from an `eyewhite` (or `eyebrow`) layer; the note names the tags looked for; a `no eyewhite part for eye_<s> …` note — the iris and lash parts it names ride `head` and do not blink, because no `eyewhite-<s>` part made that side's eye bone: take the eyewhite from the other run, or keep them on `head`; a `no face part: face box derived from …` note (always the first) — `head`, `neck` and every face-height scale come from a box guessed off the hair and neck (§3): check `head` and `neck` on `landmarks_head.png` and move them; when it adds `the blink shuts the eyes over no face part`, read `BLINK_NO_HOLE` after `check` — an eye with no face under it can open a hole (the `reference-sample` with only its face removed: 587 px). Under `--keypoints`: the `lint rule set:` line first (joints, or screen and why), then the joint notes — `missing: the rule "…" placed bone X instead` is a bone still guessed; `not used — …` names why an arm's joints did not place its sleeve (for a wrist on no part, the nearest part and the `--wrist-snap` that would bind it); a `wrist snap: …` note is a wrist bound past 15 px by the flag — check that chain's tip on `landmarks.png`; a `one handwear part holds both wrists` note is two chains on one mesh — cut the part with `assemble.cuts` to separate them; `LINT chest at … is not between the neck joint …`, `LINT hip at … is not past chest …`, `LINT hip at … is nearer the neck joint …` and `LINT <chain><i> at … does not advance from <s>_shoulder toward <s>_wrist` are the joint-frame lines: move that bone between, past or along the joints they name |
|
|
1584
1865
|
| `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 |
|
|
1585
1866
|
| `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` |
|
|
1586
|
-
| `check` (inside `build`) | the gate lines verbatim, the pack line, `loop:` (and `loop physics:` when the rig declares a physics constraint, §7), `seam:`, the six judgement lines, then — when `motion.json` holds an animation, the idle included — `ANIMATIONS_DO_NOT_FOLD` and one `folds` line per folding mesh, rigc's own (issues #320, #333), `RECOMPOSITE_HOLES` and, under `--source`, `SETUP_POSE_VS_SOURCE` (§7), `check.json`; the last line says how many of the bars measured — nine, ten with the fold bar — and names the ones that said SKIP | `check: PASS;
|
|
1867
|
+
| `check` (inside `build`) | the gate lines verbatim, the pack line, `loop:` (and `loop physics:` when the rig declares a physics constraint, §7), `seam:`, the six judgement lines, then — when `motion.json` holds an animation, the idle included — `ANIMATIONS_DO_NOT_FOLD` and one `folds` line per folding mesh, rigc's own (issues #320, #333), then — where a `footwear` part rides a bone that hangs from another — `FEET_STAY_LEVEL` and one `foot` line per such part (issue #348), then — when `parts.json` names a `handwear` part and there is an idle — `STRESS_OFF_CHAIN` (issue #347), then — where `parts.json` holds a limb segment's part — `LIMB_ONE_BONE` and one `limb` line per such part (issue #349), `RECOMPOSITE_HOLES` and, under `--source`, `SETUP_POSE_VS_SOURCE` (§7), `check.json`; the last line says how many of the bars measured — nine, ten with the fold bar, one more each with the feet-level bar, the stress bar and the limb bar — and names the ones that said SKIP | `check: PASS; 11 of 11 bar(s) measured, 0 skipped` on `demo` and `scarf`, whose sleeves each ride one chain; `check: PASS; 10 of 11 bar(s) measured, 1 skipped (STRESS_OFF_CHAIN)` on `reference-demo` and `reference-sample`, whose one `sleeves` mesh rides both sleeve chains (issue #347) — no example writes `FEET_STAY_LEVEL`, every shoe riding `root`; a judgement line SKIP only where the character lacks what it reads; on a rig spec with no `parts.json` or no `idle` (a merged rig, §7 *Measuring a rig rig-parts did not assemble*), PASS with the skipped bars named | `CHECK_SEAM_WITHIN_BAR` or `CHECK_LOOP_CLOSES` (§6); `CHECK_ANIMATIONS_DO_NOT_FOLD` — a clip turns a mesh over (§6); `CHECK_STRESS_OFF_CHAIN` — a sleeve weighted off its chain (§6); `CHECK_LIMB_ONE_BONE` — a limb part on a second bone (§6) |
|
|
1587
1868
|
| `check --requirements` (and `build --requirements`) | after the lines above, one line per declared requirement, `NAME: PASS`, `FAIL` or `NOT MEASURABLE — …` with its figures, the bar as declared and the worst frame; then `requirements: N declared — M measured (P PASS, F FAIL), K NOT MEASURABLE; not declared: <kinds>`; `check.json`'s `requirements` block; the renders under `requirements/` (§7, *Declared requirements*) | every line PASS and `0 NOT MEASURABLE`; a follow's `released_copy` says whether rigc's consumer-driven door was taken on the throwaway copy; a `stretch` line's `held_by` (issue #321), right after `worst`, names the bones the worst edge's two vertices hang on and their idle chains, in `CHECK_TEXTURE_STRETCH`'s words (§6) and by the same function, on a PASS as on a FAIL | `CHECK_REQUIREMENT_MET` (a figure past its bar; the line names the frame) or `CHECK_REQUIREMENT_MEASURABLE` (the quantity is undefined in the frames: a tip or an axis on a bone of length 0, an aim with no line, a follow no frame of which reaches its least drive); the last line then reads `check: FAIL — B bar(s) not met, R declared requirement(s) not PASS` |
|
|
1588
1869
|
| `compose` | the `[compose]` lines: one per character (its offset, its scale, its placed bounds, the shift and how many first-level bones, regions on `root` and weights bound to `root` it moved, its bone, slot, track and constraint counts, its `rig_scale` and idle), the declared order and the slot count it expands to, rigc's gate lines; then the `[check]` lines as for `check`, and `scene.json` | both gate summaries `0 failed`, `check: PASS` with the bars that measured and the ones that said SKIP named — on a composed rig `4 of 9`: the gate, the loop, `CHAIN_LAG` and `TEXTURE_STRETCH` (there is no `parts.json`; each character's own `build` measured all nine). `scene.json`'s `order.slots` is the order the compiled skeleton draws | a `SCENE_*` refusal (§6, *compose*) — the scene file, a build, or the order; `COMPOSE_RIGC_GREEN` — rigc's own line, quoted |
|
|
1589
1870
|
| `loop` (inside `build --loop`, or `loop --frames … --out …`) | the dropped-duplicate line, or, when the end frame is not frame 0, the jump line: its max \|d\| and px over 40, then why — under `build`, `the rig holds N physics constraint(s), and rigc's render starts every one at rest at frame 0` with the remedy `build --loop --lead-in <n>`, or `the rig holds no physics constraint, so the idle itself does not close` (`CHECK_LOOP_CLOSES`); under `loop --frames`, which reads frames alone, both causes (issue #325); under `build --lead-in`, the lead-in line in its place (the frames kept and the jump at the wrap); each file's line, then `loop: idle.png N B (lossless); idle-indexed.png N B (max …, mean …); idle.gif N B (max …, mean …)` | `f0048.png equals f0000.png byte for byte, so it is dropped` | `LOOP_ENCODE` (§6) |
|
|
@@ -1932,6 +2213,21 @@ note saying so — give it a mesh in `meshes` if it should bend. The one excepti
|
|
|
1932
2213
|
sleeve rule, which names its chains by side and takes a cut sleeve as a blob of its own
|
|
1933
2214
|
(the `one handwear part holds both wrists` note, §3).
|
|
1934
2215
|
|
|
2216
|
+
With one run whose file is not the `seethrough.resolution` square (issue #366, §4
|
|
2217
|
+
*For artists*), `parts.json` ends with a `single_run_pad` block: where the file sat on
|
|
2218
|
+
the square it was read on. Two runs and a file that is the square write none.
|
|
2219
|
+
|
|
2220
|
+
| field | holds |
|
|
2221
|
+
| --- | --- |
|
|
2222
|
+
| `file_size` | the file's `[w, h]` — the painting's too |
|
|
2223
|
+
| `side` | the square's side, `seethrough.resolution` |
|
|
2224
|
+
| `left`, `top` | the margins, `floor((side - w) / 2)` and `floor((side - h) / 2)`; the right and the bottom take the remainder |
|
|
2225
|
+
|
|
2226
|
+
Run pixel `(x, y)` is file pixel `(x - left, y - top)`, and a part's box over
|
|
2227
|
+
`scale_rig_per_source` is its box in the file. The reader refuses margins that are not
|
|
2228
|
+
that centring, a file larger than its square or equal to it, and a `rig_size` that is
|
|
2229
|
+
not `file_size` times the scale, truncated (`PARTS_SINGLE_RUN_PAD`).
|
|
2230
|
+
|
|
1935
2231
|
`visible_px + occluded_px = opaque_px`, and every projected pixel is visible; the stage
|
|
1936
2232
|
refuses (`ASSEMBLE_COUNTS_ADD_UP`) rather than write counts that break either, and the
|
|
1937
2233
|
reader refuses a record that breaks them (`PARTS_COUNTS_ADD_UP`). The three visibility
|
|
@@ -1983,7 +2279,11 @@ row in this page's `rule` and `line` tables; the selftest holds that (`TY16`).
|
|
|
1983
2279
|
| <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 |
|
|
1984
2280
|
| <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 |
|
|
1985
2281
|
| <a id="LAYERS_TAG_KNOWN"></a><a id="LAYERS_NAME_UNIQUE"></a><a id="LAYERS_BBOX_INSIDE_CANVAS"></a>`LAYERS_TAG_KNOWN`, `LAYERS_NAME_UNIQUE`, `LAYERS_BBOX_INSIDE_CANVAS` | a layer name that is neither a See-through v3 tag nor `author:<label>` (or is `author:` with a label that is empty, starts with `.` or whitespace, ends with whitespace, or holds a slash, backslash or colon — the detail says which), a repeated name, a box off the canvas | the run (these are See-through's own outputs); a layer you added by hand is named `author:<label>` (§5, *A layer the author made*) |
|
|
1986
|
-
| <a id="PSD_FILE_PRESENT"></a><a id="PSD_PARSES"></a><a id="PSD_IS_RGB8"></a><a id="PSD_HAS_LAYERS"></a><a id="PSD_LAYER_PLAIN"></a><a id="PSD_LAYER_HAS_PIXELS"></a>`PSD_FILE_PRESENT`, `PSD_PARSES`, `PSD_IS_RGB8`, `PSD_HAS_LAYERS`, `PSD_LAYER_PLAIN`, `PSD_LAYER_HAS_PIXELS` | the `.psd` is missing, unreadable, not 8-bit RGB, has no layer, or has a layer that is hidden, not at full opacity, or without pixels | the `.psd
|
|
2282
|
+
| <a id="PSD_FILE_PRESENT"></a><a id="PSD_PARSES"></a><a id="PSD_IS_RGB8"></a><a id="PSD_HAS_LAYERS"></a><a id="PSD_LAYER_PLAIN"></a><a id="PSD_LAYER_HAS_PIXELS"></a>`PSD_FILE_PRESENT`, `PSD_PARSES`, `PSD_IS_RGB8`, `PSD_HAS_LAYERS`, `PSD_LAYER_PLAIN`, `PSD_LAYER_HAS_PIXELS` | the `.psd` is missing, unreadable, not 8-bit RGB, has no layer, or has a layer that is hidden (a hidden-art layer, `<layer>:hidden`, may be), not at full opacity, blended other than normal, clipped, masked, or without pixels | the `.psd`; an artist's file: §4, *For artists* |
|
|
2283
|
+
| <a id="LAYERS_DEPTH_ALL_OR_NONE"></a>`LAYERS_DEPTH_ALL_OR_NONE` | some drawn layers of a wrapper `layers.json` carry a `depth_median` and others carry `null` (issue #359): a mix has no one draw order | a number on every drawn layer (a See-through run), or `null` on every one (an export of a PSD: the manifest's order is then the draw order) |
|
|
2284
|
+
| <a id="LAYERS_HIDDEN_OF_PRESENT"></a><a id="LAYERS_HIDDEN_NO_DEPTH"></a>`LAYERS_HIDDEN_OF_PRESENT`, `LAYERS_HIDDEN_NO_DEPTH` | a hidden-art layer, `<layer>:hidden` (issue #359), belongs to a drawn layer the input does not hold; or, in a wrapper `layers.json`, carries a `depth_median` other than `null` (it is never drawn) | the layer's name, or the layer it names; `depth_median: null` |
|
|
2285
|
+
| <a id="LAYERS_EXPORT_DIR_EMPTY"></a>`LAYERS_EXPORT_DIR_EMPTY` | `layers --export <dir>` was given a file, or a directory that is not empty (issue #359): a file left there could be read in place of the one written | a new directory, or empty it |
|
|
2286
|
+
| <a id="LAYERS_EXPORT_ROUND_TRIP"></a>`LAYERS_EXPORT_ROUND_TRIP` | the export, read back, differs from the input's reading (the detail names the first difference) — an exporter bug, not an input problem | report it with the input named |
|
|
1987
2287
|
| <a id="ASSEMBLE_SOURCE_PRESENT"></a><a id="ASSEMBLE_SOURCE_DECODES"></a>`ASSEMBLE_SOURCE_PRESENT`, `ASSEMBLE_SOURCE_DECODES` | the painting is missing or not a PNG | `--source` |
|
|
1988
2288
|
| <a id="SHEET_SOURCE_PRESENT"></a><a id="SHEET_PNG_PRESENT"></a>`SHEET_SOURCE_PRESENT`, `SHEET_PNG_PRESENT` | `sheet`'s painting, or a part PNG a `parts.json` lists, is missing | `--source`, `--layers` |
|
|
1989
2289
|
|
|
@@ -2051,6 +2351,8 @@ See-through with another seed.
|
|
|
2051
2351
|
| <a id="CONFIG_TAG_KNOWN"></a><a id="CONFIG_PART_NAME"></a><a id="CONFIG_PART_UNIQUE"></a>`CONFIG_TAG_KNOWN`, `CONFIG_PART_NAME`, `CONFIG_PART_UNIQUE` | a plan or extend entry names neither a v3 tag nor `author:<label>` (an `author:` tag with a malformed label says what the label must be), a part name that is not a file name, or a part or layer twice — a patch named like a plan part or another patch, and a cut's `into` named like a plan part, a patch or another cut's, included | `assemble.plan`, `assemble.patches`, `assemble.cuts` |
|
|
2052
2352
|
| <a id="CONFIG_NAME_RESOLVES"></a>`CONFIG_NAME_RESOLVES` | a parent, segment, region, track, blink member, blink still part or bone, extend part, patch or cut `draw.before`, or cut `from` names nothing declared above it (a cut's `from` must be a plan part); or a constraint's `bones`, `target`, `source` or `bone` names a bone `bones` does not declare (the detail names the constraint and lists the bones that exist); in `meshes.<part>.auto`, a bone or region name nothing declares | the named reference, or the declaration it needs |
|
|
2053
2353
|
| <a id="CONFIG_BONE_UNIQUE"></a>`CONFIG_BONE_UNIQUE` | a bone or chain declared twice, or `root` declared | `bones` |
|
|
2354
|
+
| <a id="CONFIG_BONE_INHERIT"></a>`CONFIG_BONE_INHERIT` | a single bone's `inherit` is a string that is not one of rig-c's five mode names as its table spells them (issue #348); the line lists all five. `"NoScale"` is refused too: the runtime would fold the first letter, and this loader folds and maps nothing | `bones[i].inherit` — `normal`, `onlyTranslation`, `noRotationOrReflection`, `noScale` or `noScaleOrReflection` (§3, `bones`) |
|
|
2355
|
+
| <a id="CONFIG_LIMB_BONE"></a><a id="CONFIG_LIMB_BONES_CHAIN"></a><a id="CONFIG_LIMB_FROM_RUN"></a><a id="CONFIG_LIMB_RANGE"></a>`CONFIG_LIMB_BONE`, `CONFIG_LIMB_BONES_CHAIN`, `CONFIG_LIMB_FROM_RUN`, `CONFIG_LIMB_RANGE` | `config.limbs` (issue #349): a limb part's `regions` entry names a bone other than its segment's; a segment's bone is not the child of the segment above's (the lower segment turns about the joint on a bone that hangs from the upper's); two segments of one limb take one part with another part between them; a `range` without `min <= 0 <= max` | `regions.<part>` the segment's `bone`; `bones` (parent the lower bone under the upper) or the segment's `bone`; the segments' order or `from`; the `range` (degrees from rest, rest in it) |
|
|
2054
2356
|
| <a id="CONFIG_PART_ATTACHED"></a>`CONFIG_PART_ATTACHED` | a plan part or a cut's piece with neither or both of a mesh and a region; a patch with a mesh, or with no region | `meshes` / `regions` |
|
|
2055
2357
|
| <a id="CONFIG_RIBS_MODE"></a>`CONFIG_RIBS_MODE` | `meshes.<part>.ribs` on a lattice (`grid`) mesh: its vertices are its grid's corners, which no row can move (issue #188) | put the part in `contour` or `auto`, or remove `ribs` |
|
|
2056
2358
|
| <a id="CONFIG_INFLUENCES_MODE"></a>`CONFIG_INFLUENCES_MODE` | `meshes.<part>.influences` on an automatic (`auto`) mesh: that mode's cap and floor are `auto.influences`, which rig-c's reduction is handed (issue #284) | declare them in `auto.influences`, or remove `influences` |
|
|
@@ -2077,6 +2379,24 @@ See-through with another seed.
|
|
|
2077
2379
|
| <a id="INPUTS_PAINTING_OPAQUE"></a>`INPUTS_PAINTING_OPAQUE` | the painting has a pixel below alpha 255 | the painting |
|
|
2078
2380
|
| <a id="INPUTS_HEAD_BOX_INSIDE"></a>`INPUTS_HEAD_BOX_INSIDE` | `seethrough.head_box` leaves the painting (the crop is not padded) | `head_box` — take `propose --head-box`'s |
|
|
2079
2381
|
|
|
2382
|
+
### painting
|
|
2383
|
+
|
|
2384
|
+
`rig-parts painting --keypoints <k.json> <painting.png>` (issue #350) reads the painting
|
|
2385
|
+
before See-through runs on it. *Background* is ground (alpha 8 or less, or every channel
|
|
2386
|
+
above 235) connected to the painting's edge; the *figure* is the rest; a *gap* is one
|
|
2387
|
+
background pixel on the straight line between two joints. [PAINTING.md](PAINTING.md)
|
|
2388
|
+
says why each requirement exists and what breaks without it.
|
|
2389
|
+
|
|
2390
|
+
| rule | means | change |
|
|
2391
|
+
| --- | --- | --- |
|
|
2392
|
+
| <a id="PAINTING_SOURCE_PRESENT"></a><a id="PAINTING_SOURCE_DECODES"></a>`PAINTING_SOURCE_PRESENT`, `PAINTING_SOURCE_DECODES` | the painting is missing or not a PNG | the path |
|
|
2393
|
+
| <a id="PAINTING_BACKGROUND_PLAIN"></a>`PAINTING_BACKGROUND_PLAIN` | a border pixel is neither near-white nor unpainted: scenery, a floor, a gradient or a vignette reaches the edge, or the figure is cut by the frame; the detail counts them and names the first | a painting on a plain light background with the whole figure inside the frame (PAINTING.md, *Background*) |
|
|
2394
|
+
| <a id="PAINTING_JOINTS_STATED"></a>`PAINTING_JOINTS_STATED` | a wrist, hip, knee or ankle is `occluded` or `missing` in the keypoint file: the gap rules read those eight joints, and a hand or foot the painting does not show is a limb nothing can rig | a painting that shows it; or the joint, if the estimator missed one the painting shows |
|
|
2395
|
+
| <a id="PAINTING_JOINT_ON_FIGURE"></a>`PAINTING_JOINT_ON_FIGURE` | an observed joint's pixel is background | the keypoint file (measured on this painting?), or the painting |
|
|
2396
|
+
| <a id="PAINTING_ARM_CLEAR"></a>`PAINTING_ARM_CLEAR` | the line from a wrist to the hip of its side crosses no background: the arm is painted against the body. Hands that meet close a loop round the torso, so this fires with `PAINTING_HANDS_APART` on both sides | a painting with the arm held away from the body (PAINTING.md, *Arms*) |
|
|
2397
|
+
| <a id="PAINTING_HANDS_APART"></a>`PAINTING_HANDS_APART` | the line between the two wrists crosses no background: hands clasped or touching | a painting with the hands apart and open (PAINTING.md, *Hands*) |
|
|
2398
|
+
| <a id="PAINTING_LEGS_APART"></a>`PAINTING_LEGS_APART` | the line between the knees, or between the ankles, crosses no background: the legs are one column there (a long skirt over the knees is refused here too) | a painting with the legs apart and a visible gap at the knees and the ankles (PAINTING.md, *Legs*) |
|
|
2399
|
+
|
|
2080
2400
|
### The optional ComfyUI adapter (`comfy paint`, `comfy seethrough`)
|
|
2081
2401
|
|
|
2082
2402
|
| rule | means | change |
|
|
@@ -2096,7 +2416,11 @@ See-through with another seed.
|
|
|
2096
2416
|
| <a id="ASSEMBLE_FIELD_PRESENT"></a>`ASSEMBLE_FIELD_PRESENT` | `seethrough.head_box` is missing | `propose --head-box` proposes it (§3) |
|
|
2097
2417
|
| <a id="ASSEMBLE_RIG_SIZE"></a>`ASSEMBLE_RIG_SIZE` | `rig_scale` makes an empty rig | `assemble.rig_scale` |
|
|
2098
2418
|
| <a id="ASSEMBLE_HEAD_BOX_INSIDE"></a>`ASSEMBLE_HEAD_BOX_INSIDE` | the head box is outside the painting | `head_box` — `propose --head-box` holds it inside |
|
|
2099
|
-
| <a id="ASSEMBLE_RUN_CANVAS"></a>`ASSEMBLE_RUN_CANVAS` | a run's canvas is not `resolution` square | `seethrough.resolution`, or the run |
|
|
2419
|
+
| <a id="ASSEMBLE_RUN_CANVAS"></a>`ASSEMBLE_RUN_CANVAS` | a run's canvas is not `resolution` square; with one run (issue #366), a side of the file is above `resolution` — a smaller one is padded, a larger one would have to be shrunk | `seethrough.resolution`, or the run |
|
|
2420
|
+
| <a id="ASSEMBLE_SINGLE_RUN_SOURCE"></a>`ASSEMBLE_SINGLE_RUN_SOURCE` | with no `--head` (issue #366), the painting `--source` names is not the one run's file's size: the painting is the file flattened, and every part's box maps back to the file through it | the painting — the file flattened, at the file's size |
|
|
2421
|
+
| <a id="ASSEMBLE_SINGLE_RUN_FACE"></a>`ASSEMBLE_SINGLE_RUN_FACE` | with no `--head` (issue #359), the one run holds no painted layer for one of `face`, `eyewhite-r`, `eyewhite-l` — the parts the head run exists to separate (one problem per tag) | paint the layer, or give the head run with `--head` |
|
|
2422
|
+
| <a id="ASSEMBLE_SINGLE_RUN_PLAN"></a>`ASSEMBLE_SINGLE_RUN_PLAN` | with no `--head`, a plan row takes `"head"`, or an `extend_below_crop` entry continues a part below a crop there is not | `"full"` in the row; remove the entry |
|
|
2423
|
+
| <a id="ASSEMBLE_SINGLE_RUN_HEAD_BOX"></a>`ASSEMBLE_SINGLE_RUN_HEAD_BOX` | with no `--head`, the config sets `seethrough.head_box`, which places a head run there is not | remove `head_box`, or give the head run with `--head` |
|
|
2100
2424
|
| <a id="ASSEMBLE_PLAN_TAG_IN_RUN"></a><a id="ASSEMBLE_EXTEND_TAG_IN_RUN"></a>`ASSEMBLE_PLAN_TAG_IN_RUN`, `ASSEMBLE_EXTEND_TAG_IN_RUN` | an entry takes a tag its run does not hold (the detail lists what it does hold) | that entry's run or tag |
|
|
2101
2425
|
| <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 |
|
|
2102
2426
|
| <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 |
|
|
@@ -2108,6 +2432,10 @@ See-through with another seed.
|
|
|
2108
2432
|
| <a id="ASSEMBLE_UNDER_SIZE"></a>`ASSEMBLE_UNDER_SIZE` | a cut's `under` image is not the size of its polygon's bounding box (the detail gives both, and the box's corner) | the image: `max x − min x` by `max y − min y` of the polygon, in rig pixels |
|
|
2109
2433
|
| <a id="ASSEMBLE_UNDER_EMPTY"></a>`ASSEMBLE_UNDER_EMPTY` | a cut's `under` image fills none of the pixels the cut removed: its alpha is below the alpha removed at every one (all transparent there, or another file) | the image or its path; to leave the whole hole, drop `under` |
|
|
2110
2434
|
| <a id="ASSEMBLE_UNDER_DRAW"></a>`ASSEMBLE_UNDER_DRAW` | a cut with `under` draws its piece behind its part, which the fill would hide it under | that cut's `draw`: `"front"`, or `{"before": …}` the plan part after its `from` |
|
|
2435
|
+
| <a id="ASSEMBLE_LIMB_TAKES"></a><a id="ASSEMBLE_LIMB_PIXELS"></a>`ASSEMBLE_LIMB_TAKES`, `ASSEMBLE_LIMB_PIXELS` | a part a limb takes has no opaque pixel, or a limb segment gets none of it — the joint lines through the bones' origins put none of the limb's pixels on it, or another limb's polyline is nearer every one (issue #349) | the bones: their origins on the limb's art, each joint where the limb bends |
|
|
2436
|
+
| <a id="ASSEMBLE_LIMB_JOINT"></a><a id="ASSEMBLE_LIMB_JOINT_ON_ART"></a>`ASSEMBLE_LIMB_JOINT`, `ASSEMBLE_LIMB_JOINT_ON_ART` | a joint has no line — the upper bone has no length, or the limb folds straight back on itself — or its bone's origin lies on none of the limb's art within 1 px along the line, so the limb has no width there to size the cap by (issue #349) | the joint's bone: on the limb's art, at the joint |
|
|
2437
|
+
| <a id="ASSEMBLE_LIMB_CAP_PRESENT"></a><a id="ASSEMBLE_LIMB_CAP_DECODES"></a><a id="ASSEMBLE_LIMB_CAP_SIZE"></a><a id="ASSEMBLE_LIMB_CAP_EMPTY"></a>`ASSEMBLE_LIMB_CAP_PRESENT`, `ASSEMBLE_LIMB_CAP_DECODES`, `ASSEMBLE_LIMB_CAP_SIZE`, `ASSEMBLE_LIMB_CAP_EMPTY` | a segment's `cap` names no file (relative to the config's directory) or one that is not a PNG; an image not the size of the cap's box (the detail gives both, and the box's corner); or one transparent over every cap pixel (issue #349) | the image: the box the `limb cap:` line prints, `w`x`h`, its pixel `(i, j)` rig pixel `(x + i, y + j)`; to keep the layer's cap, drop `cap` |
|
|
2438
|
+
| <a id="ASSEMBLE_HIDDEN_ONE_SOURCE"></a>`ASSEMBLE_HIDDEN_ONE_SOURCE` | a cut's `under`, or a limb segment's `cap`, names a file, and the part it reads also has a hidden-art layer (`<layer>:hidden`, issue #359): reading either would be a guess | remove the file from the config, or the hidden-art layer from the input |
|
|
2111
2439
|
| <a id="ASSEMBLE_COUNTS_ADD_UP"></a>`ASSEMBLE_COUNTS_ADD_UP` | a part's visible and occluded counts do not add up to its opaque pixels, or a projected pixel is not visible, or a cut part's pieces do not add up to it — an assembler bug, not an input problem | report it with the part named; nothing was written |
|
|
2112
2440
|
|
|
2113
2441
|
### propose
|
|
@@ -2118,15 +2446,16 @@ See-through with another seed.
|
|
|
2118
2446
|
| <a id="PROPOSE_SOURCE_PRESENT"></a><a id="PROPOSE_PNG_PRESENT"></a><a id="PROPOSE_PNG_MATCHES_BOX"></a>`PROPOSE_SOURCE_PRESENT`, `PROPOSE_PNG_PRESENT`, `PROPOSE_PNG_MATCHES_BOX` | the painting or a part PNG is missing, or a PNG is not its box | `--source`, `--parts` (re-run assemble) |
|
|
2119
2447
|
| <a id="PROPOSE_FACE_PRESENT"></a>`PROPOSE_FACE_PRESENT` | no part comes from a `face` layer, and the face-less fallback cannot derive a face box either: it needs the head run's `neck` and at least one head-run `front hair`/`back hair` part whose top is above the neck's, and the message names the half that is missing. With both, `propose` does not refuse — it derives the box and says so in its first note (§5) | `assemble.plan`: take the face, or the head run's hair and neck |
|
|
2120
2448
|
| <a id="PROPOSE_CUT_FROM_RECORDED"></a>`PROPOSE_CUT_FROM_RECORDED` | two parts share one layer and none of them records `cut_from`: a `parts.json` an `assemble` before 1.6.0 wrote with `assemble.cuts`. The records are in draw order, so which is the plan part and which the piece cannot be read off the file; the message names the parts and the layer. Every other reader still takes the file | `--parts`: re-run assemble (1.6.0+) to record `cut_from` |
|
|
2449
|
+
| <a id="PROPOSE_LIMB_PARTS"></a>`PROPOSE_LIMB_PARTS` | `parts.json` holds parts `config.limbs` cut at their joints (issue #349); the limbs are drafted from the parts before the cut, which the rules read by layer | `--parts` a build assembled with the config's `limbs` left out; keep the block the config holds (its caps and ranges are the author's). `propose --from-config` reads the config's bones on either build |
|
|
2121
2450
|
| <a id="PROPOSE_ACCESSORY_BODY"></a>`PROPOSE_ACCESSORY_BODY` | an accessory has nothing above its pendant rows to hang its bone on | that part's plan entry, or author its bones by hand |
|
|
2122
|
-
| <a id="PROPOSE_TURN_RUN_GIVEN"></a><a id="PROPOSE_TURN_LAYER_PRESENT"></a><a id="PROPOSE_TURN_DEPTH_PRESENT"></a>`PROPOSE_TURN_RUN_GIVEN`, `PROPOSE_TURN_LAYER_PRESENT`, `PROPOSE_TURN_DEPTH_PRESENT` | with `--full`/`--head` (issue #251): a part came from a run whose layers were not given, the run given holds no layer of the part's tag, or its layer carries no `depth_median` (a PSD
|
|
2451
|
+
| <a id="PROPOSE_TURN_RUN_GIVEN"></a><a id="PROPOSE_TURN_LAYER_PRESENT"></a><a id="PROPOSE_TURN_DEPTH_PRESENT"></a>`PROPOSE_TURN_RUN_GIVEN`, `PROPOSE_TURN_LAYER_PRESENT`, `PROPOSE_TURN_DEPTH_PRESENT` | with `--full`/`--head` (issue #251): a part came from a run whose layers were not given, the run given holds no layer of the part's tag, or its layer carries no `depth_median` while another part's layer carries one; a run with no depth on any part's layer (a PSD, or an export of one) is not refused — the turn is skipped, and the note says `turn: SKIP` and names the run (issue #359) | give the run the parts were assembled from, as the wrapper's `layers.json` |
|
|
2123
2452
|
| <a id="BASIS_RECORDS_EVERY_BONE"></a><a id="BASIS_NAMES_RESOLVE"></a><a id="BASIS_KIND_KNOWN"></a><a id="BASIS_KIND_HOLDS"></a>`BASIS_RECORDS_EVERY_BONE`, `BASIS_NAMES_RESOLVE`, `BASIS_KIND_KNOWN`, `BASIS_KIND_HOLDS` | `propose --keypoints`'s record of how it placed each bone, `basis.json`, does not hold to the proposal it was written with: a proposed bone with no record, one recorded twice or out of the proposal's order; a part, bone or frame quantity it names that is not there; a placement kind outside the known ones; or a placement that does not hold what its kind says (a joint placement with no joint, a non-joint one with a joint, a derived one with no bone, a constant that is not finite). Nothing is written | a proposer bug, not an input problem: report it with the line |
|
|
2124
2453
|
| <a id="KEYPOINTS_FILE_PRESENT"></a><a id="KEYPOINTS_IS_JSON"></a><a id="KEYPOINTS_KEY_KNOWN"></a><a id="KEYPOINTS_FIELD_PRESENT"></a><a id="KEYPOINTS_FIELD_TYPE"></a>`KEYPOINTS_FILE_PRESENT`, `KEYPOINTS_IS_JSON`, `KEYPOINTS_KEY_KNOWN`, `KEYPOINTS_FIELD_PRESENT`, `KEYPOINTS_FIELD_TYPE` | the `--keypoints` file is absent, not JSON, has a key this reader does not know, lacks one of `spec`, `space`, `width`, `height`, `source`, `people`, or holds a value of the wrong type (a size that is not a whole positive px count, an empty `source`, no people) | the keypoint file (§3, *A posed figure*) |
|
|
2125
2454
|
| <a id="KEYPOINTS_SPEC_KNOWN"></a><a id="KEYPOINTS_SPACE_STATED"></a>`KEYPOINTS_SPEC_KNOWN`, `KEYPOINTS_SPACE_STATED` | the file is not `spine-parts-keypoints/1`, or states a space other than painting-px, origin top-left, y down | convert the file to that space; it is not read as if it were in it |
|
|
2126
2455
|
| <a id="KEYPOINTS_JOINT_KNOWN"></a><a id="KEYPOINTS_JOINT_STATE"></a><a id="KEYPOINTS_POSITION_STATED"></a><a id="KEYPOINTS_POSITION_INSIDE"></a>`KEYPOINTS_JOINT_KNOWN`, `KEYPOINTS_JOINT_STATE`, `KEYPOINTS_POSITION_STATED`, `KEYPOINTS_POSITION_INSIDE` | a joint name outside body-18, a state outside observed / occluded / missing, an observed joint with no `at` or a missing one with one, or a position outside the `width`x`height` image | that joint |
|
|
2127
2456
|
| <a id="KEYPOINTS_PERSON_UNIQUE"></a><a id="KEYPOINTS_PERSON_CHOSEN"></a>`KEYPOINTS_PERSON_UNIQUE`, `KEYPOINTS_PERSON_CHOSEN` | two people share an id; or the file holds more than one person and no `--person` names one, or `--person` names none of them (the message lists the ids) | `--person <id>` |
|
|
2128
2457
|
| <a id="KEYPOINTS_IMAGE_SIZE"></a>`KEYPOINTS_IMAGE_SIZE` | the file's `width`x`height` is not the `--source` painting's | a file measured on this painting |
|
|
2129
|
-
| <a id="PARTS_FILE_PRESENT"></a><a id="PARTS_IS_JSON"></a><a id="PARTS_KEY_KNOWN"></a><a id="PARTS_FIELD_PRESENT"></a><a id="PARTS_FIELD_TYPE"></a><a id="PARTS_NAME_UNIQUE"></a><a id="PARTS_FROM_KNOWN"></a><a id="PARTS_CUT_FROM_KNOWN"></a><a id="PARTS_BOX_INSIDE_RIG"></a><a id="PARTS_COUNTS_ADD_UP"></a><a id="PARTS_HOLES_LARGEST_FIRST"></a><a id="PARTS_HOLE_PART_KNOWN"></a>`PARTS_*` (`PARTS_FILE_PRESENT`, `PARTS_IS_JSON`, `PARTS_KEY_KNOWN`, `PARTS_FIELD_PRESENT`, `PARTS_FIELD_TYPE`, `PARTS_NAME_UNIQUE`, `PARTS_FROM_KNOWN`, `PARTS_CUT_FROM_KNOWN`, `PARTS_BOX_INSIDE_RIG`, `PARTS_COUNTS_ADD_UP`, `PARTS_HOLES_LARGEST_FIRST`, `PARTS_HOLE_PART_KNOWN`) | the `parts.json` read is not assemble's contract (`PARTS_COUNTS_ADD_UP`: `visible_px + occluded_px` is not `opaque_px`, `visible_not_projected_px` is above `visible_px`, or the `recomposite` block's figures disagree — more uncovered than error pixels, a hole larger than its box, the wrong number of holes listed, listed areas that do not sum to `uncovered_error_px`; the last two codes are the block's order and its border names, §5) | re-run assemble; do not edit `parts.json` |
|
|
2458
|
+
| <a id="PARTS_FILE_PRESENT"></a><a id="PARTS_IS_JSON"></a><a id="PARTS_KEY_KNOWN"></a><a id="PARTS_FIELD_PRESENT"></a><a id="PARTS_FIELD_TYPE"></a><a id="PARTS_NAME_UNIQUE"></a><a id="PARTS_FROM_KNOWN"></a><a id="PARTS_CUT_FROM_KNOWN"></a><a id="PARTS_BOX_INSIDE_RIG"></a><a id="PARTS_COUNTS_ADD_UP"></a><a id="PARTS_HOLES_LARGEST_FIRST"></a><a id="PARTS_HOLE_PART_KNOWN"></a><a id="PARTS_SINGLE_RUN_PAD"></a>`PARTS_*` (`PARTS_FILE_PRESENT`, `PARTS_IS_JSON`, `PARTS_KEY_KNOWN`, `PARTS_FIELD_PRESENT`, `PARTS_FIELD_TYPE`, `PARTS_NAME_UNIQUE`, `PARTS_FROM_KNOWN`, `PARTS_CUT_FROM_KNOWN`, `PARTS_BOX_INSIDE_RIG`, `PARTS_COUNTS_ADD_UP`, `PARTS_HOLES_LARGEST_FIRST`, `PARTS_HOLE_PART_KNOWN`, `PARTS_SINGLE_RUN_PAD`) | the `parts.json` read is not assemble's contract (`PARTS_COUNTS_ADD_UP`: `visible_px + occluded_px` is not `opaque_px`, `visible_not_projected_px` is above `visible_px`, or the `recomposite` block's figures disagree — more uncovered than error pixels, a hole larger than its box, the wrong number of holes listed, listed areas that do not sum to `uncovered_error_px`; the last two codes are the block's order and its border names; `PARTS_SINGLE_RUN_PAD`: the `single_run_pad` block's margins are not the centring, its file is not smaller than its square, or `rig_size` is not the file at the scale, issue #366 — §5) | re-run assemble; do not edit `parts.json` |
|
|
2130
2459
|
|
|
2131
2460
|
### compare
|
|
2132
2461
|
|
|
@@ -2221,6 +2550,9 @@ documents do not ship). Each is an input this package built; none is an author's
|
|
|
2221
2550
|
| <a id="CHECK_STILL_REGIONS_DARK"></a>`CHECK_STILL_REGIONS_DARK` | a slot of the face's still set (the `face` slots and every slot drawn over the face that the head bone alone carries) moves relative to the bone the face's slot rides, or a `footwear` slot moves on the screen, by more than the arithmetic's own rounding — measured from the idle's posed geometry (the slot, its largest displacement in rig px, the idle frame and the tolerance quoted) | on the face: a face mesh with a weight on a bone other than the head (`segments`), or one deformed by another bone; on the feet: a mesh weighted to a swinging bone, or a region on the wrong bone. A bang swinging over the face is not this line's failure: its depth is reported (`crossing`), with no bar |
|
|
2222
2551
|
| <a id="CHECK_TEXTURE_STRETCH"></a>`CHECK_TEXTURE_STRETCH` | a mesh triangle's edge, in some idle frame, is more than 1.926544 times its rest length or less than 1/1.926544 of it (the mesh, triangle, its three vertices, the edge and the frame are named, and — issue #314 — the bones the edge's two vertices hang on: each vertex's two largest weights from `rig.json`, `vertex <i> is weighted to "<bone>" <w>, …`, then `on the idle's chains, the chain from "<first link>" carries "<bone>", …` — each chain `CHAIN_LAG` reads named once, by its first link — with a bone on no chain said, or `none of these bones hangs on a chain of the idle`); or a rest edge has length 0 | the chain tracks' `amps` on the bones that mesh is weighted to (the stretch grows with them), or the mesh's `segments` — a vertex blending two bones that swing against each other. Two chains named, with opposed amplitudes: give both one sign, or drop one — `propose --from-config` measures the pair before a build (§3, *Two chains on one lattice*). An edge whose two ends disagree on whether a bone's small share survives the 0.03 cut (one vertex keeps the bone at a few hundredths, the next drops it — read the two vertices' weights the line quotes): declare `influences: {floor: "continuous"}` on that mesh first, `rule: "heat"` second; the weight `exponent` moves the figure without removing the cut (issue #115, `reference-demo` `hair_back` as contour at tolerance 1, rig-c 2.47.1: 2.535 as drafted; continuous floor 1.422, heat 1.816, both PASS; exponent 1.5 / 3 / 4 / 6 read 2.564 / 1.902 / 2.119 / 4.816, the one pass on the same cut at another edge) |
|
|
2223
2552
|
| <a id="CHECK_ANIMATIONS_DO_NOT_FOLD"></a>`CHECK_ANIMATIONS_DO_NOT_FOLD` | an animation, the idle included, turns a mesh triangle over against the setup pose on some frame, read by `rigc render --folds` over the build with its physics constraints left out (issues #320, #333); the object names the animation and the mesh, the detail quotes rigc's line — the frames, the worst frame, which triangles, their world box and setup-pose area. Or a `may_fold` entry of the `--requirements` file names a slot and animation that does not fold: an exception that excepts nothing | the clip's keys on the bones that mesh is weighted to (a bend past what the weights carry, a translate that carries one row of vertices past the next), or the mesh's `segments`; to look, `rigc inspect --candidate <out>/check/build --show art,folds,mesh --animation <name> --at <the worst frame's time> --zoom folds`. A fold the art means (a sleeve behind the body) is declared, `"may_fold": [{ "slot": …, "animation": …, "why": … }]` (§7, *Declared requirements*); a stale entry is taken out |
|
|
2553
|
+
| <a id="CHECK_FEET_STAY_LEVEL"></a>`CHECK_FEET_STAY_LEVEL` | a `footwear` part riding a bone that hangs from another turns more than ±10 degrees from its setup pose on a frame where a bone above that bone is turned (issue #348) — the bar is HQ's line (rig-c/parts#347 ruling 4), not a figure measured here; the object names the part, its bone and the bone it hangs from, the detail the animation, frame and time, the turned bone and its turn, the drawing's turn, and every bone the turn came through with its `inherit` | config.bones' `inherit` on the foot's bone (§3, `bones`): a mode that does not take the parent's rotation (`noRotationOrReflection`, `onlyTranslation`) keeps the drawing at its setup angle; which mode is the author's. Or the clip's keys on the foot's own bone, which turn the shoe whatever its mode |
|
|
2554
|
+
| <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) |
|
|
2555
|
+
| <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 |
|
|
2224
2556
|
| <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` |
|
|
2225
2557
|
| <a id="CHECK_SEAM_FRAME_SIZE"></a><a id="FRAMES_SIDECAR"></a><a id="CHECK_GEOMETRY_FILE"></a>`CHECK_SEAM_FRAME_SIZE`, `FRAMES_SIDECAR`, `CHECK_GEOMETRY_FILE` | rigc's render is not what its `frames.json` says, or its `geometry.json` is not a whole `rigc-geometry/1` export of the frames beside it (a frame count, a mesh whose vertex count differs from its rest entry; for the face half, another viewport than `frames.json`'s, other frame indices, the head bone or a feature's bone missing from a frame, a non-finite transform or time) | a rigc problem; report it |
|
|
2226
2558
|
| <a id="LOOP_ENCODE"></a>`LOOP_ENCODE` | the loop encoder refused a frame (translucent pixel in a GIF, a size change) | the frames; for a translucent frame write the lossless or the indexed APNG, which keep alpha |
|
|
@@ -2392,6 +2724,9 @@ the two examples [observed]:
|
|
|
2392
2724
|
| <a id="STILL_REGIONS_DARK"></a>`STILL_REGIONS_DARK` | from the idle's posed geometry (`geometry.json`), `instrument: "geometry"` in each half (issue #123). **Face:** the still set — the `face` slots, and every slot drawn after the first of them whose art overlaps a face part's at the setup pose, that the head bone alone carries (a region on it, or a mesh whose every weight is it) and that the idle does not key by slot (`deform`, `sequence`, a slot track: `driven_slots`). The head bone is the bone the `face` parts' slot rides in `rig.json`. Each still slot's art (alpha above 8), carried through its posed attachment and back through the head bone's setup-to-frame map on every idle frame, against its setup position: `max_rig_px` (and where, `at`, when it is a motion), `rms_rig_px`, per slot in `still_slots`. **Feet:** the `footwear` slots the same way in screen space — they ride the root, which the idle does not key, so the screen is their own frame. **Reported, no bar:** `crossing` — per slot drawn after the still set's first slot and not in it, how deep its art reaches into the region (`face` on top at the setup pose, clear of where `eyewhite`, `irides`, `eyelash`, `eyebrow` and `mouth` go in the head's frame; for the feet, `footwear` on top), `depth_rig_px`, and the most of it covered on one frame, `area_rig_px2` | each half's `max_rig_px` ≤ its `tolerance_rig_px`: nothing moves, up to the arithmetic's rounding, √2 × 2 × 35 × M² × ulp(C) (below) | face 4.202e-13 ≤ 2.286e-11; `hair_front` crossing 2.816 rig px deep, 89.395 rig px² (frame 47); feet 0 ≤ 2.814e-12 | face 4.418e-13 ≤ 2.286e-11 (`face`, `ear_l`, `ear_r`); `hair_front` 2.194 deep, 50.605 rig px² (frame 23); feet 0 ≤ 2.814e-12 | no `face` and no `footwear` part (one of the two absent leaves that half unmeasured; so do face parts on two slot bones, or idle frames with no `geometry.json`) |
|
|
2393
2725
|
| <a id="TEXTURE_STRETCH"></a>`TEXTURE_STRETCH` | every mesh triangle's three edges in every idle frame, read from the `geometry.json` the idle render writes (`rigc render --geometry`, rig-c 1.4.0 or later): skinned length over rest length, the rest being the setup pose's bones with no deform. The figure is the rig's worst max(ratio, 1/ratio); each mesh's largest and smallest ratio, with triangle, vertices, edge and frame, is the detail | max(ratio, 1/ratio) ≤ 1.926544 | 1.388: `hair_back` triangle 449, edge 42-43 at 0.720 in frame 26 (largest stretch 1.291, frame 2) | 1.227: `sleeves` triangle 169, edge 126-51 at 0.815 in frame 28 (largest stretch 1.196, frame 5) | the rig draws no mesh, or the render wrote no `geometry.json` |
|
|
2394
2726
|
| <a id="ANIMATIONS_DO_NOT_FOLD"></a>`ANIMATIONS_DO_NOT_FOLD` | every animation, the idle included, through one `rigc render --folds` at 12 fps over the build with its physics constraints left out: the meshes with a triangle turned over against the setup pose on some frame, less those the requirements file's `may_fold` declares (below) | 0 undeclared folding meshes, and no `may_fold` entry that matches no fold | 0, over `idle` | 0, over `idle` and `hop`, 2 physics constraints left out | never SKIP: written whenever `motion.json` holds an animation |
|
|
2727
|
+
| <a id="FEET_STAY_LEVEL"></a>`FEET_STAY_LEVEL` | issue #348. Each `footwear` part whose slot's bone hangs from another bone (rig.json's `parent`; a part on `root` hangs from nothing and is not read), over every animation, the idle included, from the same `rigc render --folds` run, which then also writes `--geometry`: on each frame where a bone above the part's bone — its parent or any bone over that — has a world rotation other than its setup pose's (no threshold; the nearest is named), the drawing's turn: the least-squares rotation carrying the attachment's setup-pose vertices onto the frame's, both centred (a region's four corners turn exactly with its bone). **This is the reading of "the sole's angle"**: no rig data marks a sole, so level is the drawing's setup angle, the painting's own; a declared sole line would replace it if one ever exists | every read foot within ±10 degrees — HQ's line (rig-c/parts#347 ruling 4), not a figure measured here | not written: the shoes ride `root` | not written: the shoes ride `root` | SKIP when no frame turns a bone above any read foot (not counted as measured); not written at all when no `footwear` part hangs from a bone, or `motion.json` holds no animation, so the examples' `check.json` keep their keys |
|
|
2728
|
+
| <a id="STRESS_OFF_CHAIN"></a>`STRESS_OFF_CHAIN` | each mesh from a See-through `handwear` layer whose weights ride exactly one chain the idle keys: the chain's first link turned 90°, −90° and 180° from its setup rotation (interim, issue #347: until a config block declares each limb and its joint's range), one `rigc build --profile spine` and one `rigc render --geometry` of a copy with its physics constraints left out; per turn, the part's worst edge as max(ratio, 1/ratio) against the setup pose, at four places, and the bones it is weighted to off the chain. A part riding two chains, or none, or not a weighted mesh, is listed under `not_read` with the reason, and so is a part a `config.limbs` segment made (issue #349): `LIMB_ONE_BONE` holds it to its one bone, so it turns rigidly by construction | 1.0000 — the part turning rigidly with its chain; float32 noise on rigid sleeves measured at most 1.0000090 | SKIP: `sleeves` rides 2 chains | SKIP: `sleeves` rides 2 chains | no part comes from a `handwear` layer or the motion has no idle (the line is not written); every `handwear` part not read (SKIP, each reason) |
|
|
2729
|
+
| <a id="LIMB_ONE_BONE"></a>`LIMB_ONE_BONE` | issue #349. Each part `parts.json` records as a limb segment's (`limb`), read off `rig.json` alone, nothing rendered: the bones its attachments are bound to — a mesh's weights, or its slot's bone for a region — against its segment's bone (under `config.turn`, that bone's leaf twin counts as the bone) | every limb part bound to its one bone alone, weight 1 | not written: no limbs | not written: no limbs | never: written only when `parts.json` holds a limb part, so the examples' `check.json` keep their keys |
|
|
2395
2730
|
|
|
2396
2731
|
**`TIP_OVER_ROOT` is measured from geometry, not pixels (issue #118).** Until #118 the
|
|
2397
2732
|
line rendered each part alone at `--max 640` and took, per half of its frame box, the
|
|
@@ -2521,6 +2856,30 @@ The selftest's planted cases (by hand):
|
|
|
2521
2856
|
| a foot whose root moves `fround(0.05)` rig px | PASS, heat 0.779–0.785 | FAIL, 0.05 rig px |
|
|
2522
2857
|
| a face mesh weighted to a bone sliding `fround(0.05)` under a rolling head | PASS, 0.851–0.855 against 0.936–0.948 | FAIL, 0.05 rig px |
|
|
2523
2858
|
|
|
2859
|
+
**`STRESS_OFF_CHAIN`: a sleeve turned far (issue #347).** The idle moves a sleeve a few
|
|
2860
|
+
degrees, so a sleeve that only looks right at rest passes every idle bar and is first seen
|
|
2861
|
+
broken by whoever poses it. This bar poses it first. For each part from a See-through
|
|
2862
|
+
`handwear` layer whose weights ride exactly one chain the idle keys, the chain's first link
|
|
2863
|
+
(the bone the idle keys) is turned 90°, −90° and 180° from its setup rotation — the card's
|
|
2864
|
+
arm raised, arm across and arm behind the head — on a throwaway copy with its physics
|
|
2865
|
+
constraints left out: one `rigc build --profile spine`, one `rigc render --geometry` at
|
|
2866
|
+
rigc's smallest `--max` (the reading is world units), and the part's worst edge as
|
|
2867
|
+
max(ratio, 1/ratio) against the setup pose, the quantity `TEXTURE_STRETCH` reads. A part
|
|
2868
|
+
weighted to its chain alone turns with the link as one piece, so it reads exactly 1; the bar
|
|
2869
|
+
is that reading at four places, **1.0000** — not a margin: the rigid sleeves of `demo` and of
|
|
2870
|
+
a test rig read 1.0000038 to 1.0000090 (float32 bone arithmetic), and the same sleeves with a
|
|
2871
|
+
16 px `chest` segment at the shoulder read 2.41 to 1,498 (rigc 2.47.1). A vertex weighted to a
|
|
2872
|
+
bone the turn does not move is pulled between the two, and the line names that bone, how many
|
|
2873
|
+
vertices carry it and the largest weight: the segment on it in `meshes.<part>.segments` is
|
|
2874
|
+
the one to drop. `propose` drafted that stub on every sleeve until issue #347; `demo` and
|
|
2875
|
+
`scarf` carried it, and dropping it moved neither the setup-pose seam nor any bar's status.
|
|
2876
|
+
Two limits, both stated rather than guessed. **The turns are interim:** a config block that
|
|
2877
|
+
names each limb and its joint's declared range is the next card, and the bar then poses each
|
|
2878
|
+
limb at its range's ends. **The scope is the `handwear` tag:** a part riding two chains (one
|
|
2879
|
+
mesh for both sleeves, as `reference-demo` and `reference-sample` have) stretches between them
|
|
2880
|
+
by construction and is listed under `not_read` with that reason, and a one-chain pendant on
|
|
2881
|
+
another tag is not read — `reference-demo`'s `hairpin`, its tassel turned 180°, reads 23.7.
|
|
2882
|
+
|
|
2524
2883
|
**`ANIMATIONS_DO_NOT_FOLD`: every animation, the idle included (issues #320, #333).** The one
|
|
2525
2884
|
bar that reads an animation other than the idle, and it reads the idle with them, written — as
|
|
2526
2885
|
a key of `check.json` after `TEXTURE_STRETCH`, and a console line after the judgement lines —
|
|
@@ -2705,6 +3064,7 @@ it reads is there, and says SKIP with the reason when it is not:
|
|
|
2705
3064
|
| `BREATH_VISIBLE`, `BLINK_NO_HOLE`, `TIP_OVER_ROOT`, `STILL_REGIONS_DARK` | `parts.json`'s tags and the idle | either missing, or as in the table above |
|
|
2706
3065
|
| `CHAIN_LAG`, `TEXTURE_STRETCH` | the idle (and `rig.json`) | no `idle`, or as in the table above |
|
|
2707
3066
|
| `ANIMATIONS_DO_NOT_FOLD` | every animation, the idle included, through `rigc render --folds` | never SKIP: written when `motion.json` holds an animation, the idle alone included (issue #333), absent when it holds none (with or without a `parts.json`) |
|
|
3067
|
+
| `STRESS_OFF_CHAIN` | `parts.json`'s tags, the idle's chains, `rig.json`'s weights, and a turned copy rigc poses | written only when `parts.json` names a `handwear` part and there is an idle (absent on a rig with no `parts.json`); SKIP when no such part rides exactly one chain, each part's reason |
|
|
2708
3068
|
| `RECOMPOSITE_HOLES` | `parts.json`'s `recomposite` block | no `parts.json`, or no block |
|
|
2709
3069
|
| `SETUP_POSE_VS_SOURCE` | the rig spec and the painting | no `--source` |
|
|
2710
3070
|
|
|
@@ -2740,7 +3100,7 @@ single-character path (§4, steps 0–9) on its own, already isolated input to a
|
|
|
2740
3100
|
| `characters[].build` | a `build --out` directory (relative to the scene file) whose own `check/check.json` is green |
|
|
2741
3101
|
| `characters[].offset` | `[x, y]`, a translation from the build's rig px to canvas px. Every character is built at one `assemble.rig_scale`, so its rig px are canvas px at scale 1 |
|
|
2742
3102
|
| `characters[].scale` | optional, a positive number; absent is 1, and 1 composes to the bytes a scene without the key writes. The character's size on the canvas, about its stage's top-left corner: a rig px `p` lands at canvas px `scale * p + offset` (issue #197). A character scaled up past its art is drawn soft — the art is not resampled; rebuild it at a higher `assemble.rig_scale` if that matters |
|
|
2743
|
-
| `characters[].accept` | optional, a list of bar names: the bars this character's own check may have failed and still be composed (issue #203). `compose --accept <bar>[,<bar>…]` is the same list for every character; the two are joined, so the command line applies to every character and a character's list adds to it. Each name is one of the bars `check`'s summary names, less the gate — `loop`, `seam`, `BREATH_VISIBLE`, `BLINK_NO_HOLE`, `CHAIN_LAG`, `TIP_OVER_ROOT`, `STILL_REGIONS_DARK`, `TEXTURE_STRETCH`, `ANIMATIONS_DO_NOT_FOLD` (issue #320); anything else is `SCENE_ACCEPT_KNOWN`. A build is taken when every bar its `check.json` failed is named; its gate red, a declared requirement not PASS, or a bar not named is `SCENE_BUILD_GREEN` as before. **It is for looking, not for shipping:** the `[compose]` line says which failing bars were accepted, `scene.json` records them as the character's `accepted`, the composed rig's own check measures them again and reports them as it finds them, and the run exits 1 even when that check is PASS (a composed rig has no `parts.json`, so the seam and four judgement lines say SKIP there). A list that accepts nothing — every build green — composes to the bytes a scene without one writes |
|
|
3103
|
+
| `characters[].accept` | optional, a list of bar names: the bars this character's own check may have failed and still be composed (issue #203). `compose --accept <bar>[,<bar>…]` is the same list for every character; the two are joined, so the command line applies to every character and a character's list adds to it. Each name is one of the bars `check`'s summary names, less the gate — `loop`, `seam`, `BREATH_VISIBLE`, `BLINK_NO_HOLE`, `CHAIN_LAG`, `TIP_OVER_ROOT`, `STILL_REGIONS_DARK`, `TEXTURE_STRETCH`, `ANIMATIONS_DO_NOT_FOLD` (issue #320), `STRESS_OFF_CHAIN` (issue #347); anything else is `SCENE_ACCEPT_KNOWN`. A build is taken when every bar its `check.json` failed is named; its gate red, a declared requirement not PASS, or a bar not named is `SCENE_BUILD_GREEN` as before. **It is for looking, not for shipping:** the `[compose]` line says which failing bars were accepted, `scene.json` records them as the character's `accepted`, the composed rig's own check measures them again and reports them as it finds them, and the run exits 1 even when that check is PASS (a composed rig has no `parts.json`, so the seam and four judgement lines say SKIP there). A list that accepts nothing — every build green — composes to the bytes a scene without one writes |
|
|
2744
3104
|
| `order` | the draw order, back to front: a character id (its slots not named elsewhere, in its own order) or one `<id>:<slot>`. Every slot of every character is drawn exactly once. Interleaving is written by naming slots: `["a", "b:arm_l", "b"]` draws `b`'s left arm between `a` and the rest of `b` |
|
|
2745
3105
|
|
|
2746
3106
|
What is composed:
|