-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
215 lines (192 loc) · 6.55 KB
/
index.ts
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import seedrandom from "seedrandom";
import randomColor from "randomcolor";
import { Pond, init } from "ripples";
import { memory } from "ripples/ripples_bg";
import params from "./params.json";
init();
const HEIGHT = window.innerHeight;
const WIDTH = window.innerWidth;
// Scales the alpha by this fraction
const GLOBAL_ALPHA_SCALE_NUMER = 1;
const GLOBAL_ALPHA_SCALE_DENOM = 15;
const pond = Pond.new(WIDTH, HEIGHT);
const canvas = <HTMLCanvasElement> document.getElementById("pond-canvas");
canvas.height = HEIGHT;
canvas.width = WIDTH;
const ctx = canvas.getContext("2d");
var animHandle: number;
var paused = false;
const togglePause = () => {
if (paused) {
console.log("Unpaused.");
animHandle = requestAnimationFrame(interactiveRenderLoop);
} else {
console.log("Paused.");
cancelAnimationFrame(animHandle);
}
paused = !paused;
};
const interactiveRenderLoop = () => {
pond.tick();
drawPond();
animHandle = requestAnimationFrame(interactiveRenderLoop);
};
var seed = "aldf";
if (params.backgroundColor) {
canvas.style.backgroundColor = params.backgroundColor;
seed = params.backgroundColor;
}
const rng = seedrandom(seed);
let currColor = 0x08FF; // TODO lift state
let currMagnitude = 200;
let currFreq = 50;
const colorIter = function* () {
while (true) {
yield parseInt("0x" + randomColor({
seed: Math.trunc(rng() * 0xFFFFFF),
hue: params.hue,
luminosity: "bright"
}).substr(1));
}
}();
const freqScale = params.freqMax - params.freqMin;
const magScale = params.magMax - params.magMin;
const addDroplet = (x: number, y: number) => {
currFreq = rng() * freqScale + params.freqMin;
currMagnitude = rng() * magScale + params.magMin;
currColor = colorIter.next().value;
pond.add_droplet(x, y, currMagnitude, currColor, currFreq);
};
const TAU = 2 * Math.PI;
const drawPond = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const dropletCount = pond.droplet_count();
const rippleCount = pond.total_ripples();
const xs = new Uint16Array(memory.buffer, pond.droplet_xs(), dropletCount);
const ys = new Uint16Array(memory.buffer, pond.droplet_ys(), dropletCount);
const colors = new Uint32Array(memory.buffer, pond.droplet_colors(), dropletCount);
const rippleCounts = new Uint32Array(memory.buffer, pond.ripple_counts(), dropletCount);
let mags = new Uint16Array(memory.buffer, pond.ripple_mags(), rippleCount);
let max_mags = new Uint16Array(memory.buffer, pond.ripple_max_mags(), rippleCount);
let rippleId = 0;
for (let i = 0; i < dropletCount; i++) {
let color = colors[i];
let r = (color >> 16) & 0xFF;
let g = (color >> 8) & 0xFF;
let b = color & 0xFF;
let rippleCount = rippleCounts[i];
let colorStr = `rgb(${r},${g},${b})`;
ctx.fillStyle = colorStr;
let nextBound = rippleId + rippleCount;
for (; rippleId < nextBound; rippleId++) {
let max_mag = max_mags[rippleId];
let mag = mags[rippleId];
// We scale by integers rather than a floating point scalar for efficiency
let a = ((max_mag - mag) * GLOBAL_ALPHA_SCALE_NUMER) / (max_mag * GLOBAL_ALPHA_SCALE_DENOM);
ctx.beginPath();
ctx.globalAlpha = a;
ctx.arc(
xs[i],
ys[i],
mag,
0,
TAU,
false
);
ctx.fill();
}
}
};
// === Interactivity ===
const enableInteractive = () => {
let mouseDown = false;
let mmCtr = 0;
canvas.addEventListener("mousedown", (e) => {
mouseDown = e.button === 0;
mmCtr = 0;
if (mouseDown) {
addDroplet(e.offsetX, e.offsetY);
}
});
// Prevents circles from being drawn too close together when the mouse is held down
const HOLD_INTERVAL = 3;
canvas.addEventListener("mousemove", (e) => {
if (mouseDown && mmCtr++ % HOLD_INTERVAL === HOLD_INTERVAL - 1) {
addDroplet(e.offsetX, e.offsetY);
}
});
canvas.addEventListener("mouseup", (e) => mouseDown = mouseDown && !(e.button === 0));
// Pauses
window.addEventListener("keydown", (e) => {
switch (e.code) {
case "Space":
togglePause();
}
});
};
let renderLoop = interactiveRenderLoop;
// Autodraw
if (params.autodraw.active) {
console.log("Detected autodraw configuration; interactivity disabled.");
let {
stopFrame, // Number of frames to linger after last thing drawn until pause
circlesPerFrame,
xStartOffs,
xEndOffs,
yStartOffs,
yEndOffs,
xSpread,
ySpread,
xStep,
yStep,
} = params.autodraw;
// Allow drawing off screen
// canvas.height += yEndOffs;
// canvas.width += xEndOffs;
// After the circles are drawn, wait for some amount of frames
let waitCountdown = stopFrame;
const waitRenderLoop = () => {
pond.tick();
drawPond();
if (--waitCountdown == 0) {
// Pause the animation and begin interactivity
togglePause();
} else {
animHandle = requestAnimationFrame(waitRenderLoop);
}
};
// Allow negative random displacements
let xShift = xSpread / 2, yShift = ySpread / 2;
let currX = xStartOffs, currY = yStartOffs;
// Draw circles somewhat randomly according to the parameters
const autoRenderLoop = () => {
// Draw up to circlesPerFrame circles
for (let i = 0; i < circlesPerFrame; i++) {
if (currY > HEIGHT + yEndOffs) {
// Move on to next column
currY = yStartOffs;
currX += xStep;
}
let offsX = rng() * xSpread - xShift;
let offsY = rng() * ySpread - yShift;
addDroplet(currX + offsX, currY + offsY);
currY += yStep;
}
pond.tick();
drawPond();
if (currX > WIDTH + xEndOffs) {
console.log(`Completed autodraw (last coordinate x=${currX}, y=${currY})`);
// console.log("Reenabling interactivity");
animHandle = requestAnimationFrame(waitRenderLoop);
// requestAnimationFrame(interactiveRenderLoop);
} else {
animHandle = requestAnimationFrame(autoRenderLoop);
}
};
// Update the global render loop
renderLoop = autoRenderLoop;
}
enableInteractive();
// === Initialization ===
drawPond();
animHandle = requestAnimationFrame(renderLoop);