/*------------------------------------------------------------------------------*/
GW_Bool GW_Vertex::IsBoundaryVertex()
{
	for( GW_VertexIterator it = this->BeginVertexIterator(); it!=this->EndVertexIterator(); ++it )
	{
		if( it.GetLeftFace()==NULL || it.GetRightFace()==NULL )
			return GW_True;
	}
	return GW_False;
}
/*------------------------------------------------------------------------------*/
GW_Face* GW_VoronoiMesh::FindMaxFace( GW_GeodesicVertex& Vert )
{
	GW_Float rBestDistance = -GW_INFINITE;
	GW_Face* pCurFace_ = NULL;
	for( GW_VertexIterator it = Vert.BeginVertexIterator(); it!=Vert.EndVertexIterator(); ++it )
	{
		GW_GeodesicVertex* pVert = (GW_GeodesicVertex*)  *it;
		if( pVert->GetDistance()>rBestDistance )
		{
			rBestDistance = pVert->GetDistance();
			GW_GeodesicVertex* pVert1 = (GW_GeodesicVertex*) it.GetLeftVertex();
			GW_GeodesicVertex* pVert2 = (GW_GeodesicVertex*) it.GetRightVertex();
			if( pVert1!=NULL && pVert2!=NULL )
			{
				if( pVert1->GetDistance()<pVert2->GetDistance() )
					pCurFace_ = it.GetLeftFace();
				else
					pCurFace_ = it.GetRightFace();
			}
			else if( pVert1!=NULL )
			{
				pCurFace_ = it.GetLeftFace();
			}
			else
			{
				GW_ASSERT( pVert2!=NULL );
				pCurFace_ = it.GetRightFace();
			}
		}
	}	
	return pCurFace_;
}
/*------------------------------------------------------------------------------*/
void GW_GeodesicPath::AddVertexToPath( GW_GeodesicVertex& Vert )
{
	pPrevFace_ = pCurFace_;
	GW_Float rBestDistance = GW_INFINITE;
	pCurFace_ = NULL;
	GW_GeodesicVertex* pSelectedVert = NULL;
	for( GW_VertexIterator it = Vert.BeginVertexIterator(); it!=Vert.EndVertexIterator(); ++it )
	{
		GW_GeodesicVertex* pVert = (GW_GeodesicVertex*)  *it;
		if( pVert->GetDistance()<rBestDistance )
		{
			rBestDistance = pVert->GetDistance();
			pSelectedVert = pVert;
			GW_GeodesicVertex* pVert1 = (GW_GeodesicVertex*) it.GetLeftVertex();
			GW_GeodesicVertex* pVert2 = (GW_GeodesicVertex*) it.GetRightVertex();
			if( pVert1!=NULL && pVert2!=NULL )
			{
				if( pVert1->GetDistance()<pVert2->GetDistance() )
					pCurFace_ = (GW_GeodesicFace*) it.GetLeftFace();
				else
					pCurFace_ = (GW_GeodesicFace*) it.GetRightFace();
			}
			else if( pVert1!=NULL )
			{
				pCurFace_ = (GW_GeodesicFace*) it.GetLeftFace();
			}
			else
			{
				GW_ASSERT( pVert2!=NULL );
				pCurFace_ = (GW_GeodesicFace*) it.GetRightFace();
			}
		}
	}
	GW_ASSERT( pCurFace_!=NULL );
	GW_ASSERT( pSelectedVert!=NULL );

	GW_GeodesicPoint* pPoint = new GW_GeodesicPoint;
	Path_.push_back( pPoint );
	pPoint->SetVertex1( Vert );
	pPoint->SetVertex2( *pSelectedVert );
	pPoint->SetCoord(1);
	pPoint->SetCurFace( *pCurFace_ );
}
/*------------------------------------------------------------------------------*/
void GW_Vertex::ComputeCurvatureDirections( GW_Float rArea )
{
	GW_Vector3D CurEdge;
	GW_Vector3D CurEdgeNormalized;
	GW_Float rCurEdgeLength;
	GW_Float rCotan;
	GW_Vector3D TempEdge1, TempEdge2;
	GW_Vertex* pTempVert = NULL;
	GW_Float rDotP;

	/***********************************************************************************/
	/* compute the two curvature directions */
	/* (v1,v2) form a basis of the tangent plante */
	GW_Vector3D v1 = Normal_ ^ GW_Vector3D(0,0,1);
	GW_Float rNorm = v1.Norm();
	if( rNorm<GW_EPSILON )
	{
		/* orthogonalize using another direction */
		v1 = Normal_ ^ GW_Vector3D(0,1,0);
		rNorm = v1.Norm();
		GW_ASSERT( rNorm>GW_EPSILON );
	}
	v1 /= rNorm;
	GW_Vector3D v2 = Normal_ ^ v1;

	/* now we must find the curvature matrix entry by minimising a mean square problem 
	the 3 entry of the symetric curvature matrix in (v1,v2) basis are (a,b,c), stored in vector x.
	IMPORTANT : we must ensure a<c, so that eigenvalues are in correct order. */
	GW_Float a = 0, b = 0, c = 0;			// the vector (a,b,c) we are searching. We use a+c=2*MeanCurv so we don't take care of c.
	GW_Float D[2] = {0,0};					// the right side of the equation.
	GW_Float M00 = 0, M11 = 0, M01 = 0;		// the positive-definite matrix entries of the mean-square problem.
	GW_Float d1, d2;	// decomposition of current edge on (v1,v2) basis
	GW_Float w, k;		// current weight and current approximated directional curvature.

	/* now build the matrix by iterating around the vertex */
	for( GW_VertexIterator it = this->BeginVertexIterator(); it!=this->EndVertexIterator(); ++it )
	{
		GW_Vertex* pVert = *it;
		GW_ASSERT( pVert!=NULL );
		CurEdge = pVert->GetPosition() - this->GetPosition();
		rCurEdgeLength = CurEdge.Norm();
		CurEdgeNormalized = CurEdge/rCurEdgeLength;

		/* here we store the value of the sum of the cotan */
		rCotan = 0;

		/* compute projection onto v1,v2 basis */
		d1 = v1*CurEdge;
		d2 = v2*CurEdge;
		rNorm = sqrt(d1*d1 + d2*d2);
		if( rNorm>0 )
		{
			d1 /= rNorm;
			d2 /= rNorm;
		}

		/* compute left angle */
		pTempVert = it.GetLeftVertex();
		if( pTempVert!=NULL )
		{
			TempEdge1 = this->GetPosition() -  pTempVert->GetPosition();
			TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition();
			TempEdge1.Normalize();
			TempEdge2.Normalize();
			/* we use tan(acos(x))=sqrt(1-x^2)/x */
			rDotP = TempEdge1*TempEdge2;
			if( rDotP!=1 && rDotP!=-1 )
				rCotan += rDotP/sqrt(1-rDotP*rDotP);
		}

		/* compute right angle AND Gaussian contribution */
		pTempVert = it.GetRightVertex();
		if( pTempVert!=NULL )
		{
			TempEdge1 = this->GetPosition() -  pTempVert->GetPosition();
			TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition();
			TempEdge1.Normalize();
			TempEdge2.Normalize();
			/* we use tan(acos(x))=sqrt(1-x^2)/x */
			rDotP = TempEdge1*TempEdge2;
			if( rDotP!=1 && rDotP!=-1 )
				rCotan += (GW_Float) rDotP/sqrt(1-rDotP*rDotP);

		}
		GW_CHECK_MATHSBIT();

		/*compute weight */
		w = 0.125/rArea*rCotan*rCurEdgeLength*rCurEdgeLength;
		/* compute directional curvature */
		k = -2*(CurEdge*Normal_)/(rCurEdgeLength*rCurEdgeLength);
		k = k-(rMinCurv_+rMaxCurv_);	// modified by the fact that we use a+c=2*MeanCurv
		/* add contribution to M matrix and D vector*/ 
		M00		+=   w*(d1*d1-d2*d2)*(d1*d1-d2*d2);
		M11		+= 4*w*d1*d1*d2*d2;
		M01		+= 2*w*(d1*d1-d2*d2)*d1*d2;
		D[0]    +=   w*k*( d1*d1-d2*d2);
		D[1]    += 2*w*k*d1*d2;
	}
	GW_CHECK_MATHSBIT();

	/* solve the system */
	GW_Float rDet = M00*M11 - M01*M01;
	if( rDet!=0 )
	{
		/*	The inverse matrix is :      | M11 -M01|
								1/rDet * |-M01  M00| */
		a = 1/rDet * ( M11*D[0] - M01*D[1] );
		b = 1/rDet * (-M01*D[0] + M00*D[1] );
	}

	c = (rMinCurv_+rMaxCurv_) - a;
	// GW_ORDER(a,c);

	/* compute the direction via Givens rotations */
	GW_Float rTheta;
	if( GW_ABS(c-a) < GW_EPSILON )
	{
		if( b==0 )
			rTheta = 0;
		else
			rTheta = GW_HALFPI;
	}
	else
	{
		rTheta = (GW_Float) 2.0f*b/(c-a);
		rTheta = (GW_Float) 0.5f*atan(rTheta);
	}

	GW_CHECK_MATHSBIT();

	CurvDirMin_ = v1*cos(rTheta) - v2*sin(rTheta);
	CurvDirMax_ = v1*sin(rTheta) + v2*cos(rTheta);

	GW_Float vp1 = 0, vp2 = 0;
	if( rTheta!=0 )
	{
		GW_Float r1 = a*cos(rTheta) - b*sin(rTheta);
		GW_Float r2 = b*cos(rTheta) - c*sin(rTheta);
		r1 =(GW_Float)  r1/cos(rTheta);
		r2 = (GW_Float) -r2/sin(rTheta);
		vp1 = r1;
//		GW_ASSERT( GW_ABS(r1-r2)<0.001*GW_ABS(r1) );

		r1 = (GW_Float) a*sin(rTheta) + b*cos(rTheta);
		r2 = (GW_Float) b*sin(rTheta) + c*cos(rTheta);
		r1 = (GW_Float) r1/sin(rTheta);
		r2 = (GW_Float) r2/cos(rTheta);
		vp2 = r2;
//		GW_ASSERT( GW_ABS(r1-r2)<0.001*GW_ABS(r1) );
	}

	if( vp1>vp2 )
	{
		GW_Vector3D vtemp = CurvDirMin_;
		CurvDirMin_ = CurvDirMax_;
		CurvDirMax_ = vtemp;
	}
}
/*------------------------------------------------------------------------------*/
void GW_Vertex::ComputeNormalAndCurvature( GW_Float& rArea )
{
	GW_Vector3D CurEdge;
	GW_Vector3D CurEdgeNormalized;
	GW_Float rCurEdgeLength;
	GW_Float rCotan;
	GW_Vector3D TempEdge1, TempEdge2;
	GW_Float rTempEdge1Length, rTempEdge2Length;
	GW_Float rAngle, rInnerAngle;
	GW_Vertex* pTempVert = NULL;
	GW_Float rDotP;

	Normal_.SetZero();
	rArea = 0;
	GW_Float rGaussianCurv = 0;

	for( GW_VertexIterator it = this->BeginVertexIterator(); it!=this->EndVertexIterator(); ++it )
	{
		GW_Vertex* pVert = *it;
		GW_ASSERT( pVert!=NULL );
		CurEdge = pVert->GetPosition() - this->GetPosition();
		rCurEdgeLength = CurEdge.Norm();
		CurEdgeNormalized = CurEdge/rCurEdgeLength;

		/* here we store the value of the sum of the cotan */
		rCotan = 0;

		/* compute left angle */
		pTempVert = it.GetLeftVertex();
		if( pTempVert!=NULL )
		{
			TempEdge1 = this->GetPosition() -  pTempVert->GetPosition();
			TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition();

			TempEdge1.Normalize();
			TempEdge2.Normalize();
			/* we use tan(acos(x))=sqrt(1-x^2)/x */
			rDotP = TempEdge1*TempEdge2;
			if( rDotP!=1 && rDotP!=-1 )
				rCotan += (GW_Float) rDotP/((GW_Float) sqrt(1-rDotP*rDotP));
		}

		/* compute right angle AND Gaussian contribution */
		pTempVert = it.GetRightVertex();
		if( pTempVert!=NULL )
		{
			TempEdge1 = this->GetPosition() -  pTempVert->GetPosition();
			TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition();
			rTempEdge1Length = TempEdge1.Norm();
			rTempEdge2Length = TempEdge2.Norm();
			TempEdge1 /= rTempEdge1Length;
			TempEdge2 /= rTempEdge2Length;
			rAngle      = (GW_Float) acos( -(TempEdge1 * CurEdgeNormalized) );
			/* we use tan(acos(x))=sqrt(1-x^2)/x */
			rDotP = TempEdge1*TempEdge2;
			rInnerAngle = (GW_Float) acos( rDotP  );
			if( rDotP!=1 && rDotP!=-1 )
				rCotan += rDotP/((GW_Float) sqrt(1-rDotP*rDotP));

			rGaussianCurv += rAngle;

			/* compute the contribution to area, testing for special obtuse angle */
			if(	   rAngle<GW_HALFPI							// condition on 1st angle
				&& rInnerAngle<GW_HALFPI					// condition on 2nd angle
				&& (GW_PI-rAngle-rInnerAngle)<GW_HALFPI )	// condition on 3rd angle
			{
				/* non-obtuse : 1/8*( |PR|²cot(Q)+|PQ|²cot(R) ) where P=this, Q=pVert, R=pTempVert   */
				rArea += ( rCurEdgeLength*rCurEdgeLength*rDotP/sqrt(1-rDotP*rDotP)
					+ rTempEdge1Length*rTempEdge1Length/tan(GW_PI-rAngle-rInnerAngle) )*0.125;
			}
			else if( rAngle>=GW_HALFPI )
			{
				/* obtuse at the central vertex : 0.5*area(T) */
				rArea += 0.25*rCurEdgeLength*rTempEdge1Length* ~(CurEdgeNormalized^TempEdge1);
			}
			else
			{
				/* obtuse at one of side vertex */
				rArea += 0.125*rCurEdgeLength*rTempEdge1Length* ~(CurEdgeNormalized^TempEdge1);
			}
		}
		GW_CHECK_MATHSBIT();

		/* add the contribution to Normal */
		Normal_ -= CurEdge*rCotan;
	}
	GW_CHECK_MATHSBIT();

	GW_ASSERT( rArea!=0 );	// remove this !

	/* the Gaussian curv */
	rGaussianCurv = (GW_TWOPI - rGaussianCurv)/rArea;
	/* compute Normal and mean curv */
	Normal_ /= 4.0*rArea;
	GW_Float rMeanCurv = Normal_.Norm();
	if( GW_ABS(rMeanCurv)>GW_EPSILON )
	{
		GW_Vector3D Normal = Normal_/rMeanCurv;
		/* see if we need to flip the normal */
		this->BuildRawNormal();
		if( Normal*Normal_<0 )
			Normal_ = -Normal;
		else
			Normal_ = Normal;
	}
	else
	{
		/* we must use another method to compute normal */
		this->BuildRawNormal();
	}

	GW_Vertex::rTotalArea_ += rArea;

	/* compute the two curv values */
	GW_Float rDelta = rMeanCurv*rMeanCurv - rGaussianCurv;
	if( rDelta<0 )
		rDelta = 0;
	rDelta = sqrt(rDelta);
	rMinCurv_ = rMeanCurv - rDelta;
	rMaxCurv_ = rMeanCurv + rDelta;
}