/***************************************************************************
     Copyright (C) 2009 by Pierre,,,
     pierre@pierre-laptop
 ***************************************************************************/

#include "vecteur.h"


///fonction qui permet d'initialiser et de renvoyer un Vecteur
/**	@param x : composante x du Vecteur
	@param y : composante y du Vecteur
	@param z : composante z du Vecteur
*/
Vecteur initVecteur(double x, double y, double z){
	Vecteur vect(x, y, z);
	return vect;
}

using namespace std;

////////////////////////////////////////////////////////////////////////
//                                                                    //
//                          Classe Vecteur                            //
//                                                                    //
////////////////////////////////////////////////////////////////////////

///	constructeur de Vecteur
/**	@param vecX : composante x du Vecteur
	@param vecY : composante y du Vecteur
	@param vecZ : composante z du Vecteur
*/
Vecteur::Vecteur(double vecX, double vecY, double vecZ){
	initialisationVecteur(vecX, vecY, vecZ);
}

///initialise un Vecteur avec une chaîne
/** @param str : chaîne d'initialisation
*/
Vecteur::Vecteur(const char * str){
	initialisationVecteur(0.0, 0.0, 0.0);
	loadFromConfig(str);
}

///	constructeur de copie de Vecteur
/**	@param vec : Vecteur
*/
Vecteur::Vecteur(const Vecteur & vec){
	initialisationVecteur(vec.p_x, vec.p_y, vec.p_z);
}

///destructeur de Vecteur
Vecteur::~Vecteur(){}

/// 	fonction qui :sauvegarde les coordonnées du Vecteur (x,y,z) dans le fichier fp
/**	@param fp : fichier de savegarde du vecteur
*/
void Vecteur::save(FILE* fp){
	fprintf(fp, "(%lf,", p_x);
	fprintf(fp, "%lf,", p_y);
	fprintf(fp, "%lf)", p_z);
}

/// 	fonction qui :sauvegarde les coordonnées du Vecteur (x,y) dans le fichier fp
/**	@param fp : fichier de savegarde du vecteur
*/
void Vecteur::save2d(FILE* fp){
	fprintf(fp, "(%lf,", p_x);
	fprintf(fp, "%lf)", p_y);
}

/// 	Cette fonction initialise le Vecteur avec les valeurs de coordonnées de Vecteur enregistrées sous forme de fichiers
/**	@param fp : fichier où doit se trouver des coordonnées de Vecteur

*/
void Vecteur::load(FILE* fp){
	double nb;
	if(fscanf(fp, "(%lf,", &nb) == 0) return;
	p_x = nb;
	if(fscanf(fp, " %lf,", &nb) == 0) return;
	p_y = nb;
	if(fscanf(fp, " %lf)", &nb) == 0) return;
	p_z = nb;
}

///initialisation avec une chaine de caractères
/**	@param str : chaîne de caractères pour l'initialisation du Vecteur
 * 	@return true si l'initialisation s'est faite, false sinon
*/
bool Vecteur::initString(const char * str){
	if(str == NULL) return false;
	if(sscanf(str,"(%lf,%lf,%lf)",&p_x,&p_y,&p_z) == 0){
		if(sscanf(str,"(%lf,%lf)",&p_x,&p_y) == 0){
			if(sscanf(str,"(%lf)",&p_x) == 0){
				return false;
			}else return true;
		}else return true;
	}else return true;
}

///	redéfinition de l'opérateur =
/**	@param vect : Vecteur
	@return Vecteur (*this)
*/
Vecteur & Vecteur::operator = ( const Vecteur & vect){
	initialisationVecteur(vect.p_x, vect.p_y, vect.p_z);
	return *this;
}

///	redéfinition de l'opérateur +=
/**	@param vect : Vecteur
	@return Vecteur (*this)
*/
Vecteur & Vecteur::operator += ( const Vecteur & vect){
	p_x += vect.p_x;
	p_y += vect.p_y;
	p_z += vect.p_z;
	return *this;
}

///	redéfinition de l'opérateur -=
/**	@param vect : Vecteur
	@return Vecteur (*this)
*/
Vecteur & Vecteur::operator -= ( const Vecteur & vect){
	p_x -= vect.p_x;
	p_y -= vect.p_y;
	p_z -= vect.p_z;
	return *this;
}

///	redéfinition de l'opérateur *=
/**	@param coef : double
	@return Vecteur (*this)*coef
*/
Vecteur & Vecteur::operator *= (double coef){    //redéfinition de l'opérateur *=
	p_x *= coef;
	p_y *= coef;
	p_z *= coef;
	return *this;
}

///	redéfinition de l'opérateur /=
/**	@param coef : double
	@return Vecteur (*this)/coef
*/
Vecteur & Vecteur::operator /= (double coef){   //redéfinition de l'opérateur /=
	p_x /= coef;
	p_y /= coef;
	p_z /= coef;
	return *this;
}

///	redéfinition de l'opérateur - pour un Vecteur
/**	@return Vecteur opposé de *this
*/
Vecteur Vecteur::operator - (){
	Vecteur v(-p_x,-p_y,-p_z);
	return v;
}

///renvoie la norme euclidienne du Vecteur
/**	@return taille du Vecteur
*/
double Vecteur::taille() const{                                           //calcul de la taille du vecteur
	double taille = sqrt(p_x*p_x + p_y*p_y + p_z*p_z);
	return taille;
}

///donne la norme du vecteur en coordonnées de Leci-Civita (u, v, 0.0)
/**	@return norme du vecteur en coordonnées de Leci-Civita (u, v, 0.0) (= u² + v²)
*/
double Vecteur::tailleLeviCivita() const{
	return p_x*p_x + p_y*p_y;
}

/// 	fonction qui calcul et renvoie la norme au carré de *this
/**	@return norme au carré du Vecteur *this
	très utile pour faire de l'économie de calcul en faisant des simulations qui font intervenir des forces
*/
double Vecteur::normeSQR() const{
	return p_x*p_x + p_y*p_y + p_z*p_z;
}

///	Retourne un Vecteur ayant pour composantes les valeurs absolues des composantes de this
/**	@return Vecteur positif
*/
Vecteur Vecteur::abs() const{
	Vecteur vect(fabs(p_x), fabs(p_y), fabs(p_z));
	return vect;
}

///renvoie un Vecteur qui a un x positif
/**	@return Vecteur x positif
*/
Vecteur Vecteur::absX() const{
	Vecteur vect(fabs(p_x), p_y, p_z);
	return vect;
}

///renvoie un Vecteur qui a un y positif
/**	@return Vecteur y positif
*/
Vecteur Vecteur::absY() const{
	Vecteur vect(p_x, fabs(p_y), p_z);
	return vect;
}

///renvoie un Vecteur qui a un z positif
/**	@return Vecteur z positif
*/
Vecteur Vecteur::absZ() const{
	Vecteur vect(p_x, p_y, fabs(p_z));
	return vect;
}

///Renvoie un  vecteur (x, 0, 0)
/** @return un Vecteur de la composante x du Vecteur
*/
Vecteur Vecteur::getVectX() const{
	Vecteur vect(p_x, 0, 0);
	return vect;
}

///Renvoie un  vecteur (0, y, 0)
/** @return un Vecteur de la composante y du Vecteur
*/
Vecteur Vecteur::getVectY() const{
	Vecteur vect(0, p_y, 0);
	return vect;
}

///Renvoie un  vecteur (0, 0, z)
/** @return un Vecteur de la composante z du Vecteur
*/
Vecteur Vecteur::getVectZ() const{
	Vecteur vect(0, 0, p_z);
	return vect;
}

///Renvoie un Vecteur (x, y, 0)
/** @return un Vecteur (x, y, 0)
*/
Vecteur Vecteur::getVectXY() const{return Vecteur(p_x, p_y, 0.0);}
///Renvoie un Vecteur (0, y, z)
/** @return un Vecteur (0, y, z)
*/
Vecteur Vecteur::getVectYZ() const{return Vecteur(0.0, p_y, p_z);}
///Renvoie un Vecteur (x, 0, z)
/** @return un Vecteur (x, 0, z)
*/
Vecteur Vecteur::getVectXZ() const{return Vecteur(p_x, 0.0, p_z);}

///Renvoie le vecteur unitaire de *this
/** @return le Vecteur unitaire correspondant au Vecteur courrant
*/
Vecteur Vecteur::getUnit() const{
	if(p_x == 0.0 && p_y == 0.0 && p_z == 0.0) return VECT3D_NULL;
	double tailleVec = this->taille();
	Vecteur unit(p_x/tailleVec, p_y/tailleVec, p_z/tailleVec);
	return unit;
}

///	fonction de calcul d'angle qui doit être plus rapide que le calcul avec des nombres complexes
/**	@param normale : Vecteur qui est la référence du calcul suivant
	@return angle entre le Vecteur normal et *this en radian
*/
double Vecteur::getAngle(const Vecteur& normale) const{
	if(p_x == 0.0 && p_y == 0.0 && p_z == 0.0) return 0.0; 
	return acos(((*this)*normale)/this->taille());
}

///fonction qui transforme un vecteur en coordonnées de Leci-Civita (u, v, 0) en un Vecteur normal (x, y, 0) en 2D en tout cas pour l'instant
/**	@return Vecteur en coordonnées normales (x, y, 0) car Leci-Civita est en 2D
*/
Vecteur Vecteur::leviCivitaToVecteur() const{
	Vecteur vect(p_x*p_x - p_y*p_y, 2.0*p_x*p_y, 0.0);
	return vect;
}

///	fonction qui initialise les composantes du Vecteur
/**	@param vecX : composante x
	@param vecY : composante y
	@param vecZ : composante z
*/
void Vecteur::setXYZ(double vecX, double vecY, double vecZ){
	p_x = vecX;
	p_y = vecY;
	p_z = vecZ;
}

///	initialise la composantes x du Vecteur
/**	@param x : conposante x du Vecteur
*/
void Vecteur::setx(const double x){p_x = x;}

///	initialise la composantes y du Vecteur
/**	@param y : conposante y du Vecteur
*/
void Vecteur::sety(const double y){p_y = y;}

///	initialise la composantes z du Vecteur
/**	@param z : conposante z du Vecteur
*/
void Vecteur::setz(const double z){p_z = z;}

///ajoute une valeur à la composante en x du Vecteur
/** @param dx : variation de x
*/
void Vecteur::addx(double dx){p_x += dx;}
///ajoute une valeur à la composante en y du Vecteur
/** @param dy : variation de y
*/
void Vecteur::addy(double dy){p_y += dy;}
///ajoute une valeur à la composante en z du Vecteur
/** @param dz : variation de z
*/
void Vecteur::addz(double dz){p_z += dz;}

///fonction qui initialise le vecteur avec des angles en coordonnées spériques
/**	@param angleTeta : angle des coordonnées polaires (si phi = M_PI/2)
	@param anglePhi : angle des coordonnées sphériques
*/
void Vecteur::setRotation(double angleTeta, double anglePhi){
	double longeur = this->taille();
	p_x = sin(anglePhi)*cos(angleTeta)*longeur;
	p_y = sin(anglePhi)*sin(angleTeta)*longeur;
	p_z = cos(anglePhi)*longeur;
}

///fonction qui initialise le vecteur avec des angles en coordonnées spériques
/**	@param rho : norme du Vecteur
 * 	@param angleTeta : angle des coordonnées polaires (si phi = M_PI/2)
	@param anglePhi : angle des coordonnées sphériques
*/
void Vecteur::setSpheric(double rho, double angleTeta, double anglePhi){
	p_x = sin(anglePhi)*cos(angleTeta)*rho;
	p_y = sin(anglePhi)*sin(angleTeta)*rho;
	p_z = cos(anglePhi)*rho;
}

///	fonction qui calcul la rotation du vecteur avec les variations de ses angles en coordonnées spériques
/**	@param axe : axe de rotation autour duquel on doit faire tourner le Vecteur
	@param angle : angle de rotation autour de l'axe de rotation
*/
void Vecteur::rotation(const Vecteur& axe, double angle){
	Vecteur tourner = cos(angle)*(*this) + (1 - cos(angle))*((*this)*axe)*axe + sin(angle)*(axe^(*this));
	(*this) = tourner;
}

///fonction qui retourne l'angle dans le plan du Vecteur
/** @return angle du Vecteur
*/
double Vecteur::getAngleTeta() const{
	double longeur = this->taille();
	double anglePhi = acosf(p_z/longeur);
	double cosTeta = p_x /  (longeur*sinf(anglePhi));
	double sinTeta = p_y / (longeur*sinf(anglePhi));
	double complex zTeta = longeur*(cosTeta + I*sinTeta);  //I est le i des nombres complexes
	double angleTeta = cargf(zTeta);
	return angleTeta;
}

///fonction qui calcul le réflexion du Vecteur *this par rapport à un vecteur normal
/**	le vecteur étant réfléchit dans la direction du Vecteur normal
	@param normal : Vecteur qui est la référence du calcul suivant
*/
void Vecteur::reflexion(const Vecteur& normal){                //vecteur normal à une surface plane
	Vecteur xF = (normal*(-(*this)))/(normal.taille()*normal.taille())*normal;
	*this = xF*2 + (*this);
}

///Définition de l'addition de deux Vecteur
/**	@param vect1 : Vecteur
	@param vect2 : Vecteur
	@return Vecteur addition de vect1 + vect2
*/
Vecteur operator + (const Vecteur & vect1, const Vecteur & vect2){
	Vecteur vect3(vect1.p_x + vect2.p_x, vect1.p_y + vect2.p_y, vect1.p_z + vect2.p_z);
	return vect3;
}

///	Définition de la soustraction de deux Vecteur, const à droite
/**	@param vect1 : Vecteur
	@param vect2 : Vecteur
	@return Vecteur soustraction de vect1 - vect2
*/
Vecteur operator - (const Vecteur & vect1, const Vecteur & vect2){
	Vecteur vect3(vect1.p_x - vect2.p_x, vect1.p_y - vect2.p_y, vect1.p_z - vect2.p_z);
	return vect3;
}

///	Définition du produit scalaire entre deux Vecteur
/**	@param vect1 : Vecteur
	@param vect2 : Vecteur
	@return double : résultat du produit scalaire
*/
double operator * (const Vecteur & vect1, const Vecteur & vect2){
	return vect1.p_x*vect2.p_x + vect1.p_y*vect2.p_y + vect1.p_z*vect2.p_z;
}

///mutiplication par un scalaire à droite
/**	Définition de la multiplication par un scalaire à droite
	@param vect : Vecteur
	@param scal : scalaire
	@return Vecteur produit de vect1*scal
*/
Vecteur operator * (const Vecteur & vect, double scal){
	Vecteur vect2(vect.p_x*scal, vect.p_y*scal, vect.p_z*scal);
	return vect2;
}

///mutiplication par un scalaire à gauche
/**	Définition de la multiplication par un scalaire à gauche
	@param scal : scalaire
	@param vect : Vecteur
	@return Vecteur produit de scal*vect1
*/
Vecteur operator * (double scal, const Vecteur & vect){
	Vecteur vect2(vect.p_x*scal, vect.p_y*scal, vect.p_z*scal);
	return vect2;
}

///	division par un scalaire
/**	@param vect : Vecteur
	@param scal : double
	@return Vecteur division de vect1/scal
*/
Vecteur operator / (const Vecteur & vect, double scal){
	Vecteur vect2(vect.p_x/scal, vect.p_y/scal, vect.p_z/scal);
	return vect2;
}

///	Définition  du produit vectoriel
/**	@param vect1 : Vecteur
	@param vect2 : Vecteur
	@return Vecteur produit vectoriel vect1^vect2
*/
Vecteur operator ^ (const Vecteur & vect1, const Vecteur & vect2){
	Vecteur vect(vect1.p_y*vect2.p_z - vect1.p_z*vect2.p_y, -vect1.p_x*vect2.p_z + vect1.p_z*vect2.p_x, vect1.p_x*vect2.p_y - vect1.p_y*vect2.p_x);
	return vect;
}

///	Définition de l'égalité entre deux Vecteur
/**	@param vect1 : Vecteur
	@param vect2 : Vecteur
	@return booleen true si toutes les composantes des deux Vecteur sont égalles, false sinon
*/
bool operator == (const Vecteur & vect1, const Vecteur & vect2){
	return (vect1.p_x == vect2.p_x && vect1.p_y == vect2.p_y && vect1.p_z == vect2.p_z);
}

///	Définition de l'inégalité entre deux Vecteur
/**	@param vect1 : Vecteur
	@param vect2 : Vecteur
	@return  booleen true si toutes les composantes des deux Vecteur sont inégalles, false sinon
*/
bool operator != (const Vecteur & vect1, const Vecteur & vect2){
	return !(vect1.p_x == vect2.p_x && vect1.p_y == vect2.p_y && vect1.p_z == vect2.p_z);
}

///	permet l'affichage d'un vecteur dans la console
/**	@param out : sortie standart de la console (cout)
	@param vecteur : Vecteur à afficher dans la console
*/
std::ostream & operator << (std::ostream & out, const Vecteur & vecteur){
	out << "(" << vecteur.p_x << ", " << vecteur.p_y << ", " << vecteur.p_z << ")";
	return out;
}

///permet l'initialisation d'un Vecteur dans la console où avec un fichier de config
/**	@param in : entrée standart de la console (cin)
	@param v : Vecteur à initialiser
*/
std::istream & operator >> (std::istream & in, Vecteur & v){
	in >> v.p_x >> v.p_y  >> v.p_z;
	return in;
}

///initialise un Vecteur à partir d'une chaine de type (x,y,z)
/** @param source : chaine d'initlisation
 *  @return true si la fonction à réussie, false sinon
*/
bool Vecteur::loadFromConfig(const char* source)
{
	return (sscanf(source,"(%lf,%lf,%lf)",&p_x,&p_y,&p_z) != 0);
}

///	fonction qui initialise un Vecteur
/**	@param vecX : composante x du Vecteur
	@param vecY : composante y du Vecteur
	@param vecZ : composante z du Vecteur
*/
void Vecteur::initialisationVecteur(double vecX, double vecY, double vecZ){
	p_x = vecX;
	p_y = vecY;
	p_z = vecZ;
}


