@codexo/exojs-tilemap 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.
Files changed (151) hide show
  1. package/README.md +33 -26
  2. package/dist/esm/ChunkSource.d.ts +33 -0
  3. package/dist/esm/ChunkSource.d.ts.map +1 -0
  4. package/dist/esm/ChunkStreamer.d.ts +98 -0
  5. package/dist/esm/ChunkStreamer.d.ts.map +1 -0
  6. package/dist/esm/ChunkStreamer.js +201 -0
  7. package/dist/esm/ChunkStreamer.js.map +1 -0
  8. package/dist/esm/ImageLayer.d.ts +11 -3
  9. package/dist/esm/ImageLayer.d.ts.map +1 -0
  10. package/dist/esm/ImageLayer.js +71 -64
  11. package/dist/esm/ImageLayer.js.map +1 -1
  12. package/dist/esm/ImageLayerNode.d.ts +89 -0
  13. package/dist/esm/ImageLayerNode.d.ts.map +1 -0
  14. package/dist/esm/ImageLayerNode.js +205 -0
  15. package/dist/esm/ImageLayerNode.js.map +1 -0
  16. package/dist/esm/MapObject.d.ts +70 -0
  17. package/dist/esm/MapObject.d.ts.map +1 -0
  18. package/dist/esm/MapObject.js +34 -0
  19. package/dist/esm/MapObject.js.map +1 -0
  20. package/dist/esm/MapObjectSpawner.d.ts +133 -0
  21. package/dist/esm/MapObjectSpawner.d.ts.map +1 -0
  22. package/dist/esm/MapObjectSpawner.js +147 -0
  23. package/dist/esm/MapObjectSpawner.js.map +1 -0
  24. package/dist/esm/MapSpawnSession.d.ts +53 -0
  25. package/dist/esm/MapSpawnSession.d.ts.map +1 -0
  26. package/dist/esm/MapSpawnSession.js +88 -0
  27. package/dist/esm/MapSpawnSession.js.map +1 -0
  28. package/dist/esm/MapWorld.d.ts +134 -0
  29. package/dist/esm/MapWorld.d.ts.map +1 -0
  30. package/dist/esm/MapWorld.js +150 -0
  31. package/dist/esm/MapWorld.js.map +1 -0
  32. package/dist/esm/MapWorldRuntime.d.ts +182 -0
  33. package/dist/esm/MapWorldRuntime.d.ts.map +1 -0
  34. package/dist/esm/MapWorldRuntime.js +261 -0
  35. package/dist/esm/MapWorldRuntime.js.map +1 -0
  36. package/dist/esm/ObjectLayer.d.ts +93 -7
  37. package/dist/esm/ObjectLayer.d.ts.map +1 -0
  38. package/dist/esm/ObjectLayer.js +184 -167
  39. package/dist/esm/ObjectLayer.js.map +1 -1
  40. package/dist/esm/SampledChunkSource.d.ts +43 -0
  41. package/dist/esm/SampledChunkSource.d.ts.map +1 -0
  42. package/dist/esm/SampledChunkSource.js +48 -0
  43. package/dist/esm/SampledChunkSource.js.map +1 -0
  44. package/dist/esm/TileAnimator.d.ts +12 -2
  45. package/dist/esm/TileAnimator.d.ts.map +1 -0
  46. package/dist/esm/TileAnimator.js +154 -165
  47. package/dist/esm/TileAnimator.js.map +1 -1
  48. package/dist/esm/TileChunk.d.ts +30 -5
  49. package/dist/esm/TileChunk.d.ts.map +1 -0
  50. package/dist/esm/TileChunk.js +190 -188
  51. package/dist/esm/TileChunk.js.map +1 -1
  52. package/dist/esm/TileChunkNode.d.ts +18 -3
  53. package/dist/esm/TileChunkNode.d.ts.map +1 -0
  54. package/dist/esm/TileChunkNode.js +104 -85
  55. package/dist/esm/TileChunkNode.js.map +1 -1
  56. package/dist/esm/TileLayer.d.ts +104 -24
  57. package/dist/esm/TileLayer.d.ts.map +1 -0
  58. package/dist/esm/TileLayer.js +511 -469
  59. package/dist/esm/TileLayer.js.map +1 -1
  60. package/dist/esm/TileLayerNode.d.ts +52 -11
  61. package/dist/esm/TileLayerNode.d.ts.map +1 -0
  62. package/dist/esm/TileLayerNode.js +224 -166
  63. package/dist/esm/TileLayerNode.js.map +1 -1
  64. package/dist/esm/TileMap.d.ts +96 -31
  65. package/dist/esm/TileMap.d.ts.map +1 -0
  66. package/dist/esm/TileMap.js +401 -357
  67. package/dist/esm/TileMap.js.map +1 -1
  68. package/dist/esm/TileMapBand.d.ts +42 -29
  69. package/dist/esm/TileMapBand.d.ts.map +1 -0
  70. package/dist/esm/TileMapBand.js +151 -158
  71. package/dist/esm/TileMapBand.js.map +1 -1
  72. package/dist/esm/TileMapNode.d.ts +34 -18
  73. package/dist/esm/TileMapNode.d.ts.map +1 -0
  74. package/dist/esm/TileMapNode.js +122 -109
  75. package/dist/esm/TileMapNode.js.map +1 -1
  76. package/dist/esm/TileMapView.d.ts +108 -47
  77. package/dist/esm/TileMapView.d.ts.map +1 -0
  78. package/dist/esm/TileMapView.js +385 -338
  79. package/dist/esm/TileMapView.js.map +1 -1
  80. package/dist/esm/TileSet.d.ts +15 -6
  81. package/dist/esm/TileSet.d.ts.map +1 -0
  82. package/dist/esm/TileSet.js +161 -181
  83. package/dist/esm/TileSet.js.map +1 -1
  84. package/dist/esm/WangSet.d.ts +5 -4
  85. package/dist/esm/WangSet.d.ts.map +1 -0
  86. package/dist/esm/WangSet.js +70 -78
  87. package/dist/esm/WangSet.js.map +1 -1
  88. package/dist/esm/WorkerSampledChunkSource.d.ts +95 -0
  89. package/dist/esm/WorkerSampledChunkSource.d.ts.map +1 -0
  90. package/dist/esm/WorkerSampledChunkSource.js +117 -0
  91. package/dist/esm/WorkerSampledChunkSource.js.map +1 -0
  92. package/dist/esm/autoTile.d.ts +10 -5
  93. package/dist/esm/autoTile.d.ts.map +1 -0
  94. package/dist/esm/autoTile.js +164 -205
  95. package/dist/esm/autoTile.js.map +1 -1
  96. package/dist/esm/chunkGeometry.d.ts +8 -7
  97. package/dist/esm/chunkGeometry.d.ts.map +1 -0
  98. package/dist/esm/chunkGeometry.js +83 -94
  99. package/dist/esm/chunkGeometry.js.map +1 -1
  100. package/dist/esm/index.d.ts +1 -0
  101. package/dist/esm/index.d.ts.map +1 -0
  102. package/dist/esm/index.js +26 -15
  103. package/dist/esm/nodeBounds.d.ts +20 -0
  104. package/dist/esm/nodeBounds.d.ts.map +1 -0
  105. package/dist/esm/nodeBounds.js +40 -0
  106. package/dist/esm/nodeBounds.js.map +1 -0
  107. package/dist/esm/pixelSnap.d.ts +4 -3
  108. package/dist/esm/pixelSnap.d.ts.map +1 -0
  109. package/dist/esm/pixelSnap.js +18 -16
  110. package/dist/esm/pixelSnap.js.map +1 -1
  111. package/dist/esm/public.d.ts +23 -3
  112. package/dist/esm/public.d.ts.map +1 -0
  113. package/dist/esm/tileCollision.d.ts +190 -0
  114. package/dist/esm/tileCollision.d.ts.map +1 -0
  115. package/dist/esm/tileCollision.js +367 -0
  116. package/dist/esm/tileCollision.js.map +1 -0
  117. package/dist/esm/tileWord.d.ts +12 -0
  118. package/dist/esm/tileWord.d.ts.map +1 -0
  119. package/dist/esm/tileWord.js +16 -0
  120. package/dist/esm/tileWord.js.map +1 -0
  121. package/dist/esm/tilemapExtension.d.ts +4 -3
  122. package/dist/esm/tilemapExtension.d.ts.map +1 -0
  123. package/dist/esm/tilemapExtension.js +47 -47
  124. package/dist/esm/tilemapExtension.js.map +1 -1
  125. package/dist/esm/tilemapSerializers.d.ts +5 -4
  126. package/dist/esm/tilemapSerializers.d.ts.map +1 -0
  127. package/dist/esm/tilemapSerializers.js +48 -41
  128. package/dist/esm/tilemapSerializers.js.map +1 -1
  129. package/dist/esm/types.d.ts +34 -18
  130. package/dist/esm/types.d.ts.map +1 -0
  131. package/dist/esm/types.js +111 -120
  132. package/dist/esm/types.js.map +1 -1
  133. package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts +50 -2
  134. package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts.map +1 -0
  135. package/dist/esm/webgl2/WebGl2TileChunkRenderer.js +366 -332
  136. package/dist/esm/webgl2/WebGl2TileChunkRenderer.js.map +1 -1
  137. package/dist/esm/webgl2/shaders/tile-chunk.frag.js +6 -0
  138. package/dist/esm/webgl2/shaders/tile-chunk.frag.js.map +1 -0
  139. package/dist/esm/webgl2/shaders/tile-chunk.vert.js +6 -0
  140. package/dist/esm/webgl2/shaders/tile-chunk.vert.js.map +1 -0
  141. package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts +70 -4
  142. package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts.map +1 -0
  143. package/dist/esm/webgpu/WebGpuTileChunkRenderer.js +496 -383
  144. package/dist/esm/webgpu/WebGpuTileChunkRenderer.js.map +1 -1
  145. package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js +6 -0
  146. package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js.map +1 -0
  147. package/package.json +8 -15
  148. package/dist/esm/index.js.map +0 -1
  149. package/dist/esm/register.d.ts +0 -1
  150. package/dist/esm/register.js +0 -25
  151. package/dist/esm/register.js.map +0 -1
@@ -1,192 +1,194 @@
1
- import { validateInteger } from './types.js';
1
+ import { validateInteger } from "./types.js";
2
2
 
3
+ //#region src/TileChunk.ts
3
4
  /**
4
- * A compact, fixed-size chunk of tile data within a layer.
5
- *
6
- * Storage is a single {@link Uint32Array} of length `width * height`,
7
- * laid out row-major. Each cell is a packed tile word (0 = empty).
8
- * Chunk coordinates are signed to support future infinite maps.
9
- *
10
- * The backing array is **private** external code cannot mutate storage
11
- * directly. All mutation must flow through {@link TileLayer} public APIs,
12
- * which call the package-internal `_setRawAt` method.
13
- *
14
- * The chunk tracks a revision counter that increments on every mutation
15
- * so the future renderer can detect which chunks need GPU rebuilds.
16
- *
17
- * @advanced
18
- */
19
- class TileChunk {
20
- /** Signed chunk X coordinate. */
21
- cx;
22
- /** Signed chunk Y coordinate. */
23
- cy;
24
- /** Width of this chunk in tiles. */
25
- width;
26
- /** Height of this chunk in tiles. */
27
- height;
28
- /** Packed tile storage (row-major). Private never exposed directly. */
29
- _tiles;
30
- /** Whether every cell in this chunk is empty (0). Lazy-computed. */
31
- _empty = null;
32
- /** Monotonic revision counter incremented on every mutation. */
33
- _revision = 0;
34
- /**
35
- * @param cx Signed chunk X coordinate (must be finite safe integer).
36
- * @param cy Signed chunk Y coordinate (must be finite safe integer).
37
- * @param width Chunk width in tiles (positive safe integer).
38
- * @param height Chunk height in tiles (positive safe integer).
39
- * @param source Optional source data to copy (must match length).
40
- */
41
- constructor(cx, cy, width, height, source) {
42
- // Validate chunk coordinates as finite safe integers (negative OK).
43
- if (!Number.isFinite(cx) || !Number.isInteger(cx) || !Number.isSafeInteger(cx)) {
44
- throw new Error(`TileChunk cx must be a finite safe integer (got ${cx}).`);
45
- }
46
- if (!Number.isFinite(cy) || !Number.isInteger(cy) || !Number.isSafeInteger(cy)) {
47
- throw new Error(`TileChunk cy must be a finite safe integer (got ${cy}).`);
48
- }
49
- if (width <= 0 || height <= 0) {
50
- throw new Error(`TileChunk dimensions must be positive (got ${width}x${height}).`);
51
- }
52
- if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height)) {
53
- throw new Error(`TileChunk dimensions must be safe integers (got ${width}x${height}).`);
54
- }
55
- const size = width * height;
56
- if (!Number.isSafeInteger(size)) {
57
- throw new Error(`TileChunk size overflow: ${width} * ${height} is not safe.`);
58
- }
59
- // Guard against TypedArray length limits.
60
- if (size > 0xFFFFFFFF) {
61
- throw new Error(`TileChunk size ${size} exceeds TypedArray maximum length.`);
62
- }
63
- this.cx = cx;
64
- this.cy = cy;
65
- this.width = width;
66
- this.height = height;
67
- if (source) {
68
- if (source.length !== size) {
69
- throw new Error(`TileChunk source length ${source.length} != ${size}.`);
70
- }
71
- // Defensive copy — caller mutation of source array will not affect storage.
72
- this._tiles = new Uint32Array(source);
73
- }
74
- else {
75
- this._tiles = new Uint32Array(size);
76
- }
77
- }
78
- /** Row-major index for cell (lx, ly) within this chunk. */
79
- _index(lx, ly) {
80
- return ly * this.width + lx;
81
- }
82
- /**
83
- * Validate local-in-chunk coordinates before access.
84
- * @throws If lx or ly is not a finite integer or out of [0, dimension).
85
- */
86
- _validateLocalCoord(lx, ly) {
87
- validateInteger(lx, 'lx');
88
- validateInteger(ly, 'ly');
89
- if (lx < 0 || lx >= this.width) {
90
- throw new Error(`lx ${lx} out of chunk bounds [0, ${this.width - 1}].`);
91
- }
92
- if (ly < 0 || ly >= this.height) {
93
- throw new Error(`ly ${ly} out of chunk bounds [0, ${this.height - 1}].`);
94
- }
95
- }
96
- // ── Readonly public API (ReadonlyTileChunk) ──────────────────────────
97
- /** @inheritdoc */
98
- getRawAt(lx, ly) {
99
- this._validateLocalCoord(lx, ly);
100
- return this._tiles[this._index(lx, ly)];
101
- }
102
- /** @inheritdoc */
103
- get empty() {
104
- if (this._empty === null) {
105
- this._empty = this._tiles.every(v => v === 0);
106
- }
107
- return this._empty;
108
- }
109
- /** @inheritdoc */
110
- get revision() {
111
- return this._revision;
112
- }
113
- /** @inheritdoc */
114
- cloneTiles() {
115
- return new Uint32Array(this._tiles);
116
- }
117
- // ── Internal mutation (package-private, NOT public API) ──────────────
118
- /**
119
- * Package-internal access to the underlying tile storage.
120
- * Returns the actual mutable `Uint32Array` — DO NOT USE publicly.
121
- *
122
- * Provided for the future tilemap renderer (same package) so it can
123
- * read chunk data efficiently without a full `cloneTiles()` copy per frame.
124
- *
125
- * External consumers and other packages MUST NOT call this.
126
- * @internal
127
- */
128
- _getRawStorage() {
129
- return this._tiles;
130
- }
131
- /**
132
- * Validate a packed tile value before storing.
133
- * Accepts any finite integer — `Uint32Array` stores via unsigned 32-bit
134
- * truncation (`ToUint32`). Negative values produced by JavaScript's
135
- * signed bitwise ops (e.g. when the transform bits set bit 31) are valid.
136
- * @throws If the value is NaN, Infinity, or non-integer.
137
- * @internal
138
- */
139
- _validatePacked(packed) {
140
- if (typeof packed !== 'number' || !Number.isFinite(packed) || !Number.isInteger(packed)) {
141
- throw new Error(`Packed tile must be a finite integer (got ${packed}).`);
142
- }
143
- }
144
- /**
145
- * Write a packed tile word at local-in-chunk coordinates.
146
- * Returns true if the stored value actually changed.
147
- *
148
- * Package-internal: only {@link TileLayer} may call this.
149
- * @internal
150
- */
151
- _setRawAt(lx, ly, packed) {
152
- this._validateLocalCoord(lx, ly);
153
- this._validatePacked(packed);
154
- const i = this._index(lx, ly);
155
- const prev = this._tiles[i];
156
- if (prev === packed)
157
- return false;
158
- this._tiles[i] = packed;
159
- this._revision++;
160
- this._empty = null; // invalidate cache
161
- return true;
162
- }
163
- /**
164
- * Mark the chunk as needing a GPU rebuild.
165
- * Invalidates the empty cache and increments revision.
166
- *
167
- * Package-internal: for future renderer use only.
168
- * @internal
169
- */
170
- _markDirty() {
171
- this._empty = null;
172
- this._revision++;
173
- }
174
- /**
175
- * Clear every cell in this chunk. Does not re-allocate storage.
176
- * Increments revision if any cell was non-zero.
177
- *
178
- * Package-internal: only {@link TileLayer} may call this.
179
- * @internal
180
- */
181
- _clear() {
182
- const hadContent = this._tiles.some(v => v !== 0);
183
- if (!hadContent)
184
- return;
185
- this._tiles.fill(0);
186
- this._revision++;
187
- this._empty = true;
188
- }
189
- }
5
+ * A compact, fixed-size chunk of tile data within a layer.
6
+ *
7
+ * Storage is a single {@link Uint32Array} of length `width * height`,
8
+ * laid out row-major. Each cell is a packed tile word (0 = empty).
9
+ * Chunk coordinates are signed to support future infinite maps.
10
+ *
11
+ * The backing array is **private** - external code cannot mutate storage
12
+ * directly. All mutation must flow through {@link TileLayer} public APIs,
13
+ * which call the package-internal `_setRawAt` method.
14
+ *
15
+ * The chunk tracks a revision counter that increments on every mutation
16
+ * so the future renderer can detect which chunks need GPU rebuilds.
17
+ *
18
+ * @advanced
19
+ */
20
+ var TileChunk = class {
21
+ /** Signed chunk X coordinate. */
22
+ cx;
23
+ /** Signed chunk Y coordinate. */
24
+ cy;
25
+ /** Width of this chunk in tiles. */
26
+ width;
27
+ /** Height of this chunk in tiles. */
28
+ height;
29
+ /** Packed tile storage (row-major). Private - never exposed directly. */
30
+ _tiles;
31
+ /** Whether every cell in this chunk is empty (0). Lazy-computed. */
32
+ _empty = null;
33
+ /** Monotonic revision counter - incremented on every mutation. */
34
+ _revision = 0;
35
+ /**
36
+ * Render-node-owned callbacks notified whenever `_revision` advances.
37
+ * `TileChunkNode.pages` caches geometry against `revision`, but that cache
38
+ * is only consulted from `render()` - a call the engine skips entirely for
39
+ * a content-clean subtree under a `RetainedContainer`. Without
40
+ * this push, an in-place tile edit after capture would replay stale cached
41
+ * geometry forever. Package-internal; not part of {@link ReadonlyTileChunk}.
42
+ */
43
+ _dirtyListeners = null;
44
+ /**
45
+ * @param cx Signed chunk X coordinate (must be finite safe integer).
46
+ * @param cy Signed chunk Y coordinate (must be finite safe integer).
47
+ * @param width Chunk width in tiles (positive safe integer).
48
+ * @param height Chunk height in tiles (positive safe integer).
49
+ * @param source Optional source data to copy (must match length).
50
+ */
51
+ constructor(cx, cy, width, height, source) {
52
+ if (!Number.isFinite(cx) || !Number.isInteger(cx) || !Number.isSafeInteger(cx)) throw new Error(`TileChunk cx must be a finite safe integer (got ${cx}).`);
53
+ if (!Number.isFinite(cy) || !Number.isInteger(cy) || !Number.isSafeInteger(cy)) throw new Error(`TileChunk cy must be a finite safe integer (got ${cy}).`);
54
+ if (width <= 0 || height <= 0) throw new Error(`TileChunk dimensions must be positive (got ${width}x${height}).`);
55
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height)) throw new Error(`TileChunk dimensions must be safe integers (got ${width}x${height}).`);
56
+ const size = width * height;
57
+ if (!Number.isSafeInteger(size)) throw new Error(`TileChunk size overflow: ${width} * ${height} is not safe.`);
58
+ if (size > 4294967295) throw new Error(`TileChunk size ${size} exceeds TypedArray maximum length.`);
59
+ this.cx = cx;
60
+ this.cy = cy;
61
+ this.width = width;
62
+ this.height = height;
63
+ if (source) {
64
+ if (source.length !== size) throw new Error(`TileChunk source length ${source.length} != ${size}.`);
65
+ this._tiles = new Uint32Array(source);
66
+ } else this._tiles = new Uint32Array(size);
67
+ }
68
+ /** Row-major index for cell (lx, ly) within this chunk. */
69
+ _index(lx, ly) {
70
+ return ly * this.width + lx;
71
+ }
72
+ /**
73
+ * Validate local-in-chunk coordinates before access.
74
+ * @throws If lx or ly is not a finite integer or out of [0, dimension).
75
+ */
76
+ _validateLocalCoord(lx, ly) {
77
+ validateInteger(lx, "lx");
78
+ validateInteger(ly, "ly");
79
+ if (lx < 0 || lx >= this.width) throw new Error(`lx ${lx} out of chunk bounds [0, ${this.width - 1}].`);
80
+ if (ly < 0 || ly >= this.height) throw new Error(`ly ${ly} out of chunk bounds [0, ${this.height - 1}].`);
81
+ }
82
+ /** @inheritdoc */
83
+ getRawAt(lx, ly) {
84
+ this._validateLocalCoord(lx, ly);
85
+ return this._tiles[this._index(lx, ly)];
86
+ }
87
+ /** @inheritdoc */
88
+ get empty() {
89
+ if (this._empty === null) this._empty = this._tiles.every((v) => v === 0);
90
+ return this._empty;
91
+ }
92
+ /** @inheritdoc */
93
+ get revision() {
94
+ return this._revision;
95
+ }
96
+ /** @inheritdoc */
97
+ cloneTiles() {
98
+ return new Uint32Array(this._tiles);
99
+ }
100
+ /**
101
+ * Package-internal access to the underlying tile storage.
102
+ * Returns the actual mutable `Uint32Array` - DO NOT USE publicly.
103
+ *
104
+ * Provided for the future tilemap renderer (same package) so it can
105
+ * read chunk data efficiently without a full `cloneTiles()` copy per frame.
106
+ *
107
+ * External consumers and other packages MUST NOT call this.
108
+ * @internal
109
+ */
110
+ _getRawStorage() {
111
+ return this._tiles;
112
+ }
113
+ /**
114
+ * Validate a packed tile value before storing.
115
+ * Accepts any finite integer - `Uint32Array` stores via unsigned 32-bit
116
+ * truncation (`ToUint32`). Negative values produced by JavaScript's
117
+ * signed bitwise ops (e.g. when the transform bits set bit 31) are valid.
118
+ * @throws If the value is NaN, Infinity, or non-integer.
119
+ * @internal
120
+ */
121
+ _validatePacked(packed) {
122
+ if (typeof packed !== "number" || !Number.isFinite(packed) || !Number.isInteger(packed)) throw new Error(`Packed tile must be a finite integer (got ${packed}).`);
123
+ }
124
+ /**
125
+ * Write a packed tile word at local-in-chunk coordinates.
126
+ * Returns true if the stored value actually changed.
127
+ *
128
+ * Package-internal: only {@link TileLayer} may call this.
129
+ * @internal
130
+ */
131
+ _setRawAt(lx, ly, packed) {
132
+ this._validateLocalCoord(lx, ly);
133
+ this._validatePacked(packed);
134
+ const i = this._index(lx, ly);
135
+ if (this._tiles[i] === packed) return false;
136
+ this._tiles[i] = packed;
137
+ this._revision++;
138
+ this._empty = null;
139
+ this._notifyDirty();
140
+ return true;
141
+ }
142
+ /**
143
+ * Mark the chunk as needing a GPU rebuild.
144
+ * Invalidates the empty cache and increments revision.
145
+ *
146
+ * Package-internal: for future renderer use only.
147
+ * @internal
148
+ */
149
+ _markDirty() {
150
+ this._empty = null;
151
+ this._revision++;
152
+ this._notifyDirty();
153
+ }
154
+ /**
155
+ * Clear every cell in this chunk. Does not re-allocate storage.
156
+ * Increments revision if any cell was non-zero.
157
+ *
158
+ * Package-internal: only {@link TileLayer} may call this.
159
+ * @internal
160
+ */
161
+ _clear() {
162
+ if (!this._tiles.some((v) => v !== 0)) return;
163
+ this._tiles.fill(0);
164
+ this._revision++;
165
+ this._empty = true;
166
+ this._notifyDirty();
167
+ }
168
+ /**
169
+ * Register a callback invoked synchronously whenever this chunk's revision
170
+ * advances. Multiple listeners are supported (a chunk may back more than one
171
+ * `TileChunkNode` - e.g. two `TileLayerNode`s over the same `TileLayer`).
172
+ * Package-internal: called by `TileChunkNode` to push mutations onto the
173
+ * scene-node content-revision chain (see the `_dirtyListeners` field).
174
+ * @internal
175
+ */
176
+ _addDirtyListener(listener) {
177
+ (this._dirtyListeners ??= /* @__PURE__ */ new Set()).add(listener);
178
+ }
179
+ /**
180
+ * Unregister a listener added via {@link _addDirtyListener} (node destroy).
181
+ * @internal
182
+ */
183
+ _removeDirtyListener(listener) {
184
+ this._dirtyListeners?.delete(listener);
185
+ }
186
+ _notifyDirty() {
187
+ if (this._dirtyListeners === null) return;
188
+ for (const listener of this._dirtyListeners) listener();
189
+ }
190
+ };
190
191
 
192
+ //#endregion
191
193
  export { TileChunk };
192
- //# sourceMappingURL=TileChunk.js.map
194
+ //# sourceMappingURL=TileChunk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TileChunk.js","sources":["../../../src/TileChunk.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TileChunk.js","names":[],"sources":["../../src/TileChunk.ts"],"sourcesContent":["import type { PackedTile } from './types';\nimport { validateInteger } from './types';\n\n/**\n * Public readonly view of a tile chunk.\n *\n * Provides read-only inspection access for iteration, queries, and the\n * future renderer. The underlying storage is never exposed directly.\n *\n * @advanced\n */\nexport interface ReadonlyTileChunk {\n /** Signed chunk X coordinate. */\n readonly cx: number;\n /** Signed chunk Y coordinate. */\n readonly cy: number;\n /** Width of this chunk in tiles. */\n readonly width: number;\n /** Height of this chunk in tiles. */\n readonly height: number;\n /** Whether every cell is empty (all-zero). */\n readonly empty: boolean;\n /**\n * Monotonic revision counter. Increments on every cell mutation\n * within this chunk. No-ops (writing same value) do NOT increment.\n */\n readonly revision: number;\n\n /**\n * Read the raw packed tile word at local-in-chunk coordinates.\n * Returns 0 for empty.\n * @throws If coordinates are invalid.\n */\n getRawAt(lx: number, ly: number): PackedTile;\n\n /**\n * Create a defensive copy of the tile data. The caller owns the copy;\n * mutation of the returned array cannot affect the chunk.\n */\n cloneTiles(): Uint32Array;\n}\n\n/**\n * A compact, fixed-size chunk of tile data within a layer.\n *\n * Storage is a single {@link Uint32Array} of length `width * height`,\n * laid out row-major. Each cell is a packed tile word (0 = empty).\n * Chunk coordinates are signed to support future infinite maps.\n *\n * The backing array is **private** - external code cannot mutate storage\n * directly. All mutation must flow through {@link TileLayer} public APIs,\n * which call the package-internal `_setRawAt` method.\n *\n * The chunk tracks a revision counter that increments on every mutation\n * so the future renderer can detect which chunks need GPU rebuilds.\n *\n * @advanced\n */\nexport class TileChunk implements ReadonlyTileChunk {\n /** Signed chunk X coordinate. */\n public readonly cx: number;\n /** Signed chunk Y coordinate. */\n public readonly cy: number;\n\n /** Width of this chunk in tiles. */\n public readonly width: number;\n /** Height of this chunk in tiles. */\n public readonly height: number;\n\n /** Packed tile storage (row-major). Private - never exposed directly. */\n private readonly _tiles: Uint32Array;\n\n /** Whether every cell in this chunk is empty (0). Lazy-computed. */\n private _empty: boolean | null = null;\n\n /** Monotonic revision counter - incremented on every mutation. */\n private _revision = 0;\n\n /**\n * Render-node-owned callbacks notified whenever `_revision` advances.\n * `TileChunkNode.pages` caches geometry against `revision`, but that cache\n * is only consulted from `render()` - a call the engine skips entirely for\n * a content-clean subtree under a `RetainedContainer`. Without\n * this push, an in-place tile edit after capture would replay stale cached\n * geometry forever. Package-internal; not part of {@link ReadonlyTileChunk}.\n */\n private _dirtyListeners: Set<() => void> | null = null;\n\n /**\n * @param cx Signed chunk X coordinate (must be finite safe integer).\n * @param cy Signed chunk Y coordinate (must be finite safe integer).\n * @param width Chunk width in tiles (positive safe integer).\n * @param height Chunk height in tiles (positive safe integer).\n * @param source Optional source data to copy (must match length).\n */\n public constructor(cx: number, cy: number, width: number, height: number, source?: Uint32Array) {\n // Validate chunk coordinates as finite safe integers (negative OK).\n if (!Number.isFinite(cx) || !Number.isInteger(cx) || !Number.isSafeInteger(cx)) {\n throw new Error(`TileChunk cx must be a finite safe integer (got ${cx}).`);\n }\n if (!Number.isFinite(cy) || !Number.isInteger(cy) || !Number.isSafeInteger(cy)) {\n throw new Error(`TileChunk cy must be a finite safe integer (got ${cy}).`);\n }\n\n if (width <= 0 || height <= 0) {\n throw new Error(`TileChunk dimensions must be positive (got ${width}x${height}).`);\n }\n if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height)) {\n throw new Error(`TileChunk dimensions must be safe integers (got ${width}x${height}).`);\n }\n\n const size = width * height;\n if (!Number.isSafeInteger(size)) {\n throw new Error(`TileChunk size overflow: ${width} * ${height} is not safe.`);\n }\n\n // Guard against TypedArray length limits.\n if (size > 0xffffffff) {\n throw new Error(`TileChunk size ${size} exceeds TypedArray maximum length.`);\n }\n\n this.cx = cx;\n this.cy = cy;\n this.width = width;\n this.height = height;\n\n if (source) {\n if (source.length !== size) {\n throw new Error(`TileChunk source length ${source.length} != ${size}.`);\n }\n // Defensive copy - caller mutation of source array will not affect storage.\n this._tiles = new Uint32Array(source);\n } else {\n this._tiles = new Uint32Array(size);\n }\n }\n\n /** Row-major index for cell (lx, ly) within this chunk. */\n private _index(lx: number, ly: number): number {\n return ly * this.width + lx;\n }\n\n /**\n * Validate local-in-chunk coordinates before access.\n * @throws If lx or ly is not a finite integer or out of [0, dimension).\n */\n private _validateLocalCoord(lx: number, ly: number): void {\n validateInteger(lx, 'lx');\n validateInteger(ly, 'ly');\n if (lx < 0 || lx >= this.width) {\n throw new Error(`lx ${lx} out of chunk bounds [0, ${this.width - 1}].`);\n }\n if (ly < 0 || ly >= this.height) {\n throw new Error(`ly ${ly} out of chunk bounds [0, ${this.height - 1}].`);\n }\n }\n\n // ── Readonly public API (ReadonlyTileChunk) ──────────────────────────\n\n /** @inheritdoc */\n public getRawAt(lx: number, ly: number): PackedTile {\n this._validateLocalCoord(lx, ly);\n return this._tiles[this._index(lx, ly)]!;\n }\n\n /** @inheritdoc */\n public get empty(): boolean {\n if (this._empty === null) {\n this._empty = this._tiles.every(v => v === 0);\n }\n return this._empty;\n }\n\n /** @inheritdoc */\n public get revision(): number {\n return this._revision;\n }\n\n /** @inheritdoc */\n public cloneTiles(): Uint32Array {\n return new Uint32Array(this._tiles);\n }\n\n // ── Internal mutation (package-private, NOT public API) ──────────────\n\n /**\n * Package-internal access to the underlying tile storage.\n * Returns the actual mutable `Uint32Array` - DO NOT USE publicly.\n *\n * Provided for the future tilemap renderer (same package) so it can\n * read chunk data efficiently without a full `cloneTiles()` copy per frame.\n *\n * External consumers and other packages MUST NOT call this.\n * @internal\n */\n public _getRawStorage(): Uint32Array {\n return this._tiles;\n }\n\n /**\n * Validate a packed tile value before storing.\n * Accepts any finite integer - `Uint32Array` stores via unsigned 32-bit\n * truncation (`ToUint32`). Negative values produced by JavaScript's\n * signed bitwise ops (e.g. when the transform bits set bit 31) are valid.\n * @throws If the value is NaN, Infinity, or non-integer.\n * @internal\n */\n private _validatePacked(packed: PackedTile): void {\n if (typeof packed !== 'number' || !Number.isFinite(packed) || !Number.isInteger(packed)) {\n throw new Error(`Packed tile must be a finite integer (got ${packed}).`);\n }\n }\n\n /**\n * Write a packed tile word at local-in-chunk coordinates.\n * Returns true if the stored value actually changed.\n *\n * Package-internal: only {@link TileLayer} may call this.\n * @internal\n */\n public _setRawAt(lx: number, ly: number, packed: PackedTile): boolean {\n this._validateLocalCoord(lx, ly);\n this._validatePacked(packed);\n const i = this._index(lx, ly);\n const prev = this._tiles[i];\n if (prev === packed) return false;\n this._tiles[i] = packed;\n this._revision++;\n this._empty = null; // invalidate cache\n this._notifyDirty();\n return true;\n }\n\n /**\n * Mark the chunk as needing a GPU rebuild.\n * Invalidates the empty cache and increments revision.\n *\n * Package-internal: for future renderer use only.\n * @internal\n */\n public _markDirty(): void {\n this._empty = null;\n this._revision++;\n this._notifyDirty();\n }\n\n /**\n * Clear every cell in this chunk. Does not re-allocate storage.\n * Increments revision if any cell was non-zero.\n *\n * Package-internal: only {@link TileLayer} may call this.\n * @internal\n */\n public _clear(): void {\n const hadContent = this._tiles.some(v => v !== 0);\n if (!hadContent) return;\n this._tiles.fill(0);\n this._revision++;\n this._empty = true;\n this._notifyDirty();\n }\n\n /**\n * Register a callback invoked synchronously whenever this chunk's revision\n * advances. Multiple listeners are supported (a chunk may back more than one\n * `TileChunkNode` - e.g. two `TileLayerNode`s over the same `TileLayer`).\n * Package-internal: called by `TileChunkNode` to push mutations onto the\n * scene-node content-revision chain (see the `_dirtyListeners` field).\n * @internal\n */\n public _addDirtyListener(listener: () => void): void {\n (this._dirtyListeners ??= new Set()).add(listener);\n }\n\n /**\n * Unregister a listener added via {@link _addDirtyListener} (node destroy).\n * @internal\n */\n public _removeDirtyListener(listener: () => void): void {\n this._dirtyListeners?.delete(listener);\n }\n\n private _notifyDirty(): void {\n if (this._dirtyListeners === null) return;\n for (const listener of this._dirtyListeners) {\n listener();\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0DA,IAAa,YAAb,MAAoD;;CAElD,AAAgB;;CAEhB,AAAgB;;CAGhB,AAAgB;;CAEhB,AAAgB;;CAGhB,AAAiB;;CAGjB,AAAQ,SAAyB;;CAGjC,AAAQ,YAAY;;;;;;;;;CAUpB,AAAQ,kBAA0C;;;;;;;;CASlD,AAAO,YAAY,IAAY,IAAY,OAAe,QAAgB,QAAsB;EAE9F,IAAI,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC,OAAO,UAAU,EAAE,KAAK,CAAC,OAAO,cAAc,EAAE,GAC3E,MAAM,IAAI,MAAM,mDAAmD,GAAG,GAAG;EAE3E,IAAI,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC,OAAO,UAAU,EAAE,KAAK,CAAC,OAAO,cAAc,EAAE,GAC3E,MAAM,IAAI,MAAM,mDAAmD,GAAG,GAAG;EAG3E,IAAI,SAAS,KAAK,UAAU,GAC1B,MAAM,IAAI,MAAM,8CAA8C,MAAM,GAAG,OAAO,GAAG;EAEnF,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,CAAC,OAAO,cAAc,MAAM,GAC9D,MAAM,IAAI,MAAM,mDAAmD,MAAM,GAAG,OAAO,GAAG;EAGxF,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,OAAO,cAAc,IAAI,GAC5B,MAAM,IAAI,MAAM,4BAA4B,MAAM,KAAK,OAAO,cAAc;EAI9E,IAAI,OAAO,YACT,MAAM,IAAI,MAAM,kBAAkB,KAAK,oCAAoC;EAG7E,KAAK,KAAK;EACV,KAAK,KAAK;EACV,KAAK,QAAQ;EACb,KAAK,SAAS;EAEd,IAAI,QAAQ;GACV,IAAI,OAAO,WAAW,MACpB,MAAM,IAAI,MAAM,2BAA2B,OAAO,OAAO,MAAM,KAAK,EAAE;GAGxE,KAAK,SAAS,IAAI,YAAY,MAAM;EACtC,OACE,KAAK,SAAS,IAAI,YAAY,IAAI;CAEtC;;CAGA,AAAQ,OAAO,IAAY,IAAoB;EAC7C,OAAO,KAAK,KAAK,QAAQ;CAC3B;;;;;CAMA,AAAQ,oBAAoB,IAAY,IAAkB;EACxD,gBAAgB,IAAI,IAAI;EACxB,gBAAgB,IAAI,IAAI;EACxB,IAAI,KAAK,KAAK,MAAM,KAAK,OACvB,MAAM,IAAI,MAAM,MAAM,GAAG,2BAA2B,KAAK,QAAQ,EAAE,GAAG;EAExE,IAAI,KAAK,KAAK,MAAM,KAAK,QACvB,MAAM,IAAI,MAAM,MAAM,GAAG,2BAA2B,KAAK,SAAS,EAAE,GAAG;CAE3E;;CAKA,AAAO,SAAS,IAAY,IAAwB;EAClD,KAAK,oBAAoB,IAAI,EAAE;EAC/B,OAAO,KAAK,OAAO,KAAK,OAAO,IAAI,EAAE;CACvC;;CAGA,IAAW,QAAiB;EAC1B,IAAI,KAAK,WAAW,MAClB,KAAK,SAAS,KAAK,OAAO,OAAM,MAAK,MAAM,CAAC;EAE9C,OAAO,KAAK;CACd;;CAGA,IAAW,WAAmB;EAC5B,OAAO,KAAK;CACd;;CAGA,AAAO,aAA0B;EAC/B,OAAO,IAAI,YAAY,KAAK,MAAM;CACpC;;;;;;;;;;;CAcA,AAAO,iBAA8B;EACnC,OAAO,KAAK;CACd;;;;;;;;;CAUA,AAAQ,gBAAgB,QAA0B;EAChD,IAAI,OAAO,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,KAAK,CAAC,OAAO,UAAU,MAAM,GACpF,MAAM,IAAI,MAAM,6CAA6C,OAAO,GAAG;CAE3E;;;;;;;;CASA,AAAO,UAAU,IAAY,IAAY,QAA6B;EACpE,KAAK,oBAAoB,IAAI,EAAE;EAC/B,KAAK,gBAAgB,MAAM;EAC3B,MAAM,IAAI,KAAK,OAAO,IAAI,EAAE;EAE5B,IADa,KAAK,OAAO,OACZ,QAAQ,OAAO;EAC5B,KAAK,OAAO,KAAK;EACjB,KAAK;EACL,KAAK,SAAS;EACd,KAAK,aAAa;EAClB,OAAO;CACT;;;;;;;;CASA,AAAO,aAAmB;EACxB,KAAK,SAAS;EACd,KAAK;EACL,KAAK,aAAa;CACpB;;;;;;;;CASA,AAAO,SAAe;EAEpB,IAAI,CADe,KAAK,OAAO,MAAK,MAAK,MAAM,CACjC,GAAG;EACjB,KAAK,OAAO,KAAK,CAAC;EAClB,KAAK;EACL,KAAK,SAAS;EACd,KAAK,aAAa;CACpB;;;;;;;;;CAUA,AAAO,kBAAkB,UAA4B;EACnD,CAAC,KAAK,oCAAoB,IAAI,IAAI,EAAC,CAAE,IAAI,QAAQ;CACnD;;;;;CAMA,AAAO,qBAAqB,UAA4B;EACtD,KAAK,iBAAiB,OAAO,QAAQ;CACvC;CAEA,AAAQ,eAAqB;EAC3B,IAAI,KAAK,oBAAoB,MAAM;EACnC,KAAK,MAAM,YAAY,KAAK,iBAC1B,SAAS;CAEb;AACF"}
@@ -1,4 +1,4 @@
1
- import type { Rectangle } from '@codexo/exojs';
1
+ import type { ReadonlyRectangle } from '@codexo/exojs';
2
2
  import { Drawable } from '@codexo/exojs/renderer-sdk';
3
3
  import type { ChunkPage } from './chunkGeometry';
4
4
  import type { ReadonlyTileChunk } from './TileChunk';
@@ -15,7 +15,7 @@ import type { TileSet } from './TileSet';
15
15
  * chunks are culled by their accurate {@link getLocalBounds} before `render`
16
16
  * is ever called, so a camera pan rebuilds nothing.
17
17
  *
18
- * The node references but never owns the runtime chunk, tilesets, and
18
+ * The node references - but never owns - the runtime chunk, tilesets, and
19
19
  * tileset textures. Destroying it releases only its cached CPU geometry; the
20
20
  * `TileMap`/`TileLayer` data and Loader-owned textures are untouched.
21
21
  *
@@ -31,6 +31,15 @@ export declare class TileChunkNode extends Drawable {
31
31
  private readonly _pixelHeight;
32
32
  private _pages;
33
33
  private _builtRevision;
34
+ /**
35
+ * Bound once so `TileChunk._addDirtyListener`/`_removeDirtyListener` add and
36
+ * remove the SAME function reference. Pushes the chunk's revision bump onto
37
+ * this node's own content revision (see `TileChunk._dirtyListeners`) so a
38
+ * `RetainedContainer` ancestor - which skips walking a content-clean
39
+ * subtree entirely - observes the mutation and re-collects instead of
40
+ * replaying stale cached geometry.
41
+ */
42
+ private readonly _onChunkDirty;
34
43
  /**
35
44
  * @param chunk The readonly chunk this node renders.
36
45
  * @param tilesets The owning layer's tileset array.
@@ -54,6 +63,12 @@ export declare class TileChunkNode extends Drawable {
54
63
  get pages(): readonly ChunkPage[];
55
64
  /** `true` when the chunk has no drawable tiles (no geometry, no draw calls). */
56
65
  get isEmpty(): boolean;
57
- getLocalBounds(): Rectangle;
66
+ /**
67
+ * Recomputed lazily on read, so it writes `_localBounds` directly rather than
68
+ * going through `_setLocalBounds`: an invalidating write inside a getter
69
+ * would re-dirty the node on every read.
70
+ */
71
+ getLocalBounds(): ReadonlyRectangle;
58
72
  destroy(): void;
59
73
  }
74
+ //# sourceMappingURL=TileChunkNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TileChunkNode.d.ts","sourceRoot":"","sources":["../../src/TileChunkNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,cAAc,CAAM;IAE5B;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE5B;IAEF;;;;;;;;;;OAUG;gBAED,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM;IAoB1B,sDAAsD;IACtD,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;;;OAIG;IACH,IAAW,KAAK,IAAI,SAAS,SAAS,EAAE,CAOvC;IAED,gFAAgF;IAChF,IAAW,OAAO,IAAI,OAAO,CAE5B;IAED;;;;OAIG;IACa,cAAc,IAAI,iBAAiB;IAInC,OAAO,IAAI,IAAI;CAUhC"}