/****************************************************************************
* 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: not supported by cvs2svn $
Revision 1.9  2006/02/06 10:45:47  cignoni
Added missing typenames

Revision 1.7  2006/01/24 13:23:22  pietroni
used template types instead of point3f and float inside function calls

Revision 1.6  2005/12/06 17:55:16  pietroni
1 bug corrected

Revision 1.5  2005/12/02 16:24:56  pietroni
corrected 1 bug in Cross Prod Gradient

Revision 1.4  2005/11/23 16:24:44  pietroni
corrected CrossProdGradient( )

Revision 1.3  2005/07/11 13:12:05  cignoni
small gcc-related compiling issues (typenames,ending cr, initialization order)

Revision 1.2  2005/03/16 16:14:12  spinelli
aggiunta funzione PasoDobleSmooth e relative:

- FitMesh
- FaceErrorGrad
- CrossProdGradient
- TriAreaGradient
- NormalSmooth

e le classi:

- PDVertInfo
- PDFaceInfo

necessarie per utilizzare SimpleTempData

Revision 1.1  2004/12/11 14:53:19  ganovelli
first partial porting: compiled gcc,intel and msvc


****************************************************************************/


#ifndef __VCGLIB__SMOOTH
#define __VCGLIB__SMOOTH

#include <vcg/space/point3.h>
#include <vcg/space/line3.h>
#include <vcg/container/simple_temporary_data.h>
#include <vcg/complex/trimesh/update/normal.h>

namespace vcg
{

template<class FLT> 
class ScaleLaplacianInfo 
{
public:
	Point3<FLT> PntSum;
	FLT LenSum;
};

// Scale dependent laplacian smoothing [fujimori 95]
// Nuova versione, l'idea e'quella di usare anche gli angoli delle facce per pesare lo spostamento.
// 
// in pratica si sposta solo lungo la componente che e' parallela alla normale al vertice 
// (che si suppone esserci!!)
 

// Non ha bisogno della topologia
// Non fa assunzioni sull'ordinamento delle facce, ma vuole che i border flag ci siano!
//
// 

template<class MESH_TYPE>
void ScaleLaplacianSmooth(MESH_TYPE &m, int step, typename  MESH_TYPE::ScalarType delta)
{
	SimpleTempData<typename MESH_TYPE::VertContainer, ScaleLaplacianInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
	ScaleLaplacianInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.PntSum=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.LenSum=0;
	TD.Start(lpz);
	typename  MESH_TYPE::FaceIterator fi;
	for(int i=0;i<step;++i)
	{
		typename  MESH_TYPE::VertexIterator vi;
		for(vi=m.vert.begin();vi!=m.vert.end();++vi)
			 TD[*vi]=lpz;
		typename MESH_TYPE::ScalarType a[3];
		for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD())
		{
			typename  MESH_TYPE::CoordType  mp=((*fi).V(0)->P() + (*fi).V(1)->P() + (*fi).V(2)->P())/3.0;
			typename  MESH_TYPE::CoordType  e0=((*fi).V(0)->P() - (*fi).V(1)->P()).Normalize(); 
			typename  MESH_TYPE::CoordType  e1=((*fi).V(1)->P() - (*fi).V(2)->P()).Normalize();
			typename  MESH_TYPE::CoordType  e2=((*fi).V(2)->P() - (*fi).V(0)->P()).Normalize();

			a[0]=AngleN(-e0,e2);
			a[1]=AngleN(-e1,e0);
			a[2]=AngleN(-e2,e1);
			//assert(fabs(M_PI -a[0] -a[1] -a[2])<0.0000001);

			for(int j=0;j<3;++j){
						typename  MESH_TYPE::CoordType dir= (mp-(*fi).V(j)->P()).Normalize();
						TD[(*fi).V(j)].PntSum+=dir*a[j];
						TD[(*fi).V(j)].LenSum+=a[j];
			}
		}		
		for(vi=m.vert.begin();vi!=m.vert.end();++vi)
				if(!(*vi).IsD() && TD[*vi].LenSum>0 )
				 (*vi).P() = (*vi).P() +  (TD[*vi].PntSum/TD[*vi].LenSum ) * delta;
				
	}		 			
	TD.Stop();
};

// Scale dependent laplacian smoothing [fujimori 95]
// Non ha bisogno della topologia
// Non fa assunzioni sull'ordinamento delle facce, ma vuole che i border flag ci siano!
//
// 
template<class MESH_TYPE>
void ScaleLaplacianSmoothOld(MESH_TYPE &m, int step, typename  MESH_TYPE::ScalarType delta)
{
	SimpleTempData<typename MESH_TYPE::VertContainer, ScaleLaplacianInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
	ScaleLaplacianInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.PntSum=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.LenSum=0;
	TD.Start(lpz);
	typename  MESH_TYPE::FaceIterator fi;
	for(int i=0;i<step;++i)
	{
			typename  MESH_TYPE::VertexIterator vi;
			for(vi=m.vert.begin();vi!=m.vert.end();++vi)
				 TD[*vi]=lpz;

			for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD())
				for(int j=0;j<3;++j)
					if(!(*fi).IsB(j)) {
						typename  MESH_TYPE::CoordType edge= (*fi).V1(j)->P() -(*fi).V(j)->P();
						typename MESH_TYPE::ScalarType len=Norm(edge);
						edge/=len;
						TD[(*fi).V(j)].PntSum+=edge;
						TD[(*fi).V1(j)].PntSum-=edge;
						TD[(*fi).V(j)].LenSum+=len;
						TD[(*fi).V1(j)].LenSum+=len;
					}
				
			for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD())
				for(int j=0;j<3;++j)
					// se l'edge j e' di bordo si riazzera tutto e si riparte
					if((*fi).IsB(j)) {
							TD[(*fi).V(j)].PntSum=typename  MESH_TYPE::CoordType(0,0,0);
							TD[(*fi).V1(j)].PntSum=typename  MESH_TYPE::CoordType(0,0,0);
							TD[(*fi).V(j)].LenSum=0;
							TD[(*fi).V1(j)].LenSum=0;
					}
				

			for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
					for(int j=0;j<3;++j)
						if((*fi).IsB(j)) 
						{ 
							typename  MESH_TYPE::CoordType edge= (*fi).V1(j)->P() -(*fi).V(j)->P();
							typename MESH_TYPE::ScalarType len=Norm(edge);
							edge/=len;
							TD[(*fi).V(j)].PntSum+=edge;
							TD[(*fi).V1(j)].PntSum-=edge;
							TD[(*fi).V(j)].LenSum+=len;
							TD[(*fi).V1(j)].LenSum+=len;
						}

			for(vi=m.vert.begin();vi!=m.vert.end();++vi)
				if(!(*vi).IsD() && TD[*vi].LenSum>0 )
				 (*vi).P() = (*vi).P() + (TD[*vi].PntSum/TD[*vi].LenSum)*delta;
	}		 			
	TD.Stop();
};


template<class FLT> 
class LaplacianInfo 
{
public:
	Point3<FLT> sum;
	FLT cnt;
};

template<class MESH_TYPE>
void LaplacianSmooth(MESH_TYPE &m, int step,bool SmoothSelected=false)
{
	SimpleTempData<typename MESH_TYPE::VertContainer,LaplacianInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
  LaplacianInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.sum=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.cnt=0;
	TD.Start(lpz);
	for(int i=0;i<step;++i)
	{
		typename  MESH_TYPE::VertexIterator vi;
		for(vi=m.vert.begin();vi!=m.vert.end();++vi)
			 TD[*vi]=lpz;

		typename  MESH_TYPE::FaceIterator fi;
		for(fi=m.face.begin();fi!=m.face.end();++fi)
			if(!(*fi).IsD()) 
				for(int j=0;j<3;++j)
					if(!(*fi).IsB(j)) 
						{
							TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
							TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
							++TD[(*fi).V(j)].cnt;
							++TD[(*fi).V1(j)].cnt;
					}

			// si azzaera i dati per i vertici di bordo
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j))
							{
								TD[(*fi).V(j)]=lpz;
								TD[(*fi).V1(j)]=lpz;
							}

			// se l'edge j e' di bordo si deve mediare solo con gli adiacenti
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j)) 
							{
								TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
								TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
								++TD[(*fi).V(j)].cnt;
								++TD[(*fi).V1(j)].cnt;
						}
	
	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		if(!(*vi).IsD() && TD[*vi].cnt>0 )
			if(!SmoothSelected || (*vi).IsS())
					(*vi).P()=TD[*vi].sum/TD[*vi].cnt;
	}
	 	
	TD.Stop();
};


template<class FLT> 
class HCSmoothInfo 
{
public:
	Point3<FLT> dif;
	Point3<FLT> sum;
	int cnt;
};
template<class MESH_TYPE>
void HCSmooth(MESH_TYPE &m, int step)
{
	typename MESH_TYPE::ScalarType beta=0.5;
	SimpleTempData<typename MESH_TYPE::VertContainer,HCSmoothInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
  HCSmoothInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.sum=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.dif=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.cnt=0;
	TD.Start(lpz);
	// First Loop compute the laplacian
	typename  MESH_TYPE::FaceIterator fi;
	for(fi=m.face.begin();fi!=m.face.end();++fi)
		{
			for(int j=0;j<3;++j)
			{
				TD[(*fi).V(j)].sum+=(*fi).V1(j)->P();
				TD[(*fi).V1(j)].sum+=(*fi).V(j)->P();
				++TD[(*fi).V(j)].cnt;
				++TD[(*fi).V1(j)].cnt;
				// se l'edge j e' di bordo si deve sommare due volte
				if((*fi).IsB(j)) 
				{ 
					TD[(*fi).V(j)].sum+=(*fi).V1(j)->P(); 
					TD[(*fi).V1(j)].sum+=(*fi).V(j)->P(); 
					++TD[(*fi).V(j)].cnt;
					++TD[(*fi).V1(j)].cnt;
				}
			}
		}
	typename  MESH_TYPE::VertexIterator vi;
	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		 TD[*vi].sum/=(float)TD[*vi].cnt;
	
	// Second Loop compute average difference
	for(fi=m.face.begin();fi!=m.face.end();++fi)
		{
			for(int j=0;j<3;++j)
			{
				TD[(*fi).V(j)].dif +=TD[(*fi).V1(j)].sum-(*fi).V1(j)->P();
				TD[(*fi).V1(j)].dif+=TD[(*fi).V(j)].sum-(*fi).V(j)->P();
				// se l'edge j e' di bordo si deve sommare due volte
				if((*fi).IsB(j)) 
				{ 
					TD[(*fi).V(j)].dif +=TD[(*fi).V1(j)].sum-(*fi).V1(j)->P();
					TD[(*fi).V1(j)].dif+=TD[(*fi).V(j)].sum-(*fi).V(j)->P();
				}
			}
		}

	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		{
		 TD[*vi].dif/=(float)TD[*vi].cnt;
		 (*vi).P()=TD[*vi].sum -((TD[*vi].sum-(*vi).P()*beta) + TD[*vi].dif)*(1.f-beta);
		}
		 	
	TD.Stop();
};


template<class FLT> 
class QualitySmoothInfo 
{
public:
	FLT sum;
	int cnt;
};

template<class MESH_TYPE>
void LaplacianSmoothQuality(MESH_TYPE &m, int step,bool SmoothSelected=false)
{ 
	SimpleTempData<typename MESH_TYPE::VertContainer,QualitySmoothInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
  QualitySmoothInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.sum=0;
	lpz.cnt=0;
	TD.Start(lpz);
	for(int i=0;i<step;++i)
	{
		typename  MESH_TYPE::VertexIterator vi;
		for(vi=m.vert.begin();vi!=m.vert.end();++vi)
			 TD[*vi]=lpz;

		typename  MESH_TYPE::FaceIterator fi;
		for(fi=m.face.begin();fi!=m.face.end();++fi)
			if(!(*fi).IsD()) 
				for(int j=0;j<3;++j)
					if(!(*fi).IsB(j)) 
						{
							TD[(*fi).V(j)].sum+=(*fi).V1(j)->Q();
							TD[(*fi).V1(j)].sum+=(*fi).V(j)->Q();
							++TD[(*fi).V(j)].cnt;
							++TD[(*fi).V1(j)].cnt;
					}

			// si azzaera i dati per i vertici di bordo
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j))
							{
								TD[(*fi).V(j)]=lpz;
								TD[(*fi).V1(j)]=lpz;
							}

			// se l'edge j e' di bordo si deve mediare solo con gli adiacenti
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j)) 
							{
								TD[(*fi).V(j)].sum+=(*fi).V1(j)->Q();
								TD[(*fi).V1(j)].sum+=(*fi).V(j)->Q();
								++TD[(*fi).V(j)].cnt;
								++TD[(*fi).V1(j)].cnt;
						}

	//typename  MESH_TYPE::VertexIterator vi;
	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		if(!(*vi).IsD() && TD[*vi].cnt>0 )
			if(!SmoothSelected || (*vi).IsS())
					(*vi).Q()=TD[*vi].sum/TD[*vi].cnt;
	}
		 	
	TD.Stop();
};
template<class MESH_TYPE>
void LaplacianSmoothNormals(MESH_TYPE &m, int step,bool SmoothSelected=false)
{
	SimpleTempData<typename MESH_TYPE::VertContainer,LaplacianInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
  LaplacianInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.sum=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.cnt=0;
	TD.Start(lpz);
	for(int i=0;i<step;++i)
	{
		typename  MESH_TYPE::VertexIterator vi;
		for(vi=m.vert.begin();vi!=m.vert.end();++vi)
			 TD[*vi]=lpz;

		typename  MESH_TYPE::FaceIterator fi;
		for(fi=m.face.begin();fi!=m.face.end();++fi)
			if(!(*fi).IsD()) 
				for(int j=0;j<3;++j)
					if(!(*fi).IsB(j)) 
						{
							TD[(*fi).V(j)].sum+=(*fi).V1(j)->N();
							TD[(*fi).V1(j)].sum+=(*fi).V(j)->N();
							++TD[(*fi).V(j)].cnt;
							++TD[(*fi).V1(j)].cnt;
					}

			// si azzaera i dati per i vertici di bordo
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j))
							{
								TD[(*fi).V(j)]=lpz;
								TD[(*fi).V1(j)]=lpz;
							}

			// se l'edge j e' di bordo si deve mediare solo con gli adiacenti
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j)) 
							{
								TD[(*fi).V(j)].sum+=(*fi).V1(j)->N();
								TD[(*fi).V1(j)].sum+=(*fi).V(j)->N();
								++TD[(*fi).V(j)].cnt;
								++TD[(*fi).V1(j)].cnt;
						}

	//typename  MESH_TYPE::VertexIterator vi;
	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		if(!(*vi).IsD() && TD[*vi].cnt>0 )
			if(!SmoothSelected || (*vi).IsS())
					(*vi).N()=TD[*vi].sum/TD[*vi].cnt;
	}
		 	
	TD.Stop();
};

// Smooth solo lungo la direzione di vista
	// alpha e' compreso fra 0(no smoot) e 1 (tutto smoot)
  // Nota che se smootare il bordo puo far fare bandierine.
template<class MESH_TYPE>
void DepthSmooth(MESH_TYPE &m,
				 const typename  MESH_TYPE::CoordType & viewpoint,
				 const typename  MESH_TYPE::ScalarType alpha,
				 int step, bool SmoothBorder=false )
{
	typedef typename  MESH_TYPE::CoordType v_type;
	typedef typename MESH_TYPE::ScalarType    s_type;


	//const typename  MESH_TYPE::CoordType viewpoint;
	//const typename MESH_TYPE::ScalarType alpha;

	SimpleTempData<typename MESH_TYPE::VertContainer,LaplacianInfo<typename MESH_TYPE::ScalarType> > TD(m.vert);
	LaplacianInfo<typename MESH_TYPE::ScalarType> lpz;
	lpz.sum=typename  MESH_TYPE::CoordType(0,0,0);
	lpz.cnt=0;
	TD.Start(lpz);
	for(int i=0;i<step;++i)
	{
		typename  MESH_TYPE::VertexIterator vi;
		for(vi=m.vert.begin();vi!=m.vert.end();++vi)
			 TD[*vi]=lpz;

		typename  MESH_TYPE::FaceIterator fi;
		for(fi=m.face.begin();fi!=m.face.end();++fi)
			if(!(*fi).IsD()) 
				for(int j=0;j<3;++j)
					if(!(*fi).IsB(j)) 
						{
							TD[(*fi).V(j)].sum+=(*fi).V1(j)->Supervisor_P();
							TD[(*fi).V1(j)].sum+=(*fi).V(j)->Supervisor_P();
							++TD[(*fi).V(j)].cnt;
							++TD[(*fi).V1(j)].cnt;
					}

			// si azzaera i dati per i vertici di bordo
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j))
							{
								TD[(*fi).V(j)]=lpz;
								TD[(*fi).V1(j)]=lpz;
							}

			// se l'edge j e' di bordo si deve mediare solo con gli adiacenti
     if(SmoothBorder)
			for(fi=m.face.begin();fi!=m.face.end();++fi)
				if(!(*fi).IsD()) 
					for(int j=0;j<3;++j)
						if((*fi).IsB(j)) 
							{
								TD[(*fi).V(j)].sum+=(*fi).V1(j)->Supervisor_P();
								TD[(*fi).V1(j)].sum+=(*fi).V(j)->Supervisor_P();
								++TD[(*fi).V(j)].cnt;
								++TD[(*fi).V1(j)].cnt;
						}
	
	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		if(!(*vi).IsD() && TD[*vi].cnt>0 )
			{
				v_type np = TD[*vi].sum/TD[*vi].cnt;
				v_type d = (*vi).Supervisor_P() - viewpoint; d.Normalize();
				s_type s = d * ( np - (*vi).Supervisor_P() );
				(*vi).Supervisor_P() += d * (s*alpha);
			}
	}
	 	
	TD.Stop();
}



/****************************************************************************************************************/
/****************************************************************************************************************/
// Paso Double Smoothing
//  The proposed
// approach is a two step method where  in the first step the face normals
// are adjusted and then, in a second phase, the vertex positions are updated.
/****************************************************************************************************************/
/****************************************************************************************************************/
// Classi di info
template<class FLT> 
class PDVertInfo 
{
public:
	Point3<FLT> np;
};

template<class FLT> 
class PDFaceInfo 
{
public:
	Point3<FLT> m;
};
/***************************************************************************/
// Paso Doble Step 1 compute the smoothed normals
/***************************************************************************/
// Requirements:
// VF Topology
// Normalized Face Normals
// 
// This is the Normal Smoothing approach of Shen and Berner
// Fuzzy Vector Median-Based Surface Smoothing TVCG 2004

template<class MESH_TYPE>
void NormalSmoothSB(MESH_TYPE &m, 	
				  SimpleTempData<typename MESH_TYPE::FaceContainer,PDFaceInfo< typename MESH_TYPE::ScalarType > > &TD,
				  typename MESH_TYPE::ScalarType sigma)
{
	int i;
	
	typedef typename MESH_TYPE::CoordType CoordType;
	typedef typename MESH_TYPE::ScalarType ScalarType;
	typename MESH_TYPE::FaceIterator fi;

	for(fi=m.face.begin();fi!=m.face.end();++fi)
	{
		CoordType bc=(*fi).Barycenter();
    // 1) Clear all the selected flag of faces that are vertex-adjacent to fi
		for(i=0;i<3;++i)
		{
    	vcg::face::VFIterator<typename MESH_TYPE::FaceType> ep(&*fi,i);
		  while (!ep.End())
			{
				ep.f->ClearS();
				++ep;
			}
		}

    // 1) Effectively average the normals weighting them with 
    (*fi).SetS();
		CoordType mm=CoordType(0,0,0);
		for(i=0;i<3;++i)
		{
		  vcg::face::VFIterator<typename MESH_TYPE::FaceType> ep(&*fi,i);
		  while (!ep.End())
			{
				if(! (*ep.f).IsS() )
				{
					if(sigma>0) 
					{
						ScalarType dd=SquaredDistance(ep.f->Barycenter(),bc);
						ScalarType ang=AngleN(ep.f->N(),(*fi).N());
						mm+=ep.f->N()*exp((-sigma)*ang*ang/dd);
					}
					else mm+=ep.f->N();
					(*ep.f).SetS();
				}
				++ep;
			}
		}
		mm.Normalize();
		TD[*fi].m=mm;
	}
}

/***************************************************************************/
// Paso Doble Step 1 compute the smoothed normals
/***************************************************************************/
// Requirements:
// VF Topology
// Normalized Face Normals
// 
// This is the Normal Smoothing approach bsased on a angle thresholded weighting
// sigma is in the 0 .. 1 range
template<class MESH_TYPE>
void NormalSmooth(MESH_TYPE &m, 	
				  SimpleTempData<typename MESH_TYPE::FaceContainer,PDFaceInfo< typename MESH_TYPE::ScalarType > > &TD,
				  typename MESH_TYPE::ScalarType sigma)
{
	int i;
	
	typedef typename MESH_TYPE::CoordType CoordType;
	typedef typename MESH_TYPE::ScalarType ScalarType;
	typedef typename vcg::face::VFIterator<typename MESH_TYPE::FaceType> VFLocalIterator;
  typename MESH_TYPE::FaceIterator fi;
	
	for(fi=m.face.begin();fi!=m.face.end();++fi)
	{
		CoordType bc=Barycenter<MESH_TYPE::FaceType>(*fi);
    // 1) Clear all the selected flag of faces that are vertex-adjacent to fi
		for(i=0;i<3;++i)
		{
    	VFLocalIterator ep(&*fi,i);
		  for (;!ep.End();++ep)
				ep.f->ClearS();
		}

    // 1) Effectively average the normals weighting them with 
    //(*fi).SetS();
		CoordType mm=CoordType(0,0,0);
		//CoordType mm=(*fi).N();
		for(i=0;i<3;++i)
		{
    	VFLocalIterator ep(&*fi,i);
		  for (;!ep.End();++ep)
			{
				if(! (*ep.f).IsS() )
				{ 
          ScalarType cosang=ep.f->N()*(*fi).N();
          if(cosang >= sigma)
          {
            ScalarType w = cosang-sigma;
						mm += ep.f->N()*(w*w);
          }
					(*ep.f).SetS();
				}
			}
		}
		mm.Normalize();
		TD[*fi].m=mm;
	}
  
  for(fi=m.face.begin();fi!=m.face.end();++fi) 
    (*fi).N()=TD[*fi].m;
}

/****************************************************************************************************************/
// Restituisce il gradiente dell'area del triangolo nel punto p.
// Nota che dovrebbe essere sempre un vettore che giace nel piano del triangolo e perpendicolare al lato opposto al vertice p.
// Ottimizzato con Maple e poi pesantemente a mano.
template <class FLT>
Point3<FLT> TriAreaGradient(Point3<FLT> &p,Point3<FLT> &p0,Point3<FLT> &p1)
{
	Point3<FLT> dd = p1-p0;
	Point3<FLT> d0 = p-p0;
	Point3<FLT> d1 = p-p1;
	Point3<FLT> grad;

	FLT t16 =  d0[1]* d1[2] - d0[2]* d1[1];
	FLT t5  = -d0[2]* d1[0] + d0[0]* d1[2];
	FLT t4  = -d0[0]* d1[1] + d0[1]* d1[0];

	FLT delta= sqrtf(t4*t4 + t5*t5 +t16*t16);

	grad[0]= (t5  * (-dd[2]) + t4 * ( dd[1]))/delta;
	grad[1]= (t16 * (-dd[2]) + t4 * (-dd[0]))/delta;
	grad[2]= (t16 * ( dd[1]) + t5 * ( dd[0]))/delta;

	return grad;
}

template <class FLT>
Point3<FLT> CrossProdGradient(Point3<FLT> &p, Point3<FLT> &p0, Point3<FLT> &p1, Point3<FLT> &m)
{
	Point3<FLT> grad;
	Point3<FLT> p00=p0-p;
	Point3<FLT> p01=p1-p;
	grad[0] = (-p00[2] + p01[2])*m[1] + (-p01[1] + p00[1])*m[2];
	grad[1] = (-p01[2] + p00[2])*m[0] + (-p00[0] + p01[0])*m[2];
	grad[2] = (-p00[1] + p01[1])*m[0] + (-p01[0] + p00[0])*m[1];

	return grad;
}

/*
Deve Calcolare il gradiente di 
E(p) = A(p,p0,p1) (n - m)^2 =
A(...) (2-2nm)   = 
(p0-p)^(p1-p) 
2A - 2A * ------------- m  =
2A

2A  -  2 (p0-p)^(p1-p) * m
*/
template <class FLT>
Point3<FLT> FaceErrorGrad(Point3<FLT> &p,Point3<FLT> &p0,Point3<FLT> &p1, Point3<FLT> &m)
{
	return     TriAreaGradient(p,p0,p1) *2.0f
		- CrossProdGradient(p,p0,p1,m) *2.0f ;
}
/***************************************************************************/
// Paso Doble Step 2 Fitta la mesh a un dato insieme di normali
/***************************************************************************/

template<class MESH_TYPE>
void FitMesh(MESH_TYPE &m, 
			 SimpleTempData<typename MESH_TYPE::VertContainer, PDVertInfo<typename MESH_TYPE::ScalarType> > &TDV,
			 SimpleTempData<typename MESH_TYPE::FaceContainer, PDFaceInfo<typename MESH_TYPE::ScalarType> > &TDF,
			 float lambda)
{
	//vcg::face::Pos<typename MESH_TYPE::FaceType> ep;
	vcg::face::VFIterator<typename MESH_TYPE::FaceType> ep;
	typename MESH_TYPE::VertexIterator vi;
	typedef typename MESH_TYPE::ScalarType ScalarType;
	typedef typename MESH_TYPE::CoordType CoordType;
	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
	{
		CoordType ErrGrad=CoordType(0,0,0);

		ep.f=(*vi).VFp();
		ep.z=(*vi).VFi();
		while (!ep.End())
		{
			ErrGrad+=FaceErrorGrad(ep.f->V(ep.z)->P(),ep.f->V1(ep.z)->P(),ep.f->V2(ep.z)->P(),TDF[ep.f].m);
			++ep;
		}
		TDV[*vi].np=(*vi).P()-ErrGrad*(ScalarType)lambda;					
	}

	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		(*vi).P()=TDV[*vi].np;

}
/****************************************************************************************************************/


template<class MESH_TYPE>
void FastFitMesh(MESH_TYPE &m, 
			 SimpleTempData<typename MESH_TYPE::VertContainer, PDVertInfo<typename MESH_TYPE::ScalarType> > &TDV,
			 SimpleTempData<typename MESH_TYPE::FaceContainer, PDFaceInfo<typename MESH_TYPE::ScalarType> > &TDF)
{
	//vcg::face::Pos<typename MESH_TYPE::FaceType> ep;
	vcg::face::VFIterator<typename MESH_TYPE::FaceType> ep;
	typename MESH_TYPE::VertexIterator vi;
	typedef typename MESH_TYPE::ScalarType ScalarType;
	typedef typename MESH_TYPE::CoordType CoordType;
	typedef typename vcg::face::VFIterator<typename MESH_TYPE::FaceType> VFLocalIterator;

	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
	{
   CoordType Sum(0,0,0);
   ScalarType cnt=0;
   VFLocalIterator ep(&*vi);
	 for (;!ep.End();++ep)
		{
      CoordType bc=Barycenter<MESH_TYPE::FaceType>(*ep.F());
      Sum += ep.F()->N()*(ep.F()->N()*(bc - (*vi).P()));
      ++cnt;
		}
		TDV[*vi].np=(*vi).P()+ Sum*(1.0/cnt);					
	}

	for(vi=m.vert.begin();vi!=m.vert.end();++vi)
		(*vi).P()=TDV[*vi].np;

}






template<class MeshType>
void PasoDobleSmooth(MeshType &m, int step, typename MeshType::ScalarType Sigma=0, int FitStep=10, typename MeshType::ScalarType FitLambda=0.05)
{
  typedef typename MeshType::ScalarType     ScalarType;
  typedef typename MeshType::CoordType     CoordType;


	SimpleTempData< typename MeshType::VertContainer, PDVertInfo<ScalarType> > TDV(m.vert);
	SimpleTempData< typename MeshType::FaceContainer, PDFaceInfo<ScalarType> > TDF(m.face);
	PDVertInfo<ScalarType> lpzv;
	lpzv.np=CoordType(0,0,0);
	PDFaceInfo<ScalarType> lpzf;
	lpzf.m=CoordType(0,0,0);

	assert(m.HasVFTopology());
	m.HasVFTopology();
	TDV.Start(lpzv);
	TDF.Start(lpzf);
	for(int j=0;j<step;++j)
	{

		vcg::tri::UpdateNormals<MeshType>::PerFace(m);
		NormalSmooth<MeshType>(m,TDF,Sigma);
		for(int k=0;k<FitStep;k++)
			FitMesh<MeshType>(m,TDV,TDF,FitLambda);
	}

	TDF.Stop();
	TDV.Stop();

}
template<class MeshType>
void PasoDobleSmoothFast(MeshType &m, int step, typename MeshType::ScalarType Sigma=0, int FitStep=50)
{
  typedef typename MeshType::ScalarType     ScalarType;
  typedef typename MeshType::CoordType     CoordType;


	SimpleTempData< typename MeshType::VertContainer, PDVertInfo<ScalarType> > TDV(m.vert);
	SimpleTempData< typename MeshType::FaceContainer, PDFaceInfo<ScalarType> > TDF(m.face);
	PDVertInfo<ScalarType> lpzv;
	lpzv.np=CoordType(0,0,0);
	PDFaceInfo<ScalarType> lpzf;
	lpzf.m=CoordType(0,0,0);

	assert(m.HasVFTopology());
	m.HasVFTopology();
	TDV.Start(lpzv);
	TDF.Start(lpzf);
	
  for(int j=0;j<step;++j)
	   NormalSmooth<MeshType>(m,TDF,Sigma);
		
  for(int j=0;j<FitStep;++j)
	  FastFitMesh<MeshType>(m,TDV,TDF);

	

	TDF.Stop();
	TDV.Stop();

}



}		// End namespace vcg

#endif //  VCG_SMOOTH