spine-rigc 2.49.0 → 2.51.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/NOTICE.md +2 -2
- package/README.md +8 -8
- package/docs/AUTHORING.md +31 -33
- package/docs/FACE.md +18 -18
- package/docs/INGEST.md +17 -15
- package/docs/RIGGING.md +3 -3
- package/package.json +1 -1
- package/skills/rigc-face/SKILL.md +1 -1
- package/src/assertions/bodies/a06.ts +1 -1
- package/src/assertions/bodies/a12.ts +6 -3
- package/src/assertions/bodies/a19.ts +7 -7
- package/src/assertions/bodies/a20.ts +2 -2
- package/src/assertions/emitted/index.ts +4 -2
- package/src/assertions/facts/atlas_regions.ts +2 -2
- package/src/assertions/facts/skin_entries.ts +1 -3
- package/src/assertions/harness.ts +5 -1
- package/src/assertions/kinds.ts +35 -5
- package/src/assertions/model/index.ts +6 -8
- package/src/assertions/model/skin_entries.ts +2 -4
- package/src/assertions/reasons.ts +1 -1
- package/src/assertions/report.ts +13 -2
- package/src/atlas.ts +1 -1
- package/src/cli/shared.ts +25 -17
- package/src/cli/spine_commands.ts +1 -1
- package/src/compile.ts +1 -1
- package/src/rig.ts +1 -1
- package/src/validate.ts +18 -24
- package/src/assertions/bodies/a11.ts +0 -15
- package/src/assertions/bodies/a27.ts +0 -33
package/src/validate.ts
CHANGED
|
@@ -77,14 +77,13 @@ import {
|
|
|
77
77
|
walkTimelines,
|
|
78
78
|
} from './timelines.ts';
|
|
79
79
|
import type { RigInfo } from './types.ts';
|
|
80
|
-
import { ASSERTION_KIND, kindRunsUnder, type AssertionProfile } from './assertions/kinds.ts';
|
|
80
|
+
import { ASSERTION_KIND, kindRunsUnder, resolveProfile, type AssertionProfile } from './assertions/kinds.ts';
|
|
81
81
|
import { verdictHarness, type Failure } from './assertions/harness.ts';
|
|
82
82
|
import { isObj, type Json } from './assertions/values.ts';
|
|
83
83
|
import type { SkinEntryFacts } from './assertions/facts/skin_entries.ts';
|
|
84
84
|
import type { AtlasPageFacts } from './assertions/facts/atlas_pages.ts';
|
|
85
85
|
import type { SlotColourFacts, SlotTimelines } from './assertions/facts/slot_colour.ts';
|
|
86
86
|
import { a03RegionWidthHeightFinite } from './assertions/bodies/a03.ts';
|
|
87
|
-
import { a11NoClippingAttachments } from './assertions/bodies/a11.ts';
|
|
88
87
|
import { a17AtlasPageFilesExist } from './assertions/bodies/a17.ts';
|
|
89
88
|
import { a18DeterministicEmit } from './assertions/bodies/a18.ts';
|
|
90
89
|
import { a45SeparableColorTimelinesOwnTheirChannelsAndPoseAsWritten } from './assertions/bodies/a45.ts';
|
|
@@ -108,7 +107,6 @@ import { a50StageBoxIsTheStage } from './assertions/bodies/a50.ts';
|
|
|
108
107
|
import { a51BonesLieAlongTheirDeclaredAxis } from './assertions/bodies/a51.ts';
|
|
109
108
|
import { inactiveAtSetup, type SetupWorldFacts } from './assertions/facts/setup_world.ts';
|
|
110
109
|
import { SKIP_NO_STAGE_BOX, type StageBoxFacts } from './assertions/facts/stage_box.ts';
|
|
111
|
-
import { a27RegionNameMatchesPageFilename } from './assertions/bodies/a27.ts';
|
|
112
110
|
import type { MeshEntry as MeshAttachmentEntry, MeshFacts } from './assertions/facts/mesh_attachments.ts';
|
|
113
111
|
import type { ClipEnd, PolygonEntry, PolygonFacts } from './assertions/facts/vertex_polygons.ts';
|
|
114
112
|
import type { LinkEntry, LinkFacts } from './assertions/facts/linked_meshes.ts';
|
|
@@ -168,14 +166,14 @@ export type { Failure } from './assertions/harness.ts';
|
|
|
168
166
|
* ⭐ The distinction this draws is the difference between "wrong" and "not how we
|
|
169
167
|
* do it here", and conflating the two is how a validator stops being usable on
|
|
170
168
|
* anybody else's data. Fifteen of the 40 assertions are policy — seven for one
|
|
171
|
-
* renderer (`
|
|
169
|
+
* renderer (`rig-play`) and one project's canvas budget, eight for rigc's own
|
|
172
170
|
* formations — and every one of them fires
|
|
173
171
|
* on real, correct, editor-produced Spine data — the official example projects
|
|
174
172
|
* carry clipping attachments, unweighted meshes, 116-triangle meshes and packed
|
|
175
173
|
* atlases, all of which are perfectly valid and none of which spine-html likes.
|
|
176
174
|
*
|
|
177
175
|
* - `spine` — is this valid Spine 4.3 that any runtime will play correctly?
|
|
178
|
-
* - `
|
|
176
|
+
* - `rig-play` — the above, plus this project's renderer and archetype policy.
|
|
179
177
|
*
|
|
180
178
|
* ⚠️ `validate()` has NO default profile — `ValidateInput.profile` is required,
|
|
181
179
|
* and that is deliberate. A silent default here can only be wrong in one of two
|
|
@@ -207,7 +205,7 @@ export const ASSERTION_NAMES: readonly string[] = Object.keys(ASSERTION_KIND);
|
|
|
207
205
|
|
|
208
206
|
/** How many assertions this profile applies, by the kinds it carries. */
|
|
209
207
|
export function assertionCountForProfile(profile: ValidateProfile): number {
|
|
210
|
-
if (profile === '
|
|
208
|
+
if (profile === 'rig-play') return ASSERTION_NAMES.length;
|
|
211
209
|
return ASSERTION_NAMES.filter((name) => ASSERTION_KIND[name] === 'validity').length;
|
|
212
210
|
}
|
|
213
211
|
|
|
@@ -846,14 +844,12 @@ export function timelineAddCells(data: ReturnType<SkeletonJson['readSkeletonData
|
|
|
846
844
|
*/
|
|
847
845
|
export function spineSkinEntries(data: ReturnType<SkeletonJson['readSkeletonData']>): SkinEntryFacts {
|
|
848
846
|
const regionAttachments: RegionAttachment[] = [];
|
|
849
|
-
let clippingCount = 0;
|
|
850
847
|
for (const skin of data.skins) {
|
|
851
848
|
for (const entry of skin.getAttachments()) {
|
|
852
849
|
if (entry.attachment instanceof RegionAttachment) regionAttachments.push(entry.attachment);
|
|
853
|
-
else if (entry.attachment instanceof ClippingAttachment) clippingCount++;
|
|
854
850
|
}
|
|
855
851
|
}
|
|
856
|
-
return { regionAttachments
|
|
852
|
+
return { regionAttachments };
|
|
857
853
|
}
|
|
858
854
|
|
|
859
855
|
/**
|
|
@@ -2012,9 +2008,10 @@ function once<T>(supply: () => T): () => T {
|
|
|
2012
2008
|
}
|
|
2013
2009
|
|
|
2014
2010
|
export function validate(input: ValidateInput): ValidateReport {
|
|
2015
|
-
|
|
2011
|
+
// A former name reads as its profile (issue #1277): the policy switch below and the report both use the resolved one.
|
|
2012
|
+
const profile = resolveProfile(input.profile, 'validate');
|
|
2016
2013
|
/** True when this profile's rulebook includes the policy layer. */
|
|
2017
|
-
const policy = profile === '
|
|
2014
|
+
const policy = profile === 'rig-play';
|
|
2018
2015
|
// The harness — `check`, `fail`, `skip` and the lists they fill — is
|
|
2019
2016
|
// `./assertions/harness.ts`'s since issue #1025, and the rule is the one it
|
|
2020
2017
|
// always was: the model side runs the bodies that moved there in the same
|
|
@@ -2566,7 +2563,7 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
2566
2563
|
});
|
|
2567
2564
|
|
|
2568
2565
|
// --- A12: no dark / two-colour tint --------------------------------------
|
|
2569
|
-
// Parsed, then
|
|
2566
|
+
// Parsed, then ignored by rig-play's DOM mode.
|
|
2570
2567
|
check('A12_NO_DARK_COLOR', () => a12NoDarkColor(verdicts, rawSkeletonRoster(raw), rawSlotTimelines(raw)));
|
|
2571
2568
|
|
|
2572
2569
|
// --- A05: curve arrays are 4 numbers per value channel --------------------
|
|
@@ -2667,8 +2664,9 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
2667
2664
|
// `./assertions/bodies/a33.ts` (issue #1025, cut 4c-2).
|
|
2668
2665
|
check('A33_VERTEX_ATTACHMENT_GEOMETRY', () => a33VertexAttachmentGeometry(verdicts, polygonFacts()));
|
|
2669
2666
|
|
|
2670
|
-
// ---
|
|
2671
|
-
|
|
2667
|
+
// --- A13 / A14: canvas budgets ----
|
|
2668
|
+
// A11 stood here until issue #1404 retired it: the player `rig-play` names
|
|
2669
|
+
// draws a clipping attachment in both of its modes.
|
|
2672
2670
|
// 📐 The two numbers come from the rig spec's `invariants`, never from here.
|
|
2673
2671
|
// A mesh budget is one consumer's frame time written down — the editor's own
|
|
2674
2672
|
// example projects ship meshes many times denser and they are valid — so a
|
|
@@ -3309,7 +3307,7 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
3309
3307
|
// first build — and told them, untruthfully, that the file held no transparency
|
|
3310
3308
|
// at all. That audit is also why the CLI's default is now `spine`, which does
|
|
3311
3309
|
// not run this rule at all (#221): a stranger reaches this refusal only by
|
|
3312
|
-
// asking for `
|
|
3310
|
+
// asking for `rig-play`. It still has to be both TRUE and actionable when it
|
|
3313
3311
|
// fires, and it now has to name the profile it belongs to as well as the one
|
|
3314
3312
|
// that does not ask — the reader opted in, and the message is where they find
|
|
3315
3313
|
// out what they opted into.
|
|
@@ -3385,15 +3383,11 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
3385
3383
|
// what a `drawOrder` key's offsets are counted against.
|
|
3386
3384
|
check('A26_SLOT_DRAW_ORDER', () => a26SlotDrawOrder(verdicts, rawSkeletonRoster(raw), input));
|
|
3387
3385
|
|
|
3388
|
-
//
|
|
3389
|
-
//
|
|
3390
|
-
//
|
|
3391
|
-
//
|
|
3392
|
-
//
|
|
3393
|
-
// called anything at all, every attachment could agree with it, and the rig
|
|
3394
|
-
// would load with the wrong pixels under the right name. One part per page (A06
|
|
3395
|
-
// forces it) makes the check exact.
|
|
3396
|
-
check('A27_REGION_NAME_MATCHES_PAGE_FILENAME', () => a27RegionNameMatchesPageFilename(verdicts, { atlas }));
|
|
3386
|
+
// A27 stood here until issue #1404 retired it. It held a page's only region
|
|
3387
|
+
// to the page's file basename, a join no reader makes: spine-core and the
|
|
3388
|
+
// player `rig-play` names both take a region off the page record the atlas
|
|
3389
|
+
// states it under, and the player draws a region on a page named for
|
|
3390
|
+
// something else in both of its modes.
|
|
3397
3391
|
|
|
3398
3392
|
// --- A28: a ribbon's rows share their weights ----------------------------
|
|
3399
3393
|
//
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A11, the body (issue #1025, step 4c of #380): no clipping attachment — the
|
|
3
|
-
* renderer skips them silently. A `renderer` rule, so `spine` never runs it.
|
|
4
|
-
*
|
|
5
|
-
* Moved out of `src/validate.ts` unchanged but for what it reads: the count is
|
|
6
|
-
* a fact (`../facts/skin_entries.ts`).
|
|
7
|
-
*/
|
|
8
|
-
import type { Verdicts } from '../harness.ts';
|
|
9
|
-
import type { SkinEntryFacts } from '../facts/skin_entries.ts';
|
|
10
|
-
|
|
11
|
-
export function a11NoClippingAttachments({ fail }: Verdicts, { clippingCount }: SkinEntryFacts): void {
|
|
12
|
-
if (clippingCount > 0) {
|
|
13
|
-
fail('A11_NO_CLIPPING_ATTACHMENTS', `${clippingCount} clipping attachment(s); the renderer skips them silently`);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A27, the body (issue #1025, step 4c of #380): the only region on a page is
|
|
3
|
-
* named for the page's file.
|
|
4
|
-
*
|
|
5
|
-
* Moved out of `src/validate.ts` unchanged but for what it reads: the regions
|
|
6
|
-
* are a fact (`../facts/atlas_regions.ts`). Why the second link of the
|
|
7
|
-
* attachment -> region -> file chain needs holding stays above the `check`
|
|
8
|
-
* call in `validate()`.
|
|
9
|
-
*/
|
|
10
|
-
import { basename } from 'node:path';
|
|
11
|
-
import type { Verdicts } from '../harness.ts';
|
|
12
|
-
import type { AtlasRegionFacts } from '../facts/atlas_regions.ts';
|
|
13
|
-
import { SKIP_NO_ATLAS, SKIP_NO_ATLAS_REGION } from '../reasons.ts';
|
|
14
|
-
|
|
15
|
-
export function a27RegionNameMatchesPageFilename({ fail, skip }: Verdicts, { atlas }: AtlasRegionFacts): void {
|
|
16
|
-
if (!atlas) return skip('A27_REGION_NAME_MATCHES_PAGE_FILENAME', SKIP_NO_ATLAS);
|
|
17
|
-
if (atlas.regions.length === 0) return skip('A27_REGION_NAME_MATCHES_PAGE_FILENAME', SKIP_NO_ATLAS_REGION);
|
|
18
|
-
const perPage = new Map<string, number>();
|
|
19
|
-
for (const region of atlas.regions) perPage.set(region.page.name, (perPage.get(region.page.name) ?? 0) + 1);
|
|
20
|
-
for (const region of atlas.regions) {
|
|
21
|
-
// A real packer puts many regions on one page and the names stop matching
|
|
22
|
-
// filenames by design. Then this check has nothing to say, so it says
|
|
23
|
-
// nothing rather than something wrong.
|
|
24
|
-
if ((perPage.get(region.page.name) ?? 0) !== 1) continue;
|
|
25
|
-
const expected = basename(region.page.name).replace(/\.png$/i, '');
|
|
26
|
-
if (region.name !== expected) {
|
|
27
|
-
fail(
|
|
28
|
-
'A27_REGION_NAME_MATCHES_PAGE_FILENAME',
|
|
29
|
-
`region "${region.name}" is the only region on page "${region.page.name}", whose basename is "${expected}"`,
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|