rig-parts 1.5.2 → 1.5.3

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 CHANGED
@@ -40,7 +40,7 @@ is taken from the full run, because the twin tails leave the head crop sideways
40
40
  <code>seam: setup pose at 714x1216, scale 0.9422: mean |d| 0.234 (&lt;= 1.0), 2 px over 40, 2.25 rig px (the count / s²; &lt;= 50), 0 px over 80 (reported)</code><br/>
41
41
  <code>pack: skeleton.png 922x1348, 22 region(s), 94.9% covered, padding 2, page edges free, shape polygon</code><br/>
42
42
  <code>--seam silhouette</code> repaired the navy blobs the default rule leaves on the white
43
- blouse (recomposite error pixels 11,050 → 9,540); the default stays
43
+ blouse (recomposite error pixels 10,340 → 8,644); the default stays
44
44
  <code>near-white</code>, the reference implementation's rule, so the examples stay
45
45
  comparable with it. The loop is <code>rig-parts loop --palette</code>'s indexed APNG, 48 frames at
46
46
  12 fps, 1,706,719 bytes, one 256-entry palette at a measured error of max 57, mean 1.601
package/cli.ts CHANGED
@@ -550,6 +550,39 @@ function printLayerTable(set: LayerSet): void {
550
550
  console.log(` ${warned} WARN line(s)`);
551
551
  }
552
552
 
553
+ /**
554
+ * Every flag each command takes: `valued` flags carry a value, `switches` are bare. Each parser reads its row to
555
+ * decide what it accepts, and {@link notTaken} prints the same row in the unknown-flag refusal, so the refusal
556
+ * lists exactly the flags the parser accepts (CL26). `layers` takes one positional path and no flag, so it has no row.
557
+ */
558
+ const TAKES = {
559
+ sheet: { valued: ['--source', '--layers', '--out', '--cell', '--cols'], switches: [] },
560
+ rig: { valued: ['--config', '--parts', '--out', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
561
+ propose: { valued: ['--parts', '--source', '--out', '--from-config', '--compare', '--full', '--canvas', '--keypoints', '--person', '--wrist-snap'], switches: ['--head-box'] },
562
+ compare: { valued: ['--left', '--right', '--map'], switches: [] },
563
+ check: { valued: ['--rig', '--out', '--parts', '--source', '--page-edges', '--pack-shape', '--requirements'], switches: ['--seam-pairs', '--keep-stills'] },
564
+ loop: { valued: ['--frames', '--out'], switches: ['--palette'] },
565
+ assemble: { valued: ['--source', '--full', '--head', '--config', '--out', '--seam', '--project'], switches: ['--propose-plan'] },
566
+ 'comfy seethrough': { valued: ['--image', '--out', '--resolution', '--steps', '--seed', '--prefix', '--host', '--wait', '--timeout', '--poll'], switches: ['--offload', '--lama', '--nf4'] },
567
+ 'comfy paint': { valued: ['--config', '--out', '--seeds', '--seed0', '--host', '--wait', '--timeout', '--poll'], switches: [] },
568
+ inputs: { valued: ['--source', '--config', '--out'], switches: [] },
569
+ build: { valued: ['--config', '--source', '--full', '--head', '--out', '--seam', '--project', '--page-edges', '--pack-shape', '--requirements', '--idle-keys'], switches: ['--loop'] },
570
+ compose: { valued: ['--scene', '--out', '--requirements', '--accept'], switches: [] },
571
+ } as const satisfies Record<string, { valued: readonly string[]; switches: readonly string[] }>;
572
+
573
+ type Command = keyof typeof TAKES;
574
+
575
+ /** Whether `command` takes `flag` as a valued flag, read off its {@link TAKES} row. */
576
+ function takesValued(command: Command, flag: string): boolean {
577
+ return (TAKES[command].valued as readonly string[]).includes(flag);
578
+ }
579
+
580
+ /** The unknown-flag refusal: the flag, and every flag the command's {@link TAKES} row holds, valued then bare. */
581
+ function notTaken(command: Command, flag: string): string {
582
+ const row = TAKES[command];
583
+ return `${command} does not take "${flag}"; it takes ${[...row.valued, ...row.switches].join(', ')}`;
584
+ }
585
+
553
586
  function cmdLayers(args: string[]): number {
554
587
  if (args.length !== 1) return usage(`layers takes one path; got ${args.length}`);
555
588
  try {
@@ -569,7 +602,7 @@ function cmdSheet(args: string[]): number {
569
602
  for (let i = 0; i < args.length; i++) {
570
603
  const flag = args[i];
571
604
  const value = args[i + 1];
572
- if (!['--source', '--layers', '--out', '--cell', '--cols'].includes(flag)) return usage(`sheet does not take "${flag}"`);
605
+ if (!takesValued('sheet', flag)) return usage(notTaken('sheet', flag));
573
606
  if (value === undefined) return usage(`${flag} needs a value`);
574
607
  i++;
575
608
  if (flag === '--source') source = value;
@@ -639,7 +672,7 @@ function cmdRig(args: string[]): number {
639
672
  for (let i = 0; i < args.length; i++) {
640
673
  const flag = args[i];
641
674
  const value = args[i + 1];
642
- if (!['--config', '--parts', '--out', '--idle-keys', '--page-edges', '--pack-shape'].includes(flag)) return usage(`rig does not take "${flag}"`);
675
+ if (!takesValued('rig', flag)) return usage(notTaken('rig', flag));
643
676
  if (value === undefined) return usage(`${flag} needs a value`);
644
677
  i++;
645
678
  if (flag === '--config') config = value;
@@ -699,18 +732,17 @@ function rigcRunner(bin: string): RigcRunner {
699
732
  };
700
733
  }
701
734
 
702
- /** `switches` are the command's bare flags, taken out by the caller before this reads the valued ones; they are only named in the unknown-flag refusal, so it lists every flag the command takes. */
703
- function flags(args: string[], known: readonly string[], command: string, optional: readonly string[] = [], switches: readonly string[] = []): Map<string, string> | string {
735
+ /** The command's valued flags as `--flag value` pairs, each one its {@link TAKES} row holds; `required` must all be given. Its bare switches are taken out by the caller before this reads the rest. A string is a usage error. */
736
+ function flags(args: string[], command: Command, required: readonly string[]): Map<string, string> | string {
704
737
  const got = new Map<string, string>();
705
- const all = [...known, ...optional];
706
738
  for (let i = 0; i < args.length; i += 2) {
707
739
  const flag = args[i];
708
- if (!all.includes(flag)) return `${command} does not take "${flag}"; it takes ${[...all, ...switches].join(', ')}`;
740
+ if (!takesValued(command, flag)) return notTaken(command, flag);
709
741
  if (args[i + 1] === undefined) return `${flag} needs a value`;
710
742
  if (got.has(flag)) return `${flag} is given twice`;
711
743
  got.set(flag, args[i + 1]);
712
744
  }
713
- for (const k of known) if (!got.has(k)) return `${command} needs ${k} <value>`;
745
+ for (const k of required) if (!got.has(k)) return `${command} needs ${k} <value>`;
714
746
  return got;
715
747
  }
716
748
 
@@ -722,10 +754,8 @@ function cmdCheck(args: string[]): number {
722
754
  if (keepStills > 1) return usage('--keep-stills is given twice');
723
755
  const f = flags(
724
756
  args.filter((a) => a !== '--seam-pairs' && a !== '--keep-stills'),
725
- ['--rig', '--out'],
726
757
  'check',
727
- ['--parts', '--source', '--page-edges', '--pack-shape', '--requirements'],
728
- ['--seam-pairs', '--keep-stills'],
758
+ ['--rig', '--out'],
729
759
  );
730
760
  if (typeof f === 'string') return usage(f);
731
761
  const rig = f.get('--rig') as string;
@@ -775,14 +805,13 @@ function printLint(P: PartSet, spec: { bones: CharacterConfig['bones']; meshes:
775
805
  function cmdPropose(args: string[]): number {
776
806
  const flags = new Map<string, string>();
777
807
  let headBox = false;
778
- const valued = ['--parts', '--source', '--out', '--from-config', '--compare', '--full', '--canvas', '--keypoints', '--person', '--wrist-snap'];
779
808
  for (let i = 0; i < args.length; i++) {
780
809
  const flag = args[i];
781
810
  if (flag === '--head-box') {
782
811
  headBox = true;
783
812
  continue;
784
813
  }
785
- if (!valued.includes(flag)) return usage(`propose does not take "${flag}"; it takes --head-box and ${valued.join(', ')}`);
814
+ if (!takesValued('propose', flag)) return usage(notTaken('propose', flag));
786
815
  const value = args[i + 1];
787
816
  if (value === undefined) return usage(`${flag} needs a value`);
788
817
  if (flags.has(flag)) return usage(`${flag} is given twice`);
@@ -880,10 +909,9 @@ function cmdPropose(args: string[]): number {
880
909
 
881
910
  function cmdCompare(args: string[]): number {
882
911
  const flags = new Map<string, string>();
883
- const valued = ['--left', '--right', '--map'];
884
912
  for (let i = 0; i < args.length; i++) {
885
913
  const flag = args[i];
886
- if (!valued.includes(flag)) return usage(`compare does not take "${flag}"; it takes ${valued.join(', ')}`);
914
+ if (!takesValued('compare', flag)) return usage(notTaken('compare', flag));
887
915
  const value = args[i + 1];
888
916
  if (value === undefined) return usage(`${flag} needs a value`);
889
917
  if (flags.has(flag)) return usage(`${flag} is given twice`);
@@ -912,8 +940,8 @@ function cmdLoop(args: string[]): number {
912
940
  if (palettes > 1) return usage('--palette is given twice');
913
941
  const f = flags(
914
942
  args.filter((a) => a !== '--palette'),
915
- ['--frames', '--out'],
916
943
  'loop',
944
+ ['--frames', '--out'],
917
945
  );
918
946
  if (typeof f === 'string') return usage(f);
919
947
  const out = f.get('--out') as string;
@@ -932,14 +960,13 @@ function cmdLoop(args: string[]): number {
932
960
  function cmdAssemble(args: string[]): number {
933
961
  const flags = new Map<string, string>();
934
962
  let propose = false;
935
- const valued = ['--source', '--full', '--head', '--config', '--out', '--seam', '--project'];
936
963
  for (let i = 0; i < args.length; i++) {
937
964
  const flag = args[i];
938
965
  if (flag === '--propose-plan') {
939
966
  propose = true;
940
967
  continue;
941
968
  }
942
- if (!valued.includes(flag)) return usage(`assemble does not take "${flag}"`);
969
+ if (!takesValued('assemble', flag)) return usage(notTaken('assemble', flag));
943
970
  const value = args[i + 1];
944
971
  if (value === undefined) return usage(`${flag} needs a value`);
945
972
  if (flags.has(flag)) return usage(`${flag} is given twice`);
@@ -975,8 +1002,9 @@ function cmdAssemble(args: string[]): number {
975
1002
  }
976
1003
  }
977
1004
 
978
- /** Parse `--flag value` pairs and bare `--switch`es; a string is a usage error. */
979
- function comfyFlags(args: string[], valued: readonly string[], switches: readonly string[], command: string): { v: Map<string, string>; on: Set<string> } | string {
1005
+ /** Parse `--flag value` pairs and bare `--switch`es, as the command's {@link TAKES} row names them; a string is a usage error. */
1006
+ function comfyFlags(args: string[], command: 'comfy seethrough' | 'comfy paint'): { v: Map<string, string>; on: Set<string> } | string {
1007
+ const switches: readonly string[] = TAKES[command].switches;
980
1008
  const v = new Map<string, string>();
981
1009
  const on = new Set<string>();
982
1010
  for (let i = 0; i < args.length; i++) {
@@ -986,7 +1014,7 @@ function comfyFlags(args: string[], valued: readonly string[], switches: readonl
986
1014
  on.add(flag);
987
1015
  continue;
988
1016
  }
989
- if (!valued.includes(flag)) return `${command} does not take "${flag}"; it takes ${[...valued, ...switches].join(', ')}`;
1017
+ if (!takesValued(command, flag)) return notTaken(command, flag);
990
1018
  const value = args[i + 1];
991
1019
  if (value === undefined) return `${flag} needs a value`;
992
1020
  if (v.has(flag)) return `${flag} is given twice`;
@@ -1012,14 +1040,10 @@ function secondsFlag(v: Map<string, string>, flag: string, fallback: number): nu
1012
1040
  return Number.isFinite(n) && n > 0 ? n : `${flag} ${raw} is not a number of seconds above 0`;
1013
1041
  }
1014
1042
 
1015
- const COMFY_TIMING = ['--host', '--wait', '--timeout', '--poll'] as const;
1016
-
1017
1043
  async function cmdComfy(args: string[]): Promise<number> {
1018
1044
  const [sub, ...rest] = args;
1019
1045
  if (sub !== 'seethrough' && sub !== 'paint') return usage(`comfy takes seethrough or paint; got ${sub === undefined ? 'nothing' : `"${sub}"`}`);
1020
- const valued = sub === 'seethrough' ? ['--image', '--out', '--resolution', '--steps', '--seed', '--prefix', ...COMFY_TIMING] : ['--config', '--out', '--seeds', '--seed0', ...COMFY_TIMING];
1021
- const switches = sub === 'seethrough' ? ['--offload', '--lama', '--nf4'] : [];
1022
- const f = comfyFlags(rest, valued, switches, `comfy ${sub}`);
1046
+ const f = comfyFlags(rest, `comfy ${sub}`);
1023
1047
  if (typeof f === 'string') return usage(f);
1024
1048
  const { v, on } = f;
1025
1049
  const wait = secondsFlag(v, '--wait', 1800);
@@ -1082,7 +1106,7 @@ async function cmdComfy(args: string[]): Promise<number> {
1082
1106
  }
1083
1107
 
1084
1108
  function cmdInputs(args: string[]): number {
1085
- const f = flags(args, ['--source', '--config', '--out'], 'inputs');
1109
+ const f = flags(args, 'inputs', ['--source', '--config', '--out']);
1086
1110
  if (typeof f === 'string') return usage(f);
1087
1111
  const source = f.get('--source') as string;
1088
1112
  const out = f.get('--out') as string;
@@ -1114,7 +1138,6 @@ function cmdInputs(args: string[]): number {
1114
1138
  function cmdBuild(args: string[]): number {
1115
1139
  const flags = new Map<string, string>();
1116
1140
  let loop = false;
1117
- const valued = ['--config', '--source', '--full', '--head', '--out', '--seam', '--project', '--page-edges', '--pack-shape', '--requirements', '--idle-keys'];
1118
1141
  for (let i = 0; i < args.length; i++) {
1119
1142
  const flag = args[i];
1120
1143
  if (flag === '--loop') {
@@ -1122,7 +1145,7 @@ function cmdBuild(args: string[]): number {
1122
1145
  loop = true;
1123
1146
  continue;
1124
1147
  }
1125
- if (!valued.includes(flag)) return usage(`build does not take "${flag}"; it takes --loop and ${valued.join(', ')}`);
1148
+ if (!takesValued('build', flag)) return usage(notTaken('build', flag));
1126
1149
  const value = args[i + 1];
1127
1150
  if (value === undefined) return usage(`${flag} needs a value`);
1128
1151
  if (flags.has(flag)) return usage(`${flag} is given twice`);
@@ -1161,7 +1184,7 @@ function cmdBuild(args: string[]): number {
1161
1184
  }
1162
1185
 
1163
1186
  function cmdCompose(args: string[]): number {
1164
- const f = flags(args, ['--scene', '--out'], 'compose', ['--requirements', '--accept']);
1187
+ const f = flags(args, 'compose', ['--scene', '--out']);
1165
1188
  if (typeof f === 'string') return usage(f);
1166
1189
  let bin: string;
1167
1190
  try {
package/docs/AUTHORING.md CHANGED
@@ -1230,8 +1230,10 @@ names it — `[29]` on both examples.
1230
1230
  flat stack of parts differs from the painting by more than 60, the top part takes the
1231
1231
  painting's colour — except where the painting is near-white, which protects the page
1232
1232
  and also every white garment. `--seam silhouette` protects near-white pixels only
1233
- outside the figure. On the demo it lowered recomposite error pixels from 11,050 to
1234
- 9,540 and changed no check bar; on the sample it changed nothing. The default stays
1233
+ outside the figure. On the demo it lowers recomposite error pixels from 10,340 to
1234
+ 8,644 (`assemble`'s `error px > 40`, at v1.5.2); on the sample it changes nothing
1235
+ (4,359 under both). At v0.14.0 the demo's were 11,050 and 9,540, and no check bar
1236
+ changed. The default stays
1235
1237
  the reference's so the examples stay comparable with it.
1236
1238
 
1237
1239
  **The fringe push-back** (issue #119; under either seam rule, no flag). Both places
@@ -1253,9 +1255,9 @@ and a cleared bridge to opaque art is put back, so no part gains an island
1253
1255
  names them. On the examples (default flags, measured against the v0.14.0 build of
1254
1256
  the same inputs): `demo` 1,193 px over 16 parts, recomposite error pixels 11,050 →
1255
1257
  10,340; `sample` 276 px over 15 parts, 4,512 → 4,359; uncovered error pixels and
1256
- holes unchanged on both, `check` 9 of 9 on both. The `--seam` and `--project`
1257
- comparisons in this section and the `SETUP_POSE_VS_SOURCE` example figures in §7
1258
- were measured before it, at v0.14.0.
1258
+ holes unchanged on both, `check` 9 of 9 on both. The `--project` comparison in
1259
+ this section and the `SETUP_POSE_VS_SOURCE` example figures in §7 were measured
1260
+ before it, at v0.14.0; the `--seam` figures above are v1.5.2's.
1259
1261
 
1260
1262
  **Page edges.** `rig`, `check` and `build` pack through `rigc build --pack
1261
1263
  --page-edges <v>`, and hand the value to rigc verbatim. The default is `free`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rig-parts",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
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": {
@@ -125,7 +125,7 @@ it and the order as `scene.json` records it (AUTHORING §7, *Composing several
125
125
  characters*).
126
126
 
127
127
  `--seam silhouette` repairs more of the seam on white garments than the default
128
- `near-white` (the demo: 11,050 → 9,540 recomposite error pixels); the default is
128
+ `near-white` (the demo: 10,340 → 8,644 recomposite error pixels); the default is
129
129
  the reference implementation's rule. `--project visible` lets thin visible parts
130
130
  (lashes, brows, irises) take the painting's pixels, which the default `core` rule
131
131
  erodes away. Name both rules you used in your report, and quote the assemble