rig-parts 1.51.0 → 1.53.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
@@ -27,10 +27,11 @@ import { makeInputs } from './src/inputs.ts';
27
27
  import { idleKeysOf, isIdleKeys } from './src/rig.ts';
28
28
  import { depthSkipLine, exportLayers, figuresPhrase, hiddenLayerLine, implausibleRules, layerFigures, type LayerSet, layerSetDiff, pct, readLayers, readWrapperLayers, ruleSummary, templateLayerNames, templatePsdBytes, times } from './src/layers.ts';
29
29
  import { checkImageSize, choosePerson, KEYPOINTS_SPEC, loadKeypoints, type RigJoints, toRigJoints } from './src/keypoints.ts';
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';
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, proposalAsConfig, 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, previousCount, writtenClothConfig } from './src/clothconfig.ts';
33
+ import { meshStatsStage } from './src/meshstats.ts';
34
+ import { chainsLeftBehind, 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';
@@ -223,8 +224,12 @@ usage:
223
224
  ({across, max, shape}, issue #518) drafts the parts it meshes in the
224
225
  influence mode at their lattice's cell, the radius as stated or drafted
225
226
  as the farthest lattice vertex from its nearest link plus 3 cells, and
226
- a LINT line names each drafted radius and both figures. Without --cloth
227
- nothing changes.
227
+ a LINT line names each drafted radius and both figures. A keyed bone
228
+ the recipe leaves the only keyed child of its parent joins it in one
229
+ chain CHAIN_LAG holds to non-decreasing amplitudes (issue #572): a LINT
230
+ line names each link of such a chain below the link above, with both
231
+ amplitudes and the least that passes; no amplitude is changed. Without
232
+ --cloth nothing changes.
228
233
 
229
234
  rig-parts propose … --cloth <config.json> [--write-config <out.json> [--from <previous.json>]]
230
235
  Also write the --cloth config's own text with what the recipe drafts
@@ -373,6 +378,21 @@ usage:
373
378
  Written only when the drafted config's check is PASS. --requirements, --idle-keys,
374
379
  --page-edges and --pack-shape are as for rig and check.
375
380
 
381
+ rig-parts mesh-stats --config <config.json> --parts <dir>
382
+ Print the vertices and triangles of every mesh the config states, as the
383
+ rig stage would make them from the parts in --parts (the directory
384
+ holding parts.json and parts/<name>.png), and their total, in seconds:
385
+ the rig stage's own mesh construction, without rigc, without assemble
386
+ and without check (issue #566). One line per mesh in parts.json's order,
387
+ "mesh-stats: <part> — <mode> <V> vertices, <T> triangles" (mode lattice,
388
+ contour, auto or influence), then "mesh-stats: total <N> triangles over
389
+ <M> mesh(es)"; the counts are the ones the rig stage writes to
390
+ mesh_report.json, except an auto part's when rigc's motion gate refuses
391
+ its reduction and the rig stage replays an earlier accepted step, which
392
+ the auto line says. A problem the rig stage refuses (a missing part PNG,
393
+ an outline that does not close, a bone no config entry declares) is the
394
+ rig stage's own refusal, exit 1. Writes nothing.
395
+
376
396
  rig-parts remesh --rig <dir> --config <config.json> --parts <dir> --part <name>
377
397
  --mesh <entry.json> --out <dir> [--requirements <file.json>]
378
398
  (or --delta <mesh-edit.json> in place of --mesh)
@@ -934,6 +954,7 @@ const TAKES = {
934
954
  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'] },
935
955
  compose: { valued: ['--scene', '--out', '--requirements', '--accept'], switches: [] },
936
956
  'mesh-draft': { valued: ['--config', '--parts', '--out', '--requirements', '--idle-keys', '--page-edges', '--pack-shape'], switches: ['--more-triangles'] },
957
+ 'mesh-stats': { valued: ['--config', '--parts'], switches: [] },
937
958
  remesh: { valued: ['--rig', '--config', '--parts', '--part', '--mesh', '--delta', '--out', '--requirements', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
938
959
  retune: { valued: ['--rig', '--config', '--parts', '--out', '--requirements', '--idle-keys', '--page-edges', '--pack-shape'], switches: [] },
939
960
  painting: { valued: ['--keypoints', '--person'], switches: [] },
@@ -1371,18 +1392,39 @@ function cmdPropose(args: string[]): number {
1371
1392
  : withTurn(drafted, draftTurn(P, { ...(fullRun === undefined ? {} : { full: readLayers(fullRun) }), ...(headRun === undefined ? {} : { head: readLayers(headRun) }) }, drafted));
1372
1393
  // Emit only after green: a proposal the config loader would refuse is not written, nor a basis record that does not hold to it.
1373
1394
  checkProposal(P, prop);
1395
+ // The same parts drafted without the recipe: what --write-config measures its edits against, and what issue #572's chain reading compares with.
1396
+ const plain = cloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, null).proposal;
1374
1397
  // Issue #537: the sections the recipe moves, measured against the same parts drafted without it, spliced before anything is written.
1375
1398
  const written =
1376
- writeArg === undefined || clothArg === undefined
1399
+ writeArg === undefined || clothArg === undefined || plain === null
1377
1400
  ? null
1378
1401
  : (() => {
1379
1402
  const text = readFileSync(clothArg, 'utf8');
1380
1403
  // Issue #552: bones, constraints and motion.tracks entry by entry; a hand edit the recipe would drop is refused by name.
1381
1404
  // Issue #571: with --from, the same parts drafted a third time with the previous recipe.
1382
1405
  const previous = previousCloth === null ? null : proposeWithBasis(P, joints, wristSnap, gridRule, previousCloth).proposal;
1383
- const edits = clothConfigEdits(drafted, proposeWithBasis(P, joints, wristSnap, gridRule, null).proposal, JSON.parse(text) as unknown, false, previous);
1384
- 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>) };
1406
+ const edits = clothConfigEdits(drafted, plain, JSON.parse(text) as unknown, false, previous);
1407
+ return { at: writeArg, input: text, text: writtenClothConfig(clothArg, text, edits), paths: editPaths(edits), vouched: previousCount(edits), relative: relativeConfigPaths(dirname(resolve(clothArg)), JSON.parse(text) as Record<string, unknown>) };
1385
1408
  })();
1409
+ // Issue #572: the keyed chains CHAIN_LAG reads only once the recipe took its trees' idle keys — in the config written
1410
+ // against the --cloth config with --write-config, else in the proposal against the draft without the recipe.
1411
+ const leftBehind: { findings: DraftLintFinding[]; unread: string | null } = (() => {
1412
+ if (plain === null) return { findings: [], unread: null };
1413
+ if (written === null) return { findings: chainsLeftBehind(proposalAsConfig(P, plain), proposalAsConfig(P, prop)), unread: null };
1414
+ const read = (what: string, text: string): CharacterConfig | string => {
1415
+ try {
1416
+ return proposalAsConfig(P, JSON.parse(text) as Record<string, unknown>);
1417
+ } catch (err) {
1418
+ if (!(err instanceof PartsError)) throw err;
1419
+ return `${what} does not load as a config (${err.problems[0].code}: ${err.problems[0].object})`;
1420
+ }
1421
+ };
1422
+ const before = read(String(clothArg), written.input);
1423
+ const after = read(written.at, written.text);
1424
+ if (typeof before === 'string') return { findings: [], unread: before };
1425
+ if (typeof after === 'string') return { findings: [], unread: after };
1426
+ return { findings: chainsLeftBehind(before, after), unread: null };
1427
+ })();
1386
1428
  const bfile = basisFile(P, prop, basis, cfg === null || cmpPath === undefined ? null : { where: cmpPath, config: cfg });
1387
1429
  const img = drawLandmarks(P, painting, prop.bones, 'PROPOSAL - CORRECT ME');
1388
1430
  mkdirSync(render, { recursive: true });
@@ -1396,7 +1438,8 @@ function cmdPropose(args: string[]): number {
1396
1438
  }
1397
1439
  console.log(`wrote proposal.json (${prop.bones.length} bone entries, ${Object.keys(prop.meshes).length} meshes) and render/landmarks.png (+_head) under ${out}`);
1398
1440
  for (const n of prop.notes) console.log(`note: ${n}`);
1399
- printLint(P, prop, joints, draftLint);
1441
+ if (leftBehind.unread !== null) console.log(`note: the keyed chains were not read after the recipe (issue #572): ${leftBehind.unread}`);
1442
+ printLint(P, prop, joints, [...draftLint, ...leftBehind.findings]);
1400
1443
  console.log(basisLine(bfile, out));
1401
1444
  if (written !== null && clothArg !== undefined) {
1402
1445
  const from = fromArg === undefined ? '' : `; --from ${fromArg}: ${written.vouched} ${written.vouched === 1 ? 'entry' : 'entries'} taken as the previous recipe's draft`;
@@ -1789,6 +1832,18 @@ function cmdMeshDraft(args: string[]): number {
1789
1832
  }
1790
1833
  }
1791
1834
 
1835
+ /** `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. */
1836
+ function cmdMeshStats(args: string[]): number {
1837
+ const f = flags(args, 'mesh-stats', ['--config', '--parts']);
1838
+ if (typeof f === 'string') return usage(f);
1839
+ try {
1840
+ meshStatsStage({ config: f.get('--config') as string, parts: f.get('--parts') as string }, console.log);
1841
+ return EXIT_OK;
1842
+ } catch (err) {
1843
+ return printRefusal(err);
1844
+ }
1845
+ }
1846
+
1792
1847
  /** `remesh` (issue #495): one part of a gated rig re-fitted under a requested mesh entry, into a new revision. */
1793
1848
  function cmdRemesh(args: string[]): number {
1794
1849
  const f = flags(args, 'remesh', ['--rig', '--config', '--parts', '--part', '--out']);
@@ -1937,6 +1992,7 @@ function main(argv: string[]): number | Promise<number> {
1937
1992
  if (command === 'build') return cmdBuild(rest);
1938
1993
  if (command === 'compose') return cmdCompose(rest);
1939
1994
  if (command === 'mesh-draft') return cmdMeshDraft(rest);
1995
+ if (command === 'mesh-stats') return cmdMeshStats(rest);
1940
1996
  if (command === 'remesh') return cmdRemesh(rest);
1941
1997
  if (command === 'retune') return cmdRetune(rest);
1942
1998
  if (command === 'painting') return cmdPainting(rest);