rig-parts 2.8.1 → 2.9.1
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 +1 -1
- package/cli.ts +42 -5
- package/docs/AUTHORING.md +177 -9
- package/docs/MIGRATION.md +20 -1
- package/docs/STABILITY.md +7 -1
- package/package.json +1 -1
- package/skills/rig-parts/SKILL.md +1 -1
- package/src/assemble.ts +32 -11
- package/src/build.ts +29 -7
- package/src/check.ts +162 -19
- package/src/coincide.ts +157 -0
- package/src/coincideio.ts +79 -0
- package/src/diffcomponents.ts +208 -0
- package/src/edgeresidual.ts +191 -0
- package/src/mesh.ts +29 -2
- package/src/meshdraft.ts +2 -27
- package/src/paintingedges.ts +11 -3
- package/src/propose.ts +150 -5
- package/src/supportdomains.ts +76 -0
- package/tools/build-process.ts +31 -9
package/README.md
CHANGED
|
@@ -276,7 +276,7 @@ rig-parts assemble --source painting.png --full layers/full --head layers/head -
|
|
|
276
276
|
# -> read the `uncovered hole N:` lines and look at work/render/recomposite_error_rig.png:
|
|
277
277
|
# red is painting that no part holds, and no later gate can see it
|
|
278
278
|
# -> a large red hole neither run holds? add an assemble.patches entry (cut from the painting) and assemble again
|
|
279
|
-
rig-parts propose --parts work/rig --source painting.png --out work
|
|
279
|
+
rig-parts propose --parts work/rig --bust-landmarks bust-landmarks.json --source painting.png --out work
|
|
280
280
|
# -> proposal.json, basis.json (what each bone rests on) and render/landmarks.png; per-bone coverage lines;
|
|
281
281
|
# correct it, copy bones/meshes/regions/limbs/motion/constraints/turn/motion_seams/proposed_rig_scale into config.json, each it wrote
|
|
282
282
|
# (limbs only with --keypoints, constraints only with --cloth, turn only with --full/--head;
|
package/cli.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { diffComponentsCommand } from './src/diffcomponents.ts';
|
|
2
3
|
import { applyTakenLayers } from './src/takelayers.ts';
|
|
3
4
|
import { applyDepth, prepareDepth } from './src/depthworkflow.ts';
|
|
4
5
|
/**
|
|
@@ -33,7 +34,7 @@ import { makeInputs } from './src/inputs.ts';
|
|
|
33
34
|
import { idleKeysOf, isIdleKeys } from './src/rig.ts';
|
|
34
35
|
import { bodyCoreBox, bodyOpaqueBox, CORE_ALPHA, depthSkipLine, exportLayers, figuresPhrase, hiddenLayerLine, implausibleRules, layerCoreBox, layerFigures, layerOpaqueBox, type LayerSet, layerSetDiff, type OpaqueBox, OPAQUE_ALPHA_ABOVE, pct, readLayers, readWrapperLayers, ruleSummary, templateLayerNames, templatePsdBytes, times } from './src/layers.ts';
|
|
35
36
|
import { checkImageSize, choosePerson, KEYPOINTS_SPEC, loadKeypoints, type RigJoints, toRigJoints } from './src/keypoints.ts';
|
|
36
|
-
import { basisLines, branchSqueezeLine, checkProposal, compare, compareLines, DEFAULT_GRID_RULE, draftTurn, drawLandmarks, FIGURE_GRID_DIVISOR, FRINGE_STEP_PX, GRID_RULES, type GridRule, HIP_MIN_FRACTION, type DraftLintFinding, lint, lintLine, type PartSet, PROPOSAL_CONFIG_SECTIONS, proposalAsConfig, proposeWithBasis, readPartSet, serializeProposal, withTurn, WRIST_ON_PART_PX } from './src/propose.ts';
|
|
37
|
+
import { basisLines, branchSqueezeLine, checkProposal, compare, compareLines, DEFAULT_GRID_RULE, draftTurn, drawLandmarks, FIGURE_GRID_DIVISOR, FRINGE_STEP_PX, GRID_RULES, type GridRule, type BustDraft, HIP_MIN_FRACTION, type DraftLintFinding, lint, lintLine, type PartSet, PROPOSAL_CONFIG_SECTIONS, proposalAsConfig, proposeWithBasis, readBustLandmarks, readPartSet, serializeProposal, withTurn, WRIST_ON_PART_PX } from './src/propose.ts';
|
|
37
38
|
import { BASIS_FILE, BASIS_SPEC, basisFile, basisLine, coverageLines, legChainChoice, serializeBasis } from './src/diagnostics.ts';
|
|
38
39
|
import { BEND_DEGREES, BEND_WORST_SHARE, DUST_MAX_PX, DUST_MAX_SHARE, IDLE_BOUND_PX, MESH_DRAFT_REPORT, MESH_DRAFT_SPEC, meshDraftStage, MORE_TRIANGLES_FLAG, relativeConfigPaths, TOLERANCE_FIGURE_DIVISOR } from './src/meshdraft.ts';
|
|
39
40
|
import { meshStatsStage } from './src/meshstats.ts';
|
|
@@ -68,6 +69,11 @@ const LATER: ReadonlyArray<[string, string]> = [];
|
|
|
68
69
|
const HELP = `rig-parts ${version()} — Spine-ready parts from one painting and its See-through layers
|
|
69
70
|
|
|
70
71
|
usage:
|
|
72
|
+
rig-parts diff-components --stack <png> --painting <png> --parts <parts.json> --floor <px>
|
|
73
|
+
[--threshold <channel>] [--figure <binary-png>] (--out <json> | --verify <json>)
|
|
74
|
+
Exhaustive native difference inventory; default max RGB channel > 40.
|
|
75
|
+
|
|
76
|
+
|
|
71
77
|
rig-parts depth-prepare --parts <assembled-dir> --config <depth-recipe.json> --out <empty-dir>
|
|
72
78
|
Write per-part inference PNGs on neutral gray, upscaled and padded as
|
|
73
79
|
declared, plus a source-hashed manifest. No model is bundled or called.
|
|
@@ -177,6 +183,10 @@ usage:
|
|
|
177
183
|
bone differs (origin, tip, parent; how far, in px). No confidence is
|
|
178
184
|
computed. It prints "wrote ${BASIS_FILE} (…)" after the coverage lines.
|
|
179
185
|
|
|
186
|
+
rig-parts propose … (--bust-landmarks <landmarks.json> | --no-bust <reason>)
|
|
187
|
+
Draft the chest-holding topwear as contour. Landmark JSON declares apex_r,
|
|
188
|
+
apex_l and optional cleavage in rig pixels. Without cleavage, intersect
|
|
189
|
+
their segment with the neck-to-hip midline. --no-bust needs a stated reason.
|
|
180
190
|
rig-parts propose … [--keypoints <keypoints.json> [--person <id>] [--wrist-snap <px>]]
|
|
181
191
|
Read the figure's pose from one explicit file (spec "${KEYPOINTS_SPEC}"):
|
|
182
192
|
its space (painting-px, origin top-left, y down — another is refused),
|
|
@@ -1016,6 +1026,14 @@ function usage(message: string): number {
|
|
|
1016
1026
|
return EXIT_USAGE;
|
|
1017
1027
|
}
|
|
1018
1028
|
|
|
1029
|
+
function cmdDiffComponents(args: string[]): number {
|
|
1030
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
1031
|
+
if (!takesValued('diff-components', args[i])) return usage(notTaken('diff-components', args[i]));
|
|
1032
|
+
if (args[i + 1] === undefined || args[i + 1].startsWith('--')) return usage(`diff-components ${args[i]} requires a value`);
|
|
1033
|
+
}
|
|
1034
|
+
try { diffComponentsCommand(args); return EXIT_OK; } catch (err) { return printRefusal(err); }
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1019
1037
|
/** `guide`: one argument, a rule name or a section; the slice printed, or the refusal. */
|
|
1020
1038
|
function cmdGuide(args: string[]): number {
|
|
1021
1039
|
if (args.length !== 1) return usage(`guide takes one rule name or one §<n> / §<n>.<m> / §<n>.<m>.<k>; got ${args.length} argument(s)`);
|
|
@@ -1104,12 +1122,13 @@ function printLayerTable(set: LayerSet): void {
|
|
|
1104
1122
|
* no row; `layers` takes one positional path beside its flags (issue #359).
|
|
1105
1123
|
*/
|
|
1106
1124
|
const TAKES = {
|
|
1125
|
+
'diff-components': { valued: ['--stack', '--painting', '--parts', '--figure', '--floor', '--threshold', '--out', '--verify'], switches: [] },
|
|
1107
1126
|
'depth-prepare': { valued: ['--parts', '--config', '--out'], switches: [] },
|
|
1108
1127
|
'depth-apply': { valued: ['--parts', '--manifest', '--config', '--rig-config', '--out'], switches: [] },
|
|
1109
1128
|
layers: { valued: ['--export', '--template', '--canvas', '--full', '--source', '--config'], switches: [] },
|
|
1110
1129
|
sheet: { valued: ['--source', '--layers', '--out', '--cell', '--cols'], switches: [] },
|
|
1111
1130
|
rig: { valued: ['--config', '--parts', '--out', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
|
|
1112
|
-
propose: { valued: ['--parts', '--source', '--out', '--from-config', '--compare', '--full', '--head', '--canvas', '--keypoints', '--person', '--wrist-snap', '--grid-rule', '--cloth', '--write-config', '--from'], switches: ['--head-box', '--no-back-hair', '--only-missing'] },
|
|
1131
|
+
propose: { valued: ['--parts', '--source', '--out', '--from-config', '--compare', '--full', '--head', '--canvas', '--keypoints', '--person', '--wrist-snap', '--grid-rule', '--cloth', '--write-config', '--from', '--bust-landmarks', '--no-bust'], switches: ['--head-box', '--no-back-hair', '--only-missing'] },
|
|
1113
1132
|
compare: { valued: ['--left', '--right', '--map'], switches: [] },
|
|
1114
1133
|
check: { valued: ['--rig', '--out', '--parts', '--source', '--page-edges', '--pack-shape', '--requirements', '--set', '--environment', '--wind-band'], switches: ['--seam-pairs', '--keep-stills', '--all-sets', '--no-extremes', '--json'] },
|
|
1115
1134
|
loop: { valued: ['--frames', '--out'], switches: ['--palette'] },
|
|
@@ -1557,6 +1576,7 @@ function cmdPropose(args: string[]): number {
|
|
|
1557
1576
|
return EXIT_OK;
|
|
1558
1577
|
}
|
|
1559
1578
|
}
|
|
1579
|
+
if (flags.has('--from-config') && (flags.has('--bust-landmarks') || flags.has('--no-bust'))) return usage('--bust-landmarks and --no-bust draft a torso. --from-config reads authored meshes');
|
|
1560
1580
|
const P = readPartSet(partsDir);
|
|
1561
1581
|
const painting = readPng(source);
|
|
1562
1582
|
// Issue #75: the keypoint file, held to this painting's size and taken to rig px by the overlay's map.
|
|
@@ -1580,7 +1600,23 @@ function cmdPropose(args: string[]): number {
|
|
|
1580
1600
|
}
|
|
1581
1601
|
const cmpPath = flags.get('--compare');
|
|
1582
1602
|
const cfg = cmpPath === undefined ? null : loadConfig(cmpPath);
|
|
1583
|
-
const
|
|
1603
|
+
const noBust = flags.get('--no-bust');
|
|
1604
|
+
const bustPath = flags.get('--bust-landmarks');
|
|
1605
|
+
if (noBust !== undefined && (noBust.trim().length === 0 || bustPath !== undefined)) return usage('--no-bust requires a non-blank reason and cannot accompany --bust-landmarks');
|
|
1606
|
+
const bust: BustDraft = {};
|
|
1607
|
+
if (noBust !== undefined) bust.noBust = noBust;
|
|
1608
|
+
if (bustPath !== undefined) {
|
|
1609
|
+
try {
|
|
1610
|
+
bust.landmarks = readBustLandmarks(JSON.parse(readFileSync(bustPath, 'utf8')) as unknown);
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
if (error instanceof PartsError) throw error;
|
|
1613
|
+
throw new PartsError([{
|
|
1614
|
+
code: 'PROPOSE_BUST_INPUT', object: '--bust-landmarks',
|
|
1615
|
+
detail: `cannot read the declared JSON file (${error instanceof Error ? error.message : String(error)}). Give an object with apex_r, apex_l and optional cleavage in rig pixels`,
|
|
1616
|
+
}]);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
const { proposal: drafted, basis, draftLint } = proposeWithBasis(P, joints, wristSnap, gridRule, cloth, true, bust);
|
|
1584
1620
|
const fullRun = flags.get('--full');
|
|
1585
1621
|
const headRun = flags.get('--head');
|
|
1586
1622
|
const prop =
|
|
@@ -1590,7 +1626,7 @@ function cmdPropose(args: string[]): number {
|
|
|
1590
1626
|
// Emit only after green: a proposal the config loader would refuse is not written, nor a basis record that does not hold to it.
|
|
1591
1627
|
checkProposal(P, prop);
|
|
1592
1628
|
// The same parts drafted without the recipe: what --write-config measures its edits against, and what issue #572's chain reading compares with.
|
|
1593
|
-
const plain = cloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, null).proposal;
|
|
1629
|
+
const plain = cloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, null, true, bust).proposal;
|
|
1594
1630
|
// Issue #537: the sections the recipe moves, measured against the same parts drafted without it, spliced before anything is written.
|
|
1595
1631
|
const written =
|
|
1596
1632
|
writeArg === undefined || clothArg === undefined || plain === null
|
|
@@ -1599,7 +1635,7 @@ function cmdPropose(args: string[]): number {
|
|
|
1599
1635
|
const text = readFileSync(clothArg, 'utf8');
|
|
1600
1636
|
// Issue #552: bones, constraints and motion.tracks entry by entry; a hand edit the recipe would drop is refused by name.
|
|
1601
1637
|
// Issue #571: with --from, the same parts drafted a third time with the previous recipe.
|
|
1602
|
-
const previous = previousCloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, previousCloth).proposal;
|
|
1638
|
+
const previous = previousCloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, previousCloth, true, bust).proposal;
|
|
1603
1639
|
const edits = clothConfigEdits(drafted, plain, JSON.parse(text) as unknown, false, previous);
|
|
1604
1640
|
return { at: writeArg, input: text, text: writtenClothConfig(clothArg, text, edits), paths: editPaths(edits), vouched: previousCount(edits), returned: plainCount(edits), relative: relativeConfigPaths(dirname(resolve(clothArg)), JSON.parse(text) as Record<string, unknown>) };
|
|
1605
1641
|
})();
|
|
@@ -2257,6 +2293,7 @@ function main(argv: string[]): number | Promise<number> {
|
|
|
2257
2293
|
console.log(version());
|
|
2258
2294
|
return EXIT_OK;
|
|
2259
2295
|
}
|
|
2296
|
+
if (command === 'diff-components') return cmdDiffComponents(rest);
|
|
2260
2297
|
if (command === 'layers') return cmdLayers(rest);
|
|
2261
2298
|
if (command === 'depth-prepare') return cmdDepthPrepare(rest);
|
|
2262
2299
|
if (command === 'depth-apply') return cmdDepthApply(rest);
|
package/docs/AUTHORING.md
CHANGED
|
@@ -163,11 +163,11 @@ read by no CPU stage).
|
|
|
163
163
|
| `assemble.editor_receipts` | assemble, build | **authored**, optional (issue #946): `{receipts: [{file, sha256, from_sha256, editor}], required?, trace_parts?}`. Each receipt records an image edited in an image editor; `file` (relative to the config's directory) must exist and hash to `sha256`, verified at assemble; `from_sha256` and `editor` are the author's declaration, held only to form. With `required: true`, every author-made layer, hidden-art layer, cut `under` and limb `cap` the build reads, alternate sets' runs included, needs a receipt whose `sha256` is its file's. With `trace_parts: true` (issue #947), every part's pixels must trace to a layer of the split or to a receipted image: the receipts are demanded as under `required`, and every shape the config draws through a part — a cut's `polygon`, `seethrough.arm_split`, a limb's `split.line`, a patch's `box`, a painting extension's `band` — is refused (`ASSEMBLE_PART_SOURCE`). Absent, no file is read and no byte moves. See [Editor receipts](#editor-receipts-assembleeditor_receipts-issue-946) and [Part sources](#part-sources-assembleeditor_receiptstrace_parts-issue-947). |
|
|
164
164
|
| `limbs` | assemble (the cut and the caps), rig (through `regions`), check (`LIMB_ONE_BONE`) | **proposed** by `propose --keypoints` (issue #349), then checked: `{<limb>: {segments: [{part, from, bone}, {part, from, bone, joint, range?, cap?, edge?, cap_shape?, cut_band?, hybrid?}, …], split?, layout?, front?}}`, the upper segment first. The rule is *Limbs: cut at their joints* below the table. `part` is a new part (`CONFIG_PART_NAME`, `CONFIG_PART_UNIQUE` against the plan, patches, cuts and every other limb part); `from` a plan part or a cut's piece (`CONFIG_NAME_RESOLVES`) — the layer the segment's pixels come from, which is cut into the limb parts and is not itself made: a `meshes` or `regions` entry for it is `CONFIG_NAME_RESOLVES`, and the segments of one limb that take one part are consecutive (`CONFIG_LIMB_FROM_RUN`); `bone` a declared bone, each segment's the child of the one before (`CONFIG_LIMB_BONES_CHAIN`); the upper segment names no `joint`, `range`, `cap`, `edge`, `cap_shape` or `cut_band` (`CONFIG_KEY_KNOWN`), every other names its `joint` (`CONFIG_FIELD_PRESENT`) — the joint is its bone's origin; `range` is `[min, max]` degrees from rest with `min <= 0 <= max` (`CONFIG_LIMB_RANGE`), which `assemble` writes into the lower part's `parts.json` record and `check` poses the joint at — its two ends and its middle — reading the seam there (`SEAM_OVER_RANGE`, issue #360; rig-c needs no rotation limit); `cap` a PNG path relative to the config's directory (`CONFIG_FIELD_TYPE`); `edge` `"feather"` (issue #360), the one value it takes, and `cap_shape` `"disc"` (issue #360) or `"ball"` (issue #579) (`CONFIG_FIELD_TYPE` on any other) — the cut's geometry at that joint, *Limbs: cut at their joints* below; absent, the cut is hard and the cap the layer's half-disc of half the limb's width; `cut_band` a whole number of px, 1 or more (`CONFIG_FIELD_TYPE`), on a segment that takes the segment above's part — between two layers nothing is cut, so a band there is `CONFIG_KEY_KNOWN` — places the cut within that many px of the joint line (issue #360, *Limbs: cut at their joints*); absent, the cut is the joint line. Each limb part is attached like any part — `regions.<part>`, which must be the segment's `bone` (`CONFIG_LIMB_BONE`), or a mesh, which `check` holds to that bone alone (`LIMB_ONE_BONE`). With `limbs`, `assemble` reads `bones` too (`CONFIG_FIELD_PRESENT` without it). Absent, nothing is cut at a joint and no byte of any output moves. Optional `front` (issue #664), `{parts: [<own segment part>, …], after: "<produced part>"}`, gives each named segment a second slot `<part>_front`, empty at setup, placed after `after`'s slot family, for a gesture set to draw on (`CONFIG_FRONT_SCOPE`, `CONFIG_FRONT_NAME`, `CONFIG_FRONT_LAYOUT`; *Front slots (issue #664)* below) |
|
|
165
165
|
| `bones` | rig; `propose --from-config` | **proposed** by `propose`, then corrected. A single bone `{name, parent, at, tip?}` or a chain `{chain, parent, points, tip, link_inherit?}` whose links are named `<chain>0 … <chain>n`. Parents come before children. In `rig.json` each link is **turned along its chain** — `rotation` is the direction from its origin to the next link's (the last link's to `tip`) and `length` that distance (issue #73), so a physics constraint added downstream finds a lever; under `--idle-keys ctl` the link's `<link>_ctl` carries the turn and the length and the link sits at local rotation 0 beneath it. A single bone with a `tip` is turned the same way, towards its tip, with that distance as its length (issue #239); a tip on the bone's own origin is refused (`RIG_TIP_DISTINCT`). The five face bones `eye_r`, `eye_l`, `brow_r`, `brow_l` and `mouth` (by name, when one has no tip of its own) are turned to the eye line — the line through the origins of `eye_r` and `eye_l` as `bones` writes them, read image-left to image-right, drawn when parts.json holds both an `eyewhite-r` and an `eyewhite-l` part — so the blink's `scaley` squashes across the eye line on a tilted head; move an eye bone and the line moves with it. With one eyewhite or none, or an eye bone missing, the rig stage's `face axis:` line says so and they keep the page's axes. A bone under a face bone keeps the face's axis. With the line, `rig.json` also declares `invariants.axes` — one entry, `"face"`, through `eye_r` and `eye_l`, holding the turned face bones (and their controls) — so rig-c's gate holds the build to the line; the line is the one rigc draws through the same two bones, so the tolerance is only the float floor — the largest gap the emitted (float32) bones have to it, rounded up to 6 places plus one 6-place unit — and the `face axis:` line prints it. With the line the rig stage also adds `head_up` (issue #250): a slotless bone under `head`, at head's origin, turned to the eye line + 90 degrees and as long as the eye midpoint lies from head's origin along that axis — the head's frame, which a rule that needs it reads; `head` keeps its own frame, so no key on it or under it changes meaning, and a config bone of that name is refused (`RIG_HEAD_UP_NAME_FREE`). A face bone with its own `tip` is held too (issue #258), in a second entry, `"face_tips"`, when its tip lies along the line within whole-pixel rounding — a tip written in whole pixels sits at most sqrt(2)/2 px off the point of the line it stands for, so its direction may sit up to asin((sqrt(2)/2) / length) degrees off — with its tolerance measured the same way; a tip further off is the bone's own axis, and the `face axis:` line names it with its gap and that bound. A failure is rig-c's `A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS` line, which names the bone and the fix (rig-c's `docs/AUTHORING.md` §3.7 and §5.2); with no line no entry is written and A51 reports SKIP. Every region on a turned bone is turned back, so the setup pose does not move. Any other single bone keeps world rotation 0; one whose parent is a link is turned back upright. A single bone may declare `inherit` (issue #348): what it takes from its parent's transform, one of rig-c's own mode names exactly as rig-c's table spells them — `normal`, `onlyTranslation`, `noRotationOrReflection`, `noScale`, `noScaleOrReflection` (rig-c's `RigBone.inherit`) — written to that bone in `rig.json` as declared; any other spelling, `"NoScale"` included (the runtime would fold its first letter; nothing here folds or maps), is refused (`CONFIG_BONE_INHERIT`), and a chain takes no `inherit` (`CONFIG_KEY_KNOWN`). A chain may instead declare `link_inherit` (issue #369), an object mapping its own expanded link names to those same modes, for example `"link_inherit": {"leg_r1": "noScale"}`. Only the named link changes; omitted links inherit normally. A key outside this chain is `CONFIG_NAME_RESOLVES`, with this chain's valid link names in the detail; a non-object or non-string mode is `CONFIG_FIELD_TYPE`, and another mode spelling is `CONFIG_BONE_INHERIT`. The mode is forwarded to the expanded link, or to its control under `--idle-keys ctl`, by the same rule as a single bone. This is an authored opt-in: `propose` does not add it. Its setup rotation is the turn from where rig-c's own poser puts the bone with no rotation of its own under that mode, so it still stands upright (or along its tip) at the setup pose. Where the idle gives the bone a `<bone>_ctl`, the mode goes on the control: the control is the bone the parent's rotation reaches, and the bone under it inherits normally, so the idle's keys moved onto the control still turn it. A foot that does not take its shin's rotation (`noRotationOrReflection` or `onlyTranslation`) keeps its shoe level while the leg is posed (`FEET_STAY_LEVEL`, §7); which mode is the author's. `noScale` keeps a shin or shoe's shape under a scaled parent but still takes the parent's rotation: a shoe that must also stay level needs a separate rotation constraint, for example a transform constraint from an unrotated bone. This does not change the proposed feet's existing inheritance or `FEET_STAY_LEVEL` default. An automatic mesh weighted through a bone with a mode other than `normal` gets no derived amplitude (its row names the bone) and no residual envelope (rig-c's `SKINNING_RANGE_UNSUPPORTED`). Absent, `rig.json` carries no `inherit` and no byte moves |
|
|
166
|
-
| `meshes.<part>` | rig | **proposed**, then corrected. Generated modes below; `explicit` transfers an accepted mesh (see “An accepted mesh”). Exactly one mode is allowed. `grid` (the lattice: cell size, px — what `propose` writes: by default one spacing for the figure, round(H / 37) rig px with H the height of every part's box together, a part whose tenth of its short side is already coarser keeping that, and under `propose --grid-rule part` the reference's tenth of the part's short side clamped to 5..36, issue #265), `contour` (the outline mode, authored; the next row and "A contour mesh" below) or `auto` (the automatic mode, authored; "An automatic mesh" below) — two, or none, is `CONFIG_MESH_MODE`. In distance-generated modes: `r` (added to every distance before weighting: `w = 1/(d + r)²`), `segments` (a chain name, a bone name with a `tip`, or `[bone, [x0,y0], [x1,y1]]`), and, **authored**, optional, `exponent` (issue #161): a number above 0 that replaces the 2 in that rule, `w = 1/(d + r)^exponent` — a larger one hands each vertex more fully to its nearest bone; absent is 2 and changes no byte, a value that is not a number above 0 is `CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`, and a declared one is echoed as `exponent` in the mesh's `mesh_report.json` row; the exponent is the distance rule's, so declared beside `rule: "heat"` it is refused (`CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`). The segment list is the one authored decision about a layer: which bones may pull it. The slot's bone is the first segment's. Optional `rule` (issue #161): `"distance"` — the rule above, and what an absent `rule` means (every byte is the same) — or `"heat"`, bone heat over the part's own silhouette, in every mode: the lattice's art (alpha above 8, less any island a declared `stray` left out) with holes filled, or for `contour` and `auto` the silhouette their outline is traced from (that mode's art — alpha above 8 for `contour`, alpha 1 and above for `auto` — less any island a declared `stray` left out, holes filled, grown by its `margin`); each bound bone's sources are the silhouette pixels whose centre lies within 0.75 px of its segment (a pixel two bones reach goes to the nearer, a tie to the first listed), held at 1 for that bone and 0 for the others; the field is solved by row-major Gauss–Seidel over-relaxation to a sweep change under 1e-7 within 20000 sweeps; a vertex reads the nearest silhouette pixel and takes the same cap and floor (and `regions`) as the distance rule; `r` is not read. The mesh's `mesh_report.json` row gains `heat` (the constants, each bone's sources and sweeps, the residual). Any other value is `CONFIG_FIELD_TYPE`; a bound bone with no source pixel is `RIG_HEAT_BONE_SOURCE`, an island no bone has a source in `RIG_HEAT_ISLAND_SOURCE`, a solve past its sweep bound `RIG_HEAT_CONVERGED` — none is weighted 0. The rule is chosen per mesh, so `RIG_HEAT_BONE_SOURCE` names three remedies: move the segment onto the art, take the bone out of `segments`, or keep this part on the distance rule — the one to take when the bone anchors the part from outside its art (a torso bone under a neck), since the first moves a bone another part placed and the second drops the anchor (issue #303). On a lattice, `RIG_HEAT_ISLAND_SOURCE` on an island that is dust — at most 4 px and at most 0.1 % of the part's art px, `mesh-draft`'s line — names `meshes.<part>.stray` and the figure that leaves it out, with the value declared; an island above that line keeps the segment remedy, a real piece being a real authoring question. In every mode the mesh's `mesh_report.json` row's `bones` names the bones the emitted model binds for the attachment — the sorted set its weights in the written `rig.json` name, so a `turn` twin (`turn_<part>_<bone>`) or a stopped link's `<link>_d` where those rebind it, never the segment bone it stands for — the same list in every mode, `remesh --delta`'s included (issue #536) Optional `gather: false` keeps an exclusively bound non-segment limb rider at its authored draw position (issue #680); its binding and mesh geometry are unchanged. Remove the key for normal gathering; a non-rider is refused as `RIG_GATHER_RIDER`. |
|
|
166
|
+
| `meshes.<part>` | rig | **proposed**, then corrected. Generated modes below; `explicit` transfers an accepted mesh (see “An accepted mesh”). Exactly one mode is allowed. `grid` (the lattice: cell size, px — what `propose` writes for non-torso parts: 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 or drafted for the chest carrier; the next row and "A contour mesh" below) or `auto` (the automatic mode, authored; "An automatic mesh" below) — two, or none, is `CONFIG_MESH_MODE`. In distance-generated modes: `r` (added to every distance before weighting: `w = 1/(d + r)²`), `segments` (a chain name, a bone name with a `tip`, or `[bone, [x0,y0], [x1,y1]]`), and, **authored**, optional, `exponent` (issue #161): a number above 0 that replaces the 2 in that rule, `w = 1/(d + r)^exponent` — a larger one hands each vertex more fully to its nearest bone; absent is 2 and changes no byte, a value that is not a number above 0 is `CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`, and a declared one is echoed as `exponent` in the mesh's `mesh_report.json` row; the exponent is the distance rule's, so declared beside `rule: "heat"` it is refused (`CONFIG_FIELD_TYPE` at `meshes.<part>.exponent`). The segment list is the one authored decision about a layer: which bones may pull it. The slot's bone is the first segment's. Optional `rule` (issue #161): `"distance"` — the rule above, and what an absent `rule` means (every byte is the same) — or `"heat"`, bone heat over the part's own silhouette, in every mode: the lattice's art (alpha above 8, less any island a declared `stray` left out) with holes filled, or for `contour` and `auto` the silhouette their outline is traced from (that mode's art — alpha above 8 for `contour`, alpha 1 and above for `auto` — less any island a declared `stray` left out, holes filled, grown by its `margin`); each bound bone's sources are the silhouette pixels whose centre lies within 0.75 px of its segment (a pixel two bones reach goes to the nearer, a tie to the first listed), held at 1 for that bone and 0 for the others; the field is solved by row-major Gauss–Seidel over-relaxation to a sweep change under 1e-7 within 20000 sweeps; a vertex reads the nearest silhouette pixel and takes the same cap and floor (and `regions`) as the distance rule; `r` is not read. The mesh's `mesh_report.json` row gains `heat` (the constants, each bone's sources and sweeps, the residual). Any other value is `CONFIG_FIELD_TYPE`; a bound bone with no source pixel is `RIG_HEAT_BONE_SOURCE`, an island no bone has a source in `RIG_HEAT_ISLAND_SOURCE`, a solve past its sweep bound `RIG_HEAT_CONVERGED` — none is weighted 0. The rule is chosen per mesh, so `RIG_HEAT_BONE_SOURCE` names three remedies: move the segment onto the art, take the bone out of `segments`, or keep this part on the distance rule — the one to take when the bone anchors the part from outside its art (a torso bone under a neck), since the first moves a bone another part placed and the second drops the anchor (issue #303). On a lattice, `RIG_HEAT_ISLAND_SOURCE` on an island that is dust — at most 4 px and at most 0.1 % of the part's art px, `mesh-draft`'s line — names `meshes.<part>.stray` and the figure that leaves it out, with the value declared; an island above that line keeps the segment remedy, a real piece being a real authoring question. In every mode the mesh's `mesh_report.json` row's `bones` names the bones the emitted model binds for the attachment — the sorted set its weights in the written `rig.json` name, so a `turn` twin (`turn_<part>_<bone>`) or a stopped link's `<link>_d` where those rebind it, never the segment bone it stands for — the same list in every mode, `remesh --delta`'s included (issue #536) Optional `gather: false` keeps an exclusively bound non-segment limb rider at its authored draw position (issue #680); its binding and mesh geometry are unchanged. Remove the key for normal gathering; a non-rider is refused as `RIG_GATHER_RIDER`. `propose` drafts the chest-holding topwear as `contour` instead (issue #986), using the figure tolerance and spacing described below. For a one-layer figure it drafts `trunk_chest` as contour. Other garments keep their existing modes. |
|
|
167
167
|
| `meshes.<part>.influence` | rig | **authored**, optional (issue #378), a mode beside `grid`, `contour` and `auto`: `{cell, radius, across, max, shape, hold_tip?}`, every field but `hold_tip` required, none defaulted. rig-c's `segments` lattice over the part at `cell` px, each vertex pulled by every link of the mesh's `segments` within `radius` px of it — a pull that falls to exactly 0 at `radius` (`shape` `"linear"` or `"smooth"`), across the `segments` entries or only within the nearest one (`across`), the strongest `max` kept. The lattice and the shares are computed by rig-c's own promised functions (`buildSegmentsLattice`, `segmentDistance`, `influenceShares`), so the rule is rig-c's (its AUTHORING §3.4.7), and the mesh is written with explicit weights like every other mode. `radius` is a length on this part: the rig stage measures the farthest lattice vertex's distance to its nearest link and refuses a radius at or under it (`RIG_INFLUENCE_REACH`), naming the distance. `propose --cloth` drafts it when a recipe declares `influence` and no `radius` (issue #518): the farthest vertex's distance plus 3 of the part's lattice cells, printed on a `LINT` line with both figures. Measured on the cloth fixture and the public `wind` example (#513: 8 covered parts, `render --folds` with the physics on under wind and a hip sway of 8° and 30°): a radius just above the floor folds **more** than the lattice (one skirt went from 0 to 55 folded frames), and a larger one is **not** safer (one fringe folded again at about 3–4× its farthest distance); +2 to +6 cells read 0 on every part, +2 at the edge. That reading counted folds only; read against `check` as well (the rig as built, under the wind schedule +1 → −1 → calm, `build --environment`, the three covered `wind` parts at the same number of cells), the window that holds is **+2 to +3 cells**, and +4 is the ceiling: `wind` `bottomwear`'s `CHECK_TIP_OVER_ROOT` ratio is 1.898 on the lattice, 1.801 at +2 cells, 1.590 at +3 and **1.471 at +4**, under the floor 1.4725, so the build fails there, and 1.351 at +6 (#513, rig-parts 1.52.0, rigc 2.60.0; the lattice, +3 and +4 read again on rig-parts 1.54.0, rigc 2.60.0, the same figures, #599). The drafted +3 sits in that window, one cell under the ceiling (no radius between +3 and +4 was read), and the window is a hypothesis until a third subject — each edge rests on one part — so state `radius` in the recipe to keep your own. `r`, `exponent`, `rule`, `influences`, `stray` and `ribs` belong to the distance rule or another mode and are refused beside it (`CONFIG_INFLUENCE_MODE`). `hold_tip` (issue #615) — `true` only, or absent: the pulled shares are smoothed by the lattice's held-fork passes (the cloth recipe's `split.hold_tip` sentence below: four passes of 1-ring mesh-edge averaging, self weight 0.5, floor 0.005) capped at the declared `max`, so no vertex binds more than `max` bones; absent, the shares are rig-c's as pulled and every byte is the one written before the field existed (`false`, a second spelling of absent, is `CONFIG_FIELD_TYPE`). The mesh's `mesh_report.json` row echoes it in `influence`. `propose --cloth` writes it, with `max` raised to at least 8 (the lattice's smoothing cap), on the parts a recipe with `influence` and `split.hold_tip: true` meshes; the rig stage reads it off the mesh and never the recipe. Measured on `wind` `bottomwear` and the cloth fixture's skirt under a 60° hip sway (#615, `render --folds`, physics on): `wind` `bottomwear` folds 41 frames as drafted before the field (max 6) and **0** with `hold_tip` and max 8, against its lattice's 5; with `hold_tip` at max 6 it reads 23, and at max 8 without `hold_tip` 3; the fixture skirt (recipe given `split.hold_tip`) reads 20 before and 0 with it, against its lattice's 0, and 0 at max 8 without it. So the raised `max` carries most of the gap and the smoothing the rest on `wind` (rig-parts 1.54.0, rigc 2.60.0; a split skirt without `hold_tip` is not smoothed and reads its 44 as before). For cloth and hair that should bend as one surface across its chains |
|
|
168
|
-
| `meshes.<part>.contour` | rig | **authored**, optional (issue #84): `{tolerance, margin, spacing, maxEdge?, budget?, stray?, bridges?, regions?, bust?}`. `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; a bridge adds no vertex (issue #570): its four corners, `contour.bridges.joins[].vertices`, are outline vertices of the two silhouettes it joins, and they are weighted as every vertex of the mesh is, by distance to every bone the mesh's `segments` name — no step of the weighting reads which island a vertex lies on, so where two islands ride two bones (two legs in one `legwear` on `leg_r` and `leg_l`) each island's vertices, the corners among them, take weight from the other island's bones too (selftest `CW100`: on its fixture the left island's two corners each take 0.35821 from the right island's bone, and none of its vertices less than 0.30431), and a part whose pieces must not pull each other is split into one part per piece; `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`). `bust` (issue #475) — `{apex_r, apex_l, cleavage, alpha?: 0.22, clearance?: 3.8, bone_r?: "boob_r", bone_l?: "boob_l", spacing?: 14}`: derives the two circular regions `bust_r` and `bust_l` from the three rig-px landmarks `apex_r`, `apex_l` and `cleavage` (multiples of 1/256 px, `CONFIG_FIELD_TYPE` otherwise; projecting the torso midline `neck → chest → hip` onto `apex_r → apex_l` misses the cleavage by 14–93 px across all five calibrated 3/4-yaw characters, so all three points are required). With `d_s = \|cleavage − apex_s\|` and `D = \|apex_r − apex_l\|`, each side `s ∈ {r, l}` is placed at `cx = apex_s[0]`, `cy = apex_s[1]` with outer radius `R_s = d_s − clearance`, core radius `r_s = round(alpha × R_s)` and `band_s = round(R_s) − r_s` (`alpha` = 0.22, `clearance` = 3.8 px, `spacing` = 14 px, `bone_r` = `"boob_r"`, `bone_l` = `"boob_l"` by default, reproducing the five calibrated example characters within `<= 6 px`, max `\|ΔR\| = 5 px`, max `\|Δr\| = 3 px`). Refused when `clearance <= 0`, `R_r + R_l >= D`, or a derived `r_s` or `band_s` is not positive (`CONFIG_BUST_CLEARANCE`), when `bone_r` or `bone_l` is not declared in `bones` (`CONFIG_NAME_RESOLVES`), or when `contour.regions` also names `bust_r` or `bust_l` (`CONFIG_REGION_NAME_UNIQUE`). Both `propose` and `rig` read `contour.bust` through `contourRegionsOf` (`deriveBustRegions`), and hand-written `contour.regions` entries build byte-identically when `bust` is absent. The alpha threshold is not a field: art is alpha above 8, the lattice's and `check`'s |
|
|
168
|
+
| `meshes.<part>.contour` | rig | **authored or proposed**, optional (issue #84): `{tolerance, margin, spacing, maxEdge?, budget?, stray?, bridges?, regions?, bust?}`. `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; a bridge adds no vertex (issue #570): its four corners, `contour.bridges.joins[].vertices`, are outline vertices of the two silhouettes it joins, and they are weighted as every vertex of the mesh is, by distance to every bone the mesh's `segments` name — no step of the weighting reads which island a vertex lies on, so where two islands ride two bones (two legs in one `legwear` on `leg_r` and `leg_l`) each island's vertices, the corners among them, take weight from the other island's bones too (selftest `CW100`: on its fixture the left island's two corners each take 0.35821 from the right island's bone, and none of its vertices less than 0.30431), and a part whose pieces must not pull each other is split into one part per piece; `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`). `bust` (issue #475) — `{apex_r, apex_l, cleavage, alpha?: 0.22, clearance?: 3.8, bone_r?: "boob_r", bone_l?: "boob_l", spacing?: 14}`: derives the two circular regions `bust_r` and `bust_l` from the three rig-px landmarks `apex_r`, `apex_l` and `cleavage` (multiples of 1/256 px, `CONFIG_FIELD_TYPE` otherwise; projecting the torso midline `neck → chest → hip` onto `apex_r → apex_l` misses the cleavage by 14–93 px across all five calibrated 3/4-yaw characters, so all three points are required). With `d_s = \|cleavage − apex_s\|` and `D = \|apex_r − apex_l\|`, each side `s ∈ {r, l}` is placed at `cx = apex_s[0]`, `cy = apex_s[1]` with outer radius `R_s = d_s − clearance`, core radius `r_s = round(alpha × R_s)` and `band_s = round(R_s) − r_s` (`alpha` = 0.22, `clearance` = 3.8 px, `spacing` = 14 px, `bone_r` = `"boob_r"`, `bone_l` = `"boob_l"` by default, reproducing the five calibrated example characters within `<= 6 px`, max `\|ΔR\| = 5 px`, max `\|Δr\| = 3 px`). Refused when `clearance <= 0`, `R_r + R_l >= D`, or a derived `r_s` or `band_s` is not positive (`CONFIG_BUST_CLEARANCE`), when `bone_r` or `bone_l` is not declared in `bones` (`CONFIG_NAME_RESOLVES`), or when `contour.regions` also names `bust_r` or `bust_l` (`CONFIG_REGION_NAME_UNIQUE`). Both `propose` and `rig` read `contour.bust` through `contourRegionsOf` (`deriveBustRegions`), and hand-written `contour.regions` entries build byte-identically when `bust` is absent. The alpha threshold is not a field: art is alpha above 8, the lattice's and `check`'s Short outline edges amplify a cap or floor step in the vertex weights; the recorded grid, contour, bust and continuous-cut comparison is in "The weight cut" below. |
|
|
169
169
|
| `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 |
|
|
170
|
-
| `meshes.<part>.influences` | rig | **authored**, optional (issue #284), in the `grid` and `contour` modes: `{maxInfluences?, floor?}`, at least one. `maxInfluences` — the most bones that pull one vertex, a whole number 1 or more, or `null`, no cap (every bone the mesh's `segments` name; `null` is the config's spelling of a bound declared absent, as in `maxOvershoot` and `weightJump`); absent is 4. `floor` — `"cut"` (the capped shares normalised, any under 0.03 dropped, the rest normalised again; what an absent `floor` means) or `"continuous"` (0.03 subtracted from every capped share, those at or below 0 dropped, the rest normalised again, so a bone fades out of a vertex instead of leaving it in one step). Absent, the mesh is weighted at 4 and the cut and every byte is the one written before the field existed; declared, it applies under either `rule`, and is echoed as `influences` in the mesh's `mesh_report.json` row. `propose`
|
|
170
|
+
| `meshes.<part>.influences` | rig | **authored or proposed**, optional (issue #284), in the `grid` and `contour` modes: `{maxInfluences?, floor?}`, at least one. `maxInfluences` — the most bones that pull one vertex, a whole number 1 or more, or `null`, no cap (every bone the mesh's `segments` name; `null` is the config's spelling of a bound declared absent, as in `maxOvershoot` and `weightJump`); absent is 4. `floor` — `"cut"` (the capped shares normalised, any under 0.03 dropped, the rest normalised again; what an absent `floor` means) or `"continuous"` (0.03 subtracted from every capped share, those at or below 0 dropped, the rest normalised again, so a bone fades out of a vertex instead of leaving it in one step). Absent, the mesh is weighted at 4 and the cut and every byte is the one written before the field existed; declared, it applies under either `rule`, and is echoed as `influences` in the mesh's `mesh_report.json` row. `propose` writes `{maxInfluences: null, floor: "continuous"}` only on a newly drafted contour torso whose segments name a chain or its links, with a note explaining the short-outline-edge weight step (issues #986, #991). Other drafts and authored contour entries retain their policy, and `mesh-draft` carries a lattice's declaration to the contour candidate it drafts. A cap that is not a whole number 1 or more or `null` (the string `"none"` included), another `floor`, or an object naming neither is `CONFIG_FIELD_TYPE`; a vertex the declared cut leaves with no bone is `RIG_INFLUENCES_KEPT`; beside `auto` it is `CONFIG_INFLUENCES_MODE` (that mode's cap and floor are `auto.influences`). What each setting trades is "The weight cut" below |
|
|
171
171
|
| `meshes.<part>.stray` | rig | **authored**, optional (issue #303), in the `grid` mode: the largest island, in art pixels, that may be left out of the lattice — the contour mode's `contour.stray`, with the same meaning, over the same islands (the 4-connected islands of the art, alpha above 8): every island of `stray` art px or fewer is cleared from the art the lattice and, under `rule: "heat"`, its silhouette are built over, except an island as large as the largest, which is never left out (two equal largest pieces both stay). Unlike the contour mode an island above the figure is not refused — the lattice bridges every island it keeps, as it always has. An island that sits in a cell the lattice keeps for other art leaves the lattice unchanged: there `stray` changes only the silhouette `rule: "heat"` reads, and gives back no triangle (demo `bottomwear`'s 2 px island, in the cell kept for the hem's tip, is that case). Absent, nothing is left out and every byte is the one written before the field existed; a figure that leaves nothing out changes no byte of `rig.json` or `motion.json`. The part's image is not touched: a pixel left out stays in it, and is drawn only where a cell kept for other art happens to cover it; `art_coverage` is over all the art, the islands left out included. Declared, the mesh's `mesh_report.json` row echoes `stray` and adds `stray_cleared` (`islands`, `pixels`, `art_pixels`, after `influences`), and the rig stage's mesh line ends `stray <n> left out <i> island(s), <p> of <a> art px`. A value that is not a whole number from 0 is `CONFIG_FIELD_TYPE`; beside `contour` or `auto` it is `CONFIG_STRAY_MODE` (those modes state it in `contour.stray` and `auto.source.stray`). `propose` and `mesh-draft` never write it |
|
|
172
172
|
| `regions.<part>` | rig | **proposed**: the bone a rigid part rides. Every plan part is exactly one of a mesh or a region (`CONFIG_PART_ATTACHED`) Also accepts `{bone, gather?}`: `bone` is the same required bone name and `gather: false` keeps an exclusive non-segment limb rider at its authored draw position without changing its binding (issue #680). Remove `gather` for normal gathering; other values are refused and a non-rider is `RIG_GATHER_RIDER`. |
|
|
173
173
|
| `motion.duration` | rig; check (the loop is measured at this time) | proposed as 40 frames at 12 fps, `40/12` s, written `3.3333333333333335` (issue #252; the reference proposed 4 s, and every period it drafted is scaled with it so each track keeps its cycles per idle); a whole number of 1/12 s ticks, because `check` renders at 12 fps and compares frame 0 with the frame at this time — read within 10^-9 s (`TIME_SLACK`, issue #244), since a whole number of frames is often not exact in binary: rigc's 40th frame sits at `40 × (1/12)` = `3.333333333333333`. Half a frame off is `CHECK_LOOP_LAST_FRAME_AT_DURATION` |
|
|
@@ -184,6 +184,39 @@ read by no CPU stage).
|
|
|
184
184
|
| `motion_seams` | propose, assemble, check | Optional `[{part, neighbour}]`, using output part names. Each unordered pair is declared once; self-pairs, unknown names, wrong shapes and unknown fields refuse (`CONFIG_MOTION_SEAM`, `PARTS_MOTION_SEAM`, `CHECK_MOTION_SEAM_DECLARATION`). Empty adds no pair. Assemble copies the declaration into `parts.json`; check merges it with existing pairs and never regenerates a removed declaration. It uses the existing non-root WARN floor and changes no bar or exit status. See *Explicit motion seam observations* below. |
|
|
185
185
|
| `requirements` | build (read in place of `--requirements` when the flag is not given), and so its check | **authored**, optional, never proposed (issue #556): a path, relative to the config file's directory, to the `spine-parts-requirements/1` file the build's check reads (§7, *Declared requirements*) — so a rig that needs one (a `not_swinging` part, a `may_fold`) rebuilds from its config alone. `build` reads it before assemble, as it reads the flag's file, and `check.json`'s `requirements.file` records it with `path` as declared. Given beside `--requirements`, the two must name the same file once resolved, or a file of the same bytes, and the declaration is read and recorded (a build given both writes what the config alone writes); otherwise `BUILD_REQUIREMENTS_CONFLICT`, naming both. Not a non-empty string: `CONFIG_FIELD_TYPE`; no such file, where the config is read from a path: `CONFIG_FILE_PRESENT`. `check`, `compose`, `mesh-draft`, `remesh` and `retune` do not read it: their `--requirements` is the only door. Absent, nothing is read and no byte of any output moves |
|
|
186
186
|
|
|
187
|
+
### Drafting the chest carrier
|
|
188
|
+
|
|
189
|
+
The drafted contour torso also uses mesh-draft's existing dust rule. Extra islands
|
|
190
|
+
of at most 4 pixels and at most 0.1% of the part's art set `contour.stray` to the
|
|
191
|
+
largest dust island. The proposal note names the islands and pixels left out.
|
|
192
|
+
A real extra piece is refused by `PROPOSE_TORSO_ISLANDS` and must be separated.
|
|
193
|
+
|
|
194
|
+
`propose --bust-landmarks <landmarks.json>` reads an object with `apex_r` and `apex_l`,
|
|
195
|
+
and optional `cleavage`, each a finite `[x, y]` in rig pixels (crop origin at top left,
|
|
196
|
+
y down). If `cleavage` is absent, its point is the crossing of the apex segment and the
|
|
197
|
+
neck-to-hip midline. A missing unique crossing inside the segment is refused by
|
|
198
|
+
name, requiring an explicit cleavage point or apices on opposite sides of the midline.
|
|
199
|
+
The derived point is rounded to the existing 1/256-pixel contour grid. Declared apex
|
|
200
|
+
coordinates are preserved exactly in both the regions and their bones.
|
|
201
|
+
The proposal writes `contour.bust` and `boob_r` / `boob_l` under `chest`.
|
|
202
|
+
|
|
203
|
+
A chest-holding torso with neither landmarks nor an explicit omission reason is
|
|
204
|
+
refused with `PROPOSE_BUST_INPUT` before any proposal is written. `--no-bust "rigid breastplate"` omits the bust and records the
|
|
205
|
+
reason. Blank reasons and combining the two flags are refused. Both flags belong to
|
|
206
|
+
drafting and are refused with `--from-config`, which reads authored meshes unchanged.
|
|
207
|
+
Only one topwear part may cover the chest bone when no figure layer supplies its
|
|
208
|
+
trunk. Ambiguous art ownership is refused by name. A named dress whose topwear art
|
|
209
|
+
covers the chest also takes this contour rule, keeping its recipe's segments. A
|
|
210
|
+
recipe that explicitly declares `influence` for that torso conflicts with the contour
|
|
211
|
+
mode and is refused, naming the field to remove or the cloth part to separate.
|
|
212
|
+
|
|
213
|
+
Drafted torso bytes change from lattice (or a figure trunk's region) to contour.
|
|
214
|
+
Non-torso meshes retain their modes. A contour torso whose segments name a chain
|
|
215
|
+
also declares `{ "maxInfluences": null, "floor": "continuous" }`, avoiding the
|
|
216
|
+
influence cut's weight step across short outline edges (#991). This is a drafting
|
|
217
|
+
policy. It does not change the influence defaults on authored meshes.
|
|
218
|
+
|
|
219
|
+
|
|
187
220
|
A skeleton alternate may declare `anchor: {neck: "head"}` (issue #543):
|
|
188
221
|
one or two body-18 keypoints mapped to declared base-bone origins, in rig pixels.
|
|
189
222
|
One pair reads offset with scale held at 1; two read scale and offset without
|
|
@@ -735,6 +768,33 @@ identity an executable check on supplied original inputs instead of trusting
|
|
|
735
768
|
that claim or hand-editing `layers.json`. Seed and painting selection remain
|
|
736
769
|
the author's decisions.
|
|
737
770
|
|
|
771
|
+
### Residual front/back edges (`edge_residuals.json`, issue #999)
|
|
772
|
+
|
|
773
|
+
Under `assemble.project: "visible"`, assemble writes `edge_residuals.json` beside
|
|
774
|
+
`parts.json` after the existing painting-edge pass and final seam correction.
|
|
775
|
+
`EDGE_PAIR_RESIDUAL` prints its coverage and one row for every draw-order pair.
|
|
776
|
+
This is a report of candidates. The painting's semantic ownership is unproven.
|
|
777
|
+
It adds no gate or acceptance threshold.
|
|
778
|
+
|
|
779
|
+
The report uses the existing recomposite `ERROR_LIMIT` for max-channel RGB
|
|
780
|
+
error and `EDGE_OWNER_ALPHA` for visible ownership. It finds 8-connected
|
|
781
|
+
visible-back error components touching a front boundary, where those two parts
|
|
782
|
+
also have actual alpha overlap. There is no new distance cap. A pair gives its
|
|
783
|
+
front/back names, `residual_px`, `[left, top, width, height]` rig-pixel `box`,
|
|
784
|
+
and `back_pixels` as a count and `[y, left, right-exclusive]` row spans. Those
|
|
785
|
+
coordinates reference the existing back PNG at its `parts.json` placement.
|
|
786
|
+
|
|
787
|
+
Pairs without overlap or an exposed boundary carry an explicit skip reason.
|
|
788
|
+
A hidden observation, a component touching multiple overlapping fronts, or a
|
|
789
|
+
component with partial-alpha/additional foreground contribution is unavailable.
|
|
790
|
+
Its `residual_px` is null. `unavailable_px` preserves the ambiguous population,
|
|
791
|
+
and the summary also counts unassigned error pixels, including retained candidates
|
|
792
|
+
from unavailable pairs. Its residual plus unassigned total accounts for every
|
|
793
|
+
observed error pixel. A measured zero means zero
|
|
794
|
+
candidates under this observation model. It does not identify the true painted
|
|
795
|
+
hem or prove source material correct. Under `core` the visible pass and this
|
|
796
|
+
report do not run. Reassembling that output removes a stale report file.
|
|
797
|
+
|
|
738
798
|
### Taking a derived region (`assemble.take`, issue #1000)
|
|
739
799
|
|
|
740
800
|
A region entry names one existing plan part, its target run, one donor run and
|
|
@@ -1452,6 +1512,46 @@ lattice and contour modes a mesh may declare it:
|
|
|
1452
1512
|
"bottomwear": { "grid": 25, "r": 10, "segments": [["hip", [232, 425], [601, 425]], "skirt_r", "skirt_c", "skirt_l"], "influences": { "maxInfluences": 6, "floor": "continuous" } }
|
|
1453
1513
|
```
|
|
1454
1514
|
|
|
1515
|
+
**A weight step on an outline edge (issue #991).** `CHECK_TEXTURE_STRETCH` and
|
|
1516
|
+
`CHECK_ANIMATIONS_DO_NOT_FOLD` name a bone held by one vertex and absent from its
|
|
1517
|
+
neighbour, with both emitted shares. A build's hash-matched config record lets the
|
|
1518
|
+
check replay the production distance weighting before the cap and floor. The
|
|
1519
|
+
replay must reproduce every written share on the named vertices before the detail
|
|
1520
|
+
attributes a dropped neighbour to the cap or the cut floor. A recovered continuous
|
|
1521
|
+
floor is labelled as such. Without recoverable pre-cut shares, the detail reports
|
|
1522
|
+
the observed step and offers the remedy conditionally, `if the influence cut made
|
|
1523
|
+
this step`. The existing declaration is
|
|
1524
|
+
|
|
1525
|
+
```json
|
|
1526
|
+
"influences": { "maxInfluences": null, "floor": "continuous" }
|
|
1527
|
+
```
|
|
1528
|
+
|
|
1529
|
+
A standalone check without the config record takes that conditional path. A stale
|
|
1530
|
+
record or weights that disagree with the replay do too. The diagnostics use the
|
|
1531
|
+
existing stretch and fold bars.
|
|
1532
|
+
|
|
1533
|
+
**Recorded phase-1 measurements.** On the public `scarf` analogue, whose `topwear`
|
|
1534
|
+
also holds two scarf ends on `robe_r` and `robe_l`, the recorded run used heavier
|
|
1535
|
+
robe physics and idle amplitudes multiplied by five. The figures below are the
|
|
1536
|
+
phase-1 record at `2233cf3`, core `3.2.1`. [Issue #991 phase 1](https://github.com/rig-c/parts/issues/991#issuecomment-6617133128).
|
|
1537
|
+
|
|
1538
|
+
| topwear mesh | TEXTURE_STRETCH | folding meshes | BOUNDARY_COLOUR_STEP |
|
|
1539
|
+
| --- | --- | --- | --- |
|
|
1540
|
+
| grid 29 | 1.311 | 0 | 1 |
|
|
1541
|
+
| contour, bust, bridges | 2.212 FAIL | 1 FAIL | 4 |
|
|
1542
|
+
| contour, bust, bridges, maxEdge 29 | 2.212 FAIL | 1 FAIL | 4 |
|
|
1543
|
+
| contour, no bust | 2.212 FAIL | 1 FAIL | 4 |
|
|
1544
|
+
| contour, bust, continuous floor, no cap | 1.406 | 0 | 4 |
|
|
1545
|
+
| contour, no bust, continuous floor, no cap | 1.406 | 0 | 4 |
|
|
1546
|
+
| contour, bust, maxEdge 29, continuous floor, no cap | 1.406 | 0 | 4 |
|
|
1547
|
+
|
|
1548
|
+
The same phase-1 record reports a failing 4.24 px outline edge and a change from
|
|
1549
|
+
mean 3.19 / maximum 4 influences to mean 3.99 / maximum 9 under the continuous
|
|
1550
|
+
uncapped declaration. Keeping cap 4 with the continuous floor retained a cap swap.
|
|
1551
|
+
Removing the cap with the cut floor gave stretch 5.167. On `reference-sample`, the
|
|
1552
|
+
continuous uncapped declaration kept every line passing and moved `topwear`'s
|
|
1553
|
+
stretch from 1.048 to 1.051. These figures retain the phase-1 source above.
|
|
1554
|
+
|
|
1455
1555
|
**A topwear hem over a rigid bottomwear (issue #650).** Only an explicit `waist`
|
|
1456
1556
|
declaration enables this construction. Without it, even an eligible pair keeps
|
|
1457
1557
|
the original bindings, rigid region, graph, motion, reports and images. A distance lattice whose
|
|
@@ -3197,6 +3297,8 @@ world units. Other proposal modes (`--keypoints`, `--compare`, `--grid-rule`,
|
|
|
3197
3297
|
|
|
3198
3298
|
| rule | means | change |
|
|
3199
3299
|
| --- | --- | --- |
|
|
3300
|
+
| <a id="PROPOSE_TORSO_ISLANDS"></a>`PROPOSE_TORSO_ISLANDS` | the chest carrier has no art, tied largest islands, or an extra island exceeding the existing mesh-draft dust limits | separate real pieces so the torso has one largest art island and only dust beside it |
|
|
3301
|
+
| <a id="PROPOSE_BUST_INPUT"></a>`PROPOSE_BUST_INPUT` | a chest torso has neither a bust declaration nor an omission reason, a bust landmark is missing, malformed or unknown, the omission reason is blank or conflicts with landmarks, the midline has no unique crossing inside the apex segment, or the chest carrier is absent or ambiguous, or its recipe declares an incompatible influence mode | give finite rig-pixel apex_r/apex_l and optional cleavage, or a non-blank omission reason; separate overlapping chest-holding topwear |
|
|
3200
3302
|
| <a id="PROPOSE_NECK_BODY_TURN"></a>`PROPOSE_NECK_BODY_TURN` | no authored sampled body turn | follow this recipe and supply the named input; the refusal writes no config |
|
|
3201
3303
|
| <a id="PROPOSE_NECK_PLAN"></a>`PROPOSE_NECK_PLAN` | no unique neck plan row | follow this recipe and supply the named input; the refusal writes no config |
|
|
3202
3304
|
| <a id="PROPOSE_NECK_FULL_SOURCE"></a>`PROPOSE_NECK_FULL_SOURCE` | no full-run neck source | follow this recipe and supply the named input; the refusal writes no config |
|
|
@@ -3549,7 +3651,7 @@ full loader before its assemble stage writes anything.
|
|
|
3549
3651
|
7. `rig-parts assemble … --out work` — read its `uncovered hole` and `uncovered:` lines and look at
|
|
3550
3652
|
`work/render/recomposite_error_rig.png` (§5): red is painting no part holds, which
|
|
3551
3653
|
no later stage can see — then
|
|
3552
|
-
`rig-parts propose --parts work/rig --source inputs/painting.png --out work --write-config config.json` →
|
|
3654
|
+
`rig-parts propose --parts work/rig --bust-landmarks bust-landmarks.json --source inputs/painting.png --out work --write-config config.json` →
|
|
3553
3655
|
`work/proposal.json` and `work/render/landmarks.png` (+ `_head`). The flag saves
|
|
3554
3656
|
each of `bones`, `meshes`, `regions`, `limbs`, `motion`, `constraints`, `turn`, `motion_seams` and `proposed_rig_scale` it
|
|
3555
3657
|
wrote into the config: `limbs` is written only with `--keypoints`, `constraints`
|
|
@@ -3830,7 +3932,7 @@ everywhere:
|
|
|
3830
3932
|
rig-parts layers art.psd
|
|
3831
3933
|
rig-parts assemble --propose-plan --source painting.png --full art.psd --config config.json
|
|
3832
3934
|
rig-parts assemble --source painting.png --full art.psd --config config.json --out .
|
|
3833
|
-
rig-parts propose --parts rig --source painting.png --out .
|
|
3935
|
+
rig-parts propose --parts rig --bust-landmarks bust-landmarks.json --source painting.png --out .
|
|
3834
3936
|
rig-parts build --config config.json --source painting.png --full art.psd --out build
|
|
3835
3937
|
```
|
|
3836
3938
|
|
|
@@ -5019,6 +5121,7 @@ those settings. Changing a config seed alone cannot change a no-config run.
|
|
|
5019
5121
|
| <a id="ASSEMBLE_TAKE_SUPPORT"></a>`ASSEMBLE_TAKE_SUPPORT` | selected layer has zero alpha > 8 pixels | choose a nonempty donor layer |
|
|
5020
5122
|
| <a id="ASSEMBLE_TAKE_DEPTH_AMBIGUOUS"></a>`ASSEMBLE_TAKE_DEPTH_AMBIGUOUS` | different original runs have equal or null depths | use a run with distinct numeric depths; do not invent an inter-run tie order |
|
|
5021
5123
|
| <a id="ASSEMBLE_TAKE_PLAN_ORDER"></a>`ASSEMBLE_TAKE_PLAN_ORDER` | supplied plan reverses an overlapping mixed-run pair: two-run full-layer head roles first, scalar depth within a role band (head-run and single-run rows use scalar depth); names the parts and support intersection | use the order drafted by `assemble --propose-plan`; fallback headwear follows the body and precedes the face, front hair follows the face; disjoint supports impose no plan-order constraint |
|
|
5124
|
+
| <a id="EDGE_PAIR_RESIDUAL"></a>`EDGE_PAIR_RESIDUAL` | report-only front/back residual candidates after visible edge composition, with pixel spans, boxes and coverage | read measured candidates and explicit skipped/unavailable reasons in `edge_residuals.json`. No pass/fail threshold is applied |
|
|
5022
5125
|
| <a id="ASSEMBLE_REGION_TAKE_TOLERANCE"></a>`ASSEMBLE_REGION_TAKE_TOLERANCE` | a region take has no authored `setup_painting.rgb_tolerance` | supply the same requirements file to assemble/build and check |
|
|
5023
5126
|
| <a id="ASSEMBLE_REGION_TAKE_GEOMETRY"></a>`ASSEMBLE_REGION_TAKE_GEOMETRY` | donor and target canvases or original input RGBA differ, or the target head run is absent | supply the same crop and original input PNG. Cropped donors await #973 |
|
|
5024
5127
|
| <a id="ASSEMBLE_REGION_TAKE_PART"></a>`ASSEMBLE_REGION_TAKE_PART` | the named plan part/run or its base/donor tag is absent, or the target is taken twice | name an existing plan part and one donor holding its tag |
|
|
@@ -5056,6 +5159,9 @@ those settings. Changing a config seed alone cannot change a no-config run.
|
|
|
5056
5159
|
| <a id="ASSEMBLE_PAINTING_EXTENSION_CONFLICT"></a>`ASSEMBLE_PAINTING_EXTENSION_CONFLICT` | two declared final parts can reach the same eligible painting pixel without explicit precedence | make their reachable claims disjoint, or declare a unique nonnegative integer `order` on every entry; lower orders claim first, never array order |
|
|
5057
5160
|
| <a id="ASSEMBLE_PAINTING_CANVAS"></a>`ASSEMBLE_PAINTING_CANVAS` | a callable extension stage receives a part outside the rig canvas | pass the final in-canvas assembled stack with its crop offsets |
|
|
5058
5161
|
| <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 |
|
|
5162
|
+
| <a id="DIFF_COMPONENTS_INPUT"></a>`DIFF_COMPONENTS_INPUT` | `diff-components` needs readable PNG stack/painting/part images, a writable `--out` target when writing, explicit parts, positive integer floor, a finite threshold in [0,255], matching native dimensions and valid coverage/domain inputs; flags must be unique and known, with nonempty values | Supply the named missing or invalid input; no resampling or inferred figure is performed |
|
|
5163
|
+
| <a id="DIFF_COMPONENTS_BINDING"></a>`DIFF_COMPONENTS_BINDING` | The shipped report reader found a stack or painting SHA-256 different from the supplied bytes; found and required digests are printed | Regenerate against the bytes being reviewed |
|
|
5164
|
+
| <a id="DIFF_COMPONENTS_REPORT"></a>`DIFF_COMPONENTS_REPORT` | Invalid report JSON/spec, or its exhaustive inventory, definition, paths, domain or coverage differs from remeasurement; capped inventories are refused | Regenerate the complete versioned report against the same input paths, bytes, parts and figure domain |
|
|
5059
5165
|
| <a id="ASSEMBLE_PINHOLES"></a>`ASSEMBLE_PINHOLES` | the requirements file declares `pinholes.required: true` and the flat parts stack holds a pinhole: an 8-connected opening (alpha 0 on every part, enclosed by the parts' alpha above 0) whose every pixel the painting shows as figure. One refusal per pinhole, largest first, naming its box, its px and the parts bordering it; nothing is written (issue #944, [Pinholes at rest](#pinholes-at-rest-issue-944)) | `assemble.plan` / `assemble.take` / the head pass: take the pinhole's pixels from a layer that holds them ([Source before scissors](#source-before-scissors)); where no layer holds them, the defect is the input's. Without the declaration the bar reports SKIP |
|
|
5060
5166
|
| <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 |
|
|
5061
5167
|
| <a id="ASSEMBLE_PATCH_OPAQUE"></a>`ASSEMBLE_PATCH_OPAQUE` | a patch takes no pixel: a `"silhouette"` patch whose box holds none of the figure | move the box onto the figure, or `"alpha": "box"` |
|
|
@@ -5415,11 +5521,11 @@ documents do not ship). Each is an input this package built; none is an author's
|
|
|
5415
5521
|
| <a id="CHECK_CHAIN_LAG"></a>`CHECK_CHAIN_LAG` | a rotate track leads (or does not lag) the keyed bone above it, or a chain link swings less than the link above — or, in a chain that carries a part the scene file declares `rests`, more than the link above (issue #245; the object names the part and the file). A lag refusal (issue #580) prints the step, then the two phases it read — as the sine reader returns them, a negative amplitude read as amplitude > 0 at phase + 0.5 — then their difference, folded into (−¼, ¼] cycle because a sine's sign is half a cycle (so flipping an amplitude's sign does not change the step), then the child phases that pass for this parent: `phase step -0.230 cycle(s); >= 0.001 is required — … Phases read: "chest" 0.000, "sleeve_l0" 0.270 …; the step is their difference, 0.270, folded into (-1/4, 1/4] cycle … here a "sleeve_l0" phase in [0.001, 0.250] or [0.501, 0.750]` | for a lag, that chain track's `phase`/`lag`: a child phase from the parent's + 0.001 to + 0.25, modulo 0.5 — the windows the line prints. A larger phase helps only inside them: a child 0.25 to 0.5 above its parent still reads as a lead, and a negated `amps` moves nothing. For a swing, its `amps` (non-decreasing toward the tip; non-increasing on a chain declared resting) |
|
|
5416
5522
|
| <a id="CHECK_TIP_OVER_ROOT"></a>`CHECK_TIP_OVER_ROOT` | a `handwear`/`bottomwear` part's art in the lower half of its box travels less than 1.4725 times as far as in the upper half, measured from the idle's posed geometry against the bone the part hangs from (issue #987; rig px quoted, the world reading beside it) | the chain track's `amps` (grow toward the tip), or the mesh's `segments` (the chain must be among them), or its mode: on `scarf`'s short skirt the proposal's lattice read 1.207 and the same part in `contour` mode 1.738 with the same segments and idle (issue #118) — both carried the `chest` segment #262 removed, after which the lattice reads 3.487. When the detail says `the root travels with the breath (x px of y px): it is carried by \`<bone>\`` (issue #262) the chain is not the cause: a bone an idle `translate` track moves (the breath) moves the root more than the tip, and with that translate held still (the part posed again without it, in proportion to each vertex's weight on the bone and those under it) the ratio passes — the row adds `carried_by`, `breath_rig_px`, `root_without_breath_rig_px` and `tip_without_breath_rig_px`; take that bone's segment off the mesh, or bind the root to the bone the part hangs from, which the detail names. Since issue #987 only a breath keyed under that bone is read: one at or above it is in the frame the halves are read in and carries nothing. When the bone named is a link of the chain the part hangs on, its segment stays, and the detail says to key the translate on the bone the part hangs from or above it. `nothing in it moves against \`<bone>\`` is a part that rides the bone it hangs from whole — a rigid sleeve, whose world ratio is 1. Since issue #995 a lone swing counts as a link too — a rotate-keyed bone with no child bone, or whose only child is its control twin (a one-link chain's control) — so a skirt bound to the one bone that swings it is read against the bone above, where its swing shows. A rotate-keyed body segment other bones hang under (a pelvis carrying the legs) is not one: a part bound whole to it reads `nothing in it moves against` that bone and fails, its world ratio passing only through the lever arm. Hang swinging cloth on its own bone under the body segment; a fitted garment the rig holds rigid on purpose (shorts on the pelvis) is declared in the scene file's `not_swinging` (§7), and the line then reads it SKIP with the reason. A part the scene file declares `rests` (issue #245) is held the other way, its tip half's travel at most its root half's (ratio ≤ 1); the detail then reads `the scene file's rests declares its tip at rest, so a ratio <= 1 is required`, and for it the fix is the chain's `amps` (shrinking toward the tip) or the mesh's tip weighted to a bone the idle does not swing |
|
|
5417
5523
|
| <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 |
|
|
5418
|
-
| <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) |
|
|
5419
|
-
| <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 on — and with its physics constraints left out beside it when the rig declares any, so each row reports `physics_on` and `physics_off` side by side (issues #320, #333, #363); 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 — and when the fold appears only with physics on, names the physics constraint(s) on the mesh's bones and the worst frame. After rigc's line it names what the worst frame's folded triangles hang on (issue #567), in `TEXTURE_STRETCH`'s words: each vertex's dominant bone is its largest weight in `rig.json`, the triangles whose vertices share one set of dominant bones are one group (the set in `rig.json`'s bone order), and each group lists its first 6 triangles (`triangles 63, 78, 79 dominated by "knee_r", "knee_l"`, past 6 `… and N more`) and spells out its first triangle's vertices — the largest two weights of each, and the idle chains those bones hang on; 3 groups are written and the rest counted (`; … and N more group(s), M triangle(s)`). An attachment the skin does not hold, or a triangle its table does not reach, is said, not guessed. Or a `may_fold` entry of the `--requirements` file names a slot and animation that does not fold: an exception that excepts nothing | the physics constraint(s) the line names (lower `inertia`, raise `strength`, or lower `damping`), the clip's keys on the bones the line names (a bend past what the weights carry, two bones turned toward each other until the rows between them pass, 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 |
|
|
5524
|
+
| <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 A differing bone set on the failing edge or triangle names the bone and both shares. Cut attribution requires a matching config replay; otherwise the influence-cut remedy is conditional ("The weight cut"). | 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) |
|
|
5525
|
+
| <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 on — and with its physics constraints left out beside it when the rig declares any, so each row reports `physics_on` and `physics_off` side by side (issues #320, #333, #363); 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 — and when the fold appears only with physics on, names the physics constraint(s) on the mesh's bones and the worst frame. After rigc's line it names what the worst frame's folded triangles hang on (issue #567), in `TEXTURE_STRETCH`'s words: each vertex's dominant bone is its largest weight in `rig.json`, the triangles whose vertices share one set of dominant bones are one group (the set in `rig.json`'s bone order), and each group lists its first 6 triangles (`triangles 63, 78, 79 dominated by "knee_r", "knee_l"`, past 6 `… and N more`) and spells out its first triangle's vertices — the largest two weights of each, and the idle chains those bones hang on; 3 groups are written and the rest counted (`; … and N more group(s), M triangle(s)`). An attachment the skin does not hold, or a triangle its table does not reach, is said, not guessed. Or a `may_fold` entry of the `--requirements` file names a slot and animation that does not fold: an exception that excepts nothing A differing bone set on the failing edge or triangle names the bone and both shares. Cut attribution requires a matching config replay; otherwise the influence-cut remedy is conditional ("The weight cut"). | the physics constraint(s) the line names (lower `inertia`, raise `strength`, or lower `damping`), the clip's keys on the bones the line names (a bend past what the weights carry, two bones turned toward each other until the rows between them pass, 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 |
|
|
5420
5526
|
| <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 |
|
|
5421
5527
|
| <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) |
|
|
5422
|
-
| <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
|
|
5528
|
+
| <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 bone outside its segment carrier and its own declared contour region bones, 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 |
|
|
5423
5529
|
| <a id="CHECK_SEAM_OVER_RANGE"></a>`CHECK_SEAM_OVER_RANGE` | a limb joint with a declared `range` (issue #360), bent to one of its range's ends or its middle, fails the rest-pose seam's bar against the same pose with the joint's own art drawn under the limb — the bend uncovers the joint; the object names the joint, its limb and segment, the part, the bone and the angle. Under `cap_shape: "disc"` (issue #360, unit B) or `"ball"` (issue #598), also any pixel of the disc over 40 at a pose, under the seam's bar or not. Also a joint the line cannot read (its bone has no parent in `rig.json`, no slot shows its limb, or its cap's disc holds no opaque art), and, refused, two renders of the bent copy that rigc did not put on one grid | [Source before scissors](#source-before-scissors) first; a cap that covers the turn: the layer's art within the cap's radius on the upper side of the joint line, or a `cap` image painting what the layer lacks (`limb cap:` names the missing pixels); or a narrower `range` |
|
|
5424
5530
|
| <a id="SEAM_OVER_MOTION"></a>`SEAM_OVER_MOTION` | **Existing internal-joint and explicit-seam pairs remain warnings** (issue #493; a seam that opens in motion may be intended). **Proven body-root pairs are a bar** (issue #686): an above-floor opening adds `CHECK_SEAM_OVER_MOTION`, sets `PASS` false and exits 1. **Under the requirements file's `seam_over_motion.required: true`** (issue #941) every declared pair, of all three classes, is also held to 0 background cells on every frame — [`CHECK_SEAM_OVER_MOTION_REQUIRED`](#CHECK_SEAM_OVER_MOTION_REQUIRED); undeclared, nothing in this row moves. Every declared neighbour pair — each limb's two segments at a joint, the upper and the one below (`parts.json`'s limb records, `config.limbs`), and under `--requirements` each `seam`'s `part` and `neighbour`, one pair however many declare it — read in every animation `motion.json` holds, the idle included, by rig-c's seam reading: one `rigc render --seam <a>:<b>,… --fps 12` over the packed build as played (physics on), whose `seam.json` (`rigc-seam/1`, rig-c's AUTHORING §8.8) names per pair and frame a **gap** (background inside the joint) or a **ghost** (one piece's overlap band swung clear of its partner), each frame's cells (`px`, one texel of the coarser drawing — a rig px on a build at scale 1), its cell size and its world box. Printed for existing warning pairs and animations with a frame over the floor: `WARN seam "<a>\|<b>" opens on N frame(s) [<frames>] max <px> px in "<animation>" — over the one-cell floor`, then how many more frames rigc named that were under it. **The floor**: a frame's gap or ghost is reported when it holds more cells than its box is long (its longer side, in cells) — thicker than one cell. A cell is drawn when the renderer paints its centre at half opacity, so an outline that moves by less than a cell flips the cells along it: a line one cell deep, joined or scattered, holds at most as many cells as its box is long, and a hole of any area more. The rule types no number; measured on `examples/wind`'s idle, rigc names 43 openings over its six limb pairs (the two elbows, 1 to 5 cells, boxes up to 17 cells long), every one one cell thick, and on rig-c's `gallery/walk` 13 joint cracks of 1 to 3 cells, all one cell thick. `check.json`'s `SEAM_OVER_MOTION` holds `status` (`WARN`, `CLEAR`, `SKIP`, or `FAIL` for a new root tear), `fps`, `floor`, `pairs`, `animations`, `warnings` and one row per pair and animation (`opens` and `px` over the floor, `max_px`, `under_floor`, rigc's `status`, `drawn` and `banded`); for existing warning pairs it moves no exit status, no `check: PASS` and no bar count — the summary ends `; N warning(s) (SEAM_OVER_MOTION, not a bar)`. **SKIP**, named: no pair declared (no limb segment, no `seam`; then `check.json` has no key), no `parts.json`, no animation, every pair naming a slot `rig.json` lacks (each printed `not read`), or rigc reading no band on any frame of any pair (rigc's own `skip`: the two drawings share no cell at rest) — nothing measured, never a pass | [Source before scissors](#source-before-scissors) first; if the opening is not intended: a cap that covers the turn (`config.limbs`, *Limbs: cut at their joints*; `limb cap:` names the pixels the layer lacks), or a motion that bends that joint less; if it is intended, nothing |
|
|
5425
5531
|
| <a id="CHECK_MOTION_SEAM_ROOT"></a>`CHECK_MOTION_SEAM_ROOT` | a body-root declaration has missing or ambiguous segment0, bone, parent, slot or default-skin attachment; the refusal names the part and candidates | supply the declared one-to-one hierarchy/slot/provenance binding; no nearest or mesh-influence fallback |
|
|
@@ -5830,7 +5936,7 @@ the two examples [observed]:
|
|
|
5830
5936
|
| <a id="ANIMATIONS_DO_NOT_FOLD"></a>`ANIMATIONS_DO_NOT_FOLD` | every animation, the idle included, through `rigc render --folds` at 12 fps over the build with its physics stepping and, when the rig declares physics constraints, over the keyed build with them left out beside it (issue #363): 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 on and off | never SKIP: written whenever `motion.json` holds an animation |
|
|
5831
5937
|
| <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 |
|
|
5832
5938
|
| <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) |
|
|
5833
|
-
| <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`
|
|
5939
|
+
| <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` and the adjacent optional `mesh_report.json`, 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); a contour mesh may also bind the region bones its own part declares in `mesh_report.json`, named as `region_bones` in the row, with the same existing turn-twin equivalence. Hybrid bands retain their two-carrier rule | every limb part bound only to its segment carrier or its own declared contour region bones | 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 |
|
|
5834
5940
|
| <a id="SEAM_OVER_RANGE"></a>`SEAM_OVER_RANGE` | issue #360. Each limb part `parts.json` records with a `range` (`config.limbs.<limb>.segments[k].range`): a copy of the rig with its physics constraints left out, its joint's bone keyed `rotate` to the range's min, its middle and its max (each angle once, rigc's sense, counter-clockwise on screen), one `rigc build --profile spine` and two `rigc render`s on one grid — one with the joint's own art drawn under the limb (the limb's parts at rest inside the cap's disc, one pixel in from every edge of what is opaque there, on the bone the joint's bone hangs from), one with it hidden (`--hide`); per pose, the seam figures of the second against the first. Issue #557: per pose also the **contour step**, a reading and never a bar — on the frame with the joint's art hidden, lines across the upper segment (perpendicular to its bone's direction at the setup pose, origin to the joint) one frame pixel apart, from one cap radius before the joint line to one past it; on each, from the axis outward on each side, the outline's extent (the first sample within 40 of the background, out to two radii); the step on a side is the largest change between adjacent lines, in rig px, `left` and `right` as the cap's `short_px` names them. A limb whose outline the bend keeps reads one frame pixel, 1 / s rig px (1.161 at the synthetic fixtures' s 0.861413); the unfilled two-layer knee of the selftest reads 11.609 on the opening side at ±15° and the filled one 1.161. The row prints it; `check.json` carries it (`step_px` on each pose) only for a joint whose cap record carries `short_px` | the rest-pose seam's: mean \|d\| ≤ 1.0 and ≤ 50 rig px over 40 at every pose; a `cap_shape: "disc"` or `"ball"` joint also 0 frame px over 40 (issue #598: the ball over its whole disc; the row says `shape`) | not written: no limbs | not written: no limbs | never: written only when `parts.json` holds a limb record with a `range`, so the examples' `check.json` keep their keys |
|
|
5835
5941
|
|
|
5836
5942
|
**Small link motion (issue #994).** After the carrier-frame zero guard, a part on
|
|
@@ -6134,9 +6240,26 @@ Under `--source <painting.png>`, a second reported line, also never a FAIL
|
|
|
6134
6240
|
| line | reports | bar | `reference-demo` | `reference-sample` | SKIP when |
|
|
6135
6241
|
| --- | --- | --- | --- | --- | --- |
|
|
6136
6242
|
| <a id="SETUP_POSE_VS_SOURCE"></a>`SETUP_POSE_VS_SOURCE` | the setup pose against the painting, by `assemble`'s own `measureRecomposite` over the stage's pixels — `mean_abs` and `within_share` (mean channel, within 8), `error_px` (max channel over 40), `uncovered_error_px` (of those, where the pose has alpha 128 or less), `hole_count` and the largest hole's box — with `painting_px`, `stage_px`, `render_px` and `render_scale` beside them. The painting is resampled onto the stage as assemble resamples it (lanczos3, alpha dropped). rigc renders onto an opaque grey, so the pose over white and its alpha are read off a second still of the same pose with every slot tinted black; both are carried onto the stage with the seam's map turned round (bilinear) | none — status `REPORTED`; assemble applies no bar to the figures it measures, so none is derived here | 3.146 / 93.43 % / 12,399 / 1,732, 518 holes, the largest 61 px at 351,157 6x20 (assemble's recomposite: 2.391 / 95.81 % / 11,050 / 1,564, 453) | 1.095 / 97.30 % / 5,348 / 1,239, 313 holes, the largest 88 px at 479,416 6x29 (assemble's: 0.835 / 97.95 % / 4,512 / 1,185, 259) | not written without `--source` |
|
|
6243
|
+
| <a id="PART_SUPPORT_REPORT"></a>`PART_SUPPORT_REPORT` | report-only alpha >= 128 observation; pairs use pre-rig `parts.json` placements in setup slot order, and Unowned uses the official renderer's setup on the native stage grid | every pair is measured with its independent authored cut domain or unavailable with its reason; the headline gives adjacent raw pairs and classified coverage, with UNCLASSIFIED when there is no extent witness. Every touching or overlapping raw pair carries shared boundary length, bounds of shared segments and overlap cells, overlap area, and the label "ownership unknown, unclassified". Disjoint pairs are absent from raw rows. Exact shared 4-neighbour unit edges carry both names, an edge box and candidate missing-domain area under the front part. Unowned painted silhouette cells outside the actual setup union carry 4-connected boxes and areas, including open-edge gaps. Default build supplies its painting internally for this report only. Standalone check needs `--source`. Legacy output without the optional domain sidecar, bone-only bindings and limbs without an authored radial width are unavailable. WARN, REPORTED and UNCLASSIFIED add no gate. |
|
|
6244
|
+
| <a id="CHECK_SUPPORT_GRID"></a>`CHECK_SUPPORT_GRID` | a support sidecar, part PNG or painting cannot be registered to the declared native canvas | use the same parts.json dimensions and source scale, and regenerate assemble output; no resized or guessed domain is substituted |
|
|
6137
6245
|
| <a id="SETUP_MATCHES_PAINTING"></a>`SETUP_MATCHES_PAINTING` | declared region count, native box, limit and actual top contributor rows; observations source-over white unless `setup_painting.backdrop` declares the opaque plate behind a full-frame painting | PASS/FAIL against authored limits; no limit is SKIP, or the refusal `SETUP_PAINTING_REQUIRED_LIMIT` under `setup_painting.required: true`; with `--source`, no declaration is SKIP with its reason and declaration remedy; undeclared regions are not judged |
|
|
6138
6246
|
| <a id="BOUNDARY_COLOUR_STEP"></a>`BOUNDARY_COLOUR_STEP` | the worst colour-step excess over the rig's own pieces at any part boundary of the setup and every idle frame, its part pair, frame and stage pixel, and the declared `max_step_excess` | PASS/FAIL against the declared allowance (FAIL is [`CHECK_BOUNDARY_COLOUR_STEP`](#CHECK_BOUNDARY_COLOUR_STEP), an unreadable piece [`CHECK_BOUNDARY_COLOUR_UNMEASURED`](#CHECK_BOUNDARY_COLOUR_UNMEASURED)); no `boundary_colour` in the requirements file is SKIP with its reason and the declaration to add, never a pass |
|
|
6139
6247
|
|
|
6248
|
+
`assemble` writes optional `part_support_domains.json` beside `parts.json`, with
|
|
6249
|
+
spec `rig-parts-support-domains/1`. It binds the exact parts.json and named PNG
|
|
6250
|
+
hashes, the validated cut declarations' digest, and its domain payload digest.
|
|
6251
|
+
Each measured domain carries its cut-polygon kind, config entry and sorted
|
|
6252
|
+
flattened `[start, length]` mask runs. These masks are copied from assemble's
|
|
6253
|
+
existing polygon rasteriser before art-dependent operations. The reader only
|
|
6254
|
+
decodes the runs. Other parts carry explicit unavailable reasons. Limb joint
|
|
6255
|
+
lines have no authored radial width, so neither their art-derived cap radii nor
|
|
6256
|
+
an inferred fraction of bone length supplies a capsule. No new config field is
|
|
6257
|
+
introduced. Missing or stale sidecars give unavailable pairs. A grid mismatch
|
|
6258
|
+
is `CHECK_SUPPORT_GRID`. The sidecar changes neither parts.json nor part PNGs.
|
|
6259
|
+
The report reuses check's setup candidate and samples official per-piece alpha
|
|
6260
|
+
callbacks. It adds no rigc build/render command. Its sampling cost is separate
|
|
6261
|
+
from the opaque setup PNG, whose alpha cannot identify uncovered cells.
|
|
6262
|
+
|
|
6140
6263
|
The figures are assemble's by construction, not a second definition beside them;
|
|
6141
6264
|
what differs is what they are taken of — the pose rigc draws, not the flat stack —
|
|
6142
6265
|
and the gap between the two columns above is that drawing: the render's resampling at
|
|
@@ -7647,3 +7770,48 @@ contract records the measured grid residual and preserves its seam readings.
|
|
|
7647
7770
|
No-image builds keep the legacy output bytes. `comfy seethrough --config <file> --depth-run <full|head>`
|
|
7648
7771
|
uses the same opt-in; its default captures art only. Standalone authored part
|
|
7649
7772
|
references remain active declarations. See [the input contract and refusals](AUTHORING.md#per-layer-depth-sheets-773).
|
|
7773
|
+
|
|
7774
|
+
### Whole-figure difference inventory
|
|
7775
|
+
|
|
7776
|
+
`rig-parts diff-components --stack stack.png --painting painting.png --parts parts.json --floor 2 --out diff-components.json`
|
|
7777
|
+
writes `rig-parts-diff-components/1`. Supply the assembled stack and the painting
|
|
7778
|
+
**already in the same native canvas** (for a scaled assembly, use its rig-sized
|
|
7779
|
+
painting). Parts are read in manifest order from the `parts/` sibling directory.
|
|
7780
|
+
No registration or resampling occurs. For scene assemblies pass
|
|
7781
|
+
`--figure figure_rig.png`, the exact opaque black/white figure image used by
|
|
7782
|
+
assemble; omitting it explicitly selects the entire canvas.
|
|
7783
|
+
|
|
7784
|
+
`--threshold` is caller-given, with the existing recomposite default 40:
|
|
7785
|
+
maximum absolute RGB-channel difference **strictly greater** than the threshold;
|
|
7786
|
+
alpha is not a difference channel. `--floor` is required, a positive integer;
|
|
7787
|
+
a component exactly at it is included. The floor and any supplied threshold must
|
|
7788
|
+
contain a number: blank values are refused, never converted to zero. Connectivity is 8. The report's
|
|
7789
|
+
`definition` records these values, native coordinates (y down, top-left),
|
|
7790
|
+
width/height, no resampling, the existing coverage alpha cut (>128), and the
|
|
7791
|
+
SHA-256 of the binary figure domain (`null` for the whole canvas).
|
|
7792
|
+
|
|
7793
|
+
`stack` and `painting` each record the supplied path and SHA-256 of the exact
|
|
7794
|
+
PNG buffer decoded for measurement. `components` is uncapped and ordered by
|
|
7795
|
+
first pixel in row-major scan order. Each entry records `id` (`p<first pixel
|
|
7796
|
+
index>-n<pixel count>`), `px`, `box: {x,y,w,h}`, `coverage`, `uncovered_px` and
|
|
7797
|
+
`borders`. Coverage is `covered`, `uncovered` or `mixed`: a whole-error component
|
|
7798
|
+
can cross a part's edge, so a boolean would lose pixels. Borders use the existing
|
|
7799
|
+
hole convention: part pixels above the coverage cut touching a component in
|
|
7800
|
+
their 3x3 neighbourhood, counted once per part pixel, sorted by count then
|
|
7801
|
+
manifest order. A covered component may therefore name its own covering part.
|
|
7802
|
+
|
|
7803
|
+
`below_floor: {count,px,uncovered_px}` accounts for all omitted components.
|
|
7804
|
+
Listed `px` plus `below_floor.px` equals `error_px`; listed `uncovered_px` plus
|
|
7805
|
+
`below_floor.uncovered_px` equals `uncovered_error_px`. The existing
|
|
7806
|
+
`parts.json.recomposite` block, including `holes_listed: 5`, is unchanged.
|
|
7807
|
+
|
|
7808
|
+
Replace `--out` with `--verify diff-components.json` to use the shipped reader.
|
|
7809
|
+
Use the same paths and floor/threshold flags. It collects both source-binding mismatches before refusing. With matching bindings,
|
|
7810
|
+
it recomputes every field against the supplied parts and domain, refusing
|
|
7811
|
+
missing, capped or altered components, stale coverage and definition drift. Every
|
|
7812
|
+
changed, missing or extra inventory field names its path, the value found and the
|
|
7813
|
+
value required; independent differences are collected into one refusal.
|
|
7814
|
+
It does not authenticate a caller's chosen threshold or floor: consumers must
|
|
7815
|
+
compare those to their own policy. The report does not choose which components
|
|
7816
|
+
a consumer accepts. The named refusals are `DIFF_COMPONENTS_INPUT`,
|
|
7817
|
+
`DIFF_COMPONENTS_BINDING` and `DIFF_COMPONENTS_REPORT` in the rule table.
|