@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,232 @@
|
|
|
1
|
+
import { applyAnchor } from "../../mixins";
|
|
2
|
+
import { GameObject } from "./go";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ShapeGOFactory
|
|
6
|
+
* --------------
|
|
7
|
+
*
|
|
8
|
+
* Utility factory for creating GameObjects from Shapes.
|
|
9
|
+
*/
|
|
10
|
+
export class ShapeGOFactory {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a GameObject wrapper around a Shape
|
|
13
|
+
*
|
|
14
|
+
* @param {Game} game - Game instance
|
|
15
|
+
* @param {Shape} shape - Shape to wrap
|
|
16
|
+
* @param {Object} opts - Additional options
|
|
17
|
+
* @returns {GameObjectShapeWrapper} - The created wrapper
|
|
18
|
+
*/
|
|
19
|
+
static create(game, shape, opts = {}) {
|
|
20
|
+
// Create wrapper with combined options
|
|
21
|
+
const combinedOpts = {
|
|
22
|
+
// Default position & size from shape
|
|
23
|
+
x: shape?.x ?? 0,
|
|
24
|
+
y: shape?.y ?? 0,
|
|
25
|
+
width: shape?.width ?? 0,
|
|
26
|
+
height: shape?.height ?? 0,
|
|
27
|
+
rotation: shape?.rotation ?? 0,
|
|
28
|
+
scaleX: shape?.scaleX ?? 1,
|
|
29
|
+
scaleY: shape?.scaleY ?? 1,
|
|
30
|
+
opacity: shape?.opacity ?? 1,
|
|
31
|
+
visible: shape?.visible ?? true,
|
|
32
|
+
active: true,
|
|
33
|
+
debug: shape?.debug ?? false,
|
|
34
|
+
|
|
35
|
+
// Shape-specific properties
|
|
36
|
+
color: shape?.color ?? null,
|
|
37
|
+
stroke: shape?.stroke ?? null,
|
|
38
|
+
lineWidth: shape?.lineWidth ?? 1,
|
|
39
|
+
lineJoin: shape?.lineJoin ?? "miter",
|
|
40
|
+
lineCap: shape?.lineCap ?? "butt",
|
|
41
|
+
miterLimit: shape?.miterLimit ?? 10,
|
|
42
|
+
|
|
43
|
+
// Override with any user-provided options
|
|
44
|
+
...opts,
|
|
45
|
+
|
|
46
|
+
// Default name from shape class
|
|
47
|
+
name: opts.name ?? shape?.constructor.name ?? "ShapeWrapper",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return new GameObjectShapeWrapper(game, shape, combinedOpts);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* GameObjectShapeWrapper
|
|
56
|
+
* ----------------------
|
|
57
|
+
*
|
|
58
|
+
* A specialized GameObject that wraps a Shape.
|
|
59
|
+
*
|
|
60
|
+
* @extends GameObject
|
|
61
|
+
*/
|
|
62
|
+
export class GameObjectShapeWrapper extends GameObject {
|
|
63
|
+
/**
|
|
64
|
+
* Creates a GameObject wrapper around a Shape instance
|
|
65
|
+
*
|
|
66
|
+
* @param {Game} game - The game instance
|
|
67
|
+
* @param {Shape} shape - The shape to wrap
|
|
68
|
+
* @param {Object} options - Configuration options
|
|
69
|
+
*/
|
|
70
|
+
constructor(game, shape, options = {}) {
|
|
71
|
+
super(game, options);
|
|
72
|
+
|
|
73
|
+
// Validate shape
|
|
74
|
+
if (!shape || shape == null || shape == undefined) {
|
|
75
|
+
throw new Error("GameObjectShapeWrapper requires a shape");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Store the shape
|
|
79
|
+
this.shape = shape;
|
|
80
|
+
|
|
81
|
+
// Apply Shape-specific properties directly to the shape
|
|
82
|
+
if (options.color !== undefined) shape.color = options.color;
|
|
83
|
+
if (options.stroke !== undefined) shape.stroke = options.stroke;
|
|
84
|
+
if (options.lineWidth !== undefined) shape.lineWidth = options.lineWidth;
|
|
85
|
+
if (options.lineJoin !== undefined) shape.lineJoin = options.lineJoin;
|
|
86
|
+
if (options.lineCap !== undefined) shape.lineCap = options.lineCap;
|
|
87
|
+
if (options.miterLimit !== undefined) shape.miterLimit = options.miterLimit;
|
|
88
|
+
|
|
89
|
+
// Apply standard properties
|
|
90
|
+
this.syncPropertiesToShape();
|
|
91
|
+
|
|
92
|
+
this.logger.log(`Created GameObject(${this.constructor.name}):`, {
|
|
93
|
+
x: this.x,
|
|
94
|
+
y: this.y,
|
|
95
|
+
width: this.width,
|
|
96
|
+
height: this.height,
|
|
97
|
+
color: this.color,
|
|
98
|
+
stroke: this.stroke
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Synchronizes common properties from wrapper to shape
|
|
104
|
+
*/
|
|
105
|
+
syncPropertiesToShape() {
|
|
106
|
+
if (!this.shape) return;
|
|
107
|
+
|
|
108
|
+
// Common transformable properties
|
|
109
|
+
const propsToSync = [
|
|
110
|
+
'width', 'height', 'rotation', 'scaleX', 'scaleY',
|
|
111
|
+
'visible', 'debug', 'debugColor'
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
// Sync each property from this wrapper to the shape
|
|
115
|
+
for (const prop of propsToSync) {
|
|
116
|
+
if (prop in this && prop in this.shape) {
|
|
117
|
+
if (this[prop] !== this.shape[prop]) {
|
|
118
|
+
this.shape[prop] = this[prop];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Shape-specific property getters and setters
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Fill color for the shape
|
|
128
|
+
* @type {string|null}
|
|
129
|
+
*/
|
|
130
|
+
get color() {
|
|
131
|
+
return this.shape ? this.shape.color : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
set color(value) {
|
|
135
|
+
if (this.shape) {
|
|
136
|
+
this.shape.color = value;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Stroke color for the shape
|
|
142
|
+
* @type {string|null}
|
|
143
|
+
*/
|
|
144
|
+
get stroke() {
|
|
145
|
+
return this.shape ? this.shape.stroke : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
set stroke(value) {
|
|
149
|
+
if (this.shape) {
|
|
150
|
+
this.shape.stroke = value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Line width for the shape's stroke
|
|
156
|
+
* @type {number}
|
|
157
|
+
*/
|
|
158
|
+
get lineWidth() {
|
|
159
|
+
return this.shape ? this.shape.lineWidth : 1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
set lineWidth(value) {
|
|
163
|
+
if (this.shape) {
|
|
164
|
+
this.shape.lineWidth = value;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Line join style ("miter", "round", "bevel")
|
|
170
|
+
* @type {string}
|
|
171
|
+
*/
|
|
172
|
+
get lineJoin() {
|
|
173
|
+
return this.shape ? this.shape.lineJoin : "miter";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
set lineJoin(value) {
|
|
177
|
+
if (this.shape) {
|
|
178
|
+
this.shape.lineJoin = value;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Line cap style ("butt", "round", "square")
|
|
184
|
+
* @type {string}
|
|
185
|
+
*/
|
|
186
|
+
get lineCap() {
|
|
187
|
+
return this.shape ? this.shape.lineCap : "butt";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
set lineCap(value) {
|
|
191
|
+
if (this.shape) {
|
|
192
|
+
this.shape.lineCap = value;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Miter limit for line joins
|
|
198
|
+
* @type {number}
|
|
199
|
+
*/
|
|
200
|
+
get miterLimit() {
|
|
201
|
+
return this.shape ? this.shape.miterLimit : 10;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
set miterLimit(value) {
|
|
205
|
+
if (this.shape) {
|
|
206
|
+
this.shape.miterLimit = value;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Update method called each frame
|
|
212
|
+
* @param {number} dt - Delta time in seconds
|
|
213
|
+
*/
|
|
214
|
+
update(dt) {
|
|
215
|
+
if (!this.active) return;
|
|
216
|
+
this.onUpdate?.(dt);
|
|
217
|
+
// Check if bounds need to be recalculated
|
|
218
|
+
if (this._boundsDirty || this.tweening) {
|
|
219
|
+
this.syncPropertiesToShape();
|
|
220
|
+
this._boundsDirty = false;
|
|
221
|
+
}
|
|
222
|
+
super.update(dt);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Draw method to render the shape
|
|
227
|
+
*/
|
|
228
|
+
draw() {
|
|
229
|
+
super.draw();
|
|
230
|
+
this.shape.render();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/***************************************************************
|
|
2
|
+
* Pipeline.js
|
|
3
|
+
*
|
|
4
|
+
* Manages and updates/renders a collection of GameObjects.
|
|
5
|
+
* Handles input event dispatch to the topmost interactive object,
|
|
6
|
+
* including special handling for Scenes (nested children).
|
|
7
|
+
***************************************************************/
|
|
8
|
+
|
|
9
|
+
import { Scene } from "./objects";
|
|
10
|
+
import { Tweenetik } from "../motion";
|
|
11
|
+
import { Loggable } from "../logger/loggable";
|
|
12
|
+
import { ZOrderedCollection } from "../util";
|
|
13
|
+
/**
|
|
14
|
+
* Pipeline - Maintains a list of GameObjects, updating and rendering them
|
|
15
|
+
* each frame. It also centralizes and dispatches pointer events (inputdown,
|
|
16
|
+
* inputup, inputmove) to interactive objects, including nested Scene children.
|
|
17
|
+
*/
|
|
18
|
+
export class Pipeline extends Loggable {
|
|
19
|
+
/**
|
|
20
|
+
* Create a new Pipeline.
|
|
21
|
+
* @param {Game} game - A reference to the main Game instance.
|
|
22
|
+
*/
|
|
23
|
+
constructor(game) {
|
|
24
|
+
super();
|
|
25
|
+
/**
|
|
26
|
+
* Reference to the owning Game.
|
|
27
|
+
* @type {Game}
|
|
28
|
+
*/
|
|
29
|
+
this.game = game;
|
|
30
|
+
/**
|
|
31
|
+
* The master list of top-level GameObjects to update and render.
|
|
32
|
+
* @type {GameObject[]}
|
|
33
|
+
*/
|
|
34
|
+
// Create the z-ordered collection
|
|
35
|
+
this._collection = new ZOrderedCollection();
|
|
36
|
+
this._collection._owner = this; // Give collection a reference to its owner
|
|
37
|
+
|
|
38
|
+
// Listen for pointer events from the Game's central event system.
|
|
39
|
+
const types = ["inputdown", "inputup", "inputmove", "click"];
|
|
40
|
+
types.forEach((type) => {
|
|
41
|
+
this.game.events.on(type, (e) => {
|
|
42
|
+
this.dispatchInputEvent(type, e);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Internal helper to check if a single GameObject is hovered, and emit
|
|
49
|
+
* mouseover/mouseout as needed.
|
|
50
|
+
* @param {GameObject} obj - The object to hover-test.
|
|
51
|
+
* @param {object} e - Event data containing pointer coordinates (e.x, e.y).
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
_hoverObject(obj, e) {
|
|
55
|
+
//console.log("hoverObject", obj.constructor.name, obj.interactive, obj._hitTest != null);
|
|
56
|
+
// Only applies to interactive objects with a shape and a _hitTest method.
|
|
57
|
+
if (!obj.interactive || !obj._hitTest) return;
|
|
58
|
+
const hit = obj._hitTest(e.x, e.y);
|
|
59
|
+
if (hit && !obj._hovered) {
|
|
60
|
+
// Pointer entered this object
|
|
61
|
+
obj._hovered = true;
|
|
62
|
+
obj.events.emit("mouseover", e);
|
|
63
|
+
//this.logger.log("Mouseover", obj, e.x, e.y);
|
|
64
|
+
} else if (!hit && obj._hovered) {
|
|
65
|
+
// Pointer left this object
|
|
66
|
+
obj._hovered = false;
|
|
67
|
+
obj.events.emit("mouseout", e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Recursively checks all children of a Scene for hover state.
|
|
73
|
+
* @param {Scene} scene - The scene whose children will be hover-tested.
|
|
74
|
+
* @param {object} e - Event data containing pointer coordinates (e.x, e.y).
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
_hoverScene(scene, e) {
|
|
78
|
+
for (let i = scene.children.length - 1; i >= 0; i--) {
|
|
79
|
+
const child = scene.children[i];
|
|
80
|
+
if (child instanceof Scene) {
|
|
81
|
+
this._hoverScene(child, e); // recurse into nested scenes
|
|
82
|
+
} else {
|
|
83
|
+
this._hoverObject(child, e); // hover actual objects
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Dispatch a pointer event (inputdown, inputup, inputmove) to the first
|
|
90
|
+
* GameObject that is hit, or to Scenes that can recursively handle children.
|
|
91
|
+
* Also triggers _dispatchHover if needed.
|
|
92
|
+
* @param {string} type - Event type (e.g., "inputdown", "inputup", "inputmove").
|
|
93
|
+
* @param {object} e - Event data containing pointer coordinates (e.x, e.y).
|
|
94
|
+
*/
|
|
95
|
+
dispatchInputEvent(type, e) {
|
|
96
|
+
let handled = false;
|
|
97
|
+
// Check from topmost to bottommost object to find the first that was hit.
|
|
98
|
+
for (let i = this.gameObjects.length - 1; i >= 0; i--) {
|
|
99
|
+
const obj = this.gameObjects[i];
|
|
100
|
+
if (type === "inputdown") {
|
|
101
|
+
//this.logger.log("inputdown", obj);
|
|
102
|
+
}
|
|
103
|
+
if (obj instanceof Scene) {
|
|
104
|
+
// If it's a Scene, see if any of its children were hit.
|
|
105
|
+
if (this._dispatchToScene(obj, type, e)) {
|
|
106
|
+
handled = true;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
} else if (obj.interactive && obj._hitTest?.(e.x, e.y)) {
|
|
110
|
+
// Found a regular interactive GameObject that was hit
|
|
111
|
+
obj.events.emit(type, e);
|
|
112
|
+
handled = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If this is a pointer move event, we also check for hover transitions.
|
|
118
|
+
if (type === "inputmove") {
|
|
119
|
+
this._dispatchHover(e);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* After handling inputmove at the top level, this updates hover states
|
|
125
|
+
* for all interactive objects, including children in Scenes.
|
|
126
|
+
* @param {object} e - Event data containing pointer coordinates (e.x, e.y).
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
_dispatchHover(e) {
|
|
130
|
+
// Check from topmost to bottommost for hover changes.
|
|
131
|
+
for (let i = this.gameObjects.length - 1; i >= 0; i--) {
|
|
132
|
+
const obj = this.gameObjects[i];
|
|
133
|
+
//this.logger.log("Hover test for", obj, e.x, e.y);
|
|
134
|
+
if (obj instanceof Scene) {
|
|
135
|
+
this._hoverScene(obj, e);
|
|
136
|
+
} else {
|
|
137
|
+
this._hoverObject(obj, e);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Recursively dispatch an event to a Scene and possibly its nested child Scenes.
|
|
144
|
+
* @param {Scene} scene - The scene to dispatch the event to.
|
|
145
|
+
* @param {string} type - The type of pointer event ("inputdown", "inputup", etc).
|
|
146
|
+
* @param {object} e - Event data with pointer coordinates.
|
|
147
|
+
* @returns {boolean} True if the event was handled by a child, false otherwise.
|
|
148
|
+
* @private
|
|
149
|
+
*/
|
|
150
|
+
_dispatchToScene(scene, type, e) {
|
|
151
|
+
//if(type === "inputdown") this.logger.log("inputdown", scene);
|
|
152
|
+
for (let i = scene.children.length - 1; i >= 0; i--) {
|
|
153
|
+
const child = scene.children[i];
|
|
154
|
+
if (child instanceof Scene) {
|
|
155
|
+
// Recurse deeper if child is also a Scene
|
|
156
|
+
const hit = this._dispatchToScene(child, type, e);
|
|
157
|
+
if (hit) {
|
|
158
|
+
//if(type === "inputdown") this.logger.log("HIT", child, type);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
} else if (child.interactive && child._hitTest?.(e.x, e.y)) {
|
|
162
|
+
// Found a child that was hit
|
|
163
|
+
//if(type === "inputdown") this.logger.log("Dispatching to child", child, type);
|
|
164
|
+
child.events.emit(type, e);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add a GameObject to the pipeline so it will be updated and rendered each frame.
|
|
173
|
+
* @param {GameObject} gameObject - The game object to add.
|
|
174
|
+
* @returns {GameObject} Returns the same object for convenience.
|
|
175
|
+
*/
|
|
176
|
+
add(gameObject) {
|
|
177
|
+
gameObject.parent = this.game;
|
|
178
|
+
const go = this._collection.add(gameObject);
|
|
179
|
+
// call the init() method on the go if it exists
|
|
180
|
+
if (go.init) {
|
|
181
|
+
go.init();
|
|
182
|
+
}
|
|
183
|
+
return go;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Remove a GameObject from the pipeline.
|
|
188
|
+
* @param {GameObject} gameObject - The object to remove.
|
|
189
|
+
*/
|
|
190
|
+
remove(gameObject) {
|
|
191
|
+
if(gameObject === undefined || gameObject === null) {
|
|
192
|
+
this.logger.warn("Cannot remove undefined or null object", gameObject);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this._collection.remove(gameObject);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
bringToFront(gameObject) {
|
|
199
|
+
return this._collection.bringToFront(gameObject);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
sendToBack(gameObject) {
|
|
203
|
+
return this._collection.sendToBack(gameObject);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
bringForward(gameObject) {
|
|
207
|
+
return this._collection.bringForward(gameObject);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
sendBackward(gameObject) {
|
|
211
|
+
return this._collection.sendBackward(gameObject);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
clear() {
|
|
215
|
+
return this._collection.clear();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Getter to access children
|
|
219
|
+
get gameObjects() {
|
|
220
|
+
return this._collection.children;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
update(dt) {
|
|
224
|
+
this.logger.groupCollapsed("Pipeline.update");
|
|
225
|
+
this._collection.children
|
|
226
|
+
.filter((obj) => obj.active)
|
|
227
|
+
.forEach((obj) => obj.update(dt));
|
|
228
|
+
Tweenetik.updateAll(dt);
|
|
229
|
+
this.logger.groupEnd();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
render() {
|
|
233
|
+
const renderObj = (obj) => obj.render();
|
|
234
|
+
const filterVisible = (obj) => obj.visible;
|
|
235
|
+
const filterActive = (obj) => obj.active;
|
|
236
|
+
this.logger.groupCollapsed("Pipeline.render");
|
|
237
|
+
this._collection.getSortedChildren()
|
|
238
|
+
.filter(filterVisible)
|
|
239
|
+
.filter(filterActive)
|
|
240
|
+
.forEach(renderObj);
|
|
241
|
+
this.logger.groupEnd();
|
|
242
|
+
}
|
|
243
|
+
}
|