@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,295 @@
|
|
|
1
|
+
import { Logger } from "../logger/logger";
|
|
2
|
+
import { PainterColors } from "./painter.colors";
|
|
3
|
+
import { PainterEffects } from "./painter.effects";
|
|
4
|
+
import { PainterImages } from "./painter.img";
|
|
5
|
+
import { PainterLines } from "./painter.lines";
|
|
6
|
+
import { PainterOpacity } from "./painter.opacity";
|
|
7
|
+
import { PainterShapes } from "./painter.shapes";
|
|
8
|
+
import { PainterText } from "./painter.text";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Painter - Static utility class for all canvas drawing operations
|
|
12
|
+
* Provides a cleaner API for common canvas operations with additional utilities
|
|
13
|
+
*/
|
|
14
|
+
export class Painter {
|
|
15
|
+
static #_colors = null;
|
|
16
|
+
static #_effects = null;
|
|
17
|
+
static #_img = null;
|
|
18
|
+
static #_lines = null;
|
|
19
|
+
static #_opacity = null;
|
|
20
|
+
static #_shapes = null;
|
|
21
|
+
static #_text = null;
|
|
22
|
+
|
|
23
|
+
static #checkInitialized(propName, value) {
|
|
24
|
+
if (!value) {
|
|
25
|
+
throw new Error(`Painter.${propName} is not initialized. Call Painter.init(ctx) first.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @type {PainterColors}
|
|
31
|
+
*/
|
|
32
|
+
static get colors() {
|
|
33
|
+
this.#checkInitialized('colors', this.#_colors);
|
|
34
|
+
return this.#_colors;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @type {PainterEffects}
|
|
38
|
+
*/
|
|
39
|
+
static get effects() {
|
|
40
|
+
this.#checkInitialized('effects', this.#_effects);
|
|
41
|
+
return this.#_effects;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* @type {PainterImages}
|
|
45
|
+
*/
|
|
46
|
+
static get img() {
|
|
47
|
+
this.#checkInitialized('img', this.#_img);
|
|
48
|
+
return this.#_img;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @type {PainterLines}
|
|
52
|
+
*/
|
|
53
|
+
static get lines() {
|
|
54
|
+
this.#checkInitialized('lines', this.#_lines);
|
|
55
|
+
return this.#_lines;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @type {PainterOpacity}
|
|
59
|
+
*/
|
|
60
|
+
static get opacity() {
|
|
61
|
+
this.#checkInitialized('opacity', this.#_opacity);
|
|
62
|
+
return this.#_opacity;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* @type {PainterShapes}
|
|
66
|
+
*/
|
|
67
|
+
static get shapes() {
|
|
68
|
+
this.#checkInitialized('shapes', this.#_shapes);
|
|
69
|
+
return this.#_shapes;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @type {PainterText}
|
|
73
|
+
*/
|
|
74
|
+
static get text() {
|
|
75
|
+
this.#checkInitialized('text', this.#_text);
|
|
76
|
+
return this.#_text;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @type {Logger}
|
|
80
|
+
*/
|
|
81
|
+
static logger;
|
|
82
|
+
|
|
83
|
+
static set ctx(ctx) {
|
|
84
|
+
this._ctx = ctx;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @type {CanvasRenderingContext2D}
|
|
89
|
+
*/
|
|
90
|
+
static get ctx() {
|
|
91
|
+
if (!this._ctx) {
|
|
92
|
+
throw new Error("Cannot access Painter.ctx before initialization!");
|
|
93
|
+
}
|
|
94
|
+
return this._ctx;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Initialize the painter with a canvas context
|
|
99
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas context
|
|
100
|
+
*/
|
|
101
|
+
static init(ctx) {
|
|
102
|
+
this._ctx = ctx;
|
|
103
|
+
this.saveStack = [];
|
|
104
|
+
this.#_colors = PainterColors;
|
|
105
|
+
this.#_effects = PainterEffects;
|
|
106
|
+
this.#_img = PainterImages;
|
|
107
|
+
this.#_lines = PainterLines;
|
|
108
|
+
this.#_opacity = PainterOpacity;
|
|
109
|
+
this.#_shapes = PainterShapes;
|
|
110
|
+
this.#_text = PainterText;
|
|
111
|
+
Painter.logger = Logger.getLogger("Painter");
|
|
112
|
+
Painter.saveStack = [];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Switch the painter to a different canvas context.
|
|
117
|
+
* Used when multiple games/canvases exist on the same page.
|
|
118
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas context to use
|
|
119
|
+
*/
|
|
120
|
+
static setContext(ctx) {
|
|
121
|
+
this._ctx = ctx;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static save() {
|
|
125
|
+
// Extract just the method name from the stack trace
|
|
126
|
+
const stackLine = new Error().stack.split("\n")[2] || "";
|
|
127
|
+
const methodMatch = stackLine.match(/at\s+(\w+)\.(\w+)/);
|
|
128
|
+
const callerInfo = methodMatch
|
|
129
|
+
? `${methodMatch[1]}.${methodMatch[2]}`
|
|
130
|
+
: "unknown";
|
|
131
|
+
|
|
132
|
+
// Add to tracking stack
|
|
133
|
+
this.saveStack.push(callerInfo);
|
|
134
|
+
|
|
135
|
+
// Log with caller info
|
|
136
|
+
this.logger.log(`Painter.save() by: ${callerInfo}`);
|
|
137
|
+
|
|
138
|
+
// Perform actual save
|
|
139
|
+
this.ctx.save();
|
|
140
|
+
Painter.opacity.saveOpacityState();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static restore() {
|
|
144
|
+
// Check for stack underflow
|
|
145
|
+
if (this.saveStack.length === 0) {
|
|
146
|
+
console.error("PAINTER ERROR: restore() without matching save()!");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Get the matching caller from stack
|
|
151
|
+
const caller = this.saveStack.pop();
|
|
152
|
+
|
|
153
|
+
// Log with caller info
|
|
154
|
+
this.logger.log(`Painter.restore() balancing save from: ${caller}`);
|
|
155
|
+
|
|
156
|
+
// Perform actual restore
|
|
157
|
+
// Note: ctx.restore() already restores fillStyle/strokeStyle from the save stack.
|
|
158
|
+
// Do NOT set fillStyle/strokeStyle to null here - it breaks subsequent fills.
|
|
159
|
+
this.ctx.restore();
|
|
160
|
+
Painter.opacity.restoreOpacityState();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static translateTo(x, y) {
|
|
164
|
+
// make sure x a valid number not NaN or undefined. default to 0 if so
|
|
165
|
+
if (isNaN(x) || x === undefined) {
|
|
166
|
+
x = 0;
|
|
167
|
+
}
|
|
168
|
+
if (isNaN(y) || y === undefined) {
|
|
169
|
+
y = 0;
|
|
170
|
+
}
|
|
171
|
+
this.logger.log("moveTo", x, y);
|
|
172
|
+
this.ctx.translate(x, y);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static resetPosition() {
|
|
176
|
+
// Reset just the translation portion of the transform
|
|
177
|
+
this.logger.log("resetPosition");
|
|
178
|
+
const transform = this.ctx.getTransform();
|
|
179
|
+
this.ctx.setTransform(
|
|
180
|
+
transform.a,
|
|
181
|
+
transform.b,
|
|
182
|
+
transform.c,
|
|
183
|
+
transform.d,
|
|
184
|
+
0,
|
|
185
|
+
0
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static withPosition(x, y, callback) {
|
|
190
|
+
this.logger.log("withPosition", x, y);
|
|
191
|
+
this.save();
|
|
192
|
+
this.translateTo(x, y);
|
|
193
|
+
callback();
|
|
194
|
+
this.restore();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Clear the entire canvas or a specific rectangle
|
|
199
|
+
* @param {number} [x=0] - X coordinate
|
|
200
|
+
* @param {number} [y=0] - Y coordinate
|
|
201
|
+
* @param {number} [width=canvas.width] - Width
|
|
202
|
+
* @param {number} [height=canvas.height] - Height
|
|
203
|
+
* @returns {void}
|
|
204
|
+
*/
|
|
205
|
+
static clear(
|
|
206
|
+
x = 0,
|
|
207
|
+
y = 0,
|
|
208
|
+
width = Painter.ctx.canvas.width,
|
|
209
|
+
height = Painter.ctx.canvas.height
|
|
210
|
+
) {
|
|
211
|
+
Painter.ctx.clearRect(x, y, width, height);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Translate the canvas
|
|
216
|
+
* @param {number} x - X translation
|
|
217
|
+
* @param {number} y - Y translation
|
|
218
|
+
* @returns {void}
|
|
219
|
+
*/
|
|
220
|
+
static translate(x, y) {
|
|
221
|
+
Painter.ctx.translate(x, y);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Rotate the canvas
|
|
226
|
+
* @param {number} angle - Rotation angle in radians
|
|
227
|
+
* @returns {void}
|
|
228
|
+
*/
|
|
229
|
+
static rotate(angle) {
|
|
230
|
+
Painter.logger.log("Painter.rotate", angle);
|
|
231
|
+
Painter.ctx.rotate(angle);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Scale the canvas
|
|
236
|
+
* @param {number} x - X scale factor
|
|
237
|
+
* @param {number} y - Y scale factor
|
|
238
|
+
* @returns {void}
|
|
239
|
+
*/
|
|
240
|
+
static scale(x, y) {
|
|
241
|
+
Painter.logger.log("Painter.scale", x, y);
|
|
242
|
+
Painter.ctx.scale(x, y);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Safely use the canvas context for direct drawing operations.
|
|
247
|
+
* Automatically handles path cleanup to prevent fill/stroke bleeding
|
|
248
|
+
* between different drawing operations.
|
|
249
|
+
*
|
|
250
|
+
* @param {function(CanvasRenderingContext2D): void} callback - Function that receives ctx
|
|
251
|
+
* @param {object} [options={}] - Options for context handling
|
|
252
|
+
* @param {boolean} [options.saveState=false] - Whether to save/restore full canvas state
|
|
253
|
+
* @returns {void}
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* // Basic usage - draws a custom path with automatic cleanup
|
|
257
|
+
* Painter.useCtx((ctx) => {
|
|
258
|
+
* ctx.strokeStyle = "white";
|
|
259
|
+
* ctx.lineWidth = 2;
|
|
260
|
+
* ctx.moveTo(0, 0);
|
|
261
|
+
* ctx.lineTo(100, 100);
|
|
262
|
+
* ctx.stroke();
|
|
263
|
+
* });
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* // With state preservation - restores all canvas state after
|
|
267
|
+
* Painter.useCtx((ctx) => {
|
|
268
|
+
* ctx.fillStyle = "red";
|
|
269
|
+
* ctx.fillRect(0, 0, 50, 50);
|
|
270
|
+
* }, { saveState: true });
|
|
271
|
+
*/
|
|
272
|
+
static useCtx(callback, options = {}) {
|
|
273
|
+
const ctx = this.ctx;
|
|
274
|
+
const { saveState = false } = options;
|
|
275
|
+
|
|
276
|
+
// Optionally save full canvas state
|
|
277
|
+
if (saveState) {
|
|
278
|
+
this.save();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Start with a fresh path
|
|
282
|
+
ctx.beginPath();
|
|
283
|
+
|
|
284
|
+
// Execute user's drawing code
|
|
285
|
+
callback(ctx);
|
|
286
|
+
|
|
287
|
+
// Clear path to prevent bleeding into subsequent renders
|
|
288
|
+
ctx.beginPath();
|
|
289
|
+
|
|
290
|
+
// Optionally restore full canvas state
|
|
291
|
+
if (saveState) {
|
|
292
|
+
this.restore();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Painter } from "./painter";
|
|
2
|
+
|
|
3
|
+
export class PainterLines {
|
|
4
|
+
static path(commands, fill, stroke, lineWidth = 1) {
|
|
5
|
+
const ctx = Painter.ctx;
|
|
6
|
+
ctx.beginPath();
|
|
7
|
+
for (const cmd of commands) {
|
|
8
|
+
const [type, ...args] = cmd;
|
|
9
|
+
if (type === "M") ctx.moveTo(...args);
|
|
10
|
+
else if (type === "L") ctx.lineTo(...args);
|
|
11
|
+
else if (type === "C") ctx.bezierCurveTo(...args);
|
|
12
|
+
else if (type === "Q") ctx.quadraticCurveTo(...args);
|
|
13
|
+
else if (type === "Z") ctx.closePath();
|
|
14
|
+
}
|
|
15
|
+
if (fill) {
|
|
16
|
+
ctx.fillStyle = fill;
|
|
17
|
+
Painter.colors.fill(fill);
|
|
18
|
+
}
|
|
19
|
+
if (stroke) {
|
|
20
|
+
ctx.strokeStyle = stroke;
|
|
21
|
+
ctx.lineWidth = lineWidth;
|
|
22
|
+
Painter.colors.stroke();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Draw a line
|
|
28
|
+
* @param {number} x1 - Start X
|
|
29
|
+
* @param {number} y1 - Start Y
|
|
30
|
+
* @param {number} x2 - End X
|
|
31
|
+
* @param {number} y2 - End Y
|
|
32
|
+
* @param {string|CanvasGradient} [color] - Line color
|
|
33
|
+
* @param {number} [lineWidth] - Line width
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*/
|
|
36
|
+
static line(x1, y1, x2, y2, color, lineWidth) {
|
|
37
|
+
Painter.ctx.beginPath();
|
|
38
|
+
Painter.ctx.moveTo(x1, y1);
|
|
39
|
+
Painter.ctx.lineTo(x2, y2);
|
|
40
|
+
Painter.colors.stroke(color, lineWidth);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Start a new path
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
static beginPath() {
|
|
48
|
+
Painter.ctx.beginPath();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Close the current path
|
|
53
|
+
* @returns {void}
|
|
54
|
+
*/
|
|
55
|
+
static closePath() {
|
|
56
|
+
Painter.ctx.closePath();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Move to a point without drawing
|
|
61
|
+
* @param {number} x - X coordinate
|
|
62
|
+
* @param {number} y - Y coordinate
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
static moveTo(x, y) {
|
|
66
|
+
Painter.ctx.moveTo(x, y);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Draw a line to a point
|
|
71
|
+
* @param {number} x - X coordinate
|
|
72
|
+
* @param {number} y - Y coordinate
|
|
73
|
+
* @returns {void}
|
|
74
|
+
*/
|
|
75
|
+
static lineTo(x, y) {
|
|
76
|
+
Painter.ctx.lineTo(x, y);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Add a bezier curve to the current path
|
|
81
|
+
* @param {number} cp1x - Control point 1 X
|
|
82
|
+
* @param {number} cp1y - Control point 1 Y
|
|
83
|
+
* @param {number} cp2x - Control point 2 X
|
|
84
|
+
* @param {number} cp2y - Control point 2 Y
|
|
85
|
+
* @param {number} x - End X
|
|
86
|
+
* @param {number} y - End Y
|
|
87
|
+
* @returns {void}
|
|
88
|
+
*/
|
|
89
|
+
static bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
90
|
+
Painter.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Draw a dashed line
|
|
95
|
+
* @param {number} x1 - Start X
|
|
96
|
+
* @param {number} y1 - Start Y
|
|
97
|
+
* @param {number} x2 - End X
|
|
98
|
+
* @param {number} y2 - End Y
|
|
99
|
+
* @param {Array<number>} dash - Dash pattern array [dash, gap, dash, gap, ...]
|
|
100
|
+
* @param {string|CanvasGradient} [color] - Line color
|
|
101
|
+
* @param {number} [lineWidth] - Line width
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
static dashedLine(x1, y1, x2, y2, dash, color, lineWidth) {
|
|
105
|
+
Painter.ctx.beginPath();
|
|
106
|
+
if (color) Painter.ctx.strokeStyle = color;
|
|
107
|
+
if (lineWidth !== undefined) Painter.ctx.lineWidth = lineWidth;
|
|
108
|
+
|
|
109
|
+
// Set the dash pattern
|
|
110
|
+
Painter.ctx.setLineDash(dash);
|
|
111
|
+
|
|
112
|
+
Painter.ctx.moveTo(x1, y1);
|
|
113
|
+
Painter.ctx.lineTo(x2, y2);
|
|
114
|
+
Painter.colors.stroke();
|
|
115
|
+
|
|
116
|
+
// Reset the dash pattern
|
|
117
|
+
Painter.ctx.setLineDash([]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Draw a dotted line
|
|
122
|
+
* @param {number} x1 - Start X
|
|
123
|
+
* @param {number} y1 - Start Y
|
|
124
|
+
* @param {number} x2 - End X
|
|
125
|
+
* @param {number} y2 - End Y
|
|
126
|
+
* @param {number} dotSize - Size of dots
|
|
127
|
+
* @param {number} gap - Gap between dots
|
|
128
|
+
* @param {string|CanvasGradient} [color] - Line color
|
|
129
|
+
* @returns {void}
|
|
130
|
+
*/
|
|
131
|
+
static dottedLine(x1, y1, x2, y2, dotSize = 2, gap = 5, color) {
|
|
132
|
+
return Painter.lines.dashedLine(
|
|
133
|
+
x1,
|
|
134
|
+
y1,
|
|
135
|
+
x2,
|
|
136
|
+
y2,
|
|
137
|
+
[dotSize, gap],
|
|
138
|
+
color,
|
|
139
|
+
dotSize
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Set line dash pattern
|
|
145
|
+
* @param {Array<number>} segments - Array of line, gap lengths
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
148
|
+
static setLineDash(segments) {
|
|
149
|
+
Painter.ctx.setLineDash(segments);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Reset line dash to solid line
|
|
154
|
+
* @returns {void}
|
|
155
|
+
*/
|
|
156
|
+
static resetLineDash() {
|
|
157
|
+
Painter.ctx.setLineDash([]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Set line width
|
|
162
|
+
* @param {number} width - Line width
|
|
163
|
+
* @returns {void}
|
|
164
|
+
*/
|
|
165
|
+
static setLineWidth(width) {
|
|
166
|
+
Painter.ctx.lineWidth = width;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Draw quadratic curve
|
|
171
|
+
* @param {number} x1 - Start X
|
|
172
|
+
* @param {number} y1 - Start Y
|
|
173
|
+
* @param {number} cpx - Control point X
|
|
174
|
+
* @param {number} cpy - Control point Y
|
|
175
|
+
* @param {number} x2 - End X
|
|
176
|
+
* @param {number} y2 - End Y
|
|
177
|
+
* @param {string|CanvasGradient} [color] - Stroke color
|
|
178
|
+
* @param {number} [lineWidth] - Line width
|
|
179
|
+
* @returns {void}
|
|
180
|
+
*/
|
|
181
|
+
static quadraticCurve(x1, y1, cpx, cpy, x2, y2, color, lineWidth) {
|
|
182
|
+
Painter.ctx.beginPath();
|
|
183
|
+
Painter.ctx.moveTo(x1, y1);
|
|
184
|
+
Painter.ctx.quadraticCurveTo(cpx, cpy, x2, y2);
|
|
185
|
+
if (color) Painter.ctx.strokeStyle = color;
|
|
186
|
+
if (lineWidth !== undefined) Painter.ctx.lineWidth = lineWidth;
|
|
187
|
+
Painter.colors.stroke();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Painter } from "./painter";
|
|
2
|
+
|
|
3
|
+
export class PainterOpacity {
|
|
4
|
+
static _opacityStack = [1]; // Initialize with full opacity
|
|
5
|
+
|
|
6
|
+
static pushOpacity(opacity) {
|
|
7
|
+
// Compute compound opacity (multiply with current)
|
|
8
|
+
const currentOpacity = this._opacityStack[this._opacityStack.length - 1];
|
|
9
|
+
const newOpacity = currentOpacity * opacity;
|
|
10
|
+
// Push to stack and apply
|
|
11
|
+
this._opacityStack.push(newOpacity);
|
|
12
|
+
Painter.logger.log("NEXT OPACITY WILL BE", newOpacity);
|
|
13
|
+
Painter.effects.setAlpha(newOpacity);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static popOpacity() {
|
|
17
|
+
if (this._opacityStack.length > 1) {
|
|
18
|
+
this._opacityStack.pop();
|
|
19
|
+
const newOpacity = this._opacityStack[this._opacityStack.length - 1];
|
|
20
|
+
Painter.logger.log("NEXT OPACITY WILL BE", newOpacity);
|
|
21
|
+
Painter.effects.setAlpha(newOpacity);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static _clone() {
|
|
26
|
+
// Create a deep copy of the opacity stack
|
|
27
|
+
this._opacityStack = [...this._opacityStack];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
static saveOpacityState() {
|
|
32
|
+
this._opacityStateBackup = [...this._opacityStack];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static restoreOpacityState() {
|
|
36
|
+
if (this._opacityStateBackup) {
|
|
37
|
+
this._opacityStack = this._opacityStateBackup;
|
|
38
|
+
delete this._opacityStateBackup;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|