@flighthq/skeleton2d 0.2.1-next.841.373388f → 0.3.0-edge.1516.1b4fb2b

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,3 +1,3 @@
1
- import type { AnimationClip, Skeleton2D } from '@flighthq/types';
2
- export declare function applyAnimationClipToSkeleton2D(clip: Readonly<AnimationClip>, skeleton: Skeleton2D, time: number): void;
1
+ import type { AnimationClip, Skeleton2D } from '@flighthq/types/contract';
2
+ export declare function applyAnimationClipToSkeleton2D(clip: Readonly<AnimationClip>, setup: Readonly<Skeleton2D>, pose: Skeleton2D, time: number): void;
3
3
  //# sourceMappingURL=applyAnimationClipToSkeleton2D.d.ts.map
@@ -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,iBAAiB,CAAC;AAU5F,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC7B,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,GACX,IAAI,CA+BN"}
1
+ {"version":3,"file":"applyAnimationClipToSkeleton2D.d.ts","sourceRoot":"","sources":["../src/applyAnimationClipToSkeleton2D.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,aAAa,EACb,UAAU,EAIX,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,45 +1,134 @@
1
- import { sampleAnimationTrack } from '@flighthq/animation';
2
- import { Skeleton2DAnimationPath } from '@flighthq/types';
3
- // Poses a Skeleton2D from an AnimationClip at `time`: for each channel whose `targetRef` is a
4
- // Skeleton2DAnimationTarget, samples the channel's track and writes the value into the target bone's local
5
- // setup-transform fields. The 2D-skeletal analogue of @flighthq/scene3d's `applyAnimationClipToScene3D`, over
6
- // `Bone2D` instead of a `Node3D`. It mutates only the bones' local pose — the caller runs
7
- // `computeSkeleton2DWorldTransforms` (then the deform) afterward, so a whole clip applies before one world
8
- // propagation. Module scratch, allocation-free. A channel whose target is foreign (not a
9
- // Skeleton2DAnimationTarget) or out of range is skipped (sentinel guard, no throw).
10
- export function applyAnimationClipToSkeleton2D(clip, skeleton, time) {
1
+ import { sampleAnimationTrack } from '@flighthq/animation/contract';
2
+ import { Skeleton2DAnimationPath, Skeleton2DSlotAnimationPath } from '@flighthq/types/contract';
3
+ // Poses `pose` from an AnimationClip at `time` by COMPOSING each channel's sampled value onto the
4
+ // corresponding `setup` bone — the 2D-skeletal analogue of @flighthq/scene3d's `applyAnimationClipToScene3D`,
5
+ // but relative rather than absolute. This mirrors Spine's SkeletonData(setup)/Skeleton(instance) split:
6
+ // `setup` is the immutable rest pose, `pose` is the animated instance (typically a `cloneSkeleton2D(setup)`),
7
+ // and a clip carries RELATIVE deltas — translate/rotate/shear ADD to setup, scale MULTIPLIES it (Spine's
8
+ // timeline semantics; the `path` distinguishes which). Composing from `setup` every call (not from `pose`)
9
+ // is what makes clips blendable — a mixer accumulates deltas as `pose = setup + Σ wᵢ·deltaᵢ`, which averaging
10
+ // baked-absolute poses cannot express — and is why re-applying a clip does not accumulate across frames.
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
+ //
17
+ // It writes only the bones a channel targets; untouched pose bones keep their clone-of-setup values. The
18
+ // caller runs `computeSkeleton2DWorldTransforms(pose, …)` (then the deform) afterward, so a whole clip
19
+ // applies before one world propagation. Module scratch, allocation-free. A channel whose target is foreign
20
+ // (not a Skeleton2DAnimationTarget) or out of range is skipped (sentinel guard, no throw).
21
+ //
22
+ // This is the documented exception to the out-param aliasing rule: `setup` is read across the whole pass
23
+ // while `pose` is written, so they cannot be the same object. Passing one skeleton as both would clobber
24
+ // the rest pose the binder composes against and then accumulate against garbage every subsequent frame —
25
+ // a silent, delayed corruption — so it is a programmer error the guard below throws on, not a sentinel.
26
+ export function applyAnimationClipToSkeleton2D(clip, setup, pose, time) {
27
+ if (setup === pose) {
28
+ throw new Error('applyAnimationClipToSkeleton2D: setup and pose must be distinct skeletons — pass a cloneSkeleton2D(setup) as pose');
29
+ }
11
30
  const channels = clip.channels;
12
- const bones = skeleton.bones;
31
+ const setupBones = setup.bones;
32
+ const poseBones = pose.bones;
13
33
  for (let i = 0; i < channels.length; i++) {
14
34
  const channel = channels[i];
15
35
  const target = channel.targetRef;
16
- 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);
17
43
  continue;
44
+ }
18
45
  const boneIndex = target.boneIndex;
19
- if (boneIndex < 0 || boneIndex >= bones.length)
46
+ if (boneIndex < 0 || boneIndex >= poseBones.length || boneIndex >= setupBones.length)
20
47
  continue;
21
48
  sampleAnimationTrack(_scratch, channel.track, time);
22
- const bone = bones[boneIndex];
49
+ const setupBone = setupBones[boneIndex];
50
+ const poseBone = poseBones[boneIndex];
23
51
  switch (target.path) {
24
52
  case Skeleton2DAnimationPath.Translation:
25
- bone.x = _scratch[0];
26
- bone.y = _scratch[1];
53
+ poseBone.x = setupBone.x + _scratch[0];
54
+ poseBone.y = setupBone.y + _scratch[1];
27
55
  break;
28
56
  case Skeleton2DAnimationPath.Rotation:
29
- bone.rotation = _scratch[0];
57
+ poseBone.rotation = setupBone.rotation + _scratch[0];
30
58
  break;
31
59
  case Skeleton2DAnimationPath.Scale:
32
- bone.scaleX = _scratch[0];
33
- bone.scaleY = _scratch[1];
60
+ poseBone.scaleX = setupBone.scaleX * _scratch[0];
61
+ poseBone.scaleY = setupBone.scaleY * _scratch[1];
34
62
  break;
35
63
  case Skeleton2DAnimationPath.Shear:
36
- bone.shearX = _scratch[0];
37
- bone.shearY = _scratch[1];
64
+ poseBone.shearX = setupBone.shearX + _scratch[0];
65
+ poseBone.shearY = setupBone.shearY + _scratch[1];
38
66
  break;
39
67
  default:
40
68
  break;
41
69
  }
42
70
  }
43
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.Attachment) {
89
+ applySkeleton2DSlotAttachment(channel, slots[slotIndex], target, time);
90
+ return;
91
+ }
92
+ if (target.path !== Skeleton2DSlotAnimationPath.Color)
93
+ return;
94
+ sampleAnimationTrack(_scratch, channel.track, time);
95
+ slots[slotIndex].color =
96
+ ((clampColorChannel(_scratch[0]) << 24) |
97
+ (clampColorChannel(_scratch[1]) << 16) |
98
+ (clampColorChannel(_scratch[2]) << 8) |
99
+ clampColorChannel(_scratch[3])) >>>
100
+ 0;
101
+ }
102
+ // Swaps the slot's attachment by looking the sampled INDEX up in the target's table.
103
+ //
104
+ // It reads the track with its own STEP walk rather than `sampleAnimationTrack`, and that is deliberate
105
+ // rather than duplication: an attachment index must never be interpolated. If a track were built (or later
106
+ // edited) as Linear, sampling would blend between two TABLE INDICES and hand back something between them —
107
+ // a plausible-looking index that names the wrong art, or a fractional one. Forcing the step here means the
108
+ // channel is correct regardless of what the track claims its interpolation is.
109
+ //
110
+ // An index outside the table, or the `-1` Spine writes for "no attachment", clears the slot.
111
+ function applySkeleton2DSlotAttachment(channel, slot, target, time) {
112
+ const table = target.attachments;
113
+ if (table === undefined || table === null)
114
+ return;
115
+ const times = channel.track.times;
116
+ const count = times.length;
117
+ if (count === 0)
118
+ return;
119
+ // The last keyframe at or before `time`; before the first keyframe the first value holds.
120
+ let keyframe = 0;
121
+ for (let i = count - 1; i >= 0; i--) {
122
+ if (times[i] <= time) {
123
+ keyframe = i;
124
+ break;
125
+ }
126
+ }
127
+ const index = Math.round(channel.track.values[keyframe * channel.track.components]);
128
+ slot.attachment = index >= 0 && index < table.length ? table[index] : null;
129
+ }
130
+ function clampColorChannel(value) {
131
+ return value <= 0 ? 0 : value >= 1 ? 255 : Math.round(value * 255);
132
+ }
44
133
  const _scratch = [0, 0, 0, 0];
45
134
  //# 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,qBAAqB,CAAC;AAE3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,8FAA8F;AAC9F,2GAA2G;AAC3G,8GAA8G;AAC9G,0FAA0F;AAC1F,2GAA2G;AAC3G,yFAAyF;AACzF,oFAAoF;AACpF,MAAM,UAAU,8BAA8B,CAC5C,IAA6B,EAC7B,QAAoB,EACpB,IAAY;IAEZ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,MAAM,KAAK,GAAG,QAAQ,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,KAAK,CAAC,MAAM;YAAE,SAAS;QACzD,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,uBAAuB,CAAC,WAAW;gBACtC,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACrB,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACrB,MAAM;YACR,KAAK,uBAAuB,CAAC,QAAQ;gBACnC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,uBAAuB,CAAC,KAAK;gBAChC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,MAAM;YACR,KAAK,uBAAuB,CAAC,KAAK;gBAChC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,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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"}
@@ -0,0 +1,5 @@
1
+ export * from './skeleton2d';
2
+ export * from './deformMeshAttachment2D';
3
+ export * from './regionAttachment2D';
4
+ export * from './applyAnimationClipToSkeleton2D';
5
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kCAAkC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from './skeleton2d';
2
+ export * from './deformMeshAttachment2D';
3
+ export * from './regionAttachment2D';
4
+ export * from './applyAnimationClipToSkeleton2D';
5
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kCAAkC,CAAC"}
@@ -1,3 +1,3 @@
1
- import type { MeshAttachment2D, Skeleton2D } from '@flighthq/types';
1
+ import type { MeshAttachment2D, Skeleton2D } from '@flighthq/types/contract';
2
2
  export declare function deformSkeleton2DMeshAttachment(out: Float32Array, attachment: Readonly<MeshAttachment2D>, skeleton: Readonly<Skeleton2D>, boneIndex: number): void;
3
3
  //# sourceMappingURL=deformMeshAttachment2D.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deformMeshAttachment2D.d.ts","sourceRoot":"","sources":["../src/deformMeshAttachment2D.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgBpE,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,YAAY,EACjB,UAAU,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EACtC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,MAAM,GAChB,IAAI,CA4CN"}
1
+ {"version":3,"file":"deformMeshAttachment2D.d.ts","sourceRoot":"","sources":["../src/deformMeshAttachment2D.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAgB7E,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,YAAY,EACjB,UAAU,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EACtC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,MAAM,GAChB,IAAI,CA4CN"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,2 @@
1
- export * from './skeleton2d';
2
- export * from './deformMeshAttachment2D';
3
- export * from './regionAttachment2D';
4
- export * from './applyAnimationClipToSkeleton2D';
1
+ export { applyAnimationClipToSkeleton2D, cloneSkeleton2D, computeSkeleton2DBoneMatrices, computeSkeleton2DRegionAttachmentVertices, computeSkeleton2DWorldTransforms, createSkeleton2D, deformSkeleton2DMeshAttachment, disposeSkeleton2D, equalsSkeleton2D, getSkeleton2DBoneIndexByName, getSkeleton2DBoneWorldMatrix, setSkeleton2DBindPose, validateSkeleton2D, } from './contract';
5
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,eAAe,EACf,6BAA6B,EAC7B,yCAAyC,EACzC,gCAAgC,EAChC,gBAAgB,EAChB,8BAA8B,EAC9B,iBAAiB,EACjB,gBAAgB,EAChB,4BAA4B,EAC5B,4BAA4B,EAC5B,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,2 @@
1
- export * from './skeleton2d';
2
- export * from './deformMeshAttachment2D';
3
- export * from './regionAttachment2D';
4
- export * from './applyAnimationClipToSkeleton2D';
1
+ export { applyAnimationClipToSkeleton2D, cloneSkeleton2D, computeSkeleton2DBoneMatrices, computeSkeleton2DRegionAttachmentVertices, computeSkeleton2DWorldTransforms, createSkeleton2D, deformSkeleton2DMeshAttachment, disposeSkeleton2D, equalsSkeleton2D, getSkeleton2DBoneIndexByName, getSkeleton2DBoneWorldMatrix, setSkeleton2DBindPose, validateSkeleton2D, } from './contract';
5
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,eAAe,EACf,6BAA6B,EAC7B,yCAAyC,EACzC,gCAAgC,EAChC,gBAAgB,EAChB,8BAA8B,EAC9B,iBAAiB,EACjB,gBAAgB,EAChB,4BAA4B,EAC5B,4BAA4B,EAC5B,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
@@ -1,3 +1,3 @@
1
- import type { RegionAttachment2D, Skeleton2D } from '@flighthq/types';
1
+ import type { RegionAttachment2D, Skeleton2D } from '@flighthq/types/contract';
2
2
  export declare function computeSkeleton2DRegionAttachmentVertices(out: Float32Array, attachment: Readonly<RegionAttachment2D>, skeleton: Readonly<Skeleton2D>, boneIndex: number): void;
3
3
  //# sourceMappingURL=regionAttachment2D.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"regionAttachment2D.d.ts","sourceRoot":"","sources":["../src/regionAttachment2D.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAc,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAUlF,wBAAgB,yCAAyC,CACvD,GAAG,EAAE,YAAY,EACjB,UAAU,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACxC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,MAAM,GAChB,IAAI,CAkCN"}
1
+ {"version":3,"file":"regionAttachment2D.d.ts","sourceRoot":"","sources":["../src/regionAttachment2D.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAc,kBAAkB,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAU3F,wBAAgB,yCAAyC,CACvD,GAAG,EAAE,YAAY,EACjB,UAAU,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACxC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,MAAM,GAChB,IAAI,CAkCN"}
@@ -1,5 +1,5 @@
1
- import { matrixTransformPointXY, multiplyMatrix, setTransformMatrix } from '@flighthq/geometry';
2
- import { DEG_TO_RAD } from '@flighthq/math';
1
+ import { matrixTransformPointXY, multiplyMatrix, setTransformMatrix } from '@flighthq/geometry/contract';
2
+ import { DEG_TO_RAD } from '@flighthq/math/contract';
3
3
  // 6 floats per bone in the flat world-transform buffer (a, b, c, d, tx, ty).
4
4
  const MATRIX_STRIDE = 6;
5
5
  // Writes a RegionAttachment2D's four world corner positions into `out` as flat interleaved pairs — order
@@ -1 +1 @@
1
- {"version":3,"file":"regionAttachment2D.js","sourceRoot":"","sources":["../src/regionAttachment2D.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"regionAttachment2D.js","sourceRoot":"","sources":["../src/regionAttachment2D.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Bone2D, MatrixLike, Skeleton2D } from '@flighthq/types';
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,iBAAiB,CAAC;AAStE,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
- import { createEntity } from '@flighthq/entity';
2
- import { inverseMatrix, multiplyMatrix } from '@flighthq/geometry';
3
- import { DEG_TO_RAD } from '@flighthq/math';
4
- import { TransformMode2D } from '@flighthq/types';
1
+ import { createEntity } from '@flighthq/entity/contract';
2
+ import { inverseMatrix, multiplyMatrix } from '@flighthq/geometry/contract';
3
+ import { DEG_TO_RAD } from '@flighthq/math/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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/skeleton2d",
3
- "version": "0.2.1-next.841.373388f",
3
+ "version": "0.3.0-edge.1516.1b4fb2b",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/flighthq/flight.git",
@@ -13,6 +13,10 @@
13
13
  ".": {
14
14
  "types": "./dist/index.d.ts",
15
15
  "default": "./dist/index.js"
16
+ },
17
+ "./contract": {
18
+ "types": "./dist/contract.d.ts",
19
+ "default": "./dist/contract.js"
16
20
  }
17
21
  },
18
22
  "files": [
@@ -32,11 +36,11 @@
32
36
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
33
37
  },
34
38
  "dependencies": {
35
- "@flighthq/animation": "0.2.1-next.841.373388f",
36
- "@flighthq/entity": "0.2.1-next.841.373388f",
37
- "@flighthq/geometry": "0.2.1-next.841.373388f",
38
- "@flighthq/math": "0.2.1-next.841.373388f",
39
- "@flighthq/types": "0.2.1-next.841.373388f"
39
+ "@flighthq/animation": "0.3.0-edge.1516.1b4fb2b",
40
+ "@flighthq/entity": "0.3.0-edge.1516.1b4fb2b",
41
+ "@flighthq/geometry": "0.3.0-edge.1516.1b4fb2b",
42
+ "@flighthq/math": "0.3.0-edge.1516.1b4fb2b",
43
+ "@flighthq/types": "0.3.0-edge.1516.1b4fb2b"
40
44
  },
41
45
  "devDependencies": {
42
46
  "typescript": "^5.3.0"
@@ -1,10 +1,15 @@
1
- import { createAnimationChannel, createAnimationClip, createAnimationTrack } from '@flighthq/animation';
2
- import type { Bone2D } from '@flighthq/types';
3
- import { AnimationInterpolationLinear, Skeleton2DAnimationPath, TransformMode2D } from '@flighthq/types';
1
+ import { createAnimationChannel, createAnimationClip, createAnimationTrack } from '@flighthq/animation/contract';
2
+ import type { Bone2D } 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';
7
- import { createSkeleton2D } from './skeleton2d';
12
+ import { cloneSkeleton2D, createSkeleton2D } from './skeleton2d';
8
13
 
9
14
  function makeBone(overrides: Partial<Bone2D> = {}): Bone2D {
10
15
  return {
@@ -28,23 +33,26 @@ function scalarTrack(times: number[], values: number[], components: number) {
28
33
  }
29
34
 
30
35
  describe('applyAnimationClipToSkeleton2D', () => {
31
- it('samples a rotation track into the target bone at the given time', () => {
32
- const s = createSkeleton2D([makeBone()]);
36
+ it('composes a rotation delta onto the setup pose (setup + delta), leaving setup untouched', () => {
37
+ const setup = createSkeleton2D([makeBone({ rotation: 10 })]);
38
+ const pose = cloneSkeleton2D(setup);
33
39
  const clip = createAnimationClip([
34
40
  createAnimationChannel(scalarTrack([0, 1], [0, 90], 1), { boneIndex: 0, path: Skeleton2DAnimationPath.Rotation }),
35
41
  ]);
36
- applyAnimationClipToSkeleton2D(clip, s, 0.5);
37
- expect(s.bones[0].rotation).toBeCloseTo(45, 5); // linear 0→90 at t=0.5
42
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 0.5);
43
+ expect(pose.bones[0].rotation).toBeCloseTo(55, 5); // setup 10 + delta 45 (linear 0→90 at t=0.5)
44
+ expect(setup.bones[0].rotation).toBe(10); // the rest pose is read-only, unchanged
38
45
  });
39
46
 
40
- it('drives translation, scale, and shear across their two-component paths', () => {
41
- const s = createSkeleton2D([makeBone()]);
47
+ it('adds translation/shear deltas and multiplies scale deltas onto setup', () => {
48
+ const setup = createSkeleton2D([makeBone({ x: 5, y: -2, scaleX: 2, scaleY: 3, shearX: 1, shearY: 4 })]);
49
+ const pose = cloneSkeleton2D(setup);
42
50
  const clip = createAnimationClip([
43
51
  createAnimationChannel(scalarTrack([0, 1], [0, 0, 10, 20], 2), {
44
52
  boneIndex: 0,
45
53
  path: Skeleton2DAnimationPath.Translation,
46
54
  }),
47
- createAnimationChannel(scalarTrack([0, 1], [1, 1, 3, 4], 2), {
55
+ createAnimationChannel(scalarTrack([0, 1], [1, 1, 3, 0.5], 2), {
48
56
  boneIndex: 0,
49
57
  path: Skeleton2DAnimationPath.Scale,
50
58
  }),
@@ -53,22 +61,126 @@ describe('applyAnimationClipToSkeleton2D', () => {
53
61
  path: Skeleton2DAnimationPath.Shear,
54
62
  }),
55
63
  ]);
56
- applyAnimationClipToSkeleton2D(clip, s, 1);
57
- expect(s.bones[0].x).toBeCloseTo(10, 5);
58
- expect(s.bones[0].y).toBeCloseTo(20, 5);
59
- expect(s.bones[0].scaleX).toBeCloseTo(3, 5);
60
- expect(s.bones[0].scaleY).toBeCloseTo(4, 5);
61
- expect(s.bones[0].shearX).toBeCloseTo(5, 5);
62
- expect(s.bones[0].shearY).toBeCloseTo(6, 5);
64
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 1);
65
+ expect(pose.bones[0].x).toBeCloseTo(15, 5); // 5 + 10
66
+ expect(pose.bones[0].y).toBeCloseTo(18, 5); // -2 + 20
67
+ expect(pose.bones[0].scaleX).toBeCloseTo(6, 5); // 2 * 3 (multiplier)
68
+ expect(pose.bones[0].scaleY).toBeCloseTo(1.5, 5); // 3 * 0.5 (multiplier)
69
+ expect(pose.bones[0].shearX).toBeCloseTo(6, 5); // 1 + 5
70
+ expect(pose.bones[0].shearY).toBeCloseTo(10, 5); // 4 + 6
71
+ });
72
+
73
+ it('re-applies from setup each frame — no accumulation across repeated calls', () => {
74
+ const setup = createSkeleton2D([makeBone({ rotation: 10 })]);
75
+ const pose = cloneSkeleton2D(setup);
76
+ const clip = createAnimationClip([
77
+ createAnimationChannel(scalarTrack([0, 1], [0, 90], 1), { boneIndex: 0, path: Skeleton2DAnimationPath.Rotation }),
78
+ ]);
79
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 1); // 10 + 90 = 100
80
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 1); // composes from setup again → still 100, not 190
81
+ expect(pose.bones[0].rotation).toBeCloseTo(100, 5);
82
+ });
83
+
84
+ it('throws when setup and pose are the same skeleton (aliasing would corrupt the rest pose)', () => {
85
+ const s = createSkeleton2D([makeBone({ rotation: 10 })]);
86
+ const clip = createAnimationClip([
87
+ createAnimationChannel(scalarTrack([0, 1], [0, 90], 1), { boneIndex: 0, path: Skeleton2DAnimationPath.Rotation }),
88
+ ]);
89
+ expect(() => applyAnimationClipToSkeleton2D(clip, s, s, 1)).toThrow(/distinct skeletons/);
63
90
  });
64
91
 
65
92
  it('skips a channel with a foreign target or an out-of-range bone (no throw, no mutation)', () => {
66
- const s = createSkeleton2D([makeBone({ rotation: 7 })]);
93
+ const setup = createSkeleton2D([makeBone({ rotation: 7 })]);
94
+ const pose = cloneSkeleton2D(setup);
67
95
  const clip = createAnimationClip([
68
96
  createAnimationChannel(scalarTrack([0, 1], [0, 90], 1), { someOtherTarget: true }),
69
97
  createAnimationChannel(scalarTrack([0, 1], [0, 90], 1), { boneIndex: 9, path: Skeleton2DAnimationPath.Rotation }),
70
98
  ]);
71
- applyAnimationClipToSkeleton2D(clip, s, 1);
72
- expect(s.bones[0].rotation).toBe(7); // untouched
99
+ applyAnimationClipToSkeleton2D(clip, setup, pose, 1);
100
+ expect(pose.bones[0].rotation).toBe(7); // untouched
101
+ });
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
73
185
  });
74
186
  });
@@ -1,5 +1,5 @@
1
- import type { Bone2D, MeshAttachment2D, Skin2D } from '@flighthq/types';
2
- import { MeshAttachment2DKind, TransformMode2D } from '@flighthq/types';
1
+ import type { Bone2D, MeshAttachment2D, Skin2D } from '@flighthq/types/contract';
2
+ import { MeshAttachment2DKind, TransformMode2D } from '@flighthq/types/contract';
3
3
  import { describe, expect, it } from 'vitest';
4
4
 
5
5
  import { deformSkeleton2DMeshAttachment } from './deformMeshAttachment2D';
@@ -1,5 +1,5 @@
1
- import type { Bone2D, RegionAttachment2D } from '@flighthq/types';
2
- import { RegionAttachment2DKind, TransformMode2D } from '@flighthq/types';
1
+ import type { Bone2D, RegionAttachment2D } from '@flighthq/types/contract';
2
+ import { RegionAttachment2DKind, TransformMode2D } from '@flighthq/types/contract';
3
3
  import { describe, expect, it } from 'vitest';
4
4
 
5
5
  import { computeSkeleton2DRegionAttachmentVertices } from './regionAttachment2D';
@@ -1,6 +1,6 @@
1
- import { createMatrix } from '@flighthq/geometry';
2
- import type { Bone2D } from '@flighthq/types';
3
- import { TransformMode2D } from '@flighthq/types';
1
+ import { createMatrix } from '@flighthq/geometry/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
 
@@ -151,6 +153,120 @@ describe('computeSkeleton2DWorldTransforms', () => {
151
153
  const det = out.a * out.d - out.c * out.b;
152
154
  expect(det).toBeCloseTo(1, 5);
153
155
  });
156
+
157
+ // FORMULA parity — expected world matrices computed BY HAND from Spine's published bone-transform
158
+ // formulas (updateWorldTransform per transformMode), NOT from a real rig corpus. These use an ASYMMETRIC
159
+ // parent (rotation 90°, scaleX 2, scaleY 1 → world column-lengths psx=2, psy=1) so a bug that confused
160
+ // the two column scales, or that skipped the parent×local compose, would surface — cases the symmetric
161
+ // fixtures above cannot catch. Corpus parity against Spine's own runtime output remains open and requires
162
+ // a permissively-licensed rig AND oracle (see skeleton2d-formats charter); this proves formula match only.
163
+ it('FORMULA parity: Normal composes parent × local (asymmetric 90°/2×1 parent + a 90° child)', () => {
164
+ // Parent world = [a,b,c,d] = [cos90·2, sin90·2, cos180·1, sin180·1] = [0, 2, -1, 0].
165
+ // Child local (rot 90°, unit scale) = [0, 1, -1, 0]. Normal: world = parentMatrix × localMatrix:
166
+ // a=pa·la+pc·lb=0·0+(-1)·1=-1 b=pb·la+pd·lb=2·0+0·1=0
167
+ // c=pa·lc+pc·ld=0·(-1)+(-1)·0=0 d=pb·lc+pd·ld=2·(-1)+0·0=-2 → [-1, 0, 0, -2] (det 2 = parent det × child det).
168
+ const s = createSkeleton2D([
169
+ makeBone({ rotation: 90, scaleX: 2, scaleY: 1 }),
170
+ makeBone({ parentIndex: 0, rotation: 90 }),
171
+ ]);
172
+ computeSkeleton2DWorldTransforms(s);
173
+ const out = createMatrix();
174
+ getSkeleton2DBoneWorldMatrix(out, s, 1);
175
+ expect(out.a).toBeCloseTo(-1, 5);
176
+ expect(out.b).toBeCloseTo(0, 5);
177
+ expect(out.c).toBeCloseTo(0, 5);
178
+ expect(out.d).toBeCloseTo(-2, 5);
179
+ });
180
+
181
+ it('FORMULA parity: NoRotationOrReflection keeps per-axis parent scale (2,1), strips rotation', () => {
182
+ // Parent world [0,2,-1,0] → psx=hypot(0,2)=2, psy=hypot(-1,0)=1. Identity child, axis-aligned scale-only
183
+ // parent: a=psx·la=2·1=2, b=psy·lb=1·0=0, c=psx·lc=2·0=0, d=psy·ld=1·1=1 → [2, 0, 0, 1]. Asymmetric a≠d
184
+ // is the point: a uniform-scale parent would hide a psx/psy mix-up.
185
+ const s = createSkeleton2D([
186
+ makeBone({ rotation: 90, scaleX: 2, scaleY: 1 }),
187
+ makeBone({ parentIndex: 0, transformMode: TransformMode2D.NoRotationOrReflection }),
188
+ ]);
189
+ computeSkeleton2DWorldTransforms(s);
190
+ const out = createMatrix();
191
+ getSkeleton2DBoneWorldMatrix(out, s, 1);
192
+ expect(out.a).toBeCloseTo(2, 5);
193
+ expect(out.b).toBeCloseTo(0, 5);
194
+ expect(out.c).toBeCloseTo(0, 5);
195
+ expect(out.d).toBeCloseTo(1, 5);
196
+ });
197
+
198
+ it('FORMULA parity: NoScale keeps the parent rotation but strips its (2,1) scale to unit', () => {
199
+ // Parent world [0,2,-1,0], normalized columns: nax=0/2=0, nay=2/2=1, ncx=-1/1=-1, ncy=0/1=0. Identity
200
+ // child: a=nax·la+ncx·lb=0, b=nay·la+ncy·lb=1, c=nax·lc+ncx·ld=-1, d=nay·lc+ncy·ld=0 → [0,1,-1,0]:
201
+ // a pure 90° rotation (det +1), the parent's orientation with the scale removed.
202
+ const s = createSkeleton2D([
203
+ makeBone({ rotation: 90, scaleX: 2, scaleY: 1 }),
204
+ makeBone({ parentIndex: 0, transformMode: TransformMode2D.NoScale }),
205
+ ]);
206
+ computeSkeleton2DWorldTransforms(s);
207
+ const out = createMatrix();
208
+ getSkeleton2DBoneWorldMatrix(out, s, 1);
209
+ expect(out.a).toBeCloseTo(0, 5);
210
+ expect(out.b).toBeCloseTo(1, 5);
211
+ expect(out.c).toBeCloseTo(-1, 5);
212
+ expect(out.d).toBeCloseTo(0, 5);
213
+ });
214
+
215
+ it('FORMULA parity: NoScale vs NoScaleOrReflection differ on a reflected parent (keep vs strip the flip)', () => {
216
+ // Reflected parent (scaleY -1) world = [1, 0, 0, -1] (det -1). Identity children. NoScale keeps the
217
+ // parent reflection → [1,0,0,-1] (det -1); NoScaleOrReflection forces y-axis = +90° of x-axis → [1,0,0,1]
218
+ // (det +1). This side-by-side contrast is the exact semantic boundary between the two modes.
219
+ const s = createSkeleton2D([
220
+ makeBone({ scaleX: 1, scaleY: -1 }),
221
+ makeBone({ parentIndex: 0, transformMode: TransformMode2D.NoScale }),
222
+ makeBone({ parentIndex: 0, transformMode: TransformMode2D.NoScaleOrReflection }),
223
+ ]);
224
+ computeSkeleton2DWorldTransforms(s);
225
+ const kept = createMatrix();
226
+ getSkeleton2DBoneWorldMatrix(kept, s, 1);
227
+ expect(kept.a).toBeCloseTo(1, 5);
228
+ expect(kept.d).toBeCloseTo(-1, 5);
229
+ expect(kept.a * kept.d - kept.c * kept.b).toBeCloseTo(-1, 5); // reflection kept
230
+ const stripped = createMatrix();
231
+ getSkeleton2DBoneWorldMatrix(stripped, s, 2);
232
+ expect(stripped.a).toBeCloseTo(1, 5);
233
+ expect(stripped.d).toBeCloseTo(1, 5);
234
+ expect(stripped.a * stripped.d - stripped.c * stripped.b).toBeCloseTo(1, 5); // reflection stripped
235
+ });
236
+
237
+ it('FORMULA parity: a root bone applies shearX as an offset to the x-axis angle', () => {
238
+ // Local formula: a=cos(rot+shearX)·scaleX, b=sin(rot+shearX)·scaleX. With rot 0, shearX 45°, unit scale:
239
+ // x-axis is at 45° (a=b=cos45=√2/2) while the y-axis stays at rot+90°=90° (c=0,d=1) — non-orthogonal
240
+ // axes, the defining signature of shear.
241
+ const s = createSkeleton2D([makeBone({ shearX: 45 })]);
242
+ computeSkeleton2DWorldTransforms(s);
243
+ const root2 = Math.SQRT1_2; // cos45 = sin45 = √2/2
244
+ expect(s.worldMatrices[0]).toBeCloseTo(root2, 5); // a
245
+ expect(s.worldMatrices[1]).toBeCloseTo(root2, 5); // b
246
+ expect(s.worldMatrices[2]).toBeCloseTo(0, 5); // c
247
+ expect(s.worldMatrices[3]).toBeCloseTo(1, 5); // d
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
+ });
154
270
  });
155
271
 
156
272
  describe('createSkeleton2D', () => {
@@ -202,6 +318,23 @@ describe('getSkeleton2DBoneWorldMatrix', () => {
202
318
  });
203
319
  });
204
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
+
205
338
  describe('setSkeleton2DBindPose', () => {
206
339
  it('captures the inverse of the current world so the palette is identity at bind', () => {
207
340
  const s = createSkeleton2D([makeBone({ x: 4, rotation: 60, scaleX: 2 })]);
@@ -215,6 +348,49 @@ describe('setSkeleton2DBindPose', () => {
215
348
  });
216
349
  });
217
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
+
218
394
  describe('validateSkeleton2D', () => {
219
395
  it('returns null for a valid parent-before-child skeleton', () => {
220
396
  const s = createSkeleton2D([makeBone(), makeBone({ parentIndex: 0 })]);
@@ -232,3 +408,12 @@ describe('validateSkeleton2D', () => {
232
408
  expect(validateSkeleton2D(s)).toContain('worldMatrices');
233
409
  });
234
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
+ }