/*!
 * Star Object
 * Copyright(c) 2010 Harry Sorensen
 * Requires SimplexNoise library
 * 
 * @class Star
 * @param {Object} config Configuration options
 */

var Star = function(config){
	this.init(config);
};

Star.prototype.init = function(config){
	// Create the masks for the star and flames 	
	if (isNaN(config.radius)){this.radius = 50;} else {
		this.radius = Math.min(300,Math.max(5,config.radius)); // Between 5 and 300
	}
	if (isNaN(config.halo)){this.halo = 10;} else {
		this.halo = Math.min(50,Math.max(0,config.halo)); // Between 0 and 50
	}
	if (undefined == config.debug){
		this.debug = false;
	} else {
		this.debug = config.debug;
	}		
	this.color = config.color;  // TODO: Check for proper hexcode
	this.context = config.context; // TODO: Check for valid type
	if (isNaN(config.activity)) { this.activity=.5;} else {
		this.activity = Math.min(1,Math.max(0,config.activity));  // Must be between 0 and 1 for now
	}
	if (this.debug) console.log('Star.init() radius:'+this.radius+' , halo:'+this.halo+' , debug:'+this.debug);
	var canvas = this.context.canvas;
	// TODO: Make sure canvas is large enough for star values
	this.cy = Math.floor(canvas.height/2);
	this.cx = Math.floor(canvas.width/2);
	if (this.debug) console.log('Star.init() width,height: '+canvas.width+','+canvas.height);
	if (this.debug) console.log('Star.init() cx,cy: '+this.cx+','+this.cy);
	var c = this.context;
	c.fillRect(0,0,canvas.width,canvas.height);
	this.maxy = canvas.height;
	this.maxx = canvas.width;
	this.id = this.context.getImageData(0,0,this.maxx,this.maxy);
	// Noise generator
	this.noise = new SimplexNoise();
	// Variable to itself 
	var self = this;

	// Precreate the array of pixels with noise variables and function
	// The halo is from circle radius to circle radius + halo
	//    with xn as angle and yn as radius-circle radius
	// The flame is from circle radius to circle radius*activity, 
	//    with xn as angle and yn as radius-circle radius
	// The star body is from 0 to circle radius with xn and yn 
	//    scaled values of x and y
	var start = new Date();
	var scale = 1/this.radius;
	var sradius2 = this.radius*this.radius; // star radius squared
	var fradius = this.halo+this.radius;
	var fradius2 = fradius*fradius; // flame radius squared
	this.pixelArray = new Array();
	var cell = 0;
	for(var y=0;y<this.maxy;y++){
		var testy = y-this.cy;	
		var testy2 = testy*testy;
		for (var x=0;x<this.maxx;x++){
			var testx = x-this.cx;
			var rad2 = testy2+testx*testx;
			// Check if this is part of star body
			if (rad2 <= sradius2){
				var idcell = (y*this.maxy+x)*4;
				var z = Math.sqrt(sradius2-testx*testx-testy2);
				var fudge = 1+(Math.PI/2-1)*rad2/sradius2; // For the appearance of a sphere compress larger radii noise
//				var pix = {idcell:idcell,x:x,y:y,z:z,xn:testx*scale*fudge,yn:testy*scale*fudge,zn:z*scale,c:function(pix,f){return self.starColor(pix,f);},c2:function(pix,f){return self.starColor3d(pix,f);}};
				var pix = {idcell:idcell,x:x,y:y,z:z,xn:testx*scale*fudge,yn:testy*scale*fudge,zn:z*scale,c:function(x,y,f,cell){return self.starColor(x,y,f,cell);},c2:function(pix,f){return self.starColor3d(pix,f);}};
				this.pixelArray[cell++]=pix;
			} else if (rad2 <= fradius2){
			// Check if this is part of star flame
				var idcell = (y*this.maxy+x)*4;
				var rad = Math.sqrt(rad2);
				var ar = (this.activity+1)*this.radius;
				var damp = (ar-rad)/(this.activity*this.radius);
				damp *=damp;
//				var temp = this.activity*this.radius-yrad-ar;
				//var damp = temp*temp/ar/ar;  // Full at this.radius, 0 at this.fradius
//				var pix = {x:x,y:y,xn:Math.atan2(y,x),yn:(rad-this.radius)*scale,c:function(x,y,f,damp){ return self.flameColor(x,y,f,damp);},damp:damp};
				var pix = {idcell:idcell,x:x,y:y,xn:Math.atan2(testy,testx),yn:(rad-this.radius),c:function(x,y,f,cell){ return self.flameColor(x,y,f,cell);},c2:function(pix,f){return {r:0,g:0,b:0};}};
				this.pixelArray[cell++]=pix;
			}
		}
	}
	var end = new Date();
	var tim =end-start;
	this.arraytime = tim;
	if (this.debug) console.log('Array size: '+this.pixelArray.length+' Calculation time: '+tim+' ms');
}

// Do a fill of the pixel array skipping skip entries each loop
// Return time to compute in ms, will force skip to be between 1 and 1000
Star.prototype.fill = function(start,skip){
	var sdate = new Date();
	if (isNaN(start)){ start=0; } else { 
		// Don't let a bad start value crash page 
		start = Math.floor(Math.min(this.pixelArray.length-1,Math.max(0,start))); // Between 0 and length-1
	}
	if (isNaN(skip)){ skip=1; } else { 
		// Don't let a bad skip value crash page 
		skip = Math.floor(Math.min(1000,Math.max(1,skip))); // Between 1 and 1000
	}
	// Calculate f based on current date
//	var f = (sdate.getTime()&65535)/20000; // Just get the last 16 bits
	var f = sdate.getTime()/20000; // Just get the last 16 bits
	for (var i=start;i<this.pixelArray.length;i+=skip){
		var pix = this.pixelArray[i];
		if (undefined == pix){
			if (this.debug) console.log('pix undef at i='+i);
		}
//		var c = pix.c(pix,f);  // Massive slowdown when passing objects?
		var c = pix.c(pix.xn,pix.yn,f,pix.idcell);
//		var datacell = 4*(pix.y*this.maxx+pix.x);
//		this.id.data[datacell++]=c.r;
//		this.id.data[datacell++]=c.g;
//		this.id.data[datacell]=c.b;
	}
	this.context.putImageData(this.id,0,0);
	var end = new Date();
	if (this.debug) console.log('Rendered f '+f+' start '+start+' skip '+skip+' in '+(end-sdate)+'ms  Maxn:'+this.nmax+' minn:'+this.nmin);
	if (this.debug) console.log('   xnmax,xnmin:'+this.xnmax+','+this.xnmin);
	return end-sdate;	
}

// Animate to keep calculation below 10ms
Star.prototype.animate = function(){
	if (undefined == this.skip){
		//this.skip = Math.floor(this.fill(0,1)/20); // Create the initial picture
		this.skip=8;
		this.start = 0;
		this.animating=true;
		if (this.debug) console.log('Setting skip for first time');
	}
	if (!this.animating)return;
	if (this.start>=this.skip){
		// TODO: Set skip based on last average of frames
		this.start=0;
		if (this.debug) console.log('Recalculating skip');
		//return;
	}
	var time = this.fill(this.start++,this.skip);
	this.frametime = time;
	var self = this;
	setTimeout(function(){self.animate();},30);	
//	console.log('Skip: '+this.skip+'  This frame render time: '+time);
}

Star.prototype.stop = function(){
	this.animating=false;
}
// Color for star body, returns object with r,g,b variables
Star.prototype.starColor = function(xn,yn,f,cell){
	var oct = 3;
//	var freq = [1,2,4,8];
//	var amp = [1,.5,.25,.125];
	var freq = [1,2,8];
	var amp = [1,.5,.75];
	var n = 0;
	var nscale = 0;
//	var xn = pix.xn;
//	var yn = pix.yn;
	for (var i=0;i<oct;i++){
		n += amp[i]*this.noise.noise3d(xn*freq[i],yn*freq[i], f*freq[i]);
		nscale += amp[i];
	}
	n /= nscale;
	n=(n+1)/2; // noise now between 0 and 1
	// Need to replace this with a colormap array for rgba lookup
	// Can multiply noise by 128, add 127 and take the floor.  
	// Each color table will have 255 entries with the proper color.
	var c = {b:0};
	var cbreak = 180/255; // noise over this level has some Blue, and R=255
	if (n>cbreak){
//		c.r=255;
//		c.g=Math.floor(205*(n-cbreak)/(1-cbreak))+50;
//		c.b=Math.floor((n-cbreak)*255/(1-cbreak));
		this.id.data[cell]=255;
		this.id.data[cell+1]=Math.floor(205*(n-cbreak)/(1-cbreak))+50;
		this.id.data[cell+2]=Math.floor((n-cbreak)*255/(1-cbreak));
	}else {
//		c.r=128+Math.floor(128*n/cbreak);
//		c.g=Math.floor(50*n/cbreak);
		this.id.data[cell]=128+Math.floor(128*n/cbreak);
		this.id.data[cell+1]=Math.floor(50*n/cbreak);
		this.id.data[cell+2]=0;
	}
//	return c;
}

//Color for star body with 3d noise, returns object with r,g,b variables
Star.prototype.starColor3d = function(pix,f){
	var oct = 1;
	var freq = [1,2,4,8];
	var amp = [1,.5,.25,.125];
	var n = 0;
	var nscale = 0;
	for (var i=0;i<oct;i++){
		n += amp[i]*this.noise.noise4d(pix.xn*freq[i],pix.yn*freq[i],pix.zn*freq[i],f);
		nscale += amp[i];
	}
	n /= nscale;
	n=(n+1)/2; // noise now between 0 and 1
	// Need to replace this with a colormap array for rgba lookup
	// Can multiply noise by 128, add 127 and take the floor.  
	// Each color table will have 255 entries with the proper color.
	var c = {b:0};
	var cbreak = 180/255; // noise over this level has some Blue, and R=255
	if (n>cbreak){
		c.r=255;
		c.g=Math.floor(205*(n-cbreak)/(1-cbreak))+50;
		c.b=Math.floor((n-cbreak)*255/(1-cbreak));
	}else {
		c.r=128+Math.floor(128*n/cbreak);
		c.g=Math.floor(50*n/cbreak);
	}
	return c;
}




Star.prototype.scurve = function(t){
	//return (3-2*t)*t*t;
	return t*t*t*(10+t*(6*t-15));
}


Star.prototype.xnmax = -99;
Star.prototype.xnmin = 99;
// Color for flame activity
Star.prototype.flameColor = function(xn,yn,f,cell){
//	return {r:128,g:128,b:128};
//	var t=0;
//	for (var f=4.5;f <= w/12;f *= 2) {
//	for (var j=1;j <= 8;j *= 2) {
//		t += Math.abs(this.noise.noise4d(xn,yn,f,j/16)/j);
//		t += Math.abs(this.noise.noise3d(xn/j,yn/j,f/j)/j); // between .02 and 1.6
//		t += Math.abs(this.noise.noise3d(xn*j,yn*j,f*j)/j);
//	}
	// Expect yn values between 0 and activity*3
	// Want noise very close to 1 where yn is 0 and rarely any where yn is activity*3
	// Try the scurve from perlin noise function
//	var sc = this.scurve(yn/this.activity/3); // should always be between 0 and 1
//	var t=t*(1-sc);
//	if (t>this.nmax){this.nmax=t;}
//	if (t<this.nmin){this.nmin=t;}
	// Normalize t
	//t/=yn;
	// Make t->0 as yn->this.activity*this.radius
//	t=t*(this.activity*this.radius-yn);
//	t=t*damp;
//	if (t>.9) t=0; // Create flame circle effects
//	if (t>1) t=0;
	// t will always be between 0 and .9
//	var c={r:Math.floor(255*t/.9),g:0,b:0};
//	var c={r:Math.floor(255*t),g:0,b:0};
	var c={r:0,g:0,b:0};
//	var v = this.noise.noise2d(xn,f);
	var t = 0;
	var tspike = this.noise.noise2d(xn*20,f); // Need high frequency, low amp noise spikes
	var tback = this.noise.noise2d(xn*4,f); // Need low frequency, med amp noise background
	tback += this.noise.noise2d(xn*8,f)/2; // Need low frequency, med amp noise background
//	for (var j=1;j <= 8;j *= 2) {
//		t += Math.abs(this.noise.noise2d(xn/j,f/j)/j);
//	t += Math.abs(this.noise.noise3d(xn/j,yn/j,f/j)/j); // between .02 and 1.6
//	t += Math.abs(this.noise.noise3d(xn*j,yn*j,f*j)/j);
//	}
	if (t>this.nmax){this.nmax=t;}
	if (t<this.nmin){this.nmin=t;}
	if (t<0) { t=0;}
//	var h = t*this.activity*this.radius/1.8;
//	var i = 1-yn/h; // TODO: Fix this?
//	if (i>0){
//		c.r=Math.floor(255*i);
//	} else {
//	var hspikemax = this.radius*.05;
	var hspikemax = this.halo/3;
	var hspike = hspikemax*tspike;
//	var hbackmax = this.radius*.15;
	var hbackmax = this.halo;
	var hback = hbackmax*(tback+1.5)/3;
	if (yn<hspike){
		//c.r=200;
		this.id.data[cell]=200
	} else if (yn<hback){
//		c.r=Math.floor(200*((hbackmax-yn)*(hbackmax-yn)/hbackmax/hbackmax));
		this.id.data[cell]=Math.floor(200*((hbackmax-yn)*(hbackmax-yn)/hbackmax/hbackmax));
	} else {
//		c.r=0;
		this.id.data[cell]=0;
	}
//	if (xn>this.xnmax) this.xnmax=xn;
//	if (xn<this.xnmin) this.xnmin=xn;
	// Need to replace this with a colormap array for rgba lookup
	// Can multiply noise by 128, add 127 and take the floor.  
	// Each color table will have 255 entries with the proper color.
//	var c = {b:0};
//	var cbreak = 180/255; // noise over this level has some Blue, and R=255
//	if (n>cbreak){
//		c.r=255;
//		c.g=Math.floor(215*(n-cbreak)/(1-cbreak))+50;
//		c.b=Math.floor((n-cbreak)*255/(1-cbreak));
//	}else {
//		c.r=128+Math.floor(128*n/cbreak);
//		c.g=Math.floor(50*n/cbreak);
//	}
//	return c;
}
Star.prototype.flatflame = function(){
	if (!this.animating)return;
	var scale=1/300;
	var cell=0;
	var smallheightmax = 50;
	var sdate=new Date();
	var f = sdate.getTime()/20000; // Just get the last 16 bits
	// Have flames along y axis to cheapen calculations
	for(var y=0;y<300;y++){
		var yn=y*scale;
		var highnoise = this.noise.noise2d(yn,f);
		var t=highnoise;
		if (t>this.nmax){this.nmax=t;}
		if (t<this.nmin){this.nmin=t;}
		var c = {r:0,g:0,b:0};
		for (var x=0;x<300;x++){
			// Flame outline at x
			// Try low level high frequency noise for small height flames
			// high level low frequency noise for Fire rings
			if (t>-1){
				var tmod = Math.abs(t)
				var h = Math.floor(tmod*400);
				var level = Math.abs(h-x);
				if (level<3){
					this.id.data[cell]=255*(1-Math.floor(255*level/4));
				}else {
					this.id.data[cell]=0;
				}
			} else {
				this.id.data[cell]=0;
			}
			cell+=4;
		}
	}
	this.context.putImageData(this.id,0,0);
	//this.animating=false;
	var self = this;
	setTimeout(function(){self.flatflame();},40);	
	var end = new Date();
	console.log('Rendered flatflame in '+(end-sdate)+'ms  Maxn:'+this.nmax+' minn:'+this.nmin);
}
Star.prototype.testsphere = function(){
	var sdate = new Date();
	var scale = 1/this.radius;
	var sradius2 = this.radius*this.radius; // star radius squared
	var cell = 0;
	var f = sdate.getTime()/20000; // Just get the last 16 bits
	for(var y=0;y<this.maxy;y++){
		var testy = y-this.cy;	
		var testy2 = testy*testy;
		for (var x=0;x<this.maxx;x++){
			var testx = x-this.cx;
			var rad2 = testy2+testx*testx;
			// Check if this is part of star body
			if (rad2 <= sradius2){
				var z = Math.sqrt(sradius2-testx*testx-testy2);
				var theta = Math.atan2(testy,testx);
				var fudge = 1+(Math.PI/2-1)*rad2/sradius2; // For the appearance of a sphere compress larger radii noise
				var fudgex = Math.abs(fudge*Math.cos(theta));
				var fudgey = Math.abs(fudge*Math.sin(theta));
//				var pix = {x:x,y:y,z:z,xn:x*scale,yn:y*scale,zn:z*scale,c:function(x,y,f){return self.starColor(x,y,f);},c2:function(pix,f){return self.starColor3d(pix,f);}};
				var pix = {x:x,y:y,z:z,xn:testx*scale*fudgex,yn:testy*scale*fudgey,zn:z*scale,c:function(x,y,f){return self.starColor(x,y,f);},c2:function(pix,f){return self.starColor3d(pix,f);}};
//				var pix = {x:x,y:y,z:z,xn:testx*scale,yn:testy*scale,zn:z*scale,c:function(x,y,f){return self.starColor(x,y,f);},c2:function(pix,f){return self.starColor3d(pix,f);}};
				//this.pixelArray[cell++]=pix;
				this.id.data[cell]=Math.floor(255*(1-rad2/sradius2));
			}
			cell+=4;
		}
	}
	this.context.putImageData(this.id,0,0);
	var end = new Date();
	console.log('f:'+f+' in '+(end-sdate)+'ms  Maxn:'+this.nmax+' minn:'+this.nmin);
//	console.log('   start '+start+' skip '+skip+'  Maxn:'+this.nmax+' minn:'+this.nmin);
	console.log('   xnmax,xnmin:'+this.xnmax+','+this.xnmin);
	return end-sdate;	
}



Star.prototype.f = 1;
Star.prototype.nmax = -1;
Star.prototype.nmin = 1;
Star.prototype.animate1d = function(){
		var start = new Date();
		var w = 50;
		var h = 100;
		var scale = 100;
		var id = this.con.createImageData(w,h);
		var nm = new Array(h);
//		var max=-1;
//		var min=1;
		var pers = .5;
		var oct = 3;
		for (var y=0;y<h;y++){
//			var n = (this.noise1(2*y/scale)+1)/2*.7+(this.noise1(4*y/scale)+1)/2*.2+(this.noise1(8*y/scale)+1)/2*.1; //repeat at arsize
//			var n = this.noise(y/scale,this.f/scale);
			var n = 0;
			for (var i=0;i<oct;i++){
				var freq = Math.pow(2, i);
				var a = Math.pow(pers,i);
				n += this.noise(y*freq/scale, this.f*freq/scale);
			}
			if (n>this.nmax) this.nmax=n;
			if (n<this.nmin) this.nmin=n;
			nm[y]=n;
		}
//		var amp = 255/(max-min);
//		var alpha= 0;
//		var mina = 255;
//		var maxa = 0;
		for (var y=0;y<h;y++){
//			alpha=Math.floor((nm[y]-min)*amp);
//			alpha=Math.floor((nm[y]+.4)*256); // damn noise only seems to return between -.30 and .3
			alpha=Math.floor((nm[y]+.7)*256/1.4); // damn noise only seems to return between -.70 and .7
//			if (alpha>maxa) maxa=alpha;
//			if (alpha<mina) mina=alpha;
			for (var x=0;x<w;x++){
				id.data[(y*w+x)*4+3]=alpha;
			}
		}
		this.con.putImageData(id,50,0);
//		console.log('Animated noise max,min:'+max+','+min);
//		console.log('Animated noise maxa,mina:'+maxa+','+mina);
		var end = new Date();
		var tim =end-start;
		console.log('Frame '+this.f+' render: '+tim+' ms');
		this.f++;
		var thisO = this;
		if (this.f < 100){
			setTimeout(function(){thisO.animate();},tim*10);
		} else {
			console.log('Animated 1d noise max,min:'+this.nmax+','+this.nmin);
		}	
	
}
// Create mapping of noise to rgba
Star.prototype.createColorMap = function(){
	
	
}
// Checks if povar is inside star centered at (this.r,this.r)
Star.prototype.inStar = function(x,y){
	var rx = x-this.r;
	var ry = y-this.r;
	var l = rx*rx+ry*ry;
	return (l<this.r*this.r);
}
Star.prototype.createPixelMask = function(){
	var maxsize = this.r*2
	var r2 = this.r*this.r;
	this.pm = new Array(this.r*this.r);
	var i=0;
	for (var y=-this.r;y<this.r;y++){
		var y2 = y*y;
		for (var x=-this.r;x<this.r;x++){
			if ((y2+x*x)<r2) {this.pm[i]=1;}
			else {this.pm[i]=0;}
			i++;
		}
	}		
}
Star.prototype.drawPixelMask = function(){
	if (undefined == this.pm){
		this.createPixelMask();
	}
	var id = this.con.createImageData(this.r*2,this.r*2);
	for (var i=0; i<this.pm.length;i++){
		id.data[i*4+3]=255*this.pm[i];		
	}
	this.con.putImageData(id,this.cx-this.r,this.cy-this.r);
}
Star.prototype.avgrender={count:0,total:0};
Star.prototype.animate2d = function(){
	var start = new Date();
	var w = 100;
	var h = 100;
	var maxsize = this.r*2
	var scale = this.r/3;
	var id = this.con.createImageData(maxsize,maxsize);
	var nm = new Array(maxsize*maxsize);
//	var max=-1;
//	var min=1;
	var pers = .5;
	var oct = 3;
	var cell=0;
	var freq = [1,2,4,8];
	var amp = [1,.5,.25,.125]
	for (var y=0;y<maxsize;y++){
		for (var x=0;x<maxsize;x++){
//			if (!this.inStar(x,y)){nm[cell]=-99;cell++;continue;}
			if (!this.pm[cell]){nm[cell]=-99;cell++;continue;}
			var xp = (x+.01);
			var yp = (y+.01);
			var n = 0;
			var nscale = 0;
			for (var i=0;i<oct;i++){
				n += amp[i]*this.noise3d(xp*freq[i]/scale,yp*freq[i]/scale, this.f*freq[i]/scale);
				nscale += amp[i];
			}
			n /= nscale;
			if (n>this.nmax) this.nmax=n;
			if (n<this.nmin) this.nmin=n;
			nm[cell]=n;
			cell++;
		}
	}
//	var amp = 255/(max-min);
//	var alpha= 0;
//	var mina = 255;
//	var maxa = 0;
	var cbreak = 180/255; // noise over this level has some Blue, and R=255
	for (var cell=0;cell<nm.length;cell++){
		if (nm[cell] == -99) {alpha = 255;id.data[cell*4+3]=255;continue;}
		var nn = (nm[cell]+1)/2; // noise now between 0 and 1
		// Need to replace this with a colormap array for rgba lookup
		// Can multiply noise by 128, add 127 and take the floor.  
		// Each color table will have 255 entries with the proper color.
		if (nn>cbreak){
			id.data[cell*4]=255;
			id.data[cell*4+1]=Math.floor(215*(nn-cbreak)/(1-cbreak))+50;
			id.data[cell*4+2]=Math.floor((nn-cbreak)*255/(1-cbreak));
		}else {
			id.data[cell*4]=128+Math.floor(128*nn/cbreak);
			id.data[cell*4+1]=Math.floor(50*nn/cbreak);
		}
		id.data[cell*4+3]=255;
	}
	this.con.putImageData(id,this.cx-this.r,this.cy-this.r);
//	console.log('Animated noise max,min:'+max+','+min);
//	console.log('Animated noise maxa,mina:'+maxa+','+mina);
	var end = new Date();
	var tim =end-start;
	console.log('Frame '+this.f+' render: '+tim+' ms');
	this.f++;
	var thisO = this;
	if (this.f < 50){
		this.avgrender.count++;
		this.avgrender.total+=tim;
		setTimeout(function(){thisO.animate2d();},50); // There's gotta be a better way to get the scope set in js
	} else {
		console.log('Animated 2d noise max,min:'+this.nmax+','+this.nmin);
		console.log('Animated 2d avg render:'+this.avgrender.total/this.avgrender.count);
	}	

}
// Test turbulance output in a 50x200 strip
Star.prototype.turbulence = function(){
	var start = new Date();
	var t = -.5;
	var w = 157;
	var h = 50;
	var scale = 75;
	var maxt=-1;
	var mvar = 1;
	var n = new Array(w,h);
	var cell=0;
	var id = this.con.createImageData(w,h);
	for (var y=0;y<h;y++){
		var damp = (y-h)*(y-h)/h/h;
		for (var x=0;x<w;x++){
			// turbulence from gpugems
//			var t=-.5;
			var t=0;
//			for (var f=4.5;f <= w/12;f *= 2) {
			for (var f=1;f <= 8;f *= 2) {
//				t += Math.abs(this.sn.noise4d(x/scale,y/scale,this.f/scale,f)/f);
				t += Math.abs(this.sn.noise3d(x/f/scale,y/f/scale,this.f*4/f/scale)/f);
			}
			if (t>this.nmax){this.nmax=t;}
			if (t<this.nmin){this.nmin=t;}
			t=t*damp;
			if (t>.9) t=0;
			// pavar here
			var i = (y*w + x)*4;
			id.data[i]=Math.floor(255*t/.9);
			n[cell]=Math.floor(255*t/.9); // color mapping to x, y
			cell++;
			id.data[i+3]=255;
		}
	}
	this.con.putImageData(id,0,0);
	// Map this turbulence to a circle starting where x=r*cos(a), y=r*sin(a) for a=1...360
	for (var i=0; i<w*h;i++){
		//r=this.r+1 maps to y=0, 
	}
	var end = new Date();
	var tim =end-start;
	this.f++;
	console.log('Frame '+this.f+' render: '+tim+' ms');
	var thisO = this;
	if (this.f < 100){
		this.avgrender.count++;
		this.avgrender.total+=tim;
		setTimeout(function(){thisO.turbulence();},5); // There's gotta be a better way to get the scope set in js
	} else {
		console.log('Animated 2d turb noise max,min:'+this.nmax+','+this.nmin);
		console.log('Animated 2d turb avg render:'+this.avgrender.total/this.avgrender.count);
	}	
	//console.log('Turbulence noise max,min:'+maxt+','+mvar);
	//console.log('Turbulence 1 frame render:'+tim);
	
}

