@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,275 @@
1
+ # IsometricScene
2
+
3
+ > A Scene subclass for rendering isometric tile-based games and visualizations.
4
+
5
+ ## Overview
6
+
7
+ `IsometricScene` extends `Scene` to provide automatic isometric projection for child GameObjects. It uses the standard "diamond" isometric projection where grid coordinates (x, y) are transformed to 2D screen positions, with optional z-axis support for height above the ground plane.
8
+
9
+ ## Quick Start
10
+
11
+ ```javascript
12
+ import { Game, IsometricScene, GameObject } from "gcanvas";
13
+
14
+ class MyGame extends Game {
15
+ init() {
16
+ super.init();
17
+
18
+ // Create an isometric scene centered on canvas
19
+ const isoScene = new IsometricScene(this, {
20
+ x: this.width / 2,
21
+ y: this.height / 2,
22
+ tileWidth: 64,
23
+ tileHeight: 32,
24
+ gridSize: 10,
25
+ depthSort: true,
26
+ });
27
+
28
+ // Add objects using grid coordinates
29
+ const player = new Player(this);
30
+ player.x = 0; // Grid X
31
+ player.y = 0; // Grid Y
32
+ player.z = 0; // Height above ground
33
+ isoScene.add(player);
34
+
35
+ this.pipeline.add(isoScene);
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Constructor Options
41
+
42
+ | Option | Type | Default | Description |
43
+ |--------|------|---------|-------------|
44
+ | `tileWidth` | number | 64 | Width of a tile in pixels |
45
+ | `tileHeight` | number | tileWidth / 2 | Height of a tile in pixels |
46
+ | `gridSize` | number | 10 | Size of the grid (used for scale calculations) |
47
+ | `depthSort` | boolean | true | Sort children by depth (back-to-front) |
48
+ | `scaleByDepth` | boolean | false | Scale children by perspective distance |
49
+ | `elevationScale` | number | 1 | Multiplier for z-axis visual offset |
50
+ | `camera` | IsometricCamera | null | Optional camera for animated view rotation |
51
+
52
+ ## Coordinate System
53
+
54
+ IsometricScene uses a diamond-shaped isometric projection:
55
+
56
+ ```
57
+ Y-
58
+ \
59
+ \
60
+ \
61
+ X- ------+------ X+
62
+ /
63
+ /
64
+ /
65
+ Y+
66
+ ```
67
+
68
+ - **Grid coordinates (x, y)**: Position on the isometric grid
69
+ - **Z coordinate**: Height above the ground plane
70
+ - **Screen coordinates**: Calculated automatically via projection
71
+
72
+ ### Projection Formula
73
+
74
+ ```javascript
75
+ // Grid to screen conversion
76
+ screenX = (x - y) * (tileWidth / 2)
77
+ screenY = (x + y) * (tileHeight / 2) - z * elevationScale
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### toIsometric(x, y, z?)
83
+
84
+ Converts grid coordinates to screen coordinates.
85
+
86
+ ```javascript
87
+ const screen = isoScene.toIsometric(5, 3, 0);
88
+ // { x: 64, y: 128, depth: 8 }
89
+ ```
90
+
91
+ **Parameters:**
92
+ - `x` (number): Grid X coordinate
93
+ - `y` (number): Grid Y coordinate
94
+ - `z` (number, optional): Height above ground (default: 0)
95
+
96
+ **Returns:** `{ x: number, y: number, depth: number }`
97
+
98
+ ### fromIsometric(screenX, screenY)
99
+
100
+ Converts screen coordinates back to grid coordinates (inverse transform).
101
+
102
+ ```javascript
103
+ const grid = isoScene.fromIsometric(64, 128);
104
+ // { x: 5, y: 3 }
105
+ ```
106
+
107
+ **Parameters:**
108
+ - `screenX` (number): Screen X relative to scene center
109
+ - `screenY` (number): Screen Y relative to scene center
110
+
111
+ **Returns:** `{ x: number, y: number }`
112
+
113
+ ### getTileAt(screenX, screenY)
114
+
115
+ Gets the tile coordinates at a screen position (floored to integers).
116
+
117
+ ```javascript
118
+ const tile = isoScene.getTileAt(mouseX - scene.x, mouseY - scene.y);
119
+ // { x: 5, y: 3 }
120
+ ```
121
+
122
+ **Parameters:**
123
+ - `screenX` (number): Screen X relative to scene center
124
+ - `screenY` (number): Screen Y relative to scene center
125
+
126
+ **Returns:** `{ x: number, y: number }`
127
+
128
+ ### getDepthScale(y)
129
+
130
+ Calculates scale factor for perspective effect based on Y position.
131
+
132
+ ```javascript
133
+ const scale = isoScene.getDepthScale(5);
134
+ // 1.0 (range: 0.7 to 1.3)
135
+ ```
136
+
137
+ **Parameters:**
138
+ - `y` (number): Grid Y position
139
+
140
+ **Returns:** `number` (scale factor)
141
+
142
+ ## Depth Sorting
143
+
144
+ When `depthSort` is enabled (default), children are rendered in back-to-front order based on:
145
+
146
+ 1. **zIndex** (primary): Higher zIndex renders on top
147
+ 2. **Isometric depth** (secondary): Calculated as `x + y - z * 0.01`
148
+
149
+ Objects further back (lower x + y values) render first, allowing closer objects to overlap them correctly.
150
+
151
+ ```javascript
152
+ // Force an object to render behind/in front
153
+ myObject.zIndex = -1; // Render behind others
154
+ myObject.zIndex = 10; // Render on top
155
+ ```
156
+
157
+ ## Working with Children
158
+
159
+ ### Grid-Based GameObjects
160
+
161
+ Children added to IsometricScene use grid coordinates:
162
+
163
+ ```javascript
164
+ class Tile extends GameObject {
165
+ constructor(game, gridX, gridY) {
166
+ super(game);
167
+ this.x = gridX; // Grid coordinate, not pixels
168
+ this.y = gridY;
169
+ this.z = 0; // Ground level
170
+ }
171
+
172
+ render() {
173
+ // Render at local origin - scene handles projection
174
+ Painter.shapes.fillRect(-16, -8, 32, 16, "#8B4513");
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Manual Projection
180
+
181
+ For complex rendering (shadows, effects), access the scene's projection:
182
+
183
+ ```javascript
184
+ class Ball extends GameObject {
185
+ render() {
186
+ // Get projected position for manual drawing
187
+ const pos = this.parent.toIsometric(this.x, this.y, 0);
188
+ const elevation = this.z * 0.7;
189
+
190
+ // Draw shadow at ground level
191
+ Painter.shapes.fillCircle(pos.x, pos.y, 10, "rgba(0,0,0,0.3)");
192
+
193
+ // Draw ball at elevated position
194
+ Painter.shapes.fillCircle(pos.x, pos.y - elevation, 10, "blue");
195
+ }
196
+ }
197
+ ```
198
+
199
+ ## Example: Tile Map
200
+
201
+ ```javascript
202
+ class TileMap extends GameObject {
203
+ constructor(game, isoScene) {
204
+ super(game);
205
+ this.isoScene = isoScene;
206
+ this.tiles = [
207
+ [1, 1, 1, 1, 1],
208
+ [1, 0, 0, 0, 1],
209
+ [1, 0, 2, 0, 1],
210
+ [1, 0, 0, 0, 1],
211
+ [1, 1, 1, 1, 1],
212
+ ];
213
+ this.colors = {
214
+ 0: "#90EE90", // Grass
215
+ 1: "#8B4513", // Wall
216
+ 2: "#4169E1", // Water
217
+ };
218
+ this.zIndex = -1;
219
+ }
220
+
221
+ render() {
222
+ const size = this.tiles.length;
223
+ const halfW = this.isoScene.tileWidth / 2;
224
+ const halfH = this.isoScene.tileHeight / 2;
225
+
226
+ for (let y = 0; y < size; y++) {
227
+ for (let x = 0; x < size; x++) {
228
+ const tileType = this.tiles[y][x];
229
+ const pos = this.isoScene.toIsometric(x - size/2, y - size/2);
230
+
231
+ // Draw diamond tile
232
+ Painter.useCtx((ctx) => {
233
+ ctx.beginPath();
234
+ ctx.moveTo(pos.x, pos.y - halfH);
235
+ ctx.lineTo(pos.x + halfW, pos.y);
236
+ ctx.lineTo(pos.x, pos.y + halfH);
237
+ ctx.lineTo(pos.x - halfW, pos.y);
238
+ ctx.closePath();
239
+ ctx.fillStyle = this.colors[tileType];
240
+ ctx.fill();
241
+ ctx.strokeStyle = "#000";
242
+ ctx.lineWidth = 1;
243
+ ctx.stroke();
244
+ });
245
+ }
246
+ }
247
+ }
248
+ }
249
+ ```
250
+
251
+ ## Example: Mouse Picking
252
+
253
+ ```javascript
254
+ class PickableGrid extends IsometricScene {
255
+ constructor(game, options) {
256
+ super(game, { ...options, interactive: true });
257
+ this.hoveredTile = null;
258
+ }
259
+
260
+ onMouseMove(e) {
261
+ // Convert screen position to scene-local coordinates
262
+ const localX = e.clientX - this.x;
263
+ const localY = e.clientY - this.y;
264
+
265
+ // Get tile at mouse position
266
+ this.hoveredTile = this.getTileAt(localX, localY);
267
+ }
268
+ }
269
+ ```
270
+
271
+ ## See Also
272
+
273
+ - [Scene](./scene.md) - Base container class
274
+ - [Scene3D](./scene3d.md) - Camera-based 3D projection
275
+ - [Rendering Pipeline](../concepts/rendering-pipeline.md) - How rendering works
@@ -0,0 +1,328 @@
1
+ # Painter Module
2
+
3
+ > Low-level canvas drawing abstraction.
4
+
5
+ ## Overview
6
+
7
+ Painter is a static utility class that wraps the Canvas 2D API. It provides centralized drawing operations and state management, used internally by all shapes.
8
+
9
+ ## Quick Start
10
+
11
+ ```js
12
+ import { Painter } from 'gcanvas';
13
+
14
+ // Initialize with canvas context
15
+ const canvas = document.getElementById('canvas');
16
+ Painter.init(canvas.getContext('2d'));
17
+
18
+ // Draw shapes directly
19
+ Painter.shapes.fillCircle(200, 150, 50, 'red');
20
+ Painter.shapes.strokeRect(300, 100, 100, 80, 'blue', 2);
21
+
22
+ // Draw text
23
+ Painter.text.fill('Hello World', 400, 200, {
24
+ font: '24px monospace',
25
+ color: 'white'
26
+ });
27
+ ```
28
+
29
+ ## Architecture
30
+
31
+ Painter is composed of specialized sub-modules:
32
+
33
+ ```
34
+ ┌─────────────────────────────────────────────────────────────┐
35
+ │ Painter │
36
+ ├─────────────────────────────────────────────────────────────┤
37
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
38
+ │ │ shapes │ │ text │ │ lines │ │
39
+ │ │ fillCircle │ │ fill │ │ line │ │
40
+ │ │ strokeRect │ │ stroke │ │ polyline │ │
41
+ │ │ arc │ │ measure │ │ bezier │ │
42
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
43
+ │ │
44
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
45
+ │ │ colors │ │ opacity │ │ effects │ │
46
+ │ │ fill │ │ push │ │ setBlendMode│ │
47
+ │ │ stroke │ │ pop │ │ shadow │ │
48
+ │ │ gradient │ │ current │ │ filter │ │
49
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
50
+ │ │
51
+ │ ┌─────────────┐ │
52
+ │ │ img │ │
53
+ │ │ draw │ │
54
+ │ │ crop │ │
55
+ │ │ pattern │ │
56
+ │ └─────────────┘ │
57
+ └─────────────────────────────────────────────────────────────┘
58
+ ```
59
+
60
+ ## Core API
61
+
62
+ ### Initialization
63
+
64
+ ```js
65
+ Painter.init(ctx); // Set the canvas context
66
+ const ctx = Painter.ctx; // Get the context
67
+ ```
68
+
69
+ ### Canvas State
70
+
71
+ ```js
72
+ Painter.save(); // Save canvas state
73
+ Painter.restore(); // Restore canvas state
74
+ Painter.clear(); // Clear entire canvas
75
+ Painter.clear(x, y, w, h); // Clear rectangle
76
+ ```
77
+
78
+ ### Transforms
79
+
80
+ ```js
81
+ Painter.translateTo(x, y); // Move origin
82
+ Painter.rotate(radians); // Rotate context
83
+ Painter.scale(sx, sy); // Scale context
84
+ ```
85
+
86
+ ## Sub-Modules
87
+
88
+ ### Painter.shapes
89
+
90
+ Drawing geometric shapes.
91
+
92
+ ```js
93
+ // Circles
94
+ Painter.shapes.fillCircle(x, y, radius, color);
95
+ Painter.shapes.strokeCircle(x, y, radius, color, lineWidth);
96
+
97
+ // Rectangles
98
+ Painter.shapes.fillRect(x, y, width, height, color);
99
+ Painter.shapes.strokeRect(x, y, width, height, color, lineWidth);
100
+
101
+ // Rounded rectangles
102
+ Painter.shapes.fillRoundedRect(x, y, w, h, radius, color);
103
+ Painter.shapes.strokeRoundedRect(x, y, w, h, radius, color, lineWidth);
104
+
105
+ // Arcs
106
+ Painter.shapes.arc(x, y, radius, startAngle, endAngle, color);
107
+ ```
108
+
109
+ ### Painter.text
110
+
111
+ Text rendering and measurement.
112
+
113
+ ```js
114
+ // Fill text
115
+ Painter.text.fill(text, x, y, {
116
+ font: '16px Arial',
117
+ color: 'white',
118
+ align: 'center', // left, center, right
119
+ baseline: 'middle' // top, middle, bottom
120
+ });
121
+
122
+ // Stroke text
123
+ Painter.text.stroke(text, x, y, {
124
+ font: '24px Arial',
125
+ color: 'black',
126
+ lineWidth: 2
127
+ });
128
+
129
+ // Measure text
130
+ const metrics = Painter.text.measure(text, font);
131
+ console.log(metrics.width);
132
+ ```
133
+
134
+ ### Painter.lines
135
+
136
+ Line and path drawing.
137
+
138
+ ```js
139
+ // Simple line
140
+ Painter.lines.line(x1, y1, x2, y2, color, lineWidth);
141
+
142
+ // Polyline
143
+ Painter.lines.polyline([
144
+ { x: 0, y: 0 },
145
+ { x: 100, y: 50 },
146
+ { x: 200, y: 25 }
147
+ ], color, lineWidth);
148
+
149
+ // Bezier curve
150
+ Painter.lines.bezier(
151
+ x1, y1, // Start
152
+ cp1x, cp1y, // Control point 1
153
+ cp2x, cp2y, // Control point 2
154
+ x2, y2, // End
155
+ color, lineWidth
156
+ );
157
+ ```
158
+
159
+ ### Painter.colors
160
+
161
+ Fill and stroke color management.
162
+
163
+ ```js
164
+ // Set fill style
165
+ Painter.colors.fill(color);
166
+
167
+ // Set stroke style
168
+ Painter.colors.stroke(color, lineWidth);
169
+
170
+ // Create gradient
171
+ const gradient = Painter.colors.linearGradient(x1, y1, x2, y2, stops);
172
+ // stops: [{ offset: 0, color: 'red' }, { offset: 1, color: 'blue' }]
173
+
174
+ const radial = Painter.colors.radialGradient(x, y, r1, r2, stops);
175
+ ```
176
+
177
+ ### Painter.opacity
178
+
179
+ Opacity stack management.
180
+
181
+ ```js
182
+ // Push new opacity (multiplies with current)
183
+ Painter.opacity.pushOpacity(0.5);
184
+
185
+ // Drawing here is 50% transparent
186
+
187
+ // Pop to restore previous
188
+ Painter.opacity.popOpacity();
189
+
190
+ // Get current effective opacity
191
+ const current = Painter.opacity.currentOpacity;
192
+ ```
193
+
194
+ ### Painter.effects
195
+
196
+ Visual effects.
197
+
198
+ ```js
199
+ // Blend modes
200
+ Painter.effects.setBlendMode('multiply');
201
+ Painter.effects.setBlendMode('screen');
202
+ Painter.effects.setBlendMode('source-over'); // default
203
+
204
+ // Shadow
205
+ Painter.effects.shadow(color, blur, offsetX, offsetY);
206
+ Painter.effects.clearShadow();
207
+ ```
208
+
209
+ ### Painter.img
210
+
211
+ Image drawing.
212
+
213
+ ```js
214
+ // Draw image
215
+ Painter.img.draw(image, x, y);
216
+ Painter.img.draw(image, x, y, width, height);
217
+
218
+ // Draw cropped
219
+ Painter.img.drawCropped(
220
+ image,
221
+ sx, sy, sw, sh, // Source rectangle
222
+ dx, dy, dw, dh // Destination rectangle
223
+ );
224
+
225
+ // Create pattern
226
+ const pattern = Painter.img.createPattern(image, 'repeat');
227
+ ```
228
+
229
+ ## Usage Patterns
230
+
231
+ ### Direct Drawing (No Shapes)
232
+
233
+ ```js
234
+ Painter.init(ctx);
235
+
236
+ function draw() {
237
+ Painter.clear();
238
+
239
+ // Draw background
240
+ Painter.shapes.fillRect(0, 0, 800, 600, '#1a1a2e');
241
+
242
+ // Draw shapes
243
+ Painter.shapes.fillCircle(400, 300, 50, 'red');
244
+
245
+ // Draw text
246
+ Painter.text.fill('Score: 100', 10, 30, {
247
+ font: '20px Arial',
248
+ color: 'white'
249
+ });
250
+ }
251
+ ```
252
+
253
+ ### With State Management
254
+
255
+ ```js
256
+ Painter.save();
257
+ Painter.translateTo(400, 300);
258
+ Painter.rotate(Math.PI / 4);
259
+ Painter.shapes.fillRect(-50, -25, 100, 50, 'blue');
260
+ Painter.restore();
261
+ ```
262
+
263
+ ### Opacity Stacking
264
+
265
+ ```js
266
+ Painter.opacity.pushOpacity(0.8);
267
+ // 80% opacity
268
+ Painter.shapes.fillCircle(100, 100, 50, 'red');
269
+
270
+ Painter.opacity.pushOpacity(0.5);
271
+ // 40% opacity (0.8 * 0.5)
272
+ Painter.shapes.fillCircle(150, 100, 50, 'blue');
273
+ Painter.opacity.popOpacity();
274
+
275
+ // Back to 80%
276
+ Painter.shapes.fillCircle(200, 100, 50, 'green');
277
+ Painter.opacity.popOpacity();
278
+ ```
279
+
280
+ ## When to Use Painter Directly
281
+
282
+ Most of the time, use Shape classes. Use Painter directly when:
283
+
284
+ - Drawing one-off graphics
285
+ - Custom shape implementations
286
+ - Performance-critical drawing
287
+ - Procedural graphics
288
+ - Effects not supported by shapes
289
+
290
+ ## Example: Custom Shape
291
+
292
+ ```js
293
+ class Diamond extends Shape {
294
+ draw() {
295
+ super.draw(); // Apply transforms
296
+
297
+ const hw = this.width / 2;
298
+ const hh = this.height / 2;
299
+
300
+ Painter.ctx.beginPath();
301
+ Painter.ctx.moveTo(0, -hh);
302
+ Painter.ctx.lineTo(hw, 0);
303
+ Painter.ctx.lineTo(0, hh);
304
+ Painter.ctx.lineTo(-hw, 0);
305
+ Painter.ctx.closePath();
306
+
307
+ if (this.color) {
308
+ Painter.colors.fill(this.color);
309
+ Painter.ctx.fill();
310
+ }
311
+
312
+ if (this.stroke) {
313
+ Painter.colors.stroke(this.stroke, this.lineWidth);
314
+ Painter.ctx.stroke();
315
+ }
316
+ }
317
+ }
318
+ ```
319
+
320
+ ## Related
321
+
322
+ - [Shapes Module](../shapes/README.md) - High-level shape classes
323
+ - [Rendering Pipeline](../../concepts/rendering-pipeline.md) - How shapes use Painter
324
+
325
+ ## See Also
326
+
327
+ - [Hello World](../../getting-started/hello-world.md)
328
+ - [Architecture Overview](../../concepts/architecture-overview.md)