#ifndef VCG_BITQUAD_SUPPORT #define VCG_BITQUAD_SUPPORT #include #include #include #include #include /** BIT-QUAD creation support: a few basic operations to work with bit-quads simplices (quads defined by faux edges over a tri mesh backbone) [ basic operations: ] bool IsDoublet(const FaceType& f, int wedge) void RemoveDoublet(FaceType &f, int wedge, MeshType& m) - identifies and removed "Doublets" (pair of quads sharing two consecutive edges) bool IsSinglet(const FaceType& f, int wedge) void RemoveSinglet(FaceType &f, int wedge, MeshType& m) void FlipDiag(FaceType &f) - rotates the faux edge of a quad (quad only change internally) bool RotateEdge(FaceType& f, int w0a); - rotate a quad edge (clockwise or counterclockwise, specified via template) bool RotateVertex(FaceType &f, int w0) - rotate around a quad vertex ("wind-mill" operation) void CollapseDiag(FaceType &f, ... p , MeshType& m) - collapses a quad on its diagonal. - p identifies the pos of collapsed point (as either the parametric pos on the diagonal, or a fresh coordtype) [ helper functions: ] ScalarType quadQuality( ... ); - returns the quality for a given quad - (should be made into a template parameter for methods using it) - currently measures how squared each angle is int FauxIndex(const FaceType* f); - returns index of the only faux edge of a quad (otherwise, assert) int CountBitPolygonInternalValency(const FaceType& f, int wedge) - returns valency of vertex in terms of polygons (quads, tris...) */ // these should become a parameter in the corresponding class #define DELETE_VERTICES 1 // Reason not to delete vertices: // if not vertex TwoManyfold, the vertex could still be used elsewhere... // if one, use length to determine if rotations are profitable // if zero, maximize conformal quality #define LENGTH_CRITERION 1 namespace vcg{namespace tri{ /* simple geometric-interpolation mono-function class used as a default template parameter to BitQuad class */ template class GeometricInterpolator{ public: typedef typename VertexType::ScalarType ScalarType; static void Apply( const VertexType &a, const VertexType &b, ScalarType t, VertexType &res){ /*assert (&a != &b);*/ res.P() = a.cP()*(1-t) + b.cP()*(t); if (a.IsB()||b.IsB()) res.SetB(); } }; template < // first template parameter: the tri mesh (with face-edges flagged) class _MeshType, // second template parameter: used to define interpolations between points class Interpolator = GeometricInterpolator > class BitQuad{ public: typedef _MeshType MeshType; typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::CoordType CoordType; typedef typename MeshType::FaceType FaceType; typedef typename MeshType::FaceType* FaceTypeP; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::FaceIterator FaceIterator; typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::VertexPointer VertexPointer; class Pos{ FaceType *f; int e; public: enum{ PAIR, AROUND , NOTHING } mode; FaceType* &F(){return f;} FaceType* F() const {return f;} VertexType* V() {return f->V(e);} const VertexType* cV() const {return f->V(e);} int& E(){return e;} int E() const {return e;} Pos(){ f=NULL; e=0; mode=AROUND;} Pos(FaceType* _f, int _e){f=_f; e=_e;} Pos NextE()const {return Pos(f, (e+1)%3); } Pos PrevE(){return Pos(f, (e+2)%3); } bool IsF(){return f->IsF(e);} Pos FlipF(){return Pos(f->FFp(e), f->FFi(e)); } }; static void MarkFaceF(FaceType *f){ f->V(0)->SetS(); f->V(1)->SetS(); f->V(2)->SetS(); int i=FauxIndex(f); f->FFp( i )->V2( f->FFi(i) )->SetS(); f->V(0)->SetV(); f->V(1)->SetV(); f->V(2)->SetV(); f->FFp( i )->V2( f->FFi(i) )->SetV(); } template static bool RotateEdge(FaceType& f, int w0a, MeshType &m, Pos *affected=NULL){ FaceType *fa = &f; assert(! fa->IsF(w0a) ); VertexType *v0, *v1; v0= fa->V0(w0a); v1= fa->V1(w0a); // int w1a = (w0a+1)%3; int w2a = (w0a+2)%3; FaceType *fb = fa->FFp(w0a); MarkFaceF(fa); MarkFaceF(fb); int w0b = fa->FFi(w0a); // int w1b = (w0b+1)%3; int w2b = (w0b+2)%3; if (fa->IsF(w2a) == verse) { if (!CheckFlipDiag(*fa)) return false; FlipDiag(*fa); // hack: recover edge index, so that (f, w0a) identifies the same edge as before fa = fb->FFp(w0b); w0a = fb->FFi(w0b); } if (fb->IsF(w2b) == verse) { if (!CheckFlipDiag(*fb)) return false; FlipDiag(*fb); } if (!CheckFlipEdge(*fa,w0a)) return false; FlipEdge(*fa,w0a,m); if (affected) { affected->F() = fa; affected->E() = (FauxIndex(fa)+2)%3; affected->mode = Pos::PAIR; } return true; } /* small helper function which returns the index of the only faux index, assuming there is exactly one (asserts out otherwise) */ static int FauxIndex(const FaceType* f){ if (f->IsF(0)) return 0; if (f->IsF(1)) return 1; assert(f->IsF(2)); return 2; } // rotates the diagonal of a quad static void FlipDiag(FaceType &f){ int faux = FauxIndex(&f); FaceType* fa = &f; FaceType* fb = f.FFp(faux); vcg::face::FlipEdge(f, faux); // ripristinate faux flags fb->ClearAllF(); fa->ClearAllF(); for (int k=0; k<3; k++) { if (fa->FFp(k) == fb) fa->SetF(k); if (fb->FFp(k) == fa) fb->SetF(k); } } // given a vertex (i.e. a face and a wedge), // this function tells us how the totale edge length around a vertex would change // if that vertex is rotated static ScalarType EdgeLenghtVariationIfVertexRotated(const FaceType &f, int w0) { assert(!f.IsD()); ScalarType before=0, // sum of quad edges (originating from v) after=0; // sum of quad diag (orginating from v) int guard = 0; // rotate arond vertex const FaceType* pf = &f; int pi = w0; int n = 0; // vertex valency int na = 0; do { ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm(); if (pf->IsF(pi)) { after += triEdge; na++;} else { before+= triEdge; n++; } if ( pf->IsF((pi+1)%3)) { after += CounterDiag( pf ).Norm(); na++; } const FaceType *t = pf; t = pf->FFp( pi ); if (pf == t ) return std::numeric_limits::max(); // it's a mesh border! flee! pi = pf->cFFi( pi ); pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); pf = t; assert(guard++<100); } while (pf != &f); assert (na == n); return (after-before); } // given a vertex (i.e. a face and a wedge), // this function tells us how the totale edge length around a vertex would change // if that vertex is rotated static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0) { assert(!f.IsD()); ScalarType before=0, // sum of quad quality around v after=0; // same after the collapse int guard = 0; // rotate arond vertex const FaceType* pf = &f; int pi = w0; int nb = 0; // vertex valency int na = 0; std::vector s; // 1 star around v do { // ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm(); if (!pf->IsF(pi)) { if ( pf->IsF((pi+1)%3)) { s.push_back(pf->cFFp((pi+1)%3)->V2( pf->cFFi((pi+1)%3) )); } else { s.push_back( pf->V2(pi) ); } s.push_back( pf->V1(pi) ); } const FaceType *t = pf; t = pf->FFp( pi ); if (pf == t ) return std::numeric_limits::max(); // it's a mesh border! flee! pi = pf->cFFi( pi ); pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); pf = t; assert(guard++<100); } while (pf != &f); assert(s.size()%2==0); int N = s.size(); for (int i=0; iP(),s[j]->P(),s[k]->P(),f.P(w0) ); after+=quadQuality( s[h]->P(),s[i]->P(),s[j]->P(),f.P(w0) ); } assert (na == nb); return (after-before); } /* const FaceType* pf = &f; int pi = wedge; int res = 0, guard=0; do { if (!pf->IsAnyF()) return false; // there's a triangle! if (!pf->IsF(pi)) res++; const FaceType *t = pf; t = pf->FFp( pi ); if (pf == t ) return false; pi = pf->cFFi( pi ); pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); pf = t; assert(guard++<100); } while (pf != &f); */ // given a vertex (i.e. a face and a wedge), // this function tells us if it should be rotated or not // (currently, we should iff it is shortened) static bool TestVertexRotation(const FaceType &f, int w0) { assert(!f.IsD()); #if (LENGTH_CRITERION) // rotate vertex IFF this way edges become shorter: return EdgeLenghtVariationIfVertexRotated(f,w0)<0; #else // rotate vertex IFF overall Quality increase #endif return QuadQualityVariationIfVertexRotated(f,w0)<0; } static bool RotateVertex(FaceType &f, int w0, MeshType &/*m*/, Pos *affected=NULL) { // int guard = 0; FaceType* pf = &f; int pi = w0; if (pf->IsF((pi+2) % 3)) { pi = (pi+2)%3; // do one step back int tmp = pf->FFi(pi); pf = pf->FFp(pi); pi = tmp; // flipF } const FaceType* stopA = pf; const FaceType* stopB = pf->FFp(FauxIndex(pf)); // rotate around vertex, flipping diagonals if necessary, do { bool mustFlip; if (pf->IsF(pi)) { // if next edge is faux, move on other side of quad int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF mustFlip = false; } else { mustFlip = true; } FaceType *lastF = pf; int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF if (mustFlip) { if (!CheckFlipDiag(*lastF)) return false; // cannot flip?? FlipDiag(*lastF); } MarkFaceF(pf); } while (pf != stopA && pf!= stopB); // last pass: rotate arund vertex again, changing faux status stopA=pf; do { int j = pi; if (pf->IsF(j)) { pf->ClearF(j); IncreaseValency(pf->V1(j)); } else { pf->SetF(j); DecreaseValencySimple(pf->V1(j),1); } j = (j+2)%3; if (pf->IsF(j)) pf->ClearF(j); else pf->SetF(j); int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF flipV } while (pf != stopA ); if (affected) { affected->F() = pf; affected->E()=pi; } return true; } // flips the faux edge of a quad static void FlipEdge(FaceType &f, int k, MeshType &m){ assert(!f.IsF(k)); FaceType* fa = &f; FaceType* fb = f.FFp(k); assert(fa!=fb); // else, rotating a border edge // backup prev other-quads-halves FaceType* fa2 = fa->FFp( FauxIndex(fa) ); FaceType* fb2 = fb->FFp( FauxIndex(fb) ); IncreaseValency( fa->V2(k) ); IncreaseValency( fb->V2(f.FFi(k)) ); //DecreaseValency( fa->V0(k) ); //DecreaseValency( fa->V1(k) ); DecreaseValency(fa, k ,m); DecreaseValency(fa,(k+1)%3,m ); vcg::face::FlipEdge(*fa, k); // ripristinate faux flags fb->ClearAllF(); fa->ClearAllF(); for (int k=0; k<3; k++) { //if (fa->FFp(k) == fa2) fa->SetF(k); //if (fb->FFp(k) == fb2) fb->SetF(k); if (fa->FFp(k)->IsF( fa->FFi(k) )) fa->SetF(k); if (fb->FFp(k)->IsF( fb->FFi(k) )) fb->SetF(k); } } // check if a quad diagonal can be topologically flipped static bool CheckFlipDiag(FaceType &f){ return (vcg::face::CheckFlipEdge(f, FauxIndex(&f) ) ); } // given a face (part of a quad), returns its diagonal static CoordType Diag(const FaceType* f){ int i = FauxIndex(f); return f->P1( i ) - f->P0( i ); } // given a face (part of a quad), returns other diagonal static CoordType CounterDiag(const FaceType* f){ int i = FauxIndex(f); return f->cP2( i ) - f->cFFp( i )->cP2(f->cFFi(i) ) ; } /* helper function: collapses a single face along its faux edge. Updates FF adj of other edges. */ static void _CollapseDiagHalf(FaceType &f, int faux, MeshType& /*m*/) { int faux1 = (faux+1)%3; int faux2 = (faux+2)%3; FaceType* fA = f.FFp( faux1 ); FaceType* fB = f.FFp( faux2 ); MarkFaceF(fA); MarkFaceF(fB); int iA = f.FFi( faux1 ); int iB = f.FFi( faux2 ); if (fA==&f && fB==&f) { // both non-faux edges are borders: tri-face disappears, just remove the vertex //if (DELETE_VERTICES) //if (GetValency(f.V(faux2))==0) Allocator::DeleteVertex(m,*(f.V(faux2))); } else { if (fA==&f) { fB->FFp(iB) = fB; fB->FFi(iB) = iB; } else { fB->FFp(iB) = fA; fB->FFi(iB) = iA; } if (fB==&f) { fA->FFp(iA) = fA; fA->FFi(iA) = iA; } else { fA->FFp(iA) = fB; fA->FFi(iA) = iB; } } //DecreaseValency(&f,faux2,m); // update valency //Allocator::DeleteFace(m,f); } static void RemoveDoublet(FaceType &f, int wedge, MeshType& m, Pos* affected=NULL){ if (f.IsF((wedge+1)%3) ) { VertexType *v = f.V(wedge); FlipDiag(f); // quick hack: recover wedge index after flip if (f.V(0)==v) wedge = 0; else if (f.V(1)==v) wedge = 1; else { assert(f.V(2)==v); wedge = 2; } } ScalarType k=(f.IsF(wedge))?1:0; CollapseDiag(f, k, m, affected); VertexType *v = f.V(wedge); } static void RemoveSinglet(FaceType &f, int wedge, MeshType& m, Pos* affected=NULL){ if (affected) affected->mode = Pos::NOTHING; // singlets leave nothing to update behind if (f.V(wedge)->IsB()) return; // hack: lets detect FaceType *fa, *fb; // these will die FaceType *fc, *fd; // their former neight fa = & f; fb = fa->FFp(wedge); int wa0 = wedge; int wa1 = (wa0+1)%3 ; int wa2 = (wa0+2)%3 ; int wb0 = (fa->FFi(wa0)+1)%3; int wb1 = (wb0+1)%3 ; // int wb2 = (wb0+2)%3 ; assert (fb == fa->FFp( wa2 ) ); // otherwise, not a singlet // valency decrease DecreaseValency(fa, wa1, m); DecreaseValency(fa, wa2, m); if (fa->IsF(wa0)) { DecreaseValency(fa,wa2,m); // double decrease of valency on wa2 } else { DecreaseValency(fa,wa1,m); // double decrease of valency on wa1 } // no need to MarkFaceF ! fc = fa->FFp(wa1); fd = fb->FFp(wb1); int wc = fa->FFi(wa1); int wd = fb->FFi(wb1); fc->FFp(wc) = fd; fc->FFi(wc) = wd; fd->FFp(wd) = fc; fd->FFi(wd) = wc; // faux status of survivors: unchanged assert( ! ( fc->IsF( wc) ) ); assert( ! ( fd->IsF( wd) ) ); Allocator::DeleteFace( m,*fa ); Allocator::DeleteFace( m,*fb ); DecreaseValency(fa,wedge,m ); //if (DELETE_VERTICES) //if (GetValency(fa->V(wedge))==0) Allocator::DeleteVertex( m,*fa->V(wedge) ); } static bool TestAndRemoveDoublet(FaceType &f, int wedge, MeshType& m){ if (IsDoublet(f,wedge)) { RemoveDoublet(f,wedge,m); return true; } return false; } static bool TestAndRemoveSinglet(FaceType &f, int wedge, MeshType& m){ if (IsSinglet(f,wedge)) { RemoveSinglet(f,wedge,m); return true; } return false; } // given a face and a wedge, counts its valency in terms of quads (and triangles) // uses only FF, assumes twomanyfold // returns -1 if border static int CountBitPolygonInternalValency(const FaceType& f, int wedge){ const FaceType* pf = &f; int pi = wedge; int res = 0; do { if (!pf->IsF(pi)) res++; const FaceType *t = pf; t = pf->FFp( pi ); if (pf == t ) return -1; pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); pf = t; } while (pf != &f); return res; } // given a face and a wedge, returns if it host a doubet // assumes tri and quad only. uses FF topology only. static bool IsDoubletFF(const FaceType& f, int wedge){ const FaceType* pf = &f; int pi = wedge; int res = 0, guard=0; do { if (!pf->IsAnyF()) return false; // there's a triangle! if (!pf->IsF(pi)) res++; const FaceType *t = pf; t = pf->FFp( pi ); if (pf == t ) return false; pi = pf->cFFi( pi ); pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); pf = t; assert(guard++<100); } while (pf != &f); return (res == 2); } // version that uses vertex valency static bool IsDoublet(const FaceType& f, int wedge){ return (GetValency( f.V(wedge)) == 2) && (!f.V(wedge)->IsB() ) ; } static bool IsDoubletOrSinglet(const FaceType& f, int wedge){ return (GetValency( f.V(wedge)) <= 2) && (!f.V(wedge)->IsB() ) ; } static bool RemoveDoubletOrSinglet(FaceType& f, int wedge, MeshType& m, Pos* affected=NULL){ if (GetValency( f.V(wedge)) == 2) { RemoveDoublet(f,wedge,m,affected) ; return true; } assert (GetValency( f.V(wedge)) == 1) ; RemoveSinglet(f,wedge,m,affected) ; return true; } // given a face and a wedge, returns if it host a singlets // assumes tri and quad only. uses FF topology only. static bool IsSingletFF(const FaceType& f, int wedge){ const FaceType* pf = &f; int pi = wedge; int res = 0, guard=0; do { if (!pf->IsAnyF()) return false; // there's a triangle! if (!pf->IsF(pi)) res++; const FaceType *t = pf; t = pf->FFp( pi ); if (pf == t ) return false; pi = pf->cFFi( pi ); pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); pf = t; assert(guard++<100); } while (pf != &f); return (res == 1); } // version that uses vertex valency static bool IsSinglet(const FaceType& f, int wedge){ return (GetValency( f.cV(wedge) ) == 1) && (!f.cV(wedge)->IsB() ) ; } static bool CollapseEdgeDirect(FaceType &f, int w0, MeshType& m){ FaceType * f0 = &f; assert( !f0->IsF(w0) ); VertexType *v0, *v1; v0 = f0->V0(w0); v1 = f0->V1(w0); if (!RotateVertex(*f0,w0,m)) return false; // quick hack: recover original wedge if (f0->V(0) == v0) w0 = 0; else if (f0->V(1) == v0) w0 = 1; else if (f0->V(2) == v0) w0 = 2; else assert(0); assert( f0->V1(w0) == v1 ); assert( f0->IsF(w0) ); return CollapseDiag(*f0,PosOnDiag(*f0,false), m); } // collapses an edge. Optional output pos can be iterated around to find affected faces static bool CollapseEdge(FaceType &f, int w0, MeshType& m, Pos *affected=NULL){ FaceTypeP f0 = &f; assert(!f0->IsF(w0)); // don't use this method to collapse diag. if (IsDoubletOrSinglet(f,w0)) return false; //{ RemoveDoubletOrSinglet(f,w0,m, affected); return true;} if (IsDoubletOrSinglet(f,(w0+1)%3)) return false; //{ RemoveDoubletOrSinglet(f,(w0+1)%3,m, affected); return true;} if (affected) { int w1 = 3-w0-FauxIndex(f0); // the edge whihc is not the collapsed one nor the faux affected->F() = f0->FFp(w1); affected->E() = (f0->FFi(w1)+2+w1-FauxIndex(f0))%3; } FaceTypeP f1 = f0->FFp(w0); int w1 = f0->FFi(w0); assert(f0!=f1); // can't collapse border edges! // choose: rotate around V0 or around V1? if ( EdgeLenghtVariationIfVertexRotated(*f0,w0) < EdgeLenghtVariationIfVertexRotated(*f1,w1) ) return CollapseEdgeDirect(*f0,w0,m); else return CollapseEdgeDirect(*f1,w1,m); } /** collapses a quad diagonal a-b forming the new vertex in between the two old vertices. if k == 0, new vertex is in a if k == 1, new vertex is in b if k == 0.5, new vertex in the middle, etc */ static bool CollapseCounterDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){ if (!CheckFlipDiag(f)) return false; FlipDiag(f); return CollapseDiag(f,interpol,m,affected); } // rotates around vertex class Iterator{ private: typedef typename face::Pos FPos; Pos start, cur; bool over; public: Iterator(Pos& pos){ if (pos.mode==Pos::NOTHING) {over = true; return; } start = pos; //FPos(pos.F(), pos.E()); if (start.F()->IsD()) { over = true; return;} assert(!start.F()->IsD()); if (pos.mode==Pos::AROUND) { if (start.F()->IsF((start.E()+2)%3)) { int i = start.F()->FFi( start.E() ); start.F() = start.F()->FFp( start.E() ); start.E() = (i+1)%3; } } cur=start; over = false; } bool End() const { return over; } void operator ++ () { if (start.mode==Pos::PAIR) { if (cur.F()!=start.F()) over=true; int i = (cur.E()+2)%3; cur.E() = (cur.F()->FFi( i )+1)%3; cur.F() = cur.F()->FFp( i ); } else { if (cur.F()->IsF(cur.E())) { // jump over faux diag int i = cur.F()->FFi( cur.E() ); cur.F() = cur.F()->FFp( cur.E() ); cur.E() = (i+1)%3; } // jump over real edge FaceType *f =cur.F()->FFp( cur.E() ); if (f==cur.F()) over=true; // border found cur.E() = (cur.F()->FFi( cur.E() ) +1 )%3; cur.F() = f; if (cur.F()==start.F()) over=true; } } Pos GetPos(){ return cur; } }; static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){ FaceType* fa = &f; // fa lives int fauxa = FauxIndex(fa); //if (IsDoubletOrSinglet(f,fauxa)) { RemoveDoubletOrSinglet(f,fauxa,m, affected); return true;} // if (IsDoubletOrSinglet(f,(fauxa+2)%3)) { RemoveDoubletOrSinglet(f,(fauxa+2)%3,m, affected); return true;} if (IsDoubletOrSinglet(f,(fauxa+2)%3)) return false; if (IsDoubletOrSinglet(*(f.FFp(fauxa)),(f.FFi(fauxa)+2)%3)) return false; if (affected) { int w1 = (fauxa+2)%3; // any edge but not the faux affected->F() = fa->FFp(w1); affected->E() = fa->FFi(w1); if (affected->F() == fa){ int w1 = (fauxa+1)%3; // any edge but not the faux affected->F() = fa->FFp(w1); affected->E() = (fa->FFi(w1)+2)%3; } } FaceType* fb = fa->FFp(fauxa); // fb dies assert (fb!=fa); // otherwise, its a singlet int fauxb = FauxIndex(fb); VertexType* va = fa->V(fauxa); // va lives VertexType* vb = fb->V(fauxb); // vb dies Interpolator::Apply( *(f.V0(fauxa)), *(f.V1(fauxa)), interpol, *va); bool border = false; int val =0; // number of faces around vb, which dies // update FV... // rotate around vb, (same-sense-as-face)-wise int pi = fauxb; FaceType* pf = fb; /* pf, pi could be put in a Pos p(pb, fauxb) */ do { //pf->V(pi) = va; if (((pf->V2(pi) == va)||(pf->V1(pi) == va)) &&(pf!=fa)&&(pf!=fb)) return false; pi=(pi+2)%3; FaceType *t = pf->FFp(pi); if (t==pf) { border= true; break; } pi = pf->FFi(pi); pf = t; } while ((pf!=fb)); pi = fauxb; pf = fb; do { pf->V(pi) = va; pi=(pi+2)%3; FaceType *t = pf->FFp(pi); if (t==pf) { border= true; break; } if (!pf->IsF(pi)) val++; pi = pf->FFi(pi); pf = t; } while (pf!=fb); // of found a border, also rotate around vb, (counter-sense-as-face)-wise if (border) { val++; int pi = fauxa; FaceType* pf = fa; /* pf, pi could be a Pos p(pf, pi) */ do { pi=(pi+1)%3; pf->V(pi) = va; FaceType *t = pf->FFp(pi); if (t==pf) break; if (!pf->IsF(pi)) val++; pi = pf->FFi(pi); pf = t; } while (pf!=fb); } // update FF, delete faces _CollapseDiagHalf(*fb, fauxb, m); _CollapseDiagHalf(*fa, fauxa, m); SetValency(va, GetValency(va)+val-2); DecreaseValency(fb,(fauxb+2)%3,m); // update valency DecreaseValency(fa,(fauxa+2)%3,m); // update valency Allocator::DeleteFace(m,*fa); Allocator::DeleteFace(m,*fb); //assert(val == GetValency(vb)); DecreaseValencyNoSingletTest(vb, val, m); // note: don't directly kill vb. In non-twomanifold, it could still be referecned // but: don't hunt for doublets either. assert(GetValency(vb)!=1 || vb->IsB()); // if this asserts, you are in trouble. // It means that the vertex that was supposed to die is still attached // somewhere else (non-twomanifold) // BUT in its other attachments it is a singlet, and that singlet cannot be // found now (would require VF) return true; } // helper function: find a good position on a diag to collapse a point // currently, it is point in the middle, // unless a mixed border-non border edge is collapsed, then it is an exreme static ScalarType PosOnDiag(const FaceType& f, bool counterDiag){ bool b0, b1, b2, b3; // which side of the quads are border const FaceType* fa=&f; int ia = FauxIndex(fa); const FaceType* fb=fa->cFFp(ia); int ib = fa->cFFi(ia); b0 = fa->FFp((ia+1)%3) == fa; b1 = fa->FFp((ia+2)%3) == fa; b2 = fb->FFp((ib+1)%3) == fb; b3 = fb->FFp((ib+2)%3) == fb; if (counterDiag) { if ( (b0||b1) && !(b2||b3) ) return 1; if ( !(b0||b1) && (b2||b3) ) return 0; } else { if ( (b1||b2) && !(b3||b0) ) return 0; if ( !(b1||b2) && (b3||b0) ) return 1; } //if (f->FF( FauxIndex(f) )->IsB( return 0.5f; } // trick! hide valency in flags typedef enum { VALENCY_FLAGS = 24 } ___; // this bit and the 4 successive one are devoted to store valency static void SetValency(VertexType *v, int n){ //v->Q() = n; assert(n>=0 && n<=255); v->Flags()&= ~(255<Flags()|= n<cQ()); return ( v->cFlags() >> (VALENCY_FLAGS) ) & 255; } static void IncreaseValency(VertexType *v, int dv=1){ #ifdef NDEBUG v->Flags() += dv<Flags() -= dv<V(wedge); int val = GetValency(v)-1; SetValency( v, val ); if (val==0) Allocator::DeleteVertex(m,*v); if (val==1) // singlet! RemoveSinglet(*f,wedge,m); // this could be recursive... } // decrease valency, remove unreferenced vertices too, but don't check for singlets... static void DecreaseValencyNoSingletTest(VertexType *v, int dv, MeshType &m){ int val = GetValency(v)-dv; SetValency( v, val ); if (DELETE_VERTICES) if (val==0) Allocator::DeleteVertex(m,*v); } static void DecreaseValencySimple(VertexType *v, int dv){ int val = GetValency(v)-dv; SetValency( v, val ); } static void UpdateValencyInFlags(MeshType& m){ for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) { SetValency(&*vi,0); } for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { for (int w=0; w<3; w++) if (!fi->IsF(w)) IncreaseValency( fi->V(w)); } } static void UpdateValencyInQuality(MeshType& m){ for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) { vi->Q() = 0; } for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { for (int w=0; w<3; w++) fi->V(w)->Q() += (fi->IsF(w)||fi->IsF((w+2)%3) )? 0.5f:1; } } static bool HasConsistentValencyFlag(MeshType &m) { UpdateValencyInQuality(m); bool isok=true; for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { for (int k=0; k<3; k++) if (GetValency(fi->V(k))!=fi->V(k)->Q()){ MarkFaceF(&*fi); isok=false; } } return isok; } // helper function: // returns quality of a given (potential) quad static ScalarType quadQuality(FaceType *f, int edge){ CoordType a = f->V0(edge)->P(), b = f->FFp(edge)->V2( f->FFi(edge) )->P(), c = f->V1(edge)->P(), d = f->V2(edge)->P(); return quadQuality(a,b,c,d); } /** helper function: given a quad edge, retruns: 0 if that edge should not be rotated +1 if it should be rotated clockwise (+1) -1 if it should be rotated counterclockwise (-1) Currently an edge is rotated iff it is shortened by that rotations (shortcut criterion) */ static int TestEdgeRotation(const FaceType &f, int w0, ScalarType *gain=NULL) { const FaceType *fa = &f; assert(! fa->IsF(w0) ); ScalarType q0,q1,q2; CoordType v0,v1,v2,v3,v4,v5; int w1 = (w0+1)%3; int w2 = (w0+2)%3; v0 = fa->P(w0); v3 = fa->P(w1); if (fa->IsF(w2) ) { v1 = fa->cFFp(w2)->V2( fa->cFFi(w2) )->P(); v2 = fa->P(w2); } else { v1 = fa->P(w2); v2 = fa->cFFp(w1)->V2( fa->cFFi(w1) )->P(); } const FaceType *fb = fa->cFFp(w0); w0 = fa->cFFi(w0); w1 = (w0+1)%3; w2 = (w0+2)%3; if (fb->IsF(w2) ) { v4 = fb->cFFp(w2)->V2( fb->cFFi(w2) )->P(); v5 = fb->P(w2); } else { v4 = fb->P(w2); v5 = fb->cFFp(w1)->V2( fb->cFFi(w1) )->P(); } #if (!LENGTH_CRITERION) // max overall CONFORMAL quality criterion: q0 = quadQuality(v0,v1,v2,v3) + quadQuality(v3,v4,v5,v0); // keep as is? q1 = quadQuality(v1,v2,v3,v4) + quadQuality(v4,v5,v0,v1); // rotate CW? q2 = quadQuality(v5,v0,v1,v2) + quadQuality(v2,v3,v4,v5); // rotate CCW? if (q0>=q1 && q0>=q2) return 0; if (q1>=q2) return 1; #else // min distance (shortcut criterion) q0 = (v0 - v3).SquaredNorm(); q1 = (v1 - v4).SquaredNorm(); q2 = (v5 - v2).SquaredNorm(); if (q0<=q1 && q0<=q2) return 0; // there's no rotation shortening this edge //static int stop=0; //static int go=0; //if ((stop+go)%100==99) printf("Stop: %4.1f%%\n",(stop*100.0/(stop+go)) ); if (q1<=q2) { if (gain) *gain = sqrt(q1)-sqrt(q0); // test: two diagonals should become shorter (the other two reamin the same) if ( (v0-v2).SquaredNorm() < (v4-v2).SquaredNorm() || (v3-v5).SquaredNorm() < (v1-v5).SquaredNorm() ) { //stop++; return 0; } //go++; return 1; } { if (gain) *gain = sqrt(q2)-sqrt(q0); // diagonal test, as above: if ( (v0-v4).SquaredNorm() < (v2-v4).SquaredNorm() || (v3-v1).SquaredNorm() < (v5-v1).SquaredNorm() ) { //stop++; return 0; } //go++; return -1; } #endif } private: // helper function: // returns quality of a quad formed by points a,b,c,d // quality is computed as "how squared angles are" static ScalarType quadQuality(const CoordType &a, const CoordType &b, const CoordType &c, const CoordType &d){ ScalarType score = 0; score += 1 - math::Abs( Cos( a,b,c) ); score += 1 - math::Abs( Cos( b,c,d) ); score += 1 - math::Abs( Cos( c,d,a) ); score += 1 - math::Abs( Cos( d,a,b) ); return score / 4; } private: // helper function: // cos of angle abc. This should probably go elsewhere static ScalarType Cos(const CoordType &a, const CoordType &b, const CoordType &c ) { CoordType e0 = b - a, e1 = b - c; ScalarType d = (e0.Norm()*e1.Norm()); if (d==0) return 0.0; return (e0*e1)/d; } public: /** Generic quad triangulation function. It take in input 4 vertex pointrs and rotate them so that a simple fan triangulation is Ok. It uses geometric criteria for avoiding bad shaped triangles, and folds and it use an internal set of already created diagonal to avoid the creation of non manifold situations. At the begin you shoud call this function with an empty vector to reset the set of existing diagonals. */ static void QuadTriangulate(std::vector &q) { typedef typename std::set > diagSetType; static diagSetType diagSet; // the set of already created diagonals if(q.size()!=4) { diagSet.clear(); return; } const CoordType &P0=q[0]->cP(); const CoordType &P1=q[1]->cP(); const CoordType &P2=q[2]->cP(); const CoordType &P3=q[3]->cP(); CoordType N00 = Normal(P0,P1,P2); CoordType N01 = Normal(P0,P2,P3); CoordType N10 = Normal(P1,P2,P3); CoordType N11 = Normal(P1,P3,P0); ScalarType Angle0Rad=Angle(N00,N01); ScalarType Angle1Rad=Angle(N10,N11); // QualityRadii is inradius/circumradius; bad when close to zero. // swap diagonal if the worst triangle improve. bool qualityImprove = std::min(QualityRadii(P0,P1,P2),QualityRadii(P0,P2,P3)) < std::min(QualityRadii(P1,P2,P3),QualityRadii(P1,P3,P0)); bool swapCauseFlip = (Angle1Rad > M_PI/2.0) && (Angle0Rad res; if(q[0]