/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

/****************************************************************************
  History

$Log: point_matching.h,v $

****************************************************************************/
#ifndef _VCG_MATH_POINTMATCHING_H
#define _VCG_MATH_POINTMATCHING_H

#include <vcg/math/matrix33.h>
#include <vcg/math/quaternion.h>
#include <vcg/math/lin_algebra.h>
namespace vcg
{
template<class ScalarType> 
class PointMatching 
{
public:
  typedef Point3<ScalarType> Point3x;
  typedef Matrix33<ScalarType> Matrix33x;
  typedef Matrix44<ScalarType> Matrix44x;
  typedef Quaternion<ScalarType> Quaternionx;


/*
Compute a similarity matching (rigid + uniform scaling)
simply create a temporary point set with the correct scaling factor


*/ 
static bool ComputeSimilarityMatchMatrix(		Matrix44x &res,
                                    std::vector<Point3x> &Pfix,		// vertici corrispondenti su fix (rossi)
						std::vector<Point3x> &Pmov) 		// normali scelti su mov (verdi)
{
	Quaternionx qtmp;
	Point3x tr;
	
	std::vector<Point3x> Pnew(Pmov.size());
	
	ScalarType scalingFactor=0;
	
	for(size_t i=0;i<( Pmov.size()-1);++i)
	{
			scalingFactor += Distance(Pmov[i],Pmov[i+1])/ Distance(Pfix[i],Pfix[i+1]);
#ifdef _DEBUG
			printf("Scaling Factor is %f",scalingFactor/(i+1));
#endif
	}
	scalingFactor/=(Pmov.size()-1);

	for(size_t i=0;i<Pmov.size();++i)
		Pnew[i]=Pmov[i]/scalingFactor;		
		
	
	bool ret=ComputeRigidMatchMatrix(res,Pfix,Pnew,qtmp,tr);
	if(!ret) return false;
	Matrix44x scaleM; scaleM.SetDiagonal(1.0/scalingFactor);
	
	res = res * scaleM;
	return true;
}



static bool ComputeRigidMatchMatrix(		Matrix44x &res,
                                    std::vector<Point3x> &Pfix,		// vertici corrispondenti su fix (rossi)
						std::vector<Point3x> &Pmov) 		// normali scelti su mov (verdi)
{
	Quaternionx qtmp;
	Point3x tr;
	return ComputeRigidMatchMatrix(res,Pfix,Pmov,qtmp,tr);
}


/* 
Calcola la matrice di rototraslazione 
che porta i punti Pmov su Pfix

Basata sul paper 

Besl, McKay
A method for registration o f 3d Shapes 
IEEE TPAMI Vol 14, No 2 1992


	Esempio d'uso 
			const int np=1000;
			std::vector<Point3x> pfix(np),pmov(np);

			Matrix44x Rot,Trn,RotRes;
			Rot.Rotate(30,Point3x(1,0,1));
			Trn.Translate(0,0,100);
			Rot=Trn*Rot;
			
			for(int i=0;i<np;++i){
				pfix[i]=Point3x(-150+rand()%1000,-150+rand()%1000,0);
				pmov[i]=Rot.Apply(pfix[i]);
			}
			
			ComputeRigidMatchMatrix(RotRes,pfix,pmov);
      
			RotRes.Invert();
			assert( RotRes==Rot);  
			assert( RotRes.Apply(pmov[i]) == pfix[i] );
			
*/
static
bool ComputeWeightedRigidMatchMatrix(Matrix44x &res,
                  std::vector<Point3x> &Pfix,		
									std::vector<Point3x> &Pmov,
									std::vector<ScalarType> weights,
									Quaternionx &q,
									Point3x &tr
									) 	
{

  Matrix33x ccm; 
	Point3x bfix,bmov; // baricenter of src e trg
	ccm.WeightedCrossCovariance(weights,Pmov,Pfix,bmov,bfix);
	Matrix33x cyc; // the cyclic components of the cross covariance matrix.

	cyc=ccm - ccm.transpose();

	Matrix44x QQ;
	QQ.SetZero();
	Point3x D(cyc[1][2],cyc[2][0],cyc[0][1]);

  Matrix33x RM;
	RM.SetZero();
	RM[0][0]=-ccm.Trace();
  RM[1][1]=-ccm.Trace();
  RM[2][2]=-ccm.Trace();
  RM += ccm + ccm.transpose();

	QQ[0][0] = ccm.Trace();
	QQ[0][1] = D[0]; QQ[0][2] = D[1]; QQ[0][3] = D[2];
	QQ[1][0] = D[0]; QQ[2][0] = D[1];	QQ[3][0] = D[2];

	int i,j;
  for(i=0;i<3;i++)
		for(j=0;j<3;j++)
			QQ[i+1][j+1]=RM[i][j];

//  printf(" Quaternion Matrix\n");
//	print(QQ);
	Point4d d;
  Matrix44x v;
	int nrot;
	Jacobi(QQ,d,v,nrot);
//	printf("Done %i iterations\n %f %f %f %f\n",nrot,d[0],d[1],d[2],d[3]);
//	print(v);
	// Now search the maximum eigenvalue
	double maxv=0;
	int maxind=-1;
  for(i=0;i<4;i++)
		if(maxv<fabs(d[i])) {
			q=Quaternionx(v[0][i],v[1][i],v[2][i],v[3][i]);
			maxind=i;
			maxv=d[i];
		}
  // The corresponding eigenvector define the searched rotation,
		Matrix44x Rot;
	q.ToMatrix(Rot);
  // the translation (last row) is simply the difference between the transformed src barycenter and the trg baricenter
	tr= (bfix - Rot *bmov);
	//res[3][0]=tr[0];res[3][1]=tr[1];res[3][2]=tr[2];
	Matrix44x Trn;
	Trn.SetTranslate(tr);
		
	res=Trn*Rot;
	return true;
}

static
bool ComputeRigidMatchMatrix(Matrix44x &res,
 						std::vector<Point3x> &Pfix,		
						std::vector<Point3x> &Pmov,
							Quaternionx &q,
							Point3x &tr) 	
{

  Matrix33x ccm; 
	Point3x bfix,bmov; // baricenter of src e trg
	ccm.CrossCovariance(Pmov,Pfix,bmov,bfix);
	Matrix33x cyc; // the cyclic components of the cross covariance matrix.

	cyc=ccm-ccm.transpose();

	Matrix44x QQ;
	QQ.SetZero();
	Point3x D(cyc[1][2],cyc[2][0],cyc[0][1]);

  Matrix33x RM;
	RM.SetZero();
	RM[0][0]=-ccm.Trace();
  RM[1][1]=-ccm.Trace();
  RM[2][2]=-ccm.Trace();
  RM += ccm + ccm.transpose();

	QQ[0][0] = ccm.Trace();
	QQ[0][1] = D[0]; QQ[0][2] = D[1]; QQ[0][3] = D[2];
	QQ[1][0] = D[0]; QQ[2][0] = D[1];	QQ[3][0] = D[2];

	int i,j;
  for(i=0;i<3;i++)
		for(j=0;j<3;j++)
			QQ[i+1][j+1]=RM[i][j];

//  printf(" Quaternion Matrix\n");
//	print(QQ);
	Point4d d;
  Matrix44x v;
	int nrot;
	//QQ.Jacobi(d,v,nrot);
	Jacobi(QQ,d,v,nrot);
//	printf("Done %i iterations\n %f %f %f %f\n",nrot,d[0],d[1],d[2],d[3]);
//	print(v);
	// Now search the maximum eigenvalue
	double maxv=0;
	int maxind=-1;
  for(i=0;i<4;i++)
		if(maxv<fabs(d[i])) {
			q=Quaternionx(v[0][i],v[1][i],v[2][i],v[3][i]);
			maxind=i;
			maxv=d[i];
		}
  // The corresponding eigenvector define the searched rotation,
	Matrix44x Rot;
	q.ToMatrix(Rot);
  // the translation (last row) is simply the difference between the transformed src barycenter and the trg baricenter
	tr= (bfix - Rot*bmov);
	//res[3][0]=tr[0];res[3][1]=tr[1];res[3][2]=tr[2];
	Matrix44x Trn;
	Trn.SetTranslate(tr);
		
	res=Trn*Rot;
	return true;
}

// Dati due insiemi di punti e normali corrispondenti calcola la migliore trasformazione 
// che li fa corrispondere
static bool ComputeMatchMatrix(		Matrix44x &res,
 						std::vector<Point3x> &Ps,		// vertici corrispondenti su src (rossi)
						std::vector<Point3x> &Ns, 		// normali corrispondenti su src (rossi)
						std::vector<Point3x> &Pt)		// vertici scelti su trg (verdi) 
//						vector<Point3x> &Nt) 		// normali scelti su trg (verdi)
{
  assert(0);
  // Da qui in poi non compila che ha bisogno dei minimiquadrati
#if 0
  int sz=Ps.size();

	Matrix<double> A(sz,12);
	Vector<double> b(sz);
	Vector<double> x(12);

	//inizializzo il vettore per minimi quadrati
	// la matrice di trasf che calcolo con LeastSquares cerca avvicinare il piu' 
	// possibile le coppie di punti che trovo ho scelto  
	// Le coppie di punti sono gia' trasformate secondo la matrice <In> quindi come scelta iniziale 
	// per il metodo minimiquadrati scelgo l'identica (e.g. se ho allineato a mano perfettamente e 
	// le due mesh sono perfettamente uguali DEVE restituire l'identica)
	
	res.SetIdentity();
	int i,j,k;
	for(i=0; i<=2; ++i)
		for(j=0; j<=3; ++j)
			x[i*4+j] = res[i][j];


	//costruzione della matrice
	for(i=0;i<sz;++i)
	{
		for(j=0;j<3;++j)
			for(k=0;k<4;++k)
				if(k<3)
				{
					A[i][k+j*4] = Ns[i][j]*Pt[i][k];
				}
				else
				{
					A[i][k+j*4] = Ns[i][j];
				}
		b[i] = Ps[i]*Ns[i];
	}
	const int maxiter = 4096;
	int iter;
	LSquareGC(x,A,b,1e-16,maxiter,iter);
	
	TRACE("LSQ Solution");
	for(int ind=0; ind<12; ++ind) {
		if((ind%4)==0) TRACE("\n");
		TRACE("%8.5lf ", x[ind]); 
	} TRACE("\n");

	if(iter==maxiter)
	{
		TRACE("I minimi quadrati non convergono!!\n");
		return false;
	}
	else { TRACE("Convergenza in %d passi\n",iter); }

	//Devo riapplicare la matrice di trasformazione globale a 
	//trg inserendo il risultato nel vettore trgvert contenente 
	//copia dei suoi vertici
	Matrix44x tmp;
	for(i=0; i<=2; ++i)
		for(j=0; j<=3; ++j)
			res[j][i] = x[i*4+j];
	res[0][3] = 0.0;
	res[1][3] = 0.0;
	res[2][3] = 0.0;
	res[3][3] = 1.0;
	/*
	res.Transpose();
	Point3x scv,shv,rtv,trv;
	res.Decompose(scv,shv,rtv,trv);
	vcg::print(res);
	printf("Scale %f %f %f\n",scv[0],scv[1],scv[2]);
	printf("Shear %f %f %f\n",shv[0],shv[1],shv[2]);
	printf("Rotat %f %f %f\n",rtv[0],rtv[1],rtv[2]);
	printf("Trans %f %f %f\n",trv[0],trv[1],trv[2]);
	
	printf("----\n"); res.Decompose(scv,shv,rtv,trv);
	vcg::print(res);
	printf("Scale %f %f %f\n",scv[0],scv[1],scv[2]);
	printf("Shear %f %f %f\n",shv[0],shv[1],shv[2]);
	printf("Rotat %f %f %f\n",rtv[0],rtv[1],rtv[2]);
	printf("Trans %f %f %f\n",trv[0],trv[1],trv[2]);
	
	res.Transpose();
	*/
#endif
	return true;
}

/*
****** Questa parte per compilare ha bisogno di leastsquares e matrici generiche 
****** Da controllare meglio


static void CreatePairMatrix( Matrix<double> & A2, const Point3x & p, const Point3x & n, double d )
{	
	double t1 = p[0]*p[0];
	double t2 = n[0]*n[0];
	double t4 = t1*n[0];
	double t5 = t4*n[1];
	double t6 = t4*n[2];
	double t7 = p[0]*t2;
	double t8 = t7*p[1];
	double t9 = p[0]*n[0];
	double t10 = p[1]*n[1];
	double t11 = t9*t10;
	double t12 = p[1]*n[2];
	double t13 = t9*t12;
	double t14 = t7*p[2];
	double t15 = p[2]*n[1];
	double t16 = t9*t15;
	double t17 = p[2]*n[2];
	double t18 = t9*t17;
	double t19 = t9*n[1];
	double t20 = t9*n[2];
	double t21 = t9*d;
	double t22 = n[1]*n[1];
	double t25 = t1*n[1]*n[2];
	double t26 = p[0]*t22;
	double t27 = t26*p[1];
	double t28 = p[0]*n[1];
	double t29 = t28*t12;
	double t30 = t26*p[2];
	double t31 = t28*t17;
	double t32 = t28*n[2];
	double t33 = t28*d;
	double t34 = n[2]*n[2];

	double t36 = p[0]*t34;
	double t41 = p[1]*p[1]; double t43 = t41*n[0];
	double t46 = p[1]*t2;   double t48 = p[1]*n[0];
	double t49 = t48*t15;   double t50 = t48*t17;
	double t51 = t48*n[1];  double t52 = t48*n[2];
	double t57 = p[1]*t22;  double t59 = t10*t17;
	double t60 = t10*n[2];  double t63 = p[1]*t34;
	double t66 = p[2]*p[2]; double t68 = t66*n[0];
	double t72 = p[2]*n[0]; double t73 = t72*n[1];
	double t74 = t72*n[2];	double t80 = t15*n[2];
	
	A2[0][0] = t1*t2; A2[0][1] = t5;  A2[0][2] = t6;
	A2[0][3] = t8;    A2[0][4] = t11; A2[0][5] = t13;
	A2[0][6] = t14;   A2[0][7] = t16; A2[0][8] = t18;
	A2[0][9] = t7;   A2[0][10] = t19; A2[0][11] = t20;
	A2[0][12] = -t21;
	
	A2[1][1] = t1*t22; A2[1][2]  = t25; A2[1][3] = t11;
	A2[1][4] = t27;    A2[1][5]  = t29; A2[1][6] = t16;
	A2[1][7] = t30;    A2[1][8]  = t31; A2[1][9] = t19;
	A2[1][10] = t26;   A2[1][11] = t32; A2[1][12] = -t33;
	
	A2[2][2] = t1*t34; A2[2][3] = t13; A2[2][4] = t29;
	A2[2][5] = t36*p[1];    A2[2][6] = t18; A2[2][7] = t31;
	A2[2][8] = t36*p[2];    A2[2][9] = t20; A2[2][10] = t32;
	A2[2][11] = t36;   A2[2][12] = -p[0]*n[2]*d;
	
	A2[3][3] = t41*t2; A2[3][4] = t43*n[1]; A2[3][5] = t43*n[2];
	A2[3][6] = t46*p[2]; A2[3][7] = t49;  A2[3][8] = t50;
    A2[3][9] = t46; A2[3][10] = t51; A2[3][11] = t52;
    A2[3][12] = -t48*d;
	
	A2[4][4]  = t41*t22;  A2[4][5]  = t41*n[1]*n[2]; A2[4][6] = t49;
	A2[4][7]  = t57*p[2]; A2[4][8]  = t59; A2[4][9] = t51;
	A2[4][10] = t57;      A2[4][11] = t60; A2[4][12] = -t10*d;
	
	A2[5][5]  = t41*t34;  A2[5][6] = t50; A2[5][7] = t59;
	A2[5][8]  = t63*p[2]; A2[5][9] = t52; A2[5][10] = t60;
	A2[5][11] = t63;      A2[5][12] = -t12*d;
	
	A2[6][6]  = t66*t2;  A2[6][7] = t68*n[1]; A2[6][8] = t68*n[2];
	A2[6][9]  = p[2]*t2; A2[6][10] = t73;     A2[6][11] = t74;
	A2[6][12] = -t72*d;
	
	A2[7][7] = t66*t22;   A2[7][8] = t66*n[1]*n[2]; A2[7][9] = t73;
	A2[7][10] = p[2]*t22; A2[7][11] = t80;          A2[7][12] = -t15*d;
	
	A2[8][8] = t66*t34;   A2[8][9] = t74; A2[8][10] = t80;
	A2[8][11] = p[2]*t34; A2[8][12] = -t17*d;
	
	A2[9][9]   = t2;        A2[9][10]  = n[0]*n[1];
	A2[9][11]  = n[0]*n[2]; A2[9][12]  = -n[0]*d;
	
	A2[10][10] = t22; A2[10][11] = n[1]*n[2]; A2[10][12] = -n[1]*d;
	A2[11][11] = t34; A2[11][12] = -n[2]*d;
	A2[12][12] = d*d;
}

// Dati due insiemi di punti e normali corrispondenti calcola la migliore trasformazione 
// che li fa corrispondere
static bool ComputeMatchMatrix2(		Matrix44x &res,
 						std::vector<Point3x> &Ps,		// vertici corrispondenti su src (rossi)
						std::vector<Point3x> &Ns, 		// normali corrispondenti su src (rossi)
						std::vector<Point3x> &Pt)		// vertici scelti su trg (verdi) 
						//vector<Point3x> &Nt) 		// normali scelti su trg (verdi)
{
	const int N = 13;
	int i,j,k;

	Matrixd AT(N,N);
	Matrixd TT(N,N);
		// Azzeramento matrice totale (solo tri-superiore)
	for(i=0;i<N;++i)
		for(j=i;j<N;++j)
			AT[i][j] = 0;
		// Calcolo matrici locali e somma
	for(k=0;k<Ps.size();++k)
	{		
		CreatePairMatrix(TT,Pt[k],Ns[k],Ps[k]*Ns[k]);
		for(i=0;i<N;++i)
			for(j=i;j<N;++j)
				AT[i][j] += TT[i][j];
	}

	for(i=0;i<N;++i)
		for(j=0;j<i;++j)
				AT[i][j] = AT[j][i];

	std::vector<double> q;
	double error;
	affine_ls2(AT,q,error);
	//printf("error: %g \n",error);
	res[0][0] = q[0];
	res[0][1] = q[1];
	res[0][2] = q[2];
	res[0][3] = 0;
	res[1][0] = q[3];
	res[1][1] = q[4];
	res[1][2] = q[5];
	res[1][3] = 0;
	res[2][0] = q[6];
	res[2][1] = q[7];
	res[2][2] = q[8];
	res[2][3] = 0;
	res[3][0] = q[9];
	res[3][1] = q[10];
	res[3][2] = q[11];
	res[3][3] = q[12];

	return true;
}
*/
};
} // end namespace

#endif