/*------------------------------------------------------------------------------*/
GW_U32 GW_VoronoiMesh::AddFurthestPoint( T_GeodesicVertexList& VertList,GW_GeodesicMesh& Mesh, GW_Bool bUseRandomStartVertex )
{
	if( VertList.empty() )
	{
		Mesh.ResetGeodesicMesh();
		/* choose 1 point at random */
		GW_GeodesicVertex* pStartVertex = (GW_GeodesicVertex*) Mesh.GetRandomVertex();
		GW_ASSERT( pStartVertex!=NULL );
		if( pStartVertex==NULL )
			return 0;		
		VertList.push_back( pStartVertex );
		if( !bUseRandomStartVertex )
		{
			/* find furthest point from this one */
			GW_VoronoiMesh::PerformFastMarching( Mesh, VertList );
			GW_GeodesicVertex* pSelectedVert = GW_VoronoiMesh::FindMaxVertex( Mesh );
			GW_ASSERT( pSelectedVert!=NULL );
			VertList.push_back( pSelectedVert );
			VertList.pop_front();	// remove random choosen vertex
			Mesh.ResetGeodesicMesh();
		}
	}
	else
	{
		Mesh.RegisterVertexInsersionCallbackFunction( GW_VoronoiMesh::FastMarchingCallbackFunction_VertexInsersion );

		/* perform marching */
		GW_VoronoiMesh::ResetOnlyVertexState( Mesh );
		GW_GeodesicVertex* pStartVert = VertList.back();
		GW_ASSERT( pStartVert!=NULL );
		Mesh.PerformFastMarching( pStartVert  );

		/* find intersection points */
		GW_GeodesicVertex* pSelectedVert = GW_VoronoiMesh::FindMaxVertex( Mesh );
		GW_ASSERT( pSelectedVert!=NULL );
		/* find the triangle where the maximum occurs */
#if 0
		GW_Face* pMaxFace = GW_VoronoiMesh::FindMaxFace( *pSelectedVert );
		GW_ASSERT( pMaxFace!=NULL );
		/* this is a critical point */
		GW_Float x=1.0f/3, y=1.0f/3, z=1.0f/3;
		GW_GeodesicVertex* pNewVert = (GW_GeodesicVertex*) Mesh.InsertVertexInFace( *pMaxFace, x,y,z );
		pNewVert->SetFront( pSelectedVert->GetFront() );
		pNewVert->SetDistance( pSelectedVert->GetDistance() );
#else
		GW_GeodesicVertex* pNewVert = pSelectedVert;
#endif
		/* compute the real point */
		VertList.push_back( pNewVert );

		Mesh.RegisterVertexInsersionCallbackFunction( NULL );
	}

	return 1;
}
void mexFunction(	int nlhs, mxArray *plhs[], 
				 int nrhs, const mxArray*prhs[] ) 
{ 
	nbr_iter = 0;
	/* retrive arguments */
	if( nrhs<6 ) 
		mexErrMsgTxt("6 or 7 input arguments are required."); 
	if( nlhs<1 ) 
		mexErrMsgTxt("1 or 2 output arguments are required."); 

	// arg1 : vertex
	vertex = mxGetPr(prhs[0]);
	nverts = mxGetN(prhs[0]); 
	if( mxGetM(prhs[0])!=3 )
		mexErrMsgTxt("vertex must be of size 3 x nverts."); 
	// arg2 : faces
	faces = mxGetPr(prhs[1]);
	nfaces = mxGetN(prhs[1]);
	if( mxGetM(prhs[1])!=3 )
		mexErrMsgTxt("face must be of size 3 x nfaces."); 
	// arg3 : W
	Ww = mxGetPr(prhs[2]);
	int m = mxGetM(prhs[2]);
	if( m!=nverts )
		mexErrMsgTxt("W must be of same size as vertex."); 
	// arg4 : start_points
	start_points = mxGetPr(prhs[3]);
	nstart = mxGetM(prhs[3]);
	// arg5 : end_points
	end_points = mxGetPr(prhs[4]);
	nend = mxGetM(prhs[4]);
	// arg6 : niter_max
	niter_max = (int) *mxGetPr(prhs[5]);
	// arg7 : H
	if( nrhs>=7 )
	{
		H = mxGetPr(prhs[6]);
		int m =mxGetM(prhs[6]);
		if( m>0 && m!=nverts )
			mexErrMsgTxt("H must be of size nverts."); 
		if( m==0 )
			H = NULL;
	}
	else
	{
		H = NULL;
	}
	// arg8 : L
	if( nrhs>=8 )
	{
		L = mxGetPr(prhs[7]);
		int m =mxGetM(prhs[7]);
		if( m>0 && mxGetM(prhs[7])!=nverts )
			mexErrMsgTxt("L must be of size nverts."); 
		if( m==0 )
			L = NULL;
	}
	else
		L = NULL;
		
	// argument 9: value list
	if( nrhs>=9 )
	{
		values = mxGetPr(prhs[8]);
		if( mxGetM(prhs[8])==0 && mxGetN(prhs[8])==0 )
			values=NULL;
		if( values!=NULL && (mxGetM(prhs[8])!=nstart || mxGetN(prhs[8])!=1) )
			mexErrMsgTxt("values must be of size nb_start_points x 1."); 
	}
	else
		values = NULL;
	// argument 10: dmax
	if( nrhs>=9 )
		dmax = *mxGetPr(prhs[9]);
	else
		dmax = 1e9;


	// first ouput : distance
	plhs[0] = mxCreateDoubleMatrix(nverts, 1, mxREAL); 
	D = mxGetPr(plhs[0]);
	// second output : state
	plhs[1] = mxCreateDoubleMatrix(nverts, 1, mxREAL); 
	S = mxGetPr(plhs[1]);
	// second output : segmentation
	plhs[2] = mxCreateDoubleMatrix(nverts, 1, mxREAL); 
	Q = mxGetPr(plhs[2]);

	// create the mesh
	GW_GeodesicMesh Mesh;
	Mesh.SetNbrVertex(nverts);
	for( int i=0; i<nverts; ++i )
	{
		GW_GeodesicVertex& vert = (GW_GeodesicVertex&) Mesh.CreateNewVertex();
		vert.SetPosition( GW_Vector3D(vertex_(0,i),vertex_(1,i),vertex_(2,i)) );
		Mesh.SetVertex(i, &vert);
	}
	Mesh.SetNbrFace(nfaces);
	for( int i=0; i<nfaces; ++i )
	{
		GW_GeodesicFace& face = (GW_GeodesicFace&) Mesh.CreateNewFace();
		GW_Vertex* v1 = Mesh.GetVertex((int) faces_(0,i)); GW_ASSERT( v1!=NULL );
		GW_Vertex* v2 = Mesh.GetVertex((int) faces_(1,i)); GW_ASSERT( v2!=NULL );
		GW_Vertex* v3 = Mesh.GetVertex((int) faces_(2,i)); GW_ASSERT( v3!=NULL );
		face.SetVertex( *v1,*v2,*v3 );
		Mesh.SetFace(i, &face);
	}
	Mesh.BuildConnectivity();

	// set up fast marching	
	Mesh.ResetGeodesicMesh();
	for( int i=0; i<nstart; ++i )
	{
		GW_GeodesicVertex* v = (GW_GeodesicVertex*) Mesh.GetVertex((GW_U32) start_points[i]);
		GW_ASSERT( v!=NULL );
		Mesh.AddStartVertex( *v );
	}
	Mesh.SetUpFastMarching();
	Mesh.RegisterWeightCallbackFunction( WeightCallback );
	Mesh.RegisterForceStopCallbackFunction( StopMarchingCallback );
	Mesh.RegisterVertexInsersionCallbackFunction( InsersionCallback );
	if( H!=NULL )
		Mesh.RegisterHeuristicToGoalCallbackFunction( HeuristicCallback );
	// initialize the distance of the starting points
	if( values!=NULL )
	for( int i=0; i<nstart; ++i )
	{
		GW_GeodesicVertex* v = (GW_GeodesicVertex*) Mesh.GetVertex((GW_U32) start_points[i]);
		GW_ASSERT( v!=NULL );
		v->SetDistance( values[i] );
	}
	
	// perform fast marching
//	display_message("itermax=%d", niter_max);
	Mesh.PerformFastMarching();

	// output result
	for( int i=0; i<nverts; ++i )
	{
		GW_GeodesicVertex* v = (GW_GeodesicVertex*) Mesh.GetVertex((GW_U32) i);
		GW_ASSERT( v!=NULL );
		D[i] = v->GetDistance();
		S[i] = v->GetState();
		GW_GeodesicVertex* v1 = v->GetFront();
		if( v1==NULL )
			Q[i] = -1;
		else
			Q[i] = v1->GetID();
	}
	

	return;
}
/*------------------------------------------------------------------------------*/
void GW_VoronoiMesh::FastMarchingCallbackFunction_MeshBuilding( GW_GeodesicVertex& CurVert )
{
	GW_GeodesicVertex* pFront = CurVert.GetFront();
	GW_ASSERT( pFront!=NULL );
	/* retrieve the voronoi vertex corresponding to the front */
	GW_VoronoiVertex* pVoronoiVert0 = GW_VoronoiMesh::GetVoronoiFromGeodesic( *pFront );
	GW_ASSERT( pVoronoiVert0!=NULL );
	/* test if this point is a saddle point */
	for( GW_VertexIterator it=CurVert.BeginVertexIterator(); it!=CurVert.EndVertexIterator(); ++it )
	{
		GW_GeodesicVertex* pNeighborVert = (GW_GeodesicVertex*) *it;
		GW_GeodesicVertex* pNeighborFront = pNeighborVert->GetFront();
		if( pNeighborFront!=NULL && pNeighborFront!=pFront )
		{
			/* that's it ! */
			GW_VoronoiVertex* pVoronoiVert1 = GW_VoronoiMesh::GetVoronoiFromGeodesic( *pNeighborFront );
			GW_ASSERT( pVoronoiVert1!=NULL );
			if( !pVoronoiVert0->IsNeighbor(*pVoronoiVert1) )
			{
				GW_ASSERT( ! pVoronoiVert1->IsNeighbor(*pVoronoiVert0) );
				/* test for manifold structure for the future edge */
				GW_U32 nNumTriangle = 0;
				for( IT_VoronoiVertexList itVoronoi=pVoronoiVert1->BeginNeighborIterator(); itVoronoi!=pVoronoiVert1->EndNeighborIterator(); ++itVoronoi )
				{
					GW_VoronoiVertex* pVoronoiVert2 = *itVoronoi;
					GW_ASSERT( pVoronoiVert2!=NULL );
					if( pVoronoiVert2!=pVoronoiVert0 && pVoronoiVert0->IsNeighbor(*pVoronoiVert2) )
						nNumTriangle++;
				}
				if( nNumTriangle<=2 )
				{
					pVoronoiVert0->AddNeighbor( *pVoronoiVert1 );
					pVoronoiVert1->AddNeighbor( *pVoronoiVert0 );
				}
				/* test for manifold structure on the newly created edges */
				for( IT_VoronoiVertexList itVoronoi=pVoronoiVert1->BeginNeighborIterator(); itVoronoi!=pVoronoiVert1->EndNeighborIterator(); ++itVoronoi )
				{
					GW_VoronoiVertex* pVoronoiVert2 = *itVoronoi;
					GW_ASSERT( pVoronoiVert2!=NULL );
					if( pVoronoiVert2!=pVoronoiVert0 && pVoronoiVert0->IsNeighbor(*pVoronoiVert2) )
					{
						/* we are on a newly created face : test for structure integrity */
						if( !GW_VoronoiMesh::TestManifoldStructure(*pVoronoiVert0, *pVoronoiVert2) )
						{
							pVoronoiVert0->RemoveNeighbor( *pVoronoiVert1 );
							pVoronoiVert1->RemoveNeighbor( *pVoronoiVert0 );
							break;
						}
						if( !GW_VoronoiMesh::TestManifoldStructure(*pVoronoiVert1, *pVoronoiVert2) )
						{
							pVoronoiVert0->RemoveNeighbor( *pVoronoiVert1 );
							pVoronoiVert1->RemoveNeighbor( *pVoronoiVert0 );
							break;
						}
					}
				}
			}
			else
			{
				/* the graph must be undirected */
				GW_ASSERT( pVoronoiVert1->IsNeighbor(*pVoronoiVert0) );
			}
		}
	}
}