-
Notifications
You must be signed in to change notification settings - Fork 15
/
lorenz-attractor.js
125 lines (106 loc) Β· 2.94 KB
/
lorenz-attractor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const canvasSketch = require('canvas-sketch');
const chroma = require('chroma-js');
const Random = require('canvas-sketch-util/random');
const { point, line, drawShape } = require('./geometry');
const { matrixMultiply } = require('./matrix');
const { hueCycle } = require('./clrs');
const settings = {
dimensions: [1600, 1600],
animate: true,
duration: 40,
scaleToView: true,
};
const sketch = () => {
console.clear();
let angle = Math.PI / 4;
Random.setSeed(Random.getRandomSeed());
// Choose a new starting hue
let hueStart = Random.value();
const attractor = [];
let x = 0.01;
let y = 0;
let z = 0;
let a = 10;
let b = 28;
let c = 8.0 / 3.0;
return ({ context, width, height, playhead }) => {
angle += 0.01;
// prettier-ignore
const rotationZ = [
[Math.cos(angle / 2), -Math.sin(angle / 2), 0],
[Math.sin(angle / 2), Math.cos(angle / 2), 0],
[0, 0, 1],
];
// prettier-ignore
const rotationX = [
[1, 0, 0],
[0, Math.cos(angle), -Math.sin(angle)],
[0, Math.sin(angle), Math.cos(angle)],
];
// prettier-ignore
const rotationY = [
[ Math.cos(angle), 0, Math.sin(angle)],
[ 0, 1, 0],
[-Math.sin(angle), 0, Math.cos(angle)],
];
let dt = 0.01;
let dx = a * (y - x) * dt;
let dy = (x * (b - z) - y) * dt;
let dz = (x * y - c * z) * dt;
x = x + dx;
y = y + dy;
z = z + dz;
attractor.push([x, y, z]);
const projected = attractor.map((vertex) => {
const v = vertex.map((v) => [v]);
let rotated = matrixMultiply(rotationX, v);
rotated = matrixMultiply(rotationY, rotated);
rotated = matrixMultiply(rotationZ, rotated);
const scaled = matrixMultiply(scale(5), rotated);
// prettier-ignore
const projection2d = [
[1, 0, 0],
[0, 1, 0],
];
const projected2d = matrixMultiply(projection2d, scaled);
return projected2d;
});
context.fillStyle = '#000022';
context.clearRect(0, 0, width, height);
context.fillRect(0, 0, width, height);
context.translate(width / 2, height / 2);
const color = hueCycle(hueStart, playhead);
context.strokeStyle = color;
context.lineWidth = 16;
context.lineCap = 'round';
context.lineJoin = 'round';
// // Rainbow version
// const [start, ...pts] = projected;
// let prev = start;
// const l = pts.length;
// pts.forEach((pt, idx) => {
// const color = hueCycle(hueStart, idx / l);
// context.strokeStyle = color;
// context.beginPath();
// context.moveTo(...prev);
// context.lineTo(...pt);
// prev = pt;
// context.stroke();
// });
// Dancing version
drawShape(context, projected, false);
context.stroke();
if (attractor.length > 40) {
attractor.shift();
}
};
};
canvasSketch(sketch, settings);
function scale(v) {
// prettier-ignore
return [
[v, 0, 0],
[0, v, 0],
[0, 0, v],
];
}