@codexo/exojs-aseprite 0.15.3 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,232 +1,219 @@
1
- import { Spritesheet, AnimatedSprite } from '@codexo/exojs';
2
- import { isAsepriteArrayData } from './AsepriteData.js';
1
+ import { isAsepriteArrayData } from "./AsepriteData.js";
2
+ import { AnimatedSprite, Spritesheet } from "@codexo/exojs";
3
3
 
4
+ //#region src/AsepriteSheet.ts
4
5
  /**
5
- * Normalises an {@link AsepriteData} document into an ordered array of
6
- * {@link AsepriteFrameData} entries regardless of whether the JSON was
7
- * produced in array or hash mode.
8
- */
9
- function normaliseFrames(data) {
10
- if (isAsepriteArrayData(data)) {
11
- return [...data.frames];
12
- }
13
- return Object.values(data.frames);
14
- }
6
+ * Normalises an {@link AsepriteData} document into an ordered array of
7
+ * {@link AsepriteFrameData} entries regardless of whether the JSON was
8
+ * produced in array or hash mode.
9
+ */
10
+ const normaliseFrames = (data) => {
11
+ if (isAsepriteArrayData(data)) return [...data.frames];
12
+ return Object.values(data.frames);
13
+ };
15
14
  /**
16
- * Expands a frame tag's inclusive `[from, to]` range into the ordered
17
- * sequence of frame indices it actually plays, according to its
18
- * {@link AsepriteDirection}. Indices are not bounds-checked against the
19
- * frame array here; callers filter out-of-range entries separately.
20
- *
21
- * - `forward`: `[from, from+1, ..., to]`.
22
- * - `reverse`: `[to, to-1, ..., from]`.
23
- * - `pingpong`: a forward pass followed by a backward pass that excludes
24
- * both endpoints, e.g. `[0,1,2]` becomes `[0,1,2,1]`.
25
- * - `pingpong_reverse`: the mirrored shape, starting from `to`.
26
- * - A single-frame tag (`from === to`) always yields just that one frame.
27
- */
28
- function expandFrameIndices(tag) {
29
- const { from, to } = tag;
30
- if (from === to) {
31
- return [from];
32
- }
33
- const indices = [];
34
- switch (tag.direction) {
35
- case 'reverse':
36
- for (let i = to; i >= from; i--)
37
- indices.push(i);
38
- break;
39
- case 'pingpong':
40
- for (let i = from; i <= to; i++)
41
- indices.push(i);
42
- for (let i = to - 1; i > from; i--)
43
- indices.push(i);
44
- break;
45
- case 'pingpong_reverse':
46
- for (let i = to; i >= from; i--)
47
- indices.push(i);
48
- for (let i = from + 1; i < to; i++)
49
- indices.push(i);
50
- break;
51
- case 'forward':
52
- default:
53
- for (let i = from; i <= to; i++)
54
- indices.push(i);
55
- break;
56
- }
57
- return indices;
58
- }
15
+ * Expands a frame tag's inclusive `[from, to]` range into the ordered
16
+ * sequence of frame indices it actually plays, according to its
17
+ * {@link AsepriteDirection}. Indices are not bounds-checked against the
18
+ * frame array here; callers filter out-of-range entries separately.
19
+ *
20
+ * - `forward`: `[from, from+1, ..., to]`.
21
+ * - `reverse`: `[to, to-1, ..., from]`.
22
+ * - `pingpong`: a forward pass followed by a backward pass that excludes
23
+ * both endpoints, e.g. `[0,1,2]` becomes `[0,1,2,1]`.
24
+ * - `pingpong_reverse`: the mirrored shape, starting from `to`.
25
+ * - A single-frame tag (`from === to`) always yields just that one frame.
26
+ */
27
+ const expandFrameIndices = (tag) => {
28
+ const { from, to } = tag;
29
+ if (from === to) return [from];
30
+ const indices = [];
31
+ switch (tag.direction) {
32
+ case "reverse":
33
+ for (let i = to; i >= from; i--) indices.push(i);
34
+ break;
35
+ case "pingpong":
36
+ for (let i = from; i <= to; i++) indices.push(i);
37
+ for (let i = to - 1; i > from; i--) indices.push(i);
38
+ break;
39
+ case "pingpong_reverse":
40
+ for (let i = to; i >= from; i--) indices.push(i);
41
+ for (let i = from + 1; i < to; i++) indices.push(i);
42
+ break;
43
+ default: for (let i = from; i <= to; i++) indices.push(i);
44
+ }
45
+ return indices;
46
+ };
59
47
  /**
60
- * Calculates the average frames-per-second for a sequence of frame indices,
61
- * based on the per-frame `duration` field (milliseconds per frame) exported
62
- * by Aseprite. Every occurrence of an index counts toward the average for
63
- * ping-pong sequences that means repeated (bounced) frames are weighted twice.
64
- * Falls back to `12` fps when all durations are zero or the sequence is empty.
65
- */
66
- function avgFps(frameArray, indices) {
67
- const durations = indices.filter(i => i >= 0 && i < frameArray.length).map(i => frameArray[i].duration);
68
- if (durations.length === 0) {
69
- return 12;
70
- }
71
- const totalMs = durations.reduce((sum, d) => sum + d, 0);
72
- const avgMs = totalMs / durations.length;
73
- return avgMs > 0 ? 1000 / avgMs : 12;
74
- }
48
+ * Calculates the average frames-per-second for a sequence of frame indices,
49
+ * based on the per-frame `duration` field (milliseconds per frame) exported
50
+ * by Aseprite. Every occurrence of an index counts toward the average - for
51
+ * ping-pong sequences that means repeated (bounced) frames are weighted twice.
52
+ * Falls back to `12` fps when all durations are zero or the sequence is empty.
53
+ */
54
+ const avgFps = (frames) => {
55
+ if (frames.length === 0) return 12;
56
+ const avgMs = frames.reduce((sum, { frameData }) => sum + frameData.duration, 0) / frames.length;
57
+ return avgMs > 0 ? 1e3 / avgMs : 12;
58
+ };
75
59
  /**
76
- * Parsed representation of an Aseprite JSON sprite sheet export.
77
- *
78
- * `AsepriteSheet.parse(data, texture)` converts the raw JSON document into:
79
- * - A {@link Spritesheet} whose frames correspond to the Aseprite frame array
80
- * (keyed by zero-based index string: `"0"`, `"1"`, …).
81
- * - A `clips` map of {@link AnimatedSpriteClipDefinition} entries built from
82
- * `meta.frameTags`, one per named tag.
83
- *
84
- * Call {@link createAnimatedSprite} to obtain a ready-to-use
85
- * {@link AnimatedSprite} with all clips pre-registered.
86
- *
87
- * @example
88
- * ```ts
89
- * const sheet = await loader.load(AsepriteSheet, 'hero.aseprite.json');
90
- * const sprite = sheet.createAnimatedSprite();
91
- * sprite.play('run');
92
- * scene.addChild(sprite);
93
- * ```
94
- */
95
- class AsepriteSheet {
96
- /** The underlying {@link Spritesheet} whose frames are keyed by index string. */
97
- spritesheet;
98
- /**
99
- * Animation clips derived from the Aseprite `frameTags` metadata.
100
- * Each clip's frames are live references into {@link spritesheet.frames};
101
- * they are cloned automatically when passed to {@link AnimatedSprite.defineClip}.
102
- */
103
- clips;
104
- /**
105
- * Named slices from the Aseprite `meta.slices` metadata, keyed by slice
106
- * name. Slices describe editor-defined regions — hitboxes, nine-patch
107
- * borders, UI anchor points — that aren't part of the frame/animation
108
- * data itself. Each {@link AsepriteSlice} carries one {@link AsepriteSliceKey}
109
- * per frame at which its bounds change; consumers resolve the applicable
110
- * key for a given frame index themselves.
111
- */
112
- slices;
113
- /**
114
- * @internal — use {@link AsepriteSheet.parse} to create instances.
115
- * The public modifier is required for the Loader's `AssetConstructor` token
116
- * contract; users should call `parse()` instead of constructing directly.
117
- */
118
- constructor(spritesheet, clips, slices) {
119
- this.spritesheet = spritesheet;
120
- this.clips = clips;
121
- this.slices = slices;
122
- }
123
- /**
124
- * Parse a raw {@link AsepriteData} document and the already-loaded
125
- * {@link Texture} into an {@link AsepriteSheet}.
126
- *
127
- * Supports both Aseprite array mode and hash mode. Frame indices from
128
- * `frameTags` are resolved against the ordered frame array; out-of-range
129
- * indices are silently skipped.
130
- *
131
- * A tag's `direction` determines the expanded frame sequence fed into the
132
- * clip `forward` and `reverse` play the `[from, to]` range in order or
133
- * in reverse, while `pingpong`/`pingpong_reverse` append a backward pass
134
- * (excluding both endpoints) so the bounce plays back correctly on the
135
- * engine's forward-only {@link AnimatedSprite} playback. The tag's
136
- * `repeat` field maps directly onto {@link AnimatedSpriteClipDefinition.repeat}:
137
- * absent means the clip loops indefinitely (`repeat: -1`); a numeric
138
- * string (`"1"`, `"2"`, …) means it plays exactly that many full cycles
139
- * before stopping.
140
- *
141
- * Each clip's `frameDurations` carries the real per-frame `duration` from
142
- * the export (falling back to the tag's average when a frame's duration is
143
- * non-positive), so uneven hold-frames survive into playback instead of
144
- * being flattened to a uniform fps. `frameOffsets` carries each frame's
145
- * `spriteSourceSize` `{x,y}` — its trimmed content's offset within the
146
- * untrimmed canvas whenever any frame in the tag is trimmed, so frames
147
- * trimmed by different amounts stay anchored instead of jittering; it's
148
- * omitted entirely for tags with no trimmed frames.
149
- */
150
- static parse(data, texture) {
151
- const frameArray = normaliseFrames(data);
152
- // Build SpritesheetData: frame names are zero-based index strings.
153
- const spritesheetFrames = {};
154
- for (let i = 0; i < frameArray.length; i++) {
155
- const frameData = frameArray[i];
156
- spritesheetFrames[String(i)] = { frame: frameData.frame };
157
- }
158
- const spritesheet = new Spritesheet(texture, { frames: spritesheetFrames });
159
- // Build clips from frameTags, resolving frame indices into Rectangles.
160
- const clips = new Map();
161
- const frameTags = data.meta.frameTags ?? [];
162
- for (const tag of frameTags) {
163
- // Out-of-range indices are silently skipped; `validIndices` parallels
164
- // `frames` exactly, so it's the basis for every other per-frame array
165
- // (durations, offsets) built below.
166
- const validIndices = expandFrameIndices(tag).filter(i => i >= 0 && i < frameArray.length);
167
- const frames = validIndices.map(i => spritesheet.getFrame(String(i)));
168
- if (frames.length === 0) {
169
- continue;
170
- }
171
- // Aseprite's `tag.repeat` (a numeric string, `'1'` through any N) maps
172
- // directly onto the engine's `repeat` count. Absent means the tag
173
- // loops indefinitely, the engine's `-1` sentinel.
174
- const repeat = tag.repeat !== undefined ? Number(tag.repeat) : -1;
175
- const fps = avgFps(frameArray, validIndices);
176
- // Per-frame hold duration (Aseprite "duration"), so uneven hold-frames
177
- // (e.g. a lingering idle frame) survive into playback instead of being
178
- // flattened to the tag's average fps. A non-positive duration (same
179
- // degenerate case `avgFps` guards against) falls back to the average.
180
- const avgDurationFallback = 1000 / fps;
181
- const frameDurations = validIndices.map(i => {
182
- const duration = frameArray[i].duration;
183
- return duration > 0 ? duration : avgDurationFallback;
184
- });
185
- // Per-frame trim offset (Aseprite "spriteSourceSize"), so frames trimmed
186
- // by different amounts stay anchored to the same point in the untrimmed
187
- // canvas instead of jittering frame to frame. Omitted entirely when no
188
- // frame in the tag is trimmed, to avoid noise on untrimmed sheets.
189
- const anyTrimmed = validIndices.some(i => frameArray[i].trimmed);
190
- const frameOffsets = anyTrimmed
191
- ? validIndices.map(i => {
192
- const { x, y } = frameArray[i].spriteSourceSize;
193
- return { x, y };
194
- })
195
- : undefined;
196
- clips.set(tag.name, {
197
- fps,
198
- frames,
199
- repeat,
200
- frameDurations,
201
- ...(frameOffsets ? { frameOffsets } : {}),
202
- });
203
- }
204
- // Build the slices map from meta.slices, keyed by slice name.
205
- const slices = new Map();
206
- for (const slice of data.meta.slices ?? []) {
207
- slices.set(slice.name, slice);
208
- }
209
- return new AsepriteSheet(spritesheet, clips, slices);
210
- }
211
- /**
212
- * Create an {@link AnimatedSprite} with all frame-tag clips pre-defined.
213
- *
214
- * Each clip is registered via {@link AnimatedSprite.defineClip}, which
215
- * clones the frame {@link Rectangle}s so the sprite owns its own copies.
216
- * Call {@link AnimatedSprite.play} with a tag name to start playback.
217
- */
218
- createAnimatedSprite() {
219
- const sprite = new AnimatedSprite(this.spritesheet.texture);
220
- for (const [name, clip] of this.clips) {
221
- sprite.defineClip(name, clip);
222
- }
223
- return sprite;
224
- }
225
- /** Destroy the underlying {@link Spritesheet} and release its frame resources. */
226
- destroy() {
227
- this.spritesheet.destroy();
228
- }
229
- }
60
+ * Resolves a tag's frame indices against the frame array, dropping any that
61
+ * fall outside it - Aseprite exports can reference frames a later edit removed.
62
+ */
63
+ const resolveTaggedFrames = (frameArray, indices) => {
64
+ const resolved = [];
65
+ for (const index of indices) {
66
+ const frameData = frameArray[index];
67
+ if (frameData !== void 0) resolved.push({
68
+ index,
69
+ frameData
70
+ });
71
+ }
72
+ return resolved;
73
+ };
74
+ /**
75
+ * Parsed representation of an Aseprite JSON sprite sheet export.
76
+ *
77
+ * `AsepriteSheet.parse(data, texture)` converts the raw JSON document into:
78
+ * - A {@link Spritesheet} whose frames correspond to the Aseprite frame array
79
+ * (keyed by zero-based index string: `"0"`, `"1"`, ...).
80
+ * - A `clips` map of {@link AnimatedSpriteClipDefinition} entries built from
81
+ * `meta.frameTags`, one per named tag.
82
+ * - The `slices` and `layers` metadata maps, carried through verbatim.
83
+ *
84
+ * Call {@link createAnimatedSprite} to obtain a ready-to-use
85
+ * {@link AnimatedSprite} with all clips pre-registered.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const sheet = await loader.load(Asset.type('asepriteSheet', 'hero.aseprite.json'));
90
+ * const sprite = sheet.createAnimatedSprite();
91
+ * sprite.play('run');
92
+ * scene.addChild(sprite);
93
+ * ```
94
+ */
95
+ var AsepriteSheet = class AsepriteSheet {
96
+ /** The underlying {@link Spritesheet} whose frames are keyed by index string. */
97
+ spritesheet;
98
+ /**
99
+ * Animation clips derived from the Aseprite `frameTags` metadata.
100
+ * Each clip's frames are live references into {@link spritesheet.frames};
101
+ * they are cloned automatically when passed to {@link AnimatedSprite.defineClip}.
102
+ */
103
+ clips;
104
+ /**
105
+ * Named slices from the Aseprite `meta.slices` metadata, keyed by slice
106
+ * name. Slices describe editor-defined regions - hitboxes, nine-patch
107
+ * borders, UI anchor points - that aren't part of the frame/animation
108
+ * data itself. Each {@link AsepriteSlice} carries one {@link AsepriteSliceKey}
109
+ * per frame at which its bounds change; consumers resolve the applicable
110
+ * key for a given frame index themselves.
111
+ */
112
+ slices;
113
+ /**
114
+ * Layers from the Aseprite `meta.layers` metadata, keyed by layer name and
115
+ * in export order (bottom-most first). Aseprite packs the sheet already
116
+ * composited, so these are descriptive rather than renderable: they tell a
117
+ * consumer which layers went into a frame and how - opacity, blend mode,
118
+ * editor user data - and {@link AsepriteLayer.group} names the enclosing
119
+ * group layer, so the flat map still encodes the layer tree.
120
+ *
121
+ * Empty when the export carries no `meta.layers` block. Layer names are
122
+ * unique within an Aseprite document, so keying by name loses nothing.
123
+ */
124
+ layers;
125
+ /**
126
+ * @internal - use {@link AsepriteSheet.parse} to create instances.
127
+ * The public modifier is required for the Loader's `AssetConstructor` token
128
+ * contract; users should call `parse()` instead of constructing directly.
129
+ */
130
+ constructor(spritesheet, clips, slices, layers = /* @__PURE__ */ new Map()) {
131
+ this.spritesheet = spritesheet;
132
+ this.clips = clips;
133
+ this.slices = slices;
134
+ this.layers = layers;
135
+ }
136
+ /**
137
+ * Parse a raw {@link AsepriteData} document and the already-loaded
138
+ * {@link Texture} into an {@link AsepriteSheet}.
139
+ *
140
+ * Supports both Aseprite array mode and hash mode. Frame indices from
141
+ * `frameTags` are resolved against the ordered frame array; out-of-range
142
+ * indices are silently skipped.
143
+ *
144
+ * A tag's `direction` determines the expanded frame sequence fed into the
145
+ * clip - `forward` and `reverse` play the `[from, to]` range in order or
146
+ * in reverse, while `pingpong`/`pingpong_reverse` append a backward pass
147
+ * (excluding both endpoints) so the bounce plays back correctly on the
148
+ * engine's forward-only {@link AnimatedSprite} playback. The tag's
149
+ * `repeat` field maps directly onto {@link AnimatedSpriteClipDefinition.repeat}:
150
+ * absent means the clip loops indefinitely (`repeat: -1`); a numeric
151
+ * string (`"1"`, `"2"`, ...) means it plays exactly that many full cycles
152
+ * before stopping.
153
+ *
154
+ * Each clip's `frameDurations` carries the real per-frame `duration` from
155
+ * the export (falling back to the tag's average when a frame's duration is
156
+ * non-positive), so uneven hold-frames survive into playback instead of
157
+ * being flattened to a uniform fps. `frameOffsets` carries each frame's
158
+ * `spriteSourceSize` `{x,y}` - its trimmed content's offset within the
159
+ * untrimmed canvas - whenever any frame in the tag is trimmed, so frames
160
+ * trimmed by different amounts stay anchored instead of jittering; it's
161
+ * omitted entirely for tags with no trimmed frames.
162
+ */
163
+ static parse(data, texture) {
164
+ const frameArray = normaliseFrames(data);
165
+ const spritesheetFrames = {};
166
+ for (const [i, frameData] of frameArray.entries()) spritesheetFrames[String(i)] = { frame: frameData.frame };
167
+ const spritesheet = new Spritesheet(texture, { frames: spritesheetFrames });
168
+ const clips = /* @__PURE__ */ new Map();
169
+ const frameTags = data.meta.frameTags ?? [];
170
+ for (const tag of frameTags) {
171
+ const taggedFrames = resolveTaggedFrames(frameArray, expandFrameIndices(tag));
172
+ const frames = taggedFrames.map(({ index }) => spritesheet.getFrame(String(index)));
173
+ if (frames.length === 0) continue;
174
+ const repeat = tag.repeat !== void 0 ? Number(tag.repeat) : -1;
175
+ const fps = avgFps(taggedFrames);
176
+ const avgDurationFallback = 1e3 / fps;
177
+ const frameDurations = taggedFrames.map(({ frameData }) => frameData.duration > 0 ? frameData.duration : avgDurationFallback);
178
+ const frameOffsets = taggedFrames.some(({ frameData }) => frameData.trimmed) ? taggedFrames.map(({ frameData }) => {
179
+ const { x, y } = frameData.spriteSourceSize;
180
+ return {
181
+ x,
182
+ y
183
+ };
184
+ }) : void 0;
185
+ clips.set(tag.name, {
186
+ fps,
187
+ frames,
188
+ repeat,
189
+ frameDurations,
190
+ ...frameOffsets ? { frameOffsets } : {}
191
+ });
192
+ }
193
+ const slices = /* @__PURE__ */ new Map();
194
+ for (const slice of data.meta.slices ?? []) slices.set(slice.name, slice);
195
+ const layers = /* @__PURE__ */ new Map();
196
+ for (const layer of data.meta.layers ?? []) layers.set(layer.name, layer);
197
+ return new AsepriteSheet(spritesheet, clips, slices, layers);
198
+ }
199
+ /**
200
+ * Create an {@link AnimatedSprite} with all frame-tag clips pre-defined.
201
+ *
202
+ * Each clip is registered via {@link AnimatedSprite.defineClip}, which
203
+ * clones the frame {@link Rectangle}s so the sprite owns its own copies.
204
+ * Call {@link AnimatedSprite.play} with a tag name to start playback.
205
+ */
206
+ createAnimatedSprite() {
207
+ const sprite = new AnimatedSprite(this.spritesheet.texture);
208
+ for (const [name, clip] of this.clips) sprite.defineClip(name, clip);
209
+ return sprite;
210
+ }
211
+ /** Destroy the underlying {@link Spritesheet} and release its frame resources. */
212
+ destroy() {
213
+ this.spritesheet.destroy();
214
+ }
215
+ };
230
216
 
217
+ //#endregion
231
218
  export { AsepriteSheet };
232
- //# sourceMappingURL=AsepriteSheet.js.map
219
+ //# sourceMappingURL=AsepriteSheet.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AsepriteSheet.js","sources":["../../../src/AsepriteSheet.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AsepriteSheet.js","names":[],"sources":["../../src/AsepriteSheet.ts"],"sourcesContent":["import { AnimatedSprite, type AnimatedSpriteClipDefinition, Spritesheet, type Texture } from '@codexo/exojs';\n\nimport { type AsepriteData, type AsepriteFrameData, type AsepriteFrameTag, type AsepriteLayer, type AsepriteSlice, isAsepriteArrayData } from './AsepriteData';\n\n/**\n * Normalises an {@link AsepriteData} document into an ordered array of\n * {@link AsepriteFrameData} entries regardless of whether the JSON was\n * produced in array or hash mode.\n */\nconst normaliseFrames = (data: AsepriteData): AsepriteFrameData[] => {\n if (isAsepriteArrayData(data)) {\n return [...data.frames];\n }\n\n return Object.values(data.frames);\n};\n\n/**\n * Expands a frame tag's inclusive `[from, to]` range into the ordered\n * sequence of frame indices it actually plays, according to its\n * {@link AsepriteDirection}. Indices are not bounds-checked against the\n * frame array here; callers filter out-of-range entries separately.\n *\n * - `forward`: `[from, from+1, ..., to]`.\n * - `reverse`: `[to, to-1, ..., from]`.\n * - `pingpong`: a forward pass followed by a backward pass that excludes\n * both endpoints, e.g. `[0,1,2]` becomes `[0,1,2,1]`.\n * - `pingpong_reverse`: the mirrored shape, starting from `to`.\n * - A single-frame tag (`from === to`) always yields just that one frame.\n */\nconst expandFrameIndices = (tag: AsepriteFrameTag): number[] => {\n const { from, to } = tag;\n\n if (from === to) {\n return [from];\n }\n\n const indices: number[] = [];\n\n switch (tag.direction) {\n case 'reverse':\n for (let i = to; i >= from; i--) indices.push(i);\n break;\n\n case 'pingpong':\n for (let i = from; i <= to; i++) indices.push(i);\n for (let i = to - 1; i > from; i--) indices.push(i);\n break;\n\n case 'pingpong_reverse':\n for (let i = to; i >= from; i--) indices.push(i);\n for (let i = from + 1; i < to; i++) indices.push(i);\n break;\n\n case 'forward':\n default:\n for (let i = from; i <= to; i++) indices.push(i);\n break;\n }\n\n return indices;\n};\n\n/**\n * Calculates the average frames-per-second for a sequence of frame indices,\n * based on the per-frame `duration` field (milliseconds per frame) exported\n * by Aseprite. Every occurrence of an index counts toward the average - for\n * ping-pong sequences that means repeated (bounced) frames are weighted twice.\n * Falls back to `12` fps when all durations are zero or the sequence is empty.\n */\nconst avgFps = (frames: TaggedFrame[]): number => {\n if (frames.length === 0) {\n return 12;\n }\n\n const totalMs = frames.reduce((sum, { frameData }) => sum + frameData.duration, 0);\n const avgMs = totalMs / frames.length;\n\n return avgMs > 0 ? 1000 / avgMs : 12;\n};\n\n/**\n * A frame a tag actually resolved to, paired with the index it came from.\n * Resolving index and data together is what keeps every per-frame array below\n * (durations, offsets, spritesheet lookups) aligned without re-indexing.\n */\ninterface TaggedFrame {\n index: number;\n frameData: AsepriteFrameData;\n}\n\n/**\n * Resolves a tag's frame indices against the frame array, dropping any that\n * fall outside it - Aseprite exports can reference frames a later edit removed.\n */\nconst resolveTaggedFrames = (frameArray: AsepriteFrameData[], indices: number[]): TaggedFrame[] => {\n const resolved: TaggedFrame[] = [];\n\n for (const index of indices) {\n const frameData = frameArray[index];\n\n if (frameData !== undefined) {\n resolved.push({ index, frameData });\n }\n }\n\n return resolved;\n};\n\n/**\n * Parsed representation of an Aseprite JSON sprite sheet export.\n *\n * `AsepriteSheet.parse(data, texture)` converts the raw JSON document into:\n * - A {@link Spritesheet} whose frames correspond to the Aseprite frame array\n * (keyed by zero-based index string: `\"0\"`, `\"1\"`, ...).\n * - A `clips` map of {@link AnimatedSpriteClipDefinition} entries built from\n * `meta.frameTags`, one per named tag.\n * - The `slices` and `layers` metadata maps, carried through verbatim.\n *\n * Call {@link createAnimatedSprite} to obtain a ready-to-use\n * {@link AnimatedSprite} with all clips pre-registered.\n *\n * @example\n * ```ts\n * const sheet = await loader.load(Asset.type('asepriteSheet', 'hero.aseprite.json'));\n * const sprite = sheet.createAnimatedSprite();\n * sprite.play('run');\n * scene.addChild(sprite);\n * ```\n */\nexport class AsepriteSheet {\n /** The underlying {@link Spritesheet} whose frames are keyed by index string. */\n public readonly spritesheet: Spritesheet;\n\n /**\n * Animation clips derived from the Aseprite `frameTags` metadata.\n * Each clip's frames are live references into {@link spritesheet.frames};\n * they are cloned automatically when passed to {@link AnimatedSprite.defineClip}.\n */\n public readonly clips: ReadonlyMap<string, AnimatedSpriteClipDefinition>;\n\n /**\n * Named slices from the Aseprite `meta.slices` metadata, keyed by slice\n * name. Slices describe editor-defined regions - hitboxes, nine-patch\n * borders, UI anchor points - that aren't part of the frame/animation\n * data itself. Each {@link AsepriteSlice} carries one {@link AsepriteSliceKey}\n * per frame at which its bounds change; consumers resolve the applicable\n * key for a given frame index themselves.\n */\n public readonly slices: ReadonlyMap<string, AsepriteSlice>;\n\n /**\n * Layers from the Aseprite `meta.layers` metadata, keyed by layer name and\n * in export order (bottom-most first). Aseprite packs the sheet already\n * composited, so these are descriptive rather than renderable: they tell a\n * consumer which layers went into a frame and how - opacity, blend mode,\n * editor user data - and {@link AsepriteLayer.group} names the enclosing\n * group layer, so the flat map still encodes the layer tree.\n *\n * Empty when the export carries no `meta.layers` block. Layer names are\n * unique within an Aseprite document, so keying by name loses nothing.\n */\n public readonly layers: ReadonlyMap<string, AsepriteLayer>;\n\n /**\n * @internal - use {@link AsepriteSheet.parse} to create instances.\n * The public modifier is required for the Loader's `AssetConstructor` token\n * contract; users should call `parse()` instead of constructing directly.\n */\n public constructor(\n spritesheet: Spritesheet,\n clips: ReadonlyMap<string, AnimatedSpriteClipDefinition>,\n slices: ReadonlyMap<string, AsepriteSlice>,\n layers: ReadonlyMap<string, AsepriteLayer> = new Map(),\n ) {\n this.spritesheet = spritesheet;\n this.clips = clips;\n this.slices = slices;\n this.layers = layers;\n }\n\n /**\n * Parse a raw {@link AsepriteData} document and the already-loaded\n * {@link Texture} into an {@link AsepriteSheet}.\n *\n * Supports both Aseprite array mode and hash mode. Frame indices from\n * `frameTags` are resolved against the ordered frame array; out-of-range\n * indices are silently skipped.\n *\n * A tag's `direction` determines the expanded frame sequence fed into the\n * clip - `forward` and `reverse` play the `[from, to]` range in order or\n * in reverse, while `pingpong`/`pingpong_reverse` append a backward pass\n * (excluding both endpoints) so the bounce plays back correctly on the\n * engine's forward-only {@link AnimatedSprite} playback. The tag's\n * `repeat` field maps directly onto {@link AnimatedSpriteClipDefinition.repeat}:\n * absent means the clip loops indefinitely (`repeat: -1`); a numeric\n * string (`\"1\"`, `\"2\"`, ...) means it plays exactly that many full cycles\n * before stopping.\n *\n * Each clip's `frameDurations` carries the real per-frame `duration` from\n * the export (falling back to the tag's average when a frame's duration is\n * non-positive), so uneven hold-frames survive into playback instead of\n * being flattened to a uniform fps. `frameOffsets` carries each frame's\n * `spriteSourceSize` `{x,y}` - its trimmed content's offset within the\n * untrimmed canvas - whenever any frame in the tag is trimmed, so frames\n * trimmed by different amounts stay anchored instead of jittering; it's\n * omitted entirely for tags with no trimmed frames.\n */\n public static parse(data: AsepriteData, texture: Texture): AsepriteSheet {\n const frameArray = normaliseFrames(data);\n\n // Build SpritesheetData: frame names are zero-based index strings.\n const spritesheetFrames: Record<string, { frame: { x: number; y: number; w: number; h: number } }> = {};\n\n for (const [i, frameData] of frameArray.entries()) {\n spritesheetFrames[String(i)] = { frame: frameData.frame };\n }\n\n const spritesheet = new Spritesheet(texture, { frames: spritesheetFrames });\n\n // Build clips from frameTags, resolving frame indices into Rectangles.\n const clips = new Map<string, AnimatedSpriteClipDefinition>();\n const frameTags = data.meta.frameTags ?? [];\n\n for (const tag of frameTags) {\n // Out-of-range indices are silently skipped; `taggedFrames` parallels\n // `frames` exactly, so it's the basis for every other per-frame array\n // (durations, offsets) built below.\n const taggedFrames = resolveTaggedFrames(frameArray, expandFrameIndices(tag));\n const frames = taggedFrames.map(({ index }) => spritesheet.getFrame(String(index)));\n\n if (frames.length === 0) {\n continue;\n }\n\n // Aseprite's `tag.repeat` (a numeric string, `'1'` through any N) maps\n // directly onto the engine's `repeat` count. Absent means the tag\n // loops indefinitely, the engine's `-1` sentinel.\n const repeat = tag.repeat !== undefined ? Number(tag.repeat) : -1;\n const fps = avgFps(taggedFrames);\n\n // Per-frame hold duration (Aseprite \"duration\"), so uneven hold-frames\n // (e.g. a lingering idle frame) survive into playback instead of being\n // flattened to the tag's average fps. A non-positive duration (same\n // degenerate case `avgFps` guards against) falls back to the average.\n const avgDurationFallback = 1000 / fps;\n const frameDurations = taggedFrames.map(({ frameData }) => (frameData.duration > 0 ? frameData.duration : avgDurationFallback));\n\n // Per-frame trim offset (Aseprite \"spriteSourceSize\"), so frames trimmed\n // by different amounts stay anchored to the same point in the untrimmed\n // canvas instead of jittering frame to frame. Omitted entirely when no\n // frame in the tag is trimmed, to avoid noise on untrimmed sheets.\n const anyTrimmed = taggedFrames.some(({ frameData }) => frameData.trimmed);\n const frameOffsets = anyTrimmed\n ? taggedFrames.map(({ frameData }) => {\n const { x, y } = frameData.spriteSourceSize;\n\n return { x, y };\n })\n : undefined;\n\n clips.set(tag.name, {\n fps,\n frames,\n repeat,\n frameDurations,\n ...(frameOffsets ? { frameOffsets } : {}),\n });\n }\n\n // Build the slices map from meta.slices, keyed by slice name.\n const slices = new Map<string, AsepriteSlice>();\n\n for (const slice of data.meta.slices ?? []) {\n slices.set(slice.name, slice);\n }\n\n // Same for meta.layers - insertion order is the export order, so the map\n // doubles as the ordered layer list.\n const layers = new Map<string, AsepriteLayer>();\n\n for (const layer of data.meta.layers ?? []) {\n layers.set(layer.name, layer);\n }\n\n return new AsepriteSheet(spritesheet, clips, slices, layers);\n }\n\n /**\n * Create an {@link AnimatedSprite} with all frame-tag clips pre-defined.\n *\n * Each clip is registered via {@link AnimatedSprite.defineClip}, which\n * clones the frame {@link Rectangle}s so the sprite owns its own copies.\n * Call {@link AnimatedSprite.play} with a tag name to start playback.\n */\n public createAnimatedSprite(): AnimatedSprite {\n const sprite = new AnimatedSprite(this.spritesheet.texture);\n\n for (const [name, clip] of this.clips) {\n sprite.defineClip(name, clip);\n }\n\n return sprite;\n }\n\n /** Destroy the underlying {@link Spritesheet} and release its frame resources. */\n public destroy(): void {\n this.spritesheet.destroy();\n }\n}\n"],"mappings":"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"}
@@ -2,12 +2,13 @@ import type { Extension } from '@codexo/exojs/extensions';
2
2
  /**
3
3
  * Default immutable Aseprite extension descriptor.
4
4
  *
5
- * Registers one asset binding:
6
- * - {@link asepriteBinding} `loader.load(AsepriteSheet, 'hero.aseprite.json')`
5
+ * Installs one asset type:
6
+ * - {@link asepriteType} - `loader.load(asepriteType.asset('hero.aseprite.json'))`
7
7
  * fetches the Aseprite JSON, resolves and loads the packed texture, and
8
8
  * returns a fully-parsed {@link AsepriteSheet} with all frame-tag clips.
9
9
  *
10
- * Use with `ApplicationOptions.extensions` or call
11
- * `import '@codexo/exojs-aseprite/register'` for global auto-registration.
10
+ * Pass it to the application that should have it via
11
+ * `ApplicationOptions.extensions`.
12
12
  */
13
13
  export declare const asepriteExtension: Extension;
14
+ //# sourceMappingURL=asepriteExtension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asepriteExtension.d.ts","sourceRoot":"","sources":["../../src/asepriteExtension.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAI1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAG9B,CAAC"}
@@ -1,22 +1,22 @@
1
- import { asepriteBinding } from './asepriteBinding.js';
1
+ import { asepriteType } from "./asepriteType.js";
2
2
 
3
+ //#region src/asepriteExtension.ts
3
4
  /**
4
- * Default immutable Aseprite extension descriptor.
5
- *
6
- * Registers one asset binding:
7
- * - {@link asepriteBinding} `loader.load(AsepriteSheet, 'hero.aseprite.json')`
8
- * fetches the Aseprite JSON, resolves and loads the packed texture, and
9
- * returns a fully-parsed {@link AsepriteSheet} with all frame-tag clips.
10
- *
11
- * Use with `ApplicationOptions.extensions` or call
12
- * `import '@codexo/exojs-aseprite/register'` for global auto-registration.
13
- */
5
+ * Default immutable Aseprite extension descriptor.
6
+ *
7
+ * Installs one asset type:
8
+ * - {@link asepriteType} - `loader.load(asepriteType.asset('hero.aseprite.json'))`
9
+ * fetches the Aseprite JSON, resolves and loads the packed texture, and
10
+ * returns a fully-parsed {@link AsepriteSheet} with all frame-tag clips.
11
+ *
12
+ * Pass it to the application that should have it via
13
+ * `ApplicationOptions.extensions`.
14
+ */
14
15
  const asepriteExtension = Object.freeze({
15
- id: '@codexo/exojs-aseprite',
16
- // Localized erasure cast: typed binding (Options=undefined) meets the
17
- // untyped Extension.assets contract here. Runtime behavior is unaffected.
18
- assets: [asepriteBinding],
16
+ id: "@codexo/exojs-aseprite",
17
+ assets: [asepriteType]
19
18
  });
20
19
 
20
+ //#endregion
21
21
  export { asepriteExtension };
22
- //# sourceMappingURL=asepriteExtension.js.map
22
+ //# sourceMappingURL=asepriteExtension.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"asepriteExtension.js","sources":["../../../src/asepriteExtension.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAIA;;;;;;;;;;AAUG;AACI,MAAM,iBAAiB,GAAc,MAAM,CAAC,MAAM,CAAC;AACxD,IAAA,EAAE,EAAE,wBAAwB;;;IAG5B,MAAM,EAAE,CAAC,eAAe,CAA8B;AACvD,CAAA;;;;"}
1
+ {"version":3,"file":"asepriteExtension.js","names":[],"sources":["../../src/asepriteExtension.ts"],"sourcesContent":["import type { Extension } from '@codexo/exojs/extensions';\n\nimport { asepriteType } from './asepriteType';\n\n/**\n * Default immutable Aseprite extension descriptor.\n *\n * Installs one asset type:\n * - {@link asepriteType} - `loader.load(asepriteType.asset('hero.aseprite.json'))`\n * fetches the Aseprite JSON, resolves and loads the packed texture, and\n * returns a fully-parsed {@link AsepriteSheet} with all frame-tag clips.\n *\n * Pass it to the application that should have it via\n * `ApplicationOptions.extensions`.\n */\nexport const asepriteExtension: Extension = Object.freeze({\n id: '@codexo/exojs-aseprite',\n assets: [asepriteType],\n});\n"],"mappings":";;;;;;;;;;;;;;AAeA,MAAa,oBAA+B,OAAO,OAAO;CACxD,IAAI;CACJ,QAAQ,CAAC,YAAY;AACvB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { AssetFactory, AssetSourceCodec } from '@codexo/exojs';
2
+ import { AssetType } from '@codexo/exojs';
3
+ import type { AsepriteData } from './AsepriteData';
4
+ import { AsepriteSheet } from './AsepriteSheet';
5
+ /**
6
+ * Thrown when an Aseprite JSON document does not match the expected shape.
7
+ * `source` is the URL of the file being parsed.
8
+ */
9
+ export declare class AsepriteFormatError extends Error {
10
+ readonly source: string;
11
+ constructor(source: string, message: string);
12
+ }
13
+ /**
14
+ * Aseprite JSON exports, together with the packed sheet they reference.
15
+ *
16
+ * The image URL is read from `meta.image` and resolved against the JSON file's
17
+ * own location; the texture is claimed by this sheet's dependency scope, so it
18
+ * lives exactly as long as the sheet does.
19
+ */
20
+ export declare class AsepriteAssetType extends AssetType<AsepriteData, AsepriteSheet, undefined, string> {
21
+ readonly id = "asepriteSheet";
22
+ readonly _token: typeof AsepriteSheet;
23
+ readonly codec: AssetSourceCodec<AsepriteData, string>;
24
+ createFactory(): AssetFactory<AsepriteData, AsepriteSheet>;
25
+ }
26
+ /** The Aseprite sheet asset type. Install it through {@link asepriteExtension}. */
27
+ export declare const asepriteType: AsepriteAssetType;
28
+ //# sourceMappingURL=asepriteType.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asepriteType.d.ts","sourceRoot":"","sources":["../../src/asepriteType.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAW,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAS,SAAS,EAAmB,MAAM,eAAe,CAAC;AAKlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA0ChD;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAEZ,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKnD;AAqCD;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,SAAS,CAAC,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC;IAC9F,SAAgB,EAAE,mBAAmB;IACrC,SAAyB,MAAM,uBAAiB;IAGhD,SAAyB,KAAK,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC,CAIpE;IAEK,aAAa,IAAI,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC;CASlE;AAED,mFAAmF;AACnF,eAAO,MAAM,YAAY,mBAA0B,CAAC"}