@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,317 @@
1
+ /**
2
+ * Applies layout positions to items with optional transformation
3
+ *
4
+ * @param {Object[]} items - Array of objects to position (must have x, y properties)
5
+ * @param {Object[]} positions - Array of position objects from layout functions
6
+ * @param {Object} options - Options for applying positions
7
+ * @param {number} [options.offsetX=0] - X offset to apply to all positions
8
+ * @param {number} [options.offsetY=0] - Y offset to apply to all positions
9
+ * @param {function} [options.transform] - Optional transform function to apply to positions
10
+ * @return {Object[]} The items with updated positions
11
+ */
12
+ export function applyLayout(items, positions, options = {}) {
13
+ const offsetX = options.offsetX ?? 0;
14
+ const offsetY = options.offsetY ?? 0;
15
+ const transform = options.transform;
16
+
17
+ items.forEach((item, index) => {
18
+ if (index < positions.length) {
19
+ const pos = positions[index];
20
+
21
+ if (transform) {
22
+ const transformed = transform(pos);
23
+ item.x = transformed.x + offsetX;
24
+ item.y = transformed.y + offsetY;
25
+ } else {
26
+ item.x = pos.x + offsetX;
27
+ item.y = pos.y + offsetY;
28
+ }
29
+ }
30
+ });
31
+
32
+ return items;
33
+ }
34
+
35
+ /**
36
+ * Creates a horizontal layout for the given items
37
+ *
38
+ * @param {Object[]} items - Array of objects with width and height properties
39
+ * @param {Object} options - Layout configuration options
40
+ * @param {number} [options.spacing=10] - Space between items
41
+ * @param {number} [options.padding=0] - Padding around the entire layout
42
+ * @param {string} [options.align="start"] - Vertical alignment ("start", "center", "end")
43
+ * @param {boolean} [options.centerItems=true] - Whether to position items relative to their centers
44
+ * @return {Object} Result containing positioned items and layout dimensions
45
+ */
46
+ export function horizontalLayout(items, options = {}) {
47
+ const spacing = options.spacing ?? 10;
48
+ const padding = options.padding ?? 0;
49
+ const align = options.align ?? "start";
50
+ const centerItems = options.centerItems ?? true;
51
+
52
+ let x = padding;
53
+ let maxHeight = 0;
54
+ const positions = [];
55
+
56
+ // First pass: get max height
57
+ for (const item of items) {
58
+ maxHeight = Math.max(maxHeight, item.height ?? 0);
59
+ }
60
+
61
+ // Second pass: calculate positions
62
+ for (let i = 0; i < items.length; i++) {
63
+ const item = items[i];
64
+ const width = item.width ?? 0;
65
+ const height = item.height ?? 0;
66
+
67
+ // X position depends on whether we're centering the items
68
+ const itemX = centerItems ? x + width / 2 : x;
69
+
70
+ // Y position determined by alignment
71
+ let itemY;
72
+ switch (align) {
73
+ case "center":
74
+ itemY = (maxHeight - height) / 2;
75
+ break;
76
+ case "end":
77
+ itemY = maxHeight - height;
78
+ break;
79
+ case "start":
80
+ default:
81
+ itemY = 0;
82
+ }
83
+
84
+ positions.push({ x: itemX, y: itemY });
85
+
86
+ // Move to next position
87
+ x += width;
88
+ if (i < items.length - 1) {
89
+ x += spacing;
90
+ }
91
+ }
92
+
93
+ // Calculate total dimensions
94
+ const totalWidth = x + padding;
95
+ const totalHeight = maxHeight + padding * 2;
96
+
97
+ return {
98
+ positions,
99
+ width: totalWidth,
100
+ height: totalHeight,
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Creates a vertical layout for the given items
106
+ *
107
+ * @param {Object[]} items - Array of objects with width and height properties
108
+ * @param {Object} options - Layout configuration options
109
+ * @param {number} [options.spacing=10] - Space between items
110
+ * @param {number} [options.padding=0] - Padding around the entire layout
111
+ * @param {string} [options.align="start"] - Horizontal alignment ("start", "center", "end")
112
+ * @param {boolean} [options.centerItems=true] - Whether to position items relative to their centers
113
+ * @return {Object} Result containing positioned items and layout dimensions
114
+ */
115
+ export function verticalLayout(items, options = {}) {
116
+ const spacing = options.spacing ?? 10;
117
+ const padding = options.padding ?? 0;
118
+ const align = options.align ?? "start";
119
+ const centerItems = options.centerItems ?? true;
120
+
121
+ let y = padding;
122
+ let maxWidth = 0;
123
+ const positions = [];
124
+
125
+ // First pass: get max width
126
+ for (const item of items) {
127
+ maxWidth = Math.max(maxWidth, item.width ?? 0);
128
+ }
129
+
130
+ // Second pass: calculate positions
131
+ for (let i = 0; i < items.length; i++) {
132
+ const item = items[i];
133
+ const width = item.width ?? 0;
134
+ const height = item.height ?? 0;
135
+
136
+ // Y position depends on whether we're centering the items
137
+ const itemY = centerItems ? y + height / 2 : y;
138
+
139
+ // X position determined by alignment
140
+ let itemX;
141
+ switch (align) {
142
+ case "center":
143
+ itemX = (maxWidth - width) / 2;
144
+ break;
145
+ case "end":
146
+ itemX = maxWidth - width;
147
+ break;
148
+ case "start":
149
+ default:
150
+ itemX = 0;
151
+ }
152
+
153
+ positions.push({ x: itemX, y: itemY });
154
+
155
+ // Move to next position
156
+ y += height;
157
+ if (i < items.length - 1) {
158
+ y += spacing;
159
+ }
160
+ }
161
+
162
+ // Calculate total dimensions
163
+ const totalWidth = maxWidth + padding * 2;
164
+ const totalHeight = y + padding;
165
+
166
+ return {
167
+ positions,
168
+ width: totalWidth,
169
+ height: totalHeight,
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Creates a tile layout for the given items
175
+ *
176
+ * @param {Object[]} items - Array of objects with width and height properties
177
+ * @param {Object} options - Layout configuration options
178
+ * @param {number} [options.columns=4] - Number of columns in the grid
179
+ * @param {number} [options.spacing=10] - Space between items
180
+ * @param {number} [options.padding=0] - Padding around the entire layout
181
+ * @param {boolean} [options.centerItems=true] - Whether to position items relative to their centers
182
+ * @return {Object} Result containing positioned items and layout dimensions
183
+ */
184
+ export function tileLayout(items, options = {}) {
185
+ if (items.length === 0) {
186
+ return { positions: [], width: 0, height: 0 };
187
+ }
188
+
189
+ const columns = options.columns ?? 4;
190
+ const spacing = options.spacing ?? 10;
191
+ const padding = options.padding ?? 0;
192
+ const centerItems = options.centerItems ?? true;
193
+
194
+ // Assume uniform tile size based on first item
195
+ const tileWidth = items[0].width ?? 0;
196
+ const tileHeight = items[0].height ?? 0;
197
+
198
+ const rowCount = Math.ceil(items.length / columns);
199
+ const positions = [];
200
+
201
+ // Calculate grid dimensions
202
+ const totalWidth =
203
+ columns * tileWidth + (columns - 1) * spacing + padding * 2;
204
+ const totalHeight =
205
+ rowCount * tileHeight + (rowCount - 1) * spacing + padding * 2;
206
+
207
+ // Set starting position at top-left corner
208
+ let x = padding;
209
+ let y = padding;
210
+ let colIndex = 0;
211
+
212
+ // Calculate positions for each item
213
+ for (let i = 0; i < items.length; i++) {
214
+ // Position depends on whether we're centering the items
215
+ const itemX = centerItems ? x + tileWidth / 2 : x;
216
+ const itemY = centerItems ? y + tileHeight / 2 : y;
217
+
218
+ positions.push({ x: itemX, y: itemY });
219
+
220
+ colIndex++;
221
+ if (colIndex < columns) {
222
+ x += tileWidth + spacing;
223
+ } else {
224
+ colIndex = 0;
225
+ x = padding;
226
+ y += tileHeight + spacing;
227
+ }
228
+ }
229
+
230
+ return {
231
+ positions,
232
+ width: totalWidth,
233
+ height: totalHeight,
234
+ };
235
+ }
236
+
237
+ export function gridLayout(items, options = {}) {
238
+ if (items.length === 0) {
239
+ return { positions: [], width: 0, height: 0 };
240
+ }
241
+
242
+ const columns = options.columns ?? 4;
243
+ const spacing = options.spacing ?? 10;
244
+ const padding = options.padding ?? 0;
245
+ const centerItems = options.centerItems ?? true;
246
+
247
+ // Determine max width and height for each column and row
248
+ const colWidths = new Array(columns).fill(0);
249
+ const rowHeights = [];
250
+
251
+ items.forEach((item, index) => {
252
+ const col = index % columns;
253
+ const row = Math.floor(index / columns);
254
+
255
+ const itemWidth = item.width ?? 0;
256
+ const itemHeight = item.height ?? 0;
257
+
258
+ // Track max width for each column
259
+ colWidths[col] = Math.max(colWidths[col], itemWidth);
260
+
261
+ // Track max height for each row
262
+ if (rowHeights[row] === undefined) {
263
+ rowHeights[row] = itemHeight;
264
+ } else {
265
+ rowHeights[row] = Math.max(rowHeights[row], itemHeight);
266
+ }
267
+ });
268
+
269
+ // Calculate positions
270
+ const positions = [];
271
+ let x = padding;
272
+ let y = padding;
273
+ let colIndex = 0;
274
+
275
+ for (let i = 0; i < items.length; i++) {
276
+ const item = items[i];
277
+ const itemWidth = item.width ?? 0;
278
+ const itemHeight = item.height ?? 0;
279
+ const rowHeight = rowHeights[Math.floor(i / columns)];
280
+
281
+ // Position depends on whether we're centering the items
282
+ const posX = centerItems ? x + itemWidth / 2 : x;
283
+ const posY = centerItems ? y + itemHeight / 2 : y;
284
+
285
+ positions.push({ x: posX, y: posY });
286
+
287
+ // Move to next position
288
+ colIndex++;
289
+ if (colIndex < columns) {
290
+ x += colWidths[colIndex - 1] + spacing;
291
+ } else {
292
+ // Move to next row
293
+ colIndex = 0;
294
+ x = padding;
295
+ y += rowHeight + spacing;
296
+ }
297
+ }
298
+
299
+ // Calculate total dimensions
300
+ const totalWidth =
301
+ padding * 2 +
302
+ colWidths.reduce((sum, width) => sum + width, 0) +
303
+ spacing * (columns - 1);
304
+
305
+ const totalHeight =
306
+ padding * 2 +
307
+ rowHeights.reduce((sum, height) => sum + height, 0) +
308
+ spacing * (rowHeights.length - 1);
309
+
310
+ return {
311
+ positions,
312
+ width: totalWidth,
313
+ height: totalHeight,
314
+ cols: columns,
315
+ rows: rowHeights.length
316
+ };
317
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Position utility for consistently positioning objects relative to containers
3
+ * Provides anchor point constants and methods for calculating positions
4
+ */
5
+ export class Position {
6
+ /**
7
+ * Anchor position constants
8
+ */
9
+ static TOP_LEFT = "top-left";
10
+ static TOP_CENTER = "top-center";
11
+ static TOP_RIGHT = "top-right";
12
+ static CENTER_LEFT = "center-left";
13
+ static CENTER = "center";
14
+ static CENTER_RIGHT = "center-right";
15
+ static BOTTOM_LEFT = "bottom-left";
16
+ static BOTTOM_CENTER = "bottom-center";
17
+ static BOTTOM_RIGHT = "bottom-right";
18
+
19
+ /**
20
+ * Calculates position based on anchor point
21
+ *
22
+ * @param {string} anchor - Anchor position constant
23
+ * @param {Object} object - Object being positioned (with width and height)
24
+ * @param {Object} container - Container to position relative to (with x, y, width, height)
25
+ * @param {number} margin - Margin from the container edges
26
+ * @param {number} offsetX - Additional X offset
27
+ * @param {number} offsetY - Additional Y offset
28
+ * @returns {Object} Position and alignment information
29
+ */
30
+ static calculate(anchor, object, container, margin = 10, offsetX = 0, offsetY = 0) {
31
+ // Ensure we have valid dimensions
32
+ const objectWidth = object.width || 0;
33
+ const objectHeight = object.height || 0;
34
+
35
+ const containerWidth = container.width || 0;
36
+ const containerHeight = container.height || 0;
37
+ const containerX = container.x || 0;
38
+ const containerY = container.y || 0;
39
+
40
+ // Calculate position based on anchor
41
+ let x, y, align, baseline;
42
+
43
+ switch (anchor) {
44
+ // Top row
45
+ case Position.TOP_LEFT:
46
+ x = (containerX - containerWidth/2) + margin + objectWidth/2;
47
+ y = (containerY - containerHeight/2) + margin + objectHeight/2;
48
+ align = "left";
49
+ baseline = "top";
50
+ break;
51
+
52
+ case Position.TOP_CENTER:
53
+ x = containerX;
54
+ y = (containerY - containerHeight/2) + margin + objectHeight/2;
55
+ align = "center";
56
+ baseline = "top";
57
+ break;
58
+
59
+ case Position.TOP_RIGHT:
60
+ x = (containerX + containerWidth/2) - margin - objectWidth/2;
61
+ y = (containerY - containerHeight/2) + margin + objectHeight/2;
62
+ align = "right";
63
+ baseline = "top";
64
+ break;
65
+
66
+ // Middle row
67
+ case Position.CENTER_LEFT:
68
+ x = (containerX - containerWidth/2) + margin + objectWidth/2;
69
+ y = containerY;
70
+ align = "left";
71
+ baseline = "middle";
72
+ break;
73
+
74
+ case Position.CENTER:
75
+ x = containerX;
76
+ y = containerY;
77
+ align = "center";
78
+ baseline = "middle";
79
+ break;
80
+
81
+ case Position.CENTER_RIGHT:
82
+ x = (containerX + containerWidth/2) - margin - objectWidth/2;
83
+ y = containerY;
84
+ align = "right";
85
+ baseline = "middle";
86
+ break;
87
+
88
+ // Bottom row
89
+ case Position.BOTTOM_LEFT:
90
+ //console.log("BOTTOM_LEFT", containerX, containerWidth, margin, objectWidth);
91
+ x = (containerX - containerWidth/2) + margin + objectWidth/2;
92
+ y = (containerY + containerHeight/2) - margin - objectHeight/2;
93
+ align = "left";
94
+ baseline = "bottom";
95
+ break;
96
+
97
+ case Position.BOTTOM_CENTER:
98
+ x = containerX;
99
+ y = (containerY + containerHeight/2) - margin - objectHeight/2;
100
+ align = "center";
101
+ baseline = "bottom";
102
+ break;
103
+
104
+ case Position.BOTTOM_RIGHT:
105
+ x = (containerX + containerWidth/2) - margin - objectWidth/2;
106
+ y = (containerY + containerHeight/2) - margin - objectHeight/2;
107
+ align = "right";
108
+ baseline = "bottom";
109
+ break;
110
+
111
+ default:
112
+ // Fallback to top-left
113
+ x = (containerX - containerWidth/2) + margin + objectWidth/2;
114
+ y = (containerY - containerHeight/2) + margin + objectHeight/2;
115
+ align = "left";
116
+ baseline = "top";
117
+ }
118
+
119
+ // Apply custom offsets
120
+ x += offsetX;
121
+ y += offsetY;
122
+
123
+ return { x, y, align, baseline };
124
+ }
125
+
126
+ /**
127
+ * Calculates absolute position relative to the game canvas
128
+ *
129
+ * @param {string} anchor - Anchor position constant
130
+ * @param {Object} object - Object being positioned
131
+ * @param {Object} game - Game object with canvas dimensions
132
+ * @param {number} margin - Margin from the edges
133
+ * @param {number} offsetX - Additional X offset
134
+ * @param {number} offsetY - Additional Y offset
135
+ * @returns {Object} Position and alignment information
136
+ */
137
+ static calculateAbsolute(anchor, object, game, margin = 10, offsetX = 0, offsetY = 0) {
138
+ const container = {
139
+ width: game.width,
140
+ height: game.height,
141
+ x: game.width / 2,
142
+ y: game.height / 2
143
+ };
144
+
145
+ return Position.calculate(anchor, object, container, margin, offsetX, offsetY);
146
+ }
147
+ }
@@ -0,0 +1,47 @@
1
+ export class TaskManager {
2
+ constructor(workerUrl) {
3
+ this.worker = new Worker(workerUrl);
4
+ this.nextTaskId = 1;
5
+ this.pendingTasks = new Map();
6
+
7
+ // Set up message handler
8
+ this.worker.onmessage = this.handleMessage.bind(this);
9
+ }
10
+
11
+ handleMessage(e) {
12
+ const { taskId, status, result, error } = e.data;
13
+
14
+ if (this.pendingTasks.has(taskId)) {
15
+ const { resolve, reject } = this.pendingTasks.get(taskId);
16
+
17
+ if (status === 'complete') {
18
+ resolve(result);
19
+ } else if (status === 'error') {
20
+ reject(new Error(error));
21
+ }
22
+
23
+ // Remove the completed task
24
+ this.pendingTasks.delete(taskId);
25
+ }
26
+ }
27
+
28
+ runTask(taskName, params) {
29
+ return new Promise((resolve, reject) => {
30
+ const taskId = this.nextTaskId++;
31
+
32
+ // Store the promise callbacks
33
+ this.pendingTasks.set(taskId, { resolve, reject });
34
+
35
+ // Send the task to the worker
36
+ this.worker.postMessage({
37
+ taskId,
38
+ taskName,
39
+ params
40
+ });
41
+ });
42
+ }
43
+
44
+ terminate() {
45
+ this.worker.terminate();
46
+ }
47
+ }