/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#ifndef _Affine3DTransform_hpp
#define _Affine3DTransform_hpp
 
/*!
  \file   Affine3DTransform.hpp
  \brief  File with foundamental geometric classes 

  <long description> We chose to put all the classes containing some kind of basis concept used throughout the library (and the use of the library) in this file.
  Examples of classes: Size3D, Vect3D, CAffineTransform, Parrallellogram and so on.

  See AUTHORS and COPYING files for more information
  \date   2001-01-23
*/

#include<ImLib3D/CppTools.hpp>
#include<ImLib3D/Vect3D.hpp>
#include<ImLib3D/ImLib3DFile.hpp>

#define WANT_STREAM
#define WANT_MATH  
#include<newmat/newmatap.h>
#include<newmat/newmatio.h>
#include<newmat/newmat.h>
//#include<ImLib3D/quat.hpp>
using namespace NEWMAT;


inline ColumnVector ToColumnVector(const Vect3Df &C0)     {ColumnVector C(3);C(1)=C0.x;C(2)=C0.y;C(3)=C0.z;return C;}
inline Vect3Df      ToVect3Df     (const ColumnVector &C0){return Vect3Df(C0(1),C0(2),C0(3));}

//! An 3 dimensional affine transformation : X'=MX+T 
/*! An affine transforma is composed of a  matrix M a translation vector T: X'=MX+T  */
class Affine3DTransform : public Streamable
{
	friend void Affine3DTransform_Test();
	friend Affine3DTransform operator*(const Affine3DTransform& a1,const Affine3DTransform& a2);
private:
	Matrix M;
	ColumnVector T;
public:
	//! accesors
	Matrix GetMatrix(){return M;}
	ColumnVector Translation(){return T;}
	double operator()(int i,int j){return M(i+1,j+1);}
	double operator()(int i){return T(i+1);}
	void Set(const Matrix &_M,const ColumnVector &_T){M=_M;T=_T;}

	//! aplies transform on vector
	Vect3Df operator()(const Vect3Df &X0) const
	{
		return ToVect3Df(M*ToColumnVector(X0)+T);
	}
    //! inverse: X=M'(X'-T)=M'X'-M'T
	void i();

	virtual ~Affine3DTransform(){;}
	Affine3DTransform():
		M(3,3),T(3)
	{
		Reset();
	}

	Affine3DTransform(const Vect3Df &T0):
		M(3,3),T(3)
	{
		Reset();
		SetTranslation(T0);
	}
	Affine3DTransform(float theta,Vect3Df N,Vect3Df C0=Vect3Df(0,0,0)):
		M(3,3),T(3)
	{
		Reset();
		SetRotation(theta,N,C0);
	}
	//! reads transform from file
	Affine3DTransform(const string &fname);
	virtual void Read( ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL)      ;
	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const;
	void Show()
	{
		cout << "M:" << endl;
		cout << M << endl;
		cout << "T:" << endl;
		cout << T << endl;
	}
    //! Resets transformation: M is set to identity, and T to 0.
	void Reset()
	{
		M << 1 << 0 << 0
		  << 0 << 1 << 0
		  << 0 << 0 << 1;
		T(1) = 0;
		T(2) = 0;
		T(3) = 0;
	}
    //@{ \name Convenience Functions 
    /** Set rotation around line passing through C and parallel to X Axis,
		The rotation will be set absolutely, to allow more sophisticated rotation use concatenate 
		several Affine3DTransforms using the *= operator as in:
		Affine3DTransform Ax;
		Ax.SetRotationX(phiX);
		Affine3DTransform Ay;
		Ay.SetRotationX(phiY);
		Ax*=Ay;
    */
  	void SetRotationX(float theta,Vect3Df C0=Vect3Df(0,0,0));
  	void SetRotationY(float theta,Vect3Df C0=Vect3Df(0,0,0));
  	void SetRotationZ(float theta,Vect3Df C0=Vect3Df(0,0,0));
  	void SetRotation (float theta,Vect3Df N,Vect3Df C0=Vect3Df(0,0,0));
  	void SetTranslation(Vect3Df T0){T=ToColumnVector(T0);}
  	void SetScale(float sx,float sy,float sz){Reset();M(1,1)=sx;M(2,2)=sy;M(3,3)=sz;}
  	void SetScale(Vect3Df S){SetScale(S.x, S.y, S.z);}
    //@}
	//! composes transform with other Affine3DTransform
	Affine3DTransform operator*=(const Affine3DTransform& other);
	
};
extern Affine3DTransform operator*(const Affine3DTransform& a1,const Affine3DTransform& a2);



#endif //_Affine3DTransform_hpp
