spine-rigc 1.9.2 → 1.9.3
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/cli.ts +28 -4397
- package/docs/AUTHORING.md +1 -1
- package/package.json +1 -1
- package/src/assertions/bodies/a13.ts +3 -3
- package/src/assertions/bodies/a14.ts +3 -3
- package/src/assertions/bodies/a15.ts +5 -5
- package/src/assertions/bodies/a22.ts +3 -3
- package/src/assertions/facts/animated_bones.ts +4 -2
- package/src/assertions/facts/mesh_attachments.ts +33 -4
- package/src/assertions/model/animated_bones.ts +13 -5
- package/src/assertions/model/bone_timelines.ts +5 -3
- package/src/assertions/model/declared.ts +127 -0
- package/src/assertions/model/index.ts +63 -40
- package/src/assertions/model/mesh_attachments.ts +31 -2
- package/src/assertions/model/parse.ts +9 -1
- package/src/assertions/model/runtime_timelines.ts +78 -0
- package/src/assertions/model/slider_composition.ts +10 -8
- package/src/assertions/model/stepped_poses.ts +3 -2
- package/src/bonedist.ts +4 -5
- package/src/check.ts +3 -2
- package/src/checkpics.ts +1 -1
- package/src/cli/core_commands.ts +1532 -0
- package/src/cli/shared.ts +1935 -0
- package/src/cli/spine_commands.ts +1141 -0
- package/src/core/additive.ts +52 -62
- package/src/core/constraints.ts +26 -3
- package/src/core/constraints_physics.ts +11 -1
- package/src/core/constraints_slider.ts +136 -6
- package/src/core/index.ts +27 -3
- package/src/core/skins.ts +28 -3
- package/src/core/walk.ts +9 -36
- package/src/correspondence.ts +15 -0
- package/src/deformbuild.ts +60 -0
- package/src/deformmeasure.ts +20 -48
- package/src/deformreport.ts +2 -2
- package/src/framing.ts +1 -1
- package/src/pose.ts +1 -1
- package/src/preview.ts +1 -1
- package/src/region_joins.ts +54 -0
- package/src/render.ts +206 -2836
- package/src/render_core.ts +19 -36
- package/src/render_shared.ts +2887 -0
- package/src/slots.ts +1 -1
- package/src/spine_side.ts +123 -0
- package/src/validate.ts +11 -91
- package/src/assertions/facts/skin_meshes.ts +0 -49
- package/src/assertions/model/skin_meshes.ts +0 -90
package/src/core/walk.ts
CHANGED
|
@@ -65,22 +65,17 @@
|
|
|
65
65
|
* `CoreInputError` naming why, as there — a pose with a block missing is not
|
|
66
66
|
* a pose whose values can be judged finite.
|
|
67
67
|
*
|
|
68
|
-
*
|
|
69
|
-
* (`sliderPhysicsWhy`):
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* Which of the class it reaches is not measured, so the walk refuses the
|
|
77
|
-
* class by name rather than return a pose it cannot vouch for. On the same
|
|
78
|
-
* five inputs, `./additive.ts` and spine-core's probe agree that each slider's
|
|
79
|
-
* `wind` or `gravity` timeline accumulates when applied twice with `add` — so
|
|
80
|
-
* the gap is downstream of that rule, in the stepped bones, not in it.
|
|
68
|
+
* 🔁 **A slider whose animation keys a physics timeline** was refused here
|
|
69
|
+
* by name until issue #1049 (`sliderPhysicsWhy`): six of the path-slider
|
|
70
|
+
* suite's distinct builds walked off spine-core from the third step, and the
|
|
71
|
+
* raw entry read the same gap. The core had not applied a slider's physics keys at all;
|
|
72
|
+
* it now does, under the step, by the rule `./constraints_slider.ts` *Its
|
|
73
|
+
* physics timelines* states and the core suite's `CO31` holds — so this walk
|
|
74
|
+
* poses the class as the raw entry does, and refuses nothing the raw entry
|
|
75
|
+
* does not.
|
|
81
76
|
*/
|
|
82
77
|
import { setupPoseIn, walkIn, type RawBone, type RawDrawn, type WalkMode } from './raw.ts';
|
|
83
|
-
import { activeBones,
|
|
78
|
+
import { activeBones, type CompiledDocument, type CorePlant, type CoreSlotRow } from './index.ts';
|
|
84
79
|
import { historyTaint } from './constraints.ts';
|
|
85
80
|
import type { TimelinePlant } from './animation.ts';
|
|
86
81
|
|
|
@@ -100,20 +95,6 @@ export interface WalkPose {
|
|
|
100
95
|
drawn: RawDrawn[];
|
|
101
96
|
}
|
|
102
97
|
|
|
103
|
-
/**
|
|
104
|
-
* Why the looping walk refuses this document, or `null` (the header's last
|
|
105
|
-
* ⛔, issue #1049): every slider whose animation keys a physics timeline,
|
|
106
|
-
* named with it.
|
|
107
|
-
*/
|
|
108
|
-
export function sliderPhysicsWhy(doc: CompiledDocument): string | null {
|
|
109
|
-
const keyed = constraintRecords(doc).flatMap((r) => {
|
|
110
|
-
if (r.kind !== 'slider') return [];
|
|
111
|
-
const anim = doc.animations.find((a) => a.name === r.animation);
|
|
112
|
-
return anim !== undefined && anim.constraints.physics > 0 ? [`slider "${r.name}" applies animation "${r.animation}", which keys physics timelines`] : [];
|
|
113
|
-
});
|
|
114
|
-
return keyed.length === 0 ? null : `${keyed.join('; ')} — under the stepped walk spine-core's bones leave the core's from the third step, on the raw entry's walk as on this one, and that reading is not measured (issue #1049)`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
98
|
/**
|
|
118
99
|
* The bones and slots whose numbers the walk does not vouch for, because the
|
|
119
100
|
* runtime's own value there is HISTORY (issue #979): a bone the view leaves
|
|
@@ -142,12 +123,6 @@ export function walkHistory(view: CompiledDocument): { bones: Map<string, string
|
|
|
142
123
|
return { bones, slots: new Map(view.slots.filter((s) => bones.has(s.bone)).map((s) => [s.name, s.bone])) };
|
|
143
124
|
}
|
|
144
125
|
|
|
145
|
-
/** Refuse, by name, a document the looping walk cannot vouch for (`sliderPhysicsWhy`). */
|
|
146
|
-
function refuseUnmeasured(doc: CompiledDocument): void {
|
|
147
|
-
const why = sliderPhysicsWhy(doc);
|
|
148
|
-
if (why !== null) throw new CoreInputError(`the looping walk leaves the bones out: ${why}`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
126
|
/** The looping walk's mode: the track loops, and a non-finite value stays in the pose. */
|
|
152
127
|
const LOOPING: WalkMode = { loop: true, keep: true };
|
|
153
128
|
|
|
@@ -184,7 +159,6 @@ function narrow(p: { trackTime: number; animationTime: number; bones: RawBone[];
|
|
|
184
159
|
* what A10 reads before any animation is set.
|
|
185
160
|
*/
|
|
186
161
|
export function poseWalkSetup(doc: CompiledDocument, plant: CorePlant = {}, walkPlant: WalkPlant = {}): WalkPose {
|
|
187
|
-
refuseUnmeasured(doc);
|
|
188
162
|
return narrow(setupPoseIn(doc, plant, LOOPING), walkPlant.number);
|
|
189
163
|
}
|
|
190
164
|
|
|
@@ -196,7 +170,6 @@ export function poseWalkSetup(doc: CompiledDocument, plant: CorePlant = {}, walk
|
|
|
196
170
|
* refuses the call by name; a value that is not finite does not.
|
|
197
171
|
*/
|
|
198
172
|
export function poseLoopingWalk(doc: CompiledDocument, animation: string, steps: readonly number[], plant: TimelinePlant = {}, walkPlant: WalkPlant = {}): WalkPose[] {
|
|
199
|
-
refuseUnmeasured(doc);
|
|
200
173
|
const mode: WalkMode = { ...LOOPING, ...(walkPlant.time === undefined ? {} : { time: walkPlant.time }), ...(walkPlant.clock === undefined ? {} : { clock: walkPlant.clock }), ...(walkPlant.wrapResets === undefined ? {} : { wrapResets: walkPlant.wrapResets }) };
|
|
201
174
|
return walkIn(doc, animation, steps, plant, 'setup', mode).map((p) => narrow(p, walkPlant.number));
|
|
202
175
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two spellings of a bone correspondence — what `rigc bonedist --bones`
|
|
3
|
+
* and `rigc bench --bones` take — moved here unchanged from `./bonedist.ts`
|
|
4
|
+
* (issue #1052), which re-exports both.
|
|
5
|
+
*
|
|
6
|
+
* Their own module because the CLI's help states them (`--bones`'s flag row)
|
|
7
|
+
* and an entry that links nothing of the runtime prints that help, while
|
|
8
|
+
* `./bonedist.ts` poses both skeletons through spine-core by design.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** The sidecar spec a correspondence file declares, and the report's own. */
|
|
12
|
+
export const BONEDIST_SPEC = 'rigc-bonedist/1';
|
|
13
|
+
|
|
14
|
+
/** What `--bones identity` is spelled as, where a file path would go. */
|
|
15
|
+
export const IDENTITY_CORRESPONDENCE = 'identity';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deform survey of a build, through the reader and poser asked for —
|
|
3
|
+
* `explain`'s `DEFORM` block and `tools/survey_hashes.ts` (issues #969,
|
|
4
|
+
* #1019).
|
|
5
|
+
*
|
|
6
|
+
* Moved here unchanged from `./deformmeasure.ts` (issue #1052, step 4e of
|
|
7
|
+
* #380), which re-exports both names: the choice between the model document
|
|
8
|
+
* posed by the core and the Spine skeleton posed by spine-core names no
|
|
9
|
+
* runtime class, and `explain` makes it on a rigc build without touching the
|
|
10
|
+
* runtime — so an entry that links nothing of spine-core has to be able to
|
|
11
|
+
* load it. The spine-core half (`throughSpine`) is `./deformmeasure.ts`'s,
|
|
12
|
+
* reached through the seam (`./spine_side.ts`); where nothing registered it,
|
|
13
|
+
* a survey that needs it is refused by name (`SpineRuntimeError`).
|
|
14
|
+
*/
|
|
15
|
+
import { CoreInputError, readModel } from './core/index.ts';
|
|
16
|
+
import { surveyOfModel, type DeformSurvey, type DeformSurveySource } from './deformsurvey.ts';
|
|
17
|
+
import { spineFileSha256 } from './model.ts';
|
|
18
|
+
import { spineSurveyFor } from './spine_side.ts';
|
|
19
|
+
|
|
20
|
+
/** A build's three texts, as `explain` and `tools/survey_hashes.ts` hold them. */
|
|
21
|
+
export interface DeformSurveyInput {
|
|
22
|
+
skeletonText: string;
|
|
23
|
+
atlasText: string;
|
|
24
|
+
/** The model document (`skeleton.model.json`), or `null` when the input carries none (a Spine export). */
|
|
25
|
+
modelText: string | null;
|
|
26
|
+
/** What a refusal names the survey's input as; `the deform survey` unstated. */
|
|
27
|
+
label?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The survey of a build, through the reader and poser asked for (issues #969,
|
|
32
|
+
* #1019): `model` — the model document's structure posed by the core,
|
|
33
|
+
* refused when there is none or when the Spine file beside it is not the one
|
|
34
|
+
* it records; `spine-core` — the Spine skeleton read and posed by the runtime;
|
|
35
|
+
* `auto` — the model document when the input carries one and the core poses
|
|
36
|
+
* it, spine-core otherwise, the reason named in `source.why`. On the model
|
|
37
|
+
* path spine-core is not touched: the Spine text is hashed, never parsed.
|
|
38
|
+
*/
|
|
39
|
+
export function surveyOfBuild(input: DeformSurveyInput, exempt: ReadonlySet<string>, asked: 'auto' | DeformSurveySource): DeformSurvey {
|
|
40
|
+
const label = input.label ?? 'the deform survey';
|
|
41
|
+
// `./deformmeasure.ts`'s reader and poser, through the seam: it touches the runtime first and refuses by name where it cannot be used.
|
|
42
|
+
const throughSpine = (why: string | null): DeformSurvey => spineSurveyFor(label, why ?? '--poser spine').throughSpine(label, why, input, exempt);
|
|
43
|
+
if (asked === 'spine-core') return throughSpine(null);
|
|
44
|
+
if (input.modelText === null) {
|
|
45
|
+
if (asked === 'model') throw new CoreInputError('the survey was asked to pose the model document, and the input carries none');
|
|
46
|
+
return throughSpine('the input carries no model document (skeleton.model.json), so the survey posed the Spine skeleton through spine-core');
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const doc = readModel(input.modelText);
|
|
50
|
+
// The document reads the rig it was built with; the Spine file beside it must be that build's (issue #968's rule).
|
|
51
|
+
const found = spineFileSha256(input.skeletonText);
|
|
52
|
+
if (found !== doc.spine.sha256) {
|
|
53
|
+
throw new CoreInputError(`the skeleton is not the one the model document was written beside: its sha256 is ${found}, the document records ${doc.spine.sha256 || 'none'}`);
|
|
54
|
+
}
|
|
55
|
+
return surveyOfModel(doc, exempt);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (!(err instanceof CoreInputError) || asked === 'model') throw err;
|
|
58
|
+
return throughSpine(`the core refused to pose the model document — ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/deformmeasure.ts
CHANGED
|
@@ -124,7 +124,7 @@ import {
|
|
|
124
124
|
type Timeline,
|
|
125
125
|
} from '@esotericsoftware/spine-core';
|
|
126
126
|
// #969: the core's side of the seam — the model document read.
|
|
127
|
-
import {
|
|
127
|
+
import { readModel } from './core/index.ts';
|
|
128
128
|
// #1019: what the survey reads of the skeleton's structure, and the model document's reading of it.
|
|
129
129
|
import {
|
|
130
130
|
modelStructure,
|
|
@@ -137,11 +137,15 @@ import {
|
|
|
137
137
|
type SurveyStructure,
|
|
138
138
|
} from './deformstructure.ts';
|
|
139
139
|
// Issue #1025 (cut 4c-3): the half of the survey that names no runtime class.
|
|
140
|
-
import { BEZIER_POINTS, corePoser,
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
import { BEZIER_POINTS, corePoser, surveyWith, type DeformSurvey, type DialField, type ShownReading, type SurveyPose, type SurveyPoser } from './deformsurvey.ts';
|
|
141
|
+
// Issue #1052: the choice of reader and poser over a build is `./deformbuild.ts`'s, where an entry that links nothing of
|
|
142
|
+
// the runtime can load it; this file's half of it — the survey through spine-core — is registered into the seam below.
|
|
143
|
+
import type { DeformSurveyInput } from './deformbuild.ts';
|
|
144
|
+
import { registerSpineSurvey, SpineRuntimeError, spineRuntimeSentence, SURVEY_RUNTIME_TAIL } from './spine_side.ts';
|
|
143
145
|
|
|
144
146
|
export { BEZIER_POINTS, DEFORM_AREA_EPSILON, float32AreaNoise, stretchSingularValues, surveyOfModel, triangleAreas, unreachableWhy } from './deformsurvey.ts';
|
|
147
|
+
export { surveyOfBuild } from './deformbuild.ts';
|
|
148
|
+
export type { DeformSurveyInput } from './deformbuild.ts';
|
|
145
149
|
export type {
|
|
146
150
|
DeformDial,
|
|
147
151
|
DeformDialDispute,
|
|
@@ -694,16 +698,6 @@ export function deformPosers(input: DeformSurveyInput & { modelText: string }):
|
|
|
694
698
|
return { data, structure: side.structure, model: modelStructure(doc), spine: side.poser, core: corePoser(side.structure, doc) };
|
|
695
699
|
}
|
|
696
700
|
|
|
697
|
-
/** A build's three texts, as `explain` and `tools/survey_hashes.ts` hold them. */
|
|
698
|
-
export interface DeformSurveyInput {
|
|
699
|
-
skeletonText: string;
|
|
700
|
-
atlasText: string;
|
|
701
|
-
/** The model document (`skeleton.model.json`), or `null` when the input carries none (a Spine export). */
|
|
702
|
-
modelText: string | null;
|
|
703
|
-
/** What a refusal names the survey's input as; `the deform survey` unstated. */
|
|
704
|
-
label?: string;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
701
|
/**
|
|
708
702
|
* Touch the runtime once, before the skeleton is parsed through it, and refuse
|
|
709
703
|
* by name when it cannot be used (issue #1019) — the sentence `render` and
|
|
@@ -716,45 +710,23 @@ function requireSpineRuntime(label: string, why: string): void {
|
|
|
716
710
|
// A property read on the class the parse starts from: no runtime code runs, and a runtime that cannot be used throws here.
|
|
717
711
|
void TextureAtlas.prototype;
|
|
718
712
|
} catch (err) {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
'spine-core is what reads and poses a Spine export, --poser spine and a fallback the survey names; ' +
|
|
722
|
-
'a rigc build the core poses reads nothing through it',
|
|
723
|
-
);
|
|
713
|
+
// The sentence an entry that registered no Spine side is refused in too (`./spine_side.ts`), with the runtime's own words as the reason.
|
|
714
|
+
throw new SpineRuntimeError(spineRuntimeSentence(label, why, (err as Error).message, SURVEY_RUNTIME_TAIL));
|
|
724
715
|
}
|
|
725
716
|
}
|
|
726
717
|
|
|
727
718
|
/**
|
|
728
|
-
* The survey
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
*
|
|
734
|
-
* path spine-core is not touched: the Spine text is hashed, never parsed.
|
|
719
|
+
* The survey through spine-core — the half of `surveyOfBuild`
|
|
720
|
+
* (`./deformbuild.ts`, moved there unchanged in issue #1052) that names the
|
|
721
|
+
* runtime, registered into the seam when this file is loaded: it touches the
|
|
722
|
+
* runtime once (`requireSpineRuntime`), then reads and poses the Spine
|
|
723
|
+
* skeleton and names `why` as the survey's source. Every program that imports
|
|
724
|
+
* this file surveys as before; one that does not refuses this half by name.
|
|
735
725
|
*/
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const throughSpine = (why: string | null): DeformSurvey => {
|
|
726
|
+
registerSpineSurvey({
|
|
727
|
+
throughSpine: (label, why, input, exempt) => {
|
|
739
728
|
requireSpineRuntime(label, why ?? '--poser spine');
|
|
740
729
|
const side = runtimeSide(skeletonDataFromText(input.skeletonText, input.atlasText));
|
|
741
730
|
return { ...surveyWith(side.structure, exempt, side.poser), source: { used: 'spine-core', why } };
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
if (input.modelText === null) {
|
|
745
|
-
if (asked === 'model') throw new CoreInputError('the survey was asked to pose the model document, and the input carries none');
|
|
746
|
-
return throughSpine('the input carries no model document (skeleton.model.json), so the survey posed the Spine skeleton through spine-core');
|
|
747
|
-
}
|
|
748
|
-
try {
|
|
749
|
-
const doc = readModel(input.modelText);
|
|
750
|
-
// The document reads the rig it was built with; the Spine file beside it must be that build's (issue #968's rule).
|
|
751
|
-
const found = spineFileSha256(input.skeletonText);
|
|
752
|
-
if (found !== doc.spine.sha256) {
|
|
753
|
-
throw new CoreInputError(`the skeleton is not the one the model document was written beside: its sha256 is ${found}, the document records ${doc.spine.sha256 || 'none'}`);
|
|
754
|
-
}
|
|
755
|
-
return surveyOfModel(doc, exempt);
|
|
756
|
-
} catch (err) {
|
|
757
|
-
if (!(err instanceof CoreInputError) || asked === 'model') throw err;
|
|
758
|
-
return throughSpine(`the core refused to pose the model document — ${err.message}`);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
731
|
+
},
|
|
732
|
+
});
|
package/src/deformreport.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* block is a report and not an assertion, and what it deliberately leaves out.
|
|
8
8
|
*/
|
|
9
9
|
import type { CompileResult } from './types.ts';
|
|
10
|
-
import type { DeformExtreme, DeformKeyMeasure, DeformSpan, DeformSurvey } from './
|
|
11
|
-
import { unreachableWhy } from './
|
|
10
|
+
import type { DeformExtreme, DeformKeyMeasure, DeformSpan, DeformSurvey } from './deformsurvey.ts';
|
|
11
|
+
import { unreachableWhy } from './deformsurvey.ts';
|
|
12
12
|
import { float32Step } from './timelines.ts';
|
|
13
13
|
|
|
14
14
|
/**
|
package/src/framing.ts
CHANGED
package/src/pose.ts
CHANGED
|
@@ -80,7 +80,7 @@ import { Plate, readPlate } from '../tools/plate.ts';
|
|
|
80
80
|
// straddles an edge. #301 deliberately left this call on the old arithmetic to
|
|
81
81
|
// hold the fitting numbers still while the from-zero run 2 was in flight; #306
|
|
82
82
|
// moved it, with the re-baseline of every figure that quoted a residual.
|
|
83
|
-
import { bilinear } from './
|
|
83
|
+
import { bilinear } from './render_shared.ts';
|
|
84
84
|
|
|
85
85
|
export class PoseError extends Error {}
|
|
86
86
|
|
package/src/preview.ts
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* loud when the script does not arrive.
|
|
46
46
|
*/
|
|
47
47
|
import { SPINE_VERSION } from './compile.ts';
|
|
48
|
-
import { BACKGROUND } from './
|
|
48
|
+
import { BACKGROUND } from './render_shared.ts';
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* The Spine Web Player line the generated page loads.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every atlas-region lookup a skeleton's attachments ask for, read off its raw
|
|
3
|
+
* JSON (issue #589) — moved here unchanged from `./validate.ts` (issue #1052),
|
|
4
|
+
* which re-exports it.
|
|
5
|
+
*
|
|
6
|
+
* Its own module because two readers need it and only one of them links the
|
|
7
|
+
* runtime: `A08`'s runtime supplier in `./validate.ts`, and `explain`, which
|
|
8
|
+
* refuses art it cannot pose (`refuseUnposableArt`) and runs in an entry that
|
|
9
|
+
* links nothing of spine-core. The walk names no runtime class — it reads the
|
|
10
|
+
* file, and is measured against the runtime's loader by `PS127`.
|
|
11
|
+
*/
|
|
12
|
+
import { attachmentRegionLookups, type AttachmentRegionJoin } from './assertions/region_lookups.ts';
|
|
13
|
+
import { isObj } from './assertions/values.ts';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Every atlas-region lookup `AtlasAttachmentLoader` will perform, read off the
|
|
17
|
+
* RAW skeleton JSON — before the loader is asked, which is the whole point.
|
|
18
|
+
*
|
|
19
|
+
* 🚨 This is a SECOND implementation of a join `spine-core` already performs,
|
|
20
|
+
* and the tree's standing judgment about a second opinion on somebody else's
|
|
21
|
+
* format is that it is measured rather than asserted: `PS127` runs the loader
|
|
22
|
+
* with its `findRegion` recording what it asked for, and compares. A wrong walk
|
|
23
|
+
* here would refuse correct foreign data by name, which is the one failure that
|
|
24
|
+
* would be worse than the silence #589 removed.
|
|
25
|
+
*
|
|
26
|
+
* Which entries resolve a region is the parser's list, not a guess:
|
|
27
|
+
* `SkeletonJson.readAttachment` (`dist/SkeletonJson.js:524-575`) calls the
|
|
28
|
+
* loader with a path for `region`, `mesh` and `linkedmesh` — a linked mesh
|
|
29
|
+
* resolves its own region before the `source` branch — and for nothing else.
|
|
30
|
+
* `type` defaults to `region` (`:527`), `name` to the placeholder (`:526`) and
|
|
31
|
+
* `path` to the name (`:529`, `:560`): three names that default into one
|
|
32
|
+
* another, which is why a report printing only the last of them cannot say
|
|
33
|
+
* what to change.
|
|
34
|
+
*/
|
|
35
|
+
export function attachmentRegionJoins(raw: unknown): AttachmentRegionJoin[] {
|
|
36
|
+
const joins: AttachmentRegionJoin[] = [];
|
|
37
|
+
if (!isObj(raw) || !Array.isArray(raw.skins)) return joins;
|
|
38
|
+
for (const skin of raw.skins as unknown[]) {
|
|
39
|
+
if (!isObj(skin) || !isObj(skin.attachments)) continue;
|
|
40
|
+
const skinName = typeof skin.name === 'string' ? skin.name : '(unnamed)';
|
|
41
|
+
for (const [slot, entries] of Object.entries(skin.attachments)) {
|
|
42
|
+
if (!isObj(entries)) continue;
|
|
43
|
+
for (const [placeholder, entry] of Object.entries(entries)) {
|
|
44
|
+
if (!isObj(entry)) continue;
|
|
45
|
+
const type = entry.type === undefined ? 'region' : entry.type;
|
|
46
|
+
if (type !== 'region' && type !== 'mesh' && type !== 'linkedmesh') continue;
|
|
47
|
+
const name = typeof entry.name === 'string' ? entry.name : placeholder;
|
|
48
|
+
const path = typeof entry.path === 'string' ? entry.path : name;
|
|
49
|
+
joins.push({ skin: skinName, slot, placeholder, name, lookups: attachmentRegionLookups(entry.sequence, path) });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return joins;
|
|
54
|
+
}
|