@guinetik/gcanvas 1.0.5 → 2.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/dist/aizawa.html +27 -0
- package/dist/clifford.html +25 -0
- package/dist/cmb.html +24 -0
- package/dist/dadras.html +26 -0
- package/dist/dejong.html +25 -0
- package/dist/gcanvas.es.js +5130 -372
- package/dist/gcanvas.es.min.js +1 -1
- package/dist/gcanvas.umd.js +1 -1
- package/dist/gcanvas.umd.min.js +1 -1
- package/dist/halvorsen.html +27 -0
- package/dist/index.html +96 -48
- package/dist/js/aizawa.js +425 -0
- package/dist/js/bezier.js +5 -5
- package/dist/js/clifford.js +236 -0
- package/dist/js/cmb.js +594 -0
- package/dist/js/dadras.js +405 -0
- package/dist/js/dejong.js +257 -0
- package/dist/js/halvorsen.js +405 -0
- package/dist/js/isometric.js +34 -46
- package/dist/js/lorenz.js +425 -0
- package/dist/js/painter.js +8 -8
- package/dist/js/rossler.js +480 -0
- package/dist/js/schrodinger.js +314 -18
- package/dist/js/thomas.js +394 -0
- package/dist/lorenz.html +27 -0
- package/dist/rossler.html +27 -0
- package/dist/scene-interactivity-test.html +220 -0
- package/dist/thomas.html +27 -0
- package/package.json +1 -1
- package/readme.md +30 -22
- package/src/game/objects/go.js +7 -0
- package/src/game/objects/index.js +2 -0
- package/src/game/objects/isometric-scene.js +53 -3
- package/src/game/objects/layoutscene.js +57 -0
- package/src/game/objects/mask.js +241 -0
- package/src/game/objects/scene.js +19 -0
- package/src/game/objects/wrapper.js +14 -2
- package/src/game/pipeline.js +17 -0
- package/src/game/ui/button.js +101 -16
- package/src/game/ui/theme.js +0 -6
- package/src/game/ui/togglebutton.js +25 -14
- package/src/game/ui/tooltip.js +12 -4
- package/src/index.js +3 -0
- package/src/io/gesture.js +409 -0
- package/src/io/index.js +4 -1
- package/src/io/keys.js +9 -1
- package/src/io/screen.js +476 -0
- package/src/math/attractors.js +664 -0
- package/src/math/heat.js +106 -0
- package/src/math/index.js +1 -0
- package/src/mixins/draggable.js +15 -19
- package/src/painter/painter.shapes.js +11 -5
- package/src/particle/particle-system.js +165 -1
- package/src/physics/index.js +26 -0
- package/src/physics/physics-updaters.js +333 -0
- package/src/physics/physics.js +375 -0
- package/src/shapes/image.js +5 -5
- package/src/shapes/index.js +2 -0
- package/src/shapes/parallelogram.js +147 -0
- package/src/shapes/righttriangle.js +115 -0
- package/src/shapes/svg.js +281 -100
- package/src/shapes/text.js +22 -6
- package/src/shapes/transformable.js +5 -0
- package/src/sound/effects.js +807 -0
- package/src/sound/index.js +13 -0
- package/src/webgl/index.js +7 -0
- package/src/webgl/shaders/clifford-point-shaders.js +131 -0
- package/src/webgl/shaders/dejong-point-shaders.js +131 -0
- package/src/webgl/shaders/point-sprite-shaders.js +152 -0
- package/src/webgl/webgl-clifford-renderer.js +477 -0
- package/src/webgl/webgl-dejong-renderer.js +472 -0
- package/src/webgl/webgl-line-renderer.js +391 -0
- package/src/webgl/webgl-particle-renderer.js +410 -0
- package/types/index.d.ts +30 -2
- package/types/io.d.ts +217 -0
- package/types/physics.d.ts +299 -0
- package/types/shapes.d.ts +8 -0
- package/types/webgl.d.ts +188 -109
package/dist/js/bezier.js
CHANGED
|
@@ -75,7 +75,7 @@ class BezierDemoGame extends Game {
|
|
|
75
75
|
const config = this.getResponsiveConfig();
|
|
76
76
|
|
|
77
77
|
this.uiScene = new BezierUIScene(this, this.bezierScene, {
|
|
78
|
-
debug:
|
|
78
|
+
debug: false,
|
|
79
79
|
debugColor: "magenta",
|
|
80
80
|
width: config.layoutWidth,
|
|
81
81
|
height: config.layoutHeight,
|
|
@@ -612,7 +612,7 @@ class BezierUIScene extends Scene {
|
|
|
612
612
|
this.currentMode = this.addModeButton;
|
|
613
613
|
}
|
|
614
614
|
},
|
|
615
|
-
})
|
|
615
|
+
}),
|
|
616
616
|
);
|
|
617
617
|
|
|
618
618
|
this.editModeButton = this.layout.add(
|
|
@@ -630,7 +630,7 @@ class BezierUIScene extends Scene {
|
|
|
630
630
|
this.currentMode = this.editModeButton;
|
|
631
631
|
}
|
|
632
632
|
},
|
|
633
|
-
})
|
|
633
|
+
}),
|
|
634
634
|
);
|
|
635
635
|
|
|
636
636
|
this.cutModeButton = this.layout.add(
|
|
@@ -645,7 +645,7 @@ class BezierUIScene extends Scene {
|
|
|
645
645
|
this.currentMode = null;
|
|
646
646
|
this.bezierScene.cutShape();
|
|
647
647
|
},
|
|
648
|
-
})
|
|
648
|
+
}),
|
|
649
649
|
);
|
|
650
650
|
|
|
651
651
|
this.clearButton = this.layout.add(
|
|
@@ -657,7 +657,7 @@ class BezierUIScene extends Scene {
|
|
|
657
657
|
onClick: () => {
|
|
658
658
|
this.bezierScene.clear();
|
|
659
659
|
},
|
|
660
|
-
})
|
|
660
|
+
}),
|
|
661
661
|
);
|
|
662
662
|
|
|
663
663
|
this.add(this.layout);
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clifford Attractor 2D Visualization
|
|
3
|
+
*
|
|
4
|
+
* A 2D iterative attractor that creates intricate fractal patterns
|
|
5
|
+
* using simple trigonometric functions. Unlike continuous attractors,
|
|
6
|
+
* this is a discrete map revealed by accumulating many iterations.
|
|
7
|
+
*
|
|
8
|
+
* Engine-aligned procedural WebGL approach:
|
|
9
|
+
* - Seed buffer stays on the GPU
|
|
10
|
+
* - Vertex shader iterates the Clifford map
|
|
11
|
+
* - Output is composited onto the main 2D canvas for trail accumulation
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Game, Gesture, Screen, Attractors, Painter, WebGLCliffordRenderer } from "/gcanvas.es.min.js";
|
|
15
|
+
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// CONFIGURATION
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
const CONFIG = {
|
|
21
|
+
// Attractor settings (GPU iterations per point)
|
|
22
|
+
attractor: {
|
|
23
|
+
iterations: 120,
|
|
24
|
+
params: {
|
|
25
|
+
// Default Clifford parameters
|
|
26
|
+
a: -1.4,
|
|
27
|
+
b: 1.6,
|
|
28
|
+
c: 1.0,
|
|
29
|
+
d: 0.7,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Procedural point settings
|
|
34
|
+
points: {
|
|
35
|
+
seedCount: 1 << 18, // 262144
|
|
36
|
+
pointSize: 1.0,
|
|
37
|
+
pointScale: 0.5,
|
|
38
|
+
shape: "glow", // 'circle' | 'glow' | 'square' | 'softSquare'
|
|
39
|
+
blendMode: "additive", // WebGL blending
|
|
40
|
+
compositeBlendMode: "lighter", // Canvas 2D blend for compositing
|
|
41
|
+
color: { r: 1, g: 1, b: 1, a: 0.12 }, // used when colorMode=0
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Visual settings
|
|
45
|
+
visual: {
|
|
46
|
+
// Lorenz-style speed→hue coloring
|
|
47
|
+
minHue: 180, // fast
|
|
48
|
+
maxHue: 320, // slow
|
|
49
|
+
maxSpeed: 0.9,
|
|
50
|
+
saturation: 80,
|
|
51
|
+
lightness: 60,
|
|
52
|
+
alpha: 0.14,
|
|
53
|
+
hueShiftSpeed: 6,
|
|
54
|
+
fadeSpeed: 0.02,
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// Zoom settings
|
|
58
|
+
zoom: {
|
|
59
|
+
min: 0.3,
|
|
60
|
+
max: 3.0,
|
|
61
|
+
speed: 0.5,
|
|
62
|
+
easing: 0.12,
|
|
63
|
+
baseScreenSize: 600,
|
|
64
|
+
initialMultiplier: 0.75,
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// Rotation settings (drag to rotate)
|
|
68
|
+
rotation: {
|
|
69
|
+
speed: 0.01, // radians per pixel
|
|
70
|
+
easing: 0.15,
|
|
71
|
+
autoSpeed: 0.18, // radians/sec
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// DEMO CLASS
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
class CliffordDemo extends Game {
|
|
80
|
+
constructor(canvas) {
|
|
81
|
+
super(canvas);
|
|
82
|
+
this.backgroundColor = "#000";
|
|
83
|
+
this.enableFluidSize();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
init() {
|
|
87
|
+
super.init();
|
|
88
|
+
|
|
89
|
+
this.attractor = Attractors.clifford;
|
|
90
|
+
console.log(`Attractor: ${this.attractor.name}`);
|
|
91
|
+
console.log(`Equations:`, this.attractor.equations);
|
|
92
|
+
|
|
93
|
+
const { min, max, baseScreenSize } = CONFIG.zoom;
|
|
94
|
+
const initialZoomRaw = Screen.minDimension() / baseScreenSize;
|
|
95
|
+
const initialZoom = Math.min(
|
|
96
|
+
max,
|
|
97
|
+
Math.max(min, initialZoomRaw * CONFIG.zoom.initialMultiplier)
|
|
98
|
+
);
|
|
99
|
+
this.zoom = initialZoom;
|
|
100
|
+
this.targetZoom = initialZoom;
|
|
101
|
+
this.defaultZoom = initialZoom;
|
|
102
|
+
|
|
103
|
+
// Continuous auto-rotation + user-controlled offset (drag)
|
|
104
|
+
this.baseRotation = 0;
|
|
105
|
+
this.userRotation = 0;
|
|
106
|
+
this.targetUserRotation = 0;
|
|
107
|
+
|
|
108
|
+
// Gesture handler for zoom + rotation
|
|
109
|
+
this.gesture = new Gesture(this.canvas, {
|
|
110
|
+
onZoom: (delta) => {
|
|
111
|
+
this.targetZoom *= 1 + delta * CONFIG.zoom.speed;
|
|
112
|
+
},
|
|
113
|
+
onPan: (dx) => {
|
|
114
|
+
this.targetUserRotation += dx * CONFIG.rotation.speed;
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Double-click to reset
|
|
119
|
+
this.canvas.addEventListener("dblclick", () => {
|
|
120
|
+
this.targetZoom = this.defaultZoom;
|
|
121
|
+
this.baseRotation = 0;
|
|
122
|
+
this.targetUserRotation = 0;
|
|
123
|
+
this._didFirstClear = false;
|
|
124
|
+
this.renderer?.regenerateSeeds();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.time = 0;
|
|
128
|
+
this._didFirstClear = false;
|
|
129
|
+
|
|
130
|
+
// Procedural renderer (GPU iteration)
|
|
131
|
+
this.renderer = new WebGLCliffordRenderer(CONFIG.points.seedCount, {
|
|
132
|
+
width: this.width,
|
|
133
|
+
height: this.height,
|
|
134
|
+
shape: CONFIG.points.shape,
|
|
135
|
+
blendMode: CONFIG.points.blendMode,
|
|
136
|
+
pointSize: CONFIG.points.pointSize,
|
|
137
|
+
pointScale: CONFIG.points.pointScale,
|
|
138
|
+
iterations: CONFIG.attractor.iterations,
|
|
139
|
+
color: CONFIG.points.color,
|
|
140
|
+
colorMode: 1,
|
|
141
|
+
hueRange: { minHue: CONFIG.visual.minHue, maxHue: CONFIG.visual.maxHue },
|
|
142
|
+
maxSpeed: CONFIG.visual.maxSpeed,
|
|
143
|
+
saturation: CONFIG.visual.saturation / 100,
|
|
144
|
+
lightness: CONFIG.visual.lightness / 100,
|
|
145
|
+
alpha: CONFIG.visual.alpha,
|
|
146
|
+
hueShiftSpeed: CONFIG.visual.hueShiftSpeed,
|
|
147
|
+
params: CONFIG.attractor.params,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!this.renderer.isAvailable()) {
|
|
151
|
+
console.warn("WebGL not available for Clifford demo");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
onResize() {
|
|
156
|
+
const { min, max, baseScreenSize } = CONFIG.zoom;
|
|
157
|
+
const initialZoomRaw = Screen.minDimension() / baseScreenSize;
|
|
158
|
+
this.defaultZoom = Math.min(
|
|
159
|
+
max,
|
|
160
|
+
Math.max(min, initialZoomRaw * CONFIG.zoom.initialMultiplier)
|
|
161
|
+
);
|
|
162
|
+
this._didFirstClear = false;
|
|
163
|
+
this.renderer?.resize(this.width, this.height);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
update(dt) {
|
|
167
|
+
super.update(dt);
|
|
168
|
+
this.zoom += (this.targetZoom - this.zoom) * CONFIG.zoom.easing;
|
|
169
|
+
this.time += dt;
|
|
170
|
+
|
|
171
|
+
const TAU = Math.PI * 2;
|
|
172
|
+
this.baseRotation = (this.baseRotation + CONFIG.rotation.autoSpeed * dt) % TAU;
|
|
173
|
+
this.userRotation += (this.targetUserRotation - this.userRotation) * CONFIG.rotation.easing;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
clear() {
|
|
177
|
+
// Fade the canvas to create persistent trails.
|
|
178
|
+
if (!this._didFirstClear) {
|
|
179
|
+
Painter.useCtx((ctx) => {
|
|
180
|
+
ctx.fillStyle = "#000";
|
|
181
|
+
ctx.fillRect(0, 0, this.width, this.height);
|
|
182
|
+
});
|
|
183
|
+
this._didFirstClear = true;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const fade = CONFIG.visual.fadeSpeed;
|
|
188
|
+
if (fade <= 0) return;
|
|
189
|
+
|
|
190
|
+
Painter.useCtx((ctx) => {
|
|
191
|
+
ctx.globalCompositeOperation = "source-over";
|
|
192
|
+
ctx.fillStyle = `rgba(0, 0, 0, ${fade})`;
|
|
193
|
+
ctx.fillRect(0, 0, this.width, this.height);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
render() {
|
|
198
|
+
Painter.setContext(this.ctx);
|
|
199
|
+
if (this.running) this.clear();
|
|
200
|
+
|
|
201
|
+
if (this.renderer?.isAvailable()) {
|
|
202
|
+
this.renderer.setParams(CONFIG.attractor.params);
|
|
203
|
+
this.renderer.setIterations(CONFIG.attractor.iterations);
|
|
204
|
+
this.renderer.setZoom(this.zoom);
|
|
205
|
+
this.renderer.setTransform(
|
|
206
|
+
WebGLCliffordRenderer.rotationMat3(this.baseRotation + this.userRotation)
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
this.renderer.clear(0, 0, 0, 0);
|
|
210
|
+
this.renderer.render(this.time);
|
|
211
|
+
|
|
212
|
+
Painter.useCtx((ctx) => {
|
|
213
|
+
const prev = ctx.globalCompositeOperation;
|
|
214
|
+
ctx.globalCompositeOperation = CONFIG.points.compositeBlendMode;
|
|
215
|
+
this.renderer.compositeOnto(ctx, 0, 0);
|
|
216
|
+
ctx.globalCompositeOperation = prev;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
destroy() {
|
|
222
|
+
this.gesture?.destroy();
|
|
223
|
+
this.renderer?.destroy();
|
|
224
|
+
super.destroy?.();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
229
|
+
// INITIALIZATION
|
|
230
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
window.addEventListener("load", () => {
|
|
233
|
+
const canvas = document.getElementById("game");
|
|
234
|
+
const demo = new CliffordDemo(canvas);
|
|
235
|
+
demo.start();
|
|
236
|
+
});
|