rapid-render 0.1.21 → 1.0.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.
@@ -0,0 +1,3258 @@
1
+ var N = /* @__PURE__ */ ((s) => (s[s.Float32 = 0] = "Float32", s[s.Uint32 = 1] = "Uint32", s[s.Uint16 = 2] = "Uint16", s))(N || {});
2
+ class W {
3
+ /** The number of elements currently used in the buffer. */
4
+ usedElemNum;
5
+ /** The main typed array view corresponding to the specified ArrayType. */
6
+ typedArray;
7
+ arrayType;
8
+ /** The maximum number of elements the buffer can currently hold without resizing. */
9
+ maxElemNum;
10
+ /** The number of bytes per element based on the `ArrayType`. */
11
+ bytePerElem;
12
+ arraybuffer;
13
+ /** `Uint32Array` view of the underlying memory buffer. */
14
+ uint32;
15
+ /** `Float32Array` view of the underlying memory buffer. */
16
+ float32;
17
+ /** `Uint16Array` view of the underlying memory buffer. */
18
+ uint16;
19
+ /**
20
+ * Initializes a new dynamic array buffer.
21
+ * @param arrayType - The type of elements this buffer will store primarily.
22
+ */
23
+ constructor(t) {
24
+ this.usedElemNum = 0, this.maxElemNum = 512, this.bytePerElem = this.getArrayType(t).BYTES_PER_ELEMENT, this.arrayType = t, this.arraybuffer = new ArrayBuffer(this.maxElemNum * this.bytePerElem), this.updateTypedArray();
25
+ }
26
+ /**
27
+ * Returns the typed array constructor for the corresponding `ArrayType`.
28
+ * @param arrayType - The type of the array.
29
+ * @returns The typed array constructor.
30
+ * @throws Dispatches an error if the specified type is unsupported.
31
+ */
32
+ getArrayType(t) {
33
+ switch (t) {
34
+ case 0:
35
+ return Float32Array;
36
+ case 1:
37
+ return Uint32Array;
38
+ case 2:
39
+ return Uint16Array;
40
+ default:
41
+ throw new Error("Unsupported ArrayType");
42
+ }
43
+ }
44
+ /**
45
+ * Updates the typed array views when the internal array buffer gets reallocated.
46
+ */
47
+ updateTypedArray() {
48
+ switch (this.uint32 = new Uint32Array(this.arraybuffer), this.float32 = new Float32Array(this.arraybuffer), this.uint16 = new Uint16Array(this.arraybuffer), this.arrayType) {
49
+ case 0:
50
+ this.typedArray = this.float32;
51
+ break;
52
+ case 1:
53
+ this.typedArray = this.uint32;
54
+ break;
55
+ case 2:
56
+ this.typedArray = this.uint16;
57
+ break;
58
+ }
59
+ }
60
+ /**
61
+ * Resets the element usage count to zero. The underlying memory capacity is preserved.
62
+ */
63
+ clear() {
64
+ this.usedElemNum = 0;
65
+ }
66
+ /**
67
+ * Checks if the buffer has enough space for additional elements and resizes the buffer if necessary.
68
+ * @param size - The number of new elements that need to be accommodated.
69
+ * @returns `true` if the buffer was resized, `false` otherwise.
70
+ */
71
+ resize(t = 0) {
72
+ if (t += this.usedElemNum, t > this.maxElemNum) {
73
+ for (; t > this.maxElemNum; )
74
+ this.maxElemNum *= 2;
75
+ return this.setMaxSize(this.maxElemNum), !0;
76
+ }
77
+ return !1;
78
+ }
79
+ /**
80
+ * Reallocates the underlying array buffer to a new size and copies existing data.
81
+ * @param size - The new maximum number of elements.
82
+ */
83
+ setMaxSize(t = this.maxElemNum) {
84
+ const e = this.typedArray;
85
+ this.maxElemNum = t, this.arraybuffer = new ArrayBuffer(t * this.bytePerElem), this.updateTypedArray(), this.typedArray.set(e);
86
+ }
87
+ /**
88
+ * Appends a new 32-bit unsigned integer element to the buffer.
89
+ * @param value - The item to add.
90
+ */
91
+ pushUint32(t) {
92
+ this.resize(1), this.uint32[this.usedElemNum++] = t;
93
+ }
94
+ /**
95
+ * Appends a new 32-bit floating point element to the buffer.
96
+ * @param value - The item to add.
97
+ */
98
+ pushFloat32(t) {
99
+ this.resize(1), this.float32[this.usedElemNum++] = t;
100
+ }
101
+ /**
102
+ * Appends an element to the buffer, using the primary typed array view.
103
+ * @param value - The item to add.
104
+ */
105
+ push(t) {
106
+ this.resize(1), this.typedArray[this.usedElemNum++] = t;
107
+ }
108
+ /**
109
+ * Removes and returns the last element in the buffer.
110
+ * @returns The removed element or undefined if the buffer is empty.
111
+ */
112
+ pop() {
113
+ return this.typedArray[--this.usedElemNum];
114
+ }
115
+ /**
116
+ * Returns the last element pushed to the buffer without removing it.
117
+ * @returns The last element.
118
+ */
119
+ top() {
120
+ return this.typedArray[this.usedElemNum - 1];
121
+ }
122
+ /**
123
+ * Gets the value of the element at the specified index.
124
+ * @param index - The index of the element to retrieve.
125
+ * @returns The requested element.
126
+ */
127
+ get(t) {
128
+ return this.typedArray[t];
129
+ }
130
+ /**
131
+ * Returns a constrained view of the primary typed array up to the specified range.
132
+ * If `end` is omitted, returns the whole reallocated buffer slice.
133
+ * @param begin - The starting index (inclusive). Defaults to 0.
134
+ * @param end - The ending index (exclusive). Optional.
135
+ * @returns A subarray corresponding to the specified range.
136
+ */
137
+ getArray(t = 0, e) {
138
+ return e === void 0 ? this.typedArray : this.typedArray.subarray(t, e);
139
+ }
140
+ /**
141
+ * The number of elements currently stored in the buffer.
142
+ */
143
+ get length() {
144
+ return this.usedElemNum;
145
+ }
146
+ /**
147
+ * Resets the buffer's used element count, effectively emptying it.
148
+ * Same behavior as `clear`.
149
+ */
150
+ reset() {
151
+ this.usedElemNum = 0;
152
+ }
153
+ }
154
+ class j extends W {
155
+ /** The actual WebGLBuffer object maintained by this instance. */
156
+ buffer;
157
+ /** The related WebGL rendering context. */
158
+ gl;
159
+ /** Signifies whether the buffer has been updated and requires resyncing to the GPU. */
160
+ dirty = !0;
161
+ /** The WebGL buffer type, e.g., gl.ARRAY_BUFFER. */
162
+ type;
163
+ /** Size of the allocated buffer block on the GPU side, bounded in elements count. */
164
+ webglBufferSize = 0;
165
+ /** Usage hint for WebGL, e.g., gl.DYNAMIC_DRAW. */
166
+ usage;
167
+ /**
168
+ * Initializes a new WebGL dynamic buffer.
169
+ * @param gl - The active WebGL rendering context state.
170
+ * @param arrayType - Type of the underlying data elements to bind.
171
+ * @param type - Determines the type of WebGLBuffer (defaults to gl.ARRAY_BUFFER).
172
+ * @param usage - Determines the data usage pattern (defaults to gl.DYNAMIC_DRAW).
173
+ */
174
+ constructor(t, e, i = t.ARRAY_BUFFER, r = t.DYNAMIC_DRAW) {
175
+ super(e), this.gl = t, this.buffer = t.createBuffer(), this.type = i, this.usage = r;
176
+ }
177
+ /**
178
+ * Flags the buffer as dirty, marking it for future data synchronization.
179
+ */
180
+ makeDirty() {
181
+ this.dirty = !0;
182
+ }
183
+ /**
184
+ * Clears CPU side data usage and marks buffer as synced to stop needless updates.
185
+ */
186
+ clear() {
187
+ super.clear(), this.dirty = !1;
188
+ }
189
+ /**
190
+ * Binds this buffer object tracking it as the current bound buffer to the GPU.
191
+ */
192
+ bindBuffer() {
193
+ this.gl.bindBuffer(this.type, this.buffer);
194
+ }
195
+ /**
196
+ * Uploads the local buffer data to the GPU memory synchronously.
197
+ */
198
+ bufferData() {
199
+ if (this.dirty) {
200
+ const t = this.gl;
201
+ this.maxElemNum > this.webglBufferSize && (t.bufferData(this.type, this.maxElemNum * this.bytePerElem, this.usage), this.webglBufferSize = this.maxElemNum), t.bufferSubData(this.type, 0, this.getArray(0, this.usedElemNum)), this.dirty = !1;
202
+ }
203
+ }
204
+ }
205
+ class d {
206
+ _r;
207
+ _g;
208
+ _b;
209
+ _a;
210
+ uint32;
211
+ premultipliedUint32;
212
+ static clampByte(t) {
213
+ return t <= 0 ? 0 : t >= 255 ? 255 : Math.round(t);
214
+ }
215
+ /**
216
+ * Creates an instance of Color.
217
+ * @param r - The red component (0-255).
218
+ * @param g - The green component (0-255).
219
+ * @param b - The blue component (0-255).
220
+ * @param a - The alpha component (0-255).
221
+ */
222
+ constructor(t, e, i, r = 255) {
223
+ this._r = t, this._g = e, this._b = i, this._a = r, this.updateUint();
224
+ }
225
+ setClearColor(t) {
226
+ t.clearColor(this.r / 255, this.g / 255, this.b / 255, this.a / 255);
227
+ }
228
+ /**
229
+ * Gets the red component.
230
+ */
231
+ get r() {
232
+ return this._r;
233
+ }
234
+ /**
235
+ * Sets the red component and updates the uint32 representation.
236
+ * @param value - The new red component (0-255).
237
+ */
238
+ set r(t) {
239
+ this._r = t, this.updateUint();
240
+ }
241
+ /**
242
+ * Gets the green component.
243
+ */
244
+ get g() {
245
+ return this._g;
246
+ }
247
+ /**
248
+ * Sets the green component and updates the uint32 representation.
249
+ * @param value - The new green component (0-255).
250
+ */
251
+ set g(t) {
252
+ this._g = t, this.updateUint();
253
+ }
254
+ /**
255
+ * Gets the blue component.
256
+ */
257
+ get b() {
258
+ return this._b;
259
+ }
260
+ /**
261
+ * Sets the blue component and updates the uint32 representation.
262
+ * @param value - The new blue component (0-255).
263
+ */
264
+ set b(t) {
265
+ this._b = t, this.updateUint();
266
+ }
267
+ /**
268
+ * Gets the alpha component.
269
+ */
270
+ get a() {
271
+ return this._a;
272
+ }
273
+ /**
274
+ * Sets the alpha component and updates the uint32 representation.
275
+ * @param value - The new alpha component (0-255).
276
+ */
277
+ set a(t) {
278
+ this._a = t, this.updateUint();
279
+ }
280
+ /**
281
+ * Updates the uint32 representation of the color.
282
+ * @private
283
+ */
284
+ updateUint() {
285
+ this.uint32 = (this._a << 24 | this._b << 16 | this._g << 8 | this._r) >>> 0;
286
+ const t = d.clampByte(this._a), e = d.clampByte(this._r * t / 255), i = d.clampByte(this._g * t / 255), r = d.clampByte(this._b * t / 255);
287
+ this.premultipliedUint32 = (t << 24 | r << 16 | i << 8 | e) >>> 0;
288
+ }
289
+ reset() {
290
+ this._r = 0, this._g = 0, this._b = 0, this._a = 255, this.updateUint();
291
+ }
292
+ /**
293
+ * Sets the RGBA values of the color and updates the uint32 representation.
294
+ * @param r - The red component (0-255).
295
+ * @param g - The green component (0-255).
296
+ * @param b - The blue component (0-255).
297
+ * @param a - The alpha component (0-255).
298
+ */
299
+ setRGBA(t, e, i, r) {
300
+ this.r = t, this.g = e, this.b = i, this.a = r, this.updateUint();
301
+ }
302
+ setHSL(t, e, i) {
303
+ t = (t % 360 + 360) % 360, e = Math.max(0, Math.min(100, e)) / 100, i = Math.max(0, Math.min(100, i)) / 100;
304
+ const r = (1 - Math.abs(2 * i - 1)) * e, n = r * (1 - Math.abs(t / 60 % 2 - 1)), a = i - r / 2;
305
+ let o = 0, h = 0, u = 0;
306
+ return t < 60 ? [o, h, u] = [r, n, 0] : t < 120 ? [o, h, u] = [n, r, 0] : t < 180 ? [o, h, u] = [0, r, n] : t < 240 ? [o, h, u] = [0, n, r] : t < 300 ? [o, h, u] = [n, 0, r] : [o, h, u] = [r, 0, n], this.setRGBA((o + a) * 255, (h + a) * 255, (u + a) * 255, 255), this;
307
+ }
308
+ toHex() {
309
+ const t = (e) => Math.max(0, Math.min(255, Math.round(e))).toString(16).padStart(2, "0");
310
+ return `#${t(this._r)}${t(this._g)}${t(this._b)}${t(this._a)}`;
311
+ }
312
+ /**
313
+ * Copies the RGBA values from another color.
314
+ * @param color - The color to copy from.
315
+ */
316
+ copy(t) {
317
+ this.setRGBA(t.r, t.g, t.b, t.a);
318
+ }
319
+ /**
320
+ * Clone the current color
321
+ * @returns A new `Color` instance with the same RGBA values.
322
+ */
323
+ clone() {
324
+ return new d(this._r, this._g, this._b, this._a);
325
+ }
326
+ /**
327
+ * Converts the color to a hexadecimal string representation (e.g., '#RRGGBBAA').
328
+ * @param includeAlpha - Whether to include the alpha channel in the hex string.
329
+ * @returns The hexadecimal string representation of the color.
330
+ */
331
+ toHexString(t = !0) {
332
+ const e = this.r.toString(16).padStart(2, "0"), i = this.g.toString(16).padStart(2, "0"), r = this.b.toString(16).padStart(2, "0"), n = this.a.toString(16).padStart(2, "0");
333
+ return `#${e}${i}${r}${t ? n : ""}`;
334
+ }
335
+ /**
336
+ * Checks if the current color is equal to another color.
337
+ * @param color - The color to compare with.
338
+ * @returns True if the colors are equal, otherwise false.
339
+ */
340
+ equal(t) {
341
+ return t.r === this.r && t.g === this.g && t.b === this.b && t.a === this.a;
342
+ }
343
+ equals(t) {
344
+ return this.equal(t);
345
+ }
346
+ /**
347
+ * Creates a Color instance from normalized float components (0–1 range).
348
+ * Use this instead of `new Color()` when working with shader-style 0.0–1.0 values.
349
+ * @param r - Red channel (0.0–1.0)
350
+ * @param g - Green channel (0.0–1.0)
351
+ * @param b - Blue channel (0.0–1.0)
352
+ * @param a - Alpha channel (0.0–1.0), defaults to 1.0
353
+ * @returns A new Color instance with components scaled to 0–255.
354
+ * @example
355
+ * Color.fromNorm(0.9, 0.87, 0.55, 0.1) // moon glow
356
+ */
357
+ static FromHSL(t, e, i) {
358
+ return new d(0, 0, 0).setHSL(t, e, i);
359
+ }
360
+ static FromRGB(t, e, i) {
361
+ return new d(t, e, i);
362
+ }
363
+ static fromNorm(t, e, i, r = 1) {
364
+ return new d(
365
+ Math.round(t * 255),
366
+ Math.round(e * 255),
367
+ Math.round(i * 255),
368
+ Math.round(r * 255)
369
+ );
370
+ }
371
+ /**
372
+ * Creates a Color instance from a hexadecimal color string.
373
+ * @param hexString - The hexadecimal color string, e.g., '#RRGGBB' or '#RRGGBBAA'.
374
+ * @returns A new Color instance.
375
+ */
376
+ static fromHex(t) {
377
+ t.startsWith("#") && (t = t.slice(1));
378
+ const e = parseInt(t.slice(0, 2), 16), i = parseInt(t.slice(2, 4), 16), r = parseInt(t.slice(4, 6), 16);
379
+ let n = 255;
380
+ return t.length >= 8 && (n = parseInt(t.slice(6, 8), 16)), new d(e, i, r, n);
381
+ }
382
+ /**
383
+ * Adds the components of another color to this color, clamping the result to 255.
384
+ * @param color - The color to add.
385
+ * @returns A new Color instance with the result of the addition.
386
+ */
387
+ add(t) {
388
+ return new d(
389
+ Math.min(this.r + t.r, 255),
390
+ Math.min(this.g + t.g, 255),
391
+ Math.min(this.b + t.b, 255),
392
+ Math.min(this.a + t.a, 255)
393
+ );
394
+ }
395
+ /**
396
+ * Subtracts the components of another color from this color, clamping the result to 0.
397
+ * @param color - The color to subtract.
398
+ * @returns A new Color instance with the result of the subtraction.
399
+ */
400
+ subtract(t) {
401
+ return new d(
402
+ Math.max(0, this.r - t.r),
403
+ // Clamp to 0
404
+ Math.max(0, this.g - t.g),
405
+ // Clamp to 0
406
+ Math.max(0, this.b - t.b),
407
+ // Clamp to 0
408
+ Math.max(0, this.a - t.a)
409
+ // Clamp to 0
410
+ );
411
+ }
412
+ divide(t) {
413
+ return t instanceof d ? new d(
414
+ this.r / t.r,
415
+ this.g / t.g,
416
+ this.b / t.b,
417
+ this.a / t.a
418
+ ) : new d(
419
+ this.r / t,
420
+ this.g / t,
421
+ this.b / t,
422
+ this.a / t
423
+ );
424
+ }
425
+ multiply(t) {
426
+ return t instanceof d ? new d(
427
+ this.r * t.r,
428
+ this.g * t.g,
429
+ this.b * t.b,
430
+ this.a * t.a
431
+ ) : new d(
432
+ this.r * t,
433
+ this.g * t,
434
+ this.b * t,
435
+ this.a * t
436
+ );
437
+ }
438
+ clamp() {
439
+ this.r = Math.max(0, Math.min(255, this.r)), this.g = Math.max(0, Math.min(255, this.g)), this.b = Math.max(0, Math.min(255, this.b)), this.a = Math.max(0, Math.min(255, this.a));
440
+ }
441
+ static Red = new d(255, 0, 0, 255);
442
+ static Green = new d(0, 255, 0, 255);
443
+ static Blue = new d(0, 0, 255, 255);
444
+ static Yellow = new d(255, 255, 0, 255);
445
+ static Purple = new d(128, 0, 128, 255);
446
+ static Orange = new d(255, 165, 0, 255);
447
+ static Pink = new d(255, 192, 203, 255);
448
+ static Gray = new d(128, 128, 128, 255);
449
+ static Brown = new d(139, 69, 19, 255);
450
+ static Cyan = new d(0, 255, 255, 255);
451
+ static Magenta = new d(255, 0, 255, 255);
452
+ static Lime = new d(192, 255, 0, 255);
453
+ static White = new d(255, 255, 255, 255);
454
+ static Black = new d(0, 0, 0, 255);
455
+ static Transparent = new d(0, 0, 0, 0);
456
+ static TRANSPARENT = new d(0, 0, 0, 0);
457
+ }
458
+ const p = 6;
459
+ class pt {
460
+ /** Total number of matrices currently allocated in the store. */
461
+ matrixCount = 0;
462
+ /** The dynamic buffer used to hold matrix data. */
463
+ buffer;
464
+ /** The raw floating-point data of all matrices. */
465
+ data;
466
+ /**
467
+ * Creates a new MatrixStore.
468
+ * @param capacity - The initial capacity of the matrix store (default is 10).
469
+ */
470
+ constructor(t = 10) {
471
+ this.buffer = new W(N.Float32), this.buffer.resize(t * p), this.data = this.buffer.getArray();
472
+ }
473
+ /**
474
+ * Allocates a new matrix and initializes it to the identity matrix.
475
+ * @returns The index of the newly allocated matrix.
476
+ */
477
+ alloc() {
478
+ const t = this.allocDirty();
479
+ return this.identity(t), t;
480
+ }
481
+ /**
482
+ * Allocates a new matrix without initializing its elements.
483
+ * @returns The index of the newly allocated matrix.
484
+ */
485
+ allocDirty() {
486
+ return this.buffer.resize(p) && (this.data = this.buffer.getArray()), this.buffer.usedElemNum += p, this.matrixCount++;
487
+ }
488
+ /**
489
+ * Resets the store, clearing all allocated matrices.
490
+ */
491
+ reset() {
492
+ this.matrixCount = 0, this.buffer.usedElemNum = 0;
493
+ }
494
+ /**
495
+ * Sets the matrix at the given index to the identity matrix.
496
+ * @param index - The index of the matrix to modify.
497
+ */
498
+ identity(t) {
499
+ const e = t * p, i = this.data;
500
+ i[e] = 1, i[e + 1] = 0, i[e + 2] = 0, i[e + 3] = 1, i[e + 4] = 0, i[e + 5] = 0;
501
+ }
502
+ /**
503
+ * Translates the matrix at the given index by x and y.
504
+ * @param index - The index of the matrix to modify.
505
+ * @param x - The x translation.
506
+ * @param y - The y translation.
507
+ */
508
+ translate(t, e, i) {
509
+ const r = t * p, n = this.data;
510
+ n[r + 4] = n[r] * e + n[r + 2] * i + n[r + 4], n[r + 5] = n[r + 1] * e + n[r + 3] * i + n[r + 5];
511
+ }
512
+ /**
513
+ * Scales the matrix at the given index by scaleX and scaleY.
514
+ * @param index - The index of the matrix to modify.
515
+ * @param scaleX - The scale factor along the x-axis.
516
+ * @param scaleY - The scale factor along the y-axis.
517
+ */
518
+ scale(t, e, i) {
519
+ const r = t * p, n = this.data;
520
+ n[r] *= e, n[r + 1] *= e, n[r + 2] *= i, n[r + 3] *= i;
521
+ }
522
+ /**
523
+ * Rotates the matrix at the given index by the specified radians.
524
+ * @param index - The index of the matrix to modify.
525
+ * @param radians - The rotation angle in radians.
526
+ */
527
+ rotate(t, e) {
528
+ const i = t * p, r = this.data, n = Math.cos(e), a = Math.sin(e), o = r[i], h = r[i + 1], u = r[i + 2], c = r[i + 3];
529
+ r[i] = o * n + u * a, r[i + 1] = h * n + c * a, r[i + 2] = o * -a + u * n, r[i + 3] = h * -a + c * n;
530
+ }
531
+ /**
532
+ * Rotates the matrix at the given index around a local offset point (pivot).
533
+ *
534
+ * This is equivalent to:
535
+ * 1. Translating by `(offsetX, offsetY)` to move the pivot to the origin.
536
+ * 2. Rotating by `radians`.
537
+ * 3. Translating back by `(-offsetX, -offsetY)`.
538
+ *
539
+ * Useful for rotating a sprite around a point other than its own origin,
540
+ * e.g. a character's limb rotating around its joint.
541
+ *
542
+ * @param index - The index of the matrix to modify.
543
+ * @param radians - The rotation angle in radians.
544
+ * @param offsetX - The x component of the pivot point in local space.
545
+ * @param offsetY - The y component of the pivot point in local space.
546
+ */
547
+ rotateWithOffset(t, e, i, r) {
548
+ this.translate(t, i, r), this.rotate(t, e), this.translate(t, -i, -r);
549
+ }
550
+ /**
551
+ * Copies the elements from the source matrix to the destination matrix.
552
+ * @param dst - The index of the destination matrix.
553
+ * @param src - The index of the source matrix.
554
+ */
555
+ copy(t, e) {
556
+ const i = t * p, r = e * p, n = this.data;
557
+ n[i] = n[r], n[i + 1] = n[r + 1], n[i + 2] = n[r + 2], n[i + 3] = n[r + 3], n[i + 4] = n[r + 4], n[i + 5] = n[r + 5];
558
+ }
559
+ /**
560
+ * Multiplies the destination matrix by the source matrix and stores the result in the destination.
561
+ * @param dst - The index of the destination matrix.
562
+ * @param src - The index of the source matrix.
563
+ */
564
+ multiply(t, e) {
565
+ this.multiplyOut(t, t, e);
566
+ }
567
+ /**
568
+ * Multiplies matrix A by matrix B and stores the result in the output matrix.
569
+ * @param out - The index of the output matrix.
570
+ * @param aIdx - The index of matrix A.
571
+ * @param bIdx - The index of matrix B.
572
+ */
573
+ multiplyOut(t, e, i) {
574
+ const r = t * p, n = e * p, a = i * p, o = this.data, h = o[n], u = o[n + 1], c = o[n + 2], m = o[n + 3], f = o[n + 4], v = o[n + 5], x = o[a], T = o[a + 1], E = o[a + 2], R = o[a + 3], _ = o[a + 4], A = o[a + 5];
575
+ o[r] = h * x + c * T, o[r + 1] = u * x + m * T, o[r + 2] = h * E + c * R, o[r + 3] = u * E + m * R, o[r + 4] = h * _ + c * A + f, o[r + 5] = u * _ + m * A + v;
576
+ }
577
+ /**
578
+ * Inverts the matrix at the given index. If the matrix is not invertible, it defaults to the identity matrix.
579
+ * @param index - The index of the matrix to invert.
580
+ */
581
+ invert(t) {
582
+ const e = t * p, i = this.data, r = i[e], n = i[e + 1], a = i[e + 2], o = i[e + 3], h = i[e + 4], u = i[e + 5];
583
+ let c = r * o - n * a;
584
+ if (!c) {
585
+ this.identity(t);
586
+ return;
587
+ }
588
+ c = 1 / c, i[e] = o * c, i[e + 1] = -n * c, i[e + 2] = -a * c, i[e + 3] = r * c, i[e + 4] = (a * u - o * h) * c, i[e + 5] = (n * h - r * u) * c;
589
+ }
590
+ /**
591
+ * Transforms a point by the matrix at the given index.
592
+ * @param index - The index of the transformation matrix.
593
+ * @param x - The x coordinate of the point.
594
+ * @param y - The y coordinate of the point.
595
+ * @returns The transformed point {x, y}.
596
+ */
597
+ transformPoint(t, e, i) {
598
+ const r = t * p, n = this.data;
599
+ return {
600
+ x: e * n[r] + i * n[r + 2] + n[r + 4],
601
+ y: e * n[r + 1] + i * n[r + 3] + n[r + 5]
602
+ };
603
+ }
604
+ /**
605
+ * Transforms a point from world coordinates to local coordinates using the matrix at the specified index.
606
+ * Useful for hit testing against objects placed in world space.
607
+ * @param index - The index of the world transformation matrix.
608
+ * @param x - World x coordinate.
609
+ * @param y - World y coordinate.
610
+ * @returns The transformed point in local coordinates.
611
+ */
612
+ worldToLocal(t, e, i) {
613
+ const r = this.allocDirty();
614
+ this.copy(r, t), this.invert(r);
615
+ const n = this.transformPoint(r, e, i);
616
+ return this.matrixCount--, this.buffer.usedElemNum -= p, n;
617
+ }
618
+ /**
619
+ * Transforms a point from local coordinates to world coordinates.
620
+ * @param index - The index of the local transformation matrix.
621
+ * @param x - Local x coordinate.
622
+ * @param y - Local y coordinate.
623
+ * @returns The transformed point in world coordinates.
624
+ */
625
+ localToWorld(t, e, i) {
626
+ return this.transformPoint(t, e, i);
627
+ }
628
+ /**
629
+ * Extracts the global position (translation) from the matrix at the given index.
630
+ * @param index - The index of the matrix.
631
+ * @returns An object containing `x` and `y` global coordinates.
632
+ */
633
+ getPosition(t) {
634
+ const e = t * p, i = this.data;
635
+ return { x: i[e + 4], y: i[e + 5] };
636
+ }
637
+ /**
638
+ * Extracts the global scale from the matrix at the given index.
639
+ * @param index - The index of the matrix.
640
+ * @returns An object containing `x` and `y` global scale factors.
641
+ */
642
+ getScale(t) {
643
+ const e = t * p, i = this.data, r = Math.sqrt(i[e] * i[e] + i[e + 1] * i[e + 1]), n = Math.sqrt(i[e + 2] * i[e + 2] + i[e + 3] * i[e + 3]);
644
+ return { x: r, y: n };
645
+ }
646
+ /**
647
+ * Extracts the global rotation (in radians) from the matrix at the given index.
648
+ * @param index - The index of the matrix.
649
+ * @returns The global rotation in radians.
650
+ */
651
+ getRotation(t) {
652
+ const e = t * p, i = this.data;
653
+ return Math.atan2(i[e + 1], i[e]);
654
+ }
655
+ /**
656
+ * Converts the matrix at the given index to a CSS matrix string.
657
+ * @param index - The index of the matrix.
658
+ * @param scaleX - Extra scale applied to the a/c/tx components (e.g. to convert logic pixels to CSS pixels).
659
+ * @param scaleY - Extra scale applied to the b/d/ty components.
660
+ * @returns A CSS matrix string.
661
+ */
662
+ toCSSMatrix(t, e = 1, i = 1) {
663
+ const r = t * p, n = this.data;
664
+ return `matrix(${n[r] * e}, ${n[r + 1] * i}, ${n[r + 2] * e}, ${n[r + 3] * i}, ${n[r + 4] * e}, ${n[r + 5] * i})`;
665
+ }
666
+ /**
667
+ * Retrieves a copy of the 6 elements [a, b, c, d, tx, ty] for the matrix at the specified index.
668
+ * Note: This uses `slice` to return a new Float32Array instance, ensuring the original data remains safely isolated.
669
+ *
670
+ * @param index - The index of the matrix.
671
+ * @returns A new Float32Array containing the 6 elements of the matrix.
672
+ */
673
+ getMatrix(t) {
674
+ const e = t * p;
675
+ return this.data.slice(e, e + p);
676
+ }
677
+ /**
678
+ * Retrieves a direct reference (view) to the 6 elements [a, b, c, d, tx, ty] for the matrix at the specified index.
679
+ * Note: This uses `subarray`. Any modifications made to the returned array will directly affect the underlying MatrixStore data.
680
+ *
681
+ * @param index - The index of the matrix.
682
+ * @returns A Float32Array view pointing directly to the matrix's data in memory.
683
+ */
684
+ getMatrixRef(t) {
685
+ const e = t * p;
686
+ return this.data.subarray(e, e + p);
687
+ }
688
+ /**
689
+ * Overwrites the matrix at the specified index with the provided 6 elements [a, b, c, d, tx, ty].
690
+ *
691
+ * @param index - The index of the matrix to modify.
692
+ * @param f - An array (Float32Array or standard number array) containing the 6 new elements.
693
+ */
694
+ setMatrix(t, e) {
695
+ const i = t * p, r = this.data;
696
+ r[i] = e[0], r[i + 1] = e[1], r[i + 2] = e[2], r[i + 3] = e[3], r[i + 4] = e[4], r[i + 5] = e[5];
697
+ }
698
+ }
699
+ class gt {
700
+ /** The underlying store for matrices. */
701
+ matrix = new pt();
702
+ /** The current step or depth in the transformation hierarchy. */
703
+ step = 0;
704
+ /** Internal stack maintaining structural information. */
705
+ stack = new W(N.Uint32);
706
+ /** Records the world matrices generated at each step. */
707
+ stepWorldM = new W(N.Uint32);
708
+ /** Records actions (push/pop) taken during the traversal. */
709
+ stepAction = new W(N.Uint32);
710
+ /** Records the parent world matrix for each step (used by updateMatrix). */
711
+ stepParentM = new W(N.Uint32);
712
+ /** Buffer used during hierarchy traversal updates. */
713
+ parentStack = new W(N.Uint32);
714
+ /** The index of the current local matrix in the matrix store. */
715
+ curLocalM = -1;
716
+ /** The index of the current world matrix in the matrix store. */
717
+ curWorldM = -1;
718
+ rapid;
719
+ /**
720
+ * Creates a new MatrixStack and initializes its state.
721
+ */
722
+ constructor(t) {
723
+ this.reset(), this.rapid = t;
724
+ }
725
+ /**
726
+ * Saves the current matrix state and pushes it onto the stack.
727
+ * Equivalent to context.save().
728
+ * @returns The current step counter before saving.
729
+ */
730
+ save() {
731
+ this.stack.push(this.curWorldM);
732
+ const t = this.curWorldM;
733
+ return this.step++, this.curLocalM = this.matrix.alloc(), this.curWorldM = this.matrix.allocDirty(), this.stepWorldM.push(this.curWorldM), this.stepParentM.push(t), this.matrix.copy(this.curWorldM, t), this.stepAction.push(1), { world: this.curWorldM, local: this.curLocalM, step: this.step };
734
+ }
735
+ /**
736
+ * Restores the matrix state from the top of the stack.
737
+ * Equivalent to context.restore().
738
+ */
739
+ restore() {
740
+ this.stack.length != 0 && (this.curWorldM = this.stack.pop(), this.curLocalM = this.curWorldM - 1, this.stepAction.push(0));
741
+ }
742
+ /**
743
+ * Evaluates and updates matrices from the given step. Used primarily for deferred transformation.
744
+ * @param step - The initial step to update matrices from.
745
+ */
746
+ updateMatrix(t) {
747
+ const e = (typeof t == "number" ? t : t.step) - 1, i = this.stepParentM.get(e);
748
+ let r = 0, n = e;
749
+ const a = this.parentStack;
750
+ for (a.reset(), a.push(i); n < this.stepAction.length; ) {
751
+ if (this.stepAction.get(n) === 1) {
752
+ const h = this.stepWorldM.get(n), u = h - 1, c = a.top();
753
+ this.matrix.multiplyOut(h, c, u), a.push(h), r++;
754
+ } else if (a.pop(), r--, r === 0)
755
+ break;
756
+ n++;
757
+ }
758
+ }
759
+ /**
760
+ * Translates the current local and world matrices by x and y.
761
+ * @param x - Translation along the x-axis.
762
+ * @param y - Translation along the y-axis.
763
+ */
764
+ translate(t, e) {
765
+ this.matrix.translate(this.curLocalM, t, e), this.matrix.translate(this.curWorldM, t, e);
766
+ }
767
+ /**
768
+ * Scales the current local and world matrices by scaleX and scaleY.
769
+ * @param scaleX - Scaling factor along the x-axis.
770
+ * @param scaleY - Scaling factor along the y-axis.
771
+ */
772
+ scale(t, e = t) {
773
+ this.matrix.scale(this.curLocalM, t, e), this.matrix.scale(this.curWorldM, t, e);
774
+ }
775
+ /**
776
+ * Rotates the current local and world matrices by the given radians.
777
+ * @param radians - The rotation angle in radians.
778
+ */
779
+ rotate(t) {
780
+ this.matrix.rotate(this.curLocalM, t), this.matrix.rotate(this.curWorldM, t);
781
+ }
782
+ /**
783
+ * Rotates the current local and world matrices around a pivot point
784
+ * that is offset from the matrix origin by `(offsetX, offsetY)`.
785
+ *
786
+ * This is a convenience wrapper around the common translate → rotate → translate-back
787
+ * pattern, avoiding manual coordinate juggling at the call site.
788
+ *
789
+ * @example
790
+ * ```ts
791
+ * // Rotate a 64×64 sprite around its centre
792
+ * stack.rotateWithOffset(angle, 32, 32);
793
+ * ```
794
+ *
795
+ * @param radians - The rotation angle in radians.
796
+ * @param offsetX - The x component of the pivot point in local space.
797
+ * @param offsetY - The y component of the pivot point in local space.
798
+ */
799
+ rotateWithOffset(t, e, i) {
800
+ this.matrix.rotateWithOffset(this.curLocalM, t, e, i), this.matrix.rotateWithOffset(this.curWorldM, t, e, i);
801
+ }
802
+ /**
803
+ * Sets the current local and world matrices to the identity matrix.
804
+ */
805
+ identity() {
806
+ this.matrix.identity(this.curLocalM), this.matrix.identity(this.curWorldM);
807
+ }
808
+ /**
809
+ * Resets the entire stack state context, clearing matrices and step actions.
810
+ */
811
+ reset() {
812
+ this.matrix.reset(), this.stack.reset(), this.stepAction.reset(), this.stepWorldM.reset(), this.stepParentM.reset(), this.step = 0, this.curLocalM = this.matrix.alloc(), this.curWorldM = this.matrix.alloc();
813
+ }
814
+ /**
815
+ * Transforms a point from local coordinates to world coordinates.
816
+ * Uses the current world matrix to apply the transformation.
817
+ * @param x - Local x coordinate.
818
+ * @param y - Local y coordinate.
819
+ * @returns The transformed point in world coordinates.
820
+ */
821
+ localToWorld(t, e) {
822
+ return this.matrix.localToWorld(this.curWorldM, t, e);
823
+ }
824
+ /**
825
+ * Transforms a point from world coordinates to local coordinates.
826
+ * Useful for hit testing against objects placed in world space.
827
+ * @param x - World x coordinate.
828
+ * @param y - World y coordinate.
829
+ * @returns The transformed point in local coordinates.
830
+ */
831
+ worldToLocal(t, e) {
832
+ return this.matrix.worldToLocal(this.curWorldM, t, e);
833
+ }
834
+ /**
835
+ * Extracts the global position (translation) from the current world matrix.
836
+ * @returns An object containing `x` and `y` global coordinates.
837
+ */
838
+ getGlobalPosition() {
839
+ return this.matrix.getPosition(this.curWorldM);
840
+ }
841
+ /**
842
+ * Extracts the global scale from the current world matrix.
843
+ * @returns An object containing `x` and `y` global scale factors.
844
+ */
845
+ getGlobalScale() {
846
+ return this.matrix.getScale(this.curWorldM);
847
+ }
848
+ /**
849
+ * Extracts the global rotation (in radians) from the current world matrix.
850
+ * @returns The global rotation in radians.
851
+ */
852
+ getGlobalRotation() {
853
+ return this.matrix.getRotation(this.curWorldM);
854
+ }
855
+ toCSSMatrix() {
856
+ return this.matrix.toCSSMatrix(this.curWorldM);
857
+ }
858
+ /**
859
+ * Returns the current world matrix as Float32Array [a, b, c, d, tx, ty].
860
+ */
861
+ getTransform() {
862
+ const t = this.curWorldM * p;
863
+ return this.matrix.data.slice(t, t + p);
864
+ }
865
+ /**
866
+ * Directly overwrites the current world matrix with the given 6-element 2D transform.
867
+ */
868
+ setTransform(t) {
869
+ const e = this.curWorldM * p, i = this.matrix.data;
870
+ i[e] = t[0], i[e + 1] = t[1], i[e + 2] = t[2], i[e + 3] = t[3], i[e + 4] = t[4], i[e + 5] = t[5];
871
+ }
872
+ transformPoint(t, e) {
873
+ return this.matrix.transformPoint(this.curLocalM, t, e);
874
+ }
875
+ /**
876
+ * Applies a transform options object to the current matrix state.
877
+ * Optionally saves the matrix first (saveTransform defaults to true).
878
+ */
879
+ applyTransform(t, e = 0, i = 0) {
880
+ (t.saveTransform ?? !0) && this.save(), t.afterSave?.();
881
+ const r = t.x ?? 0, n = t.y ?? 0;
882
+ (r || n) && this.translate(r, n), t.position && this.translate(t.position.x, t.position.y), t.rotation && this.rotate(t.rotation);
883
+ const a = t.scale, o = typeof a == "number";
884
+ a && this.scale(
885
+ o ? a : a.x,
886
+ o ? a : a.y
887
+ );
888
+ let h = t.offsetX ?? 0, u = t.offsetY ?? 0;
889
+ t.offset && (h += t.offset.x, u += t.offset.y);
890
+ const c = t.origin;
891
+ c !== void 0 && (typeof c == "number" ? (h -= c * e, u -= c * i) : (h -= c.x * e, u -= c.y * i)), this.translate(h, u);
892
+ }
893
+ }
894
+ var H = /* @__PURE__ */ ((s) => (s[s.REPEAT = 0] = "REPEAT", s[s.CLAMP = 1] = "CLAMP", s[s.MIRRORED_REPEAT = 2] = "MIRRORED_REPEAT", s))(H || {});
895
+ class vt {
896
+ render;
897
+ cache = /* @__PURE__ */ new Map();
898
+ constructor(t) {
899
+ this.render = t;
900
+ }
901
+ /**
902
+ * Asynchronously loads a texture from a URL.
903
+ * @param url - The image URL to load.
904
+ * @param options - Optional configuration for the texture.
905
+ * @returns A promise that resolves to the loaded Texture.
906
+ */
907
+ async load(t, e) {
908
+ let i = this.cache.get(t);
909
+ if (i)
910
+ return new C(i);
911
+ try {
912
+ const r = await this._fetchImage(t);
913
+ return i = D.fromSource(this.render, r, e), i.uid = t, this.cache.set(t, i), new C(i);
914
+ } catch (r) {
915
+ throw console.error(`[TextureManager] Failed to load: ${t}`, r), r;
916
+ }
917
+ }
918
+ /**
919
+ * Creates a texture synchronously from an existing HTML element (Image, Canvas, Video).
920
+ * @param source - The source image element.
921
+ * @param options - Optional configuration for the texture.
922
+ * @returns The newly created Texture.
923
+ */
924
+ create(t, e) {
925
+ if (e?.key && this.cache.has(e.key))
926
+ return new C(this.cache.get(e.key));
927
+ const i = D.fromSource(this.render, t, e);
928
+ return e?.key && (i.uid = e.key, this.cache.set(e.key, i)), new C(i);
929
+ }
930
+ /**
931
+ * Creates a generic Render Texture (FrameBuffer).
932
+ * @param width - The width of the render texture.
933
+ * @param height - The height of the render texture.
934
+ * @param options - Optional configuration for the texture.
935
+ * @returns The newly created RenderTexture.
936
+ */
937
+ createRenderTexture(t) {
938
+ return new Tt(this.render, t);
939
+ }
940
+ /**
941
+ * Creates a TextTexture for rendering text.
942
+ * @param options - Optional configuration for the texture.
943
+ * @returns The newly created TextTexture.
944
+ */
945
+ createTextTexture(t) {
946
+ return new Et(this.render, t);
947
+ }
948
+ /**
949
+ * Destroys a texture and potentially removes its BaseTexture from the cache.
950
+ * If a Texture instance is provided, it decrements the reference count of the BaseTexture.
951
+ * The BaseTexture will only be destroyed if its reference count drops to 0, or if `force` is true.
952
+ * @param textureOrUrl - The texture instance, base texture, or cache key (URL) to destroy.
953
+ * @param force - If true, destroys the underlying BaseTexture immediately regardless of reference count.
954
+ */
955
+ destroy(t, e = !1) {
956
+ let i, r;
957
+ typeof t == "string" ? (r = t, i = this.cache.get(r)) : t instanceof C ? (i = t.base, r = i?.uid, t.destroy()) : (i = t, r = i.uid), i && (i.refCount <= 0 || e) && (i.destroy(this.render.gl), r && this.cache.delete(r));
958
+ }
959
+ /**
960
+ * Destroys all cached textures and clears the cache, ignoring reference counts.
961
+ */
962
+ destroyAll() {
963
+ for (const t of this.cache.values())
964
+ t.destroy(this.render.gl);
965
+ this.cache.clear();
966
+ }
967
+ _fetchImage(t) {
968
+ return new Promise((e, i) => {
969
+ const r = new Image();
970
+ r.crossOrigin = "anonymous", r.onload = () => e(r), r.onerror = () => i(new Error(`Failed to load image resource: ${t}`)), r.src = t;
971
+ });
972
+ }
973
+ }
974
+ class D {
975
+ glTexture = null;
976
+ width;
977
+ height;
978
+ uid;
979
+ refCount = 0;
980
+ constructor(t, e, i) {
981
+ this.glTexture = t, this.width = e, this.height = i;
982
+ }
983
+ /**
984
+ * Creates a BaseTexture from an image source.
985
+ * @param render - The Rapid renderer instance.
986
+ * @param source - The image source.
987
+ * @param options - Optional configuration.
988
+ * @returns The created BaseTexture.
989
+ */
990
+ static fromSource(t, e, i = {}) {
991
+ const r = X(t, e, i);
992
+ return new D(r, e.width, e.height);
993
+ }
994
+ /**
995
+ * Updates the content of the existing texture (e.g. for Video or dynamic Canvas).
996
+ * Uses texSubImage2D if dimensions haven't changed for better performance.
997
+ * @param gl - The WebGL rendering context.
998
+ * @param source - The new image source.
999
+ */
1000
+ updateSource(t, e, i = {}) {
1001
+ this.glTexture && (t.bindTexture(t.TEXTURE_2D, this.glTexture), i.textureFilter !== void 0 && Z(t, i.textureFilter), i.wrap !== void 0 && q(t, i.wrap), t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL, 1), t.pixelStorei(t.UNPACK_ALIGNMENT, 1), i.premultipliedAlpha !== void 0 && t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL, i.premultipliedAlpha), this.width === e.width && this.height === e.height ? t.texSubImage2D(t.TEXTURE_2D, 0, 0, 0, t.RGBA, t.UNSIGNED_BYTE, e) : (t.texImage2D(t.TEXTURE_2D, 0, t.RGBA, t.RGBA, t.UNSIGNED_BYTE, e), this.width = e.width, this.height = e.height), i.premultipliedAlpha !== void 0 && t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1));
1002
+ }
1003
+ /**
1004
+ * Dynamically updates the texture filtering mode.
1005
+ */
1006
+ setFilterMode(t, e) {
1007
+ this.glTexture && (t.bindTexture(t.TEXTURE_2D, this.glTexture), Z(t, e), t.bindTexture(t.TEXTURE_2D, null));
1008
+ }
1009
+ /**
1010
+ * Dynamically updates the texture wrap mode.
1011
+ */
1012
+ setWrapMode(t, e) {
1013
+ this.glTexture && (t.bindTexture(t.TEXTURE_2D, this.glTexture), q(t, e), t.bindTexture(t.TEXTURE_2D, null));
1014
+ }
1015
+ /**
1016
+ * Destroys the WebGL texture resource.
1017
+ * @param gl - The WebGL rendering context.
1018
+ */
1019
+ destroy(t) {
1020
+ this.glTexture && (t.deleteTexture(this.glTexture), this.glTexture = null);
1021
+ }
1022
+ }
1023
+ class C {
1024
+ base;
1025
+ uvX = 0;
1026
+ uvY = 0;
1027
+ uvW = 1;
1028
+ uvH = 1;
1029
+ rawWidth = 0;
1030
+ rawHeight = 0;
1031
+ offsetX = 0;
1032
+ offsetY = 0;
1033
+ scale = 1;
1034
+ isAtlas = !1;
1035
+ flipY = !1;
1036
+ isRotated = !1;
1037
+ glTexture = null;
1038
+ get width() {
1039
+ return (this.isRotated ? this.rawHeight : this.rawWidth) * this.scale;
1040
+ }
1041
+ get height() {
1042
+ return (this.isRotated ? this.rawWidth : this.rawHeight) * this.scale;
1043
+ }
1044
+ // Stored pixel-space region (before UV conversion), used by getSubTexture()
1045
+ _px = 0;
1046
+ _py = 0;
1047
+ _pw = 0;
1048
+ _ph = 0;
1049
+ constructor(t) {
1050
+ t && this.setBase(t);
1051
+ }
1052
+ /**
1053
+ * Sets the base texture and increments its reference count.
1054
+ * @param base - The BaseTexture instance.
1055
+ * @returns The current Texture instance.
1056
+ */
1057
+ setBase(t) {
1058
+ return this.base === t ? this : (this.base && this.base.refCount--, this.base = t, this.base.refCount++, this.setRegion(0, 0, t.width, t.height), this.glTexture = t.glTexture, this);
1059
+ }
1060
+ /**
1061
+ * Sets the visible region (clipping) in pixels relative to the BaseTexture.
1062
+ * @param x - The x coordinate in pixels.
1063
+ * @param y - The y coordinate in pixels.
1064
+ * @param w - The width in pixels.
1065
+ * @param h - The height in pixels.
1066
+ * @returns The current Texture instance.
1067
+ */
1068
+ setRegion(t, e, i, r) {
1069
+ return this.base ? (this._px = t, this._py = e, this._pw = i, this._ph = r, this.isAtlas = t !== 0 || e !== 0 || i !== this.base.width || r !== this.base.height, this.uvX = t / this.base.width, this.uvY = e / this.base.height, this.uvW = this.uvX + i / this.base.width, this.uvH = this.uvY + r / this.base.height, this.rawWidth = i, this.rawHeight = r, this) : this;
1070
+ }
1071
+ /**
1072
+ * Creates a new Texture representing a sub-region of this Texture.
1073
+ * @param x - The x coordinate in pixels, relative to this texture's logical start.
1074
+ * @param y - The y coordinate in pixels, relative to this texture's logical start.
1075
+ * @param width - The width of the sub-texture in pixels.
1076
+ * @param height - The height of the sub-texture in pixels.
1077
+ * @returns A new Texture pointing to the sub-region.
1078
+ */
1079
+ getSubTexture(t, e, i, r) {
1080
+ if (!this.base) return new C();
1081
+ const n = this.clone(), a = this._px, o = this._py;
1082
+ return n.setRegion(a + t, o + e, i, r), n;
1083
+ }
1084
+ /**
1085
+ * Creates a shallow copy of this Texture, sharing the same BaseTexture.
1086
+ * @returns A new Texture instance.
1087
+ */
1088
+ clone(t) {
1089
+ const e = t ?? new C(this.base);
1090
+ return t && this.base && t.setBase(this.base), e.scale = this.scale, e.uvX = this.uvX, e.uvY = this.uvY, e.uvW = this.uvW, e.uvH = this.uvH, e.rawWidth = this.rawWidth, e.rawHeight = this.rawHeight, e.offsetX = this.offsetX, e.offsetY = this.offsetY, e.flipY = this.flipY, e.isRotated = this.isRotated, e.isAtlas = this.isAtlas, e._px = this._px, e._py = this._py, e._pw = this._pw, e._ph = this._ph, e;
1091
+ }
1092
+ /**
1093
+ * Utility to split this texture into a grid of sprite textures.
1094
+ * Sub-textures will respect the current UV offsets.
1095
+ * @param cellWidth - The width of each cell in pixels.
1096
+ * @param cellHeight - The height of each cell in pixels.
1097
+ * @param cols - Optional number of columns. Truncates based on texture width if omitted.
1098
+ * @param rows - Optional number of rows. Truncates based on texture height if omitted.
1099
+ * @param gap - Optional pixel gap between cells.
1100
+ * @returns An array of split Textures.
1101
+ */
1102
+ splitGrid(t, e, i, r, n = 0) {
1103
+ if (!this.base) return [];
1104
+ const a = [], o = this.rawWidth, h = this.rawHeight, u = Math.max(0, n), c = t + u, m = e + u, f = i ?? Math.floor((o + u) / c), v = r ?? Math.floor((h + u) / m);
1105
+ for (let x = 0; x < v; x++)
1106
+ for (let T = 0; T < f; T++)
1107
+ a.push(this.getSubTexture(T * c, x * m, t, e));
1108
+ return a;
1109
+ }
1110
+ /**
1111
+ * Creates a Texture directly from an image source, bypassing the TextureManager.
1112
+ * @param source - The image element, canvas, video, or bitmap.
1113
+ * @param options - Optional antialias and wrap mode settings.
1114
+ */
1115
+ static fromImageSource(t, e, i = {}) {
1116
+ const r = X(t, e, i);
1117
+ return new C(new D(r, e.width, e.height));
1118
+ }
1119
+ /**
1120
+ * Marks this Texture as destroyed, decrementing the BaseTexture reference count.
1121
+ */
1122
+ destroy() {
1123
+ this.base && (this.base.refCount--, this.base = void 0), this.glTexture = null;
1124
+ }
1125
+ }
1126
+ class Tt extends C {
1127
+ framebuffer;
1128
+ renderbuffer;
1129
+ gl;
1130
+ flipY = !0;
1131
+ clearColor;
1132
+ /** Actual GPU-allocated dimensions — grow-only, never shrink */
1133
+ _allocW = 0;
1134
+ _allocH = 0;
1135
+ constructor(t, e) {
1136
+ super(), this.gl = t.gl, this.clearColor = e.clearColor ?? d.Black;
1137
+ const i = Math.max(Math.round(e.width), 1), r = Math.max(Math.round(e.height), 1), a = X(t, { width: i, height: r }, { ...e, onlySize: !0 });
1138
+ if (this.setBase(new D(a, i, r)), this.framebuffer = this.gl.createFramebuffer(), this.renderbuffer = this.gl.createRenderbuffer(), !this.framebuffer || !this.renderbuffer)
1139
+ throw new Error("Failed to create Framebuffer or Renderbuffer");
1140
+ this.resize(i, r, !0);
1141
+ }
1142
+ /**
1143
+ * Resizes the render texture using a grow-only GPU allocation strategy.
1144
+ *
1145
+ * - GPU memory is only reallocated when the new size **exceeds** the current allocation.
1146
+ * - Shrinking only updates the logical dimensions and UV sub-region — no GPU work.
1147
+ *
1148
+ * This makes it safe to call every frame with varying sizes (e.g. inside applyFilters).
1149
+ *
1150
+ * @param width - New logical width.
1151
+ * @param height - New logical height.
1152
+ * @param force - Force full GPU reallocation regardless of current allocation size.
1153
+ */
1154
+ resize(t, e, i = !1) {
1155
+ t = Math.max(Math.round(t), 1), e = Math.max(Math.round(e), 1);
1156
+ const r = this.rawWidth === t && this.rawHeight === e;
1157
+ if (!i && r) return;
1158
+ const n = this.gl;
1159
+ (i || t > this._allocW || e > this._allocH) && this.base && (this._allocW = Math.max(this._allocW, t), this._allocH = Math.max(this._allocH, e), this.base.width = this._allocW, this.base.height = this._allocH, n.bindTexture(n.TEXTURE_2D, this.base.glTexture), n.texImage2D(n.TEXTURE_2D, 0, n.RGBA, this._allocW, this._allocH, 0, n.RGBA, n.UNSIGNED_BYTE, null), n.bindRenderbuffer(n.RENDERBUFFER, this.renderbuffer), n.renderbufferStorage(n.RENDERBUFFER, n.STENCIL_INDEX8, this._allocW, this._allocH), n.bindFramebuffer(n.FRAMEBUFFER, this.framebuffer), n.framebufferTexture2D(n.FRAMEBUFFER, n.COLOR_ATTACHMENT0, n.TEXTURE_2D, this.base.glTexture, 0), n.framebufferRenderbuffer(n.FRAMEBUFFER, n.STENCIL_ATTACHMENT, n.RENDERBUFFER, this.renderbuffer), n.bindFramebuffer(n.FRAMEBUFFER, null), n.bindRenderbuffer(n.RENDERBUFFER, null), n.bindTexture(n.TEXTURE_2D, null)), this.rawWidth = t, this.rawHeight = e, this.uvX = 0, this.uvW = t / this._allocW, this.uvY = 0, this.uvH = e / this._allocH;
1160
+ }
1161
+ /**
1162
+ * Activates this texture as the current render target.
1163
+ * @param clear - Whether to clear the render target after activation.
1164
+ */
1165
+ activate(t = !1) {
1166
+ if (!this.framebuffer) return;
1167
+ const e = this.gl;
1168
+ e.bindFramebuffer(e.FRAMEBUFFER, this.framebuffer), e.viewport(0, 0, this.rawWidth, this.rawHeight), t && this.clear();
1169
+ }
1170
+ clear(t) {
1171
+ if (!this.framebuffer) return;
1172
+ const e = this.gl, i = t ?? this.clearColor;
1173
+ i && (i.setClearColor(e), e.clear(e.COLOR_BUFFER_BIT | e.STENCIL_BUFFER_BIT));
1174
+ }
1175
+ /**
1176
+ * Deactivates this texture, returning rendering to the default frame buffer.
1177
+ */
1178
+ deactivate() {
1179
+ const t = this.gl;
1180
+ t.bindFramebuffer(t.FRAMEBUFFER, null);
1181
+ }
1182
+ /**
1183
+ * Get raw pixels from the render texture.
1184
+ * @param x - X coordinate in logical pixel space (top-left origin).
1185
+ * @param y - Y coordinate in logical pixel space (top-left origin).
1186
+ * @param width - Width of the region to read.
1187
+ * @param height - Height of the region to read.
1188
+ */
1189
+ GetPixels(t = 0, e = 0, i = this.rawWidth, r = this.rawHeight) {
1190
+ if (!this.framebuffer) return new Uint8Array(0);
1191
+ const n = Math.floor(t), a = Math.floor(e), o = Math.floor(i), h = Math.floor(r);
1192
+ if (o <= 0 || h <= 0) return new Uint8Array(0);
1193
+ if (n < 0 || a < 0 || n >= this.rawWidth || a >= this.rawHeight)
1194
+ return new Uint8Array(0);
1195
+ const u = Math.min(o, this.rawWidth - n), c = Math.min(h, this.rawHeight - a), m = this.rawHeight - a - c, f = this.gl, v = f.getParameter(f.FRAMEBUFFER_BINDING);
1196
+ f.bindFramebuffer(f.FRAMEBUFFER, this.framebuffer);
1197
+ const x = new Uint8Array(u * c * 4);
1198
+ return f.readPixels(n, m, u, c, f.RGBA, f.UNSIGNED_BYTE, x), f.bindFramebuffer(f.FRAMEBUFFER, v), x;
1199
+ }
1200
+ /**
1201
+ * Get the color at a pixel in the render texture.
1202
+ * @param x - X coordinate in logical pixel space (top-left origin).
1203
+ * @param y - Y coordinate in logical pixel space (top-left origin).
1204
+ */
1205
+ GetColorAt(t, e) {
1206
+ const i = this.GetPixels(t, e, 1, 1);
1207
+ return i.length < 4 ? new d(0, 0, 0, 0) : new d(i[0], i[1], i[2], i[3]);
1208
+ }
1209
+ /**
1210
+ * Destroys the framebuffer, renderbuffer, and base texture.
1211
+ */
1212
+ destroy() {
1213
+ super.destroy(), this.framebuffer && this.gl.deleteFramebuffer(this.framebuffer), this.renderbuffer && this.gl.deleteRenderbuffer(this.renderbuffer), this.base?.destroy(this.gl);
1214
+ }
1215
+ }
1216
+ const yt = {
1217
+ fontFamily: "Arial",
1218
+ fontSize: 24,
1219
+ fontWeight: "normal",
1220
+ fill: "#000000",
1221
+ strokeThickness: 1,
1222
+ align: "left",
1223
+ baseline: "top"
1224
+ };
1225
+ class Et extends C {
1226
+ canvas;
1227
+ ctx;
1228
+ render;
1229
+ _text;
1230
+ _style;
1231
+ options;
1232
+ flipY = !0;
1233
+ constructor(t, e) {
1234
+ super(), this.render = t, this._style = { ...yt, ...e }, this._text = e?.text ?? "", this.options = { premultipliedAlpha: t.premultipliedAlpha, ...e }, this.scale = 1 / t.dpr, this.canvas = document.createElement("canvas");
1235
+ const i = this.canvas.getContext("2d", { willReadFrequently: !0 });
1236
+ if (!i) throw new Error("Failed to get 2d context for TextTexture");
1237
+ this.ctx = i, this.canvas.width = 1, this.canvas.height = 1;
1238
+ const r = X(t, this.canvas, this.options);
1239
+ this.setBase(new D(r, 1, 1)), this.update();
1240
+ }
1241
+ get text() {
1242
+ return this._text;
1243
+ }
1244
+ /**
1245
+ * Set new text and update the texture
1246
+ */
1247
+ set text(t) {
1248
+ this._text !== t && (this._text = t, this.update());
1249
+ }
1250
+ get style() {
1251
+ return this._style;
1252
+ }
1253
+ /**
1254
+ * Set new style partially and update the texture
1255
+ */
1256
+ set style(t) {
1257
+ this._style = { ...this._style, ...t }, this.update();
1258
+ }
1259
+ updateOffset(t, e) {
1260
+ switch (this._style.align) {
1261
+ case "center":
1262
+ this.offsetX = -t / 2;
1263
+ break;
1264
+ case "right":
1265
+ this.offsetX = -t;
1266
+ break;
1267
+ default:
1268
+ this.offsetX = 0;
1269
+ break;
1270
+ }
1271
+ switch (this._style.baseline) {
1272
+ case "middle":
1273
+ this.offsetY = -e / 2;
1274
+ break;
1275
+ case "bottom":
1276
+ this.offsetY = -e;
1277
+ break;
1278
+ default:
1279
+ this.offsetY = 0;
1280
+ break;
1281
+ }
1282
+ }
1283
+ /**
1284
+ * Updates the internal canvas and uploads it to WebGL
1285
+ */
1286
+ update() {
1287
+ const t = this.ctx, e = this._style.fontSize, i = this._style.fontWeight, r = this._style.fontFamily, n = `${i} ${e}px ${r}`, a = this.render.dpr;
1288
+ t.font = n;
1289
+ const o = this._text.split(`
1290
+ `);
1291
+ let h = 0, u = 0;
1292
+ t.textBaseline = "alphabetic";
1293
+ const c = t.measureText(o[0]), m = c.fontBoundingBoxAscent, f = c.fontBoundingBoxDescent;
1294
+ for (const A of o) {
1295
+ const g = t.measureText(A);
1296
+ g.width > h && (h = g.width), u += m + f;
1297
+ }
1298
+ u -= f;
1299
+ const v = (this._style.strokeThickness || 0) * 2, x = Math.ceil(h + v) || 1, T = Math.ceil(u + v) || 1, E = x * a, R = T * a;
1300
+ (this.canvas.width !== E || this.canvas.height !== R) && (this.canvas.width = E, this.canvas.height = R), t.setTransform(a, 0, 0, a, 0, 0), t.clearRect(0, 0, x, T), t.font = n, t.textBaseline = "top", t.textAlign = "left", this.updateOffset(x, T);
1301
+ let _ = v / 2;
1302
+ for (const A of o) {
1303
+ let g = v / 2;
1304
+ this._style.stroke && this._style.strokeThickness > 0 && (t.lineWidth = this._style.strokeThickness, t.strokeStyle = this._style.stroke, t.strokeText(A, g, _)), this._style.fill && (t.fillStyle = this._style.fill, t.fillText(A, g, _)), _ += m + f;
1305
+ }
1306
+ this.base?.updateSource(this.render.gl, this.canvas, this.options), this.setRegion(0, 0, this.canvas.width, this.canvas.height);
1307
+ }
1308
+ }
1309
+ const Rt = (s, t = !1, e = !0) => {
1310
+ const i = s.getContext("webgl2", { stencil: !0, antialias: t, premultipliedAlpha: e });
1311
+ if (!i)
1312
+ throw new Error("WebGL2 is not supported in this browser.");
1313
+ return i;
1314
+ }, et = (s, t, e) => {
1315
+ const i = s.createShader(e);
1316
+ if (!i)
1317
+ throw new Error("Unable to create webgl shader");
1318
+ if (s.shaderSource(i, t), s.compileShader(i), !s.getShaderParameter(i, s.COMPILE_STATUS)) {
1319
+ const n = s.getShaderInfoLog(i);
1320
+ throw console.error("Shader compilation failed:", n), new Error("Unable to compile shader: " + n + t);
1321
+ }
1322
+ return i;
1323
+ }, Mt = (s, t, e) => {
1324
+ var i = s.createProgram(), r = et(s, t, 35633), n = et(s, e, 35632);
1325
+ if (!i)
1326
+ throw new Error("Unable to create program shader");
1327
+ if (s.attachShader(i, r), s.attachShader(i, n), s.linkProgram(i), !s.getProgramParameter(i, s.LINK_STATUS)) {
1328
+ const o = s.getProgramInfoLog(i);
1329
+ throw new Error("Unable to link shader program: " + o);
1330
+ }
1331
+ return i;
1332
+ };
1333
+ function q(s, t) {
1334
+ const e = {
1335
+ [H.CLAMP]: s.CLAMP_TO_EDGE,
1336
+ [H.REPEAT]: s.REPEAT,
1337
+ [H.MIRRORED_REPEAT]: s.MIRRORED_REPEAT
1338
+ }[t] || s.CLAMP_TO_EDGE;
1339
+ s.texParameteri(s.TEXTURE_2D, s.TEXTURE_WRAP_S, e), s.texParameteri(s.TEXTURE_2D, s.TEXTURE_WRAP_T, e);
1340
+ }
1341
+ function Z(s, t) {
1342
+ const e = {
1343
+ [Q.LINEAR]: s.LINEAR,
1344
+ [Q.NEAREST]: s.NEAREST
1345
+ }[t] ?? s.NEAREST;
1346
+ s.texParameteri(s.TEXTURE_2D, s.TEXTURE_MIN_FILTER, e), s.texParameteri(s.TEXTURE_2D, s.TEXTURE_MAG_FILTER, e);
1347
+ }
1348
+ function X(s, t, e) {
1349
+ const i = s.gl, r = i.createTexture();
1350
+ if (!r)
1351
+ throw new Error("Unable to create WebGL texture");
1352
+ if (i.bindTexture(i.TEXTURE_2D, r), i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL, 0), i.pixelStorei(i.UNPACK_ALIGNMENT, 1), q(i, e.wrap ?? H.CLAMP), Z(i, e.textureFilter ?? s.textureFilter), e.onlySize) {
1353
+ const n = t;
1354
+ i.texImage2D(i.TEXTURE_2D, 0, i.RGBA, n.width, n.height, 0, i.RGBA, i.UNSIGNED_BYTE, null);
1355
+ } else {
1356
+ const n = e.premultipliedAlpha ?? s.premultipliedAlpha;
1357
+ n && i.pixelStorei(i.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !0), i.texImage2D(i.TEXTURE_2D, 0, i.RGBA, i.RGBA, i.UNSIGNED_BYTE, t), n && i.pixelStorei(i.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1);
1358
+ }
1359
+ return i.bindTexture(i.TEXTURE_2D, null), r;
1360
+ }
1361
+ function F(s, t) {
1362
+ if (s.includes("%TEXTURE_NUM%") && (s = s.replace("%TEXTURE_NUM%", t.toString())), s.includes("%GET_COLOR%")) {
1363
+ let e = "";
1364
+ for (let i = 0; i < t; i++)
1365
+ i == 0 ? e += `if(vTextureId == ${i})` : i == t - 1 ? e += "else" : e += `else if(vTextureId == ${i})`, e += `{return texture(uTextures[${i}], uv) * vColor;}`;
1366
+ s = s.replace("%GET_COLOR%", e);
1367
+ }
1368
+ return s;
1369
+ }
1370
+ const wt = 5126, it = 5121;
1371
+ function bt(s, t, e) {
1372
+ s.vertexAttribDivisor(t, e);
1373
+ }
1374
+ function At(s, t, e, i, r) {
1375
+ s.drawArraysInstanced(t, e, i, r);
1376
+ }
1377
+ let St = 0;
1378
+ class ct {
1379
+ program;
1380
+ attributeLoc = {};
1381
+ uniformLoc = {};
1382
+ isCustom = !1;
1383
+ /** Padding in pixels this shader needs beyond the sprite bounds (for outline/glow effects) */
1384
+ padding = 0;
1385
+ /** GLSL type for each uniform, parsed from source */
1386
+ uniformType = {};
1387
+ /** Whether the uniform is an array (e.g. sampler2D uTextures[8]) */
1388
+ uniformIsArray = {};
1389
+ gl;
1390
+ shaderId = St++;
1391
+ // for debug
1392
+ vao;
1393
+ constructor(t, e, i, r) {
1394
+ this.gl = t, this.program = Mt(t, e, i), this.parseShader(e), this.parseShader(i), this.vao = t.createVertexArray(), r && this.setAttributes(r);
1395
+ }
1396
+ /** Switch GPU to use this shader program */
1397
+ use() {
1398
+ this.gl.useProgram(this.program);
1399
+ }
1400
+ /** Bind this shader's VAO (start recording or replaying attribute state) */
1401
+ bindVAO() {
1402
+ this.gl.bindVertexArray(this.vao);
1403
+ }
1404
+ /** Unbind VAO (finish recording) */
1405
+ unbindVAO() {
1406
+ this.gl.bindVertexArray(null);
1407
+ }
1408
+ // ── Uniforms ─────────────────────────────────────────────────────────────
1409
+ /**
1410
+ * Set a single uniform by name. Type is inferred from the parsed GLSL source.
1411
+ * @example
1412
+ * shader.setUniform("uTime", 1.5);
1413
+ * shader.setUniform("uColor", [1, 0, 0, 1]);
1414
+ * shader.setUniform("uMVP", mat4array);
1415
+ */
1416
+ setUniform(t, e) {
1417
+ const i = this.uniformLoc[t];
1418
+ if (i === void 0) return;
1419
+ const r = this.uniformType[t], n = this.uniformIsArray[t];
1420
+ Ut(this.gl, i, r, e, n);
1421
+ }
1422
+ /**
1423
+ * Batch-set uniforms via a plain value map.
1424
+ * @example
1425
+ * shader.setUniforms({
1426
+ * uTime: 1.5,
1427
+ * uResolution: [800, 600],
1428
+ * uMVP: mat4,
1429
+ * });
1430
+ */
1431
+ setUniforms(t) {
1432
+ for (const e in t)
1433
+ this.setUniform(e, t[e]);
1434
+ }
1435
+ // ── Attributes ────────────────────────────────────────────────────────────
1436
+ /**
1437
+ * Bind a single vertex attribute pointer.
1438
+ * The VBO must already be bound before calling this.
1439
+ */
1440
+ setAttribute(t) {
1441
+ const e = this.attributeLoc[t.name];
1442
+ if (e === void 0) return;
1443
+ const i = this.gl;
1444
+ i.vertexAttribPointer(
1445
+ e,
1446
+ t.size,
1447
+ t.type ?? wt,
1448
+ t.normalized ?? !1,
1449
+ t.stride,
1450
+ t.offset ?? 0
1451
+ ), i.enableVertexAttribArray(e), bt(i, e, t.divisor ?? 0);
1452
+ }
1453
+ /** Bind all vertex attribute pointers at once */
1454
+ setAttributes(t) {
1455
+ for (const e of t)
1456
+ this.setAttribute(e);
1457
+ }
1458
+ // ── Lifecycle ─────────────────────────────────────────────────────────────
1459
+ /** Free GPU resources */
1460
+ destroy() {
1461
+ this.gl.deleteProgram(this.program);
1462
+ }
1463
+ /** Parse attribute/uniform names and GLSL types from raw shader source */
1464
+ parseShader(t) {
1465
+ const e = this.gl, i = t.match(/(?:in|attribute)\s+\w+\s+\w+/g);
1466
+ if (i)
1467
+ for (const n of i) {
1468
+ const o = n.split(/\s+/)[2], h = e.getAttribLocation(this.program, o);
1469
+ h !== -1 && (this.attributeLoc[o] = h);
1470
+ }
1471
+ const r = t.match(/uniform\s+\w+\s+\w+(?:\[\d+\])?/g);
1472
+ if (r)
1473
+ for (const n of r) {
1474
+ const a = n.split(/\s+/), o = a[1], h = a[2].replace(/\[\d+\]$/, ""), u = e.getUniformLocation(this.program, h);
1475
+ u !== null && (this.uniformLoc[h] = u, this.uniformType[h] = o, this.uniformIsArray[h] = /\[\d+\]$/.test(a[2]));
1476
+ }
1477
+ }
1478
+ }
1479
+ function Ut(s, t, e, i, r = !1) {
1480
+ switch (e) {
1481
+ // scalars
1482
+ case "float":
1483
+ return s.uniform1f(t, i);
1484
+ case "int":
1485
+ case "bool":
1486
+ case "sampler2D":
1487
+ case "samplerCube":
1488
+ return r ? s.uniform1iv(t, i) : s.uniform1i(t, i);
1489
+ // float vectors
1490
+ case "vec2":
1491
+ return s.uniform2fv(t, i);
1492
+ case "vec3":
1493
+ return s.uniform3fv(t, i);
1494
+ case "vec4":
1495
+ return s.uniform4fv(t, i);
1496
+ // int / bool vectors
1497
+ case "ivec2":
1498
+ case "bvec2":
1499
+ return s.uniform2iv(t, i);
1500
+ case "ivec3":
1501
+ case "bvec3":
1502
+ return s.uniform3iv(t, i);
1503
+ case "ivec4":
1504
+ case "bvec4":
1505
+ return s.uniform4iv(t, i);
1506
+ // matrices (column-major, transpose = false)
1507
+ case "mat2":
1508
+ return s.uniformMatrix2fv(t, !1, i);
1509
+ case "mat3":
1510
+ return s.uniformMatrix3fv(t, !1, i);
1511
+ case "mat4":
1512
+ return s.uniformMatrix4fv(t, !1, i);
1513
+ // fallback: guess by value shape
1514
+ default:
1515
+ return _t(s, t, i);
1516
+ }
1517
+ }
1518
+ function _t(s, t, e) {
1519
+ if (typeof e == "number")
1520
+ return s.uniform1f(t, e);
1521
+ const i = e;
1522
+ switch (i.length) {
1523
+ case 2:
1524
+ return s.uniform2fv(t, i);
1525
+ case 3:
1526
+ return s.uniform3fv(t, i);
1527
+ case 4:
1528
+ return s.uniform4fv(t, i);
1529
+ case 9:
1530
+ return s.uniformMatrix3fv(t, !1, i);
1531
+ case 16:
1532
+ return s.uniformMatrix4fv(t, !1, i);
1533
+ default:
1534
+ return s.uniform1fv(t, i);
1535
+ }
1536
+ }
1537
+ class rt {
1538
+ /** Custom vertex shader code */
1539
+ vs;
1540
+ /** Custom fragment shader code */
1541
+ fs;
1542
+ /** Map of compiled GLShader instances per region key */
1543
+ glshader = /* @__PURE__ */ new Map();
1544
+ /** Currently stored uniform values */
1545
+ uniforms = {};
1546
+ /** Set of region keys that need uniform updates */
1547
+ uniformDirty = /* @__PURE__ */ new Set();
1548
+ /** Map of textures to be bound to this shader */
1549
+ uniformTextures = {};
1550
+ /** Number of texture units reserved by this custom shader */
1551
+ usedTextureUnitNum = 0;
1552
+ /** Padding in pixels this shader needs beyond the sprite bounds (for outline/glow effects) */
1553
+ padding = 0;
1554
+ rapid;
1555
+ constructor(t, e, i, r = 0, n) {
1556
+ this.vs = e, this.fs = i, this.rapid = t, this.uniforms = n ?? {}, this.usedTextureUnitNum = r;
1557
+ }
1558
+ /**
1559
+ * Updates uniform values or textures for the custom shader.
1560
+ * @param uniforms Map of uniform values or WebGL textures to apply.
1561
+ */
1562
+ setUniforms(t) {
1563
+ this.rapid.flush();
1564
+ const e = {}, i = {};
1565
+ for (const [r, n] of Object.entries(t))
1566
+ n instanceof WebGLTexture ? i[r] = n : e[r] = n;
1567
+ Object.assign(this.uniforms, e), Object.assign(this.uniformTextures, i);
1568
+ for (const r of this.glshader.keys())
1569
+ this.uniformDirty.add(r);
1570
+ }
1571
+ /**
1572
+ * Sets the padding (in pixels) for this shader and propagates to all compiled GLShaders.
1573
+ * Must be called before the first draw call if set after construction.
1574
+ * @param pixels - Number of pixels to expand the quad on each side.
1575
+ */
1576
+ setPadding(t) {
1577
+ this.padding = t;
1578
+ for (const e of this.glshader.values())
1579
+ e.padding = t;
1580
+ return this;
1581
+ }
1582
+ /**
1583
+ * Applies current uniforms and texture unit mappings to the region-specific shader.
1584
+ * @param key The region key.
1585
+ * @param textureUniforms Map of texture uniform names to assigned texture unit indices.
1586
+ */
1587
+ applyUniform(t, e) {
1588
+ const i = this.glshader.get(t);
1589
+ i && (Object.keys(e).length > 0 && i.setUniforms(e), this.uniformDirty.has(t) && (i.setUniforms(this.uniforms), this.uniformDirty.delete(t)));
1590
+ }
1591
+ /**
1592
+ * Retrieves or compiles a customized GLShader for a specific region.
1593
+ * @param region The Region requesting the shader.
1594
+ * @param key The region key.
1595
+ * @param baseVS The base vertex shader template.
1596
+ * @param baseFS The base fragment shader template.
1597
+ * @returns The combined and compiled GLShader.
1598
+ */
1599
+ getGLShader(t, e, i, r) {
1600
+ if (this.glshader.has(e))
1601
+ return this.glshader.get(e);
1602
+ if (r == null || i == null)
1603
+ return console.warn("CustomGlShader: missing base shader for " + e), null;
1604
+ i = i.replace("// CUSTOM_CODE_CALL", "vertex(position, vRegion);"), r = r.replace("// CUSTOM_CODE_CALL", "fragment(fragColor);"), i = i.replace("// CUSTOM_CODE", this.vs), r = r.replace("// CUSTOM_CODE", this.fs);
1605
+ const n = t.createShader(i, r);
1606
+ return n.isCustom = !0, n.padding = this.padding, this.glshader.set(e, n), this.uniformDirty.add(e), n;
1607
+ }
1608
+ }
1609
+ class lt {
1610
+ constructor(t) {
1611
+ this.rapid = t, this.gl = t.gl, this.maxTextureUnits = t.maxTextureUnits, this.matrixStore = t.matrix;
1612
+ }
1613
+ defaultShader;
1614
+ currentShader;
1615
+ customShader = null;
1616
+ customShaderUsedTextureNum = 0;
1617
+ gl;
1618
+ maxTextureUnits;
1619
+ matrixStore;
1620
+ isDefaultShader = !0;
1621
+ KEY = "default";
1622
+ vs = "";
1623
+ fs = "";
1624
+ usedTextures = [];
1625
+ usedTexturePadding = [];
1626
+ createBuffer() {
1627
+ }
1628
+ createDefaultShader() {
1629
+ }
1630
+ get freeTextureUnitNum() {
1631
+ return this.maxTextureUnits - this.usedTextures.length - this.customShaderUsedTextureNum;
1632
+ }
1633
+ getTextureUnitList() {
1634
+ return Array.from(
1635
+ { length: this.maxTextureUnits },
1636
+ (t, e) => e
1637
+ );
1638
+ }
1639
+ findTextureUnit(t, e = 0, i = 0) {
1640
+ for (let r = 0; r < this.usedTextures.length; r++) {
1641
+ const n = r * 2;
1642
+ if (this.usedTextures[r] === t && this.usedTexturePadding[n] === e && this.usedTexturePadding[n + 1] === i)
1643
+ return r;
1644
+ }
1645
+ return -1;
1646
+ }
1647
+ useTexture(t, e = 0, i = 0) {
1648
+ const r = this.findTextureUnit(t, e, i);
1649
+ return r == -1 ? (this.usedTextures.push(t), this.usedTexturePadding.push(e), this.usedTexturePadding.push(i), this.usedTextures.length - 1) : r;
1650
+ }
1651
+ createShader(t, e) {
1652
+ return new ct(this.gl, t, e);
1653
+ }
1654
+ createCustomShader(t) {
1655
+ return t.getGLShader(this, this.KEY, this.vs, this.fs);
1656
+ }
1657
+ getCustomShader(t) {
1658
+ if (!t)
1659
+ return this.defaultShader;
1660
+ if (t instanceof rt) {
1661
+ const e = this.createCustomShader(t);
1662
+ return e ?? this.defaultShader;
1663
+ }
1664
+ return t;
1665
+ }
1666
+ /**
1667
+ * Checks if the given shader is the same as the currently bound shader.
1668
+ * @param customShader Optional custom shader.
1669
+ * @returns True if already bound, otherwise false.
1670
+ */
1671
+ isSameShader(t) {
1672
+ return this.isDefaultShader && !t ? !0 : this.getCustomShader(t) == this.currentShader;
1673
+ }
1674
+ enter(t) {
1675
+ this.resetRender(), this.currentShader = this.getCustomShader(t), t instanceof rt ? (this.customShader = t, this.customShaderUsedTextureNum = t.usedTextureUnitNum) : (this.customShader = null, this.customShaderUsedTextureNum = 0), this.isDefaultShader = this.currentShader == this.defaultShader, this.currentShader.use(), this.currentShader.setUniform("u_projection", this.rapid.projection);
1676
+ }
1677
+ exit() {
1678
+ this.hasPendingContent() && this.flush(), this.gl.bindVertexArray(null);
1679
+ }
1680
+ flush() {
1681
+ this.render(), this.resetRender();
1682
+ }
1683
+ render() {
1684
+ const t = this.currentShader;
1685
+ if (t.use(), this.customShader) {
1686
+ const i = this.customShader, r = {};
1687
+ for (const n in i.uniformTextures)
1688
+ r[n] = this.useTexture(i.uniformTextures[n]);
1689
+ this.customShader.applyUniform(this.KEY, r);
1690
+ }
1691
+ const e = this.gl;
1692
+ for (let i = 0; i < this.usedTextures.length; i++)
1693
+ e.activeTexture(e.TEXTURE0 + i), e.bindTexture(e.TEXTURE_2D, this.usedTextures[i]);
1694
+ t.setUniform("uTextures", Int32Array.from(
1695
+ { length: this.usedTextures.length },
1696
+ (i, r) => r
1697
+ )), t.setUniform("uPadding", Float32Array.from(
1698
+ { length: this.usedTexturePadding.length },
1699
+ (i, r) => this.usedTexturePadding[r]
1700
+ ));
1701
+ }
1702
+ resetRender() {
1703
+ this.usedTextures.length = 0, this.usedTexturePadding.length = 0;
1704
+ }
1705
+ hasPendingContent() {
1706
+ return !1;
1707
+ }
1708
+ }
1709
+ const z = `#version 300 es\r
1710
+ precision mediump float;\r
1711
+ \r
1712
+ // per-vertex\r
1713
+ in vec2 aVertex;\r
1714
+ \r
1715
+ // per-instance:2x3 仿射矩阵,拆成两个 vec3\r
1716
+ in vec3 aMatrixRow0; // [a, c, tx]\r
1717
+ in vec3 aMatrixRow1; // [b, d, ty]\r
1718
+ \r
1719
+ // per-instance:UV 区域 (u0, v0, u1, v1)\r
1720
+ in vec4 aUVRect;\r
1721
+ \r
1722
+ // per-instance:tint color\r
1723
+ in vec4 aColor;\r
1724
+ \r
1725
+ // per-instance:纹理索引\r
1726
+ in float aTextureId;\r
1727
+ \r
1728
+ uniform mat4 u_projection;\r
1729
+ uniform vec2 uPadding[%TEXTURE_NUM%];\r
1730
+ \r
1731
+ out vec2 vRegion;\r
1732
+ flat out int vTextureId;\r
1733
+ out vec4 vColor;\r
1734
+ \r
1735
+ out vec4 vUVRect;\r
1736
+ out vec2 vPadding;\r
1737
+ // CUSTOM_CODE\r
1738
+ \r
1739
+ void main(void) {\r
1740
+ vPadding = uPadding[int(aTextureId)];\r
1741
+ vUVRect = aUVRect;\r
1742
+ vRegion = mix(aUVRect.xy - vPadding, aUVRect.zw + vPadding, aVertex.xy);\r
1743
+ \r
1744
+ vTextureId = int(aTextureId);\r
1745
+ vColor = aColor;\r
1746
+ \r
1747
+ vec4 position = vec4(\r
1748
+ dot(aMatrixRow0.xyz, vec3(aVertex.xy, 1.0)), \r
1749
+ dot(aMatrixRow1.xyz, vec3(aVertex.xy, 1.0)), \r
1750
+ 0.0, \r
1751
+ 1.0\r
1752
+ );\r
1753
+ // CUSTOM_CODE_CALL\r
1754
+ \r
1755
+ gl_Position = u_projection * position;\r
1756
+ }\r
1757
+ `, st = `#version 300 es\r
1758
+ precision mediump float;\r
1759
+ \r
1760
+ uniform sampler2D uTextures[%TEXTURE_NUM%];\r
1761
+ \r
1762
+ in vec2 vRegion;\r
1763
+ flat in int vTextureId;\r
1764
+ in vec4 vColor;\r
1765
+ \r
1766
+ in vec4 vUVRect;\r
1767
+ \r
1768
+ out vec4 fragColor;\r
1769
+ in vec2 vPadding;\r
1770
+ \r
1771
+ bool clampUV(vec2 uv) {\r
1772
+ return uv.x < vUVRect.x || uv.x > vUVRect.z || uv.y < vUVRect.y || uv.y > vUVRect.w;\r
1773
+ }\r
1774
+ \r
1775
+ vec4 sampleTexture(vec2 uv) {\r
1776
+ %GET_COLOR%\r
1777
+ }\r
1778
+ \r
1779
+ vec4 sampleClampTexture(vec2 uv) {\r
1780
+ if (clampUV(uv)) {\r
1781
+ return vec4(0.0, 0.0, 0.0, 0.0);\r
1782
+ }\r
1783
+ return sampleTexture(uv);\r
1784
+ }\r
1785
+ \r
1786
+ vec4 sampleTextureLocal(vec2 uv){\r
1787
+ vec2 gUV = mix(vUVRect.xy, vUVRect.zw, uv);\r
1788
+ if (clampUV(gUV)) {\r
1789
+ return vec4(0.0, 0.0, 0.0, 0.0);\r
1790
+ }\r
1791
+ return sampleTexture(gUV);\r
1792
+ }\r
1793
+ \r
1794
+ // CUSTOM_CODE\r
1795
+ \r
1796
+ void main(void) {\r
1797
+ fragColor = sampleTexture(vRegion);\r
1798
+ // if (vPadding.x != 0.0) {\r
1799
+ // if (clampUV(vRegion)) {\r
1800
+ // fragColor = vec4(0.0, 0.0, 0.0, 0.0);\r
1801
+ // }\r
1802
+ // }\r
1803
+ \r
1804
+ // CUSTOM_CODE_CALL\r
1805
+ }\r
1806
+ `, k = 48, Ct = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);
1807
+ class dt extends lt {
1808
+ instanceBuffer;
1809
+ quadBuffer;
1810
+ instanceCount = 0;
1811
+ localSpriteMatrix = new Float32Array(6);
1812
+ worldSpriteMatrix = new Float32Array(6);
1813
+ KEY = "Sprite";
1814
+ constructor(t) {
1815
+ super(t), this.createBuffer(), this.createDefaultShader();
1816
+ }
1817
+ createBuffer() {
1818
+ const t = this.gl;
1819
+ this.quadBuffer = new j(t, N.Float32, t.ARRAY_BUFFER, t.STATIC_DRAW), this.quadBuffer.bindBuffer();
1820
+ for (const e of Ct) this.quadBuffer.pushFloat32(e);
1821
+ this.quadBuffer.makeDirty(), this.quadBuffer.bufferData(), this.instanceBuffer = new j(t, N.Uint32, t.ARRAY_BUFFER, t.DYNAMIC_DRAW);
1822
+ }
1823
+ createDefaultShader() {
1824
+ const t = this.rapid, e = F(st, t.maxTextureUnits), i = F(z, t.maxTextureUnits);
1825
+ return this.vs = i, this.fs = e, this.defaultShader = this.createShader(i, e), this.defaultShader;
1826
+ }
1827
+ createShader(t, e) {
1828
+ const i = super.createShader(t, e);
1829
+ return i.use(), i.bindVAO(), this.instanceBuffer.bindBuffer(), i.setAttributes([
1830
+ { name: "aMatrixRow0", size: 3, stride: k, offset: 0, divisor: 1 },
1831
+ { name: "aMatrixRow1", size: 3, stride: k, offset: 12, divisor: 1 },
1832
+ { name: "aUVRect", size: 4, stride: k, offset: 24, divisor: 1 },
1833
+ { name: "aColor", size: 4, type: it, normalized: !0, stride: k, offset: 40, divisor: 1 },
1834
+ { name: "aTextureId", size: 1, type: it, stride: k, offset: 44, divisor: 1 }
1835
+ ]), this.quadBuffer.bindBuffer(), i.setAttributes([
1836
+ { name: "aVertex", size: 2, stride: 8, offset: 0, divisor: 0 }
1837
+ ]), i.unbindVAO(), this.currentShader && this.currentShader.use(), i;
1838
+ }
1839
+ createCustomShader(t) {
1840
+ const e = F(st, this.rapid.maxTextureUnits - t.usedTextureUnitNum), i = F(z, this.rapid.maxTextureUnits - t.usedTextureUnitNum);
1841
+ return t.getGLShader(this, this.KEY, i, e);
1842
+ }
1843
+ /**
1844
+ * Draws a textured sprite via instanced rendering to significantly improve performance.
1845
+ * @param texture The WebGLTexture to map.
1846
+ * @param matrixIndex The index for the transform matrix in MatrixStore.
1847
+ * @param width Render width.
1848
+ * @param height Render height.
1849
+ * @param u0 Left UV mapped coordinate (default 0).
1850
+ * @param v0 Top UV mapped coordinate (default 0).
1851
+ * @param u1 Right UV mapped coordinate (default 1).
1852
+ * @param v1 Bottom UV mapped coordinate (default 1).
1853
+ * @param color The tint color as packed ABGR uniform uint32 (default 0xFFFFFFFF).
1854
+ */
1855
+ drawSprite(t, e, i = 0, r = 0, n = 1, a = 1, o = 4294967295, h = 0, u = 0, c = !1, m = !1, f = !1) {
1856
+ this.freeTextureUnitNum === 0 && this.flush();
1857
+ const v = t.base, x = t.scale || 1, T = t.rawWidth, E = t.rawHeight, R = t.offsetX, _ = t.offsetY, A = this.useTexture(
1858
+ t.glTexture,
1859
+ h / x / v.width,
1860
+ u / x / v.height
1861
+ ), g = e * 6, M = this.matrixStore.data, L = this.instanceBuffer, S = L.usedElemNum;
1862
+ L.resize(12);
1863
+ const U = L.float32, O = L.uint32, B = (f ? E : T) * x, K = (f ? T : E) * x, P = B + 2 * h, I = K + 2 * u, Y = R - h, tt = _ - u, y = this.localSpriteMatrix;
1864
+ f ? (y[0] = 0, y[1] = m ? I : -I, y[2] = c ? -P : P, y[3] = 0, y[4] = Y + (c ? P : 0), y[5] = tt + (m ? 0 : I)) : (y[0] = c ? -P : P, y[1] = 0, y[2] = 0, y[3] = m ? -I : I, y[4] = Y + (c ? P : 0), y[5] = tt + (m ? I : 0));
1865
+ const w = this.worldSpriteMatrix;
1866
+ w[0] = M[g] * y[0] + M[g + 2] * y[1], w[1] = M[g + 1] * y[0] + M[g + 3] * y[1], w[2] = M[g] * y[2] + M[g + 2] * y[3], w[3] = M[g + 1] * y[2] + M[g + 3] * y[3], w[4] = M[g + 4] + M[g] * y[4] + M[g + 2] * y[5], w[5] = M[g + 5] + M[g + 1] * y[4] + M[g + 3] * y[5], U[S + 6] = i, U[S + 7] = r, U[S + 8] = n, U[S + 9] = a, O[S + 10] = o, O[S + 11] = A, this.rapid.roundPixels && (w[4] = Math.round(w[4]), w[5] = Math.round(w[5])), U[S] = w[0], U[S + 1] = w[2], U[S + 2] = w[4], U[S + 3] = w[1], U[S + 4] = w[3], U[S + 5] = w[5], L.usedElemNum += 12, L.makeDirty(), this.instanceCount++;
1867
+ }
1868
+ render() {
1869
+ if (this.instanceCount === 0) return;
1870
+ super.render();
1871
+ const t = this.gl, e = this.currentShader;
1872
+ this.instanceBuffer.bindBuffer(), this.instanceBuffer.bufferData(), e.bindVAO(), At(t, t.TRIANGLE_STRIP, 0, 4, this.instanceCount), this.rapid.drawcallCount++;
1873
+ }
1874
+ resetRender() {
1875
+ super.resetRender(), this.instanceBuffer.clear(), this.instanceCount = 0;
1876
+ }
1877
+ hasPendingContent() {
1878
+ return this.instanceCount > 0;
1879
+ }
1880
+ }
1881
+ const Nt = `#version 300 es\r
1882
+ precision mediump float;\r
1883
+ \r
1884
+ in vec2 aPosition;\r
1885
+ in vec4 aColor;\r
1886
+ in vec2 aUV;\r
1887
+ \r
1888
+ uniform mat4 u_projection;\r
1889
+ uniform vec3 uMatrixRow0; // [a, c, tx]\r
1890
+ uniform vec3 uMatrixRow1; // [b, d, ty]\r
1891
+ uniform vec4 uUVRect;\r
1892
+ \r
1893
+ out vec4 vColor;\r
1894
+ out vec2 vRegion;\r
1895
+ out vec4 vUVRect;\r
1896
+ \r
1897
+ // CUSTOM_CODE\r
1898
+ \r
1899
+ void main(void) {\r
1900
+ vColor = aColor;\r
1901
+ vUVRect = uUVRect;\r
1902
+ vRegion = mix(uUVRect.xy, uUVRect.zw, aUV);\r
1903
+ vec4 position = vec4(\r
1904
+ dot(uMatrixRow0.xyz, vec3(aPosition.xy, 1.0)), \r
1905
+ dot(uMatrixRow1.xyz, vec3(aPosition.xy, 1.0)), \r
1906
+ 0.0, \r
1907
+ 1.0\r
1908
+ );\r
1909
+ // CUSTOM_CODE_CALL\r
1910
+ gl_Position = u_projection * position;\r
1911
+ }\r
1912
+ `, Lt = `#version 300 es\r
1913
+ precision mediump float;\r
1914
+ \r
1915
+ in vec4 vColor;\r
1916
+ in vec2 vRegion;\r
1917
+ in vec4 vUVRect;\r
1918
+ \r
1919
+ uniform sampler2D uTexture;\r
1920
+ uniform int uUseTexture;\r
1921
+ \r
1922
+ out vec4 fragColor;\r
1923
+ \r
1924
+ bool clampUV(vec2 uv) {\r
1925
+ vec2 uvMin = min(vUVRect.xy, vUVRect.zw);\r
1926
+ vec2 uvMax = max(vUVRect.xy, vUVRect.zw);\r
1927
+ return uv.x < uvMin.x || uv.x > uvMax.x || uv.y < uvMin.y || uv.y > uvMax.y;\r
1928
+ }\r
1929
+ \r
1930
+ vec4 sampleTexture(vec2 uv) {\r
1931
+ if (uUseTexture > 0) {\r
1932
+ return texture(uTexture, uv) * vColor;\r
1933
+ }\r
1934
+ return vColor;\r
1935
+ }\r
1936
+ \r
1937
+ vec4 sampleClampTexture(vec2 uv) {\r
1938
+ if (clampUV(uv)) {\r
1939
+ return vec4(0.0, 0.0, 0.0, 0.0);\r
1940
+ }\r
1941
+ return sampleTexture(uv);\r
1942
+ }\r
1943
+ \r
1944
+ vec4 sampleTextureLocal(vec2 uv) {\r
1945
+ vec2 gUV = mix(vUVRect.xy, vUVRect.zw, uv);\r
1946
+ if (clampUV(gUV)) {\r
1947
+ return vec4(0.0, 0.0, 0.0, 0.0);\r
1948
+ }\r
1949
+ return sampleTexture(gUV);\r
1950
+ }\r
1951
+ \r
1952
+ // CUSTOM_CODE\r
1953
+ \r
1954
+ void main(void) {\r
1955
+ fragColor = sampleTexture(vRegion);\r
1956
+ // CUSTOM_CODE_CALL\r
1957
+ }\r
1958
+ `, Pt = `#version 300 es\r
1959
+ precision mediump float;\r
1960
+ \r
1961
+ in vec2 aPosition;\r
1962
+ in vec2 aUV;\r
1963
+ \r
1964
+ uniform mat4 u_projection;\r
1965
+ uniform vec3 uMatrixRow0; // [a, c, tx]\r
1966
+ uniform vec3 uMatrixRow1; // [b, d, ty]\r
1967
+ uniform vec4 uUVRect;\r
1968
+ \r
1969
+ out vec2 vRegion;\r
1970
+ \r
1971
+ void main(void) {\r
1972
+ vRegion = mix(uUVRect.xy, uUVRect.zw, aUV);\r
1973
+ vec4 position = vec4(\r
1974
+ dot(uMatrixRow0.xyz, vec3(aPosition.xy, 1.0)), \r
1975
+ dot(uMatrixRow1.xyz, vec3(aPosition.xy, 1.0)), \r
1976
+ 0.0, \r
1977
+ 1.0\r
1978
+ );\r
1979
+ gl_Position = u_projection * position;\r
1980
+ }\r
1981
+ `, It = `#version 300 es\r
1982
+ precision mediump float;\r
1983
+ \r
1984
+ in vec2 vRegion;\r
1985
+ \r
1986
+ uniform sampler2D uTexture;\r
1987
+ uniform int uUseTexture;\r
1988
+ \r
1989
+ out vec4 fragColor;\r
1990
+ \r
1991
+ void main(void) {\r
1992
+ if (uUseTexture > 0) {\r
1993
+ fragColor = texture(uTexture, vRegion);\r
1994
+ } else {\r
1995
+ fragColor = vec4(1.0);\r
1996
+ }\r
1997
+ if(fragColor.a == 0.0) {\r
1998
+ discard;\r
1999
+ }\r
2000
+ }\r
2001
+ `, G = 20;
2002
+ class Ot extends lt {
2003
+ vertexBuffer;
2004
+ vertexCount = 0;
2005
+ matrixIndex = -1;
2006
+ drawMode = this.gl.TRIANGLES;
2007
+ texture;
2008
+ maskShader;
2009
+ KEY = "Graphic";
2010
+ constructor(t) {
2011
+ super(t), this.createBuffer(), this.createDefaultShader();
2012
+ }
2013
+ createBuffer() {
2014
+ const t = this.gl;
2015
+ this.vertexBuffer = new j(t, N.Float32, t.ARRAY_BUFFER, t.DYNAMIC_DRAW), this.maskShader = this.createMaskShader(Pt, It);
2016
+ }
2017
+ createDefaultShader() {
2018
+ return this.vs = Nt, this.fs = Lt, this.defaultShader = this.createShader(this.vs, this.fs), this.defaultShader;
2019
+ }
2020
+ createMaskShader(t, e) {
2021
+ const i = this.gl, r = new ct(i, t, e);
2022
+ return r.use(), r.bindVAO(), this.vertexBuffer.bindBuffer(), r.setAttributes([
2023
+ { name: "aPosition", size: 2, type: i.FLOAT, stride: G - 4, offset: 0, divisor: 0 },
2024
+ { name: "aUV", size: 2, type: i.FLOAT, stride: G - 4, offset: 8, divisor: 0 }
2025
+ ]), r.unbindVAO(), this.currentShader && this.currentShader.use(), r;
2026
+ }
2027
+ createShader(t, e) {
2028
+ const i = this.gl, r = super.createShader(t, e);
2029
+ return r.use(), r.bindVAO(), this.vertexBuffer.bindBuffer(), r.setAttributes([
2030
+ { name: "aPosition", size: 2, type: i.FLOAT, stride: G, offset: 0, divisor: 0 },
2031
+ { name: "aColor", size: 4, type: i.UNSIGNED_BYTE, normalized: !0, stride: G, offset: 8, divisor: 0 },
2032
+ { name: "aUV", size: 2, type: i.FLOAT, stride: G, offset: 12, divisor: 0 }
2033
+ ]), r.unbindVAO(), this.currentShader && this.currentShader.use(), r;
2034
+ }
2035
+ /**
2036
+ * Begins a new polygon drawing sequence. Must be followed by addVertex() calls and endGraphic().
2037
+ * @param matrixIndex The index targeting a specific transform matrix in the MatrixStore.
2038
+ * @param drawMode The WebGL drawing primitive mode (e.g., gl.TRIANGLES).
2039
+ * @param texture Optional texture to apply over the polygon.
2040
+ */
2041
+ startGraphic(t, e = this.gl.TRIANGLES, i) {
2042
+ this.vertexBuffer.clear(), this.vertexCount = 0, this.matrixIndex = t, this.drawMode = e, this.texture = i;
2043
+ }
2044
+ /**
2045
+ * Adds a vertex to the current polygon. Local coordinates and color settings.
2046
+ * @param x Local X coordinate.
2047
+ * @param y Local Y coordinate.
2048
+ * @param u UV mapped X value (0-1, default 0).
2049
+ * @param v UV mapped Y value (0-1, default 0).
2050
+ * @param color Pre-multiplied hex color combined using ABGR byte order (default 0xFFFFFFFF).
2051
+ */
2052
+ addVertex(t, e, i = 0, r = 0, n = 4294967295) {
2053
+ const a = this.vertexBuffer;
2054
+ a.pushFloat32(t), a.pushFloat32(e), this.currentShader != this.maskShader && a.pushUint32(n), a.pushFloat32(i), a.pushFloat32(r), this.vertexCount++, this.vertexBuffer.makeDirty();
2055
+ }
2056
+ /**
2057
+ * Finishes the graphic construction and dispatches the rendering call immediately.
2058
+ */
2059
+ endGraphic() {
2060
+ this.render();
2061
+ }
2062
+ render() {
2063
+ if (this.vertexCount === 0) return;
2064
+ super.render();
2065
+ const t = this.gl, e = this.currentShader, i = this.matrixIndex * 6, r = this.matrixStore.data;
2066
+ let n = r[i + 4], a = r[i + 5];
2067
+ this.rapid.roundPixels && (n = Math.round(n), a = Math.round(a)), e.setUniform("uMatrixRow0", [r[i], r[i + 2], n]), e.setUniform("uMatrixRow1", [r[i + 1], r[i + 3], a]), e.setUniform(
2068
+ "uUVRect",
2069
+ this.texture ? [this.texture.uvX, this.texture.uvY, this.texture.uvW, this.texture.uvH] : [0, 0, 1, 1]
2070
+ ), this.texture?.glTexture ? (t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.texture.glTexture), e.setUniform("uTexture", 0), e.setUniform("uUseTexture", 1)) : e.setUniform("uUseTexture", 0), this.vertexBuffer.bindBuffer(), this.vertexBuffer.bufferData(), e.bindVAO(), t.drawArrays(this.drawMode, 0, this.vertexCount), this.rapid.drawcallCount++;
2071
+ }
2072
+ hasPendingContent() {
2073
+ return !1;
2074
+ }
2075
+ }
2076
+ class l {
2077
+ x;
2078
+ y;
2079
+ static ZERO = new l(0, 0);
2080
+ static ONE = new l(1, 1);
2081
+ static UP = new l(0, 1);
2082
+ static DOWN = new l(0, -1);
2083
+ static LEFT = new l(-1, 0);
2084
+ static RIGHT = new l(1, 0);
2085
+ constructor(t, e) {
2086
+ this.x = t !== void 0 ? t : 0, this.y = e !== void 0 ? e : 0;
2087
+ }
2088
+ set(t, e) {
2089
+ return e === void 0 ? (this.x = t, this.y = t) : (this.x = t, this.y = e), this;
2090
+ }
2091
+ add(t) {
2092
+ return new l(this.x + t.x, this.y + t.y);
2093
+ }
2094
+ subtract(t) {
2095
+ return new l(this.x - t.x, this.y - t.y);
2096
+ }
2097
+ sub(t) {
2098
+ return new l(this.x - t.x, this.y - t.y);
2099
+ }
2100
+ multiply(t) {
2101
+ return t instanceof l ? new l(this.x * t.x, this.y * t.y) : new l(this.x * t, this.y * t);
2102
+ }
2103
+ mul(t) {
2104
+ return this.multiply(t);
2105
+ }
2106
+ divide(t) {
2107
+ return t instanceof l ? new l(this.x / t.x, this.y / t.y) : new l(this.x / t, this.y / t);
2108
+ }
2109
+ dot(t) {
2110
+ return this.x * t.x + this.y * t.y;
2111
+ }
2112
+ cross(t) {
2113
+ return this.x * t.y - this.y * t.x;
2114
+ }
2115
+ distanceTo(t) {
2116
+ const e = this.x - t.x, i = this.y - t.y;
2117
+ return Math.sqrt(e * e + i * i);
2118
+ }
2119
+ clone() {
2120
+ return new l(this.x, this.y);
2121
+ }
2122
+ to(t) {
2123
+ this.x = t.x, this.y = t.y;
2124
+ }
2125
+ copy() {
2126
+ return new l(this.x, this.y);
2127
+ }
2128
+ equals(t) {
2129
+ return t.x == this.x && t.y == this.y;
2130
+ }
2131
+ perpendicular() {
2132
+ const t = this.x;
2133
+ return this.x = -this.y, this.y = t, this;
2134
+ }
2135
+ invert() {
2136
+ return this.x = -this.x, this.y = -this.y, this;
2137
+ }
2138
+ inverted() {
2139
+ return new l(this.x * -1, this.y * -1);
2140
+ }
2141
+ length() {
2142
+ return Math.sqrt(this.x * this.x + this.y * this.y);
2143
+ }
2144
+ squaredLength() {
2145
+ return this.x * this.x + this.y * this.y;
2146
+ }
2147
+ normalize() {
2148
+ const t = this.length();
2149
+ return this.x = this.x / t || 0, this.y = this.y / t || 0, this;
2150
+ }
2151
+ normalized() {
2152
+ const t = this.length();
2153
+ return new l(this.x / t || 0, this.y / t || 0);
2154
+ }
2155
+ fix(t = 1e-13) {
2156
+ this.x = Math.abs(this.x) < t ? 0 : this.x, this.y = Math.abs(this.y) < t ? 0 : this.y;
2157
+ }
2158
+ fixed(t = 1e-13) {
2159
+ return new l(
2160
+ Math.abs(this.x) < t ? 0 : this.x,
2161
+ Math.abs(this.y) < t ? 0 : this.y
2162
+ );
2163
+ }
2164
+ angle() {
2165
+ return Math.atan2(this.y, this.x);
2166
+ }
2167
+ abs() {
2168
+ return new l(Math.abs(this.x), Math.abs(this.y));
2169
+ }
2170
+ floor() {
2171
+ return new l(Math.floor(this.x), Math.floor(this.y));
2172
+ }
2173
+ ceil() {
2174
+ return new l(Math.ceil(this.x), Math.ceil(this.y));
2175
+ }
2176
+ stringify() {
2177
+ return `Vec2(${this.x}, ${this.y})`;
2178
+ }
2179
+ clear() {
2180
+ this.x = 0, this.y = 0;
2181
+ }
2182
+ isPrettyMuchZero() {
2183
+ return Math.abs(this.x) < 1e-4 && Math.abs(this.y) < 1e-4;
2184
+ }
2185
+ isZero() {
2186
+ return this.x == 0 && this.y == 0;
2187
+ }
2188
+ isNotZero() {
2189
+ return this.x !== 0 || this.y !== 0;
2190
+ }
2191
+ setToPolar(t, e = 1) {
2192
+ return this.x = Math.cos(t) * e, this.y = Math.sin(t) * e, this;
2193
+ }
2194
+ static FromArray(t) {
2195
+ return t.map((e) => new l(e[0], e[1]));
2196
+ }
2197
+ static fromAngle(t) {
2198
+ return new l(Math.cos(t), Math.sin(t));
2199
+ }
2200
+ }
2201
+ const $ = 10, nt = (s, t, e, i, r, n) => {
2202
+ const a = [], o = [], h = i ? Math.atan2(t.y, t.x) : Math.atan2(-t.y, -t.x), u = Math.PI, c = new l(t.y, -t.x);
2203
+ for (let m = 0; m < $; m++) {
2204
+ const f = m / $, v = (m + 1) / $, x = h + f * u, T = h + v * u, E = new l(Math.cos(x) * e, Math.sin(x) * e), R = new l(Math.cos(T) * e, Math.sin(T) * e);
2205
+ a.push(s), o.push(new l(r, 0.5)), a.push(s.add(E)), o.push(new l(r + E.dot(c) * n, 0.5 - 0.5 * E.dot(t) / e)), a.push(s.add(R)), o.push(new l(r + R.dot(c) * n, 0.5 - 0.5 * R.dot(t) / e));
2206
+ }
2207
+ return { vertices: a, uv: o };
2208
+ }, Ft = (s, t = !1) => {
2209
+ const e = [];
2210
+ if (s.length < 2 || t && s.length < 3) return { normals: e, length: 0 };
2211
+ const i = 4, r = 1e-3, n = s.length;
2212
+ let a = 0;
2213
+ if (t)
2214
+ for (let h = 0; h < n; h++) {
2215
+ const u = s[h], c = s[(h + 1) % n];
2216
+ a += u.distanceTo(c);
2217
+ }
2218
+ else
2219
+ for (let h = 0; h < n - 1; h++)
2220
+ a += s[h].distanceTo(s[h + 1]);
2221
+ const o = (h, u, c) => {
2222
+ const m = u.subtract(h).normalize(), f = u.subtract(c).normalize(), v = f.dot(m);
2223
+ if (v < -1 + r)
2224
+ return { normal: m.perpendicular(), miters: 1 };
2225
+ {
2226
+ let x = f.add(m).normalize();
2227
+ m.cross(f) < 0 && (x = x.multiply(-1));
2228
+ let T = 1 / Math.sqrt((1 - v) / 2);
2229
+ return { normal: x, miters: Math.min(T, i) };
2230
+ }
2231
+ };
2232
+ if (t)
2233
+ for (let h = 0; h < n; h++) {
2234
+ const u = h === 0 ? s[n - 1] : s[h - 1], c = s[h], m = h === n - 1 ? s[0] : s[h + 1];
2235
+ e.push(o(u, c, m));
2236
+ }
2237
+ else
2238
+ for (let h = 0; h < n; h++)
2239
+ if (h === 0) {
2240
+ const u = s[1].subtract(s[0]).normalize();
2241
+ e.push({ normal: u.perpendicular(), miters: 1 });
2242
+ } else if (h === n - 1) {
2243
+ const u = s[h].subtract(s[h - 1]).normalize();
2244
+ e.push({ normal: u.perpendicular(), miters: 1 });
2245
+ } else
2246
+ e.push(o(s[h - 1], s[h], s[h + 1]));
2247
+ return { normals: e, length: a };
2248
+ }, Wt = (s) => {
2249
+ const t = s.points;
2250
+ if (t.length < 2) return { vertices: [], uv: [] };
2251
+ const { normals: e, length: i } = Ft(t, s.closed), r = (s.width || 1) / 2, n = [], a = [], o = s.roundCap || !1, h = s.textureMode || 0;
2252
+ let u = 0;
2253
+ const c = s.texture?.rawWidth || 1, m = s.closed ? t.length : t.length - 1;
2254
+ for (let f = 0; f < m; f++) {
2255
+ const v = t[f], x = e[f].normal, T = e[f].miters, E = (f + 1) % t.length, R = t[E], _ = e[E].normal, A = e[E].miters, g = v.add(x.multiply(T * r)), M = v.subtract(x.multiply(T * r)), L = R.add(_.multiply(A * r)), S = R.subtract(_.multiply(A * r)), U = v.distanceTo(R);
2256
+ let O = 0, B = 0;
2257
+ h === 0 ? (O = u / i, B = (u + U) / i) : (O = u / c, B = O + U / c);
2258
+ const K = new l(O, 0), P = new l(O, 1), I = new l(B, 0), Y = new l(B, 1);
2259
+ n.push(g), a.push(K), n.push(M), a.push(P), n.push(L), a.push(I), n.push(L), a.push(I), n.push(S), a.push(Y), n.push(M), a.push(P), u += U;
2260
+ }
2261
+ if (o && !s.closed) {
2262
+ const f = t[0], v = e[0].normal, x = h === 0 ? i > 0 ? 1 / i : 0 : 1 / c, T = nt(f, v, r, !0, 0, x);
2263
+ n.push(...T.vertices), a.push(...T.uv);
2264
+ const E = t[t.length - 1], R = e[t.length - 1].normal, _ = h === 0 ? 1 : u / c, A = h === 0 ? i > 0 ? 1 / i : 0 : 1 / c, g = nt(E, R, r, !1, _, A);
2265
+ n.push(...g.vertices), a.push(...g.uv);
2266
+ }
2267
+ return { vertices: n, uv: a };
2268
+ }, Dt = (s) => s.saveTransform !== void 0 || s.afterSave !== void 0 || s.x !== void 0 || s.y !== void 0 || s.position !== void 0 || s.rotation !== void 0 || s.scale !== void 0 || s.offsetX !== void 0 || s.offsetY !== void 0 || s.offset !== void 0 || s.origin !== void 0, V = (s, t, e, i, r) => {
2269
+ if (!Dt(t)) {
2270
+ r();
2271
+ return;
2272
+ }
2273
+ s.matrixStack.applyTransform(t, e, i);
2274
+ try {
2275
+ r();
2276
+ } finally {
2277
+ (t.saveTransform ?? !0) && s.matrixStack.restore();
2278
+ }
2279
+ }, J = (s, t) => t ? s.premultipliedAlpha ? t.premultipliedUint32 : t.uint32 : 4294967295, Bt = 2, ft = (s, t) => {
2280
+ const e = t.texture;
2281
+ if (!e?.base || s.inCreateMask)
2282
+ return;
2283
+ const i = e.isAtlas ? s.atlasSpriteRegion : s.spriteRegion;
2284
+ s.enterRegion(i, t.shader);
2285
+ let r = e.uvX, n = e.uvY, a = e.uvW, o = e.uvH;
2286
+ const h = !!t.flipX, u = !!t.flipY != !!e.flipY;
2287
+ let c = t.padding ?? i.currentShader.padding;
2288
+ e.isAtlas && (c += Bt);
2289
+ const m = r <= a ? c : -c, f = n <= o ? c : -c;
2290
+ i.drawSprite(
2291
+ e,
2292
+ t.customMatrix ?? s.matrixStack.curWorldM,
2293
+ r,
2294
+ n,
2295
+ a,
2296
+ o,
2297
+ J(s, t.color),
2298
+ m,
2299
+ f,
2300
+ h,
2301
+ u,
2302
+ e.isRotated
2303
+ );
2304
+ }, kt = (s, t) => {
2305
+ V(s, t, t.texture.rawWidth, t.texture.rawHeight, () => {
2306
+ ft(s, t);
2307
+ });
2308
+ }, mt = (s, t) => {
2309
+ t.points.length !== 0 && V(s, t, 0, 0, () => {
2310
+ s.startGraphic(
2311
+ t.drawMode ?? s.gl.TRIANGLES,
2312
+ t.texture,
2313
+ t.shader,
2314
+ t.customMatrix
2315
+ );
2316
+ for (let e = 0; e < t.points.length; e++) {
2317
+ const i = t.points[e], r = t.uv?.[e], n = Array.isArray(t.color) ? t.color[e] : t.color;
2318
+ s.addGraphicVertex(
2319
+ i.x,
2320
+ i.y,
2321
+ r?.x ?? 0,
2322
+ r?.y ?? 0,
2323
+ J(s, n)
2324
+ );
2325
+ }
2326
+ s.endGraphic();
2327
+ });
2328
+ }, Gt = (s, t) => {
2329
+ if (s.inCreateMask)
2330
+ return;
2331
+ const { vertices: e, uv: i } = Wt(t);
2332
+ mt(s, {
2333
+ ...t,
2334
+ points: e,
2335
+ uv: i,
2336
+ drawMode: s.gl.TRIANGLES
2337
+ });
2338
+ }, Ht = (s, t) => {
2339
+ V(s, t, t.texture.rawWidth, t.texture.rawHeight, () => {
2340
+ s.startMaskGraphic(
2341
+ s.gl.TRIANGLE_FAN,
2342
+ t.texture,
2343
+ t.customMatrix
2344
+ ), s.addRectVertex(t.texture.rawWidth, t.texture.rawHeight), s.endGraphic();
2345
+ });
2346
+ }, Vt = (s, t) => {
2347
+ V(s, t, t.width, t.height, () => {
2348
+ s.startGraphic(
2349
+ s.gl.TRIANGLE_FAN,
2350
+ t.texture,
2351
+ t.shader,
2352
+ t.customMatrix
2353
+ ), s.addRectVertex(t.width, t.height, t.color), s.endGraphic();
2354
+ });
2355
+ }, Yt = (s, t) => {
2356
+ const e = t.segments ?? 32;
2357
+ V(s, t, 0, 0, () => {
2358
+ s.startGraphic(
2359
+ s.gl.TRIANGLE_FAN,
2360
+ void 0,
2361
+ t.shader,
2362
+ t.customMatrix
2363
+ );
2364
+ const i = J(s, t.color);
2365
+ s.addGraphicVertex(0, 0, 0.5, 0.5, i), s.addCircleVertex(t.radius, t.color, e), s.addGraphicVertex(t.radius, 0, 1, 0.5, i), s.endGraphic();
2366
+ });
2367
+ }, at = `#version 300 es\r
2368
+ precision mediump float;\r
2369
+ \r
2370
+ uniform sampler2D uTextures[%TEXTURE_NUM%];\r
2371
+ \r
2372
+ in vec2 vRegion;\r
2373
+ flat in int vTextureId;\r
2374
+ in vec4 vColor;\r
2375
+ \r
2376
+ in vec4 vUVRect;\r
2377
+ \r
2378
+ out vec4 fragColor;\r
2379
+ in vec2 vPadding;\r
2380
+ \r
2381
+ bool clampUV(vec2 uv) {\r
2382
+ return uv.x < vUVRect.x || uv.x > vUVRect.z || uv.y < vUVRect.y || uv.y > vUVRect.w;\r
2383
+ }\r
2384
+ \r
2385
+ vec4 sampleTexture(vec2 uv) {\r
2386
+ %GET_COLOR%\r
2387
+ }\r
2388
+ \r
2389
+ vec4 sampleClampTexture(vec2 uv) {\r
2390
+ if (clampUV(uv)) {\r
2391
+ return vec4(0.0, 0.0, 0.0, 0.0);\r
2392
+ }\r
2393
+ return sampleTexture(uv);\r
2394
+ }\r
2395
+ \r
2396
+ vec4 sampleTextureLocal(vec2 uv){\r
2397
+ vec2 gUV = mix(vUVRect.xy, vUVRect.zw, uv);\r
2398
+ if (clampUV(gUV)) {\r
2399
+ return vec4(0.0, 0.0, 0.0, 0.0);\r
2400
+ }\r
2401
+ return sampleTexture(gUV);\r
2402
+ }\r
2403
+ \r
2404
+ // CUSTOM_CODE\r
2405
+ \r
2406
+ void main(void) {\r
2407
+ fragColor = sampleClampTexture(vRegion);\r
2408
+ // if (vPadding.x != 0.0) {\r
2409
+ // if (clampUV(vRegion)) {\r
2410
+ // fragColor = vec4(0.0, 0.0, 0.0, 0.0);\r
2411
+ // }\r
2412
+ // }\r
2413
+ \r
2414
+ // CUSTOM_CODE_CALL\r
2415
+ }\r
2416
+ `;
2417
+ class zt extends dt {
2418
+ KEY = "AtlasSprite";
2419
+ constructor(t) {
2420
+ super(t);
2421
+ }
2422
+ createCustomShader(t) {
2423
+ const e = F(at, this.rapid.maxTextureUnits - t.usedTextureUnitNum), i = F(z, this.rapid.maxTextureUnits - t.usedTextureUnitNum);
2424
+ return t.getGLShader(this, this.KEY, i, e);
2425
+ }
2426
+ createDefaultShader() {
2427
+ const t = this.rapid, e = F(at, t.maxTextureUnits), i = F(z, t.maxTextureUnits);
2428
+ return this.vs = i, this.fs = e, this.defaultShader = this.createShader(i, e), this.defaultShader;
2429
+ }
2430
+ }
2431
+ var Xt = /* @__PURE__ */ ((s) => (s[s.EQUAL = 0] = "EQUAL", s[s.NOT_EQUAL = 1] = "NOT_EQUAL", s))(Xt || {}), Kt = /* @__PURE__ */ ((s) => (s[s.NORMAL = 0] = "NORMAL", s[s.ADD = 1] = "ADD", s[s.MULTIPLY = 2] = "MULTIPLY", s[s.SCREEN = 3] = "SCREEN", s[s.ERASE = 4] = "ERASE", s))(Kt || {}), Q = /* @__PURE__ */ ((s) => (s[s.LINEAR = 0] = "LINEAR", s[s.NEAREST = 1] = "NEAREST", s))(Q || {}), $t = /* @__PURE__ */ ((s) => (s[s.CanvasItem = 0] = "CanvasItem", s[s.Viewport = 1] = "Viewport", s))($t || {});
2432
+ class Qt {
2433
+ /** The active WebGL context. */
2434
+ gl;
2435
+ /** The target HTMLCanvasElement. */
2436
+ canvas;
2437
+ /** The current orthographic projection matrix (16 elements). */
2438
+ projection = new Float32Array(16);
2439
+ /** Indicates if the projection matrix has changed and needs to be uploaded to shaders. */
2440
+ projectionDirty = !0;
2441
+ /** The current device pixel ratio. */
2442
+ dpr;
2443
+ /** Background clear color [r, g, b, a], values range from 0 to 255. */
2444
+ backgroundColor = d.Black;
2445
+ /** Logical width in CSS pixels, used for coordinate system and projection matrix. */
2446
+ logicWidth = 0;
2447
+ /** Logical height in CSS pixels, used for coordinate system and projection matrix. */
2448
+ logicHeight = 0;
2449
+ /** Physical width (canvas actual pixels = logical width * dpr). */
2450
+ physicsWidth = 0;
2451
+ /** Physical height (canvas actual pixels = logical height * dpr). */
2452
+ physicsHeight = 0;
2453
+ /** The currently active rendering region. */
2454
+ currentRegion = null;
2455
+ /** Maximum number of texture units supported by the device. */
2456
+ maxTextureUnits = 0;
2457
+ /** Matrix stack for hierarchical transformations. */
2458
+ matrixStack = new gt(this);
2459
+ /** Direct access to the underlying matrix store. */
2460
+ matrix;
2461
+ /** Region dedicated to fast sprite rendering. */
2462
+ spriteRegion;
2463
+ /** Region dedicated to arbitrary geometry and shapes rendering. */
2464
+ graphicRegion;
2465
+ atlasSpriteRegion;
2466
+ /** Counts the number of WebGL draw calls made in the current frame. */
2467
+ drawcallCount = 0;
2468
+ /** Indicates whether we are currently writing to the stencil buffer to create a mask. */
2469
+ inCreateMask = !1;
2470
+ /** Manager for creating and organizing textures. */
2471
+ texture;
2472
+ /** Whether textures use premultiplied alpha. Set once at construction. */
2473
+ premultipliedAlpha;
2474
+ /** Default texture filtering preference and requested canvas MSAA setting. */
2475
+ antialias;
2476
+ roundPixels;
2477
+ /** Default filtering mode used when sampling textures. */
2478
+ textureFilter;
2479
+ scaleMode;
2480
+ /** Internal ping-pong RenderTextures for multi-filter chains. */
2481
+ _filterRT = [null, null];
2482
+ _filterInput = new C();
2483
+ get height() {
2484
+ return this.logicHeight;
2485
+ }
2486
+ get width() {
2487
+ return this.logicWidth;
2488
+ }
2489
+ /**
2490
+ * Creates a new Rapid application instance.
2491
+ * @param options Initialization options including the target canvas.
2492
+ */
2493
+ constructor(t) {
2494
+ this.canvas = t.canvas, this.dpr = window.devicePixelRatio || 1, this.antialias = t.antialias ?? !1, this.textureFilter = t.textureFilter ?? 1, this.premultipliedAlpha = t.premultipliedAlpha ?? !0, this.roundPixels = t.roundPixels ?? !1, this.scaleMode = t.scaleMode ?? 0;
2495
+ const e = Rt(this.canvas, this.antialias, this.premultipliedAlpha);
2496
+ this.gl = e, this.maxTextureUnits = e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS), this.matrix = this.matrixStack.matrix, this.spriteRegion = new dt(this), this.graphicRegion = new Ot(this), this.atlasSpriteRegion = new zt(this);
2497
+ const i = this.canvas.clientWidth || this.canvas.width, r = this.canvas.clientHeight || this.canvas.height;
2498
+ this.physicsWidth = t.physicsWidth || Math.round(i * this.dpr), this.physicsHeight = t.physicsHeight || Math.round(r * this.dpr), this.logicWidth = t.logicWidth || this.physicsWidth / this.dpr, this.logicHeight = t.logicHeight || this.physicsHeight / this.dpr, this.resize(this.logicWidth, this.logicHeight, this.physicsWidth, this.physicsHeight), this.texture = new vt(this), t.backgroundColor && (this.backgroundColor = t.backgroundColor), e.enable(e.BLEND), this.premultipliedAlpha ? e.blendFunc(e.ONE, e.ONE_MINUS_SRC_ALPHA) : e.blendFunc(e.SRC_ALPHA, e.ONE_MINUS_SRC_ALPHA), e.enable(e.STENCIL_TEST), e.stencilFunc(e.ALWAYS, 1, 255), e.stencilOp(e.KEEP, e.KEEP, e.KEEP);
2499
+ }
2500
+ setTextureFilter(t) {
2501
+ this.textureFilter = t;
2502
+ }
2503
+ setAntialias(t) {
2504
+ this.antialias = t;
2505
+ }
2506
+ getColorUint32(t) {
2507
+ return t ? this.premultipliedAlpha ? t.premultipliedUint32 : t.uint32 : 4294967295;
2508
+ }
2509
+ /**
2510
+ * Enters a specific rendering region, flushing the previous one if necessary.
2511
+ * @param region The rendering region to enter.
2512
+ * @param customShader An optional custom shader to use for this region.
2513
+ */
2514
+ enterRegion(t, e) {
2515
+ this.currentRegion === t && this.currentRegion.isSameShader(e) || (this.flush(), this.currentRegion = t, t.enter(e));
2516
+ }
2517
+ drawSprite(t) {
2518
+ kt(this, t);
2519
+ }
2520
+ drawLine(t) {
2521
+ Gt(this, t);
2522
+ }
2523
+ drawGraphic(t) {
2524
+ mt(this, t);
2525
+ }
2526
+ drawRect(t) {
2527
+ Vt(this, t);
2528
+ }
2529
+ drawCircle(t) {
2530
+ Yt(this, t);
2531
+ }
2532
+ /**
2533
+ * Starts rendering arbitrary graphics geometries.
2534
+ * @param drawMode The WebGL drawing mode (e.g., gl.TRIANGLES, gl.TRIANGLE_FAN).
2535
+ * @param texture An optional texture applied to the graphic vertices.
2536
+ * @param customShader An optional custom shader overriding the region's default shader.
2537
+ */
2538
+ startGraphic(t = this.gl.TRIANGLES, e, i, r) {
2539
+ this.enterRegion(this.graphicRegion, i), this.graphicRegion.startGraphic(r ?? this.matrixStack.curWorldM, t, e);
2540
+ }
2541
+ /**
2542
+ * Starts rendering graphics explicitly for use as a mask, overriding the shader.
2543
+ * @param drawMode The WebGL drawing mode (e.g., gl.TRIANGLES).
2544
+ * @param texture An optional texture whose alpha channel may dictate masking rules.
2545
+ */
2546
+ startMaskGraphic(t = this.gl.TRIANGLES, e, i) {
2547
+ this.startGraphic(t, e, this.graphicRegion.maskShader, i);
2548
+ }
2549
+ /**
2550
+ * Utility method: Draws an image directly as a mask using a generic rectangle geometry.
2551
+ * @param texture The texture to be used as a mask.
2552
+ */
2553
+ drawMaskImage(t) {
2554
+ Ht(this, t);
2555
+ }
2556
+ /**
2557
+ * Pushes vertices for a rectangle geometry. Should be enclosed by startGraphic and endGraphic.
2558
+ * @param w The width of the rectangle.
2559
+ * @param h The height of the rectangle.
2560
+ * @param color An optional tint color.
2561
+ */
2562
+ addRectVertex(t, e, i) {
2563
+ const r = this.getColorUint32(i);
2564
+ this.addGraphicVertex(0, 0, 0, 0, r), this.addGraphicVertex(t, 0, 1, 0, r), this.addGraphicVertex(t, e, 1, 1, r), this.addGraphicVertex(0, e, 0, 1, r);
2565
+ }
2566
+ /**
2567
+ * Pushes vertices for a circle geometry using TRIANGLES or similar primitives.
2568
+ * @param r The radius of the circle.
2569
+ * @param color An optional tint color.
2570
+ * @param segments The number of segments (polygons) used to approximate the circle.
2571
+ */
2572
+ addCircleVertex(t, e, i = 32) {
2573
+ const r = Math.PI * 2 / i, n = this.getColorUint32(e);
2574
+ for (let a = 0; a < i; a++) {
2575
+ const o = r * a, h = Math.cos(o) * t, u = Math.sin(o) * t, c = 0.5 + Math.cos(o) * 0.5, m = 0.5 + Math.sin(o) * 0.5;
2576
+ this.addGraphicVertex(h, u, c, m, n);
2577
+ }
2578
+ }
2579
+ /**
2580
+ * Adds an individual vertex to the current graphics batch.
2581
+ * @param x The relative X coordinate of the vertex.
2582
+ * @param y The relative Y coordinate of the vertex.
2583
+ * @param u The U texture coordinate (0 to 1).
2584
+ * @param v The V texture coordinate (0 to 1).
2585
+ * @param color The vertex color as a 32-bit unsigned integer.
2586
+ */
2587
+ addGraphicVertex(t, e, i = 0, r = 0, n = 4294967295) {
2588
+ this.graphicRegion.addVertex(t, e, i, r, n);
2589
+ }
2590
+ /**
2591
+ * Ends the current graphics geometry definition, readying it for rendering.
2592
+ */
2593
+ endGraphic() {
2594
+ this.graphicRegion.endGraphic();
2595
+ }
2596
+ /**
2597
+ * Resizes the canvas, updates internal viewport values, and recreates projection boundaries.
2598
+ * @param logicWidth The new logical display width.
2599
+ * @param logicHeight The new logical display height.
2600
+ * @param cssWidth Optional CSS display width.
2601
+ * @param cssHeight Optional CSS display height.
2602
+ */
2603
+ resize(t, e, i, r) {
2604
+ this.flush();
2605
+ const n = i ?? this.canvas.clientWidth ?? this.canvas.width, a = r ?? this.canvas.clientHeight ?? this.canvas.height;
2606
+ switch (i !== void 0 && (this.canvas.style.width = n + "px"), r !== void 0 && (this.canvas.style.height = a + "px"), this.physicsWidth = n * this.dpr, this.physicsHeight = a * this.dpr, this.logicWidth = t, this.logicHeight = e, this.scaleMode) {
2607
+ case 1:
2608
+ this.canvas.width = t, this.canvas.height = e, this.canvas.style.imageRendering = "pixelated", this.gl.viewport(0, 0, t, e);
2609
+ break;
2610
+ case 0:
2611
+ this.canvas.width = this.physicsWidth, this.canvas.height = this.physicsHeight, this.canvas.style.imageRendering = "auto", this.gl.viewport(0, 0, this.physicsWidth, this.physicsHeight);
2612
+ break;
2613
+ default:
2614
+ throw new Error("scaleMode can only be CanvasScaleMode.Viewport or CanvasScaleMode.CanvasItem");
2615
+ }
2616
+ this.updateProjection(0, this.logicWidth, this.logicHeight, 0);
2617
+ }
2618
+ /**
2619
+ * Updates the projection matrix using an orthographic mapping.
2620
+ * Automatically sets local flag projectionDirty.
2621
+ */
2622
+ updateProjection(t, e, i, r) {
2623
+ this.updateOrthMatrix(this.projection, t, e, i, r), this.projectionDirty = !0;
2624
+ }
2625
+ /**
2626
+ * Clears the active framebuffer applying the default background color.
2627
+ */
2628
+ clear() {
2629
+ const t = this.gl;
2630
+ this.backgroundColor.setClearColor(t), t.clear(t.COLOR_BUFFER_BIT | t.STENCIL_BUFFER_BIT), this.drawcallCount = 0, this.matrixStack.reset();
2631
+ }
2632
+ /**
2633
+ * Populates an orthographic projection matrix in place.
2634
+ * Avoids continuous Float32Array allocations for performance reasons.
2635
+ */
2636
+ updateOrthMatrix(t, e, i, r, n) {
2637
+ t[0] = 2 / (i - e), t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = 2 / (n - r), t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[10] = -1, t[11] = 0, t[12] = -(i + e) / (i - e), t[13] = -(n + r) / (n - r), t[14] = 0, t[15] = 1;
2638
+ }
2639
+ /**
2640
+ * Flushes currently buffered rendering operations across all active regions.
2641
+ */
2642
+ flush() {
2643
+ this.currentRegion && (this.currentRegion.exit(), this.currentRegion = null);
2644
+ }
2645
+ /**
2646
+ * Applies a chain of shaders to a texture sequentially using ping-pong RenderTextures.
2647
+ * Each shader receives the output of the previous one as its input.
2648
+ * Two internal RenderTextures are reused across calls (resized as needed).
2649
+ *
2650
+ * @param source The input texture to start the filter chain from.
2651
+ * @param shaders An ordered array of CustomGlShader to apply in sequence.
2652
+ * @returns The RenderTexture containing the final filtered result.
2653
+ * Draw it with `rapid.drawSprite({ texture: result })` to display it on screen.
2654
+ *
2655
+ * @example
2656
+ * const result = rapid.applyFilters(tex, [blurShader, outlineShader]);
2657
+ * rapid.drawSprite({ texture: result });
2658
+ */
2659
+ applyFilters(t, e) {
2660
+ if (e.length === 0)
2661
+ throw new Error("applyFilters: shaders array must not be empty.");
2662
+ const i = Math.max(...e.map((u) => u.padding)), r = i, n = t.rawWidth + i * 2, a = t.rawHeight + i * 2;
2663
+ for (let u = 0; u < 2; u++)
2664
+ this._filterRT[u] ? this._filterRT[u].resize(n, a) : this._filterRT[u] = this.texture.createRenderTexture({ width: n, height: a });
2665
+ let o = t.clone(this._filterInput), h = 0;
2666
+ this.matrixStack.save(), this.matrixStack.identity();
2667
+ for (let u = 0; u < e.length; u++) {
2668
+ const c = this._filterRT[h % 2];
2669
+ c.offsetX = 0, c.offsetY = 0, this.enterRenderTexture(c), this.clearRenderTexture(), this.drawSprite({
2670
+ texture: o,
2671
+ shader: e[u],
2672
+ offsetX: u == 0 ? r : 0,
2673
+ // padding back
2674
+ offsetY: u == 0 ? r : 0,
2675
+ padding: i
2676
+ }), this.leaveRenderTexture(), o = c, h++;
2677
+ }
2678
+ return this.matrixStack.restore(), o.offsetX = -r, o.offsetY = -r, o;
2679
+ }
2680
+ enterRenderTexture(t) {
2681
+ this.flush(), t.activate(), this.gl.viewport(0, 0, t.rawWidth, t.rawHeight), this.updateProjection(0, t.rawWidth, t.rawHeight, 0);
2682
+ }
2683
+ /**
2684
+ * Clears a RenderTexture to a solid color.
2685
+ * Must be called while the RT is the active render target (i.e. inside enterRenderTexture/leaveRenderTexture).
2686
+ * Can also be called standalone — it will bind the RT, clear it, but NOT restore the main framebuffer.
2687
+ * @param rt The render texture to clear.
2688
+ * @param color Clear color. Defaults to transparent black (0, 0, 0, 0).
2689
+ */
2690
+ clearRenderTexture(t = new d(0, 0, 0, 0)) {
2691
+ this.flush();
2692
+ const e = this.gl;
2693
+ t.setClearColor(e), e.clear(e.COLOR_BUFFER_BIT);
2694
+ }
2695
+ /**
2696
+ * Completes rendering to an offscreen render texture and reverts rendering back to the main canvas.
2697
+ */
2698
+ leaveRenderTexture() {
2699
+ this.flush(), this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null), this.gl.viewport(0, 0, this.physicsWidth, this.physicsHeight), this.updateProjection(0, this.logicWidth, this.logicHeight, 0);
2700
+ }
2701
+ /**
2702
+ * Convenience wrapper: enters a RenderTexture, optionally clears it, runs a callback, then leaves.
2703
+ * @param rt The RenderTexture to render into.
2704
+ * @param cb The callback containing draw calls to execute inside the RT.
2705
+ * @param color Clear color before rendering. Pass `null` to skip clearing. Defaults to transparent black.
2706
+ *
2707
+ * @example
2708
+ * rapid.drawToRenderTexture(myRT, () => {
2709
+ * rapid.drawSprite({ texture: mySprite });
2710
+ * });
2711
+ */
2712
+ drawToRenderTexture(t, e, i = new d(0, 0, 0, 0)) {
2713
+ this.enterRenderTexture(t);
2714
+ try {
2715
+ i !== null && this.clearRenderTexture(i), e();
2716
+ } finally {
2717
+ this.leaveRenderTexture();
2718
+ }
2719
+ }
2720
+ /**
2721
+ * Convenience wrapper: writes a mask using the stencil buffer, then renders within it, then exits the mask.
2722
+ * @param maskCb Callback that defines the mask geometry (drawn to stencil, not visible).
2723
+ * @param drawCb Callback containing the actual draw calls masked by the stencil.
2724
+ * @param type Mask type: EQUAL (draw inside) or NOT_EQUAL (draw outside). Defaults to EQUAL.
2725
+ * @param ref Stencil reference value. Defaults to 1.
2726
+ *
2727
+ * @example
2728
+ * rapid.withMask(
2729
+ * () => rapid.drawRect({ width: 200, height: 200 }),
2730
+ * () => rapid.drawSprite({ texture: myTexture }),
2731
+ * );
2732
+ */
2733
+ withMask(t, e, i = 0, r = 1) {
2734
+ this.clearMask(), this.startDrawMask(r);
2735
+ try {
2736
+ t();
2737
+ } finally {
2738
+ this.endDrawMask();
2739
+ }
2740
+ this.enterMask(i, r);
2741
+ try {
2742
+ e();
2743
+ } finally {
2744
+ this.exitMask();
2745
+ }
2746
+ }
2747
+ /**
2748
+ * Convenience wrapper: saves the matrix stack, applies an optional transform, runs a callback, then restores.
2749
+ * When `transform` is provided, delegates to `matrixStack.applyTransform()` which handles
2750
+ * position, rotation, scale, offset, and origin automatically.
2751
+ * @param cb The callback to execute within the saved transform context.
2752
+ * @param transform Optional transform options to apply before running the callback.
2753
+ * @param width The logical width used to resolve `origin` anchoring. Defaults to 0.
2754
+ * @param height The logical height used to resolve `origin` anchoring. Defaults to 0.
2755
+ *
2756
+ * @example
2757
+ * // Simple save/restore
2758
+ * rapid.withTransform(() => {
2759
+ * rapid.matrixStack.translate(100, 100);
2760
+ * rapid.drawSprite({ texture: myTexture });
2761
+ * });
2762
+ *
2763
+ * @example
2764
+ * // With a transform applied
2765
+ * rapid.withTransform(() => {
2766
+ * rapid.drawSprite({ texture: myTexture });
2767
+ * }, { x: 100, y: 50, rotation: Math.PI / 4, origin: 0.5 }, myTexture.width, myTexture.height);
2768
+ */
2769
+ withTransform(t, e, i = 0, r = 0) {
2770
+ if (e) {
2771
+ const n = e.saveTransform ?? !0;
2772
+ try {
2773
+ this.matrixStack.applyTransform(e, i, r), t();
2774
+ } finally {
2775
+ n && this.matrixStack.restore();
2776
+ }
2777
+ } else {
2778
+ this.matrixStack.save();
2779
+ try {
2780
+ t();
2781
+ } finally {
2782
+ this.matrixStack.restore();
2783
+ }
2784
+ }
2785
+ }
2786
+ /**
2787
+ * Convenience wrapper: enables scissor clipping for a region, runs a callback, then disables it.
2788
+ * @param x Left edge in logical pixels.
2789
+ * @param y Top edge in logical pixels.
2790
+ * @param width Width in logical pixels.
2791
+ * @param height Height in logical pixels.
2792
+ * @param cb The callback to execute within the scissor region.
2793
+ *
2794
+ * @example
2795
+ * rapid.withScissor(50, 50, 300, 200, () => {
2796
+ * rapid.drawSprite({ texture: myTexture });
2797
+ * });
2798
+ */
2799
+ withScissor(t, e, i, r, n) {
2800
+ this.startScissor(t, e, i, r);
2801
+ try {
2802
+ n();
2803
+ } finally {
2804
+ this.endScissor();
2805
+ }
2806
+ }
2807
+ /**
2808
+ * Convenience wrapper: sets a blend mode, runs a callback, then restores NORMAL blend mode.
2809
+ * @param mode The BlendMode to apply for the duration of the callback.
2810
+ * @param cb The callback to execute under the given blend mode.
2811
+ *
2812
+ * @example
2813
+ * rapid.withBlendMode(BlendMode.ADD, () => {
2814
+ * rapid.drawSprite({ texture: glowTexture });
2815
+ * });
2816
+ */
2817
+ withBlendMode(t, e) {
2818
+ this.setBlendMode(t);
2819
+ try {
2820
+ e();
2821
+ } finally {
2822
+ this.setBlendMode(
2823
+ 0
2824
+ /* NORMAL */
2825
+ );
2826
+ }
2827
+ }
2828
+ /**
2829
+ * Starts drawing into the stencil buffer to construct a rendering mask.
2830
+ * @param ref The stencil reference value.
2831
+ * @param mask The stencil bitmask.
2832
+ */
2833
+ startDrawMask(t = 1, e = 255) {
2834
+ this.flush();
2835
+ const i = this.gl;
2836
+ i.stencilMask(e), i.colorMask(!1, !1, !1, !1), i.stencilFunc(i.ALWAYS, t, e), i.stencilOp(i.KEEP, i.KEEP, i.REPLACE), this.enterRegion(this.graphicRegion, this.graphicRegion.maskShader), this.inCreateMask = !0;
2837
+ }
2838
+ /**
2839
+ * Finishes the mask drawing phase and restores color buffer writing.
2840
+ */
2841
+ endDrawMask() {
2842
+ this.flush();
2843
+ const t = this.gl;
2844
+ t.colorMask(!0, !0, !0, !0), t.stencilMask(0), this.inCreateMask = !1;
2845
+ }
2846
+ /**
2847
+ * Clears bounds created into the stencil mask.
2848
+ * @param mask The bitmask specifying which stencil layer to clear.
2849
+ */
2850
+ clearMask(t = 255) {
2851
+ this.flush();
2852
+ const e = this.gl;
2853
+ e.stencilMask(t), e.clear(e.STENCIL_BUFFER_BIT), this.inCreateMask = !1;
2854
+ }
2855
+ /**
2856
+ * Enters a constrained rendering phase masked by the existing stencil buffer values.
2857
+ * @param type Equality check type. Use "equal" to draw inside the mask, or "notEqual" to draw outside.
2858
+ * @param ref The reference value to test against.
2859
+ * @param mask The bitmask specifying which stencil bits to consider.
2860
+ */
2861
+ enterMask(t, e = 1, i = 255) {
2862
+ this.flush();
2863
+ const r = this.gl;
2864
+ r.colorMask(!0, !0, !0, !0), r.stencilMask(0), r.stencilFunc(t === 0 ? r.EQUAL : r.NOTEQUAL, e, i), r.stencilOp(r.KEEP, r.KEEP, r.KEEP), this.inCreateMask = !1;
2865
+ }
2866
+ /**
2867
+ * Exits the masked rendering phase, restoring default full-screen stencil values tests.
2868
+ */
2869
+ exitMask() {
2870
+ this.flush();
2871
+ const t = this.gl;
2872
+ t.colorMask(!0, !0, !0, !0), t.stencilMask(255), t.stencilFunc(t.ALWAYS, 1, 255), t.stencilOp(t.KEEP, t.KEEP, t.KEEP), this.inCreateMask = !1;
2873
+ }
2874
+ /**
2875
+ * Configures the global WebGL blending behavior.
2876
+ * @param mode The targeted BlendMode to switch onto.
2877
+ */
2878
+ setBlendMode(t) {
2879
+ this.flush();
2880
+ const e = this.gl;
2881
+ switch (t) {
2882
+ case 0:
2883
+ this.premultipliedAlpha ? e.blendFuncSeparate(e.ONE, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA) : e.blendFuncSeparate(e.SRC_ALPHA, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA);
2884
+ break;
2885
+ case 1:
2886
+ e.blendFuncSeparate(e.ONE, e.ONE, e.ONE, e.ONE);
2887
+ break;
2888
+ case 2:
2889
+ e.blendFuncSeparate(e.DST_COLOR, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA);
2890
+ break;
2891
+ case 3:
2892
+ e.blendFuncSeparate(e.ONE, e.ONE_MINUS_SRC_COLOR, e.ONE, e.ONE_MINUS_SRC_ALPHA);
2893
+ break;
2894
+ case 4:
2895
+ e.blendFuncSeparate(e.ZERO, e.ONE_MINUS_SRC_ALPHA, e.ZERO, e.ONE_MINUS_SRC_ALPHA);
2896
+ break;
2897
+ }
2898
+ }
2899
+ /**
2900
+ * Enables rectangular scissor clipping. Only pixels within the specified
2901
+ * rectangle (in logical coordinates) will be rendered.
2902
+ * Coordinates use the same system as your drawing calls (top-left origin).
2903
+ * @param x Left edge in logical pixels.
2904
+ * @param y Top edge in logical pixels.
2905
+ * @param width Width in logical pixels.
2906
+ * @param height Height in logical pixels.
2907
+ */
2908
+ startScissor(t, e, i, r) {
2909
+ this.flush();
2910
+ const n = this.gl, a = this.physicsWidth / this.logicWidth, o = this.physicsHeight / this.logicHeight, h = Math.round(t * a), u = Math.round(this.physicsHeight - (e + r) * o), c = Math.round(i * a), m = Math.round(r * o);
2911
+ n.enable(n.SCISSOR_TEST), n.scissor(h, u, c, m);
2912
+ }
2913
+ /**
2914
+ * Disables scissor clipping, restoring full-canvas rendering.
2915
+ */
2916
+ endScissor() {
2917
+ this.flush(), this.gl.disable(this.gl.SCISSOR_TEST);
2918
+ }
2919
+ renderCamera(t) {
2920
+ this.matrixStack.applyTransform(t);
2921
+ const e = this.matrixStack.curWorldM;
2922
+ this.matrix.invert(e);
2923
+ }
2924
+ logicToPhysics(t) {
2925
+ return t.multiply(new l(
2926
+ this.physicsWidth / this.logicWidth,
2927
+ this.physicsHeight / this.logicHeight
2928
+ ));
2929
+ }
2930
+ physicsToLogic(t) {
2931
+ return t.multiply(new l(
2932
+ this.logicWidth / this.physicsWidth,
2933
+ this.logicHeight / this.physicsHeight
2934
+ ));
2935
+ }
2936
+ cssToDevicePixel(t) {
2937
+ return t.mul(this.dpr);
2938
+ }
2939
+ cssToLogic(t) {
2940
+ const e = this.cssToDevicePixel(t);
2941
+ return this.physicsToLogic(e);
2942
+ }
2943
+ devicePixelToCss(t) {
2944
+ return t.divide(this.dpr);
2945
+ }
2946
+ /**
2947
+ * Exports a world matrix as a CSS `matrix(...)` string, ready to assign
2948
+ * directly to a DOM element's `style.transform`.
2949
+ * Unlike `matrixStack.toCSSMatrix()`, this bakes in the logic-pixel → CSS-pixel
2950
+ * scale (accounting for dpr and a custom `logicWidth`/`logicHeight`), so the
2951
+ * result lines up with the canvas without any extra `scale(...)` on your end.
2952
+ * @param index - Matrix index to export. Defaults to the current world matrix.
2953
+ */
2954
+ toCSSMatrix(t) {
2955
+ const e = this.physicsWidth / this.dpr / this.logicWidth, i = this.physicsHeight / this.dpr / this.logicHeight;
2956
+ return this.matrix.toCSSMatrix(t ?? this.matrixStack.curWorldM, e, i);
2957
+ }
2958
+ }
2959
+ function jt(s) {
2960
+ return !(s === null || typeof s != "object" || Array.isArray(s) || s instanceof l || s instanceof d);
2961
+ }
2962
+ class b {
2963
+ /** Random float in [min, max). */
2964
+ static float(t, e) {
2965
+ return Math.random() * (e - t) + t;
2966
+ }
2967
+ /** Random integer in [min, max] (inclusive). */
2968
+ static int(t, e) {
2969
+ return Math.floor(Math.random() * (e - t + 1)) + t;
2970
+ }
2971
+ /** Random angle in [0, 2π). */
2972
+ static angle() {
2973
+ return Math.random() * Math.PI * 2;
2974
+ }
2975
+ /** Random Vec2 with components in the supplied per-axis ranges. */
2976
+ static vector(t, e, i, r) {
2977
+ return new l(b.float(t, e), b.float(i, r));
2978
+ }
2979
+ /** Random Color with each component interpolated between minColor and maxColor. */
2980
+ static randomColor(t, e) {
2981
+ return new d(
2982
+ b.float(t.r, e.r),
2983
+ b.float(t.g, e.g),
2984
+ b.float(t.b, e.b),
2985
+ b.float(t.a, e.a)
2986
+ );
2987
+ }
2988
+ /** Pick a random element from an array with uniform probability. */
2989
+ static pick(t) {
2990
+ return t[b.int(0, t.length - 1)];
2991
+ }
2992
+ /**
2993
+ * Pick a random element using weighted probability.
2994
+ * @param array - Pairs of [item, weight]. Higher weight = more likely.
2995
+ */
2996
+ static pickWeight(t) {
2997
+ if (!t || t.length === 0) return null;
2998
+ let e = 0;
2999
+ for (const [, r] of t) e += r;
3000
+ let i = Math.random() * e;
3001
+ for (const [r, n] of t)
3002
+ if (i -= n, i <= 0) return r;
3003
+ return t[t.length - 1][0];
3004
+ }
3005
+ /**
3006
+ * Resolves a scalar-or-range value to a concrete T.
3007
+ * - `undefined` → `defaultValue`
3008
+ * - `T` → `T` (or a clone for objects)
3009
+ * - `[T, T]` → random value between the two bounds
3010
+ */
3011
+ static scalarOrRange(t, e) {
3012
+ if (t == null) return e;
3013
+ if (Array.isArray(t)) {
3014
+ const [i, r] = t;
3015
+ if (typeof i == "number")
3016
+ return b.float(i, r);
3017
+ if (i instanceof l)
3018
+ return b.vector(i.x, r.x, i.y, r.y);
3019
+ if (i instanceof d)
3020
+ return b.randomColor(i, r);
3021
+ }
3022
+ return typeof t == "number" ? t : t.clone();
3023
+ }
3024
+ }
3025
+ var qt = /* @__PURE__ */ ((s) => (s.POINT = "point", s.CIRCLE = "circle", s.RECT = "rect", s))(qt || {});
3026
+ const ht = 10, ot = 0, ut = !0;
3027
+ class Zt {
3028
+ life = 0;
3029
+ maxLife;
3030
+ texture;
3031
+ options;
3032
+ position;
3033
+ datas;
3034
+ rapid;
3035
+ constructor(t, e) {
3036
+ this.rapid = t, this.options = e, e.texture instanceof C ? this.texture = e.texture : e.texture instanceof Array && e.texture[0] instanceof Array ? this.texture = b.pickWeight(e.texture) : e.texture instanceof Array && (this.texture = b.pick(e.texture)), this.maxLife = b.scalarOrRange(e.life, 1), this.datas = {
3037
+ speed: this.processAttribute(e.animation.speed, 0),
3038
+ rotation: this.processAttribute(e.animation.rotation, 0),
3039
+ scale: this.processAttribute(e.animation.scale, 1),
3040
+ color: this.processAttribute(e.animation.color, d.White.clone()),
3041
+ velocity: this.processAttribute(e.animation.velocity, l.ZERO),
3042
+ acceleration: this.processAttribute(e.animation.acceleration, l.ZERO)
3043
+ }, this.position = l.ZERO, this.initializePosition();
3044
+ }
3045
+ processAttribute(t, e) {
3046
+ if (t == null)
3047
+ return { value: e };
3048
+ if (jt(t)) {
3049
+ const i = t, r = b.scalarOrRange(i.start, e), n = i.end === void 0 ? r : b.scalarOrRange(i.end, e);
3050
+ return {
3051
+ delta: i.delta ?? this.getDelta(r, n, this.maxLife),
3052
+ value: r,
3053
+ damping: i.damping
3054
+ };
3055
+ } else
3056
+ return this.processAttribute({ start: t }, e);
3057
+ }
3058
+ updateNumberAttribute(t, e) {
3059
+ t.damping !== void 0 && (t.value *= Math.pow(t.damping, e)), t.delta !== void 0 && (t.value += t.delta * e);
3060
+ }
3061
+ updateVec2Attribute(t, e) {
3062
+ t.damping !== void 0 && (t.value = t.value.multiply(Math.pow(t.damping, e))), t.delta !== void 0 && (t.value = t.value.add(t.delta.multiply(e)));
3063
+ }
3064
+ updateColorAttribute(t, e) {
3065
+ t.damping !== void 0 && (t.value = t.value.multiply(Math.pow(t.damping, e))), t.delta !== void 0 && (t.value = t.value.add(t.delta.multiply(e))), t.value.clamp();
3066
+ }
3067
+ updateAttributes(t) {
3068
+ const e = this.datas;
3069
+ this.updateNumberAttribute(e.speed, t), this.updateNumberAttribute(e.rotation, t), this.updateNumberAttribute(e.scale, t), this.updateColorAttribute(e.color, t), this.updateVec2Attribute(e.velocity, t), this.updateVec2Attribute(e.acceleration, t), e.velocity.value = e.velocity.value.add(
3070
+ e.acceleration.value.multiply(t)
3071
+ );
3072
+ const r = l.fromAngle(e.rotation.value).multiply(e.speed.value * t);
3073
+ this.position = this.position.add(r).add(e.velocity.value.multiply(t));
3074
+ }
3075
+ getDelta(t, e, i) {
3076
+ return typeof t == "number" && typeof e == "number" ? (e - t) / i : t instanceof l && e instanceof l || t instanceof d && e instanceof d ? e.subtract(t).divide(i) : t;
3077
+ }
3078
+ /**
3079
+ * Updates particle state.
3080
+ * @param deltaTime - Seconds elapsed since last frame
3081
+ * @returns `true` while the particle is alive, `false` when it should be removed
3082
+ */
3083
+ update(t) {
3084
+ return this.life += t, this.life >= this.maxLife ? !1 : (this.updateAttributes(t), !0);
3085
+ }
3086
+ /**
3087
+ * Renders the particle using the Rapid engine's matrixStack + drawSprite.
3088
+ * The emitter is responsible for calling save/restore around a batch of particles.
3089
+ */
3090
+ render() {
3091
+ const t = this.rapid.matrixStack, e = this.datas.scale.value, i = this.datas.rotation.value, r = this.datas.color.value;
3092
+ t.save(), t.translate(this.position.x, this.position.y), i !== 0 && t.rotate(i), e !== 1 && t.scale(e, e);
3093
+ const n = this.options.origin ? this.options.origin.x : 0.5, a = this.options.origin ? this.options.origin.y : 0.5;
3094
+ (n !== 0 || a !== 0) && t.translate(-n * this.texture.rawWidth, -a * this.texture.rawHeight), ft(this.rapid, { texture: this.texture, color: r }), t.restore();
3095
+ }
3096
+ initializePosition() {
3097
+ switch (this.options.emitShape) {
3098
+ case "circle": {
3099
+ const t = Math.random() * Math.PI * 2, e = (this.options.emitRadius ?? 0) * Math.sqrt(Math.random());
3100
+ this.position = new l(Math.cos(t) * e, Math.sin(t) * e);
3101
+ break;
3102
+ }
3103
+ case "rect": {
3104
+ this.position = new l(
3105
+ (Math.random() - 0.5) * (this.options.emitRect?.width ?? 0),
3106
+ (Math.random() - 0.5) * (this.options.emitRect?.height ?? 0)
3107
+ );
3108
+ break;
3109
+ }
3110
+ default:
3111
+ this.position = l.ZERO;
3112
+ break;
3113
+ }
3114
+ !this.options.localSpace && this.options.position && (this.position = this.position.add(this.options.position));
3115
+ }
3116
+ }
3117
+ class xt {
3118
+ particles = [];
3119
+ options;
3120
+ emitting = !1;
3121
+ emitTimer = 0;
3122
+ emitRate = ht;
3123
+ emitTime = ot;
3124
+ emitTimeCounter = 0;
3125
+ /** Whether spawned particles are positioned in local emitter space (default: true). */
3126
+ localSpace = ut;
3127
+ /** World position of the emitter. Used for both local-space transform and world-space spawn offset. */
3128
+ position = l.ZERO;
3129
+ rapid;
3130
+ gameObject;
3131
+ /**
3132
+ * Creates a new particle emitter.
3133
+ * @param rapid - The Rapid renderer instance
3134
+ * @param options - Emitter configuration options
3135
+ */
3136
+ constructor(t, e) {
3137
+ this.rapid = t, this.options = e, this.emitRate = e.emitRate !== void 0 ? e.emitRate : ht, this.emitTime = e.emitTime !== void 0 ? e.emitTime : ot, this.localSpace = e.localSpace !== void 0 ? e.localSpace : ut, this.position = e.position ?? l.ZERO;
3138
+ }
3139
+ /** Replaces the emitter's texture at runtime. */
3140
+ setTexture(t) {
3141
+ this.options.texture = t;
3142
+ }
3143
+ /**
3144
+ * Creates a new emitter sharing the same options object.
3145
+ * Particle state is NOT copied.
3146
+ */
3147
+ clone() {
3148
+ return new xt(this.rapid, { ...this.options });
3149
+ }
3150
+ /** Sets particles-per-second emission rate (continuous mode). */
3151
+ setEmitRate(t) {
3152
+ this.emitRate = t;
3153
+ }
3154
+ /** Sets the burst interval in seconds (0 = continuous). */
3155
+ setEmitTime(t) {
3156
+ this.emitTime = t;
3157
+ }
3158
+ /** Starts continuous particle emission. */
3159
+ start() {
3160
+ this.emitting = !0, this.emitTimeCounter = 0;
3161
+ }
3162
+ /** Stops new particle emission; existing particles finish their lifecycle. */
3163
+ stop() {
3164
+ this.emitting = !1;
3165
+ }
3166
+ /** Removes all particles and resets timers. */
3167
+ clear() {
3168
+ this.particles = [], this.emitTimeCounter = 0;
3169
+ }
3170
+ /**
3171
+ * Spawns `count` particles immediately.
3172
+ * Respects `maxParticles` if set.
3173
+ */
3174
+ emit(t) {
3175
+ const e = this.options.maxParticles ?? 1 / 0, i = Math.min(t, e - this.particles.length);
3176
+ for (let r = 0; r < i; r++)
3177
+ this.particles.push(new Zt(this.rapid, { ...this.options }));
3178
+ }
3179
+ /**
3180
+ * Updates the emitter and all live particles.
3181
+ * @param deltaTime - Seconds elapsed since last frame
3182
+ */
3183
+ update(t) {
3184
+ if (this.emitting && this.emitRate > 0)
3185
+ if (this.emitTime > 0) {
3186
+ if (this.emitTimeCounter += t, this.emitTimeCounter >= this.emitTime) {
3187
+ const e = Math.floor(this.emitTimeCounter / this.emitTime);
3188
+ this.emit(this.emitRate * e), this.emitTimeCounter -= e * this.emitTime;
3189
+ }
3190
+ } else {
3191
+ this.emitTimer += t;
3192
+ const e = Math.floor(this.emitTimer * this.emitRate);
3193
+ e > 0 && (this.emit(e), this.emitTimer -= e / this.emitRate);
3194
+ }
3195
+ for (let e = this.particles.length - 1; e >= 0; e--)
3196
+ this.particles[e].update(t) || this.particles.splice(e, 1);
3197
+ }
3198
+ /**
3199
+ * Renders all live particles.
3200
+ * In local-space mode the emitter's own transform is applied around the batch.
3201
+ */
3202
+ render() {
3203
+ const t = this.rapid.matrixStack;
3204
+ this.localSpace && (t.save(), t.translate(this.position.x, this.position.y));
3205
+ for (const e of this.particles)
3206
+ e.render();
3207
+ this.localSpace && t.restore();
3208
+ }
3209
+ /** Returns the current number of live particles. */
3210
+ getParticleCount() {
3211
+ return this.particles.length;
3212
+ }
3213
+ /** Returns `true` if the emitter is running or still has live particles. */
3214
+ isActive() {
3215
+ return this.emitting || this.particles.length > 0;
3216
+ }
3217
+ /**
3218
+ * Convenience: emit `emitRate` particles in one shot (fire-and-forget burst).
3219
+ */
3220
+ oneShot() {
3221
+ this.emit(this.emitRate);
3222
+ }
3223
+ }
3224
+ export {
3225
+ N as ArrayType,
3226
+ D as BaseTexture,
3227
+ Kt as BlendMode,
3228
+ $t as CanvasScaleMode,
3229
+ d as Color,
3230
+ rt as CustomGlShader,
3231
+ W as DynamicArrayBuffer,
3232
+ ct as GLShader,
3233
+ Ot as GraphicRegion,
3234
+ Xt as MaskType,
3235
+ gt as MatrixStack,
3236
+ pt as MatrixStore,
3237
+ Zt as Particle,
3238
+ xt as ParticleEmitter,
3239
+ qt as ParticleShape,
3240
+ Qt as Rapid,
3241
+ lt as Region,
3242
+ Tt as RenderTexture,
3243
+ dt as SpriteRegion,
3244
+ Et as TextTexture,
3245
+ C as Texture,
3246
+ Q as TextureFilterMode,
3247
+ vt as TextureManager,
3248
+ H as TextureWrapMode,
3249
+ l as Vec2,
3250
+ j as WebglBufferArray,
3251
+ Yt as drawCircle,
3252
+ mt as drawGraphic,
3253
+ Gt as drawLine,
3254
+ Ht as drawMaskImage,
3255
+ Vt as drawRect,
3256
+ kt as drawSprite,
3257
+ ft as drawSpriteRaw
3258
+ };