GW_Float GW_Mesh::GetPerimeter( T_VertexList& boundary, GW_Bool bCyclic ) { GW_Float rPerimeter = 0; GW_Vertex* pPrev = NULL; GW_Vertex* pVert = NULL; for( IT_VertexList it = boundary.begin(); it!=boundary.end(); ++it ) { pVert = *it; if( pPrev!=NULL ) { rPerimeter += (pPrev->GetPosition()-pVert->GetPosition()).Norm(); } pPrev = pVert; } if( boundary.size()>1 && bCyclic ) { rPerimeter += (boundary.front()->GetPosition()-pVert->GetPosition()).Norm(); } return rPerimeter; }
/*------------------------------------------------------------------------------*/ void GW_TriangularInterpolation_Cubic::ComputeLocalGradient( GW_GeodesicVertex& Vert ) { /* compute the total angle */ GW_Vector3D PrevEdge; GW_Float rTotalAngle = 0; for( GW_VertexIterator it=Vert.BeginVertexIterator(); it!=Vert.EndVertexIterator(); ++it ) { GW_Vertex* pVert = *it; GW_ASSERT( pVert!=NULL ); if( it==Vert.BeginVertexIterator() ) { PrevEdge = pVert->GetPosition() - Vert.GetPosition(); PrevEdge.Normalize(); } else { GW_Vector3D NextEdge = pVert->GetPosition() - Vert.GetPosition(); NextEdge.Normalize(); rTotalAngle += acos( NextEdge*PrevEdge ); PrevEdge = NextEdge; } } /* matrix and RHS for least square minimusation */ GW_Float M[2][2] = {{0,0},{0,0}}; GW_Float b[2] = {0,0}; GW_Float rCurAngle = 0; PrevEdge.SetZero(); for( GW_VertexIterator it=Vert.BeginVertexIterator(); it!=Vert.EndVertexIterator(); ++it ) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) *it; GW_ASSERT( pVert!=NULL ); GW_Vector3D Edge = pVert->GetPosition() - Vert.GetPosition(); GW_Float a = Edge.Norm(); Edge /= a; if( it!=Vert.BeginVertexIterator() ) { /* update the angle */ rCurAngle += acos( Edge*PrevEdge ); } /* directional gradient estimation */ GW_Float delta = (pVert->GetDistance() - Vert.GetDistance())/a; /* coordonate of the edge on (u,v) [flatened coords] */ GW_Float eu = a*cos( rCurAngle/rTotalAngle ); GW_Float ev = a*sin( rCurAngle/rTotalAngle ); /* update the matrix */ M[0][0] += eu*eu; M[0][1] += eu*ev; M[1][0] += eu*ev; M[1][1] += ev*ev; b[0] += delta*eu; b[1] += delta*ev; PrevEdge = Edge; } /* invert the system */ GW_Float det = M[0][0]*M[1][1] - M[0][1]*M[1][0]; GW_ASSERT( det!=0 ); GW_Float gu = 1/det * ( M[1][1]*b[0] - M[0][1]*b[1] ); GW_Float gv = 1/det * (-M[1][0]*b[0] + M[0][0]*b[1] ); /* set the gradient in local coords for each surrounding face */ rCurAngle = 0; for( GW_FaceIterator it = Vert.BeginFaceIterator(); it!=Vert.EndFaceIterator(); ++it ) { GW_GeodesicFace* pFace = (GW_GeodesicFace*) *it; GW_ASSERT( pFace!=NULL ); GW_Vertex* pVert1 = it.GetLeftVertex(); GW_ASSERT( pVert1!=NULL ); GW_Vertex* pVert2 = it.GetRightVertex(); GW_ASSERT( pVert1!=NULL ); GW_Vector3D e1 = pVert1->GetPosition() - Vert.GetPosition(); GW_Vector3D e2 = pVert2->GetPosition() - Vert.GetPosition(); GW_Float a1 = e1.Norm(); GW_Float a2 = e2.Norm(); e1 /= a1; e2 /= a2; GW_Float rInnerAngle = acos( e1*e2 ); /* flattened position of the two vertex */ GW_Float p1[2], p2[2]; p1[0] = cos( rCurAngle ); p1[1] = sin( rCurAngle ); p2[0] = cos( rCurAngle+rInnerAngle ); p2[1] = sin( rCurAngle+rInnerAngle ); /* we have grad = gu*u + gv*v we are searching for grad = g1*p1 + g2*p2, so: gu = g1*<p1,u> + g2*<p2,u> gv = g1*<p1,v> + g2*<p2,v> i.e. |p1[0] p2[0]| |g1| |gu| |p1[1] p2[1]|*|g2| = |gv| */ det = p1[0]*p2[1]-p1[1]*p2[0]; GW_ASSERT( det!=0 ); GW_Float g1 = 1/det * ( p2[1]*gu - p2[0]*gv ); GW_Float g2 = 1/det * (-p1[1]*gu + p1[0]*gv ); /* now compute the gradient in world coords */ GW_Vector3D LocGrad = e1*g1 + e2*g2; GW_TriangularInterpolation_ABC* pInterp = pFace->GetTriangularInterpolation(); if( pInterp==NULL ) { pInterp = new GW_TriangularInterpolation_Cubic; pFace->SetTriangularInterpolation( *pInterp ); } GW_ASSERT( pInterp->GetType()==kCubicTriangulationInterpolation ); ((GW_TriangularInterpolation_Cubic*) pInterp)->SetLocalGradient( LocGrad, *pFace, Vert ); rCurAngle += rInnerAngle; } }
/*------------------------------------------------------------------------------*/ 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_GeodesicDisplayer::DisplayPath( GW_GeodesicPath& CurPath, GW_Vector3D& Color, GW_Float rLineWidth ) { glLineWidth( (GLfloat) rLineWidth ); glPointSize( (GLfloat) rLineWidth ); T_GeodesicPointList& PointList = CurPath.GetPointList(); glDisable( GL_LIGHTING ); glColor( Color ); glBegin( GL_LINE_STRIP ); for( IT_GeodesicPointList it = PointList.begin(); it!=PointList.end(); ++it ) { GW_GeodesicPoint* pPoint = *it; GW_ASSERT( pPoint->GetVertex1()!=NULL ); GW_ASSERT( pPoint->GetVertex2()!=NULL ); GW_Vector3D Pos = pPoint->GetVertex1()->GetPosition()*pPoint->GetCoord() + pPoint->GetVertex2()->GetPosition()*(1-pPoint->GetCoord()); glColor( Color ); glVertex( Pos ); GW_GeodesicFace* pFace = pPoint->GetCurFace(); GW_ASSERT( pFace!=NULL ); T_SubPointVector& SubPointVector = pPoint->GetSubPointVector(); GW_Vector3D& v0 = pPoint->GetVertex1()->GetPosition(); GW_Vector3D& v1 = pPoint->GetVertex2()->GetPosition(); GW_Vertex* pLastVert = pFace->GetVertex( *pPoint->GetVertex1(), *pPoint->GetVertex2() ); GW_ASSERT( pLastVert!=NULL ); GW_Vector3D& v2 = pLastVert->GetPosition(); for( IT_SubPointVector it=SubPointVector.begin(); it!=SubPointVector.end(); ++it ) { GW_Vector3D& coord = *it; Pos = v0*coord[0] + v1*coord[1] + v2*coord[2]; glVertex( Pos ); } } glEnd(); #if 0 glColor3f( 0,0,0 ); glPointSize(4); glBegin( GL_POINTS ); for( IT_GeodesicPointList it = PointList.begin(); it!=PointList.end(); ++it ) { GW_GeodesicPoint* pPoint = *it; GW_ASSERT( pPoint->GetVertex1()!=NULL ); GW_ASSERT( pPoint->GetVertex2()!=NULL ); GW_Vector3D Pos = pPoint->GetVertex1()->GetPosition()*pPoint->GetCoord() + pPoint->GetVertex2()->GetPosition()*(1-pPoint->GetCoord()); glVertex( Pos ); GW_GeodesicFace* pFace = pPoint->GetCurFace(); GW_ASSERT( pFace!=NULL ); T_SubPointVector& SubPointVector = pPoint->GetSubPointVector(); GW_Vector3D& v0 = pPoint->GetVertex1()->GetPosition(); GW_Vector3D& v1 = pPoint->GetVertex2()->GetPosition(); GW_Vertex* pLastVert = pFace->GetVertex( *pPoint->GetVertex1(), *pPoint->GetVertex2() ); GW_ASSERT( pLastVert!=NULL ); GW_Vector3D& v2 = pLastVert->GetPosition(); for( IT_SubPointVector it=SubPointVector.begin(); it!=SubPointVector.end(); ++it ) { GW_Vector3D& coord = *it; Pos = v0*coord[0] + v1*coord[1] + v2*coord[2]; glVertex( Pos ); } } glEnd(); glEnable( GL_LIGHTING ); glLineWidth( 1 ); #endif }
/*------------------------------------------------------------------------------*/ 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; }