@guinetik/gcanvas 1.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 (349) hide show
  1. package/.github/workflows/release.yaml +70 -0
  2. package/.jshintrc +4 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CLAUDE.md +310 -0
  5. package/blackhole.jpg +0 -0
  6. package/demo.png +0 -0
  7. package/demos/CNAME +1 -0
  8. package/demos/animations.html +31 -0
  9. package/demos/basic.html +38 -0
  10. package/demos/baskara.html +31 -0
  11. package/demos/bezier.html +35 -0
  12. package/demos/beziersignature.html +29 -0
  13. package/demos/blackhole.html +28 -0
  14. package/demos/blob.html +35 -0
  15. package/demos/demos.css +289 -0
  16. package/demos/easing.html +28 -0
  17. package/demos/events.html +195 -0
  18. package/demos/fluent.html +647 -0
  19. package/demos/fractals.html +36 -0
  20. package/demos/genart.html +26 -0
  21. package/demos/gendream.html +26 -0
  22. package/demos/group.html +36 -0
  23. package/demos/home.html +587 -0
  24. package/demos/index.html +364 -0
  25. package/demos/isometric.html +34 -0
  26. package/demos/js/animations.js +452 -0
  27. package/demos/js/basic.js +204 -0
  28. package/demos/js/baskara.js +751 -0
  29. package/demos/js/bezier.js +692 -0
  30. package/demos/js/beziersignature.js +241 -0
  31. package/demos/js/blackhole/accretiondisk.obj.js +379 -0
  32. package/demos/js/blackhole/blackhole.obj.js +318 -0
  33. package/demos/js/blackhole/index.js +409 -0
  34. package/demos/js/blackhole/particle.js +56 -0
  35. package/demos/js/blackhole/starfield.obj.js +218 -0
  36. package/demos/js/blob.js +2263 -0
  37. package/demos/js/easing.js +477 -0
  38. package/demos/js/fluent.js +183 -0
  39. package/demos/js/fractals.js +931 -0
  40. package/demos/js/fractalworker.js +93 -0
  41. package/demos/js/genart.js +268 -0
  42. package/demos/js/gendream.js +209 -0
  43. package/demos/js/group.js +140 -0
  44. package/demos/js/info-toggle.js +25 -0
  45. package/demos/js/isometric.js +863 -0
  46. package/demos/js/kerr.js +1556 -0
  47. package/demos/js/lavalamp.js +590 -0
  48. package/demos/js/layout.js +354 -0
  49. package/demos/js/mondrian.js +285 -0
  50. package/demos/js/opacity.js +275 -0
  51. package/demos/js/painter.js +484 -0
  52. package/demos/js/particles-showcase.js +514 -0
  53. package/demos/js/particles.js +299 -0
  54. package/demos/js/patterns.js +397 -0
  55. package/demos/js/penrose/artifact.js +69 -0
  56. package/demos/js/penrose/blackhole.js +121 -0
  57. package/demos/js/penrose/constants.js +73 -0
  58. package/demos/js/penrose/game.js +943 -0
  59. package/demos/js/penrose/lore.js +278 -0
  60. package/demos/js/penrose/penrosescene.js +892 -0
  61. package/demos/js/penrose/ship.js +216 -0
  62. package/demos/js/penrose/sounds.js +211 -0
  63. package/demos/js/penrose/voidparticle.js +55 -0
  64. package/demos/js/penrose/voidscene.js +258 -0
  65. package/demos/js/penrose/voidship.js +144 -0
  66. package/demos/js/penrose/wormhole.js +46 -0
  67. package/demos/js/pipeline.js +555 -0
  68. package/demos/js/scene.js +304 -0
  69. package/demos/js/scenes.js +320 -0
  70. package/demos/js/schrodinger.js +410 -0
  71. package/demos/js/schwarzschild.js +1023 -0
  72. package/demos/js/shapes.js +628 -0
  73. package/demos/js/space/alien.js +171 -0
  74. package/demos/js/space/boom.js +98 -0
  75. package/demos/js/space/boss.js +353 -0
  76. package/demos/js/space/buff.js +73 -0
  77. package/demos/js/space/bullet.js +102 -0
  78. package/demos/js/space/constants.js +85 -0
  79. package/demos/js/space/game.js +1884 -0
  80. package/demos/js/space/hud.js +112 -0
  81. package/demos/js/space/laserbeam.js +179 -0
  82. package/demos/js/space/lightning.js +277 -0
  83. package/demos/js/space/minion.js +192 -0
  84. package/demos/js/space/missile.js +212 -0
  85. package/demos/js/space/player.js +430 -0
  86. package/demos/js/space/powerup.js +90 -0
  87. package/demos/js/space/starfield.js +58 -0
  88. package/demos/js/space/starpower.js +90 -0
  89. package/demos/js/spacetime.js +559 -0
  90. package/demos/js/svgtween.js +204 -0
  91. package/demos/js/tde/accretiondisk.js +418 -0
  92. package/demos/js/tde/blackhole.js +219 -0
  93. package/demos/js/tde/blackholescene.js +209 -0
  94. package/demos/js/tde/config.js +59 -0
  95. package/demos/js/tde/index.js +695 -0
  96. package/demos/js/tde/jets.js +290 -0
  97. package/demos/js/tde/lensedstarfield.js +147 -0
  98. package/demos/js/tde/tdestar.js +317 -0
  99. package/demos/js/tde/tidalstream.js +356 -0
  100. package/demos/js/tde_old/blackhole.obj.js +354 -0
  101. package/demos/js/tde_old/debris.obj.js +791 -0
  102. package/demos/js/tde_old/flare.obj.js +239 -0
  103. package/demos/js/tde_old/index.js +448 -0
  104. package/demos/js/tde_old/star.obj.js +812 -0
  105. package/demos/js/tiles.js +312 -0
  106. package/demos/js/tweendemo.js +79 -0
  107. package/demos/js/visibility.js +102 -0
  108. package/demos/kerr.html +28 -0
  109. package/demos/lavalamp.html +27 -0
  110. package/demos/layouts.html +37 -0
  111. package/demos/logo.svg +4 -0
  112. package/demos/loop.html +84 -0
  113. package/demos/mondrian.html +32 -0
  114. package/demos/og_image.png +0 -0
  115. package/demos/opacity.html +36 -0
  116. package/demos/painter.html +39 -0
  117. package/demos/particles-showcase.html +28 -0
  118. package/demos/particles.html +24 -0
  119. package/demos/patterns.html +33 -0
  120. package/demos/penrose-game.html +31 -0
  121. package/demos/pipeline.html +737 -0
  122. package/demos/scene.html +33 -0
  123. package/demos/scenes.html +96 -0
  124. package/demos/schrodinger.html +27 -0
  125. package/demos/schwarzschild.html +27 -0
  126. package/demos/shapes.html +16 -0
  127. package/demos/space.html +85 -0
  128. package/demos/spacetime.html +27 -0
  129. package/demos/svgtween.html +29 -0
  130. package/demos/tde.html +28 -0
  131. package/demos/tiles.html +28 -0
  132. package/demos/transforms.html +400 -0
  133. package/demos/tween.html +45 -0
  134. package/demos/visibility.html +33 -0
  135. package/disk_example.png +0 -0
  136. package/docs/README.md +222 -0
  137. package/docs/concepts/architecture-overview.md +204 -0
  138. package/docs/concepts/lifecycle.md +255 -0
  139. package/docs/concepts/rendering-pipeline.md +279 -0
  140. package/docs/concepts/tde-zorder.md +106 -0
  141. package/docs/concepts/two-layer-architecture.md +229 -0
  142. package/docs/getting-started/first-game.md +354 -0
  143. package/docs/getting-started/hello-world.md +269 -0
  144. package/docs/getting-started/installation.md +157 -0
  145. package/docs/modules/collision/README.md +453 -0
  146. package/docs/modules/fluent/README.md +1075 -0
  147. package/docs/modules/game/README.md +303 -0
  148. package/docs/modules/isometric-camera.md +210 -0
  149. package/docs/modules/isometric.md +275 -0
  150. package/docs/modules/painter/README.md +328 -0
  151. package/docs/modules/particle/README.md +559 -0
  152. package/docs/modules/shapes/README.md +221 -0
  153. package/docs/modules/shapes/base/euclidian.md +123 -0
  154. package/docs/modules/shapes/base/geometry2d.md +204 -0
  155. package/docs/modules/shapes/base/renderable.md +215 -0
  156. package/docs/modules/shapes/base/shape.md +262 -0
  157. package/docs/modules/shapes/base/transformable.md +243 -0
  158. package/docs/modules/shapes/hierarchy.md +218 -0
  159. package/docs/modules/state/README.md +577 -0
  160. package/docs/modules/util/README.md +99 -0
  161. package/docs/modules/util/camera3d.md +412 -0
  162. package/docs/modules/util/scene3d.md +395 -0
  163. package/index.html +17 -0
  164. package/jsdoc.json +50 -0
  165. package/package.json +55 -0
  166. package/readme.md +599 -0
  167. package/scripts/build-demo.js +69 -0
  168. package/scripts/bundle4llm.js +276 -0
  169. package/scripts/clearconsole.js +48 -0
  170. package/src/collision/collision-system.js +332 -0
  171. package/src/collision/collision.js +303 -0
  172. package/src/collision/index.js +10 -0
  173. package/src/fluent/fluent-game.js +430 -0
  174. package/src/fluent/fluent-go.js +1060 -0
  175. package/src/fluent/fluent-layer.js +152 -0
  176. package/src/fluent/fluent-scene.js +291 -0
  177. package/src/fluent/index.js +98 -0
  178. package/src/fluent/sketch.js +380 -0
  179. package/src/game/game.js +467 -0
  180. package/src/game/index.js +49 -0
  181. package/src/game/objects/go.js +220 -0
  182. package/src/game/objects/imagego.js +30 -0
  183. package/src/game/objects/index.js +54 -0
  184. package/src/game/objects/isometric-scene.js +260 -0
  185. package/src/game/objects/layoutscene.js +549 -0
  186. package/src/game/objects/scene.js +175 -0
  187. package/src/game/objects/scene3d.js +118 -0
  188. package/src/game/objects/text.js +221 -0
  189. package/src/game/objects/wrapper.js +232 -0
  190. package/src/game/pipeline.js +243 -0
  191. package/src/game/ui/button.js +396 -0
  192. package/src/game/ui/cursor.js +93 -0
  193. package/src/game/ui/fps.js +91 -0
  194. package/src/game/ui/index.js +5 -0
  195. package/src/game/ui/togglebutton.js +93 -0
  196. package/src/game/ui/tooltip.js +249 -0
  197. package/src/index.js +25 -0
  198. package/src/io/events.js +20 -0
  199. package/src/io/index.js +86 -0
  200. package/src/io/input.js +70 -0
  201. package/src/io/keys.js +152 -0
  202. package/src/io/mouse.js +61 -0
  203. package/src/io/touch.js +39 -0
  204. package/src/logger/debugtab.js +138 -0
  205. package/src/logger/index.js +3 -0
  206. package/src/logger/loggable.js +47 -0
  207. package/src/logger/logger.js +113 -0
  208. package/src/math/complex.js +37 -0
  209. package/src/math/constants.js +1 -0
  210. package/src/math/fractal.js +1271 -0
  211. package/src/math/gr.js +201 -0
  212. package/src/math/heat.js +202 -0
  213. package/src/math/index.js +12 -0
  214. package/src/math/noise.js +433 -0
  215. package/src/math/orbital.js +191 -0
  216. package/src/math/patterns.js +1339 -0
  217. package/src/math/penrose.js +259 -0
  218. package/src/math/quantum.js +115 -0
  219. package/src/math/random.js +195 -0
  220. package/src/math/tensor.js +1009 -0
  221. package/src/mixins/anchor.js +131 -0
  222. package/src/mixins/draggable.js +72 -0
  223. package/src/mixins/index.js +2 -0
  224. package/src/motion/bezier.js +132 -0
  225. package/src/motion/bounce.js +58 -0
  226. package/src/motion/easing.js +349 -0
  227. package/src/motion/float.js +130 -0
  228. package/src/motion/follow.js +125 -0
  229. package/src/motion/hop.js +52 -0
  230. package/src/motion/index.js +82 -0
  231. package/src/motion/motion.js +1124 -0
  232. package/src/motion/orbit.js +49 -0
  233. package/src/motion/oscillate.js +39 -0
  234. package/src/motion/parabolic.js +141 -0
  235. package/src/motion/patrol.js +147 -0
  236. package/src/motion/pendulum.js +48 -0
  237. package/src/motion/pulse.js +88 -0
  238. package/src/motion/shake.js +83 -0
  239. package/src/motion/spiral.js +144 -0
  240. package/src/motion/spring.js +150 -0
  241. package/src/motion/swing.js +47 -0
  242. package/src/motion/tween.js +92 -0
  243. package/src/motion/tweenetik.js +139 -0
  244. package/src/motion/waypoint.js +210 -0
  245. package/src/painter/index.js +8 -0
  246. package/src/painter/painter.colors.js +331 -0
  247. package/src/painter/painter.effects.js +230 -0
  248. package/src/painter/painter.img.js +229 -0
  249. package/src/painter/painter.js +295 -0
  250. package/src/painter/painter.lines.js +189 -0
  251. package/src/painter/painter.opacity.js +41 -0
  252. package/src/painter/painter.shapes.js +277 -0
  253. package/src/painter/painter.text.js +273 -0
  254. package/src/particle/emitter.js +124 -0
  255. package/src/particle/index.js +11 -0
  256. package/src/particle/particle-system.js +322 -0
  257. package/src/particle/particle.js +71 -0
  258. package/src/particle/updaters.js +170 -0
  259. package/src/shapes/arc.js +43 -0
  260. package/src/shapes/arrow.js +33 -0
  261. package/src/shapes/bezier.js +42 -0
  262. package/src/shapes/circle.js +62 -0
  263. package/src/shapes/clouds.js +56 -0
  264. package/src/shapes/cone.js +219 -0
  265. package/src/shapes/cross.js +70 -0
  266. package/src/shapes/cube.js +244 -0
  267. package/src/shapes/cylinder.js +254 -0
  268. package/src/shapes/diamond.js +48 -0
  269. package/src/shapes/euclidian.js +111 -0
  270. package/src/shapes/figure.js +115 -0
  271. package/src/shapes/geometry.js +220 -0
  272. package/src/shapes/group.js +375 -0
  273. package/src/shapes/heart.js +42 -0
  274. package/src/shapes/hexagon.js +26 -0
  275. package/src/shapes/image.js +192 -0
  276. package/src/shapes/index.js +111 -0
  277. package/src/shapes/line.js +29 -0
  278. package/src/shapes/pattern.js +90 -0
  279. package/src/shapes/pin.js +44 -0
  280. package/src/shapes/poly.js +31 -0
  281. package/src/shapes/prism.js +226 -0
  282. package/src/shapes/rect.js +35 -0
  283. package/src/shapes/renderable.js +333 -0
  284. package/src/shapes/ring.js +26 -0
  285. package/src/shapes/roundrect.js +95 -0
  286. package/src/shapes/shape.js +117 -0
  287. package/src/shapes/slice.js +26 -0
  288. package/src/shapes/sphere.js +314 -0
  289. package/src/shapes/sphere3d.js +537 -0
  290. package/src/shapes/square.js +15 -0
  291. package/src/shapes/star.js +99 -0
  292. package/src/shapes/svg.js +408 -0
  293. package/src/shapes/text.js +553 -0
  294. package/src/shapes/traceable.js +83 -0
  295. package/src/shapes/transform.js +357 -0
  296. package/src/shapes/transformable.js +172 -0
  297. package/src/shapes/triangle.js +26 -0
  298. package/src/sound/index.js +17 -0
  299. package/src/sound/sound.js +473 -0
  300. package/src/sound/synth.analyzer.js +149 -0
  301. package/src/sound/synth.effects.js +207 -0
  302. package/src/sound/synth.envelope.js +59 -0
  303. package/src/sound/synth.js +229 -0
  304. package/src/sound/synth.musical.js +160 -0
  305. package/src/sound/synth.noise.js +85 -0
  306. package/src/sound/synth.oscillators.js +293 -0
  307. package/src/state/index.js +10 -0
  308. package/src/state/state-machine.js +371 -0
  309. package/src/util/camera3d.js +438 -0
  310. package/src/util/index.js +6 -0
  311. package/src/util/isometric-camera.js +235 -0
  312. package/src/util/layout.js +317 -0
  313. package/src/util/position.js +147 -0
  314. package/src/util/tasks.js +47 -0
  315. package/src/util/zindex.js +287 -0
  316. package/src/webgl/index.js +9 -0
  317. package/src/webgl/shaders/sphere-shaders.js +994 -0
  318. package/src/webgl/webgl-renderer.js +388 -0
  319. package/tde.png +0 -0
  320. package/test/math/orbital.test.js +61 -0
  321. package/test/math/tensor.test.js +114 -0
  322. package/test/particle/emitter.test.js +204 -0
  323. package/test/particle/particle-system.test.js +310 -0
  324. package/test/particle/particle.test.js +116 -0
  325. package/test/particle/updaters.test.js +386 -0
  326. package/test/setup.js +120 -0
  327. package/test/shapes/euclidian.test.js +44 -0
  328. package/test/shapes/geometry.test.js +86 -0
  329. package/test/shapes/group.test.js +86 -0
  330. package/test/shapes/rectangle.test.js +64 -0
  331. package/test/shapes/transform.test.js +379 -0
  332. package/test/util/camera3d.test.js +428 -0
  333. package/test/util/scene3d.test.js +352 -0
  334. package/types/collision.d.ts +249 -0
  335. package/types/common.d.ts +155 -0
  336. package/types/game.d.ts +497 -0
  337. package/types/index.d.ts +309 -0
  338. package/types/io.d.ts +188 -0
  339. package/types/logger.d.ts +127 -0
  340. package/types/math.d.ts +268 -0
  341. package/types/mixins.d.ts +92 -0
  342. package/types/motion.d.ts +678 -0
  343. package/types/painter.d.ts +378 -0
  344. package/types/shapes.d.ts +864 -0
  345. package/types/sound.d.ts +672 -0
  346. package/types/state.d.ts +251 -0
  347. package/types/util.d.ts +253 -0
  348. package/vite.config.js +50 -0
  349. package/vitest.config.js +13 -0
@@ -0,0 +1,304 @@
1
+ import {
2
+ Game,
3
+ Scene,
4
+ Button,
5
+ FPSCounter,
6
+ VerticalLayout,
7
+ Rectangle,
8
+ Group,
9
+ applyDraggable,
10
+ Painter,
11
+ Tween,
12
+ Easing,
13
+ Random,
14
+ GameObjectShapeWrapper,
15
+ Position,
16
+ } from "../../src/index.js";
17
+ // Demo Game with Scenes
18
+ export class DemoGame extends Game {
19
+ constructor(canvas) {
20
+ super(canvas);
21
+ this.MARGIN = 0;
22
+ this.enableFluidSize();
23
+ this.backgroundColor = "white";
24
+ }
25
+
26
+ getResponsiveConfig() {
27
+ const isNarrow = this.width < 500;
28
+ return {
29
+ buttonWidth: isNarrow ? 120 : 150,
30
+ layerCount: isNarrow ? 25 : 50,
31
+ };
32
+ }
33
+
34
+ init() {
35
+ super.init();
36
+ const config = this.getResponsiveConfig();
37
+ this.currentButtonWidth = config.buttonWidth;
38
+
39
+ this.scene = new Scene(this, { debug: true, debugColor: "black" });
40
+ this.ui = new Scene(this, {
41
+ debug: true,
42
+ anchor: Position.BOTTOM_LEFT,
43
+ anchorRelative: this.scene,
44
+ });
45
+ this.pipeline.add(this.scene); // scene below
46
+ this.pipeline.add(this.ui); // UI on top
47
+ this.pipeline.add(
48
+ new FPSCounter(this, { anchor: "bottom-right", color: "black" })
49
+ );
50
+ this.buttons = this.ui.add(
51
+ new VerticalLayout(this, {
52
+ debug: true,
53
+ debugColor: "black",
54
+ spacing: 10,
55
+ padding: 10,
56
+ })
57
+ );
58
+ this.buttons.width = config.buttonWidth;
59
+ this.buttons.height = 200;
60
+ // Small demo of Random functions
61
+ let random = Random.symmetric;
62
+ const options = [
63
+ Random.symmetric,
64
+ Random.centered,
65
+ Random.gaussian,
66
+ Random.radial,
67
+ ];
68
+ this.buttons.add(
69
+ this.createButton("➕ Add Layer", () => this.addLayer(random))
70
+ );
71
+ this.buttons.add(this.createButton("❎ Clear", () => this.scene.clear()));
72
+ this.buttons.add(
73
+ this.createButton("🎲 Randomize", () => {
74
+ this.scene.children.forEach((layer) => {
75
+ layer.scaleX = layer.scaleY = layer.scaleTime = 0;
76
+ layer.random = random;
77
+ layer.randomize();
78
+ });
79
+ })
80
+ );
81
+ const rollBtn = this.createButton("🔄️" + random.name, () => {
82
+ random = Random.pickOther(options, random);
83
+ console.log(random.name);
84
+ rollBtn.text = "🔄️" + random.name;
85
+ });
86
+ this.buttons.add(rollBtn);
87
+ // Add random layers
88
+ for (let i = 0; i < config.layerCount; i++) {
89
+ this.addLayer(random, i);
90
+ }
91
+ this.positionScene();
92
+ }
93
+
94
+ createButton(text, onClick) {
95
+ return new Button(this, {
96
+ text: text,
97
+ width: this.currentButtonWidth,
98
+ textAlign: "left",
99
+ onClick: onClick,
100
+ });
101
+ }
102
+
103
+ #prevWidth = 0;
104
+ #prevHeight = 0;
105
+
106
+ positionScene() {
107
+ this.scene.width = this.width - this.MARGIN * 2;
108
+ this.scene.height = this.height - this.MARGIN * 2;
109
+ this.scene.x = this.width / 2;
110
+ this.scene.y = this.height / 2;
111
+ }
112
+
113
+ update(dt) {
114
+ this.positionScene();
115
+ //
116
+ if (this.ui.width !== this.buttons.width + 10) {
117
+ this.ui.width = this.buttons.width + 10;
118
+ }
119
+ if (this.ui.height !== this.buttons.height + 10) {
120
+ this.ui.height = this.buttons.height + 10;
121
+ }
122
+ super.update(dt);
123
+ if (this.#prevWidth !== this.width || this.#prevHeight !== this.height) {
124
+ this.scene.markBoundsDirty();
125
+ }
126
+ this.#prevWidth = this.scene.width;
127
+ this.#prevHeight = this.scene.height;
128
+ }
129
+
130
+ addLayer(random, i) {
131
+ const layer = new LayerBox(this, this.scene, {}, random);
132
+ this.scene.add(layer);
133
+ setTimeout(layer.randomize.bind(layer), 50 * i);
134
+ return layer;
135
+ }
136
+
137
+ onResize() {
138
+ if (!this.buttons) return;
139
+
140
+ const config = this.getResponsiveConfig();
141
+
142
+ // Update button widths if changed
143
+ if (this.currentButtonWidth !== config.buttonWidth) {
144
+ this.currentButtonWidth = config.buttonWidth;
145
+ this.buttons.transform.width(config.buttonWidth);
146
+
147
+ // Update each button's width
148
+ if (this.buttons.children) {
149
+ this.buttons.children.forEach((btn) => {
150
+ if (btn.transform) {
151
+ btn.transform.width(config.buttonWidth);
152
+ }
153
+ });
154
+ }
155
+ this.buttons.markBoundsDirty();
156
+ }
157
+
158
+ this.positionScene();
159
+ }
160
+ }
161
+ // LayerBox class
162
+ class LayerBox extends GameObjectShapeWrapper {
163
+ constructor(game, scene, options, random = Random.symmetric) {
164
+ const group = new Group();
165
+ super(game, group, options);
166
+ this.interactive = true;
167
+ this.scene = scene;
168
+ this.random = random;
169
+ this.group = group;
170
+ }
171
+
172
+ init() {
173
+ // Create a random rectangle
174
+ const size = 100;
175
+ const color = Painter.colors.randomColorHSL();
176
+ // Create a rectangle and group
177
+ this.rect = new Rectangle({
178
+ width: size,
179
+ height: size,
180
+ color: color,
181
+ stroke: "white",
182
+ lineWidth: 2,
183
+ });
184
+ this.width = size;
185
+ this.height = size;
186
+ this.group.add(this.rect);
187
+ //
188
+ this.startScale = 0;
189
+ this.targetScale = 1;
190
+ this.scaleTime = 0;
191
+ this.scaleDuration = 1; // seconds
192
+ //
193
+ this.on("mouseover", () => {
194
+ if (!this.scene.dragging) {
195
+ this.startScale = this.scaleX;
196
+ this.targetScale = 1.3;
197
+ this.scaleTime = 0;
198
+ }
199
+ });
200
+ this.on("mouseout", () => {
201
+ if (!this.dragging) {
202
+ this.startScale = this.scaleX;
203
+ this.scaleTime = 0;
204
+ this.targetScale = 1;
205
+ }
206
+ });
207
+ //
208
+ this.applyDraggable();
209
+ }
210
+
211
+ applyDraggable() {
212
+ applyDraggable(this, {
213
+ onDragStart: () => {
214
+ console.log("Drag start", this.constructor.name);
215
+ this.scene.dragging = true;
216
+ this.scene.bringToFront(this);
217
+ this.targetScale = 1.5;
218
+ this.scaleTime = 0;
219
+ this.shadowBox();
220
+ },
221
+ onDragEnd: () => {
222
+ console.log("Drag end", this.constructor.name);
223
+ //this.scene.bringToFront(this);
224
+ this.scaleTime = 0;
225
+ this.targetScale = 1.0;
226
+ this.clearShadowBox();
227
+ this.scene.dragging = false;
228
+ },
229
+ });
230
+ }
231
+
232
+ shadowBox() {
233
+ if (this.rect.shadorColor == null) {
234
+ this.rect.shadowColor = "rgba(0, 0, 0, 0.5)";
235
+ this.rect.shadowBlur = 20;
236
+ this.rect.shadowOffsetX = 10;
237
+ this.rect.shadowOffsetY = 10;
238
+ }
239
+ }
240
+
241
+ clearShadowBox() {
242
+ this.rect.shadowColor = null;
243
+ }
244
+
245
+ randomize() {
246
+ const param = {
247
+ symmetric: 1,
248
+ centered: 500,
249
+ gaussian: Math.random() * this.parent.width,
250
+ radial: this.parent.width / 3,
251
+ };
252
+ const { x, y } = this.random(
253
+ -this.parent.width / 2,
254
+ -this.parent.height / 2,
255
+ this.parent.width,
256
+ this.parent.height,
257
+ param[this.random.name]
258
+ );
259
+ // console.log(x, y);
260
+ this.x = x;
261
+ this.y = y;
262
+ this.scaleX = this.scaleY = 0;
263
+ this.scaleTime = 0;
264
+ this.targetScale = 1;
265
+ }
266
+
267
+ update(dt) {
268
+ // Smooth scale animation
269
+ // Animate scale using bounce
270
+ if (this.scaleTime < this.scaleDuration) {
271
+ this.scaleTime += dt;
272
+ const t = Math.min(this.scaleTime / this.scaleDuration, 1);
273
+ const eased = Easing.easeOutElastic(t);
274
+ const scale = Tween.lerp(this.startScale, this.targetScale, eased);
275
+ this.scaleX = this.scaleY = scale;
276
+ this.tweening = true;
277
+ } else {
278
+ this.tweening = false;
279
+ }
280
+ //if (this.dragging) {
281
+ // Set the object's own half-dimensions
282
+ const halfW = 50;
283
+ const halfH = 50;
284
+
285
+ // Calculate the bounds within the parent's coordinate space
286
+ // This creates a "safe zone" that's 10px from the edge
287
+ const parentHalfWidth = this.parent.width / 2;
288
+ const parentHalfHeight = this.parent.height / 2;
289
+
290
+ // Maximum boundaries (accounting for the object's size and parent position)
291
+ const maxX = parentHalfWidth - halfW - 10;
292
+ const maxY = parentHalfHeight - halfH - 10;
293
+
294
+ // Minimum boundaries (accounting for the object's size and parent position)
295
+ const minX = -parentHalfWidth + halfW + 10;
296
+ const minY = -parentHalfHeight + halfH + 10;
297
+
298
+ // Clamp to parent bounds
299
+ this.x = Math.max(minX, Math.min(maxX, this.x));
300
+ this.y = Math.max(minY, Math.min(maxY, this.y));
301
+ //}
302
+ super.update(dt);
303
+ }
304
+ }
@@ -0,0 +1,320 @@
1
+ import {
2
+ Game,
3
+ GameObject,
4
+ Scene,
5
+ Rectangle,
6
+ Circle,
7
+ Star,
8
+ Triangle,
9
+ TextShape,
10
+ Group,
11
+ FPSCounter,
12
+ Motion,
13
+ Easing,
14
+ Painter,
15
+ } from "../../src/index";
16
+
17
+ /**
18
+ * Helper class to wrap a Shape in a GameObject for use in Scenes.
19
+ * Scenes expect GameObjects, not raw Shapes.
20
+ */
21
+ class ShapeWrapper extends GameObject {
22
+ constructor(game, shape) {
23
+ super(game);
24
+ this.shape = shape;
25
+ }
26
+
27
+ draw() {
28
+ super.draw();
29
+ this.shape.render();
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Utility function to wrap a shape in a GameObject
35
+ */
36
+ function wrapShape(game, shape) {
37
+ return new ShapeWrapper(game, shape);
38
+ }
39
+
40
+ /**
41
+ * SceneDemo Game
42
+ * Demonstrates Scene capabilities: nesting, transforms, and z-ordering
43
+ */
44
+ export class MyGame extends Game {
45
+ constructor(canvas) {
46
+ super(canvas);
47
+ this.enableFluidSize();
48
+ this.backgroundColor = "black";
49
+ }
50
+
51
+ init() {
52
+ super.init();
53
+
54
+ // Main container scene (centered)
55
+ this.mainScene = new Scene(this, {
56
+ width: 600,
57
+ height: 400,
58
+ debug: false,
59
+ debugColor: "#ff00ff",
60
+ anchor: "center"
61
+ });
62
+ this.pipeline.add(this.mainScene);
63
+
64
+ // Add demo scenes
65
+ this.mainScene.add(new TransformingSceneDemo(this));
66
+ this.mainScene.add(new NestedSceneDemo(this));
67
+ this.mainScene.add(new ZOrderDemo(this));
68
+
69
+ // FPS counter
70
+ this.pipeline.add(new FPSCounter(this, { anchor: "bottom-right" }));
71
+ }
72
+
73
+ onResize() {
74
+ if (this.mainScene) {
75
+ // Adjust main scene to fit screen
76
+ this.mainScene.width = Math.min(this.width - 40, 800);
77
+ this.mainScene.height = Math.min(this.height - 80, 500);
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Demo 1: Scene with animated transforms
84
+ * Shows rotation and scaling applied to a scene with children
85
+ */
86
+ class TransformingSceneDemo extends GameObject {
87
+ constructor(game) {
88
+ super(game);
89
+
90
+ // Create a scene that will be transformed
91
+ this.scene = new Scene(game, {
92
+ width: 120,
93
+ height: 120,
94
+ debug: false,
95
+ debugColor: "#ff00ff"
96
+ });
97
+
98
+ // Position using transform API
99
+ this.scene.transform.position(-200, -80);
100
+
101
+ // Add shapes to the scene
102
+ const rect = new Rectangle({
103
+ width: 40,
104
+ height: 40,
105
+ color: "#e94560",
106
+ stroke: "#fff",
107
+ lineWidth: 2
108
+ });
109
+ rect.transform.position(-25, -25);
110
+
111
+ const circle = new Circle(15, {
112
+ color: "#00d9ff",
113
+ stroke: "#fff",
114
+ lineWidth: 2
115
+ });
116
+ circle.transform.position(25, 25);
117
+
118
+ const star = new Star(12, 5, 0.5, {
119
+ color: "#ffc107",
120
+ stroke: "#fff",
121
+ lineWidth: 1
122
+ });
123
+ star.transform.position(25, -25);
124
+
125
+ // Add shapes via wrapper
126
+ this.scene.add(wrapShape(game, rect));
127
+ this.scene.add(wrapShape(game, circle));
128
+ this.scene.add(wrapShape(game, star));
129
+
130
+ // Label
131
+ this.label = new TextShape("Scene Transforms", {
132
+ x: -200,
133
+ y: -150,
134
+ font: "bold 12px monospace",
135
+ color: "#fff",
136
+ align: "center"
137
+ });
138
+
139
+ this.elapsed = 0;
140
+ }
141
+
142
+ update(dt) {
143
+ super.update(dt);
144
+ this.elapsed += dt;
145
+
146
+ // Animate the scene's transforms
147
+ this.scene.transform
148
+ .rotation(this.elapsed * 30)
149
+ .scale(0.8 + Math.sin(this.elapsed * 2) * 0.2);
150
+ }
151
+
152
+ draw() {
153
+ super.draw();
154
+ this.scene.render();
155
+ this.label.render();
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Demo 2: Nested Scenes
161
+ * Shows scenes containing other scenes
162
+ */
163
+ class NestedSceneDemo extends GameObject {
164
+ constructor(game) {
165
+ super(game);
166
+
167
+ // Outer scene
168
+ this.outerScene = new Scene(game, {
169
+ width: 150,
170
+ height: 150,
171
+ debug: false,
172
+ debugColor: "#ff00ff"
173
+ });
174
+ this.outerScene.transform.position(0, -80);
175
+
176
+ // Inner scene (nested inside outer)
177
+ this.innerScene = new Scene(game, {
178
+ width: 80,
179
+ height: 80,
180
+ debug: false,
181
+ debugColor: "#00ffff"
182
+ });
183
+
184
+ // Add shapes to inner scene
185
+ const innerRect = new Rectangle({
186
+ width: 30,
187
+ height: 30,
188
+ color: "#7bed9f",
189
+ stroke: "#fff",
190
+ lineWidth: 2
191
+ });
192
+ this.innerScene.add(wrapShape(game, innerRect));
193
+
194
+ // Add inner scene to outer
195
+ this.outerScene.add(this.innerScene);
196
+
197
+ // Add corner markers to outer scene
198
+ const corners = [
199
+ { x: -50, y: -50 },
200
+ { x: 50, y: -50 },
201
+ { x: 50, y: 50 },
202
+ { x: -50, y: 50 }
203
+ ];
204
+ corners.forEach((pos, i) => {
205
+ const marker = new Circle(8, {
206
+ color: ["#ff6b6b", "#ffc107", "#00d9ff", "#7bed9f"][i],
207
+ stroke: "#fff",
208
+ lineWidth: 1
209
+ });
210
+ marker.transform.position(pos.x, pos.y);
211
+ this.outerScene.add(wrapShape(game, marker));
212
+ });
213
+
214
+ // Label
215
+ this.label = new TextShape("Nested Scenes", {
216
+ x: 0,
217
+ y: -150,
218
+ font: "bold 12px monospace",
219
+ color: "#fff",
220
+ align: "center"
221
+ });
222
+
223
+ this.elapsed = 0;
224
+ }
225
+
226
+ update(dt) {
227
+ super.update(dt);
228
+ this.elapsed += dt;
229
+
230
+ // Outer scene rotates slowly
231
+ this.outerScene.transform.rotation(this.elapsed * 20);
232
+
233
+ // Inner scene counter-rotates and pulses
234
+ this.innerScene.transform
235
+ .rotation(-this.elapsed * 60)
236
+ .scale(0.9 + Math.sin(this.elapsed * 3) * 0.2);
237
+ }
238
+
239
+ draw() {
240
+ super.draw();
241
+ this.outerScene.render();
242
+ this.label.render();
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Demo 3: Z-Ordering in Scenes
248
+ * Shows bring to front / send to back functionality
249
+ */
250
+ class ZOrderDemo extends GameObject {
251
+ constructor(game) {
252
+ super(game);
253
+
254
+ this.scene = new Scene(game, {
255
+ width: 140,
256
+ height: 140,
257
+ debug: false,
258
+ debugColor: "#ff00ff"
259
+ });
260
+ this.scene.transform.position(200, -80);
261
+
262
+ // Create overlapping shapes
263
+ this.shapes = [];
264
+ const colors = ["#e94560", "#ffc107", "#00d9ff", "#7bed9f"];
265
+ const positions = [
266
+ { x: -20, y: -20 },
267
+ { x: 0, y: -10 },
268
+ { x: 20, y: 0 },
269
+ { x: 0, y: 10 }
270
+ ];
271
+
272
+ colors.forEach((color, i) => {
273
+ const rect = new Rectangle({
274
+ width: 50,
275
+ height: 50,
276
+ color: color,
277
+ stroke: "#fff",
278
+ lineWidth: 2
279
+ });
280
+ rect.transform.position(positions[i].x, positions[i].y);
281
+ const wrapped = wrapShape(game, rect);
282
+ this.shapes.push(wrapped);
283
+ this.scene.add(wrapped);
284
+ });
285
+
286
+ // Label
287
+ this.label = new TextShape("Z-Ordering", {
288
+ x: 200,
289
+ y: -150,
290
+ font: "bold 12px monospace",
291
+ color: "#fff",
292
+ align: "center"
293
+ });
294
+
295
+ this.elapsed = 0;
296
+ this.lastSwap = 0;
297
+ this.swapInterval = 1.5; // seconds between z-order changes
298
+ }
299
+
300
+ update(dt) {
301
+ super.update(dt);
302
+ this.elapsed += dt;
303
+
304
+ // Periodically bring a different shape to front
305
+ if (this.elapsed - this.lastSwap > this.swapInterval) {
306
+ this.lastSwap = this.elapsed;
307
+ const index = Math.floor(this.elapsed / this.swapInterval) % this.shapes.length;
308
+ this.scene.bringToFront(this.shapes[index]);
309
+ }
310
+
311
+ // Subtle pulse on the scene
312
+ this.scene.transform.scale(0.95 + Math.sin(this.elapsed * 2) * 0.05);
313
+ }
314
+
315
+ draw() {
316
+ super.draw();
317
+ this.scene.render();
318
+ this.label.render();
319
+ }
320
+ }