@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,230 @@
|
|
|
1
|
+
import { Painter } from "./painter";
|
|
2
|
+
|
|
3
|
+
export class PainterEffects {
|
|
4
|
+
/**
|
|
5
|
+
* Set shadow properties
|
|
6
|
+
* @param {string} color - Shadow color
|
|
7
|
+
* @param {number} blur - Shadow blur
|
|
8
|
+
* @param {number} [offsetX=0] - Shadow X offset
|
|
9
|
+
* @param {number} [offsetY=0] - Shadow Y offset
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
static dropShadow(color, blur, offsetX = 0, offsetY = 0) {
|
|
13
|
+
Painter.ctx.shadowColor = color;
|
|
14
|
+
Painter.ctx.shadowBlur = blur;
|
|
15
|
+
Painter.ctx.shadowOffsetX = offsetX;
|
|
16
|
+
Painter.ctx.shadowOffsetY = offsetY;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Clear shadow
|
|
21
|
+
* @returns {void}
|
|
22
|
+
*/
|
|
23
|
+
static clearShadow() {
|
|
24
|
+
Painter.ctx.shadowColor = "rgba(0, 0, 0, 0)";
|
|
25
|
+
Painter.ctx.shadowBlur = 0;
|
|
26
|
+
Painter.ctx.shadowOffsetX = 0;
|
|
27
|
+
Painter.ctx.shadowOffsetY = 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Set global alpha
|
|
32
|
+
* @param {number} alpha - Alpha value (0-1)
|
|
33
|
+
* @returns {void}
|
|
34
|
+
*/
|
|
35
|
+
static setAlpha(alpha) {
|
|
36
|
+
Painter.ctx.globalAlpha = alpha;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set global composite operation
|
|
41
|
+
* @param {GlobalCompositeOperation} operation - Composite operation
|
|
42
|
+
* @returns {void}
|
|
43
|
+
*/
|
|
44
|
+
static setBlendMode(operation) {
|
|
45
|
+
Painter.ctx.globalCompositeOperation = operation;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Clip to a rectangular region
|
|
50
|
+
* @param {number} x - X coordinate
|
|
51
|
+
* @param {number} y - Y coordinate
|
|
52
|
+
* @param {number} width - Width
|
|
53
|
+
* @param {number} height - Height
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
static clipRect(x, y, width, height) {
|
|
57
|
+
Painter.ctx.beginPath();
|
|
58
|
+
Painter.ctx.rect(x, y, width, height);
|
|
59
|
+
Painter.ctx.clip();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clip to a circular region
|
|
64
|
+
* @param {number} x - Center X
|
|
65
|
+
* @param {number} y - Center Y
|
|
66
|
+
* @param {number} radius - Circle radius
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
static clipCircle(x, y, radius) {
|
|
70
|
+
Painter.ctx.beginPath();
|
|
71
|
+
Painter.shapes.arc(x, y, radius, 0, Math.PI * 2);
|
|
72
|
+
Painter.ctx.clip();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Apply a blur filter to a region
|
|
77
|
+
* @param {number} x - X coordinate
|
|
78
|
+
* @param {number} y - Y coordinate
|
|
79
|
+
* @param {number} width - Width
|
|
80
|
+
* @param {number} height - Height
|
|
81
|
+
* @param {number} blur - Blur amount (pixels)
|
|
82
|
+
* @returns {void}
|
|
83
|
+
*/
|
|
84
|
+
static blurRegion(x, y, width, height, blur) {
|
|
85
|
+
// Save current filter
|
|
86
|
+
const currentFilter = Painter.ctx.filter;
|
|
87
|
+
|
|
88
|
+
// Apply blur filter
|
|
89
|
+
Painter.ctx.filter = `blur(${blur}px)`;
|
|
90
|
+
|
|
91
|
+
// Draw the region from the canvas back onto itself with the filter
|
|
92
|
+
const imageData = Painter.ctx.getImageData(x, y, width, height);
|
|
93
|
+
Painter.ctx.putImageData(imageData, x, y);
|
|
94
|
+
|
|
95
|
+
// Restore previous filter
|
|
96
|
+
Painter.ctx.filter = currentFilter;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
// Static properties to track current effect state
|
|
101
|
+
static _activeEffects = new Map();
|
|
102
|
+
static _animationId = null;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a glow effect that can be animated
|
|
106
|
+
* @param {string} color - Base color for the glow
|
|
107
|
+
* @param {number} blur - Initial blur amount
|
|
108
|
+
* @param {Object} [options] - Animation options
|
|
109
|
+
* @param {number} [options.pulseSpeed=0] - Speed of pulsing (0 for static)
|
|
110
|
+
* @param {number} [options.pulseMin=blur*0.5] - Minimum blur during pulsing
|
|
111
|
+
* @param {number} [options.pulseMax=blur*1.5] - Maximum blur during pulsing
|
|
112
|
+
* @param {number} [options.colorShift=0] - Hue shift speed (0 for static)
|
|
113
|
+
* @returns {Object} - Control object for the effect
|
|
114
|
+
*/
|
|
115
|
+
static createGlow(color, blur, options = {}) {
|
|
116
|
+
const id = 'glow-' + Math.random().toString(36).substr(2, 9);
|
|
117
|
+
const defaultOptions = {
|
|
118
|
+
pulseSpeed: 0,
|
|
119
|
+
pulseMin: blur * 0.5,
|
|
120
|
+
pulseMax: blur * 1.5,
|
|
121
|
+
colorShift: 0
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const effectOptions = {...defaultOptions, ...options};
|
|
125
|
+
|
|
126
|
+
// Create effect state
|
|
127
|
+
const effectState = {
|
|
128
|
+
id,
|
|
129
|
+
type: 'glow',
|
|
130
|
+
active: true,
|
|
131
|
+
time: 0,
|
|
132
|
+
color,
|
|
133
|
+
blur,
|
|
134
|
+
options: effectOptions,
|
|
135
|
+
|
|
136
|
+
// Method to update effect parameters
|
|
137
|
+
update(params) {
|
|
138
|
+
Object.assign(this, params);
|
|
139
|
+
return this;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// Method to stop the effect
|
|
143
|
+
stop() {
|
|
144
|
+
this.active = false;
|
|
145
|
+
PainterEffects._activeEffects.delete(this.id);
|
|
146
|
+
return this;
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Method to apply the effect for the current frame
|
|
150
|
+
apply() {
|
|
151
|
+
if (!this.active) return;
|
|
152
|
+
|
|
153
|
+
let currentBlur = this.blur;
|
|
154
|
+
let currentColor = this.color;
|
|
155
|
+
|
|
156
|
+
// Apply animation if enabled
|
|
157
|
+
if (this.options.pulseSpeed > 0) {
|
|
158
|
+
const pulseFactor = Math.sin(this.time * this.options.pulseSpeed) * 0.5 + 0.5;
|
|
159
|
+
currentBlur = this.options.pulseMin +
|
|
160
|
+
pulseFactor * (this.options.pulseMax - this.options.pulseMin);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (this.options.colorShift > 0) {
|
|
164
|
+
// Apply color shift - would need color parsing and HSL conversion
|
|
165
|
+
// This is a simplified placeholder
|
|
166
|
+
currentColor = currentColor.replace('hue',
|
|
167
|
+
(this.time * this.options.colorShift) % 360);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Apply the actual glow effect
|
|
171
|
+
Painter.ctx.shadowColor = currentColor;
|
|
172
|
+
Painter.ctx.shadowBlur = currentBlur;
|
|
173
|
+
Painter.ctx.shadowOffsetX = 0;
|
|
174
|
+
Painter.ctx.shadowOffsetY = 0;
|
|
175
|
+
|
|
176
|
+
this.time += 1/60; // Assuming 60fps
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Store and start tracking the effect
|
|
182
|
+
PainterEffects._activeEffects.set(id, effectState);
|
|
183
|
+
PainterEffects._startAnimationLoop();
|
|
184
|
+
|
|
185
|
+
return effectState;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Start the animation loop if not already running
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
static _startAnimationLoop() {
|
|
193
|
+
if (PainterEffects._animationId !== null) return;
|
|
194
|
+
|
|
195
|
+
const animate = () => {
|
|
196
|
+
// Apply all active effects
|
|
197
|
+
PainterEffects._activeEffects.forEach(effect => {
|
|
198
|
+
if (effect.active) effect.apply();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Stop the loop if no active effects
|
|
202
|
+
if (PainterEffects._activeEffects.size === 0) {
|
|
203
|
+
cancelAnimationFrame(PainterEffects._animationId);
|
|
204
|
+
PainterEffects._animationId = null;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
PainterEffects._animationId = requestAnimationFrame(animate);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
PainterEffects._animationId = requestAnimationFrame(animate);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Clear all active effects
|
|
216
|
+
*/
|
|
217
|
+
static clearAllEffects() {
|
|
218
|
+
PainterEffects._activeEffects.forEach(effect => effect.stop());
|
|
219
|
+
PainterEffects._activeEffects.clear();
|
|
220
|
+
|
|
221
|
+
// Reset canvas context properties
|
|
222
|
+
Painter.ctx.shadowColor = "rgba(0, 0, 0, 0)";
|
|
223
|
+
Painter.ctx.shadowBlur = 0;
|
|
224
|
+
Painter.ctx.shadowOffsetX = 0;
|
|
225
|
+
Painter.ctx.shadowOffsetY = 0;
|
|
226
|
+
Painter.ctx.filter = "none";
|
|
227
|
+
Painter.ctx.globalAlpha = 1;
|
|
228
|
+
Painter.ctx.globalCompositeOperation = "source-over";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// src/painter-images.js
|
|
3
|
+
// Centralised helpers for Image, ImageData and bitmap work.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import { Painter } from "./painter";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Painter.img – all Canvas 2D image utilities in one names‑space.
|
|
9
|
+
* Relies on a live Painter.ctx that points at the active canvas context.
|
|
10
|
+
*/
|
|
11
|
+
export class PainterImages {
|
|
12
|
+
/* ─────────────────────────────── DRAWING ────────────────────────────── */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Draw an image or canvas onto the current context.
|
|
16
|
+
* @param {CanvasImageSource} source <img>, <canvas>, <video>, etc.
|
|
17
|
+
* @param {number} x, y Destination position (after transforms)
|
|
18
|
+
* @param {object} [opts] Declarative options
|
|
19
|
+
* @prop {number} width,height Destination size (defaults → intrinsic)
|
|
20
|
+
* @prop {object} crop { sx, sy, sw, sh } source rectangle
|
|
21
|
+
* @prop {string} anchor top‑left | center | bottom‑right …
|
|
22
|
+
* @prop {number} rotation Radians, about anchor point
|
|
23
|
+
* @prop {number} scaleX,scaleY Independent or uniform scaling
|
|
24
|
+
* @prop {boolean} flipX,flipY Mirror horizontally / vertically
|
|
25
|
+
* @prop {number} alpha Multiplicative opacity (1 = opaque)
|
|
26
|
+
* @prop {boolean} smoothing ImageSmoothingEnabled toggle
|
|
27
|
+
*/
|
|
28
|
+
static draw(
|
|
29
|
+
source,
|
|
30
|
+
x = 0,
|
|
31
|
+
y = 0,
|
|
32
|
+
{
|
|
33
|
+
width,
|
|
34
|
+
height,
|
|
35
|
+
crop = null,
|
|
36
|
+
anchor = "top‑left",
|
|
37
|
+
rotation = 0,
|
|
38
|
+
scaleX = 1,
|
|
39
|
+
scaleY = 1,
|
|
40
|
+
flipX = false,
|
|
41
|
+
flipY = false,
|
|
42
|
+
alpha = 1,
|
|
43
|
+
smoothing = true,
|
|
44
|
+
} = {}
|
|
45
|
+
) {
|
|
46
|
+
const ctx = Painter.ctx;
|
|
47
|
+
if (!ctx || !source) return;
|
|
48
|
+
|
|
49
|
+
const iw = width ?? (crop ? crop.sw : source.width ?? source.videoWidth);
|
|
50
|
+
const ih = height ?? (crop ? crop.sh : source.height ?? source.videoHeight);
|
|
51
|
+
|
|
52
|
+
const ax = { left: 0, center: 0.5, right: 1 }[anchor.split("-").pop()] ?? 0;
|
|
53
|
+
const ay = { top: 0, center: 0.5, bottom: 1 }[anchor.split("-")[0]] ?? 0;
|
|
54
|
+
const ox = -iw * ax;
|
|
55
|
+
const oy = -ih * ay;
|
|
56
|
+
|
|
57
|
+
ctx.save();
|
|
58
|
+
ctx.imageSmoothingEnabled = smoothing;
|
|
59
|
+
ctx.globalAlpha *= alpha;
|
|
60
|
+
ctx.translate(x, y);
|
|
61
|
+
if (rotation) ctx.rotate(rotation);
|
|
62
|
+
if (flipX || flipY) ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
|
|
63
|
+
ctx.scale(scaleX, scaleY);
|
|
64
|
+
|
|
65
|
+
if (crop) {
|
|
66
|
+
const { sx, sy, sw, sh } = crop;
|
|
67
|
+
ctx.drawImage(source, sx, sy, sw, sh, ox, oy, iw, ih);
|
|
68
|
+
} else {
|
|
69
|
+
ctx.drawImage(source, ox, oy, iw, ih);
|
|
70
|
+
}
|
|
71
|
+
ctx.restore();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Quick alias for draw that only needs `{width,height}` overrides. */
|
|
75
|
+
static blit(source, x, y, w, h) {
|
|
76
|
+
this.draw(source, x, y, { width: w, height: h });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ───────────────────────────── PATTERNS ─────────────────────────────── */
|
|
80
|
+
|
|
81
|
+
/** `ctx.createPattern` shortcut. */
|
|
82
|
+
static createPattern(image, repetition = "repeat") {
|
|
83
|
+
return Painter.ctx.createPattern(image, repetition);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fill a rectangle with a pattern in one line.
|
|
88
|
+
*/
|
|
89
|
+
static fillPattern(image, x, y, width, height) {
|
|
90
|
+
const ctx = Painter.ctx;
|
|
91
|
+
ctx.save();
|
|
92
|
+
ctx.fillStyle = image;
|
|
93
|
+
ctx.fillRect(x, y, width, height);
|
|
94
|
+
ctx.restore();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ─────────────────────────── IMAGE DATA API ─────────────────────────── */
|
|
98
|
+
|
|
99
|
+
static createImageData(width, height) {
|
|
100
|
+
return Painter.ctx.createImageData(width, height);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static cloneImageData(imageData) {
|
|
104
|
+
return new ImageData(
|
|
105
|
+
new Uint8ClampedArray(imageData.data),
|
|
106
|
+
imageData.width,
|
|
107
|
+
imageData.height
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static getImageData(x, y, width, height) {
|
|
112
|
+
return Painter.ctx.getImageData(x, y, width, height);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static putImageData(
|
|
116
|
+
imageData,
|
|
117
|
+
x,
|
|
118
|
+
y,
|
|
119
|
+
dirtyX = 0,
|
|
120
|
+
dirtyY = 0,
|
|
121
|
+
dirtyWidth = imageData.width,
|
|
122
|
+
dirtyHeight = imageData.height
|
|
123
|
+
) {
|
|
124
|
+
Painter.ctx.putImageData(
|
|
125
|
+
imageData,
|
|
126
|
+
x,
|
|
127
|
+
y,
|
|
128
|
+
dirtyX,
|
|
129
|
+
dirtyY,
|
|
130
|
+
dirtyWidth,
|
|
131
|
+
dirtyHeight
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Map‑style pixel transform: pass a callback that returns [r,g,b,a].
|
|
137
|
+
* Very handy for simple filters without manual loop boilerplate.
|
|
138
|
+
*/
|
|
139
|
+
static mapPixels(imageData, fn) {
|
|
140
|
+
const d = imageData.data;
|
|
141
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
142
|
+
const idx = i >> 2;
|
|
143
|
+
const res = fn(d[i], d[i + 1], d[i + 2], d[i + 3], idx);
|
|
144
|
+
if (res) [d[i], d[i + 1], d[i + 2], d[i + 3]] = res;
|
|
145
|
+
}
|
|
146
|
+
return imageData;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Set one pixel in an ImageData object. */
|
|
150
|
+
static setPixel(imageData, x, y, r, g, b, a = 255) {
|
|
151
|
+
const i = (y * imageData.width + x) * 4;
|
|
152
|
+
const d = imageData.data;
|
|
153
|
+
d[i] = r;
|
|
154
|
+
d[i + 1] = g;
|
|
155
|
+
d[i + 2] = b;
|
|
156
|
+
d[i + 3] = a;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* ───────────────────────────── BITMAP HELPERS ───────────────────────── */
|
|
160
|
+
|
|
161
|
+
/** Convert the **current canvas** to an ImageBitmap (cheap GPU upload). */
|
|
162
|
+
static async toBitmap({ type = "image/png", quality = 0.92 } = {}) {
|
|
163
|
+
const canvas = Painter.ctx.canvas;
|
|
164
|
+
const blob = await canvas.convertToBlob({ type, quality });
|
|
165
|
+
return createImageBitmap(blob);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Create an ImageBitmap from any CanvasImageSource. */
|
|
169
|
+
static async createBitmap(image) {
|
|
170
|
+
return createImageBitmap(image);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ───────────── ImageData ← raw pixels convenience ───────────── */
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Converts a flat RGBA array into an ImageData object.
|
|
177
|
+
* @param {Uint8ClampedArray} rgbaArray
|
|
178
|
+
* @param {number} width
|
|
179
|
+
* @param {number} height
|
|
180
|
+
* @returns {ImageData}
|
|
181
|
+
*/
|
|
182
|
+
static toImageData(rgbaArray, width, height) {
|
|
183
|
+
if (rgbaArray.length !== width * height * 4) {
|
|
184
|
+
throw new Error("Invalid RGBA array size for given dimensions");
|
|
185
|
+
}
|
|
186
|
+
return new ImageData(rgbaArray, width, height);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Asynchronously creates an ImageBitmap from raw RGBA data.
|
|
191
|
+
* Can be used directly with createPattern or drawImage.
|
|
192
|
+
* @param {Uint8ClampedArray} rgbaArray
|
|
193
|
+
* @param {number} width
|
|
194
|
+
* @param {number} height
|
|
195
|
+
* @returns {Promise<ImageBitmap>}
|
|
196
|
+
*/
|
|
197
|
+
static async createImageBitmapFromPixels(rgbaArray, width, height) {
|
|
198
|
+
const imgData = this.toImageData(rgbaArray, width, height);
|
|
199
|
+
return await createImageBitmap(imgData);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Creates a pattern from ImageData via a temporary canvas.
|
|
204
|
+
* @param {ImageData} imageData
|
|
205
|
+
* @param {"repeat"|"repeat-x"|"repeat-y"|"no-repeat"} repeat
|
|
206
|
+
* @returns {CanvasPattern}
|
|
207
|
+
*/
|
|
208
|
+
static createPatternFromImageData(imageData, repeat = "repeat") {
|
|
209
|
+
const canvas = document.createElement("canvas");
|
|
210
|
+
canvas.width = imageData.width;
|
|
211
|
+
canvas.height = imageData.height;
|
|
212
|
+
const ctx = canvas.getContext("2d");
|
|
213
|
+
ctx.putImageData(imageData, 0, 0);
|
|
214
|
+
return ctx.createPattern(canvas, repeat);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Shortcut to go directly from pixel array to CanvasPattern
|
|
219
|
+
* @param {Uint8ClampedArray} rgbaArray
|
|
220
|
+
* @param {number} width
|
|
221
|
+
* @param {number} height
|
|
222
|
+
* @param {string} repeat
|
|
223
|
+
* @returns {CanvasPattern}
|
|
224
|
+
*/
|
|
225
|
+
static createPatternFromPixels(rgbaArray, width, height, repeat = "repeat") {
|
|
226
|
+
const imgData = this.toImageData(rgbaArray, width, height);
|
|
227
|
+
return this.createPatternFromImageData(imgData, repeat);
|
|
228
|
+
}
|
|
229
|
+
}
|