diff --git a/vcg/complex/trimesh/hole.h b/vcg/complex/trimesh/hole.h new file mode 100644 index 00000000..52614f75 --- /dev/null +++ b/vcg/complex/trimesh/hole.h @@ -0,0 +1,589 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2004 \/)\/ * +* 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. * +* * +****************************************************************************/ +/**************************************************************************** + History + +$Log: not supported by cvs2svn $ +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_HOLE +#define __VCG_TRI_UPDATE_HOLE + +/* +Questa Classe serve per gestire la non duplicazione degli edge durante la chiusura +di un buco. +*/ +namespace vcg { +namespace tri { + +template +class SimpleEdge +{ + public: + MESH::VertexPointer v[2]; + SimpleEdge(MESH::VertexPointer v0, MESH::VertexPointer v1) + { + if(v0>v1) {v[0]=v1; v[1]=v0;} + else {v[0]=v0; v[1]=v1;} + } + + SimpleEdge(MESH::hedgepos_type &ep) { + *this=SimpleEdge(ep.VFlip(), ep.v); + } + + bool operator < (const SimpleEdge & e) const + { return (v[0]!=e.v[0])?(v[0] +class HoleInfo +{ +public: + HoleInfo(){} + HoleInfo(MESH::hedgepos_type const &pHole, int const pHoleSize, Box3 &pHoleBB) + { + p=pHole; + size=pHoleSize; + bb=pHoleBB; + } + MESH::hedgepos_type p; + int size; + Box3 bb; + + 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;} + bool operator >= (const HoleInfo & hh) const {return size >= hh.size;} + bool operator <= (const HoleInfo & hh) const {return size <= hh.size;} + + MESH::scalar_type Perimeter() + { + MESH::scalar_type sum=0; + MESH::hedgepos_type ip = p; + do + { + sum+=Distance(ip.v->cP(),ip.VFlip()->cP()); + ip.NextB(); + } + while (ip != p); + return sum; + } + + + int CollectEdges(set< SimpleEdge > &EV) + { + assert(p.IsBorder()); + EV.clear(); + int tsz=0; + MESH::hedgepos_type ip=p; + MESH::hedgepos_type tp; + + do + { + // Stesso codice della nextb + do + { + ip.NextE(); + EV.insert(SimpleEdge(ip)); // l'edge che sto scorrendo + tp=ip; + tp.FlipV();tp.FlipE(); + EV.insert(SimpleEdge(tp)); // l'edge della faccia su cui sono e opposto al vertice su cui ruoto + tp.FlipF(); tp.FlipE(); + EV.insert(SimpleEdge(tp)); // gli altri due edge della faccia opposta a questa + tp.FlipE(); + EV.insert(SimpleEdge(tp)); + } + while(!ip.f->IsBorder(ip.z)); + ip.FlipV(); + ++tsz; + } + while (ip != p); + assert(tsz==size); + + return EV.size(); + } +}; + +template +void FindHole(MESH &m, MESH::hedgepos_type ep, HoleInfo &h) +{ + if(!ep.IsBorder()) return; + + int holesize = 0; + + Box3 hbox; + if(ep.v->IsR()) hbox.Add(ep.v->cP()); + MESH::hedgepos_type init; + init = ep; + do + { + ep.NextB(); + ep.f->SetV(); + if(ep.v->IsR()) hbox.Add(ep.v->cP()); + ++holesize; + } + while (ep != init); + h=HoleInfo(ep,holesize,hbox); +} + +template +void FindHole(MESH &m, STL_CONTAINER_HOLES & H) +{ + MESH::face_iterator pf; + int holesize; + for (pf=m.face.begin(); pf!=m.face.end(); ++pf) + if( !(*pf).IsD() && (*pf).IsW() ) + (*pf).ClearV(); + + MESH::hedgepos_type ep; + for (pf=m.face.begin(); pf!=m.face.end(); ++pf) + { + if( !(*pf).IsDeleted() && !(*pf).IsV() && (*pf).IsR() ) + { + for(int j=0; j<3; ++j) + if( (*pf).IsBorder(j) && !(*pf).IsV() && (*pf).IsR() ) + { + (*pf).SetV(); + ep.Set(&*pf, j, (*pf).V(j)); + holesize = 0; + + Box3 hbox; + if(ep.v->IsR()) hbox.Add(ep.v->cP()); + MESH::hedgepos_type init; + init = ep; + do + { + ep.NextB(); + ep.f->SetV(); + if(ep.v->IsR()) hbox.Add(ep.v->cP()); + ++holesize; + } + while (ep != init); + H.push_back(HoleInfo(ep,holesize,hbox)); + break; + } + } + } +}; + + +/* +Un ear e' identificato da due hedge pos. + + i vertici dell'ear sono + e0.FlipV().v + e0.v + e1.v + + Vale che e1== e0.NextB(); + e che e1.FlipV() == e0; + +Situazioni ear non manifold, e degeneri (buco triangolare) + + + T XXXXXXXXXXXXX A /XXXXX B en/XXXXX +/XXXXXXXXXXXXXXX /XXXXXX /XXXXXX +XXXXXXep==en XXX ep\ /en XXXX /e1 XXXX +XXXXXX ----/| XX ------ ----/| XX ------ ----/|XXX +XXXXXX| /e1 XX XXXXXX| /e1 XX XXXXXX| o/e0 XX +XXXXXX| /XXXXXX XXXXXX| /XXXXXX XXXXXX| /XXXXXX +XXX e0|o/XXXXXXX XXX e0|o/XXXXXXX XXX ep| /XXXXXXX +XXX \|/XXXXXXXX XXX \|/XXXXXXXX XXX \|/XXXXXXXX +XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX + +*/ +template class TrivialEar +{ + public: + MSH_TYPE::hedgepos_type e0; // + MSH_TYPE::hedgepos_type e1; // + MSH_TYPE::scalar_type quality; + TrivialEar(){} + TrivialEar(const MSH_TYPE::hedgepos_type & ep) + { + e0=ep; + assert(e0.IsBorder()); + e1=e0; + e1.NextB(); + ComputeQuality(); + } + + // Nota: minori invertiti + inline bool operator < ( const TrivialEar & c ) const { return quality > c.quality; } + inline bool operator > ( const TrivialEar & c ) const { return quality < c.quality; } + inline bool operator == ( const TrivialEar & c ) const { return quality == c.quality; } + inline bool operator != ( const TrivialEar & c ) const { return quality != c.quality; } + inline bool operator >= ( const TrivialEar & c ) const { return quality <= c.quality; } + inline bool operator <= ( const TrivialEar & c ) const { return quality >= c.quality; } + + bool IsNull(){return e0.IsNull() || e1.IsNull();} + void SetNull(){e0.SetNull();e1.SetNull();} + void ComputeQuality(){ quality = Distance(e0.VFlip()->P(),e1.v->P());}; + bool IsUpToDate() {return (e0.IsBorder() && e1.IsBorder());}; + + bool Degen() + { + MSH_TYPE::hedgepos_type ep=e0; ep.FlipV(); ep.NextB(); ep.FlipV(); // he precedente a e0 + MSH_TYPE::hedgepos_type en=e1; en.NextB(); // he successivo a e1 + + // caso ear degenere per buco triangolare + if(ep==en) return true; + // Caso ear non manifold a + if(ep.v==en.v) return true; + // Caso ear non manifold b + if(ep.VFlip()==e1.v) return true; + + return false; + } + + bool Close(TrivialEar &ne0, TrivialEar &ne1, MSH_TYPE::face_type* f) + { + // simple topological check + if(e0.f==e1.f) { + TRACE("Avoided bad ear"); + return false; + } + + //usato per generare una delle due nuove orecchie. + MSH_TYPE::hedgepos_type ep=e0; ep.FlipV(); ep.NextB(); ep.FlipV(); // he precedente a e0 + MSH_TYPE::hedgepos_type en=e1; en.NextB(); // he successivo a e1 + + (*f).V(0) = e0.VFlip(); + (*f).V(1) = e0.v; + (*f).V(2) = e1.v; + + (*f).F(0) = e0.f; + (*f).Z(0) = e0.z; + (*f).F(1) = e1.f; + (*f).Z(1) = e1.z; + (*f).F(2) = f; + (*f).Z(2) = 2; + + e0.f->F(e0.z)=f; + e0.f->Z(e0.z)=0; + + e1.f->F(e1.z)=f; + e1.f->Z(e1.z)=1; + + // caso ear degenere per buco triangolare + if(ep==en) + { + TRACE("Closing the last triangle"); + f->F(2)=en.f; + f->Z(2)=en.z; + en.f->F(en.z)=f; + en.f->Z(en.z)=2; + ne0.SetNull(); + ne1.SetNull(); + } + // Caso ear non manifold a + else if(ep.v==en.v) + { + TRACE("Ear Non manif A\n"); + MSH_TYPE::hedgepos_type enold=en; + en.NextB(); + f->F(2)=enold.f; + f->Z(2)=enold.z; + enold.f->F(enold.z)=f; + enold.f->Z(enold.z)=2; + ne0=TrivialEar(ep); + ne1=TrivialEar(en); + } + // Caso ear non manifold b + else if(ep.VFlip()==e1.v) + { + TRACE("Ear Non manif B\n"); + MSH_TYPE::hedgepos_type epold=ep; + ep.FlipV(); ep.NextB(); ep.FlipV(); + f->F(2)=epold.f; + f->Z(2)=epold.z; + epold.f->F(epold.z)=f; + epold.f->Z(epold.z)=2; + ne0=TrivialEar(ep); + ne1=TrivialEar(en); + } + else // caso standard + // Now compute the new ears; + { + ne0=TrivialEar(ep); + ne1=TrivialEar(MSH_TYPE::hedgepos_type(f,2,e1.v)); + } + + return true; + } +}; + + + + +// Funzione principale per chiudier un buco in maniera topologicamente corretta. +// Gestisce situazioni non manifold ragionevoli +// (tutte eccetto quelle piu' di 2 facce per 1 edge). +// Controlla che non si generino nuove situazioni non manifold chiudendo orecchie +// che sottendono un edge che gia'esiste. +// +// Attenzione: se per riaggiungere facce deve riallocare il vettore non funge!!!! +// +template +MESH::face_iterator CloseHole(MESH &m, HoleInfo &h) +{ + + set > ES; // vettore con tutti gli edge adiacenti al buco. + h.CollectEdges(ES); + vector H; // Heap delle ear da chiudere + H.reserve(h.size); + + assert(h.p.IsBorder()); + MESH::hedgepos_type ep=h.p; + do { + H.push_back(EAR(ep)); + ep.NextB(); + } while(ep!=h.p); + + make_heap(H.begin(),H.end()); + int cnt=h.size; + EAR en0,en1; + MESH::face_iterator f=m.AddFaces(h.size-2); + + MESH::face_iterator firstf=f; + SimpleEdge se(0,0); + while(cnt>2 && !H.empty()) + { + pop_heap(H.begin(),H.end()); + se=SimpleEdge(H.back().e0.VFlip(), H.back().e1.v); + if(H.back().IsUpToDate()) + { + if(!H.back().Degen() && ES.find(se)!=ES.end()){ + // Nota che nel caso di ear degeneri si DEVE permettere la creazione di un edge che gia'esiste + TRACE("Evitata orecchia brutta!"); + } + else if(H.back().Close(en0,en1,&*f)) + { + ES.insert(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; + ++f; + //return firstf;///////////////dbug + } + } + H.pop_back(); + } + //Delete the unused faces (caused by non 1-manifold vertexes) + while(f!=m.face.end()) + { + (*f).SetD(); + ++f; + m.fn--; + } + return firstf; +}; + +/* +Trivial Ear con preferred Normal +*/ +template class TrivialEarN : public TrivialEar +{ + public: + + TrivialEarN(){} + TrivialEarN(const MSH_TYPE::hedgepos_type & ep) + { + e0=ep; + assert(e0.IsBorder()); + e1=e0; + e1.NextB(); + ComputeQuality(); + } + + + static MSH_TYPE::vectorial_type &PreferredNormal() + { + static MSH_TYPE::vectorial_type nn; + return nn; + } + + void ComputeQuality(){ + Point3d nn= -Normal( e0.VFlip()->P(), e0.v->P(), e1.v->P()); + quality = Distance(e0.VFlip()->P(),e1.v->P()); + if(nn*PreferredNormal() < -0.1) + quality*=1000000; + + }; + +}; + +/* 2d Triangulation Code */ +class Triangulate2D +{ + +static double Area(const vector &contour) +{ + int n = contour.size(); + + double A=0.0f; + + for(int p=n-1,q=0; q= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +}; + +static bool Snip(const vector &contour,int u,int v,int w,int n,int *V) +{ + int p; + double Ax, Ay, Bx, By, Cx, Cy, Px, Py; + const double epsilon =1e-2; + + Ax = contour[V[u]].x(); + Ay = contour[V[u]].y(); + + Bx = contour[V[v]].x(); + By = contour[V[v]].y(); + + Cx = contour[V[w]].x(); + Cy = contour[V[w]].y(); + + if ( epsilon> (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) ) return false; + + for (p=0;p &contour,vector &result) +{ + /* allocate and initialize list of Vertices in polygon */ + + int n = contour.size(); + double area=Area(contour); + if ( n < 3 ) return false; + + int *V = new int[n]; + + /* we want a counter-clockwise polygon in V */ + + if ( 0.0f < area ) for (int v=0; v2; ) + { + count--; + /* if we loop, it is probably a non-simple polygon */ + if( count<0) + { + CurrBest*=1.3; + count = 2*nv; + + if(CurrBest>Sqrt(area)*2) + return false; + } + + /* three consecutive vertices in current polygon, */ + int u = v ; if (nv <= u) u = 0; /* previous */ + v = u+1; if (nv <= v) v = 0; /* new v */ + int w = v+1; if (nv <= w) w = 0; /* next */ + + if(Distance(contour[u],contour[w]) < CurrBest) + if ( Snip(contour,u,v,w,nv,V) ) + { + int a,b,c,s,t; + + /* true names of the vertices */ + a = V[u]; b = V[v]; c = V[w]; + + /* output Triangle */ + result.push_back( a ); + result.push_back( b ); + result.push_back( c ); + + m++; + + /* remove v from remaining polygon */ + for(s=v,t=v+1;t