spine-rigc 1.5.3 → 1.6.2

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/src/transform.ts CHANGED
@@ -31,7 +31,29 @@
31
31
  * wrong. `computeWorldTransforms` below is `BonePose.updateWorldTransform`
32
32
  * (spine-core 4.3.13, `BonePose.js:110-216`) at setup, with a skeleton scale of 1.
33
33
  */
34
- import type { SpineBone } from './types.ts';
34
+ /**
35
+ * What `computeWorldTransforms` reads off a bone — a structural type, so both a
36
+ * Spine bone (`SpineBone`, what spine-parts and every skeleton reader hold) and
37
+ * a compiled-model bone (`ModelBone`, what `compile` holds) satisfy it as they
38
+ * are, without a cast.
39
+ *
40
+ * The two differ in one key, the inherit mode: Spine 4.3 spells it `inherit`
41
+ * and the model `inheritMode`. The union below admits exactly one of the two on
42
+ * any one bone (the other is `never`), so a bone carrying both is a type error
43
+ * rather than a question of which one wins, and the mode is read as
44
+ * `inheritMode ?? inherit` — whichever the bone has.
45
+ */
46
+ export type PosableBone = {
47
+ name: string;
48
+ parent?: string;
49
+ x?: number;
50
+ y?: number;
51
+ rotation?: number;
52
+ scaleX?: number;
53
+ scaleY?: number;
54
+ shearX?: number;
55
+ shearY?: number;
56
+ } & ({ inherit?: string; inheritMode?: never } | { inheritMode?: string; inherit?: never });
35
57
 
36
58
  export interface BoneTransform {
37
59
  a: number;
@@ -79,7 +101,7 @@ export function cropToSpineY(cropY: number, cropHeight: number): number {
79
101
  * inheriting rig emits moves by a byte. The five `inherit` cases are the
80
102
  * runtime's, transcribed.
81
103
  */
82
- export function computeWorldTransforms(bones: SpineBone[]): Map<string, BoneTransform> {
104
+ export function computeWorldTransforms(bones: readonly PosableBone[]): Map<string, BoneTransform> {
83
105
  const out = new Map<string, BoneTransform>();
84
106
  for (const bone of bones) {
85
107
  const rotation = bone.rotation ?? 0;
@@ -144,8 +166,8 @@ const INHERIT_MODES: readonly InheritMode[] = ['normal', 'onlyTranslation', 'noR
144
166
  * leaves the matrix where it was, and the rig spec's parse refuses that
145
167
  * spelling first (issue #733), so reaching this throw is rigc's own defect.
146
168
  */
147
- function inheritMode(bone: SpineBone): InheritMode {
148
- const value = bone.inherit;
169
+ function inheritMode(bone: PosableBone): InheritMode {
170
+ const value = bone.inheritMode ?? bone.inherit;
149
171
  if (value === undefined) return 'normal';
150
172
  const folded = value.length === 0 ? value : value[0].toLowerCase() + value.slice(1);
151
173
  const mode = INHERIT_MODES.find((m) => m === folded);
package/src/types.ts CHANGED
@@ -19,6 +19,7 @@ import type { DeformTransform, DeformTransformReport } from './deformgen.ts';
19
19
  import type { TurnCeiling } from './depth.ts';
20
20
  import type { SpecEnumTable, SpecTypeRow } from './keys.ts';
21
21
  import type { MeshKind } from './mesh.ts';
22
+ import type { CompiledModel } from './model.ts';
22
23
  import type { TrackDerive, TrackDeriveReport } from './trackgen.ts';
23
24
 
24
25
  // ---------------------------------------------------------------------------
@@ -1320,6 +1321,12 @@ export interface SpineSkeletonJson {
1320
1321
  >;
1321
1322
  }
1322
1323
 
1324
+ /** One entry of the emitted `skins` array — what `emitSkins` in `src/emit_spine.ts` writes per skin. */
1325
+ export type SpineSkin = SpineSkeletonJson['skins'][number];
1326
+
1327
+ /** One animation of the emitted `animations` object — what `emitAnimations` in `src/emit_spine.ts` writes per animation. */
1328
+ export type SpineAnimation = SpineSkeletonJson['animations'][string];
1329
+
1323
1330
  /** One entry of the emitted `events` map: the payload a firing inherits. */
1324
1331
  export interface SpineEvent {
1325
1332
  int?: number;
@@ -1550,6 +1557,17 @@ export interface DroppedState {
1550
1557
  }
1551
1558
 
1552
1559
  export interface CompileResult {
1560
+ /**
1561
+ * The compiled model (`src/model.ts`): what the skeleton below was emitted
1562
+ * from. This cut fills its `bones` and `setupWorld`; every other field is
1563
+ * this result's own, carried by reference.
1564
+ */
1565
+ model: CompiledModel;
1566
+ /**
1567
+ * The Spine 4.3 skeleton: `emitSkeleton`'s value over `model` (issue #922),
1568
+ * the one call the assembly makes. `build` writes its text, and beside it
1569
+ * `modelDocument(model)` as `skeleton.model.json`.
1570
+ */
1553
1571
  skeleton: SpineSkeletonJson;
1554
1572
  skeletonText: string;
1555
1573
  atlasText: string;
package/src/validate.ts CHANGED
@@ -355,8 +355,14 @@ export interface ValidateInput {
355
355
  atlasDir: string;
356
356
  /** Declared durations from the motion spec. */
357
357
  declaredDurations?: Record<string, number>;
358
- /** Re-emitted artifacts, for the determinism check. */
359
- reEmit?: { skeletonText: string; atlasText: string };
358
+ /**
359
+ * The compiled model's document (`modelDocument`, `rigc-compiled/1`) — the
360
+ * third file `build` writes. Required whenever `reEmit` is given, since `A18`
361
+ * compares it with the second compile's (issue #922).
362
+ */
363
+ modelText?: string;
364
+ /** Re-emitted artifacts, for the determinism check: a second, independent compile's three texts. */
365
+ reEmit?: { skeletonText: string; atlasText: string; modelText: string };
360
366
  /**
361
367
  * Structural expectations the artifact cannot state about itself: which mesh is
362
368
  * a ribbon, which bone carries the axis, which parentage is forbidden, what the
@@ -1123,6 +1129,19 @@ export function timelineAddBehaviour(data: ReturnType<SkeletonJson['readSkeleton
1123
1129
  return wrote ? 'overwrites' : 'inert';
1124
1130
  }
1125
1131
 
1132
+ /**
1133
+ * Where two texts first differ, as A18 names it: the 1-based line and each
1134
+ * side's line, trimmed and cut to 120 characters.
1135
+ */
1136
+ function firstDifferingLine(first: string, second: string): string {
1137
+ const a = first.split('\n');
1138
+ const b = second.split('\n');
1139
+ let i = 0;
1140
+ while (i < a.length && i < b.length && a[i] === b[i]) i += 1;
1141
+ const cut = (line: string | undefined): string => (line === undefined ? '(end of text)' : JSON.stringify(line.trim().slice(0, 120)));
1142
+ return `${i + 1}: ${cut(a[i])} first, ${cut(b[i])} second`;
1143
+ }
1144
+
1126
1145
  export function validate(input: ValidateInput): ValidateReport {
1127
1146
  const failures: Failure[] = [];
1128
1147
  const passed: string[] = [];
@@ -6149,6 +6168,15 @@ export function validate(input: ValidateInput): ValidateReport {
6149
6168
  if (input.reEmit.atlasText !== input.atlasText) {
6150
6169
  fail('A18_DETERMINISTIC_EMIT', 'recompiling produced a different skeleton.atlas');
6151
6170
  }
6171
+ // The model document is compared like the Spine pair (issue #922): it is
6172
+ // written beside them, and a map iterated in an order nothing fixed would
6173
+ // reach it before it reached either of them — the document writes every
6174
+ // `Map` of the model as an array in the map's order.
6175
+ if (input.modelText === undefined) {
6176
+ fail('A18_DETERMINISTIC_EMIT', 'a second compile was handed over with no first model document to compare its skeleton.model.json against');
6177
+ } else if (input.reEmit.modelText !== input.modelText) {
6178
+ fail('A18_DETERMINISTIC_EMIT', `recompiling produced a different skeleton.model.json (first differing line ${firstDifferingLine(input.modelText, input.reEmit.modelText)})`);
6179
+ }
6152
6180
  });
6153
6181
 
6154
6182
  // --- every assertion leaves a row ----------------------------------------