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

#include "vecteur3f.h"
#include "matrice3f.h"

const Vecteur3f VECT3F_NULL(0,0,0);
const Vecteur3f GTerre3f(0.0, 10.0, 0.0);

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

using namespace std;

///	Fonction qui converti un Point en Vecteur3f
/**	@param point : Point que l'on veut convertir
	@return Vecteur3f calculé avec le Point
*/
Vecteur3f pointToVecteur3f(const Pointf & point){
	Vecteur3f vect(point.x, point.y, point.z);
	return vect;
}

////////////////////////////////////////////////////////////////////////
//                                                                    //
//                          Classe Vecteur3f                            //
//                                                                    //
////////////////////////////////////////////////////////////////////////

///	constructeur de Vecteur3f
/**	@param vecX : composante x du Vecteur3f
	@param vecY : composante y du Vecteur3f
	@param vecZ : composante z du Vecteur3f
*/
Vecteur3f::Vecteur3f(float vecX, float vecY, float vecZ){
	this->init(vecX, vecY, vecZ);
}

///initialise un Vecteur3f avec une chaîne
/** @param str : chaîne d'initialisation
*/
Vecteur3f::Vecteur3f(const char * str){
	this->init(0.0, 0.0, 0.0);
	this->initString(str);
}

///	Fonction qui permet d'initialiser un Vecteur3f avec un Point
/**	@param point : point qui initialisera le Vecteur3f
*/
Vecteur3f::Vecteur3f(const Pointf& point){
	this->init(point.x, point.y, point.z);
}

///	constructeur de copie de Vecteur3f
/**	@param vec : Vecteur3f
*/
Vecteur3f::Vecteur3f(const Vecteur3f & vec){
	this->init(vec.x, vec.y, vec.z);
}

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

/// 	fonction qui :sauvegarde les coordonnées du Vecteur3f (x,y,z) dans le fichier fp
/**	@param fp : fichier de savegarde du vecteur
*/
void Vecteur3f::save(FILE* fp){
	fprintf(fp, "(%f,", this->x);
	fprintf(fp, "%f,", this->y);
	fprintf(fp, "%f)", this->z);
}

/// 	fonction qui :sauvegarde les coordonnées du Vecteur3f (x,y) dans le fichier fp
/**	@param fp : fichier de savegarde du vecteur
*/
void Vecteur3f::save2d(FILE* fp){
	fprintf(fp, "(%f,", this->x);
	fprintf(fp, "%f)", this->y);
}

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

*/
void Vecteur3f::load(FILE* fp){
	float nb;
	fscanf(fp, "(%f,", &nb);
	this->x = nb;
	fscanf(fp, " %f,", &nb);
	this->y = nb;
	fscanf(fp, " %f)", &nb);
	this->z = nb;
}

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

///	redéfinition de l'opérateur =
/**	@param vect : Vecteur3f
	@return Vecteur3f (*this)
*/
Vecteur3f & Vecteur3f::operator = ( const Vecteur3f & vect){
	this->init(vect.x, vect.y, vect.z);
	return *this;
}

///	redéfinition de l'opérateur +=
/**	@param vect : Vecteur3f
	@return Vecteur3f (*this)
*/
Vecteur3f & Vecteur3f::operator += ( const Vecteur3f & vect){
	this->x += vect.x;
	this->y += vect.y;
	this->z += vect.z;
	return *this;
}

///	redéfinition de l'opérateur -=
/**	@param vect : Vecteur3f
	@return Vecteur3f (*this)
*/
Vecteur3f & Vecteur3f::operator -= ( const Vecteur3f & vect){
	this->x -= vect.x;
	this->y -= vect.y;
	this->z -= vect.z;
	return *this;
}

///	redéfinition de l'opérateur *=
/**	@param coef : float
	@return Vecteur3f (*this)*coef
*/
Vecteur3f & Vecteur3f::operator *= (float coef){    //redéfinition de l'opérateur *=
	this->x *= coef;
	this->y *= coef;
	this->z *= coef;
	return *this;
}

///	redéfinition de l'opérateur /=
/**	@param coef : float
	@return Vecteur3f (*this)/coef
*/
Vecteur3f & Vecteur3f::operator /= (float coef){   //redéfinition de l'opérateur /=
	this->x /= coef;
	this->y /= coef;
	this->z /= coef;
	return *this;
}

///	redéfinition de l'opérateur - pour un Vecteur3f
/**	@return Vecteur3f opposé de *this
*/
Vecteur3f Vecteur3f::operator - (){
	Vecteur3f v(-x,-y,-z);
	return v;
}

///renvoie la norme euclidienne du Vecteur3f
/**	@return taille du Vecteur3f
*/
float Vecteur3f::taille() const{                                           //calcul de la taille du vecteur
	float taille = sqrt(x*x + y*y + z*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²)
*/
float Vecteur3f::tailleLeviCivita() const{
	return x*x + y*y;
}

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

///	Retourne un Vecteur3f ayant pour composantes les valeurs absolues des composantes de this
/**	@return Vecteur3f positif
*/
Vecteur3f Vecteur3f::abs() const{
	Vecteur3f vect(fabs(this->x), fabs(this->y), fabs(this->z));
	return vect;
}

///renvoie un Vecteur3f qui a un x positif
/**	@return Vecteur3f x positif
*/
Vecteur3f Vecteur3f::absX() const{
	Vecteur3f vect(fabs(this->x), this->y, this->z);
	return vect;
}

///renvoie un Vecteur3f qui a un y positif
/**	@return Vecteur3f y positif
*/
Vecteur3f Vecteur3f::absY() const{
	Vecteur3f vect(this->x, fabs(this->y), this->z);
	return vect;
}

///renvoie un Vecteur3f qui a un z positif
/**	@return Vecteur3f z positif
*/
Vecteur3f Vecteur3f::absZ() const{
	Vecteur3f vect(this->x, this->y, fabs(this->z));
	return vect;
}

///Renvoie un  vecteur (x, 0, 0)
/** @return un Vecteur3f de la composante x du Vecteur3f
*/
Vecteur3f Vecteur3f::getVectX() const{
	Vecteur3f vect(this->x, 0, 0);
	return vect;
}

///Renvoie un  vecteur (0, y, 0)
/** @return un Vecteur3f de la composante y du Vecteur3f
*/
Vecteur3f Vecteur3f::getVectY() const{
	Vecteur3f vect(0, this->y, 0);
	return vect;
}

///Renvoie un  vecteur (0, 0, z)
/** @return un Vecteur3f de la composante z du Vecteur3f
*/
Vecteur3f Vecteur3f::getVectZ() const{
	Vecteur3f vect(0, 0, this->z);
	return vect;
}

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

///Renvoie le vecteur unitaire de *this
/** @return le Vecteur3f unitaire correspondant au Vecteur3f courrant
*/
Vecteur3f Vecteur3f::getUnit() const{
	if(x == 0.0 && y == 0.0 && z == 0.0) return VECT3F_NULL;
	float taille = this->taille();
	Vecteur3f unit(this->x/taille, this->y/taille, this->z/taille);
	return unit;
}

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

///	Fonction qui converti un Vecteur3f en Point
/**	@return Point correspondant u Vecteur3f
*/
Pointf Vecteur3f::toPoint() const{
	Pointf point;
	point.x = this->x;
	point.y = this->y;
	point.z = this->z;
	return point;
}

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

///	fonction qui initialise les composantes du Vecteur3f
/**	@param vecX : composante x
	@param vecY : composante y
	@param vecZ : composante z
*/
void Vecteur3f::setXYZ(float vecX, float vecY, float vecZ){
	this->x = vecX;
	this->y = vecY;
	this->z = vecZ;
}

///	fonction qui initialise les composantes du Vecteur3f avec un Point
/**	@param pt : Point servant à initialiser le Vecteur3f
*/
void Vecteur3f::setXYZ(const Pointf& pt){
	this->init(pt.x, pt.y, pt.z);
}

///	initialise la composantes x du Vecteur3f
/**	@param x : conposante x du Vecteur3f
*/
void Vecteur3f::setx(const float x){this->x = x;}

///	initialise la composantes y du Vecteur3f
/**	@param y : conposante y du Vecteur3f
*/
void Vecteur3f::sety(const float y){this->y = y;}

///	initialise la composantes z du Vecteur3f
/**	@param z : conposante z du Vecteur3f
*/
void Vecteur3f::setz(const float z){this->z = z;}

///ajoute une valeur à la composante en x du Vecteur3f
/** @param dx : variation de x
*/
void Vecteur3f::addx(float dx){this->x += dx;}
///ajoute une valeur à la composante en y du Vecteur3f
/** @param dy : variation de y
*/
void Vecteur3f::addy(float dy){this->y += dy;}
///ajoute une valeur à la composante en z du Vecteur3f
/** @param dz : variation de z
*/
void Vecteur3f::addz(float dz){this->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 Vecteur3f::setRotation(float angleTeta, float anglePhi){
	float longeur = this->taille();
	this->x = sin(anglePhi)*cos(angleTeta)*longeur;
	this->y = sin(anglePhi)*sin(angleTeta)*longeur;
	this->z = cos(anglePhi)*longeur;
}

///fonction qui initialise le vecteur avec des angles en coordonnées spériques
/**	@param rho : norme du Vecteur3f
 * 	@param angleTeta : angle des coordonnées polaires (si phi = M_PI/2)
	@param anglePhi : angle des coordonnées sphériques
*/
void Vecteur3f::setSpheric(float rho, float angleTeta, float anglePhi){
	this->x = sin(anglePhi)*cos(angleTeta)*rho;
	this->y = sin(anglePhi)*sin(angleTeta)*rho;
	this->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 Vecteur3f
	@param angle : angle de rotation autour de l'axe de rotation
*/
void Vecteur3f::rotation(const Vecteur3f& axe, float angle){
	Vecteur3f tourner = cos(angle)*(*this) + (1 - cos(angle))*((*this)*axe)*axe + sin(angle)*(axe^(*this));
	(*this) = tourner;
}

///fonction qui calcul la rotation du vecteur avec les variations de ses angles en coordonnées spériques
/**	@param matriceRotation : matricede rotation
 * 	@param centreRotation : position du centre de rotation du Vecteur3f
*/
void Vecteur3f::rotation(const Matrice3f& matriceRotation, const Vecteur3f& centreRotation){
	Vecteur3f rot(*this);
	rot = matriceRotation*(rot - centreRotation) + centreRotation;
	(*this) = rot;
}

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

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

///Définition de l'addition de deux Vecteur3f
/**	@param vect1 : Vecteur3f
	@param vect2 : Vecteur3f
	@return Vecteur3f addition de vect1 + vect2
*/
Vecteur3f operator + (const Vecteur3f & vect1, const Vecteur3f & vect2){
	Vecteur3f vect3(vect1.x + vect2.x, vect1.y + vect2.y, vect1.z + vect2.z);
	return vect3;
}

///	Définition de la soustraction de deux Vecteur3f, const à droite
/**	@param vect1 : Vecteur3f
	@param vect2 : Vecteur3f
	@return Vecteur3f soustraction de vect1 - vect2
*/
Vecteur3f operator - (const Vecteur3f & vect1, const Vecteur3f & vect2){
	Vecteur3f vect3(vect1.x - vect2.x, vect1.y - vect2.y, vect1.z - vect2.z);
	return vect3;
}

///	Définition du produit scalaire entre deux Vecteur3f
/**	@param vect1 : Vecteur3f
	@param vect2 : Vecteur3f
	@return float : résultat du produit scalaire
*/
float operator * (const Vecteur3f & vect1, const Vecteur3f & vect2){
	return vect1.x*vect2.x + vect1.y*vect2.y + vect1.z*vect2.z;
}

///mutiplication par un scalaire à droite
/**	Définition de la multiplication par un scalaire à droite
	@param vect : Vecteur3f
	@param scal : scalaire
	@return Vecteur3f produit de vect1*scal
*/
Vecteur3f operator * (const Vecteur3f & vect, float scal){
	Vecteur3f vect2(vect.x*scal, vect.y*scal, vect.z*scal);
	return vect2;
}

///mutiplication par un scalaire à gauche
/**	Définition de la multiplication par un scalaire à gauche
	@param scal : scalaire
	@param vect : Vecteur3f
	@return Vecteur3f produit de scal*vect1
*/
Vecteur3f operator * (float scal, const Vecteur3f & vect){
	Vecteur3f vect2(vect.x*scal, vect.y*scal, vect.z*scal);
	return vect2;
}

///	division par un scalaire
/**	@param vect : Vecteur3f
	@param scal : float
	@return Vecteur3f division de vect1/scal
*/
Vecteur3f operator / (const Vecteur3f & vect, float scal){
	Vecteur3f vect2(vect.x/scal, vect.y/scal, vect.z/scal);
	return vect2;
}

///	Définition  du produit vectoriel
/**	@param vect1 : Vecteur3f
	@param vect2 : Vecteur3f
	@return Vecteur3f produit vectoriel vect1^vect2
*/
Vecteur3f operator ^ (const Vecteur3f & vect1, const Vecteur3f & vect2){
	Vecteur3f vect(vect1.y*vect2.z - vect1.z*vect2.y, -vect1.x*vect2.z + vect1.z*vect2.x, vect1.x*vect2.y - vect1.y*vect2.x);
	return vect;
}

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

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

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

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

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

///	fonction qui initialise un Vecteur3f
/**	@param vecX : composante x du Vecteur3f
	@param vecY : composante y du Vecteur3f
	@param vecZ : composante z du Vecteur3f
*/
void Vecteur3f::init(float vecX, float vecY, float vecZ){
	this->x = vecX;
	this->y = vecY;
	this->z = vecZ;
}

///fonction qui indique comment initilialiser un Vecteur3f avec un fichier de config
/**	@param source : chaine d'initlisation
	@param v : Vecteur3f à initialiser
*/
void pConfig_load_obj(const char * source, Vecteur3f & v)
{
	v.loadFromConfig(source);
}

///fonction qui permet d'initialiser une PListeVecteur3f
/**	@param fileName : nom du fichier à lire
 * 	@param listeVecteur3f : PListeVecteur3f à initialiser
 * 	@return true si la fonction à réussie, false sinon
*/
bool loadPListeVecteur3f(const char * fileName, PListeVecteur3f & listeVecteur3f){
	if(fileName == NULL) return false;
	FILE* fp = fopen(fileName, "r");
	bool find(false);
	if(fp != NULL){
		char buffer[1024];
		Vecteur3f vect;
		while(!feof(fp)){
			if (fgets(buffer,sizeof(buffer) - 1, fp)){
				if(vect.initString(buffer)){
					listeVecteur3f.push_back(vect);
					find = true;
				}
			}
		}
		fclose(fp);
	}
	return find;
}

///fonction qui permet de sauvergarder une PListeVecteur3f
/**	@param fileName : nom du fichier à lire
 * 	@param listeVecteur3f : PListeVecteur3f à sauvergarder
*/
void savePlisteVecteur3f(const char * fileName, const PListeVecteur3f & listeVecteur3f){
	if(fileName == NULL) return;
	FILE* fp = fopen(fileName, "w");
	if(fp != NULL){
		for(PListeVecteur3f::const_iterator it = listeVecteur3f.begin(); it != listeVecteur3f.end(); it++){
			fprintf(fp, "(%f,%f,%f)\n", it->getx(), it->gety(), it->getz());
		}
		fclose(fp);
	}
}

///fonction qui permet de sauvergarder une PListeVecteur3f sur x
/**	@param fileName : nom du fichier à lire
 * 	@param listeVecteur3f : PListeVecteur3f à sauvergarder
*/
void savePlisteVecteur3fX(const char * fileName, const PListeVecteur3f & listeVecteur3f){
	if(fileName == NULL) return;
	FILE* fp = fopen(fileName, "w");
	if(fp != NULL){
		for(PListeVecteur3f::const_iterator it = listeVecteur3f.begin(); it != listeVecteur3f.end(); it++){
			fprintf(fp, "%f\n", it->getx());
		}
		fclose(fp);
	}
}

///fonction qui permet de sauvergarder une PListeVecteur3f sur y
/**	@param fileName : nom du fichier à lire
 * 	@param listeVecteur3f : PListeVecteur3f à sauvergarder
*/
void savePlisteVecteur3fY(const char * fileName, const PListeVecteur3f & listeVecteur3f){
	if(fileName == NULL) return;
	FILE* fp = fopen(fileName, "w");
	if(fp != NULL){
		for(PListeVecteur3f::const_iterator it = listeVecteur3f.begin(); it != listeVecteur3f.end(); it++){
			fprintf(fp, "%f\n", it->gety());
		}
		fclose(fp);
	}
}

///fonction qui permet de sauvergarder une PListeVecteur3f sur z
/**	@param fileName : nom du fichier à lire
 * 	@param listeVecteur3f : PListeVecteur3f à sauvergarder
*/
void savePlisteVecteur3fZ(const char * fileName, const PListeVecteur3f & listeVecteur3f){
	if(fileName == NULL) return;
	FILE* fp = fopen(fileName, "w");
	if(fp != NULL){
		for(PListeVecteur3f::const_iterator it = listeVecteur3f.begin(); it != listeVecteur3f.end(); it++){
			fprintf(fp, "%f\n", it->getz());
		}
		fclose(fp);
	}
}

