@al8b/screen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +23 -0
  2. package/dist/core/base-screen.d.mts +84 -0
  3. package/dist/core/base-screen.d.ts +84 -0
  4. package/dist/core/base-screen.js +419 -0
  5. package/dist/core/base-screen.js.map +1 -0
  6. package/dist/core/base-screen.mjs +396 -0
  7. package/dist/core/base-screen.mjs.map +1 -0
  8. package/dist/core/index.d.mts +10 -0
  9. package/dist/core/index.d.ts +10 -0
  10. package/dist/core/index.js +1208 -0
  11. package/dist/core/index.js.map +1 -0
  12. package/dist/core/index.mjs +1183 -0
  13. package/dist/core/index.mjs.map +1 -0
  14. package/dist/core/screen.d.mts +15 -0
  15. package/dist/core/screen.d.ts +15 -0
  16. package/dist/core/screen.js +1209 -0
  17. package/dist/core/screen.js.map +1 -0
  18. package/dist/core/screen.mjs +1184 -0
  19. package/dist/core/screen.mjs.map +1 -0
  20. package/dist/drawing/primitives-screen.d.mts +28 -0
  21. package/dist/drawing/primitives-screen.d.ts +28 -0
  22. package/dist/drawing/primitives-screen.js +685 -0
  23. package/dist/drawing/primitives-screen.js.map +1 -0
  24. package/dist/drawing/primitives-screen.mjs +662 -0
  25. package/dist/drawing/primitives-screen.mjs.map +1 -0
  26. package/dist/drawing/sprite-screen.d.mts +41 -0
  27. package/dist/drawing/sprite-screen.d.ts +41 -0
  28. package/dist/drawing/sprite-screen.js +853 -0
  29. package/dist/drawing/sprite-screen.js.map +1 -0
  30. package/dist/drawing/sprite-screen.mjs +830 -0
  31. package/dist/drawing/sprite-screen.mjs.map +1 -0
  32. package/dist/drawing/text-screen.d.mts +19 -0
  33. package/dist/drawing/text-screen.d.ts +19 -0
  34. package/dist/drawing/text-screen.js +909 -0
  35. package/dist/drawing/text-screen.js.map +1 -0
  36. package/dist/drawing/text-screen.mjs +884 -0
  37. package/dist/drawing/text-screen.mjs.map +1 -0
  38. package/dist/index.d.mts +10 -0
  39. package/dist/index.d.ts +10 -0
  40. package/dist/index.js +1210 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/index.mjs +1184 -0
  43. package/dist/index.mjs.map +1 -0
  44. package/dist/tri/index.d.mts +3 -0
  45. package/dist/tri/index.d.ts +3 -0
  46. package/dist/tri/index.js +231 -0
  47. package/dist/tri/index.js.map +1 -0
  48. package/dist/tri/index.mjs +203 -0
  49. package/dist/tri/index.mjs.map +1 -0
  50. package/dist/tri/triangle-screen.d.mts +16 -0
  51. package/dist/tri/triangle-screen.d.ts +16 -0
  52. package/dist/tri/triangle-screen.js +1147 -0
  53. package/dist/tri/triangle-screen.js.map +1 -0
  54. package/dist/tri/triangle-screen.mjs +1122 -0
  55. package/dist/tri/triangle-screen.mjs.map +1 -0
  56. package/dist/tri/ttri.d.mts +71 -0
  57. package/dist/tri/ttri.d.ts +71 -0
  58. package/dist/tri/ttri.js +229 -0
  59. package/dist/tri/ttri.js.map +1 -0
  60. package/dist/tri/ttri.mjs +203 -0
  61. package/dist/tri/ttri.mjs.map +1 -0
  62. package/dist/types/index.d.mts +64 -0
  63. package/dist/types/index.d.ts +64 -0
  64. package/dist/types/index.js +19 -0
  65. package/dist/types/index.js.map +1 -0
  66. package/dist/types/index.mjs +1 -0
  67. package/dist/types/index.mjs.map +1 -0
  68. package/package.json +37 -0
@@ -0,0 +1,853 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/drawing/sprite-screen.ts
22
+ var sprite_screen_exports = {};
23
+ __export(sprite_screen_exports, {
24
+ SpriteScreen: () => SpriteScreen
25
+ });
26
+ module.exports = __toCommonJS(sprite_screen_exports);
27
+ var import_diagnostics3 = require("@al8b/diagnostics");
28
+
29
+ // src/drawing/primitives-screen.ts
30
+ var import_diagnostics2 = require("@al8b/diagnostics");
31
+
32
+ // src/core/base-screen.ts
33
+ var import_diagnostics = require("@al8b/diagnostics");
34
+
35
+ // src/tri/ttri.ts
36
+ var ZBuffer = class {
37
+ static {
38
+ __name(this, "ZBuffer");
39
+ }
40
+ buffer;
41
+ width;
42
+ height;
43
+ constructor(width, height) {
44
+ this.width = width;
45
+ this.height = height;
46
+ this.buffer = new Float32Array(width * height);
47
+ }
48
+ clear() {
49
+ this.buffer.fill(0);
50
+ }
51
+ get(x, y) {
52
+ return this.buffer[y * this.width + x] || 0;
53
+ }
54
+ set(x, y, z) {
55
+ this.buffer[y * this.width + x] = z;
56
+ }
57
+ resize(width, height) {
58
+ if (this.width !== width || this.height !== height) {
59
+ this.width = width;
60
+ this.height = height;
61
+ this.buffer = new Float32Array(width * height);
62
+ }
63
+ }
64
+ };
65
+
66
+ // src/core/base-screen.ts
67
+ var BaseScreen = class {
68
+ static {
69
+ __name(this, "BaseScreen");
70
+ }
71
+ canvas;
72
+ context;
73
+ runtime;
74
+ width;
75
+ height;
76
+ // Drawing state
77
+ alpha = 1;
78
+ pixelated = 1;
79
+ line_width = 1;
80
+ font = "BitCell";
81
+ // Transformations
82
+ translation_x = 0;
83
+ translation_y = 0;
84
+ rotation = 0;
85
+ scale_x = 1;
86
+ scale_y = 1;
87
+ screen_transform = false;
88
+ // Object transformations
89
+ object_rotation = 0;
90
+ object_scale_x = 1;
91
+ object_scale_y = 1;
92
+ anchor_x = 0;
93
+ anchor_y = 0;
94
+ // Blending + font caches
95
+ blending = {};
96
+ font_load_requested = {};
97
+ font_loaded = {};
98
+ // Interface cache
99
+ interfaceCache = null;
100
+ // Cursor management
101
+ cursor = "default";
102
+ cursor_visibility = "auto";
103
+ last_mouse_move = Date.now();
104
+ // 3D helper
105
+ zBuffer;
106
+ constructor(options = {}) {
107
+ this.runtime = options.runtime;
108
+ if (options.canvas) {
109
+ this.canvas = options.canvas;
110
+ if (this.canvas.width === 0 || this.canvas.height === 0) {
111
+ this.canvas.width = options.width || 1080;
112
+ this.canvas.height = options.height || 1920;
113
+ }
114
+ } else {
115
+ this.canvas = document.createElement("canvas");
116
+ this.canvas.width = options.width || 1080;
117
+ this.canvas.height = options.height || 1920;
118
+ }
119
+ this.initContext();
120
+ this.blending = {
121
+ normal: "source-over",
122
+ additive: "lighter"
123
+ };
124
+ const blendModes = [
125
+ "source-over",
126
+ "source-in",
127
+ "source-out",
128
+ "source-atop",
129
+ "destination-over",
130
+ "destination-in",
131
+ "destination-out",
132
+ "destination-atop",
133
+ "lighter",
134
+ "copy",
135
+ "xor",
136
+ "multiply",
137
+ "screen",
138
+ "overlay",
139
+ "darken",
140
+ "lighten",
141
+ "color-dodge",
142
+ "color-burn",
143
+ "hard-light",
144
+ "soft-light",
145
+ "difference",
146
+ "exclusion",
147
+ "hue",
148
+ "saturation",
149
+ "color",
150
+ "luminosity"
151
+ ];
152
+ for (const mode of blendModes) {
153
+ this.blending[mode] = mode;
154
+ }
155
+ this.loadFont(this.font);
156
+ this.zBuffer = new ZBuffer(this.canvas.width, this.canvas.height);
157
+ this.cursor = "default";
158
+ this.canvas.addEventListener("mousemove", () => {
159
+ this.last_mouse_move = Date.now();
160
+ if (this.cursor !== "default" && this.cursor_visibility === "auto") {
161
+ this.cursor = "default";
162
+ this.canvas.style.cursor = "default";
163
+ }
164
+ });
165
+ this.canvas.addEventListener("contextrestored", () => {
166
+ this.initContext();
167
+ });
168
+ setInterval(() => this.checkMouseCursor(), 1e3);
169
+ this.cursor_visibility = "auto";
170
+ }
171
+ initContext() {
172
+ const ctx = this.canvas.getContext("2d", {
173
+ alpha: false
174
+ });
175
+ if (!ctx) {
176
+ const diagnostic = (0, import_diagnostics.createDiagnostic)(import_diagnostics.APIErrorCode.E7001);
177
+ const formatted = (0, import_diagnostics.formatForBrowser)(diagnostic);
178
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7001, {});
179
+ throw new Error(formatted);
180
+ }
181
+ if (ctx !== this.context) {
182
+ this.context = ctx;
183
+ } else {
184
+ this.context.restore();
185
+ }
186
+ this.context.save();
187
+ this.context.translate(this.canvas.width / 2, this.canvas.height / 2);
188
+ const ratio = Math.min(this.canvas.width / 200, this.canvas.height / 200);
189
+ this.context.scale(ratio, ratio);
190
+ this.width = this.canvas.width / ratio;
191
+ this.height = this.canvas.height / ratio;
192
+ this.context.lineCap = "round";
193
+ }
194
+ /**
195
+ * Initialize draw state (called before each draw frame)
196
+ */
197
+ initDraw() {
198
+ this.alpha = 1;
199
+ this.line_width = 1;
200
+ }
201
+ /**
202
+ * Update interface dimensions (called before each draw frame)
203
+ */
204
+ updateInterface() {
205
+ if (this.interfaceCache) {
206
+ this.interfaceCache.width = this.width;
207
+ this.interfaceCache.height = this.height;
208
+ }
209
+ }
210
+ clear(color) {
211
+ this.context.globalAlpha = 1;
212
+ this.context.globalCompositeOperation = "source-over";
213
+ this.context.fillStyle = color || "#000";
214
+ this.context.strokeStyle = color || "#000";
215
+ this.context.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
216
+ this.zBuffer.clear();
217
+ }
218
+ setColor(color) {
219
+ if (!color) return;
220
+ if (!Number.isNaN(Number.parseInt(String(color)))) {
221
+ const num = Number.parseInt(String(color));
222
+ const r = Math.floor(num / 100) % 10 / 9 * 255;
223
+ const g = Math.floor(num / 10) % 10 / 9 * 255;
224
+ const b = num % 10 / 9 * 255;
225
+ const c = 4278190080 + (r << 16) + (g << 8) + b;
226
+ const hex = "#" + c.toString(16).substring(2, 8);
227
+ this.context.fillStyle = hex;
228
+ this.context.strokeStyle = hex;
229
+ } else if (typeof color === "string") {
230
+ const isValidColor = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) || /^rgb\(|^rgba\(|^hsl\(|^hsla\(/.test(color) || /^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);
231
+ if (!isValidColor) {
232
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7003, {
233
+ color
234
+ });
235
+ return;
236
+ }
237
+ this.context.fillStyle = color;
238
+ this.context.strokeStyle = color;
239
+ }
240
+ }
241
+ setAlpha(alpha) {
242
+ this.alpha = alpha;
243
+ }
244
+ setPixelated(pixelated) {
245
+ this.pixelated = pixelated;
246
+ }
247
+ setBlending(blending) {
248
+ const blend = this.blending[blending || "normal"];
249
+ if (!blend) {
250
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7007, {
251
+ blendMode: blending
252
+ });
253
+ this.context.globalCompositeOperation = "source-over";
254
+ return;
255
+ }
256
+ this.context.globalCompositeOperation = blend;
257
+ }
258
+ setLineWidth(width) {
259
+ this.line_width = width;
260
+ }
261
+ setLineDash(dash) {
262
+ if (!Array.isArray(dash)) {
263
+ this.context.setLineDash([]);
264
+ } else {
265
+ this.context.setLineDash(dash);
266
+ }
267
+ }
268
+ setLinearGradient(x1, y1, x2, y2, c1, c2) {
269
+ const grd = this.context.createLinearGradient(x1, -y1, x2, -y2);
270
+ grd.addColorStop(0, c1);
271
+ grd.addColorStop(1, c2);
272
+ this.context.fillStyle = grd;
273
+ this.context.strokeStyle = grd;
274
+ }
275
+ setRadialGradient(x, y, radius, c1, c2) {
276
+ const grd = this.context.createRadialGradient(x, -y, 0, x, -y, radius);
277
+ grd.addColorStop(0, c1);
278
+ grd.addColorStop(1, c2);
279
+ this.context.fillStyle = grd;
280
+ this.context.strokeStyle = grd;
281
+ }
282
+ setFont(font) {
283
+ this.font = font || "Verdana";
284
+ this.loadFont(this.font);
285
+ }
286
+ loadFont(font = "BitCell") {
287
+ if (this.font_load_requested[font]) {
288
+ return;
289
+ }
290
+ this.font_load_requested[font] = true;
291
+ try {
292
+ document.fonts?.load?.(`16pt ${font}`).catch(() => {
293
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7006, {
294
+ font
295
+ });
296
+ });
297
+ } catch {
298
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7006, {
299
+ font
300
+ });
301
+ }
302
+ }
303
+ isFontReady(font = this.font) {
304
+ if (this.font_loaded[font]) {
305
+ return 1;
306
+ }
307
+ try {
308
+ const ready = document.fonts?.check?.(`16pt ${font}`) ?? true;
309
+ if (ready) {
310
+ this.font_loaded[font] = true;
311
+ }
312
+ return ready ? 1 : 0;
313
+ } catch {
314
+ return 1;
315
+ }
316
+ }
317
+ setTranslation(tx, ty) {
318
+ this.translation_x = isFinite(tx) ? tx : 0;
319
+ this.translation_y = isFinite(ty) ? ty : 0;
320
+ this.updateScreenTransform();
321
+ }
322
+ setScale(x, y) {
323
+ this.scale_x = isFinite(x) && x !== 0 ? x : 1;
324
+ this.scale_y = isFinite(y) && y !== 0 ? y : 1;
325
+ this.updateScreenTransform();
326
+ }
327
+ setRotation(rotation) {
328
+ this.rotation = isFinite(rotation) ? rotation : 0;
329
+ this.updateScreenTransform();
330
+ }
331
+ updateScreenTransform() {
332
+ this.screen_transform = this.translation_x !== 0 || this.translation_y !== 0 || this.scale_x !== 1 || this.scale_y !== 1 || this.rotation !== 0;
333
+ }
334
+ setDrawAnchor(ax, ay) {
335
+ this.anchor_x = typeof ax === "number" ? ax : 0;
336
+ this.anchor_y = typeof ay === "number" ? ay : 0;
337
+ }
338
+ setDrawRotation(rotation) {
339
+ this.object_rotation = rotation;
340
+ }
341
+ setDrawScale(x, y = x) {
342
+ this.object_scale_x = x;
343
+ this.object_scale_y = y;
344
+ }
345
+ initDrawOp(x, y, object_transform = true) {
346
+ let res = false;
347
+ if (this.screen_transform) {
348
+ this.context.save();
349
+ res = true;
350
+ this.context.translate(this.translation_x, -this.translation_y);
351
+ this.context.scale(this.scale_x, this.scale_y);
352
+ this.context.rotate(-this.rotation / 180 * Math.PI);
353
+ this.context.translate(x, y);
354
+ }
355
+ if (object_transform && (this.object_rotation !== 0 || this.object_scale_x !== 1 || this.object_scale_y !== 1)) {
356
+ if (!res) {
357
+ this.context.save();
358
+ res = true;
359
+ this.context.translate(x, y);
360
+ }
361
+ if (this.object_rotation !== 0) {
362
+ this.context.rotate(-this.object_rotation / 180 * Math.PI);
363
+ }
364
+ if (this.object_scale_x !== 1 || this.object_scale_y !== 1) {
365
+ this.context.scale(this.object_scale_x, this.object_scale_y);
366
+ }
367
+ }
368
+ return res;
369
+ }
370
+ closeDrawOp() {
371
+ this.context.restore();
372
+ }
373
+ /**
374
+ * Check mouse cursor visibility
375
+ * Auto-hides cursor after 4 seconds of inactivity
376
+ */
377
+ checkMouseCursor() {
378
+ if (Date.now() > this.last_mouse_move + 4e3 && this.cursor_visibility === "auto") {
379
+ if (this.cursor !== "none") {
380
+ this.cursor = "none";
381
+ this.canvas.style.cursor = "none";
382
+ }
383
+ }
384
+ }
385
+ /**
386
+ * Set cursor visibility
387
+ */
388
+ setCursorVisible(visible) {
389
+ this.cursor_visibility = visible ? "default" : "none";
390
+ if (visible) {
391
+ this.cursor = "default";
392
+ this.canvas.style.cursor = "default";
393
+ } else {
394
+ this.cursor = "none";
395
+ this.canvas.style.cursor = "none";
396
+ }
397
+ }
398
+ getCanvas() {
399
+ return this.canvas;
400
+ }
401
+ getContext() {
402
+ return this.context;
403
+ }
404
+ resize(width, height) {
405
+ if (width && height) {
406
+ if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
407
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7002, {
408
+ width,
409
+ height
410
+ });
411
+ return;
412
+ }
413
+ this.canvas.width = width;
414
+ this.canvas.height = height;
415
+ this.initContext();
416
+ this.zBuffer.resize(width, height);
417
+ this.updateInterface();
418
+ }
419
+ }
420
+ };
421
+
422
+ // src/drawing/primitives-screen.ts
423
+ var PrimitiveScreen = class extends BaseScreen {
424
+ static {
425
+ __name(this, "PrimitiveScreen");
426
+ }
427
+ fillRect(x, y, w, h, color) {
428
+ if (!this.context) {
429
+ (0, import_diagnostics2.reportRuntimeError)(this.runtime?.listener, import_diagnostics2.APIErrorCode.E7092, {});
430
+ return;
431
+ }
432
+ if (!isFinite(x) || !isFinite(y) || !isFinite(w) || !isFinite(h) || w <= 0 || h <= 0) {
433
+ (0, import_diagnostics2.reportRuntimeError)(this.runtime?.listener, import_diagnostics2.APIErrorCode.E7093, {
434
+ error: `Invalid parameters: x=${x}, y=${y}, w=${w}, h=${h}`
435
+ });
436
+ return;
437
+ }
438
+ if (color) this.setColor(color);
439
+ this.context.globalAlpha = this.alpha;
440
+ if (this.initDrawOp(x, -y)) {
441
+ this.context.fillRect(-w / 2 - this.anchor_x * w / 2, -h / 2 + this.anchor_y * h / 2, w, h);
442
+ this.closeDrawOp();
443
+ } else {
444
+ this.context.fillRect(x - w / 2 - this.anchor_x * w / 2, -y - h / 2 + this.anchor_y * h / 2, w, h);
445
+ }
446
+ }
447
+ fillRoundRect(x, y, w, h, round = 10, color) {
448
+ if (color) this.setColor(color);
449
+ this.context.globalAlpha = this.alpha;
450
+ const transform = this.initDrawOp(x, -y);
451
+ const rx = (transform ? -w / 2 : x - w / 2) - this.anchor_x * w / 2;
452
+ const ry = (transform ? -h / 2 : -y - h / 2) + this.anchor_y * h / 2;
453
+ this.context.beginPath();
454
+ if (this.context.roundRect) {
455
+ this.context.roundRect(rx, ry, w, h, round);
456
+ } else {
457
+ const r = Math.min(round, w / 2, h / 2);
458
+ this.context.moveTo(rx + r, ry);
459
+ this.context.lineTo(rx + w - r, ry);
460
+ this.context.quadraticCurveTo(rx + w, ry, rx + w, ry + r);
461
+ this.context.lineTo(rx + w, ry + h - r);
462
+ this.context.quadraticCurveTo(rx + w, ry + h, rx + w - r, ry + h);
463
+ this.context.lineTo(rx + r, ry + h);
464
+ this.context.quadraticCurveTo(rx, ry + h, rx, ry + h - r);
465
+ this.context.lineTo(rx, ry + r);
466
+ this.context.quadraticCurveTo(rx, ry, rx + r, ry);
467
+ this.context.closePath();
468
+ }
469
+ this.context.fill();
470
+ if (transform) this.closeDrawOp();
471
+ }
472
+ fillRound(x, y, w, h, color) {
473
+ if (color) this.setColor(color);
474
+ this.context.globalAlpha = this.alpha;
475
+ w = Math.abs(w);
476
+ h = Math.abs(h);
477
+ if (this.initDrawOp(x, -y)) {
478
+ this.context.beginPath();
479
+ this.context.ellipse(-this.anchor_x * w / 2, this.anchor_y * h / 2, w / 2, h / 2, 0, 0, Math.PI * 2, false);
480
+ this.context.fill();
481
+ this.closeDrawOp();
482
+ } else {
483
+ this.context.beginPath();
484
+ this.context.ellipse(x - this.anchor_x * w / 2, -y + this.anchor_y * h / 2, w / 2, h / 2, 0, 0, Math.PI * 2, false);
485
+ this.context.fill();
486
+ }
487
+ }
488
+ drawRect(x, y, w, h, color) {
489
+ if (color) this.setColor(color);
490
+ this.context.globalAlpha = this.alpha;
491
+ this.context.lineWidth = this.line_width;
492
+ if (this.initDrawOp(x, -y)) {
493
+ this.context.strokeRect(-w / 2 - this.anchor_x * w / 2, -h / 2 + this.anchor_y * h / 2, w, h);
494
+ this.closeDrawOp();
495
+ } else {
496
+ this.context.strokeRect(x - w / 2 - this.anchor_x * w / 2, -y - h / 2 + this.anchor_y * h / 2, w, h);
497
+ }
498
+ }
499
+ drawRoundRect(x, y, w, h, round = 10, color) {
500
+ if (color) this.setColor(color);
501
+ this.context.globalAlpha = this.alpha;
502
+ this.context.lineWidth = this.line_width;
503
+ const transform = this.initDrawOp(x, -y);
504
+ const rx = (transform ? -w / 2 : x - w / 2) - this.anchor_x * w / 2;
505
+ const ry = (transform ? -h / 2 : -y - h / 2) + this.anchor_y * h / 2;
506
+ this.context.beginPath();
507
+ if (this.context.roundRect) {
508
+ this.context.roundRect(rx, ry, w, h, round);
509
+ } else {
510
+ const r = Math.min(round, w / 2, h / 2);
511
+ this.context.moveTo(rx + r, ry);
512
+ this.context.lineTo(rx + w - r, ry);
513
+ this.context.quadraticCurveTo(rx + w, ry, rx + w, ry + r);
514
+ this.context.lineTo(rx + w, ry + h - r);
515
+ this.context.quadraticCurveTo(rx + w, ry + h, rx + w - r, ry + h);
516
+ this.context.lineTo(rx + r, ry + h);
517
+ this.context.quadraticCurveTo(rx, ry + h, rx, ry + h - r);
518
+ this.context.lineTo(rx, ry + r);
519
+ this.context.quadraticCurveTo(rx, ry, rx + r, ry);
520
+ this.context.closePath();
521
+ }
522
+ this.context.stroke();
523
+ if (transform) this.closeDrawOp();
524
+ }
525
+ drawRound(x, y, w, h, color) {
526
+ if (color) this.setColor(color);
527
+ this.context.globalAlpha = this.alpha;
528
+ this.context.lineWidth = this.line_width;
529
+ w = Math.abs(w);
530
+ h = Math.abs(h);
531
+ if (this.initDrawOp(x, -y)) {
532
+ this.context.beginPath();
533
+ this.context.ellipse(-this.anchor_x * w / 2, this.anchor_y * h / 2, w / 2, h / 2, 0, 0, Math.PI * 2, false);
534
+ this.context.stroke();
535
+ this.closeDrawOp();
536
+ } else {
537
+ this.context.beginPath();
538
+ this.context.ellipse(x - this.anchor_x * w / 2, -y + this.anchor_y * h / 2, w / 2, h / 2, 0, 0, Math.PI * 2, false);
539
+ this.context.stroke();
540
+ }
541
+ }
542
+ drawLine(x1, y1, x2, y2, color) {
543
+ if (color) this.setColor(color);
544
+ this.context.globalAlpha = this.alpha;
545
+ this.context.lineWidth = this.line_width;
546
+ const transform = this.initDrawOp(0, 0, false);
547
+ this.context.beginPath();
548
+ this.context.moveTo(x1, -y1);
549
+ this.context.lineTo(x2, -y2);
550
+ this.context.stroke();
551
+ if (transform) this.closeDrawOp();
552
+ }
553
+ drawPolygon(args) {
554
+ const { color, points } = this.extractPoints(args);
555
+ if (!points || points.length < 4) return;
556
+ if (color) this.setColor(color);
557
+ this.context.globalAlpha = this.alpha;
558
+ this.context.lineWidth = this.line_width;
559
+ const len = Math.floor(points.length / 2);
560
+ const transform = this.initDrawOp(0, 0, false);
561
+ this.context.beginPath();
562
+ this.context.moveTo(points[0], -points[1]);
563
+ for (let i = 1; i < len; i++) {
564
+ this.context.lineTo(points[i * 2], -points[i * 2 + 1]);
565
+ }
566
+ this.context.closePath();
567
+ this.context.stroke();
568
+ if (transform) this.closeDrawOp();
569
+ }
570
+ drawPolyline(args) {
571
+ const { color, points } = this.extractPoints(args);
572
+ if (!points || points.length < 4) return;
573
+ if (color) this.setColor(color);
574
+ this.context.globalAlpha = this.alpha;
575
+ this.context.lineWidth = this.line_width;
576
+ const len = Math.floor(points.length / 2);
577
+ const transform = this.initDrawOp(0, 0, false);
578
+ this.context.beginPath();
579
+ this.context.moveTo(points[0], -points[1]);
580
+ for (let i = 1; i < len; i++) {
581
+ this.context.lineTo(points[i * 2], -points[i * 2 + 1]);
582
+ }
583
+ this.context.stroke();
584
+ if (transform) this.closeDrawOp();
585
+ }
586
+ fillPolygon(args) {
587
+ const { color, points } = this.extractPoints(args);
588
+ if (!points || points.length < 4) return;
589
+ if (color) this.setColor(color);
590
+ this.context.globalAlpha = this.alpha;
591
+ const len = Math.floor(points.length / 2);
592
+ const transform = this.initDrawOp(0, 0, false);
593
+ this.context.beginPath();
594
+ this.context.moveTo(points[0], -points[1]);
595
+ for (let i = 1; i < len; i++) {
596
+ this.context.lineTo(points[i * 2], -points[i * 2 + 1]);
597
+ }
598
+ this.context.fill();
599
+ if (transform) this.closeDrawOp();
600
+ }
601
+ drawQuadCurve(args) {
602
+ const { color, points } = this.extractPoints(args);
603
+ if (!points || points.length < 4) return;
604
+ if (color) this.setColor(color);
605
+ this.context.globalAlpha = this.alpha;
606
+ this.context.lineWidth = this.line_width;
607
+ const transform = this.initDrawOp(0, 0, false);
608
+ this.context.beginPath();
609
+ this.context.moveTo(points[0], -points[1]);
610
+ let index = 2;
611
+ while (index <= points.length - 4) {
612
+ this.context.quadraticCurveTo(points[index], -points[index + 1], points[index + 2], -points[index + 3]);
613
+ index += 4;
614
+ }
615
+ this.context.stroke();
616
+ if (transform) this.closeDrawOp();
617
+ }
618
+ drawBezierCurve(args) {
619
+ const { color, points } = this.extractPoints(args);
620
+ if (!points || points.length < 4) return;
621
+ if (color) this.setColor(color);
622
+ this.context.globalAlpha = this.alpha;
623
+ this.context.lineWidth = this.line_width;
624
+ const transform = this.initDrawOp(0, 0, false);
625
+ this.context.beginPath();
626
+ this.context.moveTo(points[0], -points[1]);
627
+ let index = 2;
628
+ while (index <= points.length - 6) {
629
+ this.context.bezierCurveTo(points[index], -points[index + 1], points[index + 2], -points[index + 3], points[index + 4], -points[index + 5]);
630
+ index += 6;
631
+ }
632
+ this.context.stroke();
633
+ if (transform) this.closeDrawOp();
634
+ }
635
+ drawArc(x, y, radius, angle1, angle2, ccw, color) {
636
+ if (color) this.setColor(color);
637
+ this.context.globalAlpha = this.alpha;
638
+ this.context.lineWidth = this.line_width;
639
+ if (this.initDrawOp(x, -y)) {
640
+ this.context.beginPath();
641
+ this.context.arc(0, 0, radius, -angle1 / 180 * Math.PI, -angle2 / 180 * Math.PI, ccw);
642
+ this.context.stroke();
643
+ this.closeDrawOp();
644
+ } else {
645
+ this.context.beginPath();
646
+ this.context.arc(x, -y, radius, -angle1 / 180 * Math.PI, -angle2 / 180 * Math.PI, ccw);
647
+ this.context.stroke();
648
+ }
649
+ }
650
+ fillArc(x, y, radius, angle1, angle2, ccw, color) {
651
+ if (color) this.setColor(color);
652
+ this.context.globalAlpha = this.alpha;
653
+ if (this.initDrawOp(x, -y)) {
654
+ this.context.beginPath();
655
+ this.context.arc(0, 0, radius, -angle1 / 180 * Math.PI, -angle2 / 180 * Math.PI, ccw);
656
+ this.context.fill();
657
+ this.closeDrawOp();
658
+ } else {
659
+ this.context.beginPath();
660
+ this.context.arc(x, -y, radius, -angle1 / 180 * Math.PI, -angle2 / 180 * Math.PI, ccw);
661
+ this.context.fill();
662
+ }
663
+ }
664
+ extractPoints(args) {
665
+ let color;
666
+ let points;
667
+ if (args.length > 0 && args.length % 2 === 1 && typeof args[args.length - 1] === "string") {
668
+ color = args[args.length - 1];
669
+ points = args.slice(0, -1);
670
+ } else if (Array.isArray(args[0])) {
671
+ if (args[1] && typeof args[1] === "string") {
672
+ color = args[1];
673
+ }
674
+ points = args[0];
675
+ } else {
676
+ points = args;
677
+ }
678
+ return {
679
+ color,
680
+ points
681
+ };
682
+ }
683
+ };
684
+
685
+ // src/drawing/sprite-screen.ts
686
+ var SpriteScreen = class extends PrimitiveScreen {
687
+ static {
688
+ __name(this, "SpriteScreen");
689
+ }
690
+ // Cache imageSmoothingEnabled — only set when pixelated flag changes
691
+ _lastImageSmoothing = true;
692
+ // Cache frame time once per draw frame instead of per-sprite
693
+ _frameTime = 0;
694
+ /**
695
+ * Initialize draw state (called before each draw frame)
696
+ */
697
+ initDraw() {
698
+ super.initDraw();
699
+ this._frameTime = performance.now();
700
+ }
701
+ /**
702
+ * Set imageSmoothingEnabled only when it actually changes
703
+ */
704
+ setImageSmoothing() {
705
+ const smooth = !this.pixelated;
706
+ if (smooth !== this._lastImageSmoothing) {
707
+ this.context.imageSmoothingEnabled = smooth;
708
+ this._lastImageSmoothing = smooth;
709
+ }
710
+ }
711
+ /**
712
+ * Get the canvas for the current sprite frame
713
+ */
714
+ getSpriteFrame(sprite) {
715
+ let frame = null;
716
+ if (typeof sprite === "string") {
717
+ const spriteName = sprite;
718
+ let spriteObj2 = null;
719
+ if (this.runtime && this.runtime.sprites) {
720
+ spriteObj2 = this.runtime.sprites[sprite];
721
+ }
722
+ if (!spriteObj2) {
723
+ const parts = sprite.split(".");
724
+ if (parts.length > 1 && this.runtime && this.runtime.sprites) {
725
+ spriteObj2 = this.runtime.sprites[parts[0]];
726
+ frame = Number.parseInt(parts[1]) || 0;
727
+ }
728
+ }
729
+ if (!spriteObj2) {
730
+ (0, import_diagnostics3.reportRuntimeError)(this.runtime?.listener, import_diagnostics3.APIErrorCode.E7004, {
731
+ spriteName
732
+ });
733
+ return null;
734
+ }
735
+ sprite = spriteObj2;
736
+ } else if (sprite && typeof sprite === "object" && sprite.canvas && !sprite.frames) {
737
+ return sprite.canvas || sprite.image || null;
738
+ }
739
+ if (!sprite || !sprite.ready) {
740
+ const spriteName = typeof sprite === "string" ? sprite : "unknown";
741
+ (0, import_diagnostics3.reportRuntimeError)(this.runtime?.listener, import_diagnostics3.APIErrorCode.E7005, {
742
+ spriteName
743
+ });
744
+ return null;
745
+ }
746
+ const spriteObj = sprite;
747
+ if (spriteObj.frames && spriteObj.frames.length > 1) {
748
+ if (frame === null) {
749
+ if (spriteObj.animation_start === 0) {
750
+ spriteObj.animation_start = this._frameTime;
751
+ }
752
+ const dt = 1e3 / spriteObj.fps;
753
+ frame = Math.floor((this._frameTime - spriteObj.animation_start) / dt) % spriteObj.frames.length;
754
+ }
755
+ if (frame >= 0 && frame < spriteObj.frames.length) {
756
+ return spriteObj.frames[frame].canvas;
757
+ }
758
+ return spriteObj.frames[0].canvas;
759
+ } else if (spriteObj.frames && spriteObj.frames[0]) {
760
+ return spriteObj.frames[0].canvas;
761
+ }
762
+ return null;
763
+ }
764
+ /**
765
+ * Draw a sprite
766
+ */
767
+ drawSprite(sprite, x, y, w, h) {
768
+ const canvas = this.getSpriteFrame(sprite);
769
+ if (!canvas) return;
770
+ if (w == null) {
771
+ w = canvas.width;
772
+ }
773
+ if (!h) {
774
+ h = w / canvas.width * canvas.height;
775
+ }
776
+ if (!this.screen_transform && this.object_rotation === 0 && this.object_scale_x === 1 && this.object_scale_y === 1) {
777
+ const drawX = x - w / 2 - this.anchor_x * w / 2;
778
+ const drawY = -y - h / 2 + this.anchor_y * h / 2;
779
+ const halfW = this.width / 2;
780
+ const halfH = this.height / 2;
781
+ if (drawX > halfW || drawX + w < -halfW || drawY > halfH || drawY + h < -halfH) {
782
+ return;
783
+ }
784
+ }
785
+ this.context.globalAlpha = this.alpha;
786
+ this.setImageSmoothing();
787
+ if (this.initDrawOp(x, -y)) {
788
+ this.context.drawImage(canvas, -w / 2 - this.anchor_x * w / 2, -h / 2 + this.anchor_y * h / 2, w, h);
789
+ this.closeDrawOp();
790
+ } else {
791
+ this.context.drawImage(canvas, x - w / 2 - this.anchor_x * w / 2, -y - h / 2 + this.anchor_y * h / 2, w, h);
792
+ }
793
+ }
794
+ /**
795
+ * Draw a portion of a sprite
796
+ */
797
+ drawSpritePart(sprite, sx, sy, sw, sh, x, y, w, h) {
798
+ const canvas = this.getSpriteFrame(sprite);
799
+ if (!canvas) return;
800
+ if (w == null) {
801
+ w = sw;
802
+ }
803
+ if (!h) {
804
+ h = w / sw * sh;
805
+ }
806
+ if (!this.screen_transform && this.object_rotation === 0 && this.object_scale_x === 1 && this.object_scale_y === 1) {
807
+ const drawX = x - w / 2 - this.anchor_x * w / 2;
808
+ const drawY = -y - h / 2 + this.anchor_y * h / 2;
809
+ const halfW = this.width / 2;
810
+ const halfH = this.height / 2;
811
+ if (drawX > halfW || drawX + w < -halfW || drawY > halfH || drawY + h < -halfH) {
812
+ return;
813
+ }
814
+ }
815
+ this.context.globalAlpha = this.alpha;
816
+ this.setImageSmoothing();
817
+ if (this.initDrawOp(x, -y)) {
818
+ this.context.drawImage(canvas, sx, sy, sw, sh, -w / 2 - this.anchor_x * w / 2, -h / 2 + this.anchor_y * h / 2, w, h);
819
+ this.closeDrawOp();
820
+ } else {
821
+ this.context.drawImage(canvas, sx, sy, sw, sh, x - w / 2 - this.anchor_x * w / 2, -y - h / 2 + this.anchor_y * h / 2, w, h);
822
+ }
823
+ }
824
+ /**
825
+ * Draw a map
826
+ */
827
+ drawMap(map, x, y, w, h) {
828
+ let mapObj = null;
829
+ if (typeof map === "string") {
830
+ if (this.runtime && this.runtime.maps) {
831
+ mapObj = this.runtime.maps[map];
832
+ }
833
+ } else {
834
+ mapObj = map;
835
+ }
836
+ if (!(mapObj && mapObj.ready)) {
837
+ return;
838
+ }
839
+ this.context.globalAlpha = this.alpha;
840
+ this.setImageSmoothing();
841
+ if (this.initDrawOp(x, -y)) {
842
+ mapObj.draw(this.context, -w / 2 - this.anchor_x * w / 2, -h / 2 + this.anchor_y * h / 2, w, h);
843
+ this.closeDrawOp();
844
+ } else {
845
+ mapObj.draw(this.context, x - w / 2 - this.anchor_x * w / 2, -y - h / 2 + this.anchor_y * h / 2, w, h);
846
+ }
847
+ }
848
+ };
849
+ // Annotate the CommonJS export names for ESM import in node:
850
+ 0 && (module.exports = {
851
+ SpriteScreen
852
+ });
853
+ //# sourceMappingURL=sprite-screen.js.map