From f4bc92f6f7e303043e500c4b46ed28e31f176c89 Mon Sep 17 00:00:00 2001 From: mtarini Date: Tue, 30 Jun 2009 14:09:09 +0000 Subject: [PATCH] Bitquad_* first version. --- vcg/complex/trimesh/bitquad_creation.h | 698 +++++++++++++++++++++ vcg/complex/trimesh/bitquad_optimization.h | 118 ++++ vcg/complex/trimesh/bitquad_support.h | 515 +++++++++++++++ 3 files changed, 1331 insertions(+) create mode 100644 vcg/complex/trimesh/bitquad_creation.h create mode 100644 vcg/complex/trimesh/bitquad_optimization.h create mode 100644 vcg/complex/trimesh/bitquad_support.h diff --git a/vcg/complex/trimesh/bitquad_creation.h b/vcg/complex/trimesh/bitquad_creation.h new file mode 100644 index 00000000..80882172 --- /dev/null +++ b/vcg/complex/trimesh/bitquad_creation.h @@ -0,0 +1,698 @@ + +//#include + +/** BIT-QUAD creation support: + a collection of methods that, + starting from a triangular mesh, will create your quad-only or quad-domainant mesh. + + They all require: + - per face Q, and FF connectivity, 2-manyfold meshes, + - and tri- or quad- meshes (no penta, etc) (if in need, use MakeBitTriOnly) + + +[ list of available methods: ] + +void MakeBitQuadOnlyByRefine(Mesh &m) + - adds a vertex for each tri or quad present + - thus, miminal complexity increase is the mesh is quad-dominant already + - old non-border edges are made faux + - never fails + +void MakeBitQuadOnlyByCatmullClark(Mesh &m) + - adds a vertex in each (non-faux) edge. + - twice complexity increase w.r.t. "ByRefine" method. + - preserves edges: old edges are still edges + - never fails + +bool MakeBitQuadOnlyByFlip(Mesh &m [, int maxdist] ) + - does not increase # vertices, just flips edges + - call in a loop until it returns true (temporary hack) + - fails if number of triangle is odd (only happens in open meshes) + - add "StepByStep" to method name if you want it to make a single step (debugging purposes) + +bool MakeTriEvenBySplit(Mesh& m) +bool MakeTriEvenByDelete(Mesh& m) + - two simple variants that either delete or split *at most one* border face + so that the number of tris will be made even. Return true if it did it. + - useful to use the previous method, when mesh is still all triangle + +void MakeBitQuadDominant(Mesh &m, int level) + - just merges traingle pairs into quads, trying its best + - various heuristic available, see descr. for parameter "level" + - provides good starting point for make-Quad-Only methods + - uses an ad-hoc measure for "quad quality" (which is hard-wired, for now) + +void MakeBitTriOnly(Mesh &m) + - inverse process: returns to tri-only mesh + + +(more info in comments before each method) + +*/ + +namespace vcg{namespace tri{ + + +// helper function: +// given a triangle, merge it with its best neightboord to form a quad +template +static void selectBestQuadDiag(Face *fi){ + + typedef typename Face::ScalarType ScalarType; + typedef typename Face::VertexType VertexType; + if (!override) { + if (fi->IsAnyF()) return; + } + + // select which edge to make faux (if any)... + int whichEdge = -1; + ScalarType bestScore = fi->Q(); + + whichEdge=-1; + + for (int k=0; k<3; k++){ + + // todo: check creases? (continue if edge k is a crease) + + if (!override) { + if (fi->FFp(k)->IsAnyF()) continue; + } + if (fi->FFp(k)==fi) continue; // never make a border faux + + ScalarType score = quadQuality( &*fi, k ); + if (override) { + // don't override anyway iff other face has a better match + if (score < fi->FFp(k)->Q()) continue; + } + if (score>bestScore) { + bestScore = score; + whichEdge = k; + } + } + + // ...and make it faux + if (whichEdge>=0) { + //if (override && fi->FFp(whichEdge)->IsAnyF()) { + // new score is the average of both scores + // fi->Q() = fi->FFp(whichEdge)->Q() = ( bestScore + fi->FFp(whichEdge)->Q() ) /2; + //} else { + //} + + if (override) { + // clear any faux edge of the other face + for (int k=0; k<3; k++) + if (fi->FFp(whichEdge)->IsF(k)) { + fi->FFp(whichEdge)->ClearF(k); + fi->FFp(whichEdge)->FFp(k)->ClearF( fi->FFp(whichEdge)->FFi(k) ); + fi->FFp(whichEdge)->FFp(k)->Q()=0.0; // other face's ex-buddy is now single and sad :( + } + + // clear all faux edges of this face... + for (int k=0; k<3; k++) + if (fi->IsF(k)) { + fi->ClearF(k); + fi->FFp(k)->ClearF( fi->FFi(k) ); + fi->FFp(k)->Q()= 0.0; // my ex-buddy is now sad + } + } + // set (new?) quad + fi->SetF(whichEdge); + fi->FFp(whichEdge)->SetF( fi->FFi(whichEdge) ); + fi->Q() = fi->FFp(whichEdge)->Q() = bestScore; + + } + + +} + + + +// helper funcion: +// a pass though all triangles to merge triangle pairs into quads +template // override previous decisions? +static void MakeQuadDominantPass(Mesh &m){ + typedef typename Mesh::FaceType Face; + typedef typename Mesh::FaceIterator FaceIterator; + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + selectBestQuadDiag(&(*fi)); + } + +} + +// make tri count even by splitting a single triangle... +template +bool MakeTriEvenBySplit(Mesh& m){ + if (m.fn%2==0) return false; // it's already Even + assert(0); // todo! +} + +// make tri count even by delete... +template +bool MakeTriEvenByDelete(Mesh& m) +{ + + if (m.fn%2==0) return false; // it's already Even + + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::FaceType Face; + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) { + for (int k=0; k<3; k++) { + if (fi->FFp(k) == &* fi) { + + // mark the two old neight as border and not faux + for (int h=1; h<3; h++) { + int kh=(k+h)%3; + int j = fi->FFi( kh ); + Face *f = fi->FFp(kh); + if (f != &* fi) { + f->FFp( j ) = f; + f->FFi( j ) = j; + f->ClearF(j); + } + } + + // delete found face + Allocator::DeleteFace(m,*fi); + return true; + } + } + } + assert(0); // no border face found? then how could the number of tri be Odd? + return true; +} + + +/** + Given a mesh, makes it bit trianglular (makes all edges NOT faux) +*/ +template +void MakeBitTriOnly(Mesh &m){ + typedef typename Mesh::FaceIterator FaceIterator; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) { + fi->ClearAllF(); + } +} + +/** given a quad-and-tree mesh, enforces the "faux edge is 2nd edge" convention. + * Requires (and updates): FV and FF structure + * Updates: faux flags + * Updates: per wedge attributes, if any + * Other connectivity structures, and per edge and per wedge flags are ignored + */ +template +bool MakeBitTriQuadConventional(Mesh &m){ + assert(0); // todo +} + +/* returns true if mesh is a "conventional" quad mesh. + I.e. if it is all quads, with third edge faux fora all triangles*/ +template +bool IsBitTriQuadConventional(Mesh &m){ + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::FaceType FaceType; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (fi->IsAnyF()) + if ( fi->Flags() & ( FaceType::FAUX012 ) != FaceType::FAUX2 ) { + return false; + } + } + return true; +} + +/** + makes any mesh quad only by refining it so that a quad is created over all + previous diags + requires that the mesh is made only of quads and tris. +*/ +template +void MakeBitQuadOnlyByRefine(Mesh &m){ + + // todo: update VF connectivity if present + + + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::VertexIterator VertexIterator; + typedef typename Mesh::FaceType Face; + typedef typename Mesh::VertexType Vert; + + int ev = 0; // EXTRA vertices (times 2) + int ef = 0; // EXTRA faces + + // first pass: count triangles to be added + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + int k=0; // number of borders + if (fi->FFp(0) == &*fi) k++; + if (fi->FFp(1) == &*fi) k++; + if (fi->FFp(2) == &*fi) k++; + + if (!fi->IsAnyF()) { + // it's a triangle + if (k==0) // add a vertex in the center of the face, splitting it in 3 + { ev+=2; ef+=2; } + if (k==1) // add a vertex in the border edge, splitting it in 2 + { } + if (k==2) // do nothing, just mark the non border edge as faux + { } + if (k==3) // disconnected single triangle (all borders): make one edge as faus + { } + } + else { + // assuming is a quad (not a penta, etc), i.e. only one faux + // add a vertex in the center of the faux edge, splitting the face in 2 + ev+=1; ef+=1; + assert(k!=3); + } + } + assert(ev%2==0); // should be even by now + ev/=2; // I was counting each of them twice + + int originalFaceNum = m.fn; + FaceIterator nfi = tri::Allocator::AddFaces(m,ef); + VertexIterator nvi = tri::Allocator::AddVertices(m,ev); + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->ClearV(); + + + // second pass: add faces and vertices + int nsplit=0; // spits to be done on border in the third pass + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD() && !fi->IsV() ) { + + fi->SetV(); + + if (!fi->IsAnyF()) { + // it's a triangle + + int k=0; // number of borders + if (fi->FFp(0) == &*fi) k++; + if (fi->FFp(1) == &*fi) k++; + if (fi->FFp(2) == &*fi) k++; + if (k==0) // add a vertex in the center of the face, splitting it in 3 + { + assert(nvi!=m.vert.end()); + Vert *nv = &*nvi; nvi++; + *nv = *fi->V0( 0 ); // lazy: copy everything from the old vertex + nv->P() = ( fi->V(0)->P() + fi->V(1)->P() + fi->V(2)->P() ) /3.0; + Face *fa = &*fi; + Face *fb = &*nfi; nfi++; + Face *fc = &*nfi; nfi++; + *fb = *fc = *fa; // lazy: copy everything from the old faces + fa->V(0) = nv; + fb->V(1) = nv; + fc->V(2) = nv; + + assert( fa->FFp(1)->FFp(fa->FFi(1)) == fa ); + /* */fb->FFp(2)->FFp(fb->FFi(2)) = fb; + /* */fc->FFp(0)->FFp(fc->FFi(0)) = fc; + + fa->FFp(0) = fc; fa->FFp(2) = fb; fa->FFi(0) = fa->FFi(2) = 1; + fb->FFp(1) = fa; fb->FFp(0) = fc; fb->FFi(0) = fb->FFi(1) = 2; + fc->FFp(1) = fa; fc->FFp(2) = fb; fc->FFi(1) = fc->FFi(2) = 0; + + if (fb->FFp(2)==fa) fb->FFp(2)=fb; // recover border status + if (fc->FFp(0)==fa) fc->FFp(0)=fc; + + fa->ClearAllF(); + fb->ClearAllF(); + fc->ClearAllF(); + fa->SetF(1); + fb->SetF(2); + fc->SetF(0); + + fa->SetV();fb->SetV();fc->SetV(); + } + if (k==1) { // make a border face faux, anf other two as well + fi->SetF(0); + fi->SetF(1); + fi->SetF(2); + nsplit++; + } + if (k==2) // do nothing, just mark the non border edge as faux + { + fi->ClearAllF(); + for (int w=0; w<3; w++) if (fi->FFp(w) != &*fi) fi->SetF(w); + } + if (k==3) // disconnected single triangle (all borders): use catmull-clark (tree vertices, split it in 6 + { + fi->ClearAllF(); + fi->SetF(2); + nsplit++; + } + } + else { + // assuming is a part of quad (not a penta, etc), i.e. only one faux + Face *fa = &*fi; + int ea2 = FauxIndex(fa); // index of the only faux edge + Face *fb = fa->FFp(ea2); + int eb2 = fa->FFi(ea2); + assert(fb->FFp(eb2)==fa) ; + assert(fa->IsF(ea2)); + //assert(fb->IsF(eb2)); // reciprocal faux edge + + int ea0 = (ea2+1) %3; + int ea1 = (ea2+2) %3; + int eb0 = (eb2+1) %3; + int eb1 = (eb2+2) %3; + + // create new vert in center of faux edge + assert(nvi!=m.vert.end()); + Vert *nv = &*nvi; nvi++; + *nv = * fa->V0( ea2 ); + nv->P() = ( fa->V(ea2)->P() + fa->V(ea0)->P() ) /2.0; + + // split faces: add 2 faces (one per side) + assert(nfi!=m.face.end()); + Face *fc = &*nfi; nfi++; + assert(nfi!=m.face.end()); + Face *fd = &*nfi; nfi++; + *fc = *fa; + *fd = *fb; + + fa->V(ea2) = fc->V(ea0) = + fb->V(eb2) = fd->V(eb0) = nv ; + + fa->FFp(ea1)->FFp( fa->FFi(ea1) ) = fc; + fb->FFp(eb1)->FFp( fb->FFi(eb1) ) = fd; + + fa->FFp(ea1) = fc ; fa->FFp(ea2) = fd; + fa->FFi(ea1) = ea0; fa->FFi(ea2) = eb2; + fb->FFp(eb1) = fd ; fb->FFp(eb2) = fc; + fb->FFi(eb1) = eb0; fb->FFi(eb2) = ea2; + fc->FFp(ea0) = fa ; fc->FFp(ea2) = fb; + fc->FFi(ea0) = ea1; fc->FFi(ea2) = eb2; + fd->FFp(eb0) = fb ; fd->FFp(eb2) = fa; + fd->FFi(eb0) = eb1; fd->FFi(eb2) = ea2; + + // detect boundaries + bool ba = fa->FFp(ea0)==fa; + bool bc = fc->FFp(ea1)==fa; + bool bb = fb->FFp(eb0)==fb; + bool bd = fd->FFp(eb1)==fb; + + if (bc) fc->FFp(ea1)=fc; // repristinate boundary status + if (bd) fd->FFp(eb1)=fd; // of new faces + + fa->SetV(); + fb->SetV(); + fc->SetV(); + fd->SetV(); + + fa->ClearAllF(); + fb->ClearAllF(); + fc->ClearAllF(); + fd->ClearAllF(); + + fa->SetF( ea0 ); + fb->SetF( eb0 ); + fc->SetF( ea1 ); + fd->SetF( eb1 ); + + // fix faux mesh boundary... if two any consecutive, merge it in a quad + if (ba&&bc) { + fa->ClearAllF(); fa->SetF(ea1); + fc->ClearAllF(); fc->SetF(ea0); + ba = bc = false; + } + if (bc&&bb) { + fc->ClearAllF(); fc->SetF(ea2); + fb->ClearAllF(); fb->SetF(eb2); + bc = bb = false; + } + if (bb&&bd) { + fb->ClearAllF(); fb->SetF(eb1); + fd->ClearAllF(); fd->SetF(eb0); + bb = bd = false; + } + if (bd&&ba) { + fd->ClearAllF(); fd->SetF(eb2); + fa->ClearAllF(); fa->SetF(ea2); + bd = ba = false; + } + // remaninig boudaries will be fixed by splitting in the last pass + if (ba) nsplit++; + if (bb) nsplit++; + if (bc) nsplit++; + if (bd) nsplit++; + } + } + assert(nfi==m.face.end()); + assert(nvi==m.vert.end()); + + // now and there are no tris left, but there can be faces with ONE edge border & faux () + + // last pass: add vertex on faux border faces... (if any) + if (nsplit>0) { + FaceIterator nfi = tri::Allocator::AddFaces(m,nsplit); + VertexIterator nvi = tri::Allocator::AddVertices(m,nsplit); + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + Face* fa = &*fi; + int ea2 = -1; // border and faux face (if any) + if (fa->FFp(0)==fa && fa->IsF(0) ) ea2=0; + if (fa->FFp(1)==fa && fa->IsF(1) ) ea2=1; + if (fa->FFp(2)==fa && fa->IsF(2) ) ea2=2; + + if (ea2 != -1) { // ea2 edge is naughty (border AND faux) + + int ea0 = (ea2+1) %3; + int ea1 = (ea2+2) %3; + + // create new vert in center of faux edge + Vert *nv = &*nvi; nvi++; + *nv = * fa->V0( ea2 ); + nv->P() = ( fa->V(ea2)->P() + fa->V(ea0)->P() ) /2.0; + + // split face: add 1 face + Face *fc = &*nfi; nfi++; + *fc = *fa; + + fa->V(ea2) = fc->V(ea0) = nv ; + + fc->FFp(ea2) = fc; + + fa->FFp(ea1)->FFp( fa->FFi(ea1) ) = fc; + + fa->FFp(ea1) = fc ; + fa->FFi(ea1) = ea0; + fc->FFp(ea0) = fa ; fc->FFp(ea2) = fc; + fc->FFi(ea0) = ea1; + + if (fc->FFp(ea1)==fa) fc->FFp(ea1)=fc; // recover border status + + assert(fa->IsF(ea0) == fa->IsF(ea1) ); + bool b = fa->IsF(ea1); + + fa->ClearAllF(); + fc->ClearAllF(); + + if (b) { + fa->SetF( ea0 ); + fc->SetF( ea1 ); + } else { + fa->SetF( ea1 ); + fc->SetF( ea0 ); + } + } + } + } + + +} + + +// uses Catmull Clark to enforce quad only meshes +// each old edge (but not faux) is split in two. +template +void MakeBitQuadOnlyByCatmullClark(Mesh &m){ + MakeBitQuadOnlyByRefine(m); + MakeBitQuadOnlyByRefine(m); + // et-voilą!!! +} + +// Helper funcion: +// marks edge distance froma a given face. +// Stops at maxDist or at the distance when a triangle is found +template +typename Mesh::FaceType * MarkEdgeDistance(Mesh &m, typename Mesh::FaceType *f, int maxDist){ + typedef typename Mesh::FaceType Face; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::VertexIterator VertexIterator; + assert(Mesh::HasPerFaceQuality()); + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!f->IsD()) { + fi->Q()=maxDist; + } + + Face * firstTriangleFound = NULL; + + f->Q() = 0; + std::vector stack; + int stackPos=0; + stack.push_back(f); + + while ( stackPosFFp(k); + int fq = int(f->Q()) + ( ! f->IsF(k) ); + if (fk->Q()> fq && fq <= maxDist) { + if (!fk->IsAnyF()) { firstTriangleFound = fk; maxDist = fq;} + fk->Q() = fq; + stack.push_back(fk); + } + } + } + return firstTriangleFound; +} + + +/* + given a tri-quad mesh, + uses edge rotates to make a tri move toward another tri and to merges them into a quad. + + Retunrs number of surviving triangles (0, or 1), or -1 if not done yet. + StepbyStep: makes just one step! + use it in a loop as long as it returns 0 or 1. + + maxdist is the maximal edge distance where to look for a companion triangle +*/ +template +int MakeBitQuadOnlyByFlipStepByStep(Mesh &m, int maxdist=10000, int restart=false){ + typedef typename Mesh::FaceType Face; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::VertexIterator VertexIterator; + + static Face *ta, *tb; // faces to be matched into a quad + + static int step = 0; // hack + + if (restart) { step=0; return false; } +if (step==0) { + + // find a triangular face ta + ta = NULL; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (!fi->IsAnyF()) { ta=&*fi; break; } + } + if (!ta) return 0; // success: no triangle left (done?) + + + tb = MarkEdgeDistance(m,ta,maxdist); + if (!tb) return 1; // fail: no matching triagle found (increase maxdist?) + + step=1; + +} else { + int marriageEdge=-1; + bool done = false; + while (!done) { + + int bestScore = int(tb->Q()); + int edge = -1; + bool mustDoFlip; + + // select which edge to use + for (int k=0; k<3; k++) { + if (tb->FFp(k) == tb) continue; // border + + Face* tbk = tb->FFp(k); + + if (!tbk->IsAnyF()) {done=true; marriageEdge=k; break; } // found my match + + int back = tb->FFi(k); + int faux = FauxIndex(tbk); + int other = 3-back-faux; + + int scoreA = int(tbk->FFp(other)->Q()); + + Face* tbh = tbk->FFp(faux); + int fauxh = FauxIndex(tbh); + + int scoreB = int(tbh->FFp( (fauxh+1)%3 )->Q()); + int scoreC = int(tbh->FFp( (fauxh+2)%3 )->Q()); + + int scoreABC = std::min( scoreC, std::min( scoreA, scoreB ) ); + if (scoreABCFFp(edge)) ); + } + + Face* next = tb->FFp(edge)->FFp( FauxIndex(tb->FFp(edge)) ); + + // create new edge + next->ClearAllF(); + tb->FFp(edge)->ClearAllF(); + + // dissolve old edge + tb->SetF(edge); + tb->FFp(edge)->SetF( tb->FFi(edge) ); + tb->FFp(edge)->Q() = tb->Q(); + + tb = next; +break; + } + + if (marriageEdge!=-1) { + // consume the marriage (two tris = one quad) + assert(!(tb->IsAnyF())); + assert(!(tb->FFp(marriageEdge)->IsAnyF())); + tb->SetF(marriageEdge); + tb->FFp(marriageEdge)->SetF(tb->FFi(marriageEdge)); + + step=0; + } +} + return -1; // not done yet +} + +/* + given a tri-quad mesh, + uses edge rotates to make a tri move toward another tri and to merges them into a quad. + - maxdist is the maximal edge distance where to look for a companion triangle + - retunrs true if all triangles are merged (always, unless they are odd, or maxdist not enough). +*/ +template +bool MakeBitQuadOnlyByFlip(Mesh &m, int maxdist=10000) +{ + MakeBitQuadOnlyByFlipStepByStep(m, maxdist, true); // restart + int res=-1; + while (res==-1) res = MakeBitQuadOnlyByFlipStepByStep(m, maxdist); + return res==0; +} + +/** + given a triangle mesh, makes it quad dominant by merging triangle pairs into quads + various euristics: + level = 0: maximally greedy. Leaves fewest triangles + level = 1: smarter: leaves more triangles, but makes better quality quads + level = 2: even more so (marginally) +*/ +template +void MakeBitQuadDominant(Mesh &m, int level){ + + assert(Mesh::HasPerFaceQuality()); + assert(Mesh::HasPerFaceFlags()); + + typedef typename Mesh::FaceIterator FaceIterator; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) { + fi->ClearAllF(); + fi->Q() = 0; + } + + + MakeQuadDominantPass (m); + if (level>0) MakeQuadDominantPass (m); + if (level>1) MakeQuadDominantPass (m); + if (level>0) MakeQuadDominantPass (m); +} + +}} // end namespace vcg::tri diff --git a/vcg/complex/trimesh/bitquad_optimization.h b/vcg/complex/trimesh/bitquad_optimization.h new file mode 100644 index 00000000..73c8c080 --- /dev/null +++ b/vcg/complex/trimesh/bitquad_optimization.h @@ -0,0 +1,118 @@ + +namespace vcg{namespace tri{ + +/* +seeks and removes all doublets (a pair of quads sharing two consecutive edges) +by merging them into a single quad (thus removing one vertex and two tri faces)- +Returns number of removed Doublets +*/ +template +int BitQuadRemoveDoublets(Mesh &m) +{ + int res=0; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::FaceType FaceType; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=1; + for (int k=0; k<3; k++) { + if ( IsDoublet(*fi,k) ){ + res++; + RemoveDoublet(*fi,k,m); + if (fi->IsD()) break; // break wedge circle, if face disappeard + } + } + } + return res; +} + +/* +marks (Quality=0) and approx. counts doublets (a pair of quads sharing two consecutive edges) +*/ +template +int BitQuadMarkDoublets(Mesh &m) +{ + int res=0; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::FaceType FaceType; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=1; + for (int k=0; k<3; k++) { + if ( IsDoublet(*fi,k) ){ + res++; + if (fi->IsF((k+1)%3)) res++; // counts for a quad + fi->Q()=0; + } + } + } + assert (res%2==0); + return res/4; // return doublet pairs (approx, as a quad could be a part of many pairs) +} + +/* +marks (Quality=0) and counts singlets (vertex B in an A-B-A-C quad) +*/ +template +int BitQuadMarkSinglets(Mesh &m) +{ + int res=0; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::FaceType FaceType; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=1; + for (int k=0; k<3; k++) { + if ( IsSinglet(*fi,k) ){ + res++; + fi->Q()=0; + } + } + } + assert (res%2==0); + return res/2; // return number of singlet pairs +} + +/* +deletes singlets, reutrns number of +*/ +template +int BitQuadRemoveSinglets(Mesh &m) +{ + int res=0; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::FaceType FaceType; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + for (int k=0; k<3; k++) { + if ( IsSinglet(*fi,k) ){ + res++; + RemoveSinglet(*fi,k,m); + return res; + break; + } + } + } + return res; // return singlet pairs (approx, as a quad could be a part of many pairs) +} + + +/* returns average quad quality, and assigns it to triangle quality +*/ +template +typename Mesh::ScalarType MeasureBitQuadQuality(Mesh &m) +{ + assert(Mesh::HasPerFaceFlags()); + typename Mesh::ScalarType res = 0; + int div = 0; + typedef typename Mesh::FaceIterator FaceIterator; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (fi->IsAnyF()) { + + typename Mesh::ScalarType q = quadQuality( &*fi, FauxIndex(&*fi) ); + + if (Mesh::HasPerFaceQuality()) fi->Q() = q; + res += q; + div++; + } + } + if (!div) return 0; else return res / div; +} + +}} // end namespace vcg::tri diff --git a/vcg/complex/trimesh/bitquad_support.h b/vcg/complex/trimesh/bitquad_support.h new file mode 100644 index 00000000..4e3d53b7 --- /dev/null +++ b/vcg/complex/trimesh/bitquad_support.h @@ -0,0 +1,515 @@ +#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 Face& f, int wedge) + void RemoveDoublet(Face &f, int wedge, Mesh& m) + - identifies and removed "Doublets" (pair of quads sharing two consecutive edges) + + void FlipBitQuadDiag(Face &f) + - rotates the faux edge of a quad + + void CollapseQuadDiag(Face &f, ... p , Mesh& 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 Face* f); + - returns index of the only faux edge of a quad (otherwise, assert) + + int CountBitPolygonInternalValency(const Face& f, int wedge) + - returns valency of vertex in terms of polygons (quads, tris...) + + +*/ + +// this must become a parameter in the corresponding class +#define DELETE_VERTICES 0 +// let's not remove them after all... +// since TwoManyfold is weak, the vertex could still be used elsewhere... + +namespace vcg{namespace tri{ + +// helper function: +// cos of angle abc. This should probably go elsewhere +template +static typename CoordType::ScalarType Cos(const CoordType &a, const CoordType &b, const CoordType &c ) +{ + CoordType + e0 = b - a, + e1 = b - c; + typename CoordType::ScalarType d = (e0.Norm()*e1.Norm()); + if (d==0) return 0.0; + return (e0*e1)/d; +} + +// helper function: +// returns quality of a quad formed by points a,b,c,d +// quality is computed as "how squared angles are" +template +inline static typename Coord::ScalarType quadQuality(const Coord &a, const Coord &b, const Coord &c, const Coord &d){ + typename Coord::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; +} + +// helper function: +// returns quality of a given (potential) quad +template +static typename Face::ScalarType quadQuality(Face *f, int edge){ + + typedef typename Face::CoordType CoordType; + + 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) +Uses the notion of quad-quailty +*/ +template +int TestBitQuadEdgeRotation(const Face &f, int w0) +{ + const Face *fa = &f; + assert(! fa->IsF(w0) ); + typename Face::ScalarType q0,q1,q2; + typename Face::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 Face *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(); + } + + /* + // max overall 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;*/ + + // min distance (shortcut criterion) + q0 = (v0 - v3).SquaredNorm(); + q1 = (v1 - v4).SquaredNorm(); + q2 = (v5 - v2).SquaredNorm(); + if (q0<=q1 && q0<=q2) return 0; + if (q1<=q2) return 1; + return -1; +} + +template +bool RotateBitQuadEdge(Face& f, int w0a){ + Face *fa = &f; + assert(! fa->IsF(w0a) ); + + typename Face::VertexType *v0, *v1; + v0= fa->V0(w0a); + v1= fa->V1(w0a); + + int w1a = (w0a+1)%3; + int w2a = (w0a+2)%3; + + Face *fb = fa->FFp(w0a); + int w0b = fa->FFi(w0a); + int w1b = (w0b+1)%3; + int w2b = (w0b+2)%3; + + if (fa->IsF(w2a) == verse) { + if (!CheckFlipBitQuadDiag(*fa)) return false; + FlipBitQuadDiag(*fa); + // recover edge index, so that (f, w0a) identifies the same edge as before + Face *fc = fa->FFp(FauxIndex(fa)); + for (int i=0; i<3; i++){ + if ( v0==fa->V0(i) && v1==fa->V1(i) ) w0a = i; + if ( v0==fc->V0(i) && v1==fc->V1(i) ) { fa = fc; w0a = i; } + } + } + + if (fb->IsF(w2b) == verse) { + if (!CheckFlipBitQuadDiag(*fb)) return false; + FlipBitQuadDiag(*fb); + } + + if (!CheckFlipEdge(*fa,w0a)) return false; + FlipBitQuadEdge(*fa,w0a); + return true; +} + +/* small helper function which returns the index of the only + faux index, assuming there is exactly one (asserts out otherwise) +*/ +template +int FauxIndex(const Face* 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 +template +void FlipBitQuadDiag(Face &f){ + int faux = FauxIndex(&f); + Face* fa = &f; + Face* 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); + } +} + +// flips the edge of a quad +template +void FlipBitQuadEdge(Face &f, int k){ + assert(!f.IsF(k)); + Face* fa = &f; + Face* fb = f.FFp(k); + assert(fa!=fb); // else, rotating a border edge + + // backup prev other-quads-halves + Face* fa2 = fa->FFp( FauxIndex(fa) ); + Face* fb2 = fb->FFp( FauxIndex(fb) ); + + 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 +template +bool CheckFlipBitQuadDiag(Face &f){ + return (vcg::face::CheckFlipEdge(f, FauxIndex(&f) ) ); +} + +// given a face (part of a quad), returns its diagonal +template +typename Face::CoordType Diag(const Face* f){ + int i = FauxIndex(f); + return f->P1( i ) - f->P0( i ); +} + + +// given a face (part of a quad), returns other diagonal +template +typename Face::CoordType CounterDiag(const Face* 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. */ +template +void _CollapseQuadDiagHalf(typename Mesh::FaceType &f, int faux, Mesh& m) +{ + typedef typename Mesh::FaceType Face; + int faux1 = (faux+1)%3; + int faux2 = (faux+2)%3; + + Face* fA = f.FFp( faux1 ); + Face* fB = f.FFp( faux2 ); + 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) + 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; + } + } + + Allocator::DeleteFace(m,f); + +} + +template +void RemoveDoublet(typename Mesh::FaceType &f, int wedge, Mesh& m){ + if (f.IsF((wedge+1)%3) ) { + typename Mesh::VertexType *v = f.V(wedge); + FlipBitQuadDiag(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; + } + } + typename Mesh::ScalarType k=(f.IsF(wedge))?1:0; + CollapseQuadDiag(f, k, m); + typename Mesh::VertexType *v = f.V(wedge); +} + +template +void RemoveSinglet(typename Mesh::FaceType &f, int wedge, Mesh& m){ + typename Mesh::FaceType *fa, *fb; // these will die + typename Mesh::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 + 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 ); + if (DELETE_VERTICES) + Allocator::DeleteVertex( m,*fa->V(wedge) ); +} + + +template +bool TestAndRemoveDoublet(typename Mesh::FaceType &f, int wedge, Mesh& m){ + if (IsDoublet(f,wedge)) { + RemoveDoublet(f,wedge,m); + return true; + } + return false; +} + +template +bool TestAndRemoveSinglet(typename Mesh::FaceType &f, int wedge, Mesh& m){ + if (IsSinglet(f,wedge)) { + RemoveSinglet(f,wedge,m); + return true; + } + return false; +} +template +void RotateBitQuadEdge(const Face& f, int wedge){ +} + +// given a face and a wedge, counts its valency in terms of quads (and triangles) +// uses only FF, assumes twomanyfold +// returns -1 if border +template +int CountBitPolygonInternalValency(const Face& f, int wedge){ + const Face* pf = &f; + int pi = wedge; + int res = 0; + do { + if (!pf->IsF(pi)) res++; + const Face *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return -1; + pi = (pi+1)%3; // Face::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. +template +bool IsDoublet(const Face& f, int wedge){ + const Face* 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 Face *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return false; + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // Face::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); + return (res == 2); +} + +template +bool IsSinglet(const Face& f, int wedge){ + const Face* 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 Face *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return false; + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // Face::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); + return (res == 1); +} + + + +/** 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 +*/ +template +void CollapseQuadDiag(typename Mesh::FaceType &f, typename Mesh::ScalarType k, Mesh& m){ + typename Mesh::CoordType p; + int fauxa = FauxIndex(&f); + p = f.V(fauxa)->P()*(1-k) + f.V( (fauxa+1)%3 )->P()*(k); + CollapseQuadDiag(f,p,m); +} + +template +void CollapseQuadDiag(typename Mesh::FaceType &f, const typename Mesh::CoordType &p, Mesh& m){ + + typedef typename Mesh::FaceType Face; + typedef typename Mesh::VertexType Vert; + + Face* fa = &f; + int fauxa = FauxIndex(fa); + Face* fb = fa->FFp(fauxa); + assert (fb!=fa); + int fauxb = FauxIndex(fb); + + Vert* va = fa->V(fauxa); // va lives + Vert* vb = fb->V(fauxb); // vb dies + + // update FV... + bool border = false; + int pi = fauxb; + Face* pf = fb; /* pf, pi could be a Pos p(pf, pi) */ + // rotate around vb, (same-sense-as-face)-wise + do { + pf->V(pi) = va; + + pi=(pi+2)%3; + Face *t = pf->FFp(pi); + if (t==pf) { border= true; break; } + pi = pf->FFi(pi); + pf = t; + } while (pf!=fb); + + // rotate around va, (counter-sense-as-face)-wise + if (border) { + int pi = fauxa; + Face* pf = fa; /* pf, pi could be a Pos p(pf, pi) */ + do { + pi=(pi+1)%3; + pf->V(pi) = va; + Face *t = pf->FFp(pi); + if (t==pf) break; + pi = pf->FFi(pi); + pf = t; + } while (pf!=fb); + } + + // update FF, delete faces + _CollapseQuadDiagHalf(*fb, fauxb, m); + _CollapseQuadDiagHalf(*fa, fauxa, m); + + if (DELETE_VERTICES) Allocator::DeleteVertex(m,*vb); + va->P() = p; +} + + +template +void CollapseQuadCounterDiag(typename Mesh::FaceType &f, typename Mesh::ScalarType k, Mesh& m){ + typename Mesh::CoordType p; + int fauxa = FauxIndex(&f); + p = f.P2(fauxa)*(1-k) + f.FFp( fauxa )->P2( f.FFi( fauxa ) )*(k); + CollapseQuadCounterDiag(f,p,m); +} + +template +void CollapseQuadCounterDiag(typename Mesh::FaceType &f, const typename Mesh::CoordType &p, Mesh& m){ + FlipBitQuadDiag(f); + CollapseQuadDiag(f,p,m); +} + + + +}} // end namespace vcg::tri