rig-parts 1.86.0 → 1.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.ts +52 -12
- package/docs/AUTHORING.md +205 -2
- package/docs/STABILITY.md +50 -5
- package/package.json +1 -1
- package/src/assemble.ts +2 -10
- package/src/build.ts +12 -8
- package/src/check.ts +5 -4
- package/src/config.ts +14 -3
- package/src/depthmaps.ts +170 -0
- package/src/depthworkflow.ts +239 -0
- package/src/rig.ts +17 -0
- package/src/seamrule.ts +11 -0
- package/src/setuppainting.ts +6 -1
package/cli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { applyTakenLayers } from './src/takelayers.ts';
|
|
3
|
+
import { applyDepth, prepareDepth } from './src/depthworkflow.ts';
|
|
3
4
|
/**
|
|
4
5
|
* rig-parts — one painting and its See-through layers in, Spine-ready parts
|
|
5
6
|
* and rig specs out, verified through rig-c.
|
|
@@ -67,6 +68,16 @@ const LATER: ReadonlyArray<[string, string]> = [];
|
|
|
67
68
|
const HELP = `rig-parts ${version()} — Spine-ready parts from one painting and its See-through layers
|
|
68
69
|
|
|
69
70
|
usage:
|
|
71
|
+
rig-parts depth-prepare --parts <assembled-dir> --config <depth-recipe.json> --out <empty-dir>
|
|
72
|
+
Write per-part inference PNGs on neutral gray, upscaled and padded as
|
|
73
|
+
declared, plus a source-hashed manifest. No model is bundled or called.
|
|
74
|
+
|
|
75
|
+
rig-parts depth-apply --parts <assembled-dir> --manifest <manifest.json> --config <depth-recipe.json> --rig-config <config.json> --out <empty-dir>
|
|
76
|
+
Register floating-point JSON depth or signed normals, normalize inside
|
|
77
|
+
alpha >=128, apply declared median/relief and group near/zScale/pivot,
|
|
78
|
+
extend past alpha, and write parts, relocated config and depth-report.json.
|
|
79
|
+
Normals require one closed convex island. See AUTHORING for the schemas.
|
|
80
|
+
|
|
70
81
|
rig-parts layers <dir | layers.json | file.psd>
|
|
71
82
|
Read a See-through decomposition — the ComfyUI wrapper form (a directory
|
|
72
83
|
holding layers.json and one PNG per layer) or an upstream .psd — and print
|
|
@@ -544,7 +555,8 @@ usage:
|
|
|
544
555
|
[--page-edges pot|free] [--pack-shape rect|polygon]
|
|
545
556
|
[--requirements <file.json>] [--seam-pairs] [--keep-stills]
|
|
546
557
|
[--set <name> | --all-sets] [--environment <schedule.json>]
|
|
547
|
-
[--wind-band <spec.json>]
|
|
558
|
+
[--wind-band <spec.json>] [--json]
|
|
559
|
+
--json prints the completed check.json object only, with unchanged exit codes.
|
|
548
560
|
Build, gate, render and measure a rig through rig-c's CLI (the rigc at
|
|
549
561
|
node_modules/.bin/rigc, or on PATH). --rig holds rig.json and motion.json:
|
|
550
562
|
a rig spec, which rigc build compiles, not a compiled skeleton.json.
|
|
@@ -766,7 +778,7 @@ usage:
|
|
|
766
778
|
extends below a crop (ASSEMBLE_SINGLE_RUN_PLAN), and the config sets no
|
|
767
779
|
seethrough.head_box (ASSEMBLE_SINGLE_RUN_HEAD_BOX).
|
|
768
780
|
Writes nothing unless every check passed.
|
|
769
|
-
--seam
|
|
781
|
+
--seam overrides config.assemble.seam, otherwise ${DEFAULT_SEAM_RULE}. --project says where a layer takes
|
|
770
782
|
the painting's pixel: core (the reference's) erodes every layer's top-most
|
|
771
783
|
opaque area by 5x5 first, so a part a few pixels wide takes none; visible
|
|
772
784
|
erodes only along a rim where a later layer is in front. It defaults to
|
|
@@ -850,7 +862,7 @@ usage:
|
|
|
850
862
|
green build ends with the pack line beside the spineboy yardstick and the
|
|
851
863
|
three artifact paths — skeleton .json, .atlas and the packed page: the
|
|
852
864
|
packed atlas is the result, the loose parts are the intermediate it was
|
|
853
|
-
made from. --seam
|
|
865
|
+
made from. --seam overrides config.assemble.seam, otherwise ${DEFAULT_SEAM_RULE}; --project to
|
|
854
866
|
${DEFAULT_PROJECT_RULE} (both as for assemble), --page-edges to ${DEFAULT_PAGE_EDGES} and
|
|
855
867
|
--pack-shape to ${DEFAULT_PACK_SHAPE} (as for check; both packed builds, rig's gate
|
|
856
868
|
and check's artifact, take them). --requirements is forwarded to check
|
|
@@ -1077,12 +1089,14 @@ function printLayerTable(set: LayerSet): void {
|
|
|
1077
1089
|
* no row; `layers` takes one positional path beside its flags (issue #359).
|
|
1078
1090
|
*/
|
|
1079
1091
|
const TAKES = {
|
|
1092
|
+
'depth-prepare': { valued: ['--parts', '--config', '--out'], switches: [] },
|
|
1093
|
+
'depth-apply': { valued: ['--parts', '--manifest', '--config', '--rig-config', '--out'], switches: [] },
|
|
1080
1094
|
layers: { valued: ['--export', '--template', '--canvas', '--full', '--source', '--config'], switches: [] },
|
|
1081
1095
|
sheet: { valued: ['--source', '--layers', '--out', '--cell', '--cols'], switches: [] },
|
|
1082
1096
|
rig: { valued: ['--config', '--parts', '--out', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
|
|
1083
1097
|
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'] },
|
|
1084
1098
|
compare: { valued: ['--left', '--right', '--map'], switches: [] },
|
|
1085
|
-
check: { valued: ['--rig', '--out', '--parts', '--source', '--page-edges', '--pack-shape', '--requirements', '--set', '--environment', '--wind-band'], switches: ['--seam-pairs', '--keep-stills', '--all-sets', '--no-extremes'] },
|
|
1099
|
+
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'] },
|
|
1086
1100
|
loop: { valued: ['--frames', '--out'], switches: ['--palette'] },
|
|
1087
1101
|
assemble: { valued: ['--source', '--full', '--head', '--config', '--out', '--seam', '--project', '--alt', '--write-config', '--requirements'], switches: ['--propose-plan'] },
|
|
1088
1102
|
'comfy seethrough': { valued: ['--image', '--out', '--resolution', '--steps', '--seed', '--prefix', '--host', '--wait', '--timeout', '--poll', '--config', '--depth-run'], switches: ['--offload', '--no-offload', '--lama', '--nf4'] },
|
|
@@ -1325,6 +1339,8 @@ function flags(args: string[], command: Command, required: readonly string[]): M
|
|
|
1325
1339
|
|
|
1326
1340
|
function cmdCheck(args: string[]): number {
|
|
1327
1341
|
// The switches check takes (issues #202, #209, #454): they carry no value, so they are taken out before the valued flags are read.
|
|
1342
|
+
const json = args.filter((a) => a === '--json').length;
|
|
1343
|
+
if (json > 1) return usage('--json is given twice');
|
|
1328
1344
|
const seamPairs = args.filter((a) => a === '--seam-pairs').length;
|
|
1329
1345
|
if (seamPairs > 1) return usage('--seam-pairs is given twice');
|
|
1330
1346
|
const keepStills = args.filter((a) => a === '--keep-stills').length;
|
|
@@ -1334,7 +1350,7 @@ function cmdCheck(args: string[]): number {
|
|
|
1334
1350
|
const allSets = args.filter((a) => a === '--all-sets').length;
|
|
1335
1351
|
if (allSets > 1) return usage('--all-sets is given twice');
|
|
1336
1352
|
const f = flags(
|
|
1337
|
-
args.filter((a) => a !== '--seam-pairs' && a !== '--keep-stills' && a !== '--all-sets' && a !== '--no-extremes'),
|
|
1353
|
+
args.filter((a) => a !== '--seam-pairs' && a !== '--keep-stills' && a !== '--all-sets' && a !== '--no-extremes' && a !== '--json'),
|
|
1338
1354
|
'check',
|
|
1339
1355
|
['--rig', '--out'],
|
|
1340
1356
|
);
|
|
@@ -1352,8 +1368,9 @@ function cmdCheck(args: string[]): number {
|
|
|
1352
1368
|
{ rig, parts: f.get('--parts'), source: f.get('--source'), out, pageEdges: edges, packShape: shape, requirements: f.get('--requirements'), seamPairs: seamPairs === 1, keepStills: keepStills === 1, noExtremes: noExtremes === 1, ...(f.has('--set') ? { set: f.get('--set') as string } : {}), allSets: allSets === 1, ...(f.has('--environment') ? { environment: f.get('--environment') as string } : {}), ...(f.has('--wind-band') ? { windBand: f.get('--wind-band') as string } : {}) },
|
|
1353
1369
|
rigcRunner(bin),
|
|
1354
1370
|
bin,
|
|
1355
|
-
console.log,
|
|
1371
|
+
json === 1 ? () => {} : console.log,
|
|
1356
1372
|
);
|
|
1373
|
+
if (json === 1) process.stdout.write(`${JSON.stringify(r.figures, null, 1)}\n`);
|
|
1357
1374
|
return r.figures.PASS ? EXIT_OK : EXIT_REFUSED;
|
|
1358
1375
|
} catch (err) {
|
|
1359
1376
|
return printRefusal(err);
|
|
@@ -1713,8 +1730,8 @@ function cmdAssemble(args: string[]): number {
|
|
|
1713
1730
|
if (propose && (flags.has('--out') || flags.has('--seam') || flags.has('--project') || alts.length > 0)) return usage('--propose-plan prints to the console; it takes none of --out, --seam, --project, --alt');
|
|
1714
1731
|
if (!propose && flags.has('--write-config')) return usage('--write-config belongs to assemble --propose-plan');
|
|
1715
1732
|
if (!propose && !flags.has('--out')) return usage('assemble needs --out <dir>');
|
|
1716
|
-
const seam = flags.get('--seam')
|
|
1717
|
-
if (!(SEAM_RULES as readonly string[]).includes(seam)) return usage(`--seam ${seam}; one of ${SEAM_RULES.join(', ')} is required`);
|
|
1733
|
+
const seam = flags.get('--seam');
|
|
1734
|
+
if (seam !== undefined && !(SEAM_RULES as readonly string[]).includes(seam)) return usage(`--seam ${seam}; one of ${SEAM_RULES.join(', ')} is required`);
|
|
1718
1735
|
const project = flags.get('--project') ?? DEFAULT_PROJECT_RULE;
|
|
1719
1736
|
if (!(PROJECT_RULES as readonly string[]).includes(project)) return usage(`--project ${project}; one of ${PROJECT_RULES.join(', ')} is required`);
|
|
1720
1737
|
const [source, full, config] = ['--source', '--full', '--config'].map((f) => flags.get(f) as string);
|
|
@@ -1742,7 +1759,7 @@ function cmdAssemble(args: string[]): number {
|
|
|
1742
1759
|
}
|
|
1743
1760
|
const out = flags.get('--out') as string;
|
|
1744
1761
|
assembleStage(
|
|
1745
|
-
{ source, full, head, config, ...(flags.has('--requirements') ? { requirements: flags.get('--requirements') as string } : {}), seam: seam as SeamRule, project: project as ProjectRule, ...(alts.length === 0 ? {} : { alts }) },
|
|
1762
|
+
{ source, full, head, config, ...(flags.has('--requirements') ? { requirements: flags.get('--requirements') as string } : {}), ...(seam === undefined ? {} : { seam: seam as SeamRule }), project: project as ProjectRule, ...(alts.length === 0 ? {} : { alts }) },
|
|
1746
1763
|
{ partsJson: join(out, 'rig', 'parts.json'), partsDir: join(out, 'rig', 'parts'), recomposite: join(out, 'render', 'recomposite_rig.png'), errorMap: join(out, 'render', ERROR_MAP_FILE) },
|
|
1747
1764
|
console.log,
|
|
1748
1765
|
);
|
|
@@ -1933,8 +1950,8 @@ function cmdBuild(args: string[]): number {
|
|
|
1933
1950
|
// --head is optional (issue #359), as for assemble.
|
|
1934
1951
|
for (const f of ['--config', '--source', '--full', '--out']) if (!flags.has(f)) return usage(`build needs ${f}`);
|
|
1935
1952
|
if (on.has('--all-sets') && flags.has('--set')) return usage('--set and --all-sets are two modes; give one');
|
|
1936
|
-
const seam = flags.get('--seam')
|
|
1937
|
-
if (!(SEAM_RULES as readonly string[]).includes(seam)) return usage(`--seam ${seam}; one of ${SEAM_RULES.join(', ')} is required`);
|
|
1953
|
+
const seam = flags.get('--seam');
|
|
1954
|
+
if (seam !== undefined && !(SEAM_RULES as readonly string[]).includes(seam)) return usage(`--seam ${seam}; one of ${SEAM_RULES.join(', ')} is required`);
|
|
1938
1955
|
const project = flags.get('--project') ?? DEFAULT_PROJECT_RULE;
|
|
1939
1956
|
if (!(PROJECT_RULES as readonly string[]).includes(project)) return usage(`--project ${project}; one of ${PROJECT_RULES.join(', ')} is required`);
|
|
1940
1957
|
const edges = pageEdgesOf(flags.get('--page-edges'));
|
|
@@ -1958,7 +1975,7 @@ function cmdBuild(args: string[]): number {
|
|
|
1958
1975
|
const scratch = mkdtempSync(join(tmpdir(), 'rig-parts-build-'));
|
|
1959
1976
|
try {
|
|
1960
1977
|
const r = build(
|
|
1961
|
-
{ config, source, full, head, out, seam: seam as SeamRule, project: project as ProjectRule, loop, pageEdges: edges, packShape: shape, ...(flags.has('--requirements') ? { requirements: flags.get('--requirements') as string } : {}), idleKeys: keys, ...(leadText === undefined ? {} : { leadIn: Number(leadText) }), seamPairs: on.has('--seam-pairs'), keepStills: on.has('--keep-stills'), noExtremes: on.has('--no-extremes'), ...(alts.length === 0 ? {} : { alts }), ...(flags.has('--set') ? { set: flags.get('--set') as string } : {}), allSets: on.has('--all-sets'), ...(flags.has('--environment') ? { environment: flags.get('--environment') as string } : {}), ...(flags.has('--wind-band') ? { windBand: flags.get('--wind-band') as string } : {}) },
|
|
1978
|
+
{ config, source, full, head, out, ...(seam === undefined ? {} : { seam: seam as SeamRule }), project: project as ProjectRule, loop, pageEdges: edges, packShape: shape, ...(flags.has('--requirements') ? { requirements: flags.get('--requirements') as string } : {}), idleKeys: keys, ...(leadText === undefined ? {} : { leadIn: Number(leadText) }), seamPairs: on.has('--seam-pairs'), keepStills: on.has('--keep-stills'), noExtremes: on.has('--no-extremes'), ...(alts.length === 0 ? {} : { alts }), ...(flags.has('--set') ? { set: flags.get('--set') as string } : {}), allSets: on.has('--all-sets'), ...(flags.has('--environment') ? { environment: flags.get('--environment') as string } : {}), ...(flags.has('--wind-band') ? { windBand: flags.get('--wind-band') as string } : {}) },
|
|
1962
1979
|
{ rig: rigcRunner(bin), check: rigcRunner(bin), checkBin: bin, scratch: join(scratch, 'rig-gate'), stages: {
|
|
1963
1980
|
assemble: (input, outputs) => runBuildProcess({ stage: 'assemble', bin, input, outputs }, join(scratch, 'workers')),
|
|
1964
1981
|
rig: (input, _rigc, gateScratch) => runBuildProcess({ stage: 'rig', bin, input, scratch: gateScratch }, join(scratch, 'workers')),
|
|
@@ -2181,6 +2198,27 @@ function cmdPainting(args: string[]): number {
|
|
|
2181
2198
|
}
|
|
2182
2199
|
}
|
|
2183
2200
|
|
|
2201
|
+
function cmdDepthPrepare(args: string[]): number {
|
|
2202
|
+
const f = flags(args, 'depth-prepare', ['--parts', '--config', '--out']);
|
|
2203
|
+
if (typeof f === 'string') return usage(f);
|
|
2204
|
+
try {
|
|
2205
|
+
const result = prepareDepth({ parts: f.get('--parts')!, config: f.get('--config')!, out: f.get('--out')! });
|
|
2206
|
+
console.log(`depth-prepare: ${result.parts.length} part(s); manifest.json binds crop, padding and source/input hashes`);
|
|
2207
|
+
return EXIT_OK;
|
|
2208
|
+
} catch (error) { return printRefusal(error); }
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
function cmdDepthApply(args: string[]): number {
|
|
2212
|
+
const f = flags(args, 'depth-apply', ['--parts', '--manifest', '--config', '--rig-config', '--out']);
|
|
2213
|
+
if (typeof f === 'string') return usage(f);
|
|
2214
|
+
try {
|
|
2215
|
+
const result = applyDepth({ parts: f.get('--parts')!, manifest: f.get('--manifest')!, config: f.get('--config')!, rigConfig: f.get('--rig-config')!, out: f.get('--out')! });
|
|
2216
|
+
for (const row of result.parts as Array<Record<string, unknown>>) console.log(`depth-apply: ${row.part} levels=${row.levels} clip=${row.clipFraction} reliefRigPixels=${row.reliefRigPixels} sign=${row.sign}`);
|
|
2217
|
+
console.log('depth-apply: wrote registered parts, config.json and depth-report.json; run rig for the core gate');
|
|
2218
|
+
return EXIT_OK;
|
|
2219
|
+
} catch (error) { return printRefusal(error); }
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2184
2222
|
function main(argv: string[]): number | Promise<number> {
|
|
2185
2223
|
const [command, ...rest] = argv;
|
|
2186
2224
|
if (command === undefined || command === '--help' || command === '-h' || command === 'help') {
|
|
@@ -2192,6 +2230,8 @@ function main(argv: string[]): number | Promise<number> {
|
|
|
2192
2230
|
return EXIT_OK;
|
|
2193
2231
|
}
|
|
2194
2232
|
if (command === 'layers') return cmdLayers(rest);
|
|
2233
|
+
if (command === 'depth-prepare') return cmdDepthPrepare(rest);
|
|
2234
|
+
if (command === 'depth-apply') return cmdDepthApply(rest);
|
|
2195
2235
|
if (command === 'sheet') return cmdSheet(rest);
|
|
2196
2236
|
if (command === 'rig') return cmdRig(rest);
|
|
2197
2237
|
if (command === 'propose') return cmdPropose(rest);
|
package/docs/AUTHORING.md
CHANGED
|
@@ -147,6 +147,7 @@ read by no CPU stage).
|
|
|
147
147
|
| `seethrough.steps`, `.seed`, `.offload` | `comfy seethrough --config` | run inputs when `--config <config.json>` is given; explicit CLI flags override them, including `--no-offload` for false |
|
|
148
148
|
| `seethrough.head_box` | inputs (cuts the head image there; optional at the first call); assemble — where the head run sits on the painting | **proposed** by `propose --head-box` from the full run, square, source pixels, shifted inside the painting when needed; the head run must then be fed exactly that crop. With one run (no `--head`, issue #359: an artist's own file, §4 *For artists*) it is left out — there is no crop to place — and a config that sets it is refused (`ASSEMBLE_SINGLE_RUN_HEAD_BOX`) |
|
|
149
149
|
| `seethrough.arm_split` | assemble, `assemble --propose-plan` — separates a single full-run `handwear-r` or `handwear-l` layer that holds both overlapping arms into both arm tags | **authored**, optional (issue #439): `{x?: number, line?: [[x, y], …], order: ["handwear-r", "handwear-l"] \| ["handwear-l", "handwear-r"]}` in `seethrough.resolution` full-run pixels. Exactly one of `x` (image column, `0 <= x <= resolution`) or `line` (a polyline of two or more `[x, y]` points in `[0, resolution]`, interpolated in x along y and clamped above/below its endpoints) is required (`CONFIG_FIELD_PRESENT`, `CONFIG_FIELD_TYPE`), and `order` is `[back_tag, front_tag]`, the draw order of the two separated arm layers (`CONFIG_FIELD_PRESENT`, `CONFIG_FIELD_TYPE`). Pixels at `< splitX(y)` go to `handwear-r` (image left, the subject's right arm) and pixels at `>= splitX(y)` go to `handwear-l` (image right, the subject's left arm). A split line that cuts a horizontal opaque run of the arm layer into more than two segments is refused (`ASSEMBLE_ARM_SPLIT_CUTS_ARM`). Without the declaration, when one arm layer's 8-connected component reaches both wrists under `propose --keypoints`, `propose` emits an `overlapping-arms` `LINT` naming the part, both wrists and `seethrough.arm_split`; absent, no byte of any output moves |
|
|
150
|
+
| `assemble.seam` | assemble, build | **authored**, optional `"near-white"` or `"silhouette"` (issue #905). An explicit `--seam` overrides the config; otherwise this field overrides the existing `near-white` default. An invalid value is `CONFIG_FIELD_TYPE` at `config.assemble.seam`, including when a CLI override is given. Absent, existing build bytes are unchanged. |
|
|
150
151
|
| `assemble.rig_scale` | assemble — the rig is `trunc(w × rig_scale)` x `trunc(h × rig_scale)`, and every rig-px value after it (`bones`, `meshes`, `motion`, `patches`, `cuts`) is in that rig's pixels; `check --source` (`CHECK_SOURCE_SIZE`) and `compose` (`SCENE_RIG_SCALE_AGREES`) read it back off `parts.json` | authored; rig pixels per source pixel. 0.5 in both examples (a 1664x2432 painting makes an 832x1216 rig). **Keep the figure about 1,100 to 1,200 rig px tall** — the `H` of `propose`'s `mesh spacing:` note, the height of the parts' boxes together — so for another painting, about 1,150 over the figure's height in painting px: that is the size the proposer's spacing divisor was calibrated at (H 1106–1185, the `mesh spacing` note says so) and the size of the two examples `check`'s bars are derived from, and the proposer and `check` also hold fixed rig-px values that do not scale with it (the fringe's 10 px step, the trunk segments' 18, 20, 30 and 100 px, the 15 px wrist tolerance, the 10-row pendant floor, the seam bar's 50 rig px over 40). What else it trades, measured on `demo` with the README loop from a fresh config at 0.25, 0.5 and 1 (issue #345, rig-c 2.47.1): the same plan and the same ten chains; every bar but the one the uncorrected `demo` always fails (`bottomwear` `TIP_OVER_ROOT`, the README loop's `RL01`) passing at all three; the atlas page 418x606, 832x1205 and 1825x2047; the figure's mesh spacing 16, 32 and 64, so 830, 868 and 932 triangles; the breath 2.5 % of a torso that scales with it; and the proposed bones, scaled to 0.5, off the 0.5 proposal's by up to 38 px at 0.25 and 22.5 px at 1 (the hairpin's origin; the skirt roots 20 and 10.5) — the fixed-px values above. `propose` records it on the proposal as `proposed_rig_scale` (issue #354), and changing `assemble.rig_scale` after the rig sections are written is refused (`CONFIG_PROPOSED_RIG_SCALE`): without that refusal `demo`'s tracked config built at 0.25 gated green and `check` failed only `TIP_OVER_ROOT` on both sleeves (`propose --from-config` named 54 `LINT` lines, links off the art) — re-run `assemble` and `propose` at the new scale. Absent: `CONFIG_FIELD_PRESENT`; 0, negative or not a number: `CONFIG_FIELD_TYPE`; a rig under 1x1: `ASSEMBLE_RIG_SIZE` |
|
|
151
152
|
| `assemble.plan` | assemble; also the list every `meshes`/`regions` key must come from | **proposed** by `assemble --propose-plan`. `[part name, "full" \| "head", See-through tag]`, in draw order back to front. Names are free; roles come from the tag. A layer the author made and added to a run takes `author:<label>` in the tag's place, and has no role (§5, *A layer the author made*). When `generation.control.skeleton` is present and a full-run `handwear-r` or `handwear-l` layer merges sleeve, bare hand and overgarment drape into one layer — a skin-coloured hand cluster (`>= 40` run px, `GHOST_MIN_AREA`, measured `516..4,787` run px across the 9-standee corpus) lies within `0.35` forearm lengths of `<s>_wrist` and non-skin pixels reach more than `1.0x` the forearm length (`<s>_elbow -> <s>_wrist`) below `<s>_wrist` (issue #490; measured `-0.15x..0.45x` on the 6 clean standees vs `1.25x..2.02x` on the 3 merged standees, placing `1.0x` inside the `0.45x..1.25x` gap) — the layer is split into `drape-<s>`, `handwear-<s>` and `hand-<s>` (back to front) when the Euclidean RGB distance between the sleeve median colour above `<s>_wrist` and the drape median colour below `<s>_wrist` is at least `80` (`ARM_DRAPE_COLOR_DISTANCE`, measured `103.6..236.0` on the two distinct-colour merged standees vs `20.8..28.2` on the same-colour merged standee, placing `80` between `28.2` and `103.6`), or refused with `SEETHROUGH_ARM_RUN_MERGED` when the distance is under `80` |
|
|
152
153
|
| `assemble.extend_below_crop` | assemble | **proposed** with the plan: `{part, run, tag}` — a head-run part that reaches the bottom of the head crop is continued from a full-run layer, whole connected components |
|
|
@@ -5233,6 +5234,7 @@ and stay absent).
|
|
|
5233
5234
|
| <a id="RIG_TURN_SURFACE_MESH_BINDINGS"></a>`RIG_TURN_SURFACE_MESH_BINDINGS` | an authored surface mesh has unresolved/nonclosing weights or shares a frame moved by a unit/rear turn carrier; declared stop draw aliases resolve to their links for setup sampling | provide weighted bindings closing at 1 in the authored frame; `<link>_d` resolves only for links declared in `config.stops`; use independent chains for parts with different roles |
|
|
5234
5235
|
| <a id="RIG_TURN_SURFACE_MESH_ATTACHMENT"></a>`RIG_TURN_SURFACE_MESH_ATTACHMENT` | a surface mesh has an alternate drawing without its own registered depth sampling frame | use the base attachment; explicit islands retain their own slot, attachment and bindings |
|
|
5235
5236
|
| <a id="RIG_TURN_BODY_UNCOVERED"></a>`RIG_TURN_BODY_UNCOVERED` | **Warning:** a shown part without a body role overlaps a moved `turn.body` surface at setup alpha >= 128 and has different emitted endpoint displacement | declare the intended body role and inspect both endpoints; no role or depth is inferred |
|
|
5237
|
+
| <a id="RIG_TURN_HEAD_ROLL"></a>`RIG_TURN_HEAD_ROLL` | **Warning:** a declared head turn has an absolute authored-eye-origin roll above the empirical limit displayed as 18.485385 degrees; names the face/eye parts, both eye bones, signed roll and limit | inspect the neck at both extremes; this proxy does not detect the painting independently, correct tilt or change emitted bytes; see [Head roll before a declared turn](#head-roll-before-a-declared-turn) |
|
|
5236
5238
|
| <a id="RIG_TURN_SURFACE_DEPTH_REQUIRED"></a>`RIG_TURN_SURFACE_DEPTH_REQUIRED` | a part with role `"surface"` in `config.turn.roles` or `config.turn.body.roles` carries no depth sheet in `parts.json` | run `assemble` with `config.depth` or provide `<part>_depth.png` in `parts.json` |
|
|
5237
5239
|
| <a id="RIG_TURN_GRID_BUDGET"></a>`RIG_TURN_GRID_BUDGET` | a `"surface"` grid exceeds 2,400 triangles on one slot or 4,000 triangles across all `"surface"` slots | lower `config.turn.grid` / `config.turn.body.grid` `[cols, rows]` within the per-slot and total budgets |
|
|
5238
5240
|
| <a id="RIG_TURN_HEAD_BONE"></a>`RIG_TURN_HEAD_BONE` | `buildRig` found no `"head"` (or body turn root) bone when emitting 2.5D turn carriers | declare `"head"` (or the body turn root bone) in `config.bones` |
|
|
@@ -5663,7 +5665,7 @@ Under `--source <painting.png>`, a second reported line, also never a FAIL
|
|
|
5663
5665
|
| line | reports | bar | `reference-demo` | `reference-sample` | SKIP when |
|
|
5664
5666
|
| --- | --- | --- | --- | --- | --- |
|
|
5665
5667
|
| <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` |
|
|
5666
|
-
| <a id="SETUP_MATCHES_PAINTING"></a>`SETUP_MATCHES_PAINTING` | declared region count, native box, limit and actual top contributor rows | PASS/FAIL against authored limits; no limit is SKIP; undeclared regions are not judged |
|
|
5668
|
+
| <a id="SETUP_MATCHES_PAINTING"></a>`SETUP_MATCHES_PAINTING` | declared region count, native box, limit and actual top contributor rows | PASS/FAIL against authored limits; no limit is SKIP; with `--source`, no declaration is SKIP with its reason and declaration remedy; undeclared regions are not judged |
|
|
5667
5669
|
|
|
5668
5670
|
The figures are assemble's by construction, not a second definition beside them;
|
|
5669
5671
|
what differs is what they are taken of — the pose rigc draws, not the flat stack —
|
|
@@ -6229,7 +6231,22 @@ maximum absolute RGB-channel difference is **strictly greater** than that
|
|
|
6229
6231
|
value. The region passes when its total count is at most its authored
|
|
6230
6232
|
nonnegative integer `max_pixels`. Without that limit the region reports
|
|
6231
6233
|
**SKIP**, including a zero-count region. An undeclared region has no verdict.
|
|
6232
|
-
|
|
6234
|
+
With `--source` but no `setup_painting` declaration, `SETUP_MATCHES_PAINTING`
|
|
6235
|
+
is explicitly **SKIP** in the console, `check.json`, the report's skipped-bar
|
|
6236
|
+
list and `check --json`. Its reason says the comparison was not measured and
|
|
6237
|
+
asks for `--requirements <file>` declaring `setup_painting.rgb_tolerance` and
|
|
6238
|
+
named `regions` with `box` and authored `max_pixels` limits. The SKIP changes
|
|
6239
|
+
no exit code and invents no metric, region or limit. This applies both without
|
|
6240
|
+
a requirements file and with one that omits the key. Without `--source` and
|
|
6241
|
+
without the declaration, the line stays absent. Declared observations retain
|
|
6242
|
+
their output bytes and renderer calls.
|
|
6243
|
+
|
|
6244
|
+
`check --json` prints only the completed `check.json` object to stdout, with
|
|
6245
|
+
the same bytes as the file and the same exit code as ordinary `check` (0 or 1).
|
|
6246
|
+
Early input refusals and usage errors retain their named text and exits (1 or 2);
|
|
6247
|
+
they have no completed check object. `build --json` is refused by name: run
|
|
6248
|
+
`check --rig <out>/rig --parts <out> --out <dir> --json` after the build to read
|
|
6249
|
+
the completed check object.
|
|
6233
6250
|
|
|
6234
6251
|
The observation is the actual renderer's setup pose, without animation or
|
|
6235
6252
|
physics advancement, from the already gated setup candidate. Its viewport is
|
|
@@ -6566,6 +6583,47 @@ art. An authored `parts.json` depth reference remains an active standalone
|
|
|
6566
6583
|
guards still apply. With binding inactive there is no added reference, file or
|
|
6567
6584
|
legacy-byte change. #773 adds no image generation, geometry or motion model.
|
|
6568
6585
|
|
|
6586
|
+
### Head roll before a declared turn
|
|
6587
|
+
|
|
6588
|
+
`rig` and `build` report `WARN RIG_TURN_HEAD_ROLL` when a declared head
|
|
6589
|
+
turn (`turn.depths` or `turn.roles`, including the depth-sampled producer)
|
|
6590
|
+
has an absolute setup eye-line roll above 18.485385 degrees (six-place display).
|
|
6591
|
+
The line names the face part (or the eye parts without a face), the signed
|
|
6592
|
+
roll, its absolute value, the limit, and both eye bones and parts. It reads
|
|
6593
|
+
the existing `eyeLineDegrees` on the authored `eye_r` and `eye_l` origins,
|
|
6594
|
+
image-left to image-right in Spine degrees, counter-clockwise; it does not
|
|
6595
|
+
detect the painting's tilt independently of those authored origins, estimate
|
|
6596
|
+
tilt from a bounding box, or invent a reading without both eye parts
|
|
6597
|
+
and bones or when their origins share a column.
|
|
6598
|
+
|
|
6599
|
+
The limit is the midpoint between the maximum absolute roll of the measured
|
|
6600
|
+
public controls, 4.864514 degrees, and the tilted `three-quarter`
|
|
6601
|
+
input from issue #900, 32.106256 degrees. The comparison uses the unrounded
|
|
6602
|
+
midpoint. The controls are the five
|
|
6603
|
+
parts examples plus the published dancer config and head-turn demo's compiled
|
|
6604
|
+
setup eye origins. The named quiet controls read 3.239700 degrees
|
|
6605
|
+
(`reference-demo`), -1.941486 degrees (`dancer`), and
|
|
6606
|
+
0.520856 degrees (head-turn demo). This is an empirical separation
|
|
6607
|
+
of these inputs, not an anatomical limit or a visual-quality verdict.
|
|
6608
|
+
|
|
6609
|
+
The parts configs were read at
|
|
6610
|
+
[`38e5f574`](https://github.com/rig-c/parts/tree/38e5f574a88b2e6753a982b0ee0952dbf273a12e/examples),
|
|
6611
|
+
the [dancer config](https://github.com/rig-c/examples/blob/564285414fccf7b47df32501c0b3c2968db77691/dancer/config.json)
|
|
6612
|
+
at `56428541`, and the
|
|
6613
|
+
[head-turn model](https://github.com/rig-c/rig-c.github.io/blob/989cebbd6733a58142923cec96fe2333da08194a/demos/head-turn/bundle/skeleton.model.json)
|
|
6614
|
+
at `989cebbd`. The [issue #900](https://github.com/rig-c/parts/issues/900)
|
|
6615
|
+
bootstrap used public `three-quarter` inputs at examples commit
|
|
6616
|
+
`ba99e403d4058fc1cfe40699bdcde8aaf00a90c0`; its authored crop-pixel eye origins
|
|
6617
|
+
are `[236, 136]` and `[287, 104]`. The compiled demo is a setup metric control,
|
|
6618
|
+
not a rebuild of its unavailable parts config.
|
|
6619
|
+
|
|
6620
|
+
An already rolled head is turned about upright yaw/pitch axes; inspect its
|
|
6621
|
+
neck at both extremes before accepting that motion. This warning does not
|
|
6622
|
+
refuse the build, correct the tilt, add a roll axis, or change any pose or
|
|
6623
|
+
emitted file. With no head turn, including a body-only turn, it adds no warning.
|
|
6624
|
+
With an unreadable eye line the existing face-axis report retains its reason;
|
|
6625
|
+
silence is not a measured zero. The warning lives only in the stage report.
|
|
6626
|
+
|
|
6569
6627
|
### Missing body-turn coverage
|
|
6570
6628
|
|
|
6571
6629
|
`RIG_TURN_BODY_UNCOVERED` warns when a shown part without a
|
|
@@ -6586,6 +6644,151 @@ and never assign a role, depth, binding or draw order. A part already bound to
|
|
|
6586
6644
|
its actual intended role, then inspect setup, both turn endpoints and their diff;
|
|
6587
6645
|
an explicit role alone does not establish that two drawings remain coincident.
|
|
6588
6646
|
|
|
6647
|
+
### Per-part provider maps (#897)
|
|
6648
|
+
|
|
6649
|
+
`depth-prepare` and `depth-apply` are explicit stages after `assemble`, before
|
|
6650
|
+
`rig`. They carry no model, weights, provider service or downloads. Prepare
|
|
6651
|
+
writes one inference PNG per selected assembled part, composited onto gray 128,
|
|
6652
|
+
with integer LANCZOS upscaling and padding. A `rig-parts-depth-inputs/1` manifest
|
|
6653
|
+
binds the original parts.json hash, part PNG hash, rig crop `[x,y,w,h]`, native
|
|
6654
|
+
and upscaled sizes, padding, recipe hash and inference PNG hash. Map registration
|
|
6655
|
+
is declared, never inferred from a filename or guessed from art.
|
|
6656
|
+
|
|
6657
|
+
```sh
|
|
6658
|
+
rig-parts depth-prepare --parts assembled --config depth.json --out inference
|
|
6659
|
+
# A provider reads inference/*.png and writes the declared floating-point maps.
|
|
6660
|
+
rig-parts depth-apply --parts assembled --manifest inference/manifest.json --config depth.json --rig-config config.json --out mapped
|
|
6661
|
+
rig-parts rig --parts mapped --config mapped/config.json --out compiled
|
|
6662
|
+
```
|
|
6663
|
+
|
|
6664
|
+
The separate recipe is not a new field on character config:
|
|
6665
|
+
|
|
6666
|
+
```json
|
|
6667
|
+
{
|
|
6668
|
+
"spec": "rig-parts-depth/1",
|
|
6669
|
+
"prepare": {"longSide": 1024, "paddingFraction": 0.08, "padding": 4},
|
|
6670
|
+
"groups": {"head": {"near": "white", "zScale": 30, "pivot": 15}},
|
|
6671
|
+
"parts": {
|
|
6672
|
+
"face": {"map": "face.json", "source": "depth", "frame": "part", "group": "head", "median": 21, "relief": 13}
|
|
6673
|
+
},
|
|
6674
|
+
"clear": []
|
|
6675
|
+
}
|
|
6676
|
+
```
|
|
6677
|
+
|
|
6678
|
+
All displayed numbers are authored example choices, not defaults. `longSide`
|
|
6679
|
+
and additive `padding` are integers; upscale is `max(1,ceil(longSide/max(w,h)))`
|
|
6680
|
+
and pad is `floor(max(upscaledSize)*paddingFraction)+padding`. Each part declares
|
|
6681
|
+
`median` and nonnegative `relief` in rig pixels. Each group (`head` or `body`)
|
|
6682
|
+
declares `near`, positive `zScale` and `pivot`; optional `unitDepths` explicitly
|
|
6683
|
+
replaces that group's rigid depths. `clear` explicitly lists part references to
|
|
6684
|
+
remove, including any small units intended to sample the surface. Unselected
|
|
6685
|
+
parts retain their artwork and depth references. The output config copies the
|
|
6686
|
+
original, sets the declared group pivots/unit depths, and relocates animation,
|
|
6687
|
+
requirements and control-skeleton file paths. Built-in skeleton names remain
|
|
6688
|
+
names. A selected part whose provenance records `depth.kind: "authored-unit"`
|
|
6689
|
+
is refused before output (`DEPTH_AUTHORED_UNIT_CONFLICT`); apply does not invent
|
|
6690
|
+
a transition to a transported scalar reference. Unselected authored units keep
|
|
6691
|
+
their original representation. Roles and draw order stay the author's.
|
|
6692
|
+
|
|
6693
|
+
Maps are UTF-8 JSON `{"spec":"rig-parts-map/1","width":2,"height":2,
|
|
6694
|
+
"channels":1,"data":[0.125,0.25,0.5,0.875]}`: row-major finite numeric values,
|
|
6695
|
+
with channels interleaved for signed XYZ normals (`channels:3`). JSON is open,
|
|
6696
|
+
lossless for binary64 numbers, and requires no numeric-array dependency; an
|
|
6697
|
+
8-bit preview cannot preserve the raw map's normalization. Paths resolve beside
|
|
6698
|
+
the recipe. `frame:"part"` requires native part dimensions; `"upscaled"` requires
|
|
6699
|
+
the prepared art's enlarged dimensions without padding; `"input"` requires the
|
|
6700
|
+
whole prepared image and crops its declared padding before reduction. Scalar
|
|
6701
|
+
maps use float32 BOX reduction. Normal sources require signed image-axis XYZ
|
|
6702
|
+
(x right, y down), not an RGB normal preview.
|
|
6703
|
+
|
|
6704
|
+
Apply computes `u=(raw-median(raw))/(p95-p5)` inside original alpha >=128,
|
|
6705
|
+
using linear percentiles, then `Z=median+relief*u`. A flat map requires explicit
|
|
6706
|
+
relief 0. Encode `Z/zScale` for near white, `1-Z/zScale` for near black, clip to
|
|
6707
|
+
[0,1], and round to the nearest 8-bit level with even ties. Extend every
|
|
6708
|
+
non-support pixel with the Euclidean nearest support value, with row-major ties.
|
|
6709
|
+
The resulting opaque gray sheet carries no background cliff. The report names
|
|
6710
|
+
levels, clip fraction (Z <=0 or >=zScale on support), declared relief in rig
|
|
6711
|
+
pixels, group pivot, sign, raw median/p5/p95 and source/input/map/sheet hashes.
|
|
6712
|
+
It reports measurements, not a depth-quality pass mark. `rig` still runs Core's
|
|
6713
|
+
gate; apply writes parts and configuration, never a skeleton or a gate verdict.
|
|
6714
|
+
Outputs must be absent or empty; all declared inputs validate before emitting.
|
|
6715
|
+
|
|
6716
|
+
`source:"normals"` is optional and limited to one closed convex part. It refuses
|
|
6717
|
+
multiple four-connected alpha islands both before and after stride2 sampling.
|
|
6718
|
+
At half resolution it integrates adjacent gradients `-nx/max(abs(nz),0.15*norm)`
|
|
6719
|
+
and `-ny/max(abs(nz),0.15*norm)` with step2 trapezoids and matrix-free LSQR
|
|
6720
|
+
(maximum 3000 iterations, atol/btol 1e-6, condition limit 1e8). Cross erosion
|
|
6721
|
+
finds an interior core (max(3,floor(0.12*sqrt(area))) steps) and a three-step rim.
|
|
6722
|
+
The sign makes mean core depth greater than mean rim depth; no reference is
|
|
6723
|
+
read. An empty core or unresolved sign refuses. Before float32 BOX, unsupported
|
|
6724
|
+
integration-grid samples receive the Euclidean nearest supported float height,
|
|
6725
|
+
with row-major ties; supported integrated heights are unchanged. This fill uses
|
|
6726
|
+
the stride2 integration mask itself, not the native mask or encoded gray levels,
|
|
6727
|
+
so unsupported zeros cannot enter a supported native pixel through BOX. BOX
|
|
6728
|
+
then brings the field back to the native grid before the same normalization.
|
|
6729
|
+
Normalization still reads all native alpha>=128 support; a boundary repair can
|
|
6730
|
+
change its percentiles and therefore interior levels too. Iterations and
|
|
6731
|
+
residual are reported. One connected silhouette does not certify convexity or
|
|
6732
|
+
exclude internal occlusion edges; choose scalar depth for strands and collars.
|
|
6733
|
+
|
|
6734
|
+
The [provider trial](https://github.com/rig-c/parts/issues/897) measured Depth
|
|
6735
|
+
Anything V2 Large and Small, DA3MONO-LARGE, DepthPro, Lotus depth/normal G,
|
|
6736
|
+
Marigold depth, and bundled MiDaS/Zoe/LeReS/Metric3D routes. DA3 and Small rows
|
|
6737
|
+
were added after the initial report. The stage2 reproduction uses V2 Large
|
|
6738
|
+
with the Lotus-normal face, not DA3. No model is required by these commands.
|
|
6739
|
+
The trial's role relief/width ratios (face 0.1264803, front hair 0.1018772,
|
|
6740
|
+
back hair 0.0857722, ornament 0.0375868, garment 0.0990473) came from one
|
|
6741
|
+
head-turn demo's p5..p95 depth and part widths. They are measured starting
|
|
6742
|
+
points for declared proportions, not anatomical laws or default calibration.
|
|
6743
|
+
For a head-referenced calibration, declare
|
|
6744
|
+
`relief = referenceRoleRelief * targetFaceWidth / referenceFaceWidth`, using
|
|
6745
|
+
the assembled target face width and the same declared reference head measure
|
|
6746
|
+
for every role. This keeps head relief on one scale when a hair silhouette
|
|
6747
|
+
is much wider than the face. In contrast, `roleRatio * targetPartWidth`
|
|
6748
|
+
increases relief with that silhouette while the group's `zScale` stays fixed.
|
|
6749
|
+
An ornament narrower than its reference can gain relief under the head rule;
|
|
6750
|
+
inspect it as well as the widest part. The command derives neither rule:
|
|
6751
|
+
write the resulting rig-pixel `relief` explicitly in each recipe entry.
|
|
6752
|
+
|
|
6753
|
+
Compare calibrations on identical prepared inputs and numeric maps, with the
|
|
6754
|
+
same medians, group pivot and `zScale`; only the declared relief changes.
|
|
6755
|
+
Read each named report row's `relief`, `zScale` and `clipFraction` before
|
|
6756
|
+
judging its render. The clipping denominator is every original part pixel
|
|
6757
|
+
with alpha >=128, including boundary support, never the padded input area.
|
|
6758
|
+
A low clipped fraction does not establish shape quality. Inspect setup,
|
|
6759
|
+
both yaw endpoints and their difference at native scale, then enlarge the
|
|
6760
|
+
same pixels with nearest-neighbour sampling. Neither clipping nor agreement
|
|
6761
|
+
with a reference map substitutes for that inspection.
|
|
6762
|
+
|
|
6763
|
+
The owner's preference for per-part volume is subjective provenance, not an
|
|
6764
|
+
automated quality conclusion. Model licence posture remains the owner's choice.
|
|
6765
|
+
|
|
6766
|
+
`depth.full.layers` and `depth.head.layers` still accept authored source-grid
|
|
6767
|
+
sheets. They bind the See-through layer before assembly transports/scales/crops
|
|
6768
|
+
it; they do not bind an already assembled part's post-merge image. Use them
|
|
6769
|
+
when a sheet genuinely occupies that source grid. The commands above bind the
|
|
6770
|
+
final assembled grid and preserve the existing `parts.json` reference consumed
|
|
6771
|
+
by `rig`, so their output requires no alternate compiler or dependency pin.
|
|
6772
|
+
|
|
6773
|
+
| rule | refused input | remedy |
|
|
6774
|
+
| --- | --- | --- |
|
|
6775
|
+
| <a id="DEPTH_RECIPE"></a>`DEPTH_RECIPE` | unknown, absent or wrongly typed recipe field, group, source or frame | use the documented explicit recipe schema; no guessed field |
|
|
6776
|
+
| <a id="DEPTH_RECIPE_NUMBER"></a>`DEPTH_RECIPE_NUMBER` | missing/nonfinite median, relief, near-scale pivot or prepare number; invalid range/integer | declare every numeric field in its documented units and range |
|
|
6777
|
+
| <a id="DEPTH_INPUT_PRESENT"></a>`DEPTH_INPUT_PRESENT` | declared JSON input missing or not a file | restore the named file |
|
|
6778
|
+
| <a id="DEPTH_INPUT_JSON"></a>`DEPTH_INPUT_JSON` | malformed numeric or configuration JSON | write valid finite JSON |
|
|
6779
|
+
| <a id="DEPTH_PART_RESOLVES"></a>`DEPTH_PART_RESOLVES` | selected or cleared name absent from parts.json | use the assembled part name |
|
|
6780
|
+
| <a id="DEPTH_OUTPUT_EMPTY"></a>`DEPTH_OUTPUT_EMPTY` | nonempty output or output containing an input | choose a separate absent or empty directory |
|
|
6781
|
+
| <a id="DEPTH_INPUT_BINDING"></a>`DEPTH_INPUT_BINDING` | changed recipe, art, part record, crop, padding or inference PNG | prepare again for these exact inputs |
|
|
6782
|
+
| <a id="DEPTH_MAP_FORMAT"></a>`DEPTH_MAP_FORMAT` | wrong numeric schema, channels, shape, count, unknown field or nonfinite element | provide the declared float JSON map, not a preview |
|
|
6783
|
+
| <a id="DEPTH_MAP_SIZE"></a>`DEPTH_MAP_SIZE` | map dimensions differ from its explicit frame | use the registered part, enlarged art or padded input grid |
|
|
6784
|
+
| <a id="DEPTH_MAP_SUPPORT"></a>`DEPTH_MAP_SUPPORT` | no alpha>=128 support | provide a supported part |
|
|
6785
|
+
| <a id="DEPTH_MAP_SPREAD"></a>`DEPTH_MAP_SPREAD` | p5=p95 with nonzero relief | supply a nonflat map or declare relief 0 |
|
|
6786
|
+
| <a id="DEPTH_NORMAL_ISLANDS"></a>`DEPTH_NORMAL_ISLANDS` | zero/multiple connected components at native or integration resolution | scalar source, or a larger registered normal input that preserves one island |
|
|
6787
|
+
| <a id="DEPTH_NORMAL_VECTOR"></a>`DEPTH_NORMAL_VECTOR` | a sampled support normal is zero | provide nonzero signed XYZ normals |
|
|
6788
|
+
| <a id="DEPTH_NORMAL_CONVEXITY"></a>`DEPTH_NORMAL_CONVEXITY` | core/rim or its convexity sign is unresolved | larger closed convex mask or scalar source |
|
|
6789
|
+
| <a id="DEPTH_GROUP_BINDING"></a>`DEPTH_GROUP_BINDING` | part outside declared turn roles, mixed retained near/zScale, or filename collision | register exact roles and one shared scale/polarity, replace/clear conflicting sheets, use distinct names |
|
|
6790
|
+
| <a id="DEPTH_AUTHORED_UNIT_CONFLICT"></a>`DEPTH_AUTHORED_UNIT_CONFLICT` | a selected part already records `provenance.depth.kind: "authored-unit"`; adding scalar depth would make its two representations conflict | retain the authored unit or supply an explicitly reassembled scalar-compatible input; apply never replaces the provenance automatically |
|
|
6791
|
+
|
|
6589
6792
|
### Unit depth precedence
|
|
6590
6793
|
|
|
6591
6794
|
For `"unit"` roles, a transported part's sheet is recorded and validated but is not
|
package/docs/STABILITY.md
CHANGED
|
@@ -49,6 +49,8 @@ defaults are the help's too, and none is guessed from the art.
|
|
|
49
49
|
<!-- stable:commands -->
|
|
50
50
|
| command | flags |
|
|
51
51
|
| --- | --- |
|
|
52
|
+
| `depth-prepare` | `--parts`, `--config`, `--out` (issue #897; separate numeric-depth recipe) |
|
|
53
|
+
| `depth-apply` | `--parts`, `--manifest`, `--config`, `--rig-config`, `--out` (issue #897; original character config is read-only) |
|
|
52
54
|
| `layers` | one positional path: a wrapper directory, a `layers.json` or a `.psd`; with it, optional `--export` (a directory to write the wrapper form into, issue #359); or `--template` with `--canvas` and no path; or a positional head run with `--full`, `--source`, `--config` together for `PLAN_HEAD_GARMENT_OVERLAP` (issue #826) |
|
|
53
55
|
| `sheet` | `--source`, `--layers` (repeatable), `--out`; optional `--cell`, `--cols` |
|
|
54
56
|
| `inputs` | `--source`, `--config`, `--out` |
|
|
@@ -60,7 +62,7 @@ defaults are the help's too, and none is guessed from the art.
|
|
|
60
62
|
| `propose` | `--parts`, `--source`, `--out`, `--full`, `--head`, `--write-config` together: the [neck draft](AUTHORING.md#a-neck-that-moves-on-its-own) for an existing config with `turn.body` and one neck plan row; reassembles under `<out>/rig`, preserves the sampled turn and drafts a rigid neck unit (issue #826) |
|
|
61
63
|
| `compare` | `--left`, `--right`; optional `--map` |
|
|
62
64
|
| `rig` | `--config`, `--parts`, `--out`; optional `--idle-keys` (ctl, direct), `--page-edges` (pot, free), `--pack-shape` (rect, polygon) |
|
|
63
|
-
| `check` | `--rig`, `--out`; optional `--parts`, `--source`, `--page-edges`, `--pack-shape`, `--requirements`, `--seam-pairs`, `--keep-stills`, `--no-extremes` (issue #829), `--set` or `--all-sets` (issue #454), `--environment` (issue #538, additive), `--wind-band` (issue #590, additive) |
|
|
65
|
+
| `check` | `--rig`, `--out`; optional `--parts`, `--source`, `--page-edges`, `--pack-shape`, `--requirements`, `--seam-pairs`, `--keep-stills`, `--no-extremes` (issue #829), `--set` or `--all-sets` (issue #454), `--environment` (issue #538, additive), `--wind-band` (issue #590, additive), `--json` (issue #904) |
|
|
64
66
|
| `loop` | `--frames`, `--out`; optional `--palette` |
|
|
65
67
|
| `build` | `--config`, `--source`, `--full`, `--out`; optional `--head` (as for `assemble`), `--seam`, `--project`, `--page-edges`, `--pack-shape`, `--loop`, `--lead-in`, `--requirements`, `--idle-keys`, `--seam-pairs`, `--keep-stills`, `--no-extremes` (issue #829), `--alt` (as for `assemble`, issue #454), `--set` or `--all-sets` (as for `check`, issue #454), `--environment` (as for `check`, issue #538, additive), `--wind-band` (as for `check`, issue #590, additive) |
|
|
66
68
|
| `compose` | `--scene`, `--out`; optional `--requirements`, `--accept` |
|
|
@@ -114,6 +116,18 @@ rules that make the contract:
|
|
|
114
116
|
- **`turn`** (issue #251) adds a control bone `turn` under `root` that nothing
|
|
115
117
|
keys, and carries each part in `depths` by `(pivot − depth) / (pivot − nearest
|
|
116
118
|
depth)` of its x. Without the field no byte of any output moves.
|
|
119
|
+
A declared head turn (`depths` or `roles`) reports `RIG_TURN_HEAD_ROLL`
|
|
120
|
+
when the absolute setup eye-line roll exceeds the limit displayed as
|
|
121
|
+
18.485385 degrees
|
|
122
|
+
(issue #903). This empirical limit is the midpoint of the measured public
|
|
123
|
+
control maximum, 4.864514 degrees, and `three-quarter`,
|
|
124
|
+
32.106256 degrees (the comparison uses the unrounded midpoint). The warning
|
|
125
|
+
names the face/eye parts, the eye
|
|
126
|
+
bones, signed roll and limit. It is report-only: no refusal, field, pose or
|
|
127
|
+
emitted-byte change. No head turn, a body-only turn, or an unreadable eye
|
|
128
|
+
line adds no warning; unreadable retains the existing face-axis reason.
|
|
129
|
+
See [Head roll before a declared turn](AUTHORING.md#head-roll-before-a-declared-turn)
|
|
130
|
+
for the measurement scope and its limits.
|
|
117
131
|
- **`scene`** (issue #242) declares the painting a scene — the figure and what
|
|
118
132
|
is round it — and takes no field: `{}`. With it `assemble` reads the
|
|
119
133
|
recomposite inside the figure (the union of the full run's layers at alpha
|
|
@@ -133,7 +147,7 @@ part name, `[i]` an entry of a list):
|
|
|
133
147
|
- **generation.control.skeleton**, the pose file it names when it is no built-in — required: `width`, `height`, `points`
|
|
134
148
|
- **seethrough** — required: `resolution`, `steps`, `seed`, `offload`; optional: `head_box`, `arm_split`
|
|
135
149
|
- **seethrough.arm_split** — required: `order`, and one of `x` or `line` (issue #439)
|
|
136
|
-
- **assemble** — required: `rig_scale`, `plan` (from plain `assemble` on); optional: `extend_below_crop`, `patches`, `cuts`, `alternates`, `head_face_underfill`, `yield_refused`, `keep_under_occluders`, `extend_painting`, `take`
|
|
150
|
+
- **assemble** — required: `rig_scale`, `plan` (from plain `assemble` on); optional: `seam`, `extend_below_crop`, `patches`, `cuts`, `alternates`, `head_face_underfill`, `yield_refused`, `keep_under_occluders`, `extend_painting`, `take`
|
|
137
151
|
- **assemble.alternates[i]** — required: `name`; optional: `source`, `full`, `head`, `parts`, `hides`, `head_box`, `cuts`, `skeleton`, `anchor`, `base_anchor`, `drawn`, `tips`, `front`, `layers`, `focus` (issues #454, #465, #543, #652, #664, #666, #719, #780). `focus` is an authored finite `[x, y]` in painting pixels, within its canvas (edges included), refused as CONFIG_SET_FOCUS before assemble output when invalid; only when present, `parts.json` records it with focus_source_size and `sets.json` emits it in setup-world coordinates through the actual rig resize and `src/coords.ts`. Absent focus emits no field or inferred value; changing/removing it requires reassembly
|
|
138
152
|
- **assemble.extend_below_crop[i]** — required: `part`, `run`, `tag`
|
|
139
153
|
- **assemble.extend_painting[i]** — required: `part`, `band`, `max_distance`; optional: `min_held_alpha`, `order` (issues #848, #854, #855; explicit final layer-derived part, half-open rig band and positive four-neighbour path bound; base painting only). The optional integer floor 1–255 admits exact original alpha-over coverage strictly below it; absent or 1 blocks every nonzero holder. Only selected target samples below the floor may take painting RGBA; target samples at/above the floor and every non-target holder remain exact. By default overlapping reachable claims refuse. With `order` on every entry (unique nonnegative safe integers), lower values claim first and their gains block later candidates and paths; array order never breaks ties or supplies missing precedence
|
|
@@ -204,6 +218,11 @@ part name, `[i]` an entry of a list):
|
|
|
204
218
|
- **waist** — required: `top`, `bottom`, `upper`, `lower`, `bone`, `constraint` (#650; absent keeps the ordinary bindings and graph)
|
|
205
219
|
<!-- /stable:config -->
|
|
206
220
|
|
|
221
|
+
`assemble.seam` accepts `near-white` or `silhouette` (issue #905). CLI
|
|
222
|
+
`--seam` overrides config, then the existing `near-white` default applies.
|
|
223
|
+
Invalid values refuse as `CONFIG_FIELD_TYPE` at `config.assemble.seam`;
|
|
224
|
+
an absent field preserves existing output bytes.
|
|
225
|
+
|
|
207
226
|
`TY14` holds the set of field names in backticks above to the set of names the
|
|
208
227
|
loader's object checks spell (every `c.object(…)` in `src/config.ts`, with the lists
|
|
209
228
|
they name), in both directions, and refuses an object check whose key list it cannot
|
|
@@ -505,10 +524,17 @@ The separate ordering scalar is unchanged. No-image outputs retain legacy bytes.
|
|
|
505
524
|
### Declared setup-painting regions
|
|
506
525
|
|
|
507
526
|
The optional `setup_painting` key in `spine-parts-requirements/1` adds a
|
|
508
|
-
`SETUP_MATCHES_PAINTING`
|
|
527
|
+
`SETUP_MATCHES_PAINTING` measurement when declared. It requires authored
|
|
509
528
|
`rgb_tolerance` and named native-stage boxes. Each optional `max_pixels` is
|
|
510
|
-
that whole region's limit; omission is SKIP, never PASS.
|
|
511
|
-
|
|
529
|
+
that whole region's limit; omission is SKIP, never PASS. With `--source` but
|
|
530
|
+
no declaration, the console, `check.json` and the report's skipped-bar list now
|
|
531
|
+
say SKIP with the reason and the `--requirements` declaration remedy; exit
|
|
532
|
+
codes stay unchanged. No metric, region or limit is invented. Without source
|
|
533
|
+
and without the key, the line stays absent. Declared output bytes and renderer
|
|
534
|
+
calls remain unchanged. `check --json` prints the completed `check.json` bytes
|
|
535
|
+
to stdout and keeps the ordinary check's exit code; early input refusals and
|
|
536
|
+
usage errors keep their existing named text and exits. `build --json` is refused
|
|
537
|
+
with a `check --json` remedy; build keeps its multi-stage text report. Other requirement fields
|
|
512
538
|
remain independent. The [authoring contract](AUTHORING.md#setup-against-the-painting)
|
|
513
539
|
defines white compositing, the figure domain, strict RGB comparison, top
|
|
514
540
|
contributor attribution and named measurement refusals. The check observes the
|
|
@@ -525,3 +551,22 @@ only for that explicit block; `check --requirements` refuses unavailable or
|
|
|
525
551
|
stale evidence rather than reconstructing original art from emitted siblings.
|
|
526
552
|
Without the block the API reading is SKIP; CLI stdout and emitted bytes remain unchanged.
|
|
527
553
|
See [Source before scissors](AUTHORING.md#source-before-scissors).
|
|
554
|
+
|
|
555
|
+
## Provider-map depth stages (#897)
|
|
556
|
+
|
|
557
|
+
The opt-in `depth-prepare` and `depth-apply` commands accept the separate
|
|
558
|
+
`rig-parts-depth/1` recipe and finite floating-point `rig-parts-map/1` JSON.
|
|
559
|
+
Prepared provenance is `rig-parts-depth-inputs/1`; application writes
|
|
560
|
+
`rig-parts-depth-report/1`, standard part depth references and a relocated
|
|
561
|
+
character config. Their schemas, frames, alpha threshold, normalization,
|
|
562
|
+
encoding, convexity sign and refusal remedies are in
|
|
563
|
+
[AUTHORING](AUTHORING.md#per-part-provider-maps-897). No existing build path
|
|
564
|
+
calls these stages; no-config builds retain their existing behavior and bytes.
|
|
565
|
+
Group numbers and unit depths are explicit; no provider or absolute scale is
|
|
566
|
+
inferred. A new depth stage never replaces Core's rig gate.
|
|
567
|
+
`relief` is an explicit rig-pixel value, not an implicit width multiplier.
|
|
568
|
+
Part-width and head-referenced calibrations are authored recipe choices;
|
|
569
|
+
changing a calibration requires preparing its recipe again. Compare the same
|
|
570
|
+
provider maps and preserve their provenance. Each report row names the part,
|
|
571
|
+
declared relief, group `zScale` and clipping over original alpha>=128 support;
|
|
572
|
+
it remains a measurement rather than a shape-quality gate.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-parts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.88.0",
|
|
4
4
|
"description": "AI-authored 2D character parts from one anime painting and its See-through layer decomposition: measured parts, weighted-mesh rig specs, verified through rig-c before anything is written. A CLI for agents that cannot see the image.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/assemble.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SeamRule } from './seamrule.ts';
|
|
1
2
|
import { edgeLength } from './length.ts';
|
|
2
3
|
import { materialSource, type MaterialSource, materialRows, polygonMaterialEdges, type MaterialRow, type MaterialEdge, type PlacedArt } from './cutmaterial.ts';
|
|
3
4
|
import { unionTakenSources } from './takesource.ts';
|
|
@@ -188,16 +189,7 @@ export const SILHOUETTE_BORDER = 8;
|
|
|
188
189
|
/** `figureSilhouette`: a pixel is figure where it differs from that median by more than this, max channel. */
|
|
189
190
|
export const SILHOUETTE_LIMIT = 40;
|
|
190
191
|
|
|
191
|
-
export type SeamRule
|
|
192
|
-
export const SEAM_RULES: readonly SeamRule[] = ['near-white', 'silhouette'];
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* The seam rule a run uses when none is named. Set by measurement, not taste:
|
|
196
|
-
* the silhouette rule ships as the default only if it lowers the error pixel
|
|
197
|
-
* count on every oracle character without raising mean |d| on any
|
|
198
|
-
* (ORACLE_assemble.md, *faithful vs silhouette*).
|
|
199
|
-
*/
|
|
200
|
-
export const DEFAULT_SEAM_RULE: SeamRule = 'near-white';
|
|
192
|
+
export { DEFAULT_SEAM_RULE, SEAM_RULES, type SeamRule } from './seamrule.ts';
|
|
201
193
|
|
|
202
194
|
/**
|
|
203
195
|
* Where a layer may take the painting's pixel (`projectSource`). `core` is the
|