@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
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: true,
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
+ });