ยด
Pi
e
Golden ratio
Sqrt(2)

Save as png


	
	var canvas = document.getElementById('irrationalCanvas');
/* Colors for 0 to 9 */
var color_arr = new Array(	"#FFFFFF", 
				"#FFBB00",
				"#FF4500",
				"#FF0000",
				"#D02090",
				"#7B68EE",
				"#0000FF",
				"#7FFFD4",
				"#00FF00",
				"#ADFF2F");
var dots = new Array;
var number_dots = 0;
	/*
	sA = start at the top (270 degrees)
	pA = one part is 36 degrees 
	ld = last digit (array (last digit, in a row) (5999 => last digit = 9,in a row = 3) 
	mp = midpoint (arc)
	br = big radius (from mp to arc)
	sr = small radius (from mp to end of lines)
	nods = number of digits
	irrat_name = name of irrational number (pi,e,golden_ratio,...)
	show_dots = true or false for a visualtization of sequences (see ld)
*/
var sA = (Math.PI / 180) * 270, 
	pA = (Math.PI / 180) * 36,  
	ld,  
	mp = 350, 
	br = 320, 
	sr = 270;
var irrat_number,irrat_label;
var tooltips;
var irrat_name,nods,show_dots;

var ctx = canvas.getContext("2d");

function draw_diagram(func_irrat_name,func_nods,func_show_dots) {
	ctx = canvas.getContext("2d");
	tooltips = [];
	irrat_name = func_irrat_name ? func_irrat_name : GET("n");
		irrat_name = irrat_name ? irrat_name : "pi";
	nods = func_nods ? func_nods : GET("nods");
		nods = nods ? nods : 800;
	show_dots = func_show_dots ? func_show_dots : GET("show_dots");
		show_dots = show_dots ? show_dots : true;

	nods = nods+2; // +2 because nods = digits after point
	switch(show_dots) {
		case "true":
			show_dots = true;
			break;
		case "false":
			show_dots = false;
			sr = Math.floor((sr+br)/2);
			break;
		default:
			show_dots = true;
	}

  /* Black background */
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.fillStyle = "black"; 
  ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height);
  
  ctx.fillStyle = "white";
  ctx.strokeStyle = "white";
  ctx.font = "italic 60pt Arial";
  switch (irrat_name) {
	case "pi":  
	 irrat_label = String.fromCharCode(parseInt('03C0', 16)); 
	 break;
	case "golden_ratio": 
	 irrat_label = String.fromCharCode(parseInt('03A6', 16)); // Phi
	 break;		
	case "e":
	 irrat_label = "e";
	 break;
	default:
	 var sqrt = irrat_name.substr(0,4);
	 var sqrt_of = parseInt(irrat_name.substr(4));
	 if (sqrt == "sqrt" && sqrt_of >=2 && sqrt_of <=8 && sqrt_of !=4) {
 		irrat_label = String.fromCharCode(parseInt('221A', 16))+irrat_name.substr(4);
	 } else {
		alert("We don't have this number in our database :/");
		// using pi instead because pi is awesome :D
		irrat_label = String.fromCharCode(parseInt('03C0', 16)); 
		irrat_name = "pi";
	 }
  }
  
  // write irrat_label in the "middle"
  ctx.fillText(irrat_label, mp-35,mp+30);

  for (var i=0; i <= 9; i++) {
	  // every digit has a color
	  ctx.strokeStyle = color_arr[i]; 
	  ctx.lineWidth = 5;
	  ctx.beginPath();
	  ctx.arc (mp,  mp,  br,  sA+(i)*pA,  sA+(i+1)*pA,  false);
	  ctx.stroke();
	  ctx.closePath();
	  ctx.fillStyle = "white";
	  ctx.strokeStyle = "white";
	  ctx.font = "italic 12pt Arial";
	  if (i > 4 && i < 7) {
		ctx.fillText(i.toString(), mp+(br+40)*Math.cos(sA+(i+0.5)*pA),mp+(br+40)*Math.sin(sA+(i+0.5)*pA));
	  } else {
		if (i == 3 || i == 4 || i == 7 || i == 8) {
			ctx.fillText(i.toString(), mp+(br+25)*Math.cos(sA+(i+0.5)*pA),mp+(br+25)*Math.sin(sA+(i+0.5)*pA));
		} else {
			ctx.fillText(i.toString(), mp+(br+10)*Math.cos(sA+(i+0.5)*pA),mp+(br+10)*Math.sin(sA+(i+0.5)*pA));
		}
	  }
  }
	  
	  
	  // supported irrational numbers (first 9999 digits)
	  switch(irrat_name) {
		case "pi" :
			irrat_number = '3.141592653589793238462643...';			break;
		case "golden_ratio": 
			irrat_number = '1.618033988749894848204586...';			break;
		case "e": 
			irrat_number = '2.718281828459045235360287...';			break;
		case "sqrt2": 
			irrat_number = '1.414213562373095048801688...';			break;
		case "sqrt3": 
			irrat_number = '1.732050807568877293527446...';			break;
		case "sqrt5": 
			irrat_number = '2.236067977499789696409173...';			break;
		case "sqrt6": 
			irrat_number = '2.449489742783178098197284...';			break;
		case "sqrt7": 
			irrat_number = '2.645751311064590590501615...';			break;
		case "sqrt8": 
			irrat_number = '2.828427124746190097603377...';			break;
	  }
	  
	  ld = new Array('',0); // last digit not set and the sequence has the length 0
	  if (nods >= 10000) { nods = 9999; alert("Showing only 9999 decimal places..."); }
	  for (i = 0; i < nods; i++) {
		  if (i != 1) { // decimal point
			if (i == 0) {
				ld = line(1,parseInt(irrat_number.substr(i,1)),parseInt(irrat_number.substr(2,1)),ld);
			} else {
				ld = line(i,parseInt(irrat_number.substr(i,1)),parseInt(irrat_number.substr(i+1,1)),ld);
			}
		  }
		  if (i == nods-1) {
			setAllTooltips();
		  }
	  }
}

function line(dp,no_1,no_2,ld) {
	if (show_dots) {
		if (no_2 == ld[0]) {
			ld[1]++; // increment in a row
		} else {
			ld[1]++;
			if (ld[1] >= 2) {
			 ctx.strokeStyle = color_arr[ld[0]];
			 ctx.beginPath();
			 ctx.fillStyle = color_arr[ld[0]];
			 ctx.arc(
				mp+((sr+br)/2)*Math.cos(sA+(ld[0]+(dp-ld[1]/2)/nods)*pA), // midpoint x
				mp+((sr+br)/2)*Math.sin(sA+(ld[0]+(dp-ld[1]/2)/nods)*pA), // midpoint y
				Math.min(2*Math.PI*sr*((ld[1]/2)/nods),(br-sr)/2-2),  // radius should not be bigger than ((big radius)-(small-radius))/2-2
				0, 2*Math.PI); 
			 ctx.fill();
			 ctx.closePath();
			 dots[number_dots] = new Array(
									mp+((sr+br)/2)*Math.cos(sA+(ld[0]+(dp-ld[1]/2)/nods)*pA), // midpoint x
									mp+((sr+br)/2)*Math.sin(sA+(ld[0]+(dp-ld[1]/2)/nods)*pA), // midpoint y
									Math.min(2*Math.PI*sr*((ld[1]/2)/nods),(br-sr)/2-2), // radius
									(dp-ld[1]).toString()+' - '+(dp-1).toString()); // decimal place begin - end
			 number_dots++;
			}
			ld[1] = 0;
			ld[0] = no_2;
		}
	}

	var nod_1 = dp/nods; 
	var nod_2 = (dp+1)/nods;
	// get the middle of no_1+nod_1 and no_2+nod_2 (middle of 9 and 0 isn't 4.5 it's 9.5)
	var quadcurve_int = middle(no_1+nod_1,no_2+nod_2); 
	
	var grad = ctx.createLinearGradient(
		mp+sr*Math.cos(sA+(no_1+nod_1)*pA),
		mp+sr*Math.sin(sA+(no_1+nod_1)*pA),
		mp+sr*Math.cos(sA+(no_2+nod_2)*pA),
		mp+sr*Math.sin(sA+(no_2+nod_2)*pA)
	);
	grad.addColorStop(0, color_arr[no_2]); // can be switched so no_2 = no_1 and no_1 = no_2 but I think this is prettier
	grad.addColorStop(1, color_arr[no_1]);
	
	ctx.lineWidth = 1;
	ctx.strokeStyle = grad;
	ctx.beginPath();
	
	// drawing a curved line from no_1 to no_2
	ctx.moveTo(mp+sr*Math.cos(sA+(no_1+nod_1)*pA), mp+sr*Math.sin(sA+(no_1+nod_1)*pA));
	ctx.quadraticCurveTo(
		mp+0.7*sr*Math.cos(sA+(quadcurve_int)*pA),
		mp+0.7*sr*Math.sin(sA+(quadcurve_int)*pA),
		mp+sr*Math.cos(sA+(no_2+nod_2)*pA),
		mp+sr*Math.sin(sA+(no_2+nod_2)*pA)
	);
	ctx.stroke();
	return ld;
}



function middle(no_1,no_2) {
	var min = Math.min(no_1,no_2);
	var max = Math.max(no_1,no_2);
	var i = max-min; // counterclockwise 
	var j = 10+Math.floor(min) - Math.floor(max); // clockwise
	if (i < j) {
		return min+i/2;
	} else {
		return max+j/2;
	}
}



/* Tooltip function */
var width = ctx.canvas.width,
	height = ctx.canvas.height;

savedState = null;

var defaults = {
			tooltips: {
				background: 'rgba(0,0,0,0.6)',
				fontFamily : "'Times New Roman'",
				fontStyle : "normal",
				fontColor: 'white',
				fontSize: '12px',
				labelTemplate: '<%=label%>',
				padding: {
					top: 10,
					right: 10,
					bottom: 10,
					left: 10
				},
				offset: {
					left: 0,
					top: 0
				},
				border: {
					width: 0,
					color: '#000'
				},
				showHighlight: true,
				highlight: {
					stroke: {
						width: 1,
						color: 'rgba(230,230,230,0.25)'
					},
					fill: 'rgba(255,255,255,0.25)'
				}
			}
		},
		options = (options) ? mergeChartConfig(defaults, options) : defaults;

	function registerTooltip(ctx,areaObj,data) {
		tooltips.push(new Tooltip(
			ctx,
			areaObj,
			data
		));
	}

	var Tooltip = function(ctx, areaObj, data) {
		this.ctx = ctx;
		this.areaObj = areaObj;
		this.data = data;
		this.savedState = null;
		this.highlightState = null;
		this.x = null;
		this.y = null;

		this.inRange = function(x,y) {
			if(this.areaObj.type) {
				switch(this.areaObj.type) {
					case 'rect':
						return (x >= this.areaObj.x && x <= this.areaObj.x+this.areaObj.width) &&
						   (y >= this.areaObj.y && y <= this.areaObj.y+this.areaObj.height);
						   break;
					case 'circle':
						return ((Math.pow(x-this.areaObj.x, 2)+Math.pow(y-this.areaObj.y, 2)) < Math.pow(this.areaObj.r,2));
						break;
					case 'shape':
						var poly = this.areaObj.points;
						for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
							((poly[i].y <= y && y < poly[j].y) || (poly[j].y <= y && y < poly[i].y))
							&& (x < (poly[j].x - poly[i].x) * (y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)
							&& (c = !c);
						return c;
						break;
				}
			}
		}

		this.render = function(x,y) {
			if(this.savedState == null) {
				this.ctx.putImageData(savedState,0,0);
				this.savedState = this.ctx.getImageData(0,0,this.ctx.canvas.width,this.ctx.canvas.height);
			}
			this.ctx.putImageData(this.savedState,0,0);
			if(options.tooltips.showHighlight) {
				if(this.highlightState == null) {
					this.ctx.strokeStyle = options.tooltips.highlight.stroke.color;
					this.ctx.lineWidth = options.tooltips.highlight.stroke.width;
					this.ctx.fillStyle = options.tooltips.highlight.fill;
					switch(this.areaObj.type) {
						case 'rect':
							this.ctx.strokeRect(this.areaObj.x, this.areaObj.y, this.areaObj.width, this.areaObj.height);
							this.ctx.fillStyle = options.tooltips.highlight.fill;
							this.ctx.fillRect(this.areaObj.x, this.areaObj.y, this.areaObj.width, this.areaObj.height);
							break;
						case 'circle':
							this.ctx.beginPath();
							this.ctx.arc(this.areaObj.x, this.areaObj.y, this.areaObj.r, 0, 2*Math.PI, false);
							this.ctx.stroke();
							this.ctx.fill();
							break;
						case 'shape':
							this.ctx.beginPath();
							this.ctx.moveTo(this.areaObj.points[0].x, this.areaObj.points[0].y);
							for(var p in this.areaObj.points) {
								this.ctx.lineTo(this.areaObj.points[p].x, this.areaObj.points[p].y);
							}
							this.ctx.stroke();
							this.ctx.fill();
							break;
					}
					this.highlightState = this.ctx.getImageData(0,0,this.ctx.canvas.width,this.ctx.canvas.height);
				} else {
					this.ctx.putImageData(this.highlightState,0,0);
				}
			}
			//if(this.x != x || this.y != y) {
				var posX = x+options.tooltips.offset.left,
					posY = y+options.tooltips.offset.top,
					tpl = tmpl(options.tooltips.labelTemplate, this.data),
					rectWidth = options.tooltips.padding.left+this.ctx.measureText(tpl).width+options.tooltips.padding.right;
				if(posX + rectWidth > ctx.canvas.width) {
					posX -= posX-rectWidth < 0 ? posX : rectWidth;
				}
				if(posY + 24 > ctx.canvas.height) {
					posY -= 24;
				}
				this.ctx.fillStyle = options.tooltips.background;
				this.ctx.fillRect(posX, posY, rectWidth, 24);
				if(options.tooltips.border.width > 0) {
					this.ctx.fillStyle = options.tooltips.order.color;
					this.ctx.lineWidth = options.tooltips.border.width;
					this.ctx.strokeRect(posX, posY, rectWidth, 24);
				}
				this.ctx.font = options.tooltips.fontStyle+ " "+options.tooltips.fontSize+" " + options.tooltips.fontFamily;
				this.ctx.fillStyle = options.tooltips.fontColor;
				this.ctx.textAlign = 'center';
				this.ctx.textBaseline = 'middle';
				this.ctx.fillText(tpl, posX+rectWidth/2, posY+12);
				this.x = x;
				this.y = y;
			//}
		}
	}
	
	function getPosition(e) {
		var xPosition = 0;
		var yPosition = 0;

		while(e) {
			xPosition += (e.offsetLeft + e.clientLeft);
			yPosition += (e.offsetTop + e.clientTop);
			e = e.offsetParent;
		}
		if(window.pageXOffset > 0 || window.pageYOffset > 0) {
			xPosition -= window.pageXOffset;
			yPosition -= window.pageYOffset;
		} else if(document.body.scrollLeft > 0 || document.body.scrollTop > 0) {
			xPosition -= document.body.scrollLeft;
			yPosition -= document.body.scrollTop;
		}
		return { x: xPosition, y: yPosition };
	}
	
	function tooltipEventHandler(e) {
		if(tooltips.length > 0) {
			savedState = savedState == null ? ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height) : savedState;
			var rendered = 0;
			for(var i in tooltips) {
				var position = getPosition(ctx.canvas),
					mx = (e.clientX)-position.x,
					my = (e.clientY)-position.y;
				if(tooltips[i].inRange(mx,my)) {
					tooltips[i].render(mx,my);
					rendered++;
				}
			}
			if(rendered == 0) {
				ctx.putImageData(savedState,0,0);
			}
		}
	}
	
	 if (is_touch_device()) {
		ctx.canvas.ontouchstart = function(e) {
			e.clientX = e.targetTouches[0].clientX;
			e.clientY = e.targetTouches[0].clientY;
			tooltipEventHandler(e);
		}
		ctx.canvas.ontouchmove = function(e) {
			e.clientX = e.targetTouches[0].clientX;
			e.clientY = e.targetTouches[0].clientY;
			tooltipEventHandler(e);
		}
	} else {
		ctx.canvas.onmousemove = function(e) {
			tooltipEventHandler(e);
		}
	}
	
  function is_touch_device() {
		return !!('ontouchstart' in window) // works on most browsers 
        || !!('onmsgesturechange' in window); // works on ie10
  };   
  
   function tmpl(str, data){
		// Figure out if we're getting a template, or if we need to
		// load the template - and be sure to cache the result.
		var fn = !/\W/.test(str) ?
		  cache[str] = cache[str] ||
			tmpl(document.getElementById(str).innerHTML) :

		  // Generate a reusable function that will serve as a template
		  // generator (and which will be cached).
		  new Function("obj",
			"var p=[],print=function(){p.push.apply(p,arguments);};" +

			// Introduce the data as local variables using with(){}
			"with(obj){p.push('" +

			// Convert the template into pure JavaScript
			str
			  .replace(/[\r\t\n]/g, " ")
			  .split("<%").join("\t")
			  .replace(/((^|%>)[^\t]*)'/g, "$1\r")
			  .replace(/\t=(.*?)%>/g, "',$1,'")
			  .split("\t").join("');")
			  .split("%>").join("p.push('")
			  .split("\r").join("\\'")
		  + "');}return p.join('');");

		// Provide some basic currying to the user
		return data ? fn( data ) : fn;
	  };
  
 function setAllTooltips() {	
	for(var i = 0; i < number_dots; i++) { 
		registerTooltip(ctx,{type:'circle',x:dots[i][0],y:dots[i][1],r:dots[i][2]},{label:dots[i][3]}); 
	}
 }
 /* Save diagram */
	function save() {
		var save_nods = nods-2;
		var canvas = document.getElementById("irrationalCanvas");
		canvas.toBlob(function(blob) {
			saveAs(blob, irrat_name+"_"+save_nods+".png");
		});
	}; 
  
 

function GET(name) {
		var result = (RegExp(name + '=' + '(.+?)(&|$)'). 
		exec(location.search)||[,undefined])[1]; 
		return result;
	}