classes/CLASS-Classe_Equivalence.js


// Class: Classe_Equivalence
// Classe qui représente une classe d'équivalence
// Hérite de <createjs.Container>
/*
var Classe_Equivalence = function(_param_)
{
	//==========================
	//Constructeur issu de l'heritage
	//==========================

		createjs.Container.call(this);


	//==========================
	//Variables Membres
	//==========================
	
		//Paramètres par défaut
		//this._numero=-1;	//Numéro de la classe
		
		// Variable : this._nom
		// (String) Nom de la classe d'équivalence
		this._nom="";
		this._couleur="black";	//Couleur de la classe
		this.liste_liaisons=[]	//Liste de toutes les référence des demi-liaisons concernées par la classe
		this._bloque = false; //Si la pièce ne doit pas bouger (bati)
		this._lastPosition = {x:0,y:0,theta:0}	//Dernière position sauvegarder (pour rétablir après simulation)
		this._selectionne = false;	//Dit si on est actuellement sélectionné
		this._type = "classe";	//Type d'objet
		this._historique = []	//Historique des positions (tableau de tableaux de la forme [[t0,x0,y0,theta0], [t1,x1,y1 .....]
		this._longueurHistorique = 5 ; //Nombre de position qu'on garde
		
		//Paramètres fournis en argument du constructeur

	//==========================
	//getter/setter
	//==========================


		this.numero=function()
		{
			return schema.classes.indexOf(this);
		}
		this.nom=function(n)
		{
			if(typeof(n)!='undefined')
				this._nom=n;
			if(this._nom=="")
				return "Classe n°"+this.numero();
			return this._nom;
		}

		this.couleur=function(c)
		{
			if(typeof(c)!='undefined')
			{
				this._couleur=c;
				for(var i=0;i<this.schema.children.length;i++)
				{
					var enfant = this.schema.children[i];
					if(enfant.couleur && typeof(enfant.couleur) === "function")	//Si la méthode couleur existe et que c'est une fonction
						enfant.couleur(c);
				}
				for(var i=0;i<this.images.children.length;i++)
				{
					var enfant = this.images.children[i];
					if(enfant.couleur && typeof(enfant.couleur) === "function")	//Si la méthode couleur existe et que c'est une fonction
						enfant.couleur(c);
				}
				for(var i=0;i<this.annotations.children.length;i++)
				{
					var enfant = this.annotations.children[i];
					if(enfant.couleur && typeof(enfant.couleur) === "function")	//Si la méthode couleur existe et que c'est une fonction
						enfant.couleur(c);
				}
			}
			return this._couleur;
		}
		
		
	//===========
	
		//Getter/Setter des coordonnées dans le repere local
		//Setter : de la forme {x:,y:,theta:} (en unité local et en radian dans le sens trigo)
		//Getter : renvoie la même chose que ci-dessus.
		this.coord=function(c)
		{
			if(typeof(c)!='undefined')
			{
				this.x=c.x*this.echelle();
				this.y=-c.y*this.echelle();
				this.rotation=-c.theta/Math.PI*180;
			}
			return {x:this.x/this.echelle(),y:-this.y/this.echelle(),thete:-this.rotation/180*Math.PI};
		}
		
		//Coordonnées sur x dans la base locale
		this.xx=function(xx)
		{
			if(typeof(xx)!='undefined')
				this.x=xx*this.echelle();
			return this.x/this.echelle();
		}
		
		//Coordonnées sur y dans la base locale
		this.yy=function(yy)
		{
			if(typeof(yy)!='undefined')
				this.y=-yy*this.echelle();
			return -this.y/this.echelle();
		}
		
		//Angle de rotation (en radian, dans le sens trigo)
		this.theta=function(t)
		{
			if(typeof(t)!='undefined')
				this.rotation=-t/Math.PI*180;
			return this.rotation/180*Math.PI;
		}


		this.echelle=function()
		{
			return schema.echelle();
		}
		
		this.bloque=function(b)
		{
			if(typeof(b)!='undefined')
				this._bloque=Boolean(b)
			return this._bloque;
		}
		
		
		this.lastPosition=function()
		{
			return this._lastPosition;
		}
		
		
		this.type=function()
		{
			return this._type;
		}
		
		
		this.longueurHistorique = function(l)
		{
			if(typeof(l)!='undefined')
				this._longueurHistorique=l
			return this._longueurHistorique;
		}
		//Renvoie la i-ème position en partant de la plus récente vers la plus vieille
		//Si i absent : renvoie tout le vecteur
		//i =1 pour la position la plus récente
		//Renvoie la plus vieille valeur si on dépasse
		this.historique=function(i)
		{
			if(typeof(i)=="undefined")
				return this._historique;
			if(this._historique.length==0)
				return [0,this._lastPosition.x,this._lastPosition.y,this._lastPosition.theta]
			if(i<1)
				return null;
			if(i>this._historique.length) //Si on remonte trop loin...
				return this._historique[0]; //Renvoie la plus vieille
			return this._historique[this._historique.length-i]
		}
		
		
		//Ajoute une position à l'historique (et supprime la plus vieille)
		this.pushHistorique=function(_date,_x,_y,_theta)
		{
			this._historique.push([_date,_x,_y,_theta]);
			if(this._historique.length>this._longueurHistorique)
				this._historique.shift();
		}
		
		
		
		// TORSEUR CINEMATIQUE
		
		this.getVRotation = function()
		{
			var a1 = this.historique(1);
			var a2 = this.historique(3);
			var a3 = this.historique(5);
			return deriveGauche3pts({x:a3[0],y:a3[3]},{x:a2[0],y:a2[3]},{x:a1[0],y:a1[3]})
		}
		
		//renvoie le torseur
		this.getTorseurCinematique=function(P)
		{
			
		}
	//===============
	//Autres fonctions membres
	//==========================
	
	
		//Fonction qui renvoie les coordonnées de la souris captées par le schéma, dans les coordonnées locales de la classe d'équivalence
		// Renvoie {x: ... , y: ....}
		this.getSourisLocal=function()
		{
			return schema.localToLocal(schema.XSOURIS(),schema.YSOURIS(),this);
		}
	

		//Fonction qui ajoute une demi-liaison
		this.ajouteDemiLiaison = function(_demiLiaison,_centre)
		{
			//Ajout graphique
			this.schema.addChild(_demiLiaison);
			_demiLiaison.classe(this);	//On ajoute la ref vers la classe
			var position = schema.localToLocal(_centre.x,_centre.y,this);
			_demiLiaison.x=position.x;
			_demiLiaison.y=position.y;
			
			//Ajout dans la liste de liaisons
			this.liste_liaisons.push(_demiLiaison);
			this.recentreOrigine();
		}
		
		//Fonction qui supprime la demi-liaison
		this.supprimeDemiLiaison = function(_demiLiaison)
		{
			var index = this.liste_liaisons.indexOf(_demiLiaison); //Trouve l'objet dans la liste des liaison
			if (index > -1)
			{
				console.log("supprime")
				//Supprime la demi-liaison
				this.liste_liaisons.splice(index, 1);//On la vire de la liste
				this.schema.removeChild(_demiLiaison);	//On la vire du container
				//this.recentreOrigine();
				//Supprime la demi-soeur
				if(_demiLiaison._demiSoeur)	//Supperssion de la demi-soeur
				{
					_demiLiaison._demiSoeur._demiSoeur = null; //On casse le lien pour pas faire de boucle infinie
					_demiLiaison._demiSoeur.autoSupprime();
				}
			}
		}
		
		//Fonction qui remplis le système à résoudre
		this.remplitSysteme=function(K,F)
		{
			if(this._bloque)
			{
				n=this.numero()
				for(i=0;i<3;i++)
				{
					K.set([3*n+i,3*n+i],1)
				}
			}
			else
			{
				for(var i=0;i<this.liste_liaisons.length;i++)
				{
					liaison = this.liste_liaisons[i];
					//On recupere les bouts de matrice
					var miniSystemeLiaison = liaison.remplisSysteme_liaison(K,F)
				}
			}
				
		}
		
		
		// Enregistre la position courante (avant simulation, par exemple)
		this.sauveLastPosition = function()
		{
			this._lastPosition = {x:this.x,y:this.y,theta:this.rotation}
		}
		//Restore la position courante (après simulation, par exemple)
		this.restoreLastPosition = function()
		{
			this.x=this._lastPosition.x;
			this.y=this._lastPosition.y;
			this.rotation=this._lastPosition.theta;
		}
		
		
		//Fonction qui décale l'origine de la pièce pour quelle soit au barycentre des liaisons (améliore le conditionnement de la matrice)
		this.recentreOrigine = function()
		{
			if(this.liste_liaisons.length==0)
				return;
		
			//recherche du barycentre des liaisons, dans le repère local
			var barycentre = {x:0,y:0}
			for(var i=0; i<this.liste_liaisons.length; i++)
			{
				var liaison_i = this.liste_liaisons[i];
				barycentre.x += liaison_i.x;
				barycentre.y += liaison_i.y;
			}
			barycentre.x/=this.liste_liaisons.length;
			barycentre.y/=this.liste_liaisons.length;
			
			//déplacement des éléments (liaison et autres objet)
//			for(var i=0; i<this.images.children.length; i++)
//			{
//				var element_i = this.images.children[i];
//				element_i.x -= barycentre.x;
//				element_i.y -= barycentre.y;
//			}
			this.images.x -= barycentre.x;
			this.images.y -= barycentre.y;
			
			for(var i=0; i<this.schema.children.length; i++)
			{
				var element_i = this.schema.children[i];
				element_i.x -= barycentre.x;
				element_i.y -= barycentre.y;
			}
			for(var i=0; i<this.annotations.children.length; i++)
			{
				var element_i = this.annotations.children[i];
				element_i.x -= barycentre.x;
				element_i.y -= barycentre.y;
			}
			//déplacement de la pièce pour revenir dans la même position
			var bary_global = this.localToLocal(barycentre.x, barycentre.y, this.parent)
			this.x += bary_global.x-this.x;
			this.y += bary_global.y-this.y;
		}
		
		
		// Fonction qui floute la CE
		this.floute = function()
		{
			this.filters = [new createjs.BlurFilter(5, 5, 10)] //Filtre flou
			
			// On met en cache la CL (dans un rectangle dont on calcule les dimensions). Voir la doc
			this.updateBounds();
			var bounds = this.getBounds();
			var boundsFlou = this.filters[0].getBounds() //Padding en plus lié au flou
			this.cache(bounds.x+boundsFlou.x, 	bounds.y+boundsFlou.y, 	bounds.width+boundsFlou.width, 	bounds.height+boundsFlou.height ) //On met en cache (nécessaire)
			this.alpha = 0.25;
		}
		
		this.updateBounds = function()
		{
			var xmin=0;
			var ymin=0;
			var xmax=0;
			var ymax=0;
			
			if(this.schema.getBounds()!=null)
			{
				var b_schema = this.schema.getBounds();
				xmin=b_schema.x;
				ymin=b_schema.y;
				xmax=b_schema.x+b_schema.width;
				ymax=b_schema.y+b_schema.height;
			}
			
			if(this.images.getBounds()!=null)
			{
				var b_image= this.images.getBounds();
				xmin=Math.min(xmin,b_image.x);
				ymin=Math.min(ymin,b_image.y);
				xmax=Math.max(xmax,b_image.x+b_image.width);
				ymax=Math.max(ymax,b_image.y+b_image.height);
			}
			
			if(this.annotations.getBounds()!=null)
			{

				var b_annotations = this.annotations.getBounds();
				xmin=Math.min(xmin,b_annotations.x);
				ymin=Math.min(ymin,b_annotations.y);
				xmax=Math.max(xmax,b_annotations.x+b_annotations.width);
				ymax=Math.max(ymax,b_annotations.y+b_annotations.height);
				// A compléter avec les autres groupes (
			}
			
			this.setBounds(xmin,ymin,xmax-xmin,ymax-ymin);
		}
		
		// Fonction qui défloute
		this.defloute = function()
		{
			this.filters = null
			this.uncache();
			this.alpha=1;
		}
		
		
		// Fonction qui sélectionne la CL (met en surbrillance, tout ça)
		this.selectionne = function()
		{
			deselectionneToutLeMonde(); //On vire tous les autres
			CLASSE = this.numero()
			update_info_CE(this);	//Met à jour la zone d'info
			
			for(var i=0;i<schema.classes.length;i++)
			{
				if(i!=CLASSE)
					schema.classes[i].floute()
			}
			this._selectionne = true;
		}
		
		
		//Fonction qui retire la surbrillance, etc...
		this.deselectionne = function()
		{
			for(var i=0;i<schema.classes.length;i++)
			{
				if(i!=CLASSE)
					schema.classes[i].defloute()
			}
			this._selectionne = false;
			CLASSE = -1;
		}
		
		//Fonction qui switch le mode sélectionné / désélectionné
		this.selectionneDeselectionne=function()
		{
			if(this._selectionne)
			{
				this.deselectionne();			
				$("#info_classe").hide(400);
			}
			else
			{
				$("#info_classe").show(400);
				this.selectionne();
			}
		}
		
		
		//Fonction qui dessine une ligne, (enregistre ses bornes) et l'ajoute à la classe
		//Renvoie un ref de la ligne
		//si repereDirect =true : Y est vers le haut. Si false : Y est vers le bas
		this.dessineLigne = function(a,b,c,d,repereDirect = true)
		{
			if(repereDirect)
				var ligne = new Ligne(a,-b,c,-d);
			else
				var ligne = new Ligne(a,b,c,d);
			ligne.couleur(this._couleur);
			this.schema.addChild(ligne)
			
			return ligne
		}
		
		// Fonction qui ajoute une image à la CE
		// _image = str
		this.ajouteImage = function(_image,_x=0,_y=0,_rot=0,_scale=1)
		{
			var img = new createjs.Bitmap("dessins/"+_image);
			this.images.addChild(img);
			img.x=_x;
			img.y=_y;
			img.rotation=_rot;
			img.scaleX=img.scaleY=_scale;
		}
		
	//==========================
	//Graphismes
	//==========================
		this.images = new createjs.Container()
		this.addChild(this.images);
		this.schema = new createjs.Container()
		this.addChild(this.schema);
		this.annotations = new createjs.Container()
		this.addChild(this.annotations);
		
		this._repere=new Repere({x:0,y:0},0);
		this.annotations.addChild(this._repere);
		


	//==========================
	//Evénements
	//==========================
		this.cursor="pointer"; // Pour le CSS de la
			
		this.addEventListener("click",function(event)
			{
				if(ACTION == "SELECTION")
				{
					cible = event.target;
					trouveClasse(cible).selectionneDeselectionne();
				}
			})
	
		this.addEventListener("mousedown",function(event)
			{
				if(ACTION == "MANIP" && SOUS_ACTION == "PREPARE")
				{
					cible = event.target;
					classe = trouveClasse(cible)
					CLASSE = classe.numero()
					videSuiveur();
					classe.accroche = new Accroche();
					classe.ajouteDemiLiaison(classe.accroche,schema.globalToLocal(event.stageX,event.stageY))
					classe.recentreOrigine();
					
					
					SOUS_ACTION = "TIRE";
				}
			})
	
}


Classe_Equivalence.prototype = Object.create(createjs.Container.prototype);//On recopie le prototype de createjs.Stage
Classe_Equivalence.prototype.constructor = Classe_Equivalence;//On recopie le constructeur de Noeud dans son prototype

*/



/**
 * Objet (graphique) représentant une classe d'équivalence.
 * @extends createjs.Container
 */
class Classe_Equivalence extends createjs.Container
{

	// **********************************************************
	/**
	 * Constructeur
	 * @param {Object} _opt - [FACULTATIF] Options de création de la classe d'équivalence. L'objet complet est de la forme : {pos, couleur, bloque}, mais seuls les informations strictement nécessaires peuvent être renseignée. pos est de type {@link Position} (par défaut : l'origine de {\@link SCHEMA}) ; couleur est STRING ; bloque est un booléen (si la pièce est bloquée, référentiel, etc.)
	 */
	constructor(_opt)
	{
		super()	// Rappelle le constructeur parent
		
		// PARAMETRES
			var options = { pos: {x:0,y:0,theta:0,contexte:SCHEMA} , couleur:"#000000", bloque:false } ;	//Paramètres par défaut
			ecraseOptions(options,_opt) ;	// Mise à jour des paramètres en fonction de ce que l'utilisateur passe
			
			var position = convertePosition(options.pos,SCHEMA,false);	// Converti la position (px, par rapport à SCHEMA)
			
			this.x = position.x ;
			this.y = position.y ;
			this.rotation = position.theta ;
			this.couleur(options.couleur) ;
			this.bloque(options.bloque) ;
			
			this.cursor="pointer"; // Pour le CSS de la
			
		// DESSINS
		
			this.addChild(this.images);
			this.addChild(this.schema);
			this.addChild(this.annotations);
			this.annotations.addChild(this._repere);
		
		// EVENEMENTS 
		
			this.addEventListener("click",function(event)
				{
					if(ACTION == "SELECTION")
					{
						var cible = event.target;
						trouveClasse(cible).selectionneDeselectionne();
					}
				})
		
			this.addEventListener("mousedown",function(event)
				{
					if(ACTION == "MANIP" && SOUS_ACTION == "PREPARE")
					{
						var cible = event.target;
						var classe = trouveClasse(cible)
						CLASSE = classe.numero()
						videSuiveur();
						classe.accroche = new Accroche();
						classe.ajouteDemiLiaison(classe.accroche,schema.globalToLocal(event.stageX,event.stageY))
						classe.recentreOrigine();
						
						
						SOUS_ACTION = "TIRE";
					}
				})
	}
	
	//==========================
	//Variables Membres
	//==========================
	
			/** Nom de la classe d'équivalence
			@type {String}  */
		_nom = "";
		
			/** Couleur de la classe d'équivalence
			@type {String}  */
		_couleur = "black";
		
			/** Liste de toutes les référence des demi-liaisons concernées par la classe
			@type {Array}  */
		liste_liaisons = []
		
			/** Bloque la pièce (true) ou laisse la pièce mobile (false)
			@type {Boolean}  */
		_bloque = false; 
		
			/** Dernière position enregistrée (utile quand on veut réinitialier une simulation)
			@type {Position}  */
		_lastPosition = {x:0,y:0,theta:0}	//Dernière position sauvegarder (pour rétablir après simulation)
		
			/** Dit si la classe est actuellement sélectionnée
			@type {Boolean}  */
		_selectionne = false;
		
			/** Type d'objet ("classe")
			@type {String}  */
		_type = "classe";
		
			/** Historique des positions (tableau de tableaux de la forme [[t0,x0,y0,theta0], [t1,x1,y1 .....]
			@type {Array}  */
		_historique = [];
		
			/** Nombre de position qu'on garde dans l'historique
			@type {Number}  */
		_longueurHistorique = 5 ; //Nombre de position qu'on garde
		
		//Paramètres fournis en argument du constructeur
		
	//==========================
	//Graphismes
	//==========================
	
			
		
			/** Groupe graphique contenant les images d'arrière plan de la classe d'équivalence.
			@type {createjs.Container}  */
		images = new createjs.Container() ;
		
			/** Groupe graphique contenant les schémas (liaisons, traites, etc.).
			@type {createjs.Container}  */
		schema = new createjs.Container() ;
		
			/** Groupe graphique contenant les annotations (points, vecteurs, etc.).
			@type {createjs.Container}  */
		annotations = new createjs.Container() ;
		
			/** Référence vers le repère initial de la classe d'équivalence (inclu dans le groupe annotations)
			@type {createjs.Container}  */
		_repere = new Repere({x:0,y:0},0);
		
	//==========================
	//getter/setter
	//==========================

	
			/** Renvoie le numéro de la classe d'équivalence, stocké dans schema.classes
			@return {Number} Numéro de la classe
			*/
		numero()
		{
			return schema.classes.indexOf(this);
		}
		
			/** GETTER/SETTER - Renvoie le numéro de la classe d'équivalence, stocké dans schema.classes
			@param {String} n - (FACULTATIF) nom de la classe à stocker.
			@return {String} Nom de la classe d'équivalence. Si chaîne vide, renvoie "Classe n°i" où i est le numéro de la classe.
			*/
		nom(n)
		{
			if(typeof(n)!='undefined')
				this._nom=n;
			if(this._nom=="")
				return "Classe n°"+this.numero();
			return this._nom;
		}
		
			/** GETTER/SETTER - Couleur de la classe d'équivalence. Si modifiée, elle affecte les objets graphique enfants (lignes, cercles, liaisons, etc.) qui possède une méthode couleur() similaire
			@param {String} c - (FACULTATIF) Couleur à stocker.
			@return {String} Couleur de la classe d'équivalence
			*/
		couleur(c)
		{
			if(typeof(c)!='undefined')
			{
				this._couleur=c;
				for(var i=0;i<this.schema.children.length;i++)
				{
					var enfant = this.schema.children[i];
					if(enfant.couleur && typeof(enfant.couleur) === "function")	//Si la méthode couleur existe et que c'est une fonction
						enfant.couleur(c);
				}
				for(var i=0;i<this.images.children.length;i++)
				{
					var enfant = this.images.children[i];
					if(enfant.couleur && typeof(enfant.couleur) === "function")	//Si la méthode couleur existe et que c'est une fonction
						enfant.couleur(c);
				}
				for(var i=0;i<this.annotations.children.length;i++)
				{
					var enfant = this.annotations.children[i];
					if(enfant.couleur && typeof(enfant.couleur) === "function")	//Si la méthode couleur existe et que c'est une fonction
						enfant.couleur(c);
				}
			}
			return this._couleur;
		}
		
			/** GETTER/SETTER - Coordonnées de la classe d'équivalence, dans les coordonnées locales (voir la classe schema)
			@param {Position} c - (FACULTATIF) Position de la classe, de la forme {x:,y:,theta:} (en unité local et en radian dans le sens trigo)
			@return {Position} Position de la classe, de la forme {x:,y:,theta:} (en unité local et en radian dans le sens trigo)
			*/
		COORD(c)
		{
			if(typeof(c)!='undefined')
			{
				this.x=c.x*this.echelle();
				this.y=-c.y*this.echelle();
				this.rotation=-c.theta/Math.PI*180;
			}
			return {x:this.x/this.echelle(),y:-this.y/this.echelle(),thete:-this.rotation/180*Math.PI};
		}
		
			/** GETTER/SETTER - Coordonnée sur X de la classe d'équivalence, dans les coordonnées locales (voir la classe schema)
			@param {Number} posX - (FACULTATIF) Coordonnées sur X en unité local.
			@return {Number} Coordonnées sur X en unité local.
			*/
		X(posX)
		{
			if(typeof(posX)!='undefined')
				this.x=posX*this.echelle();
			return this.x/this.echelle();
		}
		
			/** GETTER/SETTER - Coordonnée sur Y de la classe d'équivalence, dans les coordonnées locales (voir la classe schema)
			@param {Number} posY - (FACULTATIF) Coordonnées sur Y en unité local (positif vers le haut).
			@return {Number} Coordonnées sur Y en unité local (positif vers le haut).
			*/
		Y(posY)
		{
			if(typeof(posY)!='undefined')
				this.y=-posY*this.echelle();
			return -this.y/this.echelle();
		}
		
			/** GETTER/SETTER - Angle de rotation (en radian, dans le sens trigo)
			@param {Number} t - (FACULTATIF) Angle de rotation (en radian, dans le sens trigo)
			@return {Number} Angle de rotation (en radian, dans le sens trigo)
			*/
		THETA(t)
		{
			if(typeof(t)!='undefined')
				this.rotation=-t/Math.PI*180;
			return this.rotation/180*Math.PI;
		}


		
			/** Echelle (nombre de pixel correspondant à une unité). Pointe vers la valeur stockées dans la classe schéma.
			@return {Number} Echelle, en pixels.
			*/
		echelle()
		{
			return schema.echelle();
		}
		
			/** GETTER/SETTER - Permet de bloquer la pièce par rapport à la scène (=bâti). Si true, les coordonnées de la classe d'équivalence ne sont pas modifiées durant la simulation
			@param {Boolean} b - 'true' si la classe est bloquée. 'false' sinon.
			@return {Boolean} 'true' si la classe est bloquée. 'false' sinon.
			*/
		bloque(b)
		{
			if(typeof(b)!='undefined')
				this._bloque=Boolean(b)
			return this._bloque;
		}
		
			/** Renvoie la position précédemment enregistrée (avant simulation)
			@return {Position}
			*/
		lastPosition()
		{
			return this._lastPosition;
		}
		
			/** GETTER du type d'objet (classe d'équivalence)
			@return {String}
			*/
		type()
		{
			return this._type;
		}
		
			/** GETTER/SETTER du nombre de derniers points que l'on enregistre.
			@param {Number} l - nombre de derniers points que l'on enregistre.
			@return {Number} nombre de derniers points que l'on enregistre.
			*/
		longueurHistorique(l)
		{
			if(typeof(l)!='undefined')
				this._longueurHistorique=l
			return this._longueurHistorique;
		}
		
			/** Renvoie la i-ème position en partant de la plus récente vers la plus vieille.
			 * Si i absent : renvoie tout le vecteur.
			 * i = 1 pour la position la plus récente
			@param {Number} i - Numéro de la position que l'on souhaite récupérer
			@return {Position}
			*/
		historique(i)
		{
			if(typeof(i)=="undefined")
				return this._historique;
			if(this._historique.length==0)
				return [0,this._lastPosition.x,this._lastPosition.y,this._lastPosition.theta]
			if(i<1)
				return null;
			if(i>this._historique.length) //Si on remonte trop loin...
				return this._historique[0]; //Renvoie la plus vieille
			return this._historique[this._historique.length-i]
		}
		
			/** Ajoute une position à l'historique (et supprime la plus vieille).
			@param {Number} _date - Date de l'enregistrement
			@param {Number} _x - Position sur x à enregitrer (en pixel)
			@param {Number} _y - Position sur y à enregitrer (en pixel)
			@param {Number} _theta - Orientation à enregistrer (en degrès, sens horaire)
			*/
		pushHistorique(_date,_x,_y,_theta)
		{
			this._historique.push([_date,_x,_y,_theta]);
			if(this._historique.length>this._longueurHistorique)
				this._historique.shift();
		}
		
		
		
	//=========================
	//TORSEUR CINEMATIQUE
	//==========================
		
			/** Calcule la vitesse de rotation autour de l'axe normal au plan
			@return {Number} Vitesse de rotation (en rad/s mais je suis plus sûr)
			*/
		getVRotation()
		{
			var a1 = this.historique(1);
			var a2 = this.historique(3);
			var a3 = this.historique(5);
			return deriveGauche3pts({x:a3[0],y:a3[3]},{x:a2[0],y:a2[3]},{x:a1[0],y:a1[3]})
		}
		
			/** Calcule le torseur cinématique instantané de la classe d'équivalence
			@return {Torseur} Torseur cinématique
			*/
		getTorseurCinematique(P)
		{
			
		}
		
	//=========================
	//Autres fonctions membres
	//==========================
	
	
		
			/** Fonction qui renvoie les coordonnées de la souris captées par le schéma, dans les coordonnées locales de la classe d'équivalence
			@return {Position} Position de la souris en pixel (A VERIFIER)
			*/
		getSourisLocal()
		{
			return schema.localToLocal(schema.XSOURIS(),schema.YSOURIS(),this);
		}
	
			/** Fonction qui ajoute une demi-liaison
			@param {DemiLiaison} _demiLiaison - Référence vers l'objet "demi-liaison"
			@param {Point} _center - Position de la demi-liaison DANS LA BASE DU SCHEMA !
			*/
		ajouteDemiLiaison(_demiLiaison,_centre)
		{
			//Ajout graphique
			this.schema.addChild(_demiLiaison);
			_demiLiaison.classe(this);	//On ajoute la ref vers la classe
			var position = schema.localToLocal(_centre.x,_centre.y,this);
			_demiLiaison.x=position.x;
			_demiLiaison.y=position.y;
			
			//Ajout dans la liste de liaisons
			this.liste_liaisons.push(_demiLiaison);
			this.recentreOrigine();
		}
		
			/** Fonction qui supprime la demi-liaison (si elle existe)
			@param {DemiLiaison} _demiLiaison - Référence vers l'objet "demi-liaison" à supprimer
			*/
		supprimeDemiLiaison(_demiLiaison)
		{
			var index = this.liste_liaisons.indexOf(_demiLiaison); //Trouve l'objet dans la liste des liaison
			if (index > -1)
			{
				console.log("supprime")
				//Supprime la demi-liaison
				this.liste_liaisons.splice(index, 1);//On la vire de la liste
				this.schema.removeChild(_demiLiaison);	//On la vire du container
				//this.recentreOrigine();
				//Supprime la demi-soeur
				if(_demiLiaison._demiSoeur)	//Supperssion de la demi-soeur
				{
					_demiLiaison._demiSoeur._demiSoeur = null; //On casse le lien pour pas faire de boucle infinie
					_demiLiaison._demiSoeur.autoSupprime();
				}
			}
		}
		
			/** Fonction qui remplit le système à résoudre
			@param {Matrix} K - Matrice du système K.U=F
			@param {Vector} F - Vecteur-second membre du sytème K.U=F
			*/
		remplitSysteme(K,F)
		{
			if(this._bloque)
			{
				var n=this.numero()
				for(var i=0;i<3;i++)
				{
					K.set([3*n+i,3*n+i],1)
				}
			}
			else
			{
				for(var i=0;i<this.liste_liaisons.length;i++)
				{
					var liaison = this.liste_liaisons[i];
					//On recupere les bouts de matrice
					var miniSystemeLiaison = liaison.remplisSysteme_liaison(K,F)
				}
			}
				
		}
		
			/** Enregistre la position courante (avant simulation, par exemple)
			*/
		sauveLastPosition()
		{
			this._lastPosition = {x:this.x,y:this.y,theta:this.rotation}
		}
		
			/** Restore la position courante (après simulation, par exemple)
			*/
		restoreLastPosition()
		{
			this.x=this._lastPosition.x;
			this.y=this._lastPosition.y;
			this.rotation=this._lastPosition.theta;
		}
		
			/** Fonction qui décale l'origine de la pièce pour quelle soit au barycentre des liaisons (améliore le conditionnement de la matrice)
			*/
		recentreOrigine()
		{
			if(this.liste_liaisons.length==0)
				return;
		
			//recherche du barycentre des liaisons, dans le repère local
			var barycentre = {x:0,y:0}
			for(var i=0; i<this.liste_liaisons.length; i++)
			{
				var liaison_i = this.liste_liaisons[i];
				barycentre.x += liaison_i.x;
				barycentre.y += liaison_i.y;
			}
			barycentre.x/=this.liste_liaisons.length;
			barycentre.y/=this.liste_liaisons.length;
			
			//déplacement des éléments (liaison et autres objet)
			/*for(var i=0; i<this.images.children.length; i++)
			{
				var element_i = this.images.children[i];
				element_i.x -= barycentre.x;
				element_i.y -= barycentre.y;
			}*/
			this.images.x -= barycentre.x;
			this.images.y -= barycentre.y;
			
			for(var i=0; i<this.schema.children.length; i++)
			{
				var element_i = this.schema.children[i];
				element_i.x -= barycentre.x;
				element_i.y -= barycentre.y;
			}
			for(var i=0; i<this.annotations.children.length; i++)
			{
				var element_i = this.annotations.children[i];
				element_i.x -= barycentre.x;
				element_i.y -= barycentre.y;
			}
			//déplacement de la pièce pour revenir dans la même position
			var bary_global = this.localToLocal(barycentre.x, barycentre.y, this.parent)
			this.x += bary_global.x-this.x;
			this.y += bary_global.y-this.y;
		}
		
			/** Fonction qui floute la classe d'équivalence (Utile quand on sélectionne un autre classe d'équivalence)
			*/
		floute()
		{
			this.filters = [new createjs.BlurFilter(5, 5, 10)] //Filtre flou
			
			// On met en cache la CL (dans un rectangle dont on calcule les dimensions). Voir la doc
			this.updateBounds();
			var bounds = this.getBounds();
			var boundsFlou = this.filters[0].getBounds() //Padding en plus lié au flou
			this.cache(bounds.x+boundsFlou.x, 	bounds.y+boundsFlou.y, 	bounds.width+boundsFlou.width, 	bounds.height+boundsFlou.height ) //On met en cache (nécessaire)
			this.alpha = 0.25;
		}
		
			/** Fonction qui recalcule les limites (Largeur / Hauteur) géométrique de la classe d'équivalence (creatjs.Container ne sait pas le faire tout seul)
			*/
		updateBounds()
		{
			var xmin=0;
			var ymin=0;
			var xmax=0;
			var ymax=0;
			
			if(this.schema.getBounds()!=null)
			{
				var b_schema = this.schema.getBounds();
				var xmin = b_schema.x;
				var ymin = b_schema.y;
				var xmax = b_schema.x+b_schema.width;
				var ymax = b_schema.y+b_schema.height;
			}
			
			if(this.images.getBounds()!=null)
			{
				var b_image = this.images.getBounds();
				var xmin = Math.min(xmin,b_image.x);
				var ymin = Math.min(ymin,b_image.y);
				var xmax = Math.max(xmax,b_image.x+b_image.width);
				var ymax = Math.max(ymax,b_image.y+b_image.height);
			}
			
			if(this.annotations.getBounds()!=null)
			{

				var b_annotations = this.annotations.getBounds();
				var xmin = Math.min(xmin,b_annotations.x);
				var ymin = Math.min(ymin,b_annotations.y);
				var xmax = Math.max(xmax,b_annotations.x+b_annotations.width);
				var ymax = Math.max(ymax,b_annotations.y+b_annotations.height);
				// A compléter avec les autres groupes (
			}
			
			this.setBounds(xmin,ymin,xmax-xmin,ymax-ymin);
		}
		
			/** Fonction qui défloute la classe d'équivalence
			*/
		defloute()
		{
			this.filters = null
			this.uncache();
			this.alpha=1;
		}
		
			/** Fonction qui sélectionne la Classe d'équivalence (met en surbrillance en floutant les autres)
			*/
		selectionne()
		{
			deselectionneToutLeMonde(); //On vire tous les autres
			CLASSE = this.numero()
			update_info_CE(this);	//Met à jour la zone d'info
			
			for(var i=0;i<schema.classes.length;i++)
			{
				if(i!=CLASSE)
					schema.classes[i].floute()
			}
			this._selectionne = true;
		}
		
			/** Fonction désélectionne (qui retire la surbrillance, etc...)
			*/
		deselectionne()
		{
			for(var i=0;i<schema.classes.length;i++)
			{
				if(i!=CLASSE)
					schema.classes[i].defloute()
			}
			this._selectionne = false;
			CLASSE = -1;
		}
		
			/** Fonction qui switch le mode sélectionné / désélectionné
			*/
		selectionneDeselectionne()
		{
			if(this._selectionne)
			{
				this.deselectionne();			
				$("#info_classe").hide(400);
			}
			else
			{
				$("#info_classe").show(400);
				this.selectionne();
			}
		}
		
			/** Fonction qui dessine une ligne, (enregistre ses bornes) et l'ajoute à la classe
			@param {Number} a - Coordonnées sur x du point 1
			@param {Number} b - Coordonnées sur y du point 1
			@param {Number} c - Coordonnées sur x du point 2
			@param {Number} d - Coordonnées sur y du point 2
			@param {Boolean} repereDirect - si repereDirect =true : Y est vers le haut. Si false : Y est vers le bas. IL FAUDRAIT REMPLACER PAR L'UTILISATION DES COORDONNEES A L'ECHELLE (voir la classe {@link Schema})
			@return {Ligne} Référence vers la ligne nouvellement créée.
			*/
		dessineLigne(a,b,c,d,repereDirect = true)
		{
			if(repereDirect)
				var ligne = new Ligne(a,-b,c,-d);
			else
				var ligne = new Ligne(a,b,c,d);
			ligne.couleur(this._couleur);
			this.schema.addChild(ligne)
			
			return ligne
		}
		
			/** Fonction qui ajoute une image à la classe d'équivalence
			@param {String} _image - Adresse de l'image à insérer
			@param {Number} _x - Coordonnées sur x (local à la classe d'équivalence) en pixel. Défaut : 0.
			@param {Number} _y - Coordonnées sur y (local à la classe d'équivalence) en pixel. Défaut : 0.
			@param {Number} _rot - Orientation de l'image (local à la classe d'équivalence) en degrés, sens horaire. Défaut : 0.
			@param {Number} _scale - Échelle de l'image. Défaut : 1
			*/
		ajouteImage(_image,_x=0,_y=0,_rot=0,_scale=1)
		{
			var img = new createjs.Bitmap("dessins/"+_image);
			this.images.addChild(img);
			img.x=_x;
			img.y=_y;
			img.rotation=_rot;
			img.scaleX=img.scaleY=_scale;
		}	
}