/*------------------------------------------------------------------------------*/
void GW_VoronoiMesh::PerformLocalFastMarching( GW_GeodesicMesh& Mesh, GW_VoronoiVertex& Vert )
{
	Mesh.ResetGeodesicMesh();
	/* set up a boundary to stop the front propagation */
	for( GW_FaceIterator it=Vert.BeginFaceIterator(); it!=Vert.EndFaceIterator(); ++it )
	{
		GW_Face* pFace = *it;
		GW_ASSERT( pFace!=NULL );
		GW_VoronoiVertex* pVert1 = (GW_VoronoiVertex*) pFace->GetNextVertex( Vert );
		GW_VoronoiVertex* pVert2 = (GW_VoronoiVertex*) pFace->GetNextVertex( *pVert1 );
		GW_ASSERT( pVert1!=NULL );
		GW_ASSERT( pVert2!=NULL );
		GW_U32 nID = GW_Vertex::ComputeUniqueId( *pVert1, *pVert2 );
		/* get the geodesic edge */
		GW_ASSERT( VertexPathMap_.find(nID)!=VertexPathMap_.end() );
		T_GeodesicVertexList* pVertexGeodesicEdge = VertexPathMap_[nID];
		GW_ASSERT( pVertexGeodesicEdge!=NULL );
		if( pVertexGeodesicEdge!=NULL )
		for( IT_GeodesicVertexList VertIt = pVertexGeodesicEdge->begin(); VertIt!=pVertexGeodesicEdge->end(); ++VertIt )
		{
			GW_GeodesicVertex* pGeodesicVert = *VertIt;
			pGeodesicVert->SetStoppingVertex( GW_True );
		}
	}
	/* now compute the fast marching */
	GW_ASSERT( Vert.GetBaseVertex()!=NULL );
	Mesh.PerformFastMarching( Vert.GetBaseVertex() );
}
/*------------------------------------------------------------------------------*/
void GW_VoronoiMesh::ComputeVertexParameters( GW_GeodesicMesh& Mesh )
{
	for( GW_U32 i=0; i<Mesh.GetNbrVertex(); ++i )
	{
		GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) Mesh.GetVertex( i );
		GW_ASSERT( pVert!=NULL );
		GW_Float d0, d1, d2;
		GW_VoronoiVertex* pVornoiVert0 = pVert->GetParameterVertex( 0, d0 );
		GW_VoronoiVertex* pVornoiVert1 = pVert->GetParameterVertex( 1, d1 );
		GW_VoronoiVertex* pVornoiVert2 = pVert->GetParameterVertex( 2, d2 );
		if( pVornoiVert0!=NULL && pVornoiVert1!=NULL && pVornoiVert2!=NULL )
		{
			GW_ASSERT( pVornoiVert0!=NULL );
			GW_ASSERT( pVornoiVert1!=NULL );
			GW_ASSERT( pVornoiVert2!=NULL );
			GW_GeodesicVertex* pVert0 = pVornoiVert0->GetBaseVertex();
			GW_GeodesicVertex* pVert1 = pVornoiVert1->GetBaseVertex();
			GW_GeodesicVertex* pVert2 = pVornoiVert2->GetBaseVertex();
			GW_ASSERT( pVert0!=NULL );
			GW_ASSERT( pVert1!=NULL );
			GW_ASSERT( pVert2!=NULL );
			GW_U32 nID0 = GW_Vertex::ComputeUniqueId( *pVornoiVert1, *pVornoiVert2 );
			GW_U32 nID1 = GW_Vertex::ComputeUniqueId( *pVornoiVert0, *pVornoiVert2 );
			GW_U32 nID2 = GW_Vertex::ComputeUniqueId( *pVornoiVert0, *pVornoiVert1 );
			if( GeodesicDistanceMap_.find(nID0)!=GeodesicDistanceMap_.end() &&
				GeodesicDistanceMap_.find(nID1)!=GeodesicDistanceMap_.end() &&
				GeodesicDistanceMap_.find(nID2)!=GeodesicDistanceMap_.end() )
			{
				/* length of each triangle segment. */
				GW_Float l0 = GeodesicDistanceMap_[nID0];
				GW_Float l1 = GeodesicDistanceMap_[nID1];
				GW_Float l2 = GeodesicDistanceMap_[nID2];

				/* now use Heron rule to compute parameter */
				GW_Float a0 = GW_Maths::ComputeTriangleArea( l0, d1, d2 );
				GW_Float a1 = GW_Maths::ComputeTriangleArea( d0, l1, d2 );
				GW_Float a2 = GW_Maths::ComputeTriangleArea( d0, d1, l2 );
				GW_Float t = GW_Maths::ComputeTriangleArea( l0, l1, l2 );	// area of the big triangle
				GW_Float a = a0 + a1 + a2;
				GW_ASSERT( a>GW_EPSILON );
				/* test if the point is outside */
				if( a1+a2>t )
					pVert->SetParameterVertex( 0, a1/(a1+a2), a2/(a1+a2) );
				else if( a0+a2>t )
					pVert->SetParameterVertex( a0/(a0+a2), 0, a2/(a0+a2) );
				else if( a0+a1>t )
					pVert->SetParameterVertex( a0/(a0+a1), a1/(a0+a1), 0 );
				else
				{
					/* the point is inside the triangle */
					pVert->SetParameterVertex( a0/a, a1/a, a2/a );
				}
				pVert->SetParameterVertex( a0/a, a1/a, a2/a );
			}
			else
			{
				pVert->SetParameterVertex( 0, 0, 0 );
			}
		}
	}

	/* fix parameter for voronoi vertex */
	for( GW_U32 i=0; i<this->GetNbrVertex(); ++i )
	{
		GW_VoronoiVertex* pVoronoiVert0 = (GW_VoronoiVertex*) this->GetVertex( i );
		GW_ASSERT( pVoronoiVert0!=NULL );
		GW_GeodesicVertex* pVert = pVoronoiVert0->GetBaseVertex();
		GW_ASSERT( pVert!=NULL );
		GW_Face* pFace = pVoronoiVert0->GetFace();
		GW_ASSERT( pFace!=NULL );
		GW_VoronoiVertex* pVoronoiVert1 = (GW_VoronoiVertex*) pFace->GetNextVertex( *pVoronoiVert0 );
		GW_ASSERT( pVoronoiVert1!=NULL );
		GW_VoronoiVertex* pVoronoiVert2 = (GW_VoronoiVertex*) pFace->GetNextVertex( *pVoronoiVert1 );
		GW_ASSERT( pVoronoiVert2!=NULL );
		pVert->ResetParametrizationData();
		pVert->AddParameterVertex( *pVoronoiVert0, 1 );
		pVert->AddParameterVertex( *pVoronoiVert1, 0 );
		pVert->AddParameterVertex( *pVoronoiVert2, 0 );
	}

	/* find the center of each voronoi face */
	CentralParameterMap_.clear();
	T_FloatMap BestVertexValue;
	for( GW_U32 i=0; i<Mesh.GetNbrVertex(); ++i )
	{
		GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) Mesh.GetVertex( i );
		GW_ASSERT( pVert!=NULL );
		GW_Float a,b,c;
		GW_VoronoiVertex* pVert0 = pVert->GetParameterVertex( 0, a );
		GW_ASSERT( pVert0!=NULL );
		GW_VoronoiVertex* pVert1 = pVert->GetParameterVertex( 1, b );
		GW_ASSERT( pVert1!=NULL );
		GW_VoronoiVertex* pVert2 = pVert->GetParameterVertex( 2, c );
		if( pVert2!=NULL )
		{
			GW_Float rBestDist = GW_INFINITE;
			GW_U32 nID = GW_Vertex::ComputeUniqueId( *pVert0, *pVert1, *pVert2 );
			if( BestVertexValue.find(nID)!=BestVertexValue.end() )
				rBestDist = BestVertexValue[nID];
			GW_Float rDist = GW_ABS(a - 1.0/3.0) + GW_ABS(b - 1.0/3.0) + GW_ABS(c - 1.0/3.0);
			if( rDist<rBestDist )
			{
				BestVertexValue[nID] = rDist;
				CentralParameterMap_[nID] = pVert;
			}
		}
	}
}