rig-parts 1.52.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,11 +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
33
  import { meshStatsStage } from './src/meshstats.ts';
34
- import { clothConfigEdits, editPaths, previousCount, writtenClothConfig } from './src/clothconfig.ts';
34
+ import { chainsLeftBehind, clothConfigEdits, editPaths, previousCount, writtenClothConfig } from './src/clothconfig.ts';
35
35
  import { PROVENANCE_FILE, REMESH_SPEC, remeshStage } from './src/remesh.ts';
36
36
  import { RETUNE_FILE, RETUNE_SPEC, retuneStage } from './src/retune.ts';
37
37
  import { checkPainting } from './src/paintcheck.ts';
@@ -224,8 +224,12 @@ usage:
224
224
  ({across, max, shape}, issue #518) drafts the parts it meshes in the
225
225
  influence mode at their lattice's cell, the radius as stated or drafted
226
226
  as the farthest lattice vertex from its nearest link plus 3 cells, and
227
- a LINT line names each drafted radius and both figures. Without --cloth
228
- 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.
229
233
 
230
234
  rig-parts propose … --cloth <config.json> [--write-config <out.json> [--from <previous.json>]]
231
235
  Also write the --cloth config's own text with what the recipe drafts
@@ -1388,18 +1392,39 @@ function cmdPropose(args: string[]): number {
1388
1392
  : withTurn(drafted, draftTurn(P, { ...(fullRun === undefined ? {} : { full: readLayers(fullRun) }), ...(headRun === undefined ? {} : { head: readLayers(headRun) }) }, drafted));
1389
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.
1390
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;
1391
1397
  // Issue #537: the sections the recipe moves, measured against the same parts drafted without it, spliced before anything is written.
1392
1398
  const written =
1393
- writeArg === undefined || clothArg === undefined
1399
+ writeArg === undefined || clothArg === undefined || plain === null
1394
1400
  ? null
1395
1401
  : (() => {
1396
1402
  const text = readFileSync(clothArg, 'utf8');
1397
1403
  // Issue #552: bones, constraints and motion.tracks entry by entry; a hand edit the recipe would drop is refused by name.
1398
1404
  // Issue #571: with --from, the same parts drafted a third time with the previous recipe.
1399
1405
  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>) };
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>) };
1402
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
+ })();
1403
1428
  const bfile = basisFile(P, prop, basis, cfg === null || cmpPath === undefined ? null : { where: cmpPath, config: cfg });
1404
1429
  const img = drawLandmarks(P, painting, prop.bones, 'PROPOSAL - CORRECT ME');
1405
1430
  mkdirSync(render, { recursive: true });
@@ -1413,7 +1438,8 @@ function cmdPropose(args: string[]): number {
1413
1438
  }
1414
1439
  console.log(`wrote proposal.json (${prop.bones.length} bone entries, ${Object.keys(prop.meshes).length} meshes) and render/landmarks.png (+_head) under ${out}`);
1415
1440
  for (const n of prop.notes) console.log(`note: ${n}`);
1416
- 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]);
1417
1443
  console.log(basisLine(bfile, out));
1418
1444
  if (written !== null && clothArg !== undefined) {
1419
1445
  const from = fromArg === undefined ? '' : `; --from ${fromArg}: ${written.vouched} ${written.vouched === 1 ? 'entry' : 'entries'} taken as the previous recipe's draft`;