rig-parts 1.2.0 → 1.4.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/README.md CHANGED
@@ -306,8 +306,10 @@ These are limits of the approach, stated so nobody reads more into a green run:
306
306
  layer that is in front of another in one place and behind it in another is drawn
307
307
  right in the still (the painting's own pixels are projected onto it) and wrong once
308
308
  it moves.
309
- - **No expression or lip-sync.** The mouth is one layer; there is no mouth-shape set
310
- and no expression axis.
309
+ - **No expression or lip-sync, and no jiggle.** The mouth is one layer; there is no
310
+ mouth-shape set and no expression axis, and nothing here proposes, keys or judges a
311
+ soft region. An expression that moves the face's parts, and a jiggle, can be built on
312
+ the rig it writes ([below](#what-can-be-built-on-top)); lip-sync cannot.
311
313
  - **Occluded pixels are See-through's synthesis**, not the artist's. How much of a rig
312
314
  that is, is below.
313
315
  - **No success rate is claimed.** Ten characters have been measured stage by stage
@@ -331,6 +333,25 @@ These are limits of the approach, stated so nobody reads more into a green run:
331
333
  measures is a hanging strand on a headwear or earwear layer: each gets a pendulum
332
334
  chain, and a strand it cannot chain is named in a note rather than left stiff.
333
335
 
336
+ ### What can be built on top
337
+
338
+ This package makes no expression and no jiggle, but the rig it writes is the material for
339
+ both, and the config has a field for each. [docs/AUTHORING.md](docs/AUTHORING.md) has a recipe
340
+ for each — a config fragment, the `check` lines it passes with, and its limit:
341
+
342
+ - [**Expressions and other named animations**](docs/AUTHORING.md#recipe-expressions-and-other-named-animations):
343
+ the face's parts are bones; animations written over them in rig-c's motion-spec shape and
344
+ named by `motion.animations_from` are built and gated beside the idle on every build. One
345
+ attachment per slot, so a part moves, turns and scales but is never swapped for another
346
+ image; `check` measures the idle alone.
347
+ - [**A soft region driven by physics**](docs/AUTHORING.md#recipe-a-soft-region-driven-by-physics)
348
+ (a bust, the hips): a bone the config authors, a contour-mode region that hands it the patch,
349
+ and a `physics` constraint on it; `examples/sample` carries one. Under the sample's slow
350
+ 4 s idle the bust moves at most 0.066 rig px and the hips not at all: the spring answers
351
+ faster keys on the body. The sample's `hop`, a named animation keying only `hip`, moves the
352
+ bust up to 6.583 and the hips up to 4.432 rig px; the region's `band` caps how far a patch may
353
+ go before the texture-stretch ceiling, and `check` measures the idle alone.
354
+
334
355
  ### How much of a rig the model painted
335
356
 
336
357
  `parts.json` splits each part's opaque pixels (`opaque_px`) into `visible_px` — no
package/cli.ts CHANGED
@@ -205,7 +205,7 @@ usage:
205
205
  rounding, and so is the per-frame mesh work (AUTHORING §5).
206
206
  rig-parts check --rig <dir> --out <dir> [--parts <dir>] [--source <painting.png>]
207
207
  [--page-edges pot|free] [--pack-shape rect|polygon]
208
- [--requirements <file.json>]
208
+ [--requirements <file.json>] [--seam-pairs]
209
209
  Build, gate, render and measure a rig through rig-c's CLI (the rigc at
210
210
  node_modules/.bin/rigc, or on PATH). --rig holds rig.json and motion.json:
211
211
  a rig spec, which rigc build compiles, not a compiled skeleton.json.
@@ -222,7 +222,11 @@ usage:
222
222
  entry that gated the build, and pack_mode, the two pack flags). PASS needs every gate summary
223
223
  "0 failed", the seam (setup pose vs the flat composite of parts/) at mean
224
224
  |d| <= ${SEAM_MEAN_BAR.toFixed(1)} with <= ${SEAM_PX_BAR} px over ${SEAM_PX_LEVEL}, and the loop (idle frame 0 vs the
225
- frame at t = duration) at max |d| 0. Then six judgement lines, each in
225
+ frame at t = duration) at max |d| 0. A failing seam, or any seam under
226
+ --seam-pairs, is split by the pair of parts each counted pixel lies
227
+ between: one "seam pair" line each, worst first, with its count, frame
228
+ and rig boxes and a picture under seam/, and check.json's seam_pairs
229
+ (AUTHORING §7). Then six judgement lines, each in
226
230
  check.json and on the console as NAME: PASS|FAIL|SKIP with its figures and
227
231
  bars (AUTHORING §7): BREATH_VISIBLE (the topwear moves, the footwear does
228
232
  not, each rendered alone), BLINK_NO_HOLE (the setup pose with the blink
@@ -326,14 +330,18 @@ usage:
326
330
  <out>/render/recomposite_rig.png and <out>/render/${ERROR_MAP_FILE} (the error
327
331
  map: uncovered error px red, covered error px blue, the rest the painting in
328
332
  light grey). Reads config.seethrough.head_box and
329
- .resolution and config.assemble.rig_scale, .plan, .extend_below_crop and
333
+ .resolution and config.assemble.rig_scale, .plan, .extend_below_crop,
330
334
  .patches — extra parts cut from the painting itself over a rig-pixel box
331
335
  (alpha "silhouette": the painting's figure inside it; "box": all of it),
332
336
  drawn "back", "front" or {"before": <plan part>}, recorded in parts.json
333
- as from "painting:<name>" and counted 100 % source — and no rig section:
337
+ as from "painting:<name>" and counted 100 % source — and .cuts — a plan
338
+ part's pixels inside a rig-pixel polygon taken into a new part, drawn the
339
+ same way, recorded with the plan part's from — and no rig section:
334
340
  bones, meshes, regions and motion need not exist yet, because propose
335
341
  drafts them from these parts (AUTHORING §4, the table).
336
- Prints one line per part, the seam override counts, the \`pixels:\` totals
342
+ Prints one line per part, one \`cut:\` line per part a cut split (its
343
+ opaque pixels = what it kept + what each cut took), the seam override
344
+ counts, the \`pixels:\` totals
337
345
  (opaque = visible + occluded; taken from the painting; visible but not
338
346
  projected), and \`recomposite vs source\` (mean |d| and % within 8 over the
339
347
  mean channel; error px: max channel > 40; uncovered: of those, where no
@@ -421,9 +429,14 @@ usage:
421
429
  Bind several finished characters into one rig. The scene file (spec
422
430
  "${SCENE_SPEC}") states the canvas {width, height}, an optional plate
423
431
  {image, provenance: ${PROVENANCES.join(' | ')}, note?}, the characters
424
- [{id, build, offset: [x, y]}] — each build the --out of a green
432
+ [{id, build, offset: [x, y], scale?}] — each build the --out of a green
425
433
  \`rig-parts build\` (its check/check.json PASS), each offset a
426
- translation from its rig px to canvas px — and order: the draw order,
434
+ translation from its rig px to canvas px, each scale (a positive
435
+ number, 1 when absent) the character's size about its stage's top-left
436
+ corner: every length it carries is scaled as Spine's loader scales a
437
+ skeleton (bones, regions, weight binds, constraint lengths, translate
438
+ keys), and a physics wind or gravity, which no per-character value
439
+ can scale, is refused (SCENE_SCALE_EXACT) — and order: the draw order,
427
440
  back to front, of character ids (that character's remaining slots, in
428
441
  its own order) and "<id>:<slot>" entries, every slot exactly once.
429
442
  Paths are relative to the scene file. Every bone, slot, attachment,
@@ -435,7 +448,9 @@ usage:
435
448
  shared, so a character whose idle keys it, or whose constraint names it,
436
449
  is refused. The plate is a region on bone "${PLATE}" under
437
450
  root, drawn first, its image at canvas (0, 0). One idle holds every
438
- character's tracks. Refused by name, every problem at once: an id
451
+ character's tracks; an animation beside a character's idle (its
452
+ config's motion.animations_from) rides along as "<id>:<name>",
453
+ prefixed as the idle is and scheduled by nothing. Refused by name, every problem at once: an id
439
454
  repeated, empty or holding ":" (or "/", "\\", a leading "."); a build
440
455
  missing a file or whose own check is not green; characters built at
441
456
  different rig_scale; a character whose parts' boxes, placed, leave the
@@ -447,7 +462,7 @@ usage:
447
462
  rig-c as rig gates its own (build --profile spine-html --pack, the
448
463
  compile and the packed pages on disk); --out receives rig/ (rig.json,
449
464
  motion.json, images/) and ${SCENE_REPORT_FILE} (spec "${SCENE_REPORT_SPEC}":
450
- every setting, each character's offset, bounds and shift (and what it
465
+ every setting, each character's offset, scale, bounds and shift (and what it
451
466
  moved),
452
467
  the plate's provenance, the declared and the composed order) only when
453
468
  it is green; then check runs over rig/ into check/, as on a rig it did
@@ -682,7 +697,15 @@ function flags(args: string[], known: readonly string[], command: string, option
682
697
  }
683
698
 
684
699
  function cmdCheck(args: string[]): number {
685
- const f = flags(args, ['--rig', '--out'], 'check', ['--parts', '--source', '--page-edges', '--pack-shape', '--requirements']);
700
+ // The one switch check takes (issue #202): it carries no value, so it is taken out before the valued flags are read.
701
+ const seamPairs = args.filter((a) => a === '--seam-pairs').length;
702
+ if (seamPairs > 1) return usage('--seam-pairs is given twice');
703
+ const f = flags(
704
+ args.filter((a) => a !== '--seam-pairs'),
705
+ ['--rig', '--out'],
706
+ 'check',
707
+ ['--parts', '--source', '--page-edges', '--pack-shape', '--requirements'],
708
+ );
686
709
  if (typeof f === 'string') return usage(f);
687
710
  const rig = f.get('--rig') as string;
688
711
  const out = f.get('--out') as string;
@@ -692,7 +715,12 @@ function cmdCheck(args: string[]): number {
692
715
  if (!isPackShape(shape)) return usage(shape);
693
716
  try {
694
717
  const bin = findRigc(import.meta.dir, process.env.PATH ?? '');
695
- const r = checkStage({ rig, parts: f.get('--parts'), source: f.get('--source'), out, pageEdges: edges, packShape: shape, requirements: f.get('--requirements') }, rigcRunner(bin), bin, console.log);
718
+ const r = checkStage(
719
+ { rig, parts: f.get('--parts'), source: f.get('--source'), out, pageEdges: edges, packShape: shape, requirements: f.get('--requirements'), seamPairs: seamPairs === 1 },
720
+ rigcRunner(bin),
721
+ bin,
722
+ console.log,
723
+ );
696
724
  return r.figures.PASS ? EXIT_OK : EXIT_REFUSED;
697
725
  } catch (err) {
698
726
  return printRefusal(err);