js-metaballs/metaballs.js

82 lines
2.0 KiB
JavaScript

window.onload = function() {
console.log('init');
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
metaballs = [],
params = [],
nb = 20,
thr = 1e-4,
t = 0,
ps = 8,
pixels = [],
nc = 20,
bg = [0.35, 0, 0.5],
fg = [1, 0.5, 0],
w = canvas.width,
h = canvas.height;
for(var n = 0; n < nc; ++n) {
var pixel = ctx.createImageData(ps, ps),
pixelData = pixel.data,
col = n / nc;
for(var i = 0; i < ps * ps; ++i) {
for(var j = 0; j < 3; ++j) {
pixelData[4 * i + j] = 255 * (fg[j] * col + bg[j] * (1 - col));
}
pixelData[4 * i + 3] = 255;
}
pixels.push(pixel);
}
for(var i = 0; i < nb; ++i) {
metaballs.push([0, 0, Math.random() * (w / 100) / nb]);
params.push({
// cx: 200 + Math.random() * 200,
// cy: 200 + Math.random() * 200,
cx: w/2,
cy: h/2,
ox: w/6 + Math.random() * (w/3),
oy: h/6 + Math.random() * (h/3),
mx: Math.random() * 5,
my: Math.random() * 5,
a: Math.random() * 6.28,
});
}
function mb(p, mb) {
var dx = p[0] - mb[0],
dy = p[1] - mb[1],
d = dx * dx + dy * dy;
return mb[2] / d;
}
function f(p) {
var acc = 0,
n = metaballs.length;
for(var i = 0; i < n; ++i)
acc += mb(p, metaballs[i]);
return acc;
}
window.setInterval(function() {
ctx.setFillColor(bg[0], bg[1], bg[2], 1);
ctx.fillRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i < params.length; ++i) {
var b = metaballs[i],
p = params[i];
b[0] = p.cx + Math.cos(t * p.mx + p.a) * p.ox;
b[1] = p.cy + Math.cos(t * p.my) * p.oy;
}
for(var x = 0; x < canvas.width; x += ps) {
for(var y = 0; y < canvas.height; y += ps) {
var v = f([x,y]);
if(v > thr) {
var logv = Math.log(v/thr),
cidx = Math.round(Math.max(0, Math.min(pixels.length - 1, 20 * Math.pow(logv, 0.3))));
ctx.putImageData(pixels[cidx], x, y);
}
}
}
t += 0.005;
}, 20);
};