@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,380 @@
1
+ /**
2
+ * @module Sketch
3
+ * @description Ultra-simple sketch mode for quick creative coding prototypes
4
+ *
5
+ * Provides a minimal API for rapid generative art and creative coding experiments,
6
+ * ideal for Genuary-style sketches where brevity is key.
7
+ *
8
+ * @example
9
+ * import { sketch } from 'gcanvas';
10
+ *
11
+ * sketch(800, 600, 'black')
12
+ * .circle(400, 300, 50, 'lime')
13
+ * .update((dt, ctx) => {
14
+ * ctx.shapes[0].x += Math.sin(ctx.time) * 2;
15
+ * })
16
+ * .start();
17
+ */
18
+
19
+ import { gcanvas } from "./fluent-game.js";
20
+
21
+ /**
22
+ * Ultra-simple sketch mode for quick prototypes
23
+ * @param {number} [w=800] - Canvas width
24
+ * @param {number} [h=600] - Canvas height
25
+ * @param {string} [bg='black'] - Background color
26
+ * @returns {SketchAPI}
27
+ */
28
+ export function sketch(w = 800, h = 600, bg = 'black') {
29
+ const shapes = [];
30
+ let updateFn = null;
31
+ let setupFn = null;
32
+ let fluentGame = null;
33
+
34
+ const api = {
35
+ // ─────────────── SHAPES ───────────────
36
+
37
+ /**
38
+ * Add a circle
39
+ * @param {number} x - X position
40
+ * @param {number} y - Y position
41
+ * @param {number} r - Radius
42
+ * @param {string} [fill='white'] - Fill color
43
+ * @returns {SketchAPI}
44
+ */
45
+ circle: (x, y, r, fill = 'white') => {
46
+ shapes.push({ type: 'circle', x, y, radius: r, fill });
47
+ return api;
48
+ },
49
+
50
+ /**
51
+ * Add a rectangle
52
+ * @param {number} x - X position
53
+ * @param {number} y - Y position
54
+ * @param {number} w - Width
55
+ * @param {number} h - Height
56
+ * @param {string} [fill='white'] - Fill color
57
+ * @returns {SketchAPI}
58
+ */
59
+ rect: (x, y, w, h, fill = 'white') => {
60
+ shapes.push({ type: 'rect', x, y, width: w, height: h, fill });
61
+ return api;
62
+ },
63
+
64
+ /**
65
+ * Add a square
66
+ * @param {number} x - X position
67
+ * @param {number} y - Y position
68
+ * @param {number} size - Size
69
+ * @param {string} [fill='white'] - Fill color
70
+ * @returns {SketchAPI}
71
+ */
72
+ square: (x, y, size, fill = 'white') => {
73
+ shapes.push({ type: 'rect', x, y, width: size, height: size, fill });
74
+ return api;
75
+ },
76
+
77
+ /**
78
+ * Add a star
79
+ * @param {number} x - X position
80
+ * @param {number} y - Y position
81
+ * @param {number} points - Number of points
82
+ * @param {number} r - Outer radius
83
+ * @param {string} [fill='white'] - Fill color
84
+ * @returns {SketchAPI}
85
+ */
86
+ star: (x, y, points, r, fill = 'white') => {
87
+ shapes.push({ type: 'star', x, y, points, radius: r, fill });
88
+ return api;
89
+ },
90
+
91
+ /**
92
+ * Add a triangle
93
+ * @param {number} x - X position
94
+ * @param {number} y - Y position
95
+ * @param {number} size - Size
96
+ * @param {string} [fill='white'] - Fill color
97
+ * @returns {SketchAPI}
98
+ */
99
+ triangle: (x, y, size, fill = 'white') => {
100
+ shapes.push({ type: 'triangle', x, y, size, fill });
101
+ return api;
102
+ },
103
+
104
+ /**
105
+ * Add a hexagon
106
+ * @param {number} x - X position
107
+ * @param {number} y - Y position
108
+ * @param {number} r - Radius
109
+ * @param {string} [fill='white'] - Fill color
110
+ * @returns {SketchAPI}
111
+ */
112
+ hexagon: (x, y, r, fill = 'white') => {
113
+ shapes.push({ type: 'hexagon', x, y, radius: r, fill });
114
+ return api;
115
+ },
116
+
117
+ /**
118
+ * Add a line
119
+ * @param {number} x1 - Start X
120
+ * @param {number} y1 - Start Y
121
+ * @param {number} x2 - End X
122
+ * @param {number} y2 - End Y
123
+ * @param {string} [stroke='white'] - Stroke color
124
+ * @param {number} [lineWidth=1] - Line width
125
+ * @returns {SketchAPI}
126
+ */
127
+ line: (x1, y1, x2, y2, stroke = 'white', lineWidth = 1) => {
128
+ shapes.push({ type: 'line', x: x1, y: y1, x2, y2, stroke, lineWidth });
129
+ return api;
130
+ },
131
+
132
+ /**
133
+ * Add a ring
134
+ * @param {number} x - X position
135
+ * @param {number} y - Y position
136
+ * @param {number} innerRadius - Inner radius
137
+ * @param {number} outerRadius - Outer radius
138
+ * @param {string} [fill='white'] - Fill color
139
+ * @returns {SketchAPI}
140
+ */
141
+ ring: (x, y, innerRadius, outerRadius, fill = 'white') => {
142
+ shapes.push({ type: 'ring', x, y, innerRadius, outerRadius, fill });
143
+ return api;
144
+ },
145
+
146
+ /**
147
+ * Add text
148
+ * @param {string} content - Text content
149
+ * @param {number} x - X position
150
+ * @param {number} y - Y position
151
+ * @param {Object} [opts] - Text options
152
+ * @returns {SketchAPI}
153
+ */
154
+ text: (content, x, y, opts = {}) => {
155
+ shapes.push({ type: 'text', content, x, y, ...opts });
156
+ return api;
157
+ },
158
+
159
+ // ─────────────── BULK CREATION ───────────────
160
+
161
+ /**
162
+ * Create a grid of shapes
163
+ * @param {number} cols - Number of columns
164
+ * @param {number} rows - Number of rows
165
+ * @param {number} spacing - Spacing between cells
166
+ * @param {Function} shapeFn - Function(api, x, y, col, row) to create shapes
167
+ * @returns {SketchAPI}
168
+ */
169
+ grid: (cols, rows, spacing, shapeFn) => {
170
+ for (let i = 0; i < cols; i++) {
171
+ for (let j = 0; j < rows; j++) {
172
+ const x = i * spacing + spacing / 2;
173
+ const y = j * spacing + spacing / 2;
174
+ shapeFn(api, x, y, i, j);
175
+ }
176
+ }
177
+ return api;
178
+ },
179
+
180
+ /**
181
+ * Repeat a shape creation function
182
+ * @param {number} count - Number of repetitions
183
+ * @param {Function} shapeFn - Function(api, index, total) to create shapes
184
+ * @returns {SketchAPI}
185
+ */
186
+ repeat: (count, shapeFn) => {
187
+ for (let i = 0; i < count; i++) {
188
+ shapeFn(api, i, count);
189
+ }
190
+ return api;
191
+ },
192
+
193
+ /**
194
+ * Create shapes in a circular pattern
195
+ * @param {number} cx - Center X
196
+ * @param {number} cy - Center Y
197
+ * @param {number} radius - Circle radius
198
+ * @param {number} count - Number of items
199
+ * @param {Function} shapeFn - Function(api, x, y, angle, index)
200
+ * @returns {SketchAPI}
201
+ */
202
+ radial: (cx, cy, radius, count, shapeFn) => {
203
+ for (let i = 0; i < count; i++) {
204
+ const angle = (i / count) * Math.PI * 2;
205
+ const x = cx + Math.cos(angle) * radius;
206
+ const y = cy + Math.sin(angle) * radius;
207
+ shapeFn(api, x, y, angle, i);
208
+ }
209
+ return api;
210
+ },
211
+
212
+ // ─────────────── LIFECYCLE ───────────────
213
+
214
+ /**
215
+ * Register a setup function (called once before start)
216
+ * @param {Function} fn - Setup function(api)
217
+ * @returns {SketchAPI}
218
+ */
219
+ setup: (fn) => {
220
+ setupFn = fn;
221
+ return api;
222
+ },
223
+
224
+ /**
225
+ * Register an update function (called every frame)
226
+ * @param {Function} fn - Update function(dt, context)
227
+ * @returns {SketchAPI}
228
+ */
229
+ update: (fn) => {
230
+ updateFn = fn;
231
+ return api;
232
+ },
233
+
234
+ // ─────────────── START ───────────────
235
+
236
+ /**
237
+ * Start the sketch
238
+ * @returns {import('./fluent-game.js').FluentGame}
239
+ */
240
+ start: () => {
241
+ const ctx = gcanvas({ width: w, height: h, bg });
242
+
243
+ // Run setup
244
+ setupFn?.(api);
245
+
246
+ // Create GOs for all shapes
247
+ const goRefs = [];
248
+ let sceneCtx = ctx.scene('default');
249
+
250
+ shapes.forEach((s, i) => {
251
+ const goCtx = sceneCtx.go({ x: s.x ?? 0, y: s.y ?? 0, name: `shape_${i}` });
252
+
253
+ switch (s.type) {
254
+ case 'circle':
255
+ goCtx.circle({ radius: s.radius, fill: s.fill });
256
+ break;
257
+ case 'rect':
258
+ goCtx.rect({ width: s.width, height: s.height, fill: s.fill });
259
+ break;
260
+ case 'star':
261
+ goCtx.star({ points: s.points, radius: s.radius, fill: s.fill });
262
+ break;
263
+ case 'triangle':
264
+ goCtx.triangle({ size: s.size, fill: s.fill });
265
+ break;
266
+ case 'hexagon':
267
+ goCtx.hexagon({ radius: s.radius, fill: s.fill });
268
+ break;
269
+ case 'line':
270
+ goCtx.line({
271
+ x2: s.x2 - s.x,
272
+ y2: s.y2 - s.y,
273
+ stroke: s.stroke,
274
+ lineWidth: s.lineWidth
275
+ });
276
+ break;
277
+ case 'ring':
278
+ goCtx.ring({
279
+ innerRadius: s.innerRadius,
280
+ outerRadius: s.outerRadius,
281
+ fill: s.fill
282
+ });
283
+ break;
284
+ case 'text':
285
+ goCtx.text(s.content, {
286
+ fill: s.fill ?? 'white',
287
+ font: s.font ?? '16px monospace'
288
+ });
289
+ break;
290
+ }
291
+
292
+ goRefs.push(goCtx.goInstance);
293
+
294
+ // Return to scene context for next shape
295
+ sceneCtx = goCtx.end();
296
+ });
297
+
298
+ // Wire update
299
+ if (updateFn) {
300
+ let frameCount = 0;
301
+ let totalTime = 0;
302
+
303
+ ctx.on('update', (dt, gameCtx) => {
304
+ frameCount++;
305
+ totalTime += dt;
306
+
307
+ updateFn(dt, {
308
+ shapes: goRefs,
309
+ time: totalTime,
310
+ frame: frameCount,
311
+ width: w,
312
+ height: h,
313
+ mouse: {
314
+ x: gameCtx.game.mouse?.x ?? 0,
315
+ y: gameCtx.game.mouse?.y ?? 0
316
+ },
317
+ refs: gameCtx.refs,
318
+ game: gameCtx.game
319
+ });
320
+ });
321
+ }
322
+
323
+ fluentGame = ctx;
324
+ return ctx.start();
325
+ },
326
+
327
+ // ─────────────── UTILITIES ───────────────
328
+
329
+ /**
330
+ * Get canvas width
331
+ * @returns {number}
332
+ */
333
+ get width() { return w; },
334
+
335
+ /**
336
+ * Get canvas height
337
+ * @returns {number}
338
+ */
339
+ get height() { return h; },
340
+
341
+ /**
342
+ * Get the underlying FluentGame (after start)
343
+ * @returns {import('./fluent-game.js').FluentGame|null}
344
+ */
345
+ get game() { return fluentGame; }
346
+ };
347
+
348
+ return api;
349
+ }
350
+
351
+ /**
352
+ * @typedef {Object} SketchContext
353
+ * @property {Array<GameObject>} shapes - All created GameObjects
354
+ * @property {number} time - Elapsed time in seconds
355
+ * @property {number} frame - Current frame number
356
+ * @property {number} width - Canvas width
357
+ * @property {number} height - Canvas height
358
+ * @property {{x: number, y: number}} mouse - Mouse position
359
+ * @property {Object} refs - Named object references
360
+ * @property {import('../game/game.js').Game} game - The underlying Game instance
361
+ */
362
+
363
+ /**
364
+ * @typedef {Object} SketchAPI
365
+ * @property {function(number, number, number, string=): SketchAPI} circle - Add a circle
366
+ * @property {function(number, number, number, number, string=): SketchAPI} rect - Add a rectangle
367
+ * @property {function(number, number, number, string=): SketchAPI} square - Add a square
368
+ * @property {function(number, number, number, number, string=): SketchAPI} star - Add a star
369
+ * @property {function(number, number, number, string=): SketchAPI} triangle - Add a triangle
370
+ * @property {function(number, number, number, string=): SketchAPI} hexagon - Add a hexagon
371
+ * @property {function(number, number, number, number, string=, number=): SketchAPI} line - Add a line
372
+ * @property {function(number, number, number, number, string=): SketchAPI} ring - Add a ring
373
+ * @property {function(string, number, number, Object=): SketchAPI} text - Add text
374
+ * @property {function(number, number, number, Function): SketchAPI} grid - Create a grid of shapes
375
+ * @property {function(number, Function): SketchAPI} repeat - Repeat shape creation
376
+ * @property {function(number, number, number, number, Function): SketchAPI} radial - Create radial pattern
377
+ * @property {function(Function): SketchAPI} setup - Register setup function
378
+ * @property {function(Function): SketchAPI} update - Register update function
379
+ * @property {function(): import('./fluent-game.js').FluentGame} start - Start the sketch
380
+ */