Cleaned up a bit naming and comments and some interfaces of some bitquad functions
This commit is contained in:
parent
b8769bd3e6
commit
a1471cea44
|
@ -5,6 +5,7 @@
|
||||||
#include <vcg/simplex/face/jumping_pos.h>
|
#include <vcg/simplex/face/jumping_pos.h>
|
||||||
#include <vcg/simplex/face/topology.h>
|
#include <vcg/simplex/face/topology.h>
|
||||||
#include <vcg/space/planar_polygon_tessellation.h>
|
#include <vcg/space/planar_polygon_tessellation.h>
|
||||||
|
#include <vcg/complex/algorithms/update/quality.h>
|
||||||
|
|
||||||
/** BIT-QUAD creation support:
|
/** BIT-QUAD creation support:
|
||||||
a few basic operations to work with bit-quads simplices
|
a few basic operations to work with bit-quads simplices
|
||||||
|
@ -25,30 +26,30 @@
|
||||||
|
|
||||||
bool RotateEdge(FaceType& f, int w0a);
|
bool RotateEdge(FaceType& f, int w0a);
|
||||||
- rotate a quad edge (clockwise or counterclockwise, specified via template)
|
- rotate a quad edge (clockwise or counterclockwise, specified via template)
|
||||||
|
|
||||||
bool RotateVertex(FaceType &f, int w0)
|
bool RotateVertex(FaceType &f, int w0)
|
||||||
- rotate around a quad vertex ("wind-mill" operation)
|
- rotate around a quad vertex ("wind-mill" operation)
|
||||||
|
|
||||||
void CollapseDiag(FaceType &f, ... p , MeshType& m)
|
void CollapseDiag(FaceType &f, ... p , MeshType& m)
|
||||||
- collapses a quad on its diagonal.
|
- collapses a quad on its diagonal.
|
||||||
- p identifies the pos of collapsed point
|
- p identifies the pos of collapsed point
|
||||||
(as either the parametric pos on the diagonal, or a fresh coordtype)
|
(as either the parametric pos on the diagonal, or a fresh coordtype)
|
||||||
|
|
||||||
|
|
||||||
[ helper functions: ]
|
[ helper functions: ]
|
||||||
|
|
||||||
ScalarType quadQuality( ... );
|
ScalarType quadQuality( ... );
|
||||||
- returns the quality for a given quad
|
- returns the quality for a given quad
|
||||||
- (should be made into a template parameter for methods using it)
|
- (should be made into a template parameter for methods using it)
|
||||||
- currently measures how squared each angle is
|
- currently measures how squared each angle is
|
||||||
|
|
||||||
int FauxIndex(const FaceType* f);
|
int FauxIndex(const FaceType* f);
|
||||||
- returns index of the only faux edge of a quad (otherwise, assert)
|
- returns index of the only faux edge of a quad (otherwise, assert)
|
||||||
|
|
||||||
int CountBitPolygonInternalValency(const FaceType& f, int wedge)
|
int CountBitPolygonInternalValency(const FaceType& f, int wedge)
|
||||||
- returns valency of vertex in terms of polygons (quads, tris...)
|
- returns valency of vertex in terms of polygons (quads, tris...)
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// these should become a parameter in the corresponding class
|
// these should become a parameter in the corresponding class
|
||||||
|
@ -62,7 +63,7 @@
|
||||||
|
|
||||||
namespace vcg{namespace tri{
|
namespace vcg{namespace tri{
|
||||||
|
|
||||||
/* simple geometric-interpolation mono-function class used
|
/* simple geometric-interpolation mono-function class used
|
||||||
as a default template parameter to BitQuad class */
|
as a default template parameter to BitQuad class */
|
||||||
template <class VertexType>
|
template <class VertexType>
|
||||||
class GeometricInterpolator{
|
class GeometricInterpolator{
|
||||||
|
@ -77,9 +78,9 @@ public:
|
||||||
|
|
||||||
template <
|
template <
|
||||||
// first template parameter: the tri mesh (with face-edges flagged)
|
// first template parameter: the tri mesh (with face-edges flagged)
|
||||||
class _MeshType,
|
class _MeshType,
|
||||||
// second template parameter: used to define interpolations between points
|
// second template parameter: used to define interpolations between points
|
||||||
class Interpolator = GeometricInterpolator<typename _MeshType::VertexType>
|
class Interpolator = GeometricInterpolator<typename _MeshType::VertexType>
|
||||||
>
|
>
|
||||||
class BitQuad{
|
class BitQuad{
|
||||||
public:
|
public:
|
||||||
|
@ -97,7 +98,7 @@ typedef typename MeshType::VertexPointer VertexPointer;
|
||||||
class Pos{
|
class Pos{
|
||||||
FaceType *f;
|
FaceType *f;
|
||||||
int e;
|
int e;
|
||||||
public:
|
public:
|
||||||
enum{ PAIR, AROUND , NOTHING } mode;
|
enum{ PAIR, AROUND , NOTHING } mode;
|
||||||
FaceType* &F(){return f;}
|
FaceType* &F(){return f;}
|
||||||
FaceType* F() const {return f;}
|
FaceType* F() const {return f;}
|
||||||
|
@ -105,16 +106,16 @@ public:
|
||||||
const VertexType* cV() const {return f->V(e);}
|
const VertexType* cV() const {return f->V(e);}
|
||||||
int& E(){return e;}
|
int& E(){return e;}
|
||||||
int E() const {return e;}
|
int E() const {return e;}
|
||||||
|
|
||||||
|
|
||||||
Pos(){ f=NULL; e=0; mode=AROUND;}
|
Pos(){ f=NULL; e=0; mode=AROUND;}
|
||||||
|
|
||||||
Pos(FaceType* _f, int _e){f=_f; e=_e;}
|
Pos(FaceType* _f, int _e){f=_f; e=_e;}
|
||||||
Pos NextE()const {return Pos(f, (e+1)%3); }
|
Pos NextE()const {return Pos(f, (e+1)%3); }
|
||||||
Pos PrevE(){return Pos(f, (e+2)%3); }
|
Pos PrevE(){return Pos(f, (e+2)%3); }
|
||||||
bool IsF(){return f->IsF(e);}
|
bool IsF(){return f->IsF(e);}
|
||||||
Pos FlipF(){return Pos(f->FFp(e), f->FFi(e)); }
|
Pos FlipF(){return Pos(f->FFp(e), f->FFi(e)); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,19 +141,19 @@ static bool RotateEdge(FaceType& f, int w0a, MeshType &m, Pos *affected=NULL){
|
||||||
VertexType *v0, *v1;
|
VertexType *v0, *v1;
|
||||||
v0= fa->V0(w0a);
|
v0= fa->V0(w0a);
|
||||||
v1= fa->V1(w0a);
|
v1= fa->V1(w0a);
|
||||||
|
|
||||||
// int w1a = (w0a+1)%3;
|
// int w1a = (w0a+1)%3;
|
||||||
int w2a = (w0a+2)%3;
|
int w2a = (w0a+2)%3;
|
||||||
|
|
||||||
FaceType *fb = fa->FFp(w0a);
|
FaceType *fb = fa->FFp(w0a);
|
||||||
|
|
||||||
MarkFaceF(fa);
|
MarkFaceF(fa);
|
||||||
MarkFaceF(fb);
|
MarkFaceF(fb);
|
||||||
|
|
||||||
int w0b = fa->FFi(w0a);
|
int w0b = fa->FFi(w0a);
|
||||||
// int w1b = (w0b+1)%3;
|
// int w1b = (w0b+1)%3;
|
||||||
int w2b = (w0b+2)%3;
|
int w2b = (w0b+2)%3;
|
||||||
|
|
||||||
if (fa->IsF(w2a) == verse) {
|
if (fa->IsF(w2a) == verse) {
|
||||||
if (!CheckFlipDiag(*fa)) return false;
|
if (!CheckFlipDiag(*fa)) return false;
|
||||||
FlipDiag(*fa);
|
FlipDiag(*fa);
|
||||||
|
@ -160,12 +161,12 @@ static bool RotateEdge(FaceType& f, int w0a, MeshType &m, Pos *affected=NULL){
|
||||||
fa = fb->FFp(w0b);
|
fa = fb->FFp(w0b);
|
||||||
w0a = fb->FFi(w0b);
|
w0a = fb->FFi(w0b);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fb->IsF(w2b) == verse) {
|
if (fb->IsF(w2b) == verse) {
|
||||||
if (!CheckFlipDiag(*fb)) return false;
|
if (!CheckFlipDiag(*fb)) return false;
|
||||||
FlipDiag(*fb);
|
FlipDiag(*fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CheckFlipEdge(*fa,w0a)) return false;
|
if (!CheckFlipEdge(*fa,w0a)) return false;
|
||||||
FlipEdge(*fa,w0a,m);
|
FlipEdge(*fa,w0a,m);
|
||||||
if (affected) {
|
if (affected) {
|
||||||
|
@ -202,14 +203,14 @@ static void FlipDiag(FaceType &f){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// given a vertex (i.e. a face and a wedge),
|
// given a vertex (i.e. a face and a wedge),
|
||||||
// this function tells us how the totale edge length around a vertex would change
|
// this function tells us how the totale edge length around a vertex would change
|
||||||
// if that vertex is rotated
|
// if that vertex is rotated
|
||||||
static ScalarType EdgeLenghtVariationIfVertexRotated(const FaceType &f, int w0)
|
static ScalarType EdgeLenghtVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
{
|
{
|
||||||
assert(!f.IsD());
|
assert(!f.IsD());
|
||||||
|
|
||||||
ScalarType
|
ScalarType
|
||||||
before=0, // sum of quad edges (originating from v)
|
before=0, // sum of quad edges (originating from v)
|
||||||
after=0; // sum of quad diag (orginating from v)
|
after=0; // sum of quad diag (orginating from v)
|
||||||
int guard = 0;
|
int guard = 0;
|
||||||
|
@ -218,13 +219,13 @@ static ScalarType EdgeLenghtVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
const FaceType* pf = &f;
|
const FaceType* pf = &f;
|
||||||
int pi = w0;
|
int pi = w0;
|
||||||
int n = 0; // vertex valency
|
int n = 0; // vertex valency
|
||||||
int na = 0;
|
int na = 0;
|
||||||
do {
|
do {
|
||||||
ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm();
|
ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm();
|
||||||
if (pf->IsF(pi)) { after += triEdge; na++;}
|
if (pf->IsF(pi)) { after += triEdge; na++;}
|
||||||
else { before+= triEdge; n++; }
|
else { before+= triEdge; n++; }
|
||||||
if ( pf->IsF((pi+1)%3)) { after += CounterDiag( pf ).Norm(); na++; }
|
if ( pf->IsF((pi+1)%3)) { after += CounterDiag( pf ).Norm(); na++; }
|
||||||
|
|
||||||
const FaceType *t = pf;
|
const FaceType *t = pf;
|
||||||
t = pf->FFp( pi );
|
t = pf->FFp( pi );
|
||||||
if (pf == t ) return std::numeric_limits<ScalarType>::max(); // it's a mesh border! flee!
|
if (pf == t ) return std::numeric_limits<ScalarType>::max(); // it's a mesh border! flee!
|
||||||
|
@ -237,14 +238,14 @@ static ScalarType EdgeLenghtVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
return (after-before);
|
return (after-before);
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a vertex (i.e. a face and a wedge),
|
// given a vertex (i.e. a face and a wedge),
|
||||||
// this function tells us how the totale edge length around a vertex would change
|
// this function tells us how the totale edge length around a vertex would change
|
||||||
// if that vertex is rotated
|
// if that vertex is rotated
|
||||||
static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0)
|
static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
{
|
{
|
||||||
assert(!f.IsD());
|
assert(!f.IsD());
|
||||||
|
|
||||||
ScalarType
|
ScalarType
|
||||||
before=0, // sum of quad quality around v
|
before=0, // sum of quad quality around v
|
||||||
after=0; // same after the collapse
|
after=0; // same after the collapse
|
||||||
int guard = 0;
|
int guard = 0;
|
||||||
|
@ -253,20 +254,20 @@ static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
const FaceType* pf = &f;
|
const FaceType* pf = &f;
|
||||||
int pi = w0;
|
int pi = w0;
|
||||||
int nb = 0; // vertex valency
|
int nb = 0; // vertex valency
|
||||||
int na = 0;
|
int na = 0;
|
||||||
std::vector<const VertexType *> s; // 1 star around v
|
std::vector<const VertexType *> s; // 1 star around v
|
||||||
do {
|
do {
|
||||||
// ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm();
|
// ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm();
|
||||||
if (!pf->IsF(pi)) {
|
if (!pf->IsF(pi)) {
|
||||||
if ( pf->IsF((pi+1)%3)) {
|
if ( pf->IsF((pi+1)%3)) {
|
||||||
s.push_back(pf->cFFp((pi+1)%3)->V2( pf->cFFi((pi+1)%3) ));
|
s.push_back(pf->cFFp((pi+1)%3)->V2( pf->cFFi((pi+1)%3) ));
|
||||||
} else {
|
} else {
|
||||||
s.push_back( pf->V2(pi) );
|
s.push_back( pf->V2(pi) );
|
||||||
}
|
}
|
||||||
|
|
||||||
s.push_back( pf->V1(pi) );
|
s.push_back( pf->V1(pi) );
|
||||||
}
|
}
|
||||||
|
|
||||||
const FaceType *t = pf;
|
const FaceType *t = pf;
|
||||||
t = pf->FFp( pi );
|
t = pf->FFp( pi );
|
||||||
if (pf == t ) return std::numeric_limits<ScalarType>::max(); // it's a mesh border! flee!
|
if (pf == t ) return std::numeric_limits<ScalarType>::max(); // it's a mesh border! flee!
|
||||||
|
@ -275,7 +276,7 @@ static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
pf = t;
|
pf = t;
|
||||||
assert(guard++<100);
|
assert(guard++<100);
|
||||||
} while (pf != &f);
|
} while (pf != &f);
|
||||||
|
|
||||||
assert(s.size()%2==0);
|
assert(s.size()%2==0);
|
||||||
int N = s.size();
|
int N = s.size();
|
||||||
for (int i=0; i<N; i+=2) {
|
for (int i=0; i<N; i+=2) {
|
||||||
|
@ -307,17 +308,17 @@ static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0)
|
||||||
} while (pf != &f);
|
} while (pf != &f);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// given a vertex (i.e. a face and a wedge),
|
// given a vertex (i.e. a face and a wedge),
|
||||||
// this function tells us if it should be rotated or not
|
// this function tells us if it should be rotated or not
|
||||||
// (currently, we should iff it is shortened)
|
// (currently, we should iff it is shortened)
|
||||||
static bool TestVertexRotation(const FaceType &f, int w0)
|
static bool TestVertexRotation(const FaceType &f, int w0)
|
||||||
{
|
{
|
||||||
assert(!f.IsD());
|
assert(!f.IsD());
|
||||||
|
|
||||||
#if (LENGTH_CRITERION)
|
#if (LENGTH_CRITERION)
|
||||||
// rotate vertex IFF this way edges become shorter:
|
// rotate vertex IFF this way edges become shorter:
|
||||||
return EdgeLenghtVariationIfVertexRotated(f,w0)<0;
|
return EdgeLenghtVariationIfVertexRotated(f,w0)<0;
|
||||||
#else
|
#else
|
||||||
// rotate vertex IFF overall Quality increase
|
// rotate vertex IFF overall Quality increase
|
||||||
#endif
|
#endif
|
||||||
return QuadQualityVariationIfVertexRotated(f,w0)<0;
|
return QuadQualityVariationIfVertexRotated(f,w0)<0;
|
||||||
|
@ -326,27 +327,27 @@ static bool TestVertexRotation(const FaceType &f, int w0)
|
||||||
|
|
||||||
static bool RotateVertex(FaceType &f, int w0, MeshType &/*m*/, Pos *affected=NULL)
|
static bool RotateVertex(FaceType &f, int w0, MeshType &/*m*/, Pos *affected=NULL)
|
||||||
{
|
{
|
||||||
|
|
||||||
// int guard = 0;
|
// int guard = 0;
|
||||||
|
|
||||||
FaceType* pf = &f;
|
FaceType* pf = &f;
|
||||||
int pi = w0;
|
int pi = w0;
|
||||||
|
|
||||||
if (pf->IsF((pi+2) % 3)) {
|
if (pf->IsF((pi+2) % 3)) {
|
||||||
pi = (pi+2)%3;
|
pi = (pi+2)%3;
|
||||||
// do one step back
|
// do one step back
|
||||||
int tmp = pf->FFi(pi); pf = pf->FFp(pi); pi = tmp; // flipF
|
int tmp = pf->FFi(pi); pf = pf->FFp(pi); pi = tmp; // flipF
|
||||||
}
|
}
|
||||||
|
|
||||||
const FaceType* stopA = pf;
|
const FaceType* stopA = pf;
|
||||||
const FaceType* stopB = pf->FFp(FauxIndex(pf));
|
const FaceType* stopB = pf->FFp(FauxIndex(pf));
|
||||||
|
|
||||||
// rotate around vertex, flipping diagonals if necessary,
|
// rotate around vertex, flipping diagonals if necessary,
|
||||||
do {
|
do {
|
||||||
bool mustFlip;
|
bool mustFlip;
|
||||||
if (pf->IsF(pi)) {
|
if (pf->IsF(pi)) {
|
||||||
// if next edge is faux, move on other side of quad
|
// 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
|
int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF
|
||||||
mustFlip = false;
|
mustFlip = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -354,23 +355,23 @@ static bool RotateVertex(FaceType &f, int w0, MeshType &/*m*/, Pos *affected=NUL
|
||||||
}
|
}
|
||||||
|
|
||||||
FaceType *lastF = pf;
|
FaceType *lastF = pf;
|
||||||
|
|
||||||
int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF
|
int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF
|
||||||
|
|
||||||
if (mustFlip) {
|
if (mustFlip) {
|
||||||
if (!CheckFlipDiag(*lastF)) return false; // cannot flip??
|
if (!CheckFlipDiag(*lastF)) return false; // cannot flip??
|
||||||
FlipDiag(*lastF);
|
FlipDiag(*lastF);
|
||||||
}
|
}
|
||||||
MarkFaceF(pf);
|
MarkFaceF(pf);
|
||||||
} while (pf != stopA && pf!= stopB);
|
} while (pf != stopA && pf!= stopB);
|
||||||
|
|
||||||
// last pass: rotate arund vertex again, changing faux status
|
// last pass: rotate arund vertex again, changing faux status
|
||||||
stopA=pf;
|
stopA=pf;
|
||||||
do {
|
do {
|
||||||
int j = pi;
|
int j = pi;
|
||||||
if (pf->IsF(j))
|
if (pf->IsF(j))
|
||||||
{ pf->ClearF(j); IncreaseValency(pf->V1(j)); }
|
{ pf->ClearF(j); IncreaseValency(pf->V1(j)); }
|
||||||
else
|
else
|
||||||
{ pf->SetF(j); DecreaseValencySimple(pf->V1(j),1); }
|
{ pf->SetF(j); DecreaseValencySimple(pf->V1(j),1); }
|
||||||
|
|
||||||
j = (j+2)%3;
|
j = (j+2)%3;
|
||||||
|
@ -409,7 +410,7 @@ static void FlipEdge(FaceType &f, int k, MeshType &m){
|
||||||
|
|
||||||
|
|
||||||
vcg::face::FlipEdge(*fa, k);
|
vcg::face::FlipEdge(*fa, k);
|
||||||
|
|
||||||
// ripristinate faux flags
|
// ripristinate faux flags
|
||||||
fb->ClearAllF();
|
fb->ClearAllF();
|
||||||
fa->ClearAllF();
|
fa->ClearAllF();
|
||||||
|
@ -419,7 +420,7 @@ static void FlipEdge(FaceType &f, int k, MeshType &m){
|
||||||
if (fa->FFp(k)->IsF( fa->FFi(k) )) fa->SetF(k);
|
if (fa->FFp(k)->IsF( fa->FFi(k) )) fa->SetF(k);
|
||||||
if (fb->FFp(k)->IsF( fb->FFi(k) )) fb->SetF(k);
|
if (fb->FFp(k)->IsF( fb->FFi(k) )) fb->SetF(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if a quad diagonal can be topologically flipped
|
// check if a quad diagonal can be topologically flipped
|
||||||
|
@ -441,26 +442,26 @@ static CoordType CounterDiag(const FaceType* f){
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper function:
|
/* helper function:
|
||||||
collapses a single face along its faux edge.
|
collapses a single face along its faux edge.
|
||||||
Updates FF adj of other edges. */
|
Updates FF adj of other edges. */
|
||||||
static void _CollapseDiagHalf(FaceType &f, int faux, MeshType& /*m*/)
|
static void _CollapseDiagHalf(FaceType &f, int faux, MeshType& /*m*/)
|
||||||
{
|
{
|
||||||
int faux1 = (faux+1)%3;
|
int faux1 = (faux+1)%3;
|
||||||
int faux2 = (faux+2)%3;
|
int faux2 = (faux+2)%3;
|
||||||
|
|
||||||
FaceType* fA = f.FFp( faux1 );
|
FaceType* fA = f.FFp( faux1 );
|
||||||
FaceType* fB = f.FFp( faux2 );
|
FaceType* fB = f.FFp( faux2 );
|
||||||
|
|
||||||
MarkFaceF(fA);
|
MarkFaceF(fA);
|
||||||
MarkFaceF(fB);
|
MarkFaceF(fB);
|
||||||
|
|
||||||
int iA = f.FFi( faux1 );
|
int iA = f.FFi( faux1 );
|
||||||
int iB = f.FFi( faux2 );
|
int iB = f.FFi( faux2 );
|
||||||
|
|
||||||
if (fA==&f && fB==&f) {
|
if (fA==&f && fB==&f) {
|
||||||
// both non-faux edges are borders: tri-face disappears, just remove the vertex
|
// both non-faux edges are borders: tri-face disappears, just remove the vertex
|
||||||
//if (DELETE_VERTICES)
|
//if (DELETE_VERTICES)
|
||||||
//if (GetValency(f.V(faux2))==0) Allocator<MeshType>::DeleteVertex(m,*(f.V(faux2)));
|
//if (GetValency(f.V(faux2))==0) Allocator<MeshType>::DeleteVertex(m,*(f.V(faux2)));
|
||||||
} else {
|
} else {
|
||||||
if (fA==&f) {
|
if (fA==&f) {
|
||||||
fB->FFp(iB) = fB; fB->FFi(iB) = iB;
|
fB->FFp(iB) = fB; fB->FFi(iB) = iB;
|
||||||
|
@ -475,8 +476,8 @@ static void _CollapseDiagHalf(FaceType &f, int faux, MeshType& /*m*/)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//DecreaseValency(&f,faux2,m); // update valency
|
//DecreaseValency(&f,faux2,m); // update valency
|
||||||
//Allocator<MeshType>::DeleteFace(m,f);
|
//Allocator<MeshType>::DeleteFace(m,f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -500,9 +501,9 @@ static void RemoveDoublet(FaceType &f, int wedge, MeshType& m, Pos* affected=NUL
|
||||||
|
|
||||||
static void RemoveSinglet(FaceType &f, int wedge, MeshType& m, Pos* affected=NULL){
|
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 (affected) affected->mode = Pos::NOTHING; // singlets leave nothing to update behind
|
||||||
|
|
||||||
if (f.V(wedge)->IsB()) return; // hack: lets detect
|
if (f.V(wedge)->IsB()) return; // hack: lets detect
|
||||||
|
|
||||||
FaceType *fa, *fb; // these will die
|
FaceType *fa, *fb; // these will die
|
||||||
FaceType *fc, *fd; // their former neight
|
FaceType *fc, *fd; // their former neight
|
||||||
fa = & f;
|
fa = & f;
|
||||||
|
@ -511,10 +512,10 @@ static void RemoveSinglet(FaceType &f, int wedge, MeshType& m, Pos* affected=NUL
|
||||||
int wa1 = (wa0+1)%3 ;
|
int wa1 = (wa0+1)%3 ;
|
||||||
int wa2 = (wa0+2)%3 ;
|
int wa2 = (wa0+2)%3 ;
|
||||||
int wb0 = (fa->FFi(wa0)+1)%3;
|
int wb0 = (fa->FFi(wa0)+1)%3;
|
||||||
int wb1 = (wb0+1)%3 ;
|
int wb1 = (wb0+1)%3 ;
|
||||||
// int wb2 = (wb0+2)%3 ;
|
// int wb2 = (wb0+2)%3 ;
|
||||||
assert (fb == fa->FFp( wa2 ) ); // otherwise, not a singlet
|
assert (fb == fa->FFp( wa2 ) ); // otherwise, not a singlet
|
||||||
|
|
||||||
// valency decrease
|
// valency decrease
|
||||||
DecreaseValency(fa, wa1, m);
|
DecreaseValency(fa, wa1, m);
|
||||||
DecreaseValency(fa, wa2, m);
|
DecreaseValency(fa, wa2, m);
|
||||||
|
@ -523,9 +524,9 @@ static void RemoveSinglet(FaceType &f, int wedge, MeshType& m, Pos* affected=NUL
|
||||||
} else {
|
} else {
|
||||||
DecreaseValency(fa,wa1,m); // double decrease of valency on wa1
|
DecreaseValency(fa,wa1,m); // double decrease of valency on wa1
|
||||||
}
|
}
|
||||||
|
|
||||||
// no need to MarkFaceF !
|
// no need to MarkFaceF !
|
||||||
|
|
||||||
fc = fa->FFp(wa1);
|
fc = fa->FFp(wa1);
|
||||||
fd = fb->FFp(wb1);
|
fd = fb->FFp(wb1);
|
||||||
int wc = fa->FFi(wa1);
|
int wc = fa->FFi(wa1);
|
||||||
|
@ -537,12 +538,12 @@ static void RemoveSinglet(FaceType &f, int wedge, MeshType& m, Pos* affected=NUL
|
||||||
// faux status of survivors: unchanged
|
// faux status of survivors: unchanged
|
||||||
assert( ! ( fc->IsF( wc) ) );
|
assert( ! ( fc->IsF( wc) ) );
|
||||||
assert( ! ( fd->IsF( wd) ) );
|
assert( ! ( fd->IsF( wd) ) );
|
||||||
|
|
||||||
Allocator<MeshType>::DeleteFace( m,*fa );
|
Allocator<MeshType>::DeleteFace( m,*fa );
|
||||||
Allocator<MeshType>::DeleteFace( m,*fb );
|
Allocator<MeshType>::DeleteFace( m,*fb );
|
||||||
|
|
||||||
DecreaseValency(fa,wedge,m );
|
DecreaseValency(fa,wedge,m );
|
||||||
//if (DELETE_VERTICES)
|
//if (DELETE_VERTICES)
|
||||||
//if (GetValency(fa->V(wedge))==0) Allocator<MeshType>::DeleteVertex( m,*fa->V(wedge) );
|
//if (GetValency(fa->V(wedge))==0) Allocator<MeshType>::DeleteVertex( m,*fa->V(wedge) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,22 +647,22 @@ static bool CollapseEdgeDirect(FaceType &f, int w0, MeshType& m){
|
||||||
FaceType * f0 = &f;
|
FaceType * f0 = &f;
|
||||||
|
|
||||||
assert( !f0->IsF(w0) );
|
assert( !f0->IsF(w0) );
|
||||||
|
|
||||||
VertexType *v0, *v1;
|
VertexType *v0, *v1;
|
||||||
v0 = f0->V0(w0);
|
v0 = f0->V0(w0);
|
||||||
v1 = f0->V1(w0);
|
v1 = f0->V1(w0);
|
||||||
|
|
||||||
if (!RotateVertex(*f0,w0,m)) return false;
|
if (!RotateVertex(*f0,w0,m)) return false;
|
||||||
|
|
||||||
// quick hack: recover original wedge
|
// quick hack: recover original wedge
|
||||||
if (f0->V(0) == v0) w0 = 0;
|
if (f0->V(0) == v0) w0 = 0;
|
||||||
else if (f0->V(1) == v0) w0 = 1;
|
else if (f0->V(1) == v0) w0 = 1;
|
||||||
else if (f0->V(2) == v0) w0 = 2;
|
else if (f0->V(2) == v0) w0 = 2;
|
||||||
else assert(0);
|
else assert(0);
|
||||||
|
|
||||||
assert( f0->V1(w0) == v1 );
|
assert( f0->V1(w0) == v1 );
|
||||||
assert( f0->IsF(w0) );
|
assert( f0->IsF(w0) );
|
||||||
|
|
||||||
return CollapseDiag(*f0,PosOnDiag(*f0,false), m);
|
return CollapseDiag(*f0,PosOnDiag(*f0,false), m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,7 +670,7 @@ static bool CollapseEdgeDirect(FaceType &f, int w0, MeshType& m){
|
||||||
static bool CollapseEdge(FaceType &f, int w0, MeshType& m, Pos *affected=NULL){
|
static bool CollapseEdge(FaceType &f, int w0, MeshType& m, Pos *affected=NULL){
|
||||||
FaceTypeP f0 = &f;
|
FaceTypeP f0 = &f;
|
||||||
assert(!f0->IsF(w0)); // don't use this method to collapse diag.
|
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)) 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 (IsDoubletOrSinglet(f,(w0+1)%3)) return false; //{ RemoveDoubletOrSinglet(f,(w0+1)%3,m, affected); return true;}
|
||||||
|
|
||||||
|
@ -678,30 +679,30 @@ static bool CollapseEdge(FaceType &f, int w0, MeshType& m, Pos *affected=NULL){
|
||||||
affected->F() = f0->FFp(w1);
|
affected->F() = f0->FFp(w1);
|
||||||
affected->E() = (f0->FFi(w1)+2+w1-FauxIndex(f0))%3;
|
affected->E() = (f0->FFi(w1)+2+w1-FauxIndex(f0))%3;
|
||||||
}
|
}
|
||||||
|
|
||||||
FaceTypeP f1 = f0->FFp(w0);
|
FaceTypeP f1 = f0->FFp(w0);
|
||||||
int w1 = f0->FFi(w0);
|
int w1 = f0->FFi(w0);
|
||||||
|
|
||||||
assert(f0!=f1); // can't collapse border edges!
|
assert(f0!=f1); // can't collapse border edges!
|
||||||
|
|
||||||
// choose: rotate around V0 or around V1?
|
// choose: rotate around V0 or around V1?
|
||||||
if (
|
if (
|
||||||
EdgeLenghtVariationIfVertexRotated(*f0,w0)
|
EdgeLenghtVariationIfVertexRotated(*f0,w0)
|
||||||
<
|
<
|
||||||
EdgeLenghtVariationIfVertexRotated(*f1,w1)
|
EdgeLenghtVariationIfVertexRotated(*f1,w1)
|
||||||
) return CollapseEdgeDirect(*f0,w0,m);
|
) return CollapseEdgeDirect(*f0,w0,m);
|
||||||
else return CollapseEdgeDirect(*f1,w1,m);
|
else return CollapseEdgeDirect(*f1,w1,m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** collapses a quad diagonal a-b
|
/** collapses a quad diagonal a-b
|
||||||
forming the new vertex in between the two old vertices.
|
forming the new vertex in between the two old vertices.
|
||||||
if k == 0, new vertex is in a
|
if k == 0, new vertex is in a
|
||||||
if k == 1, new vertex is in b
|
if k == 1, new vertex is in b
|
||||||
if k == 0.5, new vertex in the middle, etc
|
if k == 0.5, new vertex in the middle, etc
|
||||||
*/
|
*/
|
||||||
static bool CollapseCounterDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){
|
static bool CollapseCounterDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){
|
||||||
if (!CheckFlipDiag(f)) return false;
|
if (!CheckFlipDiag(f)) return false;
|
||||||
FlipDiag(f);
|
FlipDiag(f);
|
||||||
return CollapseDiag(f,interpol,m,affected);
|
return CollapseDiag(f,interpol,m,affected);
|
||||||
|
@ -722,12 +723,12 @@ public:
|
||||||
if (pos.mode==Pos::AROUND) {
|
if (pos.mode==Pos::AROUND) {
|
||||||
if (start.F()->IsF((start.E()+2)%3))
|
if (start.F()->IsF((start.E()+2)%3))
|
||||||
{
|
{
|
||||||
int i = start.F()->FFi( start.E() );
|
int i = start.F()->FFi( start.E() );
|
||||||
start.F() = start.F()->FFp( start.E() );
|
start.F() = start.F()->FFp( start.E() );
|
||||||
start.E() = (i+1)%3;
|
start.E() = (i+1)%3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cur=start;
|
cur=start;
|
||||||
over = false;
|
over = false;
|
||||||
}
|
}
|
||||||
bool End() const {
|
bool End() const {
|
||||||
|
@ -742,14 +743,14 @@ public:
|
||||||
} else {
|
} else {
|
||||||
if (cur.F()->IsF(cur.E())) {
|
if (cur.F()->IsF(cur.E())) {
|
||||||
// jump over faux diag
|
// jump over faux diag
|
||||||
int i = cur.F()->FFi( cur.E() );
|
int i = cur.F()->FFi( cur.E() );
|
||||||
cur.F() = cur.F()->FFp( cur.E() );
|
cur.F() = cur.F()->FFp( cur.E() );
|
||||||
cur.E() = (i+1)%3;
|
cur.E() = (i+1)%3;
|
||||||
}
|
}
|
||||||
// jump over real edge
|
// jump over real edge
|
||||||
FaceType *f =cur.F()->FFp( cur.E() );
|
FaceType *f =cur.F()->FFp( cur.E() );
|
||||||
if (f==cur.F()) over=true; // border found
|
if (f==cur.F()) over=true; // border found
|
||||||
cur.E() = (cur.F()->FFi( cur.E() ) +1 )%3;
|
cur.E() = (cur.F()->FFi( cur.E() ) +1 )%3;
|
||||||
cur.F() = f;
|
cur.F() = f;
|
||||||
if (cur.F()==start.F()) over=true;
|
if (cur.F()==start.F()) over=true;
|
||||||
}
|
}
|
||||||
|
@ -757,11 +758,11 @@ public:
|
||||||
|
|
||||||
Pos GetPos(){
|
Pos GetPos(){
|
||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){
|
static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){
|
||||||
|
|
||||||
FaceType* fa = &f; // fa lives
|
FaceType* fa = &f; // fa lives
|
||||||
int fauxa = FauxIndex(fa);
|
int fauxa = FauxIndex(fa);
|
||||||
|
|
||||||
|
@ -784,10 +785,10 @@ static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* aff
|
||||||
FaceType* fb = fa->FFp(fauxa); // fb dies
|
FaceType* fb = fa->FFp(fauxa); // fb dies
|
||||||
assert (fb!=fa); // otherwise, its a singlet
|
assert (fb!=fa); // otherwise, its a singlet
|
||||||
int fauxb = FauxIndex(fb);
|
int fauxb = FauxIndex(fb);
|
||||||
|
|
||||||
VertexType* va = fa->V(fauxa); // va lives
|
VertexType* va = fa->V(fauxa); // va lives
|
||||||
VertexType* vb = fb->V(fauxb); // vb dies
|
VertexType* vb = fb->V(fauxb); // vb dies
|
||||||
|
|
||||||
Interpolator::Apply( *(f.V0(fauxa)), *(f.V1(fauxa)), interpol, *va);
|
Interpolator::Apply( *(f.V0(fauxa)), *(f.V1(fauxa)), interpol, *va);
|
||||||
|
|
||||||
bool border = false;
|
bool border = false;
|
||||||
|
@ -798,11 +799,11 @@ static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* aff
|
||||||
// rotate around vb, (same-sense-as-face)-wise
|
// rotate around vb, (same-sense-as-face)-wise
|
||||||
int pi = fauxb;
|
int pi = fauxb;
|
||||||
FaceType* pf = fb; /* pf, pi could be put in a Pos<FaceType> p(pb, fauxb) */
|
FaceType* pf = fb; /* pf, pi could be put in a Pos<FaceType> p(pb, fauxb) */
|
||||||
do {
|
do {
|
||||||
//pf->V(pi) = va;
|
//pf->V(pi) = va;
|
||||||
if (((pf->V2(pi) == va)||(pf->V1(pi) == va))
|
if (((pf->V2(pi) == va)||(pf->V1(pi) == va))
|
||||||
&&(pf!=fa)&&(pf!=fb))
|
&&(pf!=fa)&&(pf!=fb))
|
||||||
return false;
|
return false;
|
||||||
pi=(pi+2)%3;
|
pi=(pi+2)%3;
|
||||||
FaceType *t = pf->FFp(pi);
|
FaceType *t = pf->FFp(pi);
|
||||||
if (t==pf) { border= true; break; }
|
if (t==pf) { border= true; break; }
|
||||||
|
@ -815,7 +816,7 @@ static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* aff
|
||||||
|
|
||||||
do {
|
do {
|
||||||
pf->V(pi) = va;
|
pf->V(pi) = va;
|
||||||
|
|
||||||
pi=(pi+2)%3;
|
pi=(pi+2)%3;
|
||||||
FaceType *t = pf->FFp(pi);
|
FaceType *t = pf->FFp(pi);
|
||||||
if (t==pf) { border= true; break; }
|
if (t==pf) { border= true; break; }
|
||||||
|
@ -823,7 +824,7 @@ static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* aff
|
||||||
pi = pf->FFi(pi);
|
pi = pf->FFi(pi);
|
||||||
pf = t;
|
pf = t;
|
||||||
} while (pf!=fb);
|
} while (pf!=fb);
|
||||||
|
|
||||||
// of found a border, also rotate around vb, (counter-sense-as-face)-wise
|
// of found a border, also rotate around vb, (counter-sense-as-face)-wise
|
||||||
if (border) {
|
if (border) {
|
||||||
val++;
|
val++;
|
||||||
|
@ -839,54 +840,54 @@ static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* aff
|
||||||
pf = t;
|
pf = t;
|
||||||
} while (pf!=fb);
|
} while (pf!=fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update FF, delete faces
|
// update FF, delete faces
|
||||||
_CollapseDiagHalf(*fb, fauxb, m);
|
_CollapseDiagHalf(*fb, fauxb, m);
|
||||||
_CollapseDiagHalf(*fa, fauxa, m);
|
_CollapseDiagHalf(*fa, fauxa, m);
|
||||||
|
|
||||||
SetValency(va, GetValency(va)+val-2);
|
SetValency(va, GetValency(va)+val-2);
|
||||||
DecreaseValency(fb,(fauxb+2)%3,m); // update valency
|
DecreaseValency(fb,(fauxb+2)%3,m); // update valency
|
||||||
DecreaseValency(fa,(fauxa+2)%3,m); // update valency
|
DecreaseValency(fa,(fauxa+2)%3,m); // update valency
|
||||||
Allocator<MeshType>::DeleteFace(m,*fa);
|
Allocator<MeshType>::DeleteFace(m,*fa);
|
||||||
Allocator<MeshType>::DeleteFace(m,*fb);
|
Allocator<MeshType>::DeleteFace(m,*fb);
|
||||||
|
|
||||||
//assert(val == GetValency(vb));
|
//assert(val == GetValency(vb));
|
||||||
|
|
||||||
|
|
||||||
DecreaseValencyNoSingletTest(vb, val, m);
|
DecreaseValencyNoSingletTest(vb, val, m);
|
||||||
// note: don't directly kill vb. In non-twomanifold, it could still be referecned
|
// note: don't directly kill vb. In non-twomanifold, it could still be referecned
|
||||||
// but: don't hunt for doublets either.
|
// but: don't hunt for doublets either.
|
||||||
|
|
||||||
assert(GetValency(vb)!=1 || vb->IsB());
|
assert(GetValency(vb)!=1 || vb->IsB());
|
||||||
// if this asserts, you are in trouble.
|
// if this asserts, you are in trouble.
|
||||||
// It means that the vertex that was supposed to die is still attached
|
// It means that the vertex that was supposed to die is still attached
|
||||||
// somewhere else (non-twomanifold)
|
// somewhere else (non-twomanifold)
|
||||||
// BUT in its other attachments it is a singlet, and that singlet cannot be
|
// BUT in its other attachments it is a singlet, and that singlet cannot be
|
||||||
// found now (would require VF)
|
// found now (would require VF)
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// helper function: find a good position on a diag to collapse a point
|
// helper function: find a good position on a diag to collapse a point
|
||||||
// currently, it is point in the middle,
|
// currently, it is point in the middle,
|
||||||
// unless a mixed border-non border edge is collapsed, then it is an exreme
|
// unless a mixed border-non border edge is collapsed, then it is an exreme
|
||||||
static ScalarType PosOnDiag(const FaceType& f, bool counterDiag){
|
static ScalarType PosOnDiag(const FaceType& f, bool counterDiag){
|
||||||
bool b0, b1, b2, b3; // which side of the quads are border
|
bool b0, b1, b2, b3; // which side of the quads are border
|
||||||
|
|
||||||
const FaceType* fa=&f;
|
const FaceType* fa=&f;
|
||||||
int ia = FauxIndex(fa);
|
int ia = FauxIndex(fa);
|
||||||
const FaceType* fb=fa->cFFp(ia);
|
const FaceType* fb=fa->cFFp(ia);
|
||||||
int ib = fa->cFFi(ia);
|
int ib = fa->cFFi(ia);
|
||||||
|
|
||||||
b0 = fa->FFp((ia+1)%3) == fa;
|
b0 = fa->FFp((ia+1)%3) == fa;
|
||||||
b1 = fa->FFp((ia+2)%3) == fa;
|
b1 = fa->FFp((ia+2)%3) == fa;
|
||||||
b2 = fb->FFp((ib+1)%3) == fb;
|
b2 = fb->FFp((ib+1)%3) == fb;
|
||||||
b3 = fb->FFp((ib+2)%3) == fb;
|
b3 = fb->FFp((ib+2)%3) == fb;
|
||||||
|
|
||||||
if (counterDiag) {
|
if (counterDiag) {
|
||||||
if ( (b0||b1) && !(b2||b3) ) return 1;
|
if ( (b0||b1) && !(b2||b3) ) return 1;
|
||||||
if ( !(b0||b1) && (b2||b3) ) return 0;
|
if ( !(b0||b1) && (b2||b3) ) return 0;
|
||||||
|
@ -936,7 +937,7 @@ static void DecreaseValency(FaceType *f, int wedge, MeshType &m){
|
||||||
VertexType *v = f->V(wedge);
|
VertexType *v = f->V(wedge);
|
||||||
int val = GetValency(v)-1;
|
int val = GetValency(v)-1;
|
||||||
SetValency( v, val );
|
SetValency( v, val );
|
||||||
if (val==0) Allocator<MeshType>::DeleteVertex(m,*v);
|
if (val==0) Allocator<MeshType>::DeleteVertex(m,*v);
|
||||||
if (val==1) // singlet!
|
if (val==1) // singlet!
|
||||||
RemoveSinglet(*f,wedge,m); // this could be recursive...
|
RemoveSinglet(*f,wedge,m); // this could be recursive...
|
||||||
}
|
}
|
||||||
|
@ -946,7 +947,7 @@ static void DecreaseValencyNoSingletTest(VertexType *v, int dv, MeshType &m){
|
||||||
int val = GetValency(v)-dv;
|
int val = GetValency(v)-dv;
|
||||||
SetValency( v, val );
|
SetValency( v, val );
|
||||||
if (DELETE_VERTICES)
|
if (DELETE_VERTICES)
|
||||||
if (val==0) Allocator<MeshType>::DeleteVertex(m,*v);
|
if (val==0) Allocator<MeshType>::DeleteVertex(m,*v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecreaseValencySimple(VertexType *v, int dv){
|
static void DecreaseValencySimple(VertexType *v, int dv){
|
||||||
|
@ -959,48 +960,45 @@ static void UpdateValencyInFlags(MeshType& m){
|
||||||
SetValency(&*vi,0);
|
SetValency(&*vi,0);
|
||||||
}
|
}
|
||||||
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
||||||
for (int w=0; w<3; w++)
|
for (int w=0; w<3; w++)
|
||||||
if (!fi->IsF(w))
|
if (!fi->IsF(w))
|
||||||
IncreaseValency( fi->V(w));
|
IncreaseValency( fi->V(w));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateValencyInQuality(MeshType& m){
|
static void UpdateValencyInQuality(MeshType& m){
|
||||||
for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) {
|
tri::UpdateQuality<MeshType>::VertexConstant(m,0);
|
||||||
vi->Q() = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
||||||
for (int w=0; w<3; w++)
|
for (int w=0; w<3; w++)
|
||||||
fi->V(w)->Q() += (fi->IsF(w)||fi->IsF((w+2)%3) )? 0.5f:1;
|
fi->V(w)->Q() += (fi->IsF(w)||fi->IsF((w+2)%3) )? 0.5f:1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool HasConsistentValencyFlag(MeshType &m) {
|
static bool HasConsistentValencyFlag(MeshType &m) {
|
||||||
UpdateValencyInQuality(m);
|
UpdateValencyInQuality(m);
|
||||||
bool isok=true;
|
bool isok=true;
|
||||||
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
|
||||||
for (int k=0; k<3; k++)
|
for (int k=0; k<3; k++)
|
||||||
if (GetValency(fi->V(k))!=fi->V(k)->Q()){
|
if (GetValency(fi->V(k))!=fi->V(k)->Q()){
|
||||||
MarkFaceF(&*fi);
|
MarkFaceF(&*fi);
|
||||||
isok=false;
|
isok=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isok;
|
return isok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function:
|
// helper function:
|
||||||
// returns quality of a given (potential) quad
|
// returns quality of a given (potential) quad
|
||||||
static ScalarType quadQuality(FaceType *f, int edge){
|
static ScalarType quadQuality(FaceType *f, int edgeInd){
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
CoordType
|
||||||
|
a = f->V0(edgeInd)->P(),
|
||||||
|
b = f->FFp(edgeInd)->V2( f->FFi(edgeInd) )->P(),
|
||||||
|
c = f->V1(edgeInd)->P(),
|
||||||
|
d = f->V2(edgeInd)->P();
|
||||||
|
|
||||||
|
return quadQuality(a,b,c,d);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1020,10 +1018,10 @@ static int TestEdgeRotation(const FaceType &f, int w0, ScalarType *gain=NULL)
|
||||||
CoordType v0,v1,v2,v3,v4,v5;
|
CoordType v0,v1,v2,v3,v4,v5;
|
||||||
int w1 = (w0+1)%3;
|
int w1 = (w0+1)%3;
|
||||||
int w2 = (w0+2)%3;
|
int w2 = (w0+2)%3;
|
||||||
|
|
||||||
v0 = fa->P(w0);
|
v0 = fa->P(w0);
|
||||||
v3 = fa->P(w1);
|
v3 = fa->P(w1);
|
||||||
|
|
||||||
if (fa->IsF(w2) ) {
|
if (fa->IsF(w2) ) {
|
||||||
v1 = fa->cFFp(w2)->V2( fa->cFFi(w2) )->P();
|
v1 = fa->cFFp(w2)->V2( fa->cFFi(w2) )->P();
|
||||||
v2 = fa->P(w2);
|
v2 = fa->P(w2);
|
||||||
|
@ -1031,10 +1029,10 @@ static int TestEdgeRotation(const FaceType &f, int w0, ScalarType *gain=NULL)
|
||||||
v1 = fa->P(w2);
|
v1 = fa->P(w2);
|
||||||
v2 = fa->cFFp(w1)->V2( fa->cFFi(w1) )->P();
|
v2 = fa->cFFp(w1)->V2( fa->cFFi(w1) )->P();
|
||||||
}
|
}
|
||||||
|
|
||||||
const FaceType *fb = fa->cFFp(w0);
|
const FaceType *fb = fa->cFFp(w0);
|
||||||
w0 = fa->cFFi(w0);
|
w0 = fa->cFFi(w0);
|
||||||
|
|
||||||
w1 = (w0+1)%3;
|
w1 = (w0+1)%3;
|
||||||
w2 = (w0+2)%3;
|
w2 = (w0+2)%3;
|
||||||
if (fb->IsF(w2) ) {
|
if (fb->IsF(w2) ) {
|
||||||
|
@ -1044,17 +1042,17 @@ static int TestEdgeRotation(const FaceType &f, int w0, ScalarType *gain=NULL)
|
||||||
v4 = fb->P(w2);
|
v4 = fb->P(w2);
|
||||||
v5 = fb->cFFp(w1)->V2( fb->cFFi(w1) )->P();
|
v5 = fb->cFFp(w1)->V2( fb->cFFi(w1) )->P();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if (!LENGTH_CRITERION)
|
#if (!LENGTH_CRITERION)
|
||||||
// max overall CONFORMAL quality criterion:
|
// max overall CONFORMAL quality criterion:
|
||||||
q0 = quadQuality(v0,v1,v2,v3) + quadQuality(v3,v4,v5,v0); // keep as is?
|
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?
|
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?
|
q2 = quadQuality(v5,v0,v1,v2) + quadQuality(v2,v3,v4,v5); // rotate CCW?
|
||||||
|
|
||||||
if (q0>=q1 && q0>=q2) return 0;
|
if (q0>=q1 && q0>=q2) return 0;
|
||||||
if (q1>=q2) return 1;
|
if (q1>=q2) return 1;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// min distance (shortcut criterion)
|
// min distance (shortcut criterion)
|
||||||
q0 = (v0 - v3).SquaredNorm();
|
q0 = (v0 - v3).SquaredNorm();
|
||||||
|
@ -1067,38 +1065,38 @@ static int TestEdgeRotation(const FaceType &f, int w0, ScalarType *gain=NULL)
|
||||||
//static int go=0;
|
//static int go=0;
|
||||||
//if ((stop+go)%100==99) printf("Stop: %4.1f%%\n",(stop*100.0/(stop+go)) );
|
//if ((stop+go)%100==99) printf("Stop: %4.1f%%\n",(stop*100.0/(stop+go)) );
|
||||||
|
|
||||||
if (q1<=q2) {
|
if (q1<=q2) {
|
||||||
if (gain) *gain = sqrt(q1)-sqrt(q0);
|
if (gain) *gain = sqrt(q1)-sqrt(q0);
|
||||||
// test: two diagonals should become shorter (the other two reamin the same)
|
// test: two diagonals should become shorter (the other two reamin the same)
|
||||||
if (
|
if (
|
||||||
(v0-v2).SquaredNorm() < (v4-v2).SquaredNorm() ||
|
(v0-v2).SquaredNorm() < (v4-v2).SquaredNorm() ||
|
||||||
(v3-v5).SquaredNorm() < (v1-v5).SquaredNorm()
|
(v3-v5).SquaredNorm() < (v1-v5).SquaredNorm()
|
||||||
) {
|
) {
|
||||||
//stop++;
|
//stop++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//go++;
|
//go++;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if (gain) *gain = sqrt(q2)-sqrt(q0);
|
if (gain) *gain = sqrt(q2)-sqrt(q0);
|
||||||
// diagonal test, as above:
|
// diagonal test, as above:
|
||||||
if (
|
if (
|
||||||
(v0-v4).SquaredNorm() < (v2-v4).SquaredNorm() ||
|
(v0-v4).SquaredNorm() < (v2-v4).SquaredNorm() ||
|
||||||
(v3-v1).SquaredNorm() < (v5-v1).SquaredNorm()
|
(v3-v1).SquaredNorm() < (v5-v1).SquaredNorm()
|
||||||
) {
|
) {
|
||||||
//stop++;
|
//stop++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//go++;
|
//go++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// helper function:
|
// helper function:
|
||||||
// returns quality of a quad formed by points a,b,c,d
|
// returns quality of a quad formed by points a,b,c,d
|
||||||
// quality is computed as "how squared angles are"
|
// quality is computed as "how squared angles are"
|
||||||
|
@ -1115,12 +1113,12 @@ static ScalarType quadQuality(const CoordType &a, const CoordType &b, const Coor
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// helper function:
|
// helper function:
|
||||||
// cos of angle abc. This should probably go elsewhere
|
// cos of angle abc. This should probably go elsewhere
|
||||||
static ScalarType Cos(const CoordType &a, const CoordType &b, const CoordType &c )
|
static ScalarType Cos(const CoordType &a, const CoordType &b, const CoordType &c )
|
||||||
{
|
{
|
||||||
CoordType
|
CoordType
|
||||||
e0 = b - a,
|
e0 = b - a,
|
||||||
e1 = b - c;
|
e1 = b - c;
|
||||||
ScalarType d = (e0.Norm()*e1.Norm());
|
ScalarType d = (e0.Norm()*e1.Norm());
|
||||||
|
|
|
@ -44,108 +44,102 @@
|
||||||
#include <vcg/complex/algorithms/update/topology.h>
|
#include <vcg/complex/algorithms/update/topology.h>
|
||||||
#include <vcg/space/triangle3.h>
|
#include <vcg/space/triangle3.h>
|
||||||
|
|
||||||
|
|
||||||
namespace vcg {
|
namespace vcg {
|
||||||
namespace tri{
|
namespace tri{
|
||||||
template <class ConnectedMeshType>
|
|
||||||
class ConnectedIterator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef ConnectedMeshType MeshType;
|
|
||||||
typedef typename MeshType::VertexType VertexType;
|
|
||||||
typedef typename MeshType::VertexPointer VertexPointer;
|
|
||||||
typedef typename MeshType::VertexIterator VertexIterator;
|
|
||||||
typedef typename MeshType::ScalarType ScalarType;
|
|
||||||
typedef typename MeshType::FaceType FaceType;
|
|
||||||
typedef typename MeshType::FacePointer FacePointer;
|
|
||||||
typedef typename MeshType::FaceIterator FaceIterator;
|
|
||||||
typedef typename MeshType::ConstFaceIterator ConstFaceIterator;
|
|
||||||
typedef typename MeshType::FaceContainer FaceContainer;
|
|
||||||
|
|
||||||
|
template <class ConnectedMeshType>
|
||||||
|
class ConnectedComponentIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef ConnectedMeshType MeshType;
|
||||||
|
typedef typename MeshType::VertexType VertexType;
|
||||||
|
typedef typename MeshType::VertexPointer VertexPointer;
|
||||||
|
typedef typename MeshType::VertexIterator VertexIterator;
|
||||||
|
typedef typename MeshType::ScalarType ScalarType;
|
||||||
|
typedef typename MeshType::FaceType FaceType;
|
||||||
|
typedef typename MeshType::FacePointer FacePointer;
|
||||||
|
typedef typename MeshType::FaceIterator FaceIterator;
|
||||||
|
typedef typename MeshType::ConstFaceIterator ConstFaceIterator;
|
||||||
|
typedef typename MeshType::FaceContainer FaceContainer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void operator ++()
|
void operator ++()
|
||||||
{
|
{
|
||||||
FacePointer fpt=sf.top();
|
FacePointer fpt=sf.top();
|
||||||
sf.pop();
|
sf.pop();
|
||||||
for(int j=0;j<3;++j)
|
for(int j=0;j<3;++j)
|
||||||
if( !face::IsBorder(*fpt,j) )
|
if( !face::IsBorder(*fpt,j) )
|
||||||
{
|
{
|
||||||
FacePointer l=fpt->FFp(j);
|
FacePointer l=fpt->FFp(j);
|
||||||
if( !tri::IsMarked(*mp,l) )
|
if( !tri::IsMarked(*mp,l) )
|
||||||
{
|
{
|
||||||
tri::Mark(*mp,l);
|
tri::Mark(*mp,l);
|
||||||
sf.push(l);
|
sf.push(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void start(MeshType &m, FacePointer p)
|
void start(MeshType &m, FacePointer p)
|
||||||
{
|
{
|
||||||
mp=&m;
|
mp=&m;
|
||||||
while(!sf.empty()) sf.pop();
|
while(!sf.empty()) sf.pop();
|
||||||
UnMarkAll(m);
|
UnMarkAll(m);
|
||||||
assert(p);
|
assert(p);
|
||||||
assert(!p->IsD());
|
assert(!p->IsD());
|
||||||
tri::Mark(m,p);
|
tri::Mark(m,p);
|
||||||
sf.push(p);
|
sf.push(p);
|
||||||
}
|
}
|
||||||
bool completed() {
|
|
||||||
return sf.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
FacePointer operator *()
|
bool completed() {
|
||||||
{
|
return sf.empty();
|
||||||
return sf.top();
|
}
|
||||||
}
|
|
||||||
|
FacePointer operator *()
|
||||||
|
{
|
||||||
|
return sf.top();
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::stack<FacePointer> sf;
|
std::stack<FacePointer> sf;
|
||||||
MeshType *mp;
|
MeshType *mp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/** \addtogroup trimesh */
|
/** \addtogroup trimesh */
|
||||||
/*@{*/
|
/*@{*/
|
||||||
/// Class of static functions to clean//restore meshs.
|
/// Class of static functions to clean//restore meshs.
|
||||||
template <class CleanMeshType>
|
template <class CleanMeshType>
|
||||||
class Clean
|
class Clean
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef CleanMeshType MeshType;
|
typedef CleanMeshType MeshType;
|
||||||
typedef typename MeshType::VertexType VertexType;
|
typedef typename MeshType::VertexType VertexType;
|
||||||
typedef typename MeshType::VertexPointer VertexPointer;
|
typedef typename MeshType::VertexPointer VertexPointer;
|
||||||
typedef typename MeshType::VertexIterator VertexIterator;
|
typedef typename MeshType::VertexIterator VertexIterator;
|
||||||
typedef typename MeshType::ConstVertexIterator ConstVertexIterator;
|
typedef typename MeshType::ConstVertexIterator ConstVertexIterator;
|
||||||
typedef typename MeshType::EdgeIterator EdgeIterator;
|
typedef typename MeshType::EdgeIterator EdgeIterator;
|
||||||
typedef typename MeshType::EdgePointer EdgePointer;
|
typedef typename MeshType::EdgePointer EdgePointer;
|
||||||
typedef typename MeshType::CoordType CoordType;
|
typedef typename MeshType::CoordType CoordType;
|
||||||
typedef typename MeshType::ScalarType ScalarType;
|
typedef typename MeshType::ScalarType ScalarType;
|
||||||
typedef typename MeshType::FaceType FaceType;
|
typedef typename MeshType::FaceType FaceType;
|
||||||
typedef typename MeshType::FacePointer FacePointer;
|
typedef typename MeshType::FacePointer FacePointer;
|
||||||
typedef typename MeshType::FaceIterator FaceIterator;
|
typedef typename MeshType::FaceIterator FaceIterator;
|
||||||
typedef typename MeshType::ConstFaceIterator ConstFaceIterator;
|
typedef typename MeshType::ConstFaceIterator ConstFaceIterator;
|
||||||
typedef typename MeshType::FaceContainer FaceContainer;
|
typedef typename MeshType::FaceContainer FaceContainer;
|
||||||
typedef typename vcg::Box3<ScalarType> Box3Type;
|
typedef typename vcg::Box3<ScalarType> Box3Type;
|
||||||
|
|
||||||
typedef GridStaticPtr<FaceType, ScalarType > TriMeshGrid;
|
typedef GridStaticPtr<FaceType, ScalarType > TriMeshGrid;
|
||||||
typedef Point3<ScalarType> Point3x;
|
typedef Point3<ScalarType> Point3x;
|
||||||
|
|
||||||
//TriMeshGrid gM;
|
/* classe di confronto per l'algoritmo di eliminazione vertici duplicati*/
|
||||||
//FaceIterator fi;
|
class RemoveDuplicateVert_Compare{
|
||||||
//FaceIterator gi;
|
public:
|
||||||
//vcg::face::Pos<FaceType> he;
|
inline bool operator()(VertexPointer const &a, VertexPointer const &b)
|
||||||
//vcg::face::Pos<FaceType> hei;
|
{
|
||||||
|
return (*a).cP() < (*b).cP();
|
||||||
/* classe di confronto per l'algoritmo di eliminazione vertici duplicati*/
|
}
|
||||||
class RemoveDuplicateVert_Compare{
|
};
|
||||||
public:
|
|
||||||
inline bool operator()(VertexPointer const &a, VertexPointer const &b)
|
|
||||||
{
|
|
||||||
return (*a).cP() < (*b).cP();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** This function removes all duplicate vertices of the mesh by looking only at their spatial positions.
|
/** This function removes all duplicate vertices of the mesh by looking only at their spatial positions.
|
||||||
|
@ -627,7 +621,7 @@ private:
|
||||||
static bool IsBitQuadOnly(const MeshType &m)
|
static bool IsBitQuadOnly(const MeshType &m)
|
||||||
{
|
{
|
||||||
typedef typename MeshType::FaceType F;
|
typedef typename MeshType::FaceType F;
|
||||||
if (!HasPerFaceFlags(m)) return false;
|
tri::RequirePerFaceFlags(m);
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
||||||
unsigned int tmp = fi->Flags()&(F::FAUX0|F::FAUX1|F::FAUX2);
|
unsigned int tmp = fi->Flags()&(F::FAUX0|F::FAUX1|F::FAUX2);
|
||||||
if ( tmp != F::FAUX0 && tmp != F::FAUX1 && tmp != F::FAUX2) return false;
|
if ( tmp != F::FAUX0 && tmp != F::FAUX1 && tmp != F::FAUX2) return false;
|
||||||
|
@ -636,207 +630,187 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the mesh only composed by triangles? (non polygonal faces)
|
* Is the mesh only composed by triangles? (non polygonal faces)
|
||||||
*/
|
*/
|
||||||
static bool IsBitTriOnly(const MeshType &m)
|
static bool IsBitTriOnly(const MeshType &m)
|
||||||
{
|
{
|
||||||
if (!HasPerFaceFlags(m)) return true;
|
tri::RequirePerFaceFlags(m);
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) {
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) {
|
||||||
if (
|
if ( !fi->IsD() && fi->IsAnyF() ) return false;
|
||||||
!fi->IsD() && fi->IsAnyF()
|
}
|
||||||
) return false;
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsBitPolygonal(const MeshType &m){
|
static bool IsBitPolygonal(const MeshType &m){
|
||||||
return !IsBitTriOnly(m);
|
return !IsBitTriOnly(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the mesh only composed by quadrilaterals and triangles? (no pentas, etc)
|
* Is the mesh only composed by quadrilaterals and triangles? (no pentas, etc)
|
||||||
*/
|
* It assumes that the bits are consistent. In that case there can be only a single faux edge.
|
||||||
static bool IsBitTriQuadOnly(const MeshType &m)
|
*/
|
||||||
{
|
static bool IsBitTriQuadOnly(const MeshType &m)
|
||||||
typedef typename MeshType::FaceType F;
|
{
|
||||||
if (!HasPerFaceFlags(m)) return false;
|
tri::RequirePerFaceFlags(m);
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
typedef typename MeshType::FaceType F;
|
||||||
unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2);
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
||||||
if ( tmp!=F::FAUX0 && tmp!=F::FAUX1 && tmp!=F::FAUX2 && tmp!=0 ) return false;
|
unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2);
|
||||||
}
|
if ( tmp!=F::FAUX0 && tmp!=F::FAUX1 && tmp!=F::FAUX2 && tmp!=0 ) return false;
|
||||||
return true;
|
}
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many quadrilaterals?
|
* How many quadrilaterals?
|
||||||
*/
|
* It assumes that the bits are consistent. In that case we count the tris with a single faux edge and divide by two.
|
||||||
static int CountBitQuads(const MeshType &m)
|
*/
|
||||||
{
|
static int CountBitQuads(const MeshType &m)
|
||||||
if (!HasPerFaceFlags(m)) return 0;
|
{
|
||||||
typedef typename MeshType::FaceType F;
|
tri::RequirePerFaceFlags(m);
|
||||||
int count=0;
|
typedef typename MeshType::FaceType F;
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
int count=0;
|
||||||
unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2);
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
||||||
if ( tmp==F::FAUX0 || tmp==F::FAUX1 || tmp==F::FAUX2) count++;
|
unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2);
|
||||||
}
|
if ( tmp==F::FAUX0 || tmp==F::FAUX1 || tmp==F::FAUX2) count++;
|
||||||
return count / 2;
|
}
|
||||||
}
|
return count / 2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many triangles? (non polygonal faces)
|
* How many triangles? (non polygonal faces)
|
||||||
*/
|
*/
|
||||||
static int CountBitTris(const MeshType &m)
|
static int CountBitTris(const MeshType &m)
|
||||||
{
|
{
|
||||||
if (!HasPerFaceFlags(m)) return m.fn;
|
tri::RequirePerFaceFlags(m);
|
||||||
int count=0;
|
int count=0;
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
||||||
if (!(fi->IsAnyF())) count++;
|
if (!(fi->IsAnyF())) count++;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many polygons of any kind? (including triangles)
|
* How many polygons of any kind? (including triangles)
|
||||||
*/
|
* it assumes that there are no faux vertexes (e.g vertices completely surrounded by faux edges)
|
||||||
static int CountBitPolygons(const MeshType &m)
|
*/
|
||||||
{
|
static int CountBitPolygons(const MeshType &m)
|
||||||
if (!HasPerFaceFlags(m)) return m.fn;
|
{
|
||||||
typedef typename MeshType::FaceType F;
|
tri::RequirePerFaceFlags(m);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) {
|
||||||
if (fi->IsF(0)) count++;
|
if (fi->IsF(0)) count++;
|
||||||
if (fi->IsF(1)) count++;
|
if (fi->IsF(1)) count++;
|
||||||
if (fi->IsF(2)) count++;
|
if (fi->IsF(2)) count++;
|
||||||
}
|
}
|
||||||
return m.fn - count/2;
|
return m.fn - count/2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of polygonal faces is
|
* The number of polygonal faces is
|
||||||
* FN - EN_f (each faux edge hides exactly one triangular face or in other words a polygon of n edges has n-3 faux edges.)
|
* FN - EN_f (each faux edge hides exactly one triangular face or in other words a polygon of n edges has n-3 faux edges.)
|
||||||
* In the general case where a The number of polygonal faces is
|
* In the general case where a The number of polygonal faces is
|
||||||
* FN - EN_f + VN_f
|
* FN - EN_f + VN_f
|
||||||
* where:
|
* where:
|
||||||
* EN_f is the number of faux edges.
|
* EN_f is the number of faux edges.
|
||||||
* VN_f is the number of faux vertices (e.g vertices completely surrounded by faux edges)
|
* VN_f is the number of faux vertices (e.g vertices completely surrounded by faux edges)
|
||||||
* as a intuitive proof think to a internal vertex that is collapsed onto a border of a polygon:
|
* as a intuitive proof think to a internal vertex that is collapsed onto a border of a polygon:
|
||||||
* it deletes 2 faces, 1 faux edges and 1 vertex so to keep the balance you have to add back the removed vertex.
|
* it deletes 2 faces, 1 faux edges and 1 vertex so to keep the balance you have to add back the removed vertex.
|
||||||
*/
|
*/
|
||||||
static int CountBitLargePolygons(MeshType &m)
|
static int CountBitLargePolygons(MeshType &m)
|
||||||
{
|
{
|
||||||
|
tri::RequirePerFaceFlags(m);
|
||||||
UpdateFlags<MeshType>::VertexSetV(m);
|
UpdateFlags<MeshType>::VertexSetV(m);
|
||||||
// First loop Clear all referenced vertices
|
// First loop Clear all referenced vertices
|
||||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
if (!fi->IsD())
|
if (!fi->IsD())
|
||||||
for(int i=0;i<3;++i) fi->V(i)->ClearV();
|
for(int i=0;i<3;++i) fi->V(i)->ClearV();
|
||||||
|
|
||||||
|
|
||||||
// Second Loop, count (twice) faux edges and mark all vertices touched by non faux edges (e.g vertexes on the boundary of a polygon)
|
// Second Loop, count (twice) faux edges and mark all vertices touched by non faux edges
|
||||||
if (!HasPerFaceFlags(m)) return m.fn;
|
// (e.g vertexes on the boundary of a polygon)
|
||||||
typedef typename MeshType::FaceType F;
|
int countE = 0;
|
||||||
int countE = 0;
|
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
if (!fi->IsD()) {
|
||||||
if (!fi->IsD()) {
|
for(int i=0;i<3;++i)
|
||||||
for(int i=0;i<3;++i)
|
|
||||||
{
|
|
||||||
if (fi->IsF(i))
|
|
||||||
countE++;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fi->V0(i)->SetV();
|
|
||||||
fi->V1(i)->SetV();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Third Loop, count the number of referenced vertexes that are completely surrounded by faux edges.
|
|
||||||
|
|
||||||
int countV = 0;
|
|
||||||
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
|
||||||
if (!vi->IsD() && !vi->IsV()) countV++;
|
|
||||||
|
|
||||||
return m.fn - countE/2 + countV ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the mesh has consistent per-face faux edges
|
|
||||||
* (the ones that merges triangles into larger polygons).
|
|
||||||
* A border edge should never be faux, and faux edges should always be
|
|
||||||
* reciprocated by another faux edges.
|
|
||||||
* It requires FF adjacency.
|
|
||||||
*/
|
|
||||||
static bool HasConsistentPerFaceFauxFlag(const MeshType &m)
|
|
||||||
{
|
|
||||||
RequireFFAdjacency(m);
|
|
||||||
RequirePerFaceFlags(m);
|
|
||||||
|
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
|
||||||
if(!(*fi).IsD())
|
|
||||||
for (int k=0; k<3; k++)
|
|
||||||
if( fi->IsF(k) != fi->cFFp(k)->IsF(fi->cFFi(k)) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// non-reciprocal faux edge!
|
|
||||||
// (OR: border faux edge, which is likewise inconsistent)
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool HasConsistentEdges(const MeshType &m)
|
|
||||||
{
|
|
||||||
RequirePerFaceFlags(m);
|
|
||||||
|
|
||||||
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
|
||||||
if(!(*fi).IsD())
|
|
||||||
for (int k=0; k<3; k++)
|
|
||||||
{
|
|
||||||
VertexType *v0=(*fi).V(0);
|
|
||||||
VertexType *v1=(*fi).V(1);
|
|
||||||
VertexType *v2=(*fi).V(2);
|
|
||||||
if ((v0==v1)||(v0==v2)||(v1==v2))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count the number of non manifold edges in a polylinemesh, e.g. the edges where there are more than 2 incident faces.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false)
|
|
||||||
{
|
|
||||||
assert(m.fn == 0 && m.en >0); // just to be sure we are using an edge mesh...
|
|
||||||
RequireEEAdjacency(m);
|
|
||||||
tri::UpdateTopology<MeshType>::EdgeEdge(m);
|
|
||||||
|
|
||||||
if(SelectFlag) UpdateSelection<MeshType>::VertexClear(m);
|
|
||||||
|
|
||||||
int nonManifoldCnt=0;
|
|
||||||
SimpleTempData<typename MeshType::VertContainer, int > TD(m.vert,0);
|
|
||||||
|
|
||||||
// First Loop, just count how many faces are incident on a vertex and store it in the TemporaryData Counter.
|
|
||||||
EdgeIterator ei;
|
|
||||||
for (ei = m.edge.begin(); ei != m.edge.end(); ++ei) if (!ei->IsD())
|
|
||||||
{
|
{
|
||||||
TD[(*ei).V(0)]++;
|
if (fi->IsF(i))
|
||||||
TD[(*ei).V(1)]++;
|
countE++;
|
||||||
}
|
else
|
||||||
|
|
||||||
tri::UpdateFlags<MeshType>::VertexClearV(m);
|
|
||||||
// Second Loop, Check that each vertex have been seen 1 or 2 times.
|
|
||||||
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if (!vi->IsD())
|
|
||||||
{
|
|
||||||
if( TD[vi] >2 )
|
|
||||||
{
|
{
|
||||||
if(SelectFlag) (*vi).SetS();
|
fi->V0(i)->SetV();
|
||||||
nonManifoldCnt++;
|
fi->V1(i)->SetV();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nonManifoldCnt;
|
|
||||||
}
|
}
|
||||||
|
// Third Loop, count the number of referenced vertexes that are completely surrounded by faux edges.
|
||||||
|
|
||||||
|
int countV = 0;
|
||||||
|
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
|
||||||
|
if (!vi->IsD() && !vi->IsV()) countV++;
|
||||||
|
|
||||||
|
return m.fn - countE/2 + countV ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the mesh has consistent per-face faux edges
|
||||||
|
* (the ones that merges triangles into larger polygons).
|
||||||
|
* A border edge should never be faux, and faux edges should always be
|
||||||
|
* reciprocated by another faux edges.
|
||||||
|
* It requires FF adjacency.
|
||||||
|
*/
|
||||||
|
static bool HasConsistentPerFaceFauxFlag(const MeshType &m)
|
||||||
|
{
|
||||||
|
RequireFFAdjacency(m);
|
||||||
|
RequirePerFaceFlags(m);
|
||||||
|
|
||||||
|
for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
|
if(!(*fi).IsD())
|
||||||
|
for (int k=0; k<3; k++)
|
||||||
|
if( ( fi->IsF(k) != fi->cFFp(k)->IsF(fi->cFFi(k)) ) ||
|
||||||
|
( fi->IsF(k) && face::IsBorder(*fi,k)) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of non manifold edges in a polylinemesh, e.g. the edges where there are more than 2 incident faces.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false)
|
||||||
|
{
|
||||||
|
assert(m.fn == 0 && m.en >0); // just to be sure we are using an edge mesh...
|
||||||
|
RequireEEAdjacency(m);
|
||||||
|
tri::UpdateTopology<MeshType>::EdgeEdge(m);
|
||||||
|
|
||||||
|
if(SelectFlag) UpdateSelection<MeshType>::VertexClear(m);
|
||||||
|
|
||||||
|
int nonManifoldCnt=0;
|
||||||
|
SimpleTempData<typename MeshType::VertContainer, int > TD(m.vert,0);
|
||||||
|
|
||||||
|
// First Loop, just count how many faces are incident on a vertex and store it in the TemporaryData Counter.
|
||||||
|
EdgeIterator ei;
|
||||||
|
for (ei = m.edge.begin(); ei != m.edge.end(); ++ei) if (!ei->IsD())
|
||||||
|
{
|
||||||
|
TD[(*ei).V(0)]++;
|
||||||
|
TD[(*ei).V(1)]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tri::UpdateFlags<MeshType>::VertexClearV(m);
|
||||||
|
// Second Loop, Check that each vertex have been seen 1 or 2 times.
|
||||||
|
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if (!vi->IsD())
|
||||||
|
{
|
||||||
|
if( TD[vi] >2 )
|
||||||
|
{
|
||||||
|
if(SelectFlag) (*vi).SetS();
|
||||||
|
nonManifoldCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nonManifoldCnt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of non manifold edges in a mesh, e.g. the edges where there are more than 2 incident faces.
|
* Count the number of non manifold edges in a mesh, e.g. the edges where there are more than 2 incident faces.
|
||||||
|
@ -1572,7 +1546,7 @@ private:
|
||||||
*/
|
*/
|
||||||
static bool HasConsistentPerWedgeTexCoord(MeshType &m)
|
static bool HasConsistentPerWedgeTexCoord(MeshType &m)
|
||||||
{
|
{
|
||||||
if(!HasPerWedgeTexCoord(m)) return false;
|
tri::RequirePerFaceWedgeTexCoord(m);
|
||||||
|
|
||||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
if(!(*fi).IsD())
|
if(!(*fi).IsD())
|
||||||
|
@ -1585,20 +1559,20 @@ private:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Simple check that there are no face with all collapsed tex coords.
|
Simple check that there are no face with all collapsed tex coords.
|
||||||
*/
|
*/
|
||||||
static bool HasZeroTexCoordFace(MeshType &m)
|
static bool HasZeroTexCoordFace(MeshType &m)
|
||||||
{
|
{
|
||||||
if(!HasPerWedgeTexCoord(m)) return false;
|
tri::RequirePerFaceWedgeTexCoord(m);
|
||||||
|
|
||||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
if(!(*fi).IsD())
|
if(!(*fi).IsD())
|
||||||
{
|
{
|
||||||
if( (*fi).WT(0).P() == (*fi).WT(1).P() && (*fi).WT(0).P() == (*fi).WT(2).P() ) return false;
|
if( (*fi).WT(0).P() == (*fi).WT(1).P() && (*fi).WT(0).P() == (*fi).WT(2).P() ) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1642,51 +1616,51 @@ private:
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This function merge all the vertices that are closer than the given radius
|
This function merge all the vertices that are closer than the given radius
|
||||||
*/
|
*/
|
||||||
static int MergeCloseVertex(MeshType &m, const ScalarType radius)
|
static int MergeCloseVertex(MeshType &m, const ScalarType radius)
|
||||||
{
|
{
|
||||||
int mergedCnt=0;
|
int mergedCnt=0;
|
||||||
mergedCnt = ClusterVertex(m,radius);
|
mergedCnt = ClusterVertex(m,radius);
|
||||||
RemoveDuplicateVertex(m,true);
|
RemoveDuplicateVertex(m,true);
|
||||||
return mergedCnt;
|
return mergedCnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ClusterVertex(MeshType &m, const ScalarType radius)
|
static int ClusterVertex(MeshType &m, const ScalarType radius)
|
||||||
{
|
{
|
||||||
if(m.vn==0) return 0;
|
if(m.vn==0) return 0;
|
||||||
// some spatial indexing structure does not work well with deleted vertices...
|
// some spatial indexing structure does not work well with deleted vertices...
|
||||||
tri::Allocator<MeshType>::CompactVertexVector(m);
|
tri::Allocator<MeshType>::CompactVertexVector(m);
|
||||||
typedef vcg::SpatialHashTable<VertexType, ScalarType> SampleSHT;
|
typedef vcg::SpatialHashTable<VertexType, ScalarType> SampleSHT;
|
||||||
SampleSHT sht;
|
SampleSHT sht;
|
||||||
tri::VertTmark<MeshType> markerFunctor;
|
tri::VertTmark<MeshType> markerFunctor;
|
||||||
typedef vcg::vertex::PointDistanceFunctor<ScalarType> VDistFunct;
|
typedef vcg::vertex::PointDistanceFunctor<ScalarType> VDistFunct;
|
||||||
std::vector<VertexType*> closests;
|
std::vector<VertexType*> closests;
|
||||||
int mergedCnt=0;
|
int mergedCnt=0;
|
||||||
sht.Set(m.vert.begin(), m.vert.end());
|
sht.Set(m.vert.begin(), m.vert.end());
|
||||||
UpdateFlags<MeshType>::VertexClearV(m);
|
UpdateFlags<MeshType>::VertexClearV(m);
|
||||||
for(VertexIterator viv = m.vert.begin(); viv!= m.vert.end(); ++viv)
|
for(VertexIterator viv = m.vert.begin(); viv!= m.vert.end(); ++viv)
|
||||||
if(!(*viv).IsD() && !(*viv).IsV())
|
if(!(*viv).IsD() && !(*viv).IsV())
|
||||||
{
|
{
|
||||||
(*viv).SetV();
|
(*viv).SetV();
|
||||||
Point3<ScalarType> p = viv->cP();
|
Point3<ScalarType> p = viv->cP();
|
||||||
Box3<ScalarType> bb(p-Point3<ScalarType>(radius,radius,radius),p+Point3<ScalarType>(radius,radius,radius));
|
Box3<ScalarType> bb(p-Point3<ScalarType>(radius,radius,radius),p+Point3<ScalarType>(radius,radius,radius));
|
||||||
GridGetInBox(sht, markerFunctor, bb, closests);
|
GridGetInBox(sht, markerFunctor, bb, closests);
|
||||||
// qDebug("Vertex %i has %i closest", &*viv - &*m.vert.begin(),closests.size());
|
// qDebug("Vertex %i has %i closest", &*viv - &*m.vert.begin(),closests.size());
|
||||||
for(size_t i=0; i<closests.size(); ++i)
|
for(size_t i=0; i<closests.size(); ++i)
|
||||||
{
|
{
|
||||||
ScalarType dist = Distance(p,closests[i]->cP());
|
ScalarType dist = Distance(p,closests[i]->cP());
|
||||||
if(dist < radius && !closests[i]->IsV())
|
if(dist < radius && !closests[i]->IsV())
|
||||||
{
|
{
|
||||||
// printf("%f %f \n",dist,radius);
|
// printf("%f %f \n",dist,radius);
|
||||||
mergedCnt++;
|
mergedCnt++;
|
||||||
closests[i]->SetV();
|
closests[i]->SetV();
|
||||||
closests[i]->P()=p;
|
closests[i]->P()=p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mergedCnt;
|
return mergedCnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static std::pair<int,int> RemoveSmallConnectedComponentsSize(MeshType &m, int maxCCSize)
|
static std::pair<int,int> RemoveSmallConnectedComponentsSize(MeshType &m, int maxCCSize)
|
||||||
|
@ -1695,7 +1669,7 @@ static std::pair<int,int> RemoveSmallConnectedComponentsSize(MeshType &m, int m
|
||||||
int TotalCC=ConnectedComponents(m, CCV);
|
int TotalCC=ConnectedComponents(m, CCV);
|
||||||
int DeletedCC=0;
|
int DeletedCC=0;
|
||||||
|
|
||||||
ConnectedIterator<MeshType> ci;
|
ConnectedComponentIterator<MeshType> ci;
|
||||||
for(unsigned int i=0;i<CCV.size();++i)
|
for(unsigned int i=0;i<CCV.size();++i)
|
||||||
{
|
{
|
||||||
std::vector<typename MeshType::FacePointer> FPV;
|
std::vector<typename MeshType::FacePointer> FPV;
|
||||||
|
@ -1721,7 +1695,7 @@ static std::pair<int,int> RemoveSmallConnectedComponentsDiameter(MeshType &m, Sc
|
||||||
std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
|
std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
|
||||||
int TotalCC=ConnectedComponents(m, CCV);
|
int TotalCC=ConnectedComponents(m, CCV);
|
||||||
int DeletedCC=0;
|
int DeletedCC=0;
|
||||||
tri::ConnectedIterator<MeshType> ci;
|
tri::ConnectedComponentIterator<MeshType> ci;
|
||||||
for(unsigned int i=0;i<CCV.size();++i)
|
for(unsigned int i=0;i<CCV.size();++i)
|
||||||
{
|
{
|
||||||
Box3f bb;
|
Box3f bb;
|
||||||
|
@ -1751,7 +1725,7 @@ static std::pair<int,int> RemoveHugeConnectedComponentsDiameter(MeshType &m, Sca
|
||||||
std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
|
std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
|
||||||
int TotalCC=ConnectedComponents(m, CCV);
|
int TotalCC=ConnectedComponents(m, CCV);
|
||||||
int DeletedCC=0;
|
int DeletedCC=0;
|
||||||
tri::ConnectedIterator<MeshType> ci;
|
tri::ConnectedComponentIterator<MeshType> ci;
|
||||||
for(unsigned int i=0;i<CCV.size();++i)
|
for(unsigned int i=0;i<CCV.size();++i)
|
||||||
{
|
{
|
||||||
Box3f bb;
|
Box3f bb;
|
||||||
|
|
|
@ -287,7 +287,7 @@ It require FaceFace Adjacency becouse it relies on the output of the ConnecteCom
|
||||||
std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
|
std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
|
||||||
int ScatterSize= std::min (100,tri::Clean<MeshType>::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many.
|
int ScatterSize= std::min (100,tri::Clean<MeshType>::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many.
|
||||||
|
|
||||||
ConnectedIterator<MeshType> ci;
|
ConnectedComponentIterator<MeshType> ci;
|
||||||
for(unsigned int i=0;i<CCV.size();++i)
|
for(unsigned int i=0;i<CCV.size();++i)
|
||||||
{
|
{
|
||||||
Color4b BaseColor = Color4b::Scatter(ScatterSize, i%ScatterSize,.4f,.7f);
|
Color4b BaseColor = Color4b::Scatter(ScatterSize, i%ScatterSize,.4f,.7f);
|
||||||
|
|
Loading…
Reference in New Issue