@rig-c/core 2.39.0 → 2.40.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/NOTICE.md +3 -3
- package/README.md +48 -47
- package/docs/AUTHORING.md +86 -30
- package/docs/FACE.md +14 -14
- package/docs/INGEST.md +4 -4
- package/docs/MOTION.md +2 -2
- package/docs/PROMPTING.md +4 -4
- package/docs/RIGGING.md +33 -33
- package/package.json +4 -4
- package/skills/rigc/SKILL.md +8 -8
- package/skills/rigc-face/SKILL.md +6 -6
- package/skills/rigc-ingest/SKILL.md +5 -5
- package/skills/rigc-motion/SKILL.md +6 -6
- package/skills/rigc-rigging/SKILL.md +6 -6
- package/src/assertions/bodies/a51.ts +92 -0
- package/src/assertions/facts/setup_world.ts +40 -0
- package/src/assertions/kinds.ts +1 -0
- package/src/assertions/model/declared.ts +1 -0
- package/src/assertions/model/index.ts +7 -0
- package/src/assertions/model/setup_world.ts +35 -0
- package/src/ballot.ts +1 -1
- package/src/bonedist.ts +1 -1
- package/src/cli/shared.ts +1 -1
- package/src/cli/spine_commands.ts +1 -1
- package/src/compile.ts +2 -0
- package/src/deformmeasure.ts +3 -3
- package/src/depth.ts +3 -3
- package/src/diff.ts +1 -1
- package/src/generation.ts +1 -1
- package/src/preview.ts +2 -2
- package/src/rig.ts +104 -2
- package/src/trackgen.ts +1 -1
- package/src/types.ts +9 -0
- package/src/validate.ts +49 -0
package/docs/RIGGING.md
CHANGED
|
@@ -105,7 +105,7 @@ beside the offsets it exists to make visible:
|
|
|
105
105
|
> *"Every limb attachment is OFFSET from its bone, and that is what makes the hinge
|
|
106
106
|
> visible: art centred on its own pivot turns in place, so a search over one angle
|
|
107
107
|
> would move nothing and a wrong hinge would cost nothing either."*
|
|
108
|
-
> — [`selftest.ts`](https://github.com/
|
|
108
|
+
> — [`selftest.ts`](https://github.com/rig-c/core/blob/main/selftest.ts), the chain-fit fixture
|
|
109
109
|
|
|
110
110
|
⇒ **A correctly placed pivot also collapses the spec.** A pendulum whose bone sits
|
|
111
111
|
on its hinge needs one `rotate` track and no translate at all; the same part with
|
|
@@ -144,7 +144,7 @@ never:
|
|
|
144
144
|
it**, so the bone points at its own mass — at the cost of two emitted fields.
|
|
145
145
|
|
|
146
146
|
> *"Neither is checkable from the frames."* — rung 3's first attempt
|
|
147
|
-
> ([`2026-08-23-rung3-1`](https://github.com/
|
|
147
|
+
> ([`2026-08-23-rung3-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-rung3-1/LOOP.md) §11)
|
|
148
148
|
|
|
149
149
|
⇒ Pick one, and say in your log that you picked it. AUTHORING §10.1's naming
|
|
150
150
|
convention is the same shape of decision and is worth far more (it decides five of
|
|
@@ -162,7 +162,7 @@ rigid.
|
|
|
162
162
|
> bone you scale-key must sit at that bone's origin, or the offset rides the
|
|
163
163
|
> scale."*
|
|
164
164
|
> — spineboy attempt 4
|
|
165
|
-
> ([`2026-08-28-spineboy-1`](https://github.com/
|
|
165
|
+
> ([`2026-08-28-spineboy-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-28-spineboy-1/LOOP.md) §8)
|
|
166
166
|
|
|
167
167
|
⇒ This is the one exception to §1's rule, and it is not a contradiction: a bone
|
|
168
168
|
whose **scale** is the animated property is not a hinge, so there is no hinge to
|
|
@@ -184,10 +184,10 @@ None of them is an error message. Each one is a number that reads as ordinary.
|
|
|
184
184
|
|
|
185
185
|
| Signature | What it actually is | Record |
|
|
186
186
|
| --- | --- | --- |
|
|
187
|
-
| **A gap that letting the parts off the hierarchy closes.** `idle` fitted to 10.3 union MAE on the frame the setup pose came from and **23.9** eight frames later; a short relax off the hierarchy recovered it to **14.6** | *"A gap that a relax closes is not a pose the rig cannot reach; it is a pivot in the wrong place."* | spineboy attempt 1 ([`2026-08-23-spineboy-1`](https://github.com/
|
|
188
|
-
| **A fitted centre that drifts monotonically with the fitted scale.** The per-frame fits wanted each shadow's centre 8–12 units lower whenever the shadow was small | a part scaling about a pivot below its own centre — *"a pivot you have not modelled, not motion you have measured"* | rung 1 second attempt ([`2026-08-26-rung1-1`](https://github.com/
|
|
189
|
-
| **A knob resting exactly on its bound, on one orientation only.** `torso.x` pinned at −35.0 against a ±35 box on the lying frames and nowhere else | the optimum is outside the box, because a pivot error `e` needs a compensation `−R(θ)·e` that grows with the local angle — *"invisible upright, saturating in the lying poses"* | spineboy attempt 5 ([`2026-08-28-spineboy-2`](https://github.com/
|
|
190
|
-
| **An exact fit with no angular spread.** Two ankle joints solved at **rms 0.00 px** with a relative-angle spread of **0° and 2°** | a perfect fit to a system with no unique answer | spineboy, 2026-09-03 first attempt ([`2026-09-03-spineboy-1`](https://github.com/
|
|
187
|
+
| **A gap that letting the parts off the hierarchy closes.** `idle` fitted to 10.3 union MAE on the frame the setup pose came from and **23.9** eight frames later; a short relax off the hierarchy recovered it to **14.6** | *"A gap that a relax closes is not a pose the rig cannot reach; it is a pivot in the wrong place."* | spineboy attempt 1 ([`2026-08-23-spineboy-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-spineboy-1/LOOP.md) §8.2) |
|
|
188
|
+
| **A fitted centre that drifts monotonically with the fitted scale.** The per-frame fits wanted each shadow's centre 8–12 units lower whenever the shadow was small | a part scaling about a pivot below its own centre — *"a pivot you have not modelled, not motion you have measured"* | rung 1 second attempt ([`2026-08-26-rung1-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-26-rung1-1/LOOP.md) §2.3) |
|
|
189
|
+
| **A knob resting exactly on its bound, on one orientation only.** `torso.x` pinned at −35.0 against a ±35 box on the lying frames and nowhere else | the optimum is outside the box, because a pivot error `e` needs a compensation `−R(θ)·e` that grows with the local angle — *"invisible upright, saturating in the lying poses"* | spineboy attempt 5 ([`2026-08-28-spineboy-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-28-spineboy-2/LOOP.md) §3) |
|
|
190
|
+
| **An exact fit with no angular spread.** Two ankle joints solved at **rms 0.00 px** with a relative-angle spread of **0° and 2°** | a perfect fit to a system with no unique answer | spineboy, 2026-09-03 first attempt ([`2026-09-03-spineboy-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-09-03-spineboy-1/LOOP.md) §3.3) |
|
|
191
191
|
|
|
192
192
|
🚨 **The last one is the trap in its purest form: a zero residual reads as success
|
|
193
193
|
in every other context on this toolchain.** Here it means the estimator was handed
|
|
@@ -219,7 +219,7 @@ across all eight shots:
|
|
|
219
219
|
> 21-unit-different answer that fits the upright frames just as well. The joint is
|
|
220
220
|
> ill-conditioned without the lying poses, which is how the inherited triangulation
|
|
221
221
|
> (idle zoom-read) went wrong without any frame saying so."*
|
|
222
|
-
> — [`2026-08-28-spineboy-2`](https://github.com/
|
|
222
|
+
> — [`2026-08-28-spineboy-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-28-spineboy-2/LOOP.md) §5
|
|
223
223
|
|
|
224
224
|
Two answers 21 art units apart, at residuals that do not separate them. That is
|
|
225
225
|
what *ill-conditioned* means with a picture attached.
|
|
@@ -296,7 +296,7 @@ found that **every one of them measured a basin at the sweep's own cap** — ±4
|
|
|
296
296
|
> *"On this figure the joint offsets are **weakly identified at the scale a frame
|
|
297
297
|
> can see**, and the honest reading is that they are a prior with a measured width
|
|
298
298
|
> rather than a measurement."*
|
|
299
|
-
> — [`2026-09-03-spineboy-2`](https://github.com/
|
|
299
|
+
> — [`2026-09-03-spineboy-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-09-03-spineboy-2/LOOP.md) §3.5
|
|
300
300
|
|
|
301
301
|
⇒ That sentence is the deliverable when the pixels are silent. It is not a hedge:
|
|
302
302
|
a width is a number, and the next attempt can tell whether its own change is inside
|
|
@@ -463,7 +463,7 @@ The failure, watched happening:
|
|
|
463
463
|
> rear-thigh −199.8°`, whose net world rotations are −3°, +31° and −19°: the picture
|
|
464
464
|
> was right and the numbers were unusable. A rotate series that swings 180° between
|
|
465
465
|
> two keys does not interpolate, it spins the figure through a whole turn."*
|
|
466
|
-
> — spineboy attempt 1 ([`2026-08-23-spineboy-1`](https://github.com/
|
|
466
|
+
> — spineboy attempt 1 ([`2026-08-23-spineboy-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-spineboy-1/LOOP.md) §8.4)
|
|
467
467
|
|
|
468
468
|
### 4.1 ⚠️ The exact fold has a precondition, and it is easy to miss
|
|
469
469
|
|
|
@@ -474,7 +474,7 @@ AUTHORING §10.3 prescribes is not a null operation — it costs pixels:
|
|
|
474
474
|
> *"the hip's three children sit 9–13 units off it, so turning the hip moves their
|
|
475
475
|
> origins and the render. Folding cost MAE on every frame it touched (`idle` mean
|
|
476
476
|
> 23.0 with the fold against 19.9 without, same search)."*
|
|
477
|
-
> — spineboy attempt 2 ([`2026-08-23-spineboy-2`](https://github.com/
|
|
477
|
+
> — spineboy attempt 2 ([`2026-08-23-spineboy-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-spineboy-2/LOOP.md) §5)
|
|
478
478
|
|
|
479
479
|
⇒ **Test the precondition before folding.** Children at the origin ⇒ exact gauge,
|
|
480
480
|
fold it (the L1 optimum is the *median* of the children's deltas, which is one line).
|
|
@@ -521,10 +521,10 @@ that it is *keyed for a reason the arithmetic states*, not left free for a solve
|
|
|
521
521
|
|
|
522
522
|
| Bone | Carries | Its children then key |
|
|
523
523
|
| --- | --- | --- |
|
|
524
|
-
| `faceshift` ([`gallery/portrait`](https://github.com/
|
|
524
|
+
| `faceshift` ([`gallery/portrait`](https://github.com/rig-c/core/tree/main/gallery/portrait/)) | `−R·sin t`, the shift every feature shares in a yaw | only their residual — FACE §3 |
|
|
525
525
|
| `comet` (rung 6, rung 8) | the whole travel of a ball and its trail | the squash, and the trail's bend |
|
|
526
526
|
| `body` (rung 7, second attempt) | **translation only** — *"it holds no attachment, so keying its rotation as well would be a gauge"* | their own rotations |
|
|
527
|
-
| `ground` ([`gallery/walk`](https://github.com/
|
|
527
|
+
| `ground` ([`gallery/walk`](https://github.com/rig-c/core/tree/main/gallery/walk/)) | the contact line | the foot targets — §9.1 |
|
|
528
528
|
|
|
529
529
|
⭐ **FACE §3's argument for it is an auditing one before it is a rigging one**: a
|
|
530
530
|
residual is 1–6 units where a total is 30–40, *"nobody can eyeball an error in the
|
|
@@ -548,13 +548,13 @@ invisible in the numbers.
|
|
|
548
548
|
> and takes a `translatey` (0 → 3.4). Parent the head chain to the bone that scales
|
|
549
549
|
> and the whole face inflates 1.4% every breath — visible, and invisible in the
|
|
550
550
|
> spec."*
|
|
551
|
-
> — [`gallery/portrait/README.md`](https://github.com/
|
|
551
|
+
> — [`gallery/portrait/README.md`](https://github.com/rig-c/core/blob/main/gallery/portrait/README.md)
|
|
552
552
|
|
|
553
553
|
The same decision, on a ball and its trail:
|
|
554
554
|
|
|
555
555
|
> *"`ball` is a **sibling** of `tail0` under `comet`, not its parent, so squashing
|
|
556
556
|
> the ball does not squash the trail."*
|
|
557
|
-
> — rung 6 ([`2026-08-23-rung6-1`](https://github.com/
|
|
557
|
+
> — rung 6 ([`2026-08-23-rung6-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-rung6-1/README.md))
|
|
558
558
|
|
|
559
559
|
⇒ **The construction is always the same three bones**: an artless parent that
|
|
560
560
|
carries what both share (§4.4), and two children — one that takes the scale and one
|
|
@@ -565,7 +565,7 @@ that takes the chain.
|
|
|
565
565
|
> *"the neck plate has to take a *fraction* of the head's shift, so it cannot be an
|
|
566
566
|
> ancestor of the head. `neckbase` is the shared parent; `neck` carries the plate and
|
|
567
567
|
> `headroll` carries the head"*
|
|
568
|
-
> — [`gallery/portrait/README.md`](https://github.com/
|
|
568
|
+
> — [`gallery/portrait/README.md`](https://github.com/rig-c/core/blob/main/gallery/portrait/README.md), the part table
|
|
569
569
|
|
|
570
570
|
⭐ **And the pivot goes one link up, which is the same edit for a second reason.**
|
|
571
571
|
The same rig keys `headroll` and never `head`: *"a head rotates about the top of the
|
|
@@ -638,7 +638,7 @@ The record that named it:
|
|
|
638
638
|
> setup render is unchanged. walk 0.176 → 0.150, run 0.232 → 0.201, aim 0.212 →
|
|
639
639
|
> 0.186 after refit."*
|
|
640
640
|
> — spineboy attempt 4
|
|
641
|
-
> ([`2026-08-28-spineboy-1`](https://github.com/
|
|
641
|
+
> ([`2026-08-28-spineboy-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-28-spineboy-1/LOOP.md) §6)
|
|
642
642
|
|
|
643
643
|
⭐ **The tie-break is on the frames' side.** *"the shot's own extremes are a
|
|
644
644
|
measurement, while segment lengths taken off a folded pose are an estimate — so when
|
|
@@ -659,7 +659,7 @@ fits converge, every residual is ordinary, and the figure splays.**
|
|
|
659
659
|
> leg overshoots the floor by about 90 units and the fitter has to bend it sideways
|
|
660
660
|
> to land … Re-seeding the leg joints inset from the art's ends — thigh 86 units,
|
|
661
661
|
> shin 94 — put the ankle at world y **53.5** against the boot's own 54."*
|
|
662
|
-
> — [`2026-09-03-spineboy-1`](https://github.com/
|
|
662
|
+
> — [`2026-09-03-spineboy-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-09-03-spineboy-1/LOOP.md) §4.4
|
|
663
663
|
|
|
664
664
|
⭐ **Why the art misled it, and this generalises:** the art's outer ends are not the
|
|
665
665
|
joints. A rounded cap's centroid sits at the plate's *tip*, while the joint two
|
|
@@ -686,7 +686,7 @@ frames put the standing leg at about 233 — a 79-unit excess with a plausible c
|
|
|
686
686
|
> better rig, and the 79 units are a real knee bend in the reference's own stance
|
|
687
687
|
> rather than an error in this rig. The surgery §8.1 warns about … was therefore not
|
|
688
688
|
> performed, and the per-frame fitting budget was not spent twice."*
|
|
689
|
-
> — [`2026-09-03-spineboy-2/evidence/limb-reach.txt`](https://github.com/
|
|
689
|
+
> — [`2026-09-03-spineboy-2/evidence/limb-reach.txt`](https://github.com/rig-c/core/blob/main/bench/runs/2026-09-03-spineboy-2/evidence/limb-reach.txt)
|
|
690
690
|
|
|
691
691
|
⇒ **The reach check tells you a chain and a shot disagree. It does not tell you
|
|
692
692
|
which one is wrong.** A bent limb and a long limb produce the same excess. One
|
|
@@ -736,7 +736,7 @@ form: **is each frame's silhouette an affine image of the part's own drawing?**
|
|
|
736
736
|
> freedom the mesh was built with — a four-bone chain, weights blended by height
|
|
737
737
|
> only."*
|
|
738
738
|
> — rung 7, first attempt
|
|
739
|
-
> ([`2026-08-28-rung7-1`](https://github.com/
|
|
739
|
+
> ([`2026-08-28-rung7-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-28-rung7-1/LOOP.md) §5)
|
|
740
740
|
|
|
741
741
|
⇒ Two controls are what make that quotable and they are worth copying: the art
|
|
742
742
|
through a **known affine** read 0.0044 → 0.0007 (the floor), and the art plus a
|
|
@@ -772,10 +772,10 @@ not fail — it degenerates:
|
|
|
772
772
|
> *"The first candidate lifted the foot 56 units on a 130-unit leg: the chain has to
|
|
773
773
|
> fold to a 70-unit span, and a two-bone solve does that by throwing the knee
|
|
774
774
|
> sideways. 28 units — a fifth of the leg — is the version that reads as a step."*
|
|
775
|
-
> — [`gallery/walk/README.md`](https://github.com/
|
|
775
|
+
> — [`gallery/walk/README.md`](https://github.com/rig-c/core/blob/main/gallery/walk/README.md)
|
|
776
776
|
|
|
777
777
|
The same arithmetic on the other side of the chain, from
|
|
778
|
-
[`gallery/flex`](https://github.com/
|
|
778
|
+
[`gallery/flex`](https://github.com/rig-c/core/tree/main/gallery/flex/): rotating a rigid panel about a seam opens a wedge
|
|
779
779
|
of `halfHeight × tan(angle)` — about 22 px at 20° on that cloth — so *"the bend
|
|
780
780
|
angles in `motion.json` are chosen against that overlap, not the other way round."*
|
|
781
781
|
|
|
@@ -801,7 +801,7 @@ differ by exactly the bone's own setup `x`/`y`:
|
|
|
801
801
|
> *"the first spec moved the figure by the setup offset a second time. Rotation and
|
|
802
802
|
> scale needed no correction: their setups are 0 and 1."*
|
|
803
803
|
> — rung 7, second attempt
|
|
804
|
-
> ([`2026-08-28-rung7-2`](https://github.com/
|
|
804
|
+
> ([`2026-08-28-rung7-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-28-rung7-2/LOOP.md) §5)
|
|
805
805
|
|
|
806
806
|
The same confusion one attempt earlier, from the other end: *"The first fitter used
|
|
807
807
|
0 as every knob's neutral value. `bone.pose.x` is the bone's whole local
|
|
@@ -817,7 +817,7 @@ the diff names it in one line."*
|
|
|
817
817
|
|
|
818
818
|
⚠️ **A bone under a rotated parent is not at the parent's position plus its own
|
|
819
819
|
numbers, and a leg chain is exactly that case.** That sentence is a comment in
|
|
820
|
-
[`gallery/stage.ts`](https://github.com/
|
|
820
|
+
[`gallery/stage.ts`](https://github.com/rig-c/core/blob/main/gallery/stage.ts), which exists because a drawing script
|
|
821
821
|
needed setup-pose world positions and *"writing them a second time in the drawing
|
|
822
822
|
script is the defect that produces a shadow under nobody's feet, and it is
|
|
823
823
|
invisible in both files — each is internally consistent."*
|
|
@@ -993,7 +993,7 @@ designed around it:
|
|
|
993
993
|
> the chest and the legs as siblings. Chosen for identifiability, not anatomy …
|
|
994
994
|
> Under a pelvis the legs sit above the only reliable anchor and every leg comes back
|
|
995
995
|
> `no-anchor` on every frame — measured, not predicted."*
|
|
996
|
-
> — [`2026-09-03-spineboy-2`](https://github.com/
|
|
996
|
+
> — [`2026-09-03-spineboy-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-09-03-spineboy-2/README.md)
|
|
997
997
|
|
|
998
998
|
That is the only place in the corpus where a tree was chosen for what could be
|
|
999
999
|
measured through it rather than for anatomy or for the art, and it is worth knowing
|
|
@@ -1014,7 +1014,7 @@ backwards:
|
|
|
1014
1014
|
`bendPositive` combinations were indistinguishable at 4× zoom on the frame where the
|
|
1015
1015
|
difference is largest. `shin_f.worldX` separated them immediately. Look at frames
|
|
1016
1016
|
for whether it reads; read bone positions for what it is doing."*
|
|
1017
|
-
([`gallery/walk/README.md`](https://github.com/
|
|
1017
|
+
([`gallery/walk/README.md`](https://github.com/rig-c/core/blob/main/gallery/walk/README.md))
|
|
1018
1018
|
|
|
1019
1019
|
### 8.4 ⚠️ And `pose` can be confident about the wrong twin
|
|
1020
1020
|
|
|
@@ -1041,7 +1041,7 @@ list of structural rules and a list of refusals.
|
|
|
1041
1041
|
> at the contact line; `foot_f` and `foot_b` are children of `ground`. Parent a foot
|
|
1042
1042
|
> target to `hip` and the hip carries its own feet up with it, **the solver reports
|
|
1043
1043
|
> success, and the figure hovers.**"*
|
|
1044
|
-
> — [`gallery/walk/README.md`](https://github.com/
|
|
1044
|
+
> — [`gallery/walk/README.md`](https://github.com/rig-c/core/blob/main/gallery/walk/README.md)
|
|
1045
1045
|
|
|
1046
1046
|
Measured on that build, over the stance where `foot_f` is planted: the hip travels
|
|
1047
1047
|
**7.9 units up and 2.9 sideways** while the foot reads `382.0, 68.0` on every
|
|
@@ -1067,15 +1067,15 @@ reaches the target. So:
|
|
|
1067
1067
|
⭐ **This is where the art and the hierarchy stop being two decisions.** Both plates
|
|
1068
1068
|
have to be **drawn from their own joint**, and the joint's coordinates *inside each
|
|
1069
1069
|
drawing* are what every `length` and every attachment offset is measured from —
|
|
1070
|
-
[`gallery/rigby.ts`](https://github.com/
|
|
1071
|
-
[`gallery/walk/rig.json`](https://github.com/
|
|
1070
|
+
[`gallery/rigby.ts`](https://github.com/rig-c/core/blob/main/gallery/rigby.ts) names all four points for one leg, and
|
|
1071
|
+
[`gallery/walk/rig.json`](https://github.com/rig-c/core/blob/main/gallery/walk/rig.json) states them.
|
|
1072
1072
|
|
|
1073
1073
|
### 9.3 A constraint carries the whole subtree, and that is the point
|
|
1074
1074
|
|
|
1075
1075
|
> *"`cart` is the constrained bone; `wheel_b`/`wheel_f` and `hip` are its children,
|
|
1076
1076
|
> so the constraint carries the wheels and the whole figure with it and the tangent
|
|
1077
1077
|
> rotation tilts all three."*
|
|
1078
|
-
> — [`gallery/ride/README.md`](https://github.com/
|
|
1078
|
+
> — [`gallery/ride/README.md`](https://github.com/rig-c/core/blob/main/gallery/ride/README.md)
|
|
1079
1079
|
|
|
1080
1080
|
📌 Two placement conveniences from the same paragraph, both of the form *put the
|
|
1081
1081
|
bone where the numbers become trivial*: `root` is at the world origin, *"which is why
|
|
@@ -1104,7 +1104,7 @@ default failure mode**, because it drives bones rather than drawing anything.
|
|
|
1104
1104
|
> `true`, so any IK timeline that did not restate it overwrote the constraint's value
|
|
1105
1105
|
> for the whole animation, with the field still in the file and the gate green either
|
|
1106
1106
|
> way."*
|
|
1107
|
-
> — [`gallery/walk/README.md`](https://github.com/
|
|
1107
|
+
> — [`gallery/walk/README.md`](https://github.com/rig-c/core/blob/main/gallery/walk/README.md)
|
|
1108
1108
|
> (rigc stamps the rig's value onto every emitted ik key)
|
|
1109
1109
|
|
|
1110
1110
|
⇒ **A structural fact stated in the rig can be overwritten by a per-key default in
|
|
@@ -1121,7 +1121,7 @@ all**, unanimously, and paid for it in a measure:
|
|
|
1121
1121
|
> point at if asked why there are four rather than one … that measure is the price of
|
|
1122
1122
|
> the rule, and it is worth paying rather than winning by guessing."*
|
|
1123
1123
|
> — rung 8, first attempt
|
|
1124
|
-
> ([`2026-08-23-rung8-1`](https://github.com/
|
|
1124
|
+
> ([`2026-08-23-rung8-1`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-rung8-1/LOOP.md) §13)
|
|
1125
1125
|
|
|
1126
1126
|
⭐ **The cleanest demonstration that the rule costs something real and is still the
|
|
1127
1127
|
right rule** is in that same run, which authored two rigs against two references:
|
|
@@ -1171,7 +1171,7 @@ the draw order"* — which is what lets one part sit in front of its own parent:
|
|
|
1171
1171
|
> arrangement that satisfies both things the frames measure: the gun reads unoccluded
|
|
1172
1172
|
> in `idle`, and the near leg covers it in `walk`."*
|
|
1173
1173
|
> — spineboy attempt 2
|
|
1174
|
-
> ([`2026-08-23-spineboy-2`](https://github.com/
|
|
1174
|
+
> ([`2026-08-23-spineboy-2`](https://github.com/rig-c/core/blob/main/bench/runs/2026-08-23-spineboy-2/README.md))
|
|
1175
1175
|
|
|
1176
1176
|
⚖️ **But parentage is a real question with a real answer, and the evidence for it is
|
|
1177
1177
|
smoothness.** Rung 4 modelled a chain's top as a *sibling* of the disc it hangs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.40.0",
|
|
4
4
|
"description": "AI-authored 2D rigging and animation, verified before it is written. rigc compiles a rig spec into skeleton data in the Spine 4.3 format, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"author": "firejune",
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
|
40
|
-
"url": "https://github.com/
|
|
40
|
+
"url": "https://github.com/rig-c/core.git"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://github.com/
|
|
43
|
-
"bugs": "https://github.com/
|
|
42
|
+
"homepage": "https://github.com/rig-c/core#readme",
|
|
43
|
+
"bugs": "https://github.com/rig-c/core/issues",
|
|
44
44
|
"keywords": [
|
|
45
45
|
"spine",
|
|
46
46
|
"spine2d",
|
package/skills/rigc/SKILL.md
CHANGED
|
@@ -109,25 +109,25 @@ model space, its UVs, its triangles — which is a mesh attachment with those th
|
|
|
109
109
|
(AUTHORING §3.4, *Mesh attachment*); a region at the drawable's bounding-box centre
|
|
110
110
|
keeps only the box. rigc reads none of those formats — FACE §11, the paragraph
|
|
111
111
|
that opens *No Live2D file is read or written*
|
|
112
|
-
([FACE.md](https://github.com/
|
|
112
|
+
([FACE.md](https://github.com/rig-c/core/blob/main/docs/FACE.md#11-non-goals--stated-so-nobody-proposes-them-as-gaps)) —
|
|
113
113
|
only the pictures they produce. The rule, the background those frames need and
|
|
114
114
|
what a wrong one costs: AUTHORING §0, *When the source is a foreign player*.
|
|
115
115
|
|
|
116
116
|
## Which guide, for which need
|
|
117
117
|
|
|
118
|
-
Read [AUTHORING.md](https://github.com/
|
|
118
|
+
Read [AUTHORING.md](https://github.com/rig-c/core/blob/main/docs/AUTHORING.md) first, whatever the need: the two
|
|
119
119
|
spec files field by field, the emission rules, the loop, and the failure map. Then:
|
|
120
120
|
|
|
121
121
|
| The request is… | Open | Skill |
|
|
122
122
|
| --- | --- | --- |
|
|
123
|
-
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](https://github.com/
|
|
124
|
-
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](https://github.com/
|
|
125
|
-
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](https://github.com/
|
|
126
|
-
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](https://github.com/
|
|
127
|
-
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](https://github.com/
|
|
123
|
+
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md) | `rigc-rigging` |
|
|
124
|
+
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md) | `rigc-motion` |
|
|
125
|
+
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](https://github.com/rig-c/core/blob/main/docs/FACE.md) | `rigc-face` |
|
|
126
|
+
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) | `rigc-ingest` |
|
|
127
|
+
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](https://github.com/rig-c/core/blob/main/docs/PROMPTING.md) | — |
|
|
128
128
|
|
|
129
129
|
Every guide linked here is in the installed package at `node_modules/rig-c/docs/`,
|
|
130
130
|
which is the copy that matches the rigc you run; the links go to the repository's
|
|
131
131
|
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
132
132
|
Formats, the CLI reference and the licence chain:
|
|
133
|
-
[README.md](https://github.com/
|
|
133
|
+
[README.md](https://github.com/rig-c/core/blob/main/README.md), installed at `node_modules/rig-c/README.md`.
|
|
@@ -9,7 +9,7 @@ compatibility: Requires Bun 1.2 or later and the npm package rig-c.
|
|
|
9
9
|
|
|
10
10
|
Load this when the request is a **head rather than a body**: a drawn face that
|
|
11
11
|
breathes, blinks, moves its eyes and turns a few degrees off axis. Every rule below
|
|
12
|
-
is owned by [FACE.md](https://github.com/
|
|
12
|
+
is owned by [FACE.md](https://github.com/rig-c/core/blob/main/docs/FACE.md); this file says when to open it and what
|
|
13
13
|
it will not do for you.
|
|
14
14
|
|
|
15
15
|
## Non-negotiables
|
|
@@ -40,16 +40,16 @@ differential audit and the three limits it does not lift.
|
|
|
40
40
|
|
|
41
41
|
## Read, in this order
|
|
42
42
|
|
|
43
|
-
1. [AUTHORING.md](https://github.com/
|
|
43
|
+
1. [AUTHORING.md](https://github.com/rig-c/core/blob/main/docs/AUTHORING.md) — the `deform` timeline field by field
|
|
44
44
|
and what rigc refuses in it (§4), the failure map (§5–§6), the editor's
|
|
45
45
|
conventions (§10).
|
|
46
|
-
2. [MOTION.md](https://github.com/
|
|
46
|
+
2. [MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md) — timing, easing and the offset table (§3),
|
|
47
47
|
candidates and the ballot (§4–§5). A blink and a gaze are ordinary motion work.
|
|
48
|
-
3. [FACE.md](https://github.com/
|
|
48
|
+
3. [FACE.md](https://github.com/rig-c/core/blob/main/docs/FACE.md) — the face's own geometry: the closed form every
|
|
49
49
|
number in a turn comes from (§1), the hierarchy underneath (§3), what
|
|
50
50
|
foreshortens (§5), and the deform audit gap (§9).
|
|
51
|
-
4. Then [RIGGING.md](https://github.com/
|
|
52
|
-
rather than this closed form, and [INGEST.md](https://github.com/
|
|
51
|
+
4. Then [RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md) for the hierarchy as a general rule
|
|
52
|
+
rather than this closed form, and [INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) if the head
|
|
53
53
|
arrived as a compiled skeleton.
|
|
54
54
|
|
|
55
55
|
Every guide linked here is in the installed package at `node_modules/rig-c/docs/`,
|
|
@@ -10,7 +10,7 @@ compatibility: Requires Bun 1.2 or later and the npm package rig-c.
|
|
|
10
10
|
Load this when what you were handed is **already a skeleton**: a `skeleton.json`
|
|
11
11
|
with its `.atlas` and page images, and a request to understand it, answer a
|
|
12
12
|
complaint about it, re-express it, or extend it. Every rule below is owned by
|
|
13
|
-
[INGEST.md](https://github.com/
|
|
13
|
+
[INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md); this file says when to open it and what it will
|
|
14
14
|
not do for you.
|
|
15
15
|
|
|
16
16
|
## Non-negotiables
|
|
@@ -61,14 +61,14 @@ thing to reach for; transcription by hand is what you fall back on for a constru
|
|
|
61
61
|
|
|
62
62
|
## Read, in this order
|
|
63
63
|
|
|
64
|
-
1. [INGEST.md](https://github.com/
|
|
64
|
+
1. [INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) — what every command will and will not do with
|
|
65
65
|
a foreign file (§0), `ingest` and transcription (§2), what each validator
|
|
66
66
|
complaint means on an export (§3), and the re-pivot, rename and extend
|
|
67
67
|
recipes (§4).
|
|
68
|
-
2. [AUTHORING.md](https://github.com/
|
|
68
|
+
2. [AUTHORING.md](https://github.com/rig-c/core/blob/main/docs/AUTHORING.md) — the two spec files the transcription
|
|
69
69
|
targets (§3–§4), the failure map (§5–§6), and the coordinate contract (§11.2).
|
|
70
|
-
3. Then [RIGGING.md](https://github.com/
|
|
71
|
-
it has, and [MOTION.md](https://github.com/
|
|
70
|
+
3. Then [RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md) for why the re-pivot edit has the shape
|
|
71
|
+
it has, and [MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md) for the animation you are adding.
|
|
72
72
|
|
|
73
73
|
Every guide linked here is in the installed package at `node_modules/rig-c/docs/`,
|
|
74
74
|
which is the copy that matches the rigc you run; the links go to the repository's
|
|
@@ -10,7 +10,7 @@ compatibility: Requires Bun 1.2 or later and the npm package rig-c.
|
|
|
10
10
|
Load this when the request is a **movement rather than a skeleton**: a sentence of
|
|
11
11
|
intent, between zero and N pictures of what the movement passes through, and a
|
|
12
12
|
Spine animation somebody would choose coming back. Every rule below is owned by
|
|
13
|
-
[MOTION.md](https://github.com/
|
|
13
|
+
[MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md); this file says when to open it and what it will
|
|
14
14
|
not do for you.
|
|
15
15
|
|
|
16
16
|
## Non-negotiables
|
|
@@ -31,16 +31,16 @@ thing that judges a movement is a person's eye through `rigc vote` — MOTION §
|
|
|
31
31
|
|
|
32
32
|
## Read, in this order
|
|
33
33
|
|
|
34
|
-
1. [AUTHORING.md](https://github.com/
|
|
34
|
+
1. [AUTHORING.md](https://github.com/rig-c/core/blob/main/docs/AUTHORING.md) — the motion spec field by field (§4),
|
|
35
35
|
the failure map (§5–§6), reading reference frames (§8) and checking against
|
|
36
36
|
them (§9), what the editor does when nobody tells it otherwise (§10), and
|
|
37
37
|
reading a pose out of a picture (§11–§12).
|
|
38
|
-
2. [MOTION.md](https://github.com/
|
|
38
|
+
2. [MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md) — the normal form every motion request
|
|
39
39
|
reduces to (§0), timing, easing and the per-bone offset table (§3), and how to
|
|
40
40
|
spread candidates so a ballot informs (§4–§5).
|
|
41
|
-
3. Then [RIGGING.md](https://github.com/
|
|
42
|
-
to decide; [FACE.md](https://github.com/
|
|
43
|
-
head turn; [INGEST.md](https://github.com/
|
|
41
|
+
3. Then [RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md) if the skeleton itself is what you have
|
|
42
|
+
to decide; [FACE.md](https://github.com/rig-c/core/blob/main/docs/FACE.md) if the movement is a blink, a gaze or a
|
|
43
|
+
head turn; [INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) if the rig arrived as a compiled
|
|
44
44
|
skeleton rather than loose parts.
|
|
45
45
|
|
|
46
46
|
Every guide linked here is in the installed package at `node_modules/rig-c/docs/`,
|
|
@@ -9,7 +9,7 @@ compatibility: Requires Bun 1.2 or later and the npm package rig-c.
|
|
|
9
9
|
|
|
10
10
|
Load this when the request is a **skeleton rather than a movement**: loose part
|
|
11
11
|
PNGs in, a bone hierarchy out. Every rule below is owned by
|
|
12
|
-
[RIGGING.md](https://github.com/
|
|
12
|
+
[RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md); this file says when to open it and what it
|
|
13
13
|
will not do for you.
|
|
14
14
|
|
|
15
15
|
## Non-negotiables
|
|
@@ -30,15 +30,15 @@ are named in RIGGING §11 — neither as a pass bar.
|
|
|
30
30
|
|
|
31
31
|
## Read, in this order
|
|
32
32
|
|
|
33
|
-
1. [AUTHORING.md](https://github.com/
|
|
33
|
+
1. [AUTHORING.md](https://github.com/rig-c/core/blob/main/docs/AUTHORING.md) — the rig spec field by field (§3),
|
|
34
34
|
the failure map (§5–§6), reading a pose out of a picture (§8.1, §11, §12),
|
|
35
35
|
and the editor's own conventions (§10).
|
|
36
|
-
2. [RIGGING.md](https://github.com/
|
|
36
|
+
2. [RIGGING.md](https://github.com/rig-c/core/blob/main/docs/RIGGING.md) — the structure itself: what identifies a
|
|
37
37
|
pivot and what moving one costs (§2–§3), gauges (§4), what a chain can reach
|
|
38
38
|
(§6), and the instruments that see structure (§11).
|
|
39
|
-
3. Then [MOTION.md](https://github.com/
|
|
40
|
-
[FACE.md](https://github.com/
|
|
41
|
-
[INGEST.md](https://github.com/
|
|
39
|
+
3. Then [MOTION.md](https://github.com/rig-c/core/blob/main/docs/MOTION.md) once the skeleton exists;
|
|
40
|
+
[FACE.md](https://github.com/rig-c/core/blob/main/docs/FACE.md) if the figure is a head;
|
|
41
|
+
[INGEST.md](https://github.com/rig-c/core/blob/main/docs/INGEST.md) if the skeleton was handed to you already
|
|
42
42
|
compiled.
|
|
43
43
|
|
|
44
44
|
Every guide linked here is in the installed package at `node_modules/rig-c/docs/`,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A51, the body (issue #1351): the bones a rig declares to share an axis lie
|
|
3
|
+
* along it at the setup pose.
|
|
4
|
+
*
|
|
5
|
+
* For each `invariants.axes` entry the line is drawn through the setup-pose
|
|
6
|
+
* world origins of its two `through` bones, first to second, and every bone
|
|
7
|
+
* of its `bones` must have its setup-pose world x axis within `tolerance`
|
|
8
|
+
* degrees of that line — as a line, so a bone pointing the opposite way (a
|
|
9
|
+
* mirrored bone, a world scaleX of −1) lies along it too. Why the rule exists,
|
|
10
|
+
* and why it holds bones no animation keys, is above its `check` call in
|
|
11
|
+
* `validate()` and on `RigInvariants.axes` in `src/rig.ts`.
|
|
12
|
+
*
|
|
13
|
+
* Reads the declaration from the rig info and the pose from a fact
|
|
14
|
+
* (`../facts/setup_world.ts`), so the body is one function over spine-core's
|
|
15
|
+
* setup pose and rigc's core's. Every angle it prints is a degree in Spine's
|
|
16
|
+
* world (y up), counter-clockwise from +x.
|
|
17
|
+
*
|
|
18
|
+
* Links nothing from the runtime.
|
|
19
|
+
*/
|
|
20
|
+
import type { Verdicts } from '../harness.ts';
|
|
21
|
+
import type { SetupBoneFrame, SetupWorldFacts } from '../facts/setup_world.ts';
|
|
22
|
+
import type { RigInfo } from '../../types.ts';
|
|
23
|
+
|
|
24
|
+
const CODE = 'A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS';
|
|
25
|
+
|
|
26
|
+
/** Degrees, to the four places every angle on this rule's lines is printed at. */
|
|
27
|
+
const deg = (v: number): string => `${v.toFixed(4)}°`;
|
|
28
|
+
|
|
29
|
+
/** How far apart two directions are as lines, in degrees: 0 to 90. */
|
|
30
|
+
export function lineGap(a: number, b: number): number {
|
|
31
|
+
const d = (((a - b) % 180) + 180) % 180;
|
|
32
|
+
return Math.min(d, 180 - d);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The world angle of a bone's x axis, in degrees — `atan2(c, a)`, the angle spine-core's `getWorldRotationX` reports. */
|
|
36
|
+
export function worldAxisDegrees(frame: SetupBoneFrame): number {
|
|
37
|
+
return (Math.atan2(frame.c, frame.a) * 180) / Math.PI;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function a51BonesLieAlongTheirDeclaredAxis({ fail, skip }: Verdicts, facts: SetupWorldFacts, input: { rig?: RigInfo }): void {
|
|
41
|
+
const rig = input.rig;
|
|
42
|
+
if (!rig) return skip(CODE, 'no rig info (validating a bare directory)');
|
|
43
|
+
if (!rig.axes.length) return skip(CODE, `the rig "${rig.archetype}" declares no axes (invariants.axes)`);
|
|
44
|
+
/** A bone's frame, or the failure that says why there is none. */
|
|
45
|
+
const placed = (axis: string, bone: string): SetupBoneFrame | null => {
|
|
46
|
+
const frame = facts.frameOf(bone);
|
|
47
|
+
if (frame === null) {
|
|
48
|
+
fail(CODE, `axis "${axis}" names bone "${bone}", which the skeleton does not have`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (typeof frame === 'string') {
|
|
52
|
+
fail(CODE, `axis "${axis}": ${frame}, so it cannot be held to the axis`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return frame;
|
|
56
|
+
};
|
|
57
|
+
for (const axis of rig.axes) {
|
|
58
|
+
const [from, to] = axis.through;
|
|
59
|
+
const a = placed(axis.name, from);
|
|
60
|
+
const b = placed(axis.name, to);
|
|
61
|
+
if (a === null || b === null) continue;
|
|
62
|
+
const dx = b.worldX - a.worldX;
|
|
63
|
+
const dy = b.worldY - a.worldY;
|
|
64
|
+
// Exactly one origin, and no threshold: any distance above 0 draws a line, and a threshold would be a number the rig did not state.
|
|
65
|
+
if (dx === 0 && dy === 0) {
|
|
66
|
+
fail(
|
|
67
|
+
CODE,
|
|
68
|
+
`axis "${axis.name}": "${from}" and "${to}" share the world origin (${a.worldX}, ${a.worldY}) at the setup pose, so they draw no line — ` +
|
|
69
|
+
'name two bones whose origins differ',
|
|
70
|
+
);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const line = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
74
|
+
const head = `axis "${axis.name}" (through "${from}"→"${to}", ${deg(line)})`;
|
|
75
|
+
for (const bone of axis.bones) {
|
|
76
|
+
const frame = placed(axis.name, bone);
|
|
77
|
+
if (frame === null) continue;
|
|
78
|
+
if (frame.a === 0 && frame.c === 0) {
|
|
79
|
+
fail(CODE, `${head}: bone "${bone}" has no world x axis at the setup pose (the x column of its world matrix is 0, 0 — a scale of 0), so it lies along no line`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const found = worldAxisDegrees(frame);
|
|
83
|
+
const off = lineGap(found, line);
|
|
84
|
+
if (off <= axis.tolerance) continue;
|
|
85
|
+
fail(
|
|
86
|
+
CODE,
|
|
87
|
+
`${head}: bone "${bone}" world x axis at ${deg(found)}, ${deg(off)} off, tolerance ${axis.tolerance}° — ` +
|
|
88
|
+
'rotate the bone in the rig spec onto the line and counter-rotate its attachments so the setup pose stays put',
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Each bone's world frame at the setup pose (issue #1351) — what
|
|
3
|
+
* `A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS` reads: a bone's world origin, and
|
|
4
|
+
* the column of its world matrix its x axis points along.
|
|
5
|
+
*
|
|
6
|
+
* ⭐ **The pose is A50's**, on both suppliers: a fresh skeleton at the setup
|
|
7
|
+
* pose with constraints applied, no physics stepped and no skin set —
|
|
8
|
+
* `setupPose()` then `updateWorldTransform(Physics.none)` through spine-core
|
|
9
|
+
* (`spineSetupWorld` in `../../validate.ts`), `poseSetup` under no skin
|
|
10
|
+
* through rigc's core (`../model/setup_world.ts`). `tools/core_gate.ts` holds
|
|
11
|
+
* the core's setup world to spine-core's at tolerance 0, which is what lets the
|
|
12
|
+
* two print one line.
|
|
13
|
+
*
|
|
14
|
+
* A bone is answered lazily: a rig that declares no axis poses nothing.
|
|
15
|
+
*
|
|
16
|
+
* Links nothing from the runtime.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** A bone's world frame at the setup pose: its origin, and its world x axis `(a, c)`. */
|
|
20
|
+
export interface SetupBoneFrame {
|
|
21
|
+
readonly worldX: number;
|
|
22
|
+
readonly worldY: number;
|
|
23
|
+
readonly a: number;
|
|
24
|
+
readonly c: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** What A51 reads. */
|
|
28
|
+
export interface SetupWorldFacts {
|
|
29
|
+
/**
|
|
30
|
+
* The bone's frame at the setup pose; a sentence saying why the setup pose
|
|
31
|
+
* places it nowhere (inactive with no skin set, a setup pose the core does
|
|
32
|
+
* not pose); or `null` when the skeleton has no bone of that name.
|
|
33
|
+
*/
|
|
34
|
+
frameOf(bone: string): SetupBoneFrame | string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The sentence for a bone the setup pose leaves inactive — one spelling for both suppliers. */
|
|
38
|
+
export function inactiveAtSetup(bone: string): string {
|
|
39
|
+
return `bone "${bone}" is inactive with no skin set, so the setup pose places it nowhere`;
|
|
40
|
+
}
|
package/src/assertions/kinds.ts
CHANGED
|
@@ -97,6 +97,7 @@ export const ASSERTION_KIND: Readonly<Record<string, AssertionKind>> = {
|
|
|
97
97
|
A48_TRANSFORM_CONSTRAINT_NOT_MUTED_THROUGHOUT: 'validity',
|
|
98
98
|
A49_PACKED_FOOTPRINTS_DO_NOT_OVERLAP: 'renderer',
|
|
99
99
|
A50_STAGE_BOX_IS_THE_STAGE: 'validity',
|
|
100
|
+
A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS: 'validity',
|
|
100
101
|
};
|
|
101
102
|
|
|
102
103
|
/** Whether `profile` runs a rule of `kind`: `spine` runs validity alone, `spine-html` every kind. */
|
|
@@ -75,6 +75,7 @@ const RIG_FIELDS: Readonly<Record<keyof RigInfo, Check>> = {
|
|
|
75
75
|
deformMayFold: list(str),
|
|
76
76
|
consumerDrivenMix: list(shape({ type: oneOf(['ik', 'transform']), constraint: str, why: str })),
|
|
77
77
|
idleDrivesMeshes: nullable(str),
|
|
78
|
+
axes: list(shape({ name: str, through: list(str, 2), bones: list(str), tolerance: num })),
|
|
78
79
|
basePlates: list(str),
|
|
79
80
|
meshSlotBudget: nullable(num),
|
|
80
81
|
meshTriangleBudget: nullable(num),
|