@guinetik/gcanvas 1.0.0 → 1.0.2

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 (102) hide show
  1. package/demos/coordinates.html +698 -0
  2. package/demos/cube3d.html +23 -0
  3. package/demos/demos.css +17 -3
  4. package/demos/dino.html +42 -0
  5. package/demos/fluid-simple.html +22 -0
  6. package/demos/fluid.html +37 -0
  7. package/demos/gameobjects.html +626 -0
  8. package/demos/index.html +19 -7
  9. package/demos/js/blob.js +18 -5
  10. package/demos/js/coordinates.js +840 -0
  11. package/demos/js/cube3d.js +789 -0
  12. package/demos/js/dino.js +1420 -0
  13. package/demos/js/fluid-simple.js +253 -0
  14. package/demos/js/fluid.js +527 -0
  15. package/demos/js/gameobjects.js +176 -0
  16. package/demos/js/plane3d.js +256 -0
  17. package/demos/js/platformer.js +1579 -0
  18. package/demos/js/sphere3d.js +229 -0
  19. package/demos/js/sprite.js +473 -0
  20. package/demos/js/tde/accretiondisk.js +65 -12
  21. package/demos/js/tde/blackholescene.js +2 -2
  22. package/demos/js/tde/config.js +2 -2
  23. package/demos/js/tde/index.js +152 -27
  24. package/demos/js/tde/lensedstarfield.js +32 -25
  25. package/demos/js/tde/tdestar.js +78 -98
  26. package/demos/js/tde/tidalstream.js +24 -8
  27. package/demos/plane3d.html +24 -0
  28. package/demos/platformer.html +43 -0
  29. package/demos/sphere3d.html +24 -0
  30. package/demos/sprite.html +18 -0
  31. package/docs/README.md +230 -222
  32. package/docs/api/FluidSystem.md +173 -0
  33. package/docs/concepts/architecture-overview.md +204 -204
  34. package/docs/concepts/coordinate-system.md +384 -0
  35. package/docs/concepts/rendering-pipeline.md +279 -279
  36. package/docs/concepts/shapes-vs-gameobjects.md +187 -0
  37. package/docs/concepts/two-layer-architecture.md +229 -229
  38. package/docs/fluid-dynamics.md +99 -0
  39. package/docs/getting-started/first-game.md +354 -354
  40. package/docs/getting-started/installation.md +175 -157
  41. package/docs/modules/collision/README.md +2 -2
  42. package/docs/modules/fluent/README.md +6 -6
  43. package/docs/modules/game/README.md +303 -303
  44. package/docs/modules/isometric-camera.md +2 -2
  45. package/docs/modules/isometric.md +1 -1
  46. package/docs/modules/painter/README.md +328 -328
  47. package/docs/modules/particle/README.md +3 -3
  48. package/docs/modules/shapes/README.md +221 -221
  49. package/docs/modules/shapes/base/euclidian.md +123 -123
  50. package/docs/modules/shapes/base/shape.md +262 -262
  51. package/docs/modules/shapes/base/transformable.md +243 -243
  52. package/docs/modules/state/README.md +2 -2
  53. package/docs/modules/util/README.md +1 -1
  54. package/docs/modules/util/camera3d.md +3 -3
  55. package/docs/modules/util/scene3d.md +1 -1
  56. package/package.json +3 -1
  57. package/readme.md +19 -5
  58. package/src/collision/collision.js +75 -0
  59. package/src/game/game.js +11 -5
  60. package/src/game/index.js +2 -1
  61. package/src/game/objects/index.js +3 -0
  62. package/src/game/objects/platformer-scene.js +411 -0
  63. package/src/game/objects/scene.js +14 -0
  64. package/src/game/objects/sprite.js +529 -0
  65. package/src/game/pipeline.js +20 -16
  66. package/src/game/systems/FluidSystem.js +835 -0
  67. package/src/game/systems/index.js +11 -0
  68. package/src/game/ui/button.js +39 -18
  69. package/src/game/ui/cursor.js +14 -0
  70. package/src/game/ui/fps.js +12 -4
  71. package/src/game/ui/index.js +2 -0
  72. package/src/game/ui/stepper.js +549 -0
  73. package/src/game/ui/theme.js +123 -0
  74. package/src/game/ui/togglebutton.js +9 -3
  75. package/src/game/ui/tooltip.js +11 -4
  76. package/src/io/input.js +75 -45
  77. package/src/io/mouse.js +44 -19
  78. package/src/io/touch.js +35 -12
  79. package/src/math/fluid.js +507 -0
  80. package/src/math/index.js +2 -0
  81. package/src/mixins/anchor.js +17 -7
  82. package/src/motion/tweenetik.js +16 -0
  83. package/src/shapes/cube3d.js +599 -0
  84. package/src/shapes/index.js +3 -0
  85. package/src/shapes/plane3d.js +687 -0
  86. package/src/shapes/sphere3d.js +75 -6
  87. package/src/util/camera2d.js +315 -0
  88. package/src/util/camera3d.js +218 -12
  89. package/src/util/index.js +1 -0
  90. package/src/webgl/shaders/plane-shaders.js +332 -0
  91. package/src/webgl/shaders/sphere-shaders.js +4 -2
  92. package/types/fluent.d.ts +361 -0
  93. package/types/game.d.ts +303 -0
  94. package/types/index.d.ts +144 -5
  95. package/types/math.d.ts +361 -0
  96. package/types/motion.d.ts +271 -0
  97. package/types/particle.d.ts +373 -0
  98. package/types/shapes.d.ts +107 -9
  99. package/types/util.d.ts +353 -0
  100. package/types/webgl.d.ts +109 -0
  101. package/disk_example.png +0 -0
  102. package/tde.png +0 -0
@@ -1,11 +1,17 @@
1
1
  import { GameObject } from "../objects/go.js";
2
2
  import { Rectangle, TextShape, Group } from "../../shapes/index.js";
3
+ import { UI_THEME } from "./theme.js";
3
4
 
4
5
  /**
5
6
  * Tooltip
6
7
  *
7
8
  * A GameObject that displays text near the cursor when shown.
8
9
  * Supports multiline text with automatic word wrapping.
10
+ *
11
+ * Theme: Terminal × Vercel aesthetic
12
+ * - Dark translucent background
13
+ * - Subtle green border glow
14
+ * - Neon green monospace text
9
15
  *
10
16
  * Usage:
11
17
  * const tooltip = new Tooltip(game, { ... });
@@ -34,10 +40,11 @@ export class Tooltip extends GameObject {
34
40
  constructor(game, options = {}) {
35
41
  super(game, { ...options, zIndex: 9999 }); // Always on top
36
42
 
37
- this.font = options.font || "12px monospace";
38
- this.textColor = options.textColor || "#fff";
39
- this.bgColor = options.bgColor || "rgba(0,0,0,0.85)";
40
- this.borderColor = options.borderColor || "rgba(255,255,255,0.3)";
43
+ // Terminal × Vercel theme defaults
44
+ this.font = options.font || UI_THEME.fonts.small;
45
+ this.textColor = options.textColor || UI_THEME.tooltip.text;
46
+ this.bgColor = options.bgColor || UI_THEME.tooltip.bg;
47
+ this.borderColor = options.borderColor || UI_THEME.tooltip.border;
41
48
  this.padding = options.padding ?? 8;
42
49
  this.offsetX = options.offsetX ?? 15;
43
50
  this.offsetY = options.offsetY ?? 15;
package/src/io/input.js CHANGED
@@ -1,15 +1,38 @@
1
1
  export class Input {
2
2
  static init(game) {
3
+ // Set defaults for last initialized game (backwards compatibility)
3
4
  Input.game = game;
4
5
  Input.x = 0;
5
6
  Input.y = 0;
6
7
  Input.down = false;
7
- game.events.on("mousedown", Input._onDown);
8
- game.events.on("mouseup", Input._onUp);
9
- game.events.on("mousemove", Input._onMove);
10
- game.events.on("touchstart", Input._onTouchStart);
11
- game.events.on("touchend", Input._onTouchEnd);
12
- game.events.on("touchmove", Input._onTouchMove);
8
+
9
+ // Create bound handlers that know which game they belong to
10
+ game.events.on("mousedown", (e) => Input._onDown(e, game));
11
+ game.events.on("mouseup", (e) => Input._onUp(e, game));
12
+ game.events.on("mousemove", (e) => Input._onMove(e, game));
13
+ game.events.on("touchstart", (e) => Input._onTouchStart(e, game));
14
+ game.events.on("touchend", (e) => Input._onTouchEnd(e, game));
15
+ game.events.on("touchmove", (e) => Input._onTouchMove(e, game));
16
+ }
17
+
18
+ /**
19
+ * Scales CSS pixel coordinates to canvas internal coordinates.
20
+ * This handles the case where the canvas is CSS-scaled (display size differs from resolution).
21
+ * @param {Object} game - The game instance
22
+ * @param {number} cssX - X coordinate in CSS pixels
23
+ * @param {number} cssY - Y coordinate in CSS pixels
24
+ * @returns {{x: number, y: number}} Scaled coordinates in canvas pixels
25
+ */
26
+ static _scaleToCanvas(game, cssX, cssY) {
27
+ const canvas = game.canvas;
28
+ const rect = canvas.getBoundingClientRect();
29
+ // Scale from CSS pixels to canvas internal pixels
30
+ const scaleX = canvas.width / rect.width;
31
+ const scaleY = canvas.height / rect.height;
32
+ return {
33
+ x: cssX * scaleX,
34
+ y: cssY * scaleY
35
+ };
13
36
  }
14
37
 
15
38
  static _setPosition(x, y) {
@@ -17,54 +40,61 @@ export class Input {
17
40
  Input.y = y;
18
41
  }
19
42
 
20
- static _onDown = (e) => {
43
+ static _onDown(e, game) {
21
44
  Input.down = true;
22
- Input._setPosition(e.offsetX, e.offsetY);
23
- Object.defineProperty(e, "x", { value: e.offsetX, configurable: true });
24
- Object.defineProperty(e, "y", { value: e.offsetY, configurable: true });
25
- Input.game.events.emit("inputdown", e);
26
- };
45
+ const scaled = Input._scaleToCanvas(game, e.offsetX, e.offsetY);
46
+ Input._setPosition(scaled.x, scaled.y);
47
+ Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
48
+ Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
49
+ game.events.emit("inputdown", e);
50
+ }
27
51
 
28
- static _onUp = (e) => {
52
+ static _onUp(e, game) {
29
53
  Input.down = false;
30
- Input._setPosition(e.offsetX, e.offsetY);
31
- Object.defineProperty(e, "x", { value: e.offsetX, configurable: true });
32
- Object.defineProperty(e, "y", { value: e.offsetY, configurable: true });
33
- Input.game.events.emit("inputup", e);
34
- };
54
+ const scaled = Input._scaleToCanvas(game, e.offsetX, e.offsetY);
55
+ Input._setPosition(scaled.x, scaled.y);
56
+ Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
57
+ Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
58
+ game.events.emit("inputup", e);
59
+ }
35
60
 
36
- static _onMove = (e) => {
37
- Input._setPosition(e.offsetX, e.offsetY);
38
- Object.defineProperty(e, "x", { value: e.offsetX, configurable: true });
39
- Object.defineProperty(e, "y", { value: e.offsetY, configurable: true });
40
- Input.game.events.emit("inputmove", e);
41
- };
61
+ static _onMove(e, game) {
62
+ const scaled = Input._scaleToCanvas(game, e.offsetX, e.offsetY);
63
+ Input._setPosition(scaled.x, scaled.y);
64
+ Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
65
+ Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
66
+ game.events.emit("inputmove", e);
67
+ }
42
68
 
43
- static _onTouchStart = (e) => {
69
+ static _onTouchStart(e, game) {
44
70
  const touch = e.touches[0];
45
- const rect = Input.game.canvas.getBoundingClientRect();
71
+ const rect = game.canvas.getBoundingClientRect();
46
72
  Input.down = true;
47
- const x = touch.clientX - rect.left;
48
- const y = touch.clientY - rect.top;
49
- Input._setPosition(x, y);
50
- Object.defineProperty(e, "x", { value: x, configurable: true });
51
- Object.defineProperty(e, "y", { value: y, configurable: true });
52
- Input.game.events.emit("inputdown", e);
53
- };
73
+ // Calculate CSS-relative position, then scale to canvas coordinates
74
+ const cssX = touch.clientX - rect.left;
75
+ const cssY = touch.clientY - rect.top;
76
+ const scaled = Input._scaleToCanvas(game, cssX, cssY);
77
+ Input._setPosition(scaled.x, scaled.y);
78
+ Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
79
+ Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
80
+ game.events.emit("inputdown", e);
81
+ }
54
82
 
55
- static _onTouchEnd = (e) => {
83
+ static _onTouchEnd(e, game) {
56
84
  Input.down = false;
57
- Input.game.events.emit("inputup", e);
58
- };
85
+ game.events.emit("inputup", e);
86
+ }
59
87
 
60
- static _onTouchMove = (e) => {
88
+ static _onTouchMove(e, game) {
61
89
  const touch = e.touches[0];
62
- const rect = Input.game.canvas.getBoundingClientRect();
63
- const x = touch.clientX - rect.left;
64
- const y = touch.clientY - rect.top;
65
- Input._setPosition(x, y);
66
- Object.defineProperty(e, "x", { value: x, configurable: true });
67
- Object.defineProperty(e, "y", { value: y, configurable: true });
68
- Input.game.events.emit("inputmove", e);
69
- };
90
+ const rect = game.canvas.getBoundingClientRect();
91
+ // Calculate CSS-relative position, then scale to canvas coordinates
92
+ const cssX = touch.clientX - rect.left;
93
+ const cssY = touch.clientY - rect.top;
94
+ const scaled = Input._scaleToCanvas(game, cssX, cssY);
95
+ Input._setPosition(scaled.x, scaled.y);
96
+ Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
97
+ Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
98
+ game.events.emit("inputmove", e);
99
+ }
70
100
  }
package/src/io/mouse.js CHANGED
@@ -1,5 +1,12 @@
1
1
  export class Mouse {
2
+ // Map of canvas -> game for multi-game support
3
+ static _gameMap = new Map();
4
+
2
5
  static init(game) {
6
+ // Store mapping from canvas to game
7
+ Mouse._gameMap.set(game.canvas, game);
8
+
9
+ // Set defaults for last initialized game (backwards compatibility)
3
10
  Mouse.game = game;
4
11
  Mouse.canvas = game.canvas;
5
12
  Mouse.x = 0;
@@ -8,42 +15,59 @@ export class Mouse {
8
15
  Mouse.middleDown = false;
9
16
  Mouse.rightDown = false;
10
17
 
11
- Mouse.canvas.addEventListener("mousemove", Mouse._onMove);
12
- Mouse.canvas.addEventListener("mousedown", Mouse._onDown);
13
- Mouse.canvas.addEventListener("mouseup", Mouse._onUp);
14
- Mouse.canvas.addEventListener("click", Mouse._onClick);
15
- Mouse.canvas.addEventListener("wheel", Mouse._onWheel);
18
+ game.canvas.addEventListener("mousemove", Mouse._onMove);
19
+ game.canvas.addEventListener("mousedown", Mouse._onDown);
20
+ game.canvas.addEventListener("mouseup", Mouse._onUp);
21
+ game.canvas.addEventListener("click", Mouse._onClick);
22
+ game.canvas.addEventListener("wheel", Mouse._onWheel);
23
+ }
24
+
25
+ // Get the game instance for a given event's target canvas
26
+ static _getGameForEvent(e) {
27
+ const canvas = e.currentTarget;
28
+ return Mouse._gameMap.get(canvas) || Mouse.game;
16
29
  }
17
30
 
18
- static _updatePosition(e) {
19
- const rect = Mouse.canvas.getBoundingClientRect();
20
- Mouse.x = e.clientX - rect.left;
21
- Mouse.y = e.clientY - rect.top;
31
+ static _updatePosition(e, game) {
32
+ const canvas = game.canvas;
33
+ const rect = canvas.getBoundingClientRect();
34
+ // Calculate CSS-relative position
35
+ const cssX = e.clientX - rect.left;
36
+ const cssY = e.clientY - rect.top;
37
+ // Scale to canvas internal coordinates (handles CSS scaling)
38
+ const scaleX = canvas.width / rect.width;
39
+ const scaleY = canvas.height / rect.height;
40
+ Mouse.x = cssX * scaleX;
41
+ Mouse.y = cssY * scaleY;
22
42
  }
23
43
 
24
44
  static _onMove = (e) => {
25
- Mouse._updatePosition(e);
26
- Mouse.game.events.emit("mousemove", e);
45
+ const game = Mouse._getGameForEvent(e);
46
+ Mouse._updatePosition(e, game);
47
+ game.events.emit("mousemove", e);
27
48
  };
28
49
 
29
50
  static _onDown = (e) => {
30
- Mouse._updatePosition(e);
51
+ const game = Mouse._getGameForEvent(e);
52
+ Mouse._updatePosition(e, game);
31
53
  if (e.button === 0) Mouse.leftDown = true;
32
54
  if (e.button === 1) Mouse.middleDown = true;
33
55
  if (e.button === 2) Mouse.rightDown = true;
34
- Mouse.game.events.emit("mousedown", e);
56
+ game.events.emit("mousedown", e);
35
57
  };
36
58
 
37
59
  static _onUp = (e) => {
38
- Mouse._updatePosition(e);
60
+ const game = Mouse._getGameForEvent(e);
61
+ Mouse._updatePosition(e, game);
39
62
  if (e.button === 0) Mouse.leftDown = false;
40
63
  if (e.button === 1) Mouse.middleDown = false;
41
64
  if (e.button === 2) Mouse.rightDown = false;
42
- Mouse.game.events.emit("mouseup", e);
65
+ game.events.emit("mouseup", e);
43
66
  };
44
67
 
45
68
  static _onClick = (e) => {
46
- Mouse._updatePosition(e);
69
+ const game = Mouse._getGameForEvent(e);
70
+ Mouse._updatePosition(e, game);
47
71
  // Emit enhanced event with canvas-relative coordinates
48
72
  // Note: e is a MouseEvent, add canvas-relative x/y directly
49
73
  e.canvasX = Mouse.x;
@@ -51,11 +75,12 @@ export class Mouse {
51
75
  // Also set x/y for convenience (matches expected fluent API)
52
76
  Object.defineProperty(e, 'x', { value: Mouse.x, writable: false });
53
77
  Object.defineProperty(e, 'y', { value: Mouse.y, writable: false });
54
- Mouse.game.events.emit("click", e);
78
+ game.events.emit("click", e);
55
79
  };
56
80
 
57
81
  static _onWheel = (e) => {
58
- Mouse._updatePosition(e);
59
- Mouse.game.events.emit("wheel", e);
82
+ const game = Mouse._getGameForEvent(e);
83
+ Mouse._updatePosition(e, game);
84
+ game.events.emit("wheel", e);
60
85
  };
61
86
  }
package/src/io/touch.js CHANGED
@@ -1,39 +1,62 @@
1
1
  export class Touch {
2
+ // Map of canvas -> game for multi-game support
3
+ static _gameMap = new Map();
4
+
2
5
  static init(game) {
6
+ // Store mapping from canvas to game
7
+ Touch._gameMap.set(game.canvas, game);
8
+
9
+ // Set defaults for last initialized game (backwards compatibility)
3
10
  Touch.game = game;
4
11
  Touch.canvas = game.canvas;
5
12
  Touch.x = 0;
6
13
  Touch.y = 0;
7
14
  Touch.active = false;
8
15
 
9
- Touch.canvas.addEventListener("touchstart", Touch._onStart);
10
- Touch.canvas.addEventListener("touchend", Touch._onEnd);
11
- Touch.canvas.addEventListener("touchmove", Touch._onMove);
16
+ game.canvas.addEventListener("touchstart", Touch._onStart);
17
+ game.canvas.addEventListener("touchend", Touch._onEnd);
18
+ game.canvas.addEventListener("touchmove", Touch._onMove);
19
+ }
20
+
21
+ // Get the game instance for a given event's target canvas
22
+ static _getGameForEvent(e) {
23
+ const canvas = e.currentTarget;
24
+ return Touch._gameMap.get(canvas) || Touch.game;
12
25
  }
13
26
 
14
- static _updatePosition(touch) {
15
- const rect = Touch.canvas.getBoundingClientRect();
16
- Touch.x = touch.clientX - rect.left;
17
- Touch.y = touch.clientY - rect.top;
27
+ static _updatePosition(touch, game) {
28
+ const canvas = game.canvas;
29
+ const rect = canvas.getBoundingClientRect();
30
+ // Calculate CSS-relative position
31
+ const cssX = touch.clientX - rect.left;
32
+ const cssY = touch.clientY - rect.top;
33
+ // Scale to canvas internal coordinates (handles CSS scaling)
34
+ const scaleX = canvas.width / rect.width;
35
+ const scaleY = canvas.height / rect.height;
36
+ Touch.x = cssX * scaleX;
37
+ Touch.y = cssY * scaleY;
18
38
  }
19
39
 
20
40
  static _onStart = (e) => {
21
41
  if (e.touches.length > 0) {
42
+ const game = Touch._getGameForEvent(e);
22
43
  Touch.active = true;
23
- Touch._updatePosition(e.touches[0]);
24
- Touch.game.events.emit("touchstart", e);
44
+ Touch._updatePosition(e.touches[0], game);
45
+ game.events.emit("touchstart", e);
25
46
  }
26
47
  };
27
48
 
28
49
  static _onEnd = (e) => {
50
+ const game = Touch._getGameForEvent(e);
29
51
  Touch.active = false;
30
- Touch.game.events.emit("touchend", e);
52
+ game.events.emit("touchend", e);
31
53
  };
32
54
 
33
55
  static _onMove = (e) => {
34
56
  if (e.touches.length > 0) {
35
- Touch._updatePosition(e.touches[0]);
36
- Touch.game.events.emit("touchmove", e);
57
+ const game = Touch._getGameForEvent(e);
58
+ Touch._updatePosition(e.touches[0], game);
59
+ game.events.emit("touchmove", e);
37
60
  }
38
61
  };
39
62
  }