void GW_Mesh::CheckIntegrity() { for( GW_U32 i=0; i<this->GetNbrVertex(); ++i ) { GW_Vertex* pVert = this->GetVertex(i); GW_ASSERT( pVert!=NULL ); GW_Face* pFace = pVert->GetFace(); GW_ASSERT( pFace!=NULL ); if( pFace!=NULL && pFace->GetVertex(0)!=pVert && pFace->GetVertex(1)!=pVert && pFace->GetVertex(2)!=pVert ) GW_ASSERT( GW_False ); } for( GW_U32 i=0; i<this->GetNbrFace(); ++i ) { GW_Face* pFace = this->GetFace(i); GW_ASSERT( pFace!=NULL ); for( GW_U32 k=0; k<3; ++k ) { GW_U32 k1 = (k+1)%3; GW_U32 k2 = (k+2)%3; GW_Face* pNeighFace = pFace->GetFaceNeighbor(k); GW_Vertex* pV1 = pFace->GetVertex(k1); GW_ASSERT( pV1!=NULL ); GW_Vertex* pV2 = pFace->GetVertex(k2); GW_ASSERT( pV2!=NULL ); if( pNeighFace!=NULL ) { GW_ASSERT( pNeighFace->GetFaceNeighbor(*pV1, *pV2)==pFace ); GW_ASSERT( pFace->GetFaceNeighbor(*pV1, *pV2)==pNeighFace); } } } }
/*------------------------------------------------------------------------------*/ void GW_Mesh::FlipNormals() { for( GW_U32 i=0; i<this->GetNbrVertex(); ++i ) { GW_Vertex* pVert = this->GetVertex(i); GW_ASSERT( pVert!=NULL ); GW_Vector3D& n = pVert->GetNormal(); n = -n; } }
/*------------------------------------------------------------------------------*/ void GW_Mesh::BuildRawNormal() { for( IT_VertexVector it=VertexVector_.begin(); it!=VertexVector_.end(); ++it ) { GW_Vertex* pVert = *it; GW_ASSERT( pVert!=NULL ); pVert->BuildRawNormal(); } }
/*------------------------------------------------------------------------------*/ void GW_Mesh::BuildCurvatureData() { for( IT_VertexVector it=VertexVector_.begin(); it!=VertexVector_.end(); ++it ) { GW_Vertex* pVert = *it; GW_ASSERT( pVert!=NULL ); pVert->BuildCurvatureData(); } GW_Float rArea = this->GetArea(); GW_Float rTotalArea = GW_Vertex::rTotalArea_; }
/*------------------------------------------------------------------------------*/ GW_Vertex* GW_Mesh::InsertVertexInFace( GW_Face& Face, GW_Float x, GW_Float y, GW_Float z ) { GW_Vertex* pVert0 = Face.GetVertex(0); GW_Vertex* pVert1 = Face.GetVertex(1); GW_Vertex* pVert2 = Face.GetVertex(2); GW_ASSERT( pVert0!=NULL ); GW_ASSERT( pVert1!=NULL ); GW_ASSERT( pVert2!=NULL ); /* create two new-faces */ GW_Face* pFace1 = &this->CreateNewFace(); GW_Face* pFace2 = &this->CreateNewFace(); this->SetNbrFace( this->GetNbrFace()+2 ); this->SetFace( this->GetNbrFace()-2, pFace1 ); this->SetFace( this->GetNbrFace()-1, pFace2 ); /* create one new vertex */ GW_Vertex* pNewVert = &this->CreateNewVertex(); GW_Vector3D pos = pVert0->GetPosition()*x + pVert1->GetPosition()*y + pVert2->GetPosition()*z; pNewVert->SetPosition( pos ); pNewVert->BuildRawNormal(); this->SetNbrVertex( this->GetNbrVertex()+1 ); this->SetVertex( this->GetNbrVertex()-1, pNewVert ); /* assign vertex to faces */ pFace1->SetVertex( *pVert0, *pVert1, *pNewVert ); pFace2->SetVertex( *pNewVert, *pVert1, *pVert2 ); Face.SetVertex( *pVert0, *pNewVert, *pVert2 ); /* assign dependance vertex->mother face */ pNewVert->SetFace( Face ); pVert0->SetFace( Face ); pVert1->SetFace( *pFace1 ); pVert2->SetFace( Face ); /* outer faces */ if( Face.GetFaceNeighbor(2)!=NULL ) { GW_I32 nEdgeNumber = Face.GetFaceNeighbor(2)->GetEdgeNumber( Face ); GW_ASSERT( nEdgeNumber>=0 ); Face.GetFaceNeighbor(2)->SetFaceNeighbor( pFace1, nEdgeNumber ); } if( Face.GetFaceNeighbor(0)!=NULL ) { GW_I32 nEdgeNumber = Face.GetFaceNeighbor(0)->GetEdgeNumber( Face ); GW_ASSERT( nEdgeNumber>=0 ); Face.GetFaceNeighbor(0)->SetFaceNeighbor( pFace2, nEdgeNumber ); } /* build connectivity of inner faces */ pFace1->SetFaceNeighbor( pFace2, &Face, Face.GetFaceNeighbor(2) ); pFace2->SetFaceNeighbor( Face.GetFaceNeighbor(0), &Face, pFace1 ); GW_Face* pTempFace = Face.GetFaceNeighbor(1); Face.SetFaceNeighbor( pFace2, pTempFace, pFace1 ); return pNewVert; }
void GW_Mesh::ExtractAllBoundaries( std::list<T_VertexList>& boundary_list ) { T_VertexMap AlreadyExtracted; for( GW_U32 i=0; i<this->GetNbrVertex(); ++i ) { GW_Vertex* pVert = this->GetVertex(i); GW_ASSERT( pVert!=NULL ); if( pVert->IsBoundaryVertex() && AlreadyExtracted.find(i)==AlreadyExtracted.end() ) { T_VertexList boundary; this->ExtractBoundary( *pVert, boundary, &AlreadyExtracted ); boundary_list.push_back( boundary ); } } }
/*------------------------------------------------------------------------------*/ GW_Vertex* GW_Mesh::GetRandomVertex() { GW_U32 nNumber = 0; GW_Vertex* pStartVertex = NULL; while( pStartVertex==NULL ) { if( nNumber>=this->GetNbrVertex()/10 ) return NULL; GW_U32 nNumVert = (GW_U32) floor(GW_RAND*this->GetNbrVertex()); pStartVertex = this->GetVertex( nNumVert ); if( pStartVertex->GetFace()==NULL ) pStartVertex = NULL; nNumber++; } return pStartVertex; }
void GW_Mesh::ExtractBoundary( GW_Vertex& seed, T_VertexList& boundary, T_VertexMap* pExtracted ) { GW_ASSERT( seed.IsBoundaryVertex() ); GW_Vertex* pPrev = NULL; GW_Vertex* pCur = &seed; GW_Vertex* pNext = NULL; GW_U32 num = 0; do { num++; boundary.push_back(pCur); if( pExtracted!=NULL ) (*pExtracted)[ pCur->GetID() ] = pCur; pNext = NULL; for( GW_VertexIterator it = pCur->BeginVertexIterator(); (it!=pCur->EndVertexIterator()) && (pNext==NULL); ++it ) { GW_Vertex* pVert = *it; if( pVert->IsBoundaryVertex() && pVert!=pPrev ) pNext = pVert; } GW_ASSERT( pNext!=NULL ); pPrev = pCur; pCur = pNext; } while( pCur!=&seed && pNext!=NULL && num<this->GetNbrVertex() ); }
/*------------------------------------------------------------------------------*/ void GW_Mesh::IterateConnectedComponent_Vertex( GW_Vertex& start_vert, VertexIterate_Callback pCallback ) { /* march on the voronoi diagram */ T_VertexList VertexToProceed; VertexToProceed.push_back( &start_vert ); T_VertexMap VertexDone; VertexDone[ start_vert.GetID() ] = &start_vert; while( !VertexToProceed.empty() ) { GW_Vertex* pVert = VertexToProceed.front(); GW_ASSERT( pVert!=NULL ); VertexToProceed.pop_front(); /* cut the face */ pCallback( *pVert ); /* add neighbors */ for( GW_VertexIterator it = pVert->BeginVertexIterator(); it!=pVert->EndVertexIterator(); ++it ) { GW_Vertex* pNewVert = (GW_Vertex*) *it; if( pNewVert==NULL ) break; GW_ASSERT( pNewVert!=NULL ); if( VertexDone.find(pNewVert->GetID())==VertexDone.end() ) { VertexToProceed.push_back( pNewVert ); VertexDone[ pNewVert->GetID() ] = pNewVert; // so that it won't be added anymore } } } }
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_GeodesicDisplayer::AddFrontColor( GW_Vertex& VertFront, GW_Vector3D& Color ) { GW_U32 nID = VertFront.GetID(); VertexColorMap_[nID] = Color; }
/*------------------------------------------------------------------------------*/ void GW_GeodesicDisplayer::RemoveFrontColor( GW_Vertex& VertFront ) { VertexColorMap_.erase( VertFront.GetID() ); }
/*------------------------------------------------------------------------------*/ GW_Vertex* GW_Mesh::InsertVertexInEdge( GW_Vertex& Vert1, GW_Vertex& Vert2, GW_Float x, GW_Bool& bIsNewVertCreated ) { if( x<GW_EPSILON ) { bIsNewVertCreated = GW_False; return &Vert2; } if( x>1-GW_EPSILON ) { bIsNewVertCreated = GW_False; return &Vert1; } bIsNewVertCreated = GW_True; /* create the new vertex */ GW_Vertex* pNewVert = &this->CreateNewVertex(); this->SetNbrVertex( this->GetNbrVertex()+1 ); this->SetVertex( this->GetNbrVertex()-1, pNewVert ); /* set position */ pNewVert->SetPosition( Vert1.GetPosition()*x + Vert2.GetPosition()*(1-x) ); /* retrieve the neigbor faces face */ GW_Face* pFace1 = NULL; GW_Face* pFace2 = NULL; Vert1.GetFaces( Vert2, pFace1, pFace2 ); GW_ASSERT( pFace1!=NULL || pFace2!=NULL ); /* assign the face of new vertex */ if( pFace1!=NULL ) pNewVert->SetFace( *pFace1 ); else if( pFace2!=NULL ) pNewVert->SetFace( *pFace2 ); GW_I32 nVert1Num1, nVert1Num2, nVert1Num3, nVert2Num1, nVert2Num2, nVert2Num3; GW_Face* pNewFace1 = NULL; GW_Face* pNewFace2 = NULL; if( pFace1!=NULL ) { nVert1Num1 = pFace1->GetEdgeNumber( Vert1 ); GW_ASSERT( nVert1Num1>=0 ); nVert1Num2 = pFace1->GetEdgeNumber( Vert2 ); GW_ASSERT( nVert1Num2>=0 ); nVert1Num3 = 3-nVert1Num2-nVert1Num1; pNewFace1 = &this->CreateNewFace(); this->SetNbrFace( this->GetNbrFace()+1 ); this->SetFace( this->GetNbrFace()-1, pNewFace1 ); pNewFace1->SetVertex( *pFace1->GetVertex(nVert1Num3), nVert1Num3 ); pNewFace1->SetVertex( Vert2, nVert1Num2 ); pNewFace1->SetVertex( *pNewVert, nVert1Num1 ); /* connectivity between Face1 and new face */ GW_Face* pFaceNeighbor = pFace1->GetFaceNeighbor(nVert1Num1); pNewFace1->SetFaceNeighbor( pFaceNeighbor, nVert1Num1 ); pNewFace1->SetFaceNeighbor( pFace1, nVert1Num2 ); if( pFaceNeighbor!=NULL ) { GW_I32 nNum = pFaceNeighbor->GetEdgeNumber( Vert2, *pFace1->GetVertex(nVert1Num3) ); GW_ASSERT( nNum>=0 ); pFaceNeighbor->SetFaceNeighbor( pNewFace1, nNum ); } /* connectivity for face 1 */ pFace1->SetFaceNeighbor( pNewFace1, nVert1Num1 ); pFace1->SetVertex( *pNewVert, nVert1Num2 ); /* reassign vertex 2 */ Vert2.SetFace( *pNewFace1 ); } if( pFace2!=NULL ) { nVert2Num1 = pFace2->GetEdgeNumber( Vert1 ); GW_ASSERT( nVert2Num1>=0 ); nVert2Num2 = pFace2->GetEdgeNumber( Vert2 ); GW_ASSERT( nVert2Num2>=0 ); nVert2Num3 = 3-nVert2Num2-nVert2Num1; pNewFace2 = &this->CreateNewFace(); this->SetNbrFace( this->GetNbrFace()+1 ); this->SetFace( this->GetNbrFace()-1, pNewFace2 ); pNewFace2->SetVertex( *pFace2->GetVertex(nVert2Num3), nVert2Num3 ); pNewFace2->SetVertex( Vert2, nVert2Num2 ); pNewFace2->SetVertex( *pNewVert, nVert2Num1 ); /* connectivity between Face2 and new face */ GW_Face* pFaceNeighbor = pFace2->GetFaceNeighbor(nVert2Num1); pNewFace2->SetFaceNeighbor( pFaceNeighbor, nVert2Num1 ); pNewFace2->SetFaceNeighbor( pFace2, nVert2Num2 ); if( pFaceNeighbor!=NULL ) { GW_I32 nNum = pFaceNeighbor->GetEdgeNumber( Vert2, *pFace2->GetVertex(nVert2Num3) ); GW_ASSERT( nNum>=0 ); pFaceNeighbor->SetFaceNeighbor( pNewFace2, nNum ); } /* connectivity for face 1 */ pFace2->SetFaceNeighbor( pNewFace2, nVert2Num1 ); pFace2->SetVertex( *pNewVert, nVert2Num2 ); /* reassign vertex 2 */ Vert2.SetFace( *pNewFace2 ); } /* set inter connectivity */ if( pNewFace1!=NULL ) pNewFace1->SetFaceNeighbor( pNewFace2, nVert1Num3 ); if( pNewFace2!=NULL ) pNewFace2->SetFaceNeighbor( pNewFace1, nVert2Num3 ); /* last thing : compute normal */ pNewVert->BuildRawNormal(); return pNewVert; }
/*------------------------------------------------------------------------------*/ 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 }
GW_I32 GW_ASELoader::Load(GW_Mesh& Mesh, const char *name, GW_I32 bits) { FILE *strm = fopen(name, "r"); if (!strm) return GW_Error_Opening_File; Release(); want = bits; GetInfo(strm); Allocate(); GetData(strm); fclose(strm); if (want & kNormals) MakeNormals(); if (want & kTexCoord && numTexVerts) Align(); else if (want & kTexCoord && !numTexVerts) return 0; numIndex = numFaces * 3; /* retrieve information */ Mesh.SetNbrVertex( this->GetNumVerts() ); Mesh.SetNbrFace( this->GetNumFaces() ); GW_U32* pFace = this->GetFaces(); GW_Real32* pVert = this->GetVerts(); GW_Real32* pNormal = this->GetVertNormals(); GW_Real32* pTexture = this->GetTexture(); GW_Vertex* pCurVert = NULL; GW_Vector3D Pos, Normal; std::map<int, int> VertCorrespondance; /* load vertex */ for( GW_I32 i=0; i<this->GetNumVerts(); ++i ) { Pos = GW_Vector3D( pVert[3*i], pVert[3*i+1], pVert[3*i+2] ); /* this is to avoid an annoying problem of vertex duplication */ for( GW_I32 j=0; j<i; ++j ) { if( GW_ABS(pVert[3*i+0] - pVert[3*j+0])<GW_EPSILON && GW_ABS(pVert[3*i+1] - pVert[3*j+1])<GW_EPSILON && GW_ABS(pVert[3*i+2] - pVert[3*j+2])<GW_EPSILON ) VertCorrespondance[j] = i; } if( pNormal!=NULL ) Normal = GW_Vector3D( pNormal[3*i], pNormal[3*i+1], pNormal[3*i+2] ); pCurVert = &Mesh.CreateNewVertex(); pCurVert->SetPosition( Pos ); pCurVert->SetNormal( Normal ); if( want & kTexCoord ) { for( GW_U32 s=0; s<1; ++s ) { if( pTexture[2*i+s]<0 ) pTexture[2*i+s] += 1; if( pTexture[2*i+s]>1 ) pTexture[2*i+s] -= 1; } pCurVert->SetTexCoords( pTexture[2*i+0], pTexture[2*i+1] ); } Mesh.SetVertex( i, pCurVert ); } /* load faces */ GW_Face* pCurFace = NULL; for( GW_I32 i=0; i<this->GetNumFaces(); ++i ) { pCurFace = &Mesh.CreateNewFace(); GW_U32 FaceNumber[3]; for( GW_U32 s=0; s<3; ++s ) { FaceNumber[s] = pFace[3*i+s]; if( VertCorrespondance.find(FaceNumber[s])!=VertCorrespondance.end() ) FaceNumber[s] = VertCorrespondance[FaceNumber[s]]; } pCurFace->SetVertex( *Mesh.GetVertex(FaceNumber[0]), *Mesh.GetVertex(FaceNumber[1]), *Mesh.GetVertex(FaceNumber[2]) ); Mesh.SetFace( i, pCurFace ); } return GW_OK; }
/*------------------------------------------------------------------------------*/ 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_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_Mesh::BuildConnectivity() { T_FaceList* VertexToFaceMap = new T_FaceList[this->GetNbrVertex()]; /* build the inverse map vertex->face */ for( IT_FaceVector it = FaceVector_.begin(); it!=FaceVector_.end(); ++it ) { GW_Face* pFace = *it; GW_ASSERT( pFace!=NULL ); for( GW_U32 i=0; i<3; ++i ) { GW_Vertex* pVert = pFace->GetVertex(i); GW_ASSERT(pVert!=NULL); GW_ASSERT( pVert->GetID() <= this->GetNbrVertex() ); VertexToFaceMap[pVert->GetID()].push_back( pFace ); } } /* now we can set up connectivity */ for( IT_FaceVector it=FaceVector_.begin(); it!=FaceVector_.end(); ++it ) { GW_Face* pFace = *it; GW_ASSERT( pFace!=NULL ); /* set up the neigbooring faces of the 3 vertices */ T_FaceList* pFaceLists[3]; for( GW_U32 i=0; i<3; ++i ) { GW_Vertex* pVert = pFace->GetVertex(i); pFaceLists[i] = &VertexToFaceMap[pVert->GetID()]; } /* compute neighbor in the 3 directions */ for( GW_U32 i=0; i<3; ++i ) { GW_Face* pNeighbor = NULL; GW_U32 i1 = (i+1)%3; GW_U32 i2 = (i+2)%3; /* we must find the intersection of the surrounding faces of these 2 vertex */ GW_Bool bFind = GW_False; for( IT_FaceList it1 = pFaceLists[i1]->begin(); it1!=pFaceLists[i1]->end() && bFind!=GW_True; ++it1 ) { GW_Face* pFace1 = *it1; for( IT_FaceList it2 = pFaceLists[i2]->begin(); it2!=pFaceLists[i2]->end() && bFind!=GW_True; ++it2 ) { GW_Face* pFace2 = *it2; if( pFace1==pFace2 && pFace1!=pFace ) { pNeighbor = pFace1; bFind=GW_True; } } } // GW_ASSERT( pNeighbor!=NULL ); /* assign the face */ /* if( pFace->GetFaceNeighbor(i)!=NULL ) GW_ASSERT( pFace->GetFaceNeighbor(i)==pNeighbor ); */ pFace->SetFaceNeighbor( pNeighbor, i ); /* make some test on the neighbor to assure symetry in the connectivity relationship */ if( pNeighbor!=NULL ) { GW_I32 nEdgeNumber = pNeighbor->GetEdgeNumber( *pFace->GetVertex(i1),*pFace->GetVertex(i2) ); GW_ASSERT( nEdgeNumber>=0 ); #if 0 if( pNeighbor->GetFaceNeighbor( nEdgeNumber )!=NULL ) GW_ASSERT(pNeighbor->GetFaceNeighbor(nEdgeNumber)==pFace); #endif pNeighbor->SetFaceNeighbor( pFace, nEdgeNumber ); } } } GW_DELETEARRAY( VertexToFaceMap ); }
/*------------------------------------------------------------------------------*/ 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; }