/*------------------------------------------------------------------------------*/ 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; }