@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
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module math/attractors
|
|
3
|
+
* @description Strange attractor functions for chaotic dynamical systems.
|
|
4
|
+
*
|
|
5
|
+
* Provides pure mathematical functions for computing attractor dynamics.
|
|
6
|
+
* Each attractor returns both the new position and velocity (derivatives)
|
|
7
|
+
* for use in visualization, coloring, and analysis.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { Attractors } from '@guinetik/gcanvas';
|
|
11
|
+
*
|
|
12
|
+
* // Use default parameters
|
|
13
|
+
* const { position, velocity } = Attractors.lorenz.step({ x: 1, y: 1, z: 1 }, 0.01);
|
|
14
|
+
*
|
|
15
|
+
* // Custom parameters
|
|
16
|
+
* const step = Attractors.lorenz.createStepper({ sigma: 10, rho: 28, beta: 8/3 });
|
|
17
|
+
* const result = step({ x: 1, y: 1, z: 1 }, 0.01);
|
|
18
|
+
*
|
|
19
|
+
* // Get just derivatives (for advanced use)
|
|
20
|
+
* const { dx, dy, dz } = Attractors.lorenz.derivatives({ x: 1, y: 1, z: 1 });
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Attractor types
|
|
25
|
+
*/
|
|
26
|
+
export const AttractorType = {
|
|
27
|
+
CONTINUOUS: "continuous", // Uses dt for integration
|
|
28
|
+
ITERATIVE: "iterative", // Direct mapping (no dt)
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Attractor dimensions
|
|
33
|
+
*/
|
|
34
|
+
export const AttractorDimension = {
|
|
35
|
+
TWO_D: 2,
|
|
36
|
+
THREE_D: 3,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// LORENZ ATTRACTOR
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Lorenz Attractor (1963)
|
|
45
|
+
*
|
|
46
|
+
* The classic "butterfly effect" attractor discovered by Edward Lorenz
|
|
47
|
+
* while studying atmospheric convection.
|
|
48
|
+
*
|
|
49
|
+
* Equations:
|
|
50
|
+
* dx/dt = σ(y - x)
|
|
51
|
+
* dy/dt = x(ρ - z) - y
|
|
52
|
+
* dz/dt = xy - βz
|
|
53
|
+
*
|
|
54
|
+
* Default parameters: σ=10, ρ=28, β=8/3
|
|
55
|
+
*/
|
|
56
|
+
const lorenz = {
|
|
57
|
+
name: "Lorenz",
|
|
58
|
+
type: AttractorType.CONTINUOUS,
|
|
59
|
+
dimension: AttractorDimension.THREE_D,
|
|
60
|
+
|
|
61
|
+
equations: [
|
|
62
|
+
"dx/dt = σ(y - x)",
|
|
63
|
+
"dy/dt = x(ρ - z) - y",
|
|
64
|
+
"dz/dt = xy - βz",
|
|
65
|
+
],
|
|
66
|
+
|
|
67
|
+
defaultParams: {
|
|
68
|
+
sigma: 10,
|
|
69
|
+
rho: 28,
|
|
70
|
+
beta: 8 / 3,
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
defaultDt: 0.01,
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Compute derivatives at a point
|
|
77
|
+
* @param {Object} point - { x, y, z }
|
|
78
|
+
* @param {Object} [params] - { sigma, rho, beta }
|
|
79
|
+
* @returns {Object} { dx, dy, dz }
|
|
80
|
+
*/
|
|
81
|
+
derivatives(point, params = this.defaultParams) {
|
|
82
|
+
const { sigma, rho, beta } = { ...this.defaultParams, ...params };
|
|
83
|
+
const { x, y, z } = point;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
dx: sigma * (y - x),
|
|
87
|
+
dy: x * (rho - z) - y,
|
|
88
|
+
dz: x * y - beta * z,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Perform one integration step (Euler method)
|
|
94
|
+
* @param {Object} point - { x, y, z }
|
|
95
|
+
* @param {number} [dt] - Time step
|
|
96
|
+
* @param {Object} [params] - Attractor parameters
|
|
97
|
+
* @returns {Object} { position: {x,y,z}, velocity: {dx,dy,dz}, speed }
|
|
98
|
+
*/
|
|
99
|
+
step(point, dt = this.defaultDt, params = this.defaultParams) {
|
|
100
|
+
const d = this.derivatives(point, params);
|
|
101
|
+
const speed = Math.sqrt(d.dx * d.dx + d.dy * d.dy + d.dz * d.dz);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
position: {
|
|
105
|
+
x: point.x + d.dx * dt,
|
|
106
|
+
y: point.y + d.dy * dt,
|
|
107
|
+
z: point.z + d.dz * dt,
|
|
108
|
+
},
|
|
109
|
+
velocity: d,
|
|
110
|
+
speed,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a stepper function with fixed parameters
|
|
116
|
+
* @param {Object} [params] - Attractor parameters
|
|
117
|
+
* @returns {Function} (point, dt) => result
|
|
118
|
+
*/
|
|
119
|
+
createStepper(params = this.defaultParams) {
|
|
120
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
121
|
+
return (point, dt = this.defaultDt) => this.step(point, dt, mergedParams);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
126
|
+
// DADRAS ATTRACTOR
|
|
127
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Dadras Attractor (2010)
|
|
131
|
+
*
|
|
132
|
+
* A chaotic system with multiple scrolls and wings, discovered by
|
|
133
|
+
* Sara Dadras and Hamid Reza Momeni. Features complex folding dynamics
|
|
134
|
+
* due to nonlinear coupling terms.
|
|
135
|
+
*
|
|
136
|
+
* Equations:
|
|
137
|
+
* dx/dt = y - ax + byz
|
|
138
|
+
* dy/dt = cy - xz + z
|
|
139
|
+
* dz/dt = dxy - ez
|
|
140
|
+
*
|
|
141
|
+
* Default parameters: a=3, b=2.7, c=1.7, d=2, e=9
|
|
142
|
+
*/
|
|
143
|
+
const dadras = {
|
|
144
|
+
name: "Dadras",
|
|
145
|
+
type: AttractorType.CONTINUOUS,
|
|
146
|
+
dimension: AttractorDimension.THREE_D,
|
|
147
|
+
|
|
148
|
+
equations: [
|
|
149
|
+
"dx/dt = y - ax + byz",
|
|
150
|
+
"dy/dt = cy - xz + z",
|
|
151
|
+
"dz/dt = dxy - ez",
|
|
152
|
+
],
|
|
153
|
+
|
|
154
|
+
defaultParams: {
|
|
155
|
+
a: 3,
|
|
156
|
+
b: 2.7,
|
|
157
|
+
c: 1.7,
|
|
158
|
+
d: 2,
|
|
159
|
+
e: 9,
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
defaultDt: 0.005,
|
|
163
|
+
|
|
164
|
+
derivatives(point, params = this.defaultParams) {
|
|
165
|
+
const { a, b, c, d, e } = { ...this.defaultParams, ...params };
|
|
166
|
+
const { x, y, z } = point;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
dx: y - a * x + b * y * z,
|
|
170
|
+
dy: c * y - x * z + z,
|
|
171
|
+
dz: d * x * y - e * z,
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
step(point, dt = this.defaultDt, params = this.defaultParams) {
|
|
176
|
+
const d = this.derivatives(point, params);
|
|
177
|
+
const speed = Math.sqrt(d.dx * d.dx + d.dy * d.dy + d.dz * d.dz);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
position: {
|
|
181
|
+
x: point.x + d.dx * dt,
|
|
182
|
+
y: point.y + d.dy * dt,
|
|
183
|
+
z: point.z + d.dz * dt,
|
|
184
|
+
},
|
|
185
|
+
velocity: d,
|
|
186
|
+
speed,
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
createStepper(params = this.defaultParams) {
|
|
191
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
192
|
+
return (point, dt = this.defaultDt) => this.step(point, dt, mergedParams);
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
197
|
+
// AIZAWA ATTRACTOR
|
|
198
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Aizawa Attractor
|
|
202
|
+
*
|
|
203
|
+
* A 3D chaotic system with intricate folding structure, named after
|
|
204
|
+
* Japanese mathematician Tomohiko Aizawa.
|
|
205
|
+
*
|
|
206
|
+
* Equations:
|
|
207
|
+
* dx/dt = (z - b)x - dy
|
|
208
|
+
* dy/dt = dx + (z - b)y
|
|
209
|
+
* dz/dt = c + az - z³/3 - (x² + y²)(1 + ez) + fzx³
|
|
210
|
+
*
|
|
211
|
+
* Default parameters: a=0.95, b=0.7, c=0.6, d=3.5, e=0.25, f=0.1
|
|
212
|
+
*/
|
|
213
|
+
const aizawa = {
|
|
214
|
+
name: "Aizawa",
|
|
215
|
+
type: AttractorType.CONTINUOUS,
|
|
216
|
+
dimension: AttractorDimension.THREE_D,
|
|
217
|
+
|
|
218
|
+
equations: [
|
|
219
|
+
"dx/dt = (z - b)x - dy",
|
|
220
|
+
"dy/dt = dx + (z - b)y",
|
|
221
|
+
"dz/dt = c + az - z³/3 - (x² + y²)(1 + ez) + fzx³",
|
|
222
|
+
],
|
|
223
|
+
|
|
224
|
+
defaultParams: {
|
|
225
|
+
a: 0.95,
|
|
226
|
+
b: 0.7,
|
|
227
|
+
c: 0.6,
|
|
228
|
+
d: 3.5,
|
|
229
|
+
e: 0.25,
|
|
230
|
+
f: 0.1,
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
defaultDt: 0.01,
|
|
234
|
+
|
|
235
|
+
derivatives(point, params = this.defaultParams) {
|
|
236
|
+
const { a, b, c, d, e, f } = { ...this.defaultParams, ...params };
|
|
237
|
+
const { x, y, z } = point;
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
dx: (z - b) * x - d * y,
|
|
241
|
+
dy: d * x + (z - b) * y,
|
|
242
|
+
dz:
|
|
243
|
+
c +
|
|
244
|
+
a * z -
|
|
245
|
+
(z * z * z) / 3 -
|
|
246
|
+
(x * x + y * y) * (1 + e * z) +
|
|
247
|
+
f * z * x * x * x,
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
step(point, dt = this.defaultDt, params = this.defaultParams) {
|
|
252
|
+
const d = this.derivatives(point, params);
|
|
253
|
+
const speed = Math.sqrt(d.dx * d.dx + d.dy * d.dy + d.dz * d.dz);
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
position: {
|
|
257
|
+
x: point.x + d.dx * dt,
|
|
258
|
+
y: point.y + d.dy * dt,
|
|
259
|
+
z: point.z + d.dz * dt,
|
|
260
|
+
},
|
|
261
|
+
velocity: d,
|
|
262
|
+
speed,
|
|
263
|
+
};
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
createStepper(params = this.defaultParams) {
|
|
267
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
268
|
+
return (point, dt = this.defaultDt) => this.step(point, dt, mergedParams);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
273
|
+
// THOMAS ATTRACTOR
|
|
274
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Thomas' Cyclically Symmetric Attractor (1999)
|
|
278
|
+
*
|
|
279
|
+
* Discovered by René Thomas. Features elegant symmetry and smooth
|
|
280
|
+
* cyclical motion with a simple sinusoidal structure.
|
|
281
|
+
*
|
|
282
|
+
* Equations:
|
|
283
|
+
* dx/dt = sin(y) - bx
|
|
284
|
+
* dy/dt = sin(z) - by
|
|
285
|
+
* dz/dt = sin(x) - bz
|
|
286
|
+
*
|
|
287
|
+
* Default parameter: b=0.208186
|
|
288
|
+
*/
|
|
289
|
+
const thomas = {
|
|
290
|
+
name: "Thomas",
|
|
291
|
+
type: AttractorType.CONTINUOUS,
|
|
292
|
+
dimension: AttractorDimension.THREE_D,
|
|
293
|
+
|
|
294
|
+
equations: ["dx/dt = sin(y) - bx", "dy/dt = sin(z) - by", "dz/dt = sin(x) - bz"],
|
|
295
|
+
|
|
296
|
+
defaultParams: {
|
|
297
|
+
b: 0.208186,
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
defaultDt: 0.1,
|
|
301
|
+
|
|
302
|
+
derivatives(point, params = this.defaultParams) {
|
|
303
|
+
const { b } = { ...this.defaultParams, ...params };
|
|
304
|
+
const { x, y, z } = point;
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
dx: Math.sin(y) - b * x,
|
|
308
|
+
dy: Math.sin(z) - b * y,
|
|
309
|
+
dz: Math.sin(x) - b * z,
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
step(point, dt = this.defaultDt, params = this.defaultParams) {
|
|
314
|
+
const d = this.derivatives(point, params);
|
|
315
|
+
const speed = Math.sqrt(d.dx * d.dx + d.dy * d.dy + d.dz * d.dz);
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
position: {
|
|
319
|
+
x: point.x + d.dx * dt,
|
|
320
|
+
y: point.y + d.dy * dt,
|
|
321
|
+
z: point.z + d.dz * dt,
|
|
322
|
+
},
|
|
323
|
+
velocity: d,
|
|
324
|
+
speed,
|
|
325
|
+
};
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
createStepper(params = this.defaultParams) {
|
|
329
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
330
|
+
return (point, dt = this.defaultDt) => this.step(point, dt, mergedParams);
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
335
|
+
// CLIFFORD ATTRACTOR
|
|
336
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Clifford Attractor
|
|
340
|
+
*
|
|
341
|
+
* A 2D iterative attractor that creates intricate fractal patterns
|
|
342
|
+
* using simple trigonometric functions. Unlike continuous attractors,
|
|
343
|
+
* this is a discrete map (no dt required).
|
|
344
|
+
*
|
|
345
|
+
* Equations:
|
|
346
|
+
* x_{n+1} = sin(a·y_n) + c·cos(a·x_n)
|
|
347
|
+
* y_{n+1} = sin(b·x_n) + d·cos(b·y_n)
|
|
348
|
+
*
|
|
349
|
+
* Default parameters: a=-1.4, b=1.6, c=1.0, d=0.7
|
|
350
|
+
*/
|
|
351
|
+
const clifford = {
|
|
352
|
+
name: "Clifford",
|
|
353
|
+
type: AttractorType.ITERATIVE,
|
|
354
|
+
dimension: AttractorDimension.TWO_D,
|
|
355
|
+
|
|
356
|
+
equations: [
|
|
357
|
+
"x_{n+1} = sin(a·y_n) + c·cos(a·x_n)",
|
|
358
|
+
"y_{n+1} = sin(b·x_n) + d·cos(b·y_n)",
|
|
359
|
+
],
|
|
360
|
+
|
|
361
|
+
defaultParams: {
|
|
362
|
+
a: -1.4,
|
|
363
|
+
b: 1.6,
|
|
364
|
+
c: 1.0,
|
|
365
|
+
d: 0.7,
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Compute next position (iterative, no derivatives)
|
|
370
|
+
* @param {Object} point - { x, y }
|
|
371
|
+
* @param {Object} [params] - { a, b, c, d }
|
|
372
|
+
* @returns {Object} { x, y }
|
|
373
|
+
*/
|
|
374
|
+
next(point, params = this.defaultParams) {
|
|
375
|
+
const { a, b, c, d } = { ...this.defaultParams, ...params };
|
|
376
|
+
const { x, y } = point;
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
x: Math.sin(a * y) + c * Math.cos(a * x),
|
|
380
|
+
y: Math.sin(b * x) + d * Math.cos(b * y),
|
|
381
|
+
};
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Step function for consistency with other attractors
|
|
386
|
+
* For iterative attractors, dt is ignored
|
|
387
|
+
* @param {Object} point - { x, y }
|
|
388
|
+
* @param {number} [_dt] - Ignored for iterative attractors
|
|
389
|
+
* @param {Object} [params] - Attractor parameters
|
|
390
|
+
* @returns {Object} { position: {x,y}, velocity: {dx,dy}, speed }
|
|
391
|
+
*/
|
|
392
|
+
step(point, _dt, params = this.defaultParams) {
|
|
393
|
+
const newPos = this.next(point, params);
|
|
394
|
+
|
|
395
|
+
// Compute velocity as difference (for coloring consistency)
|
|
396
|
+
const dx = newPos.x - point.x;
|
|
397
|
+
const dy = newPos.y - point.y;
|
|
398
|
+
const speed = Math.sqrt(dx * dx + dy * dy);
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
position: newPos,
|
|
402
|
+
velocity: { dx, dy, dz: 0 },
|
|
403
|
+
speed,
|
|
404
|
+
};
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
createStepper(params = this.defaultParams) {
|
|
408
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
409
|
+
return (point, _dt) => this.step(point, _dt, mergedParams);
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
414
|
+
// ROSSLER ATTRACTOR
|
|
415
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Rössler Attractor (1976)
|
|
419
|
+
*
|
|
420
|
+
* Discovered by Otto Rössler. One of the simplest chaotic attractors,
|
|
421
|
+
* featuring a single spiral that folds back on itself.
|
|
422
|
+
*
|
|
423
|
+
* Equations:
|
|
424
|
+
* dx/dt = -y - z
|
|
425
|
+
* dy/dt = x + ay
|
|
426
|
+
* dz/dt = b + z(x - c)
|
|
427
|
+
*
|
|
428
|
+
* Default parameters: a=0.2, b=0.2, c=5.7
|
|
429
|
+
*/
|
|
430
|
+
const rossler = {
|
|
431
|
+
name: "Rössler",
|
|
432
|
+
type: AttractorType.CONTINUOUS,
|
|
433
|
+
dimension: AttractorDimension.THREE_D,
|
|
434
|
+
|
|
435
|
+
equations: ["dx/dt = -y - z", "dy/dt = x + ay", "dz/dt = b + z(x - c)"],
|
|
436
|
+
|
|
437
|
+
defaultParams: {
|
|
438
|
+
a: 0.2,
|
|
439
|
+
b: 0.2,
|
|
440
|
+
c: 5.7,
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
defaultDt: 0.01,
|
|
444
|
+
|
|
445
|
+
derivatives(point, params = this.defaultParams) {
|
|
446
|
+
const { a, b, c } = { ...this.defaultParams, ...params };
|
|
447
|
+
const { x, y, z } = point;
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
dx: -y - z,
|
|
451
|
+
dy: x + a * y,
|
|
452
|
+
dz: b + z * (x - c),
|
|
453
|
+
};
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
step(point, dt = this.defaultDt, params = this.defaultParams) {
|
|
457
|
+
const d = this.derivatives(point, params);
|
|
458
|
+
const speed = Math.sqrt(d.dx * d.dx + d.dy * d.dy + d.dz * d.dz);
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
position: {
|
|
462
|
+
x: point.x + d.dx * dt,
|
|
463
|
+
y: point.y + d.dy * dt,
|
|
464
|
+
z: point.z + d.dz * dt,
|
|
465
|
+
},
|
|
466
|
+
velocity: d,
|
|
467
|
+
speed,
|
|
468
|
+
};
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
createStepper(params = this.defaultParams) {
|
|
472
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
473
|
+
return (point, dt = this.defaultDt) => this.step(point, dt, mergedParams);
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
478
|
+
// HALVORSEN ATTRACTOR
|
|
479
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Halvorsen Attractor
|
|
483
|
+
*
|
|
484
|
+
* A symmetric chaotic attractor with three-fold rotational symmetry.
|
|
485
|
+
*
|
|
486
|
+
* Equations:
|
|
487
|
+
* dx/dt = -ax - 4y - 4z - y²
|
|
488
|
+
* dy/dt = -ay - 4z - 4x - z²
|
|
489
|
+
* dz/dt = -az - 4x - 4y - x²
|
|
490
|
+
*
|
|
491
|
+
* Default parameter: a=1.89
|
|
492
|
+
*/
|
|
493
|
+
const halvorsen = {
|
|
494
|
+
name: "Halvorsen",
|
|
495
|
+
type: AttractorType.CONTINUOUS,
|
|
496
|
+
dimension: AttractorDimension.THREE_D,
|
|
497
|
+
|
|
498
|
+
equations: [
|
|
499
|
+
"dx/dt = -ax - 4y - 4z - y²",
|
|
500
|
+
"dy/dt = -ay - 4z - 4x - z²",
|
|
501
|
+
"dz/dt = -az - 4x - 4y - x²",
|
|
502
|
+
],
|
|
503
|
+
|
|
504
|
+
defaultParams: {
|
|
505
|
+
a: 1.89,
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
defaultDt: 0.005,
|
|
509
|
+
|
|
510
|
+
derivatives(point, params = this.defaultParams) {
|
|
511
|
+
const { a } = { ...this.defaultParams, ...params };
|
|
512
|
+
const { x, y, z } = point;
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
dx: -a * x - 4 * y - 4 * z - y * y,
|
|
516
|
+
dy: -a * y - 4 * z - 4 * x - z * z,
|
|
517
|
+
dz: -a * z - 4 * x - 4 * y - x * x,
|
|
518
|
+
};
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
step(point, dt = this.defaultDt, params = this.defaultParams) {
|
|
522
|
+
const d = this.derivatives(point, params);
|
|
523
|
+
const speed = Math.sqrt(d.dx * d.dx + d.dy * d.dy + d.dz * d.dz);
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
position: {
|
|
527
|
+
x: point.x + d.dx * dt,
|
|
528
|
+
y: point.y + d.dy * dt,
|
|
529
|
+
z: point.z + d.dz * dt,
|
|
530
|
+
},
|
|
531
|
+
velocity: d,
|
|
532
|
+
speed,
|
|
533
|
+
};
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
createStepper(params = this.defaultParams) {
|
|
537
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
538
|
+
return (point, dt = this.defaultDt) => this.step(point, dt, mergedParams);
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
543
|
+
// DE JONG ATTRACTOR
|
|
544
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* De Jong Attractor (Peter de Jong)
|
|
548
|
+
*
|
|
549
|
+
* A 2D iterative attractor similar to Clifford, creating beautiful
|
|
550
|
+
* swirling patterns.
|
|
551
|
+
*
|
|
552
|
+
* Equations:
|
|
553
|
+
* x_{n+1} = sin(a·y_n) - cos(b·x_n)
|
|
554
|
+
* y_{n+1} = sin(c·x_n) - cos(d·y_n)
|
|
555
|
+
*
|
|
556
|
+
* Default parameters: a=-2.24, b=-0.65, c=-0.43, d=-2.43
|
|
557
|
+
*/
|
|
558
|
+
const deJong = {
|
|
559
|
+
name: "De Jong",
|
|
560
|
+
type: AttractorType.ITERATIVE,
|
|
561
|
+
dimension: AttractorDimension.TWO_D,
|
|
562
|
+
|
|
563
|
+
equations: [
|
|
564
|
+
"x_{n+1} = sin(a·y_n) - cos(b·x_n)",
|
|
565
|
+
"y_{n+1} = sin(c·x_n) - cos(d·y_n)",
|
|
566
|
+
],
|
|
567
|
+
|
|
568
|
+
defaultParams: {
|
|
569
|
+
a: -2.24,
|
|
570
|
+
b: -0.65,
|
|
571
|
+
c: -0.43,
|
|
572
|
+
d: -2.43,
|
|
573
|
+
},
|
|
574
|
+
|
|
575
|
+
next(point, params = this.defaultParams) {
|
|
576
|
+
const { a, b, c, d } = { ...this.defaultParams, ...params };
|
|
577
|
+
const { x, y } = point;
|
|
578
|
+
|
|
579
|
+
return {
|
|
580
|
+
x: Math.sin(a * y) - Math.cos(b * x),
|
|
581
|
+
y: Math.sin(c * x) - Math.cos(d * y),
|
|
582
|
+
};
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
step(point, _dt, params = this.defaultParams) {
|
|
586
|
+
const newPos = this.next(point, params);
|
|
587
|
+
const dx = newPos.x - point.x;
|
|
588
|
+
const dy = newPos.y - point.y;
|
|
589
|
+
const speed = Math.sqrt(dx * dx + dy * dy);
|
|
590
|
+
|
|
591
|
+
return {
|
|
592
|
+
position: newPos,
|
|
593
|
+
velocity: { dx, dy, dz: 0 },
|
|
594
|
+
speed,
|
|
595
|
+
};
|
|
596
|
+
},
|
|
597
|
+
|
|
598
|
+
createStepper(params = this.defaultParams) {
|
|
599
|
+
const mergedParams = { ...this.defaultParams, ...params };
|
|
600
|
+
return (point, _dt) => this.step(point, _dt, mergedParams);
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
605
|
+
// EXPORTS
|
|
606
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Collection of all attractor functions
|
|
610
|
+
*/
|
|
611
|
+
export const Attractors = {
|
|
612
|
+
lorenz,
|
|
613
|
+
dadras,
|
|
614
|
+
aizawa,
|
|
615
|
+
thomas,
|
|
616
|
+
clifford,
|
|
617
|
+
rossler,
|
|
618
|
+
halvorsen,
|
|
619
|
+
deJong,
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Get list of all available attractor names
|
|
624
|
+
* @returns {string[]}
|
|
625
|
+
*/
|
|
626
|
+
export function getAttractorNames() {
|
|
627
|
+
return Object.keys(Attractors);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Get attractor by name
|
|
632
|
+
* @param {string} name - Attractor name
|
|
633
|
+
* @returns {Object|null}
|
|
634
|
+
*/
|
|
635
|
+
export function getAttractor(name) {
|
|
636
|
+
return Attractors[name] || null;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Get all 3D attractors
|
|
641
|
+
* @returns {Object}
|
|
642
|
+
*/
|
|
643
|
+
export function get3DAttractors() {
|
|
644
|
+
return Object.fromEntries(
|
|
645
|
+
Object.entries(Attractors).filter(
|
|
646
|
+
([, attr]) => attr.dimension === AttractorDimension.THREE_D
|
|
647
|
+
)
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Get all 2D attractors
|
|
653
|
+
* @returns {Object}
|
|
654
|
+
*/
|
|
655
|
+
export function get2DAttractors() {
|
|
656
|
+
return Object.fromEntries(
|
|
657
|
+
Object.entries(Attractors).filter(
|
|
658
|
+
([, attr]) => attr.dimension === AttractorDimension.TWO_D
|
|
659
|
+
)
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Default export
|
|
664
|
+
export default Attractors;
|