@jiant/canvable 0.0.1-fix

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.
package/dist/main.mjs ADDED
@@ -0,0 +1,464 @@
1
+ // src/constants.ts
2
+ var TWO_PI = Math.PI * 2;
3
+
4
+ // src/classes/Vector2D.ts
5
+ var Vector2D = class _Vector2D {
6
+ /**
7
+ * Creates a new Vector2D instance
8
+ * @param x - The x component of the vector
9
+ * @param y - The y component of the vector
10
+ */
11
+ constructor(x = 0, y = 0) {
12
+ this.x = x;
13
+ this.y = y;
14
+ }
15
+ /**
16
+ * Adds another vector to this vector
17
+ * @param vector - The vector to add
18
+ * @param self - If true, modifies the current vector in place, otherwise returns a new vector
19
+ * @returns A new Vector2D representing the sum
20
+ */
21
+ add(vector, self = false) {
22
+ if (self) {
23
+ this.x += vector.x;
24
+ this.y += vector.y;
25
+ return this;
26
+ } else {
27
+ return new _Vector2D(this.x + vector.x, this.y + vector.y);
28
+ }
29
+ }
30
+ /**
31
+ * Subtracts another vector from this vector
32
+ * @param vector - The vector to subtract
33
+ * @param self - If true, modifies the current vector in place, otherwise returns a new vector
34
+ * @returns A new Vector2D representing the difference
35
+ */
36
+ subtract(vector, self = false) {
37
+ if (self) {
38
+ this.x -= vector.x;
39
+ this.y -= vector.y;
40
+ return this;
41
+ } else {
42
+ return new _Vector2D(this.x - vector.x, this.y - vector.y);
43
+ }
44
+ }
45
+ /**
46
+ * Multiplies this vector by a scalar value
47
+ * @param scalar - The scalar value to multiply by
48
+ * @returns A new Vector2D scaled by the input value
49
+ */
50
+ multiply(scalar) {
51
+ return new _Vector2D(this.x * scalar, this.y * scalar);
52
+ }
53
+ /**
54
+ * Divides this vector by a scalar value
55
+ * @param scalar - The scalar value to divide by
56
+ * @throws {Error} If scalar is zero
57
+ * @returns A new Vector2D divided by the scalar value
58
+ */
59
+ divide(scalar) {
60
+ if (scalar === 0) throw new Error("Cannot divide by zero");
61
+ return new _Vector2D(this.x / scalar, this.y / scalar);
62
+ }
63
+ /**
64
+ * Calculates the magnitude (length) of this vector
65
+ * @returns The magnitude of the vector
66
+ */
67
+ magnitude() {
68
+ return Math.sqrt(this.x * this.x + this.y * this.y);
69
+ }
70
+ /**
71
+ * Creates a normalized (unit) vector pointing in the same direction
72
+ * @returns A new Vector2D with magnitude 1 (or 0 if this vector is 0)
73
+ */
74
+ normalize() {
75
+ const mag = this.magnitude();
76
+ if (mag === 0) return new _Vector2D(0, 0);
77
+ return this.divide(mag);
78
+ }
79
+ /**
80
+ * Calculates the dot product with another vector
81
+ * @param vector - The vector to calculate the dot product with
82
+ * @returns The dot product of the two vectors
83
+ */
84
+ dot(vector) {
85
+ return this.x * vector.x + this.y * vector.y;
86
+ }
87
+ /**
88
+ * Calculates the 2D cross product with another vector
89
+ * @param vector - The vector to calculate the cross product with
90
+ * @returns The magnitude of the cross product
91
+ */
92
+ cross(vector) {
93
+ return this.x * vector.y - this.y * vector.x;
94
+ }
95
+ /**
96
+ * Calculates the angle between this vector and another vector
97
+ * @param vector - The vector to calculate the angle with
98
+ * @returns The angle in radians between the two vectors
99
+ */
100
+ angle(vector) {
101
+ const dot = this.dot(vector);
102
+ const mags = this.magnitude() * vector.magnitude();
103
+ return Math.acos(dot / mags);
104
+ }
105
+ /**
106
+ * Rotates the vector by a given angle
107
+ * @param angle - The angle to rotate by (in radians)
108
+ * @returns A new Vector2D rotated by the specified angle
109
+ */
110
+ rotate(angle) {
111
+ const cos = Math.cos(angle);
112
+ const sin = Math.sin(angle);
113
+ return new _Vector2D(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
114
+ }
115
+ /**
116
+ * Calculates the distance to another vector
117
+ * @param vector - The vector to calculate the distance to
118
+ * @returns The distance between the two vectors
119
+ */
120
+ distance(vector) {
121
+ return this.subtract(vector).magnitude();
122
+ }
123
+ /**
124
+ * Projects this vector onto another vector
125
+ * @param vector - The vector to project onto
126
+ * @returns A new Vector2D representing the projection
127
+ */
128
+ project(vector) {
129
+ const normalized = vector.normalize();
130
+ const scalar = this.dot(normalized);
131
+ return normalized.multiply(scalar);
132
+ }
133
+ /**
134
+ * Reflects this vector across a normal
135
+ * @param normal - The normal vector to reflect across
136
+ * @returns A new Vector2D representing the reflection
137
+ */
138
+ reflect(normal) {
139
+ const normalized = normal.normalize();
140
+ return this.subtract(normalized.multiply(2 * this.dot(normalized)));
141
+ }
142
+ /**
143
+ * Linearly interpolates between this vector and another vector
144
+ * @param vector - The vector to interpolate towards
145
+ * @param t - The interpolation parameter (0-1)
146
+ * @returns A new Vector2D representing the interpolated vector
147
+ */
148
+ lerp(vector, t) {
149
+ return new _Vector2D(this.x + (vector.x - this.x) * t, this.y + (vector.y - this.y) * t);
150
+ }
151
+ /**
152
+ * Limits the magnitude of this vector to a maximum value
153
+ * @param max - The maximum magnitude
154
+ * @returns A new Vector2D with limited magnitude
155
+ */
156
+ limit(max) {
157
+ const mag = this.magnitude();
158
+ if (mag > max) {
159
+ return this.normalize().multiply(max);
160
+ }
161
+ return new _Vector2D(this.x, this.y);
162
+ }
163
+ /**
164
+ * Checks if this vector is equal to another vector within a tolerance
165
+ * @param vector - The vector to compare with
166
+ * @param tolerance - The maximum difference allowed (default: 0.000001)
167
+ * @returns True if the vectors are equal within the tolerance
168
+ */
169
+ equals(vector, tolerance = 1e-6) {
170
+ return Math.abs(this.x - vector.x) < tolerance && Math.abs(this.y - vector.y) < tolerance;
171
+ }
172
+ /**
173
+ * Creates a copy of this vector
174
+ * @returns A new Vector2D with the same components
175
+ */
176
+ clone() {
177
+ return new _Vector2D(this.x, this.y);
178
+ }
179
+ /**
180
+ * Converts the vector to a string representation
181
+ * @returns A string representation of the vector
182
+ */
183
+ toString() {
184
+ return `Vector2D(${this.x}, ${this.y})`;
185
+ }
186
+ /**
187
+ * Creates a vector pointing in a specific direction
188
+ * @param angle - The angle in radians
189
+ * @param magnitude - The length of the vector (default: 1)
190
+ * @returns A new Vector2D with the specified angle and magnitude
191
+ */
192
+ static fromAngle(angle, magnitude = 1) {
193
+ return new _Vector2D(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
194
+ }
195
+ /**
196
+ * Creates a random vector with the specified magnitude
197
+ * @param magnitude - The length of the vector (default: 1)
198
+ * @returns A new Vector2D with random direction and specified magnitude
199
+ */
200
+ static random(magnitude = 1) {
201
+ const angle = Math.random() * TWO_PI;
202
+ return _Vector2D.fromAngle(angle, magnitude);
203
+ }
204
+ /**
205
+ * Adds two vectors together
206
+ * @param v1 - The first vector
207
+ * @param v2 - The second vector
208
+ * @returns A new Vector2D representing the sum
209
+ */
210
+ static add(v1, v2) {
211
+ return v1.add(v2);
212
+ }
213
+ /**
214
+ * Subtracts one vector from another
215
+ * @param v1 - The vector to subtract from
216
+ * @param v2 - The vector to subtract
217
+ * @returns A new Vector2D representing the difference
218
+ */
219
+ static subtract(v1, v2) {
220
+ return v1.subtract(v2);
221
+ }
222
+ /**
223
+ * Calculates the distance between two vectors
224
+ * @param v1 - The first vector
225
+ * @param v2 - The second vector
226
+ * @returns The distance between the vectors
227
+ */
228
+ static distance(v1, v2) {
229
+ return v1.distance(v2);
230
+ }
231
+ };
232
+
233
+ // src/nodes/Node.ts
234
+ import { v4 as uuidv4 } from "uuid";
235
+ var Node = class {
236
+ id;
237
+ pos;
238
+ children = [];
239
+ scene;
240
+ constructor(config) {
241
+ const { id, pos } = config || {};
242
+ this.id = id || this.generateID();
243
+ this.pos = pos || new Vector2D();
244
+ }
245
+ addObject(object) {
246
+ this.children.push(object);
247
+ }
248
+ removeObject(object) {
249
+ const index = this.children.indexOf(object);
250
+ if (index !== -1) {
251
+ this.children.splice(index, 1);
252
+ }
253
+ }
254
+ generateID() {
255
+ return uuidv4();
256
+ }
257
+ };
258
+
259
+ // src/nodes/Circle.ts
260
+ var Circle = class extends Node {
261
+ vel = new Vector2D();
262
+ size = 10;
263
+ friction = 0.1;
264
+ constructor(config) {
265
+ super();
266
+ this.vel = config?.vel || new Vector2D();
267
+ this.size = config?.size || 10;
268
+ this.friction = config?.friction || 0.1;
269
+ }
270
+ draw(ctx, selected) {
271
+ const radius = 10;
272
+ const cameraPos = this.scene?.camera.pos || new Vector2D();
273
+ const cameraScale = this.scene?.camera.zoom || 1;
274
+ const relativePos = new Vector2D((this.pos.x - cameraPos.x) * cameraScale, (this.pos.y - cameraPos.y) * cameraScale);
275
+ if (selected) {
276
+ ctx.beginPath();
277
+ ctx.arc(relativePos.x, relativePos.y, radius + 5, 0, Math.PI * 2);
278
+ ctx.fillStyle = "transparent";
279
+ ctx.lineWidth = 3;
280
+ ctx.strokeStyle = "white";
281
+ ctx.stroke();
282
+ ctx.closePath();
283
+ }
284
+ ctx.beginPath();
285
+ ctx.arc(relativePos.x, relativePos.y, radius, 0, Math.PI * 2);
286
+ ctx.fillStyle = "blue";
287
+ ctx.fill();
288
+ ctx.closePath();
289
+ }
290
+ };
291
+
292
+ // src/nodes/Camera.ts
293
+ var Camera = class extends Node {
294
+ zoom;
295
+ constructor(x = 0, y = 0, zoom = 1) {
296
+ super();
297
+ this.zoom = zoom;
298
+ }
299
+ update(deltaTime) {
300
+ }
301
+ transformCoordinates(position) {
302
+ return new Vector2D((position.x - this.pos.x) * this.zoom, (position.y - this.pos.y) * this.zoom);
303
+ }
304
+ };
305
+
306
+ // src/nodes/InputManager.ts
307
+ var InputManager = class extends Node {
308
+ keyState = /* @__PURE__ */ new Map();
309
+ keyJustPressed = /* @__PURE__ */ new Set();
310
+ mouseState = /* @__PURE__ */ new Map();
311
+ mouseJustPressed = /* @__PURE__ */ new Set();
312
+ constructor() {
313
+ super();
314
+ this.setupListeners();
315
+ }
316
+ setupListeners() {
317
+ window.addEventListener("keydown", this.handleKeyDown.bind(this));
318
+ window.addEventListener("keyup", this.handleKeyUp.bind(this));
319
+ window.addEventListener("mousedown", this.handleMouseDown.bind(this));
320
+ window.addEventListener("mouseup", this.handleMouseUp.bind(this));
321
+ }
322
+ handleKeyDown(event) {
323
+ const key = event.key;
324
+ if (!this.keyState.get(key)) {
325
+ this.keyState.set(key, true);
326
+ this.keyJustPressed.add(key);
327
+ }
328
+ }
329
+ handleKeyUp(event) {
330
+ const key = event.key;
331
+ this.keyState.set(key, false);
332
+ this.keyJustPressed.delete(key);
333
+ }
334
+ handleMouseDown(event) {
335
+ const button = event.button;
336
+ if (!this.mouseState.get(button)) {
337
+ this.mouseState.set(button, true);
338
+ this.mouseJustPressed.add(button);
339
+ }
340
+ }
341
+ handleMouseUp(event) {
342
+ const button = event.button;
343
+ this.mouseState.set(button, false);
344
+ this.mouseJustPressed.delete(button);
345
+ }
346
+ justPressed(keyOrButton) {
347
+ if (typeof keyOrButton === "string") {
348
+ return this.keyJustPressed.has(keyOrButton);
349
+ } else if (typeof keyOrButton === "number") {
350
+ return this.mouseJustPressed.has(keyOrButton);
351
+ }
352
+ return false;
353
+ }
354
+ isPressed(keyOrButton) {
355
+ if (typeof keyOrButton === "string") {
356
+ return !!this.keyState.get(keyOrButton);
357
+ } else if (typeof keyOrButton === "number") {
358
+ return !!this.mouseState.get(keyOrButton);
359
+ }
360
+ return false;
361
+ }
362
+ postUpdate() {
363
+ this.keyJustPressed.clear();
364
+ this.mouseJustPressed.clear();
365
+ }
366
+ };
367
+
368
+ // src/nodes/Scene.ts
369
+ var Scene = class extends Node {
370
+ camera;
371
+ inputManager;
372
+ ctx;
373
+ lastTime = 0;
374
+ totalTime = 0;
375
+ constructor(ctx, opts) {
376
+ super();
377
+ this.ctx = ctx;
378
+ this.camera = opts?.camera || new Camera();
379
+ this.inputManager = opts?.inputManager || new InputManager();
380
+ this.setup();
381
+ }
382
+ addObject(object) {
383
+ super.addObject(object);
384
+ object.scene = this;
385
+ }
386
+ setup() {
387
+ this.resizeCanvas();
388
+ }
389
+ resizeCanvas() {
390
+ const canvas = this.ctx.canvas;
391
+ const style = getComputedStyle(canvas);
392
+ const width = parseInt(style.width);
393
+ const height = parseInt(style.height);
394
+ canvas.width = width;
395
+ canvas.height = height;
396
+ }
397
+ update(deltaTime) {
398
+ this.camera.update(deltaTime);
399
+ this.children.forEach((child) => {
400
+ if (isUpdatable(child)) {
401
+ child.update(deltaTime);
402
+ }
403
+ });
404
+ }
405
+ draw(ctx) {
406
+ ctx.fillStyle = ctx.canvas.style.backgroundColor;
407
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
408
+ this.children.forEach((child) => {
409
+ if (isDrawable(child)) {
410
+ child.draw(ctx);
411
+ }
412
+ });
413
+ }
414
+ gameLoop(callback, targetFPS = 60) {
415
+ const frameInterval = 1e3 / targetFPS;
416
+ let lastFrameTime = 0;
417
+ const loop = (currentTime) => {
418
+ try {
419
+ const elapsed = currentTime - lastFrameTime;
420
+ if (elapsed > frameInterval) {
421
+ const deltaTime = (currentTime - this.lastTime) / 1e3;
422
+ this.lastTime = currentTime;
423
+ this.totalTime += deltaTime;
424
+ lastFrameTime = currentTime - elapsed % frameInterval;
425
+ this.update(deltaTime);
426
+ this.draw(this.ctx);
427
+ if (callback) {
428
+ callback(deltaTime, this.totalTime);
429
+ }
430
+ this.inputManager.postUpdate();
431
+ }
432
+ } catch (error) {
433
+ console.error("Error in game loop:", error);
434
+ }
435
+ requestAnimationFrame(loop);
436
+ };
437
+ this.lastTime = performance.now();
438
+ requestAnimationFrame(loop);
439
+ }
440
+ };
441
+ function isUpdatable(obj) {
442
+ return "update" in obj;
443
+ }
444
+ function isDrawable(obj) {
445
+ return "draw" in obj;
446
+ }
447
+
448
+ // src/main.ts
449
+ function getCanvas(id) {
450
+ const canvas = document.getElementById(id);
451
+ if (!canvas) {
452
+ throw new Error(`Canvas with id "${id}" not found`);
453
+ }
454
+ return canvas;
455
+ }
456
+ export {
457
+ Camera,
458
+ Circle,
459
+ Node,
460
+ Scene,
461
+ TWO_PI,
462
+ Vector2D,
463
+ getCanvas
464
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@jiant/canvable",
3
+ "version": "0.0.1-fix",
4
+ "description": "A tool for building canvas apps",
5
+ "main": "./dist/main.js",
6
+ "module": "./dist/main.mjs",
7
+ "types": "./dist/main.d.ts",
8
+ "type": "commonjs",
9
+ "files": [
10
+ "dist",
11
+ "package.json"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch"
16
+ },
17
+ "dependencies": {
18
+ "uuid": "^11.0.3"
19
+ },
20
+ "devDependencies": {
21
+ "typescript": "^5.7.2",
22
+ "tsup": "^8.3.5"
23
+ },
24
+ "keywords": [
25
+ "canvas",
26
+ "graphics",
27
+ "drawing",
28
+ "visualization"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ }
33
+ }