// ViewPort.js
// General Plotting routines
// =======================
  function ViewPortDoc()
  {
   return document;
  };
  function roundFactor(num,fact)
  {
   return Math.round(num*fact)/fact;
  };
  var scale,font_scale,line_scale;
  function attachSVGDocument(w,h,s,font_stretch,line_stretch)
  {
   var mainFrame;
   var el;
   var pt_scale;
   if(!font_stretch){font_stretch=0.8;};
   if(!line_stretch){line_stretch=1.0;};
   scale=s;
   font_scale=scale*font_stretch;
   line_scale=line_stretch*font_scale;
   pt_scale=s*28.3;
   el=ViewPortDoc().getElementById("top");
   el.setAttributeNS(null,"width",w*pt_scale+"pt");
   el.setAttributeNS(null,"height",h*pt_scale+"pt");
   el.setAttributeNS(null,"viewBox","0 0 " + w*pt_scale + " " + h*pt_scale);

   el=ViewPortDoc().getElementById("mainframe");
   mainFrame=new ViewPort("mainframe",0,0,w*pt_scale,h*pt_scale);
   mainFrame.element=el;
   mainFrame.container=el;
   mainFrame.map(0,0,1,1);
   mainFrame.appendChild(mainFrame.colourRectangle(-0.1,-0.1,1.2,1.2,"white"));
   return mainFrame;
  };
  function ViewPort(id,x,y,w,h)
  {
   var i;
   this.origin=new Array;
   this.size=new Array;
   this.offset=new Array;
   this.scale=new Array;
   this.origin[0]=x;
   this.origin[1]=y;
   this.size[0]=w;
   this.size[1]=h;
   this.offset[0]=0;
   this.scale[0]=w;
   this.offset[1]=-1;
   this.scale[1]=-h;
   this.maxXLabel=7;
   this.maxYLabel=7;
   this.gridStyle="gridline";
   this.minorpipStyle="minorpip";
   this.majorpipStyle="majorpip";
   if(id=="Init"){return;};
   if(ViewPortDoc())
   {
    this.element=ViewPortDoc().createElementNS("http://www.w3.org/2000/svg","g");
	this.container=this.element;
    this.element.setAttributeNS(null,"id",id);
    this.element.setAttributeNS(null,"transform",
	"translate(" + x + " " + y + ")");
   };
  };
  new ViewPort("Init",0,0,0,0);
  ViewPort.prototype.map=function(x,y,w,h)
  {
   this.offset[0]=-x;
   this.offset[1]=-y-h;
   this.scale[0]=this.size[0]/w;
   this.scale[1]=-this.size[1]/h;
  };
  ViewPort.prototype.ux=function(x){return roundFactor((x+this.offset[0])*this.scale[0],100);};
  ViewPort.prototype.uy=function(y){return roundFactor((y+this.offset[1])*this.scale[1],100);};
  ViewPort.prototype.uw=function(w){return roundFactor(w*this.scale[0],100);};
  ViewPort.prototype.uh=function(h){return roundFactor(h*this.scale[1],100);};
  ViewPort.prototype.createElement=function(name)
  {
   return ViewPortDoc().createElementNS("http://www.w3.org/2000/svg",name);
  };
  ViewPort.prototype.createTextNode=function(value){return ViewPortDoc().createTextNode(value);};
  ViewPort.prototype.appendChild=function(el){this.container.appendChild(el);};
  ViewPort.prototype.group=function(classname)
  {
   var el;
   el=ViewPort.prototype.createElement("g");
   if(classname)
   {
    el.setAttributeNS(null,"class",classname);
   };
   return el;
  };
  ViewPort.prototype.rectangle=function(x,y,w,h,classname)
  {
   var el;
   el=ViewPort.prototype.createElement("rect");
   if(classname)
   {
    el.setAttributeNS(null,"class",classname);
   };
   el.setAttributeNS(null,"x",this.ux(x));
   el.setAttributeNS(null,"width",this.uw(w));
   if(this.uh(h)<0)
   {
    el.setAttributeNS(null,"y",this.uy(y+h));
    el.setAttributeNS(null,"height",this.uh(-h));
   }
   else
   {
    el.setAttributeNS(null,"y",this.uy(y));
    el.setAttributeNS(null,"height",this.uh(h));
   }
   return el;
  };
  ViewPort.prototype.colourRectangle=function(x,y,w,h,colour)
  {
   var el;
   el=ViewPort.prototype.createElement("rect");
   el.setAttributeNS(null,"fill",colour);
   el.setAttributeNS(null,"x",this.ux(x));
   el.setAttributeNS(null,"y",this.uy(y+h));
   el.setAttributeNS(null,"width",this.uw(w));
   el.setAttributeNS(null,"height",this.uh(-h));
   el.setAttributeNS(null,"stroke","none");
   return el;
  };
  ViewPort.prototype.viewPort=function(id,x,y,w,h)
  {
   var vp;
   vp=new ViewPort(id,this.ux(x),this.uy(y+h),this.uw(w),this.uh(-h));
   return vp;
  };
  ViewPort.prototype.polygon=function(id,x,y,classname)
  {
   var el,i;
   var points="";
   var pnt=new Array;
   el=ViewPort.prototype.createElement("polygon");
   if(id!="")
   {
    el.setAttributeNS(null,"id",id);
   };
   if(classname)
   {
    el.setAttributeNS(null,"class",classname);
   };
   for(i=0;i<x.length;i++)
   {
    pnt[i]=this.ux(x[i])+","+this.uy(y[i]);
   };
   points=pnt.join("\n");
   el.setAttributeNS(null,"points",points);
   return el;
  };
  ViewPort.prototype.polyline=function(id,x,y,classname)
  {
   var el,i;
   var points="";
   var pnt=new Array;
   el=ViewPort.prototype.createElement("polyline");
   if(id!="")
   {
    el.setAttributeNS(null,"id",id);
   };
   if(classname)
   {
    el.setAttributeNS(null,"class",classname);
   };
   for(i=0;i<x.length;i++)
   {
    pnt[i]=this.ux(x[i])+","+this.uy(y[i]);
   };
   points=pnt.join("\n");
   el.setAttributeNS(null,"points",points);
   return el;
  };
  ViewPort.prototype.mapRectangle=function(rx,ry,rw,rh,x,y,w,h)
  {
   var urx=this.ux(rx),ury=this.uy(ry),urw=this.uw(rw),urh=this.uh(rh);
   this.scale[0]=urw/w;
   this.scale[1]=urh/h;
   this.offset[0]=(urx/this.scale[0])-x;
   this.offset[1]=(ury/this.scale[1])-y;
  };
  ViewPort.prototype.line=function(x1,y1,x2,y2,classname)
  {
   var el;
   el=ViewPort.prototype.createElement("line");
   if(classname)
   {
    el.setAttributeNS(null,"class",classname);
   };
   el.setAttributeNS(null,"x1",this.ux(x1));
   el.setAttributeNS(null,"y1",this.uy(y1));
   el.setAttributeNS(null,"x2",this.ux(x2));
   el.setAttributeNS(null,"y2",this.uy(y2));
   return el;
  };
  ViewPort.prototype.circle=function(x,y,r,classname)
  {
   var el;
   el=ViewPort.prototype.createElement("circle");
   if(classname)
   {
    el.setAttributeNS(null,"class",classname);
   };
   if(r)
   {
    el.setAttributeNS(null,"r",r);
   };
   el.setAttributeNS(null,"cx",this.ux(x));
   el.setAttributeNS(null,"cy",this.uy(y));
   return el;
  };
  ViewPort.prototype.symbol=function(x,y,r,symbolname)
  {
   var el,points,subel;
   if(!r){r=3;};x=this.ux(x);y=this.uy(y);
   switch(symbolname)
   {
   case "circle":
    el=ViewPort.prototype.createElement("circle");
    el.setAttributeNS(null,"r",r);
    el.setAttributeNS(null,"cx",x);
    el.setAttributeNS(null,"cy",y);
    break;
   case "square":
    el=ViewPort.prototype.createElement("polygon");
    points=(x-r)+","+(y-r)+" "+(x+r)+","+(y-r)+" "+(x+r)+","+(y+r)+" "+(x-r)+","+(y+r);
    el.setAttributeNS(null,"points",points);
    break;
   case "diamond":
    el=ViewPort.prototype.createElement("polygon");
    points=(x-r)+","+(y)+" "+(x)+","+(y-r)+" "+(x+r)+","+(y)+" "+(x)+","+(y+r);
    el.setAttributeNS(null,"points",points);
    break;
   case "cross":
    el=ViewPort.prototype.createElement("g");
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x-r);
    subel.setAttributeNS(null,"y1",y);
    subel.setAttributeNS(null,"x2",x+r);
    subel.setAttributeNS(null,"y2",y);
    el.appendChild(subel);
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x);
    subel.setAttributeNS(null,"y1",y-r);
    subel.setAttributeNS(null,"x2",x);
    subel.setAttributeNS(null,"y2",y+r);
    el.appendChild(subel);
    break;
   case "x":
    el=ViewPort.prototype.createElement("g");
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x-r);
    subel.setAttributeNS(null,"y1",y-r);
    subel.setAttributeNS(null,"x2",x+r);
    subel.setAttributeNS(null,"y2",y+r);
    el.appendChild(subel);
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x-r);
    subel.setAttributeNS(null,"y1",y+r);
    subel.setAttributeNS(null,"x2",x+r);
    subel.setAttributeNS(null,"y2",y-r);
    el.appendChild(subel);
    break;
   };
   el.setAttributeNS(null,"style","stroke:black;stroke-width:1px;fill:white;");
   return el;
  };
  ViewPort.prototype.errorbars=function(x,y,dx,dy,r)
  {
   var el,x1,x2,y1,y2;
   if(!r){r=2;};
   x1=this.ux(x-dx);
   x2=this.ux(x+dx);
   y1=this.uy(y-dy);
   y2=this.uy(y+dy);
   if((x1==x2)&&(y1==y2)){return this.symbol(x,y,0,"cross");};
   x=this.ux(x);
   y=this.uy(y);
   el=ViewPort.prototype.createElement("g");
   if(x1!=x2)
   {
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x1);
    subel.setAttributeNS(null,"y1",y);
    subel.setAttributeNS(null,"x2",x2);
    subel.setAttributeNS(null,"y2",y);
    el.appendChild(subel);
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x1);
    subel.setAttributeNS(null,"y1",y-r);
    subel.setAttributeNS(null,"x2",x1);
    subel.setAttributeNS(null,"y2",y+r);
    el.appendChild(subel);
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x2);
    subel.setAttributeNS(null,"y1",y-r);
    subel.setAttributeNS(null,"x2",x2);
    subel.setAttributeNS(null,"y2",y+r);
    el.appendChild(subel);
   };
   if(y1!=y2)
   {
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x);
    subel.setAttributeNS(null,"y1",y1);
    subel.setAttributeNS(null,"x2",x);
    subel.setAttributeNS(null,"y2",y2);
    el.appendChild(subel);
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x-r);
    subel.setAttributeNS(null,"y1",y1);
    subel.setAttributeNS(null,"x2",x+r);
    subel.setAttributeNS(null,"y2",y1);
    el.appendChild(subel);
    subel=ViewPort.prototype.createElement("line");
    subel.setAttributeNS(null,"x1",x-r);
    subel.setAttributeNS(null,"y1",y2);
    subel.setAttributeNS(null,"x2",x+r);
    subel.setAttributeNS(null,"y2",y2);
    el.appendChild(subel);
   };
   el.setAttributeNS(null,"style","stroke:black;stroke-width:1px;fill:white;");
   return el;
  };
  ViewPort.prototype.text=function(x,y,str,size,anchor,shift,fill,style)
  {
   var el,sty;
   el=this.createElement("text");
   sty="";
   if(size)
   {
    sty="font-size:"+font_scale*size+"pt";
    if(!isNaN(shift) && shift)
    {  
	 shift*=0.8;
     el.setAttributeNS(null,"transform","translate(0 "
          +roundFactor(-size*shift*line_scale,100)+")");
    };
   };
   if(anchor)
   {
    if(sty){sty=sty+";";};
    sty=sty+"text-anchor:"+anchor;
   };
   if(style)
   {
    if(sty){sty=sty+";";};
    sty=sty+"font-style:"+style;
   };
   if(fill)
   {
    if(sty){sty=sty+";";};
    sty=sty+"fill:"+fill;
   };
   if(sty)
   {
    el.setAttributeNS(null,"style",sty);
   };
   el.setAttributeNS(null,"x",this.ux(x));
   el.setAttributeNS(null,"y",this.uy(y));
   el.appendChild(ViewPort.prototype.createTextNode(str));
   return el;
  };
  ViewPort.prototype.rotatedText=function(x,y,degrees,strn,size,anchor,shift,fill,style)
  {
   var el;
   var at;
   var rad=degrees*Math.PI/180;
   var cs=roundFactor(Math.cos(rad),1000);
   var sn=roundFactor(Math.sin(rad),1000);
   el=this.text(x,y,strn,size,anchor,shift,fill,style);
   at="matrix("+cs+","+(sn)+","+(-sn)+","+cs+","+this.ux(x)+","+this.uy(y)+")";
   el.setAttributeNS(null,"transform",at);
   el.setAttributeNS(null,"x",0);
   el.setAttributeNS(null,"y",0);
   if(size)
   {
    if(!isNaN(shift) && shift)
    {  
	 shift*=0.8;
     el.setAttributeNS(null,"y",roundFactor(-size*shift*line_scale,100));
    };
   };
   return el;
  };
  ViewPort.prototype.xlabel="";
  ViewPort.prototype.ylabel="";
  ViewPort.prototype.xlabeller=function(pos,gap)
  {
   var test,fact,i;
   fact=1;
   test=pos;
   for(i=0;gap<1;i++)
   {
    fact*=10;
    test*=10;
    gap*=10;
   };
   return roundFactor(pos,fact)
  };
  ViewPort.prototype.ylabeller=ViewPort.prototype.xlabeller;
  ViewPort.prototype.pipFinder=function(val)
  {
   var ten,pwr,res;
   if(val>0)
   {
    ten=Math.log(val)/Math.LN10;
    pwr=Math.floor(ten); // power to raise major and minor by
    res=Math.pow(10,ten-pwr); // number that should be in range 1 to 9.9999
    if(res>5){return 10*Math.pow(10,pwr);};
    if(res>2){return 5*Math.pow(10,pwr);};
    return 2*Math.pow(10,pwr);
   };
   return 1000;
  };
  ViewPort.prototype.axis=function(type,from,to,major,minor,fill,style)
  {
   var pos;
   var g;
   g=this.createElement("g");
   if((!major)||(major==0))
   {
    if(type=="x")
    {
     major=this.pipFinder((to-from)/this.maxXLabel);
     minor=this.pipFinder(major/10.1);
    };
    if(type=="y")
    {
     major=this.pipFinder((to-from)/this.maxYLabel);
     minor=this.pipFinder(major/10.1);
    };
   };
   for(pos=Math.ceil(from/minor)*minor;pos<=to;pos+=minor)
   {
    if(type=="x")
    {
     g.appendChild(this.line(pos,0,pos,0.1,this.minorpipStyle));
    };
    if(type=="y")
    {
     g.appendChild(this.line(0,pos,0.1,pos,this.minorpipStyle));
    };
   };
   for(pos=Math.ceil(from/major)*major;pos<=to;pos+=major)
   {
    if(type=="x")
    {
     g.appendChild(this.line(pos,0,pos,0.2,this.majorpipStyle));
     g.appendChild(this.text(pos,0,this.xlabeller(pos,major),10,"middle",-1.45));
    };
    if(type=="y")
    {
     g.appendChild(this.line(0,pos,0.2,pos,this.majorpipStyle));
     g.appendChild(this.text(-0.1,pos,this.ylabeller(pos,major),10,"end",-0.5));
    };
   };
   if(type=="x")
   {
    g.appendChild(this.text((from+to)/2,-1.0,this.xlabel,10,"middle",-0.5,fill,style));
   };
   if(type=="y")
   {
    g.appendChild(this.rotatedText(-2.0,(from+to)/2,-90,this.ylabel,10,"middle",-0.5,fill,style));
   };
   this.appendChild(g);
  };
  ViewPort.prototype.grid=function(type,from,to,max,major)
  {
   var pos;
   var g;
   g=this.createElement("g");
   if(!major)
   {
    if(type=="x")
    {
     major=this.pipFinder((to-from)/this.maxXLabel);
    };
    if(type=="y")
    {
     major=this.pipFinder((to-from)/this.maxYLabel);
    };
   };
   for(pos=Math.ceil(from/major)*major;pos<=to;pos+=major)
   {
    if(type=="x")
    {
     g.appendChild(this.line(pos,0,pos,max,this.gridStyle));
    };
    if(type=="y")
    {
     g.appendChild(this.line(0,pos,max,pos,this.gridStyle));
    };
   };
   this.appendChild(g);
  };
  ViewPort.prototype.mapPlot=function(x,y,w,h,nx,ny,opacityFunction,style)
  {
   var el,i,j,g;
   el=ViewPort.prototype.createElement("rect");
   g=ViewPort.prototype.createElement("g");
   g.setAttributeNS(null,"style",style);
   el.setAttributeNS(null,"width",Math.ceil(this.uw(w/(nx-1))));
   el.setAttributeNS(null,"height",Math.ceil(this.uh(-h/(ny-1))));
   el.setAttributeNS(null,"class","fill");
   x-=0.5*w/(nx-1);
   y-=0.5*h/(ny-1);
   for(i=0;i<nx;i++)
   {
    for(j=0;j<ny;j++)
    {
     el=el.cloneNode(false);   
     el.setAttributeNS(null,"opacity",opacityFunction(i,j));
     el.setAttributeNS(null,"x",this.ux(x+i*w/(nx-1)));
     el.setAttributeNS(null,"y",this.uy(y+(j+1)*h/(ny-1)));
     g.appendChild(el);
    };
    this.appendChild(g);
   };
  };
  var clipCount=0;
  ViewPort.prototype.clipPath=function(el)
  {
   var cp,g;
   cp=this.createElement("clipPath");
   clipCount++;
   cp.setAttributeNS(null,"id","clip"+clipCount);
   cp.appendChild(el);
   ViewPortDoc().getElementById("definitions").appendChild(cp);
// new method
   g=this.createElement("g");
   g.setAttributeNS(null,"clip-path","url(#clip"+clipCount+")");
   this.container=g;
   this.element.appendChild(this.container);
//   this.element.setAttributeNS(null,"clip-path","url(#clip"+clipCount+")");
  };


