/**
* VmVertex : Classe Javascript de création de vertex
* <b>Constructeur</b>
* \param dX Coordonnée en X.
* \param dX Coordonnée en Y.
*/
function VmVertex(dX, dY){
	this.x=dX;
	this.y=dY;
	this.setX=setX;
	this.setY=setY;
	this.courseTo=courseTo;
	this.distanceTo=distanceTo;
	this.toString=toString;
}

/**
* Définir le X du vertex
* \param dX Valeur en X.
*/
function setX(dX){
	this.x=dX;
}

/**
* Définir le Y du vertex
* \param dY Valeur en Y.
*/
function setY(dY){
	this.y=dY;
}

/**
* Renvoie l'angle en degré fait par le segment objet -> oVertex et une ligne verticale.
* l'angle est donnée dans le sens horaire. Le nord correspond à la valeur 0.
* Cette valeur correspond au cap à suivre sur un compas pour aller de l'objet au point oPoint.
* \param oVertex Objet VmVertex.
*/
function courseTo(oVertex){
	if (oVertex.y == this.y){
	if (oVertex.x >= this.x)
		dResult=90;
	else
		dResult=270;
	}else{
	var dTangente=(oVertex.x-this.x) / (oVertex.y-this.y);
	dAngleRd=Math.atan(dTangente);
	dResult=dAngleRd*180 / Math.PI;
	if ((oVertex.y-this.y) <= 0)
		dResult=180+dResult;
	if (((oVertex.x-this.x) < 0) && ((oVertex.y-this.y) >= 0))
		dResult=dResult+360;
	}
	return dResult;
}

/**
* Permet de calculer la distance du segment objet -> oVertex.
* \param oVertex Objet VmVertex.
*/
function distanceTo(oVertex){	
	var dDistX=Math.abs(oVertex.x-this.x);
	var dDistY=Math.abs(oVertex.y-this.y);
	return Math.sqrt((dDistX*dDistX)+( dDistY*dDistY));
}

/**
* Renvoie une chaîne de caractère décrivant l'objet. Utile pour le débuggage.
*/
function toString(){
	return this.x+" "+this.y;
}
// -------------------------------

/**
* VmRectanlge : Classe Javascript de création d'enveloppe
* <b>Constructeur</b>
* \param oVertex1 Objet VmVertex 1.
* \param oVertex2 Objet VmVertex 2.
* \param iWidth Largeur (unité image).
* \param iHeight Hauteur (unité image).
*/
function VmRectangle(oVertex1, oVertex2, iWidth, iHeight){
	this.xMin=oVertex1.x;
	this.xMax=oVertex2.x;
	this.yMin=oVertex1.y;
	this.yMax=oVertex2.y;
	this.width=iWidth;
	this.height=iHeight;

	this.unitsPerDotX=(this.xMax-this.xMin) / this.width;
	this.unitsPerDotY=(this.yMax-this.yMin) / this.height;

	this.getIJVertex=getIJVertex;
	this.getXYVertex=getXYVertex;
	this.innerSegmentIJ=innerSegmentIJ;
}

/**
* Générer les coordonnées IJ (image) d'un vertex à partir de ses coordonnées terrain.
* \param oVertex Objet VmVertex.
*/
function getIJVertex(oVertex){
	var iPt=Math.round((oVertex.x-this.xMin) / this.unitsPerDotX);
	var jPt=Math.round((this.yMax-oVertex.y) / this.unitsPerDotY);

	return new VmVertex(iPt, jPt);
}

/**
* Générer les coordonnées XY (terrain) d'un vertex à partir de ses coordonnées image.
* \param oVertex Objet VmVertex.
*/
function getXYVertex(oVertex){
	var xPt=(oVertex.x*this.unitsPerDotX)+this.xMin;
	var yPt=this.yMax-(oVertex.y*this.unitsPerDotY);

	return new VmVertex(xPt, yPt);
}

/**
* Renvoie le segment correspondant à la partie du segment oV1-oV2 se trouvant dans le rectangle.
* le calcul utilise les coordonnées images du rectangle. oVertex1 et oVertex2 doivent donc être définis en coordonnées image.
* \param oVertex1 Objet de type VmVertex.
* \param oVertex2 Objet de type VmVertex.
* \return Null si la ligne n'intersecte pas le rectangle, sinon elle 
* renvoie un tableau ordonné de 2 vertex. Les coordonnées de ces vertex ne sont pas des entiers.
*/
function innerSegmentIJ(oVertex1, oVertex2){
	//alert("innersegment("+oVertex1.toString()+","+oVertex2.toString());
	//les cas les plus simples de non intersection sont éliminés
	if ((oVertex1.x<0 && oVertex2.x<0) || (oVertex1.x>this.width && oVertex2.x>this.width) || (oVertex1.y<0 && oVertex2.y<0) || (oVertex1.y>this.height && oVertex2.y>this.height)) 
		return null;
	var bSwap=false;
	//le premier point doit être à gauche
	if (oVertex1.x > oVertex2.x){
		bSwap=true;
		xtemp=oVertex1.x;
		ytemp=oVertex1.y;
		oVertex1.x=oVertex2.x;
		oVertex1.y=oVertex2.y;
		oVertex2.x=xtemp;
		oVertex2.y=ytemp;
	}

	if (oVertex1.x == oVertex2.x){ // cas particulier : segment vertical
		pt1x=oVertex1.x;
		pt1y=0;
		pt2x=oVertex1.x;
		pt2y=this.height;
	}else{ // Calcul des coeficient a et b tel que y=a*x+b
		var a=(oVertex2.y-oVertex1.y)/(oVertex2.x-oVertex1.x);
		var b=oVertex1.y-(a*oVertex1.x);
		// on considère que xmin=ymin=0
		//fxmin=f(xmin)=y=a*xmin+b
		//gymin=g(ymin) ou g() est la fonction inverse de la fonction f()
		fxmin=b;
		fxmax=(this.width*a)+b;
		gymin=-b / a;
		gymax= (this.height-b) / a;

		//Calcul du premier point
		if ((fxmin>0) && (fxmin < this.height)){
			pt1x=0;
			pt1y=fxmin;
		}else{
			if (a>0){
				if ((gymin>0) && (gymin < this.width)){
					pt1x=gymin;
					pt1y=0;
				}else{
					return null;
				}
			}else{
				if ((gymax>0) && (gymax < this.width)){
					pt1x=gymax;
					pt1y=this.height;
				}else{
					return null;
				}
			}
		}
		//Calcul du second point
		if ((fxmax>0) && (fxmax < this.height)){
			pt2x=this.width;
			pt2y=fxmax;
		}else{
			if (a<0){
				if ((gymin>0) && (gymin < this.width)){
					pt2x=gymin;
					pt2y=0;
				}else{
					//laisser l'alert pour les test car on ne doit normalement jamais passer par ici...
					alert("null 3");
					return null;
				}
			}else{
				if ((gymax>0) && (gymax < this.width)){
					pt2x=gymax;
					pt2y=this.height;
				}else{
					//laisser l'alert pour les test car on ne doit normalement jamais passer par ici...
					alert("null 4");
					return null;
				}
			}
		} 
	}

	//Traitement du cas ou un des point est déjà dans le rectangle
	if ((oVertex1.x>0) && (oVertex1.x<this.width) && (oVertex1.y>0) && (oVertex1.y<this.height)){
		pt1x=oVertex1.x;
		pt1y=oVertex1.y;
	}
	if ((oVertex2.x>0) && (oVertex2.x<this.width) && (oVertex2.y>0) && (oVertex2.y<this.height)){
		pt2x=oVertex2.x;
		pt2y=oVertex2.y;
	}
	// on remet les points dans l'ordre d'origine
	if (bSwap){
		xtemp=pt1x;
		ytemp=pt1y;
		pt1x=pt2x;
		pt1y=pt2y;
		pt2x=xtemp;
		pt2y=ytemp;
	}

	//alert("pt1x:"+pt1x+"  pt1y:"+pt1y);
	//alert("pt2x:"+pt2x+"  pt2y:"+pt2y);
	oPt1=new VmVertex(pt1x,pt1y);
	oPt2=new VmVertex(pt2x,pt2y);
	return new Array(oPt1,oPt2);
}

// -------------------------------

/**
* VmShape : Classe Javascript de création d'un objet graphique (formes du type : point, ligne, polygon, ou annotation)
* <b>Constructeur</b>
* \param sDiv Nom du Div HTML associé.
* \param oRectangle Objet VmRectangle : étendue associée.
* \param sType Type de l'objet crée (point, annotation, ligne, polygone).
*/
function VmShape(sDiv, oRectangle, sType){
	this.oWZ=new jsGraphics(sDiv);
	this.oRectangle=oRectangle;
	this.type=sType;
	this.aIJ=new Array();
	this.aXY=new Array();
	//*** Type : polygon ***//
	this.closed=false;
	//*** Type : line ***//
	this.stroke=0;
	this.color="";
	this.unit="";
	this.multiplier=1;
	this.precision=0;
	this.angle_unit="";
	this.angle_multiplier=1;
	this.vertex_show=false;
	this.vertex_color="";
	//*** Type : text ***//
	this.font="";
	this.fontsize="";
	this.fonttype=Font.PLAIN;
	this.string="";
	this.decal=0;
	//*** Type : point ***//
	this.size=0;
	this.drawing_shape="";
	//*** Méthodes ***//
	this.addIJ=addIJ;
	this.addXY=addXY;
	this.dropVertex=dropVertex;
	this.dropLastVertex=dropLastVertex;
	this.reverseShape=reverseShape;
	this.drawShape=drawShape;
	this.drawLastSegment=drawLastSegment;
	this.redrawSegments=redrawSegments;
	this.cleanShape=cleanShape;
	this.clearShape=clearShape;
	this.changeShapeExtent=changeShapeExtent;
	this.getHTMLSummary=getHTMLSummary;
	this.getTextSummary=getTextSummary;
	this.copyToClipboard=copyToClipboard;
	this.getArea=getArea;
	this.getShapeVertexXY=getShapeVertexXY;
	this.getShapeX=getShapeX;
	this.getShapeY=getShapeY;
	this.countVertex=countVertex;
	this.setVmLine=setVmLine;
	this.setVmPolygon=setVmLine;
	this.setVmLineVertex=setVmLineVertex;
	this.setVmPolygonVertex=setVmLineVertex;
	this.setVmText=setVmText;
	this.setVmPoint=setVmPoint;
	this.setVmCircle=setVmCircle;
	this.toStringXY=toStringXY;
	this.toStringIJ=toStringIJ;
	//*** Méthode privée **//
	this.isPolygonClosed=isPolygonClosed;
}

/**
* Ajouter un vertex à l'objet (coordonnées image).
* \param oVertex Objet VmVertex à ajouter.
*/
function addIJ(oVertex){
	if(!this.closed){
		this.aIJ[this.aIJ.length]=oVertex;
		this.aXY[this.aXY.length]=this.oRectangle.getXYVertex(oVertex);
		this.closed=this.isPolygonClosed();
		return true;
	}else{
		return false;
	}
}

/**
* Ajouter un vertex à l'objet (coordonnées terrain).
* \param oVertex Objet VmVertex à ajouter.
*/
function addXY(oVertex){
	if(!this.closed){
		this.aXY[this.aXY.length]=oVertex;
		this.aIJ[this.aIJ.length]=this.oRectangle.getIJVertex(oVertex);
		this.closed=this.isPolygonClosed();
		return true;
	}else{
		return false;
	}
}

/**
* Supprimer un vertex de l'objet.
* \param iIndex Index du vertex à supprimer.
*/
function dropVertex(iIndex){
	var bDrop=false;
	switch(this.type){
		case "line" :
			if(this.aXY.length > 2) bDrop=true;
		break;
		case "polygon" :
			if((this.aXY.length > 4) && (iIndex != 0) && (iIndex != (this.aXY.length-1))) bDrop=true;
		break;
	}
	if(bDrop){
		for(var i=iIndex+1; i<this.aXY.length; i++){
			this.aXY[i-1]=new VmVertex(this.aXY[i].x, this.aXY[i].y);
			this.aIJ[i-1]=this.oRectangle.getIJVertex(this.aXY[i-1]);
		}
		this.aXY.pop();
		this.aIJ.pop();
		return true;
	}
	return false;
}

/**
* Supprime le dernier vertex de l'objet .
*/
function dropLastVertex(){
	if(this.aXY.length!=0){
		this.aXY.pop();
		this.aIJ.pop();
		return true;
	}
	return false;
}

/**
* Cette méthode permet d'inverser l'ordre des vertex constituant un objet VmShape.
*/
function reverseShape(){
	this.aIJ.reverse();
	this.aXY.reverse();
}

/**
* Test si le polygone est fermé.
* \private
* \return Booléen (vrai ou faux).
*/
function isPolygonClosed(){
	if((this.aIJ[this.aIJ.length-1].x == this.aIJ[0].x) && 
  	(this.aIJ[this.aIJ.length-1].y == this.aIJ[0].y) && 
  	(this.aIJ.length > 1) && (this.type == "polygon")) 
  	return true;
  else 
  	return false;
}

/**
* Dessiner l'objet (cette méthode fait appel aux ressources Javascript de la bibliothèque JsGraphics de Walter Zorn).
*/
function drawShape(){
	switch(this.type){
		case "polygon":
			//*** Idem case "line"
		case "line":
			var aSegment;

			this.oWZ.setStroke(this.stroke);
			this.oWZ.setColor(this.color);

			for(var i=0;i<(this.aIJ.length-1);i++){
				aSegment=this.oRectangle.innerSegmentIJ(new VmVertex(this.aIJ[i].x, this.aIJ[i].y), new VmVertex(this.aIJ[i+1].x, this.aIJ[i+1].y));

				if (aSegment != null){
					this.oWZ.drawLine(aSegment[0].x, aSegment[0].y, aSegment[1].x, aSegment[1].y); 
					if (this.vertex_show){
						// Jeu de couleurs si l'on doit afficher les vertex
						this.oWZ.setColor(this.vertex_color);
						this.oWZ.fillRect(aSegment[0].x-(this.size / 2), aSegment[0].y-(this.size / 2), this.size, this.size);
						this.oWZ.setColor(this.color);
					}
				}
			}
			// Dessiner le dernier vertex
			if ((aSegment != null) && this.vertex_show){
				this.oWZ.setColor(this.vertex_color);
				this.oWZ.fillRect(aSegment[1].x-(this.size / 2), aSegment[1].y-(this.size / 2), this.size, this.size);
			}
		break;
		case "circle" :
			this.oWZ.setStroke(this.stroke);
			this.oWZ.setColor(this.color);
			/*
			Dessin d'une ellipse.
			if (this.aIJ[0].x > this.aIJ[1].x){
				iWidth=(this.aIJ[0].x-this.aIJ[1].x)*2;
			}else{
				iWidth=(this.aIJ[1].x-this.aIJ[0].x)*2;
			}
			if (this.aIJ[0].y > this.aIJ[1].y){
				iHeight=(this.aIJ[0].y-this.aIJ[1].y)*2;
			}else{
				iHeight=(this.aIJ[1].y-this.aIJ[0].y)*2;
			}*/
			if (this.aIJ[0].x > this.aIJ[1].x){
				iAC=this.aIJ[0].x-this.aIJ[1].x;
			}else{
				iAC=this.aIJ[1].x-this.aIJ[0].x;
			}
			if (this.aIJ[0].y > this.aIJ[1].y){
				iBC=this.aIJ[0].y-this.aIJ[1].y;
			}else{
				iBC=this.aIJ[1].y-this.aIJ[0].y;
			}
			iRadius=Math.round(Math.sqrt((iAC*iAC)+(iBC*iBC)));
		
			this.oWZ.drawEllipse( this.aIJ[0].x-iRadius, this.aIJ[0].y-iRadius , iRadius*2, iRadius*2);
		break;
		case "point" :
			if(this.drawing_shape == "circle")
				this.oWZ.fillEllipse(this.aIJ[0].x-(this.size / 2), this.aIJ[0].y-(this.size / 2), this.size, this.size);
			else
				this.oWZ.fillRect(this.aIJ[0].x-(this.size / 2), this.aIJ[0].y-(this.size / 2), this.size, this.size);
		break;
		case "text" :
			this.oWZ.setFont(this.font, this.fontsize, this.fonttype);
			this.oWZ.setColor(this.color);
			this.oWZ.drawString(this.string, this.aIJ[0].x+this.decal, this.aIJ[0].y);
			break;
	}
		 
	this.oWZ.paint();
}

/**
* Dessine le dernier segment d'un objet de type Ligne ou Polygone.
*/
function drawLastSegment(){
	var aSegment;
	this.oWZ.setStroke(this.stroke);
	this.oWZ.setColor(this.color);
	if(this.aIJ.length > 1){
		aSegment=this.oRectangle.innerSegmentIJ(new VmVertex(this.aIJ[this.aIJ.length-2].x, this.aIJ[this.aIJ.length-2].y), new VmVertex(this.aIJ[this.aIJ.length-1].x, this.aIJ[this.aIJ.length-1].y));
		if (aSegment != null){
			this.oWZ.drawLine(aSegment[0].x, aSegment[0].y, aSegment[1].x, aSegment[1].y); 
			if (this.vertex_show){
				// Jeu de couleurs si l'on doit afficher les vertex
				this.oWZ.setColor(this.vertex_color);
				if(this.aIJ.length == 2)
					this.oWZ.fillRect(aSegment[0].x-(this.size / 2), aSegment[0].y-(this.size / 2), this.size, this.size);
				this.oWZ.fillRect(aSegment[1].x-(this.size / 2), aSegment[1].y-(this.size / 2), this.size, this.size);
				this.oWZ.setColor(this.color);
			}
		}
	}
	this.oWZ.paint();
}

/**
* Redéssine les segments entourant le vertex dont l'index est passé en paramètre (objet de type Ligne ou Polygone).
* \param iIndex Index du vertex clé.
*/
function redrawSegments(iIndex){
	var aSegment, aIndex;
	this.oWZ.setStroke(this.stroke);
	this.oWZ.setColor(this.color);
	switch(this.type){
		case "line" :
			(iIndex == 0) ? aIndex=new Array(iIndex, (iIndex+1)) :
			(iIndex == this.aIJ.length-1) ? aIndex=new Array((iIndex -1), iIndex) :
			aIndex=new Array((iIndex-1), iIndex, (iIndex+1));
		break;
		case "polygon" :
			(iIndex == 0) ? aIndex=new Array((this.aIJ.length-2), (this.aIJ.length-1), iIndex, (iIndex+1)) :
			(iIndex == this.aIJ.length-1) ? aIndex=new Array((iIndex -1), iIndex, 0, 1) :
			aIndex=new Array((iIndex-1), iIndex, (iIndex+1));
		break;
	}
	for(var i=1; i<aIndex.length; i++){
		if(aIndex[i-1] < aIndex[i]){
			aSegment=this.oRectangle.innerSegmentIJ(new VmVertex(this.aIJ[aIndex[i-1]].x, this.aIJ[aIndex[i-1]].y), new VmVertex(this.aIJ[aIndex[i]].x, this.aIJ[aIndex[i]].y));
			this.oWZ.drawLine(aSegment[0].x, aSegment[0].y, aSegment[1].x, aSegment[1].y);
		}
	}
	this.oWZ.paint();
}

/**
* Effacer le dessin (cette méthode fait appel aux ressources Javascript de la bibliothèque JsGraphics de Walter Zorn).
*/
function cleanShape(){
	this.oWZ.clear();
}

/**
* Effacer les données coordonnées stockées de l'objet.
*/
function clearShape(){
	this.aIJ=new Array();
	this.aXY=new Array();
}

/**
* Modifier l'étendue associée à l'objet.
* \param oRectangle Nouvelle étendue.
*/
function changeShapeExtent(oRectangle){
	this.oRectangle=oRectangle;
	var i=0;
	while(this.aXY[i]){
		this.aIJ[i]=this.oRectangle.getIJVertex(new VmVertex(this.aXY[i].x, this.aXY[i].y))
		i++;
	}
}

/**
* Générer un tableau HTML contenant le compte rendu de l'objet.
*/
function getHTMLSummary(){
	var sTable="<table width='100%' border='0' cellpadding='2' cellspacing='0' align='center'>";
	sTable += "<tr class='attributeNameCenter'><td class='attributeName' width='4%'>Num</td>";
	sTable += "<td class='attributeName' width='14%'>x</td>";
	sTable += "<td class='attributeName' width='14%'>y</td>";
	sTable += "<td class='attributeName' width='16%'>Long. segment</td>";
	sTable += "<td class='attributeName' width='16%'>Long. totale</td>";
	sTable += "<td class='attributeName' width='13%'>Delta x</td>";
	sTable += "<td class='attributeName' width='13%'>Delta y</td>";
	sTable += "<td class='attributeName' width='10%'>Angle/nord</td></tr>";
	var sCss;
	var dDist=dFullDist=dDeltaX=dDeltaY=0;
	for(var i=0;i<this.aXY.length;i++){
		if(i % 2 == 0)
			sCss="measureValue";
		else
			sCss="measureValue2";

		if(i > 0){
			dAngle=this.aXY[i-1].courseTo(this.aXY[i]);
			dDist=this.aXY[i-1].distanceTo(this.aXY[i]);

			dDeltaX=this.aXY[i].x-this.aXY[i-1].x;
			dDeltaY=this.aXY[i].y-this.aXY[i-1].y;
		}else{
			dDist=dDeltaX=dDeltaY=dAngle=0;
		}
		dFullDist+=dDist;

		sTable += "<tr class='"+sCss+"'>";
		sTable += "<td>"+(i+1)+"</td>";
		sTable += "<td>"+this.aXY[i].x.toFixed(this.precision)+"</td>";
		sTable += "<td>"+this.aXY[i].y.toFixed(this.precision)+"</td>";
		sTable += "<td>"+(dDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td>";
		sTable += "<td>"+(dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td>";
		sTable += "<td>"+(dDeltaX*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td>";
		sTable += "<td>"+(dDeltaY*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td>";
		sTable += "<td>"+(dAngle*this.angle_multiplier).toFixed(1)+" "+this.angle_unit+"</td>";
		sTable += "</tr>";
	}

	sTable += "</table>";
	sTable += "<table width='100%' border='0' cellpadding='2' cellspacing='0' align='center'>"
	switch(this.type){
		case "line" :
			sTable += "<tr class='info_label'><td width='120'>Longueur totale :</td><td>"+(dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td></tr>";
		break;
		case "polygon" :
			if(this.closed){
			sTable += "<tr class='info_label'><td width='120'>Périmètre :</td><td>"+(dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td></tr>";		
			sTable += "<tr class='info_label'><td width='120'>Superficie :</td><td>"+((this.getArea()*this.multiplier)*this.multiplier).toFixed(this.precision)+" "+this.unit+"&sup2;</td></tr>";
			}
		break;
		case "circle":
			sTable += "<tr class='info_label'><td width='120'>Rayon :</td><td>"+(dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"</td></tr>";
			sTable += "<tr class='info_label'><td width='120'>Périmètre :</td><td>"+Math.round(((dFullDist*this.multiplier).toFixed(this.precision)*2* Math.PI)*100)/100+" "+this.unit+"</td></tr>";
			//sTable += "<tr class='info_label'><td width='120'>Superficie :</td><td>"+Math.round((Math.pow((dFullDist*this.multiplier).toFixed(this.precision),2)*2* Math.PI)*100)/100+" "+this.unit+"&sup2;</td></tr>";
			sTable += "<tr class='info_label'><td width='120'>Superficie :</td><td>"+Math.round(Math.PI*((dFullDist*this.multiplier).toFixed(this.precision)*(dFullDist*this.multiplier).toFixed(this.precision)))+" "+this.unit+"&sup2;</td></tr>";
		break;
	}
	sTable += "</table>"
	
	return sTable;
}

/**
* Générer un tableau texte contenant le compte rendu de l'objet.
*/
function getTextSummary(){
	var sTable="Num\tx\ty\tLong. segment\tLong. totale\tDelta x\tDelta y\tAngle/nord\n";

	var dDist=dFullDist=dDeltaX=dDeltaY=0;
	for(var i=0;i<this.aXY.length;i++){
	
		if(i > 0){
			dAngle=this.aXY[i-1].courseTo(this.aXY[i]);
			dDist=this.aXY[i-1].distanceTo(this.aXY[i]);

			dDeltaX=this.aXY[i].x-this.aXY[i-1].x;
			dDeltaY=this.aXY[i].y-this.aXY[i-1].y;
		}else{
			dDist=dDeltaX=dDeltaY=dAngle=0;
		}
		
		dFullDist += dDist;

		sTable += (i+1)+"\t";
		sTable += this.aXY[i].x.toFixed(this.precision)+"\t";
		sTable += this.aXY[i].y.toFixed(this.precision)+"\t";
		sTable += (dDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"\t";
		sTable += (dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"\t";
		sTable += (dDeltaX*this.multiplier).toFixed(this.precision)+" "+this.unit+"\t";
		sTable += (dDeltaY*this.multiplier).toFixed(this.precision)+" "+this.unit+"\t";
		sTable += (dAngle*this.angle_multiplier).toFixed(1)+" "+this.angle_unit+"\t";
		sTable += "\n";
	}
		
	sTable += "\n"
	switch(this.type){
		case "line" :
			sTable += "Longueur totale : \t"+(dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"\n";
			break;
		case "polygon" :
			if(this.closed){
			sTable += "Périmètre : \t"+(dFullDist*this.multiplier).toFixed(this.precision)+" "+this.unit+"\n";
			sTable += "Superficie : \t"+((this.getArea()*this.multiplier)*this.multiplier).toFixed(this.precision)+" "+this.unit+String.fromCharCode(178)+"\n";
			}
			break;
	}
	
	return sTable;
}

/**
* Assure la copie du contenu du tableau texte dans le presse-papier (ne fonctionne que sous IE).
*/
function copyToClipboard(){
	var sToCopy=this.getTextSummary();
  
  if(window.clipboardData)
  	window.clipboardData.setData('Text', sToCopy); 
	else
		alert("Votre navigateur ne permet pas d'effectuer la copie automatique dans le presse-papier.\nEffectuez la copie à la main.");
}

/**
* Renvoie la superficie de l'objet en coordonnées terrain.
*/
function getArea(){ 
	if (this.type!="polygon") return 0;
	var iN=this.countVertex();
	if (iN < 4) return 0;
	var dSomme=0;
	for(var iIndex=0;iIndex<iN;iIndex++){
		dSomme += (this.getShapeX(iIndex)*(this.getShapeY(iIndex-1)-this.getShapeY(iIndex+1)));
	}
	return Math.abs(dSomme / 2);
}

/**
* Renvoie un vertex de l'objet en coordonnées terrain xy .
* la fonction renvoie null si l'index est hors limite.
* \param index Index du vertex.
*/
function getShapeVertexXY(index){ 
	if ((index < this.countVertex()) && (index >= 0))
		return (new VmVertex(this.getShapeX(index),this.getShapeY(index)));
	else
		return null;
}

/**
* Renvoie la valeur de la coordonnées terrain x de l'objet.
* la fonction renvoie 0 si l'index est hors limite.
* \param index Index du vertex.
*/
function getShapeX(index){  
	if ((index < this.countVertex()) && (index >= 0))
		return this.aXY[index].x;
	else
		return 0;
}

/**
* Renvoie la valeur de la coordonnées terrain y de l'objet.
* la fonction renvoie 0 si l'index est hors limite.
* \param index Index du vertex.
*/
function getShapeY(index){  
	if ((index < this.countVertex()) && (index >= 0))
		return this.aXY[index].y;
	else
		return 0;
}

/**
* Renvoie le nombre de vertex de l'objet.
*/
function countVertex(){ 
	return this.aIJ.length;
}

/**
* Permet de définir le style d'un objet de type ligne ou polygone.
* \param iStroke Epaisseur.
* \param sColor Couleur.
* \param aMeasure Tableau contenant des informations sur les unités de mesures 
* à associer à l'objet.
*/
function setVmLine(iStroke, sColor, aMeasure){
	this.stroke=iStroke;
	this.color=sColor;
	this.unit=aMeasure["unit"];
	this.multiplier=aMeasure["multiplier"];
	this.precision=aMeasure["precision"];
	this.angle_unit=aMeasure["angle_unit"];
	this.angle_multiplier=aMeasure["angle_multiplier"];
}

/** 
* Permet de définir la taille et la couleur de la représentation 
* des vertex pour les objets ligne ou polygone.
* \param iSize Taille de la représentation des vertex dans le dessin (en pixels).
* \param sColor Couleur de la représentation des vertex dans le dessin (rvb).
*/
function setVmLineVertex(iSize, sColor){
	this.vertex_show=true;
	this.size=iSize;
	this.vertex_color=sColor;
}

/**
* Permet de définir le style d'un objet de type annotation.
* \param sFont Nom de la police.
* \param sFontSize Taille en pixels ou en points de la police.
* \param sColor Couleur de la police.
* \param oFontType Type de la police.
* \param sString Chaîne de caractères à afficher.
* \param iDecal Décalage de l'annotation.
*/
function setVmText(sFont, sFontSize, sColor, oFontType, sString, iDecal){
	this.font=sFont;
	this.fontsize=sFontSize;
	this.color=sColor;
	this.fonttype=oFontType;
	this.string=sString;
	this.decal=iDecal;
}

/**
* Permet de définir le style d'un objet de type point
* \param sColor Couleur.
* \param iSize Diamètre.
* \param sDrawingShape Type de représentation (circle / square).
*/
function setVmPoint(sColor, iSize, sDrawingShape){
	this.color=sColor;
	this.size=iSize;
	this.drawing_shape=sDrawingShape;
}

/**
* Permet de définir le style d'un objet de type cercle.
* \param iStroke Epaisseur.
* \param sColor Couleur.
*/
function setVmCircle(iStroke, sColor, aMeasure){
	this.stroke=iStroke;
	this.color=sColor;
	this.unit=aMeasure["unit"];
	this.multiplier=aMeasure["multiplier"];
	this.precision=aMeasure["precision"];
	this.angle_unit=aMeasure["angle_unit"];
	this.angle_multiplier=aMeasure["angle_multiplier"];
}

/**
* Permet de récupérer la liste des coordonnées terrain de l'objet.
* \return Une chaîne de caractères représentant les coordonnées de l'objet 
* sous la forme 'x y x y'.
*/
function toStringXY(){
	return this.aXY.join(" ");
}

/**
* Permet de récupérer la liste des coordonnées image de l'objet.
* \return Une chaîne de caractères représentant les coordonnées de l'objet 
* sous la forme 'i j i j'.
*/
function toStringIJ(){	
	return this.aIJ.join(" ");
}
// -------------------------------

/**
* GraphicLayer : Classe Javascript de création de couches de dessin.
* <b>Constructeur</b>
*/
function GraphicLayer(){
	this.iZoomLimit=Number.MAX_VALUE;
	this.aShape= new Array();
	this.addShape=addShape;
	this.dropShape=dropShape;
	this.drawShapes=drawShapes;
	this.cleanShapes=cleanShapes;
	this.clearShapes=clearShapes;
	this.changeShapesExtent=changeShapesExtent;
	//*** Debug ***//
	this.debug=debug;
}

/**
* Ajouter un objet à la couche de dessin.
* \param oShape Objet VmShape à ajouter.
*/
function addShape(oShape){
	this.aShape[this.aShape.length]=oShape;
}

/**
* Cette fonction permet de supprimer un objet de la couche de dessin.
* \param oShape Objet à supprimer.
*/
function dropShape(oShape){
	var iToDrop=-1;
	for(var i=0; i<this.aShape.length; i++)
		if(this.aShape[i] == oShape){
			iToDrop=i;
			break;
		}	
	if(iToDrop != -1){
		this.aShape[i].cleanShape();
		this.aShape[i].clearShape();
		delete(this.aShape[i]);
		for(var n=i+1; n<this.aShape.length; n++)
			this.aShape[n-1]=this.aShape[n];
		this.aShape.pop();
	}
}

/**
* Dessiner les objets contenues dans la couche de dessin.
*/
function drawShapes(){
	if(this.aShape[0]){
		this.cleanShapes();
		for(var i=0;i<this.aShape.length;i++)
			this.aShape[i].drawShape();
	}
}

/**
* Effacer le dessin en cours.
*/
function cleanShapes(){
	if(this.aShape[0])
		this.aShape[0].cleanShape();
}

/**
* Supprimer tous les objets contenus dans la couche de dessin.
*/
function clearShapes(){
	if(this.aShape[0]){
		this.cleanShapes();
		while(this.aShape[0]){
			this.aShape[0].clearShape();
			delete(this.aShape[0]);
			this.aShape.shift();
		}
	}
}

/**
* Modifier l'étendue des objets contenus dans la couche de dessin.
* \param oRectangle Nouvelle étendue.
*/
function changeShapesExtent(oRectangle){
	if(this.aShape[0])
		for(var i=0;i<this.aShape.length;i++)
			this.aShape[i].changeShapeExtent(oRectangle);
}

/**
* Permet le débuggage de la couche de dessin. Affiche le contenu de la couche de dessin
* dans un div HTML nommé "dbg_gfx".
*/
function debug(){
	var aContent=new Array();
	var n,oNode;

	if(document.getElementById('dbg_gfx') && document.getElementById('dbg_gfx').firstChild)
		while(oNode=document.getElementById('dbg_gfx').firstChild)
			document.getElementById('dbg_gfx').removeChild(oNode);

	if(this.aShape[0]){
		var sContent="<p><b>Contenu de l'objet GraphicLayer</b></p><hr>";
		for(var i=0;i<this.aShape.length;i++){
			aContent[i]=new Array();

			aContent[i]["xy"]="";
			for(n=0;n<this.aShape[i].aXY.length;n++)
				aContent[i]["xy"] += "<br><i>"+this.aShape[i].aXY[n].x+":"+this.aShape[i].aXY[n].y+"</i>";	
			aContent[i]["ij"]="";
			for(n=0;n<this.aShape[i].aIJ.length;n++)
				aContent[i]["ij"] += "<br><i>"+this.aShape[i].aIJ[n].x+":"+this.aShape[i].aIJ[n].y+"</i>";	

			aContent[i]["type"]=this.aShape[i].type;
			aContent[i]["stroke"]=this.aShape[i].stroke;
			aContent[i]["color"]=this.aShape[i].color;
			aContent[i]["string"]=this.aShape[i].string;

			sContent += "<p>Objet numéro : "+i+"<br>";
			sContent += "<ul><li>Type : "+aContent[i]["type"]+"</li>";
			sContent += "<li> Coords XY : "+aContent[i]["xy"]+"</li>";
			sContent += "<li> Coords IJ : "+aContent[i]["ij"]+"</li>";
			sContent += "<li>Trait : "+aContent[i]["stroke"]+"</li>";
			sContent += "<li>Couleur : "+aContent[i]["color"]+"</li>";
			sContent += "<li>Texte : "+aContent[i]["string"]+"</li></ul></p>";
		}

		document.getElementById('dbg_gfx').innerHTML=sContent;
	}
}
// -----------------------------------------------
