spine-rigc 0.35.1 → 0.36.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/docs/AUTHORING.md CHANGED
@@ -1103,12 +1103,14 @@ names the same comparator puts ahead of it — `default` before `2`, `default`
1103
1103
  before `A` — so renaming a skin away from `default` makes it an ordinary name that
1104
1104
  sorts like one.
1105
1105
 
1106
- **R12 — A placeholder that more than one skin fills gets a per-skin attachment
1107
- `name`, and the `default` skin may not be one of those skins.** rigc writes
1108
- `"name": "<skin>/<placeholder>"` on each of those entries and restates `path`
1109
- beside it so the texture still resolves where it did; you do not author that and
1110
- there is nothing to do about it, but it is visible in the emitted file, so §3.4.2
1111
- says what it is and why. What you *do* author is where the shared art lives: a
1106
+ **R12 — An attachment's `name` is written exactly when the spec states it, and
1107
+ the `default` skin may not share a placeholder with a named skin.** rigc derives
1108
+ no name. A placeholder several skins fill is emitted under that placeholder in
1109
+ each skin, carrying the `name` each entry states and none where it states none —
1110
+ which is the shape the editor itself exports (§3.4.2). Until
1111
+ [#796](https://github.com/firejune/rigc/issues/796) rigc composed
1112
+ `"<skin>/<placeholder>"` there, which renamed every such attachment at runtime.
1113
+ What you *do* author is where the shared art lives: a
1112
1114
  placeholder the `default` skin shares with a named skin is a **compile error**,
1113
1115
  because the Spine editor has no representation for it in either spelling (#567,
1114
1116
  measured on 4.3.26 — named, the export re-keys it and the default skin draws
@@ -1332,25 +1334,26 @@ the default `type`:
1332
1334
  | Field | Meaning |
1333
1335
  | --- | --- |
1334
1336
  | `type` | `"region"`, or omit |
1337
+ | `name` | the attachment's **own name**, when it is not the placeholder: what the runtime calls it (`Attachment.name`, which is what `slot.attachment.name` returns) and what `path` defaults to. Optional — absent, the runtime names the attachment by its placeholder. Written **verbatim**, as the attachment's first key, exactly when you state it; rigc never makes one up (R12, §3.4.2). Nothing resolves an attachment *by* it: a slot's setup `attachment`, every timeline and a linked mesh's `source` use the placeholder. Every type takes it. Not a string is refused — `"name" is 7, which is not a string` |
1335
1338
  | `image` | **rigc extension.** A PNG relative to the rig's `images` directory; rigc measures it (R5) |
1336
1339
  | `width`, `height` | required by the format — give them, or give an `image` |
1337
- | `path` | the atlas region to resolve; defaults to the attachment's own name. rigc sets it for you when the PNG basename differs from the placeholder, and whenever it composes a `name` because more than one skin fills this placeholder (§3.4.2). **One rule, both kinds** — see the note under *Mesh attachment* |
1340
+ | `path` | the atlas region to resolve; defaults to the attachment's name — its `name`, else the placeholder. rigc sets it for you when the PNG basename differs from that name. **One rule, both kinds** — see the note under *Mesh attachment* |
1338
1341
  | `x`, `y` | offset from the bone, in the bone's local space |
1339
1342
  | `rotation` | degrees; cancels a rotated bone for a plate authored screen-upright |
1340
1343
  | `scaleX`, `scaleY`, `color` | as Spine |
1341
- | `sequence` | a **numbered image series** instead of one region: `{ "count", "start"?, "digits"?, "setup"? }`, and `path` (or the placeholder) is the series' stem. No `image` beside it — the frames are the images. §3.4.3 |
1344
+ | `sequence` | a **numbered image series** instead of one region: `{ "count", "start"?, "digits"?, "setup"? }`, and `path` (or, with none, the `name`, or the placeholder) is the series' stem. No `image` beside it — the frames are the images. §3.4.3 |
1342
1345
 
1343
1346
  **Mesh attachment** ([Spine: meshes](http://esotericsoftware.com/spine-meshes)) —
1344
1347
  either authored geometry (`uvs` + `triangles` + geometry) **or** a `generator`,
1345
1348
  never both. `hull`, `edges`, `width` and `height` may be stated; whichever is
1346
1349
  omitted, rigc derives — `hull` and `edges` from the triangles, the size from the
1347
- PNG — and the rules are a few paragraphs down. `type`, `image`, `path`, `color` and
1348
- `sequence` mean exactly what they mean on a region (a sequence mesh takes authored
1350
+ PNG — and the rules are a few paragraphs down. `type`, `name`, `image`, `path`,
1351
+ `color` and `sequence` mean exactly what they mean on a region (a sequence mesh takes authored
1349
1352
  geometry, never a `generator` — §3.4.3).
1350
1353
 
1351
1354
  🔑 **`path` is one rule for both kinds.** A mesh derives it from `image` the way a
1352
1355
  region does: stated wins, otherwise the PNG's basename when that differs from the
1353
- placeholder, otherwise nothing. The parser reads `path` off both with the same line
1356
+ attachment's name — its `name`, else the placeholder — otherwise nothing. The parser reads `path` off both with the same line
1354
1357
  (`getValue(map, "path", name)`, `SkeletonJson.ts:541` and `:570`), and `path`
1355
1358
  defaults to the attachment's **name** rather than to the placeholder — so a mesh
1356
1359
  with `image: hair_short.png` under a placeholder called `hair` resolves the region
@@ -1515,11 +1518,11 @@ them (`SkeletonJson.ts:568-569`, `:582`), so rigc reads them the same way.
1515
1518
 
1516
1519
  | Field | Meaning |
1517
1520
  | --- | --- |
1518
- | `source` | **required.** The **placeholder** of the mesh whose geometry this one draws — the key it is filed under in its skin, not its `name`. A miss is refused naming the skin, the slot and what that slot holds |
1521
+ | `source` | **required.** The **placeholder** of the mesh whose geometry this one draws — the key it is filed under in its skin, not its `name`. A miss is refused naming the skin, the slot and what that slot holds; a `source` that spells the source's `name` instead is that miss, because the runtime looks the source up by skin, slot and key and never by name — measured, it throws `Source mesh not found` |
1519
1522
  | `slot` | the slot the source lives in. Default: **this attachment's own slot**. Resolved by name against the rig's slots |
1520
1523
  | `skin` | the skin the source lives in. Default: **`default`** — the default skin, *not* the skin this link is written in. Resolved by name |
1521
1524
  | `timelines` | default **`true`**: the link plays the source's `deform` keys. `false` makes it its own timeline target, so only keys written against the link move it |
1522
- | `image`, `path`, `width`, `height`, `color` | exactly as on a mesh — the link resolves **its own** region, which is the point of the type |
1525
+ | `name`, `image`, `path`, `width`, `height`, `color` | exactly as on a mesh — the link resolves **its own** region, which is the point of the type |
1523
1526
 
1524
1527
  ```json
1525
1528
  "skins": {
@@ -1605,6 +1608,7 @@ region, a trigger volume — that moves with the skeleton and draws nothing.
1605
1608
  | Field | Meaning |
1606
1609
  | --- | --- |
1607
1610
  | `type` | `"boundingbox"`. **Required**: an omitted `type` is `"region"`, and a region has nowhere to put these keys — measured, the refusal reads `attachment "mask" (region) has 2 keys this compiler does not read: "vertexCount", "vertices"` |
1611
+ | `name` | as on a region: the runtime's name for the attachment, written only when stated. A polygon resolves no region, so here it is the name and nothing else |
1608
1612
  | `vertexCount` | **required**, 3 or more. No default — the paragraph above is why |
1609
1613
  | `vertices` | the unweighted `x, y` run, in the slot bone's local space; or the index-encoded weighted run, behind `boneIndexing` |
1610
1614
  | `weights` | the by-name form: one entry per vertex, each a list of `{ "bone": …, "x": …, "y": …, "weight": … }`, each pair in that bone's local space. Never beside `vertices` |
@@ -1619,7 +1623,7 @@ attachment rather than a second set of art.
1619
1623
  | Field | Meaning |
1620
1624
  | --- | --- |
1621
1625
  | `type` | `"clipping"`. **Required** |
1622
- | `vertexCount`, `vertices`, `weights`, `boneIndexing`, `color` | exactly as on a bounding box |
1626
+ | `name`, `vertexCount`, `vertices`, `weights`, `boneIndexing`, `color` | exactly as on a bounding box |
1623
1627
  | `end` | the last slot the clip applies to, **by name**. Absent is the parser's own encoding for *clip everything after this one*, which is why a typo cannot be told from an omission once the file is loaded: `findSlot` returns null on a miss and the parser assigns that null without a word, so the clip runs to the bottom of the draw order and takes every slot below it with it. rigc refuses a name the rig does not declare — `end names slot "X", which this rig does not declare` — and `A33` refuses it again on a skeleton rigc did not write |
1624
1628
  | `convex` | default **false**. True tells the runtime the polygon is convex so it can clip without triangulating it, and a polygon that deforms concave is clipped by its convex hull instead (`ClippingAttachment.convex`). Nothing here checks that the polygon is in fact convex |
1625
1629
  | `inverse` | default **false**. True makes everything **outside** the polygon visible instead of everything inside, and inverse clipping is always treated as convex (`ClippingAttachment.inverse`) |
@@ -1637,7 +1641,7 @@ other vertex attachment.
1637
1641
  | Field | Meaning |
1638
1642
  | --- | --- |
1639
1643
  | `type` | `"path"`. **Required** |
1640
- | `vertexCount`, `vertices`, `weights`, `boneIndexing`, `color` | as on a bounding box — except that these vertices are knots **and** their handles, which the count rule below is about |
1644
+ | `name`, `vertexCount`, `vertices`, `weights`, `boneIndexing`, `color` | as on a bounding box — except that these vertices are knots **and** their handles, which the count rule below is about |
1641
1645
  | `closed` | default **false**. True joins the last knot back to the first |
1642
1646
  | `constantSpeed` | default **true** — note the direction. Leaving it out asks for the expensive-and-correct traversal, in which the runtime re-measures the path every frame and `lengths` is never read. `false` makes the runtime trust the emitted `lengths` instead: cheaper, exact only while the path holds its setup shape, and the reason a deformed path wants the default |
1643
1647
  | `lengths` | 🚫 **refused by name.** rigc measures the setup length of each curve off the geometry and emits it, the way it measures a region's size off its PNG: `"lengths" is not authored — rigc measures the setup arc length of each curve…`. The field is declared only so the refusal can say that rather than report a misspelt key. What the numbers are — and why *arc length* is the wrong name for them — is §10.6 |
@@ -2312,41 +2316,64 @@ beside them is refused rather than ignored (an ignored slot is an attachment tha
2312
2316
  vanishes), and a rig with a *slot* of one of those names is refused too, because
2313
2317
  there the two forms are genuinely ambiguous. Rename the slot.
2314
2318
 
2315
- #### 3.4.2 Two skins, one placeholder — the `name` rigc writes for you
2319
+ #### 3.4.2 Two skins, one placeholder — and the `name` you may state
2316
2320
 
2317
- Two skins putting different art under one placeholder is what a skin is *for*, and
2318
- it is the one shape rigc emitted wrongly until
2319
- [#541](https://github.com/firejune/rigc/issues/541). Without a `name` field an
2320
- attachment's name **is** its placeholder (`SkeletonJson.ts:526`), so four skins
2321
- filling `patch` are four different attachments all called `patch`. spine-core never
2322
- notices — its skin table is keyed by placeholder, so the two never meet — and the
2323
- whole gate is green. The Spine editor refuses the import outright:
2324
-
2325
- ```
2326
- ERROR: Unable to import skeleton.
2327
- [error] Error reading skeleton: skins
2328
- Cause: [error] Error reading attachment: patch (MOw)
2329
- Cause: [error] Multiple attachments have the same name: patch patch
2330
- ```
2331
-
2332
- ⇒ rigc now writes each of those entries a name of its own, composed from the two
2333
- names you already gave it:
2321
+ Two skins putting different art under one placeholder is what a skin is *for*. Each
2322
+ entry is its own attachment, filed under that placeholder in its own skin, and with
2323
+ no `name` field the runtime names every one of them by the placeholder
2324
+ (`getValue(map, "name", placeholder)`, `SkeletonJson.js:526`). That is legal Spine
2325
+ data, and it is the shape the editor exports: rigc emits it exactly as you wrote it.
2334
2326
 
2335
2327
  ```json
2336
2328
  "skins": {
2337
2329
  "default": { "block": { "block": { "image": "block.png" } } },
2338
2330
  "base": { "patch": { "patch": { "image": "patch_a.png" } } },
2339
- "zulu": { "patch": { "patch": { "image": "patch_a.png", "x": 4 } } }
2331
+ "zulu": { "patch": { "patch": { "image": "patch_b.png", "x": 4 } } }
2340
2332
  }
2341
2333
  ```
2342
2334
 
2343
- emits, for slot `patch`
2335
+ emits two `patch` attachments in slot `patch`, one per skin, each with the `path` its
2336
+ image gives it (`patch_a`, `patch_b`) and **no `name`** — both answer to `patch` at
2337
+ runtime, and each is the one its skin shows.
2338
+
2339
+ 🔑 **State a `name` when the attachment's own name is not its placeholder.** A
2340
+ transcription of an export that carries one — `ingest` writes it for you — or any
2341
+ rig whose consumer reads `slot.attachment.name`. It is written verbatim, and `path`
2342
+ defaults to it, so an image named after it needs no `path`:
2344
2343
 
2345
2344
  ```json
2346
- { "name": "base/patch", "path": "patch", "width": 64, "height": 64 }
2347
- { "name": "zulu/patch", "path": "patch", "width": 64, "height": 64, "x": 4 }
2345
+ "base": { "patch": { "patch": { "name": "patch-base", "image": "patch-base.png" } } }
2348
2346
  ```
2349
2347
 
2348
+ emits `"name": "patch-base"` first and no `path`, and the runtime draws region
2349
+ `patch-base` for an attachment it calls `patch-base`, filed under `patch`.
2350
+
2351
+ 🔬 **Until [#796](https://github.com/firejune/rigc/issues/796) rigc composed a name
2352
+ here, and the reason it did was measured wrong.**
2353
+ [#541](https://github.com/firejune/rigc/issues/541) read the editor's refusal of a
2354
+ four-skin rig — `Multiple attachments have the same name: patch patch` — as "one
2355
+ name over several attachments, because a linked mesh resolves its parent by name",
2356
+ and [#552](https://github.com/firejune/rigc/issues/552) wrote
2357
+ `"<skin>/<placeholder>"` on every contested entry. Both halves fail a measurement:
2358
+
2359
+ - **A linked mesh finds its source by skin, slot and KEY.** Through spine-core
2360
+ 4.3.13, two skins each fill `C` with a different mesh and no `name`, both load
2361
+ named `C`, and a link with `source: "C"` binds the mesh of the skin its own
2362
+ `skin` names. A `source` that spells a source's `name` instead throws
2363
+ `Source mesh not found` — which rigc refuses by name before that (§3.4, *Linked
2364
+ mesh*).
2365
+ - **The editor imports the uncomposed shape.** A production rig whose placeholders
2366
+ are each filled by two named skins, meshes and linked meshes among them, imported
2367
+ through Spine 4.3.26 with every composed name stripped. It is the file that editor
2368
+ exported.
2369
+
2370
+ What #541 had bisected was a rig whose **default** skin filled the contested
2371
+ placeholder beside the named ones — its smallest refusing variant was `default` plus
2372
+ one named skin — and that shape is refused on measurements of its own, below. The
2373
+ composition renamed every contested attachment of every multi-skin export at
2374
+ runtime, which `slot.attachment.name` shows a consumer and no gate could see; `diff`
2375
+ now reads it as `attachments.runtime_name`.
2376
+
2350
2377
  🚨 **Every skin that shares a placeholder has to be a named one — the default
2351
2378
  skin may not be among them, and rigc refuses the rig if it is.** That is not a
2352
2379
  style rule; it is the editor's model, and two round trips through Spine
@@ -2377,18 +2404,9 @@ style rule; it is the editor's model, and two round trips through Spine
2377
2404
  are the same namespace, and both are `patch`.
2378
2405
 
2379
2406
  ⇒ **The rule: move the shared art into a named skin.** Call it `base`. Every
2380
- filler of that placeholder is then a named skin, rigc composes all of them, and
2381
- the names are unique within the slot — which is all
2382
- [#541](https://github.com/firejune/rigc/issues/541) needed: `base/patch`,
2383
- `zulu/patch` and `mike/patch` are three names. That shape is the one the editor
2384
- does hold: the same three fillers in named skins imported, exported and measured
2385
- **0.00 mean MAE** with names and paths intact.
2386
-
2387
- 📎 **The earlier reading, kept because it was reasonable and wrong.** Between the
2388
- two trips this guide said *compose off the default skin only* — keep the default
2389
- skin's entry as its placeholder and name the others. Trip 7 supported it and trip
2390
- 8 refuted it: that is the spelling the editor refuses at the door. There is no
2391
- third spelling, which is why this is a refusal rather than a naming scheme.
2407
+ filler of that placeholder is then a named skin, which is the shape the editor
2408
+ holds. Stating a `name` on the default skin's entry does not lift the refusal: that
2409
+ is the first of the two spellings above.
2392
2410
 
2393
2411
  What to know about it, and nothing to author:
2394
2412
 
@@ -2408,39 +2426,27 @@ What to know about it, and nothing to author:
2408
2426
  restated as the join the renderer actually performs it was a tautology over
2409
2427
  the resolve check beside it
2410
2428
  ([#574](https://github.com/firejune/rigc/issues/574)).
2411
- - **`path` is restated, and it has to be.** `path` defaults to the attachment's
2412
- **name**, not to its placeholder, so an entry given a name and no path would
2413
- resolve its texture at `zulu/patch` and find no such region.
2414
- `A08_REGION_NAMES_MATCH_ATTACHMENTS` says so if it is ever dropped, naming the
2415
- skin, the slot, the placeholder and the path
2416
- ([#589](https://github.com/firejune/rigc/issues/589)); `A00_ROUNDTRIP_PARSE`
2417
- reported it in the parser's own words until then, and now defers to A08.
2418
- - **Only contested placeholders are touched.** One skin filling a placeholder, or
2419
- two skins filling a slot under *different* placeholders, emit exactly what they
2420
- always did — every rig in this repository is byte-identical across the change.
2421
- - **A composed name that collides is a compile error, not a surprise.** If some
2422
- other placeholder in the same slot is literally called `zulu/patch`, rigc refuses
2423
- and names both sites rather than emitting two attachments with one name again.
2424
- The walk covers every *uncontested* entry's plain name too, the default skin's
2425
- included — a name that composed nothing can still be the one another skin
2426
- composes.
2427
- (`/` is the separator because it appears in **0** of the 160 placeholder names and
2428
- 159 atlas region names in `examples/` and `gallery/`, where `-` appears in 85 and
2429
- `_` in 37.)
2429
+ - **A stated `name` with no `path` resolves the region the name spells.** `path`
2430
+ defaults to the attachment's **name**, not to its placeholder, so an entry naming
2431
+ `patch-base` and drawing `patch_a.png` needs a `path` — and gets one, because the
2432
+ image basename differs from the name. `A08_REGION_NAMES_MATCH_ATTACHMENTS` names
2433
+ the skin, the slot, the placeholder and the path if the region is not there
2434
+ ([#589](https://github.com/firejune/rigc/issues/589)).
2430
2435
  - **Each skin's art is measured and atlased on its own.** The example above points
2431
- both skins at one PNG, so there is one region; point them at two and there are
2432
- two, each attachment's `path` resolving to the file that attachment named and its
2433
- `width`/`height` measured off that file. Until
2434
- [#555](https://github.com/firejune/rigc/issues/555) only the first skin's PNG was
2435
- ever opened, and the second skin's art reached neither the atlas nor the
2436
- measurement — so name the two files **distinctly**, because the region name is
2437
- the basename and `a/patch.png` beside `b/patch.png` is refused (R5).
2438
-
2439
- ⚠️ **The uniqueness scope is the slot, not the skeleton.** `spineboy-pro.json`,
2440
- which the editor wrote, gives the name `head` to a region in slot `head` and to a
2441
- bounding box in slot `head-bb`, and reuses `hoverglow-small` across eight slots. So
2442
- a name shared between slots is normal and rigc leaves it alone; what #541 refused
2443
- was one slot holding two.
2436
+ the two skins at two PNGs, so there are two regions, each attachment's `path`
2437
+ resolving to the file that attachment named and its `width`/`height` measured off
2438
+ that file. Until [#555](https://github.com/firejune/rigc/issues/555) only the
2439
+ first skin's PNG was ever opened, and the second skin's art reached neither the
2440
+ atlas nor the measurement — so name the two files **distinctly**, because the
2441
+ region name is the basename and `a/patch.png` beside `b/patch.png` is refused
2442
+ (R5).
2443
+
2444
+ ⚠️ **Nothing measured says the editor wants names unique within a slot.**
2445
+ `spineboy-pro.json`, which the editor wrote, gives the name `head` to a region in
2446
+ slot `head` and to a bounding box in slot `head-bb`, and reuses `hoverglow-small`
2447
+ across eight slots; the production rig above holds same-named attachments in one
2448
+ slot across its named skins. The one refusal the editor was measured making is the
2449
+ default-skin share, and that is the rule rigc enforces.
2444
2450
 
2445
2451
  🚨 **Once a rig has named skins, no instrument here can see them until you say
2446
2452
  which one** ([#571](https://github.com/firejune/rigc/issues/571)). `render` and
@@ -2506,11 +2512,18 @@ other than what was written (measured on spine-core 4.3.13,
2506
2512
  only on the three kinds that draw a region, so it would be dropped in silence. The
2507
2513
  refusal names region, mesh and linkedmesh.
2508
2514
 
2509
- `width` and `height` are the attachment's one size, every frame drawn into it. Omit
2510
- them and rigc takes the frames' size — **only when every frame measures the same**;
2511
- frames of different sizes are refused until you state the size, because picking one
2512
- of them would be the compiler choosing a value. Under `--atlas-in` a stated size that
2513
- disagrees with a packed frame is refused, as it is for one region.
2515
+ `width` and `height` are the attachment's one size, every frame drawn into it. The
2516
+ frames **may differ in size**: the runtime scales each frame's region into the
2517
+ attachment's size (`RegionAttachment.computeUVs`, `width / region.originalWidth`), so a
2518
+ stated size is emitted as stated and compared with no frame, on the loose route and
2519
+ under `--atlas-in` alike. That is what an editor exports for a series that mixes image
2520
+ sizes — the setup frame's size, the other frames as they are
2521
+ ([#795](https://github.com/firejune/rigc/issues/795)). Omit them and rigc takes the
2522
+ frames' size — **only when every frame measures the same**; frames of different sizes
2523
+ are refused until you state the size, because picking one of them would be the
2524
+ compiler choosing a value. ⚠️ One region is different: its `width` is its image's size
2525
+ in every editor export, so under `--atlas-in` a stated size that disagrees with its
2526
+ packed region is still refused.
2514
2527
  `A46_SEQUENCE_ATTACHMENTS_SHOW_THE_FRAME_THE_FILE_STATES` (§5.2) holds the block and
2515
2528
  every frame the timelines show against the file.
2516
2529
 
@@ -3551,7 +3564,7 @@ a delta from the constraint's own setting.
3551
3564
  infinite inverse mass — the constraint stops moving.
3552
3565
  - **Four of the seven are bounded, and a key outside its bound is a compile
3553
3566
  error** ([#610](https://github.com/firejune/rigc/issues/610)). `mass` must be
3554
- `> 0`, `damping` must be strictly inside `(0, 1)`, and `mix` and `strength`
3567
+ `> 0`, `damping` must be inside the closed `[0, 1]`, and `mix` and `strength`
3555
3568
  must be `0` or more. `A23_PHYSICS_CONSTRAINT_EFFECTIVE` applies the same four to
3556
3569
  a file rigc did not write, naming the animation, the constraint, the key time
3557
3570
  and the value — so the compiler is where a spec you wrote is refused, and the
@@ -3564,9 +3577,19 @@ a delta from the constraint's own setting.
3564
3577
  whole number the same negative is raised to a fractional power, which is NaN.
3565
3578
  [measured] one planted `damping` key of −0.5 on the generated physics fixture
3566
3579
  stays finite at 60 fps and at 30 (exponent 2), and is NaN within three steps of
3567
- the key at 45 and at 120. `1` never decays and above `1` diverges at every
3568
- rate. The refusal names the exponent and the rate for this reason, and `T101`
3569
- holds it to the two-rate measurement.
3580
+ the key at 45 and at 120. Above `1` every velocity grows on every step and
3581
+ diverges at every rate. The refusal names the exponent and the rate for this
3582
+ reason, and `T101` holds it to the two-rate measurement.
3583
+ - ✅ **Both ends of `damping` are inside the bound**
3584
+ ([#794](https://github.com/firejune/rigc/issues/794)). `1 ** x` is 1 and
3585
+ `0 ** x` is 0 for every positive exponent, so neither depends on `fps`: at `1`
3586
+ the velocity never decays and the jiggle holds for as long as it runs, and at
3587
+ `0` every velocity is zeroed on every step, so the offset follows the bone with
3588
+ no overshoot. Both are finite, at rest and keyed — [measured] 120 steps from
3589
+ `Physics.reset` at 60, 45 and 30 fps on the generated physics fixture, which
3590
+ `T109`–`T111` hold. ⚠️ `1` is also what a 4.2 file means by saying nothing:
3591
+ 4.2's parser reads an omitted `damping` as `1` where 4.3's reads `0.85`
3592
+ (§4.6), so a rig migrated from 4.2 that keys "the default" keys `1`.
3570
3593
  - 🚫 **`inertia`, `wind` and `gravity` are bounded nowhere, and neither is the top
3571
3594
  of `mix`.** The runtime documents no range for the first three, and
3572
3595
  `PhysicsConstraintPose` documents `mix` as "a percentage (0+)" — so a negative
@@ -3971,11 +3994,16 @@ key times. One lag, one place.
3971
3994
  mass?, wind?, gravity?, mix?, fps?, limit? }`. These are emitted into the 4.3
3972
3995
  `constraints` array. Seven of them — the six tuning numbers and `mix` — can also
3973
3996
  be **keyed over time** as `tracks` entries naming this constraint (§4.4); this
3974
- table is the value at rest, and a timeline overrides it while it plays. `mass: 0` becomes an infinite inverse mass and `damping ≥ 1`
3997
+ table is the value at rest, and a timeline overrides it while it plays. `mass: 0` becomes an infinite inverse mass and a `damping` above 1
3975
3998
  never settles — both are `A23`, here and on every timeline key that states them
3976
- ([#610](https://github.com/firejune/rigc/issues/610)); a `damping` at or below 0
3977
- is refused as well, and below 0 whether it is NaN depends on `fps` — §4.4
3978
- ([#748](https://github.com/firejune/rigc/issues/748)). ⚠️ `strength: 0` is `A23` **here and not on a key**:
3999
+ ([#610](https://github.com/firejune/rigc/issues/610)); a `damping` below 0
4000
+ is refused as well, and whether it is NaN depends on `fps` — §4.4
4001
+ ([#748](https://github.com/firejune/rigc/issues/748)). `damping` is `[0, 1]`
4002
+ with both ends in: `1` never decays and `0` zeroes the velocity on every step,
4003
+ both finite at every rate ([#794](https://github.com/firejune/rigc/issues/794)).
4004
+ ⚠️ An omitted `damping` is `0.85` to 4.3's parser (`SkeletonJson.js:308` in
4005
+ 4.3.13) and `1` to 4.2's (`SkeletonJson.js:242` in 4.2.120), so a constraint
4006
+ migrated from 4.2 that stated nothing may arrive stating `1` — which builds. ⚠️ `strength: 0` is `A23` **here and not on a key**:
3979
4007
  at rest it is a constraint nothing pulls back, and on a key it is a release somebody
3980
4008
  asked for, which §4.4 states with the measurement behind it
3981
4009
  ([#727](https://github.com/firejune/rigc/issues/727)). A negative `strength` is
@@ -5450,7 +5478,7 @@ or the key's position in its own track. These are the frequent ones, verbatim:
5450
5478
  | `physics constraint "*": the name is reserved — …` / `` `physics."*"` names a physics constraint "*", and that name is reserved … `` | §3.5 — `"*"` is the target of the timeline that names no constraint; rename the constraint |
5451
5479
  | `animation "A": group "G" lists "*", which is not a constraint but the target that names none …` | §4.4 — write `"*"` as the track's `physics` field |
5452
5480
  | `animation "A" bone "X" translatex: key value must be an array of 1 number(s)` | the value shape must match the property (§4.4) |
5453
- | `animation "A" physics constraint "C" mass key at t=… is 0 (massInverse Infinity); must be > 0 — …` | §4.4 — a keyed physics value the runtime cannot use. The message names the bound and the `PhysicsConstraint.js` lines that make it one: `mass` is `> 0`, `damping` is inside `(0, 1)`, `mix` and `strength` are `0` or more, and `inertia`/`wind`/`gravity` are bounded nowhere ([#610](https://github.com/firejune/rigc/issues/610)). ⚠️ Those are the bounds a **key** is held to. A setup `strength` of `0` is refused too, but by `A23` rather than here, and with its own sentence — `physics "C" has strength 0; nothing pulls it back` ([#727](https://github.com/firejune/rigc/issues/727)); a setup `strength` below 0 says the offset is pushed away and grows, the same arm this message quotes for a negative key ([#748](https://github.com/firejune/rigc/issues/748)). A `damping` key's sentence names the exponent `60 * step` and why a negative is NaN at any `fps` where `60 / fps` is not whole (§4.4) |
5481
+ | `animation "A" physics constraint "C" mass key at t=… is 0 (massInverse Infinity); must be > 0 — …` | §4.4 — a keyed physics value the runtime cannot use. The message names the bound and the `PhysicsConstraint.js` lines that make it one: `mass` is `> 0`, `damping` is inside `[0, 1]`, both ends included ([#794](https://github.com/firejune/rigc/issues/794)), `mix` and `strength` are `0` or more, and `inertia`/`wind`/`gravity` are bounded nowhere ([#610](https://github.com/firejune/rigc/issues/610)). ⚠️ Those are the bounds a **key** is held to. A setup `strength` of `0` is refused too, but by `A23` rather than here, and with its own sentence — `physics "C" has strength 0; nothing pulls it back` ([#727](https://github.com/firejune/rigc/issues/727)); a setup `strength` below 0 says the offset is pushed away and grows, the same arm this message quotes for a negative key ([#748](https://github.com/firejune/rigc/issues/748)). A `damping` key's sentence names the exponent `60 * step` and why a negative is NaN at any `fps` where `60 / fps` is not whole (§4.4) |
5454
5482
  | `a key carries both a named easing and a raw curve; pick one` | R6 |
5455
5483
  | `last key carries an easing but has nothing to ease to` | drop `ease`/`curve` from the final key |
5456
5484
  | `key times must strictly increase (at t=…)` | including after `lag` and `stagger` |
@@ -5525,7 +5553,7 @@ or the key's position in its own track. These are the frequent ones, verbatim:
5525
5553
  | `N pair(s) of animation names have no one order: … "Fx/a" / "fx/b" (folder) — "Fx/a" and "fx/b" sit in the sibling folders "Fx" and "fx", which the comparator leaves in one place …; rename one of the two folders so they differ by more than letter case, spacing or a leading zero` | **R10** — rename until no pair is left. The kind in brackets says which of the three things the five stored round trips leave open decides the pair: `number` (two digit runs that are each one number written twice, pointing opposite ways), `separator` (a whitespace character that is not a space) or `folder` (two sibling folders the comparator cannot separate). rigc keys `animations` in the editor's own comparator, read off `fixtures/editor-order/probe{1..5}.{in,out}.json` ([#728](https://github.com/firejune/rigc/issues/728)) — so a pair those files settle is emitted rather than refused, **including a pair that differs only in case**, whose order is then the one your spec declared. On the three that are left, the editor's re-key repoints every slider whose animation moves index ([#535](https://github.com/firejune/rigc/issues/535)) |
5526
5554
  | `N pair(s) of skin names have no one order: … "Fx/a" / "fx/b" (folder) — …` | **R11** — rename until no pair is left. The same shape and the same three kinds as the row above, because it is the same comparator: two of the five round trips carried one name list as both collections and both came back in one order ([#728](https://github.com/firejune/rigc/issues/728)). ⚠️ This row was **wider** than R10's until then — `Zulu`/`mike` and `mike10`/`mike2` built as animation names and were refused as skin names ([#541](https://github.com/firejune/rigc/issues/541)) — and both build now |
5527
5555
  | `slot "patch": placeholder "patch" is filled by the "default" skin AND by skins "zulu", "mike", and the Spine editor has no way to hold that … Move the default skin's entry for this slot into a named skin — call it "base"` | **R12** — do what it says: move that entry out of `default` into a named skin. The editor has no representation for a placeholder the default skin shares with a named one, in either spelling, and §3.4.2 has both measurements. Renaming the placeholder does not help; the shape is what is refused |
5528
- | `N attachment name collision(s): a placeholder that more than one skin fills is emitted with the name "<skin>/<placeholder>" … slot "patch": skin "base" placeholder "zulu/patch" and skin "zulu" placeholder "patch" would both be named "zulu/patch"` | **R12** — rename the placeholder or the skin. rigc composes an attachment name for every placeholder more than one skin fills (§3.4.2), and this fires when a composed name is one another entry in the same slot already answers to — including a plain name in the default skin, which composed nothing. Both sites are named; either rename ends it |
5556
+ | `skin "S" slot "X" attachment "P": "name" is V, which is not a string. An attachment's name is the runtime's `Attachment.name` …` | §3.4 — write the name as a string, or leave the key out and the attachment is named by its placeholder. It is written verbatim and `path` defaults to it, so a number there would reach the atlas as a region name nobody spelled |
5529
5557
 
5530
5558
  ⚠️ **One refusal in this section is not a `CompileError`, and it is `explain`'s.**
5531
5559
  `explain` prints the `DEFORM` block by **posing** the rig, and a pose resolves every
@@ -5639,7 +5667,7 @@ Fix A00 and run it again ([#568](https://github.com/firejune/rigc/issues/568)).
5639
5667
  | `A20_MESH_WEIGHTS_COHERENT` | both ◑ | a weighted vertex with no bone, a negative weight, a bone index out of range, or weights that do not sum to 1. Under `spine-html` also: an unweighted mesh, a binding at weight 0, or **a bone the mesh declares that no vertex binds** — `mesh "x" declares bone "grip_b" and none of its 25 vertices binds it; the weights reference "box", "grip_a"`. Those three are one sentence about rigc's own generators: the bone set a generated mesh declares is the bone set its weights reference, so a `controls` or `chain` name that moves nothing is a defect where a foreign mesh's is not ([#684](https://github.com/firejune/rigc/issues/684)). Fix the rig spec's `controls`/`chain`, or the manifest's `control_bones`. **SKIP** when the skeleton carries no mesh attachment ([#580](https://github.com/firejune/rigc/issues/580)) |
5640
5668
  | `A21_MESH_RIM_PINNED` | archetype | a generated ring's rim, a ribbon's entry row, or a contour's outline (which is all of it) is not pinned to its anchor bone at weight 1 |
5641
5669
  | `A22_MESH_UVS_IN_UNIT_RANGE` | both | a mesh UV outside its region, or a UV array that disagrees with the vertex count. **SKIP** when the skeleton carries no mesh attachment ([#580](https://github.com/firejune/rigc/issues/580)) |
5642
- | `A23_PHYSICS_CONSTRAINT_EFFECTIVE` | both | a physics constraint that drives no component, rests at `mix: 0` with **no timeline in any animation keying that `mix` above 0**, has `mass: 0`, has `strength` at or below 0 — `0` says `nothing pulls it back`, below 0 says the offset `is pushed away and grows with every step`, both read off the row's `outside` arms, which the key's refusal quotes too ([#748](https://github.com/firejune/rigc/issues/748)) — or has `damping` outside `(0, 1)` so it never settles — **at rest, and on every physics timeline key** ([#610](https://github.com/firejune/rigc/issues/610), [#743](https://github.com/firejune/rigc/issues/743)). The timeline arm reads each key through the runtime's own `PhysicsConstraint*Timeline.set`, so a keyed `mass` is judged as the `massInverse` it becomes, and the detail names the animation, the constraint, the key time, the value and the bound. Two differences between the two arms, and the runtime is the reason for both: a **key** of `mix: 0` is accepted, because `update` opens with `if (mix === 0) return;` and muting a constraint for a stretch is what a mix timeline is for — the editor's own `sack-pro` example keys it there on 24 of its 36 mix keys — and a **key** of `strength: 0` is accepted, because it releases the constraint for the span with `damping` and `inertia` still applied and the next key pulls the offset back, measured through spine-core at no NaN, a coast to a limit and a return in 54 steps ([#727](https://github.com/firejune/rigc/issues/727)). As a **setup** value `strength: 0` is still refused by the arm above, and `mix: 0` is refused only when nothing keys it above 0. The `mix` branch above is why `mix` is the one setup value a key can answer for: at rest the constraint is **inert** rather than broken, so a rig that rests muted and is keyed above 0 is refused by nothing, while a rig resting at `mass: 0` is `massInverse` Infinity before anything plays and no key reaches back into that. The detail of the refusal says both halves and how many animations were searched: `physics "C" has mix 0 and none of the 3 animations keys its mix above 0; it is muted — rest it above 0, or key its mix above 0 in an animation`. The search counts the unnamed global timeline for every constraint whose own `mixGlobal` is set, reads each key through the runtime's accessor, counts every sample of a Bezier between two keys as a value the timeline poses — so two keys of 0 joined by a curve lifted above 0 are a rescue, measured to move the bone — and takes an animation a slider applies like any other. It is the one reading `A36` and `A37` use as well ([#752](https://github.com/firejune/rigc/issues/752)). `inertia`, `wind`, `gravity` and the top of `mix` are bounded nowhere, at rest or keyed. `ingest` does not carry a constraint that drives no component into the spec it writes: it omits it with its timelines and reports `PHYSICS_DRIVES_NOTHING` ([INGEST §2.0](INGEST.md), [#731](https://github.com/firejune/rigc/issues/731)), so this sentence is met on a file, never on a decompiled rebuild. **SKIP** when the skeleton declares no physics constraint ([#580](https://github.com/firejune/rigc/issues/580)) — the same sentence `A36` and `A37` have always printed for their own constraint types |
5670
+ | `A23_PHYSICS_CONSTRAINT_EFFECTIVE` | both | a physics constraint that drives no component, rests at `mix: 0` with **no timeline in any animation keying that `mix` above 0**, has `mass: 0`, has `strength` at or below 0 — `0` says `nothing pulls it back`, below 0 says the offset `is pushed away and grows with every step`, both read off the row's `outside` arms, which the key's refusal quotes too ([#748](https://github.com/firejune/rigc/issues/748)) — or has `damping` outside `[0, 1]`, which says `physics "C" has damping 1.5; must be inside [0, 1] — above 1 every velocity grows on every step, and below 0 it is NaN at any fps where 60 / fps is not whole`, the bound read off the row the key's refusal reads; `1` and `0` are inside, since both are finite at every rate ([#794](https://github.com/firejune/rigc/issues/794)) — **at rest, and on every physics timeline key** ([#610](https://github.com/firejune/rigc/issues/610), [#743](https://github.com/firejune/rigc/issues/743)). The timeline arm reads each key through the runtime's own `PhysicsConstraint*Timeline.set`, so a keyed `mass` is judged as the `massInverse` it becomes, and the detail names the animation, the constraint, the key time, the value and the bound. Two differences between the two arms, and the runtime is the reason for both: a **key** of `mix: 0` is accepted, because `update` opens with `if (mix === 0) return;` and muting a constraint for a stretch is what a mix timeline is for — the editor's own `sack-pro` example keys it there on 24 of its 36 mix keys — and a **key** of `strength: 0` is accepted, because it releases the constraint for the span with `damping` and `inertia` still applied and the next key pulls the offset back, measured through spine-core at no NaN, a coast to a limit and a return in 54 steps ([#727](https://github.com/firejune/rigc/issues/727)). As a **setup** value `strength: 0` is still refused by the arm above, and `mix: 0` is refused only when nothing keys it above 0. The `mix` branch above is why `mix` is the one setup value a key can answer for: at rest the constraint is **inert** rather than broken, so a rig that rests muted and is keyed above 0 is refused by nothing, while a rig resting at `mass: 0` is `massInverse` Infinity before anything plays and no key reaches back into that. The detail of the refusal says both halves and how many animations were searched: `physics "C" has mix 0 and none of the 3 animations keys its mix above 0; it is muted — rest it above 0, or key its mix above 0 in an animation`. The search counts the unnamed global timeline for every constraint whose own `mixGlobal` is set, reads each key through the runtime's accessor, counts every sample of a Bezier between two keys as a value the timeline poses — so two keys of 0 joined by a curve lifted above 0 are a rescue, measured to move the bone — and takes an animation a slider applies like any other. It is the one reading `A36` and `A37` use as well ([#752](https://github.com/firejune/rigc/issues/752)). `inertia`, `wind`, `gravity` and the top of `mix` are bounded nowhere, at rest or keyed. `ingest` does not carry a constraint that drives no component into the spec it writes: it omits it with its timelines and reports `PHYSICS_DRIVES_NOTHING` ([INGEST §2.0](INGEST.md), [#731](https://github.com/firejune/rigc/issues/731)), so this sentence is met on a file, never on a decompiled rebuild. **SKIP** when the skeleton declares no physics constraint ([#580](https://github.com/firejune/rigc/issues/580)) — the same sentence `A36` and `A37` have always printed for their own constraint types |
5643
5671
  | `A24_AXIS_SPACE_STROKE` | archetype | a bone under the rig's `axisBone` was keyed with a screen-space Y component, or the axis bone itself was keyed. **SKIP** when the rig declares no axis bone, and also when no animation keys that bone or anything under it ([#580](https://github.com/firejune/rigc/issues/580)) |
5644
5672
  | `A25_DETACHED_BONE_PARENTAGE` | archetype | a bone the rig declares `detached` is a descendant of the bone it must never hang under |
5645
5673
  | `A26_SLOT_DRAW_ORDER` | archetype | the emitted slots are not the rig's slot table — a slot is out of order, is not in the table at all, or is in the table and missing from the skeleton (§3.3). **SKIP** when the rig declares no canonical slot order. ⚠️ A skeleton with **no** slot beside a rig that declares some is **not** a skip, and it is the one rule in this family where an empty loop is not a vacuous pass ([#580](https://github.com/firejune/rigc/issues/580)): the completeness clause reads it as every declared slot lost and names them, which is the maximal case of what [#575](https://github.com/firejune/rigc/issues/575) filed |
package/docs/FACE.md CHANGED
@@ -1474,7 +1474,9 @@ is the card for having left the sentence standing:
1474
1474
  names. ⚠️ **The editor was never silent**: it named the section, the attachment
1475
1475
  and the rule, and `tools/editor_roundtrip.ts` had discarded its stdout and
1476
1476
  stderr. What changed is [#552](https://github.com/firejune/rigc/issues/552) —
1477
- the composed `<skin>/<placeholder>` name, with `default` pinned first — and what
1477
+ the composed `<skin>/<placeholder>` name, with `default` pinned first; the
1478
+ composition was retired by [#796](https://github.com/firejune/rigc/issues/796),
1479
+ which measured that it was never what the editor objected to — and what
1478
1480
  still binds is [#567](https://github.com/firejune/rigc/issues/567): a
1479
1481
  `CompileError` when the default skin shares a placeholder with a named one. ⇒
1480
1482
  Skins are not the reason to stay at one, and every figure on this page was
package/docs/INGEST.md CHANGED
@@ -621,7 +621,6 @@ is the one failure a comparison of two sets cannot show you.
621
621
  | `ANIMATION_GROUP` | `BLOCK` | 1 | the animation carries a group the motion spec has no home for. The detail names the ten it does carry. `drawOrderFolder` is the group to know about: the runtime reads it and builds a timeline from it, and no export in this corpus carries one | transcribe that group by hand (§2), or accept that the rebuild does not carry it |
622
622
  | `ATTACHMENT_<TYPE>` | `BLOCK` | 1 | an attachment of a type rigc does not emit; the code is composed from the type, so on the one type left it reads `ATTACHMENT_POINT`. rigc emits region, mesh, linkedmesh, boundingbox, clipping and path — `linkedmesh` since [#691](https://github.com/firejune/rigc/issues/691), and `point` is the remaining deferred type | the rebuild will not have that attachment at all. `docs/SPEC_COVERAGE.md` part 1-6 says what a deferred type would carry |
623
623
  | `ATTACHMENT_LINK_GEOMETRY` | `LOSS` | 0 | a **linked mesh** that also states `uvs`, `triangles`, `vertices`, `hull` or `edges`. The parser returns from the `source` branch before `readVertices` (`SkeletonJson.ts:582-586`), so those keys are read by nothing at all and the attachment draws the geometry its `source` names; the rig spec has no home for them either, because `build` refuses geometry on a link by name. The detail lists the keys and the source. Until [#710](https://github.com/firejune/rigc/issues/710) the rebuild dropped them with no line at all, so an `ingest` that normalised somebody's file said nothing about it | nothing. The rebuild is the mesh the runtime was already drawing — and if those keys were the geometry you meant, take `source` off and author it as a mesh of its own. `A44_LINKED_MESH_STATES_NO_GEOMETRY_OF_ITS_OWN` is the same fact at the gate |
624
- | `ATTACHMENT_NAME` | `LOSS` | 0 | the rebuilt attachment answers to a different name than the source's. **Two shapes, one comparison.** Where only **one** skin fills the placeholder, any stated `name` is lost: rigc writes none — it composes `<skin>/<placeholder>` exactly where a placeholder is contested. Where the placeholder **is** contested, rigc composes that name, and the source's name — what it states, or its placeholder where it states none (`SkeletonJson.js:526`) — is compared with it: equal is rigc's own emit and prints nothing, different is a rename and the detail states **both strings**. Until [#746](https://github.com/firejune/rigc/issues/746) the contested half printed nothing at all. A contested placeholder the **default** skin fills gets no line — the compiler composes nothing there and refuses the rebuild by name. 🚨 **The name is also the ATLAS REGION KEY**, and the detail says what became of it: `readAttachment` reads `name = getValue(map, "name", placeholder)` and then `path = getValue(map, "path", name)`, so `path` defaults to the **name** and not to the placeholder. A name that differs from the placeholder is therefore **kept as `path`** on an attachment that resolves a region — region, mesh, linked mesh — and the detail names the region. The three shapes that keep nothing say which they are: the source stated its own `path` (carried unchanged), the name **is** the placeholder (same region either way), or the type resolves no region at all (`boundingbox`, `clipping`, `path`). Until [#742](https://github.com/firejune/rigc/issues/742) nothing was written, so the rebuild asked the atlas for the placeholder and `A08_REGION_NAMES_MATCH_ATTACHMENTS` refused it | nothing about the art, which is carried. The **name** is what is gone, so this matters where something downstream looks that attachment up by the name the source gave it |
625
624
  | `ATTACHMENT_SEQUENCE` | `BLOCK` | 1 | a `sequence` block — a numbered image series — that the rig spec cannot say **as written**. Since [#729](https://github.com/firejune/rigc/issues/729) a well-formed block on a region, mesh or linked mesh is carried field for field, with no `image` on the loose route (the frames `<path><number>` are the art), and a `sequence` timeline with it; what is left here is a block the parser reads into a series other than the one written — no `count` (0 regions), a `setup` past the end (clamped), a fraction — or one on a `boundingbox`, `clipping` or `path`, where the parser never reads it. The detail quotes the block and says which | the rebuild draws the single region the attachment names. Fix the block in the source — a `count` is the usual one — and ingest again |
626
625
  | `ATTACHMENT_TIMELINE` | `BLOCK` | 1 | an attachment timeline that is neither `deform` nor `sequence`. Both are carried since [#729](https://github.com/firejune/rigc/issues/729), and `readAnimation` tests an attachment timeline for exactly those two names and ignores anything else (`SkeletonJson.js:1147-1201`) — so what reaches this line is a name outside the format, which no player plays either | fix the timeline's name in the source, or accept that the rebuild does not carry it |
627
626
  | `BONE_FIELD` | `BLOCK` | 1 | a bone field with no rig-spec field, so it is dropped. A 4.0/4.1 export spelling `transform` where 4.3 spells `inherit` lands here; so does a misspelling | check the name against AUTHORING §3 first — a typo and an unsupported field read exactly the same |
@@ -651,18 +650,16 @@ is the one failure a comparison of two sets cannot show you.
651
650
  | `TIMELINE_KEY_RESTATED` | `LOSS` | 0 | an editor omits a channel that equals the parser's default, and the motion spec's `v` is positional, so the omission is written out at that default **in the spec** — and the line is printed only where the **file** will carry it too. It was the commonest line in a real run until [#716](https://github.com/firejune/rigc/issues/716): the emitter now leaves a channel out wherever it is the one the parser reads without it ([AUTHORING §10.6c](AUTHORING.md)), so on every key kind with a row the rebuild is the source's own text and nothing is said — measured on the twelve exports, 341 tracks printed it before and 0 after. What still prints it is a timeline whose keys have no row (`shearx`, `alpha`, path `spacing`, …), and an `inherit` key: one that omits the mode is written as `normal` — the parser's default — and one spelled with a capital first letter (`NoScale`) as the editor's `noScale`, the same mode either way | nothing. The same values the runtime reads, spelled out — a larger file and the same animation |
652
651
  | `TRANSFORM_KEY_FIELD` | `BLOCK` | 1 | as `IK_KEY_FIELD`, on a `transform` timeline | as `IK_KEY_FIELD` |
653
652
 
654
- ⚠️ **One thing the table cannot carry: the region key is kept where a placeholder is
655
- CONTESTED too, whether or not a line is printed.** On a contested placeholder rigc
656
- composes `<skin>/<placeholder>`, so `ATTACHMENT_NAME` fires there only where the
657
- source's name differs from that composed one ([#746](https://github.com/firejune/rigc/issues/746);
658
- `IG68`–`IG70` hold both directions) — and until
659
- [#742](https://github.com/firejune/rigc/issues/742) the region went with the name anyway:
660
- `compile` pins a composed name's `path` at the **placeholder**, so two skins naming two
661
- regions rebuilt onto **one**, neither of them the art the source drew, with no line
662
- printed. `ingest` now writes the source's region as `path` on both, and `IG53` in
663
- `bun run selftest` is what holds it — the clause it measures is the count of *distinct*
664
- regions, because every row can name a region the pack has and still be one region doing
665
- the work of two.
653
+ ⚠️ **An attachment's `name` has no row, because nothing about it is lost any more.**
654
+ Until [#796](https://github.com/firejune/rigc/issues/796) the rig spec had no field for
655
+ one, so `ATTACHMENT_NAME` reported it as a `LOSS` in two shapes: a stated name on a
656
+ placeholder one skin fills was kept only as `path` and the rebuild answered to the
657
+ placeholder, and a contested placeholder was renamed `<skin>/<placeholder>` by the
658
+ compiler. The field exists now, `ingest` carries a stated `name` verbatim — equal to its
659
+ key or not, exactly as the source spells it — and writes none where the source states
660
+ none, and `compile` emits exactly what the spec states. The code went with the loss it
661
+ named; `IG86`–`IG89` in `bun run selftest` hold the carry, the rebuild's loaded names and
662
+ regions, and the two-skin shape with a link in each.
666
663
 
667
664
  ### Transcription — the route that made a foreign skeleton yours
668
665
 
@@ -633,20 +633,21 @@ with the member's own `skin: true` — either half alone is refused, because `Sk
633
633
  | `clipping` | ✅ under `--profile spine` · 🚫 under `spine-html` | `end` (refused when it names no slot), `convex`, `inverse`, `vertexCount`, geometry, `color`. **A33**, and **A11_NO_CLIPPING_ATTACHMENTS** is the renderer-profile refusal |
634
634
  | `sequence` block | ✅ | on a region, a mesh or a linked mesh: `count` (required — the parser's 0 loads no region), `start`, `digits`, `setup`, emitted as stated. The frames are the regions `<path><start + i>` zero-padded to `digits`, each atlased by name — the loose route's PNG of that name, or `--atlas-in`'s region — and a missing frame is refused with its number and the name looked for. A `setup` past the end (clamped), a fraction, an `image` or a `generator` beside it, and a `sequence` on any other kind are refused by name. **A46_SEQUENCE_ATTACHMENTS_SHOW_THE_FRAME_THE_FILE_STATES** ([#729](https://github.com/firejune/rigc/issues/729)) |
635
635
 
636
- Across all five types, rigc emits the attachment's own **`name`** in exactly one case: a placeholder
637
- that more than one skin fills (`compile.ts`'s `composeSkinAttachmentName` and `nameSkinAttachment`).
638
- Part 1-5 above states why it matters — `name` defaults to the placeholder and `path` defaults to
639
- `name` — so several skins under one placeholder are several attachments with one name, which
640
- spine-core accepts and the Spine editor refuses on import
641
- ([#541](https://github.com/firejune/rigc/issues/541)). The composed name is `<skin>/<placeholder>`,
642
- `path` is restated beside it so the region still resolves, and a placeholder one skin fills is
643
- emitted with neither. ⚠️ The **`default` skin may not be one of those skins**, and that is a
644
- `CompileError` rather than an emission rule: the editor holds no placeholder the default skin shares
645
- with a named one in either spelling — named, the export re-keys the attachment by its name and the
646
- slot's setup attachment stops resolving; unnamed, the import is refused with `Multiple attachments
647
- have the same name` ([#567](https://github.com/firejune/rigc/issues/567), Spine 4.3.26, round trips
648
- 7 and 8). Nothing else in the tree carries a `name`: of the twelve editor exports in `examples/`,
649
- **0** attachments do, because all twelve declare one skin.
636
+ Across all six types rigc emits, the attachment's own **`name`** is written exactly when the rig
637
+ spec states it, verbatim, as the attachment's first key — and never derived
638
+ ([#796](https://github.com/firejune/rigc/issues/796)). Part 1-5 above states why it matters:
639
+ `name` defaults to the placeholder and `path` defaults to `name`, so it is the runtime's
640
+ `Attachment.name` and, on a type that draws, the region it resolves; nothing resolves an
641
+ attachment by it. From [#541](https://github.com/firejune/rigc/issues/541) to #796 rigc composed
642
+ `<skin>/<placeholder>` for a placeholder several skins fill, on the reading that a linked mesh
643
+ resolves its source by name; it resolves it by skin, slot and key, and the editor imports the
644
+ uncomposed shape. ⚠️ The **`default` skin may not be one of the skins sharing a placeholder**, and
645
+ that is a `CompileError` rather than an emission rule: the editor holds no placeholder the default
646
+ skin shares with a named one in either spelling — named, the export re-keys the attachment by its
647
+ name and the slot's setup attachment stops resolving; unnamed, the import is refused with
648
+ `Multiple attachments have the same name` ([#567](https://github.com/firejune/rigc/issues/567),
649
+ Spine 4.3.26, round trips 7 and 8). Of the twelve editor exports in `examples/`, **0**
650
+ attachments state a `name`, because all twelve declare one skin.
650
651
 
651
652
  Mesh geometry is generated by exactly three procedural generators (`mesh.ts`): `buildRingMesh`
652
653
  (three concentric rings + hub, outer two pinned), `buildRibbonMesh` (a two-wide strip along a bone
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-rigc",
3
- "version": "0.35.1",
3
+ "version": "0.36.0",
4
4
  "description": "Rig compiler for Spine — declarative rig specs in, Spine 4.3 skeleton data out, verified by a spine-core round-trip. Built so AI agents can author rigs and check their own work; the output imports into the Spine editor.",
5
5
  "type": "module",
6
6
  "bin": {