@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,357 @@
1
+ /**
2
+ * Transform
3
+ * ---------
4
+ *
5
+ * A fluent API for modifying spatial and transform properties of Transformable objects.
6
+ * Provides a consistent, chainable interface for all transform operations.
7
+ *
8
+ * ### Usage
9
+ *
10
+ * ```javascript
11
+ * // Fluent chaining
12
+ * shape.transform
13
+ * .x(100).y(200)
14
+ * .width(50).height(50)
15
+ * .rotation(45)
16
+ * .scale(0.8);
17
+ *
18
+ * // Batch updates
19
+ * shape.transform.set({
20
+ * x: 100, y: 200,
21
+ * width: 50, height: 50,
22
+ * rotation: 45,
23
+ * scaleX: 0.8, scaleY: 0.8
24
+ * });
25
+ *
26
+ * // Relative transforms
27
+ * shape.transform.translateBy(10, 20);
28
+ * shape.transform.rotateBy(15);
29
+ * shape.transform.scaleBy(1.5);
30
+ * ```
31
+ *
32
+ * ### Design Philosophy
33
+ *
34
+ * The Transform class enforces a consistent pattern for modifying shape properties:
35
+ * - All modifications go through the transform API
36
+ * - Direct property setters (shape.width = 100) can be disabled via strictTransforms
37
+ * - Enables future features like transform animation, undo/redo, etc.
38
+ */
39
+ export class Transform {
40
+ /**
41
+ * Global flag to control whether direct property setters throw errors.
42
+ * When true, setting shape.width = 100 throws an error.
43
+ * When false, it logs a deprecation warning and allows the operation.
44
+ * @type {boolean}
45
+ */
46
+ static strictMode = false;
47
+
48
+ /**
49
+ * Creates a Transform instance bound to a Transformable owner.
50
+ * @param {import('./transformable.js').Transformable} owner - The transformable object this transform controls
51
+ */
52
+ constructor(owner) {
53
+ this._owner = owner;
54
+ }
55
+
56
+ /**
57
+ * Gets the owner object this transform is bound to.
58
+ * @returns {import('./transformable.js').Transformable}
59
+ */
60
+ get owner() {
61
+ return this._owner;
62
+ }
63
+
64
+ // ============================================================
65
+ // Position Methods
66
+ // ============================================================
67
+
68
+ /**
69
+ * Sets the X position.
70
+ * @param {number} value - X coordinate
71
+ * @returns {Transform} this for chaining
72
+ */
73
+ x(value) {
74
+ this._owner._x = value;
75
+ this._owner.markBoundsDirty();
76
+ return this;
77
+ }
78
+
79
+ /**
80
+ * Sets the Y position.
81
+ * @param {number} value - Y coordinate
82
+ * @returns {Transform} this for chaining
83
+ */
84
+ y(value) {
85
+ this._owner._y = value;
86
+ this._owner.markBoundsDirty();
87
+ return this;
88
+ }
89
+
90
+ /**
91
+ * Sets both X and Y position.
92
+ * @param {number} x - X coordinate
93
+ * @param {number} y - Y coordinate
94
+ * @returns {Transform} this for chaining
95
+ */
96
+ position(x, y) {
97
+ this._owner._x = x;
98
+ this._owner._y = y;
99
+ this._owner.markBoundsDirty();
100
+ return this;
101
+ }
102
+
103
+ /**
104
+ * Translates position by relative amounts.
105
+ * @param {number} dx - Delta X
106
+ * @param {number} dy - Delta Y
107
+ * @returns {Transform} this for chaining
108
+ */
109
+ translateBy(dx, dy) {
110
+ this._owner._x += dx;
111
+ this._owner._y += dy;
112
+ this._owner.markBoundsDirty();
113
+ return this;
114
+ }
115
+
116
+ // ============================================================
117
+ // Dimension Methods
118
+ // ============================================================
119
+
120
+ /**
121
+ * Sets the width.
122
+ * @param {number} value - Width in pixels (must be >= 0)
123
+ * @returns {Transform} this for chaining
124
+ */
125
+ width(value) {
126
+ this._owner._width = Math.max(0, value);
127
+ this._owner.markBoundsDirty();
128
+ this._owner.invalidateCache?.();
129
+ return this;
130
+ }
131
+
132
+ /**
133
+ * Sets the height.
134
+ * @param {number} value - Height in pixels (must be >= 0)
135
+ * @returns {Transform} this for chaining
136
+ */
137
+ height(value) {
138
+ this._owner._height = Math.max(0, value);
139
+ this._owner.markBoundsDirty();
140
+ this._owner.invalidateCache?.();
141
+ return this;
142
+ }
143
+
144
+ /**
145
+ * Sets both width and height.
146
+ * @param {number} width - Width in pixels
147
+ * @param {number} height - Height in pixels
148
+ * @returns {Transform} this for chaining
149
+ */
150
+ size(width, height) {
151
+ this._owner._width = Math.max(0, width);
152
+ this._owner._height = Math.max(0, height);
153
+ this._owner.markBoundsDirty();
154
+ this._owner.invalidateCache?.();
155
+ return this;
156
+ }
157
+
158
+ // ============================================================
159
+ // Rotation Methods
160
+ // ============================================================
161
+
162
+ /**
163
+ * Sets the rotation in degrees.
164
+ * @param {number} degrees - Rotation angle in degrees
165
+ * @returns {Transform} this for chaining
166
+ */
167
+ rotation(degrees) {
168
+ this._owner._rotation = degrees * Math.PI / 180;
169
+ this._owner.markBoundsDirty();
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * Sets the rotation in radians.
175
+ * @param {number} radians - Rotation angle in radians
176
+ * @returns {Transform} this for chaining
177
+ */
178
+ rotationRad(radians) {
179
+ this._owner._rotation = radians;
180
+ this._owner.markBoundsDirty();
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ * Rotates by a relative amount in degrees.
186
+ * @param {number} degrees - Amount to rotate in degrees
187
+ * @returns {Transform} this for chaining
188
+ */
189
+ rotateBy(degrees) {
190
+ this._owner._rotation += degrees * Math.PI / 180;
191
+ this._owner.markBoundsDirty();
192
+ return this;
193
+ }
194
+
195
+ // ============================================================
196
+ // Scale Methods
197
+ // ============================================================
198
+
199
+ /**
200
+ * Sets the horizontal scale factor.
201
+ * @param {number} value - Scale factor (1 = 100%)
202
+ * @returns {Transform} this for chaining
203
+ */
204
+ scaleX(value) {
205
+ this._owner._scaleX = value;
206
+ this._owner.markBoundsDirty();
207
+ return this;
208
+ }
209
+
210
+ /**
211
+ * Sets the vertical scale factor.
212
+ * @param {number} value - Scale factor (1 = 100%)
213
+ * @returns {Transform} this for chaining
214
+ */
215
+ scaleY(value) {
216
+ this._owner._scaleY = value;
217
+ this._owner.markBoundsDirty();
218
+ return this;
219
+ }
220
+
221
+ /**
222
+ * Sets both scale factors uniformly.
223
+ * @param {number} value - Scale factor for both axes
224
+ * @returns {Transform} this for chaining
225
+ */
226
+ scale(value) {
227
+ this._owner._scaleX = value;
228
+ this._owner._scaleY = value;
229
+ this._owner.markBoundsDirty();
230
+ return this;
231
+ }
232
+
233
+ /**
234
+ * Multiplies current scale by a factor.
235
+ * @param {number} factor - Multiplication factor
236
+ * @returns {Transform} this for chaining
237
+ */
238
+ scaleBy(factor) {
239
+ this._owner._scaleX *= factor;
240
+ this._owner._scaleY *= factor;
241
+ this._owner.markBoundsDirty();
242
+ return this;
243
+ }
244
+
245
+ // ============================================================
246
+ // Batch Operations
247
+ // ============================================================
248
+
249
+ /**
250
+ * Sets multiple properties at once.
251
+ * @param {Object} props - Object containing properties to set
252
+ * @param {number} [props.x] - X position
253
+ * @param {number} [props.y] - Y position
254
+ * @param {number} [props.width] - Width
255
+ * @param {number} [props.height] - Height
256
+ * @param {number} [props.rotation] - Rotation in degrees
257
+ * @param {number} [props.scaleX] - Horizontal scale
258
+ * @param {number} [props.scaleY] - Vertical scale
259
+ * @returns {Transform} this for chaining
260
+ */
261
+ set(props) {
262
+ let visualChange = false;
263
+ if (props.x !== undefined) this._owner._x = props.x;
264
+ if (props.y !== undefined) this._owner._y = props.y;
265
+ if (props.width !== undefined) { this._owner._width = Math.max(0, props.width); visualChange = true; }
266
+ if (props.height !== undefined) { this._owner._height = Math.max(0, props.height); visualChange = true; }
267
+ if (props.rotation !== undefined) { this._owner._rotation = props.rotation * Math.PI / 180; }
268
+ if (props.scaleX !== undefined) { this._owner._scaleX = props.scaleX; }
269
+ if (props.scaleY !== undefined) { this._owner._scaleY = props.scaleY; }
270
+
271
+ this._owner.markBoundsDirty();
272
+ if (visualChange) this._owner.invalidateCache?.();
273
+ return this;
274
+ }
275
+
276
+ /**
277
+ * Resets all transforms to their default values.
278
+ * Position and dimensions are preserved.
279
+ * @returns {Transform} this for chaining
280
+ */
281
+ reset() {
282
+ this._owner._rotation = 0;
283
+ this._owner._scaleX = 1;
284
+ this._owner._scaleY = 1;
285
+ this._owner.markBoundsDirty();
286
+ return this;
287
+ }
288
+
289
+ /**
290
+ * Resets everything including position and dimensions.
291
+ * @returns {Transform} this for chaining
292
+ */
293
+ resetAll() {
294
+ this._owner._x = 0;
295
+ this._owner._y = 0;
296
+ this._owner._width = 0;
297
+ this._owner._height = 0;
298
+ this._owner._rotation = 0;
299
+ this._owner._scaleX = 1;
300
+ this._owner._scaleY = 1;
301
+ this._owner.markBoundsDirty();
302
+ this._owner.invalidateCache?.(); // Only for width/height reset
303
+ return this;
304
+ }
305
+
306
+ // ============================================================
307
+ // Utility Methods
308
+ // ============================================================
309
+
310
+ /**
311
+ * Creates a copy of the current transform values.
312
+ * @returns {{x: number, y: number, width: number, height: number, rotation: number, scaleX: number, scaleY: number}}
313
+ */
314
+ toObject() {
315
+ return {
316
+ x: this._owner._x,
317
+ y: this._owner._y,
318
+ width: this._owner._width,
319
+ height: this._owner._height,
320
+ rotation: this._owner._rotation * 180 / Math.PI, // Convert back to degrees
321
+ scaleX: this._owner._scaleX,
322
+ scaleY: this._owner._scaleY
323
+ };
324
+ }
325
+
326
+ /**
327
+ * Copies transform values from another Transform or plain object.
328
+ * @param {Transform|Object} source - Source to copy from
329
+ * @returns {Transform} this for chaining
330
+ */
331
+ copyFrom(source) {
332
+ const values = source instanceof Transform ? source.toObject() : source;
333
+ return this.set(values);
334
+ }
335
+
336
+ /**
337
+ * Static helper to handle property setter access.
338
+ * Called by Geometry2d and Transformable setters to enforce strictMode.
339
+ * @param {string} property - The property name being set
340
+ * @param {*} value - The value being assigned
341
+ * @throws {Error} When strictMode is true
342
+ */
343
+ static handleDirectSet(property, value) {
344
+ if (Transform.strictMode) {
345
+ throw new Error(
346
+ `Direct property assignment "${property} = ${value}" is disabled. ` +
347
+ `Use shape.transform.${property}(${value}) instead. ` +
348
+ `Set Transform.strictMode = false to allow direct assignment.`
349
+ );
350
+ } else {
351
+ console.warn(
352
+ `[Deprecation] Direct assignment "${property} = ${value}" is deprecated. ` +
353
+ `Use shape.transform.${property}(${value}) instead.`
354
+ );
355
+ }
356
+ }
357
+ }
@@ -0,0 +1,172 @@
1
+ import { Renderable } from "./renderable.js";
2
+ import { Painter } from "../painter/painter.js";
3
+ import { Transform } from "./transform.js";
4
+
5
+ /**
6
+ * Transformable
7
+ * --------------
8
+ *
9
+ * A renderable object that supports **canvas transformations**:
10
+ * - Scaling
11
+ * - Rotation
12
+ *
13
+ * ### Architectural Role
14
+ *
15
+ * - Extends the render lifecycle by wrapping `draw()` inside a transformed context.
16
+ * - Adds transform properties (`scaleX`, `scaleY`, `rotation`)
17
+ * - Introduces the `.transform` API for fluent, chainable property access
18
+ *
19
+ * ### Transform API
20
+ *
21
+ * The `.transform` property provides a fluent API for modifying properties:
22
+ *
23
+ * ```javascript
24
+ * // Fluent chaining
25
+ * shape.transform
26
+ * .x(100).y(200)
27
+ * .width(50).height(50)
28
+ * .rotation(45)
29
+ * .scale(0.8);
30
+ *
31
+ * // Batch updates
32
+ * shape.transform.set({ x: 100, y: 200, rotation: 45 });
33
+ *
34
+ * // Relative transforms
35
+ * shape.transform.translateBy(10, 20);
36
+ * shape.transform.rotateBy(15);
37
+ * ```
38
+ *
39
+ * This is the final base layer before custom shape logic is introduced.
40
+ *
41
+ * @abstract
42
+ * @extends Renderable
43
+ */
44
+ export class Transformable extends Renderable {
45
+ /**
46
+ * @param {Object} [options={}]
47
+ * @param {number} [options.rotation=0] - Rotation in degrees (clockwise)
48
+ * @param {number} [options.scaleX=1] - Horizontal scale factor
49
+ * @param {number} [options.scaleY=1] - Vertical scale factor
50
+ */
51
+ constructor(options = {}) {
52
+ super(options);
53
+ this._rotation = options.rotation * Math.PI / 180 ?? 0;
54
+ this._scaleX = options.scaleX ?? 1;
55
+ this._scaleY = options.scaleY ?? 1;
56
+
57
+ /**
58
+ * Fluent transform API for modifying spatial and transform properties.
59
+ * @type {Transform}
60
+ */
61
+ this.transform = new Transform(this);
62
+
63
+ this.logger.log("Transformable", this.x, this.y, this.width, this.height);
64
+ }
65
+
66
+ /**
67
+ * The main rendering method.
68
+ * Applies transforms and draws debug bounding box.
69
+ * Subclasses should call super.draw() before their drawing logic.
70
+ */
71
+ draw() {
72
+ this.applyTransforms();
73
+ this.drawDebug();
74
+ }
75
+
76
+ /**
77
+ * Applies canvas transform context.
78
+ * Order: rotate → scale
79
+ */
80
+ applyTransforms() {
81
+ if (this._isCaching) return;
82
+ Painter.rotate(this._rotation);
83
+ Painter.scale(this._scaleX, this._scaleY);
84
+ }
85
+
86
+ /**
87
+ * Gets the object's rotation in radians.
88
+ * @type {number}
89
+ */
90
+ get rotation() {
91
+ return this._rotation;
92
+ }
93
+
94
+ set rotation(v) {
95
+ this._rotation = v * Math.PI / 180;
96
+ this.markBoundsDirty();
97
+ }
98
+
99
+ /**
100
+ * Gets horizontal scale factor.
101
+ * @type {number}
102
+ */
103
+ get scaleX() {
104
+ return this._scaleX;
105
+ }
106
+
107
+ set scaleX(v) {
108
+ this._scaleX = v;
109
+ this.markBoundsDirty();
110
+ }
111
+
112
+ /**
113
+ * Gets vertical scale factor.
114
+ * @type {number}
115
+ */
116
+ get scaleY() {
117
+ return this._scaleY;
118
+ }
119
+
120
+ set scaleY(v) {
121
+ this._scaleY = v;
122
+ this.markBoundsDirty();
123
+ }
124
+
125
+ /**
126
+ * Calculates the bounding box *after* applying rotation and scale.
127
+ * Used by Geometry2d → getBounds().
128
+ *
129
+ * @override
130
+ * @protected
131
+ * @returns {{x: number, y: number, width: number, height: number}}
132
+ */
133
+ calculateBounds() {
134
+ const halfW = this.width / 2;
135
+ const halfH = this.height / 2;
136
+
137
+ const corners = [
138
+ { x: -halfW, y: -halfH },
139
+ { x: halfW, y: -halfH },
140
+ { x: halfW, y: halfH },
141
+ { x: -halfW, y: halfH },
142
+ ];
143
+
144
+ const cos = Math.cos(this._rotation);
145
+ const sin = Math.sin(this._rotation);
146
+
147
+ const transformed = corners.map(({ x, y }) => {
148
+ x *= this._scaleX;
149
+ y *= this._scaleY;
150
+
151
+ const rx = x * cos - y * sin;
152
+ const ry = x * sin + y * cos;
153
+
154
+ return { x: rx + this.x, y: ry + this.y };
155
+ });
156
+
157
+ const xs = transformed.map((p) => p.x);
158
+ const ys = transformed.map((p) => p.y);
159
+
160
+ const minX = Math.min(...xs);
161
+ const maxX = Math.max(...xs);
162
+ const minY = Math.min(...ys);
163
+ const maxY = Math.max(...ys);
164
+
165
+ return {
166
+ x: (minX + maxX) / 2,
167
+ y: (minY + maxY) / 2,
168
+ width: maxX - minX,
169
+ height: maxY - minY,
170
+ };
171
+ }
172
+ }
@@ -0,0 +1,26 @@
1
+ import { Shape } from "./shape.js";
2
+ import { Painter } from "../painter/painter.js";
3
+
4
+ export class Triangle extends Shape {
5
+ constructor(size = 50, options = {}) {
6
+ super(options);
7
+ this.size = size;
8
+ }
9
+
10
+ draw() {
11
+ super.draw();
12
+ const half = this.size / 2;
13
+ const points = [
14
+ { x: 0, y: -half },
15
+ { x: half, y: half },
16
+ { x: -half, y: half },
17
+ ];
18
+
19
+ Painter.shapes.polygon(
20
+ points,
21
+ this.color,
22
+ this.stroke,
23
+ this.lineWidth
24
+ );
25
+ }
26
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * GCanvas Sound Module
3
+ * Procedural audio generation for games and creative coding
4
+ * @module sound
5
+ */
6
+
7
+ // Main classes
8
+ export { Synth } from "./synth.js";
9
+ export { Sound } from "./sound.js";
10
+
11
+ // Sub-modules (for advanced usage)
12
+ export { SynthOscillators } from "./synth.oscillators.js";
13
+ export { SynthEffects } from "./synth.effects.js";
14
+ export { SynthEnvelope } from "./synth.envelope.js";
15
+ export { SynthNoise } from "./synth.noise.js";
16
+ export { SynthMusical } from "./synth.musical.js";
17
+ export { SynthAnalyzer } from "./synth.analyzer.js";