rig-parts 1.75.0 → 1.77.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 CHANGED
@@ -16,7 +16,8 @@ import { runBuildProcess, processRigcRunner as rigcRunner } from './tools/build-
16
16
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
17
17
  import { tmpdir } from 'node:os';
18
18
  import { basename, dirname, extname, join, resolve } from 'node:path';
19
- import { DEFAULT_PROJECT_RULE, DEFAULT_SEAM_RULE, HOLES_LISTED, PROJECT_RULES, type ProjectRule, proposeFields, proposePlan, proposeSinglePlan, SEAM_RULES, type SeamRule } from './src/assemble.ts';
19
+ import { DEFAULT_PROJECT_RULE, DEFAULT_SEAM_RULE, headGarmentOverlaps, headGarmentWarning, HOLES_LISTED, PROJECT_RULES, type ProjectRule, proposeFields, proposePlan, proposeSinglePlan, SEAM_RULES, type SeamRule } from './src/assemble.ts';
20
+ import { proposeNeck } from './tools/neck-propose.ts';
20
21
  import { parseAltArg } from './tools/alternate-cli.ts';
21
22
  import { type AlternateCliInput, assembleStage, build, checkStage, ERROR_MAP_FILE, loopStage, readRuns, readSource, rigStage, suppliedCheckFlag } from './src/build.ts';
22
23
  import { ACCEPTABLE_BARS, DEFAULT_PACK_SHAPE, DEFAULT_PAGE_EDGES, findRigc, PACK_SHAPES, rigcRunnerAt, type PackShape, PAGE_EDGES, type PageEdges, PARTS_HOME_SENTENCE, RIGC_GEOMETRY_VERSION, rigcRanClause, type RigcRunner, NO_PARTS_SENTENCE, SEAM_MEAN_BAR, SOURCE_SENTENCE, SEAM_PX_BAR, SEAM_PX_LEVEL, SPINEBOY_YARDSTICK, TEXTURE_STRETCH_CEILING } from './src/check.ts';
@@ -88,6 +89,11 @@ usage:
88
89
  size is not its box, and anything else outside the input contract —
89
90
  never a WARN.
90
91
 
92
+ rig-parts layers <head-run> --full <full-run> --source <painting.png> --config <config.json>
93
+ Read PLAN_HEAD_GARMENT_OVERLAP on planned head parts against full-run
94
+ garments registered into the declared crop. Alpha >=250, any overlap;
95
+ names part, garment, count, half-open head-canvas box and source remedy.
96
+
91
97
  rig-parts layers --export <dir> <dir | layers.json | file.psd>
92
98
  Print the table, then write the input as the wrapper form into <dir>
93
99
  (absent or empty, LAYERS_EXPORT_DIR_EMPTY): layers.json and one RGBA PNG
@@ -203,6 +209,13 @@ usage:
203
209
  or a layer with no depth is refused by name. Without either flag nothing
204
210
  changes.
205
211
 
212
+ rig-parts propose … --full <run> --head <run> --write-config <existing.json>
213
+ When the target declares turn.body and one neck plan row, preserve its
214
+ sampled turn and authored rig, take a clean full-run neck, and draft one
215
+ rigid sibling unit under the body carrier. Reassembled art is written to
216
+ <out>/rig; depth comes from the existing scalar input, named in note:.
217
+ Missing depth, landmarks or clean source is refused, never invented.
218
+
206
219
  rig-parts propose … [--grid-rule figure|part]
207
220
  How each drafted lattice's spacing (meshes.<part>.grid) is chosen.
208
221
  figure, the default: round(H / ${FIGURE_GRID_DIVISOR}) rig px for every lattice, H the
@@ -1051,7 +1064,7 @@ function printLayerTable(set: LayerSet): void {
1051
1064
  * no row; `layers` takes one positional path beside its flags (issue #359).
1052
1065
  */
1053
1066
  const TAKES = {
1054
- layers: { valued: ['--export', '--template', '--canvas'], switches: [] },
1067
+ layers: { valued: ['--export', '--template', '--canvas', '--full', '--source', '--config'], switches: [] },
1055
1068
  sheet: { valued: ['--source', '--layers', '--out', '--cell', '--cols'], switches: [] },
1056
1069
  rig: { valued: ['--config', '--parts', '--out', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
1057
1070
  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'] },
@@ -1109,7 +1122,7 @@ function cmdLayers(args: string[]): number {
1109
1122
  }
1110
1123
  const template = flags.get('--template');
1111
1124
  if (template !== undefined) {
1112
- if (flags.has('--export') || paths.length > 0) return usage(`--template writes a PSD and reads nothing; it takes --canvas only, and got ${[...[...flags.keys()].filter((f) => f !== '--template' && f !== '--canvas'), ...paths].join(', ')}`);
1125
+ if (flags.has('--export') || paths.length > 0 || ['--full', '--source', '--config'].some(f => flags.has(f))) return usage(`--template writes a PSD and reads nothing; it takes --canvas only, and got ${[...[...flags.keys()].filter((f) => f !== '--template' && f !== '--canvas'), ...paths].join(', ')}`);
1113
1126
  const canvasArg = flags.get('--canvas');
1114
1127
  if (canvasArg === undefined) return usage('--template needs --canvas <W>x<H>, the canvas size in px (with one run, a square of seethrough.resolution)');
1115
1128
  const canvas = parseCanvas(canvasArg);
@@ -1127,6 +1140,14 @@ function cmdLayers(args: string[]): number {
1127
1140
  try {
1128
1141
  const set = readLayers(paths[0]);
1129
1142
  printLayerTable(set);
1143
+ const context = ['--full', '--source', '--config'];
1144
+ if (context.some(f => flags.has(f))) {
1145
+ if (!context.every(f => flags.has(f))) return usage('layers crop overlap inspection requires --full, --source and --config together; the positional path is the head run');
1146
+ const full = readLayers(flags.get('--full')!), source = readSource(flags.get('--source')!);
1147
+ const cfg = loadEarlyConfig(flags.get('--config')!, 'assemble');
1148
+ const g = { sourceW: source.width, sourceH: source.height, ...proposeFields(cfg) };
1149
+ for (const w of headGarmentOverlaps(full, set, g, cfg.assemble.plan)) console.log(` ${headGarmentWarning(w)}`);
1150
+ }
1130
1151
  const dir = flags.get('--export');
1131
1152
  if (dir !== undefined) {
1132
1153
  const w = exportLayers(set, dir);
@@ -1479,6 +1500,16 @@ function cmdPropose(args: string[]): number {
1479
1500
  if (!existsSync(source)) {
1480
1501
  throw new PartsError([{ code: 'PROPOSE_SOURCE_PRESENT', object: source, detail: 'no such file; the painting is drawn under the overlay' }]);
1481
1502
  }
1503
+ if (target !== null && typeof target.raw === 'object' && target.raw !== null && 'turn' in target.raw) {
1504
+ const turn = target.raw.turn;
1505
+ if (typeof turn === 'object' && turn !== null && 'body' in turn && (loadEarlyConfig(target.path, 'assemble').assemble.plan.some(([, , tag]) => tag === 'neck'))) {
1506
+ const unsupported = [...flags.keys()].filter(f => !['--parts', '--source', '--out', '--full', '--head', '--write-config'].includes(f));
1507
+ if (unsupported.length > 0) throw new PartsError([{ code: 'PROPOSE_NECK_MODE', object: unsupported.join(', '), detail: 'cannot be combined with the authored sampled-turn neck draft; use --parts, --source, --out, --full, --head and --write-config only; run comparison or landmark drafting separately' }]);
1508
+ if (onlyMissing) throw new PartsError([{ code: 'PROPOSE_NECK_MODE', object: '--only-missing', detail: 'cannot replace an attached neck; omit --only-missing for the explicit sampled body-turn neck draft' }]);
1509
+ proposeNeck(target, { source, parts: partsDir, out, full: flags.get('--full'), head: flags.get('--head') });
1510
+ return EXIT_OK;
1511
+ }
1512
+ }
1482
1513
  const P = readPartSet(partsDir);
1483
1514
  const painting = readPng(source);
1484
1515
  // Issue #75: the keypoint file, held to this painting's size and taken to rig px by the overlay's map.