|
<body style="margin:0"></body> |
|
|
|
<script> |
|
|
|
var CLICKZOOM = 3; |
|
var NUMWORKERS = 8; |
|
|
|
var workers = (function () { |
|
var w = []; |
|
|
|
for (var i = 0; i < NUMWORKERS; ++i) |
|
w.push(new Worker('worker.js')); |
|
|
|
return w; |
|
})(); |
|
|
|
function sendWorkerDraw(i /* args */) { |
|
var args = [].slice.call(arguments, 1); |
|
|
|
return new Promise(function (resolve) { |
|
var worker = workers[i]; |
|
|
|
worker.addEventListener('message', function done(ev) { |
|
worker.removeEventListener('message', done); |
|
|
|
resolve(ev.data.buf); |
|
}); |
|
|
|
worker.postMessage({ |
|
args: args |
|
}); |
|
}); |
|
} |
|
|
|
function draw(i, ctx, drawX, drawY, screenX, screenY, screenW, screenH, scale, maxiter) { |
|
sendWorkerDraw(i, screenX, screenY, screenW, screenH, scale, maxiter) |
|
.then(function (buf) { |
|
var arr = new Uint8ClampedArray(buf); |
|
var data = new ImageData(arr, screenW, screenH); |
|
|
|
ctx.putImageData(data, drawX, drawY); |
|
}); |
|
} |
|
|
|
function mandelbrot(canvas, planeX, planeY, scale, maxiter) { |
|
var SCALE = scale; |
|
var MAXITER = maxiter; |
|
|
|
var ISCALE = 1 / scale; |
|
|
|
var PLANEX = planeX; |
|
var PLANEY = planeY; |
|
|
|
var SCREENX = PLANEX * SCALE; |
|
var SCREENY = PLANEY * SCALE; |
|
|
|
var SCREENW = canvas.width; |
|
var SCREENH = canvas.height; |
|
|
|
var PLANEW = SCREENW * ISCALE; |
|
var PLANEH = SCREENH * ISCALE; |
|
|
|
var ctx = canvas.getContext('2d'); |
|
|
|
canvas.addEventListener('click', function zoom(ev) { |
|
canvas.removeEventListener('click', zoom); |
|
|
|
var px = ev.offsetX; |
|
var py = ev.offsetY; |
|
|
|
var scale_ = CLICKZOOM * SCALE; |
|
var maxiters_ = 1.4 * MAXITER; |
|
|
|
var planeW_ = SCREENW / scale_; |
|
var planeH_ = SCREENH / scale_; |
|
|
|
var planeX_ = PLANEX + px * ISCALE - planeW_ / 2; |
|
var planeY_ = PLANEY + py * ISCALE - planeH_ / 2; |
|
|
|
planeX_ += (SCREENW / 2 - px) / scale_; |
|
planeY_ += (SCREENH / 2 - py) / scale_; |
|
|
|
var copyW = SCREENW / CLICKZOOM; |
|
var copyH = SCREENH / CLICKZOOM; |
|
|
|
var copyX = px - copyW / 2; |
|
var copyY = py - copyH / 2; |
|
|
|
copyX += (SCREENW / 2 - px) / CLICKZOOM; |
|
copyY += (SCREENH / 2 - py) / CLICKZOOM; |
|
|
|
ctx.drawImage( |
|
canvas, |
|
copyX, copyY, copyW, copyH, |
|
0, 0, SCREENW, SCREENH |
|
); |
|
|
|
mandelbrot(canvas, planeX_, planeY_, scale_, maxiters_); |
|
}); |
|
|
|
var BLOCKY = Math.ceil(SCREENH / NUMWORKERS); |
|
|
|
for (var i = 0; i < NUMWORKERS; ++i) { |
|
draw( |
|
i, |
|
|
|
ctx, |
|
0, |
|
i * SCREENH / NUMWORKERS, |
|
|
|
SCREENX, |
|
SCREENY + i * BLOCKY, |
|
SCREENW, |
|
BLOCKY, |
|
|
|
SCALE, |
|
MAXITER |
|
); |
|
} |
|
|
|
return canvas; |
|
} |
|
|
|
window.addEventListener('load', function () { |
|
var SCREENW = document.body.clientWidth |
|
var SCREENH = document.body.clientHeight; |
|
|
|
var canvas = document.createElement('canvas'); |
|
canvas.width = SCREENW; |
|
canvas.height = SCREENH; |
|
canvas.style.background = 'black'; |
|
|
|
document.body.appendChild(canvas); |
|
|
|
var DOMAINX = -2.5; |
|
var DOMAINY = -1.1; |
|
|
|
var DOMAINW = 3.5; |
|
var DOMAINH = 2.2; |
|
|
|
var scale = Math.min( |
|
SCREENW / DOMAINW, |
|
SCREENH / DOMAINH |
|
); |
|
|
|
var startX = DOMAINX - (SCREENW - DOMAINW * scale) / 2 / scale; |
|
var startY = DOMAINY - (SCREENH - DOMAINH * scale) / 2 / scale; |
|
|
|
mandelbrot(canvas, startX, startY, scale, 100); |
|
}); |
|
|
|
</script> |