//*****************************************************************************
// Filename: plot2.js
// Description: Plot a scientific line graph.
// Version: 1.1
// Browser Compatibility: IE5.5+
//
// COPYRIGHT (C) 2003 Michel LEPAIN AND Etienne BERTAUD du CHAZAUD
//(Strongly inspired from Graph.js of WAGNER DOSANJOS)
// THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT
// UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY THE FREE
// SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR (AT YOUR OPTION)
// ANY LATER VERSION. THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE
// USEFUL, BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
// MERCHANTABILITY OF FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU GENERAL
// PUBLIC LICENSE FOR MORE DETAILS.
//
// YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE ALONG
// WITH THIS PROGRAM; IF NOT, WRITE TO:
//
// THE FREE SOFTWARE FOUNDATION, INC.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
//
// Bugs/Comments: etienne.bertaud@laposte.net
//
// Change History:
// 03-12-2003 :
//  o Release v1.1
//
//*****************************************************************************
// plot2.js
//
// This script contains a useful object that can be used to plot scientific
// line graphs.
//
//
// Object Name: plot
//
// Constructor:
// . new plot(n) [where n is the number of X items]
//
// Properties:
// . parent (HTMLElement), default: document.body [where to draw the graph]
// . version (String) [version of the object (ie '1.0')]
// . titre (String) [graph title]= 'graphe';
// . couleur_trace (String), default: 'black' [line color]
// . larg_trace (Integer), default: 1 [line width]
// . markcolor (String), default: 'blue' [line graph mark color]
// . marksize (Integer), default: 4 [line graph mark size]
// . posx (Integer), default: 30 [horizontal position of the figure]
// . posy (Integer), default: 80 [vertical position of the figure]
// . hauteur (Integer), default: 250 [height of the figure]
// . largeur (Integer), default: 500 [width of the figure]
// . couleur_fond (String), default: grey [background color]
// . reg, default: false [plot reg slope]
// . couleur_reg, default: green [line graph reg color]
//
//
// Methods
// . trace(plot) [plot graph]
//
// Limitations/TODO List:
// - Multi-plot is not possible for the moment
// - X and Y captions have not yet be added
// - Netscape/Mozilla compatibility
// - Log-Log plot is not possible for the moment
// 
//*****************************************************************************

function plot(taille)   //création de l'objet plot                                     
{
this.taille=taille;                  //nombre de points à tracer                       
this.posx=50;                        //position du graphe dans la feuille (horizontale)
this.posy=80;                        //position du graphe dans la feuille (verticale)  
this.hauteur=250;                    //hauteur du graphe                               
this.largeur=500;                    //largeur du graphe                               
this.larg_trace=1;                   //épaisseur du trait                              
this.couleur_trace="black";          //couleur de la courbe                            
this.couleur_fond="gray";            //couleur du fond de graphe                       
this.Valeurs = new Array(taille);
this.plot=trace;
this.markcolor="blue";
this.marksize=4;
this.titre="graphe";
this.reg=false;
this.couleur_reg="green";
var pos_axe_x,pos_axe_y;
var a, b, R2;
var DX,DY;

var rouge=0;
var vert=0;
var bleu=0;

document.writeln("<style>")//feuille de sytle pour les affichages de texte
document.writeln("p.titre { border:1px solid grey; background-color:#E0E0E0; padding:4px; margin:0px; }")
document.writeln("<\/style>")
}

function trace(objj)
{
 fond(objj);
 if(objj.reg==true){regression(objj);}
 max_min_range(objj);                     // cherche Extremums
 echelle(objj,x_min,x_max,y_min,y_max);   // calcul echelles  
 axe(pos_axe_x,pos_axe_y,objj);           // trace axes       
 graduations(x_max,x_min,y_max,y_min,objj);
 for(j=0;j<this.taille-1;j++)             // trace courbe     
  {
  x1=objj.Valeurs[j][0];
  y1=objj.Valeurs[j][1]; 
  x2=objj.Valeurs[j+1][0];
  y2=objj.Valeurs[j+1][1];
  ligne(XEC(x1,objj),YEC(y1,objj),XEC(x2,objj),YEC(y2,objj),objj.couleur_trace,objj.larg_trace,objj.marksize,objj.markcolor);
  }
 if(objj.reg==true){
   ligne(XEC(x_min,objj),YEC(a*x_min+b,objj),XEC(x_max,objj),YEC(x_max*a+b,objj),complement(objj.couleur_fond),objj.larg_trace,objj.marksize,objj.markcolor);}
} // fin trace

function max_min_range(objj)               //recherche des extremums des valeurs entrées
{
 x_max=objj.Valeurs[0][0];
 y_max=objj.Valeurs[0][1]; 
 x_min=objj.Valeurs[0][0];
 y_min=objj.Valeurs[0][1];
 for(j=1;j<objj.Valeurs.length;j++)
  {
   x_max=(x_max<objj.Valeurs[j][0])?objj.Valeurs[j][0]:x_max;
   y_max=(y_max<objj.Valeurs[j][1])?objj.Valeurs[j][1]:y_max;
   x_min=(x_min>objj.Valeurs[j][0])?objj.Valeurs[j][0]:x_min;
   y_min=(y_min>objj.Valeurs[j][1])?objj.Valeurs[j][1]:y_min;
  }
 if(objj.reg==true){
  y1= a*x_min+b;
  y2= a*x_max+b;
  reg_min=(y1<y2)?y1:y2;
  reg_max=(y1<y2)?y2:y1;
  y_max=(y_max>reg_max)?y_max:reg_max;
  y_min=(y_min<reg_min)?y_min:reg_min;
  
  } 
 DX=x_max-x_min;
 DY=y_max-y_min;
} // fin max_min

function echelle(objj,x_min,x_max,y_min,y_max)           //détermination de l'échelle de pixels

//                                                                                 
//      +---------------------------------------------------------------------- X  
//      |              ^                                                 ^         
//      |              |                                                 |         
//      |<    posx    >| posY                                            |         
//      |              |                                                 |         
//      |              v                                                 |         
//      |        ^     +---------------------------------------------+   |         
//      |              |           |ymax                             |   |pos_axe_y
//      |              |           |                                 |   |         
//      |              |           |                                 |   |         
//      |              |           |                                 |   |         
//      |              |           |                                 |   |         
//      |              |           |                                 |   |         
//      |    hauteur   |           |                                 |   |         
//      |              |           |                                 |   |         
//      |              | ----------+-------------------------------  |   v         
//      |              | xmin      |                             xmax|             
//      |              |           |                                 |             
//      |              |           |                                 |             
//      |              |           |                                 |             
//      |              |           |ymin                             |             
//      |        v     +---------------------------------------------+             
//      |              <                largeur                      >             
//      |                                                                          
//      |<       pos_axe_x        >                                                
//      |                                                                          
//    Y                                                                            
{
echelle_x=objj.largeur/DX;
echelle_y=objj.hauteur/DY;
pos_axe_x=(x_min*x_max>0)?objj.posx:XEC(0,objj);
pos_axe_y=(y_min*y_max>0)?objj.posy+objj.hauteur:YEC(0,objj);
//////////////fond(objj); 
}// fin echelle                                                                   



function fond(objj)//dessin du fond du graphique
{
   document.writeln('<SPAN id=line STYLE=\"position: absolute;');
   document.writeln('left: ' + objj.posx + 'px; top: ' + objj.posy + 'px;');
   document.writeln('width: ' + objj.largeur + 'px;');
   document.writeln('background-color: ' + objj.couleur_fond + ';\"><p class=\"titre\"align=\"center\"><font face=\"verdana\" size=\"+2\">'+objj.titre+'</font></p></SPAN>');
   objj.posy+=40;
   document.writeln('<SPAN id=line STYLE=\"position: absolute;');
   document.writeln('left: ' + objj.posx + 'px; top: ' + objj.posy + 'px;');
   document.writeln('width: ' + objj.largeur + 'px; height: ' + objj.hauteur + 'px;');
   document.writeln('background-color: ' + objj.couleur_fond + ';\"></SPAN>');
}
// Calcul coordonnées écran, avec mise à l'échelle
function XEC(X,objj) 
{
    var tempo; 
    tempo=objj.posx + Math.round((X-x_min)*objj.largeur/DX);
    return tempo;
}

function YEC(Y,objj)
{  
    var tempo;
    tempo=objj.posy + Math.round((y_max-Y)*objj.hauteur/DY);
    return tempo;
}

//trace axes                          
function axe(pos_axe_x,pos_axe_y,objj)
{
   document.writeln('<SPAN id=axey STYLE=\"position: absolute;');
   document.writeln('left: ' + (pos_axe_x)+ 'px; top: ' + objj.posy + 'px;');   //axe des ordonnées
   document.writeln('width: ' + 1 + 'px; height: ' + objj.hauteur + 'px;');
   document.writeln('background-color: ' + "black"+ ';\"></SPAN>');
   
   document.writeln('<SPAN id=axex STYLE=\"position: absolute;');
   document.writeln('left: ' + objj.posx + 'px; top: ' + pos_axe_y + 'px;');//axe des abscisses
   document.writeln('width: ' + objj.largeur + 'px; height: ' + 1 + 'px;');
   document.writeln('clip: rect(0 ' + objj.largeur + ' ' + 1 + ' 0); background-color: ' + "black"+ ';\"></SPAN>');

}

function ligne(x1,y1,x2,y2,couleur,epaiss,marksize,markcolor)//méthode de l'objet plot
{
Dy=Math.abs(y1-y2);
Dx=Math.abs(x1-x2);
 x=x1;
 y=y1;
    
     if (Dx > Dy){
        xInc = (x1 < x2) ? 1 : -1;
        yInc = (y1 < y2) ? (Dy / Dx) : -(Dy / Dx);
    }else{
        yInc = (y1 < y2) ? 1 : -1;
        xInc = (x1 < x2) ? (Dx / Dy) : -(Dx / Dy);
    }

    while (  ((xInc >= 0 && x <= x2) || (xInc <= 0 && x >= x2))
           && ((yInc >= 0 && y <= y2) || (yInc <= 0 && y >= y2)) ){
        document.writeln('<SPAN id=line STYLE=\"position: absolute;');
        document.writeln('left: ' + x + 'px; top: ' + y + 'px;');
        document.writeln('width: ' + epaiss + 'px; height: ' +  epaiss + 'px;');
        document.writeln(' clip: rect(0 ' + epaiss + ' ' + epaiss + ' 0); background-color: ' + couleur + ';\"></SPAN>');
        x += xInc;
        y += yInc;
    }

    document.writeln('<SPAN id=line STYLE=\"position: absolute;');
    document.writeln('left: ' + Math.round(x1-marksize/2) + 'px; top: ' + Math.round(y1-marksize/2) + 'px;');
    document.writeln('width: ' + marksize + 'px; height: ' + marksize + 'px;');
    document.writeln(' clip: rect(0 ' + marksize + ' ' + marksize + ' 0); background-color: ' + markcolor + ';\"></SPAN>');

    document.writeln('<SPAN id=line STYLE=\"position: absolute;');
    document.writeln('left: ' + Math.round(x2-marksize/2) + 'px; top: ' + Math.round(y2-marksize/2) + 'px;');
    document.writeln('width: ' + marksize + 'px; height: ' + marksize + 'px;');
    document.writeln(' clip: rect(0 ' + marksize + ' ' + marksize + ' 0); background-color: ' + markcolor + ';\"></SPAN>');
}

function formaterNbre(nbre, nbdecimales) 
  {
  puiss10=Math.log(Math.abs(nbre))/Math.LN10
  if(puiss10>=4||puiss10<-3)// si le nombre est >+/-10 000 ou <+/-0.001, écriture scientifique du nombre
  {
   nbre2=Math.round(100*nbre/Math.pow(10,Math.floor(puiss10)))/100;
   var chaine_nbre = new String(nbre2);
   chaine_nbre=chaine_nbre+"e"+Math.floor(puiss10);
   if(!nbre2){chaine_nbre="0";}// cas particulier du zéro qui génére parfois des erreurs dans son écriture scientifique
   return(chaine_nbre);
  }
  else{//autre cas, formatage du nombre pour éviter des nombres du style 3.000000000001
  var facteur=Math.pow(10,nbdecimales)
  var arrondi=Math.round(nbre*facteur)/facteur
  var chaine_nbre = new String(arrondi); // création de chaine à partir du nombre
  if(nbdecimales>0)
    {
    if(chaine_nbre.indexOf(".")<0)
	     {
         chaine_nbre = chaine_nbre + ".";
         while(chaine_nbre.length <chaine_nbre.indexOf(".")+nbdecimales+1)
               { 
           chaine_nbre = chaine_nbre+"0";
	       }
	     }  
	}	 
  return(chaine_nbre);
  }
  }
function graduations(x_max,x_min,y_max,y_min,objj)//calcul automatique des axes
{
grad_x=5*Math.pow(10,Math.floor(Math.log(x_max-x_min)/Math.LN10)-1);//calcul des graduations adaptées
grad_y=5*Math.pow(10,Math.floor(Math.log(y_max-y_min)/Math.LN10)-1);
grad_x=( (x_max-x_min)/grad_x <5 )? grad_x*2/5 : grad_x;
grad_y=( (y_max-y_min)/grad_y <5 )? grad_y*2/5 : grad_y;
//calcul de l'arrondi pour formater les valeurs affichées à l'écran
arrondi_x=(grad_x>=1)?0:-Math.floor(Math.log(grad_x)/Math.LN10);
arrondi_y=(grad_y>=1)?0:-Math.floor(Math.log(grad_y)/Math.LN10);
gradu=(x_min<0)?grad_x+x_min%grad_x+x_min:grad_x-x_min%grad_x+x_min;

while(gradu<=x_max)
{
   document.writeln('<SPAN id=grad STYLE=\"position: absolute;');
   document.writeln('left: ' + XEC(gradu,objj)+ 'px; top: ' + pos_axe_y  + 'px;');//graduation axe des abscisses
   document.writeln('width:  1 px; height: 5 px;');
   document.writeln('clip: rect(0 ' + 1 + ' ' + 5 + ' 0);background-color: ' + "black"+ ';\"></SPAN>');
   document.writeln('<SPAN id=grad2 STYLE=\"position: absolute;');
   document.writeln('left: ' + (XEC(gradu,objj)-10)+ 'px; top: ' + (pos_axe_y+6)  + 'px;');//valeur des graduations
   document.writeln('height:  20 px;');
   document.writeln('clip: rect(0 ' + 200 + ' ' + 20 + ' 0);\">'+formaterNbre(gradu,arrondi_x)+'</SPAN>');
   gradu+=grad_x;
}


gradu=(y_min<0)?grad_y+y_min%grad_y+y_min:grad_y-y_min%grad_y+y_min;
while(gradu<=y_max)
{
   document.writeln('<SPAN id=grad STYLE=\"position: absolute;');
   document.writeln('left: ' + pos_axe_x+ 'px; top: ' + YEC(gradu,objj) + 'px;');//graduation axe des ordonnées
   document.writeln('width: 5 px; height: 1 px;');
   document.writeln('clip: rect(0 ' + 5 + ' ' + 1 + ' 0);background-color: ' + "black"+ ';\"></SPAN>');
   document.writeln('<SPAN id=grad2 STYLE=\"position: absolute;');
   document.writeln('left: ' + (pos_axe_x-53)+ 'px; top: ' + (YEC(gradu,objj)-10) + 'px;');////valeur des graduations
   document.writeln('width: 50 px;height: 20 px;');
   document.writeln('clip: rect(0 ' + 200 + ' ' + 20 + ' 0);\"><p align=\"right\">'+formaterNbre(gradu,arrondi_y)+'<\/p></SPAN>');
   gradu=gradu+grad_y;
}
}


document.onmousemove = get_mouse;//obtention des valeurs X et Y dans la barre de statut (attention, spécifique IE)
function get_mouse()
{
a=(event.clientX-objet.posx)*DX/objet.largeur+x_min;
b=y_max-(event.clientY-objet.posy)*DY/objet.hauteur;
if (event.clientX>=objet.posx && event.clientX<=(objet.posx+objet.largeur) && event.clientY>=objet.posy && event.clientY<=(objet.posy+objet.hauteur))
{
window.status="x :"+Math.round(100*a)/100+"/y :"+Math.round(100*b)/100;
}else{
window.status="";
}
}

function regression(objj)// calcul de l'équation de la régression linéaire et du coefficient R²
{
 moyenne_X=0;
 moyenne_Y=0;
 for(i=0;i<=objj.taille-1;i++)
 {
  moyenne_X+=objj.Valeurs[i][0]
  moyenne_Y+=objj.Valeurs[i][1];
 }
 moyenne_X=moyenne_X/objj.taille;
 moyenne_Y=moyenne_Y/objj.taille;
 X2=0;
 Y2=0;
 XY=0;
 for(i=0;i<=objj.taille-1;i++)
 {Y2+=Math.pow((objj.Valeurs[i][1]-moyenne_Y),2);
  X2+=Math.pow((objj.Valeurs[i][0]-moyenne_X),2);
 XY+=(objj.Valeurs[i][1]-moyenne_Y)*(objj.Valeurs[i][0]-moyenne_X);
 }

 a=XY/X2;
 aa=XY/Y2;
 b=moyenne_Y-a*moyenne_X;
 R2=a*aa;
document.writeln("<div style=\"position:absolute;left:"+objj.posx+"px;top:"+(objj.posy+objj.hauteur)+"px;\" align=\"center\"><font face=\"verdana\"> <u>coefficient directeur</u> a ="+Math.round(100*a)/100+" ; <u>coefficient de corrélation</u> R<sup>2</sup> = "+Math.round(R2*10000)/10000+"</font></div>");
}

function hd(ch_Hex) // Convertit un chiffre hexa en décimal
{
var hexa="0123456789ABCDEF";
var y, z;
  y=hexa.indexOf(ch_Hex.charAt(0).toUpperCase())*16; // Seizeines
  z=hexa.indexOf(ch_Hex.charAt(1).toUpperCase());    // Unités
  return(y+z);
}
function dh(ch_dec) // Convertit un chiffre décimal en hexa
{
var hexa="0123456789ABCDEF";
var S,U;
  S=Math.floor(ch_dec/16); // Seizeines
  U=x%16            ; // Unités
  return(hexa.charAt(S)+hexa.charAt(U));
}
function hexadec(code) // Convertit un code en trois couleurs
{
  var r,v,b;
  r=code.substring(1,3); // rr du code
  v=code.substring(3,5); // vv du code
  b=code.substring(5,7); // bb du code
  rouge=hd(r);           // convertit rouge
  vert=hd(v);            // convertit vert
  bleu=hd(b);            // convertit bleu
}
function dechexa(rouge,vert,bleu) // Convertir trois couleurs en code
{
  return "#"+dh(rouge)+dh(vert)+dh(bleu);
}
function complement(codehex)  // donne la couleur complémentaire(?)
{
 hexadec(codehex);
 rouge=rouge^255;
 vert=vert^255;
 bleu=bleu^255;
 return dechexa(rouge,vert,bleu);
}