@flighthq/textureatlas 0.1.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.
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/textureAtlas.d.ts +4 -0
- package/dist/textureAtlas.d.ts.map +1 -0
- package/dist/textureAtlas.js +14 -0
- package/dist/textureAtlas.js.map +1 -0
- package/dist/textureAtlasFrom.d.ts +10 -0
- package/dist/textureAtlasFrom.d.ts.map +1 -0
- package/dist/textureAtlasFrom.js +27 -0
- package/dist/textureAtlasFrom.js.map +1 -0
- package/dist/textureAtlasRegion.d.ts +12 -0
- package/dist/textureAtlasRegion.d.ts.map +1 -0
- package/dist/textureAtlasRegion.js +102 -0
- package/dist/textureAtlasRegion.js.map +1 -0
- package/package.json +40 -0
- package/src/textureAtlas.test.ts +59 -0
- package/src/textureAtlasFrom.test.ts +142 -0
- package/src/textureAtlasRegion.test.ts +397 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TextureAtlas } from '@flighthq/types';
|
|
2
|
+
export declare function createTextureAtlas(obj?: Partial<TextureAtlas>): TextureAtlas;
|
|
3
|
+
export declare function getTextureAtlasByteSize(atlas: Readonly<TextureAtlas>): number;
|
|
4
|
+
//# sourceMappingURL=textureAtlas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureAtlas.d.ts","sourceRoot":"","sources":["../src/textureAtlas.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,wBAAgB,kBAAkB,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAK5E;AAID,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAE7E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createEntity } from '@flighthq/entity';
|
|
2
|
+
import { getImageResourceByteSize } from '@flighthq/image';
|
|
3
|
+
export function createTextureAtlas(obj) {
|
|
4
|
+
return createEntity({
|
|
5
|
+
image: obj?.image ?? null,
|
|
6
|
+
regions: obj?.regions ?? [],
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
// Returns the byte footprint of the atlas's CPU-side image data. Equivalent to calling
|
|
10
|
+
// `getImageResourceByteSize` on the atlas image; returns 0 when the image is null or element-only.
|
|
11
|
+
export function getTextureAtlasByteSize(atlas) {
|
|
12
|
+
return atlas.image !== null ? getImageResourceByteSize(atlas.image) : 0;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=textureAtlas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureAtlas.js","sourceRoot":"","sources":["../src/textureAtlas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,MAAM,UAAU,kBAAkB,CAAC,GAA2B;IAC5D,OAAO,YAAY,CAAC;QAClB,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,IAAI;QACzB,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,EAAE;KAC5B,CAAC,CAAC;AACL,CAAC;AAED,uFAAuF;AACvF,mGAAmG;AACnG,MAAM,UAAU,uBAAuB,CAAC,KAA6B;IACnE,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,wBAAwB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImageResource, TextureAtlas } from '@flighthq/types';
|
|
2
|
+
export declare function createTextureAtlasFromCanvas(canvas: HTMLCanvasElement): TextureAtlas;
|
|
3
|
+
export declare function createTextureAtlasFromImageBitmap(bitmap: ImageBitmap): TextureAtlas;
|
|
4
|
+
export declare function createTextureAtlasFromImageElement(img: HTMLImageElement): TextureAtlas;
|
|
5
|
+
export declare function createTextureAtlasFromImageResource(resource: ImageResource): TextureAtlas;
|
|
6
|
+
export declare function loadTextureAtlasFromBase64(base64: string, mimeType: string, signal?: AbortSignal): Promise<TextureAtlas>;
|
|
7
|
+
export declare function loadTextureAtlasFromBlob(blob: Blob, signal?: AbortSignal): Promise<TextureAtlas>;
|
|
8
|
+
export declare function loadTextureAtlasFromBytes(bytes: Uint8Array, mimeType?: string, signal?: AbortSignal): Promise<TextureAtlas>;
|
|
9
|
+
export declare function loadTextureAtlasFromUrl(url: string, crossOrigin?: string, signal?: AbortSignal): Promise<TextureAtlas>;
|
|
10
|
+
//# sourceMappingURL=textureAtlasFrom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureAtlasFrom.d.ts","sourceRoot":"","sources":["../src/textureAtlasFrom.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAInE,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,iBAAiB,GAAG,YAAY,CAEpF;AAED,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAEnF;AAED,wBAAgB,kCAAkC,CAAC,GAAG,EAAE,gBAAgB,GAAG,YAAY,CAEtF;AAED,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,aAAa,GAAG,YAAY,CAEzF;AAED,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAEtG;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CAEvB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createImageResourceFromCanvas, createImageResourceFromImageBitmap, createImageResourceFromImageElement, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, } from '@flighthq/image';
|
|
2
|
+
import { createTextureAtlas } from './textureAtlas';
|
|
3
|
+
export function createTextureAtlasFromCanvas(canvas) {
|
|
4
|
+
return createTextureAtlas({ image: createImageResourceFromCanvas(canvas) });
|
|
5
|
+
}
|
|
6
|
+
export function createTextureAtlasFromImageBitmap(bitmap) {
|
|
7
|
+
return createTextureAtlas({ image: createImageResourceFromImageBitmap(bitmap) });
|
|
8
|
+
}
|
|
9
|
+
export function createTextureAtlasFromImageElement(img) {
|
|
10
|
+
return createTextureAtlas({ image: createImageResourceFromImageElement(img) });
|
|
11
|
+
}
|
|
12
|
+
export function createTextureAtlasFromImageResource(resource) {
|
|
13
|
+
return createTextureAtlas({ image: resource });
|
|
14
|
+
}
|
|
15
|
+
export async function loadTextureAtlasFromBase64(base64, mimeType, signal) {
|
|
16
|
+
return createTextureAtlasFromImageResource(await loadImageResourceFromBase64(base64, mimeType, signal));
|
|
17
|
+
}
|
|
18
|
+
export async function loadTextureAtlasFromBlob(blob, signal) {
|
|
19
|
+
return createTextureAtlasFromImageResource(await loadImageResourceFromBlob(blob, signal));
|
|
20
|
+
}
|
|
21
|
+
export async function loadTextureAtlasFromBytes(bytes, mimeType, signal) {
|
|
22
|
+
return createTextureAtlasFromImageResource(await loadImageResourceFromBytes(bytes, mimeType, signal));
|
|
23
|
+
}
|
|
24
|
+
export async function loadTextureAtlasFromUrl(url, crossOrigin, signal) {
|
|
25
|
+
return createTextureAtlasFromImageResource(await loadImageResourceFromUrl(url, crossOrigin, signal));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=textureAtlasFrom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureAtlasFrom.js","sourceRoot":"","sources":["../src/textureAtlasFrom.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,EAClC,mCAAmC,EACnC,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,UAAU,4BAA4B,CAAC,MAAyB;IACpE,OAAO,kBAAkB,CAAC,EAAE,KAAK,EAAE,6BAA6B,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,MAAmB;IACnE,OAAO,kBAAkB,CAAC,EAAE,KAAK,EAAE,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,GAAqB;IACtE,OAAO,kBAAkB,CAAC,EAAE,KAAK,EAAE,mCAAmC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,mCAAmC,CAAC,QAAuB;IACzE,OAAO,kBAAkB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,MAAc,EACd,QAAgB,EAChB,MAAoB;IAEpB,OAAO,mCAAmC,CAAC,MAAM,2BAA2B,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAC1G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAU,EAAE,MAAoB;IAC7E,OAAO,mCAAmC,CAAC,MAAM,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAAiB,EACjB,QAAiB,EACjB,MAAoB;IAEpB,OAAO,mCAAmC,CAAC,MAAM,0BAA0B,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AACxG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAW,EACX,WAAoB,EACpB,MAAoB;IAEpB,OAAO,mCAAmC,CAAC,MAAM,wBAAwB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;AACvG,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RectangleLike, TextureAtlas, TextureAtlasRegion, TextureAtlasRegionLike, Vector2Like } from '@flighthq/types';
|
|
2
|
+
export declare function addTextureAtlasRegion(target: TextureAtlas, x: number, y: number, width: number, height: number, pivotX?: number, pivotY?: number, name?: string): void;
|
|
3
|
+
export declare function addTextureAtlasRegionRectangle(target: TextureAtlas, rect: Readonly<RectangleLike>, pivot?: Readonly<Vector2Like>, name?: string): void;
|
|
4
|
+
export declare function addTextureAtlasRegionRectangleXY(target: TextureAtlas, ax: number, ay: number, bx: number, by: number, pivotX?: number, pivotY?: number, name?: string): void;
|
|
5
|
+
export declare function addTextureAtlasRegionVector2(target: TextureAtlas, a: Readonly<Vector2Like>, b: Readonly<Vector2Like>, pivot?: Readonly<Vector2Like>, name?: string): void;
|
|
6
|
+
export declare function createTextureAtlasRegion(obj?: Partial<TextureAtlasRegionLike>): TextureAtlasRegion;
|
|
7
|
+
export declare function getTextureAtlasRegionById(atlas: Readonly<TextureAtlas>, id: number): TextureAtlasRegion | null;
|
|
8
|
+
export declare function getTextureAtlasRegionByName(atlas: Readonly<TextureAtlas>, name: string): TextureAtlasRegion | null;
|
|
9
|
+
export declare function getTextureAtlasRegionSequence(atlas: Readonly<TextureAtlas>, prefix: string): TextureAtlasRegion[];
|
|
10
|
+
export declare function getTextureAtlasRegionUv(region: Readonly<TextureAtlasRegion>, imageWidth: number, imageHeight: number, out: RectangleLike): RectangleLike;
|
|
11
|
+
export declare function setTextureAtlasRegion(out: TextureAtlasRegion, x: number, y?: number, width?: number, height?: number, pivotX?: number, pivotY?: number): void;
|
|
12
|
+
//# sourceMappingURL=textureAtlasRegion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureAtlasRegion.d.ts","sourceRoot":"","sources":["../src/textureAtlasRegion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAEzB,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,YAAY,EACpB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAaN;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC7B,KAAK,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAWN;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,YAAY,EACpB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAEN;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,YAAY,EACpB,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,EACxB,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,EACxB,KAAK,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAWN;AAED,wBAAgB,wBAAwB,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAAG,kBAAkB,CAiBlG;AAGD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAK9G;AAID,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAKlH;AAKD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAMjH;AAQD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACpC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,aAAa,GACjB,aAAa,CAkBf;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,kBAAkB,EACvB,CAAC,EAAE,MAAM,EACT,CAAC,GAAE,MAAU,EACb,KAAK,GAAE,MAAU,EACjB,MAAM,GAAE,MAAU,EAClB,MAAM,GAAE,MAAU,EAClB,MAAM,GAAE,MAAU,GACjB,IAAI,CAON"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createEntity } from '@flighthq/entity';
|
|
2
|
+
export function addTextureAtlasRegion(target, x, y, width, height, pivotX, pivotY, name) {
|
|
3
|
+
target.regions.push(createTextureAtlasRegion({
|
|
4
|
+
x: x,
|
|
5
|
+
y: y,
|
|
6
|
+
width: width,
|
|
7
|
+
height: height,
|
|
8
|
+
id: target.regions.length,
|
|
9
|
+
pivotX: pivotX ?? null,
|
|
10
|
+
pivotY: pivotY ?? null,
|
|
11
|
+
name: name ?? null,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
export function addTextureAtlasRegionRectangle(target, rect, pivot, name) {
|
|
15
|
+
addTextureAtlasRegion(target, rect.x, rect.y, rect.width, rect.height, pivot ? pivot.x : undefined, pivot ? pivot.y : undefined, name);
|
|
16
|
+
}
|
|
17
|
+
export function addTextureAtlasRegionRectangleXY(target, ax, ay, bx, by, pivotX, pivotY, name) {
|
|
18
|
+
addTextureAtlasRegion(target, ax, ay, bx - ax, by - ay, pivotX, pivotY, name);
|
|
19
|
+
}
|
|
20
|
+
export function addTextureAtlasRegionVector2(target, a, b, pivot, name) {
|
|
21
|
+
addTextureAtlasRegion(target, a.x, a.y, b.x - a.x, b.y - a.y, pivot ? pivot.x : undefined, pivot ? pivot.y : undefined, name);
|
|
22
|
+
}
|
|
23
|
+
export function createTextureAtlasRegion(obj) {
|
|
24
|
+
return createEntity({
|
|
25
|
+
x: obj?.x ?? 0,
|
|
26
|
+
y: obj?.y ?? 0,
|
|
27
|
+
width: obj?.width ?? 0,
|
|
28
|
+
height: obj?.height ?? 0,
|
|
29
|
+
id: obj?.id ?? -1,
|
|
30
|
+
name: obj?.name ?? null,
|
|
31
|
+
originalHeight: obj?.originalHeight ?? null,
|
|
32
|
+
originalWidth: obj?.originalWidth ?? null,
|
|
33
|
+
pivotX: obj?.pivotX ?? null,
|
|
34
|
+
pivotY: obj?.pivotY ?? null,
|
|
35
|
+
rotated: obj?.rotated ?? false,
|
|
36
|
+
sourceX: obj?.sourceX ?? 0,
|
|
37
|
+
sourceY: obj?.sourceY ?? 0,
|
|
38
|
+
trimmed: obj?.trimmed ?? false,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Returns the first region with the given id, or null if not found.
|
|
42
|
+
export function getTextureAtlasRegionById(atlas, id) {
|
|
43
|
+
for (const region of atlas.regions) {
|
|
44
|
+
if (region.id === id)
|
|
45
|
+
return region;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
// Returns the first region whose name matches exactly, or null if not found.
|
|
50
|
+
// Case-sensitive. Linear scan — acceptable for typical atlas sizes (< 2000 regions).
|
|
51
|
+
export function getTextureAtlasRegionByName(atlas, name) {
|
|
52
|
+
for (const region of atlas.regions) {
|
|
53
|
+
if (region.name === name)
|
|
54
|
+
return region;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
// Returns all regions whose name starts with the given prefix, in insertion order.
|
|
59
|
+
// Useful for collecting animation frame sequences following a `baseName_NNN` naming convention.
|
|
60
|
+
// Returns an empty array when no region names match.
|
|
61
|
+
export function getTextureAtlasRegionSequence(atlas, prefix) {
|
|
62
|
+
const result = [];
|
|
63
|
+
for (const region of atlas.regions) {
|
|
64
|
+
if (region.name !== null && region.name.startsWith(prefix))
|
|
65
|
+
result.push(region);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
// Writes normalized UV coordinates (0–1) for the region into `out`.
|
|
70
|
+
// Accounts for the atlas image dimensions: `out.x = region.x / imageWidth`, etc.
|
|
71
|
+
// When `region.rotated` is true the packed rectangle is transposed — the UV rect still
|
|
72
|
+
// covers the packed (rotated) texels; callers drawing a rotated region must swap width/height.
|
|
73
|
+
// Returns `out` for chaining. Returns `out` with all zeros when `imageWidth` or `imageHeight`
|
|
74
|
+
// is zero to avoid division by zero.
|
|
75
|
+
export function getTextureAtlasRegionUv(region, imageWidth, imageHeight, out) {
|
|
76
|
+
if (imageWidth <= 0 || imageHeight <= 0) {
|
|
77
|
+
out.x = 0;
|
|
78
|
+
out.y = 0;
|
|
79
|
+
out.width = 0;
|
|
80
|
+
out.height = 0;
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
// Read all inputs before writing — alias-safe.
|
|
84
|
+
const rx = region.x;
|
|
85
|
+
const ry = region.y;
|
|
86
|
+
const rw = region.width;
|
|
87
|
+
const rh = region.height;
|
|
88
|
+
out.x = rx / imageWidth;
|
|
89
|
+
out.y = ry / imageHeight;
|
|
90
|
+
out.width = rw / imageWidth;
|
|
91
|
+
out.height = rh / imageHeight;
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
export function setTextureAtlasRegion(out, x, y = 0, width = 0, height = 0, pivotX = 0, pivotY = 0) {
|
|
95
|
+
out.x = x;
|
|
96
|
+
out.y = y;
|
|
97
|
+
out.width = width;
|
|
98
|
+
out.height = height;
|
|
99
|
+
out.pivotX = pivotX;
|
|
100
|
+
out.pivotY = pivotY;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=textureAtlasRegion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureAtlasRegion.js","sourceRoot":"","sources":["../src/textureAtlasRegion.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/textureatlas",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"!dist/**/*.test.js",
|
|
17
|
+
"!dist/**/*.test.d.ts",
|
|
18
|
+
"!dist/**/*.test.js.map",
|
|
19
|
+
"!dist/**/*.test.d.ts.map"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b",
|
|
23
|
+
"clean": "tsc -b --clean",
|
|
24
|
+
"test": "vitest run --config vitest.config.ts",
|
|
25
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
26
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
27
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flighthq/entity": "0.1.0",
|
|
31
|
+
"@flighthq/geometry": "0.1.0",
|
|
32
|
+
"@flighthq/image": "0.1.0",
|
|
33
|
+
"@flighthq/types": "0.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^5.3.0"
|
|
37
|
+
},
|
|
38
|
+
"description": "Texture atlases and tilesets: regions, UVs, and constructors over image resources",
|
|
39
|
+
"sideEffects": false
|
|
40
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createImageResource } from '@flighthq/image';
|
|
2
|
+
import type { ImageResource, TextureAtlas, TextureAtlasRegion } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import { createTextureAtlas, getTextureAtlasByteSize } from './textureAtlas';
|
|
5
|
+
|
|
6
|
+
describe('createTextureAtlas', () => {
|
|
7
|
+
let atlas: TextureAtlas;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
atlas = createTextureAtlas();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('allows pre-defined values', () => {
|
|
14
|
+
const base = {
|
|
15
|
+
image: {} as ImageResource,
|
|
16
|
+
regions: [{} as TextureAtlasRegion],
|
|
17
|
+
};
|
|
18
|
+
const obj = createTextureAtlas(base);
|
|
19
|
+
expect(obj.image).toStrictEqual(base.image);
|
|
20
|
+
expect(obj.regions).toStrictEqual(base.regions);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('initializes default values', () => {
|
|
24
|
+
expect(atlas.image).toBeNull();
|
|
25
|
+
expect(atlas.regions).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns a new object for better hidden-class performance', () => {
|
|
29
|
+
const base = {};
|
|
30
|
+
const obj = createTextureAtlas(base);
|
|
31
|
+
expect(obj).not.toStrictEqual(base);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('uses a provided regions array directly', () => {
|
|
35
|
+
const regions = [{} as TextureAtlasRegion];
|
|
36
|
+
const atlas = createTextureAtlas({ regions });
|
|
37
|
+
expect(atlas.regions).toBe(regions);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('getTextureAtlasByteSize', () => {
|
|
42
|
+
it('returns 0 when the atlas has no image', () => {
|
|
43
|
+
const atlas = createTextureAtlas();
|
|
44
|
+
expect(getTextureAtlasByteSize(atlas)).toBe(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns 0 when the atlas image has no data (element-only)', () => {
|
|
48
|
+
const image = createImageResource();
|
|
49
|
+
const atlas = createTextureAtlas({ image });
|
|
50
|
+
expect(getTextureAtlasByteSize(atlas)).toBe(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns the image data byteLength when data is present', () => {
|
|
54
|
+
const image = createImageResource();
|
|
55
|
+
image.data = new Uint8ClampedArray(256);
|
|
56
|
+
const atlas = createTextureAtlas({ image });
|
|
57
|
+
expect(getTextureAtlasByteSize(atlas)).toBe(256);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createImageResourceFromImageElement } from '@flighthq/image';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createTextureAtlasFromCanvas,
|
|
5
|
+
createTextureAtlasFromImageBitmap,
|
|
6
|
+
createTextureAtlasFromImageElement,
|
|
7
|
+
createTextureAtlasFromImageResource,
|
|
8
|
+
loadTextureAtlasFromBase64,
|
|
9
|
+
loadTextureAtlasFromBlob,
|
|
10
|
+
loadTextureAtlasFromBytes,
|
|
11
|
+
loadTextureAtlasFromUrl,
|
|
12
|
+
} from './textureAtlasFrom';
|
|
13
|
+
|
|
14
|
+
// Stub img.decode() so async load functions resolve immediately in jsdom.
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
HTMLImageElement.prototype.decode = vi.fn().mockResolvedValue(undefined);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
vi.restoreAllMocks();
|
|
21
|
+
delete (HTMLImageElement.prototype as Partial<HTMLImageElement>).decode;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('createTextureAtlasFromCanvas', () => {
|
|
25
|
+
it('wraps a canvas with correct dimensions', () => {
|
|
26
|
+
const canvas = document.createElement('canvas');
|
|
27
|
+
canvas.width = 320;
|
|
28
|
+
canvas.height = 240;
|
|
29
|
+
const atlas = createTextureAtlasFromCanvas(canvas);
|
|
30
|
+
|
|
31
|
+
expect(atlas.image?.source).toBe(canvas);
|
|
32
|
+
expect(atlas.image?.width).toBe(320);
|
|
33
|
+
expect(atlas.image?.height).toBe(240);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('starts with an empty regions array', () => {
|
|
37
|
+
const canvas = document.createElement('canvas');
|
|
38
|
+
expect(createTextureAtlasFromCanvas(canvas).regions).toHaveLength(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns a new object each call', () => {
|
|
42
|
+
const canvas = document.createElement('canvas');
|
|
43
|
+
expect(createTextureAtlasFromCanvas(canvas)).not.toBe(createTextureAtlasFromCanvas(canvas));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('createTextureAtlasFromImageBitmap', () => {
|
|
48
|
+
it('wraps an ImageBitmap with correct dimensions', () => {
|
|
49
|
+
const bitmap = { width: 64, height: 128, close: () => {} } as ImageBitmap;
|
|
50
|
+
const atlas = createTextureAtlasFromImageBitmap(bitmap);
|
|
51
|
+
|
|
52
|
+
expect(atlas.image?.source).toBe(bitmap);
|
|
53
|
+
expect(atlas.image?.width).toBe(64);
|
|
54
|
+
expect(atlas.image?.height).toBe(128);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('returns a new object each call', () => {
|
|
58
|
+
const bitmap = { width: 1, height: 1, close: () => {} } as ImageBitmap;
|
|
59
|
+
expect(createTextureAtlasFromImageBitmap(bitmap)).not.toBe(createTextureAtlasFromImageBitmap(bitmap));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('createTextureAtlasFromImageElement', () => {
|
|
64
|
+
it('wraps an HTMLImageElement with correct dimensions', () => {
|
|
65
|
+
const img = { width: 200, height: 100 } as HTMLImageElement;
|
|
66
|
+
const atlas = createTextureAtlasFromImageElement(img);
|
|
67
|
+
|
|
68
|
+
expect(atlas.image?.source).toBe(img);
|
|
69
|
+
expect(atlas.image?.width).toBe(200);
|
|
70
|
+
expect(atlas.image?.height).toBe(100);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns a new object each call', () => {
|
|
74
|
+
const img = document.createElement('img');
|
|
75
|
+
expect(createTextureAtlasFromImageElement(img)).not.toBe(createTextureAtlasFromImageElement(img));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('createTextureAtlasFromImageResource', () => {
|
|
80
|
+
it('uses the provided ImageResource as the atlas image', () => {
|
|
81
|
+
const source = createImageResourceFromImageElement({ width: 128, height: 64 } as HTMLImageElement);
|
|
82
|
+
const atlas = createTextureAtlasFromImageResource(source);
|
|
83
|
+
|
|
84
|
+
expect(atlas.image).toBe(source);
|
|
85
|
+
expect(atlas.image?.width).toBe(128);
|
|
86
|
+
expect(atlas.image?.height).toBe(64);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('starts with an empty regions array', () => {
|
|
90
|
+
const source = createImageResourceFromImageElement({ width: 1, height: 1 } as HTMLImageElement);
|
|
91
|
+
expect(createTextureAtlasFromImageResource(source).regions).toHaveLength(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('returns a new object each call', () => {
|
|
95
|
+
const source = createImageResourceFromImageElement({ width: 1, height: 1 } as HTMLImageElement);
|
|
96
|
+
expect(createTextureAtlasFromImageResource(source)).not.toBe(createTextureAtlasFromImageResource(source));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('loadTextureAtlasFromBase64', () => {
|
|
101
|
+
it('resolves to a TextureAtlas with a non-null image', async () => {
|
|
102
|
+
const atlas = await loadTextureAtlasFromBase64('abc123', 'image/png');
|
|
103
|
+
expect(atlas.image?.source).toBeInstanceOf(HTMLImageElement);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('loadTextureAtlasFromBlob', () => {
|
|
108
|
+
it('resolves to a TextureAtlas with a non-null image', async () => {
|
|
109
|
+
const blob = new Blob([], { type: 'image/png' });
|
|
110
|
+
const atlas = await loadTextureAtlasFromBlob(blob);
|
|
111
|
+
expect(atlas.image?.source).toBeInstanceOf(HTMLImageElement);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('loadTextureAtlasFromBytes', () => {
|
|
116
|
+
it('resolves to a TextureAtlas with a non-null image', async () => {
|
|
117
|
+
const bytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0]);
|
|
118
|
+
const atlas = await loadTextureAtlasFromBytes(bytes);
|
|
119
|
+
|
|
120
|
+
expect(atlas.image).not.toBeNull();
|
|
121
|
+
expect(atlas.image?.source).toBeInstanceOf(HTMLImageElement);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('starts with an empty regions array', async () => {
|
|
125
|
+
const bytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0]);
|
|
126
|
+
const atlas = await loadTextureAtlasFromBytes(bytes);
|
|
127
|
+
|
|
128
|
+
expect(atlas.regions).toHaveLength(0);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('throws when mime type cannot be detected', async () => {
|
|
132
|
+
const bytes = new Uint8Array(16);
|
|
133
|
+
await expect(loadTextureAtlasFromBytes(bytes)).rejects.toThrow('Unable to determine image type');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('loadTextureAtlasFromUrl', () => {
|
|
138
|
+
it('resolves to a TextureAtlas whose image src is an HTMLImageElement', async () => {
|
|
139
|
+
const atlas = await loadTextureAtlasFromUrl('data:image/png;base64,abc');
|
|
140
|
+
expect(atlas.image?.source).toBeInstanceOf(HTMLImageElement);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { createRectangle, createVector2 } from '@flighthq/geometry';
|
|
2
|
+
import type { TextureAtlasRegion } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import { createTextureAtlas } from './textureAtlas';
|
|
5
|
+
import {
|
|
6
|
+
addTextureAtlasRegion,
|
|
7
|
+
addTextureAtlasRegionRectangle,
|
|
8
|
+
addTextureAtlasRegionRectangleXY,
|
|
9
|
+
addTextureAtlasRegionVector2,
|
|
10
|
+
createTextureAtlasRegion,
|
|
11
|
+
getTextureAtlasRegionById,
|
|
12
|
+
getTextureAtlasRegionByName,
|
|
13
|
+
getTextureAtlasRegionSequence,
|
|
14
|
+
getTextureAtlasRegionUv,
|
|
15
|
+
setTextureAtlasRegion,
|
|
16
|
+
} from './textureAtlasRegion';
|
|
17
|
+
|
|
18
|
+
describe('addTextureAtlasRegion', () => {
|
|
19
|
+
it('pushes a new region onto the atlas with the given coordinates', () => {
|
|
20
|
+
const atlas = createTextureAtlas();
|
|
21
|
+
addTextureAtlasRegion(atlas, 10, 20, 30, 40);
|
|
22
|
+
expect(atlas.regions).toHaveLength(1);
|
|
23
|
+
expect(atlas.regions[0].x).toBe(10);
|
|
24
|
+
expect(atlas.regions[0].y).toBe(20);
|
|
25
|
+
expect(atlas.regions[0].width).toBe(30);
|
|
26
|
+
expect(atlas.regions[0].height).toBe(40);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('assigns id equal to the region index before insertion', () => {
|
|
30
|
+
const atlas = createTextureAtlas();
|
|
31
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
32
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
33
|
+
expect(atlas.regions[0].id).toBe(0);
|
|
34
|
+
expect(atlas.regions[1].id).toBe(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('sets optional pivot values', () => {
|
|
38
|
+
const atlas = createTextureAtlas();
|
|
39
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, 5, 5);
|
|
40
|
+
expect(atlas.regions[0].pivotX).toBe(5);
|
|
41
|
+
expect(atlas.regions[0].pivotY).toBe(5);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('sets optional name', () => {
|
|
45
|
+
const atlas = createTextureAtlas();
|
|
46
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'hero');
|
|
47
|
+
expect(atlas.regions[0].name).toBe('hero');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('defaults name to null when not provided', () => {
|
|
51
|
+
const atlas = createTextureAtlas();
|
|
52
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
53
|
+
expect(atlas.regions[0].name).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('addTextureAtlasRegionRectangle', () => {
|
|
58
|
+
it('accepts rectangle-like and vector-like objects', () => {
|
|
59
|
+
const atlas = createTextureAtlas();
|
|
60
|
+
addTextureAtlasRegionRectangle(atlas, { x: 10, y: 20, width: 30, height: 40 }, { x: 5, y: 6 });
|
|
61
|
+
expect(atlas.regions[0].x).toBe(10);
|
|
62
|
+
expect(atlas.regions[0].y).toBe(20);
|
|
63
|
+
expect(atlas.regions[0].width).toBe(30);
|
|
64
|
+
expect(atlas.regions[0].height).toBe(40);
|
|
65
|
+
expect(atlas.regions[0].pivotX).toBe(5);
|
|
66
|
+
expect(atlas.regions[0].pivotY).toBe(6);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('adds a region from a Rectangle', () => {
|
|
70
|
+
const atlas = createTextureAtlas();
|
|
71
|
+
const rect = createRectangle(10, 20, 30, 40);
|
|
72
|
+
addTextureAtlasRegionRectangle(atlas, rect);
|
|
73
|
+
expect(atlas.regions[0].x).toBe(10);
|
|
74
|
+
expect(atlas.regions[0].width).toBe(30);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('sets optional name', () => {
|
|
78
|
+
const atlas = createTextureAtlas();
|
|
79
|
+
addTextureAtlasRegionRectangle(atlas, createRectangle(0, 0, 10, 10), undefined, 'frame_00');
|
|
80
|
+
expect(atlas.regions[0].name).toBe('frame_00');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('sets pivot from optional Vector2', () => {
|
|
84
|
+
const atlas = createTextureAtlas();
|
|
85
|
+
addTextureAtlasRegionRectangle(atlas, createRectangle(0, 0, 10, 10), createVector2(3, 4));
|
|
86
|
+
expect(atlas.regions[0].pivotX).toBe(3);
|
|
87
|
+
expect(atlas.regions[0].pivotY).toBe(4);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('addTextureAtlasRegionRectangleXY', () => {
|
|
92
|
+
it('computes width and height from corner coordinates', () => {
|
|
93
|
+
const atlas = createTextureAtlas();
|
|
94
|
+
addTextureAtlasRegionRectangleXY(atlas, 5, 10, 25, 30);
|
|
95
|
+
expect(atlas.regions[0].x).toBe(5);
|
|
96
|
+
expect(atlas.regions[0].y).toBe(10);
|
|
97
|
+
expect(atlas.regions[0].width).toBe(20);
|
|
98
|
+
expect(atlas.regions[0].height).toBe(20);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('sets optional name', () => {
|
|
102
|
+
const atlas = createTextureAtlas();
|
|
103
|
+
addTextureAtlasRegionRectangleXY(atlas, 0, 0, 10, 10, undefined, undefined, 'tile_0');
|
|
104
|
+
expect(atlas.regions[0].name).toBe('tile_0');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('addTextureAtlasRegionVector2', () => {
|
|
109
|
+
it('accepts vector-like corner and pivot objects', () => {
|
|
110
|
+
const atlas = createTextureAtlas();
|
|
111
|
+
addTextureAtlasRegionVector2(atlas, { x: 5, y: 10 }, { x: 25, y: 30 }, { x: 3, y: 4 });
|
|
112
|
+
expect(atlas.regions[0].x).toBe(5);
|
|
113
|
+
expect(atlas.regions[0].y).toBe(10);
|
|
114
|
+
expect(atlas.regions[0].width).toBe(20);
|
|
115
|
+
expect(atlas.regions[0].height).toBe(20);
|
|
116
|
+
expect(atlas.regions[0].pivotX).toBe(3);
|
|
117
|
+
expect(atlas.regions[0].pivotY).toBe(4);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('computes region from two Vector2 corner points', () => {
|
|
121
|
+
const atlas = createTextureAtlas();
|
|
122
|
+
addTextureAtlasRegionVector2(atlas, createVector2(5, 10), createVector2(25, 30));
|
|
123
|
+
expect(atlas.regions[0].x).toBe(5);
|
|
124
|
+
expect(atlas.regions[0].y).toBe(10);
|
|
125
|
+
expect(atlas.regions[0].width).toBe(20);
|
|
126
|
+
expect(atlas.regions[0].height).toBe(20);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('sets optional name', () => {
|
|
130
|
+
const atlas = createTextureAtlas();
|
|
131
|
+
addTextureAtlasRegionVector2(atlas, { x: 0, y: 0 }, { x: 10, y: 10 }, undefined, 'walk_01');
|
|
132
|
+
expect(atlas.regions[0].name).toBe('walk_01');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('createTextureAtlasRegion', () => {
|
|
137
|
+
let region: TextureAtlasRegion;
|
|
138
|
+
|
|
139
|
+
beforeEach(() => {
|
|
140
|
+
region = createTextureAtlasRegion();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('allows pre-defined values', () => {
|
|
144
|
+
const base = {
|
|
145
|
+
x: 1,
|
|
146
|
+
y: 2,
|
|
147
|
+
width: 3,
|
|
148
|
+
height: 4,
|
|
149
|
+
id: 5,
|
|
150
|
+
name: 'hero',
|
|
151
|
+
pivotX: 6,
|
|
152
|
+
pivotY: 7,
|
|
153
|
+
};
|
|
154
|
+
const obj = createTextureAtlasRegion(base);
|
|
155
|
+
expect(obj.x).toStrictEqual(base.x);
|
|
156
|
+
expect(obj.y).toStrictEqual(base.y);
|
|
157
|
+
expect(obj.width).toStrictEqual(base.width);
|
|
158
|
+
expect(obj.height).toStrictEqual(base.height);
|
|
159
|
+
expect(obj.id).toStrictEqual(base.id);
|
|
160
|
+
expect(obj.name).toStrictEqual(base.name);
|
|
161
|
+
expect(obj.pivotX).toStrictEqual(base.pivotX);
|
|
162
|
+
expect(obj.pivotY).toStrictEqual(base.pivotY);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('initializes default values', () => {
|
|
166
|
+
expect(region.x).toStrictEqual(0);
|
|
167
|
+
expect(region.y).toStrictEqual(0);
|
|
168
|
+
expect(region.id).toStrictEqual(-1);
|
|
169
|
+
expect(region.name).toBeNull();
|
|
170
|
+
expect(region.originalHeight).toBeNull();
|
|
171
|
+
expect(region.originalWidth).toBeNull();
|
|
172
|
+
expect(region.pivotX).toBeNull();
|
|
173
|
+
expect(region.pivotY).toBeNull();
|
|
174
|
+
expect(region.rotated).toBe(false);
|
|
175
|
+
expect(region.sourceX).toStrictEqual(0);
|
|
176
|
+
expect(region.sourceY).toStrictEqual(0);
|
|
177
|
+
expect(region.trimmed).toBe(false);
|
|
178
|
+
expect(region.width).toStrictEqual(0);
|
|
179
|
+
expect(region.height).toStrictEqual(0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('initializes trim and rotation fields', () => {
|
|
183
|
+
const trimmed = createTextureAtlasRegion({
|
|
184
|
+
trimmed: true,
|
|
185
|
+
rotated: true,
|
|
186
|
+
sourceX: 4,
|
|
187
|
+
sourceY: 8,
|
|
188
|
+
originalWidth: 64,
|
|
189
|
+
originalHeight: 32,
|
|
190
|
+
});
|
|
191
|
+
expect(trimmed.trimmed).toBe(true);
|
|
192
|
+
expect(trimmed.rotated).toBe(true);
|
|
193
|
+
expect(trimmed.sourceX).toBe(4);
|
|
194
|
+
expect(trimmed.sourceY).toBe(8);
|
|
195
|
+
expect(trimmed.originalWidth).toBe(64);
|
|
196
|
+
expect(trimmed.originalHeight).toBe(32);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('returns a new object for better hidden-class performance', () => {
|
|
200
|
+
const base = {};
|
|
201
|
+
const obj = createTextureAtlasRegion(base);
|
|
202
|
+
expect(obj).not.toStrictEqual(base);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('getTextureAtlasRegionById', () => {
|
|
207
|
+
it('returns null for an empty atlas', () => {
|
|
208
|
+
const atlas = createTextureAtlas();
|
|
209
|
+
expect(getTextureAtlasRegionById(atlas, 0)).toBeNull();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('returns null when no region matches the id', () => {
|
|
213
|
+
const atlas = createTextureAtlas();
|
|
214
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
215
|
+
expect(getTextureAtlasRegionById(atlas, 99)).toBeNull();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('returns the region with the matching id', () => {
|
|
219
|
+
const atlas = createTextureAtlas();
|
|
220
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
221
|
+
addTextureAtlasRegion(atlas, 10, 0, 10, 10);
|
|
222
|
+
const region = getTextureAtlasRegionById(atlas, 1);
|
|
223
|
+
expect(region).not.toBeNull();
|
|
224
|
+
expect(region?.x).toBe(10);
|
|
225
|
+
expect(region?.id).toBe(1);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('getTextureAtlasRegionByName', () => {
|
|
230
|
+
it('returns null for an empty atlas', () => {
|
|
231
|
+
const atlas = createTextureAtlas();
|
|
232
|
+
expect(getTextureAtlasRegionByName(atlas, 'hero')).toBeNull();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('returns null when no region matches the name', () => {
|
|
236
|
+
const atlas = createTextureAtlas();
|
|
237
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'hero');
|
|
238
|
+
expect(getTextureAtlasRegionByName(atlas, 'villain')).toBeNull();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('returns null for regions with null name', () => {
|
|
242
|
+
const atlas = createTextureAtlas();
|
|
243
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
244
|
+
expect(getTextureAtlasRegionByName(atlas, '')).toBeNull();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('returns the region with the matching name', () => {
|
|
248
|
+
const atlas = createTextureAtlas();
|
|
249
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'hero_idle_0');
|
|
250
|
+
addTextureAtlasRegion(atlas, 10, 0, 10, 10, undefined, undefined, 'hero_walk_0');
|
|
251
|
+
const region = getTextureAtlasRegionByName(atlas, 'hero_walk_0');
|
|
252
|
+
expect(region).not.toBeNull();
|
|
253
|
+
expect(region?.x).toBe(10);
|
|
254
|
+
expect(region?.name).toBe('hero_walk_0');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('is case-sensitive', () => {
|
|
258
|
+
const atlas = createTextureAtlas();
|
|
259
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'Hero');
|
|
260
|
+
expect(getTextureAtlasRegionByName(atlas, 'hero')).toBeNull();
|
|
261
|
+
expect(getTextureAtlasRegionByName(atlas, 'Hero')).not.toBeNull();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe('getTextureAtlasRegionSequence', () => {
|
|
266
|
+
it('returns an empty array when the atlas has no regions', () => {
|
|
267
|
+
const atlas = createTextureAtlas();
|
|
268
|
+
expect(getTextureAtlasRegionSequence(atlas, 'walk')).toEqual([]);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('returns regions whose names start with the given prefix', () => {
|
|
272
|
+
const atlas = createTextureAtlas();
|
|
273
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'walk_01');
|
|
274
|
+
addTextureAtlasRegion(atlas, 10, 0, 10, 10, undefined, undefined, 'walk_02');
|
|
275
|
+
addTextureAtlasRegion(atlas, 20, 0, 10, 10, undefined, undefined, 'idle_01');
|
|
276
|
+
const seq = getTextureAtlasRegionSequence(atlas, 'walk');
|
|
277
|
+
expect(seq).toHaveLength(2);
|
|
278
|
+
expect(seq[0].name).toBe('walk_01');
|
|
279
|
+
expect(seq[1].name).toBe('walk_02');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('skips regions with null names', () => {
|
|
283
|
+
const atlas = createTextureAtlas();
|
|
284
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10);
|
|
285
|
+
addTextureAtlasRegion(atlas, 10, 0, 10, 10, undefined, undefined, 'walk_01');
|
|
286
|
+
const seq = getTextureAtlasRegionSequence(atlas, 'walk');
|
|
287
|
+
expect(seq).toHaveLength(1);
|
|
288
|
+
expect(seq[0].name).toBe('walk_01');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('returns regions in insertion order', () => {
|
|
292
|
+
const atlas = createTextureAtlas();
|
|
293
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'run_03');
|
|
294
|
+
addTextureAtlasRegion(atlas, 10, 0, 10, 10, undefined, undefined, 'run_01');
|
|
295
|
+
addTextureAtlasRegion(atlas, 20, 0, 10, 10, undefined, undefined, 'run_02');
|
|
296
|
+
const seq = getTextureAtlasRegionSequence(atlas, 'run');
|
|
297
|
+
expect(seq.map((r) => r.name)).toEqual(['run_03', 'run_01', 'run_02']);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('returns an empty array when no region names match the prefix', () => {
|
|
301
|
+
const atlas = createTextureAtlas();
|
|
302
|
+
addTextureAtlasRegion(atlas, 0, 0, 10, 10, undefined, undefined, 'idle_01');
|
|
303
|
+
expect(getTextureAtlasRegionSequence(atlas, 'walk')).toEqual([]);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('getTextureAtlasRegionUv', () => {
|
|
308
|
+
it('returns zero rect when imageWidth is zero', () => {
|
|
309
|
+
const region = createTextureAtlasRegion({ x: 10, y: 20, width: 30, height: 40 });
|
|
310
|
+
const out = { x: 1, y: 1, width: 1, height: 1 };
|
|
311
|
+
getTextureAtlasRegionUv(region, 0, 100, out);
|
|
312
|
+
expect(out).toEqual({ x: 0, y: 0, width: 0, height: 0 });
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('returns zero rect when imageHeight is zero', () => {
|
|
316
|
+
const region = createTextureAtlasRegion({ x: 10, y: 20, width: 30, height: 40 });
|
|
317
|
+
const out = { x: 1, y: 1, width: 1, height: 1 };
|
|
318
|
+
getTextureAtlasRegionUv(region, 100, 0, out);
|
|
319
|
+
expect(out).toEqual({ x: 0, y: 0, width: 0, height: 0 });
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('computes normalized UV coordinates', () => {
|
|
323
|
+
const region = createTextureAtlasRegion({ x: 0, y: 0, width: 128, height: 64 });
|
|
324
|
+
const out = { x: 0, y: 0, width: 0, height: 0 };
|
|
325
|
+
getTextureAtlasRegionUv(region, 256, 256, out);
|
|
326
|
+
expect(out.x).toBe(0);
|
|
327
|
+
expect(out.y).toBe(0);
|
|
328
|
+
expect(out.width).toBeCloseTo(0.5);
|
|
329
|
+
expect(out.height).toBeCloseTo(0.25);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('computes UVs for a region offset within the atlas', () => {
|
|
333
|
+
const region = createTextureAtlasRegion({ x: 128, y: 64, width: 64, height: 64 });
|
|
334
|
+
const out = { x: 0, y: 0, width: 0, height: 0 };
|
|
335
|
+
getTextureAtlasRegionUv(region, 256, 256, out);
|
|
336
|
+
expect(out.x).toBeCloseTo(0.5);
|
|
337
|
+
expect(out.y).toBeCloseTo(0.25);
|
|
338
|
+
expect(out.width).toBeCloseTo(0.25);
|
|
339
|
+
expect(out.height).toBeCloseTo(0.25);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('is alias-safe when out shares no fields with region', () => {
|
|
343
|
+
const region = createTextureAtlasRegion({ x: 64, y: 64, width: 64, height: 64 });
|
|
344
|
+
const out = { x: 0, y: 0, width: 0, height: 0 };
|
|
345
|
+
const result = getTextureAtlasRegionUv(region, 256, 256, out);
|
|
346
|
+
expect(result).toBe(out);
|
|
347
|
+
expect(out.x).toBeCloseTo(0.25);
|
|
348
|
+
expect(out.y).toBeCloseTo(0.25);
|
|
349
|
+
expect(out.width).toBeCloseTo(0.25);
|
|
350
|
+
expect(out.height).toBeCloseTo(0.25);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe('setTextureAtlasRegion', () => {
|
|
355
|
+
it('defaults optional parameters to 0', () => {
|
|
356
|
+
const region = createTextureAtlasRegion();
|
|
357
|
+
setTextureAtlasRegion(region, 5);
|
|
358
|
+
expect(region.x).toBe(5);
|
|
359
|
+
expect(region.y).toBe(0);
|
|
360
|
+
expect(region.width).toBe(0);
|
|
361
|
+
expect(region.height).toBe(0);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('is alias-safe when out is also an input reference', () => {
|
|
365
|
+
const region = createTextureAtlasRegion({ x: 10, y: 20, width: 30, height: 40 });
|
|
366
|
+
setTextureAtlasRegion(region, region.x, region.y, region.width, region.height);
|
|
367
|
+
expect(region.x).toBe(10);
|
|
368
|
+
expect(region.y).toBe(20);
|
|
369
|
+
expect(region.width).toBe(30);
|
|
370
|
+
expect(region.height).toBe(40);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('reuses the existing region object', () => {
|
|
374
|
+
const region = createTextureAtlasRegion();
|
|
375
|
+
const target = region;
|
|
376
|
+
setTextureAtlasRegion(region, 10, 20, 30, 40, 5, 6);
|
|
377
|
+
expect(region).toBe(target);
|
|
378
|
+
expect(region.x).toBe(10);
|
|
379
|
+
expect(region.y).toBe(20);
|
|
380
|
+
expect(region.width).toBe(30);
|
|
381
|
+
expect(region.height).toBe(40);
|
|
382
|
+
expect(region.pivotX).toBe(5);
|
|
383
|
+
expect(region.pivotY).toBe(6);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('sets all fields on an existing region', () => {
|
|
387
|
+
const region = createTextureAtlasRegion();
|
|
388
|
+
const result = setTextureAtlasRegion(region, 10, 20, 30, 40, 5, 6);
|
|
389
|
+
expect(result).toBeUndefined();
|
|
390
|
+
expect(region.x).toBe(10);
|
|
391
|
+
expect(region.y).toBe(20);
|
|
392
|
+
expect(region.width).toBe(30);
|
|
393
|
+
expect(region.height).toBe(40);
|
|
394
|
+
expect(region.pivotX).toBe(5);
|
|
395
|
+
expect(region.pivotY).toBe(6);
|
|
396
|
+
});
|
|
397
|
+
});
|