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