@flighthq/skeleton2d 0.3.0-next.1032.e2b99fc → 0.3.0-next.1162.e90e518

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.
@@ -1 +1 @@
1
- {"version":3,"file":"applyAnimationClipToSkeleton2D.d.ts","sourceRoot":"","sources":["../src/applyAnimationClipToSkeleton2D.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAA6B,MAAM,0BAA0B,CAAC;AAqBrG,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC7B,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GACX,IAAI,CAsCN"}
1
+ {"version":3,"file":"applyAnimationClipToSkeleton2D.d.ts","sourceRoot":"","sources":["../src/applyAnimationClipToSkeleton2D.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,aAAa,EACb,UAAU,EAGX,MAAM,0BAA0B,CAAC;AA0BlC,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC7B,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GACX,IAAI,CA6CN"}
@@ -1,5 +1,5 @@
1
1
  import { sampleAnimationTrack } from '@flighthq/animation/contract';
2
- import { Skeleton2DAnimationPath } from '@flighthq/types/contract';
2
+ import { Skeleton2DAnimationPath, Skeleton2DSlotAnimationPath } from '@flighthq/types/contract';
3
3
  // Poses `pose` from an AnimationClip at `time` by COMPOSING each channel's sampled value onto the
4
4
  // corresponding `setup` bone — the 2D-skeletal analogue of @flighthq/scene3d's `applyAnimationClipToScene3D`,
5
5
  // but relative rather than absolute. This mirrors Spine's SkeletonData(setup)/Skeleton(instance) split:
@@ -9,6 +9,11 @@ import { Skeleton2DAnimationPath } from '@flighthq/types/contract';
9
9
  // is what makes clips blendable — a mixer accumulates deltas as `pose = setup + Σ wᵢ·deltaᵢ`, which averaging
10
10
  // baked-absolute poses cannot express — and is why re-applying a clip does not accumulate across frames.
11
11
  //
12
+ // A clip may also carry SLOT channels (a `Skeleton2DSlotAnimationTarget` rather than a bone target). Those
13
+ // are dispatched by target SHAPE and follow different rules: a slot colour is an ABSOLUTE authored value, so
14
+ // it is written rather than composed, and `setup` is not consulted at all on that path. See
15
+ // applySkeleton2DSlotChannel.
16
+ //
12
17
  // It writes only the bones a channel targets; untouched pose bones keep their clone-of-setup values. The
13
18
  // caller runs `computeSkeleton2DWorldTransforms(pose, …)` (then the deform) afterward, so a whole clip
14
19
  // applies before one world propagation. Module scratch, allocation-free. A channel whose target is foreign
@@ -28,8 +33,15 @@ export function applyAnimationClipToSkeleton2D(clip, setup, pose, time) {
28
33
  for (let i = 0; i < channels.length; i++) {
29
34
  const channel = channels[i];
30
35
  const target = channel.targetRef;
31
- if (target === null || typeof target !== 'object' || typeof target.boneIndex !== 'number')
36
+ if (target === null || typeof target !== 'object')
37
+ continue;
38
+ // `targetRef` is opaque, so the shape itself says which array a channel addresses: a bone target names
39
+ // a bone index, a slot target names a slot index. Probing (rather than a discriminator field) is what
40
+ // lets slot channels be purely additive to a bone-only clip format.
41
+ if (typeof target.boneIndex !== 'number') {
42
+ applySkeleton2DSlotChannel(channel, pose, target, time);
32
43
  continue;
44
+ }
33
45
  const boneIndex = target.boneIndex;
34
46
  if (boneIndex < 0 || boneIndex >= poseBones.length || boneIndex >= setupBones.length)
35
47
  continue;
@@ -57,5 +69,34 @@ export function applyAnimationClipToSkeleton2D(clip, setup, pose, time) {
57
69
  }
58
70
  }
59
71
  }
72
+ // Writes one slot channel's sampled value onto the pose slot. Unlike a bone channel this COMPOSES NOTHING:
73
+ // Spine and DragonBones both author slot colour as an ABSOLUTE value, so the sample replaces the slot's
74
+ // colour rather than tinting the setup colour again. There is therefore no `setup` read on this path.
75
+ //
76
+ // Channels are NORMALIZED 0..1 in R,G,B,A order and scaled to bytes here. The track holds 0..1 rather than
77
+ // 0..255 because that is the space Spine authors colour CURVE control points in — a byte-scaled track would
78
+ // rebase every colour easing against the wrong range. Samples are CLAMPED: a bezier segment may legitimately
79
+ // overshoot its endpoints (anticipation curves do), and an out-of-range channel would otherwise wrap when
80
+ // packed and flip the colour.
81
+ function applySkeleton2DSlotChannel(channel, pose, target, time) {
82
+ const slots = pose.slots;
83
+ if (slots === undefined || slots === null)
84
+ return;
85
+ const slotIndex = target.slotIndex;
86
+ if (typeof slotIndex !== 'number' || slotIndex < 0 || slotIndex >= slots.length)
87
+ return;
88
+ if (target.path !== Skeleton2DSlotAnimationPath.Color)
89
+ return;
90
+ sampleAnimationTrack(_scratch, channel.track, time);
91
+ slots[slotIndex].color =
92
+ ((clampColorChannel(_scratch[0]) << 24) |
93
+ (clampColorChannel(_scratch[1]) << 16) |
94
+ (clampColorChannel(_scratch[2]) << 8) |
95
+ clampColorChannel(_scratch[3])) >>>
96
+ 0;
97
+ }
98
+ function clampColorChannel(value) {
99
+ return value <= 0 ? 0 : value >= 1 ? 255 : Math.round(value * 255);
100
+ }
60
101
  const _scratch = [0, 0, 0, 0];
61
102
  //# sourceMappingURL=applyAnimationClipToSkeleton2D.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyAnimationClipToSkeleton2D.js","sourceRoot":"","sources":["../src/applyAnimationClipToSkeleton2D.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,kGAAkG;AAClG,8GAA8G;AAC9G,wGAAwG;AACxG,8GAA8G;AAC9G,yGAAyG;AACzG,2GAA2G;AAC3G,8GAA8G;AAC9G,yGAAyG;AACzG,EAAE;AACF,yGAAyG;AACzG,uGAAuG;AACvG,2GAA2G;AAC3G,2FAA2F;AAC3F,EAAE;AACF,yGAAyG;AACzG,yGAAyG;AACzG,yGAAyG;AACzG,wGAAwG;AACxG,MAAM,UAAU,8BAA8B,CAC5C,IAA6B,EAC7B,KAA2B,EAC3B,IAAgB,EAChB,IAAY;IAEZ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,mHAAmH,CACpH,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,SAA6C,CAAC;QACrE,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;YAAE,SAAS;QACpG,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACnC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,IAAI,SAAS,IAAI,UAAU,CAAC,MAAM;YAAE,SAAS;QAC/F,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QACtC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,uBAAuB,CAAC,WAAW;gBACtC,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACvC,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACvC,MAAM;YACR,KAAK,uBAAuB,CAAC,QAAQ;gBACnC,QAAQ,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACrD,MAAM;YACR,KAAK,uBAAuB,CAAC,KAAK;gBAChC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACjD,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,uBAAuB,CAAC,KAAK;gBAChC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACjD,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACjD,MAAM;YACR;gBACE,MAAM;QACV,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"applyAnimationClipToSkeleton2D.js","sourceRoot":"","sources":["../src/applyAnimationClipToSkeleton2D.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Bone2D, MatrixLike, Skeleton2D } from '@flighthq/types/contract';
1
+ import type { AttachmentSkin2D, Bone2D, MatrixLike, Skeleton2D } from '@flighthq/types/contract';
2
2
  export declare function cloneSkeleton2D(skeleton: Readonly<Skeleton2D>): Skeleton2D;
3
3
  export declare function computeSkeleton2DBoneMatrices(skeleton: Readonly<Skeleton2D>): void;
4
4
  export declare function computeSkeleton2DWorldTransforms(skeleton: Readonly<Skeleton2D>): void;
@@ -7,6 +7,8 @@ export declare function disposeSkeleton2D(skeleton: Skeleton2D): void;
7
7
  export declare function equalsSkeleton2D(a: Readonly<Skeleton2D>, b: Readonly<Skeleton2D>): boolean;
8
8
  export declare function getSkeleton2DBoneIndexByName(skeleton: Readonly<Skeleton2D>, name: string): number;
9
9
  export declare function getSkeleton2DBoneWorldMatrix(out: MatrixLike, skeleton: Readonly<Skeleton2D>, boneIndex: number): boolean;
10
+ export declare function getSkeleton2DSkin(skeleton: Readonly<Skeleton2D>, name: string): AttachmentSkin2D | null;
10
11
  export declare function setSkeleton2DBindPose(skeleton: Readonly<Skeleton2D>): void;
12
+ export declare function setSkeleton2DSkin(skeleton: Skeleton2D, skin: Readonly<AttachmentSkin2D>): void;
11
13
  export declare function validateSkeleton2D(skeleton: Readonly<Skeleton2D>): string | null;
12
14
  //# sourceMappingURL=skeleton2d.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton2d.d.ts","sourceRoot":"","sources":["../src/skeleton2d.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAS/E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,CAS1E;AAMD,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAYlF;AAOD,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAqFrF;AAOD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,GAAE,UAAU,CAAC,OAAO,CAAQ,GAAG,UAAU,CAS/F;AAID,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAG5D;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAsB1F;AAGD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMjG;AAID,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAIT;AAMD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAU1E;AAID,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,IAAI,CAiBhF"}
1
+ {"version":3,"file":"skeleton2d.d.ts","sourceRoot":"","sources":["../src/skeleton2d.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAQjG,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,CAS1E;AAMD,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAYlF;AAOD,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAmFrF;AAOD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,GAAE,UAAU,CAAC,OAAO,CAAQ,GAAG,UAAU,CAS/F;AAID,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAG5D;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAyB1F;AAGD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMjG;AAID,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAIT;AAID,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAOvG;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAU1E;AAeD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAM9F;AAID,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,IAAI,CAiBhF"}
@@ -1,7 +1,6 @@
1
1
  import { createEntity } from '@flighthq/entity/contract';
2
2
  import { inverseMatrix, multiplyMatrix } from '@flighthq/geometry/contract';
3
3
  import { DEG_TO_RAD } from '@flighthq/math/contract';
4
- import { TransformMode2D } from '@flighthq/types/contract';
5
4
  // 6 floats per bone in the flat 2×3 affine buffers (a, b, c, d, tx, ty), matching the Matrix field order.
6
5
  const MATRIX_STRIDE = 6;
7
6
  // Deep-copies the bone array (each Bone2D cloned) and the transform buffers, producing an independently
@@ -67,62 +66,63 @@ export function computeSkeleton2DWorldTransforms(skeleton) {
67
66
  const pb = world[p + 1];
68
67
  const pc = world[p + 2];
69
68
  const pd = world[p + 3];
70
- // Local origin placed by the parent — the same for every inherit mode.
71
- world[o + 4] = pa * bone.x + pc * bone.y + world[p + 4];
72
- world[o + 5] = pb * bone.x + pd * bone.y + world[p + 5];
73
- switch (bone.transformMode) {
74
- case TransformMode2D.OnlyTranslation:
75
- // Rotation/scale come from the bone's own local transform; only position inherits (set above).
76
- world[o] = la;
77
- world[o + 1] = lb;
78
- world[o + 2] = lc;
79
- world[o + 3] = ld;
80
- break;
81
- case TransformMode2D.NoRotationOrReflection: {
82
- // Inherit the parent's SCALE (its column lengths) but strip rotation + reflection: compose the
83
- // local transform with a diagonal, axis-aligned scale-only parent.
84
- const psx = Math.hypot(pa, pb);
85
- const psy = Math.hypot(pc, pd);
86
- world[o] = psx * la;
87
- world[o + 1] = psy * lb;
88
- world[o + 2] = psx * lc;
89
- world[o + 3] = psy * ld;
90
- break;
69
+ const inherit = bone.transformMode;
70
+ // Position: the parent places the bone's local origin — unless translation inheritance is stripped, in
71
+ // which case the bone's local (x, y) is its world position directly.
72
+ if (inherit.translation) {
73
+ world[o + 4] = pa * bone.x + pc * bone.y + world[p + 4];
74
+ world[o + 5] = pb * bone.x + pd * bone.y + world[p + 5];
75
+ }
76
+ else {
77
+ world[o + 4] = bone.x;
78
+ world[o + 5] = bone.y;
79
+ }
80
+ // Linear part: rotation, scale, and reflection are each inherited or stripped independently. When all
81
+ // three are inherited (Normal — the common case) it is the fast path: the raw parent columns × local,
82
+ // no decomposition. Otherwise the parent is decomposed into per-column scale (lengths) and a direction
83
+ // basis, each axis kept or replaced per its flag, recomposed, then × local. Reproduces the five presets
84
+ // exactly (NoScale keeps the parent's actual y-column, preserving shear; NoScaleOrReflection forces the
85
+ // y-axis to the +90° perpendicular of the x-axis, det +1, dropping shear and reflection).
86
+ let ea;
87
+ let eb;
88
+ let ec;
89
+ let ed;
90
+ if (inherit.rotation && inherit.scale && inherit.reflection) {
91
+ ea = pa;
92
+ eb = pb;
93
+ ec = pc;
94
+ ed = pd;
95
+ }
96
+ else {
97
+ const psx = Math.hypot(pa, pb) || 1;
98
+ const psy = Math.hypot(pc, pd) || 1;
99
+ const d0x = inherit.rotation ? pa / psx : 1;
100
+ const d0y = inherit.rotation ? pb / psx : 0;
101
+ let d1x;
102
+ let d1y;
103
+ if (inherit.rotation && inherit.reflection) {
104
+ d1x = pc / psy;
105
+ d1y = pd / psy;
91
106
  }
92
- case TransformMode2D.NoScale:
93
- case TransformMode2D.NoScaleOrReflection: {
94
- // Inherit the parent's ORIENTATION but not its scale: normalize the parent's columns to unit
95
- // length, then compose with the local transform. NoScale keeps the parent's reflection (its
96
- // normalized columns as-is); NoScaleOrReflection strips it, forcing the y-axis to the +90°
97
- // rotation of the x-axis (det = +1) so the child never flips under a negatively-scaled parent.
98
- const psx = Math.hypot(pa, pb) || 1;
99
- const nax = pa / psx;
100
- const nay = pb / psx;
101
- let ncx;
102
- let ncy;
103
- if (bone.transformMode === TransformMode2D.NoScaleOrReflection) {
104
- ncx = -nay;
105
- ncy = nax;
106
- }
107
- else {
108
- const psy = Math.hypot(pc, pd) || 1;
109
- ncx = pc / psy;
110
- ncy = pd / psy;
111
- }
112
- world[o] = nax * la + ncx * lb;
113
- world[o + 1] = nay * la + ncy * lb;
114
- world[o + 2] = nax * lc + ncx * ld;
115
- world[o + 3] = nay * lc + ncy * ld;
116
- break;
107
+ else if (inherit.rotation) {
108
+ d1x = -d0y;
109
+ d1y = d0x;
117
110
  }
118
- default:
119
- // Normal: full inherit — world linear part = parent × local.
120
- world[o] = pa * la + pc * lb;
121
- world[o + 1] = pb * la + pd * lb;
122
- world[o + 2] = pa * lc + pc * ld;
123
- world[o + 3] = pb * lc + pd * ld;
124
- break;
111
+ else {
112
+ d1x = 0;
113
+ d1y = inherit.reflection && pa * pd - pb * pc < 0 ? -1 : 1;
114
+ }
115
+ const sx = inherit.scale ? psx : 1;
116
+ const sy = inherit.scale ? psy : 1;
117
+ ea = d0x * sx;
118
+ eb = d0y * sx;
119
+ ec = d1x * sy;
120
+ ed = d1y * sy;
125
121
  }
122
+ world[o] = ea * la + ec * lb;
123
+ world[o + 1] = eb * la + ed * lb;
124
+ world[o + 2] = ea * lc + ec * ld;
125
+ world[o + 3] = eb * lc + ed * ld;
126
126
  }
127
127
  }
128
128
  // Allocates a Skeleton2D from a parent-before-child ordered bone array. Sizes the flat world/inverse-bind/
@@ -163,7 +163,10 @@ export function equalsSkeleton2D(a, b) {
163
163
  x.shearX !== y.shearX ||
164
164
  x.shearY !== y.shearY ||
165
165
  x.length !== y.length ||
166
- x.transformMode !== y.transformMode) {
166
+ x.transformMode.rotation !== y.transformMode.rotation ||
167
+ x.transformMode.scale !== y.transformMode.scale ||
168
+ x.transformMode.reflection !== y.transformMode.reflection ||
169
+ x.transformMode.translation !== y.transformMode.translation) {
167
170
  return false;
168
171
  }
169
172
  }
@@ -186,9 +189,18 @@ export function getSkeleton2DBoneWorldMatrix(out, skeleton, boneIndex) {
186
189
  readMatrix(out, skeleton.worldMatrices, boneIndex * MATRIX_STRIDE);
187
190
  return true;
188
191
  }
189
- // Captures the current world transforms as the bind (setup) pose: `inverseBindMatrices[i] =
190
- // inverse(worldMatrices[i])`. Call after posing the bones to setup and running
191
- // `computeSkeleton2DWorldTransforms`. A non-invertible (degenerate, zero-scale) bone leaves its
192
+ // The skeleton's named skin, or the `null` sentinel when it carries none by that name. Names are the rig's
193
+ // own (`"goblin"`, `"goblingirl"`); a rig with no wardrobe has no skins at all.
194
+ export function getSkeleton2DSkin(skeleton, name) {
195
+ const skins = skeleton.skins;
196
+ if (skins === undefined || skins === null)
197
+ return null;
198
+ for (const skin of skins) {
199
+ if (skin.name === name)
200
+ return skin;
201
+ }
202
+ return null;
203
+ }
192
204
  // inverse-bind at identity (`inverseMatrix` returns false) rather than producing NaNs.
193
205
  export function setSkeleton2DBindPose(skeleton) {
194
206
  const world = skeleton.worldMatrices;
@@ -202,6 +214,28 @@ export function setSkeleton2DBindPose(skeleton) {
202
214
  writeMatrix(out, o, _scratchB);
203
215
  }
204
216
  }
217
+ // Captures the current world transforms as the bind (setup) pose: `inverseBindMatrices[i] =
218
+ // inverse(worldMatrices[i])`. Call after posing the bones to setup and running
219
+ // `computeSkeleton2DWorldTransforms`. A non-invertible (degenerate, zero-scale) bone leaves its
220
+ // Dresses `skeleton` in `skin`: writes each of the skin's attachments onto the slot it names, leaving every
221
+ // other slot untouched. This is the whole runtime cost of the wardrobe — skins are inert until applied, and
222
+ // applying one is a slot-array write, no re-binding and no world propagation (a skin changes what is DRAWN,
223
+ // never where bones are).
224
+ //
225
+ // Slots the skin does not mention keep whatever they were showing. That is deliberate and matches how rigs
226
+ // are authored: a "default" skin carries the shared pieces while `goblin`/`goblingirl` override only the
227
+ // parts that differ, so applying one over the other must not blank the shared art. A caller wanting a clean
228
+ // swap applies the base skin first. An entry naming a slot outside the skeleton is skipped rather than
229
+ // throwing — third-party rigs are untrusted input, and a stale index is a dropped attachment, not a crash.
230
+ export function setSkeleton2DSkin(skeleton, skin) {
231
+ const slots = skeleton.slots;
232
+ if (slots === undefined || slots === null)
233
+ return;
234
+ for (const entry of skin.attachments) {
235
+ if (entry.slotIndex >= 0 && entry.slotIndex < slots.length)
236
+ slots[entry.slotIndex].attachment = entry.attachment;
237
+ }
238
+ }
205
239
  // Checks the parent-before-child ordering invariant (each bone's parentIndex is -1 or < its own index) and
206
240
  // buffer sizing. Returns null when valid, or a message describing the first violation (sentinel over throw).
207
241
  export function validateSkeleton2D(skeleton) {
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton2d.js","sourceRoot":"","sources":["../src/skeleton2d.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skeleton2d.js","sourceRoot":"","sources":["../src/skeleton2d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,0GAA0G;AAC1G,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,wGAAwG;AACxG,yGAAyG;AACzG,6DAA6D;AAC7D,MAAM,UAAU,eAAe,CAAC,QAA8B;IAC5D,OAAO,YAAY,CAAC;QAClB,YAAY,EAAE,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE;QAC3C,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QAClD,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB,CAAC,KAAK,EAAE;QACzD,KAAK,EACH,QAAQ,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAClH,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE;KAC9C,CAAC,CAAC;AACL,CAAC;AAED,yGAAyG;AACzG,6GAA6G;AAC7G,yGAAyG;AACzG,oCAAoC;AACpC,MAAM,UAAU,6BAA6B,CAAC,QAA8B;IAC1E,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,mBAAmB,CAAC;IAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,YAAY,CAAC;IAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;QAC5B,UAAU,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAChC,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAClC,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,WAAW,CAAC,GAAG,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,2GAA2G;AAC3G,2GAA2G;AAC3G,yGAAyG;AACzG,0GAA0G;AAC1G,yGAAyG;AACzG,MAAM,UAAU,gCAAgC,CAAC,QAA8B;IAC7E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,6FAA6F;QAC7F,qGAAqG;QACrG,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC;QACxD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC;QAC7D,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;QAC5B,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACd,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YAClB,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YAClB,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YAClB,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACtB,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACtB,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC;QAC3C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACpB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC;QACnC,uGAAuG;QACvG,qEAAqE;QACrE,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxD,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACtB,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACxB,CAAC;QACD,sGAAsG;QACtG,sGAAsG;QACtG,uGAAuG;QACvG,wGAAwG;QACxG,wGAAwG;QACxG,0FAA0F;QAC1F,IAAI,EAAU,CAAC;QACf,IAAI,EAAU,CAAC;QACf,IAAI,EAAU,CAAC;QACf,IAAI,EAAU,CAAC;QACf,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAC5D,EAAE,GAAG,EAAE,CAAC;YACR,EAAE,GAAG,EAAE,CAAC;YACR,EAAE,GAAG,EAAE,CAAC;YACR,EAAE,GAAG,EAAE,CAAC;QACV,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,IAAI,GAAW,CAAC;YAChB,IAAI,GAAW,CAAC;YAChB,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBAC3C,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC;gBACf,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC;YACjB,CAAC;iBAAM,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBAC5B,GAAG,GAAG,CAAC,GAAG,CAAC;gBACX,GAAG,GAAG,GAAG,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,GAAG,GAAG,CAAC,CAAC;gBACR,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;YACd,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;YACd,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;YACd,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;QAChB,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QAC7B,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACjC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACjC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,2GAA2G;AAC3G,+FAA+F;AAC/F,6GAA6G;AAC7G,uGAAuG;AACvG,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,KAAe,EAAE,QAA6B,IAAI;IACjF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,OAAO,YAAY,CAAC;QAClB,YAAY,EAAE,IAAI,YAAY,CAAC,KAAK,GAAG,aAAa,CAAC;QACrD,KAAK;QACL,mBAAmB,EAAE,IAAI,YAAY,CAAC,KAAK,GAAG,aAAa,CAAC;QAC5D,KAAK;QACL,aAAa,EAAE,IAAI,YAAY,CAAC,KAAK,GAAG,aAAa,CAAC;KACvD,CAAC,CAAC;AACL,CAAC;AAED,yGAAyG;AACzG,6FAA6F;AAC7F,MAAM,UAAU,iBAAiB,CAAC,QAAoB;IACpD,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;IACpB,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAuB,EAAE,CAAuB;IAC/E,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,IACE,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;YAC/B,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACX,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACX,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;YACzB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YACrB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YACrB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YACrB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YACrB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YACrB,CAAC,CAAC,aAAa,CAAC,QAAQ,KAAK,CAAC,CAAC,aAAa,CAAC,QAAQ;YACrD,CAAC,CAAC,aAAa,CAAC,KAAK,KAAK,CAAC,CAAC,aAAa,CAAC,KAAK;YAC/C,CAAC,CAAC,aAAa,CAAC,UAAU,KAAK,CAAC,CAAC,aAAa,CAAC,UAAU;YACzD,CAAC,CAAC,aAAa,CAAC,WAAW,KAAK,CAAC,CAAC,aAAa,CAAC,WAAW,EAC3D,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,4BAA4B,CAAC,QAA8B,EAAE,IAAY;IACvF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,4GAA4G;AAC5G,8GAA8G;AAC9G,MAAM,UAAU,4BAA4B,CAC1C,GAAe,EACf,QAA8B,EAC9B,SAAiB;IAEjB,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACtE,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,aAAa,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2GAA2G;AAC3G,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,QAA8B,EAAE,IAAY;IAC5E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,qBAAqB,CAAC,QAA8B;IAClE,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,mBAAmB,CAAC;IACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;QAC5B,UAAU,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC;YAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC5E,WAAW,CAAC,GAAG,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,4FAA4F;AAC5F,+EAA+E;AAC/E,gGAAgG;AAChG,4GAA4G;AAC5G,4GAA4G;AAC5G,4GAA4G;AAC5G,0BAA0B;AAC1B,EAAE;AACF,2GAA2G;AAC3G,yGAAyG;AACzG,4GAA4G;AAC5G,uGAAuG;AACvG,2GAA2G;AAC3G,MAAM,UAAU,iBAAiB,CAAC,QAAoB,EAAE,IAAgC;IACtF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO;IAClD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,IAAI,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM;YAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACnH,CAAC;AACH,CAAC;AAED,2GAA2G;AAC3G,6GAA6G;AAC7G,MAAM,UAAU,kBAAkB,CAAC,QAA8B;IAC/D,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IACpC,MAAM,QAAQ,GAAG,KAAK,GAAG,aAAa,CAAC;IACvC,IAAI,QAAQ,CAAC,aAAa,CAAC,MAAM,KAAK,QAAQ;QAC5C,OAAO,wBAAwB,QAAQ,CAAC,aAAa,CAAC,MAAM,OAAO,QAAQ,EAAE,CAAC;IAChF,IAAI,QAAQ,CAAC,mBAAmB,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACrD,OAAO,8BAA8B,QAAQ,CAAC,mBAAmB,CAAC,MAAM,OAAO,QAAQ,EAAE,CAAC;IAC5F,CAAC;IACD,IAAI,QAAQ,CAAC,YAAY,CAAC,MAAM,KAAK,QAAQ;QAC3C,OAAO,uBAAuB,QAAQ,CAAC,YAAY,CAAC,MAAM,OAAO,QAAQ,EAAE,CAAC;IAC9E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAClD,IAAI,WAAW,IAAI,CAAC;YAClB,OAAO,QAAQ,CAAC,gBAAgB,WAAW,qEAAqE,CAAC;QACnH,IAAI,WAAW,GAAG,CAAC,CAAC;YAAE,OAAO,QAAQ,CAAC,gBAAgB,WAAW,OAAO,CAAC;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAAC,GAAe,EAAE,MAA8B,EAAE,MAAc;IACjF,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACvB,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3B,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3B,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3B,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC5B,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAe;IAC7C,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACV,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IACX,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,MAAoB,EAAE,MAAc,EAAE,MAA4B;IACrF,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC1B,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;IAC/B,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,SAAS,GAAe,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AACvE,MAAM,SAAS,GAAe,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AACvE,MAAM,SAAS,GAAe,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/skeleton2d",
3
- "version": "0.3.0-next.1032.e2b99fc",
3
+ "version": "0.3.0-next.1162.e90e518",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/flighthq/flight.git",
@@ -36,11 +36,11 @@
36
36
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@flighthq/animation": "0.3.0-next.1032.e2b99fc",
40
- "@flighthq/entity": "0.3.0-next.1032.e2b99fc",
41
- "@flighthq/geometry": "0.3.0-next.1032.e2b99fc",
42
- "@flighthq/math": "0.3.0-next.1032.e2b99fc",
43
- "@flighthq/types": "0.3.0-next.1032.e2b99fc"
39
+ "@flighthq/animation": "0.3.0-next.1162.e90e518",
40
+ "@flighthq/entity": "0.3.0-next.1162.e90e518",
41
+ "@flighthq/geometry": "0.3.0-next.1162.e90e518",
42
+ "@flighthq/math": "0.3.0-next.1162.e90e518",
43
+ "@flighthq/types": "0.3.0-next.1162.e90e518"
44
44
  },
45
45
  "devDependencies": {
46
46
  "typescript": "^5.3.0"
@@ -1,6 +1,11 @@
1
1
  import { createAnimationChannel, createAnimationClip, createAnimationTrack } from '@flighthq/animation/contract';
2
2
  import type { Bone2D } from '@flighthq/types/contract';
3
- import { AnimationInterpolationLinear, Skeleton2DAnimationPath, TransformMode2D } from '@flighthq/types/contract';
3
+ import {
4
+ AnimationInterpolationLinear,
5
+ Skeleton2DAnimationPath,
6
+ Skeleton2DSlotAnimationPath,
7
+ TransformMode2D,
8
+ } from '@flighthq/types/contract';
4
9
  import { describe, expect, it } from 'vitest';
5
10
 
6
11
  import { applyAnimationClipToSkeleton2D } from './applyAnimationClipToSkeleton2D';
@@ -95,3 +100,87 @@ describe('applyAnimationClipToSkeleton2D', () => {
95
100
  expect(pose.bones[0].rotation).toBe(7); // untouched
96
101
  });
97
102
  });
103
+
104
+ describe('applyAnimationClipToSkeleton2D slot channels', () => {
105
+ it('WRITES an absolute slot colour rather than composing it onto the setup colour', () => {
106
+ // Composing would double-apply the tint; Spine and DragonBones both author colour absolutely.
107
+ const setup = createSkeleton2D([makeBone()], [{ attachment: null, boneIndex: 0, color: 0x112233ff, name: 's' }]);
108
+ const pose = cloneSkeleton2D(setup);
109
+ const clip = createAnimationClip([
110
+ createAnimationChannel(createAnimationTrack({ components: 4, times: [0, 1], values: [1, 0, 0, 1, 0, 0, 1, 1] }), {
111
+ path: Skeleton2DSlotAnimationPath.Color,
112
+ slotIndex: 0,
113
+ }),
114
+ ]);
115
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 0);
116
+ expect(pose.slots![0].color).toBe(0xff0000ff);
117
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 1);
118
+ expect(pose.slots![0].color).toBe(0x0000ffff);
119
+ expect(setup.slots![0].color).toBe(0x112233ff); // setup untouched
120
+ });
121
+
122
+ it('interpolates colour channels between keyframes', () => {
123
+ const setup = createSkeleton2D([makeBone()], [{ attachment: null, boneIndex: 0, color: 0, name: 's' }]);
124
+ const pose = cloneSkeleton2D(setup);
125
+ const clip = createAnimationClip([
126
+ createAnimationChannel(createAnimationTrack({ components: 4, times: [0, 1], values: [0, 0, 0, 0, 1, 1, 1, 1] }), {
127
+ path: Skeleton2DSlotAnimationPath.Color,
128
+ slotIndex: 0,
129
+ }),
130
+ ]);
131
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 0.5);
132
+ expect(pose.slots![0].color).toBe(0x80808080); // 0.5 -> 127.5 rounds to 128 on every channel
133
+ });
134
+
135
+ it('CLAMPS an overshooting sample instead of letting it wrap the packed colour', () => {
136
+ // An easing curve can legitimately overshoot its endpoints; 1.4 must saturate, not wrap to a low byte.
137
+ const setup = createSkeleton2D([makeBone()], [{ attachment: null, boneIndex: 0, color: 0, name: 's' }]);
138
+ const pose = cloneSkeleton2D(setup);
139
+ const clip = createAnimationClip([
140
+ createAnimationChannel(createAnimationTrack({ components: 4, times: [0], values: [1.4, -0.2, 1, 1] }), {
141
+ path: Skeleton2DSlotAnimationPath.Color,
142
+ slotIndex: 0,
143
+ }),
144
+ ]);
145
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 0);
146
+ expect(pose.slots![0].color).toBe(0xff00ffff);
147
+ });
148
+
149
+ it('ignores a slot channel whose index is out of range, or a skeleton with no slots', () => {
150
+ const setup = createSkeleton2D([makeBone()], [{ attachment: null, boneIndex: 0, color: 0x010203ff, name: 's' }]);
151
+ const pose = cloneSkeleton2D(setup);
152
+ const clip = createAnimationClip([
153
+ createAnimationChannel(createAnimationTrack({ components: 4, times: [0], values: [1, 1, 1, 1] }), {
154
+ path: Skeleton2DSlotAnimationPath.Color,
155
+ slotIndex: 9,
156
+ }),
157
+ ]);
158
+ expect(() => applyAnimationClipToSkeleton2D(clip, setup, pose, 0)).not.toThrow();
159
+ expect(pose.slots![0].color).toBe(0x010203ff);
160
+
161
+ const boneOnly = createSkeleton2D([makeBone()]);
162
+ const bonePose = cloneSkeleton2D(boneOnly);
163
+ expect(() => applyAnimationClipToSkeleton2D(clip, boneOnly, bonePose, 0)).not.toThrow();
164
+ });
165
+
166
+ it('drives bone and slot channels from ONE clip, dispatching on target shape', () => {
167
+ const setup = createSkeleton2D(
168
+ [makeBone({ rotation: 10 })],
169
+ [{ attachment: null, boneIndex: 0, color: 0, name: 's' }],
170
+ );
171
+ const pose = cloneSkeleton2D(setup);
172
+ const clip = createAnimationClip([
173
+ createAnimationChannel(createAnimationTrack({ components: 1, times: [0], values: [90] }), {
174
+ boneIndex: 0,
175
+ path: Skeleton2DAnimationPath.Rotation,
176
+ }),
177
+ createAnimationChannel(createAnimationTrack({ components: 4, times: [0], values: [1, 1, 1, 1] }), {
178
+ path: Skeleton2DSlotAnimationPath.Color,
179
+ slotIndex: 0,
180
+ }),
181
+ ]);
182
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 0);
183
+ expect(pose.bones[0].rotation).toBeCloseTo(100, 5); // bone still COMPOSES onto setup
184
+ expect(pose.slots![0].color).toBe(0xffffffff); // slot WRITES
185
+ });
186
+ });
@@ -1,6 +1,6 @@
1
1
  import { createMatrix } from '@flighthq/geometry/contract';
2
- import type { Bone2D } from '@flighthq/types/contract';
3
- import { TransformMode2D } from '@flighthq/types/contract';
2
+ import type { Bone2D, RegionAttachment2D, Slot2D } from '@flighthq/types/contract';
3
+ import { RegionAttachment2DKind, TransformMode2D } from '@flighthq/types/contract';
4
4
  import { describe, expect, it } from 'vitest';
5
5
 
6
6
  import {
@@ -12,7 +12,9 @@ import {
12
12
  equalsSkeleton2D,
13
13
  getSkeleton2DBoneIndexByName,
14
14
  getSkeleton2DBoneWorldMatrix,
15
+ getSkeleton2DSkin,
15
16
  setSkeleton2DBindPose,
17
+ setSkeleton2DSkin,
16
18
  validateSkeleton2D,
17
19
  } from './skeleton2d';
18
20
 
@@ -244,6 +246,27 @@ describe('computeSkeleton2DWorldTransforms', () => {
244
246
  expect(s.worldMatrices[2]).toBeCloseTo(0, 5); // c
245
247
  expect(s.worldMatrices[3]).toBeCloseTo(1, 5); // d
246
248
  });
249
+
250
+ it('FORMULA parity: an inherit combo with NO named preset — keep rotation+scale, strip reflection', () => {
251
+ // {rotation, scale} true but reflection false has no TransformMode2D preset; the factored boolean model
252
+ // handles it. Under a reflected+scaled parent [2,0,0,-2] (det −4), it keeps the (2,2) scale but strips
253
+ // the reflection, so the child basis is [2,0,0,2] (det +4) rather than Normal's [2,0,0,-2].
254
+ const s = createSkeleton2D([
255
+ makeBone({ scaleX: 2, scaleY: -2 }), // reflected (negative Y scale)
256
+ makeBone({
257
+ parentIndex: 0,
258
+ transformMode: { reflection: false, rotation: true, scale: true, translation: true },
259
+ }),
260
+ ]);
261
+ computeSkeleton2DWorldTransforms(s);
262
+ const out = createMatrix();
263
+ getSkeleton2DBoneWorldMatrix(out, s, 1);
264
+ expect(out.a).toBeCloseTo(2, 5);
265
+ expect(out.b).toBeCloseTo(0, 5);
266
+ expect(out.c).toBeCloseTo(0, 5);
267
+ expect(out.d).toBeCloseTo(2, 5); // reflection stripped: +2, not −2
268
+ expect(out.a * out.d - out.c * out.b).toBeCloseTo(4, 5); // det positive (scale kept, reflection dropped)
269
+ });
247
270
  });
248
271
 
249
272
  describe('createSkeleton2D', () => {
@@ -295,6 +318,23 @@ describe('getSkeleton2DBoneWorldMatrix', () => {
295
318
  });
296
319
  });
297
320
 
321
+ describe('getSkeleton2DSkin', () => {
322
+ it('finds a skin by name and returns the null sentinel otherwise', () => {
323
+ const skeleton = createSkeleton2D([makeBone({ name: 'root' })], [testSlot('head', 0)]);
324
+ skeleton.skins = [
325
+ { attachments: [], name: 'goblin' },
326
+ { attachments: [], name: 'goblingirl' },
327
+ ];
328
+ expect(getSkeleton2DSkin(skeleton, 'goblingirl')!.name).toBe('goblingirl');
329
+ expect(getSkeleton2DSkin(skeleton, 'nope')).toBeNull();
330
+ });
331
+
332
+ it('returns null for a rig carrying no wardrobe at all', () => {
333
+ const skeleton = createSkeleton2D([makeBone({ name: 'root' })]);
334
+ expect(getSkeleton2DSkin(skeleton, 'goblin')).toBeNull();
335
+ });
336
+ });
337
+
298
338
  describe('setSkeleton2DBindPose', () => {
299
339
  it('captures the inverse of the current world so the palette is identity at bind', () => {
300
340
  const s = createSkeleton2D([makeBone({ x: 4, rotation: 60, scaleX: 2 })]);
@@ -308,6 +348,49 @@ describe('setSkeleton2DBindPose', () => {
308
348
  });
309
349
  });
310
350
 
351
+ describe('setSkeleton2DSkin', () => {
352
+ it('writes the skin attachments onto the slots they name', () => {
353
+ const skeleton = createSkeleton2D([makeBone({ name: 'root' })], [testSlot('head', 0), testSlot('hand', 0)]);
354
+ const hat = testRegion('hat');
355
+ setSkeleton2DSkin(skeleton, { attachments: [{ attachment: hat, name: 'head', slotIndex: 0 }], name: 'goblin' });
356
+ expect(skeleton.slots![0].attachment).toBe(hat);
357
+ });
358
+
359
+ it('LEAVES slots the skin does not mention alone, so a partial skin layers over a base', () => {
360
+ const base = testRegion('body');
361
+ const skeleton = createSkeleton2D([makeBone({ name: 'root' })], [testSlot('head', 0), testSlot('body', 0)]);
362
+ skeleton.slots![1].attachment = base;
363
+ const hat = testRegion('hat');
364
+ setSkeleton2DSkin(skeleton, { attachments: [{ attachment: hat, name: 'head', slotIndex: 0 }], name: 'goblin' });
365
+ expect(skeleton.slots![0].attachment).toBe(hat);
366
+ expect(skeleton.slots![1].attachment).toBe(base); // shared art survives the overlay
367
+ });
368
+
369
+ it('skips an entry naming a slot outside the skeleton rather than throwing', () => {
370
+ const skeleton = createSkeleton2D([makeBone({ name: 'root' })], [testSlot('head', 0)]);
371
+ expect(() =>
372
+ setSkeleton2DSkin(skeleton, {
373
+ attachments: [
374
+ { attachment: testRegion('ghost'), name: 'ghost', slotIndex: 9 },
375
+ { attachment: testRegion('ghost2'), name: 'ghost2', slotIndex: -1 },
376
+ ],
377
+ name: 'broken',
378
+ }),
379
+ ).not.toThrow();
380
+ expect(skeleton.slots![0].attachment).toBeNull();
381
+ });
382
+
383
+ it('is a no-op on a skeleton with no slots', () => {
384
+ const skeleton = createSkeleton2D([makeBone({ name: 'root' })]);
385
+ expect(() =>
386
+ setSkeleton2DSkin(skeleton, {
387
+ attachments: [{ attachment: testRegion('x'), name: 'x', slotIndex: 0 }],
388
+ name: 's',
389
+ }),
390
+ ).not.toThrow();
391
+ });
392
+ });
393
+
311
394
  describe('validateSkeleton2D', () => {
312
395
  it('returns null for a valid parent-before-child skeleton', () => {
313
396
  const s = createSkeleton2D([makeBone(), makeBone({ parentIndex: 0 })]);
@@ -325,3 +408,12 @@ describe('validateSkeleton2D', () => {
325
408
  expect(validateSkeleton2D(s)).toContain('worldMatrices');
326
409
  });
327
410
  });
411
+
412
+ // A minimal slot for wardrobe tests: no attachment until a skin supplies one.
413
+ function testSlot(name: string, boneIndex: number): Slot2D {
414
+ return { attachment: null, boneIndex, color: 0xffffffff, name };
415
+ }
416
+
417
+ function testRegion(name: string): RegionAttachment2D {
418
+ return { height: 0, kind: RegionAttachment2DKind, name, rotation: 0, scaleX: 1, scaleY: 1, width: 0, x: 0, y: 0 };
419
+ }