/*------------------------------------------------------------------------------*/ void GW_VoronoiMesh::PerformLocalFastMarching( GW_GeodesicMesh& Mesh, GW_VoronoiVertex& Vert ) { Mesh.ResetGeodesicMesh(); /* set up a boundary to stop the front propagation */ for( GW_FaceIterator it=Vert.BeginFaceIterator(); it!=Vert.EndFaceIterator(); ++it ) { GW_Face* pFace = *it; GW_ASSERT( pFace!=NULL ); GW_VoronoiVertex* pVert1 = (GW_VoronoiVertex*) pFace->GetNextVertex( Vert ); GW_VoronoiVertex* pVert2 = (GW_VoronoiVertex*) pFace->GetNextVertex( *pVert1 ); GW_ASSERT( pVert1!=NULL ); GW_ASSERT( pVert2!=NULL ); GW_U32 nID = GW_Vertex::ComputeUniqueId( *pVert1, *pVert2 ); /* get the geodesic edge */ GW_ASSERT( VertexPathMap_.find(nID)!=VertexPathMap_.end() ); T_GeodesicVertexList* pVertexGeodesicEdge = VertexPathMap_[nID]; GW_ASSERT( pVertexGeodesicEdge!=NULL ); if( pVertexGeodesicEdge!=NULL ) for( IT_GeodesicVertexList VertIt = pVertexGeodesicEdge->begin(); VertIt!=pVertexGeodesicEdge->end(); ++VertIt ) { GW_GeodesicVertex* pGeodesicVert = *VertIt; pGeodesicVert->SetStoppingVertex( GW_True ); } } /* now compute the fast marching */ GW_ASSERT( Vert.GetBaseVertex()!=NULL ); Mesh.PerformFastMarching( Vert.GetBaseVertex() ); }
/*------------------------------------------------------------------------------*/ 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_; }
/*------------------------------------------------------------------------------*/ GW_Float GW_TriangularInterpolation_Cubic::ComputeValue( GW_GeodesicVertex& v0, GW_GeodesicVertex& v1, GW_GeodesicVertex& v2, GW_Float x, GW_Float y ) { /* compute local basis */ GW_Vector3D& e = v2.GetPosition(); // origin GW_Vector3D e0 = v0.GetPosition() - e; GW_Vector3D e1 = v1.GetPosition() - e; /* compute (s,t) the parameter of the point M in basis (w;u,v). We use equality : M = w + s u + t v = e + x e0 + y e1 so : s = x <e0,u> + y <e1,u> + <e-w,u> t = x <e0,v> + y <e1,v> + <e-w,v> i.e. : |s| |<e0,u> <e1,u>| |x| |<e-w,u>| |t| = |<e0,v> <e1,v>|*|y| + |<e-w,v>| */ GW_Vector3D trans = e-w; /* compute passage matrix */ GW_Float p00 = e0*u; GW_Float p01 = e1*u; GW_Float p10 = e0*v; GW_Float p11 = e1*v; GW_Float s = x*p00 + y*p01 + (trans*u); GW_Float t = x*p10 + y*p11 + (trans*v); /* now we can compute the value in orthogonal basis (w;u,v) */ return Coeffs[0] + Coeffs[1]*s + Coeffs[2]*t + Coeffs[3]*s*t + Coeffs[4]*s*s + Coeffs[5]*t*t; }
/*------------------------------------------------------------------------------*/ void GW_GeodesicMesh::ResetParametrizationData() { //!!for( IT_VertexVector it=VertexVector_.begin(); it!=VertexVector_.end(); ++it ) for(GW_Vertex** it = VertexVector_; it-VertexVector_<VertexVector_size; ++it) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) *it; pVert->ResetParametrizationData(); } }
/*------------------------------------------------------------------------------*/ void GW_VoronoiMesh::ResetOnlyVertexState( GW_GeodesicMesh& Mesh ) { for( GW_U32 i = 0; i<Mesh.GetNbrVertex(); ++i ) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) Mesh.GetVertex(i); GW_ASSERT( pVert!=NULL ); pVert->SetState( GW_GeodesicVertex::kFar ); } }
/*------------------------------------------------------------------------------*/ void GW_VoronoiVertex::SetBaseVertex( GW_GeodesicVertex& BaseVertex ) { GW_SmartCounter::CheckAndDelete( pBaseVertex_ ); pBaseVertex_ = &BaseVertex; pBaseVertex_->UseIt(); /* copy the parameters of the base mesh */ this->SetPosition( BaseVertex.GetPosition() ); this->SetNormal( BaseVertex.GetNormal() ); }
void GW_GeodesicMesh::ResetGeodesicMesh() { //!!for( IT_VertexVector it=VertexVector_.begin(); it!=VertexVector_.end(); ++it ) for(GW_Vertex** it = VertexVector_; it-VertexVector_<VertexVector_size; ++it) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) *it; pVert->ResetGeodesicVertex(); } map.clear(); }
GW_Bool StopMarchingCallback( GW_GeodesicVertex& Vert ) { // check if the end point has been reached GW_U32 i = Vert.GetID(); // display_message("ind %d",i ); // display_message("dist %f",Vert.GetDistance() ); if( Vert.GetDistance()>dmax ) return true; for( int k=0; k<nend; ++k ) if( end_points[k]==i ) return true; return false; }
/*------------------------------------------------------------------------------*/ GW_GeodesicVertex* GW_VoronoiMesh::FindMaxVertex( GW_GeodesicMesh& Mesh ) { GW_GeodesicVertex* pSelectedVert = NULL; GW_Float rMaxDist = -GW_INFINITE; for( GW_U32 i=0; i<Mesh.GetNbrVertex(); ++i ) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) Mesh.GetVertex( i ); GW_ASSERT( pVert!=NULL ); if( pVert->GetFace()!=NULL && pVert->GetDistance()>rMaxDist ) { rMaxDist = pVert->GetDistance(); pSelectedVert = pVert; } } return pSelectedVert; }
/*------------------------------------------------------------------------------*/ GW_Bool GW_VoronoiMesh::FastMarchingCallbackFunction_Boundaries( GW_GeodesicVertex& CurVert ) { // \todo for the moment propagate on the full neighborhood // for( IT_VoronoiVertexList it=CurrentTargetVertex_.begin(); it!=CurrentTargetVertex_.end(); ++it ) // { GW_ASSERT( pCurrentVoronoiVertex_!=NULL ); for( GW_VertexIterator it=pCurrentVoronoiVertex_->BeginVertexIterator(); it!=pCurrentVoronoiVertex_->EndVertexIterator(); ++it ) { GW_VoronoiVertex* pVornoiVert = (GW_VoronoiVertex*) *it; GW_ASSERT( pVornoiVert!=NULL ); GW_GeodesicVertex* pVert = pVornoiVert->GetBaseVertex(); GW_ASSERT( pVert!=NULL ); if( pVert->GetState()!=GW_GeodesicVertex::kDead ) return GW_False; } return GW_True; }
/*------------------------------------------------------------------------------*/ GW_Vertex* GW_GeodesicMesh::GetRandomVertex(GW_Bool bForceFar) { GW_U32 nNumber = 0; GW_GeodesicVertex* pStartVertex = NULL; while( pStartVertex==NULL ) { if( nNumber>=this->GetNbrVertex()/10 ) return NULL; GW_U32 nNumVert = (GW_U32) floor(GW_RAND*this->GetNbrVertex()); pStartVertex = (GW_GeodesicVertex*) this->GetVertex( nNumVert ); if( bForceFar==GW_True && pStartVertex->GetState()!=GW_GeodesicVertex::kFar ) pStartVertex = NULL; if( pStartVertex!=NULL && pStartVertex->GetFace()==NULL ) pStartVertex = NULL; nNumber++; } return pStartVertex; }
/*------------------------------------------------------------------------------*/ GW_Bool GW_VoronoiMesh::FastMarchingCallbackFunction_VertexInsersionRD1( GW_GeodesicVertex& CurVert, GW_Float rNewDist ) { GW_ASSERT( pCurWeights_!=NULL ); GW_GeodesicInformationDuplicata* pDuplicata = (GW_GeodesicInformationDuplicata*) CurVert.GetUserData(); GW_ASSERT( pDuplicata!=NULL ); GW_U32 nId = pDuplicata->pFront_->GetID(); (*pCurWeights_)[ nId ] = -1; // this is one of our natural neighbor, yeah. return pDuplicata->rDistance_+GW_EPSILON >= rNewDist; }
/*------------------------------------------------------------------------------*/ void GW_TriangularInterpolation_Quadratic::ComputeGradient( GW_GeodesicVertex& v0, GW_GeodesicVertex& v1, GW_GeodesicVertex& v2, GW_Float x, GW_Float y, GW_Float& dx, GW_Float& dy ) { /* compute local basis */ GW_Vector3D& e = v2.GetPosition(); // origin GW_Vector3D e0 = v0.GetPosition() - e; GW_Vector3D e1 = v1.GetPosition() - e; /* compute (s,t) the parameter of the point M in basis (w;u,v). We use equality : M = w + s u + t v = e + x e0 + y e1 so : s = x <e0,u> + y <e1,u> + <e-w,u> t = x <e0,v> + y <e1,v> + <e-w,v> i.e. : |s| |<e0,u> <e1,u>| |x| |<e-w,u>| |t| = |<e0,v> <e1,v>|*|y| + |<e-w,v>| */ GW_Vector3D trans = e-w; /* compute passage matrix */ GW_Float p00 = e0*u; GW_Float p01 = e1*u; GW_Float p10 = e0*v; GW_Float p11 = e1*v; GW_Float s = x*p00 + y*p01 + (trans*u); GW_Float t = x*p10 + y*p11 + (trans*v); /* now we can compute the gradient in orthogonal basis (w;u,v) */ GW_Float gu = Coeffs[1] + Coeffs[3]*t + Coeffs[4]*2*s; GW_Float gv = Coeffs[2] + Coeffs[3]*s + Coeffs[5]*2*t; /* convert from orthogonal basis to local basis : |<e0,u> <e1,u>| |dx| |gu| |<e0,v> <e1,v>|*|dy| = |gv| ie : |dx| | p11 -p01| |gu| |dy| = 1/det * |-p10 p00|*|gv| */ GW_Float rDet = p00*p11 - p01*p10; GW_ASSERT( rDet!=0 ); if( GW_ABS(rDet)>GW_EPSILON ) { dx = 1/rDet * ( p11*gu - p01*gv ) * ~e0; dy = 1/rDet * (-p10*gu + p00*gv ) * ~e1; } else dx = dy = 0; }
/*------------------------------------------------------------------------------*/ 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_ ); }
GW_Bool InsersionCallback( GW_GeodesicVertex& Vert, GW_Float rNewDist ) { // check if the distance of the new point is less than the given distance GW_U32 i = Vert.GetID(); bool doinsersion = nbr_iter<=niter_max; if( L!=NULL ) doinsersion = doinsersion && (rNewDist<L[i]); nbr_iter++; return doinsersion; }
/*------------------------------------------------------------------------------*/ void GW_VoronoiMesh::CreateVoronoiVertex() { /* Create Vornoi vertex and make the inverse map GeodesicVertex->VoronoiVertex */ this->SetNbrVertex( (GW_U32) BaseVertexList_.size() ); GW_U32 nNum = 0; for( IT_GeodesicVertexList it = BaseVertexList_.begin(); it!=BaseVertexList_.end(); ++it ) { GW_GeodesicVertex* pVert = *it; GW_ASSERT( pVert!=NULL ); /* create a new voronoi vertex */ GW_VoronoiVertex* pVornoiVert = new GW_VoronoiVertex; pVornoiVert->SetBaseVertex( *pVert ); this->SetVertex( nNum, pVornoiVert ); nNum++; /* build the inverse map */ GW_ASSERT( VoronoiVertexMap_.find( pVert->GetID() )==VoronoiVertexMap_.end() ); VoronoiVertexMap_[ pVert->GetID() ] = pVornoiVert; } }
/*------------------------------------------------------------------------------*/ void GW_GeodesicDisplayer::DisplayVoronoiPoints( GW_VoronoiMesh& VoronoiMesh, GW_Vector3D& Color, GW_Float rPointSize ) { GW_Bool bIsTextureOn = glIsEnabled( GL_TEXTURE_2D ); glDisable( GL_TEXTURE_2D ); T_GeodesicVertexList BaseVertexList = VoronoiMesh.GetBaseVertexList(); glPointSize( (GLfloat) rPointSize ); glColor( Color ); glDisable( GL_LIGHTING ); glBegin( GL_POINTS ); for( IT_GeodesicVertexList it = BaseVertexList.begin(); it!=BaseVertexList.end(); ++it ) { GW_GeodesicVertex* pVert = *it; glVertex( pVert->GetPosition() ); } glEnd(); glEnable( GL_LIGHTING ); if( bIsTextureOn ) glEnable( GL_TEXTURE_2D ); }
/*------------------------------------------------------------------------------*/ void GW_TriangularInterpolation_Linear::ComputeGradient( GW_GeodesicVertex& v0, GW_GeodesicVertex& v1, GW_GeodesicVertex& v2, GW_Float x, GW_Float y, GW_Float& dx, GW_Float& dy ) { (void) x; (void) y; GW_Float d0 = v0.GetDistance(); GW_Float d1 = v1.GetDistance(); GW_Float d2 = v2.GetDistance(); /* compute gradient */ GW_Vector3D e0 = v0.GetPosition() - v2.GetPosition(); GW_Vector3D e1 = v1.GetPosition() - v2.GetPosition(); GW_Float l0 = e0.Norm(); GW_Float l1 = e1.Norm(); e0 /= l0; e1 /= l1; GW_Float dot = e0*e1; /* The gradient in direction (e1,e2) is: |<grad(d),e0>| |(d0-d2)/l0| |gu| D = |<grad(d),e1>| = |(d1-d2)/l1| = |gv| We are searching for grad(d) = dx e0 + dy e1 wich gives rise to the system : | 1 dot| |dx| |dot 1 | * |dy| = D where dot=<e0,e2> ie it is: 1/det * | 1 -dot|*|gu| |-dot 1 | |gv| */ GW_Float det = 1-dot*dot; GW_ASSERT( det!=0 ); GW_Float gu = (d0-d2)/l0; GW_Float gv = (d1-d2)/l1; dx = 1/det * ( gu - dot*gv ); dy = 1/det * (-dot*gu + gv ); }
/*------------------------------------------------------------------------------*/ void GW_VoronoiMesh::BuildGeodesicParametrization( GW_GeodesicMesh& Mesh ) { Mesh.RegisterForceStopCallbackFunction( FastMarchingCallbackFunction_Parametrization ); Mesh.ResetParametrizationData(); /* for each base vertex, perform front propagation on the surrounding faces */ for( GW_U32 i=0; i<this->GetNbrVertex(); ++i ) { CurrentTargetVertex_.clear(); pCurrentVoronoiVertex_ = (GW_VoronoiVertex*) this->GetVertex(i); // set up global data for callback function GW_ASSERT( pCurrentVoronoiVertex_!=NULL ); GW_OutputComment("Performing local fast marching."); this->PerformLocalFastMarching( Mesh, *pCurrentVoronoiVertex_ ); /* register geodesic boundaries length */ for( GW_VertexIterator it=pCurrentVoronoiVertex_->BeginVertexIterator(); it!=pCurrentVoronoiVertex_->EndVertexIterator(); ++it ) { GW_VoronoiVertex* pNeighbor = (GW_VoronoiVertex*) *it; GW_ASSERT( pNeighbor!=NULL ); GW_GeodesicVertex* pGeodesicVert = pNeighbor->GetBaseVertex(); GW_ASSERT( pGeodesicVert!=NULL ); GW_U32 nID = GW_Vertex::ComputeUniqueId( *pCurrentVoronoiVertex_, *pNeighbor ); if( GeodesicDistanceMap_.find(nID)==GeodesicDistanceMap_.end() ) { GW_ASSERT( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead ); /* this is the first time the distance is computed */ if( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead ) GeodesicDistanceMap_[nID] = pGeodesicVert->GetDistance(); } else { GW_ASSERT( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead ); if( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead ) { /* distance alreay computed in the reverse direction : take the average */ GW_Float rPrevDist = GeodesicDistanceMap_[nID]; GW_Float rNewDist = pGeodesicVert->GetDistance(); GeodesicDistanceMap_[nID] = GW_MIN(rPrevDist, rNewDist); //(rPrevDist+rNewDist)*0.5; } } } } /* now really compute the 3 parameters for each vertex */ GW_OutputComment("Computing geodesic parametrisation."); this->ComputeVertexParameters( Mesh ); pCurrentVoronoiVertex_ = NULL; Mesh.RegisterForceStopCallbackFunction( NULL ); }
/*------------------------------------------------------------------------------*/ 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; }
/*------------------------------------------------------------------------------*/ GW_Bool GW_VoronoiMesh::FastMarchingCallbackFunction_Parametrization( GW_GeodesicVertex& CurVert ) { GW_ASSERT( pCurrentVoronoiVertex_!=NULL ); if( !CurVert.GetIsStoppingVertex() || !CurVert.GetBoundaryReached() ) { /* add the parameter value */ CurVert.AddParameterVertex( *pCurrentVoronoiVertex_, CurVert.GetDistance() ); if( CurVert.GetIsStoppingVertex() ) CurVert.SetBoundaryReached( GW_True ); } /* always continue the computations */ return GW_False; }
/*------------------------------------------------------------------------------*/ GW_Bool GW_VoronoiMesh::FastMarchingCallbackFunction_VertexInsersionRD2( GW_GeodesicVertex& CurVert, GW_Float rNewDist ) { GW_ASSERT( pCurWeights_!=NULL ); GW_U32 nId = CurVert.GetID(); if( pCurWeights_->find(nId)!=pCurWeights_->end() ) // this is one of our natural neighbor, yeah. { if( (*pCurWeights_)[nId]<0 ) { /* first time we encounter this vertex, sounds good */ if( rNewDist>0 ) (*pCurWeights_)[nId] = 1.0/(rNewDist); else (*pCurWeights_)[nId] = GW_INFINITE; nNbrBaseVertex_RD_++; } else GW_ASSERT( GW_False ); } return GW_True; }
/*------------------------------------------------------------------------------*/ void GW_GeodesicDisplayer::SetUpDraw(GW_Mesh& Mesh) { GW_BasicDisplayer::SetUpDraw( Mesh ); GW_ASSERT( strcmp(Mesh.GetClassName().c_str(), "GW_GeodesicMesh")==0 ); aMinValue_[kGeodesicDistance] = GW_INFINITE; aMaxValue_[kGeodesicDistance] = -GW_INFINITE; for( GW_U32 i = 0; i<Mesh.GetNbrVertex(); ++i ) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) Mesh.GetVertex(i); if( pVert->GetDistance()<GW_INFINITE*0.5 ) { if( pVert->GetDistance()<aMinValue_[kGeodesicDistance] ) aMinValue_[kGeodesicDistance] = pVert->GetDistance(); if( pVert->GetDistance()>aMaxValue_[kGeodesicDistance] ) aMaxValue_[kGeodesicDistance] = pVert->GetDistance(); } } }
/*------------------------------------------------------------------------------*/ GW_I32 GW_GeodesicPath::AddNewPoint() { // rStepSize_ = 1000; GW_ASSERT( pCurFace_!=NULL ); GW_ASSERT( !Path_.empty() ); GW_GeodesicPoint* pPoint = Path_.back(); GW_ASSERT( pPoint!=NULL ); GW_GeodesicVertex* pVert1 = pPoint->GetVertex1(); GW_ASSERT( pVert1!=NULL ); GW_GeodesicVertex* pVert2 = pPoint->GetVertex2(); GW_ASSERT( pVert2!=NULL ); GW_GeodesicVertex* pVert3 = (GW_GeodesicVertex*) pCurFace_->GetVertex( *pVert1, *pVert2 ); GW_ASSERT( pVert3!=NULL ); /* compute coords of the last point */ T_SubPointVector& SubPointVector = pPoint->GetSubPointVector(); GW_Float x,y,z; // barycentric coords of the point /* the sub-points should be empty */ GW_ASSERT( SubPointVector.empty() ); /* we begin on an edge */ x = pPoint->GetCoord(); y = 1-x; z = 0; GW_Float l1 = ~( pVert1->GetPosition() - pVert3->GetPosition() ); GW_Float l2 = ~( pVert2->GetPosition() - pVert3->GetPosition() ); pCurFace_->SetUpTriangularInterpolation(); GW_U32 nNum = 0; while( nNum<1000 ) // never stop, this is just to avoid infinite loop { nNum++; GW_Float dx, dy; pCurFace_->ComputeGradient( *pVert1, *pVert2, *pVert3, x, y, dx, dy ); GW_Float l, a; /* Try each kind of possible crossing. The barycentric coords of the point is (x-l*dx/l1,y-l*dy/l2,z+l*(dx/l1+dy/l2)) */ if( GW_ABS(dx)>GW_EPSILON ) { l = l1*x/dx; // position along the line a = y-l*dy/l2; // coordonate with respect to v2 if( l>0 && l<=rStepSize_ && 0<=a && a<=1 ) { /* the crossing occurs on [v2,v3] */ GW_GeodesicPoint* pNewPoint = new GW_GeodesicPoint; Path_.push_back( pNewPoint ); pNewPoint->SetVertex1( *pVert2 ); pNewPoint->SetVertex2( *pVert3 ); pNewPoint->SetCoord( a ); pPrevFace_ = pCurFace_; if( pCurFace_->GetFaceNeighbor( *pVert2, *pVert3 )==NULL ) { pNewPoint->SetCurFace( *pCurFace_ ); /* check if we are *really* on a boundarie of the mesh */ GW_ASSERT( pCurFace_->GetEdgeNumber( *pVert2, *pVert3 )>=0 ); /* we should stay on the same face */ return 0; } pCurFace_ = (GW_GeodesicFace*) pCurFace_->GetFaceNeighbor( *pVert2, *pVert3 ); GW_ASSERT( pCurFace_!=NULL ); pNewPoint->SetCurFace( *pCurFace_ ); /* test for ending */ if( a<0.01 && pVert2->GetDistance()<GW_EPSILON ) return -1; if( a>0.99 && pVert3->GetDistance()<GW_EPSILON ) return -1; return 0; } } if( GW_ABS(dy)>GW_EPSILON!=0 ) { l = l2*y/dy; // position along the line a = x-l*dx/l1; // coordonate with respect to v1 if( l>0 && l<=rStepSize_ && 0<=a && a<=1 ) { /* the crossing occurs on [v1,v3] */ GW_GeodesicPoint* pNewPoint = new GW_GeodesicPoint; Path_.push_back( pNewPoint ); pNewPoint->SetVertex1( *pVert1 ); pNewPoint->SetVertex2( *pVert3 ); pNewPoint->SetCoord( a ); pPrevFace_ = pCurFace_; if( pCurFace_->GetFaceNeighbor( *pVert1, *pVert3 )==NULL ) { pNewPoint->SetCurFace( *pCurFace_ ); /* check if we are *really* on a boundarie of the mesh */ GW_ASSERT( pCurFace_->GetEdgeNumber( *pVert1, *pVert3 )>=0 ); /* we should stay on the same face, the fact that pPrevFace_==pCurFace_ will force to go on an edge */ return 0; } pCurFace_ = (GW_GeodesicFace*) pCurFace_->GetFaceNeighbor( *pVert1, *pVert3 ); GW_ASSERT( pCurFace_!=NULL ); pNewPoint->SetCurFace( *pCurFace_ ); /* test for ending */ if( a<0.01 && pVert1->GetDistance()<GW_EPSILON ) return -1; if( a>0.99 && pVert3->GetDistance()<GW_EPSILON ) return -1; return 0; } } if( GW_ABS(dx/l1+dy/l2)>GW_EPSILON ) { l = -z/(dx/l1+dy/l2); // position along the line a = x-l*dx/l1; // coordonate with respect to v1 if( l>0 && l<=rStepSize_ && 0<=a && a<=1 ) { /* the crossing occurs on [v1,v2] */ GW_GeodesicPoint* pNewPoint = new GW_GeodesicPoint; Path_.push_back( pNewPoint ); pNewPoint->SetVertex1( *pVert1 ); pNewPoint->SetVertex2( *pVert2 ); pNewPoint->SetCoord( a ); pPrevFace_ = pCurFace_; if( pCurFace_->GetFaceNeighbor( *pVert1, *pVert2 )==NULL ) { pNewPoint->SetCurFace( *pCurFace_ ); /* check if we are *really* on a boundarie of the mesh */ GW_ASSERT( pCurFace_->GetEdgeNumber( *pVert1, *pVert2 )>=0 ); /* we should stay on the same face */ return 0; } pCurFace_ = (GW_GeodesicFace*) pCurFace_->GetFaceNeighbor( *pVert1, *pVert2 ); GW_ASSERT( pCurFace_!=NULL ); pNewPoint->SetCurFace( *pCurFace_ ); /* test for ending */ if( a<0.01 && pVert1->GetDistance()<GW_EPSILON ) return -1; if( a>0.99 && pVert2->GetDistance()<GW_EPSILON ) return -1; return 0; } } if( GW_ABS(dx)<GW_EPSILON && GW_ABS(dx)<GW_EPSILON ) { /* special case : we must follow the edge. */ GW_GeodesicVertex* pSelectedVert = pVert1; if( pVert2->GetDistance()<pSelectedVert->GetDistance() ) pSelectedVert = pVert2; if( pVert3->GetDistance()<pSelectedVert->GetDistance() ) pSelectedVert = pVert3; // just a check this->AddVertexToPath( *pSelectedVert ); GW_ASSERT( pCurFace_!=NULL ); if( pSelectedVert->GetDistance()<GW_EPSILON ) return -1; if( pCurFace_==pPrevFace_ && pPoint->GetCoord()>1-GW_EPSILON ) { /* hum, problem, we are in a local minimum */ return -1; } return 0; } /* no intersection: we can advance */ GW_Float xprev = x, yprev = y; x = x - rStepSize_*dx/l1; y = y - rStepSize_*dy/l2; if( x<0 || x>1 || y<0 || y>1 ) { GW_ASSERT( z==0 ); GW_GeodesicFace* pNextFace = (GW_GeodesicFace*) pCurFace_->GetFaceNeighbor( *pVert3 ); if( pNextFace==pPrevFace_ || pCurFace_->GetFaceNeighbor( *pVert3 )==NULL ) { /* special case : we must follow the edge. */ GW_GeodesicVertex* pSelectedVert = pVert1; if( pVert2->GetDistance()<pSelectedVert->GetDistance() ) pSelectedVert = pVert2; if( pVert3->GetDistance()<pSelectedVert->GetDistance() ) pSelectedVert = pVert3; // just a check this->AddVertexToPath( *pSelectedVert ); GW_ASSERT( pCurFace_!=NULL ); if( pSelectedVert->GetDistance()<GW_EPSILON ) return -1; if( pCurFace_==pPrevFace_ && pPoint->GetCoord()>1-GW_EPSILON ) { /* hum, problem, we are in a local minimum */ return -1; } // if( pSelectedVert==pVert3 ) // GW_ASSERT( x==0 || y==0 ); } else { /* we should go on another face */ pPrevFace_ = pCurFace_; pCurFace_ = (GW_GeodesicFace*) pCurFace_->GetFaceNeighbor( *pVert3 ); GW_ASSERT( pCurFace_!=NULL ); GW_GeodesicPoint* pNewPoint = new GW_GeodesicPoint; Path_.push_back( pNewPoint ); pNewPoint->SetVertex1( *pVert1 ); pNewPoint->SetVertex2( *pVert2 ); pNewPoint->SetCurFace( *pCurFace_ ); pNewPoint->SetCoord( xprev ); } return 0; } SubPointVector.push_back( GW_Vector3D(x,y,1-x-y) ); } GW_ASSERT( GW_False ); GW_GeodesicVertex* pSelectedVert = pVert1; if( pVert2->GetDistance()<pSelectedVert->GetDistance() ) pSelectedVert = pVert2; if( pVert3->GetDistance()<pSelectedVert->GetDistance() ) pSelectedVert = pVert3; // just a check this->AddVertexToPath( *pSelectedVert ); GW_ASSERT( pCurFace_!=NULL ); if( pSelectedVert->GetDistance()<GW_EPSILON ) return -1; if( pCurFace_==pPrevFace_ && pPoint->GetCoord()>1-GW_EPSILON ) { /* hum, problem, we are in a local minimum */ return -1; } return 0; }
/*------------------------------------------------------------------------------*/ void GW_GeodesicDisplayer::ComputeColor( GW_Vertex& Vert, float* color ) { GW_GeodesicVertex& GeoVert = (GW_GeodesicVertex&) Vert; GW_BasicDisplayer::ComputeColor( Vert, color ); if( bProrieties[kGeodesicDistance] ) { if( GeoVert.GetDistance()<GW_INFINITE/2 ) { float dist = (float) GW_SCALE_01(GeoVert.GetDistance(), aMinValue_[kGeodesicDistance], aMaxValue_[kGeodesicDistance]); GW_ASSERT(GeoVert.GetFront()!=NULL); GW_U32 nID = GeoVert.GetFront()->GetID(); if( VertexColorMap_.find(nID)!=VertexColorMap_.end() ) { GW_Vector3D colvect = VertexColorMap_[nID]; color[0] = (GLfloat) colvect[0]*(1-dist); color[1] = (GLfloat) colvect[1]*(1-dist); color[2] = (GLfloat) colvect[2]*(1-dist); } else { /* no colorisation was provided for this front */ color[0] = color[1] = color[2] = 1-dist; } if( bProrieties[kGeodesicDistanceStreamColor] ) { GW_Float max = aMaxValue_[kGeodesicDistance]; if( rColorStreamFixedRadius_>0 ) max = rColorStreamFixedRadius_; GW_Vector3D colvect = this->GetStreamColor( GeoVert.GetDistance(), max ); color[0] = (GLfloat) colvect[0]; color[1] = (GLfloat) colvect[1]; color[2] = (GLfloat) colvect[2]; } } } if( bProrieties[kMarchingState] ) { if( GeoVert.GetState()==GW_GeodesicVertex::kAlive ) { GW_Vector3D TheColor(1,0,0); // default color = red GW_ASSERT(GeoVert.GetFront()!=NULL); GW_U32 nID = GeoVert.GetFront()->GetID(); if( VertexColorMap_.find(nID)!=VertexColorMap_.end() ) TheColor = VertexColorMap_[nID]; color[0] = (float) TheColor[0]*0.5f; color[1] = (float) TheColor[1]*0.5f; color[2] = (float) TheColor[2]*0.5f; } if( GeoVert.GetState()==GW_GeodesicVertex::kDead ) { GW_Vector3D TheColor(1,0,0); // default color = red GW_ASSERT(GeoVert.GetFront()!=NULL); GW_U32 nID = GeoVert.GetFront()->GetID(); if( VertexColorMap_.find(nID)!=VertexColorMap_.end() ) TheColor = VertexColorMap_[nID]; color[0] = (float) TheColor[0]; color[1] = (float) TheColor[1]; color[2] = (float) TheColor[2]; } } if( bProrieties[kVertexParametrization] ) { #if 1 GW_U32 nNbrParameter = 0; GW_Vector3D Color; for( GW_U32 i=0; i<3; ++i ) { GW_Float rParam = 0; GW_VoronoiVertex* pParamVert = GeoVert.GetParameterVertex( i, rParam ); if( pParamVert!=NULL ) { GW_U32 nID = pParamVert->GetBaseVertex()->GetID(); if( VertexColorMap_.find(nID)!=VertexColorMap_.end() ) { Color += VertexColorMap_[nID]*rParam; nNbrParameter++; } } } color[0] = (float) Color[0]; color[1] = (float) Color[1]; color[2] = (float) Color[2]; Color /= nNbrParameter; #else for( GW_U32 i=0; i<3; ++i ) { GW_Float rParam; GW_VoronoiVertex* pParamVert = GeoVert.GetParameterVertex( i, rParam ); if( pParamVert!=NULL ) color[i] = (float) rParam; else color[i] = 0; } #endif } if( bProrieties[kStoppingVertex] && GeoVert.GetIsStoppingVertex() ) { color[0] = color[2] = 0; color[1] = 1; } #if 0 // just for debugging T_GeodesicVertexList& VertList = GW_Toolkit::GetStartVertex(); if( !VertList.empty() ) { GW_GeodesicVertex* pVert = VertList.front(); GW_Float rDist = ~(pVert->GetPosition() - GeoVert.GetPosition()); rDist = GW_ABS( rDist - GeoVert.GetDistance() ); color[0] = color[1] = color[2] = rDist*5; } #endif }
/*------------------------------------------------------------------------------*/ GW_Float GW_TriangularInterpolation_Linear::ComputeValue( GW_GeodesicVertex& v0, GW_GeodesicVertex& v1, GW_GeodesicVertex& v2, GW_Float x, GW_Float y ) { return v0.GetDistance()*x + v1.GetDistance()*y + v2.GetDistance()*(1-x-y); }
/*------------------------------------------------------------------------------*/ 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; } }
GW_Float HeuristicCallback( GW_GeodesicVertex& Vert ) { // return the heuristic distance GW_U32 i = Vert.GetID(); return H[i]; }
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; }
GW_Float WeightCallback(GW_GeodesicVertex& Vert) { GW_U32 i = Vert.GetID(); return Ww[i]; }