@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,254 @@
1
+ import { Shape } from "./shape.js";
2
+ import { Painter } from "../painter/painter.js";
3
+
4
+ /**
5
+ * Cylinder - A 3D-looking isometric cylinder with rotation support.
6
+ *
7
+ * Supports:
8
+ * - Top, bottom and side faces with individual colors
9
+ * - Full bounding box
10
+ * - Face visibility control
11
+ * - Rotation around X, Y, and Z axes
12
+ * - Adjustable segment count for smoother curves
13
+ *
14
+ * Note: This is a 2.5D visual illusion — not actual 3D rendering.
15
+ */
16
+ export class Cylinder extends Shape {
17
+ /**
18
+ * Create a cylinder
19
+ * @param {number} radius - Radius of the cylinder
20
+ * @param {number} height - Height of the cylinder
21
+ * @param {object} options - Customization options
22
+ * @param {string} [options.topColor] - Color of the top face
23
+ * @param {string} [options.bottomColor] - Color of the bottom face
24
+ * @param {string} [options.sideColor] - Color of the side face
25
+ * @param {number} [options.segments] - Number of segments to approximate the curved surface
26
+ * @param {Array<string>} [options.visibleFaces] - Array of face keys to render
27
+ * @param {string} [options.stroke] - Optional stroke around each face
28
+ * @param {number} [options.lineWidth] - Stroke width
29
+ * @param {number} [options.rotationX] - Rotation around X axis in radians
30
+ * @param {number} [options.rotationY] - Rotation around Y axis in radians
31
+ * @param {number} [options.rotationZ] - Rotation around Z axis in radians
32
+ */
33
+ constructor(radius = 40, options = {}) {
34
+ super(options);
35
+ this.radius = radius;
36
+ this.height = options.height || 80;
37
+
38
+ // Number of segments used to approximate the circle
39
+ this.segments = options.segments || 24;
40
+
41
+ // Colors for each face
42
+ this.topColor = options.topColor || "#FF00FF";
43
+ this.bottomColor = options.bottomColor || "#FF0FFF";
44
+ this.sideColor = options.sideColor || "#00FF00";
45
+
46
+ this.stroke = options.stroke || "#000000";
47
+ this.lineWidth = options.lineWidth || 1;
48
+
49
+ // Rotation angles (in radians)
50
+ this.rotationX = options.rotationX || 0;
51
+ this.rotationY = options.rotationY || 0;
52
+ this.rotationZ = options.rotationZ || 0;
53
+
54
+ /** @type {Array<'top'|'bottom'|'side'>} */
55
+ this.visibleFaces = options.visibleFaces || ["top", "bottom", "side"];
56
+ }
57
+
58
+ /**
59
+ * Set rotation angles
60
+ * @param {number} x - Rotation around X axis in radians
61
+ * @param {number} y - Rotation around Y axis in radians
62
+ * @param {number} z - Rotation around Z axis in radians
63
+ */
64
+ setRotation(x, y, z) {
65
+ this.rotationX = x;
66
+ this.rotationY = y;
67
+ this.rotationZ = z;
68
+ return this; // Enable method chaining
69
+ }
70
+
71
+ /**
72
+ * Rotate the cylinder incrementally
73
+ * @param {number} x - Increment for X rotation in radians
74
+ * @param {number} y - Increment for Y rotation in radians
75
+ * @param {number} z - Increment for Z rotation in radians
76
+ */
77
+ rotate(x, y, z) {
78
+ this.rotationX += x;
79
+ this.rotationY += y;
80
+ this.rotationZ += z;
81
+ return this; // Enable method chaining
82
+ }
83
+
84
+ /**
85
+ * Internal draw logic
86
+ */
87
+ draw() {
88
+ super.draw();
89
+ const r = this.radius;
90
+ const h = this.height / 2; // Half height for positioning
91
+
92
+ /**
93
+ * Apply 3D rotation to a point
94
+ * @param {number} x
95
+ * @param {number} y
96
+ * @param {number} z
97
+ * @returns {{x: number, y: number, z: number}}
98
+ */
99
+ const rotate3D = (x, y, z) => {
100
+ // Apply X-axis rotation
101
+ let y1 = y;
102
+ let z1 = z;
103
+ y = y1 * Math.cos(this.rotationX) - z1 * Math.sin(this.rotationX);
104
+ z = y1 * Math.sin(this.rotationX) + z1 * Math.cos(this.rotationX);
105
+
106
+ // Apply Y-axis rotation
107
+ let x1 = x;
108
+ z1 = z;
109
+ x = x1 * Math.cos(this.rotationY) + z1 * Math.sin(this.rotationY);
110
+ z = -x1 * Math.sin(this.rotationY) + z1 * Math.cos(this.rotationY);
111
+
112
+ // Apply Z-axis rotation
113
+ x1 = x;
114
+ y1 = y;
115
+ x = x1 * Math.cos(this.rotationZ) - y1 * Math.sin(this.rotationZ);
116
+ y = x1 * Math.sin(this.rotationZ) + y1 * Math.cos(this.rotationZ);
117
+
118
+ return { x, y, z };
119
+ };
120
+
121
+ /**
122
+ * Isometric projection of 3D point
123
+ * @param {number} x
124
+ * @param {number} y
125
+ * @param {number} z
126
+ * @returns {{x: number, y: number, z: number}}
127
+ */
128
+ const iso = (x, y, z) => {
129
+ // Apply rotations first
130
+ const rotated = rotate3D(x, y, z);
131
+
132
+ // Then apply isometric projection
133
+ const isoX = (rotated.x - rotated.y) * Math.cos(Math.PI / 6);
134
+ const isoY = (rotated.x + rotated.y) * Math.sin(Math.PI / 6) - rotated.z;
135
+ return { x: isoX, y: isoY, z: rotated.z }; // Include z for depth sorting
136
+ };
137
+
138
+ // Generate points for top and bottom circles
139
+ const topPoints = [];
140
+ const bottomPoints = [];
141
+
142
+ // Calculate segment angle
143
+ const angleStep = (Math.PI * 2) / this.segments;
144
+
145
+ // Generate circle points
146
+ for (let i = 0; i < this.segments; i++) {
147
+ const angle = i * angleStep;
148
+ const x = Math.cos(angle) * r;
149
+ const y = Math.sin(angle) * r;
150
+
151
+ // Project 3D points to 2D
152
+ topPoints.push(iso(x, y, h));
153
+ bottomPoints.push(iso(x, y, -h));
154
+ }
155
+
156
+ // Create side faces (quads between top and bottom points)
157
+ const sideFaces = [];
158
+ for (let i = 0; i < this.segments; i++) {
159
+ const nextIdx = (i + 1) % this.segments;
160
+ sideFaces.push({
161
+ points: [
162
+ bottomPoints[i],
163
+ bottomPoints[nextIdx],
164
+ topPoints[nextIdx],
165
+ topPoints[i],
166
+ ],
167
+ // Each segment gets its own depth for proper sorting
168
+ z:
169
+ (topPoints[i].z +
170
+ topPoints[nextIdx].z +
171
+ bottomPoints[i].z +
172
+ bottomPoints[nextIdx].z) /
173
+ 4,
174
+ });
175
+ }
176
+
177
+ // Prepare faces for depth sorting
178
+ const facesWithDepth = [];
179
+
180
+ // Add top face if visible
181
+ if (this.visibleFaces.includes("top")) {
182
+ facesWithDepth.push({
183
+ type: "top",
184
+ points: topPoints,
185
+ z: h, // Average Z of top face
186
+ });
187
+ }
188
+
189
+ // Add bottom face if visible
190
+ if (this.visibleFaces.includes("bottom")) {
191
+ facesWithDepth.push({
192
+ type: "bottom",
193
+ points: [...bottomPoints].reverse(), // Reverse for correct winding
194
+ z: -h, // Average Z of bottom face
195
+ });
196
+ }
197
+
198
+ // Add side faces if visible
199
+ if (this.visibleFaces.includes("side")) {
200
+ facesWithDepth.push(
201
+ ...sideFaces.map((face) => ({
202
+ type: "side",
203
+ points: face.points,
204
+ z: face.z,
205
+ }))
206
+ );
207
+ }
208
+
209
+ // Sort faces by depth (back to front)
210
+ facesWithDepth.sort((a, b) => b.z - a.z);
211
+
212
+ // Draw faces in depth order
213
+ for (const face of facesWithDepth) {
214
+ let color;
215
+
216
+ switch (face.type) {
217
+ case "top":
218
+ color = this.topColor;
219
+ break;
220
+ case "bottom":
221
+ color = this.bottomColor;
222
+ break;
223
+ case "side":
224
+ color = this.sideColor;
225
+ break;
226
+ }
227
+
228
+ Painter.shapes.polygon(
229
+ face.points,
230
+ color,
231
+ this.stroke,
232
+ this.lineWidth
233
+ );
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Compute bounding box for interactivity and layout
239
+ * @returns {{x: number, y: number, width: number, height: number}}
240
+ */
241
+ getBounds() {
242
+ // Calculate actual bounds based on isometric projection
243
+ const projectionFactor = 1.5; // Approximation for isometric projection
244
+ const maxDimension = Math.max(this.radius * 2, this.height);
245
+ const adjustedSize = maxDimension * projectionFactor;
246
+
247
+ return {
248
+ x: this.x - adjustedSize / 2,
249
+ y: this.y - adjustedSize / 2,
250
+ width: adjustedSize,
251
+ height: adjustedSize,
252
+ };
253
+ }
254
+ }
@@ -0,0 +1,48 @@
1
+ // Diamond.js
2
+ import { Shape } from "./shape.js";
3
+ import { Painter } from "../painter/painter.js";
4
+ /**
5
+ * Diamond - A drawable diamond-shaped canvas primitive.
6
+ *
7
+ * Draws a centered diamond using four points around its bounds.
8
+ * Uses Painter's polygon rendering under the hood.
9
+ *
10
+ * Limitations:
11
+ * - Not interactive or animated
12
+ * - For visual use only (wrap in a GameObject for dynamic behavior)
13
+ */
14
+ export class Diamond extends Shape {
15
+ /**
16
+ * @param {number} x - Center X position
17
+ * @param {number} y - Center Y position
18
+ * @param {number} width - Total width of the diamond
19
+ * @param {number} height - Total height of the diamond
20
+ * @param {Object} [options] - Shape rendering options
21
+ */
22
+ constructor(options = {}) {
23
+ super(options);
24
+ }
25
+
26
+ /**
27
+ * Renders the diamond using four corner points.
28
+ */
29
+ draw() {
30
+ super.draw();
31
+ const halfW = this.width / 2;
32
+ const halfH = this.height / 2;
33
+
34
+ const points = [
35
+ { x: 0, y: -halfH }, // Top
36
+ { x: halfW, y: 0 }, // Right
37
+ { x: 0, y: halfH }, // Bottom
38
+ { x: -halfW, y: 0 }, // Left
39
+ ];
40
+
41
+ Painter.shapes.polygon(
42
+ points,
43
+ this.color,
44
+ this.stroke,
45
+ this.lineWidth
46
+ );
47
+ }
48
+ }
@@ -0,0 +1,111 @@
1
+ import { Loggable } from "../logger/loggable";
2
+ import { Painter } from "../painter/painter";
3
+ /**
4
+ * Euclidian
5
+ * ----------
6
+ *
7
+ * The root of all drawable objects in the gcanvas engine.
8
+ *
9
+ * This class defines the fundamental spatial contract:
10
+ * - A 2D position in Euclidean space (`x`, `y`)
11
+ * - A size (`width`, `height`)
12
+ * - Optional debug rendering via `draw()` and `drawDebug()`
13
+ *
14
+ * ### Why This Exists
15
+ *
16
+ * Before something becomes a shape, a renderable, or a transformable object,
17
+ * it first **exists in space**. This class introduces that concept of space,
18
+ * without yet concerning itself with rendering pipelines, transformations,
19
+ * or bounding box calculations.
20
+ *
21
+ * ### Core Characteristics
22
+ *
23
+ * - Provides visual presence on the canvas by exposing spatial dimensions
24
+ * - Supports debug drawing for bounding boxes (green by default)
25
+ * - Serves as the base class for all shape, UI, and layout components
26
+ *
27
+ * This class is intended to be subclassed and never used directly.
28
+ *
29
+ * @abstract
30
+ * @extends Loggable
31
+ */
32
+ export class Euclidian extends Loggable {
33
+ /**
34
+ * @param {Object} [options={}]
35
+ * @param {number} [options.x=0] - X position (center-based)
36
+ * @param {number} [options.y=0] - Y position (center-based)
37
+ * @param {number} [options.width=0] - Width in pixels
38
+ * @param {number} [options.height=0] - Height in pixels
39
+ * @param {boolean} [options.debug=false] - Enables visual debug overlay
40
+ * @param {string} [options.debugColor="#0f0"] - Outline color for debug box
41
+ */
42
+ constructor(options = {}) {
43
+ super(options);
44
+ this._x = typeof options.x === "number" ? options.x : 0;
45
+ this._y = typeof options.y === "number" ? options.y : 0;
46
+ this._width = typeof options.width === "number" ? options.width : 0;
47
+ this._height = typeof options.height === "number" ? options.height : 0;
48
+ this.logger.log("Euclidian", this._x, this._y, this._width, this._height);
49
+ }
50
+
51
+ /** @type {number} X center position in canvas space */
52
+ get x() {
53
+ return this._x;
54
+ }
55
+ set x(v) {
56
+ this.validateProp(v, "x");
57
+ this._x = v;
58
+ }
59
+
60
+ /** @type {number} Y center position in canvas space */
61
+ get y() {
62
+ return this._y;
63
+ }
64
+ set y(v) {
65
+ this.validateProp(v, "y");
66
+ this._y = v;
67
+ }
68
+
69
+ /** @type {number} Width of the object (must be ≥ 0) */
70
+ get width() {
71
+ return this._width;
72
+ }
73
+ set width(v) {
74
+ this.validateProp(v, "width");
75
+ this._width = Math.max(0, v);
76
+ }
77
+
78
+ /** @type {number} Height of the object (must be ≥ 0) */
79
+ get height() {
80
+ return this._height;
81
+ }
82
+ set height(v) {
83
+ this.validateProp(v, "height");
84
+ this._height = Math.max(0, v);
85
+ }
86
+
87
+ /** @type {boolean} Whether to draw the debug box outline */
88
+ get debug() {
89
+ return this._debug;
90
+ }
91
+ set debug(v) {
92
+ this.validateProp(v, "debug");
93
+ this._debug = Boolean(v);
94
+ }
95
+
96
+ /** @type {string} Color of the debug box (e.g. "#0f0" or "red") */
97
+ get debugColor() {
98
+ return this._debugColor;
99
+ }
100
+ set debugColor(v) {
101
+ this.validateProp(v, "debugColor");
102
+ this._debugColor = v;
103
+ }
104
+
105
+ validateProp(v, prop) {
106
+ //this.trace("Euclidian.validateProp " + prop + " = " + v);
107
+ if (v === undefined || v === null) {
108
+ throw new Error("Invalid property value: " + prop + " " + v);
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,115 @@
1
+ import { Shape } from "./shape.js";
2
+ import { Painter } from "../painter/painter.js";
3
+
4
+ /**
5
+ * StickFigure - A simple humanoid stick figure composed of circles and lines.
6
+ * Good for demos, games, and jokes about bad posture.
7
+ */
8
+ export class StickFigure extends Shape {
9
+ /**
10
+ * @param {number} x - X position (center of figure)
11
+ * @param {number} y - Y position (center of figure)
12
+ * @param {number} scale - Scale multiplier for size
13
+ * @param {object} options - Style options
14
+ * @param {string} [options.stroke="#000"] - Line color
15
+ * @param {string} [options.headColor] - Fill color of the head
16
+ * @param {string} [options.jointColor] - Fill color of joint circles
17
+ * @param {number} [options.lineWidth=2] - Line width
18
+ * @param {boolean} [options.showJoints=true] - Whether to draw joints
19
+ */
20
+ constructor(scale = 1, options = {}) {
21
+ super(options);
22
+ this.scale = scale;
23
+
24
+ this.stroke = options.stroke || "#000";
25
+ this.headColor = options.headColor || this.stroke;
26
+ this.jointColor = options.jointColor || this.stroke;
27
+ this.lineWidth = options.lineWidth || 2;
28
+ this.showJoints = options.showJoints !== false; // default to true
29
+ }
30
+
31
+ draw() {
32
+ super.draw();
33
+ const s = this.scale;
34
+ // Layout
35
+ const headR = 10 * s;
36
+ const headCenterY = -30 * s;
37
+ const neckY = headCenterY + headR;
38
+ const torsoTop = neckY;
39
+ const torsoBottom = torsoTop + 40 * s;
40
+ const armY = torsoTop + 10 * s;
41
+ const shoulderX = 15 * s;
42
+ const hipX = 10 * s;
43
+ const legY = torsoBottom + 40 * s;
44
+ const jointR = 3 * s;
45
+ // Head
46
+ Painter.shapes.fillCircle(0, headCenterY, headR, this.headColor);
47
+ Painter.shapes.strokeCircle(
48
+ 0,
49
+ headCenterY,
50
+ headR,
51
+ this.stroke,
52
+ this.lineWidth
53
+ );
54
+ // Torso
55
+ Painter.lines.line(
56
+ 0,
57
+ torsoTop,
58
+ 0,
59
+ torsoBottom,
60
+ this.stroke,
61
+ this.lineWidth
62
+ );
63
+ // Arms
64
+ Painter.lines.line(
65
+ -shoulderX,
66
+ armY,
67
+ shoulderX,
68
+ armY,
69
+ this.stroke,
70
+ this.lineWidth
71
+ );
72
+ // Legs
73
+ Painter.lines.line(
74
+ 0,
75
+ torsoBottom,
76
+ -hipX,
77
+ legY,
78
+ this.stroke,
79
+ this.lineWidth
80
+ );
81
+ Painter.lines.line(
82
+ 0,
83
+ torsoBottom,
84
+ hipX,
85
+ legY,
86
+ this.stroke,
87
+ this.lineWidth
88
+ );
89
+ // Joints (optional)
90
+ if (this.showJoints) {
91
+ const joints = [
92
+ [0, torsoTop],
93
+ [-shoulderX, armY],
94
+ [shoulderX, armY],
95
+ [0, torsoBottom],
96
+ [-hipX, legY],
97
+ [hipX, legY],
98
+ ];
99
+ joints.forEach(([jx, jy]) =>
100
+ Painter.shapes.fillCircle(jx, jy, jointR, this.jointColor)
101
+ );
102
+ }
103
+ }
104
+
105
+ getBounds() {
106
+ const h = 100 * this.scale;
107
+ const w = 40 * this.scale;
108
+ return {
109
+ x: this.x,
110
+ y: this.y,
111
+ width: w,
112
+ height: h,
113
+ };
114
+ }
115
+ }