first working implementationof "EAR HOLE FILLING".

This commit is contained in:
Paolo Cignoni 2006-10-06 15:28:14 +00:00
parent d878081824
commit 2daa4cdd9b
1 changed files with 685 additions and 580 deletions

View File

@ -21,9 +21,12 @@
* * * *
****************************************************************************/ ****************************************************************************/
/**************************************************************************** /****************************************************************************
History History
$Log: not supported by cvs2svn $ $Log: not supported by cvs2svn $
Revision 1.4 2006/10/02 12:06:40 giec
BugFix
Revision 1.3 2006/09/27 15:33:32 giec Revision 1.3 2006/09/27 15:33:32 giec
It close one simple hole . . . It close one simple hole . . .
@ -43,11 +46,11 @@ Questa Classe serve per gestire la non duplicazione degli edge durante la chiusu
di un buco. di un buco.
*/ */
namespace vcg { namespace vcg {
namespace tri { namespace tri {
template<class MESH> template<class MESH>
class SimpleEdge class SimpleEdge
{ {
public: public:
typename MESH::VertexType v[2]; typename MESH::VertexType v[2];
SimpleEdge() SimpleEdge()
@ -88,12 +91,12 @@ class SimpleEdge
else return false; else return false;
} }
}; };
template<class MESH> template<class MESH>
class HoleInfo class HoleInfo
{ {
public: public:
HoleInfo(){} HoleInfo(){}
HoleInfo(face::Pos<typename MESH::FaceType> const &pHole, int const pHoleSize, vcg::Box3<typename MESH::ScalarType> &pHoleBB) HoleInfo(face::Pos<typename MESH::FaceType> const &pHole, int const pHoleSize, vcg::Box3<typename MESH::ScalarType> &pHoleBB)
{ {
@ -101,9 +104,25 @@ public:
size=pHoleSize; size=pHoleSize;
bb=pHoleBB; bb=pHoleBB;
} }
HoleInfo(face::Pos<typename MESH::FaceType> const &pHole, int const pHoleSize, vcg::Box3<typename MESH::ScalarType> &pHoleBB, int FI)
{
p=pHole;
size=pHoleSize;
bb=pHoleBB;
faceindex = FI;
}
typename face::Pos<typename MESH::FaceType> p; typename face::Pos<typename MESH::FaceType> p;
int size; int size;
vcg::Box3<typename MESH::ScalarType> bb; vcg::Box3<typename MESH::ScalarType> bb;
int faceindex;
void Refresh(MESH &m)
{
p.f = (MESH::FacePointer)(faceindex + &(*(m.face.begin())));
}
bool operator < (const HoleInfo & hh) const {return size < hh.size;} bool operator < (const HoleInfo & hh) const {return size < hh.size;}
bool operator > (const HoleInfo & hh) const {return size > hh.size;} bool operator > (const HoleInfo & hh) const {return size > hh.size;}
@ -112,7 +131,7 @@ public:
bool operator >= (const HoleInfo & hh) const {return size >= hh.size;} bool operator >= (const HoleInfo & hh) const {return size >= hh.size;}
bool operator <= (const HoleInfo & hh) const {return size <= hh.size;} bool operator <= (const HoleInfo & hh) const {return size <= hh.size;}
typename MESH::ScalarType Perimeter() typename MESH::ScalarType Perimeter()
{ {
MESH::ScalarType sum=0; MESH::ScalarType sum=0;
face::Pos<typename MESH::FaceType> ip = p; face::Pos<typename MESH::FaceType> ip = p;
@ -158,11 +177,11 @@ typename MESH::ScalarType Perimeter()
return EV.size(); return EV.size();
} }
}; };
template<class MESH> template<class MESH>
void FindHole(MESH &m, face::Pos<typename MESH::FaceType> ep, HoleInfo<MESH> &h) void FindHole(MESH &m, face::Pos<typename MESH::FaceType> ep, HoleInfo<MESH> &h)
{ {
if(!ep.IsBorder()) return; if(!ep.IsBorder()) return;
int holesize = 0; int holesize = 0;
@ -180,11 +199,11 @@ void FindHole(MESH &m, face::Pos<typename MESH::FaceType> ep, HoleInfo<MESH> &h)
} }
while (ep != init); while (ep != init);
h=HoleInfo<MESH>(ep,holesize,hbox); h=HoleInfo<MESH>(ep,holesize,hbox);
} }
template<class MESH,class STL_CONTAINER_HOLES> template<class MESH,class STL_CONTAINER_HOLES>
void FindHole(MESH &m, STL_CONTAINER_HOLES & H) void FindHole(MESH &m, STL_CONTAINER_HOLES & H)
{ {
MESH::FaceIterator pf; MESH::FaceIterator pf;
int holesize; int holesize;
for (pf=m.face.begin(); pf!=m.face.end(); ++pf) for (pf=m.face.begin(); pf!=m.face.end(); ++pf)
@ -220,11 +239,11 @@ void FindHole(MESH &m, STL_CONTAINER_HOLES & H)
} }
} }
} }
}; };
/* /*
Un ear e' identificato da due hedge pos. Un ear e' identificato da due hedge pos.
i vertici dell'ear sono i vertici dell'ear sono
e0.FlipV().v e0.FlipV().v
@ -234,22 +253,22 @@ Un ear e' identificato da due hedge pos.
Vale che e1== e0.NextB(); Vale che e1== e0.NextB();
e che e1.FlipV() == e0; e che e1.FlipV() == e0;
Situazioni ear non manifold, e degeneri (buco triangolare) Situazioni ear non manifold, e degeneri (buco triangolare)
T XXXXXXXXXXXXX A /XXXXX B en/XXXXX T XXXXXXXXXXXXX A /XXXXX B en/XXXXX
/XXXXXXXXXXXXXXX /XXXXXX /XXXXXX /XXXXXXXXXXXXXXX /XXXXXX /XXXXXX
XXXXXXep==en XXX ep\ /en XXXX /e1 XXXX XXXXXXep==en XXX ep\ /en XXXX /e1 XXXX
XXXXXX ----/| XX ------ ----/| XX ------ ----/|XXX XXXXXX ----/| XX ------ ----/| XX ------ ----/|XXX
XXXXXX| /e1 XX XXXXXX| /e1 XX XXXXXX| o/e0 XX XXXXXX| /e1 XX XXXXXX| /e1 XX XXXXXX| o/e0 XX
XXXXXX| /XXXXXX XXXXXX| /XXXXXX XXXXXX| /XXXXXX XXXXXX| /XXXXXX XXXXXX| /XXXXXX XXXXXX| /XXXXXX
XXX e0|o/XXXXXXX XXX e0|o/XXXXXXX XXX ep| /XXXXXXX XXX e0|o/XXXXXXX XXX e0|o/XXXXXXX XXX ep| /XXXXXXX
XXX \|/XXXXXXXX XXX \|/XXXXXXXX XXX \|/XXXXXXXX XXX \|/XXXXXXXX XXX \|/XXXXXXXX XXX \|/XXXXXXXX
XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
*/ */
template<class MSH_TYPE> class TrivialEar template<class MSH_TYPE> class TrivialEar
{ {
public: public:
face::Pos<typename MSH_TYPE::FaceType> e0; // face::Pos<typename MSH_TYPE::FaceType> e0; //
face::Pos<typename MSH_TYPE::FaceType> e1; // face::Pos<typename MSH_TYPE::FaceType> e1; //
@ -315,7 +334,7 @@ template<class MSH_TYPE> class TrivialEar
face::Pos<typename MSH_TYPE::FaceType> en=e1; en.NextB(); // he successivo a e1 face::Pos<typename MSH_TYPE::FaceType> en=e1; en.NextB(); // he successivo a e1
// caso ear degenere per buco triangolare // caso ear degenere per buco triangolare
if(ep==en) return true; if(ep==en) return true;//provo a togliere sto controllo
// Caso ear non manifold a // Caso ear non manifold a
if(ep.v==en.v) return true; if(ep.v==en.v) return true;
// Caso ear non manifold b // Caso ear non manifold b
@ -403,89 +422,111 @@ template<class MSH_TYPE> class TrivialEar
return true; return true;
} }
}; };
// Funzione principale per chiudier un buco in maniera topologicamente corretta. // Funzione principale per chiudier un buco in maniera topologicamente corretta.
// Gestisce situazioni non manifold ragionevoli // Gestisce situazioni non manifold ragionevoli
// (tutte eccetto quelle piu' di 2 facce per 1 edge). // (tutte eccetto quelle piu' di 2 facce per 1 edge).
// Controlla che non si generino nuove situazioni non manifold chiudendo orecchie // Controlla che non si generino nuove situazioni non manifold chiudendo orecchie
// che sottendono un edge che gia'esiste. // che sottendono un edge che gia'esiste.
// template<class MESH>
// Attenzione: se per riaggiungere facce deve riallocare il vettore non funge!!!! tri::HoleInfo<MESH> getHoleInfo(MESH &m, face::Pos<typename MESH::FaceType> sp,
// face::Pos<typename MESH::FaceType> fp,
template<class MESH, class EAR> int UBIT)
typename MESH::FaceIterator CloseHole(MESH &m, HoleInfo <MESH> &h) {
{ int holesize=0;
std::vector<SimpleEdge<MESH> > ES; Box3<MESH::ScalarType> hbox;
//set<SimpleEdge<MESH> > ES; // vettore con tutti gli edge adiacenti al buco. hbox.Add(sp.v->cP());
h.CollectEdges(ES); do
vector<EAR> H; // Heap delle ear da chiudere {
H.reserve(h.size); sp.f->SetUserBit(UBIT);
hbox.Add(sp.v->cP());
++holesize;
sp.NextB();
assert(sp.IsBorder());
}while(sp != fp);
int tmp = ((int)(sp.f - &(*(m.face.begin()))));
return tri::HoleInfo<MESH>(sp,holesize,hbox, tmp );
}
template<class MESH,class EAR , class VECTOR_EAR>
void refreshHole(MESH &m, VECTOR_EAR &ve, face::Pos<typename MESH::FaceType> &fp, std::vector<typename MESH::VertexType > &vv)
{
face::Pos<typename MESH::FaceType> ff = fp;
do{
ve.push_back(EAR(fp));
vv.push_back(*fp.v);
fp.NextB();//semmai da provare a sostituire il codice della NextB();
assert(fp.IsBorder());
}while(fp!=ff);
}
template <class MESH, class EAR>
void fillHoleEar(MESH &m, tri::HoleInfo<MESH> &h ,int UBIT)
{
//Aggiungo le facce e aggiorno il puntatore alla faccia!
std::vector<MESH::FacePointer *> app; std::vector<MESH::FacePointer *> app;
app.push_back( &h.p.f ); app.push_back( &h.p.f );
MESH::FaceIterator f = tri::Allocator<MESH>::AddFaces(m, h.size-2, app);
h.CollectEdges(ES);
assert(h.p.IsBorder()); assert(h.p.IsBorder());
MESH::FaceIterator f = tri::Allocator<MESH>::AddFaces(m, h.size-2, app);
face::Pos<typename MESH::FaceType> ep=h.p; h.Refresh(m); //rinfresco il puntatore tramite l'indice precedentemente salvato.
do { assert(h.p.IsBorder());//test fondamentale altrimenti qualcosa s'e' rotto!
H.push_back(EAR(ep));
ep.f->SetS();
ep.NextB();
assert(ep.IsBorder()); std::vector<MESH::VertexType > vv; //vettore di vertici
} while(ep!=h.p);
std::vector<EAR > H; //vettore di orecchie
make_heap(H.begin(),H.end()); H.reserve(h.size);
//prendo le informazioni sul buco
refreshHole<MESH,EAR, std::vector<EAR> >(m,H,h.p,vv);
bool fitted = false;
int cnt=h.size; int cnt=h.size;
MESH::FaceIterator tmp;
while( cnt > 2 && !H.empty() ) //finche' il buco non e' chiuso o non ci sono piu' orecchie da analizzare
{
//ordino il vettore di orecchie
//sort(H.begin(), H.end(), greater<EAR>() );//descending
sort(H.begin(), H.end(), less<EAR>() ); //ascending
EAR en0,en1; EAR en0,en1;
MESH::FaceIterator firstf = f; MESH::VertexType vfit = *H.back().e0.v;
std::vector<MESH::VertexType >::iterator it;
//SimpleEdge<MESH> se(); it = vv.begin();
while(cnt > 2 && !H.empty()) while( it != vv.end() && (vfit.P() != ((MESH::VertexType )(*it)).P() ) )
{
//pop_heap(H.begin(),H.end());
SimpleEdge<MESH> se( *(H.back().e0.VFlip()) , *(H.back().e1.v));
// se.v = p.v;
// se.v[1] = p.v[1];
//Sostituito la funzione find con la ricerca manuale
std::vector<SimpleEdge<MESH> >::iterator it;
it = ES.begin();
while( it != ES.end() &&
se != ((SimpleEdge<MESH> )(*it)) )
{it++; } {it++; }
//per far funzionare il test sottostante.
MESH::FaceIterator Fadd = f;
if(H.back().IsUpToDate()) if(H.back().IsUpToDate())
{ {
if(H.back().Degen() && it != vv.end()){
if(H.back().Degen() && it != ES.end()){
// Nota che nel caso di ear degeneri si DEVE permettere la creazione di un edge che gia'esiste // Nota che nel caso di ear degeneri si DEVE permettere la creazione di un edge che gia'esiste
printf("\n -> Evitata orecchia brutta!"); printf("\n -> Evitata orecchia brutta!");
} }
else {
if(it == ES.end())
{
//H.back().Close(en0,en1,&*f);
int i = H.size();
}
else else
{
if(H.back().Close(en0,en1,&*f)) if(H.back().Close(en0,en1,&*f))
{ {
//ES.insert(se); //ES.insert(se);
ES.push_back(se); /* ES.push_back(se);
if(!en0.IsNull()){ if(!en0.IsNull()){
H.push_back(en0); H.push_back(en0);
push_heap( H.begin(), H.end()); push_heap( H.begin(), H.end());
@ -493,51 +534,115 @@ typename MESH::FaceIterator CloseHole(MESH &m, HoleInfo <MESH> &h)
if(!en1.IsNull()){ if(!en1.IsNull()){
H.push_back(en1); H.push_back(en1);
push_heap( H.begin(), H.end()); push_heap( H.begin(), H.end());
} }*/
--cnt; --cnt;
tmp = f;
++f; ++f;
fitted = true;
} }
} }
} }
if((cnt == 3) && ( H.back().e0.IsBorder() ) ) if(cnt == 3 && !fitted)
{ {//ultimo buco o unico buco
//dovrebbe mancare un triangolo da tappare
if(H.back().Close(en0,en1,&*f)) if(H.back().Close(en0,en1,&*f))
{ {
ES.push_back(se);
if(!en0.IsNull()){
H.push_back(en0);
push_heap( H.begin(), H.end());
}
if(!en1.IsNull()){
H.push_back(en1);
push_heap( H.begin(), H.end());
}
--cnt; --cnt;
tmp = f;
++f; ++f;
fitted = true;
} }
} }
if(fitted && cnt >2)
{
face::Pos<typename MESH::FaceType> ff( &(*tmp) ,2);
//ho inserito il triangolo e devo aggiornare le strutture dati
H.clear();
vv.clear();
tmp->SetUserBit(UBIT);
//ri-prendo le informazioni sul buco
refreshHole<MESH,EAR, std::vector<EAR> >(m,H,ff,vv);
fitted = false;
}
else
{
//non ho messo il triangolo quindi tolgo l'orecchio e continuo
H.pop_back(); H.pop_back();
} }
}//fine del while principale
//Delete the unused faces (caused by non 1-manifold vertexes)
while(f!=m.face.end()) while(f!=m.face.end())
{ {
(*f).SetD(); (*f).SetD();
++f; ++f;
m.fn--; m.fn--;
} }
return firstf;
};
/* }
Trivial Ear con preferred Normal
*/
template<class MSH_TYPE> class TrivialEarN : public TrivialEar<MSH_TYPE> template<class MESH>
{ void holeFillingEar(MESH &m, int sizeHole,bool Selected = false)
{
MESH::FaceIterator fi;
std::vector<tri::HoleInfo<MESH> > vinfo;
int UBIT = fi->LastBitFlag();
for(fi = m.face.begin(); fi!=m.face.end(); ++fi)
{
if(!(*fi).IsD())
{
if(Selected && !(*fi).IsS())
{
//se devo considerare solo i triangoli selezionati e
//quello che sto considerando non lo e' lo marchio e vado avanti
(*fi).SetUserBit(UBIT);
}
else
{
if( !(*fi).IsUserBit(UBIT) )
{
(*fi).SetUserBit(UBIT);
for(int j =0; j<3 ; ++j)
{
if( (*fi).IsB(j) )
{//Trovato una faccia di bordo non ancora visitata.
face::Pos<typename MESH::FaceType> sp(&*fi, j, (*fi).V(j));
// if(!(*fi).IsR())return;
tri::HoleInfo<MESH> HI = getHoleInfo<MESH>(m,sp,sp, UBIT);
//ho recuperato l'inofrmazione su tutto il buco
vinfo.push_back(HI);
}
}//for sugli edge del triangolo
}//se e' gia stato visitato
}//S & !S
}//!IsD()
}//for principale!!!
std::vector<tri::HoleInfo<MESH> >::iterator ith;
tri::HoleInfo<MESH> app;
for(ith = vinfo.begin(); ith!= vinfo.end(); ++ith)
{
app=(tri::HoleInfo<MESH>)*ith;
if(app.size < sizeHole){
//app.Refresh(m);//non so se serve
fillHoleEar<MESH, typename tri::TrivialEar<MESH> >(m, app,UBIT);
}
}
for(fi = m.face.begin(); fi!=m.face.end(); ++fi)
{
if(!(*fi).IsD())
(*fi).ClearUserBit(UBIT);
}
}
/*
Trivial Ear con preferred Normal
*/
template<class MSH_TYPE> class TrivialEarN : public TrivialEar<MSH_TYPE>
{
public: public:
TrivialEarN(){} TrivialEarN(){}
@ -565,14 +670,14 @@ template<class MSH_TYPE> class TrivialEarN : public TrivialEar<MSH_TYPE>
}; };
}; };
/* 2d Triangulation Code */ /* 2d Triangulation Code */
class Triangulate2D class Triangulate2D
{ {
static double Area(const vector<Point2d> &contour) static double Area(const vector<Point2d> &contour)
{ {
int n = contour.size(); int n = contour.size();
double A=0.0f; double A=0.0f;
@ -581,18 +686,18 @@ static double Area(const vector<Point2d> &contour)
A+= contour[p].X()*contour[q].Y() - contour[q].X()*contour[p].Y(); A+= contour[p].X()*contour[q].Y() - contour[q].X()*contour[p].Y();
} }
return A*0.5f; return A*0.5f;
} }
/* /*
InsideTriangle decides if a point P is Inside of the triangle InsideTriangle decides if a point P is Inside of the triangle
defined by A, B, C. defined by A, B, C.
*/ */
static bool InsideTriangle(double Ax, double Ay, static bool InsideTriangle(double Ax, double Ay,
double Bx, double By, double Bx, double By,
double Cx, double Cy, double Cx, double Cy,
double Px, double Py) double Px, double Py)
{ {
double ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; double ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
double cCROSSap, bCROSScp, aCROSSbp; double cCROSSap, bCROSScp, aCROSSbp;
@ -608,10 +713,10 @@ static bool InsideTriangle(double Ax, double Ay,
bCROSScp = bx*cpy - by*cpx; bCROSScp = bx*cpy - by*cpx;
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}; };
static bool Snip(const vector<Point2d> &contour,int u,int v,int w,int n,int *V) static bool Snip(const vector<Point2d> &contour,int u,int v,int w,int n,int *V)
{ {
int p; int p;
double Ax, Ay, Bx, By, Cx, Cy, Px, Py; double Ax, Ay, Bx, By, Cx, Cy, Px, Py;
const double epsilon =1e-2; const double epsilon =1e-2;
@ -636,10 +741,10 @@ static bool Snip(const vector<Point2d> &contour,int u,int v,int w,int n,int *V)
} }
return true; return true;
} }
public: public:
static bool Process(const vector<Point2d> &contour,vector<int> &result) static bool Process(const vector<Point2d> &contour,vector<int> &result)
{ {
/* allocate and initialize list of Vertices in polygon */ /* allocate and initialize list of Vertices in polygon */
int n = contour.size(); int n = contour.size();
@ -709,9 +814,9 @@ static bool Process(const vector<Point2d> &contour,vector<int> &result)
delete V; delete V;
return true; return true;
} }
}; };
} // end namespace } // end namespace
} }
#endif #endif