topical media & game development 
  
 
 
 
 
  
    
    
  
 graphic-canvas-example-function-script.js / js
  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)+'<BR>'+str.substring(11,str.length);
                          g('errorBox').innerHTML='<A href="http://my.opera.com/Benjamin%20Joffe/homes/functions/error.html">'+str+'</A>';
                          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 (theta<Math.PI/2 || theta>Math.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 && theta<Math.PI*5/4) primaryInc=false;
                  if (eleva>Math.PI/2 && eleva<Math.PI*3/2) primaryInc=!primaryInc;        
                  if (theta\%Math.PI < Math.PI/4 || theta\%Math.PI > 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; j<arrow.length; j++) {
                                  co = iso(arrow[j][(0+i)%3], arrow[j][(1+i)%3], arrow[j][(2+i)%3]);
                                  if (arrow[j][3]) ctx.moveTo(co.x, co.y);
                                  else ctx.lineTo(co.x, co.y);
                          }
                  }
  
                  // z-letter
                  co = iso(0, 0, 1.6);
                  ctx.moveTo(co.x-0.2, co.y);
                  ctx.lineTo(co.x-0.1, co.y);
                  ctx.lineTo(co.x-0.2, co.y+0.1);
                  ctx.lineTo(co.x-0.1, co.y+0.1);
                  
                  // y-letter
                  
                  co = iso(0, 1.6, 0);
                  ctx.moveTo(co.x, co.y-0.2);
                  ctx.lineTo(co.x, co.y-0.25);
                  ctx.lineTo(co.x-0.05, co.y-0.3);
                  ctx.moveTo(co.x, co.y-0.25);
                  ctx.lineTo(co.x+0.05, co.y-0.3);
                  
                  // x-letter
                  
                  co = iso(1.6, 0, 0);
                  ctx.moveTo(co.x+0.05, co.y-0.2);
                  ctx.lineTo(co.x-0.05, co.y-0.3);
                  ctx.moveTo(co.x-0.05, co.y-0.2);
                  ctx.lineTo(co.x+0.05, co.y-0.3);
                  
                  ctx.strokeStyle='rgba(150,0,0,0.8)';
                  ctx.stroke();
                  ctx.strokeStyle = '#000';
                  
  
                  var lastRow=[];
                  var lastCo=false;
                  var co;
                  
                  var i, j;
                  var z;
          
                  ctx.lineWidth=0.003;
                  for (i=0; i<=20; i++) {
                  
                          lastCo=false;
                          
                          for (j=0; j<=20; j++) {
                  
                                  z = 2*(plot[i][j]-zMin)/(zMax-zMin)-1;
                                  
                                  if (isNaN(z)) co = false;
                                  else {
  
                                          co = iso(i/10-1, j/10-1, z>1 ? 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;
                  }
          }
          
  };
  
  
(C) Æliens 
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher. 
In case of other copyright issues, contact the author.