//
// 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);
    }
}