@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,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)
|