@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.
- package/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- 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
|
+
}
|