@argila/core 0.0.0-alpha-20250607064138

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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Argila.d.ts +24 -0
  3. package/dist/Argila.d.ts.map +1 -0
  4. package/dist/Argila.js +62 -0
  5. package/dist/Argila.test.d.ts +2 -0
  6. package/dist/Argila.test.d.ts.map +1 -0
  7. package/dist/Argila.test.js +150 -0
  8. package/dist/Canvas.d.ts +117 -0
  9. package/dist/Canvas.d.ts.map +1 -0
  10. package/dist/Canvas.js +311 -0
  11. package/dist/Canvas.test.d.ts +2 -0
  12. package/dist/Canvas.test.d.ts.map +1 -0
  13. package/dist/Canvas.test.js +311 -0
  14. package/dist/Common.d.ts +11 -0
  15. package/dist/Common.d.ts.map +1 -0
  16. package/dist/Common.js +64 -0
  17. package/dist/Common.test.js +118 -0
  18. package/dist/Event.d.ts +10 -0
  19. package/dist/Event.d.ts.map +1 -0
  20. package/dist/Event.js +23 -0
  21. package/dist/Event.test.js +72 -0
  22. package/dist/LinkedList.d.ts +44 -0
  23. package/dist/LinkedList.d.ts.map +1 -0
  24. package/dist/LinkedList.js +95 -0
  25. package/dist/LinkedList.test.d.ts +2 -0
  26. package/dist/LinkedList.test.d.ts.map +1 -0
  27. package/dist/LinkedList.test.js +197 -0
  28. package/dist/Vector3.d.ts +104 -0
  29. package/dist/Vector3.d.ts.map +1 -0
  30. package/dist/Vector3.js +185 -0
  31. package/dist/Vector3.test.d.ts +2 -0
  32. package/dist/Vector3.test.d.ts.map +1 -0
  33. package/dist/Vector3.test.js +127 -0
  34. package/dist/common.test.d.ts +2 -0
  35. package/dist/common.test.d.ts.map +1 -0
  36. package/dist/event.test.d.ts +2 -0
  37. package/dist/event.test.d.ts.map +1 -0
  38. package/dist/index.d.ts +29 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +41 -0
  41. package/dist/systems/Draw.d.ts +8 -0
  42. package/dist/systems/Draw.d.ts.map +1 -0
  43. package/dist/systems/Draw.js +15 -0
  44. package/dist/systems/Entity.d.ts +18 -0
  45. package/dist/systems/Entity.d.ts.map +1 -0
  46. package/dist/systems/Entity.js +44 -0
  47. package/dist/systems/Input.d.ts +127 -0
  48. package/dist/systems/Input.d.ts.map +1 -0
  49. package/dist/systems/Input.js +191 -0
  50. package/dist/systems/Loop.d.ts +29 -0
  51. package/dist/systems/Loop.d.ts.map +1 -0
  52. package/dist/systems/Loop.js +93 -0
  53. package/dist/systems/Render.d.ts +21 -0
  54. package/dist/systems/Render.d.ts.map +1 -0
  55. package/dist/systems/Render.js +112 -0
  56. package/dist/systems/Shape.d.ts +48 -0
  57. package/dist/systems/Shape.d.ts.map +1 -0
  58. package/dist/systems/Shape.js +146 -0
  59. package/dist/systems/System.d.ts +22 -0
  60. package/dist/systems/System.d.ts.map +1 -0
  61. package/dist/systems/System.js +20 -0
  62. package/dist/systems/World.d.ts +24 -0
  63. package/dist/systems/World.d.ts.map +1 -0
  64. package/dist/systems/World.js +84 -0
  65. package/dist/systems/debugs/Debug.d.ts +9 -0
  66. package/dist/systems/debugs/Debug.d.ts.map +1 -0
  67. package/dist/systems/debugs/Debug.js +12 -0
  68. package/dist/systems/debugs/InputDebug.d.ts +7 -0
  69. package/dist/systems/debugs/InputDebug.d.ts.map +1 -0
  70. package/dist/systems/debugs/InputDebug.js +11 -0
  71. package/dist/systems/debugs/LoopDebug.d.ts +7 -0
  72. package/dist/systems/debugs/LoopDebug.d.ts.map +1 -0
  73. package/dist/systems/debugs/LoopDebug.js +11 -0
  74. package/package.json +79 -0
package/dist/Canvas.js ADDED
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import Input from "./systems/Input";
4
+ export default class Canvas {
5
+ static id = uuidv4();
6
+ static list = /* @__PURE__ */ new Map();
7
+ static resizeObserver = null;
8
+ /**
9
+ * Creates a new canvas configuration
10
+ * @param options Canvas configuration options
11
+ * @returns The created canvas configuration
12
+ * @throws Error if a canvas with the same ID already exists
13
+ */
14
+ static create(options) {
15
+ const element = options.element || document.createElement("canvas");
16
+ const ctx = element.getContext("2d", { alpha: true });
17
+ if (!ctx) {
18
+ throw new Error("Failed to get 2D context from canvas element");
19
+ }
20
+ const canvas = {
21
+ element,
22
+ ctx,
23
+ dpr: typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1,
24
+ windowResizeCallback: () => {
25
+ Canvas.autoResize(options.id);
26
+ },
27
+ lastWidth: element.width,
28
+ clear: true,
29
+ width: 800,
30
+ height: 800,
31
+ autoHeight: true,
32
+ autoWidth: true,
33
+ lastHeight: element.height,
34
+ clearColor: "rgba(0, 0, 0, 0)",
35
+ isVisible: true,
36
+ ...options
37
+ };
38
+ if (Canvas.list.has(options.id)) {
39
+ throw Error(`Already exists a canvas with the id ${options.id}`);
40
+ }
41
+ Canvas.list.set(options.id, canvas);
42
+ Canvas.autoResize(options.id);
43
+ canvas.element.setAttribute("id", options.id);
44
+ canvas.lastWidth = canvas.width;
45
+ canvas.lastHeight = canvas.height;
46
+ if (canvas.autoWidth === true || canvas.autoHeight === true) {
47
+ if (typeof window !== "undefined") {
48
+ window.addEventListener("resize", canvas.windowResizeCallback);
49
+ if (typeof ResizeObserver !== "undefined" && !Canvas.resizeObserver) {
50
+ Canvas.resizeObserver = new ResizeObserver((entries) => {
51
+ for (const entry of entries) {
52
+ const canvasElement = entry.target;
53
+ const canvasId = canvasElement.getAttribute("id");
54
+ if (canvasId) {
55
+ Canvas.autoResize(canvasId);
56
+ }
57
+ }
58
+ });
59
+ }
60
+ if (Canvas.resizeObserver) {
61
+ Canvas.resizeObserver.observe(canvas.element);
62
+ }
63
+ }
64
+ }
65
+ canvas.element.addEventListener("webglcontextlost", (event) => {
66
+ event.preventDefault();
67
+ if (canvas.contextLostCallback) {
68
+ canvas.contextLostCallback(event);
69
+ }
70
+ });
71
+ canvas.element.addEventListener("webglcontextrestored", (event) => {
72
+ if (canvas.contextRestoredCallback) {
73
+ canvas.contextRestoredCallback(event);
74
+ }
75
+ Canvas.fixDPI(options.id);
76
+ });
77
+ Input.setupCanvasEventListeners(options.id);
78
+ return canvas;
79
+ }
80
+ /**
81
+ * Retrieves a canvas configuration by its ID
82
+ * @param id The canvas ID
83
+ * @returns The canvas configuration or undefined if not found
84
+ */
85
+ static get(id) {
86
+ return Canvas.list.get(id);
87
+ }
88
+ /**
89
+ * Updates canvas properties
90
+ * @param id The canvas ID
91
+ * @param options Options to update
92
+ */
93
+ static set(id, options) {
94
+ const canvas = Canvas.get(id);
95
+ if (canvas) {
96
+ Canvas.list.set(id, { ...canvas, ...options });
97
+ if (options.width !== void 0 || options.height !== void 0) {
98
+ Canvas.fixDPI(id);
99
+ }
100
+ }
101
+ }
102
+ /**
103
+ * Sets the clear color for a canvas
104
+ * @param id The canvas ID
105
+ * @param clearColor The color to set
106
+ */
107
+ static setClearColor(id, clearColor) {
108
+ const canvas = Canvas.get(id);
109
+ if (canvas) {
110
+ canvas.clearColor = clearColor;
111
+ }
112
+ }
113
+ /**
114
+ * Executes a callback for each canvas
115
+ * @param callback Function to execute for each canvas
116
+ */
117
+ static forEach(callback) {
118
+ Canvas.list.forEach((canvas) => {
119
+ callback(canvas);
120
+ });
121
+ }
122
+ /**
123
+ * Automatically resizes a canvas based on its parent element or window
124
+ * @param id The canvas ID
125
+ */
126
+ static autoResize(id) {
127
+ const canvas = Canvas.get(id);
128
+ if (!canvas) {
129
+ return;
130
+ }
131
+ const rootNode = canvas.element.getRootNode();
132
+ const shadowHost = rootNode instanceof ShadowRoot ? rootNode.host : null;
133
+ if (!canvas.element.parentElement && !shadowHost) {
134
+ if (canvas.autoWidth) {
135
+ canvas.width = typeof window !== "undefined" ? window.innerWidth : canvas.width;
136
+ }
137
+ if (canvas.autoHeight) {
138
+ canvas.height = typeof window !== "undefined" ? window.innerHeight : canvas.height;
139
+ }
140
+ Canvas.fixDPI(id);
141
+ return;
142
+ }
143
+ const parent = canvas.element.parentElement || shadowHost;
144
+ if (parent) {
145
+ const width = parent.offsetWidth;
146
+ const height = parent.offsetHeight;
147
+ let needsUpdate = false;
148
+ if (canvas.autoWidth && canvas.width !== width) {
149
+ canvas.width = width;
150
+ needsUpdate = true;
151
+ }
152
+ if (canvas.autoHeight && canvas.height !== height) {
153
+ canvas.height = height;
154
+ needsUpdate = true;
155
+ }
156
+ if (needsUpdate) {
157
+ Canvas.fixDPI(id);
158
+ }
159
+ }
160
+ }
161
+ /**
162
+ * Appends a canvas to a container
163
+ * @param id The canvas ID
164
+ * @param containerId The container ID or element
165
+ */
166
+ static append(id, containerId) {
167
+ const canvas = Canvas.get(id);
168
+ if (!canvas) {
169
+ return;
170
+ }
171
+ if (typeof containerId === "string") {
172
+ const container = document.getElementById(containerId);
173
+ if (container) {
174
+ container.appendChild(canvas.element);
175
+ } else {
176
+ console.warn(`Container with ID ${containerId} not found`);
177
+ return;
178
+ }
179
+ } else {
180
+ containerId.appendChild(canvas.element);
181
+ }
182
+ if (canvas.autoWidth === true || canvas.autoHeight === true) {
183
+ Canvas.autoResize(id);
184
+ }
185
+ }
186
+ /**
187
+ * Fixes the device pixel ratio for a canvas
188
+ * @param id The canvas ID
189
+ */
190
+ static fixDPI(id) {
191
+ const canvas = Canvas.get(id);
192
+ if (!canvas) {
193
+ return;
194
+ }
195
+ canvas.dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
196
+ canvas.element.style.width = `${canvas.width}px`;
197
+ canvas.element.style.height = `${canvas.height}px`;
198
+ canvas.element.setAttribute(
199
+ "width",
200
+ (canvas.width * canvas.dpr).toString()
201
+ );
202
+ canvas.element.setAttribute(
203
+ "height",
204
+ (canvas.height * canvas.dpr).toString()
205
+ );
206
+ canvas.ctx.scale(canvas.dpr, canvas.dpr);
207
+ }
208
+ /**
209
+ * Handles canvas resize events
210
+ * @param id The canvas ID
211
+ */
212
+ static onResize(id) {
213
+ const canvas = Canvas.get(id);
214
+ if (!canvas) {
215
+ return;
216
+ }
217
+ if (canvas.lastWidth !== canvas.width || canvas.lastHeight !== canvas.height) {
218
+ canvas.lastWidth = canvas.width;
219
+ canvas.lastHeight = canvas.height;
220
+ if (canvas.onResize) {
221
+ canvas.onResize();
222
+ }
223
+ }
224
+ }
225
+ /**
226
+ * Destroys a canvas
227
+ * @param id The canvas ID
228
+ */
229
+ static destroy(id) {
230
+ const canvas = Canvas.get(id);
231
+ if (!canvas) {
232
+ return;
233
+ }
234
+ if (canvas.autoWidth === true || canvas.autoHeight === true) {
235
+ if (typeof window !== "undefined") {
236
+ window.removeEventListener("resize", canvas.windowResizeCallback);
237
+ if (Canvas.resizeObserver) {
238
+ Canvas.resizeObserver.unobserve(canvas.element);
239
+ }
240
+ }
241
+ }
242
+ if (canvas.element.parentElement) {
243
+ canvas.element.parentElement.removeChild(canvas.element);
244
+ }
245
+ Canvas.list.delete(id);
246
+ }
247
+ /**
248
+ * Clears all canvases
249
+ */
250
+ static clear() {
251
+ Canvas.list.forEach((canvas) => {
252
+ if (canvas.clear) {
253
+ canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
254
+ }
255
+ if (canvas.clearColor) {
256
+ canvas.ctx.fillStyle = canvas.clearColor;
257
+ canvas.ctx.fillRect(0, 0, canvas.width, canvas.height);
258
+ }
259
+ });
260
+ }
261
+ /**
262
+ * Destroys all canvases
263
+ */
264
+ static destroyAll() {
265
+ Canvas.list.forEach((canvas) => {
266
+ Canvas.destroy(canvas.id);
267
+ });
268
+ Canvas.list.clear();
269
+ if (Canvas.resizeObserver) {
270
+ Canvas.resizeObserver.disconnect();
271
+ Canvas.resizeObserver = null;
272
+ }
273
+ }
274
+ /**
275
+ * Checks if a canvas is visible in the viewport
276
+ * @param id The canvas ID
277
+ * @returns True if the canvas is visible
278
+ */
279
+ static isVisible(id) {
280
+ const canvas = Canvas.get(id);
281
+ if (!canvas) {
282
+ return false;
283
+ }
284
+ const rect = canvas.element.getBoundingClientRect();
285
+ return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
286
+ }
287
+ /**
288
+ * Updates visibility status for all canvases
289
+ */
290
+ static updateVisibility() {
291
+ Canvas.list.forEach((canvas, id) => {
292
+ canvas.isVisible = Canvas.isVisible(id);
293
+ });
294
+ }
295
+ /**
296
+ * Resizes a canvas to specific dimensions
297
+ * @param id The canvas ID
298
+ * @param width New width
299
+ * @param height New height
300
+ */
301
+ static resize(id, width, height) {
302
+ const canvas = Canvas.get(id);
303
+ if (!canvas) {
304
+ return;
305
+ }
306
+ canvas.width = width;
307
+ canvas.height = height;
308
+ Canvas.fixDPI(id);
309
+ Canvas.onResize(id);
310
+ }
311
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Canvas.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canvas.test.d.ts","sourceRoot":"","sources":["../src/Canvas.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ import {
3
+ describe,
4
+ it,
5
+ expect,
6
+ beforeEach,
7
+ afterEach,
8
+ beforeAll,
9
+ afterAll,
10
+ vi
11
+ } from "vitest";
12
+ import Canvas from "./Canvas";
13
+ describe("Canvas", () => {
14
+ let canvasConfig;
15
+ const originalOffsetHeight = Object.getOwnPropertyDescriptor(
16
+ HTMLElement.prototype,
17
+ "offsetHeight"
18
+ );
19
+ const originalOffsetWidth = Object.getOwnPropertyDescriptor(
20
+ HTMLElement.prototype,
21
+ "offsetWidth"
22
+ );
23
+ beforeAll(() => {
24
+ Object.defineProperty(HTMLElement.prototype, "offsetHeight", {
25
+ configurable: true,
26
+ value: 800
27
+ });
28
+ Object.defineProperty(HTMLElement.prototype, "offsetWidth", {
29
+ configurable: true,
30
+ value: 600
31
+ });
32
+ Element.prototype.getBoundingClientRect = vi.fn(() => ({
33
+ top: 10,
34
+ right: 110,
35
+ bottom: 110,
36
+ left: 10,
37
+ width: 100,
38
+ height: 100,
39
+ x: 10,
40
+ y: 10,
41
+ toJSON: vi.fn()
42
+ }));
43
+ global.ResizeObserver = vi.fn().mockImplementation(() => ({
44
+ observe: vi.fn(),
45
+ unobserve: vi.fn(),
46
+ disconnect: vi.fn()
47
+ }));
48
+ });
49
+ beforeEach(() => {
50
+ vi.stubGlobal("innerWidth", 1024);
51
+ vi.stubGlobal("innerHeight", 768);
52
+ });
53
+ afterEach(() => {
54
+ vi.unstubAllGlobals();
55
+ });
56
+ afterAll(() => {
57
+ if (originalOffsetHeight) {
58
+ Object.defineProperty(
59
+ HTMLElement.prototype,
60
+ "offsetHeight",
61
+ originalOffsetHeight
62
+ );
63
+ }
64
+ if (originalOffsetWidth) {
65
+ Object.defineProperty(
66
+ HTMLElement.prototype,
67
+ "offsetWidth",
68
+ originalOffsetWidth
69
+ );
70
+ }
71
+ });
72
+ beforeEach(() => {
73
+ canvasConfig = {
74
+ id: "testCanvas",
75
+ width: 500,
76
+ height: 500,
77
+ autoWidth: false,
78
+ autoHeight: false,
79
+ clear: true,
80
+ clearColor: "#ffffff"
81
+ };
82
+ });
83
+ afterEach(() => {
84
+ Canvas.destroyAll();
85
+ });
86
+ it("should create a new canvas", () => {
87
+ const canvas = Canvas.create(canvasConfig);
88
+ expect(canvas).toBeDefined();
89
+ expect(Canvas.list.has(canvasConfig.id)).toBe(true);
90
+ });
91
+ it("should get an existing canvas by id", () => {
92
+ Canvas.create(canvasConfig);
93
+ const canvas = Canvas.get(canvasConfig.id);
94
+ expect(canvas).toBeDefined();
95
+ expect(canvas?.id).toBe(canvasConfig.id);
96
+ });
97
+ it("should set properties of an existing canvas", () => {
98
+ Canvas.create(canvasConfig);
99
+ Canvas.set(canvasConfig.id, { width: 600 });
100
+ const canvas = Canvas.get(canvasConfig.id);
101
+ expect(canvas?.width).toBe(600);
102
+ });
103
+ it("should set the clear color of an existing canvas", () => {
104
+ Canvas.create(canvasConfig);
105
+ Canvas.setClearColor(canvasConfig.id, "#000000");
106
+ const canvas = Canvas.get(canvasConfig.id);
107
+ expect(canvas?.clearColor).toBe("#000000");
108
+ });
109
+ it("should call the callback for each canvas", () => {
110
+ Canvas.create(canvasConfig);
111
+ const callback = vi.fn();
112
+ Canvas.forEach(callback);
113
+ expect(callback).toHaveBeenCalled();
114
+ });
115
+ it("should auto resize the canvas", () => {
116
+ canvasConfig.autoWidth = true;
117
+ canvasConfig.autoHeight = true;
118
+ Canvas.create(canvasConfig);
119
+ const canvas = Canvas.get(canvasConfig.id);
120
+ const parentElement = document.createElement("div");
121
+ document.body.appendChild(parentElement);
122
+ if (canvas?.element) {
123
+ parentElement.appendChild(canvas.element);
124
+ }
125
+ Canvas.autoResize(canvasConfig.id);
126
+ expect(canvas?.width).toBe(600);
127
+ expect(canvas?.height).toBe(800);
128
+ });
129
+ it("should append the canvas to a container", () => {
130
+ const container = document.createElement("div");
131
+ container.id = "container";
132
+ document.body.appendChild(container);
133
+ Canvas.create(canvasConfig);
134
+ Canvas.append(canvasConfig.id, "container");
135
+ const canvas = Canvas.get(canvasConfig.id);
136
+ expect(container.contains(canvas.element)).toBe(true);
137
+ });
138
+ it("should log warning when appending to non-existent container", () => {
139
+ const consoleSpy = vi.spyOn(console, "warn");
140
+ Canvas.create(canvasConfig);
141
+ Canvas.append(canvasConfig.id, "nonExistentContainer");
142
+ expect(consoleSpy).toHaveBeenCalledWith(
143
+ "Container with ID nonExistentContainer not found"
144
+ );
145
+ });
146
+ it("should fix the DPI of the canvas", () => {
147
+ Canvas.create(canvasConfig);
148
+ const canvas = Canvas.get(canvasConfig.id);
149
+ Canvas.fixDPI(canvasConfig.id);
150
+ if (canvas) {
151
+ expect(canvas.element.width).toBe(canvas.width * canvas.dpr);
152
+ }
153
+ if (canvas?.dpr !== void 0) {
154
+ expect(canvas.element.height).toBe(canvas.height * canvas.dpr);
155
+ }
156
+ });
157
+ it("should call onResize if dimensions change", () => {
158
+ const onResize = vi.fn();
159
+ canvasConfig.onResize = onResize;
160
+ Canvas.create(canvasConfig);
161
+ const canvas = Canvas.get(canvasConfig.id);
162
+ canvas.width = 600;
163
+ canvas.height = 600;
164
+ Canvas.onResize(canvasConfig.id);
165
+ expect(onResize).toHaveBeenCalled();
166
+ });
167
+ it("should destroy a canvas", () => {
168
+ Canvas.create(canvasConfig);
169
+ Canvas.destroy(canvasConfig.id);
170
+ expect(Canvas.list.has(canvasConfig.id)).toBe(false);
171
+ });
172
+ it("should clear all canvases", () => {
173
+ Canvas.create(canvasConfig);
174
+ const canvas = Canvas.get(canvasConfig.id);
175
+ Canvas.clear();
176
+ expect(canvas?.ctx.fillStyle).toBe(canvas?.clearColor);
177
+ });
178
+ it("should destroy all canvases", () => {
179
+ Canvas.create(canvasConfig);
180
+ Canvas.destroyAll();
181
+ expect(Canvas.list.size).toBe(0);
182
+ });
183
+ it("should not create a canvas with duplicate id", () => {
184
+ Canvas.create(canvasConfig);
185
+ expect(() => Canvas.create(canvasConfig)).toThrow(
186
+ `Already exists a canvas with the id ${canvasConfig.id}`
187
+ );
188
+ });
189
+ it("should not resize if autoWidth and autoHeight are false", () => {
190
+ canvasConfig.autoWidth = false;
191
+ canvasConfig.autoHeight = false;
192
+ Canvas.create(canvasConfig);
193
+ const canvas = Canvas.get(canvasConfig.id);
194
+ const initialWidth = canvas?.width;
195
+ const initialHeight = canvas?.height;
196
+ Canvas.autoResize(canvasConfig.id);
197
+ expect(canvas?.width).toBe(initialWidth);
198
+ expect(canvas?.height).toBe(initialHeight);
199
+ });
200
+ it("should not call onResize if dimensions do not change", () => {
201
+ const onResize = vi.fn();
202
+ canvasConfig.onResize = onResize;
203
+ Canvas.create(canvasConfig);
204
+ Canvas.onResize(canvasConfig.id);
205
+ expect(onResize).not.toHaveBeenCalled();
206
+ });
207
+ it("should remove canvas from DOM on destroy", () => {
208
+ Canvas.create(canvasConfig);
209
+ const canvas = Canvas.get(canvasConfig.id);
210
+ document.body.appendChild(canvas.element);
211
+ Canvas.destroy(canvasConfig.id);
212
+ expect(document.body.contains(canvas.element)).toBe(false);
213
+ });
214
+ it("should clear canvas with specified clear color", () => {
215
+ canvasConfig.clearColor = "#ff0000";
216
+ Canvas.create(canvasConfig);
217
+ const canvas = Canvas.get(canvasConfig.id);
218
+ Canvas.clear();
219
+ expect(canvas?.ctx.fillStyle).toBe(canvas?.clearColor);
220
+ });
221
+ it("should not clear canvas if clear is false", () => {
222
+ canvasConfig.clear = false;
223
+ Canvas.create(canvasConfig);
224
+ const canvas = Canvas.get(canvasConfig.id);
225
+ const clearRectSpy = vi.spyOn(canvas.ctx, "clearRect");
226
+ Canvas.clear();
227
+ expect(clearRectSpy).not.toHaveBeenCalled();
228
+ });
229
+ it("should handle window resize event", () => {
230
+ canvasConfig.autoWidth = true;
231
+ canvasConfig.autoHeight = true;
232
+ Canvas.create(canvasConfig);
233
+ const canvas = Canvas.get(canvasConfig.id);
234
+ window.dispatchEvent(new Event("resize"));
235
+ expect(canvas?.width).toBe(1024);
236
+ expect(canvas?.height).toBe(768);
237
+ });
238
+ it("should not throw error if trying to destroy non-existent canvas", () => {
239
+ expect(() => Canvas.destroy("nonExistentCanvas")).not.toThrow();
240
+ });
241
+ it("should not throw error if trying to get non-existent canvas", () => {
242
+ expect(Canvas.get("nonExistentCanvas")).toBeUndefined();
243
+ });
244
+ it("should not throw error if trying to set properties on non-existent canvas", () => {
245
+ expect(() => Canvas.set("nonExistentCanvas", { width: 600 })).not.toThrow();
246
+ });
247
+ it("should not throw error if trying to set clear color on non-existent canvas", () => {
248
+ expect(
249
+ () => Canvas.setClearColor("nonExistentCanvas", "#000000")
250
+ ).not.toThrow();
251
+ });
252
+ it("should not throw error if trying to append non-existent canvas", () => {
253
+ expect(() => Canvas.append("nonExistentCanvas", "container")).not.toThrow();
254
+ });
255
+ it("should not throw error if trying to fix DPI on non-existent canvas", () => {
256
+ expect(() => Canvas.fixDPI("nonExistentCanvas")).not.toThrow();
257
+ });
258
+ it("should not throw error if trying to call onResize on non-existent canvas", () => {
259
+ expect(() => Canvas.onResize("nonExistentCanvas")).not.toThrow();
260
+ });
261
+ it("should check if canvas is visible in viewport", () => {
262
+ Canvas.create(canvasConfig);
263
+ expect(Canvas.isVisible(canvasConfig.id)).toBe(true);
264
+ });
265
+ it("should update visibility status for all canvases", () => {
266
+ Canvas.create(canvasConfig);
267
+ Canvas.updateVisibility();
268
+ const canvas = Canvas.get(canvasConfig.id);
269
+ expect(canvas?.isVisible).toBe(true);
270
+ });
271
+ it("should resize canvas to specific dimensions", () => {
272
+ Canvas.create(canvasConfig);
273
+ Canvas.resize(canvasConfig.id, 800, 600);
274
+ const canvas = Canvas.get(canvasConfig.id);
275
+ expect(canvas?.width).toBe(800);
276
+ expect(canvas?.height).toBe(600);
277
+ });
278
+ it("should handle context loss and restoration", () => {
279
+ const contextLostCallback = vi.fn();
280
+ const contextRestoredCallback = vi.fn();
281
+ canvasConfig.contextLostCallback = contextLostCallback;
282
+ canvasConfig.contextRestoredCallback = contextRestoredCallback;
283
+ Canvas.create(canvasConfig);
284
+ const canvas = Canvas.get(canvasConfig.id);
285
+ canvas?.element.dispatchEvent(new Event("webglcontextlost"));
286
+ expect(contextLostCallback).toHaveBeenCalled();
287
+ canvas?.element.dispatchEvent(new Event("webglcontextrestored"));
288
+ expect(contextRestoredCallback).toHaveBeenCalled();
289
+ });
290
+ it("should throw an error if context cannot be created", () => {
291
+ const originalGetContext = HTMLCanvasElement.prototype.getContext;
292
+ HTMLCanvasElement.prototype.getContext = vi.fn().mockReturnValue(null);
293
+ expect(() => Canvas.create(canvasConfig)).toThrow(
294
+ "Failed to get 2D context from canvas element"
295
+ );
296
+ HTMLCanvasElement.prototype.getContext = originalGetContext;
297
+ });
298
+ it("should setup ResizeObserver if available", () => {
299
+ canvasConfig.autoWidth = true;
300
+ Canvas.create(canvasConfig);
301
+ expect(global.ResizeObserver).toHaveBeenCalled();
302
+ });
303
+ it("should update canvas when set() is called with width or height", () => {
304
+ Canvas.create(canvasConfig);
305
+ const fixDPISpy = vi.spyOn(Canvas, "fixDPI");
306
+ Canvas.set(canvasConfig.id, { width: 700 });
307
+ const canvas = Canvas.get(canvasConfig.id);
308
+ expect(fixDPISpy).toHaveBeenCalledWith(canvasConfig.id);
309
+ expect(canvas?.width).toBe(700);
310
+ });
311
+ });
@@ -0,0 +1,11 @@
1
+ export declare function clamp(n: number, min: number, max: number): number;
2
+ export declare function distance(ax: number, ay: number, bx: number, by: number): number;
3
+ export declare function pair(x: number, y: number): number;
4
+ export declare function unpairX(z: number): number;
5
+ export declare function unpairY(z: number): number;
6
+ export declare function degreesToRadians(degrees: number): number;
7
+ export declare function radiansToDegrees(radians: number): number;
8
+ export declare function angle(ax: number, ay: number, bx: number, by: number): number;
9
+ export declare function random(min?: number, max?: number, skew?: number): number;
10
+ export declare function randomInt(min: number, max: number): number;
11
+ //# sourceMappingURL=Common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Common.d.ts","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,QAAQ,CACtB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,MAAM,CAER;AAGD,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAKzC;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAKzC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAI5E;AAGD,wBAAgB,MAAM,CACpB,GAAG,GAAE,MAAU,EACf,GAAG,GAAE,MAAU,EACf,IAAI,GAAE,MAAU,GACf,MAAM,CA2BR;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D"}
package/dist/Common.js ADDED
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ export function clamp(n, min, max) {
3
+ return Math.min(Math.max(n, min), max);
4
+ }
5
+ export function distance(ax, ay, bx, by) {
6
+ return Math.hypot(bx - ax, by - ay);
7
+ }
8
+ export function pair(x, y) {
9
+ const xx = x >= 0 ? x * 2 : x * -2 - 1;
10
+ const yy = y >= 0 ? y * 2 : y * -2 - 1;
11
+ return xx >= yy ? xx * xx + xx + yy : yy * yy + xx;
12
+ }
13
+ export function unpairX(z) {
14
+ const sqrtz = Math.sqrt(z) >> 0;
15
+ const sqz = sqrtz * sqrtz;
16
+ const result1 = z - sqz >= sqrtz ? sqrtz : z - sqz;
17
+ return result1 % 2 === 0 ? result1 / 2 : (result1 + 1) / -2;
18
+ }
19
+ export function unpairY(z) {
20
+ const sqrtz = Math.sqrt(z) >> 0;
21
+ const sqz = sqrtz * sqrtz;
22
+ const result1 = z - sqz >= sqrtz ? z - sqz - sqrtz : sqrtz;
23
+ return result1 % 2 === 0 ? result1 / 2 : (result1 + 1) / -2;
24
+ }
25
+ export function degreesToRadians(degrees) {
26
+ return degrees * Math.PI / 180;
27
+ }
28
+ export function radiansToDegrees(radians) {
29
+ return radians * 180 / Math.PI;
30
+ }
31
+ export function angle(ax, ay, bx, by) {
32
+ const cy = by - ay;
33
+ const cx = bx - ax;
34
+ return Math.atan2(cy, cx);
35
+ }
36
+ export function random(min = 0, max = 1, skew = 1) {
37
+ if (min >= max) {
38
+ throw new Error("min must be less than max");
39
+ }
40
+ if (skew <= 0) {
41
+ throw new Error("skew must be greater than 0");
42
+ }
43
+ let u = 0;
44
+ let v = 0;
45
+ while (u === 0) {
46
+ u = crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
47
+ }
48
+ while (v === 0) {
49
+ v = crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
50
+ }
51
+ let num = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
52
+ num = num / 10 + 0.5;
53
+ if (num > 1 || num < 0) {
54
+ return random(min, max, skew);
55
+ } else {
56
+ num = Math.pow(num, skew);
57
+ num *= max - min;
58
+ num += min;
59
+ }
60
+ return num;
61
+ }
62
+ export function randomInt(min, max) {
63
+ return Math.floor(random(min, max));
64
+ }