You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
426 lines
12 KiB
426 lines
12 KiB
/**************************************************************************** |
|
* VCGLib o o * |
|
* Visual and Computer Graphics Library o o * |
|
* _ O _ * |
|
* Copyright(C) 2004-2016 \/)\/ * |
|
* Visual Computing Lab /\/| * |
|
* ISTI - Italian National Research Council | * |
|
* \ * |
|
* All rights reserved. * |
|
* * |
|
* This program is free software; you can redistribute it and/or modify * |
|
* it under the terms of the GNU General Public License as published by * |
|
* the Free Software Foundation; either version 2 of the License, or * |
|
* (at your option) any later version. * |
|
* * |
|
* This program is distributed in the hope that it will be useful, * |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
|
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * |
|
* for more details. * |
|
* * |
|
****************************************************************************/ |
|
#ifndef _BITQUAD_OPTIMIZATION |
|
#define _BITQUAD_OPTIMIZATION |
|
|
|
namespace vcg{namespace tri{ |
|
|
|
template <class BQ> |
|
class BitQuadOptimization{ |
|
|
|
typedef typename BQ::MeshType MeshType; |
|
typedef typename BQ::Pos Pos; |
|
|
|
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 BitQuad<MeshType> BQ; // static class to make basic quad operatins |
|
|
|
public: |
|
|
|
// helper function: mark a quadface, setting Q at 0, and neight at .75, 0.5... |
|
static void MarkFace(FaceType* f, MeshType &m){ |
|
|
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
fi->Q() = 1; |
|
} |
|
|
|
for (int i=0; i<3; i++) { |
|
for (int j=0; j<3; j++) f->FFp(i)->FFp(j)->Q() = 0.75; |
|
} |
|
for (int i=0; i<3; i++) { |
|
f->FFp(i)->Q() = 0.50; |
|
} |
|
f->Q() = 0; |
|
|
|
} |
|
|
|
// helper function: mark a quadface, setting Q at 0, and neight at .75, 0.5... |
|
static void MarkVertex(FaceType* f, int wedge, MeshType &m){ |
|
|
|
VertexType *v = f->V(wedge); |
|
|
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
if (fi->V0(0)==v || fi->V1(0)==v ||fi->V2(0)==v ) fi->Q() = 0; |
|
// else fi->Q() = 1; |
|
} |
|
|
|
} |
|
|
|
static bool MarkSmallestEdge(MeshType &m, bool perform) |
|
{ |
|
ScalarType min = std::numeric_limits<ScalarType>::max(); |
|
|
|
FaceType *fa=NULL; int w=0; |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) |
|
for (int k=0; k<3; k++) { |
|
FaceType *f=&*fi; |
|
|
|
if (f->IsF(k)) continue; |
|
if (f->FFp(k) == f ) continue; // skip borders |
|
|
|
ScalarType score; |
|
|
|
score = (f->P0(k) - f->P1(k)).Norm(); |
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
w = k; |
|
} |
|
|
|
} |
|
if (fa) { |
|
if (perform) { |
|
return BQ::CollapseEdge(*fa,w,m); |
|
} else { |
|
fa->Q()=0.0; |
|
fa->FFp(w)->Q()=0.0; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
static ScalarType Importance(const CoordType &/*p*/){ |
|
//return ::proceduralImportance(p); |
|
return 1; |
|
} |
|
|
|
// returns: 0 if fail. 1 if edge. 2 if diag. |
|
static int MarkSmallestEdgeOrDiag(MeshType &m, ScalarType edgeMult, bool perform, Pos* affected=NULL) |
|
{ |
|
ScalarType min = std::numeric_limits<ScalarType>::max(); |
|
|
|
FaceType *fa=NULL; int w=0; bool counterDiag = false; |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) |
|
for (int k=0; k<3; k++) { |
|
FaceType *f=&*fi; |
|
|
|
if (f->FFp(k) >= f ) continue; // skip borders (==), and do it one per edge |
|
|
|
ScalarType score; |
|
|
|
score = (f->P0(k) - f->P1(k)).Norm(); |
|
|
|
ScalarType imp = Importance( (f->P0(k) + f->P1(k))/2 ); |
|
|
|
score /= imp; |
|
|
|
if (!f->IsF(k)) score*=edgeMult; // edges are supposed to be smaller! |
|
|
|
|
|
|
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
w = k; |
|
counterDiag=false; |
|
} |
|
|
|
if (f->IsF(k)) { // for diag faces, test counterdiag too |
|
score = BQ::CounterDiag(f).Norm(); |
|
score /= imp; |
|
|
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
w = k; |
|
counterDiag=true; |
|
} |
|
} |
|
|
|
|
|
|
|
} |
|
if (fa) { |
|
if (perform) { |
|
if (fa->IsF(w)) { |
|
if (counterDiag) { |
|
if (BQ::CollapseCounterDiag(*fa, BQ::PosOnDiag(*fa,true), m , affected)) return 2; |
|
} else { |
|
if (BQ::CollapseDiag(*fa, BQ::PosOnDiag(*fa,false), m ,affected)) return 2; |
|
} |
|
} else { |
|
if (BQ::CollapseEdge(*fa,w,m, affected)) return 1; |
|
} |
|
} else { |
|
fa->Q()=0.0; |
|
fa->FFp(w)->Q()=0.0; |
|
if (fa->IsF(w)) return 2; else return 1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
static void MarkSmallestDiag(MeshType &m) |
|
{ |
|
ScalarType min = std::numeric_limits<ScalarType>::max(); |
|
|
|
FaceType *fa=NULL; |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
FaceType *f=&*fi; |
|
|
|
ScalarType score; |
|
|
|
score = BQ::Diag(f).Norm(); |
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
} |
|
|
|
score = BQ::CounterDiag(f).Norm(); |
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
} |
|
|
|
} |
|
if (fa) { |
|
fa->Q()=0.0; |
|
fa->FFp(BQ::FauxIndex(fa))->Q()=0.0; |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static bool IdentifyAndCollapseSmallestDiag(MeshType &m){ |
|
ScalarType min = std::numeric_limits<ScalarType>::max(); |
|
|
|
FaceType *fa=NULL; bool flip; |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
FaceType *f=&*fi; |
|
|
|
ScalarType score; |
|
|
|
score = BQ::Diag(f).Norm(); |
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
flip = false; |
|
} |
|
|
|
score = BQ::CounterDiag(f).Norm(); |
|
if (score<min) { |
|
min=score; |
|
fa = f; |
|
flip = true; |
|
} |
|
|
|
} |
|
if (!fa) return false; |
|
|
|
if (BQ::TestAndRemoveDoublet(*fa,0,m)) { return true; } |
|
if (BQ::TestAndRemoveDoublet(*fa,1,m)) { return true; } |
|
if (BQ::TestAndRemoveDoublet(*fa,2,m)) { return true; } |
|
int k = BQ::FauxIndex(fa); |
|
if (BQ::TestAndRemoveDoublet( *fa->FFp(k),(fa->FFi(k)+2)%3, m )) return true; |
|
|
|
if (flip) { |
|
if (!BQ::CheckFlipDiag(*fa) ) { |
|
// I can't collapse (why?) |
|
MarkFace(fa,m); |
|
return false; |
|
} else |
|
BQ::CollapseCounterDiag(*fa, BQ::PosOnDiag(*fa,true), m ); |
|
} |
|
else { |
|
BQ::CollapseDiag(*fa, BQ::PosOnDiag(*fa,false), m ); |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
/* |
|
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 |
|
*/ |
|
static int RemoveDoublets(MeshType &m, Pos *p=NULL) |
|
{ |
|
int res=0; |
|
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 ( BQ::IsDoublet(*fi,k) ){ |
|
res++; |
|
BQ::RemoveDoublet(*fi,k,m,p); |
|
if (fi->IsD()) break; // break wedge circle, if face disappeard |
|
if (p) return res; |
|
} |
|
} |
|
} |
|
return res; |
|
} |
|
|
|
/* |
|
marks (Quality=0) and approx. counts profitable vertex rotations |
|
(vertex rotations which make edge shorter |
|
*/ |
|
template <bool perform> |
|
static int MarkVertexRotations(MeshType &m, Pos *affected=NULL) |
|
{ |
|
int res=0; |
|
for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) vi->ClearV(); |
|
if (!perform) |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->Q()=1.0; |
|
|
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
|
|
for (int k=0; k<3; k++) { |
|
if (fi->V(k)->IsV()) continue; |
|
if (BQ::TestVertexRotation(*fi,k)) { |
|
res++; |
|
fi->V(k)->SetV(); |
|
if (!perform) { |
|
res++; MarkVertex(&*fi, k, m); //fi->Q()=0; |
|
} |
|
else { |
|
if (BQ::RotateVertex(*fi, k, m, affected)) res++; //fi->Q()=0; |
|
if (affected) return res; // uncomment for only one rotation |
|
} |
|
} |
|
} |
|
} |
|
return res; |
|
} |
|
|
|
// mark (and count) all edges that are worth rotating |
|
// if perform == true, actually rotate them |
|
template <bool perform> |
|
static int MarkEdgeRotations(MeshType &m, Pos *p=NULL) |
|
{ |
|
int count = 0; |
|
|
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->Q()=1; |
|
|
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
//if (count>0) break; |
|
for (int k=0; k<3; k++) { |
|
if (fi->IsF(k)) continue; |
|
if (fi->FFp(k)<= &*fi) continue; // only once per real (non faux) edge, and only for non border ones |
|
int best = BQ::TestEdgeRotation(*fi, k); |
|
if (perform) { |
|
if (best==+1) if (BQ::template RotateEdge< true>(*fi, k, m, p)) count++; |
|
if (best==-1) if (BQ::template RotateEdge<false>(*fi, k, m, p)) count++; |
|
if (p) if (count>0) return count; |
|
} |
|
else { |
|
if (best!=0) { fi->Q()=0; fi->FFp(k)->Q()=0; count++; } |
|
} |
|
} |
|
} |
|
|
|
return count; |
|
} |
|
|
|
/* |
|
marks (Quality=0) and approx. counts doublets (a pair of quads sharing two consecutive edges) |
|
*/ |
|
static int MarkDoublets(MeshType &m) |
|
{ |
|
int res=0; |
|
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 ( BQ::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) |
|
*/ |
|
static int MarkSinglets(MeshType &m) |
|
{ |
|
int res=0; |
|
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 ( BQ::IsSinglet(*fi,k) ){ |
|
res++; |
|
fi->Q()=0; |
|
} |
|
} |
|
} |
|
assert (res%2==0); |
|
return res/2; // return number of singlet pairs |
|
} |
|
|
|
/* |
|
deletes singlets, reutrns number of |
|
*/ |
|
static int RemoveSinglets(MeshType &m, Pos *p=NULL) |
|
{ |
|
int res=0; |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
for (int k=0; k<3; k++) { |
|
if ( BQ::IsSinglet(*fi,k) ){ |
|
res++; |
|
BQ::RemoveSinglet(*fi,k,m, p); |
|
if (p) 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 |
|
*/ |
|
static ScalarType MeasureQuality(MeshType &m) |
|
{ |
|
assert(MeshType::HasPerFaceFlags()); |
|
ScalarType res = 0; |
|
int div = 0; |
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { |
|
if (fi->IsAnyF()) { |
|
|
|
ScalarType q = BQ::quadQuality( &*fi, BQ::FauxIndex(&*fi) ); |
|
|
|
if (MeshType::HasPerFaceQuality()) fi->Q() = q; |
|
res += q; |
|
div++; |
|
} |
|
} |
|
if (!div) return 0; else return res / div; |
|
} |
|
|
|
}; |
|
}} // end namespace vcg::tri |
|
|
|
#endif
|
|
|