@guinetik/gcanvas 1.0.4 → 2.0.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 (261) hide show
  1. package/dist/CNAME +1 -0
  2. package/dist/aizawa.html +27 -0
  3. package/dist/animations.html +31 -0
  4. package/dist/basic.html +38 -0
  5. package/dist/baskara.html +31 -0
  6. package/dist/bezier.html +35 -0
  7. package/dist/beziersignature.html +29 -0
  8. package/dist/blackhole.html +28 -0
  9. package/dist/blob.html +35 -0
  10. package/dist/clifford.html +25 -0
  11. package/dist/cmb.html +24 -0
  12. package/dist/coordinates.html +698 -0
  13. package/dist/cube3d.html +23 -0
  14. package/dist/dadras.html +26 -0
  15. package/dist/dejong.html +25 -0
  16. package/dist/demos.css +303 -0
  17. package/dist/dino.html +42 -0
  18. package/dist/easing.html +28 -0
  19. package/dist/events.html +195 -0
  20. package/dist/fluent.html +647 -0
  21. package/dist/fluid-simple.html +22 -0
  22. package/dist/fluid.html +37 -0
  23. package/dist/fractals.html +36 -0
  24. package/dist/gameobjects.html +626 -0
  25. package/dist/gcanvas.es.js +14368 -9093
  26. package/dist/gcanvas.es.min.js +1 -1
  27. package/dist/gcanvas.umd.js +1 -1
  28. package/dist/gcanvas.umd.min.js +1 -1
  29. package/dist/genart.html +26 -0
  30. package/dist/gendream.html +26 -0
  31. package/dist/group.html +36 -0
  32. package/dist/halvorsen.html +27 -0
  33. package/dist/home.html +587 -0
  34. package/dist/hyperbolic001.html +23 -0
  35. package/dist/hyperbolic002.html +23 -0
  36. package/dist/hyperbolic003.html +23 -0
  37. package/dist/hyperbolic004.html +23 -0
  38. package/dist/hyperbolic005.html +22 -0
  39. package/dist/index.html +446 -0
  40. package/dist/isometric.html +34 -0
  41. package/dist/js/aizawa.js +425 -0
  42. package/dist/js/animations.js +452 -0
  43. package/dist/js/basic.js +204 -0
  44. package/dist/js/baskara.js +751 -0
  45. package/dist/js/bezier.js +692 -0
  46. package/dist/js/beziersignature.js +241 -0
  47. package/dist/js/blackhole/accretiondisk.obj.js +379 -0
  48. package/dist/js/blackhole/blackhole.obj.js +318 -0
  49. package/dist/js/blackhole/index.js +409 -0
  50. package/dist/js/blackhole/particle.js +56 -0
  51. package/dist/js/blackhole/starfield.obj.js +218 -0
  52. package/dist/js/blob.js +2276 -0
  53. package/dist/js/clifford.js +236 -0
  54. package/dist/js/cmb.js +594 -0
  55. package/dist/js/coordinates.js +840 -0
  56. package/dist/js/cube3d.js +789 -0
  57. package/dist/js/dadras.js +405 -0
  58. package/dist/js/dejong.js +257 -0
  59. package/dist/js/dino.js +1420 -0
  60. package/dist/js/easing.js +477 -0
  61. package/dist/js/fluent.js +183 -0
  62. package/dist/js/fluid-simple.js +253 -0
  63. package/dist/js/fluid.js +527 -0
  64. package/dist/js/fractals.js +932 -0
  65. package/dist/js/fractalworker.js +93 -0
  66. package/dist/js/gameobjects.js +176 -0
  67. package/dist/js/genart.js +268 -0
  68. package/dist/js/gendream.js +209 -0
  69. package/dist/js/group.js +140 -0
  70. package/dist/js/halvorsen.js +405 -0
  71. package/dist/js/hyperbolic001.js +310 -0
  72. package/dist/js/hyperbolic002.js +388 -0
  73. package/dist/js/hyperbolic003.js +319 -0
  74. package/dist/js/hyperbolic004.js +345 -0
  75. package/dist/js/hyperbolic005.js +340 -0
  76. package/dist/js/info-toggle.js +25 -0
  77. package/dist/js/isometric.js +851 -0
  78. package/dist/js/kerr.js +1547 -0
  79. package/dist/js/lavalamp.js +590 -0
  80. package/dist/js/layout.js +354 -0
  81. package/dist/js/lorenz.js +425 -0
  82. package/dist/js/mondrian.js +285 -0
  83. package/dist/js/opacity.js +275 -0
  84. package/dist/js/painter.js +484 -0
  85. package/dist/js/particles-showcase.js +514 -0
  86. package/dist/js/particles.js +299 -0
  87. package/dist/js/patterns.js +397 -0
  88. package/dist/js/penrose/artifact.js +69 -0
  89. package/dist/js/penrose/blackhole.js +121 -0
  90. package/dist/js/penrose/constants.js +73 -0
  91. package/dist/js/penrose/game.js +943 -0
  92. package/dist/js/penrose/lore.js +278 -0
  93. package/dist/js/penrose/penrosescene.js +892 -0
  94. package/dist/js/penrose/ship.js +216 -0
  95. package/dist/js/penrose/sounds.js +211 -0
  96. package/dist/js/penrose/voidparticle.js +55 -0
  97. package/dist/js/penrose/voidscene.js +258 -0
  98. package/dist/js/penrose/voidship.js +144 -0
  99. package/dist/js/penrose/wormhole.js +46 -0
  100. package/dist/js/pipeline.js +555 -0
  101. package/dist/js/plane3d.js +256 -0
  102. package/dist/js/platformer.js +1579 -0
  103. package/dist/js/rossler.js +480 -0
  104. package/dist/js/scene.js +304 -0
  105. package/dist/js/scenes.js +320 -0
  106. package/dist/js/schrodinger.js +706 -0
  107. package/dist/js/schwarzschild.js +1015 -0
  108. package/dist/js/shapes.js +628 -0
  109. package/dist/js/space/alien.js +171 -0
  110. package/dist/js/space/boom.js +98 -0
  111. package/dist/js/space/boss.js +353 -0
  112. package/dist/js/space/buff.js +73 -0
  113. package/dist/js/space/bullet.js +102 -0
  114. package/dist/js/space/constants.js +85 -0
  115. package/dist/js/space/game.js +1884 -0
  116. package/dist/js/space/hud.js +112 -0
  117. package/dist/js/space/laserbeam.js +179 -0
  118. package/dist/js/space/lightning.js +277 -0
  119. package/dist/js/space/minion.js +192 -0
  120. package/dist/js/space/missile.js +212 -0
  121. package/dist/js/space/player.js +430 -0
  122. package/dist/js/space/powerup.js +90 -0
  123. package/dist/js/space/starfield.js +58 -0
  124. package/dist/js/space/starpower.js +90 -0
  125. package/dist/js/spacetime.js +559 -0
  126. package/dist/js/sphere3d.js +229 -0
  127. package/dist/js/sprite.js +473 -0
  128. package/dist/js/starfaux/config.js +118 -0
  129. package/dist/js/starfaux/enemy.js +353 -0
  130. package/dist/js/starfaux/hud.js +78 -0
  131. package/dist/js/starfaux/index.js +482 -0
  132. package/dist/js/starfaux/laser.js +182 -0
  133. package/dist/js/starfaux/player.js +468 -0
  134. package/dist/js/starfaux/terrain.js +560 -0
  135. package/dist/js/study001.js +275 -0
  136. package/dist/js/study002.js +366 -0
  137. package/dist/js/study003.js +331 -0
  138. package/dist/js/study004.js +389 -0
  139. package/dist/js/study005.js +209 -0
  140. package/dist/js/study006.js +194 -0
  141. package/dist/js/study007.js +192 -0
  142. package/dist/js/study008.js +413 -0
  143. package/dist/js/svgtween.js +204 -0
  144. package/dist/js/tde/accretiondisk.js +471 -0
  145. package/dist/js/tde/blackhole.js +219 -0
  146. package/dist/js/tde/blackholescene.js +209 -0
  147. package/dist/js/tde/config.js +59 -0
  148. package/dist/js/tde/index.js +820 -0
  149. package/dist/js/tde/jets.js +290 -0
  150. package/dist/js/tde/lensedstarfield.js +154 -0
  151. package/dist/js/tde/tdestar.js +297 -0
  152. package/dist/js/tde/tidalstream.js +372 -0
  153. package/dist/js/tde_old/blackhole.obj.js +354 -0
  154. package/dist/js/tde_old/debris.obj.js +791 -0
  155. package/dist/js/tde_old/flare.obj.js +239 -0
  156. package/dist/js/tde_old/index.js +448 -0
  157. package/dist/js/tde_old/star.obj.js +812 -0
  158. package/dist/js/tetris/config.js +157 -0
  159. package/dist/js/tetris/grid.js +286 -0
  160. package/dist/js/tetris/index.js +1195 -0
  161. package/dist/js/tetris/renderer.js +634 -0
  162. package/dist/js/tetris/tetrominos.js +280 -0
  163. package/dist/js/thomas.js +394 -0
  164. package/dist/js/tiles.js +312 -0
  165. package/dist/js/tweendemo.js +79 -0
  166. package/dist/js/visibility.js +102 -0
  167. package/dist/kerr.html +28 -0
  168. package/dist/lavalamp.html +27 -0
  169. package/dist/layouts.html +37 -0
  170. package/dist/logo.svg +4 -0
  171. package/dist/loop.html +84 -0
  172. package/dist/lorenz.html +27 -0
  173. package/dist/mondrian.html +32 -0
  174. package/dist/og_image.png +0 -0
  175. package/dist/opacity.html +36 -0
  176. package/dist/painter.html +39 -0
  177. package/dist/particles-showcase.html +28 -0
  178. package/dist/particles.html +24 -0
  179. package/dist/patterns.html +33 -0
  180. package/dist/penrose-game.html +31 -0
  181. package/dist/pipeline.html +737 -0
  182. package/dist/plane3d.html +24 -0
  183. package/dist/platformer.html +43 -0
  184. package/dist/rossler.html +27 -0
  185. package/dist/scene-interactivity-test.html +220 -0
  186. package/dist/scene.html +33 -0
  187. package/dist/scenes.html +96 -0
  188. package/dist/schrodinger.html +27 -0
  189. package/dist/schwarzschild.html +27 -0
  190. package/dist/shapes.html +16 -0
  191. package/dist/space.html +85 -0
  192. package/dist/spacetime.html +27 -0
  193. package/dist/sphere3d.html +24 -0
  194. package/dist/sprite.html +18 -0
  195. package/dist/starfaux.html +22 -0
  196. package/dist/study001.html +23 -0
  197. package/dist/study002.html +23 -0
  198. package/dist/study003.html +23 -0
  199. package/dist/study004.html +23 -0
  200. package/dist/study005.html +22 -0
  201. package/dist/study006.html +24 -0
  202. package/dist/study007.html +24 -0
  203. package/dist/study008.html +22 -0
  204. package/dist/svgtween.html +29 -0
  205. package/dist/tde.html +28 -0
  206. package/dist/tetris3d.html +25 -0
  207. package/dist/thomas.html +27 -0
  208. package/dist/tiles.html +28 -0
  209. package/dist/transforms.html +400 -0
  210. package/dist/tween.html +45 -0
  211. package/dist/visibility.html +33 -0
  212. package/package.json +1 -1
  213. package/readme.md +30 -22
  214. package/src/game/objects/go.js +7 -0
  215. package/src/game/objects/index.js +2 -0
  216. package/src/game/objects/isometric-scene.js +53 -3
  217. package/src/game/objects/layoutscene.js +57 -0
  218. package/src/game/objects/mask.js +241 -0
  219. package/src/game/objects/scene.js +19 -0
  220. package/src/game/objects/wrapper.js +14 -2
  221. package/src/game/pipeline.js +17 -0
  222. package/src/game/ui/button.js +101 -16
  223. package/src/game/ui/theme.js +0 -6
  224. package/src/game/ui/togglebutton.js +25 -14
  225. package/src/game/ui/tooltip.js +12 -4
  226. package/src/index.js +3 -0
  227. package/src/io/gesture.js +409 -0
  228. package/src/io/index.js +4 -1
  229. package/src/io/keys.js +9 -1
  230. package/src/io/screen.js +476 -0
  231. package/src/math/attractors.js +664 -0
  232. package/src/math/heat.js +106 -0
  233. package/src/math/index.js +1 -0
  234. package/src/mixins/draggable.js +15 -19
  235. package/src/painter/painter.shapes.js +11 -5
  236. package/src/particle/particle-system.js +165 -1
  237. package/src/physics/index.js +26 -0
  238. package/src/physics/physics-updaters.js +333 -0
  239. package/src/physics/physics.js +375 -0
  240. package/src/shapes/image.js +5 -5
  241. package/src/shapes/index.js +2 -0
  242. package/src/shapes/parallelogram.js +147 -0
  243. package/src/shapes/righttriangle.js +115 -0
  244. package/src/shapes/svg.js +281 -100
  245. package/src/shapes/text.js +22 -6
  246. package/src/shapes/transformable.js +5 -0
  247. package/src/sound/effects.js +807 -0
  248. package/src/sound/index.js +13 -0
  249. package/src/webgl/index.js +7 -0
  250. package/src/webgl/shaders/clifford-point-shaders.js +131 -0
  251. package/src/webgl/shaders/dejong-point-shaders.js +131 -0
  252. package/src/webgl/shaders/point-sprite-shaders.js +152 -0
  253. package/src/webgl/webgl-clifford-renderer.js +477 -0
  254. package/src/webgl/webgl-dejong-renderer.js +472 -0
  255. package/src/webgl/webgl-line-renderer.js +391 -0
  256. package/src/webgl/webgl-particle-renderer.js +410 -0
  257. package/types/index.d.ts +30 -2
  258. package/types/io.d.ts +217 -0
  259. package/types/physics.d.ts +299 -0
  260. package/types/shapes.d.ts +8 -0
  261. package/types/webgl.d.ts +188 -109
@@ -0,0 +1,400 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Transform API Demo</title>
8
+ <link rel="stylesheet" href="demos.css" />
9
+ <script src="./js/info-toggle.js"></script>
10
+ <style>
11
+ #debug-toggle {
12
+ position: absolute;
13
+ bottom: 20px;
14
+ left: 20px;
15
+ z-index: 100;
16
+ padding: 10px 20px;
17
+ font-family: monospace;
18
+ font-size: 14px;
19
+ background: #333;
20
+ color: #ff00ff;
21
+ border: 2px solid #ff00ff;
22
+ cursor: pointer;
23
+ transition: all 0.2s ease;
24
+ }
25
+ #debug-toggle:hover {
26
+ background: #ff00ff;
27
+ color: #000;
28
+ }
29
+ #debug-toggle.active {
30
+ background: #ff00ff;
31
+ color: #000;
32
+ }
33
+ </style>
34
+ </head>
35
+
36
+ <body>
37
+ <div id="info" class="light">
38
+ <strong>Transform API Demo</strong> — The <code>Transform</code> API provides a consistent, chainable interface for modifying shape properties.
39
+ Use <code>shape.transform.x(100).y(200).rotation(45).scale(0.8)</code> for fluent chaining,
40
+ <code>shape.transform.set({ x: 100, rotation: 45 })</code> for batch updates, or
41
+ <code>shape.transform.translateBy(10, 20)</code> for relative transforms.
42
+ Click to randomize colors.
43
+ </div>
44
+ <button id="debug-toggle">Toggle Debug</button>
45
+ <canvas id="game"></canvas>
46
+
47
+ <script type="module">
48
+ import {
49
+ Game,
50
+ Scene,
51
+ GameObject,
52
+ FPSCounter,
53
+ Rectangle,
54
+ Circle,
55
+ Star,
56
+ Triangle,
57
+ TextShape,
58
+ Group,
59
+ Transform,
60
+ Painter,
61
+ Easing
62
+ } from "/gcanvas.es.min.js";
63
+
64
+ // Track all shapes for debug toggle
65
+ const allShapes = [];
66
+
67
+ /**
68
+ * TransformDemo Game
69
+ * Showcases the new Transform API with animated shapes
70
+ */
71
+ class TransformDemoGame extends Game {
72
+ constructor(canvas) {
73
+ super(canvas);
74
+ this.enableFluidSize();
75
+ this.backgroundColor = "black";
76
+ }
77
+
78
+ onResize() {
79
+ if (this.scene) {
80
+ this.scene.width = this.width - 20;
81
+ this.scene.height = this.height - 20;
82
+ }
83
+ }
84
+
85
+ init() {
86
+ super.init();
87
+ // Main scene
88
+ this.scene = new Scene(this, {
89
+ width: this.width - 20,
90
+ height: this.height - 20,
91
+ debug: false,
92
+ anchor: "center"
93
+ });
94
+
95
+ this.pipeline.add(this.scene);
96
+
97
+ // Add demos
98
+ this.scene.add(new FluentAPIDemo(this));
99
+ this.scene.add(new GroupTransformDemo(this));
100
+ this.scene.add(new BatchUpdateDemo(this));
101
+ this.scene.add(new RelativeTransformDemo(this));
102
+
103
+ // FPS counter
104
+ this.pipeline.add(new FPSCounter(this, { anchor: "bottom-right" }));
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Demo 1: Fluent API Chaining
110
+ * Shows basic chained transform calls
111
+ */
112
+ class FluentAPIDemo extends GameObject {
113
+ constructor(game) {
114
+ super(game);
115
+
116
+ // Create a rectangle using the new Transform API
117
+ this.rect = new Rectangle({
118
+ width: 80,
119
+ height: 60,
120
+ color: "#e94560",
121
+ stroke: "#fff",
122
+ lineWidth: 2,
123
+ debug: false,
124
+ debugColor: "#ff00ff"
125
+ });
126
+ allShapes.push(this.rect);
127
+
128
+ // Use fluent API to set initial position
129
+ this.rect.transform
130
+ .x(-200)
131
+ .y(-120);
132
+
133
+ // Label
134
+ this.label = new TextShape("Fluent API", {
135
+ x: -200,
136
+ y: -170,
137
+ font: "bold 14px monospace",
138
+ color: "#fff",
139
+ align: "center"
140
+ });
141
+
142
+ this.elapsed = 0;
143
+ }
144
+
145
+ update(dt) {
146
+ super.update(dt);
147
+ this.elapsed += dt;
148
+
149
+ // Animate using the transform API
150
+ this.rect.transform
151
+ .rotation(this.elapsed * 45)
152
+ .scaleX(0.8 + Math.sin(this.elapsed * 2) * 0.3)
153
+ .scaleY(0.8 + Math.cos(this.elapsed * 2) * 0.3);
154
+ }
155
+
156
+ draw() {
157
+ super.draw();
158
+ this.rect.render();
159
+ this.label.render();
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Demo 2: Group Transform Operations
165
+ * Shows group-wide transform operations
166
+ */
167
+ class GroupTransformDemo extends GameObject {
168
+ constructor(game) {
169
+ super(game);
170
+
171
+ // Create a group with multiple shapes
172
+ this.group = new Group({ debug: false, debugColor: "#ff00ff" });
173
+ allShapes.push(this.group);
174
+
175
+ // Add shapes to group using transform API
176
+ for (let i = 0; i < 3; i++) {
177
+ const circle = new Circle(15 + i * 5, {
178
+ color: Painter.colors.randomColorHSL(),
179
+ stroke: "#fff",
180
+ lineWidth: 1,
181
+ debug: false,
182
+ debugColor: "#ff00ff"
183
+ });
184
+ allShapes.push(circle);
185
+ // Position using transform
186
+ circle.transform.position(i * 40 - 40, 0);
187
+ this.group.add(circle);
188
+ }
189
+
190
+ // Position the group
191
+ this.group.transform.position(0, -120);
192
+
193
+ // Label
194
+ this.label = new TextShape("Group Transforms", {
195
+ x: 0,
196
+ y: -170,
197
+ font: "bold 14px monospace",
198
+ color: "#fff",
199
+ align: "center"
200
+ });
201
+
202
+ this.elapsed = 0;
203
+ }
204
+
205
+ update(dt) {
206
+ super.update(dt);
207
+ this.elapsed += dt;
208
+
209
+ // Animate group transform
210
+ this.group.transform
211
+ .rotation(this.elapsed * 30)
212
+ .scale(0.9 + Math.sin(this.elapsed * 1.5) * 0.2);
213
+
214
+ // Animate individual children using forEachTransform
215
+ this.group.forEachTransform((t, child, i) => {
216
+ // Each child rotates at different speed
217
+ t.rotation(-this.elapsed * (60 + i * 30));
218
+ });
219
+ }
220
+
221
+ draw() {
222
+ super.draw();
223
+ this.group.render();
224
+ this.label.render();
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Demo 3: Batch Updates
230
+ * Shows using set() for multiple properties at once
231
+ */
232
+ class BatchUpdateDemo extends GameObject {
233
+ constructor(game) {
234
+ super(game);
235
+
236
+ this.star = new Star(30, 5, 0.5, {
237
+ color: "#ffc107",
238
+ stroke: "#fff",
239
+ lineWidth: 2,
240
+ debug: false,
241
+ debugColor: "#ff00ff"
242
+ });
243
+ allShapes.push(this.star);
244
+
245
+ // Use batch set for initial state
246
+ this.star.transform.set({
247
+ x: 200,
248
+ y: -120,
249
+ rotation: 0,
250
+ scaleX: 1,
251
+ scaleY: 1
252
+ });
253
+
254
+ // Label
255
+ this.label = new TextShape("Batch set()", {
256
+ x: 200,
257
+ y: -170,
258
+ font: "bold 14px monospace",
259
+ color: "#fff",
260
+ align: "center"
261
+ });
262
+
263
+ this.elapsed = 0;
264
+ }
265
+
266
+ update(dt) {
267
+ super.update(dt);
268
+ this.elapsed += dt;
269
+
270
+ // Use batch update for animation
271
+ const pulse = Math.sin(this.elapsed * 3);
272
+ const wobble = Math.sin(this.elapsed * 5) * 5;
273
+
274
+ this.star.transform.set({
275
+ rotation: this.elapsed * 60,
276
+ scaleX: 1 + pulse * 0.3,
277
+ scaleY: 1 - pulse * 0.3
278
+ });
279
+
280
+ // Also translate using relative method
281
+ this.star.transform.x(200 + wobble);
282
+ }
283
+
284
+ draw() {
285
+ super.draw();
286
+ this.star.render();
287
+ this.label.render();
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Demo 4: Animated Transforms
293
+ * Shows position, rotation, and scale animations
294
+ */
295
+ class RelativeTransformDemo extends GameObject {
296
+ constructor(game) {
297
+ super(game);
298
+
299
+ // Create shapes for demonstrating animated transforms
300
+ this.shapes = [];
301
+ const positions = [
302
+ { x: -200, y: 80 },
303
+ { x: 0, y: 80 },
304
+ { x: 200, y: 80 }
305
+ ];
306
+
307
+ const types = [
308
+ { name: "position", shape: new Rectangle({ width: 50, height: 50, color: "#00d9ff", stroke: "#fff", lineWidth: 2, debug: false, debugColor: "#ff00ff" }) },
309
+ { name: "rotation", shape: new Triangle(40, { color: "#ff6b6b", stroke: "#fff", lineWidth: 2, debug: false, debugColor: "#ff00ff" }) },
310
+ { name: "scale", shape: new Circle(25, { color: "#7bed9f", stroke: "#fff", lineWidth: 2, debug: false, debugColor: "#ff00ff" }) }
311
+ ];
312
+
313
+ types.forEach((type, i) => {
314
+ allShapes.push(type.shape);
315
+ type.shape.transform.position(positions[i].x, positions[i].y);
316
+ type.label = new TextShape(type.name + "()", {
317
+ x: positions[i].x,
318
+ y: 30,
319
+ font: "bold 14px monospace",
320
+ color: "#fff",
321
+ align: "center"
322
+ });
323
+ type.baseX = positions[i].x;
324
+ type.baseY = positions[i].y;
325
+ this.shapes.push(type);
326
+ });
327
+
328
+ this.elapsed = 0;
329
+ this.lastUpdate = 0;
330
+ }
331
+
332
+ update(dt) {
333
+ super.update(dt);
334
+ this.elapsed += dt;
335
+
336
+ // position demo - oscillate position
337
+ const tx = Math.sin(this.elapsed * 2) * 30;
338
+ const ty = Math.cos(this.elapsed * 3) * 15;
339
+ this.shapes[0].shape.transform.position(this.shapes[0].baseX + tx, this.shapes[0].baseY + ty);
340
+
341
+ // rotation demo - continuous rotation
342
+ this.shapes[1].shape.transform.rotation(this.elapsed * 90);
343
+
344
+ // scale demo - pulsing scale
345
+ const scale = 0.7 + Math.abs(Math.sin(this.elapsed * 2)) * 0.6;
346
+ this.shapes[2].shape.transform.scale(scale);
347
+ }
348
+
349
+ draw() {
350
+ super.draw();
351
+ this.shapes.forEach(s => {
352
+ s.shape.render();
353
+ s.label.render();
354
+ });
355
+ }
356
+ }
357
+
358
+ // Start the game
359
+ window.addEventListener("load", () => {
360
+ const canvas = document.getElementById("game");
361
+ const game = new TransformDemoGame(canvas);
362
+ game.start();
363
+
364
+ // Debug toggle button
365
+ let debugEnabled = false;
366
+ const debugBtn = document.getElementById("debug-toggle");
367
+
368
+ debugBtn.addEventListener("click", () => {
369
+ debugEnabled = !debugEnabled;
370
+ debugBtn.classList.toggle("active", debugEnabled);
371
+ debugBtn.textContent = debugEnabled ? "Debug: ON" : "Toggle Debug";
372
+
373
+ // Toggle debug on all shapes
374
+ allShapes.forEach(shape => {
375
+ shape._debug = debugEnabled;
376
+ });
377
+ });
378
+
379
+ // Randomize colors on click
380
+ canvas.addEventListener("click", () => {
381
+ game.scene.children.forEach(child => {
382
+ if (child.rect) child.rect.color = Painter.colors.randomColorHSL();
383
+ if (child.star) child.star.color = Painter.colors.randomColorHSL();
384
+ if (child.group) {
385
+ child.group.children.forEach(c => {
386
+ c.color = Painter.colors.randomColorHSL();
387
+ });
388
+ }
389
+ if (child.shapes) {
390
+ child.shapes.forEach(s => {
391
+ s.shape.color = Painter.colors.randomColorHSL();
392
+ });
393
+ }
394
+ });
395
+ });
396
+ });
397
+ </script>
398
+ </body>
399
+
400
+ </html>
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Tween</title>
8
+ <link rel="stylesheet" href="demos.css" />
9
+ <script src="./js/info-toggle.js"></script>
10
+ </head>
11
+
12
+ <body>
13
+ <div id="info">
14
+ <strong>Tween Demo</strong> - A box animated over time. The library features two motion-related classes:
15
+ <span style="color:#CCC">
16
+ <li><code>Tween</code> - Contains motion primitives, lerps and easing functions.</li>
17
+ <li><code>Tweenetik</code> - A shorcut class that leverates Tween to interpolate object properties over time</li>
18
+ <pre>
19
+ Tweenetik.to(
20
+ mySprite, // an object or sprite
21
+ { scaleX: scale, scaleY: scale}, // the properties & end-values
22
+ 2.0, // duration in seconds
23
+ Easing.easeOutBounce, // easing function
24
+ {
25
+ delay: 0.5, // optional 0.5s delay
26
+ onStart: () => console.log("Tween started!"),
27
+ onComplete: () => console.log("Tween done!"),
28
+ onUpdate: () => console.log("Updated frame"),
29
+ }
30
+ );</pre>
31
+ </span>
32
+ </div>
33
+ <canvas id="game"></canvas>
34
+ <script type="module">
35
+ import { MyGame } from './js/tweendemo.js';
36
+
37
+ window.addEventListener("load", () => {
38
+ const canvas = document.getElementById("game");
39
+ const game = new MyGame(canvas);
40
+ game.start();
41
+ });
42
+ </script>
43
+ </body>
44
+
45
+ </html>
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Opacity</title>
8
+ <link rel="stylesheet" href="demos.css" />
9
+ <script src="./js/info-toggle.js"></script>
10
+ </head>
11
+
12
+ <body>
13
+ <div id="info">
14
+ <strong>Visibility Toggle Demo</strong> - Shows how game objects can be dynamically shown or hidden.
15
+ <span style="color:#CCC">
16
+ <li><code>Transformable</code> — The base class providing the <code>visible</code> property.</li>
17
+ <li><code>Pipeline Integration</code> — The pipeline skips invisible objects during rendering.</li>
18
+ <li><code>Dynamic Toggling</code> — Click the button to toggle visibility of a random square.</li>
19
+ </span>
20
+ </div>
21
+ <canvas id="game"></canvas>
22
+ <script type="module">
23
+ import { MyGame } from './js/visibility.js';
24
+
25
+ window.addEventListener("load", () => {
26
+ const canvas = document.getElementById("game");
27
+ const game = new MyGame(canvas);
28
+ game.start();
29
+ });
30
+ </script>
31
+ </body>
32
+
33
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guinetik/gcanvas",
3
- "version": "1.0.4",
3
+ "version": "2.0.0",
4
4
  "description": "A batteries-included 2D canvas framework for games and generative art",
5
5
  "main": "dist/gcanvas.es.js",
6
6
  "types": "types/index.d.ts",
package/readme.md CHANGED
@@ -235,7 +235,7 @@ game.start(); // Start the game loop
235
235
 
236
236
  - `update(dt)` — Run game logic each frame
237
237
  - `render()` — Optional custom rendering
238
- - Event handling through `enableInteractivity(shape)`
238
+ - Event handling through the event emitter pattern
239
239
 
240
240
  This is the base class for all interactive entities:
241
241
 
@@ -244,7 +244,14 @@ class Player extends GameObject {
244
244
  constructor(game) {
245
245
  super(game);
246
246
  this.shape = new Circle(100, 100, 40, { fillColor: "blue" });
247
- this.enableInteractivity(this.shape);
247
+
248
+ // Enable interactivity
249
+ this.interactive = true;
250
+
251
+ // Listen for input events
252
+ this.on('inputdown', (e) => {
253
+ console.log('Player clicked!');
254
+ });
248
255
  }
249
256
 
250
257
  update(dt) {
@@ -257,10 +264,6 @@ class Player extends GameObject {
257
264
  render() {
258
265
  this.shape.draw();
259
266
  }
260
-
261
- onPointerDown(e) {
262
- console.log('Player clicked!');
263
- }
264
267
  }
265
268
  ```
266
269
 
@@ -396,21 +399,26 @@ The `Tweenetik` system animates object properties directly over time using easin
396
399
 
397
400
  ```js
398
401
  // Animate a button when pressed
399
- onPointerDown() {
400
- Tweenetik.to(this.shape,
401
- { scaleX: 1.2, scaleY: 1.2 },
402
- 0.2,
403
- Easing.easeOutBack,
404
- {
405
- onComplete: () => {
406
- Tweenetik.to(this.shape,
407
- { scaleX: 1.0, scaleY: 1.0 },
408
- 0.3,
409
- Easing.easeInOutQuad
410
- );
402
+ constructor(game) {
403
+ super(game);
404
+ this.interactive = true;
405
+
406
+ this.on('inputdown', () => {
407
+ Tweenetik.to(this.shape,
408
+ { scaleX: 1.2, scaleY: 1.2 },
409
+ 0.2,
410
+ Easing.easeOutBack,
411
+ {
412
+ onComplete: () => {
413
+ Tweenetik.to(this.shape,
414
+ { scaleX: 1.0, scaleY: 1.0 },
415
+ 0.3,
416
+ Easing.easeInBack
417
+ );
418
+ }
411
419
  }
412
- }
413
- );
420
+ );
421
+ });
414
422
  }
415
423
  ```
416
424
 
@@ -545,7 +553,7 @@ class Bob extends GameObject {
545
553
  constructor(game) {
546
554
  super(game);
547
555
  this.shape = new Circle(100, 100, 40, { fillColor: "tomato" });
548
- this.enableInteractivity(this.shape);
556
+ this.interactive = true;
549
557
  }
550
558
 
551
559
  update(dt) {
@@ -575,7 +583,7 @@ class SpinningShape extends GameObject {
575
583
  constructor(game) {
576
584
  super(game);
577
585
  this.shape = new Circle(200, 200, 50, { fillColor: 'cyan' });
578
- this.enableInteractivity(this.shape);
586
+ this.interactive = true;
579
587
  this.hovered = false;
580
588
 
581
589
  this.on('mouseover', () => this.hovered = true);
@@ -163,6 +163,13 @@ export class GameObject extends Transformable {
163
163
  localX -= obj.x || 0;
164
164
  localY -= obj.y || 0;
165
165
 
166
+ // Apply additional hit test offset (e.g., scroll offset from LayoutScene)
167
+ if (obj.getHitTestOffset) {
168
+ const offset = obj.getHitTestOffset();
169
+ localX -= offset.x || 0;
170
+ localY -= offset.y || 0;
171
+ }
172
+
166
173
  // Rotation: apply inverse rotation if needed
167
174
  if (obj.rotation) {
168
175
  const cos = Math.cos(-obj.rotation);
@@ -56,3 +56,5 @@ export { Sprite } from "./sprite.js";
56
56
  export { SpriteSheet } from "./spritesheet.js";
57
57
  export { Text } from "./text.js";
58
58
  export { ImageGo } from "./imagego.js";
59
+ // Utilities
60
+ export { Mask } from "./mask.js";
@@ -178,6 +178,46 @@ export class IsometricScene extends Scene {
178
178
  };
179
179
  }
180
180
 
181
+ /**
182
+ * Compute depth value for sorting, accounting for camera rotation.
183
+ * Use this when implementing custom isoDepth getters for box-like objects.
184
+ *
185
+ * For a rectangular object, pass all 4 corners and this will return
186
+ * the depth of the "front" corner (highest rotated x+y) at the current camera angle.
187
+ *
188
+ * @param {Array<{x: number, y: number}>} corners - Array of corner positions in grid coords
189
+ * @param {number} [height=0] - Height of object for z-ordering
190
+ * @returns {number} Depth value for sorting
191
+ *
192
+ * @example
193
+ * // In a custom GameObject with grid position and size:
194
+ * get isoDepth() {
195
+ * const corners = [
196
+ * { x: this.gridX, y: this.gridY },
197
+ * { x: this.gridX + this.width, y: this.gridY },
198
+ * { x: this.gridX, y: this.gridY + this.depth },
199
+ * { x: this.gridX + this.width, y: this.gridY + this.depth },
200
+ * ];
201
+ * return this.isoScene.getRotatedDepth(corners, this.height);
202
+ * }
203
+ */
204
+ getRotatedDepth(corners, height = 0) {
205
+ const angle = this.camera ? this.camera.angle : 0;
206
+ const cos = Math.cos(angle);
207
+ const sin = Math.sin(angle);
208
+
209
+ let maxDepth = -Infinity;
210
+ for (const c of corners) {
211
+ const rotatedX = c.x * cos - c.y * sin;
212
+ const rotatedY = c.x * sin + c.y * cos;
213
+ const depth = rotatedX + rotatedY;
214
+ if (depth > maxDepth) maxDepth = depth;
215
+ }
216
+
217
+ // Height factor of 0.5 matches demo for proper depth sorting
218
+ return maxDepth + height * 0.5;
219
+ }
220
+
181
221
  /**
182
222
  * Calculate scale factor based on Y position (for perspective effect).
183
223
  *
@@ -219,16 +259,26 @@ export class IsometricScene extends Scene {
219
259
  for (const child of this._collection.getSortedChildren()) {
220
260
  if (!child.visible) continue;
221
261
 
222
- // Use custom isoDepth if available, otherwise calculate
262
+ // Use custom isoDepth if available, otherwise calculate using rotated coords
223
263
  let depth;
224
264
  if (child.isoDepth !== undefined) {
225
265
  depth = child.isoDepth;
226
266
  } else {
267
+ // Apply camera rotation to get correct depth at all angles
268
+ let rotatedX = child.x;
269
+ let rotatedY = child.y;
270
+ if (this.camera) {
271
+ const angle = this.camera.angle;
272
+ const cos = Math.cos(angle);
273
+ const sin = Math.sin(angle);
274
+ rotatedX = child.x * cos - child.y * sin;
275
+ rotatedY = child.x * sin + child.y * cos;
276
+ }
227
277
  // For moving objects, use z as height
228
278
  const height = child.z ?? 0;
229
- // Higher (x + y) = closer to viewer = higher depth = render later
279
+ // Higher (rotatedX + rotatedY) = closer to viewer = higher depth = render later
230
280
  // Higher z = on top = higher depth = render later
231
- depth = (child.x + child.y) + height * 0.05;
281
+ depth = (rotatedX + rotatedY) + height * 0.05;
232
282
  }
233
283
 
234
284
  renderList.push({