window.onload=function(){
function g(e){
return document.getElementById(e);
}
g('functions3d').style.visibility = 'visible';
g('loading').style.display = 'none';
var functionValue = 'Math.sqrt(1-x*x-y*y)';
var theta = 4.2;
var eleva = 0.6;
var col={
r:255,
g:0,
b:0
}
var paintType=0;
/*
0: single colour
1: height spectrum
*/
var
xMin = -1,
yMin = -1,
zMin = -1,
xMax = +1,
yMax = +1,
zMax = +1;
var e_preview = g('preview');
var ctx_preview = e_preview.getContext('2d');
ctx_preview.translate(150,150);
ctx_preview.scale(82,82);
var e_fullview = g('fullview');
var ctx_fullview = e_fullview.getContext('2d');
ctx_fullview.translate(200,200);
var e_container = g('container');
var e_view1 = g('view1');
var e_view2 = g('view2');
var e_view3 = g('view3');
var dragMode = 0;
var e_dragMode = document.getElementsByName('dragMode');
g('paintType').onchange=function(){
paintType=this.options.selectedIndex;
g('colourBox').style.display= paintType==0 ? 'block' : 'none';
}
e_dragMode[0].onclick = function(){
dragMode = 0;
e_preview.style.cursor="move";
g('scaleBoxes').style.display='none';
};
e_dragMode[1].onclick = function(){
dragMode = 1;
e_preview.style.cursor="move";
g('scaleBoxes').style.display='none';
};
e_dragMode[2].onclick = function(){
e_preview.style.cursor="n-resize";
dragMode = 2;
g('scaleBoxes').style.display='none';
};
e_dragMode[3].onclick = function(){
e_preview.style.cursor="n-resize";
dragMode = 3;
g('scaleBoxes').style.display='block';
};
var e_scaleBoxX=g('scaleBoxX');
var e_scaleBoxY=g('scaleBoxY');
var e_scaleBoxZ=g('scaleBoxZ');
e_scaleBoxX.onclick = e_scaleBoxY.onclick = e_scaleBoxZ.onclick = function(){
var t=this;
setTimeout(function(){
var count=0;
if (e_scaleBoxX.checked) count++;
if (e_scaleBoxY.checked) count++;
if (e_scaleBoxZ.checked) count++;
if (count==0) {
t.checked=true;
}
}, 100);
}
var
e_functionInput = g('functionInput'),
e_xMin = g('xMin'),
e_yMin = g('yMin'),
e_zMin = g('zMin'),
e_xMax = g('xMax'),
e_yMax = g('yMax'),
e_zMax = g('zMax');
e_functionInput.onkeyup = function(){
var s = parseMath(this.value);
if (s[0]) {
functionValue = s[1];
plotData();
if (!previewWait) previewWait = setTimeout(previewRender, 10);
}
}
e_functionInput.onblur = function(){
var s = parseMath(this.value);
if (!s[0]) {
var str = s[1];
str = str.substr(0,11)+'
'+str.substring(11,str.length);
g('errorBox').innerHTML=''+str+'';
g('functionRender').disabled=true;
}
else {
g('functionRender').disabled=false;
}
}
e_functionInput.onfocus = function(){
g('errorBox').innerHTML="";
g('functionRender').disabled=false;
}
g('reset').onmousedown=function(){
e_xMin.value = e_yMin.value = e_zMin.value = xMin = yMin = zMin = -1;
e_xMax.value = e_yMax.value = e_zMax.value = xMax = yMax = zMax = 1;
plotData();
previewRender();
}
g('functionRender').onclick = function(){
slideView(0, 464, fullRender);
ctx_fullview.clearRect(-200,-200,400,400);
e_view2.focus();
return false;
}
g('save').onclick=function(){
// remove black background
var temp = document.createElement('CANVAS');
temp.setAttribute('width', 400);
temp.setAttribute('height', 400);
temp = temp.getContext('2d');
temp.fillStyle='#FFF';
temp.fillRect(0,0,400,400);
temp.drawImage(ctx_fullview.canvas, 0,0);
window.open(temp.canvas.toDataURL());
}
g('arrow').onmousedown=function(){
e_view1.style.display='none';
e_view3.style.display='block';
}
g('arrow2').onmousedown=function(){
e_view1.style.display='block';
e_view3.style.display='none';
}
var slideView_active=false;
function slideView(from, to, exe){
if (slideView_active) return;
slideView_active=true;
var i = 0;
var loopy = setInterval(function(){
i++;
var n = i/20;
n = (1-Math.cos(n*3.141592653))/2;
e_container.scrollLeft=n*(to-from)+from;
if (i==20) {
clearInterval(loopy);
slideView_active=false;
if (exe) exe();
}
}, 20);
}
function HSVtoRGB(h,s,v){
h%=6;
var r,g,b;
if (s==0) r=g=b=v;
else {
var H=h>>0;
var f=h-H;
var p=v*(1-s);
var q=v*(1-f*s);
var t=v*(1-(1-f)*s);
if (H==0) r=v, g=t, b=p;
if (H==1) r=q, g=v, b=p;
if (H==2) r=p, g=v, b=t;
if (H==3) r=p, g=q, b=v;
if (H==4) r=t, g=p, b=v;
if (H==5) r=v, g=p, b=q;
}
return {r:(r = r*256 >> 0) < 0 ? 0 : r > 255 ? 255 : r,
g:(g = g*256 >> 0) < 0 ? 0 : g > 255 ? 255 : g,
b:(b = b*256 >> 0) < 0 ? 0 : b > 255 ? 255 : b
};
}
g('ring').onmousedown=function(e){
var xOff=this.offsetParent.offsetLeft+11; //the 11 is for the container's offset
var yOff=this.offsetParent.offsetTop;
var mark=g('mark1');
var x=e.clientX-xOff;
var y=e.clientY-yOff;
if ( (90-x)*(90-x)+(90-y)*(90-y) > 90*90) return;
document.onmousemove=function(e){
var x=e.clientY-yOff
var y=e.clientX-xOff
var d = Math.sqrt((90-x)*(90-x)+(90-y)*(90-y));
if (d<5) {
x=90;
y=90;
d=0;
}
if (d>72) {
x = ((x-90)*72/d+90)>>0;
y = ((y-90)*72/d+90)>>0;
d=72;
}
mark.style.top=x+'px';
mark.style.left=y+'px';
var theta=Math.atan2(90-x,y-90);
theta=(theta+Math.PI*2)%(Math.PI*2);
theta=theta*6/(Math.PI*2);
col = HSVtoRGB(theta, d/72, 1);
g('colourDisplay').style.backgroundColor='rgb('+col.r+','+col.g+','+col.b+')';
}
document.onmousemove(e);
document.onmouseup=function(){
document.onmousemove=null;
}
}
//############################################## BEGIN FULL RENDER
var renderCancel;
function fullRender(){
renderCancel=false;
var ctx = ctx_fullview;
ctx.scale(100,100);
var count = 100;
var f = new Function('x', 'y', 'return '+functionValue);
var lastRow=[];
var lastCo=false;
var co;
var x,y;
var i=0, j=0;
ctx.lineWidth=0.008;
var rr = col.r;
var gg = col.g;
var bb = col.b;
function drawSurface(a,b,c){
ctx.beginPath();
ctx.moveTo(a.draw.x, a.draw.y);
ctx.lineTo(b.draw.x, b.draw.y);
ctx.lineTo(c.draw.x, c.draw.y);
ctx.closePath();
var x = (a.y-b.y) * (a.z-c.z) - (a.z-b.z) * (a.y-c.y);
var y = (a.z-b.z) * (a.x-c.x) - (a.x-b.x) * (a.z-c.z);
var z = (a.x-b.x) * (a.y-c.y) - (a.y-b.y) * (a.x-c.x);
var X = Math.tan(theta);
var Y = 1;
var Z = Math.tan(eleva)*Math.sqrt(X*X+1);
if (thetaMath.PI*3/2) Z*=-1;
var area = (x*X + y*Y + z*Z) / Math.sqrt( (x*x + y*y + z*z)*(X*X + Y*Y + Z*Z) );
if (area<0) area*=-1;
if (paintType==1) {
var h = (a.z+b.z+c.z+3)*(5.5/6);
if (h<1) rr=1, gg=0, bb=h;
else if (h<2) rr=2-h, gg=0, bb=1;
else if (h<3) rr=0, gg=h-2, bb=1;
else if (h<4) rr=0, gg=1, bb=4-h;
else if (h<5) rr=h-4, gg=1, bb=0;
else rr=1, gg=6-h, bb=0;
rr = Math.round(rr*255);
gg = Math.round(gg*255);
bb = Math.round(bb*255);
}
ctx.strokeStyle = ctx.fillStyle = 'rgba('+ ((rr*area)>>0) +','+ ((gg*area)>>0) +','+ ((bb*area)>>0) +',1)';
ctx.fill();
ctx.stroke();
}
var xIsPrimary=true;
var primaryInc=true;
if (theta>Math.PI/4 && thetaMath.PI/2 && eleva Math.PI*3/4) xIsPrimary=false;
//alert("eleva:"+eleva+"\ntheta: "+theta+"\nxIsPrimary: "+xIsPrimary+"\nprimaryInc: "+primaryInc);
if (!xIsPrimary) theta = (theta+Math.PI*1.5)%(2*Math.PI);
if (!primaryInc) theta = (theta+Math.PI)%(2*Math.PI);
function undoChanges(){
ctx.scale(1/100,1/100);
if (!xIsPrimary) {
theta = (theta+Math.PI*0.5)%(2*Math.PI);
}
if (!primaryInc) {
theta = (theta+Math.PI)%(2*Math.PI);
}
}
function render(){
var start = new Date();
for (; i<=count; i++) {
if (xIsPrimary) {
if (primaryInc) x = xMin + (xMax-xMin)*i/count;
else x = xMax + (xMin-xMax)*i/count;
}
else {
if (primaryInc) y = yMax + (yMin-yMax)*i/count;
else y = yMin + (yMax-yMin)*i/count;
}
for (; j<=count; j++) {
if (new Date()-start>100) {setTimeout(render,10);return;}
if (renderCancel) {
undoChanges();
return;
}
if (xIsPrimary) {
if (primaryInc) y = yMin + (yMax-yMin)*j/count;
else y = yMax + (yMin-yMax)*j/count;
}
else {
if (primaryInc) x = xMin + (xMax-xMin)*j/count;
else x = xMax + (xMin-xMax)*j/count;
}
co = 2*(f(x,y)-zMin)/(zMax-zMin)-1;
if (isNaN(co) || co>1 || co<-1) co=false;
else {
co = {
x: 2*i/count-1,
y: 2*j/count-1,
z: co
};
co.draw = iso(co.x, co.y, co.z);
if (j>0 && i>0 && lastRow[j-1]) {
if (lastRow[j]) drawSurface(co, lastRow[j-1], lastRow[j]);
if (lastCo) drawSurface(co, lastCo, lastRow[j-1]);
if ((i-1)%((count/20)>>0)==0 && lastRow[j]) {
ctx.globalCompositeOperation = 'over'
ctx.beginPath();
ctx.moveTo(lastRow[j].draw.x, lastRow[j].draw.y);
ctx.lineTo(lastRow[j-1].draw.x, lastRow[j-1].draw.y);
if ((j-1)%5==0 && lastCo) ctx.lineTo(lastCo.draw.x, lastCo.draw.y);
ctx.strokeStyle="rgba(0,0,0,0.5)";
ctx.stroke();
}
else if ((j-1)%((count/20)>>0)==0 && lastCo) {
ctx.beginPath();
ctx.moveTo(lastRow[j-1].draw.x, lastRow[j-1].draw.y);
ctx.lineTo(lastCo.draw.x, lastCo.draw.y);
ctx.strokeStyle="rgba(1,1,1,0.5)";
ctx.stroke();
}
}
}
lastRow[j-1]=lastCo;
lastCo = co;
}
lastCo = false;
lastRow[count]=co;
j=0;
}
undoChanges();
}
setTimeout(render, 50);
}
g('backButton').onclick=function(){
renderCancel=true;
slideView(464,0);
}
//############################################## END FULL RENDER
function iso(x,y,z){
var dist = Math.sqrt(x*x+y*y);
var angle = (x==0 && y==0) ? 0 : Math.atan(y/x) + theta + ((x<0)? Math.PI : 0);
x=Math.cos(angle)*dist;
y=-Math.sin(angle)*dist;
var fact = (y*Math.cos(eleva) + z*Math.sin(eleva)+5)/5;
y=y*Math.sin(eleva) - z*Math.cos(eleva);
x*=fact;
y*=fact;
return {
x: x,
y: y
};
}
var plot=[];
function plotData(){
var x;
var f = new Function('x', 'y', 'return '+functionValue);
for (var i=0; i<=20; i++) {
plot[i]=[];
x = xMin+(xMax-xMin)*i/20;
for (var j=0; j<=20; j++) {
plot[i][j] = f(x, yMin+(yMax-yMin)*j/20);
}
}
}
plotData();
var previewWait = false;
function previewRender(){
var t1 = new Date();
previewWait = false;
var ctx = ctx_preview;
ctx.clearRect(-2,-2,4,4);
var co;
ctx.lineJoin = "round";
var xCenter = 150;
var yCenter = 150;
var scale = 82;
ctx.beginPath();
co = iso(-1,-1,-1); ctx.moveTo(co.x, co.y);
co = iso(-1,+1,-1); ctx.lineTo(co.x, co.y);
co = iso(+1,+1,-1); ctx.lineTo(co.x, co.y);
co = iso(+1,-1,-1); ctx.lineTo(co.x, co.y);
co = iso(+1,-1,+1); ctx.lineTo(co.x, co.y);
co = iso(+1,+1,+1); ctx.lineTo(co.x, co.y);
co = iso(-1,+1,+1); ctx.lineTo(co.x, co.y);
co = iso(-1,-1,+1); ctx.lineTo(co.x, co.y);
co = iso(-1,-1,-1); ctx.lineTo(co.x, co.y);
co = iso(+1,+1,-1); ctx.moveTo(co.x, co.y);
co = iso(+1,+1,+1); ctx.lineTo(co.x, co.y);
co = iso(-1,+1,-1); ctx.moveTo(co.x, co.y);
co = iso(-1,+1,+1); ctx.lineTo(co.x, co.y);
co = iso(-1,-1,+1); ctx.moveTo(co.x, co.y);
co = iso(+1,-1,+1); ctx.lineTo(co.x, co.y);
co = iso(-1,-1,-1); ctx.moveTo(co.x, co.y);
co = iso(+1,-1,-1); ctx.lineTo(co.x, co.y);
ctx.lineWidth=0.01;
ctx.strokeStyle = 'rgba(0,0,100,0.8)';
ctx.stroke();
// xyz thingy
ctx.beginPath();
var arrow = [
[0, 0, 0, true],
[0, 0, 1.6 ],
[0.05, 0.05, 1.4 ],
[0.05, -0.05, 1.4 ],
[-0.05, -0.05, 1.4 ],
[-0.05, 0.05, 1.4 ],
[0, 0, 1.6 ],
[-0.05, -0.05, 1.4 ],
[0.05, -0.05, 1.4, true],
[0, 0, 1.6 ],
[0.05, 0.05, 1.4, true],
[-0.05, 0.05, 1.4 ]
];
for (var i=0,j; i<3; i++) {
for (j=0; j1 ? 1 : z<-1 ? -1 : z);
ctx.beginPath();
if (lastCo) {
ctx.moveTo(lastCo.x, lastCo.y);
ctx.lineTo(co.x, co.y);
}
else if (lastRow[j]) ctx.moveTo(co.x, co.y);
if (lastRow[j]) ctx.lineTo(lastRow[j].x, lastRow[j].y);
ctx.stroke();
}
lastRow[j] = lastCo = (z<=1 && z>=-1) && co;
}
}
}
previewRender();
/************ Controlling Functions ************/
e_preview.onmousedown=function(e){
var x0 = e.clientX, y0 = e.clientY;
if (dragMode==0) {
document.onmousemove=function(e){
theta -= (x0 - (x0=e.clientX))/100;
eleva -= (y0 - (y0=e.clientY))/100;
theta%=Math.PI*2; if (theta<0) theta+=Math.PI*2;
eleva%=Math.PI*2; if (eleva<0) eleva+=Math.PI*2;
if (!previewWait) previewWait = setTimeout(previewRender, 10);
}
}
else if (dragMode==1) { // pan (x,y) ##########
document.onmousemove=function(e){
var dx = x0 - (x0=e.clientX);
var dy = y0 - (y0=e.clientY);
var cx = dy*Math.sin(theta) - dx*Math.cos(theta);
var cy = dx*Math.sin(theta) + dy*Math.cos(theta);
var stepx = (xMax-xMin)/200;
var stepy = (yMax-yMin)/200;
e_xMin.value = xMin-= cx*stepx;
e_xMax.value = xMax-= cx*stepx;
e_yMin.value = yMin-= cy*stepy;
e_yMax.value = yMax-= cy*stepy;
if (!previewWait) previewWait = setTimeout(function(){
plotData()
previewRender();
}, 10);
}
}
else if (dragMode==2) { // shift (z) ##########
document.onmousemove=function(e){
var dy = (y0 - (y0=e.clientY));
var step = (zMax-zMin)/200;
e_zMax.value = zMax -= step*dy;
e_zMin.value = zMin -= step*dy;
if (!previewWait) previewWait = setTimeout(previewRender, 10);
}
}
else if (dragMode==3) { // scale ##########
document.onmousemove=function(e){
var dy=-(y0 - (y0=e.clientY))/100;
var mid,scale;
if (g('scaleBoxX').checked){
mid = (xMin+xMax)/2;
scale = Math.abs(Math.exp(dy)*(xMax-xMin)/2);
e_xMin.value = xMin = mid-scale;
e_xMax.value = xMax = mid+scale;
}
if (g('scaleBoxY').checked){
mid = (yMin+yMax)/2;
scale = Math.abs(Math.exp(dy)*(yMax-yMin)/2);
e_yMin.value = yMin = mid-scale;
e_yMax.value = yMax = mid+scale;
}
if (g('scaleBoxZ').checked){
mid = (zMin+zMax)/2;
scale = Math.abs(Math.exp(dy)*(zMax-zMin)/2);
e_zMin.value = zMin = mid-scale;
e_zMax.value = zMax = mid+scale;
}
if (!previewWait) previewWait = setTimeout(function(){
plotData()
previewRender();
}, 10);
}
}
document.onmouseup=function(e){
document.onmousemove=null;
}
}
};