Nadesłany przez Tomasz Lubiński, 05 maja 2011 22:00
Kod przedstawiony poniżej przedstawia główną część rozwiązania problemu.Pobierz pełne rozwiązanie.
Jeżeli nie odpowiada Ci sposób formatowania kodu przez autora skorzystaj z pretty printer'a i dostosuj go automatycznie do siebie.
phoenix_mandelbrot.js:
//Phoenix-Mandelbrot //(c) 2011 by Tomasz Lubinski //www.algorytm.org /* Render area coordinates */ var minX = -2.0; var maxX = 1.0; var minY = -2.0; var maxY = 2.0; /* Render area size */ var width = 0; var height = 0; var ratioX; var ratioY; /* Number of fractal levels */ var levelNum = 100; var colors = new Array(levelNum+1); /* Fractal image */ var imageData; var canvas; var ctx; /* Selection data*/ var selectionStarted = false; var selectStartX, selectStartY, selectStopX, selectStopY; /* signum function */ function sgn(x){ if(x>0)return 1; else if(x<0)return -1; else return 0; } /* store where selection has started */ function startSelect(e) { if (e.button == 0) { /* left mouse button - select operation */ selectionStarted = true; if (e.offsetX || e.offsetX == 0) { selectStartX = e.offsetX; selectStopX = e.offsetX; selectStartY = e.offsetY; selectStopY = e.offsetY; } else { selectStartX = e.layerX; selectStopX = e.layerX; selectStartY = e.layerY; selectStopY = e.layerY; } } else { /* right mouse button - reset operation */ selectStartX = 0; selectStopX = 0; selectStartY = 0; selectStopY = 0; minX = -2.0; maxX = 1.0; minY = -2.0; maxY = 2.0; ratioX = (maxX - minX) / width; ratioY = (maxY - minY) / height; renderFractal(); } } /* selection has stopped, render new fractal */ function stopSelect(e) { selectionStarted = false; if (selectStartX != selectStopX && selectStartY != selectStopY) { maxX = Math.max(selectStartX, selectStopX)*ratioX + minX; minX = Math.min(selectStartX, selectStopX)*ratioX + minX; maxY = Math.max(selectStartY, selectStopY)*ratioY + minY; minY = Math.min(selectStartY, selectStopY)*ratioY + minY; ratioX = (maxX - minX) / width; ratioY = (maxY - minY) / height; renderFractal(); } } /* mouse is outside render area, cancel selection */ function clearSelect(e) { if (selectionStarted) { canvas.width = canvas.width; ctx.putImageData(imageData, 0, 0); } selectionStarted = false; } /* mouse is moving inside render area, show selection rectangle */ function moveSelect(e) { if (selectionStarted) { if (e.offsetX || e.offsetX == 0) { selectStopX = e.offsetX; selectStopY = e.offsetY; } else { selectStopX = e.layerX; selectStopY = e.layerY; } canvas.width = canvas.width; /* clear canvas */ ctx.putImageData(imageData, 0, 0); /* put fractal on the canvas */ if (selectStartX != selectStopX && selectStartY != selectStopY) { /* make sure that selection area has the same ratio as original area */ if (width/height < Math.abs(selectStartX-selectStopX)/Math.abs(selectStartY-selectStopY)) { selectStopY = selectStartY+sgn(selectStopY-selectStartY)*height*Math.abs(selectStartX-selectStopX)/width; } else { selectStopX = selectStartX+sgn(selectStopX-selectStartX)*width*Math.abs(selectStartY-selectStopY)/height; } /* put select rectangle on the canvas */ ctx.fillStyle = 'rgba(100,200,300,0.5)'; ctx.fillRect(Math.min(selectStartX, selectStopX), Math.min(selectStartY, selectStopY), Math.abs(selectStartX-selectStopX), Math.abs(selectStartY-selectStopY)); } } } /* initialize color table */ function initializeColors() { for (var level=0; level<=levelNum; level++) { colors[level] = new Object(); colors[level].r = 255.0*level/levelNum; colors[level].g = 255.0*level/levelNum; colors[level].b = 255.0*Math.log(level)/Math.log(levelNum); } } /* value is inside set in the returned level */ function levelSet(p_re, p_im) { var z_re = 0; var z_im = 0; var zp_re = 0; var zp_im = 0; var iteration = 0; var tmp_re, tmp_im; do { tmp_re = z_re*z_re - z_im*z_im + p_re + p_im*zp_re; tmp_im = 2*z_re*z_im + p_im*zp_im; zp_re = z_re; zp_im = z_im; z_re = tmp_re; z_im = tmp_im; iteration++; } while ((z_re*z_re+z_im*z_im) < 4 && iteration < levelNum); return iteration; } /* render Fractal */ function renderFractal() { for (var i=0; i<height; i++) { p_im = i*ratioY + minY; for (var j=0; j<width; j++) { p_re = j*ratioX + minX; index = (i*width+j)*4; imageData.data[index+3] = 0xff; level = levelSet(p_re, p_im); imageData.data[index+0] = colors[level].r; imageData.data[index+1] = colors[level].g; imageData.data[index+2] = colors[level].b; } } // copy the image data back onto the canvas ctx.putImageData(imageData, 0, 0); } /* touch handler for devices with touch screen */ function touchHandler(event) { var touches = event.changedTouches, first = touches[0], type = ""; switch(event.type) { case "touchstart": type = "mousedown"; break; case "touchmove": type = "mousemove"; break; case "touchend": type = "mouseup"; break; default: return; } if (touches.length > 1) { type = "mousedown"; } var simulatedEvent = document.createEvent("MouseEvent"); simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, touches.length-1, null); first.target.dispatchEvent(simulatedEvent); event.preventDefault(); } /* initialize environment and render Fractal */ function initializeFractal() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); canvas.onmousemove = moveSelect; canvas.onmousedown = startSelect; canvas.onmouseup = stopSelect; canvas.onmouseout = clearSelect; //support for mobile devices with touch screen if ("ontouchstart" in document.documentElement) { canvas.ontouchstart = touchHandler; canvas.ontouchmove = touchHandler; canvas.ontouchend = touchHandler; } // read the width and height of the canvas width = canvas.width; height = canvas.height; // create a new pixel array imageData = ctx.createImageData(width, height); ratioX = (maxX - minX) / width; ratioY = (maxY - minY) / height; initializeColors(); renderFractal(); }