// // The Mandelbrot Fractal Set // Copyright c Brian P. Vogl // Javascript version // October 15 - 20, 2018 // var canvas = document.getElementById("thecanvas"); canvas.style.backgroundColor = "black"; var P, X, Y, Xsq, Ysq; var i, color, row, col; var rgb = [ "#000000", "#A1A1A1", "#FF0000", "#00FF00", "#0000FF", "#FFFF10", "#52b552", "#31b5d6", "#ffce9c", "#bdc6de", "#ce007b", "#527b10", "#ffb573", "#bdc6de", "#f7bdde", "#ffffff" ]; var maxcolor = rgb.length; var maxcol = canvas.width; var maxrow = canvas.height; var maxiter = 512; var maxsize = 4; var Xmax = 0.5; var Xmin = -2.0 var Ymax = 1.15; var Ymin = -1.15; var delP = (Xmax - Xmin)/maxcol; var delQ = (Ymax - Ymin)/maxrow; var Q = new Array(350); Q[0] = Ymin; for(row = 1; row < maxrow; row++) Q[row] = Q[row-1] + delQ; var timers = []; var xs=-1,ys=-1,xn=-1,yn=-1; // Mouse position variables var ctx; if(ctx = canvas.getContext('2d')) { canvas.oncontextmenu = function (event) { event.preventDefault(); MouseDn(); }; document.addEventListener("mousedown", MouseDn); document.addEventListener("mouseup" , MouseUp); document.addEventListener("mousemove", MouseMove); do_display(ctx); } function MouseDn(event) { // Determine where on the canvas the button was pressed for (var i = 0; i < timers.length; i++) // reset any timers. { clearTimeout(timers[i]); } var bbox = canvas.getBoundingClientRect(); var buttstate = event.which > 1 ? 1 : 0; xs = event.clientX - bbox.left; ys = event.clientY - bbox.top; } function MouseMove(event) { var bbox = canvas.getBoundingClientRect(); var buttstate = event.which > 1 ? 1 : 0; var x1,y1,w1,h1; if(ys != -1) { ctx.globalCompositeOperation = "lighter"; ctx.lineWidth = 1; ctx.strokeStyle = "red"; xn = event.clientX - bbox.left; yn = event.clientY - bbox.top; if(xs>xn) { x1=xn; w1=xs-xn; } else { x1=xs; w1=xn-xs; } if(ys>yn) { y1=yn; h1=ys-yn; } else { y1=ys; h1=yn-ys; } ctx.strokeRect(x1, y1, w1, h1); } } function MouseUp(event) { var bbox = canvas.getBoundingClientRect(); xe = event.clientX - bbox.left; ye = event.clientY - bbox.top; if(xs>xe) { tmp = xs; xs = xe; xe = tmp; } if(ys>ye) { tmp = ys; ys = ye; ye = tmp; } mintmp = ((Xmax - Xmin) * xs )/ maxcol + Xmin; maxtmp = ((Xmax - Xmin) * xe )/ maxcol + Xmin; Xmin = mintmp; Xmax = maxtmp; mintmp = ((Ymax - Ymin) * ys )/ maxrow + Ymin; maxtmp = ((Ymax - Ymin) * ye )/ maxrow + Ymin; Ymin = mintmp; Ymax = maxtmp; delP = (Xmax - Xmin)/maxcol; delQ = (Ymax - Ymin)/maxrow; Q[0] = Ymin; for(row = 1; row < maxrow; row++) Q[row] = Q[row-1] + delQ; xs=-1; ys=-1; xn=-1; yn=-1; ctx.globalCompositeOperation = "source-over"; do_display(ctx); } function do_display(ctx) { P = Xmin; ctx.lineWidth = 1; for(col = 0; col < maxcol; col++) { myfunc = function(col, P){ handler(col, P); }; // Save the timer id's So we can cancel later timers.push(setTimeout(myfunc, 0, col, P)); P += delP; } //alert("Doing Display " + color + " " + col + " " + row); } function do_pixel(col, row, color, amp) { var id = ctx.createImageData(1,1); // only do this once per page var d = id.data; // only do this once per page var rgb_c = rgb[color % maxcolor]; d[0] = parseInt(rgb_c.substr(1, 2), 16); d[1] = parseInt(rgb_c.substr(3, 2), 16); d[2] = parseInt(rgb_c.substr(5, 2), 16); d[3] = 200; ctx.putImageData( id, col, row ); } // Put a single line of data on the screen function handler(col, P) { for(row = 0; row < maxrow; row++) { X=Y=Xsq=Ysq=0.0; color=0; while((color < maxiter) && (Xsq+Ysq < maxsize)) { Xsq = X*X; Ysq = Y*Y; Y *= X; Y += Y + Q[row]; X = Xsq - Ysq + P; color++; } // 2 ways to do this // The do_pixel method takes longer. // do_pixel(col, row, color, 255); ctx.strokeStyle = rgb[color % maxcolor]; ctx.strokeRect(col, row, 1, 1); } }