@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.
Files changed (78) hide show
  1. package/dist/aizawa.html +27 -0
  2. package/dist/clifford.html +25 -0
  3. package/dist/cmb.html +24 -0
  4. package/dist/dadras.html +26 -0
  5. package/dist/dejong.html +25 -0
  6. package/dist/gcanvas.es.js +5130 -372
  7. package/dist/gcanvas.es.min.js +1 -1
  8. package/dist/gcanvas.umd.js +1 -1
  9. package/dist/gcanvas.umd.min.js +1 -1
  10. package/dist/halvorsen.html +27 -0
  11. package/dist/index.html +96 -48
  12. package/dist/js/aizawa.js +425 -0
  13. package/dist/js/bezier.js +5 -5
  14. package/dist/js/clifford.js +236 -0
  15. package/dist/js/cmb.js +594 -0
  16. package/dist/js/dadras.js +405 -0
  17. package/dist/js/dejong.js +257 -0
  18. package/dist/js/halvorsen.js +405 -0
  19. package/dist/js/isometric.js +34 -46
  20. package/dist/js/lorenz.js +425 -0
  21. package/dist/js/painter.js +8 -8
  22. package/dist/js/rossler.js +480 -0
  23. package/dist/js/schrodinger.js +314 -18
  24. package/dist/js/thomas.js +394 -0
  25. package/dist/lorenz.html +27 -0
  26. package/dist/rossler.html +27 -0
  27. package/dist/scene-interactivity-test.html +220 -0
  28. package/dist/thomas.html +27 -0
  29. package/package.json +1 -1
  30. package/readme.md +30 -22
  31. package/src/game/objects/go.js +7 -0
  32. package/src/game/objects/index.js +2 -0
  33. package/src/game/objects/isometric-scene.js +53 -3
  34. package/src/game/objects/layoutscene.js +57 -0
  35. package/src/game/objects/mask.js +241 -0
  36. package/src/game/objects/scene.js +19 -0
  37. package/src/game/objects/wrapper.js +14 -2
  38. package/src/game/pipeline.js +17 -0
  39. package/src/game/ui/button.js +101 -16
  40. package/src/game/ui/theme.js +0 -6
  41. package/src/game/ui/togglebutton.js +25 -14
  42. package/src/game/ui/tooltip.js +12 -4
  43. package/src/index.js +3 -0
  44. package/src/io/gesture.js +409 -0
  45. package/src/io/index.js +4 -1
  46. package/src/io/keys.js +9 -1
  47. package/src/io/screen.js +476 -0
  48. package/src/math/attractors.js +664 -0
  49. package/src/math/heat.js +106 -0
  50. package/src/math/index.js +1 -0
  51. package/src/mixins/draggable.js +15 -19
  52. package/src/painter/painter.shapes.js +11 -5
  53. package/src/particle/particle-system.js +165 -1
  54. package/src/physics/index.js +26 -0
  55. package/src/physics/physics-updaters.js +333 -0
  56. package/src/physics/physics.js +375 -0
  57. package/src/shapes/image.js +5 -5
  58. package/src/shapes/index.js +2 -0
  59. package/src/shapes/parallelogram.js +147 -0
  60. package/src/shapes/righttriangle.js +115 -0
  61. package/src/shapes/svg.js +281 -100
  62. package/src/shapes/text.js +22 -6
  63. package/src/shapes/transformable.js +5 -0
  64. package/src/sound/effects.js +807 -0
  65. package/src/sound/index.js +13 -0
  66. package/src/webgl/index.js +7 -0
  67. package/src/webgl/shaders/clifford-point-shaders.js +131 -0
  68. package/src/webgl/shaders/dejong-point-shaders.js +131 -0
  69. package/src/webgl/shaders/point-sprite-shaders.js +152 -0
  70. package/src/webgl/webgl-clifford-renderer.js +477 -0
  71. package/src/webgl/webgl-dejong-renderer.js +472 -0
  72. package/src/webgl/webgl-line-renderer.js +391 -0
  73. package/src/webgl/webgl-particle-renderer.js +410 -0
  74. package/types/index.d.ts +30 -2
  75. package/types/io.d.ts +217 -0
  76. package/types/physics.d.ts +299 -0
  77. package/types/shapes.d.ts +8 -0
  78. package/types/webgl.d.ts +188 -109
@@ -0,0 +1,391 @@
1
+ /**
2
+ * WebGLLineRenderer - GPU-accelerated line rendering
3
+ *
4
+ * Renders line segments using WebGL GL_LINES for high performance.
5
+ * Designed for particle trails, attractor visualizations, and similar
6
+ * effects that require many line segments.
7
+ *
8
+ * Features:
9
+ * - Pre-allocated buffers for zero-allocation rendering
10
+ * - Additive or alpha blending modes
11
+ * - Per-vertex colors for gradient trails
12
+ * - Compositing onto Canvas 2D
13
+ *
14
+ * @example
15
+ * const renderer = new WebGLLineRenderer(50000); // 50k line segments
16
+ * renderer.updateLines(lineData);
17
+ * renderer.render(segmentCount);
18
+ * renderer.compositeOnto(ctx, 0, 0);
19
+ */
20
+
21
+ // Line vertex shader
22
+ const LINE_VERTEX_SHADER = `
23
+ precision highp float;
24
+
25
+ attribute vec2 aPosition; // Screen position (pixels)
26
+ attribute vec4 aColor; // RGBA color (0-1 range, premultiplied)
27
+
28
+ varying vec4 vColor;
29
+
30
+ uniform vec2 uResolution; // Canvas dimensions
31
+
32
+ void main() {
33
+ // Convert from pixel coords to clip space (-1 to 1)
34
+ vec2 clipPos = (aPosition / uResolution) * 2.0 - 1.0;
35
+ clipPos.y = -clipPos.y; // Flip Y (canvas Y is down, GL Y is up)
36
+
37
+ gl_Position = vec4(clipPos, 0.0, 1.0);
38
+ vColor = aColor;
39
+ }
40
+ `;
41
+
42
+ // Line fragment shader
43
+ const LINE_FRAGMENT_SHADER = `
44
+ precision mediump float;
45
+
46
+ varying vec4 vColor;
47
+
48
+ void main() {
49
+ gl_FragColor = vColor;
50
+ }
51
+ `;
52
+
53
+ export class WebGLLineRenderer {
54
+ /**
55
+ * Create a WebGL line renderer
56
+ * @param {number} maxSegments - Maximum number of line segments (pre-allocated)
57
+ * @param {Object} options - Configuration options
58
+ * @param {number} options.width - Initial canvas width
59
+ * @param {number} options.height - Initial canvas height
60
+ * @param {string} options.blendMode - Blend mode: 'additive' or 'alpha'
61
+ */
62
+ constructor(maxSegments = 10000, options = {}) {
63
+ this.maxSegments = maxSegments;
64
+ this.width = options.width || 800;
65
+ this.height = options.height || 600;
66
+ this.blendMode = options.blendMode || 'additive';
67
+
68
+ // Create offscreen canvas
69
+ this.canvas = document.createElement('canvas');
70
+ this.canvas.width = this.width;
71
+ this.canvas.height = this.height;
72
+
73
+ // Get WebGL context
74
+ this.gl = this.canvas.getContext('webgl', {
75
+ alpha: true,
76
+ premultipliedAlpha: true,
77
+ antialias: true,
78
+ preserveDrawingBuffer: true,
79
+ });
80
+
81
+ if (!this.gl) {
82
+ console.warn('WebGL not available for line rendering');
83
+ this.available = false;
84
+ return;
85
+ }
86
+
87
+ this.available = true;
88
+
89
+ // Pre-allocate typed arrays (2 vertices per segment)
90
+ // Each vertex: x, y (position) + r, g, b, a (color)
91
+ this._positions = new Float32Array(maxSegments * 4); // 2 vertices * 2 coords
92
+ this._colors = new Float32Array(maxSegments * 8); // 2 vertices * 4 color components
93
+
94
+ // Setup WebGL
95
+ this._initGL();
96
+ this._createBuffers();
97
+ this._compileShaders();
98
+ this._setupBlending();
99
+ }
100
+
101
+ /**
102
+ * Check if WebGL is available
103
+ * @returns {boolean}
104
+ */
105
+ isAvailable() {
106
+ return this.available;
107
+ }
108
+
109
+ /**
110
+ * Initialize WebGL state
111
+ * @private
112
+ */
113
+ _initGL() {
114
+ const gl = this.gl;
115
+ gl.viewport(0, 0, this.width, this.height);
116
+ gl.enable(gl.BLEND);
117
+ }
118
+
119
+ /**
120
+ * Setup blending mode
121
+ * @private
122
+ */
123
+ _setupBlending() {
124
+ const gl = this.gl;
125
+
126
+ if (this.blendMode === 'additive') {
127
+ // Additive blending (screen/lighter mode)
128
+ gl.blendFunc(gl.ONE, gl.ONE);
129
+ } else {
130
+ // Standard alpha blending with premultiplied alpha
131
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Set blend mode
137
+ * @param {string} mode - 'additive' or 'alpha'
138
+ */
139
+ setBlendMode(mode) {
140
+ this.blendMode = mode;
141
+ if (this.available) {
142
+ this._setupBlending();
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Create GPU buffers
148
+ * @private
149
+ */
150
+ _createBuffers() {
151
+ const gl = this.gl;
152
+
153
+ // Position buffer (vec2 per vertex, 2 vertices per segment)
154
+ this.positionBuffer = gl.createBuffer();
155
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
156
+ gl.bufferData(gl.ARRAY_BUFFER, this._positions, gl.DYNAMIC_DRAW);
157
+
158
+ // Color buffer (vec4 per vertex, 2 vertices per segment)
159
+ this.colorBuffer = gl.createBuffer();
160
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
161
+ gl.bufferData(gl.ARRAY_BUFFER, this._colors, gl.DYNAMIC_DRAW);
162
+ }
163
+
164
+ /**
165
+ * Compile shaders
166
+ * @private
167
+ */
168
+ _compileShaders() {
169
+ const gl = this.gl;
170
+
171
+ // Compile vertex shader
172
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
173
+ gl.shaderSource(vertexShader, LINE_VERTEX_SHADER);
174
+ gl.compileShader(vertexShader);
175
+
176
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
177
+ console.error('Line vertex shader error:', gl.getShaderInfoLog(vertexShader));
178
+ this.available = false;
179
+ return;
180
+ }
181
+
182
+ // Compile fragment shader
183
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
184
+ gl.shaderSource(fragmentShader, LINE_FRAGMENT_SHADER);
185
+ gl.compileShader(fragmentShader);
186
+
187
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
188
+ console.error('Line fragment shader error:', gl.getShaderInfoLog(fragmentShader));
189
+ this.available = false;
190
+ return;
191
+ }
192
+
193
+ // Link program
194
+ this.program = gl.createProgram();
195
+ gl.attachShader(this.program, vertexShader);
196
+ gl.attachShader(this.program, fragmentShader);
197
+ gl.linkProgram(this.program);
198
+
199
+ if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
200
+ console.error('Line program link error:', gl.getProgramInfoLog(this.program));
201
+ this.available = false;
202
+ return;
203
+ }
204
+
205
+ gl.useProgram(this.program);
206
+
207
+ // Get attribute locations
208
+ this.aPosition = gl.getAttribLocation(this.program, 'aPosition');
209
+ this.aColor = gl.getAttribLocation(this.program, 'aColor');
210
+
211
+ // Get uniform locations
212
+ this.uResolution = gl.getUniformLocation(this.program, 'uResolution');
213
+
214
+ // Set initial resolution
215
+ gl.uniform2f(this.uResolution, this.width, this.height);
216
+
217
+ // Clean up shader objects
218
+ gl.deleteShader(vertexShader);
219
+ gl.deleteShader(fragmentShader);
220
+ }
221
+
222
+ /**
223
+ * Resize the renderer
224
+ * @param {number} width - New width
225
+ * @param {number} height - New height
226
+ */
227
+ resize(width, height) {
228
+ this.width = width;
229
+ this.height = height;
230
+ this.canvas.width = width;
231
+ this.canvas.height = height;
232
+
233
+ if (this.available) {
234
+ const gl = this.gl;
235
+ gl.viewport(0, 0, width, height);
236
+ gl.useProgram(this.program);
237
+ gl.uniform2f(this.uResolution, width, height);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Update line segment data in GPU buffers
243
+ *
244
+ * @param {Array} segments - Array of line segments
245
+ * Each segment: { x1, y1, x2, y2, r, g, b, a } or
246
+ * { x1, y1, x2, y2, r1, g1, b1, a1, r2, g2, b2, a2 } for gradient
247
+ * Colors should be 0-255 for RGB, 0-1 for alpha
248
+ * @returns {number} Number of segments updated
249
+ */
250
+ updateLines(segments) {
251
+ if (!this.available) return 0;
252
+
253
+ const count = Math.min(segments.length, this.maxSegments);
254
+ const gl = this.gl;
255
+
256
+ // Fill typed arrays
257
+ for (let i = 0; i < count; i++) {
258
+ const s = segments[i];
259
+ const pi = i * 4; // position index (2 vertices * 2 coords)
260
+ const ci = i * 8; // color index (2 vertices * 4 components)
261
+
262
+ // Positions
263
+ this._positions[pi] = s.x1;
264
+ this._positions[pi + 1] = s.y1;
265
+ this._positions[pi + 2] = s.x2;
266
+ this._positions[pi + 3] = s.y2;
267
+
268
+ // Colors (check for gradient or single color)
269
+ const hasGradient = s.r1 !== undefined;
270
+
271
+ if (hasGradient) {
272
+ // Gradient: different colors at each end
273
+ const a1 = s.a1 !== undefined ? s.a1 : 1;
274
+ const a2 = s.a2 !== undefined ? s.a2 : 1;
275
+
276
+ // Premultiplied alpha
277
+ this._colors[ci] = (s.r1 / 255) * a1;
278
+ this._colors[ci + 1] = (s.g1 / 255) * a1;
279
+ this._colors[ci + 2] = (s.b1 / 255) * a1;
280
+ this._colors[ci + 3] = a1;
281
+
282
+ this._colors[ci + 4] = (s.r2 / 255) * a2;
283
+ this._colors[ci + 5] = (s.g2 / 255) * a2;
284
+ this._colors[ci + 6] = (s.b2 / 255) * a2;
285
+ this._colors[ci + 7] = a2;
286
+ } else {
287
+ // Single color for both vertices
288
+ const a = s.a !== undefined ? s.a : 1;
289
+ const r = (s.r / 255) * a;
290
+ const g = (s.g / 255) * a;
291
+ const b = (s.b / 255) * a;
292
+
293
+ this._colors[ci] = r;
294
+ this._colors[ci + 1] = g;
295
+ this._colors[ci + 2] = b;
296
+ this._colors[ci + 3] = a;
297
+
298
+ this._colors[ci + 4] = r;
299
+ this._colors[ci + 5] = g;
300
+ this._colors[ci + 6] = b;
301
+ this._colors[ci + 7] = a;
302
+ }
303
+ }
304
+
305
+ // Upload to GPU
306
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
307
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._positions.subarray(0, count * 4));
308
+
309
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
310
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._colors.subarray(0, count * 8));
311
+
312
+ return count;
313
+ }
314
+
315
+ /**
316
+ * Clear the canvas
317
+ * @param {number} r - Red (0-1)
318
+ * @param {number} g - Green (0-1)
319
+ * @param {number} b - Blue (0-1)
320
+ * @param {number} a - Alpha (0-1)
321
+ */
322
+ clear(r = 0, g = 0, b = 0, a = 0) {
323
+ if (!this.available) return;
324
+ const gl = this.gl;
325
+ gl.clearColor(r, g, b, a);
326
+ gl.clear(gl.COLOR_BUFFER_BIT);
327
+ }
328
+
329
+ /**
330
+ * Render lines to the WebGL canvas
331
+ * @param {number} count - Number of line segments to render
332
+ */
333
+ render(count) {
334
+ if (!this.available || count === 0) return;
335
+
336
+ const gl = this.gl;
337
+
338
+ gl.useProgram(this.program);
339
+
340
+ // Bind position attribute
341
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
342
+ gl.enableVertexAttribArray(this.aPosition);
343
+ gl.vertexAttribPointer(this.aPosition, 2, gl.FLOAT, false, 0, 0);
344
+
345
+ // Bind color attribute
346
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
347
+ gl.enableVertexAttribArray(this.aColor);
348
+ gl.vertexAttribPointer(this.aColor, 4, gl.FLOAT, false, 0, 0);
349
+
350
+ // Draw all lines in a single call (2 vertices per segment)
351
+ gl.drawArrays(gl.LINES, 0, count * 2);
352
+ }
353
+
354
+ /**
355
+ * Composite the WebGL canvas onto a 2D canvas context
356
+ * @param {CanvasRenderingContext2D} ctx - Target 2D context
357
+ * @param {number} x - X position
358
+ * @param {number} y - Y position
359
+ */
360
+ compositeOnto(ctx, x = 0, y = 0) {
361
+ if (!this.available) return;
362
+ ctx.drawImage(this.canvas, x, y);
363
+ }
364
+
365
+ /**
366
+ * Get the WebGL canvas element
367
+ * @returns {HTMLCanvasElement}
368
+ */
369
+ getCanvas() {
370
+ return this.canvas;
371
+ }
372
+
373
+ /**
374
+ * Destroy the renderer and free resources
375
+ */
376
+ destroy() {
377
+ if (!this.available) return;
378
+
379
+ const gl = this.gl;
380
+
381
+ if (this.program) {
382
+ gl.deleteProgram(this.program);
383
+ }
384
+
385
+ gl.deleteBuffer(this.positionBuffer);
386
+ gl.deleteBuffer(this.colorBuffer);
387
+
388
+ this._positions = null;
389
+ this._colors = null;
390
+ }
391
+ }