rig-parts 2.2.0 → 2.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rig-parts",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
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": {
package/src/config.ts CHANGED
@@ -1305,8 +1305,6 @@ export interface CharacterConfig {
1305
1305
  cloth?: ClothRecipes;
1306
1306
  /** The `assemble.rig_scale` (`parts.json`'s `scale_rig_per_source`) `propose` drafted the rig sections at (issue #354): absent on a hand-authored config, held equal to `assemble.rig_scale` when present. */
1307
1307
  proposed_rig_scale?: number;
1308
- /** Alternate painting sets (issue #454): absent, no alternate sets are assembled or emitted. */
1309
- alternates?: AlternateSetConfig[];
1310
1308
  /**
1311
1309
  * The requirements file the build's check reads (issue #556), a path relative
1312
1310
  * to the config's directory: `build` reads it when no `--requirements` is
@@ -1649,7 +1647,14 @@ export interface AssembleConfig {
1649
1647
  proposed_rig_scale?: number;
1650
1648
  }
1651
1649
 
1652
- const TOP_KEYS = ['key', 'generation', 'seethrough', 'assemble', 'limbs', 'bones', 'meshes', 'regions', 'motion', 'constraints', 'stops', 'turn', 'scene', 'cloth', 'proposed_rig_scale', 'requirements', 'motion_seams', 'depth', 'waist'] as const;
1650
+ /**
1651
+ * The top-level sections `parseConfig` accepts in `config.json` (issue #948),
1652
+ * exported as a runtime tuple so a consumer does not read `src/config.ts` as text.
1653
+ */
1654
+ export const CONFIG_SECTIONS = ['key', 'generation', 'seethrough', 'assemble', 'limbs', 'bones', 'meshes', 'regions', 'motion', 'constraints', 'stops', 'turn', 'scene', 'cloth', 'proposed_rig_scale', 'requirements', 'motion_seams', 'depth', 'waist'] as const;
1655
+ export type ConfigSection = (typeof CONFIG_SECTIONS)[number];
1656
+
1657
+ const TOP_KEYS = CONFIG_SECTIONS;
1653
1658
 
1654
1659
  /**
1655
1660
  * The one partial entry point. It validates exactly what its door requires —
package/src/rig.ts CHANGED
@@ -1336,7 +1336,7 @@ export function buildWaistPairSource(
1336
1336
  const CX = W / 2;
1337
1337
  const byPart = new Map(parts.parts.map((p) => [p.name, p]));
1338
1338
 
1339
- const configuredAlts = cfg.assemble.alternates ?? cfg.alternates ?? [];
1339
+ const configuredAlts = cfg.assemble.alternates ?? [];
1340
1340
  const anchorRegions = new Set(Object.keys(cfg.regions).filter(n => !(n in cfg.meshes)));
1341
1341
  for (const a of configuredAlts) {
1342
1342
  if (a.base_anchor === undefined) continue;
@@ -1354,7 +1354,7 @@ export function buildWaistPairSource(
1354
1354
  if (JSON.stringify(configured === undefined ? null : orderedBaseAnchor(configured)) !== JSON.stringify(a.base_anchor === undefined ? null : orderedBaseAnchor(a.base_anchor))) fail('RIG_ALT_BASE_ANCHOR_MATCH_CONFIG', `alternate set "${a.name}" base_anchor`, 'config and parts.json declare different base anchors; re-run assemble before rig');
1355
1355
  problems.push(...recordedFocusProblems(a, `parts.json alternate set "${a.name}".focus`, parts.rig_size, parts.scale_rig_per_source));
1356
1356
  }
1357
- for (const a of cfg.assemble.alternates ?? cfg.alternates ?? []) {
1357
+ for (const a of cfg.assemble.alternates ?? []) {
1358
1358
  const record = parts.alternates?.find((p) => p.name === a.name);
1359
1359
  if (a.base_anchor !== undefined && record === undefined) fail('RIG_ALT_BASE_ANCHOR_MATCH_CONFIG', `alternate set "${a.name}" base_anchor`, 'has no assembled set; re-run assemble before rig');
1360
1360
  if ((a.focus !== undefined || record?.focus !== undefined)
@@ -1375,7 +1375,7 @@ export function buildWaistPairSource(
1375
1375
  }
1376
1376
  }
1377
1377
  refuseIfAny(problems);
1378
- for (const a of cfg.assemble.alternates ?? cfg.alternates ?? []) {
1378
+ for (const a of cfg.assemble.alternates ?? []) {
1379
1379
  const record = parts.alternates?.find((p) => p.name === a.name);
1380
1380
  if (a.tips === undefined) {
1381
1381
  if (record?.tips !== undefined) {
@@ -2738,7 +2738,6 @@ export function buildWaistPairSource(
2738
2738
  if (parts.alternates !== undefined && parts.alternates.length > 0) {
2739
2739
  const setEntries: Record<string, AlternateSetMap> = {};
2740
2740
  for (const [altIdx, alt] of parts.alternates.entries()) {
2741
- const cfgAlt = cfg.alternates?.find((a) => a.name === alt.name);
2742
2741
  for (const [bone, tip] of Object.entries(alt.tips ?? {})) {
2743
2742
  if (bone === ROOT_BONE || !B.has(bone)) fail('RIG_NAME_RESOLVES', `parts.json alternates[${altIdx}].tips.${bone}`, `names "${bone}", which config.bones does not declare; re-run assemble with declared bone names`);
2744
2743
  if (!Array.isArray(tip) || tip.length !== 2 || !tip.every((n) => typeof n === 'number' && Number.isFinite(n))) {
@@ -2753,8 +2752,8 @@ export function buildWaistPairSource(
2753
2752
  }
2754
2753
  const altFront = new Set(alt.front ?? []);
2755
2754
  refuseIfAny(problems);
2756
- const explicitDrawn = alt.drawn ?? cfgAlt?.drawn;
2757
- const isSubsetSet = alt.parts !== undefined || cfgAlt?.parts !== undefined;
2755
+ const explicitDrawn = alt.drawn;
2756
+ const isSubsetSet = alt.parts !== undefined;
2758
2757
  let coveredBones: Set<string> | null = null;
2759
2758
  if (isSubsetSet) {
2760
2759
  coveredBones = new Set<string>();
@@ -2776,7 +2775,7 @@ export function buildWaistPairSource(
2776
2775
  if (explicitDrawn !== undefined) {
2777
2776
  for (const boneName of Object.keys(explicitDrawn)) {
2778
2777
  if (!B.has(boneName)) {
2779
- fail('RIG_NAME_RESOLVES', `config.alternates[${altIdx}].drawn.${boneName}`, `names the bone "${boneName}", which config.bones does not declare`);
2778
+ fail('RIG_NAME_RESOLVES', `parts.json alternates[${altIdx}].drawn.${boneName}`, `names the bone "${boneName}", which config.bones does not declare`);
2780
2779
  } else if (coveredBones !== null && !coveredBones.has(boneName)) {
2781
2780
  const coveredList = [...coveredBones].map((b) => `"${b}"`).join(', ') || 'none';
2782
2781
  fail(