//*****************************************************************************
// 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);
}