rig-parts 1.50.0 → 1.52.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
@@ -30,7 +30,8 @@ import { checkImageSize, choosePerson, KEYPOINTS_SPEC, loadKeypoints, type RigJo
30
30
  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, proposeWithBasis, readPartSet, serializeProposal, withTurn, WRIST_ON_PART_PX } from './src/propose.ts';
31
31
  import { BASIS_FILE, BASIS_SPEC, basisFile, basisLine, coverageLines, serializeBasis } from './src/diagnostics.ts';
32
32
  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';
33
- import { clothConfigEdits, editPaths, writtenClothConfig } from './src/clothconfig.ts';
33
+ import { meshStatsStage } from './src/meshstats.ts';
34
+ import { clothConfigEdits, editPaths, previousCount, writtenClothConfig } from './src/clothconfig.ts';
34
35
  import { PROVENANCE_FILE, REMESH_SPEC, remeshStage } from './src/remesh.ts';
35
36
  import { RETUNE_FILE, RETUNE_SPEC, retuneStage } from './src/retune.ts';
36
37
  import { checkPainting } from './src/paintcheck.ts';
@@ -226,7 +227,7 @@ usage:
226
227
  a LINT line names each drafted radius and both figures. Without --cloth
227
228
  nothing changes.
228
229
 
229
- rig-parts propose … --cloth <config.json> [--write-config <out.json>]
230
+ rig-parts propose … --cloth <config.json> [--write-config <out.json> [--from <previous.json>]]
230
231
  Also write the --cloth config's own text with what the recipe drafts
231
232
  spliced in (issue #537), the way mesh-draft writes its config, so that
232
233
  rig --config <out.json> builds it. What is spliced is measured: the
@@ -246,6 +247,14 @@ usage:
246
247
  (PROPOSE_WRITE_CONFIG_CONFLICT), or when the spliced text does not
247
248
  parse to the config with the sections set, naming the first key
248
249
  that moved (PROPOSE_WRITE_CONFIG_SPLICE).
250
+ --from <previous.json> (issue #571, only with --write-config) names
251
+ the config the recipe is changed from: its cloth block is read as
252
+ --cloth's is, the same parts are drafted a third time with it, and an
253
+ entry equal to that previous recipe's draft is the recipe's, replaced
254
+ (or removed) as one equal to the draft without the recipe is; an entry
255
+ equal to none of the three drafts is still refused by name, its field
256
+ read against the previous recipe's draft. The write line counts the
257
+ entries the previous draft vouched for. Without --from nothing changes.
249
258
 
250
259
  rig-parts propose --parts <dir> --source <painting.png> --out <dir> --from-config <config.json>
251
260
  Draw the config's CURRENT bones instead (<out>/render/landmarks_config.png
@@ -365,6 +374,21 @@ usage:
365
374
  Written only when the drafted config's check is PASS. --requirements, --idle-keys,
366
375
  --page-edges and --pack-shape are as for rig and check.
367
376
 
377
+ rig-parts mesh-stats --config <config.json> --parts <dir>
378
+ Print the vertices and triangles of every mesh the config states, as the
379
+ rig stage would make them from the parts in --parts (the directory
380
+ holding parts.json and parts/<name>.png), and their total, in seconds:
381
+ the rig stage's own mesh construction, without rigc, without assemble
382
+ and without check (issue #566). One line per mesh in parts.json's order,
383
+ "mesh-stats: <part> — <mode> <V> vertices, <T> triangles" (mode lattice,
384
+ contour, auto or influence), then "mesh-stats: total <N> triangles over
385
+ <M> mesh(es)"; the counts are the ones the rig stage writes to
386
+ mesh_report.json, except an auto part's when rigc's motion gate refuses
387
+ its reduction and the rig stage replays an earlier accepted step, which
388
+ the auto line says. A problem the rig stage refuses (a missing part PNG,
389
+ an outline that does not close, a bone no config entry declares) is the
390
+ rig stage's own refusal, exit 1. Writes nothing.
391
+
368
392
  rig-parts remesh --rig <dir> --config <config.json> --parts <dir> --part <name>
369
393
  --mesh <entry.json> --out <dir> [--requirements <file.json>]
370
394
  (or --delta <mesh-edit.json> in place of --mesh)
@@ -915,7 +939,7 @@ const TAKES = {
915
939
  layers: { valued: ['--export', '--template', '--canvas'], switches: [] },
916
940
  sheet: { valued: ['--source', '--layers', '--out', '--cell', '--cols'], switches: [] },
917
941
  rig: { valued: ['--config', '--parts', '--out', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
918
- propose: { valued: ['--parts', '--source', '--out', '--from-config', '--compare', '--full', '--head', '--canvas', '--keypoints', '--person', '--wrist-snap', '--grid-rule', '--cloth', '--write-config'], switches: ['--head-box', '--no-back-hair'] },
942
+ 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'] },
919
943
  compare: { valued: ['--left', '--right', '--map'], switches: [] },
920
944
  check: { valued: ['--rig', '--out', '--parts', '--source', '--page-edges', '--pack-shape', '--requirements', '--set', '--environment'], switches: ['--seam-pairs', '--keep-stills', '--all-sets'] },
921
945
  loop: { valued: ['--frames', '--out'], switches: ['--palette'] },
@@ -926,6 +950,7 @@ const TAKES = {
926
950
  build: { valued: ['--config', '--source', '--full', '--head', '--out', '--seam', '--project', '--page-edges', '--pack-shape', '--requirements', '--idle-keys', '--lead-in', '--alt', '--set', '--environment'], switches: ['--loop', '--seam-pairs', '--keep-stills', '--all-sets'] },
927
951
  compose: { valued: ['--scene', '--out', '--requirements', '--accept'], switches: [] },
928
952
  'mesh-draft': { valued: ['--config', '--parts', '--out', '--requirements', '--idle-keys', '--page-edges', '--pack-shape'], switches: ['--more-triangles'] },
953
+ 'mesh-stats': { valued: ['--config', '--parts'], switches: [] },
929
954
  remesh: { valued: ['--rig', '--config', '--parts', '--part', '--mesh', '--delta', '--out', '--requirements', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
930
955
  retune: { valued: ['--rig', '--config', '--parts', '--out', '--requirements', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
931
956
  painting: { valued: ['--keypoints', '--person'], switches: [] },
@@ -1316,6 +1341,9 @@ function cmdPropose(args: string[]): number {
1316
1341
  const writeArg = flags.get('--write-config');
1317
1342
  if (writeArg !== undefined && flags.has('--from-config')) return usage('--write-config writes what the recipe drafts into its config; --from-config proposes nothing');
1318
1343
  if (writeArg !== undefined && clothArg === undefined) return usage('--write-config writes the --cloth config with what its recipe drafts spliced in; give --cloth <config.json>');
1344
+ // Issue #571: the config the recipe is changed from; an entry equal to its recipe's draft is the recipe's, not a hand edit.
1345
+ const fromArg = flags.get('--from');
1346
+ if (fromArg !== undefined && writeArg === undefined) return usage('--from names the config the recipe is changed from, for --write-config; give --write-config <out.json>');
1319
1347
  if (writeArg !== undefined) {
1320
1348
  const ours = ['proposal.json', BASIS_FILE].map((f) => resolve(out, f));
1321
1349
  if (existsSync(writeArg) || ours.includes(resolve(writeArg))) {
@@ -1323,6 +1351,7 @@ function cmdPropose(args: string[]): number {
1323
1351
  }
1324
1352
  }
1325
1353
  const cloth = clothArg === undefined ? null : loadClothConfig(clothArg).cloth;
1354
+ const previousCloth = fromArg === undefined ? null : loadClothConfig(fromArg).cloth;
1326
1355
  const partsDir = existsSync(partsArg) && statSync(partsArg).isFile() ? dirname(partsArg) : partsArg;
1327
1356
  if (!existsSync(source)) {
1328
1357
  throw new PartsError([{ code: 'PROPOSE_SOURCE_PRESENT', object: source, detail: 'no such file; the painting is drawn under the overlay' }]);
@@ -1366,8 +1395,10 @@ function cmdPropose(args: string[]): number {
1366
1395
  : (() => {
1367
1396
  const text = readFileSync(clothArg, 'utf8');
1368
1397
  // Issue #552: bones, constraints and motion.tracks entry by entry; a hand edit the recipe would drop is refused by name.
1369
- const edits = clothConfigEdits(drafted, proposeWithBasis(P, joints, wristSnap, gridRule, null).proposal, JSON.parse(text) as unknown);
1370
- return { at: writeArg, text: writtenClothConfig(clothArg, text, edits), paths: editPaths(edits), relative: relativeConfigPaths(dirname(resolve(clothArg)), JSON.parse(text) as Record<string, unknown>) };
1398
+ // Issue #571: with --from, the same parts drafted a third time with the previous recipe.
1399
+ const previous = previousCloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, previousCloth).proposal;
1400
+ const edits = clothConfigEdits(drafted, proposeWithBasis(P, joints, wristSnap, gridRule, null).proposal, JSON.parse(text) as unknown, false, previous);
1401
+ return { at: writeArg, text: writtenClothConfig(clothArg, text, edits), paths: editPaths(edits), vouched: previousCount(edits), relative: relativeConfigPaths(dirname(resolve(clothArg)), JSON.parse(text) as Record<string, unknown>) };
1371
1402
  })();
1372
1403
  const bfile = basisFile(P, prop, basis, cfg === null || cmpPath === undefined ? null : { where: cmpPath, config: cfg });
1373
1404
  const img = drawLandmarks(P, painting, prop.bones, 'PROPOSAL - CORRECT ME');
@@ -1385,10 +1416,11 @@ function cmdPropose(args: string[]): number {
1385
1416
  printLint(P, prop, joints, draftLint);
1386
1417
  console.log(basisLine(bfile, out));
1387
1418
  if (written !== null && clothArg !== undefined) {
1419
+ const from = fromArg === undefined ? '' : `; --from ${fromArg}: ${written.vouched} ${written.vouched === 1 ? 'entry' : 'entries'} taken as the previous recipe's draft`;
1388
1420
  console.log(
1389
1421
  written.paths.length > 0
1390
- ? `wrote ${written.at}: ${clothArg}'s text with ${written.paths.join(', ')} spliced in from the draft; every other byte as read`
1391
- : `wrote ${written.at}: ${clothArg}'s text byte for byte; every entry the recipe drafts is already as drafted`,
1422
+ ? `wrote ${written.at}: ${clothArg}'s text with ${written.paths.join(', ')} spliced in from the draft; every other byte as read${from}`
1423
+ : `wrote ${written.at}: ${clothArg}'s text byte for byte; every entry the recipe drafts is already as drafted${from}`,
1392
1424
  );
1393
1425
  if (written.relative.length > 0 && dirname(resolve(written.at)) !== dirname(resolve(clothArg))) {
1394
1426
  console.log(`note: ${written.relative.map((r) => `${r.at} "${r.value}"`).join(', ')} written as stated; the loader resolves each against the directory the config sits in, ${dirname(written.at)} for ${written.at}, not ${dirname(clothArg)}; put the config beside ${clothArg} or rewrite them`);
@@ -1774,6 +1806,18 @@ function cmdMeshDraft(args: string[]): number {
1774
1806
  }
1775
1807
  }
1776
1808
 
1809
+ /** `mesh-stats --config <config.json> --parts <dir>` (issue #566): every configured mesh's counts, through the rig stage's own `buildRig`, without rigc. Writes nothing. */
1810
+ function cmdMeshStats(args: string[]): number {
1811
+ const f = flags(args, 'mesh-stats', ['--config', '--parts']);
1812
+ if (typeof f === 'string') return usage(f);
1813
+ try {
1814
+ meshStatsStage({ config: f.get('--config') as string, parts: f.get('--parts') as string }, console.log);
1815
+ return EXIT_OK;
1816
+ } catch (err) {
1817
+ return printRefusal(err);
1818
+ }
1819
+ }
1820
+
1777
1821
  /** `remesh` (issue #495): one part of a gated rig re-fitted under a requested mesh entry, into a new revision. */
1778
1822
  function cmdRemesh(args: string[]): number {
1779
1823
  const f = flags(args, 'remesh', ['--rig', '--config', '--parts', '--part', '--out']);
@@ -1922,6 +1966,7 @@ function main(argv: string[]): number | Promise<number> {
1922
1966
  if (command === 'build') return cmdBuild(rest);
1923
1967
  if (command === 'compose') return cmdCompose(rest);
1924
1968
  if (command === 'mesh-draft') return cmdMeshDraft(rest);
1969
+ if (command === 'mesh-stats') return cmdMeshStats(rest);
1925
1970
  if (command === 'remesh') return cmdRemesh(rest);
1926
1971
  if (command === 'retune') return cmdRetune(rest);
1927
1972
  if (command === 'painting') return cmdPainting(rest);