@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,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform
|
|
3
|
+
* ---------
|
|
4
|
+
*
|
|
5
|
+
* A fluent API for modifying spatial and transform properties of Transformable objects.
|
|
6
|
+
* Provides a consistent, chainable interface for all transform operations.
|
|
7
|
+
*
|
|
8
|
+
* ### Usage
|
|
9
|
+
*
|
|
10
|
+
* ```javascript
|
|
11
|
+
* // Fluent chaining
|
|
12
|
+
* shape.transform
|
|
13
|
+
* .x(100).y(200)
|
|
14
|
+
* .width(50).height(50)
|
|
15
|
+
* .rotation(45)
|
|
16
|
+
* .scale(0.8);
|
|
17
|
+
*
|
|
18
|
+
* // Batch updates
|
|
19
|
+
* shape.transform.set({
|
|
20
|
+
* x: 100, y: 200,
|
|
21
|
+
* width: 50, height: 50,
|
|
22
|
+
* rotation: 45,
|
|
23
|
+
* scaleX: 0.8, scaleY: 0.8
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Relative transforms
|
|
27
|
+
* shape.transform.translateBy(10, 20);
|
|
28
|
+
* shape.transform.rotateBy(15);
|
|
29
|
+
* shape.transform.scaleBy(1.5);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ### Design Philosophy
|
|
33
|
+
*
|
|
34
|
+
* The Transform class enforces a consistent pattern for modifying shape properties:
|
|
35
|
+
* - All modifications go through the transform API
|
|
36
|
+
* - Direct property setters (shape.width = 100) can be disabled via strictTransforms
|
|
37
|
+
* - Enables future features like transform animation, undo/redo, etc.
|
|
38
|
+
*/
|
|
39
|
+
export class Transform {
|
|
40
|
+
/**
|
|
41
|
+
* Global flag to control whether direct property setters throw errors.
|
|
42
|
+
* When true, setting shape.width = 100 throws an error.
|
|
43
|
+
* When false, it logs a deprecation warning and allows the operation.
|
|
44
|
+
* @type {boolean}
|
|
45
|
+
*/
|
|
46
|
+
static strictMode = false;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a Transform instance bound to a Transformable owner.
|
|
50
|
+
* @param {import('./transformable.js').Transformable} owner - The transformable object this transform controls
|
|
51
|
+
*/
|
|
52
|
+
constructor(owner) {
|
|
53
|
+
this._owner = owner;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets the owner object this transform is bound to.
|
|
58
|
+
* @returns {import('./transformable.js').Transformable}
|
|
59
|
+
*/
|
|
60
|
+
get owner() {
|
|
61
|
+
return this._owner;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================
|
|
65
|
+
// Position Methods
|
|
66
|
+
// ============================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Sets the X position.
|
|
70
|
+
* @param {number} value - X coordinate
|
|
71
|
+
* @returns {Transform} this for chaining
|
|
72
|
+
*/
|
|
73
|
+
x(value) {
|
|
74
|
+
this._owner._x = value;
|
|
75
|
+
this._owner.markBoundsDirty();
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Sets the Y position.
|
|
81
|
+
* @param {number} value - Y coordinate
|
|
82
|
+
* @returns {Transform} this for chaining
|
|
83
|
+
*/
|
|
84
|
+
y(value) {
|
|
85
|
+
this._owner._y = value;
|
|
86
|
+
this._owner.markBoundsDirty();
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sets both X and Y position.
|
|
92
|
+
* @param {number} x - X coordinate
|
|
93
|
+
* @param {number} y - Y coordinate
|
|
94
|
+
* @returns {Transform} this for chaining
|
|
95
|
+
*/
|
|
96
|
+
position(x, y) {
|
|
97
|
+
this._owner._x = x;
|
|
98
|
+
this._owner._y = y;
|
|
99
|
+
this._owner.markBoundsDirty();
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Translates position by relative amounts.
|
|
105
|
+
* @param {number} dx - Delta X
|
|
106
|
+
* @param {number} dy - Delta Y
|
|
107
|
+
* @returns {Transform} this for chaining
|
|
108
|
+
*/
|
|
109
|
+
translateBy(dx, dy) {
|
|
110
|
+
this._owner._x += dx;
|
|
111
|
+
this._owner._y += dy;
|
|
112
|
+
this._owner.markBoundsDirty();
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ============================================================
|
|
117
|
+
// Dimension Methods
|
|
118
|
+
// ============================================================
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Sets the width.
|
|
122
|
+
* @param {number} value - Width in pixels (must be >= 0)
|
|
123
|
+
* @returns {Transform} this for chaining
|
|
124
|
+
*/
|
|
125
|
+
width(value) {
|
|
126
|
+
this._owner._width = Math.max(0, value);
|
|
127
|
+
this._owner.markBoundsDirty();
|
|
128
|
+
this._owner.invalidateCache?.();
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sets the height.
|
|
134
|
+
* @param {number} value - Height in pixels (must be >= 0)
|
|
135
|
+
* @returns {Transform} this for chaining
|
|
136
|
+
*/
|
|
137
|
+
height(value) {
|
|
138
|
+
this._owner._height = Math.max(0, value);
|
|
139
|
+
this._owner.markBoundsDirty();
|
|
140
|
+
this._owner.invalidateCache?.();
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Sets both width and height.
|
|
146
|
+
* @param {number} width - Width in pixels
|
|
147
|
+
* @param {number} height - Height in pixels
|
|
148
|
+
* @returns {Transform} this for chaining
|
|
149
|
+
*/
|
|
150
|
+
size(width, height) {
|
|
151
|
+
this._owner._width = Math.max(0, width);
|
|
152
|
+
this._owner._height = Math.max(0, height);
|
|
153
|
+
this._owner.markBoundsDirty();
|
|
154
|
+
this._owner.invalidateCache?.();
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================
|
|
159
|
+
// Rotation Methods
|
|
160
|
+
// ============================================================
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Sets the rotation in degrees.
|
|
164
|
+
* @param {number} degrees - Rotation angle in degrees
|
|
165
|
+
* @returns {Transform} this for chaining
|
|
166
|
+
*/
|
|
167
|
+
rotation(degrees) {
|
|
168
|
+
this._owner._rotation = degrees * Math.PI / 180;
|
|
169
|
+
this._owner.markBoundsDirty();
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Sets the rotation in radians.
|
|
175
|
+
* @param {number} radians - Rotation angle in radians
|
|
176
|
+
* @returns {Transform} this for chaining
|
|
177
|
+
*/
|
|
178
|
+
rotationRad(radians) {
|
|
179
|
+
this._owner._rotation = radians;
|
|
180
|
+
this._owner.markBoundsDirty();
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Rotates by a relative amount in degrees.
|
|
186
|
+
* @param {number} degrees - Amount to rotate in degrees
|
|
187
|
+
* @returns {Transform} this for chaining
|
|
188
|
+
*/
|
|
189
|
+
rotateBy(degrees) {
|
|
190
|
+
this._owner._rotation += degrees * Math.PI / 180;
|
|
191
|
+
this._owner.markBoundsDirty();
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ============================================================
|
|
196
|
+
// Scale Methods
|
|
197
|
+
// ============================================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Sets the horizontal scale factor.
|
|
201
|
+
* @param {number} value - Scale factor (1 = 100%)
|
|
202
|
+
* @returns {Transform} this for chaining
|
|
203
|
+
*/
|
|
204
|
+
scaleX(value) {
|
|
205
|
+
this._owner._scaleX = value;
|
|
206
|
+
this._owner.markBoundsDirty();
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Sets the vertical scale factor.
|
|
212
|
+
* @param {number} value - Scale factor (1 = 100%)
|
|
213
|
+
* @returns {Transform} this for chaining
|
|
214
|
+
*/
|
|
215
|
+
scaleY(value) {
|
|
216
|
+
this._owner._scaleY = value;
|
|
217
|
+
this._owner.markBoundsDirty();
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Sets both scale factors uniformly.
|
|
223
|
+
* @param {number} value - Scale factor for both axes
|
|
224
|
+
* @returns {Transform} this for chaining
|
|
225
|
+
*/
|
|
226
|
+
scale(value) {
|
|
227
|
+
this._owner._scaleX = value;
|
|
228
|
+
this._owner._scaleY = value;
|
|
229
|
+
this._owner.markBoundsDirty();
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Multiplies current scale by a factor.
|
|
235
|
+
* @param {number} factor - Multiplication factor
|
|
236
|
+
* @returns {Transform} this for chaining
|
|
237
|
+
*/
|
|
238
|
+
scaleBy(factor) {
|
|
239
|
+
this._owner._scaleX *= factor;
|
|
240
|
+
this._owner._scaleY *= factor;
|
|
241
|
+
this._owner.markBoundsDirty();
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ============================================================
|
|
246
|
+
// Batch Operations
|
|
247
|
+
// ============================================================
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Sets multiple properties at once.
|
|
251
|
+
* @param {Object} props - Object containing properties to set
|
|
252
|
+
* @param {number} [props.x] - X position
|
|
253
|
+
* @param {number} [props.y] - Y position
|
|
254
|
+
* @param {number} [props.width] - Width
|
|
255
|
+
* @param {number} [props.height] - Height
|
|
256
|
+
* @param {number} [props.rotation] - Rotation in degrees
|
|
257
|
+
* @param {number} [props.scaleX] - Horizontal scale
|
|
258
|
+
* @param {number} [props.scaleY] - Vertical scale
|
|
259
|
+
* @returns {Transform} this for chaining
|
|
260
|
+
*/
|
|
261
|
+
set(props) {
|
|
262
|
+
let visualChange = false;
|
|
263
|
+
if (props.x !== undefined) this._owner._x = props.x;
|
|
264
|
+
if (props.y !== undefined) this._owner._y = props.y;
|
|
265
|
+
if (props.width !== undefined) { this._owner._width = Math.max(0, props.width); visualChange = true; }
|
|
266
|
+
if (props.height !== undefined) { this._owner._height = Math.max(0, props.height); visualChange = true; }
|
|
267
|
+
if (props.rotation !== undefined) { this._owner._rotation = props.rotation * Math.PI / 180; }
|
|
268
|
+
if (props.scaleX !== undefined) { this._owner._scaleX = props.scaleX; }
|
|
269
|
+
if (props.scaleY !== undefined) { this._owner._scaleY = props.scaleY; }
|
|
270
|
+
|
|
271
|
+
this._owner.markBoundsDirty();
|
|
272
|
+
if (visualChange) this._owner.invalidateCache?.();
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Resets all transforms to their default values.
|
|
278
|
+
* Position and dimensions are preserved.
|
|
279
|
+
* @returns {Transform} this for chaining
|
|
280
|
+
*/
|
|
281
|
+
reset() {
|
|
282
|
+
this._owner._rotation = 0;
|
|
283
|
+
this._owner._scaleX = 1;
|
|
284
|
+
this._owner._scaleY = 1;
|
|
285
|
+
this._owner.markBoundsDirty();
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Resets everything including position and dimensions.
|
|
291
|
+
* @returns {Transform} this for chaining
|
|
292
|
+
*/
|
|
293
|
+
resetAll() {
|
|
294
|
+
this._owner._x = 0;
|
|
295
|
+
this._owner._y = 0;
|
|
296
|
+
this._owner._width = 0;
|
|
297
|
+
this._owner._height = 0;
|
|
298
|
+
this._owner._rotation = 0;
|
|
299
|
+
this._owner._scaleX = 1;
|
|
300
|
+
this._owner._scaleY = 1;
|
|
301
|
+
this._owner.markBoundsDirty();
|
|
302
|
+
this._owner.invalidateCache?.(); // Only for width/height reset
|
|
303
|
+
return this;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ============================================================
|
|
307
|
+
// Utility Methods
|
|
308
|
+
// ============================================================
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Creates a copy of the current transform values.
|
|
312
|
+
* @returns {{x: number, y: number, width: number, height: number, rotation: number, scaleX: number, scaleY: number}}
|
|
313
|
+
*/
|
|
314
|
+
toObject() {
|
|
315
|
+
return {
|
|
316
|
+
x: this._owner._x,
|
|
317
|
+
y: this._owner._y,
|
|
318
|
+
width: this._owner._width,
|
|
319
|
+
height: this._owner._height,
|
|
320
|
+
rotation: this._owner._rotation * 180 / Math.PI, // Convert back to degrees
|
|
321
|
+
scaleX: this._owner._scaleX,
|
|
322
|
+
scaleY: this._owner._scaleY
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Copies transform values from another Transform or plain object.
|
|
328
|
+
* @param {Transform|Object} source - Source to copy from
|
|
329
|
+
* @returns {Transform} this for chaining
|
|
330
|
+
*/
|
|
331
|
+
copyFrom(source) {
|
|
332
|
+
const values = source instanceof Transform ? source.toObject() : source;
|
|
333
|
+
return this.set(values);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Static helper to handle property setter access.
|
|
338
|
+
* Called by Geometry2d and Transformable setters to enforce strictMode.
|
|
339
|
+
* @param {string} property - The property name being set
|
|
340
|
+
* @param {*} value - The value being assigned
|
|
341
|
+
* @throws {Error} When strictMode is true
|
|
342
|
+
*/
|
|
343
|
+
static handleDirectSet(property, value) {
|
|
344
|
+
if (Transform.strictMode) {
|
|
345
|
+
throw new Error(
|
|
346
|
+
`Direct property assignment "${property} = ${value}" is disabled. ` +
|
|
347
|
+
`Use shape.transform.${property}(${value}) instead. ` +
|
|
348
|
+
`Set Transform.strictMode = false to allow direct assignment.`
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
console.warn(
|
|
352
|
+
`[Deprecation] Direct assignment "${property} = ${value}" is deprecated. ` +
|
|
353
|
+
`Use shape.transform.${property}(${value}) instead.`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Renderable } from "./renderable.js";
|
|
2
|
+
import { Painter } from "../painter/painter.js";
|
|
3
|
+
import { Transform } from "./transform.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Transformable
|
|
7
|
+
* --------------
|
|
8
|
+
*
|
|
9
|
+
* A renderable object that supports **canvas transformations**:
|
|
10
|
+
* - Scaling
|
|
11
|
+
* - Rotation
|
|
12
|
+
*
|
|
13
|
+
* ### Architectural Role
|
|
14
|
+
*
|
|
15
|
+
* - Extends the render lifecycle by wrapping `draw()` inside a transformed context.
|
|
16
|
+
* - Adds transform properties (`scaleX`, `scaleY`, `rotation`)
|
|
17
|
+
* - Introduces the `.transform` API for fluent, chainable property access
|
|
18
|
+
*
|
|
19
|
+
* ### Transform API
|
|
20
|
+
*
|
|
21
|
+
* The `.transform` property provides a fluent API for modifying properties:
|
|
22
|
+
*
|
|
23
|
+
* ```javascript
|
|
24
|
+
* // Fluent chaining
|
|
25
|
+
* shape.transform
|
|
26
|
+
* .x(100).y(200)
|
|
27
|
+
* .width(50).height(50)
|
|
28
|
+
* .rotation(45)
|
|
29
|
+
* .scale(0.8);
|
|
30
|
+
*
|
|
31
|
+
* // Batch updates
|
|
32
|
+
* shape.transform.set({ x: 100, y: 200, rotation: 45 });
|
|
33
|
+
*
|
|
34
|
+
* // Relative transforms
|
|
35
|
+
* shape.transform.translateBy(10, 20);
|
|
36
|
+
* shape.transform.rotateBy(15);
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* This is the final base layer before custom shape logic is introduced.
|
|
40
|
+
*
|
|
41
|
+
* @abstract
|
|
42
|
+
* @extends Renderable
|
|
43
|
+
*/
|
|
44
|
+
export class Transformable extends Renderable {
|
|
45
|
+
/**
|
|
46
|
+
* @param {Object} [options={}]
|
|
47
|
+
* @param {number} [options.rotation=0] - Rotation in degrees (clockwise)
|
|
48
|
+
* @param {number} [options.scaleX=1] - Horizontal scale factor
|
|
49
|
+
* @param {number} [options.scaleY=1] - Vertical scale factor
|
|
50
|
+
*/
|
|
51
|
+
constructor(options = {}) {
|
|
52
|
+
super(options);
|
|
53
|
+
this._rotation = options.rotation * Math.PI / 180 ?? 0;
|
|
54
|
+
this._scaleX = options.scaleX ?? 1;
|
|
55
|
+
this._scaleY = options.scaleY ?? 1;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fluent transform API for modifying spatial and transform properties.
|
|
59
|
+
* @type {Transform}
|
|
60
|
+
*/
|
|
61
|
+
this.transform = new Transform(this);
|
|
62
|
+
|
|
63
|
+
this.logger.log("Transformable", this.x, this.y, this.width, this.height);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The main rendering method.
|
|
68
|
+
* Applies transforms and draws debug bounding box.
|
|
69
|
+
* Subclasses should call super.draw() before their drawing logic.
|
|
70
|
+
*/
|
|
71
|
+
draw() {
|
|
72
|
+
this.applyTransforms();
|
|
73
|
+
this.drawDebug();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Applies canvas transform context.
|
|
78
|
+
* Order: rotate → scale
|
|
79
|
+
*/
|
|
80
|
+
applyTransforms() {
|
|
81
|
+
if (this._isCaching) return;
|
|
82
|
+
Painter.rotate(this._rotation);
|
|
83
|
+
Painter.scale(this._scaleX, this._scaleY);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gets the object's rotation in radians.
|
|
88
|
+
* @type {number}
|
|
89
|
+
*/
|
|
90
|
+
get rotation() {
|
|
91
|
+
return this._rotation;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
set rotation(v) {
|
|
95
|
+
this._rotation = v * Math.PI / 180;
|
|
96
|
+
this.markBoundsDirty();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Gets horizontal scale factor.
|
|
101
|
+
* @type {number}
|
|
102
|
+
*/
|
|
103
|
+
get scaleX() {
|
|
104
|
+
return this._scaleX;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
set scaleX(v) {
|
|
108
|
+
this._scaleX = v;
|
|
109
|
+
this.markBoundsDirty();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Gets vertical scale factor.
|
|
114
|
+
* @type {number}
|
|
115
|
+
*/
|
|
116
|
+
get scaleY() {
|
|
117
|
+
return this._scaleY;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
set scaleY(v) {
|
|
121
|
+
this._scaleY = v;
|
|
122
|
+
this.markBoundsDirty();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Calculates the bounding box *after* applying rotation and scale.
|
|
127
|
+
* Used by Geometry2d → getBounds().
|
|
128
|
+
*
|
|
129
|
+
* @override
|
|
130
|
+
* @protected
|
|
131
|
+
* @returns {{x: number, y: number, width: number, height: number}}
|
|
132
|
+
*/
|
|
133
|
+
calculateBounds() {
|
|
134
|
+
const halfW = this.width / 2;
|
|
135
|
+
const halfH = this.height / 2;
|
|
136
|
+
|
|
137
|
+
const corners = [
|
|
138
|
+
{ x: -halfW, y: -halfH },
|
|
139
|
+
{ x: halfW, y: -halfH },
|
|
140
|
+
{ x: halfW, y: halfH },
|
|
141
|
+
{ x: -halfW, y: halfH },
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
const cos = Math.cos(this._rotation);
|
|
145
|
+
const sin = Math.sin(this._rotation);
|
|
146
|
+
|
|
147
|
+
const transformed = corners.map(({ x, y }) => {
|
|
148
|
+
x *= this._scaleX;
|
|
149
|
+
y *= this._scaleY;
|
|
150
|
+
|
|
151
|
+
const rx = x * cos - y * sin;
|
|
152
|
+
const ry = x * sin + y * cos;
|
|
153
|
+
|
|
154
|
+
return { x: rx + this.x, y: ry + this.y };
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const xs = transformed.map((p) => p.x);
|
|
158
|
+
const ys = transformed.map((p) => p.y);
|
|
159
|
+
|
|
160
|
+
const minX = Math.min(...xs);
|
|
161
|
+
const maxX = Math.max(...xs);
|
|
162
|
+
const minY = Math.min(...ys);
|
|
163
|
+
const maxY = Math.max(...ys);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
x: (minX + maxX) / 2,
|
|
167
|
+
y: (minY + maxY) / 2,
|
|
168
|
+
width: maxX - minX,
|
|
169
|
+
height: maxY - minY,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Shape } from "./shape.js";
|
|
2
|
+
import { Painter } from "../painter/painter.js";
|
|
3
|
+
|
|
4
|
+
export class Triangle extends Shape {
|
|
5
|
+
constructor(size = 50, options = {}) {
|
|
6
|
+
super(options);
|
|
7
|
+
this.size = size;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
draw() {
|
|
11
|
+
super.draw();
|
|
12
|
+
const half = this.size / 2;
|
|
13
|
+
const points = [
|
|
14
|
+
{ x: 0, y: -half },
|
|
15
|
+
{ x: half, y: half },
|
|
16
|
+
{ x: -half, y: half },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
Painter.shapes.polygon(
|
|
20
|
+
points,
|
|
21
|
+
this.color,
|
|
22
|
+
this.stroke,
|
|
23
|
+
this.lineWidth
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCanvas Sound Module
|
|
3
|
+
* Procedural audio generation for games and creative coding
|
|
4
|
+
* @module sound
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Main classes
|
|
8
|
+
export { Synth } from "./synth.js";
|
|
9
|
+
export { Sound } from "./sound.js";
|
|
10
|
+
|
|
11
|
+
// Sub-modules (for advanced usage)
|
|
12
|
+
export { SynthOscillators } from "./synth.oscillators.js";
|
|
13
|
+
export { SynthEffects } from "./synth.effects.js";
|
|
14
|
+
export { SynthEnvelope } from "./synth.envelope.js";
|
|
15
|
+
export { SynthNoise } from "./synth.noise.js";
|
|
16
|
+
export { SynthMusical } from "./synth.musical.js";
|
|
17
|
+
export { SynthAnalyzer } from "./synth.analyzer.js";
|