diff --git a/vcg/complex/algorithms/attribute_seam.h b/vcg/complex/algorithms/attribute_seam.h new file mode 100644 index 00000000..1f4f3366 --- /dev/null +++ b/vcg/complex/algorithms/attribute_seam.h @@ -0,0 +1,369 @@ +#ifndef VCG_TRI_ATTRIBUTE_SEAM_H +#define VCG_TRI_ATTRIBUTE_SEAM_H + +#include + +#include + +/* + +// sample extract functor +void v_extract(const src_mesh_t & wm, const src_face_t & f, int k, const dst_mesh_t & vm, dst_vertex_t & v) +{ + (void)wm; + (void)vm; + + v.P() = f.cP (k); + v.N() = f.cWN(k); + v.C() = f.cWC(k); + v.T() = f.cWT(k); +} + +// sample compare functor +bool v_compare(const dst_mesh_t & vm, const dst_vertex_t & u, const dst_vertex_t & v) +{ + (void)vm; + + return + ( + (u.cN() == v.cN()) + && (u.cC() == v.cC()) + && (u.cT() == v.cT()) + ); +} + +// sample copy functor +void v_copy(const dst_mesh_t & vm, const dst_vertex_t & u, dst_vertex_t & v) +{ + (void)vm; + + v.P() = u.cP(); + v.N() = u.cN(); + v.C() = u.cC(); + v.T() = u.cT(); +} + +// create seams +AttributeSeam::SplitVertex(src, dst, v_extract, v_compare, v_copy, 1.10f); + +*/ + +namespace vcg +{ + +namespace tri +{ + +class AttributeSeam +{ + public: + + typedef AttributeSeam ThisType; + + enum ASMask + { + POSITION_PER_VERTEX = (1 << 0), + + NORMAL_PER_VERTEX = (1 << 1), + NORMAL_PER_WEDGE = (1 << 2), + NORMAL_PER_FACE = (1 << 3), + + COLOR_PER_VERTEX = (1 << 4), + COLOR_PER_WEDGE = (1 << 5), + COLOR_PER_FACE = (1 << 6), + + TEXCOORD_PER_VERTEX = (1 << 7), + TEXCOORD_PER_WEDGE = (1 << 8) + }; + + template + struct ASExtract + { + const unsigned int mask; + + ASExtract(unsigned int vmask = 0) : mask(vmask) + { + ; + } + + void operator () (const src_trimesh_t & sm, const typename src_trimesh_t::FaceType & f, int k, const dst_trimesh_t & dm, typename dst_trimesh_t::VertexType & v) const + { + (void)sm; + (void)dm; + + const unsigned int m = this->mask; + const typename src_trimesh_t::VertexType & u = *(f.cV(k)); + + if ((m & AttributeSeam::POSITION_PER_VERTEX) != 0) v.P() = f.cP (k); + + if ((m & AttributeSeam::NORMAL_PER_VERTEX) != 0) v.N() = u.cN ( ); + if ((m & AttributeSeam::NORMAL_PER_WEDGE) != 0) v.N() = f.cWN(k); + if ((m & AttributeSeam::NORMAL_PER_FACE) != 0) v.N() = f.cN ( ); + + if ((m & AttributeSeam::COLOR_PER_VERTEX) != 0) v.C() = u.cC ( ); + if ((m & AttributeSeam::COLOR_PER_WEDGE) != 0) v.C() = f.cWC(k); + if ((m & AttributeSeam::COLOR_PER_FACE) != 0) v.C() = f.cC ( ); + + if ((m & AttributeSeam::TEXCOORD_PER_VERTEX) != 0) v.T() = u.cT ( ); + if ((m & AttributeSeam::TEXCOORD_PER_WEDGE) != 0) v.T() = f.cWT(k); + } + }; + + template + struct ASCompare + { + const unsigned int mask; + + ASCompare(unsigned int vmask = 0) : mask(vmask) + { + ; + } + + bool operator () (const dst_trimesh_t & sm, const typename dst_trimesh_t::VertexType & u, const typename dst_trimesh_t::VertexType & v) const + { + (void)sm; + + const unsigned int m = this->mask; + + /* + if ((m & (AttributeSeam::POSITION_PER_VERTEX)) != 0) + { + if (u.cP() != v.cP()) return false; + } + */ + + if ((m & (AttributeSeam::NORMAL_PER_VERTEX | AttributeSeam::NORMAL_PER_WEDGE | AttributeSeam::NORMAL_PER_FACE)) != 0) + { + if (u.cN() != v.cN()) return false; + } + + if ((m & (AttributeSeam::COLOR_PER_VERTEX | AttributeSeam::COLOR_PER_WEDGE | AttributeSeam::COLOR_PER_FACE)) != 0) + { + if (u.cC() != v.cC()) return false; + } + + if ((m & (AttributeSeam::TEXCOORD_PER_VERTEX | AttributeSeam::TEXCOORD_PER_WEDGE)) != 0) + { + if (u.cT() != v.cT()) return false; + } + + return true; + } + }; + + // in-place version + template + static inline bool SplitVertex(src_trimesh_t & src, extract_wedge_attribs_t v_extract, compare_vertex_attribs_t & v_compare) + { + typedef typename src_trimesh_t::VertexType src_vertex_t; + typedef typename src_trimesh_t::VertexIterator src_vertex_i; + typedef typename src_trimesh_t::FaceType src_face_t; + typedef typename src_trimesh_t::FaceIterator src_face_i; + typedef typename src_trimesh_t::VertContainer src_vertex_container_t; + + typedef vcg::tri::Allocator src_mesh_allocator_t; + typedef typename src_mesh_allocator_t :: template PointerUpdater src_pointer_updater_t; + + if ((src.vn <= 0) || (src.fn <= 0)) + { + return true; + } + + src_pointer_updater_t pt_upd; + src_vertex_i vi = src_mesh_allocator_t::AddVertices(src, 1, pt_upd); + src_vertex_t * vtx = &(*vi); + src_vertex_t * vtxbase = &(src.vert[0]); + + const size_t vertex_count = src.vert.size(); + const size_t vertex_pool_size = vertex_count; + + std::vector vloc; + vloc.reserve(vertex_pool_size); + vloc.resize(vertex_count, -2); + + int vcount = int(src.vert.size()); + int idx = 0; + + for (src_face_i it=src.face.begin(); it!=src.face.end(); ++it) + { + src_face_t & f = (*it); + if (f.IsD()) continue; + + for (int k=0; k<3; ++k) + { + idx = (f.cV(k) - vtxbase); + v_extract(src, f, k, src, *vtx); + + if (vloc[idx] == -2) + { + vloc[idx] = -1; + src.vert[idx].ImportData(*vtx); + } + else + { + int vidx = idx; + do + { + if (v_compare(src, src.vert[vidx], *vtx)) break; + vidx = vloc[vidx]; + } while (vidx >= 0); + + if (vidx < 0) + { + vloc.push_back(vloc[idx]); + vloc[idx] = vcount; + + vi = src_mesh_allocator_t::AddVertices(src, 1, pt_upd); + pt_upd.Update(vtx); + pt_upd.Update(vtxbase); + + (*vi).ImportData(*vtx); + + idx = vcount; + vcount++; + } + else + { + idx = vidx; + } + } + + f.V(k) = &(src.vert[idx]); + } + } + + src_mesh_allocator_t::DeleteVertex(src, *vtx); + + return true; + } + + // out-of-place version + template + static inline bool SplitVertex(const src_trimesh_t & src, dst_trimesh_t & dst, extract_wedge_attribs_t & v_extract, compare_vertex_attribs_t & v_compare, copy_vertex_t & v_copy) + { + typedef typename src_trimesh_t::VertexType src_vertex_t; + typedef typename src_trimesh_t::FaceType src_face_t; + typedef typename src_trimesh_t::ConstFaceIterator src_face_ci; + + typedef typename dst_trimesh_t::VertContainer dst_vertex_container_t; + typedef typename dst_trimesh_t::VertexType dst_vertex_t; + typedef typename dst_trimesh_t::VertexIterator dst_vertex_i; + typedef typename dst_trimesh_t::FaceType dst_face_t; + typedef typename dst_trimesh_t::FaceIterator dst_face_i; + + typedef vcg::tri::Allocator dst_mesh_allocator_t; + + /* GCC gets in troubles and need some hints ("template") to parse the following line */ + typedef typename dst_mesh_allocator_t :: template PointerUpdater dst_pointer_updater_t; + + if (reinterpret_cast(&src) == reinterpret_cast(&dst)) + { + return false; + } + + dst.Clear(); + + if ((src.vn <= 0) || (src.fn <= 0)) + { + return true; + } + + const size_t vertex_count = src.vert.size(); + const size_t vertex_pool_size = vertex_count; + + const src_vertex_t * vtxbase = &(src.vert[0]); + + std::vector vloc; + vloc.reserve(vertex_pool_size); + vloc.resize(vertex_count, -2); + + dst_vertex_i vv; + dst_pointer_updater_t pt_upd; + pt_upd.preventUpdateFlag = true; + dst_mesh_allocator_t::AddVertices(dst, 1 + int(vertex_count), pt_upd); + dst_vertex_t * vtx = &(dst.vert[0]); + + dst_face_i fbase = dst_mesh_allocator_t::AddFaces(dst, src.fn); + dst_face_i fi = fbase; + + int vcount = int(dst.vert.size()); + int idx = 0; + + for (src_face_ci it=src.face.begin(); it!=src.face.end(); ++it) + { + const src_face_t & wf = (*it); + if (wf.IsD()) continue; + + dst_face_t & vf = (*fi); + + for (int k=0; k<3; ++k) + { + idx = (wf.cV(k) - vtxbase); + + v_extract(src, wf, k, dst, *vtx); + + if (vloc[idx] == -2) + { + vloc[idx] = -1; + v_copy(dst, *vtx, dst.vert[idx]); + } + else + { + int vidx = idx; + do + { + if (v_compare(dst, dst.vert[vidx], *vtx)) break; + vidx = vloc[vidx]; + } while (vidx >= 0); + + if (vidx < 0) + { + vloc.push_back(vloc[idx]); + vloc[idx] = vcount; + + vv = dst_mesh_allocator_t::AddVertices(dst, 1, pt_upd); + pt_upd.Update(vtx); + v_copy(dst, *vtx, *vv); + + idx = vcount; + vcount++; + } + else + { + idx = vidx; + } + } + + vf.V(k) = reinterpret_cast(idx); + } + + fi++; + } + + { + std::vector tmp; + vloc.swap(tmp); + } + + dst_vertex_t * vstart = &(dst.vert[0]); + + for (dst_face_i it=fbase; it!=dst.face.end(); ++it) + { + dst_face_t & vf = (*it); + + vf.V(0) = vstart + reinterpret_cast(vf.V(0)); + vf.V(1) = vstart + reinterpret_cast(vf.V(1)); + vf.V(2) = vstart + reinterpret_cast(vf.V(2)); + } + + dst_mesh_allocator_t::DeleteVertex(dst, *vtx); + + return true; + } +}; + +} // end namespace tri + +} // end namespace vcg + +#endif // VCG_TRI_ATTRIBUTE_SEAM_H diff --git a/vcg/complex/algorithms/autoalign_4pcs.h b/vcg/complex/algorithms/autoalign_4pcs.h new file mode 100644 index 00000000..fac74aec --- /dev/null +++ b/vcg/complex/algorithms/autoalign_4pcs.h @@ -0,0 +1,677 @@ +#ifndef _4PCS_ +#define _4PCS_ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/** +implementation of the 4PCS method from the paper: +"4-Points Congruent Sets for Robust Pairwise Surface Registration" +D.Aiger, N.Mitra D.Cohen-Or, SIGGRAPH 2008 +ps: the name of the variables are out of vcg standard but like the one +used in the paper pseudocode. +*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + + +// note: temporary (callback.h should be moved inside vcg) +typedef bool AACb( const int pos,const char * str ); + +namespace vcg{ + namespace tri{ +template +class FourPCS { +public: + /* mesh only for using spatial indexing functions (to remove) */ + class PVertex; // dummy prototype never used + class PFace; + + class PUsedTypes: public vcg::UsedTypes < vcg::Use::template AsVertexType, + vcg::Use::template AsFaceType >{}; + + + class PVertex : public vcg::Vertex< PUsedTypes,vcg::vertex::BitFlags,vcg::vertex::Coord3f ,vcg::vertex::Mark>{}; + /*same as for the vertes */ + class PFace : public vcg::Face< PUsedTypes> {}; + /*the mesh is a container of vertices and a container of faces */ + class PMesh : public vcg::tri::TriMesh< std::vector, std::vector > {}; + + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::VertexType VertexType; + typedef vcg::Point4< vcg::Point3 > FourPoints; + typedef vcg::GridStaticPtr GridType; + + /* class for Parameters */ + struct Parameters{ + ScalarType delta; + int feetsize; // how many points in the neighborhood of each of the 4 points + ScalarType f; // overlapping estimation + int scoreFeet, // how many of the feetsize points must match (max feetsize*4) to try an early interrupt + scoreAln; // how good must be the alignement to end the process successfully + + void Default(){ + delta = 0.5; + feetsize = 25; + f = 0.5; + scoreFeet = 50; + scoreAln = 200; + } + }; + + Parameters prs; /// parameters + + public: + void Init(MeshType &_P,MeshType &_Q); + bool Align( int L, vcg::Matrix44f & result, AACb * cb = NULL ); // main function + + +private: + struct Couple: public std::pair{ + Couple(const int & i, const int & j, float d):std::pair(i,j),dist(d){} + Couple(float d):std::pair(0,0),dist(d){} + float dist; + const bool operator < (const Couple & o) const {return dist < o.dist;} + int & operator[](const int &i){return (i==0)? first : second;} + }; + + + + + /* returns the closest point between to segments x1-x2 and x3-x4. */ + void IntersectionLineLine(const CoordType & x1,const CoordType & x2,const CoordType & x3,const CoordType & x4, CoordType&x) + { + CoordType a = x2-x1, b = x4-x3, c = x3-x1; + x = x1 + a * ((c^b).dot(a^b)) / (a^b).SquaredNorm(); + } + + + + + struct CandiType{ + CandiType(){}; + CandiType(FourPoints _p,vcg::Matrix44_T):p(_p),T(_T){} + FourPoints p; + vcg::Matrix44 T; + ScalarType err; + int score; + int base; // debug: for which base + inline bool operator <(const CandiType & o) const {return score > o.score;} + }; + + + MeshType *P, // mesh from which the coplanar base is selected + *Q; // mesh where to find the correspondences + std::vector mapsub; // subset of index to the vertices in Q + + + PMesh Invr; // invariants + + std::vector< CandiType > U; + CandiType winner; + int iwinner; // winner == U[iwinner] + + FourPoints B; // coplanar base + std::vector bases; // used bases + ScalarType side; // side + std::vector ExtB[4]; // selection of vertices "close" to the four point + std::vector subsetP; // random selection on P + ScalarType radius; + + ScalarType Bangle; + std::vector R1/*,R2*/; + ScalarType r1,r2; + + // class for the point 'ei' + struct EPoint{ + EPoint(vcg::Point3 _p, int _i):pos(_p),pi(_i){} + vcg::Point3 pos; + int pi; //index to R[1|2] + void GetBBox(vcg::Box3 & b){b.Add(pos);} + }; + + GridType *ugrid; // griglia + vcg::GridStaticPtr ugridQ; + vcg::GridStaticPtr ugridP; + + //FILE * f; + +//private: + bool SelectCoplanarBase(); // on P + bool FindCongruent() ; // of base B, on Q, with approximation delta + +//private: + void ComputeR1R2(ScalarType d1,ScalarType d2); + + bool IsTransfCongruent(FourPoints fp,vcg::Matrix44 & mat, float & trerr); + int EvaluateSample(CandiType & fp, CoordType & tp, CoordType & np, const float & angle); + void EvaluateAlignment(CandiType & fp); + void TestAlignment(CandiType & fp); + + /* debug tools */ +public: + std::vector allTr;// tutte le trasformazioni provate + FILE * db; + char namemesh1[255],namemesh2[255]; + int n_base; + void InitDebug(const char * name1, const char * name2){ + db = fopen("debugPCS.txt","w"); + sprintf(&namemesh1[0],"%s",name1); + sprintf(&namemesh2[0],"%s",name2); + n_base = 0; + } + + void FinishDebug(){ + fclose(db); + } + //void SaveALN(char * name,vcg::Matrix44f mat ){ + // FILE * o = fopen(name,"w"); + // fprintf(o,"2\n%s\n#\n",namemesh1); + // for(int i = 0 ; i < 4; ++i) + // fprintf(o,"%f %f %f %f\n",mat[i][0],mat[i][1],mat[i][2],mat[i][3]); + // fprintf(o,"%s\n#\n",namemesh2); + // fprintf(o,"1.0 0.0 0.0 0.0 \n"); + // fprintf(o,"0.0 1.0 0.0 0.0 \n"); + // fprintf(o,"0.0 0.0 1.0 0.0 \n"); + // fprintf(o,"0.0 0.0 0.0 1.0 \n"); + + // fclose(o); + //} + +}; + +template +void +FourPCS:: Init(MeshType &_P,MeshType &_Q){ + + P = &_P;Q=&_Q; + ugridQ.Set(Q->vert.begin(),Q->vert.end()); + ugridP.Set(P->vert.begin(),P->vert.end()); + int vi; + // float areaP = vcg::tri::Stat::ComputeMeshArea(*P); + // float areaQ = vcg::tri::Stat::ComputeMeshArea(*Q); + + float ratio = 800 / (float) Q->vert.size(); + for(vi = 0; vi < Q->vert.size(); ++vi) + if(rand()/(float) RAND_MAX < ratio) + mapsub.push_back(vi); + + for(vi = 0; vi < P->vert.size(); ++vi) + if(rand()/(float) RAND_MAX < ratio) + subsetP.push_back(&P->vert[vi]); + + // estimate neigh distance + float avD = 0.0,dist; + for(int i = 0 ; i < 100; ++i){ + int ri = rand()/(float) RAND_MAX * Q->vert.size() -1; + std::vector< CoordType > samples,d_samples; + std::vector dists; + std::vector ress; + vcg::tri::GetKClosestVertex< + MeshType, + vcg::GridStaticPtr, + std::vector, + std::vector, + std::vector< CoordType > >(*Q,ugridQ,2,Q->vert[ri].cP(),Q->bbox.Diag(), ress,dists, samples); + assert(ress.size() == 2); + avD+=dists[1]; + } + avD /=100; // average vertex-vertex distance + avD /= sqrt(ratio); // take into account the ratio + + prs.delta = avD * prs.delta; + side = P->bbox.Dim()[P->bbox.MaxDim()]*prs.f; //rough implementation + + } + +template +bool +FourPCS::SelectCoplanarBase(){ + + vcg::tri::UpdateBounding::Box(*P); + + // choose the inter point distance + ScalarType dtol = side*0.1; //rough implementation + + //choose the first two points + int i = 0,ch; + + // first point random + ch = (rand()/(float)RAND_MAX)*(P->vert.size()-2); + B[0] = P->vert[ch].P(); +//printf("B[0] %d\n",ch); + // second a point at distance d+-dtol + for(i = 0; i < P->vert.size(); ++i){ + ScalarType dd = (P->vert[i].P() - B[0]).Norm(); + if( ( dd < side + dtol) && (dd > side - dtol)){ + B[1] = P->vert[i].P(); +//printf("B[1] %d\n",i); + break; + } + } + if(i == P->vert.size()) + return false; + + // third point at distance d from B[1] and forming a right angle + int best = -1; ScalarType bestv=std::numeric_limits::max(); + for(i = 0; i < P->vert.size(); ++i){ + int id = rand()/(float)RAND_MAX * (P->vert.size()-1); + ScalarType dd = (P->vert[id].P() - B[1]).Norm(); + if( ( dd < side + dtol) && (dd > side - dtol)){ + ScalarType angle = fabs( ( P->vert[id].P()-B[1]).normalized().dot((B[1]-B[0]).normalized())); + if( angle < bestv){ + bestv = angle; + best = id; + } + } + } + if(best == -1) + return false; + B[2] = P->vert[best].P(); +//printf("B[2] %d\n",best); + + CoordType n = ((B[0]-B[1]).normalized() ^ (B[2]-B[1]).normalized()).normalized(); + CoordType B4 = B[1] + (B[0]-B[1]) + (B[2]-B[1]); + VertexType * v =0; + ScalarType radius = dtol*4.0; + + std::vector closests; + std::vector distances; + std::vector points; + + vcg::tri::GetInSphereVertex< + MeshType, + vcg::GridStaticPtr, + std::vector, + std::vector, + std::vector + >(*P,ugridP,B4,radius,closests,distances,points); + + if(closests.empty()) + return false; + best = -1; bestv=std::numeric_limits::max(); + for(i = 0; i P() - B[1]).normalized().dot(n)); + if( angle < bestv){ + bestv = angle; + best = i; + } + } + B[3] = closests[best]->P(); + +//printf("B[3] %d\n", (typename MeshType::VertexType*)closests[best] - &(*P->vert.begin())); + + // compute r1 and r2 + CoordType x; + std::swap(B[1],B[2]); + IntersectionLineLine(B[0],B[1],B[2],B[3],x); + + r1 = (x - B[0]).dot(B[1]-B[0]) / (B[1]-B[0]).SquaredNorm(); + r2 = (x - B[2]).dot(B[3]-B[2]) / (B[3]-B[2]).SquaredNorm(); + + if( ((B[0]+(B[1]-B[0])*r1)-(B[2]+(B[3]-B[2])*r2)).Norm() > prs.delta ) + return false; + + radius =side*0.5; + std::vector< CoordType > samples,d_samples; + std::vector dists; + + for(int i = 0 ; i< 4; ++i){ + vcg::tri::GetKClosestVertex< + MeshType, + vcg::GridStaticPtr, + std::vector, + std::vector, + std::vector< CoordType > >(*P,ugridP, prs.feetsize ,B[i],radius, ExtB[i],dists, samples); + } + + //for(int i = 0 ; i< 4; ++i) + // printf("%d ",ExtB[i].size()); + // printf("\n"); +return true; + +} + + +template +bool +FourPCS::IsTransfCongruent(FourPoints fp,vcg::Matrix44 & mat, float & trerr){ + + std::vector > fix; + std::vector > mov; + for(int i = 0 ; i < 4; ++i) mov.push_back(B[i]); + for(int i = 0 ; i < 4; ++i) fix.push_back(fp[i]); + + vcg::Point3 n,p; + n = (( B[1]-B[0]).normalized() ^ ( B[2]- B[0]).normalized())*( B[1]- B[0]).Norm(); + p = B[0] + n; + mov.push_back(p); + n = (( fp[1]-fp[0]).normalized() ^ (fp[2]- fp[0]).normalized())*( fp[1]- fp[0]).Norm(); + p = fp[0] + n; + fix.push_back(p); + + vcg::PointMatching::ComputeRigidMatchMatrix(mat,fix,mov); + + ScalarType err = 0.0; + for(int i = 0; i < 4; ++i) err+= (mat * mov[i] - fix[i]).SquaredNorm(); + + trerr = vcg::math::Sqrt(err); + return err < prs.delta* prs.delta*4.0; + } + +template +void +FourPCS::ComputeR1R2(ScalarType d1,ScalarType d2){ + int vi,vj; + R1.clear(); + //R2.clear(); + int start = clock(); + for(vi = 0; vi < mapsub.size(); ++vi) for(vj = vi; vj < mapsub.size(); ++vj){ + ScalarType d = ((Q->vert[mapsub[vi]]).P()-(Q->vert[mapsub[vj]]).P()).Norm(); + if( (d < d1+ side*0.5) && (d > d1-side*0.5)) + { + R1.push_back(Couple(mapsub[vi],mapsub[vj],d )); + R1.push_back(Couple(mapsub[vj],mapsub[vi],d)); + } + } + //for( vi = 0; vi < mapsub.size(); ++ vi ) for( vj = vi ; vj < mapsub.size(); ++ vj ){ + // ScalarType d = ((Q->vert[mapsub[vi]]).P()-(Q->vert[mapsub[vj]]).P()).Norm(); + // if( (d < d2+side*0.5) && (d > d2-side*0.5)) + // { + // R2.push_back(Couple(mapsub[vi],mapsub[vj],d)); + // R2.push_back(Couple(mapsub[vj],mapsub[vi],d)); + // } + //} + + std::sort(R1.begin(),R1.end()); +// std::sort(R2.begin(),R2.end()); +} + +template +bool +FourPCS::FindCongruent() { // of base B, on Q, with approximation delta + bool done = false; + std::vector R2inv; + int n_closests = 0, n_congr = 0; + int ac =0 ,acf = 0,tr = 0,trf =0; + ScalarType d1,d2; + d1 = (B[1]-B[0]).Norm(); + d2 = (B[3]-B[2]).Norm(); + + int start = clock(); + //int vi,vj; + + typename PMesh::VertexIterator vii; + typename std::vector::iterator bR1,eR1,bR2,eR2,ite,cite; + bR1 = std::lower_bound::iterator,Couple>(R1.begin(),R1.end(),Couple(d1-prs.delta*2.0)); + eR1 = std::lower_bound::iterator,Couple>(R1.begin(),R1.end(),Couple(d1+prs.delta*2.0)); + bR2 = std::lower_bound::iterator,Couple>(R1.begin(),R1.end(),Couple(d2-prs.delta*2.0)); + eR2 = std::lower_bound::iterator,Couple>(R1.begin(),R1.end(),Couple(d2+prs.delta*2.0)); + + // in [bR1,eR1) there are all the pairs ad a distance d1 +- prs.delta + // in [bR1,eR1) there are all the pairs ad a distance d2 +- prs.delta + + if(bR1 == R1.end()) return false;// if there are no such pairs return + if(bR2 == R1.end()) return false; // if there are no such pairs return + + // put [bR1,eR1) in a mesh to have the search operator for free (lazy me) + Invr.Clear(); + int i = &(*bR1)-&(*R1.begin()); + for(ite = bR1; ite != eR1;++ite){ + vii = vcg::tri::Allocator::AddVertices(Invr,1); + (*vii).P() = Q->vert[R1[i][0]].P() + (Q->vert[R1[i][1]].P()-Q->vert[R1[i][0]].P()) * r1; + ++i; + } + if(Invr.vert.empty() ) return false; + + // index remaps a vertex of Invr to its corresponding point in R1 + typename PMesh::template PerVertexAttributeHandle id = vcg::tri::Allocator::template AddPerVertexAttribute(Invr,std::string("index")); + i = &(*bR1)-&(*R1.begin()); + for(vii = Invr.vert.begin(); vii != Invr.vert.end();++vii,++i) id[vii] = i; + + vcg::tri::UpdateBounding::Box(Invr); + // printf("Invr size %d\n",Invr.vn); + + ugrid = new GridType(); + ugrid->Set(Invr.vert.begin(),Invr.vert.end()); + + i = &(*bR2)-&(*R1.begin()); + // R2inv contains all the points generated by the couples in R2 (with the reference to remap into R2) + for(ite = bR2; ite != eR2;++ite){ + R2inv.push_back( EPoint( Q->vert[R1[i][0]].P() + (Q->vert[R1[i][1]].P()-Q->vert[R1[i][0]].P()) * r2,i)); + ++i; + } + + n_closests = 0; n_congr = 0; ac =0 ; acf = 0; tr = 0; trf = 0; + // fprintf(db,"R2Inv.size = %d \n",R2inv.size()); + for(uint i = 0 ; i < R2inv.size() ; ++i){ + + std::vector closests; + std::vector distances; + std::vector points; + + // for each point in R2inv get all the points in R1 closer than prs.delta + vcg::Matrix44 mat; + vcg::Box3f bb; + bb.Add(R2inv[i].pos+vcg::Point3f(prs.delta * 0.1,prs.delta * 0.1 , prs.delta * 0.1 )); + bb.Add(R2inv[i].pos-vcg::Point3f(prs.delta * 0.1,prs.delta* 0.1 , prs.delta* 0.1)); + + vcg::tri::GetInBoxVertex > + (Invr,*ugrid,bb,closests); + + n_closests+=closests.size(); + for(uint ip = 0; ip < closests.size(); ++ip){ + FourPoints p; + p[0] = Q->vert[R1[id[closests[ip]]][0]].P(); + p[1] = Q->vert[R1[id[closests[ip]]][1]].P(); + p[2] = Q->vert[R1[ R2inv[i].pi][0]].P(); + p[3] = Q->vert[R1[ R2inv[i].pi][1]].P(); + + float trerr; + n_base++; + if(!IsTransfCongruent(p,mat,trerr)) { + trf++; + //char name[255]; + //sprintf(name,"faileTR_%d_%f.aln",n_base,trerr); + //fprintf(db,"TransCongruent %s\n", name); + //SaveALN(name, mat); + } + else{ + tr++; + n_congr++; + U.push_back(CandiType(p,mat)); + EvaluateAlignment(U.back()); + U.back().base = bases.size()-1; + + if( U.back().score > prs.scoreFeet){ + TestAlignment(U.back()); + if(U.back().score > prs.scoreAln) + { + done = true; break; + } + } + //char name[255]; + //sprintf(name,"passed_score_%5d_%d.aln",U.back().score,n_base); + //fprintf(db,"OK TransCongruent %s, score: %d \n", name,U.back().score); + //SaveALN(name, mat); + } + } + } + + delete ugrid; + vcg::tri::Allocator::DeletePerVertexAttribute(Invr,id); + printf("n_closests %5d = (An %5d ) + ( Tr %5d ) + (OK) %5d\n",n_closests,acf,trf,n_congr); + + return done; +// printf("done n_closests %d congr %d in %f s\n ",n_closests,n_congr,(clock()-start)/(float)CLOCKS_PER_SEC); +// printf("angle:%d %d, trasf %d %d\n",ac,acf,tr,trf); +} + + + +template +int FourPCS::EvaluateSample(CandiType & fp, CoordType & tp, CoordType & np, const float & angle){ + VertexType* v; + ScalarType dist ; + radius = prs.delta; + tp = fp.T * tp; + + vcg::Point4 np4; + np4 = fp.T * vcg::Point4(np[0],np[1],np[2],0.0); + np[0] = np4[0]; np[1] = np4[1]; np[2] = np4[2]; + + v = 0; + //v = vcg::tri::GetClosestVertex< + // MeshType, + // vcg::GridStaticPtr + // >(*Q,ugridQ,tp,radius, dist ); + typename MeshType::VertexType vq; + vq.P() = tp; + vq.N() = np; + v = vcg::tri::GetClosestVertexNormal< + MeshType, + vcg::GridStaticPtr + >(*Q,ugridQ,vq,radius, dist ); + + if(v!=0) + if( v->N().dot(np) -angle >0) return 1; else return -1; + +} + + +template +void +FourPCS::EvaluateAlignment(CandiType & fp){ + int n_delta_close = 0; + for(int i = 0 ; i< 4; ++i) { + for(uint j = 0; j < ExtB[i].size();++j){ + CoordType np = ExtB[i][j]->cN();; + CoordType tp = ExtB[i][j]->P(); + n_delta_close+=EvaluateSample(fp,tp,np,0.9); + } + } + fp.score = n_delta_close; +} + +template +void +FourPCS::TestAlignment(CandiType & fp){ + radius = prs.delta; + int n_delta_close = 0; + for(uint j = 0; j < subsetP.size();++j){ + CoordType np = subsetP[j]->N(); + CoordType tp = subsetP[j]->P(); + n_delta_close+=EvaluateSample(fp,tp,np,0.6); + } + fp.score = n_delta_close; +} + + +template +bool +FourPCS:: Align( int L, vcg::Matrix44f & result, AACb * cb ){ // main loop + + int bestv = 0; + bool found; + int n_tries = 0; + U.clear(); + + if(L==0) + { + L = (log(1.0-0.9999) / log(1.0-pow((float)prs.f,3.f)))+1; + printf("using %d bases\n",L); + } + + ComputeR1R2(side*1.4,side*1.4); + + for(int t = 0; t < L; ++t ){ + do{ + n_tries = 0; + do{ + n_tries++; + found = SelectCoplanarBase(); + } + while(!found && (n_tries <50)); + if(!found) { + prs.f*=0.98; + side = P->bbox.Dim()[P->bbox.MaxDim()]*prs.f; //rough implementation + ComputeR1R2(side*1.4,side*1.4); + } + } while (!found && (prs.f >0.1)); + + if(prs.f <0.1) { + printf("FAILED"); + return false; + } + bases.push_back(B); + if(cb) cb(t*100/L,"trying bases"); + if(FindCongruent()) + break; + } + + if(U.empty()) return false; + + std::sort(U.begin(),U.end()); + + bestv = -std::numeric_limits::max(); + iwinner = 0; + + for(int i = 0 ; i < U.size() ;++i) + { + TestAlignment(U[i]); + if(U[i].score > bestv){ + bestv = U[i].score; + iwinner = i; + } + } + + printf("Best score: %d \n", bestv); + + winner = U[iwinner]; + result = winner.T; + + // deallocations + Invr.Clear(); + + return true; +} + + } // namespace tri +} // namespace vcg +#endif diff --git a/vcg/complex/algorithms/bitquad_creation.h b/vcg/complex/algorithms/bitquad_creation.h new file mode 100644 index 00000000..af25fd00 --- /dev/null +++ b/vcg/complex/algorithms/bitquad_creation.h @@ -0,0 +1,788 @@ +#include +#include + +/** BIT-QUAD creation support: + a collection of methods that, + starting from a triangular mesh, will create your quad-pure or quad-domainant mesh. + + They all require: + - per face Q, and FF connectivity, 2-manyfold meshes, + - and tri- or quad- meshes (no penta, etc) (if in need, use MakeBitTriOnly) + + +[ list of available methods: ] + +void MakePureByRefine(Mesh &m) + - adds a vertex for each tri or quad present + - thus, miminal complexity increase is the mesh is quad-dominant already + - old non-border edges are made faux + - never fails + +void MakePureByCatmullClark(MeshType &m) + - adds a vertex in each (non-faux) edge. + - twice complexity increase w.r.t. "ByRefine" method. + - preserves edges: old edges are still edges + - never fails + +bool MakePureByFlip(MeshType &m [, int maxdist] ) + - does not increase # vertices, just flips edges + - call in a loop until it returns true (temporary hack) + - fails if number of triangle is odd (only happens in open meshes) + - add "StepByStep" to method name if you want it to make a single step (debugging purposes) + +bool MakeTriEvenBySplit(MeshType& m) +bool MakeTriEvenByDelete(MeshType& m) + - two simple variants that either delete or split *at most one* border face + so that the number of tris will be made even. Return true if it did it. + - useful to use the previous method, when mesh is still all triangle + +void MakeDominant(MeshType &m, int level) + - just merges traingle pairs into quads, trying its best + - various heuristic available, see descr. for parameter "level" + - provides good starting point for make-Quad-Only methods + - uses an ad-hoc measure for "quad quality" (which is hard-wired, for now) + +void MakeBitTriOnly(MeshType &m) + - inverse process: returns to tri-only mesh + + +(more info in comments before each method) + +*/ +#ifndef VCG_BITQUAD_CRE +#define VCG_BITQUAD_CRE + +namespace vcg{namespace tri{ + +template > +class BitQuadCreation{ + +public: + +typedef _MeshType MeshType; +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::CoordType CoordType; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FaceType* FaceTypeP; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::FaceIterator FaceIterator; +typedef typename MeshType::VertexIterator VertexIterator; + +typedef BitQuad BQ; // static class to make basic quad operations + + +// helper function: +// given a triangle, merge it with its best neightboord to form a quad +template +static void selectBestDiag(FaceType *fi){ + + if (!override) { + if (fi->IsAnyF()) return; + } + + // select which edge to make faux (if any)... + int whichEdge = -1; + ScalarType bestScore = fi->Q(); + + whichEdge=-1; + + for (int k=0; k<3; k++){ + + // todo: check creases? (continue if edge k is a crease) + + if (!override) { + if (fi->FFp(k)->IsAnyF()) continue; + } + if (fi->FFp(k)==fi) continue; // never make a border faux + + ScalarType score = BQ::quadQuality( &*fi, k ); + if (override) { + // don't override anyway iff other face has a better match + if (score < fi->FFp(k)->Q()) continue; + } + if (score>bestScore) { + bestScore = score; + whichEdge = k; + } + } + + // ...and make it faux + if (whichEdge>=0) { + //if (override && fi->FFp(whichEdge)->IsAnyF()) { + // new score is the average of both scores + // fi->Q() = fi->FFp(whichEdge)->Q() = ( bestScore + fi->FFp(whichEdge)->Q() ) /2; + //} else { + //} + + if (override) { + // clear any faux edge of the other face + for (int k=0; k<3; k++) + if (fi->FFp(whichEdge)->IsF(k)) { + fi->FFp(whichEdge)->ClearF(k); + fi->FFp(whichEdge)->FFp(k)->ClearF( fi->FFp(whichEdge)->FFi(k) ); + fi->FFp(whichEdge)->FFp(k)->Q()=0.0; // other face's ex-buddy is now single and sad :( + } + + // clear all faux edges of this face... + for (int k=0; k<3; k++) + if (fi->IsF(k)) { + fi->ClearF(k); + fi->FFp(k)->ClearF( fi->FFi(k) ); + fi->FFp(k)->Q()= 0.0; // my ex-buddy is now sad + } + } + // set (new?) quad + fi->SetF(whichEdge); + fi->FFp(whichEdge)->SetF( fi->FFi(whichEdge) ); + fi->Q() = fi->FFp(whichEdge)->Q() = bestScore; + + } + + +} + + + +// helper funcion: +// a pass though all triangles to merge triangle pairs into quads +template // override previous decisions? +static void MakeDominantPass(MeshType &m){ + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + selectBestDiag(&(*fi)); + } + +} +/** + * This function split a face along the specified border edge it does not compute any property of the new vertex. It only do the topological work. + * @param edge Index of the edge + */ +// sideF +// sideF V2(e) ------------- v2 +// V0 -------------V2 V2(e) \ / +// | / | \ \ newF / +// | / | \ \ / e +// | f / | \ \ / +// | / e | f V1(e)=newV = +// | / | / +// | / | / +// | / | / +// V1 V0(e) +// + +static std::pair FaceSplitBorderEdge(MeshType &m, typename MeshType::FaceType &f, int edge, typename MeshType::FaceType *newFace, typename MeshType::VertexType *newVert ) +{ + + typename MeshType::FaceType *sideFFp; + int sideFFi; + + assert(tri::HasFFAdjacency(m)); + assert(face::IsBorder(f,edge)); + //qDebug("OldFacePRE %i %i %i",tri::Index(m,f.V(0)),tri::Index(m,f.V(1)),tri::Index(m,f.V(2))); + if(newFace==0) newFace=&*tri::Allocator::AddFaces(m,1); + if(newVert==0) { + newVert=&*tri::Allocator::AddVertices(m,1); + newVert->P()=(f.P0(edge)+f.P1(edge))/2.0; + } + newFace->V0(edge)=newVert; + newFace->V1(edge)=f.V1(edge); + newFace->V2(edge)=f.V2(edge); + + f.V1(edge)=newVert; + + //qDebug("NewFace %i %i %i",tri::Index(m,newFace->V(0)),tri::Index(m,newFace->V(1)),tri::Index(m,newFace->V(2))); + //qDebug("OldFace %i %i %i",tri::Index(m,f.V(0)),tri::Index(m,f.V(1)),tri::Index(m,f.V(2))); + + // Topology + + newFace->FFp((edge+2)%3) = &f; + newFace->FFi((edge+2)%3) = (edge+1)%3; + + newFace->FFp((edge+0)%3) = newFace; + newFace->FFi((edge+0)%3) = (edge+0)%3; + + newFace->FFp((edge+1)%3) = f.FFp((edge+1)%3); + newFace->FFi((edge+1)%3) = f.FFi((edge+1)%3); + + sideFFp = f.FFp((edge+1)%3); + sideFFi = f.FFi((edge+1)%3); + + f.FFp((edge+1)%3) = newFace; + f.FFi((edge+1)%3) = (edge+2)%3; + + sideFFp->FFp(sideFFi)=newFace; + sideFFp->FFi(sideFFi)=(edge+1)%3; + + assert(face::IsBorder(f,edge)); + assert(face::IsBorder(*newFace,edge)); + + return std::make_pair(newFace,newVert); +} +// make tri count even by splitting a single triangle... +// +// V0 -------V2 V0 --------V2 +// | / | \ Fnew / +// | / | Vnew +// | / | / +// | / | / +// V1 V1 +// + +static bool MakeTriEvenBySplit(MeshType& m){ + if (m.fn%2==0) return false; // it's already Even + // Search for a triangle on the border + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) + { + if(!(*fi).IsD()) + { + for (int k=0; k<3; k++) { + if (face::IsBorder(*fi,k)){ + // We have found a face with a border + int index=tri::Index(m,*fi); + VertexIterator vnew=tri::Allocator::AddVertices(m,1); + (*vnew).P()=((*fi).P0(k)+(*fi).P1(k))/2.0; + + FaceIterator fnew=tri::Allocator::AddFaces(m,1); + + FaceSplitBorderEdge(m,m.face[index],k,&*fnew,&*vnew); + return true; + } + } + } + + } + return true; +} + +// make tri count even by delete... +static bool MakeTriEvenByDelete(MeshType& m) +{ + if (m.fn%2==0) return false; // it's already Even + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) { + for (int k=0; k<3; k++) { + if (face::IsBorder(*fi,k) ) { + FFDetachManifold(*fi,(k+1)%3); + FFDetachManifold(*fi,(k+2)%3); + Allocator::DeleteFace(m,*fi); + return true; + } + } + } + assert(0); // no border face found? then how could the number of tri be Odd? + return true; +} + + +/** + Given a mesh, makes it bit trianglular (makes all edges NOT faux) +*/ +static void MakeBitTriOnly(MeshType &m){ + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) { + fi->ClearAllF(); + } +} + +/** given a quad-and-tree mesh, enforces the "faux edge is 2nd edge" convention. + * Requires (and updates): FV and FF structure + * Updates: faux flags + * Updates: per wedge attributes, if any + * Other connectivity structures, and per edge and per wedge flags are ignored + */ +static bool MakeBitTriQuadConventional(MeshType &m){ + assert(0); // todo +} + +/* returns true if mesh is a "conventional" quad mesh. + I.e. if it is all quads, with third edge faux fora all triangles*/ +static bool IsBitTriQuadConventional(MeshType &m){ + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (fi->IsAnyF()) + if ( (fi->Flags() & FaceType::FAUX012 ) != FaceType::FAUX2 ) { + return false; + } + } + return true; +} +static void CopyTopology(FaceType *fnew, FaceType * fold) +{ + fnew->FFp(0)=fold->FFp(0); fnew->FFi(0)=fold->FFi(0); + fnew->FFp(1)=fold->FFp(1); fnew->FFi(1)=fold->FFi(1); + fnew->FFp(2)=fold->FFp(2); fnew->FFi(2)=fold->FFi(2); + fnew->V(0) = fold->V(0); + fnew->V(1) = fold->V(1); + fnew->V(2) = fold->V(2); +} +/** + makes any mesh quad only by refining it so that a quad is created over all + previous diags + requires that the mesh is made only of quads and tris. +*/ +static void MakePureByRefine(MeshType &m){ + + // todo: update VF connectivity if present + + + int ev = 0; // EXTRA vertices (times 2) + int ef = 0; // EXTRA faces + + // first pass: count triangles to be added + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + int k=0; + if (face::IsBorder(*fi,0)) k++; + if (face::IsBorder(*fi,1)) k++; + if (face::IsBorder(*fi,2)) k++; + if (!fi->IsAnyF()) { + // it's a triangle + if (k==0) // add a vertex in the center of the face, splitting it in 3 + { ev+=2; ef+=2; } + if (k==1) // add a vertex in the border edge, splitting it in 2 + { } + if (k==2) // do nothing, just mark the non border edge as faux + { } + if (k==3) // disconnected single triangle (all borders): make one edge as faus + { } + } + else { + // assuming is a quad (not a penta, etc), i.e. only one faux + // add a vertex in the center of the faux edge, splitting the face in 2 + ev+=1; ef+=1; + assert(k!=3); + } + } + assert(ev%2==0); // should be even by now + ev/=2; // I was counting each of them twice + + //int originalFaceNum = m.fn; + FaceIterator nfi = tri::Allocator::AddFaces(m,ef); + VertexIterator nvi = tri::Allocator::AddVertices(m,ev); + + tri::UpdateFlags::FaceClearV(m); + + // second pass: add faces and vertices + int nsplit=0; // spits to be done on border in the third pass + for (FaceIterator fi = m.face.begin(), fend = nfi; fi!=fend; fi++) if (!fi->IsD() && !fi->IsV() ) { + + fi->SetV(); + + if (!fi->IsAnyF()) { + // it's a triangle + + int k=0; // number of borders + if (face::IsBorder(*fi,0)) k++; + if (face::IsBorder(*fi,1)) k++; + if (face::IsBorder(*fi,2)) k++; + + if (k==0) // add a vertex in the center of the face, splitting it in 3 + { + assert(nvi!=m.vert.end()); + VertexType *nv = &*nvi; nvi++; + //*nv = *fi->V0( 0 ); // lazy: copy everything from the old vertex + nv->ImportData(*(fi->V0( 0 ))); // lazy: copy everything from the old vertex + + nv->P() = ( fi->V(0)->P() + fi->V(1)->P() + fi->V(2)->P() ) /3.0; + FaceType *fa = &*fi; + FaceType *fb = &*nfi; nfi++; + FaceType *fc = &*nfi; nfi++; + + fb->ImportData(*fa); CopyTopology(fb,fa); + fc->ImportData(*fa); CopyTopology(fc,fa); + + fa->V(0) = nv; + fb->V(1) = nv; + fc->V(2) = nv; + + fb->FFp(2)=fa->FFp(2); fb->FFi(2)=fa->FFi(2); + fc->FFp(0)=fa->FFp(0); fc->FFi(0)=fa->FFi(0); + + assert( fa->FFp(1)->FFp(fa->FFi(1)) == fa ); + /* */fb->FFp(2)->FFp(fb->FFi(2)) = fb; + /* */fc->FFp(0)->FFp(fc->FFi(0)) = fc; + + fa->FFp(0) = fc; fa->FFp(2) = fb; fa->FFi(0) = fa->FFi(2) = 1; + fb->FFp(1) = fa; fb->FFp(0) = fc; fb->FFi(0) = fb->FFi(1) = 2; + fc->FFp(1) = fa; fc->FFp(2) = fb; fc->FFi(1) = fc->FFi(2) = 0; + + if (fb->FFp(2)==fa) fb->FFp(2)=fb; // recover border status + if (fc->FFp(0)==fa) fc->FFp(0)=fc; + + fa->ClearAllF(); + fb->ClearAllF(); + fc->ClearAllF(); + fa->SetF(1); + fb->SetF(2); + fc->SetF(0); + + fa->SetV();fb->SetV();fc->SetV(); + } + if (k==1) { // make a border face faux, anf other two as well + fi->SetF(0); + fi->SetF(1); + fi->SetF(2); + nsplit++; + } + if (k==2) // do nothing, just mark the non border edge as faux + { + fi->ClearAllF(); + for (int w=0; w<3; w++) if (fi->FFp(w) != &*fi) fi->SetF(w); + } + if (k==3) // disconnected single triangle (all borders): use catmull-clark (tree vertices, split it in 6 + { + fi->ClearAllF(); + fi->SetF(2); + nsplit++; + } + } + else { + // assuming is a part of quad (not a penta, etc), i.e. only one faux + FaceType *fa = &*fi; + int ea2 = BQ::FauxIndex(fa); // index of the only faux edge + FaceType *fb = fa->FFp(ea2); + int eb2 = fa->FFi(ea2); + assert(fb->FFp(eb2)==fa) ; + assert(fa->IsF(ea2)); + //assert(fb->IsF(eb2)); // reciprocal faux edge + + int ea0 = (ea2+1) %3; + int ea1 = (ea2+2) %3; + int eb0 = (eb2+1) %3; + int eb1 = (eb2+2) %3; + + // create new vert in center of faux edge + assert(nvi!=m.vert.end()); + VertexType *nv = &*nvi; nvi++; + // *nv = * fa->V0( ea2 ); + nv->ImportData(*(fa->V0( ea2 ) )); // lazy: copy everything from the old vertex + //nv->P() = ( fa->V(ea2)->P() + fa->V(ea0)->P() ) /2.0; + Interpolator::Apply(*(fa->V(ea2)),*(fa->V(ea0)),0.5,*nv); + // split faces: add 2 faces (one per side) + assert(nfi!=m.face.end()); + FaceType *fc = &*nfi; nfi++; + assert(nfi!=m.face.end()); + FaceType *fd = &*nfi; nfi++; + + fc->ImportData(*fa ); CopyTopology(fc,fa); // lazy: copy everything from the old vertex + fd->ImportData(*fb ); CopyTopology(fd,fb);// lazy: copy everything from the old vertex + + fa->V(ea2) = fc->V(ea0) = + fb->V(eb2) = fd->V(eb0) = nv ; + + fa->FFp(ea1)->FFp( fa->FFi(ea1) ) = fc; + fb->FFp(eb1)->FFp( fb->FFi(eb1) ) = fd; + + fa->FFp(ea1) = fc ; fa->FFp(ea2) = fd; + fa->FFi(ea1) = ea0; fa->FFi(ea2) = eb2; + fb->FFp(eb1) = fd ; fb->FFp(eb2) = fc; + fb->FFi(eb1) = eb0; fb->FFi(eb2) = ea2; + fc->FFp(ea0) = fa ; fc->FFp(ea2) = fb; + fc->FFi(ea0) = ea1; fc->FFi(ea2) = eb2; + fd->FFp(eb0) = fb ; fd->FFp(eb2) = fa; + fd->FFi(eb0) = eb1; fd->FFi(eb2) = ea2; + + // detect boundaries + bool ba = fa->FFp(ea0)==fa; + bool bc = fc->FFp(ea1)==fa; + bool bb = fb->FFp(eb0)==fb; + bool bd = fd->FFp(eb1)==fb; + + if (bc) fc->FFp(ea1)=fc; // repristinate boundary status + if (bd) fd->FFp(eb1)=fd; // of new faces + + fa->SetV(); + fb->SetV(); + fc->SetV(); + fd->SetV(); + + fa->ClearAllF(); + fb->ClearAllF(); + fc->ClearAllF(); + fd->ClearAllF(); + + fa->SetF( ea0 ); + fb->SetF( eb0 ); + fc->SetF( ea1 ); + fd->SetF( eb1 ); + + // fix faux mesh boundary... if two any consecutive, merge it in a quad + if (ba&&bc) { + fa->ClearAllF(); fa->SetF(ea1); + fc->ClearAllF(); fc->SetF(ea0); + ba = bc = false; + } + if (bc&&bb) { + fc->ClearAllF(); fc->SetF(ea2); + fb->ClearAllF(); fb->SetF(eb2); + bc = bb = false; + } + if (bb&&bd) { + fb->ClearAllF(); fb->SetF(eb1); + fd->ClearAllF(); fd->SetF(eb0); + bb = bd = false; + } + if (bd&&ba) { + fd->ClearAllF(); fd->SetF(eb2); + fa->ClearAllF(); fa->SetF(ea2); + bd = ba = false; + } + // remaninig boudaries will be fixed by splitting in the last pass + if (ba) nsplit++; + if (bb) nsplit++; + if (bc) nsplit++; + if (bd) nsplit++; + } + } + assert(nfi==m.face.end()); + assert(nvi==m.vert.end()); + + // now and there are no tris left, but there can be faces with ONE edge border & faux () + + + // last pass: add vertex on faux border faces... (if any) + if (nsplit>0) { + FaceIterator nfi = tri::Allocator::AddFaces(m,nsplit); + VertexIterator nvi = tri::Allocator::AddVertices(m,nsplit); + for (FaceIterator fi = m.face.begin(), fend = nfi; fi!=fend; fi++) if (!fi->IsD()) { + FaceType* fa = &*fi; + int ea2 = -1; // border and faux face (if any) + if (fa->FFp(0)==fa && fa->IsF(0) ) ea2=0; + if (fa->FFp(1)==fa && fa->IsF(1) ) ea2=1; + if (fa->FFp(2)==fa && fa->IsF(2) ) ea2=2; + + if (ea2 != -1) { // ea2 edge is naughty (border AND faux) + + int ea0 = (ea2+1) %3; + int ea1 = (ea2+2) %3; + + // create new vert in center of faux edge + VertexType *nv = &*nvi; nvi++; + //*nv = * fa->V0( ea2 ); + nv->ImportData(*(fa->V0( ea2 ) )); // lazy: copy everything from the old vertex + nv->P() = ( fa->V(ea2)->P() + fa->V(ea0)->P() ) /2.0; + Interpolator::Apply(*(fa->V(ea2)),*(fa->V(ea0)),0.5,*nv); + // split face: add 1 face + FaceType *fc = &*nfi; nfi++; + + fc->ImportData(*fa);CopyTopology(fc,fa); // lazy: copy everything from the old vertex + + fa->V(ea2) = fc->V(ea0) = nv ; + + fc->FFp(ea2) = fc; + + fa->FFp(ea1)->FFp( fa->FFi(ea1) ) = fc; + + fa->FFp(ea1) = fc ; + fa->FFi(ea1) = ea0; + fc->FFp(ea0) = fa ; fc->FFp(ea2) = fc; + fc->FFi(ea0) = ea1; + + if (fc->FFp(ea1)==fa) fc->FFp(ea1)=fc; // recover border status + + assert(fa->IsF(ea0) == fa->IsF(ea1) ); + bool b = fa->IsF(ea1); + + fa->ClearAllF(); + fc->ClearAllF(); + + if (b) { + fa->SetF( ea0 ); + fc->SetF( ea1 ); + } else { + fa->SetF( ea1 ); + fc->SetF( ea0 ); + } + } + } + } + + +} + + +// uses Catmull Clark to enforce quad only meshes +// each old edge (but not faux) is split in two. +static void MakePureByCatmullClark(MeshType &m){ + MakePureByRefine(m); + MakePureByRefine(m); + // done +} + +// Helper funcion: +// marks edge distance froma a given face. +// Stops at maxDist or at the distance when a triangle is found +static FaceType * MarkEdgeDistance(MeshType &m, FaceType *startF, int maxDist){ + assert(tri::HasPerFaceQuality(m)); + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=maxDist; + } + + FaceType * firstTriangleFound = NULL; + + startF->Q() = 0; + std::vector stack; + int stackPos=0; + stack.push_back(startF); + + while ( stackPosFFp(k); + int fq = int(f->Q()) + ( ! f->IsF(k) ); + if (fk->Q()> fq && fq <= maxDist) { + if (!fk->IsAnyF()) { firstTriangleFound = fk; maxDist = fq;} + fk->Q() = fq; + stack.push_back(fk); + } + } + } + return firstTriangleFound; +} + + +/* + given a tri-quad mesh, + uses edge rotates to make a tri move toward another tri and to merges them into a quad. + + Retunrs number of surviving triangles (0, or 1), or -1 if not done yet. + StepbyStep: makes just one step! + use it in a loop as long as it returns 0 or 1. + + maxdist is the maximal edge distance where to look for a companion triangle +*/ +static int MakePureByFlipStepByStep(MeshType &m, int maxdist=10000, int restart=false){ + + static FaceType *ta, *tb; // faces to be matched into a quad + + static int step = 0; // hack + + if (restart) { step=0; return false; } +if (step==0) { + + // find a triangular face ta + ta = NULL; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (!fi->IsAnyF()) { ta=&*fi; break; } + } + if (!ta) return 0; // success: no triangle left (done?) + + + tb = MarkEdgeDistance(m,ta,maxdist); + if (!tb) return 1; // fail: no matching triagle found (increase maxdist?) + + step=1; + +} else { + int marriageEdge=-1; + bool done = false; + while (!done) { + + int bestScore = int(tb->Q()); + int edge = -1; + bool mustDoFlip; + + // select which edge to use + for (int k=0; k<3; k++) { + if (tb->FFp(k) == tb) continue; // border + + FaceType* tbk = tb->FFp(k); + + if (!tbk->IsAnyF()) {done=true; marriageEdge=k; break; } // found my match + + int back = tb->FFi(k); + int faux = BQ::FauxIndex(tbk); + int other = 3-back-faux; + + int scoreA = int(tbk->FFp(other)->Q()); + + FaceType* tbh = tbk->FFp(faux); + int fauxh = BQ::FauxIndex(tbh); + + int scoreB = int(tbh->FFp( (fauxh+1)%3 )->Q()); + int scoreC = int(tbh->FFp( (fauxh+2)%3 )->Q()); + + int scoreABC = std::min( scoreC, std::min( scoreA, scoreB ) ); + if (scoreABCFFp(edge)) ); + } + + FaceType* next = tb->FFp(edge)->FFp( BQ::FauxIndex(tb->FFp(edge)) ); + + // create new edge + next->ClearAllF(); + tb->FFp(edge)->ClearAllF(); + + // dissolve old edge + tb->SetF(edge); + tb->FFp(edge)->SetF( tb->FFi(edge) ); + tb->FFp(edge)->Q() = tb->Q(); + + tb = next; +break; + } + + if (marriageEdge!=-1) { + // consume the marriage (two tris = one quad) + assert(!(tb->IsAnyF())); + assert(!(tb->FFp(marriageEdge)->IsAnyF())); + tb->SetF(marriageEdge); + tb->FFp(marriageEdge)->SetF(tb->FFi(marriageEdge)); + + step=0; + } +} + return -1; // not done yet +} + +/* + given a tri-quad mesh, + uses edge rotates to make a tri move toward another tri and to merges them into a quad. + - maxdist is the maximal edge distance where to look for a companion triangle + - retunrs true if all triangles are merged (always, unless they are odd, or maxdist not enough). +*/ +static bool MakePureByFlip(MeshType &m, int maxdist=10000) +{ + MakePureByFlipStepByStep(m, maxdist, true); // restart + int res=-1; + while (res==-1) res = MakePureByFlipStepByStep(m, maxdist); + return res==0; +} + +/** + given a triangle mesh, makes it quad dominant by merging triangle pairs into quads + various euristics: + level = 0: maximally greedy. Leaves fewest triangles + level = 1: smarter: leaves more triangles, but makes better quality quads + level = 2: even more so (marginally) +*/ +static void MakeDominant(MeshType &m, int level){ + + assert(MeshType::HasPerFaceQuality()); + assert(MeshType::HasPerFaceFlags()); + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) { + fi->ClearAllF(); + fi->Q() = 0; + } + + + MakeDominantPass (m); + if (level>0) MakeDominantPass (m); + if (level>1) MakeDominantPass (m); + if (level>0) MakeDominantPass (m); +} + +}; +}} // end namespace vcg::tri +#endif diff --git a/vcg/complex/algorithms/bitquad_optimization.h b/vcg/complex/algorithms/bitquad_optimization.h new file mode 100644 index 00000000..7ae503bb --- /dev/null +++ b/vcg/complex/algorithms/bitquad_optimization.h @@ -0,0 +1,404 @@ +#ifndef _BITQUAD_OPTIMIZATION +#define _BITQUAD_OPTIMIZATION + +namespace vcg{namespace tri{ + +template +class BitQuadOptimization{ + +typedef typename BQ::MeshType MeshType; +typedef typename BQ::Pos Pos; + +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::CoordType CoordType; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FaceType* FaceTypeP; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::FaceIterator FaceIterator; +typedef typename MeshType::VertexIterator VertexIterator; + +//typedef BitQuad BQ; // static class to make basic quad operatins + +public: + +// helper function: mark a quadface, setting Q at 0, and neight at .75, 0.5... +static void MarkFace(FaceType* f, MeshType &m){ + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q() = 1; + } + + for (int i=0; i<3; i++) { + for (int j=0; j<3; j++) f->FFp(i)->FFp(j)->Q() = 0.75; + } + for (int i=0; i<3; i++) { + f->FFp(i)->Q() = 0.50; + } + f->Q() = 0; + +} + +// helper function: mark a quadface, setting Q at 0, and neight at .75, 0.5... +static void MarkVertex(FaceType* f, int wedge, MeshType &m){ + + VertexType *v = f->V(wedge); + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (fi->V0(0)==v || fi->V1(0)==v ||fi->V2(0)==v ) fi->Q() = 0; + // else fi->Q() = 1; + } + +} + +static bool MarkSmallestEdge(MeshType &m, bool perform) +{ + ScalarType min = std::numeric_limits::max(); + + FaceType *fa=NULL; int w=0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) + for (int k=0; k<3; k++) { + FaceType *f=&*fi; + + if (f->IsF(k)) continue; + if (f->FFp(k) == f ) continue; // skip borders + + ScalarType score; + + score = (f->P0(k) - f->P1(k)).Norm(); + if (scoreQ()=0.0; + fa->FFp(w)->Q()=0.0; + return true; + } + } + return false; +} + +static ScalarType Importance(const CoordType &p){ + //return ::proceduralImportance(p); + return 1; +} + +// returns: 0 if fail. 1 if edge. 2 if diag. +static int MarkSmallestEdgeOrDiag(MeshType &m, ScalarType edgeMult, bool perform, Pos* affected=NULL) +{ + ScalarType min = std::numeric_limits::max(); + + FaceType *fa=NULL; int w=0; bool counterDiag = false; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) + for (int k=0; k<3; k++) { + FaceType *f=&*fi; + + if (f->FFp(k) >= f ) continue; // skip borders (==), and do it one per edge + + ScalarType score; + + score = (f->P0(k) - f->P1(k)).Norm(); + + ScalarType imp = Importance( (f->P0(k) + f->P1(k))/2 ); + + score /= imp; + + if (!f->IsF(k)) score*=edgeMult; // edges are supposed to be smaller! + + + + if (scoreIsF(k)) { // for diag faces, test counterdiag too + score = BQ::CounterDiag(f).Norm(); + score /= imp; + + if (scoreIsF(w)) { + if (counterDiag) { + if (BQ::CollapseCounterDiag(*fa, BQ::PosOnDiag(*fa,true), m , affected)) return 2; + } else { + if (BQ::CollapseDiag(*fa, BQ::PosOnDiag(*fa,false), m ,affected)) return 2; + } + } else { + if (BQ::CollapseEdge(*fa,w,m, affected)) return 1; + } + } else { + fa->Q()=0.0; + fa->FFp(w)->Q()=0.0; + if (fa->IsF(w)) return 2; else return 1; + } + } + return 0; +} + + +static void MarkSmallestDiag(MeshType &m) +{ + ScalarType min = std::numeric_limits::max(); + + FaceType *fa=NULL; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + FaceType *f=&*fi; + + ScalarType score; + + score = BQ::Diag(f).Norm(); + if (scoreQ()=0.0; + fa->FFp(BQ::FauxIndex(fa))->Q()=0.0; + } + +} + + + +static bool IdentifyAndCollapseSmallestDiag(MeshType &m){ + ScalarType min = std::numeric_limits::max(); + + FaceType *fa=NULL; bool flip; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + FaceType *f=&*fi; + + ScalarType score; + + score = BQ::Diag(f).Norm(); + if (scoreFFp(k),(fa->FFi(k)+2)%3, m )) return true; + + if (flip) { + if (!BQ::CheckFlipDiag(*fa) ) { + // I can't collapse (why?) + MarkFace(fa,m); + return false; + } else + BQ::CollapseCounterDiag(*fa, BQ::PosOnDiag(*fa,true), m ); + } + else { + BQ::CollapseDiag(*fa, BQ::PosOnDiag(*fa,false), m ); + } + return true; +} + + + +/* +seeks and removes all doublets (a pair of quads sharing two consecutive edges) +by merging them into a single quad (thus removing one vertex and two tri faces)- +Returns number of removed Doublets +*/ +static int RemoveDoublets(MeshType &m, Pos *p=NULL) +{ + int res=0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=1; + for (int k=0; k<3; k++) { + if ( BQ::IsDoublet(*fi,k) ){ + res++; + BQ::RemoveDoublet(*fi,k,m,p); + if (fi->IsD()) break; // break wedge circle, if face disappeard + if (p) return res; + } + } + } + return res; +} + +/* +marks (Quality=0) and approx. counts profitable vertex rotations +(vertex rotations which make edge shorter +*/ +template +static int MarkVertexRotations(MeshType &m, Pos *affected=NULL) +{ + int res=0; + for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) vi->ClearV(); + if (!perform) + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->Q()=1.0; + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + + for (int k=0; k<3; k++) { + if (fi->V(k)->IsV()) continue; + if (BQ::TestVertexRotation(*fi,k)) { + res++; + fi->V(k)->SetV(); + if (!perform) { + res++; MarkVertex(&*fi, k, m); //fi->Q()=0; + } + else { + if (BQ::RotateVertex(*fi, k, m, affected)) res++; //fi->Q()=0; + if (affected) return res; // uncomment for only one rotation + } + } + } + } + return res; +} + +// mark (and count) all edges that are worth rotating +// if perform == true, actually rotate them +template +static int MarkEdgeRotations(MeshType &m, Pos *p=NULL) +{ + int count = 0; + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->Q()=1; + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + //if (count>0) break; + for (int k=0; k<3; k++) { + if (fi->IsF(k)) continue; + if (fi->FFp(k)<= &*fi) continue; // only once per real (non faux) edge, and only for non border ones + int best = BQ::TestEdgeRotation(*fi, k); + if (perform) { + if (best==+1) if (BQ::template RotateEdge< true>(*fi, k, m, p)) count++; + if (best==-1) if (BQ::template RotateEdge(*fi, k, m, p)) count++; + if (p) if (count>0) return count; + } + else { + if (best!=0) { fi->Q()=0; fi->FFp(k)->Q()=0; count++; } + } + } + } + + return count; +} + +/* +marks (Quality=0) and approx. counts doublets (a pair of quads sharing two consecutive edges) +*/ +static int MarkDoublets(MeshType &m) +{ + int res=0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=1; + for (int k=0; k<3; k++) { + if ( BQ::IsDoublet(*fi,k) ){ + res++; + if (fi->IsF((k+1)%3)) res++; // counts for a quad + fi->Q()=0; + } + } + } + assert (res%2==0); + return res/4; // return doublet pairs (approx, as a quad could be a part of many pairs) +} + +/* +marks (Quality=0) and counts singlets (vertex B in an A-B-A-C quad) +*/ +static int MarkSinglets(MeshType &m) +{ + int res=0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + fi->Q()=1; + for (int k=0; k<3; k++) { + if ( BQ::IsSinglet(*fi,k) ){ + res++; + fi->Q()=0; + } + } + } + assert (res%2==0); + return res/2; // return number of singlet pairs +} + +/* +deletes singlets, reutrns number of +*/ +static int RemoveSinglets(MeshType &m, Pos *p=NULL) +{ + int res=0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + for (int k=0; k<3; k++) { + if ( BQ::IsSinglet(*fi,k) ){ + res++; + BQ::RemoveSinglet(*fi,k,m, p); + if (p) return res; + break; + } + } + } + return res; // return singlet pairs (approx, as a quad could be a part of many pairs) +} + + +/* returns average quad quality, and assigns it to triangle quality +*/ +static ScalarType MeasureQuality(MeshType &m) +{ + assert(MeshType::HasPerFaceFlags()); + ScalarType res = 0; + int div = 0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + if (fi->IsAnyF()) { + + ScalarType q = BQ::quadQuality( &*fi, BQ::FauxIndex(&*fi) ); + + if (MeshType::HasPerFaceQuality()) fi->Q() = q; + res += q; + div++; + } + } + if (!div) return 0; else return res / div; +} + +}; +}} // end namespace vcg::tri + +#endif diff --git a/vcg/complex/algorithms/bitquad_support.h b/vcg/complex/algorithms/bitquad_support.h new file mode 100644 index 00000000..e6c49d11 --- /dev/null +++ b/vcg/complex/algorithms/bitquad_support.h @@ -0,0 +1,1180 @@ +#ifndef VCG_BITQUAD_SUPPORT +#define VCG_BITQUAD_SUPPORT +#include +#include +#include +#include +#include +#include + +/** BIT-QUAD creation support: + a few basic operations to work with bit-quads simplices + (quads defined by faux edges over a tri mesh backbone) + + + [ basic operations: ] + + bool IsDoublet(const FaceType& f, int wedge) + void RemoveDoublet(FaceType &f, int wedge, MeshType& m) + - identifies and removed "Doublets" (pair of quads sharing two consecutive edges) + + bool IsSinglet(const FaceType& f, int wedge) + void RemoveSinglet(FaceType &f, int wedge, MeshType& m) + + void FlipDiag(FaceType &f) + - rotates the faux edge of a quad (quad only change internally) + + bool RotateEdge(FaceType& f, int w0a); + - rotate a quad edge (clockwise or counterclockwise, specified via template) + + bool RotateVertex(FaceType &f, int w0) + - rotate around a quad vertex ("wind-mill" operation) + + void CollapseDiag(FaceType &f, ... p , MeshType& m) + - collapses a quad on its diagonal. + - p identifies the pos of collapsed point + (as either the parametric pos on the diagonal, or a fresh coordtype) + + + [ helper functions: ] + + ScalarType quadQuality( ... ); + - returns the quality for a given quad + - (should be made into a template parameter for methods using it) + - currently measures how squared each angle is + + int FauxIndex(const FaceType* f); + - returns index of the only faux edge of a quad (otherwise, assert) + + int CountBitPolygonInternalValency(const FaceType& f, int wedge) + - returns valency of vertex in terms of polygons (quads, tris...) + + +*/ + +// these should become a parameter in the corresponding class +#define DELETE_VERTICES 1 +// Reason not to delete vertices: +// if not vertex TwoManyfold, the vertex could still be used elsewhere... + +// if one, use length to determine if rotations are profitable +// if zero, maximize conformal quality +#define LENGTH_CRITERION 1 + +namespace vcg{namespace tri{ + +/* simple geometric-interpolation mono-function class used +as a default template parameter to BitQuad class */ +template +class GeometricInterpolator{ +public: + typedef typename VertexType::ScalarType ScalarType; + static void Apply( const VertexType &a, const VertexType &b, ScalarType t, VertexType &res){ + /*assert (&a != &b);*/ + res.P() = a.P()*(1-t) + b.P()*(t); + if (a.IsB()||b.IsB()) res.SetB(); + } +}; + +template < + // first template parameter: the tri mesh (with face-edges flagged) + class _MeshType, + // second template parameter: used to define interpolations between points + class Interpolator = GeometricInterpolator +> +class BitQuad{ +public: + +typedef _MeshType MeshType; +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::CoordType CoordType; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FaceType* FaceTypeP; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::FaceIterator FaceIterator; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::VertexPointer VertexPointer; + +class Pos{ + FaceType *f; + int e; +public: + enum{ PAIR, AROUND , NOTHING } mode; + FaceType* &F(){return f;} + FaceType* F() const {return f;} + VertexType* V() {return f->V(e);} + const VertexType* cV() const {return f->V(e);} + int& E(){return e;} + int E() const {return e;} + + + Pos(){ f=NULL; e=0; mode=AROUND;} + + Pos(FaceType* _f, int _e){f=_f; e=_e;} + Pos NextE()const {return Pos(f, (e+1)%3); } + Pos PrevE(){return Pos(f, (e+2)%3); } + bool IsF(){return f->IsF(e);} + Pos FlipF(){return Pos(f->FFp(e), f->FFi(e)); } + +}; + + + +static void MarkFaceF(FaceType *f){ + f->V(0)->SetS(); + f->V(1)->SetS(); + f->V(2)->SetS(); + int i=FauxIndex(f); + f->FFp( i )->V2( f->FFi(i) )->SetS(); + f->V(0)->SetV(); + f->V(1)->SetV(); + f->V(2)->SetV(); + f->FFp( i )->V2( f->FFi(i) )->SetV(); +} + + +template +static bool RotateEdge(FaceType& f, int w0a, MeshType &m, Pos *affected=NULL){ + FaceType *fa = &f; + assert(! fa->IsF(w0a) ); + + VertexType *v0, *v1; + v0= fa->V0(w0a); + v1= fa->V1(w0a); + + int w1a = (w0a+1)%3; + int w2a = (w0a+2)%3; + + FaceType *fb = fa->FFp(w0a); + + MarkFaceF(fa); + MarkFaceF(fb); + + int w0b = fa->FFi(w0a); + int w1b = (w0b+1)%3; + int w2b = (w0b+2)%3; + + if (fa->IsF(w2a) == verse) { + if (!CheckFlipDiag(*fa)) return false; + FlipDiag(*fa); + // hack: recover edge index, so that (f, w0a) identifies the same edge as before + fa = fb->FFp(w0b); + w0a = fb->FFi(w0b); + } + + if (fb->IsF(w2b) == verse) { + if (!CheckFlipDiag(*fb)) return false; + FlipDiag(*fb); + } + + if (!CheckFlipEdge(*fa,w0a)) return false; + FlipEdge(*fa,w0a,m); + if (affected) { + affected->F() = fa; + affected->E() = (FauxIndex(fa)+2)%3; + affected->mode = Pos::PAIR; + } + return true; +} + +/* small helper function which returns the index of the only + faux index, assuming there is exactly one (asserts out otherwise) +*/ +static int FauxIndex(const FaceType* f){ + if (f->IsF(0)) return 0; + if (f->IsF(1)) return 1; + assert(f->IsF(2)); + return 2; +} + +// rotates the diagonal of a quad +static void FlipDiag(FaceType &f){ + int faux = FauxIndex(&f); + FaceType* fa = &f; + FaceType* fb = f.FFp(faux); + vcg::face::FlipEdge(f, faux); + // ripristinate faux flags + fb->ClearAllF(); + fa->ClearAllF(); + for (int k=0; k<3; k++) { + if (fa->FFp(k) == fb) fa->SetF(k); + if (fb->FFp(k) == fa) fb->SetF(k); + } +} + + +// given a vertex (i.e. a face and a wedge), +// this function tells us how the totale edge lenght around a vertex would change +// if that vertex is rotated +static ScalarType EdgeLenghtVariationIfVertexRotated(const FaceType &f, int w0) +{ + assert(!f.IsD()); + + ScalarType + before=0, // sum of quad edges (originating from v) + after=0; // sum of quad diag (orginating from v) + int guard = 0; + + // rotate arond vertex + const FaceType* pf = &f; + int pi = w0; + int n = 0; // vertex valency + int na = 0; + do { + ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm(); + if (pf->IsF(pi)) { after += triEdge; na++;} + else { before+= triEdge; n++; } + if ( pf->IsF((pi+1)%3)) { after += CounterDiag( pf ).Norm(); na++; } + + const FaceType *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return std::numeric_limits::max(); // it's a mesh border! flee! + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); + assert (na == n); + return (after-before); +} + +// given a vertex (i.e. a face and a wedge), +// this function tells us how the totale edge lenght around a vertex would change +// if that vertex is rotated +static ScalarType QuadQualityVariationIfVertexRotated(const FaceType &f, int w0) +{ + assert(!f.IsD()); + + ScalarType + before=0, // sum of quad quality around v + after=0; // same after the collapse + int guard = 0; + + // rotate arond vertex + const FaceType* pf = &f; + int pi = w0; + int nb = 0; // vertex valency + int na = 0; + std::vector s; // 1 star around v + do { + // ScalarType triEdge = (pf->P0(pi) - pf->P1(pi) ).Norm(); + if (!pf->IsF(pi)) { + if ( pf->IsF((pi+1)%3)) { + s.push_back(pf->cFFp((pi+1)%3)->V2( pf->cFFi((pi+1)%3) )); + } else { + s.push_back( pf->V2(pi) ); + } + + s.push_back( pf->V1(pi) ); + } + + const FaceType *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return std::numeric_limits::max(); // it's a mesh border! flee! + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); + + assert(s.size()%2==0); + int N = s.size(); + for (int i=0; iP(),s[j]->P(),s[k]->P(),f.P(w0) ); + after+=quadQuality( s[h]->P(),s[i]->P(),s[j]->P(),f.P(w0) ); + } + + assert (na == nb); + return (after-before); +} + +/* + const FaceType* pf = &f; + int pi = wedge; + int res = 0, guard=0; + do { + if (!pf->IsAnyF()) return false; // there's a triangle! + if (!pf->IsF(pi)) res++; + const FaceType *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return false; + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); +*/ + +// given a vertex (i.e. a face and a wedge), +// this function tells us if it should be rotated or not +// (currently, we should iff it is shortened) +static bool TestVertexRotation(const FaceType &f, int w0) +{ + assert(!f.IsD()); + +#if (LENGTH_CRITERION) + // rotate vertex IFF this way edges become shorter: + return EdgeLenghtVariationIfVertexRotated(f,w0)<0; +#else + // rotate vertex IFF overall Quality increase +#endif + return QuadQualityVariationIfVertexRotated(f,w0)<0; +} + + +static bool RotateVertex(FaceType &f, int w0, MeshType &m, Pos *affected=NULL) +{ + + int guard = 0; + + FaceType* pf = &f; + int pi = w0; + int n = 0; // vertex valency + + if (pf->IsF((pi+2) % 3)) { + pi = (pi+2)%3; + // do one step back + int tmp = pf->FFi(pi); pf = pf->FFp(pi); pi = tmp; // flipF + } + + const FaceType* stopA = pf; + const FaceType* stopB = pf->FFp(FauxIndex(pf)); + + // rotate around vertex, flipping diagonals if necessary, + do { + bool mustFlip; + if (pf->IsF(pi)) { + // 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 + mustFlip = false; + } + else { + mustFlip = true; + } + + FaceType *lastF = pf; + + int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF + + if (mustFlip) { + if (!CheckFlipDiag(*lastF)) return false; // cannot flip?? + FlipDiag(*lastF); + } + MarkFaceF(pf); + } while (pf != stopA && pf!= stopB); + + // last pass: rotate arund vertex again, changing faux status + stopA=pf; + do { + int j = pi; + if (pf->IsF(j)) + { pf->ClearF(j); IncreaseValency(pf->V1(j)); } + else + { pf->SetF(j); DecreaseValencySimple(pf->V1(j),1); } + + j = (j+2)%3; + if (pf->IsF(j)) pf->ClearF(j); else pf->SetF(j); + int tmp = (pf->FFi(pi)+1)%3; pf = pf->FFp(pi); pi = tmp; // flipF flipV + } while (pf != stopA ); + + if (affected) { + affected->F() = pf; + affected->E()=pi; + } + return true; +} + + + + + +// flips the faux edge of a quad +static void FlipEdge(FaceType &f, int k, MeshType &m){ + assert(!f.IsF(k)); + FaceType* fa = &f; + FaceType* fb = f.FFp(k); + assert(fa!=fb); // else, rotating a border edge + + // backup prev other-quads-halves + FaceType* fa2 = fa->FFp( FauxIndex(fa) ); + FaceType* fb2 = fb->FFp( FauxIndex(fb) ); + + IncreaseValency( fa->V2(k) ); + IncreaseValency( fb->V2(f.FFi(k)) ); + //DecreaseValency( fa->V0(k) ); + //DecreaseValency( fa->V1(k) ); + DecreaseValency(fa, k ,m); + DecreaseValency(fa,(k+1)%3,m ); + + + vcg::face::FlipEdge(*fa, k); + + // ripristinate faux flags + fb->ClearAllF(); + fa->ClearAllF(); + for (int k=0; k<3; k++) { + //if (fa->FFp(k) == fa2) fa->SetF(k); + //if (fb->FFp(k) == fb2) fb->SetF(k); + if (fa->FFp(k)->IsF( fa->FFi(k) )) fa->SetF(k); + if (fb->FFp(k)->IsF( fb->FFi(k) )) fb->SetF(k); + } + +} + +// check if a quad diagonal can be topologically flipped +static bool CheckFlipDiag(FaceType &f){ + return (vcg::face::CheckFlipEdge(f, FauxIndex(&f) ) ); +} + +// given a face (part of a quad), returns its diagonal +static CoordType Diag(const FaceType* f){ + int i = FauxIndex(f); + return f->P1( i ) - f->P0( i ); +} + + +// given a face (part of a quad), returns other diagonal +static CoordType CounterDiag(const FaceType* f){ + int i = FauxIndex(f); + return f->cP2( i ) - f->cFFp( i )->cP2(f->cFFi(i) ) ; +} + +/* helper function: + collapses a single face along its faux edge. + Updates FF adj of other edges. */ +static void _CollapseDiagHalf(FaceType &f, int faux, MeshType& m) +{ + int faux1 = (faux+1)%3; + int faux2 = (faux+2)%3; + + FaceType* fA = f.FFp( faux1 ); + FaceType* fB = f.FFp( faux2 ); + + MarkFaceF(fA); + MarkFaceF(fB); + + int iA = f.FFi( faux1 ); + int iB = f.FFi( faux2 ); + + if (fA==&f && fB==&f) { + // both non-faux edges are borders: tri-face disappears, just remove the vertex + //if (DELETE_VERTICES) + //if (GetValency(f.V(faux2))==0) Allocator::DeleteVertex(m,*(f.V(faux2))); + } else { + if (fA==&f) { + fB->FFp(iB) = fB; fB->FFi(iB) = iB; + } else { + fB->FFp(iB) = fA; fB->FFi(iB) = iA; + } + + if (fB==&f) { + fA->FFp(iA) = fA; fA->FFi(iA) = iA; + } else { + fA->FFp(iA) = fB; fA->FFi(iA) = iB; + } + } + + + //DecreaseValency(&f,faux2,m); // update valency + //Allocator::DeleteFace(m,f); + +} + +static void RemoveDoublet(FaceType &f, int wedge, MeshType& m, Pos* affected=NULL){ + if (f.IsF((wedge+1)%3) ) { + VertexType *v = f.V(wedge); + FlipDiag(f); + // quick hack: recover wedge index after flip + if (f.V(0)==v) wedge = 0; + else if (f.V(1)==v) wedge = 1; + else { + assert(f.V(2)==v); + wedge = 2; + } + } + ScalarType k=(f.IsF(wedge))?1:0; + CollapseDiag(f, k, m, affected); + VertexType *v = f.V(wedge); +} + +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 (f.V(wedge)->IsB()) return; // hack: lets detect + + FaceType *fa, *fb; // these will die + FaceType *fc, *fd; // their former neight + fa = & f; + fb = fa->FFp(wedge); + int wa0 = wedge; + int wa1 = (wa0+1)%3 ; + int wa2 = (wa0+2)%3 ; + int wb0 = (fa->FFi(wa0)+1)%3; + int wb1 = (wb0+1)%3 ; + int wb2 = (wb0+2)%3 ; + assert (fb == fa->FFp( wa2 ) ); // otherwise, not a singlet + + // valency decrease + DecreaseValency(fa, wa1, m); + DecreaseValency(fa, wa2, m); + if (fa->IsF(wa0)) { + DecreaseValency(fa,wa2,m); // double decrease of valency on wa2 + } else { + DecreaseValency(fa,wa1,m); // double decrease of valency on wa1 + } + + // no need to MarkFaceF ! + + fc = fa->FFp(wa1); + fd = fb->FFp(wb1); + int wc = fa->FFi(wa1); + int wd = fb->FFi(wb1); + fc->FFp(wc) = fd; + fc->FFi(wc) = wd; + fd->FFp(wd) = fc; + fd->FFi(wd) = wc; + // faux status of survivors: unchanged + assert( ! ( fc->IsF( wc) ) ); + assert( ! ( fd->IsF( wd) ) ); + + Allocator::DeleteFace( m,*fa ); + Allocator::DeleteFace( m,*fb ); + + DecreaseValency(fa,wedge,m ); + //if (DELETE_VERTICES) + //if (GetValency(fa->V(wedge))==0) Allocator::DeleteVertex( m,*fa->V(wedge) ); +} + + +static bool TestAndRemoveDoublet(FaceType &f, int wedge, MeshType& m){ + if (IsDoublet(f,wedge)) { + RemoveDoublet(f,wedge,m); + return true; + } + return false; +} + +static bool TestAndRemoveSinglet(FaceType &f, int wedge, MeshType& m){ + if (IsSinglet(f,wedge)) { + RemoveSinglet(f,wedge,m); + return true; + } + return false; +} + +// given a face and a wedge, counts its valency in terms of quads (and triangles) +// uses only FF, assumes twomanyfold +// returns -1 if border +static int CountBitPolygonInternalValency(const FaceType& f, int wedge){ + const FaceType* pf = &f; + int pi = wedge; + int res = 0; + do { + if (!pf->IsF(pi)) res++; + const FaceType *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return -1; + pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); + pf = t; + } while (pf != &f); + return res; +} + +// given a face and a wedge, returns if it host a doubet +// assumes tri and quad only. uses FF topology only. +static bool IsDoubletFF(const FaceType& f, int wedge){ + const FaceType* pf = &f; + int pi = wedge; + int res = 0, guard=0; + do { + if (!pf->IsAnyF()) return false; // there's a triangle! + if (!pf->IsF(pi)) res++; + const FaceType *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return false; + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); + return (res == 2); +} + +// version that uses vertex valency +static bool IsDoublet(const FaceType& f, int wedge){ + return (GetValency( f.V(wedge)) == 2) && (!f.V(wedge)->IsB() ) ; +} + +static bool IsDoubletOrSinglet(const FaceType& f, int wedge){ + return (GetValency( f.V(wedge)) <= 2) && (!f.V(wedge)->IsB() ) ; +} + +static bool RemoveDoubletOrSinglet(FaceType& f, int wedge, MeshType& m, Pos* affected=NULL){ + if (GetValency( f.V(wedge)) == 2) { RemoveDoublet(f,wedge,m,affected) ; return true; } + assert (GetValency( f.V(wedge)) == 1) ; + RemoveSinglet(f,wedge,m,affected) ; + return true; +} + +// given a face and a wedge, returns if it host a singlets +// assumes tri and quad only. uses FF topology only. +static bool IsSingletFF(const FaceType& f, int wedge){ + const FaceType* pf = &f; + int pi = wedge; + int res = 0, guard=0; + do { + if (!pf->IsAnyF()) return false; // there's a triangle! + if (!pf->IsF(pi)) res++; + const FaceType *t = pf; + t = pf->FFp( pi ); + if (pf == t ) return false; + pi = pf->cFFi( pi ); + pi = (pi+1)%3; // FaceType::Next( pf->FFi( pi ) ); + pf = t; + assert(guard++<100); + } while (pf != &f); + return (res == 1); +} + +// version that uses vertex valency +static bool IsSinglet(const FaceType& f, int wedge){ + return (GetValency( f.V(wedge) ) == 1) && (!f.V(wedge)->IsB() ) ; +} + +static bool CollapseEdgeDirect(FaceType &f, int w0, MeshType& m){ + FaceType * f0 = &f; + + assert( !f0->IsF(w0) ); + + VertexType *v0, *v1; + v0 = f0->V0(w0); + v1 = f0->V1(w0); + + if (!RotateVertex(*f0,w0,m)) return false; + + // quick hack: recover original wedge + if (f0->V(0) == v0) w0 = 0; + else if (f0->V(1) == v0) w0 = 1; + else if (f0->V(2) == v0) w0 = 2; + else assert(0); + + assert( f0->V1(w0) == v1 ); + assert( f0->IsF(w0) ); + + return CollapseDiag(*f0,PosOnDiag(*f0,false), m); +} + +// collapses an edge. Optional output pos can be iterated around to find affected faces +static bool CollapseEdge(FaceType &f, int w0, MeshType& m, Pos *affected=NULL){ + FaceTypeP f0 = &f; + 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+1)%3)) return false; //{ RemoveDoubletOrSinglet(f,(w0+1)%3,m, affected); return true;} + + if (affected) { + int w1 = 3-w0-FauxIndex(f0); // the edge whihc is not the collapsed one nor the faux + affected->F() = f0->FFp(w1); + affected->E() = (f0->FFi(w1)+2+w1-FauxIndex(f0))%3; + } + + FaceTypeP f1 = f0->FFp(w0); + int w1 = f0->FFi(w0); + + assert(f0!=f1); // can't collapse border edges! + + // choose: rotate around V0 or around V1? + if ( + EdgeLenghtVariationIfVertexRotated(*f0,w0) + < + EdgeLenghtVariationIfVertexRotated(*f1,w1) + ) return CollapseEdgeDirect(*f0,w0,m); + else return CollapseEdgeDirect(*f1,w1,m); +} + + + +/** collapses a quad diagonal a-b + forming the new vertex in between the two old vertices. + if k == 0, new vertex is in a + if k == 1, new vertex is in b + if k == 0.5, new vertex in the middle, etc +*/ +static bool CollapseCounterDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){ + if (!CheckFlipDiag(f)) return false; + FlipDiag(f); + return CollapseDiag(f,interpol,m,affected); +} + +// rotates around vertex +class Iterator{ +private: + typedef typename face::Pos FPos; + Pos start, cur; + bool over; +public: + Iterator(Pos& pos){ + if (pos.mode==Pos::NOTHING) {over = true; return; } + start = pos; //FPos(pos.F(), pos.E()); + if (start.F()->IsD()) { over = true; return;} + assert(!start.F()->IsD()); + if (pos.mode==Pos::AROUND) { + if (start.F()->IsF((start.E()+2)%3)) + { + int i = start.F()->FFi( start.E() ); + start.F() = start.F()->FFp( start.E() ); + start.E() = (i+1)%3; + } + } + cur=start; + over = false; + } + bool End() const { + return over; + } + void operator ++ () { + if (start.mode==Pos::PAIR) { + if (cur.F()!=start.F()) over=true; + int i = (cur.E()+2)%3; + cur.E() = (cur.F()->FFi( i )+1)%3; + cur.F() = cur.F()->FFp( i ); + } else { + if (cur.F()->IsF(cur.E())) { + // jump over faux diag + int i = cur.F()->FFi( cur.E() ); + cur.F() = cur.F()->FFp( cur.E() ); + cur.E() = (i+1)%3; + } + // jump over real edge + FaceType *f =cur.F()->FFp( cur.E() ); + if (f==cur.F()) over=true; // border found + cur.E() = (cur.F()->FFi( cur.E() ) +1 )%3; + cur.F() = f; + if (cur.F()==start.F()) over=true; + } + } + + Pos GetPos(){ + return cur; + } +}; + +static bool CollapseDiag(FaceType &f, ScalarType interpol, MeshType& m, Pos* affected=NULL){ + + FaceType* fa = &f; // fa lives + int fauxa = FauxIndex(fa); + + //if (IsDoubletOrSinglet(f,fauxa)) { RemoveDoubletOrSinglet(f,fauxa,m, affected); return true;} +// if (IsDoubletOrSinglet(f,(fauxa+2)%3)) { RemoveDoubletOrSinglet(f,(fauxa+2)%3,m, affected); return true;} + if (IsDoubletOrSinglet(f,(fauxa+2)%3)) return false; + if (IsDoubletOrSinglet(*(f.FFp(fauxa)),(f.FFi(fauxa)+2)%3)) return false; + + if (affected) { + int w1 = (fauxa+2)%3; // any edge but not the faux + affected->F() = fa->FFp(w1); + affected->E() = fa->FFi(w1); + if (affected->F() == fa){ + int w1 = (fauxa+1)%3; // any edge but not the faux + affected->F() = fa->FFp(w1); + affected->E() = (fa->FFi(w1)+2)%3; + } + } + + FaceType* fb = fa->FFp(fauxa); // fb dies + assert (fb!=fa); // otherwise, its a singlet + int fauxb = FauxIndex(fb); + + VertexType* va = fa->V(fauxa); // va lives + VertexType* vb = fb->V(fauxb); // vb dies + + Interpolator::Apply( *(f.V0(fauxa)), *(f.V1(fauxa)), interpol, *va); + + bool border = false; + int val =0; // number of faces around vb, which dies + + // update FV... + + // rotate around vb, (same-sense-as-face)-wise + int pi = fauxb; + FaceType* pf = fb; /* pf, pi could be put in a Pos p(pb, fauxb) */ + do { + //pf->V(pi) = va; + if (((pf->V2(pi) == va)||(pf->V1(pi) == va)) + &&(pf!=fa)&&(pf!=fb)) + return false; + pi=(pi+2)%3; + FaceType *t = pf->FFp(pi); + if (t==pf) { border= true; break; } + pi = pf->FFi(pi); + pf = t; + } while ((pf!=fb)); + + pi = fauxb; + pf = fb; + + do { + pf->V(pi) = va; + + pi=(pi+2)%3; + FaceType *t = pf->FFp(pi); + if (t==pf) { border= true; break; } + if (!pf->IsF(pi)) val++; + pi = pf->FFi(pi); + pf = t; + } while (pf!=fb); + + // of found a border, also rotate around vb, (counter-sense-as-face)-wise + if (border) { + val++; + int pi = fauxa; + FaceType* pf = fa; /* pf, pi could be a Pos p(pf, pi) */ + do { + pi=(pi+1)%3; + pf->V(pi) = va; + FaceType *t = pf->FFp(pi); + if (t==pf) break; + if (!pf->IsF(pi)) val++; + pi = pf->FFi(pi); + pf = t; + } while (pf!=fb); + } + + // update FF, delete faces + _CollapseDiagHalf(*fb, fauxb, m); + _CollapseDiagHalf(*fa, fauxa, m); + + SetValency(va, GetValency(va)+val-2); + DecreaseValency(fb,(fauxb+2)%3,m); // update valency + DecreaseValency(fa,(fauxa+2)%3,m); // update valency + Allocator::DeleteFace(m,*fa); + Allocator::DeleteFace(m,*fb); + + //assert(val == GetValency(vb)); + + + DecreaseValencyNoSingletTest(vb, val, m); + // note: don't directly kill vb. In non-twomanifold, it could still be referecned + // but: don't hunt for doublets either. + + assert(GetValency(vb)!=1 || vb->IsB()); + // if this asserts, you are in trouble. + // It means that the vertex that was supposed to die is still attached + // somewhere else (non-twomanifold) + // BUT in its other attachments it is a singlet, and that singlet cannot be + // found now (would require VF) + + + return true; +} + + + + +// helper function: find a good position on a diag to collapse a point +// currently, it is point in the middle, +// unless a mixed border-non border edge is collapsed, then it is an exreme +static ScalarType PosOnDiag(const FaceType& f, bool counterDiag){ + bool b0, b1, b2, b3; // which side of the quads are border + + const FaceType* fa=&f; + int ia = FauxIndex(fa); + const FaceType* fb=fa->cFFp(ia); + int ib = fa->cFFi(ia); + + b0 = fa->FFp((ia+1)%3) == fa; + b1 = fa->FFp((ia+2)%3) == fa; + b2 = fb->FFp((ib+1)%3) == fb; + b3 = fb->FFp((ib+2)%3) == fb; + + if (counterDiag) { + if ( (b0||b1) && !(b2||b3) ) return 1; + if ( !(b0||b1) && (b2||b3) ) return 0; + } else { + if ( (b1||b2) && !(b3||b0) ) return 0; + if ( !(b1||b2) && (b3||b0) ) return 1; + } + //if (f->FF( FauxIndex(f) )->IsB( + return 0.5f; +} + +// trick! hide valency in flags +typedef enum { VALENCY_FLAGS = 24 } ___; // this bit and the 4 successive one are devoted to store valency + +static void SetValency(VertexType *v, int n){ + //v->Q() = n; + assert(n>=0 && n<=255); + v->Flags()&= ~(255<Flags()|= n<cQ()); + return ( v->Flags() >> (VALENCY_FLAGS) ) & 255; +} + +static void IncreaseValency(VertexType *v, int dv=1){ +#ifdef NDEBUG + v->Flags() += dv<Flags() -= dv<V(wedge); + int val = GetValency(v)-1; + SetValency( v, val ); + if (val==0) Allocator::DeleteVertex(m,*v); + if (val==1) // singlet! + RemoveSinglet(*f,wedge,m); // this could be recursive... +} + +// decrease valency, remove unreferenced vertices too, but don't check for singlets... +static void DecreaseValencyNoSingletTest(VertexType *v, int dv, MeshType &m){ + int val = GetValency(v)-dv; + SetValency( v, val ); + if (DELETE_VERTICES) + if (val==0) Allocator::DeleteVertex(m,*v); +} + +static void DecreaseValencySimple(VertexType *v, int dv){ + int val = GetValency(v)-dv; + SetValency( v, val ); +} + +static void UpdateValencyInFlags(MeshType& m){ + for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) { + SetValency(&*vi,0); + } + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + for (int w=0; w<3; w++) + if (!fi->IsF(w)) + IncreaseValency( fi->V(w)); + } +} + +static void UpdateValencyInQuality(MeshType& m){ + for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) { + vi->Q() = 0; + } + + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + for (int w=0; w<3; w++) + fi->V(w)->Q() += (fi->IsF(w)||fi->IsF((w+2)%3) )? 0.5f:1; + } +} + +static bool HasConsistentValencyFlag(MeshType &m) { + UpdateValencyInQuality(m); + bool isok=true; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + for (int k=0; k<3; k++) + if (GetValency(fi->V(k))!=fi->V(k)->Q()){ + MarkFaceF(&*fi); + isok=false; + } + } + return isok; +} + +// helper function: +// returns quality of a given (potential) quad +static ScalarType quadQuality(FaceType *f, int edge){ + + CoordType + a = f->V0(edge)->P(), + b = f->FFp(edge)->V2( f->FFi(edge) )->P(), + c = f->V1(edge)->P(), + d = f->V2(edge)->P(); + + return quadQuality(a,b,c,d); + +} + +/** +helper function: +given a quad edge, retruns: + 0 if that edge should not be rotated + +1 if it should be rotated clockwise (+1) + -1 if it should be rotated counterclockwise (-1) +Currently an edge is rotated iff it is shortened by that rotations +(shortcut criterion) +*/ +static int TestEdgeRotation(const FaceType &f, int w0, ScalarType *gain=NULL) +{ + const FaceType *fa = &f; + assert(! fa->IsF(w0) ); + ScalarType q0,q1,q2; + CoordType v0,v1,v2,v3,v4,v5; + int w1 = (w0+1)%3; + int w2 = (w0+2)%3; + + v0 = fa->P(w0); + v3 = fa->P(w1); + + if (fa->IsF(w2) ) { + v1 = fa->cFFp(w2)->V2( fa->cFFi(w2) )->P(); + v2 = fa->P(w2); + } else { + v1 = fa->P(w2); + v2 = fa->cFFp(w1)->V2( fa->cFFi(w1) )->P(); + } + + const FaceType *fb = fa->cFFp(w0); + w0 = fa->cFFi(w0); + + w1 = (w0+1)%3; + w2 = (w0+2)%3; + if (fb->IsF(w2) ) { + v4 = fb->cFFp(w2)->V2( fb->cFFi(w2) )->P(); + v5 = fb->P(w2); + } else { + v4 = fb->P(w2); + v5 = fb->cFFp(w1)->V2( fb->cFFi(w1) )->P(); + } + + +#if (!LENGTH_CRITERION) + // max overall CONFORMAL quality criterion: + q0 = quadQuality(v0,v1,v2,v3) + quadQuality(v3,v4,v5,v0); // keep as is? + q1 = quadQuality(v1,v2,v3,v4) + quadQuality(v4,v5,v0,v1); // rotate CW? + q2 = quadQuality(v5,v0,v1,v2) + quadQuality(v2,v3,v4,v5); // rotate CCW? + + if (q0>=q1 && q0>=q2) return 0; + if (q1>=q2) return 1; + +#else + // min distance (shortcut criterion) + q0 = (v0 - v3).SquaredNorm(); + q1 = (v1 - v4).SquaredNorm(); + q2 = (v5 - v2).SquaredNorm(); + + if (q0<=q1 && q0<=q2) return 0; // there's no rotation shortening this edge + + //static int stop=0; + //static int go=0; + //if ((stop+go)%100==99) printf("Stop: %4.1f%%\n",(stop*100.0/(stop+go)) ); + + if (q1<=q2) { + if (gain) *gain = sqrt(q1)-sqrt(q0); + // test: two diagonals should become shorter (the other two reamin the same) + if ( + (v0-v2).SquaredNorm() < (v4-v2).SquaredNorm() || + (v3-v5).SquaredNorm() < (v1-v5).SquaredNorm() + ) { + //stop++; + return 0; + } + //go++; + return 1; + } + + { + if (gain) *gain = sqrt(q2)-sqrt(q0); + // diagonal test, as above: + if ( + (v0-v4).SquaredNorm() < (v2-v4).SquaredNorm() || + (v3-v1).SquaredNorm() < (v5-v1).SquaredNorm() + ) { + //stop++; + return 0; + } + //go++; + return -1; + } +#endif +} + +private: + +// helper function: +// returns quality of a quad formed by points a,b,c,d +// quality is computed as "how squared angles are" +static ScalarType quadQuality(const CoordType &a, const CoordType &b, const CoordType &c, const CoordType &d){ + ScalarType score = 0; + score += 1 - math::Abs( Cos( a,b,c) ); + score += 1 - math::Abs( Cos( b,c,d) ); + score += 1 - math::Abs( Cos( c,d,a) ); + score += 1 - math::Abs( Cos( d,a,b) ); + return score / 4; +} + + + + +private: + +// helper function: +// cos of angle abc. This should probably go elsewhere +static ScalarType Cos(const CoordType &a, const CoordType &b, const CoordType &c ) +{ + CoordType + e0 = b - a, + e1 = b - c; + ScalarType d = (e0.Norm()*e1.Norm()); + if (d==0) return 0.0; + return (e0*e1)/d; +} +public: +/** + Generic quad triangulation function. + It take in input 4 vertex pointrs and rotate them so that a simple fan triangulation is Ok. + It uses geometric criteria for avoiding bad shaped triangles, and folds + and it use an internal set of already created diagonal to avoid the creation of non manifold situations. + At the begin you shoud call this function with an empty vector to reset the set of existing diagonals. + */ +static void QuadTriangulate(std::vector &q) +{ + typedef typename std::set > diagSetType; + static diagSetType diagSet; // the set of already created diagonals + if(q.size()!=4) + { + diagSet.clear(); + return; + } + const CoordType &P0=q[0]->cP(); + const CoordType &P1=q[1]->cP(); + const CoordType &P2=q[2]->cP(); + const CoordType &P3=q[3]->cP(); + + CoordType N00 = Normal(P0,P1,P2); + CoordType N01 = Normal(P0,P2,P3); + CoordType N10 = Normal(P1,P2,P3); + CoordType N11 = Normal(P1,P3,P0); + + ScalarType Angle0Rad=Angle(N00,N01); + ScalarType Angle1Rad=Angle(N10,N11); + + // QualityRadii is inradius/circumradius; bad when close to zero. + // swap diagonal if the worst triangle improve. + bool qualityImprove = std::min(QualityRadii(P0,P1,P2),QualityRadii(P0,P2,P3)) < std::min(QualityRadii(P1,P2,P3),QualityRadii(P1,P3,P0)); + bool swapCauseFlip = (Angle1Rad > M_PI/2.0) && (Angle0Rad res; + if(q[0] +#include +#include +#include +namespace vcg { + +/** Class Boundary. +This is class for exporting the boundary of a d simplicial complex as a d-1 simplicial complex +*/ +class Boundary{ +public: + +///this function build a triangle mesh using the same pointers to the tetrahedral mesh vertex +template +static void OfTetramesh(TetraContainer &tetra,TriangleMeshType &trim) +{ +typedef typename TetraContainer::iterator TetraIterator; +typedef typename TetraContainer::value_type TetraVertexType; +typedef typename TriangleMeshType::FaceType FaceType; +typedef typename TriangleMeshType::VertexType TriangleVertexType; + +TetraIterator ti; +TetraVertexType *v0; +TetraVertexType *v2; + +trim.Clear(); +for (ti=tetra.begin();tiIsD())) + { + if ((ti->IsBorderF(0))||(ti->IsBorderF(1))||(ti->IsBorderF(2))||(ti->IsBorderF(3))) + for (int i=0;i<4;i++) + if (ti->IsBorderF(i)) + { + FaceType f=FaceType(); + f.ClearFlags(); + f.V(0)=(TriangleVertexType*)ti->V(Tetra::VofF(i,0)); + f.V(1)=(TriangleVertexType*)ti->V(Tetra::VofF(i,1)); + f.V(2)=(TriangleVertexType*)ti->V(Tetra::VofF(i,2)); + trim.face.push_back(f); + } + } + } +} + +template +struct InsertedV{ + +typedef typename TriVertexType::FaceType FaceType; +InsertedV( TriVertexType *_v, FaceType* _f,int _z):v(_v),f(_f),z(_z){} + +TriVertexType *v; +FaceType* f; +int z; + +const bool operator <(const InsertedV & o){ +return (v +static void OfTetrameshCopy(TetraContainer &tetra,TriangleMeshType &trim) +{ + typedef typename TetraContainer::iterator TetraIterator; + typedef typename TetraContainer::value_type::VertexType TetraVertexType; + typedef typename TriangleMeshType::FaceType FaceType; + typedef typename TriangleMeshType::FaceIterator FaceIterator; + typedef typename TriangleMeshType::VertexIterator TriVertexIterator; + typedef typename TriangleMeshType::VertexType TriVertexType; + + vector > newVertices; + typename vector >::iterator curr,next; + TriVertexIterator vi; + vector redirect; + + OfTetramesh(tetra,trim); + + FaceIterator fi; + + for(fi = trim.face.begin(); fi != trim.face.end(); ++fi){ + newVertices.push_back(InsertedV( (*fi).V(0),&(*fi),0)); + newVertices.push_back(InsertedV( (*fi).V(1),&(*fi),1)); + newVertices.push_back(InsertedV( (*fi).V(2),&(*fi),2)); + } + +sort(newVertices.begin(),newVertices.end()); + + +int pos = 0; +curr = next = newVertices.begin(); +while( next != newVertices.end()){ + if((*curr)!=(*next)) + pos++; + (*next).f->V( (*next).z) = (TriVertexType*)pos; + curr = next; + next++; +} + +typename vector >::iterator newE = unique(newVertices.begin(),newVertices.end()); +for(curr = newVertices.begin();curr!= newE;++curr) + trim.vert.push_back(*((*curr).v)); + +for(vi = trim.vert.begin(); vi != trim.vert.end(); ++vi) + redirect.push_back(&(*vi)); + +for(fi = trim.face.begin(); fi != trim.face.end(); ++fi){ + (*fi).V(0) = redirect[(int)(*fi).V(0)]; + (*fi).V(1) = redirect[(int)(*fi).V(1)]; + (*fi).V(2) = redirect[(int)(*fi).V(2)]; + } +trim.vn = trim.vert.size(); +trim.fn = trim.face.size(); +} + +};// End class +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/clean.h b/vcg/complex/algorithms/clean.h new file mode 100644 index 00000000..7a3dce83 --- /dev/null +++ b/vcg/complex/algorithms/clean.h @@ -0,0 +1,1529 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCGLIB_CLEAN +#define __VCGLIB_CLEAN + +// Standard headers +#include +#include +#include + +// VCG headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace vcg { + namespace tri{ +template +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; + + +public: + void operator ++() + { + FacePointer fpt=sf.top(); + sf.pop(); + for(int j=0;j<3;++j) + if( !face::IsBorder(*fpt,j) ) + { + FacePointer l=fpt->FFp(j); + if( !tri::IsMarked(*mp,l) ) + { + tri::Mark(*mp,l); + sf.push(l); + } + } +} + + void start(MeshType &m, FacePointer p) + { + mp=&m; + while(!sf.empty()) sf.pop(); + UnMarkAll(m); + assert(p); + assert(!p->IsD()); + tri::Mark(m,p); + sf.push(p); + } + bool completed() { + return sf.empty(); + } + + FacePointer operator *() + { + return sf.top(); + } +private: + std::stack sf; + MeshType *mp; +}; + + + /// + /** \addtogroup trimesh */ + /*@{*/ + /// Class of static functions to clean/correct/restore meshs. + template + class Clean + { + + public: + typedef CleanMeshType MeshType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::ConstVertexIterator ConstVertexIterator; + 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; + typedef typename vcg::Box3 Box3Type; + + typedef GridStaticPtr TriMeshGrid; + typedef Point3 Point3x; + + //TriMeshGrid gM; + //FaceIterator fi; + //FaceIterator gi; + //vcg::face::Pos he; + //vcg::face::Pos hei; + + /* 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. + Note that it does not update any topology relation that could be affected by this like the VT or TT relation. + the reason this function is usually performed BEFORE building any topology information. + */ + static int RemoveDuplicateVertex( MeshType & m, bool RemoveDegenerateFlag=true) // V1.0 + { + if(m.vert.size()==0 || m.vn==0) return 0; + + std::map mp; + size_t i,j; + VertexIterator vi; + int deleted=0; + int k=0; + size_t num_vert = m.vert.size(); + std::vector perm(num_vert); + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi, ++k) + perm[k] = &(*vi); + + RemoveDuplicateVert_Compare c_obj; + + std::sort(perm.begin(),perm.end(),c_obj); + + j = 0; + i = j; + mp[perm[i]] = perm[j]; + ++i; + for(;i!=num_vert;) + { + if( (! (*perm[i]).IsD()) && + (! (*perm[j]).IsD()) && + (*perm[i]).P() == (*perm[j]).cP() ) + { + VertexPointer t = perm[i]; + mp[perm[i]] = perm[j]; + ++i; + Allocator::DeleteVertex(m,*t); + deleted++; + } + else + { + j = i; + ++i; + } + } + FaceIterator fi; + for(fi = m.face.begin(); fi!=m.face.end(); ++fi) + if( !(*fi).IsD() ) + for(k = 0; k < 3; ++k) + if( mp.find( (typename MeshType::VertexPointer)(*fi).V(k) ) != mp.end() ) + { + (*fi).V(k) = &*mp[ (*fi).V(k) ]; + } + + if(RemoveDegenerateFlag) RemoveDegenerateFace(m); + return deleted; + } + + class SortedTriple + { + public: + SortedTriple() {} + SortedTriple(unsigned int v0, unsigned int v1, unsigned int v2,FacePointer _fp) + { + v[0]=v0;v[1]=v1;v[2]=v2; + fp=_fp; + std::sort(v,v+3); + } + bool operator < (const SortedTriple &p) const + { + return (v[2]!=p.v[2])?(v[2] fvec; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + fvec.push_back(SortedTriple( tri::Index(m,(*fi).V(0)), + tri::Index(m,(*fi).V(1)), + tri::Index(m,(*fi).V(2)), + &*fi)); + } + assert (size_t(m.fn) == fvec.size()); + //for(int i=0;i::DeleteFace(m, *(fvec[i].fp) ); + //qDebug("deleting face %i (pos in fvec %i)",tri::Index(m,fvec[i].fp) ,i); + } + } + return total; + } + /** This function removes that are not referenced by any face. The function updates the vn counter. + @param m The mesh + @return The number of removed vertices + */ + static int RemoveUnreferencedVertex( MeshType& m, bool DeleteVertexFlag=true) // V1.0 + { + FaceIterator fi; + VertexIterator vi; + int referredBit = VertexType::NewBitFlag(); + + int j; + int deleted = 0; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + (*vi).ClearUserBit(referredBit); + + for(fi=m.face.begin();fi!=m.face.end();++fi) + if( !(*fi).IsD() ) + for(j=0;j<3;++j) + (*fi).V(j)->SetUserBit(referredBit); + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if( (!(*vi).IsD()) && (!(*vi).IsUserBit(referredBit))) + { + if(DeleteVertexFlag) Allocator::DeleteVertex(m,*vi); + ++deleted; + } + VertexType::DeleteBitFlag(referredBit); + return deleted; + } + + /** + Degenerate vertices are vertices that have coords with invalid floating point values, + All the faces incident on deleted vertices are also deleted + */ + static int RemoveDegenerateVertex(MeshType& m) + { + VertexIterator vi; + int count_vd = 0; + + for(vi=m.vert.begin(); vi!=m.vert.end();++vi) + if(math::IsNAN( (*vi).P()[0]) || + math::IsNAN( (*vi).P()[1]) || + math::IsNAN( (*vi).P()[2]) ) + { + count_vd++; + Allocator::DeleteVertex(m,*vi); + } + + FaceIterator fi; + int count_fd = 0; + + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) + if( (*fi).V(0)->IsD() || + (*fi).V(1)->IsD() || + (*fi).V(2)->IsD() ) + { + count_fd++; + Allocator::DeleteFace(m,*fi); + } + return count_vd; + } + + /** + Degenerate faces are faces that are Topologically degenerate, + i.e. have two or more vertex reference that link the same vertex + (and not only two vertexes with the same coordinates). + All Degenerate faces are zero area faces BUT not all zero area faces are degenerate. + We do not take care of topology because when we have degenerate faces the + topology calculation functions crash. + */ + static int RemoveDegenerateFace(MeshType& m) + { + FaceIterator fi; + int count_fd = 0; + + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + if((*fi).V(0) == (*fi).V(1) || + (*fi).V(0) == (*fi).V(2) || + (*fi).V(1) == (*fi).V(2) ) + { + count_fd++; + Allocator::DeleteFace(m,*fi); + } + } + return count_fd; + } + + static int RemoveNonManifoldVertex(MeshType& m) + { + /*int count_vd = */ + CountNonManifoldVertexFF(m,true); + /*int count_fd = */ + tri::UpdateSelection::FaceFromVertexLoose(m); + int count_removed = 0; + FaceIterator fi; + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS()) + Allocator::DeleteFace(m,*fi); + VertexIterator vi; + for(vi=m.vert.begin(); vi!=m.vert.end();++vi) + if(!(*vi).IsD() && (*vi).IsS()) { + ++count_removed; + Allocator::DeleteVertex(m,*vi); + } + return count_removed; + } + + + /// Removal of faces that were incident on a non manifold edge. + static int SplitNonManifoldVertex(MeshType& m) + { + FaceIterator fi; + int count_sv = 0; // split vertex counter + typedef std::pair FaceInt; + + std::vector > >ToSplitVec; + + SelectionStack ss(m); + ss.push(); + CountNonManifoldVertexFF(m,true); + UpdateFlags::VertexClearV(m); + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;i++) + if((*fi).V(i)->IsS() && !(*fi).V(i)->IsV()) + { + (*fi).V(i)->SetV(); + face::Pos startPos(&*fi,i); + face::Pos curPos = startPos; + std::set faceSet; + do + { + faceSet.insert(make_pair(curPos.F(),curPos.VInd())); + curPos.NextE(); + } while (curPos != startPos); + + ToSplitVec.push_back(make_pair((*fi).V(i),std::vector())); + + typename std::set::const_iterator iii; + + for(iii=faceSet.begin();iii!=faceSet.end();++iii) + ToSplitVec.back().second.push_back(*iii); + } + } + ss.pop(); + // Second step actually add new vertices and split them. + typename tri::Allocator::template PointerUpdater pu; + VertexIterator firstVp = tri::Allocator::AddVertices(m,ToSplitVec.size(),pu); + for(int i =0;iImportData(*np); + for(int j=0;jV(ff.second)=&*firstVp; + } + firstVp++; + } + + return ToSplitVec.size(); + } + + + // Auxiliary function for sorting the non manifold faces according to their area. Used in RemoveNonManifoldFace + struct CompareAreaFP { + bool operator ()(FacePointer const& f1, FacePointer const& f2) const { + return DoubleArea(*f1) < DoubleArea(*f2); + } + }; + + /// Removal of faces that were incident on a non manifold edge. + static int RemoveNonManifoldFace(MeshType& m) + { + FaceIterator fi; + int count_fd = 0; + std::vector ToDelVec; + + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if (!fi->IsD()) + { + if ((!IsManifold(*fi,0))|| + (!IsManifold(*fi,1))|| + (!IsManifold(*fi,2))) + ToDelVec.push_back(&*fi); + } + + std::sort(ToDelVec.begin(),ToDelVec.end(),CompareAreaFP()); + + for(size_t i=0;iIsD()) + { + FaceType &ff= *ToDelVec[i]; + if ((!IsManifold(ff,0))|| + (!IsManifold(ff,1))|| + (!IsManifold(ff,2))) + { + for(int j=0;j<3;++j) + if(!face::IsBorder(ff,j)) + vcg::face::FFDetach(ff,j); + + Allocator::DeleteFace(m,ff); + count_fd++; + } + } + } + return count_fd; + } + + /* + The following functions remove faces that are geometrically "bad" according to edges and area criteria. + They remove the faces that are out of a given range of area or edges (e.g. faces too large or too small, or with edges too short or too long) + but that could be topologically correct. + These functions can optionally take into account only the selected faces. + */ + template + static int RemoveFaceOutOfRangeAreaSel(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) + { + FaceIterator fi; + int count_fd = 0; + MinAreaThr*=2; + MaxAreaThr*=2; + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) + if(!Selected || (*fi).IsS()) + { + const ScalarType doubleArea=DoubleArea(*fi); + if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) ) + { + Allocator::DeleteFace(m,*fi); + count_fd++; + } + } + return count_fd; + } + + // alias for the old style. Kept for backward compatibility + static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m);} + + // Aliases for the functions that do not look at selection + static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) + { + return RemoveFaceOutOfRangeAreaSel(m,MinAreaThr,MaxAreaThr); + } + + /** + * Is the mesh only composed by quadrilaterals? + */ + static bool IsBitQuadOnly(const MeshType &m) + { + typedef typename MeshType::FaceType F; + if (!m.HasPerFaceFlags()) return false; + 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); + if ( tmp != F::FAUX0 && tmp != F::FAUX1 && tmp != F::FAUX2) return false; + } + return true; + } + + + /** + * Is the mesh only composed by triangles? (non polygonal faces) + */ + static bool IsBitTriOnly(const MeshType &m) + { + if (!m.HasPerFaceFlags()) return true; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) { + if ( + !fi->IsD() && fi->IsAnyF() + ) return false; + } + return true; + } + + static bool IsBitPolygonal(const MeshType &m){ + return !IsBitTriOnly(m); + } + + /** + * Is the mesh only composed by quadrilaterals and triangles? (no pentas, etc) + */ + static bool IsBitTriQuadOnly(const MeshType &m) + { + typedef typename MeshType::FaceType F; + if (!m.HasPerFaceFlags()) return false; + 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); + if ( tmp!=F::FAUX0 && tmp!=F::FAUX1 && tmp!=F::FAUX2 && tmp!=0 ) return false; + } + return true; + } + + /** + * How many quadrilaterals? + */ + static int CountBitQuads(const MeshType &m) + { + if (!m.HasPerFaceFlags()) return 0; + typedef typename MeshType::FaceType F; + int count=0; + 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); + if ( tmp==F::FAUX0 || tmp==F::FAUX1 || tmp==F::FAUX2) count++; + } + return count / 2; + } + + /** + * How many triangles? (non polygonal faces) + */ + static int CountBitTris(const MeshType &m) + { + if (!m.HasPerFaceFlags()) return m.fn; + int count=0; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + if (!(fi->IsAnyF())) count++; + } + return count; + } + + /** + * How many polygons of any kind? (including triangles) + */ + static int CountBitPolygons(const MeshType &m) + { + if (!m.HasPerFaceFlags()) return m.fn; + typedef typename MeshType::FaceType F; + int count = 0; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + if (fi->IsF(0)) count++; + if (fi->IsF(1)) count++; + if (fi->IsF(2)) count++; + } + return m.fn - count/2; + } + + /** + * 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.) + * In the general case where a The number of polygonal faces is + * FN - EN_f + VN_f + * where: + * EN_f is the number of 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: + * 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) + { + + UpdateFlags::VertexSetV(m); + // First loop Clear all referenced vertices + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if (!fi->IsD()) + 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) + if (!m.HasPerFaceFlags()) return m.fn; + typedef typename MeshType::FaceType F; + int countE = 0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if (!fi->IsD()) { + 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) + { + assert(m.HasPerFaceFlags()); + assert(m.HasFFTopology()); // todo: remove this constraint + + 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) + { + assert(m.HasPerFaceFlags()); + + 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 mesh, e.g. the edges where there are more than 2 incident faces. + * + * Note that this test is not enough to say that a mesh is two manifold, + * you have to count also the non manifold vertexes. + */ + static int CountNonManifoldEdgeFF( MeshType & m, bool SelectFlag=false) + { + int nmfBit[3]; + nmfBit[0]= FaceType::NewBitFlag(); + nmfBit[1]= FaceType::NewBitFlag(); + nmfBit[2]= FaceType::NewBitFlag(); + + + UpdateFlags::FaceClear(m,nmfBit[0]+nmfBit[1]+nmfBit[2]); + + if(SelectFlag){ + UpdateSelection::ClearVertex(m); + UpdateSelection::ClearFace(m); + } + assert(tri::HasFFAdjacency(m)); + + int edgeCnt = 0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if (!fi->IsD()) + { + for(int i=0;i<3;++i) + if(!IsManifold(*fi,i)) + { + if(!(*fi).IsUserBit(nmfBit[i])) + { + ++edgeCnt; + if(SelectFlag) + { + (*fi).V0(i)->SetS(); + (*fi).V1(i)->SetS(); + } + // follow the ring of faces incident on edge i; + face::Pos nmf(&*fi,i); + do + { + if(SelectFlag) nmf.F()->SetS(); + nmf.F()->SetUserBit(nmfBit[nmf.E()]); + nmf.NextF(); + } + while(nmf.f != &*fi); + } + } + } + } + return edgeCnt; + } + + /** Count (and eventually select) non 2-Manifold vertexes of a mesh + * e.g. the vertices with a non 2-manif. neighbourhood but that do not belong to not 2-manif edges. + * typical situation two cones connected by one vertex. + */ + static int CountNonManifoldVertexFF( MeshType & m, bool selectVert = true ) + { + assert(tri::HasFFAdjacency(m)); + if(selectVert) UpdateSelection::ClearVertex(m); + + int nonManifoldCnt=0; + SimpleTempData TD(m.vert,0); + + // First Loop, just count how many faces are incident on a vertex and store it in the TemporaryData Counter. + FaceIterator fi; + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + TD[(*fi).V(0)]++; + TD[(*fi).V(1)]++; + TD[(*fi).V(2)]++; + } + + tri::UpdateFlags::VertexClearV(m); + // Second Loop. + // mark out of the game the vertexes that are incident on non manifold edges. + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;++i) + if (!IsManifold(*fi,i)) { + (*fi).V0(i)->SetV(); + (*fi).V1(i)->SetV(); + } + } + // Third Loop, for safe vertexes, check that the number of faces that you can reach starting + // from it and using FF is the same of the previously counted. + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;i++) if(!(*fi).V(i)->IsV()){ + (*fi).V(i)->SetV(); + face::Pos pos(&(*fi),i); + + int starSizeFF = pos.NumberOfIncidentFaces(); + + if (starSizeFF != TD[(*fi).V(i)]) + { + if(selectVert) (*fi).V(i)->SetS(); + nonManifoldCnt++; + } + } + } + return nonManifoldCnt; + } + + static void CountEdges( MeshType & m, int &count_e, int &boundary_e ) + { + count_e=0; + boundary_e=0; + UpdateFlags::FaceClearV(m); + FaceIterator fi; + vcg::face::Pos he; + vcg::face::Pos hei; + bool counted =false; + for(fi=m.face.begin();fi!=m.face.end();fi++) + { + if(!((*fi).IsD())) + { + (*fi).SetV(); + count_e +=3; //assume that we have to increase the number of edges with three + for(int j=0; j<3; j++) + { + if (face::IsBorder(*fi,j)) //If this edge is a border edge + boundary_e++; // then increase the number of boundary edges + else if (IsManifold(*fi,j))//If this edge is manifold + { + if((*fi).FFp(j)->IsV()) //If the face on the other side of the edge is already selected + count_e--; // we counted one edge twice + } + else//We have a non-manifold edge + { + hei.Set(&(*fi), j , fi->V(j)); + he=hei; + he.NextF(); + while (he.f!=hei.f)// so we have to iterate all faces that are connected to this edge + { + if (he.f->IsV())// if one of the other faces was already visited than this edge was counted already. + { + counted=true; + break; + } + else + { + he.NextF(); + } + } + if (counted) + { + count_e--; + counted=false; + } + } + } + } + } + } + + + static int CountHoles( MeshType & m) + { + int numholev=0; + FaceIterator fi; + FaceIterator gi; + vcg::face::Pos he; + vcg::face::Pos hei; + + std::vector< std::vector > holes; //indices of vertices + + for(fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).ClearS(); + gi=m.face.begin(); fi=gi; + + for(fi=m.face.begin();fi!=m.face.end();fi++)//for all faces do + { + for(int j=0;j<3;j++)//for all edges + { + if(fi->V(j)->IsS()) continue; + + if(face::IsBorder(*fi,j))//found an unvisited border edge + { + he.Set(&(*fi),j,fi->V(j)); //set the face-face iterator to the current face, edge and vertex + std::vector hole; //start of a new hole + hole.push_back(fi->P(j)); // including the first vertex + numholev++; + he.v->SetS(); //set the current vertex as selected + he.NextB(); //go to the next boundary edge + + + while(fi->V(j) != he.v)//will we do not encounter the first boundary edge. + { + Point3x newpoint = he.v->P(); //select its vertex. + if(he.v->IsS())//check if this vertex was selected already, because then we have an additional hole. + { + //cut and paste the additional hole. + std::vector hole2; + int index = static_cast(find(hole.begin(),hole.end(),newpoint) + - hole.begin()); + for(unsigned int i=index; iSetS(); //set the current vertex as selected + he.NextB(); //go to the next boundary edge + } + holes.push_back(hole); + } + } + } + return static_cast(holes.size()); + } + + /* + Compute the set of connected components of a given mesh + it fills a vector of pair < int , faceptr > with, for each connecteed component its size and a represnant + */ + static int CountConnectedComponents(MeshType &m) + { + std::vector< std::pair > CCV; + return ConnectedComponents(m,CCV); + } + + static int ConnectedComponents(MeshType &m, std::vector< std::pair > &CCV) + { + FaceIterator fi; + FacePointer l; + CCV.clear(); + + for(fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).ClearS(); + + int Compindex=0; + std::stack sf; + FacePointer fpt=&*(m.face.begin()); + for(fi=m.face.begin();fi!=m.face.end();++fi) + { + if(!((*fi).IsD()) && !(*fi).IsS()) + { + (*fi).SetS(); + CCV.push_back(std::make_pair(0,&*fi)); + sf.push(&*fi); + while (!sf.empty()) + { + fpt=sf.top(); + ++CCV.back().first; + sf.pop(); + for(int j=0;j<3;++j) + { + if( !face::IsBorder(*fpt,j) ) + { + l=fpt->FFp(j); + if( !(*l).IsS() ) + { + (*l).SetS(); + sf.push(l); + } + } + } + } + Compindex++; + } + } + assert(int(CCV.size())==Compindex); + return Compindex; + } + + + /** + GENUS. + + A topologically invariant property of a surface defined as + the largest number of non-intersecting simple closed curves that can be + drawn on the surface without separating it. + + Roughly speaking, it is the number of holes in a surface. + The genus g of a closed surface, also called the geometric genus, is related to the + Euler characteristic by the relation $chi$ by $chi==2-2g$. + + The genus of a connected, orientable surface is an integer representing the maximum + number of cuttings along closed simple curves without rendering the resultant + manifold disconnected. It is equal to the number of handles on it. + + For general polyhedra the Euler Formula is: + + V + F - E = 2 - 2G - B + + where V is the number of vertices, F is the number of faces, E is the + number of edges, G is the genus and B is the number of boundary polygons. + + The above formula is valid for a mesh with one single connected component. + By considering multiple connected components the formula becomes: + + V + F - E = 2C - 2Gs - B + + where C is the number of connected components and Gs is the sum of + the genus of all connected components. + + */ + static int MeshGenus(MeshType &m, int numholes, int numcomponents, int count_e) + { + int V = m.vn; + int F = m.fn; + int E = count_e; + return -((V + F - E + numholes - 2 * numcomponents) / 2); + } + + /** + * Check if the given mesh is regular, semi-regular or irregular. + * + * Each vertex of a \em regular mesh has valence 6 except for border vertices + * which have valence 4. + * + * A \em semi-regular mesh is derived from an irregular one applying + * 1-to-4 subdivision recursively. (not checked for now) + * + * All other meshes are \em irregular. + */ + static void IsRegularMesh(MeshType &m, bool &Regular, bool &Semiregular) + { + // This algorithm requires Vertex-Face topology + assert(m.HasVFTopology()); + + Regular = true; + + VertexIterator vi; + + // for each vertex the number of edges are count + for (vi = m.vert.begin(); vi != m.vert.end(); ++vi) + { + if (!vi->IsD()) + { + face::Pos he((*vi).VFp(), &*vi); + face::Pos ht = he; + + int n=0; + bool border=false; + do + { + ++n; + ht.NextE(); + if (ht.IsBorder()) + border=true; + } + while (ht != he); + + if (border) + n = n/2; + + if ((n != 6)&&(!border && n != 4)) + { + Regular = false; + break; + } + } + } + + if (!Regular) + Semiregular = false; + else + { + // For now we do not account for semi-regularity + Semiregular = false; + } + } + + static void IsOrientedMesh(MeshType &m, bool &Oriented, bool &Orientable) + { + assert(&Oriented != &Orientable); + // This algorithms requires FF topology + assert(m.HasFFTopology()); + + Orientable = true; + Oriented = true; + + // Ensure that each face is deselected + FaceIterator fi; + for (fi = m.face.begin(); fi != m.face.end(); ++fi) + fi->ClearS(); + + // initialize stack + std::stack faces; + + // for each face of the mesh + FacePointer fp,fpaux; + int iaux; + for (fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if (!fi->IsD() && !fi->IsS()) + { + // each face put in the stack is selected (and oriented) + fi->SetS(); + faces.push(&(*fi)); + + // empty the stack + while (!faces.empty()) + { + fp = faces.top(); + faces.pop(); + + // make consistently oriented the adjacent faces + for (int j = 0; j < 3; j++) + { + // get one of the adjacent face + fpaux = fp->FFp(j); + iaux = fp->FFi(j); + + if (!fpaux->IsD() && fpaux != fp && face::IsManifold(*fp, j)) + { + if (!CheckOrientation(*fpaux, iaux)) + { + Oriented = false; + + if (!fpaux->IsS()) + { + face::SwapEdge(*fpaux, iaux); + assert(CheckOrientation(*fpaux, iaux)); + } + else + { + Orientable = false; + break; + } + } + + // put the oriented face into the stack + + if (!fpaux->IsS()) + { + fpaux->SetS(); + faces.push(fpaux); + } + } + } + } + } + + if (!Orientable) break; + } + } + /// Flip the orientation of the whole mesh flipping all the faces (by swapping the first two vertices) + static void FlipMesh(MeshType &m, bool selected=false) + { + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) + if(!selected || (*fi).IsS()) + { + face::SwapEdge((*fi), 0); + if (HasPerWedgeTexCoord(m)) + std::swap((*fi).WT(0),(*fi).WT(1)); + } + } + // Search and remove small single triangle folds + // - a face has normal opposite to all other faces + // - choose the edge that brings to the face f1 containing the vertex opposite to that edge. + static int RemoveFaceFoldByFlip(MeshType &m, float normalThresholdDeg=175, bool repeat=true) + { + assert(m.HasFFTopology()); + assert(m.HasPerVertexMark()); + //Counters for logging and convergence + int count, total = 0; + + do { + tri::UpdateTopology::FaceFace(m); + tri::UnMarkAll(m); + count = 0; + + ScalarType NormalThrRad = math::ToRad(normalThresholdDeg); + ScalarType eps = 0.0001; // this epsilon value is in absolute value. It is a distance from edge in baricentric coords. + //detection stage + for(FaceIterator fi=m.face.begin();fi!= m.face.end();++fi ) if(!(*fi).IsV()) + { Point3 NN = vcg::NormalizedNormal((*fi)); + if( vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(0))) > NormalThrRad && + vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(1))) > NormalThrRad && + vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(2))) > NormalThrRad ) + { + (*fi).SetS(); + //(*fi).C()=Color4b(Color4b::Red); + // now search the best edge to flip + for(int i=0;i<3;i++) + { + Point3 &p=(*fi).P2(i); + Point3 L; + bool ret = vcg::InterpolationParameters((*(*fi).FFp(i)),vcg::Normal(*(*fi).FFp(i)),p,L); + if(ret && L[0]>eps && L[1]>eps && L[2]>eps) + { + (*fi).FFp(i)->SetS(); + (*fi).FFp(i)->SetV(); + //(*fi).FFp(i)->C()=Color4b(Color4b::Green); + if(face::CheckFlipEdge( *fi, i )) { + face::FlipEdge( *fi, i ); + ++count; ++total; + } + } + } + } + } + + // tri::UpdateNormals::PerFace(m); + } + while( repeat && count ); + return total; + } + + + static int RemoveTVertexByFlip(MeshType &m, float threshold=40, bool repeat=true) + { + assert(m.HasFFTopology()); + assert(m.HasPerVertexMark()); + //Counters for logging and convergence + int count, total = 0; + + do { + tri::UpdateTopology::FaceFace(m); + tri::UnMarkAll(m); + count = 0; + + //detection stage + for(unsigned int index = 0 ; index < m.face.size(); ++index ) + { + FacePointer f = &(m.face[index]); float sides[3]; Point3 dummy; + sides[0] = Distance(f->P(0), f->P(1)); + sides[1] = Distance(f->P(1), f->P(2)); + sides[2] = Distance(f->P(2), f->P(0)); + // Find largest triangle side + int i = std::find(sides, sides+3, std::max( std::max(sides[0],sides[1]), sides[2])) - (sides); + if( tri::IsMarked(m,f->V2(i) )) continue; + + if( PSDist(f->P2(i),f->P(i),f->P1(i),dummy)*threshold <= sides[i] ) + { + tri::Mark(m,f->V2(i)); + if(face::CheckFlipEdge( *f, i )) { + // Check if EdgeFlipping improves quality + FacePointer g = f->FFp(i); int k = f->FFi(i); + Triangle3 t1(f->P(i), f->P1(i), f->P2(i)), t2(g->P(k), g->P1(k), g->P2(k)), + t3(f->P(i), g->P2(k), f->P2(i)), t4(g->P(k), f->P2(i), g->P2(k)); + + if ( std::min( t1.QualityFace(), t2.QualityFace() ) < std::min( t3.QualityFace(), t4.QualityFace() )) + { + face::FlipEdge( *f, i ); + ++count; ++total; + } + } + + } + } + + // tri::UpdateNormals::PerFace(m); + } + while( repeat && count ); + return total; + } + + static int RemoveTVertexByCollapse(MeshType &m, float threshold=40, bool repeat=true) + { + assert(tri::HasPerVertexMark(m)); + //Counters for logging and convergence + int count, total = 0; + + do { + tri::UnMarkAll(m); + count = 0; + + //detection stage + for(unsigned int index = 0 ; index < m.face.size(); ++index ) + { + FacePointer f = &(m.face[index]); float sides[3]; Point3 dummy; + sides[0] = Distance(f->P(0), f->P(1)); sides[1] = Distance(f->P(1), f->P(2)); sides[2] = Distance(f->P(2), f->P(0)); + int i = std::find(sides, sides+3, std::max( std::max(sides[0],sides[1]), sides[2])) - (sides); + if( tri::IsMarked(m,f->V2(i) )) continue; + + if( PSDist(f->P2(i),f->P(i),f->P1(i),dummy)*threshold <= sides[i] ) + { + tri::Mark(m,f->V2(i)); + + int j = Distance(dummy,f->P(i))P1(i))?i:(i+1)%3; + f->P2(i) = f->P(j); tri::Mark(m,f->V(j)); + ++count; ++total; + } + } + + + tri::Clean::RemoveDuplicateVertex(m); + tri::Allocator::CompactFaceVector(m); + tri::Allocator::CompactVertexVector(m); + } + while( repeat && count ); + + return total; + } + + static bool SelfIntersections(MeshType &m, std::vector &ret) + { + assert(HasPerFaceMark(m));// Needed by the UG + Box3< ScalarType> bbox; + TriMeshGrid gM; + ret.clear(); + FaceIterator fi; + int referredBit = FaceType::NewBitFlag(); + tri::UpdateFlags::FaceClear(m,referredBit); + + std::vector inBox; + gM.Set(m.face.begin(),m.face.end()); + + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + (*fi).SetUserBit(referredBit); + (*fi).GetBBox(bbox); + vcg::tri::GetInBoxFace(m, gM, bbox,inBox); + bool Intersected=false; + typename std::vector::iterator fib; + for(fib=inBox.begin();fib!=inBox.end();++fib) + { + if(!(*fib)->IsUserBit(referredBit) && (*fib != &*fi) ) + if(TestIntersection(&*fi,*fib)){ + ret.push_back(*fib); + if(!Intersected) { + ret.push_back(&*fi); + Intersected=true; + } + } + } + inBox.clear(); + } + + FaceType::DeleteBitFlag(referredBit); + return (ret.size()>0); + } + + /** + This function simply test that the vn and fn counters be consistent with the size of the containers and the number of deleted simplexes. + */ + static bool IsSizeConsistent(MeshType &m) + { + int DeletedVertexNum=0; + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if((*vi).IsD()) DeletedVertexNum++; + + int DeletedFaceNum=0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if((*fi).IsD()) DeletedFaceNum++; + + if(size_t(m.vn+DeletedVertexNum) != m.vert.size()) return false; + if(size_t(m.fn+DeletedFaceNum) != m.face.size()) return false; + + return true; + } + + /** + This function simply test that all the faces have a consistent face-face topology relation. + useful for checking that a topology modifying algorithm does not mess something. + */ + static bool IsFFAdjacencyConsistent(MeshType &m) + { + if(!HasFFAdjacency(m)) return false; + + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + for(int i=0;i<3;++i) + if(!FFCorrectness(*fi, i)) return false; + } + return true; + } + +/** + This function simply test that a mesh has some reasonable tex coord. + */ + static bool HasConsistentPerWedgeTexCoord(MeshType &m) + { + if(!HasPerWedgeTexCoord(m)) return false; + + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { FaceType &f=(*fi); + if( ! ( (f.WT(0).N() == f.WT(1).N()) && (f.WT(0).N() == (*fi).WT(2).N()) ) ) + return false; // all the vertices must have the same index. + + if((*fi).WT(0).N() <0) return false; // no undefined texture should be allowed + } + return true; + } + + /** + Simple check that there are no face with all collapsed tex coords. + */ + static bool HasZeroTexCoordFace(MeshType &m) + { + if(!HasPerWedgeTexCoord(m)) return false; + + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + if( (*fi).WT(0).P() == (*fi).WT(1).P() && (*fi).WT(0).P() == (*fi).WT(2).P() ) return false; + } + return true; + } + + + /** + This function test if two face intersect. + We assume that the two faces are different. + if the faces share an edge no test is done. + if the faces share only a vertex, the opposite edge is tested against the face + */ + static bool TestIntersection(FaceType *f0,FaceType *f1) + { + assert(f0!=f1); + int sv = face::CountSharedVertex(f0,f1); + if(sv==0) return (vcg::IntersectionTriangleTriangle((*f0),(*f1))); + // if the faces share only a vertex, the opposite edge is tested against the face + if(sv==1) + { + int i0,i1; ScalarType a,b; + face::SharedVertex(f0,f1,i0,i1); + if(vcg::IntersectionSegmentTriangle(Segment3((*f0).V1(i0)->P(),(*f0).V2(i0)->P()), *f1, a, b) ) return true; + if(vcg::IntersectionSegmentTriangle(Segment3((*f1).V1(i1)->P(),(*f1).V2(i1)->P()), *f0, a, b) ) return true; + } + return false; + } + + + +/** + This function merge all the vertices that are closer than the given radius +*/ +static int MergeCloseVertex(MeshType &m, const ScalarType radius) + { + int mergedCnt=0; + mergedCnt = ClusterVertex(m,radius); + RemoveDuplicateVertex(m,true); + return mergedCnt; + } + +static int ClusterVertex(MeshType &m, const ScalarType radius) + { + typedef vcg::SpatialHashTable SampleSHT; + SampleSHT sht; + tri::VertTmark markerFunctor; + typedef vcg::vertex::PointDistanceFunctor VDistFunct; + std::vector closests; + int mergedCnt=0; + Point3f closestPt; + sht.Set(m.vert.begin(), m.vert.end()); + UpdateFlags::VertexClearV(m); + for(VertexIterator viv = m.vert.begin(); viv!= m.vert.end(); ++viv) + if(!(*viv).IsD() && !(*viv).IsV()) + { + (*viv).SetV(); + Point3f p = viv->cP(); + Box3f bb(p-Point3f(radius,radius,radius),p+Point3f(radius,radius,radius)); + GridGetInBox(sht, markerFunctor, bb, closests); + // qDebug("Vertex %i has %i closest", &*viv - &*m.vert.begin(),closests.size()); + for(size_t i=0; icP()); + if(dist < radius && !closests[i]->IsV()) + { + printf("%f %f \n",dist,radius); + mergedCnt++; + closests[i]->SetV(); + closests[i]->P()=p; + } + } + } + return mergedCnt; + } + + +static std::pair RemoveSmallConnectedComponentsSize(MeshType &m, int maxCCSize) +{ + std::vector< std::pair > CCV; + int TotalCC=ConnectedComponents(m, CCV); + int DeletedCC=0; + + ConnectedIterator ci; + for(unsigned int i=0;i FPV; + if(CCV[i].first::iterator fpvi; + for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) + Allocator::DeleteFace(m,(**fpvi)); + } + } + return std::make_pair(TotalCC,DeletedCC); +} + +/// Remove the connected components smaller than a given diameter +// it returns a pair with the number of connected components and the number of deleted ones. +static std::pair RemoveSmallConnectedComponentsDiameter(MeshType &m, ScalarType maxDiameter) +{ + std::vector< std::pair > CCV; + int TotalCC=ConnectedComponents(m, CCV); + int DeletedCC=0; + tri::ConnectedIterator ci; + for(unsigned int i=0;i FPV; + for(ci.start(m,CCV[i].second);!ci.completed();++ci) + { + FPV.push_back(*ci); + bb.Add((*ci)->P(0)); + bb.Add((*ci)->P(1)); + bb.Add((*ci)->P(2)); + } + if(bb.Diag()::iterator fpvi; + for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) + tri::Allocator::DeleteFace(m,(**fpvi)); + } + } + return std::make_pair(TotalCC,DeletedCC); +} + + }; // end class + /*@}*/ + + } //End Namespace Tri +} // End Namespace vcg +#endif diff --git a/vcg/complex/algorithms/clip.h b/vcg/complex/algorithms/clip.h new file mode 100644 index 00000000..ef3f2347 --- /dev/null +++ b/vcg/complex/algorithms/clip.h @@ -0,0 +1,1114 @@ +/**************************************************************************** +* 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 __VCGLIB_TRI_CLIP +#define __VCGLIB_TRI_CLIP + +#include + +#ifdef _WIN32 + #ifndef __MINGW32__ + #include + #define STDEXT stdext + #else + #include + #define STDEXT __gnu_cxx + #endif +#else + #include + #define STDEXT __gnu_cxx +#endif +namespace vcg +{ +namespace tri +{ + + template + class GenericVertexInterpolator + { + public: + typedef typename MESH_TYPE::VertexType VertexType; + typedef GenericVertexInterpolator ClassType; + typedef typename VertexType::CoordType CoordType; + typedef typename CoordType::ScalarType ScalarType; + GenericVertexInterpolator(MESH_TYPE &_m) : m(_m) {} + private: + MESH_TYPE &m; + public: + inline void operator () (const VertexType & v0, const VertexType & v1, const VertexType & v2, const ScalarType & a, const ScalarType & b, VertexType & r) const + { + // position + r.P() = v0.P() + (v1.P() - v0.P()) * a + (v2.P() - v0.P()) * b; + + // normal + if (tri::HasPerVertexNormal(m)) + { + r.N() = v0.cN() + (v1.cN() - v0.cN()) * a + (v2.cN() - v0.cN()) * b; + } + + // color + if (tri::HasPerVertexColor(m)) + { + vcg::Point4 vc[3]; + vc[0].Import(v0.cC()); + vc[1].Import(v1.cC()); + vc[2].Import(v2.cC()); + const vcg::Point4 rc = (vc[0] + (vc[1] - vc[0]) * a + (vc[2] - vc[0]) * b); + r.C()[0] = (typename vcg::Color4b::ScalarType)(rc[0]); + r.C()[1] = (typename vcg::Color4b::ScalarType)(rc[1]); + r.C()[2] = (typename vcg::Color4b::ScalarType)(rc[2]); + r.C()[3] = (typename vcg::Color4b::ScalarType)(rc[3]); + } + + // texcoord + if (tri::HasPerVertexTexCoord(m)) + { + const short nt = 1; //typename VertexType::TextureType::N(); + for (short i=0; i +class TriMeshClipper +{ +public: + + typedef TriMeshClipper ClassType; + typedef TRIMESHTYPE TriMeshType; + typedef typename TriMeshType::FaceType FaceType; + typedef typename FaceType::VertexType VertexType; + typedef typename VertexType::CoordType CoordType; + typedef typename CoordType::ScalarType ScalarType; + + /* + static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, TriMeshType & m); + + Clip mesh "m" against an axis aligned box (in place version); + + Notes: + 1) faces marked as deleted are skipped; + 2) faces completely outside box are marked as deleted; + 3) faces completely inside box are left unchanged; + 4) faces intersecting box's sides are marked as deleted: + they are replaced with proper tesselation; new vertices and faces + are created, so reallocation could occour; previously saved pointers + could not to be valid anymore, thus they should be updated; + 5) vInterp functor must implement a n operator with signature + void operator () (const VERTEX & v0, const VERTEX & v1, const VERTEX & v2, const Scalar & a, const Scalar & b, VERTEX & r); + its semantic is to intepolate vertex attribute across triangle; a typical implementation is; + r.P() = v0.P() + a * (v1.P() - v0.P()) + b * (v2.P() - v0.P()); // interpolate position + r.N() = v0.N() + a * (v1.N() - v0.N()) + b * (v2.N() - v0.N()); // interpolate normal + ... // interpolate other vertex attributes + */ + template + class VertexClipInfo + { + public: +// typedef VertexClipInfo ClassType; + + ScalarType fU; + ScalarType fV; + unsigned int idx; + unsigned int tref; + }; + typedef typename std::vector< VertexClipInfo > VertexClipInfoVec; + class TriangleInfo + { + public: + typedef TriangleInfo ClassType; + + unsigned int v[3]; + unsigned int idx; + }; + + typedef std::vector TriangleInfoVec; + + class EdgeIsect + { + public: + CoordType p; + unsigned int idx; + }; + + template + static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, TriMeshType & m) + { + std::vector facesToDelete; + ClassType::Box(b, vInterp, m, facesToDelete); + for (size_t i=0; in = 0; + } + }; + + typedef STDEXT::hash_map UIntHMap; + typedef typename UIntHMap::iterator UIntHMap_i; + typedef typename UIntHMap::value_type UIntHMap_v; + + typedef STDEXT::hash_map EdgeMap; + typedef typename EdgeMap::iterator EdgeMap_i; + typedef typename EdgeMap::value_type EdgeMap_v; + + typedef typename TriMeshType::FaceIterator FaceIterator; + + template + static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, TriMeshType & m, FACEINDEXCONTAINER & facesToDelete) + { + if (m.fn <= 0) + { + return; + } + + EdgeMap edges; + VertexClipInfoVec vInfos; + TriangleInfoVec tInfos; + + CoordType vTriangle[4]; + CoordType vClipped[64]; + + CoordType pvP0[64]; + CoordType pvP1[64]; + + unsigned int numDeletedTris = 0; + unsigned int numTriangles = 0; + unsigned int numVertices = 0; + + unsigned int vIdx = (unsigned int)(m.vn); + unsigned int tIdx = (unsigned int)(m.fn); + + ScalarType boxOffsets[6]; + + boxOffsets[0] = b.min[0]; + boxOffsets[1] = -b.max[0]; + boxOffsets[2] = b.min[1]; + boxOffsets[3] = -b.max[1]; + boxOffsets[4] = b.min[2]; + boxOffsets[5] = -b.max[2]; + + UIntHMap emptyMap; + EdgeIntersections emptyIsects; + + const ScalarType eps = (ScalarType)(1e-6); + + for (FaceIterator it=m.face.begin(); it!=m.face.end(); ++it) + { + if ((*it).IsD()) + { + continue; + } + + unsigned int cc[3]; + + cc[0] = ClassType::BoxClipCode(boxOffsets, (*it).V(0)->P()); + cc[1] = ClassType::BoxClipCode(boxOffsets, (*it).V(1)->P()); + cc[2] = ClassType::BoxClipCode(boxOffsets, (*it).V(2)->P()); + + if ((cc[0] | cc[1] | cc[2]) == 0) + { + continue; + } + + const unsigned int refT = (unsigned int)(std::distance(m.face.begin(), it)); + + if ((cc[0] & cc[1] & cc[2]) != 0) + { + facesToDelete.push_back(refT); + (*it).SetD(); + numDeletedTris++; + continue; + } + + facesToDelete.push_back(refT); + + vTriangle[0] = (*it).V(0)->P(); + vTriangle[1] = (*it).V(1)->P(); + vTriangle[2] = (*it).V(2)->P(); + vTriangle[3] = (*it).V(0)->P(); + + unsigned int n, n0, n1; + + ClipPolygonLine(0, b.min[0], vTriangle, 4, pvP1, n1); + ClipPolygonLine(1, b.max[0], pvP1, n1, pvP0, n0); + + ClipPolygonLine(2, b.min[1], pvP0, n0, pvP1, n1); + ClipPolygonLine(3, b.max[1], pvP1, n1, pvP0, n0); + + ClipPolygonLine(4, b.min[2], pvP0, n0, pvP1, n1); + ClipPolygonLine(5, b.max[2], pvP1, n1, vClipped, n); + + assert(n < 64); + + unsigned int firstV, lastV; + + if (n > 2) + { + if (vClipped[0] == vClipped[n - 1]) + { + n--; + } + + const CoordType vU = vTriangle[1] - vTriangle[0]; + const CoordType vV = vTriangle[2] - vTriangle[0]; + + const ScalarType tArea = (vU ^ vV).SquaredNorm(); + if (tArea < eps) + { + continue; + } + + unsigned int tvidx[3]; + tvidx[0] = (*it).V(0) - &(*(m.vert.begin())); + tvidx[1] = (*it).V(1) - &(*(m.vert.begin())); + tvidx[2] = (*it).V(2) - &(*(m.vert.begin())); + + numTriangles += n - 2; + +// size_t vBegin = vInfos.size(); + + VertexClipInfo vnfo; + TriangleInfo tnfo; + + unsigned int vmin[3]; + unsigned int vmax[3]; + + if (tvidx[0] < tvidx[1]) + { + vmin[0] = tvidx[0]; + vmax[0] = tvidx[1]; + } + else + { + vmin[0] = tvidx[1]; + vmax[0] = tvidx[0]; + } + + if (tvidx[0] < tvidx[2]) + { + vmin[1] = tvidx[0]; + vmax[1] = tvidx[2]; + } + else + { + vmin[1] = tvidx[2]; + vmax[1] = tvidx[0]; + } + + if (tvidx[1] < tvidx[2]) + { + vmin[2] = tvidx[1]; + vmax[2] = tvidx[2]; + } + else + { + vmin[2] = tvidx[2]; + vmax[2] = tvidx[1]; + } + + for (unsigned int i=0; i mi = edges.insert(std::make_pair(vmin[1], emptyMap)); + std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[1], emptyIsects)); + bool found = false; + for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) + { + if (vClipped[i] == (*(hi.first)).second.isects[s].p) + { + found = true; + vnfo.idx = (*(hi.first)).second.isects[s].idx; + break; + } + } + if (!found) + { + vnfo.idx = vIdx++; + numVertices++; + vInfos.push_back(vnfo); + + (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; + (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vnfo.idx; + (*(hi.first)).second.n++; + } + } + else if (vnfo.fU < eps) + { + std::pair mi = edges.insert(std::make_pair(vmin[0], emptyMap)); + std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[0], emptyIsects)); + bool found = false; + for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) + { + if (vClipped[i] == (*(hi.first)).second.isects[s].p) + { + found = true; + vnfo.idx = (*(hi.first)).second.isects[s].idx; + break; + } + } + if (!found) + { + vnfo.idx = vIdx++; + numVertices++; + vInfos.push_back(vnfo); + + (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; + (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vnfo.idx; + (*(hi.first)).second.n++; + } + } + else if ((vnfo.fU + vnfo.fV) >= ((ScalarType)(1.0 - 1e-5))) + { + std::pair mi = edges.insert(std::make_pair(vmin[2], emptyMap)); + std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[2], emptyIsects)); + bool found = false; + for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) + { + if (vClipped[i] == (*(hi.first)).second.isects[s].p) + { + found = true; + vnfo.idx = (*(hi.first)).second.isects[s].idx; + break; + } + } + if (!found) + { + vnfo.idx = vIdx++; + numVertices++; + vInfos.push_back(vnfo); + + (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; + (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vnfo.idx; + (*(hi.first)).second.n++; + } + } + else + { + vnfo.idx = vIdx++; + numVertices++; + vInfos.push_back(vnfo); + } + + if (i == 0) + { + firstV = vnfo.idx; + } + + if (i > 1) + { + tnfo.idx = tIdx++; + tnfo.v[0] = firstV; + tnfo.v[1] = lastV; + tnfo.v[2] = vnfo.idx; + + tInfos.push_back(tnfo); + } + + lastV = vnfo.idx; + } + } + } + + if (numTriangles == 0) + { + return; + } + + const unsigned int vSize = (unsigned int)(m.vn); + const unsigned int tSize = (unsigned int)(m.fn); + + typedef Allocator TriMeshAllocatorType; + + TriMeshAllocatorType::AddVertices(m, numVertices); + TriMeshAllocatorType::AddFaces(m, numTriangles); + + unsigned int j = vSize; + for (size_t i=0; i= vSize) + { + const unsigned int tref = vInfos[i].tref; + vInterp(*(m.face[tref].V(0)), *(m.face[tref].V(1)), *(m.face[tref].V(2)), vInfos[i].fV, vInfos[i].fU, m.vert[j]); + j++; + } + } + + j = tSize; + for (size_t i=0; i & b, VERTEXINTEPOLATOR & vInterp, const TriMeshType & m, TriMeshType & r); + + Clip mesh "m" against an axis aligned box and put resulting data in mesh "r" (out of place version); + + Notes: + 1) input mesh is not modified; + 2) faces marked as deleted are skipped; + 3) vInterp functor must implement a n operator with signature + void operator () (const VERTEX & v0, const VERTEX & v1, const VERTEX & v2, const Scalar & a, const Scalar & b, VERTEX & r); + its semantic is to intepolate vertex attribute across triangle; a typical implementation is; + r.P() = v0.P() + a * (v1.P() - v0.P()) + b * (v2.P() - v0.P()); // interpolate position + r.N() = v0.N() + a * (v1.N() - v0.N()) + b * (v2.N() - v0.N()); // interpolate normal + ... // interpolate other vertex attributes + */ + + template + static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, const TriMeshType & m, TriMeshType & r) + { + r.Clear(); + + if (m.fn <= 0) + { + return; + } + + class VertexClipInfo + { + public: + typedef VertexClipInfo ClassType; + + ScalarType fU; + ScalarType fV; + unsigned int idx; + unsigned int tref; + }; + + typedef std::vector VertexClipInfoVec; + + class TriangleInfo + { + public: + typedef TriangleInfo ClassType; + + unsigned int v[3]; + unsigned int idx; + }; + + typedef std::vector TriangleInfoVec; + + class EdgeIsect + { + public: + CoordType p; + unsigned int idx; + }; + + class EdgeIntersections + { + public: + unsigned int n; + EdgeIsect isects[6]; + + EdgeIntersections(void) + { + this->n = 0; + } + }; + + typedef STDEXT::hash_map UIntHMap; + typedef typename UIntHMap::iterator UIntHMap_i; + typedef typename UIntHMap::value_type UIntHMap_v; + + typedef STDEXT::hash_map EdgeMap; + typedef typename EdgeMap::iterator EdgeMap_i; + typedef typename EdgeMap::value_type EdgeMap_v; + + typedef STDEXT::hash_map UIHMap; + typedef typename UIHMap::iterator UIHMap_i; + + typedef typename TriMeshType::ConstFaceIterator ConstFaceIterator; + + UIHMap origVertsMap; + EdgeMap edges; + VertexClipInfoVec vInfos; + TriangleInfoVec tInfos; + + CoordType vTriangle[4]; + CoordType vClipped[64]; + + CoordType pvP0[64]; + CoordType pvP1[64]; + + unsigned int numDeletedTris = 0; + unsigned int numTriangles = 0; + unsigned int numVertices = 0; + + unsigned int vIdx = 0; + unsigned int tIdx = 0; + + ScalarType boxOffsets[6]; + + boxOffsets[0] = b.min[0]; + boxOffsets[1] = -b.max[0]; + boxOffsets[2] = b.min[1]; + boxOffsets[3] = -b.max[1]; + boxOffsets[4] = b.min[2]; + boxOffsets[5] = -b.max[2]; + + UIntHMap emptyMap; + EdgeIntersections emptyIsects; + + const ScalarType eps = (ScalarType)(1e-6); + + for (ConstFaceIterator it=m.face.begin(); it!=m.face.end(); ++it) + { + if ((*it).IsD()) + { + continue; + } + + unsigned int cc[3]; + + cc[0] = ClassType::BoxClipCode(boxOffsets, (*it).V(0)->P()); + cc[1] = ClassType::BoxClipCode(boxOffsets, (*it).V(1)->P()); + cc[2] = ClassType::BoxClipCode(boxOffsets, (*it).V(2)->P()); + + if ((cc[0] | cc[1] | cc[2]) == 0) + { + TriangleInfo tnfo; + VertexClipInfo vnfo; + + tnfo.idx = tIdx++; + + for (int i=0; i<3; ++i) + { + const unsigned int v = (*it).V(i) - &(*(m.vert.begin())); + std::pair hi = origVertsMap.insert(std::make_pair(v, vIdx)); + + if (hi.second) + { + vnfo.idx = v; + vInfos.push_back(vnfo); + tnfo.v[i] = vIdx++; + } + else + { + tnfo.v[i] = (*(hi.first)).second; + } + } + + tInfos.push_back(tnfo); + + continue; + } + + if ((cc[0] & cc[1] & cc[2]) != 0) + { + numDeletedTris++; + continue; + } + + vTriangle[0] = (*it).V(0)->P(); + vTriangle[1] = (*it).V(1)->P(); + vTriangle[2] = (*it).V(2)->P(); + vTriangle[3] = (*it).V(0)->P(); + + unsigned int n, n0, n1; + + ClipPolygonLine(0, b.min[0], vTriangle, 4, pvP1, n1); + ClipPolygonLine(1, b.max[0], pvP1, n1, pvP0, n0); + + ClipPolygonLine(2, b.min[1], pvP0, n0, pvP1, n1); + ClipPolygonLine(3, b.max[1], pvP1, n1, pvP0, n0); + + ClipPolygonLine(4, b.min[2], pvP0, n0, pvP1, n1); + ClipPolygonLine(5, b.max[2], pvP1, n1, vClipped, n); + + assert(n < 64); + + unsigned int firstV, lastV; + + + if (n > 2) + { + if (vClipped[0] == vClipped[n - 1]) + { + n--; + } + + const CoordType vU = vTriangle[1] - vTriangle[0]; + const CoordType vV = vTriangle[2] - vTriangle[0]; + + const ScalarType tArea = (vU ^ vV).SquaredNorm(); + if (tArea < eps) + { + continue; + } + + unsigned int tvidx[3]; + tvidx[0] = (*it).V(0) - &(*(m.vert.begin())); + tvidx[1] = (*it).V(1) - &(*(m.vert.begin())); + tvidx[2] = (*it).V(2) - &(*(m.vert.begin())); + + unsigned int refT = (unsigned int)(std::distance(m.face.begin(), it)); + + numTriangles += n - 2; + + size_t vBegin = vInfos.size(); + + VertexClipInfo vnfo; + TriangleInfo tnfo; + + unsigned int vmin[3]; + unsigned int vmax[3]; + + if (tvidx[0] < tvidx[1]) + { + vmin[0] = tvidx[0]; + vmax[0] = tvidx[1]; + } + else + { + vmin[0] = tvidx[1]; + vmax[0] = tvidx[0]; + } + + if (tvidx[0] < tvidx[2]) + { + vmin[1] = tvidx[0]; + vmax[1] = tvidx[2]; + } + else + { + vmin[1] = tvidx[2]; + vmax[1] = tvidx[0]; + } + + if (tvidx[1] < tvidx[2]) + { + vmin[2] = tvidx[1]; + vmax[2] = tvidx[2]; + } + else + { + vmin[2] = tvidx[2]; + vmax[2] = tvidx[1]; + } + + for (unsigned int i=0; i hi = origVertsMap.insert(std::make_pair(tvidx[0], vIdx)); + if (hi.second) + { + vnfo.idx = tvidx[0]; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + else + { + currVIdx = (*(hi.first)).second; + } + } + else if (vClipped[i] == vTriangle[1]) + { + std::pair hi = origVertsMap.insert(std::make_pair(tvidx[1], vIdx)); + if (hi.second) + { + vnfo.idx = tvidx[1]; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + else + { + currVIdx = (*(hi.first)).second; + } + } + else if (vClipped[i] == vTriangle[2]) + { + std::pair hi = origVertsMap.insert(std::make_pair(tvidx[2], vIdx)); + if (hi.second) + { + vnfo.idx = tvidx[2]; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + else + { + currVIdx = (*(hi.first)).second; + } + } + else if (vnfo.fV < eps) + { + std::pair mi = edges.insert(std::make_pair(vmin[1], emptyMap)); + std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[1], emptyIsects)); + bool found = false; + for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) + { + if (vClipped[i] == (*(hi.first)).second.isects[s].p) + { + found = true; + vnfo.idx = (unsigned int)(-1); + currVIdx = (*(hi.first)).second.isects[s].idx; + break; + } + } + if (!found) + { + (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; + (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vIdx; + (*(hi.first)).second.n++; + + vnfo.idx = (unsigned int)(-1); + numVertices++; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + } + else if (vnfo.fU < eps) + { + std::pair mi = edges.insert(std::make_pair(vmin[0], emptyMap)); + std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[0], emptyIsects)); + bool found = false; + for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) + { + if (vClipped[i] == (*(hi.first)).second.isects[s].p) + { + found = true; + vnfo.idx = (unsigned int)(-1); + currVIdx = (*(hi.first)).second.isects[s].idx; + break; + } + } + if (!found) + { + (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; + (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vIdx; + (*(hi.first)).second.n++; + + vnfo.idx = (unsigned int)(-1); + numVertices++; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + } + else if ((vnfo.fU + vnfo.fV) >= ((ScalarType)(1.0 - 1e-5))) + { + std::pair mi = edges.insert(std::make_pair(vmin[2], emptyMap)); + std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[2], emptyIsects)); + bool found = false; + for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) + { + if (vClipped[i] == (*(hi.first)).second.isects[s].p) + { + found = true; + vnfo.idx = (unsigned int)(-1); + currVIdx = (*(hi.first)).second.isects[s].idx; + break; + } + } + if (!found) + { + (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; + (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vIdx; + (*(hi.first)).second.n++; + + vnfo.idx = (unsigned int)(-1); + numVertices++; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + } + else + { + vnfo.idx = (unsigned int)(-1); + numVertices++; + vInfos.push_back(vnfo); + currVIdx = vIdx++; + } + + if (i == 0) + { + firstV = currVIdx; + } + + if (i > 1) + { + tnfo.idx = tIdx++; + tnfo.v[0] = firstV; + tnfo.v[1] = lastV; + tnfo.v[2] = currVIdx; + + tInfos.push_back(tnfo); + } + + lastV = currVIdx; + } + } + } + + if (tInfos.empty()) + { + return; + } + + const unsigned int vSize = (unsigned int)(m.vn); + const unsigned int tSize = (unsigned int)(m.fn); + + typedef Allocator TriMeshAllocatorType; + + TriMeshAllocatorType::AddVertices(r, (int)(vInfos.size())); + TriMeshAllocatorType::AddFaces(r, (int)(tInfos.size())); + + for (size_t i=0; i value + eps; + break; + case 2: + flag = p_in[1] + eps < value; + break; + case 3: + flag = p_in[1] > value + eps; + break; + case 4: + flag = p_in[2] + eps < value; + break; + case 5: + flag = p_in[2] > value + eps; + break; + default: + break; + } + + return (flag); + } + + static inline void CrossPoint(int mode, const ScalarType & value, const CoordType & SP, const CoordType & PP, CoordType & p_out) + { + switch(mode) + { + case 0: + case 1: + p_out[0] = value; + if ((PP[0] - SP[0]) == ((ScalarType)(0))) + { + p_out[1] = PP[1]; + p_out[2] = PP[2]; + } + else + { + p_out[1] = SP[1] + (value - SP[0]) * (PP[1] - SP[1]) / (PP[0] - SP[0]); + p_out[2] = SP[2] + (value - SP[0]) * (PP[2] - SP[2]) / (PP[0] - SP[0]); + } + break; + case 2: + case 3: + p_out[1] = value; + if ((PP[1] - SP[1]) == ((ScalarType)(0))) + { + p_out[0] = PP[0]; + p_out[2] = PP[2]; + } + else + { + p_out[0] = SP[0] + (value - SP[1]) * (PP[0] - SP[0]) / (PP[1] - SP[1]); + p_out[2] = SP[2] + (value - SP[1]) * (PP[2] - SP[2]) / (PP[1] - SP[1]); + } + break; + case 4: + case 5: + p_out[2] = value; + if ((PP[2] - SP[2]) == ((ScalarType)(0))) + { + p_out[0] = PP[0]; + p_out[1] = PP[1]; + } + else + { + p_out[0] = SP[0] + (value - SP[2]) * (PP[0] - SP[0]) / (PP[2] - SP[2]); + p_out[1] = SP[1] + (value - SP[2]) * (PP[1] - SP[1]) / (PP[2] - SP[2]); + } + break; + default: + break; + } + } + + static inline void ClipPolygonLine(int mode, const ScalarType & value, CoordType * P_in, unsigned int n_in, CoordType * P_out, unsigned int & n_out) + { + unsigned int ps; + CoordType * SP; + CoordType * PP; + + n_out = 0; + SP = &P_in[n_in-1]; + + if (ClassType::InRegion(mode, value, *SP)) + { + ps = 0; + } + else + { + ps = 2; + } + + for(unsigned int i=0; i> 1) | ((ClassType::InRegion(mode, value, *PP)) ? (0) : (2)); + + switch(ps) + { + case 0: + break; + case 1: + ClassType::CrossPoint(mode, value, *SP, *PP, P_out[n_out]); + n_out++; + break; + case 2: + ClassType::CrossPoint(mode, value, *SP, *PP, P_out[n_out]); + n_out++; + P_out[n_out] = *PP; + n_out++; + break; + case 3: + P_out[n_out] = *PP; + n_out++; + break; + default: + break; + } + + SP = PP; + } + } + +}; + +} // end namespace tri +} // end namespace vcg + +#endif // __VCGLIB_TRI_CLIP diff --git a/vcg/complex/algorithms/closest.h b/vcg/complex/algorithms/closest.h new file mode 100644 index 00000000..a603a684 --- /dev/null +++ b/vcg/complex/algorithms/closest.h @@ -0,0 +1,563 @@ +/**************************************************************************** +* 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 $ +Revision 1.27 2006/12/06 12:59:13 pietroni +added max distance to rayIterator + +Revision 1.26 2006/11/21 16:06:54 ponchio +passing VDistFunct() to functions wanting a reference, not a value +(why a reference btw?) + +Revision 1.25 2006/11/13 13:13:49 ponchio +Added usual typename. + +Revision 1.24 2006/11/12 02:41:03 pietroni +added normalization of normal in DoRay functions + +Revision 1.23 2006/11/10 11:41:49 pietroni +added DoRayFuntion that return interpolated normal + +Revision 1.22 2006/09/20 17:18:26 ponchio +VDistFunct() at line 292 was passed as a temporary. +Invalid under g++. Fixed. + +Revision 1.21 2006/02/09 08:38:04 pietroni +sintax error corrected + +Revision 1.20 2006/02/08 17:02:41 pietroni +commented one GetClosestFace function ... the code is the same then getClosest that return barycentric coordinates + +Revision 1.19 2006/01/10 13:31:54 pietroni +correct pass of variable closest_pt by reference in getclosestFace function + +Revision 1.18 2005/12/02 00:13:34 cignoni +Added and removed typenames for gcc compiling. +removed also some template arguments specifcation that gcc disliked... +commented out GetInSphereFace and SetMesh that are probably never used and i didnt succeed in compile + +Revision 1.17 2005/10/05 17:02:52 pietroni +corrected bugs on GEtKClosestVert and GetInSphereVert + +Revision 1.16 2005/10/03 16:19:07 spinelli +fixed some bugs + +Revision 1.15 2005/10/03 13:59:39 pietroni +added GetInSphere and GetInBox functions +rensmed Functions respectively with Face suffix or Vertex suffix for query on vertex or faces + +Revision 1.14 2005/09/30 13:10:37 pietroni +used functor defined in face/distance.h for distance point-face +used functor defined in intersection3.h for ray-triangle intersection +added GetKClosest and DoRay Functions + +Revision 1.13 2005/09/28 08:30:48 cignoni +changed name of include, removed use of an undefined type (scalar instead of Scalar) +removed unused code portions (the old closest code) + +Revision 1.12 2005/09/21 09:24:30 pietroni +Added RayIterators. +Added ClosestIterators on Triangles and Vertices. +Added Closest Functions on triangles and Vertices. + +Revision 1.11 2005/09/19 13:36:24 pietroni +added ray iterator of faces + +Revision 1.10 2005/09/16 11:53:51 cignoni +Small gcc compliling issues + +Revision 1.9 2005/09/15 13:16:10 spinelli +fixed bugs + +Revision 1.8 2005/09/15 11:15:00 pietroni +minor changes + +Revision 1.7 2005/09/14 12:56:47 pietroni +used closest function from grid + +Revision 1.6 2005/08/26 09:12:48 cignoni +changed typedef A2UGridLink da 'GridStaticPtr::Link' a typedef 'GRID::Link' + +Revision 1.5 2005/02/08 17:49:38 pietroni +added if (!l->Elem()->IsD()) test on each element + +Revision 1.4 2005/01/28 12:00:33 cignoni +small gcc compiling issues for namespaces + +Revision 1.3 2005/01/24 11:47:23 cignoni +Now used also by the official Metro +Removed using namespace (NEVER IN HEADERS!) +Made the computation of barycentric coords only when necessary +Renamed Mindistpoint to Closest + +Revision 1.2 2005/01/21 17:13:09 pietroni +included distance.h changed Dist to vcg::face::PointDistance + +Revision 1.1 2004/10/04 15:32:16 ganovelli +moved from metro core + +Revision 1.6 2004/05/14 00:34:36 ganovelli +header added + +****************************************************************************/ + +#ifndef __VCG_TRIMESH_CLOSEST +#define __VCG_TRIMESH_CLOSEST +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcg { + namespace tri { + + //**MARKER CLASSES**// + template + class Tmark + { + MESH_TYPE *m; + public: + Tmark(){} + Tmark( MESH_TYPE *m) {SetMesh(m);} + void UnMarkAll(){ vcg::tri::UnMarkAll(*m);} + bool IsMarked(OBJ_TYPE* obj){return (vcg::tri::IsMarked(*m,obj));} + void Mark(OBJ_TYPE* obj){ vcg::tri::Mark(*m,obj);} + void SetMesh(MESH_TYPE *_m) + {m=_m;} + }; + + template + class FaceTmark:public Tmark + { + public: + FaceTmark() {} + FaceTmark(MESH_TYPE *m) {this->SetMesh(m);} + }; + + template + class VertTmark + { + public: + typedef typename MESH_TYPE::VertexType VertexType; + inline VertTmark(){} + inline VertTmark(MESH_TYPE *){} + inline void UnMarkAll() const {} + inline bool IsMarked(VertexType*) const { return false; } + inline void Mark(VertexType*) const {} + inline void SetMesh(void * /*m=0*/) const {} + }; + + //**CLOSEST FUNCTION DEFINITION**// + + /* + + aka MetroCore + data una mesh m e una ug sulle sue facce trova il punto di m piu' vicino ad + un punto dato. + */ + + // input: mesh, punto, griglia (gr), distanza limite (mdist) + // output: normale (interpolata) alla faccia e punto piu' vicino su di essa, e coord baricentriche del punto trovato + + // Nota che il parametro template GRID non ci dovrebbe essere, visto che deve essere + // UGrid, ma non sono riuscito a definirlo implicitamente + + template + typename MESH::FaceType * GetClosestFace( MESH & mesh, GRID & gr, const typename GRID::CoordType & _p, + const typename GRID::ScalarType & _maxDist, typename GRID::ScalarType & _minDist, + typename GRID::CoordType &_closestPt, typename GRID::CoordType & _normf, + typename GRID::CoordType & _ip) + { + typedef typename GRID::ScalarType ScalarType; + typedef Point3 Point3x; + + typedef FaceTmark MarkerFace; + MarkerFace mf(&mesh); + vcg::face::PointDistanceFunctor FDistFunct; + _minDist=_maxDist; + typename MESH::FaceType* bestf= gr.GetClosest(FDistFunct, mf, _p, _maxDist, _minDist, _closestPt); + + if(_maxDist> ScalarType(fabs(_minDist))) + { + // f=bestf; + typename MESH::ScalarType alfa, beta, gamma; + //calcolo normale con interpolazione trilineare + InterpolationParameters(*bestf,bestf->N(),_closestPt, alfa, beta, gamma); + _normf = (bestf->V(0)->cN())*alfa+ + (bestf->V(1)->cN())*beta+ + (bestf->V(2)->cN())*gamma ; + _ip=Point3x(alfa,beta,gamma); + //normf.Normalize(); inutile si assume le normali ai vertici benfatte + + _minDist = fabs(_minDist); + return(bestf); + } + return (0); + } + + /*template + typename MESH::FaceType * GetClosestFace( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p, + const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist, + typename GRID::CoordType &_closestPt,typename GRID::CoordType & _normf) + { + Point3 _ip; + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + vcg::face::PointDistanceFunctor FDistFunct; + typename MESH::FaceType* bestf= gr.GetClosest(FDistFunct,mf,_p,_maxDist,_minDist,_closestPt) ); + }*/ + + template + typename MESH::FaceType * GetClosestFace( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p, + const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist, + typename GRID::CoordType &_closestPt) + { + typedef typename GRID::ScalarType ScalarType; + typedef Point3 Point3x; + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + vcg::face::PointDistanceFunctor PDistFunct; + _minDist=_maxDist; + return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt)); + } + + template + typename MESH::FaceType * GetClosestFaceNormal(MESH & mesh,GRID & gr,const typename MESH::VertexType & _p, + const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist, + typename GRID::CoordType &_closestPt) + { + typedef typename GRID::ScalarType ScalarType; + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + typedef vcg::face::PointNormalDistanceFunctor PDistFunct; + PDistFunct fn; + _minDist=_maxDist; + //return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt.P())); + return (gr.template GetClosest (fn,mf,_p,_maxDist,_minDist,_closestPt)); + } + + template + typename MESH::VertexType * GetClosestVertex( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p, + const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist ) + { + typedef typename GRID::ScalarType ScalarType; + typedef Point3 Point3x; + typedef VertTmark MarkerVert; + MarkerVert mv; + mv.SetMesh(&mesh); + typedef vcg::vertex::PointDistanceFunctor VDistFunct; + VDistFunct fn; + _minDist=_maxDist; + Point3x _closestPt; + return (gr.template GetClosest(fn,mv,_p,_maxDist,_minDist,_closestPt)); + } + + template + typename MESH::VertexType * GetClosestVertexNormal( MESH & mesh,GRID & gr,const typename MESH::VertexType & _p, + const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist ) + { + typedef typename GRID::ScalarType ScalarType; + typedef Point3 Point3x; + typedef VertTmark MarkerVert; + MarkerVert mv; + mv.SetMesh(&mesh); + typedef vcg::vertex::PointNormalDistanceFunctor VDistFunct; + VDistFunct fn; + _minDist=_maxDist; + Point3x _closestPt; + return (gr.template GetClosest (fn,mv,_p,_maxDist,_minDist,_closestPt)); + } + + template + unsigned int GetKClosestFace(MESH & mesh,GRID & gr, const unsigned int _k, + const typename GRID::CoordType & _p, const typename GRID::ScalarType & _maxDist, + OBJPTRCONTAINER & _objectPtrs,DISTCONTAINER & _distances, POINTCONTAINER & _points) + { + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + vcg::face::PointDistanceFunctor FDistFunct; + return (gr.GetKClosest /**/ + (FDistFunct,mf,_k,_p,_maxDist,_objectPtrs,_distances,_points)); + } + + // This version does not require that the face type has the + // EdgePlane component and use a less optimized (but more memory efficient) point-triangle distance + template + unsigned int GetKClosestFaceBase(MESH & mesh,GRID & gr, const unsigned int _k, + const typename GRID::CoordType & _p, const typename GRID::ScalarType & _maxDist, + OBJPTRCONTAINER & _objectPtrs,DISTCONTAINER & _distances, POINTCONTAINER & _points) + { + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + vcg::face::PointDistanceBaseFunctor FDistFunct; + return (gr.GetKClosest /**/ + (FDistFunct,mf,_k,_p,_maxDist,_objectPtrs,_distances,_points)); + } + + template + unsigned int GetKClosestVertex(MESH & mesh,GRID & gr, const unsigned int _k, + const typename GRID::CoordType & _p, const typename GRID::ScalarType & _maxDist, + OBJPTRCONTAINER & _objectPtrs,DISTCONTAINER & _distances, POINTCONTAINER & _points) + { + typedef VertTmark MarkerVert; + MarkerVert mv; + mv.SetMesh(&mesh); + typedef vcg::vertex::PointDistanceFunctor VDistFunct; + VDistFunct distFunct; + return (gr.GetKClosest/* */ + (distFunct,mv,_k,_p,_maxDist,_objectPtrs,_distances,_points)); + } + + template + unsigned int GetInSphereFace(MESH & mesh, + GRID & gr, + const typename GRID::CoordType & _p, + const typename GRID::ScalarType & _r, + OBJPTRCONTAINER & _objectPtrs, + DISTCONTAINER & _distances, + POINTCONTAINER & _points) + { + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + typedef vcg::face::PointDistanceFunctor FDistFunct; + return (gr.GetInSphere/**/ + (FDistFunct(),mf,_p,_r,_objectPtrs,_distances,_points)); + } + + template + unsigned int GetInSphereVertex(MESH & mesh, + GRID & gr, + const typename GRID::CoordType & _p, + const typename GRID::ScalarType & _r, + OBJPTRCONTAINER & _objectPtrs, + DISTCONTAINER & _distances, + POINTCONTAINER & _points) + { + typedef VertTmark MarkerVert; + MarkerVert mv; + mv.SetMesh(&mesh); + typedef vcg::vertex::PointDistanceFunctor VDistFunct; + VDistFunct fn; + return (gr.GetInSphere/**/ + (fn, mv,_p,_r,_objectPtrs,_distances,_points)); + } + + template + unsigned int GetInBoxFace(MESH & mesh, + GRID & gr, + const vcg::Box3 _bbox, + OBJPTRCONTAINER & _objectPtrs) + { + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + return(gr.GetInBox/**/(mf,_bbox,_objectPtrs)); + } + + template + unsigned int GetInBoxVertex(MESH & mesh, + GRID & gr, + const vcg::Box3 _bbox, + OBJPTRCONTAINER & _objectPtrs) + { + typedef VertTmark MarkerVert; + MarkerVert mv; + mv.SetMesh(&mesh); + return(gr.GetInBox/**/(mv,_bbox,_objectPtrs)); + } + + template + typename GRID::ObjPtr DoRay(MESH & mesh,GRID & gr, const Ray3 & _ray, + const typename GRID::ScalarType & _maxDist, typename GRID::ScalarType & _t) + { + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + Ray3 _ray1=_ray; + _ray1.Normalize(); + typedef vcg::RayTriangleIntersectionFunctor FintFunct; + FintFunct ff; + return(gr.DoRay(ff,mf,_ray1,_maxDist,_t)); + } + + template + typename GRID::ObjPtr DoRay(MESH & mesh,GRID & gr, const Ray3 & _ray, + const typename GRID::ScalarType & _maxDist, + typename GRID::ScalarType & _t, + typename GRID::CoordType & _normf) + { + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + typedef FaceTmark MarkerFace; + MarkerFace mf; + mf.SetMesh(&mesh); + typedef vcg::RayTriangleIntersectionFunctor FintFunct; + FintFunct fintfunct; + Ray3 _ray1=_ray; + _ray1.Normalize(); + FaceType *f=gr.DoRay(fintfunct,mf,_ray1,_maxDist,_t); + typename GRID::CoordType dir=_ray.Direction(); + dir.Normalize(); + typename GRID::CoordType int_point=_ray.Origin()+_ray1.Direction()*_t; + typename GRID::ScalarType alfa,beta,gamma; + if (f!=NULL) + { + InterpolationParameters(*f,f->N(),int_point, alfa, beta, gamma); + _normf = (f->V(0)->cN())*alfa+ + (f->V(1)->cN())*beta+ + (f->V(2)->cN())*gamma ; + } + return f; + } + + ///Iteratively Do Ray sampling on spherical coordinates + ///sampling along the two angles + template + void RaySpherical(MESH & mesh,GRID & gr, const Ray3 & _ray, + const typename GRID::ScalarType & _maxDist, + const typename GRID::ScalarType & _theta_interval, + const typename GRID::ScalarType & _phi_interval, + const int &n_samples, + OBJPTRCONTAINER & _objectPtrs, + COORDCONTAINER & _pos, + COORDCONTAINER & _norm) + { + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + ScalarType delta_theta=_theta_interval/(ScalarType)(n_samples*2); + ScalarType delta_phi =_phi_interval/(ScalarType)(n_samples*2); + ScalarType theta_init,phi_init,ro; + typename GRID::CoordType dir0=_ray.Direction(); + dir0.ToPolarRad(ro,theta_init,phi_init); + for (int x=-n_samples;x<=n_samples;x++) + for (int y=-n_samples;y<=n_samples;y++) + { + ScalarType theta=theta_init+x*delta_theta; + if (theta<0) theta=2.0*M_PI-theta; + ScalarType phi=phi_init+y*delta_phi; + + typename GRID::CoordType dir; + dir.FromxPolar(ro,theta,phi); + dir.Normalize(); + Ray3 curr_ray(_ray.Origin(),dir); + typename GRID::ScalarType _t; + typename GRID::ObjPtr f=NULL; + f=DoRay(mesh,gr,curr_ray,_maxDist,_t); + if (f!=NULL) + { + typename GRID::CoordType pos=curr_ray.Origin()+curr_ray.Direction()*_t; + _objectPtrs.push_back(f); + _pos.push_back(pos); + + ///find the normal + typename GRID::ScalarType alfa,beta,gamma; + InterpolationParameters(*f,*f.N(),pos, alfa, beta, gamma); + typename GRID::CoordType norm = (f->V(0)->cN())*alfa+ + (f->V(1)->cN())*beta+ + (f->V(2)->cN())*gamma ; + _norm.push_back(norm); + } + } + } + + //**ITERATORS DEFINITION**// + + template + class ClosestFaceIterator:public vcg::ClosestIterator, FaceTmark > + { + public: + typedef GRID GridType; + typedef MESH MeshType; + typedef FaceTmark MarkerFace; + typedef vcg::face::PointDistanceFunctor PDistFunct; + typedef vcg::ClosestIterator > ClosestBaseType; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + + //ClosestFaceIterator(GridType &_Si):ClosestBaseType(_Si,PDistFunct()){} + ClosestFaceIterator(GridType &_Si):ClosestBaseType(_Si,PDistFunct()){} + +// Commented out: it seems unuseful and make gcc complain. p. + void SetMesh(MeshType *m) + {this->tm.SetMesh(m);} + }; + + template + class ClosestVertexIterator:public vcg::ClosestIterator, VertTmark > + { + public: + typedef GRID GridType; + typedef MESH MeshType; + typedef VertTmark MarkerVert; + typedef vcg::vertex::PointDistanceFunctor VDistFunct; + typedef vcg::ClosestIterator > ClosestBaseType; + VDistFunct fn; + ClosestVertexIterator(GridType &_Si):ClosestBaseType(_Si,fn){} + +// Commented out: it seems unuseful and make gcc complain. p. + void SetMesh(MeshType *m) + {this->tm.SetMesh(m);} + }; + + template + class TriRayIterator:public vcg::RayIterator,FaceTmark > + { + public: + typedef GRID GridType; + typedef MESH MeshType; + typedef FaceTmark MarkerFace; + typedef vcg::RayTriangleIntersectionFunctor FintFunct; + typedef vcg::RayIterator > RayBaseType; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + + TriRayIterator(GridType &_Si,const ScalarType &max_d):RayBaseType(_Si,FintFunct(),max_d){} + +// Commented out: it seems unuseful and make gcc complain. p. + void SetMesh(MeshType *m) + {this->tm.SetMesh(m);} + + }; + + } // end namespace tri +} // end namespace vcg + +#endif diff --git a/vcg/complex/algorithms/clustering.h b/vcg/complex/algorithms/clustering.h new file mode 100644 index 00000000..1a67a5b9 --- /dev/null +++ b/vcg/complex/algorithms/clustering.h @@ -0,0 +1,436 @@ +/**************************************************************************** + * VCGLib o o * + * Visual and Computer Graphics Library o o * + * _ O _ * + * Copyright(C) 2006 \/)\/ * + * 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 $ +Revision 1.11 2006/06/08 13:55:16 cignoni +Added ColorPreserving Cellbase template. + +Revision 1.10 2006/05/26 10:18:11 cignoni +Re-adapted to ms compilers + +Revision 1.9 2006/05/25 09:37:14 cignoni +Many changes for the different interpretation of hash_set between gcc and .net. Probably to be completed. + +Revision 1.8 2006/05/24 16:42:22 m_di_benedetto +Corrected bbox inflation amount in case of _cellsize != 0 + +Revision 1.7 2006/05/24 15:16:01 cignoni +better comment to the init parameters + +Revision 1.6 2006/05/24 08:54:04 cignoni +Added missing std:: to swap + +Revision 1.5 2006/05/21 06:40:31 cignoni +Added DoubleFace management + +Revision 1.4 2006/05/19 20:49:03 m_di_benedetto +Added check for empty generated mesh (prevent call to mesh allocator with zero vertices or faces). + +Revision 1.3 2006/05/18 22:20:53 m_di_benedetto +added check for deleted faces and modified/added std namespace qualifier. + +Revision 1.2 2006/05/18 13:59:20 cignoni +Some minor optimizations + +Revision 1.1 2006/05/16 21:56:06 cignoni +First working Version + +****************************************************************************/ + +#ifndef __VCGLIB_CLUSTERING +#define __VCGLIB_CLUSTERING + +#include +#include +#include +#include +#include + +#include +#include +#include + +// some stuff for portable hashes... +#ifdef WIN32 + #ifndef __MINGW32__ + #include + #include + #define STDEXT stdext + #else + #include + #include + #define STDEXT __gnu_cxx + #endif +#else + #include + #include + #define STDEXT __gnu_cxx +#endif + + + +namespace vcg{ +namespace tri{ +#define HASH_P0 73856093 +#define HASH_P1 19349663 +#define HASH_P2 83492791 + +class HashedPoint3i : public Point3i +{ +public: + + size_t Hash() const + { + return (V(0)*HASH_P0 ^ V(1)*HASH_P1 ^ V(2)*HASH_P2); + } + + operator size_t () const + {return Hash();} +}; + +// needed for gcc compilation +#ifndef _MSC_VER +}} namespace STDEXT { + template <> struct hash{ + inline size_t operator ()(const vcg::tri::HashedPoint3i &p) const {return size_t(p);} +}; +} namespace vcg{ namespace tri{ +#endif + +// +template +class NearestToCenter +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::FaceType FaceType; + typedef BasicGrid GridType; + + public: + inline void AddVertex(MeshType &/*m*/, GridType &g, Point3i &pi, VertexType &v) + { + CoordType c; + g.IPiToBoxCenter(pi,c); + ScalarType newDist = Distance(c,v.cP()); + if(!valid || newDist < bestDist) + { + valid=true; + bestDist=newDist; + bestPos=v.cP(); + bestN=v.cN(); + orig=&v; + } + } + inline void AddFaceVertex(MeshType &m, FaceType &f, int i) { assert(0);} + NearestToCenter(): valid(false){} + + CoordType bestPos; + CoordType bestN; + ScalarType bestDist; + bool valid; + int id; + VertexType *orig; + CoordType Pos() const + { + assert(valid); + return bestPos; + } + Color4b Col() const {return Color4b::White;} + CoordType N() const {return bestN;} + VertexType * Ptr() const {return orig;} + +}; + + + +template +class AverageColorCell +{ + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::VertexType VertexType; + + typedef BasicGrid GridType; + + public: + inline void AddFaceVertex(MeshType &/*m*/, FaceType &f, int i) + { + p+=f.cV(i)->cP(); + c+=CoordType(f.cV(i)->C()[0],f.cV(i)->C()[1],f.cV(i)->C()[2]); + + // we prefer to use the un-normalized face normal so small faces facing away are dropped out + // and the resulting average is weighed with the size of the faces falling here. + n+=f.cN(); + cnt++; + } + inline void AddVertex(MeshType &/*m*/, GridType &/*g*/, Point3i &/*pi*/, VertexType &v) + { + p+=v.cP(); + n+=v.cN(); + c+=CoordType(v.C()[0],v.C()[1],v.C()[2]); + cnt++; + } + + AverageColorCell(): p(0,0,0), n(0,0,0), c(0,0,0),cnt(0){} + CoordType p; + CoordType n; + CoordType c; + int cnt; + int id; + Color4b Col() const + { + return Color4b(c[0]/cnt,c[1]/cnt,c[2]/cnt,255); + } + + CoordType N() const {return n;} + VertexType * Ptr() const {return 0;} + CoordType Pos() const { return p/cnt; } +}; + + +/* + Metodo di clustering +*/ +template +class Clustering +{ + public: + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + // DuplicateFace == bool means that during the clustering doublesided surface (like a thin shell) that would be clustered to a single surface + // will be merged into two identical but opposite faces. + // So in practice: + // DuplicateFace=true a model with looks ok if you enable backface culling + // DuplicateFace=false a model with looks ok if you enable doublesided lighting and disable backfaceculling + + bool DuplicateFaceParam; + + // This class keeps the references to the three cells where a face has its vertexes. + class SimpleTri + { + public: + CellType *v[3]; + int ii(int i) const {return *((int *)(&(v[i])));} + bool operator < ( const SimpleTri &p) const { + return (v[2]!=p.v[2])?(v[2] v[1] ) std::swap(v[0],v[1]); // now v0 < v1 + if(v[0] > v[2] ) std::swap(v[0],v[2]); // now v0 is the minimum + if(v[1] > v[2] ) std::swap(v[1],v[2]); // sorted! + } + // Hashing Function; + operator size_t () const + { + return (ii(0)*HASH_P0 ^ ii(1)*HASH_P1 ^ ii(2)*HASH_P2); + } + }; + + + // The init function Take two parameters + // _size is the approximate total number of cells composing the grid surrounding the objects (usually a large number) + // eg _size==1.000.000 means a 100x100x100 grid + // _cellsize is the absolute lenght of the edge of the grid cell. + // eg _cellsize==2.0 means that all the vertexes in a 2.0x2.0x2.0 cell are clustered togheter + + // Notes: + // _size is used only if the cell edge IS zero. + // _cellsize gives you an absolute measure of the maximum error introduced + // during the simplification (e.g. half of the cell edge lenght) + + + void Init(Box3 _mbb, int _size, ScalarType _cellsize=0) + { + GridCell.clear(); + TriSet.clear(); + Grid.bbox=_mbb; + ///inflate the bb calculated + ScalarType infl = (_cellsize == (ScalarType)0) ? (Grid.bbox.Diag() / _size) : (_cellsize); + Grid.bbox.min-=CoordType(infl,infl,infl); + Grid.bbox.max+=CoordType(infl,infl,infl); + Grid.dim = Grid.bbox.max - Grid.bbox.min; + if( _cellsize==0) + BestDim( _size, Grid.dim, Grid.siz ); + else + Grid.siz = Point3i::Construct(Grid.dim / _cellsize); + + // find voxel size + Grid.voxel[0] = Grid.dim[0]/Grid.siz[0]; + Grid.voxel[1] = Grid.dim[1]/Grid.siz[1]; + Grid.voxel[2] = Grid.dim[2]/Grid.siz[2]; + } + + + BasicGrid Grid; + +#ifdef _MSC_VER + STDEXT::hash_set TriSet; + typedef typename STDEXT::hash_set::iterator TriHashSetIterator; +#else + struct SimpleTriHashFunc{ + inline size_t operator ()(const SimpleTri &p) const {return size_t(p);} + }; + STDEXT::hash_set TriSet; + typedef typename STDEXT::hash_set::iterator TriHashSetIterator; +#endif + + STDEXT::hash_map GridCell; + + + void AddPointSet(MeshType &m, bool UseOnlySelected=false) + { + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) + if(!UseOnlySelected || (*vi).IsS()) + { + HashedPoint3i pi; + Grid.PToIP((*vi).cP(), pi ); + GridCell[pi].AddVertex(m,Grid,pi,*(vi)); + } + } + + void AddMesh(MeshType &m) + { + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + HashedPoint3i pi; + SimpleTri st; + for(int i=0;i<3;++i) + { + Grid.PToIP((*fi).cV(i)->cP(), pi ); + st.v[i]=&(GridCell[pi]); + st.v[i]->AddFaceVertex(m,*(fi),i); + } + if( (st.v[0]!=st.v[1]) && (st.v[0]!=st.v[2]) && (st.v[1]!=st.v[2]) ) + { // if we allow the duplication of faces we sort the vertex only partially (to maintain the original face orientation) + if(DuplicateFaceParam) st.sortOrient(); + else st.sort(); + TriSet.insert(st); + } + // printf("Inserted %8i triangles, clustered to %8i tri and %i cells\n",distance(m.face.begin(),fi),TriSet.size(),GridCell.size()); + } + } + + int CountPointSet() {return GridCell.size(); } + + void SelectPointSet(MeshType &m) + { + typename STDEXT::hash_map::iterator gi; + UpdateSelection::ClearVertex(m); + for(gi=GridCell.begin();gi!=GridCell.end();++gi) + { + VertexType *ptr=(*gi).second.Ptr(); + if(ptr && ( ptr >= &*m.vert.begin() ) && ( ptr <= &*(m.vert.end() - 1) ) ) + ptr->SetS(); + } + } + void ExtractPointSet(MeshType &m) + { + m.Clear(); + + if (GridCell.empty()) return; + + Allocator::AddVertices(m,GridCell.size()); + typename STDEXT::hash_map::iterator gi; + int i=0; + for(gi=GridCell.begin();gi!=GridCell.end();++gi) + { + m.vert[i].P()=(*gi).second.Pos(); + m.vert[i].N()=(*gi).second.N(); + m.vert[i].C()=(*gi).second.Col(); + ++i; + } + + } + + void ExtractMesh(MeshType &m) + { + m.Clear(); + + if (TriSet.empty() || GridCell.empty()) + { + return; + } + + Allocator::AddVertices(m,GridCell.size()); + typename STDEXT::hash_map::iterator gi; + int i=0; + for(gi=GridCell.begin();gi!=GridCell.end();++gi) + { + m.vert[i].P()=(*gi).second.Pos(); + if(m.vert[i].HasColor()) + m.vert[i].C()=(*gi).second.Col(); + (*gi).second.id=i; + ++i; + } + Allocator::AddFaces(m,TriSet.size()); + TriHashSetIterator ti; + i=0; + for(ti=TriSet.begin();ti!=TriSet.end();++ti) + { + m.face[i].V(0)=&(m.vert[(*ti).v[0]->id]); + m.face[i].V(1)=&(m.vert[(*ti).v[1]->id]); + m.face[i].V(2)=&(m.vert[(*ti).v[2]->id]); + // if we are merging faces even when opposite we choose + // the best orientation according to the averaged normal + if(!DuplicateFaceParam) + { + CoordType N=vcg::Normal(m.face[i]); + int badOrient=0; + if( N.dot((*ti).v[0]->N()) <0) ++badOrient; + if( N.dot((*ti).v[1]->N()) <0) ++badOrient; + if( N.dot((*ti).v[2]->N()) <0) ++badOrient; + if(badOrient>2) + std::swap(m.face[i].V(0),m.face[i].V(1)); + } + i++; + } + + } +}; //end class clustering + } // namespace tri +} // namespace vcg + +#endif diff --git a/vcg/complex/algorithms/crease_cut.h b/vcg/complex/algorithms/crease_cut.h new file mode 100644 index 00000000..572ea229 --- /dev/null +++ b/vcg/complex/algorithms/crease_cut.h @@ -0,0 +1,147 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2008 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ + +#ifndef __VCG_CREASE_CUT +#define __VCG_CREASE_CUT +#include +#include +#include +namespace vcg { +namespace tri { + +/* +Crease Angle +Assume che: +la mesh abbia la topologia ff +la mesh non abbia complex (o se li aveva fossero stati detached) +Abbia le normali per faccia normalizzate!! + + +Prende una mesh e duplica tutti gli edge le cui normali nelle facce incidenti formano un angolo maggiore +di (espresso in rad). +foreach face + foreach unvisited vert vi + scan the star of triangles around vi duplicating vi each time we encounter a crease angle. + +the new (and old) vertexes are put in a std::vector that is swapped with the original one at the end. + +Si tiene un vettore di interi 3 *fn che dice l'indice del vertice puntato da ogni faccia. +quando si scandisce la stella intorno ad un vertici, per ogni wedge si scrive l'indice del vertice corrsipondente. + + +*/ + +template +void CreaseCut(MESH_TYPE &m, float angleRad) +{ + typedef typename MESH_TYPE::CoordType CoordType; + typedef typename MESH_TYPE::ScalarType ScalarType; + typedef typename MESH_TYPE::VertexType VertexType; + typedef typename MESH_TYPE::VertexPointer VertexPointer; + typedef typename MESH_TYPE::VertexIterator VertexIterator; + typedef typename MESH_TYPE::FaceIterator FaceIterator; + typedef typename MESH_TYPE::FaceType FaceType; + typedef typename MESH_TYPE::FacePointer FacePointer; + + tri::Allocator::CompactVertexVector(m); + tri::Allocator::CompactFaceVector(m); + + tri::UpdateNormals::NormalizeFace(m); + + assert(tri::HasFFAdjacency(m)); + typename MESH_TYPE::ScalarType cosangle=math::Cos(angleRad); + + tri::UpdateFlags::VertexClearV(m); + std::vector indVec(m.fn*3,-1); + int newVertexCounter=m.vn; + int creaseCounter=0; + int startVn=m.vn; + FaceIterator fi; + //const FaceType * nextf; + for(fi=m.face.begin();fi!=m.face.end();++fi) + for(int j=0;j<3;++j) + if(!(*fi).V(j)->IsV() ) // foreach unvisited vertex we loop around it searching for creases. + { + (*fi).V(j)->SetV(); + + face::JumpingPos iPos(&*fi,j,(*fi).V(j)); + size_t vertInd = Index(m,iPos.v); // + bool isBorderVertex = iPos.FindBorder(); // for border vertex we start from the border. + face::JumpingPos startPos=iPos; + if(!isBorderVertex) // for internal vertex we search the first crease and start from it + { + do { + ScalarType dotProd = iPos.FFlip()->cN().dot(iPos.f->N()); + iPos.NextFE(); + if(dotProdcN().dot(iPos.f->N()); // test normal with the next face (fflip) + size_t faceInd = Index(m,iPos.f); + indVec[faceInd*3+ iPos.VInd()] = curVertexCounter; + + if(dotProd0 && (!isBorderVertex) ) newVertexCounter--; + } + + // A questo punto ho un vettore che mi direbbe per ogni faccia quale vertice devo mettere. Dopo che ho aggiunto i vertici necessari, + // rifaccio il giro delle facce + qDebug("adding %i vert for %i crease edges ",newVertexCounter-m.vn, creaseCounter); + tri::Allocator::AddVertices(m,newVertexCounter-m.vn); + + tri::UpdateFlags::VertexClearV(m); + for(fi=m.face.begin();fi!=m.face.end();++fi) + for(int j=0;j<3;++j) // foreach unvisited vertex + { + size_t faceInd = Index(m, *fi); + size_t vertInd = Index(m, (*fi).V(j)); + int curVertexInd = indVec[faceInd*3+ j]; + assert(curVertexInd != -1); + assert(curVertexInd < m.vn); + if(curVertexInd < startVn) assert(size_t(curVertexInd) == vertInd); + if(curVertexInd >= startVn) + { + m.vert[curVertexInd].ImportData(*((*fi).V(j))); + (*fi).V(j) = & m.vert[curVertexInd]; + } + } + tri::UpdateNormals::PerVertexFromCurrentFaceNormal(m); +} + +} // end namespace tri +} // end namespace vcg +#endif + diff --git a/vcg/complex/algorithms/create/advancing_front.h b/vcg/complex/algorithms/create/advancing_front.h new file mode 100644 index 00000000..a0689faf --- /dev/null +++ b/vcg/complex/algorithms/create/advancing_front.h @@ -0,0 +1,551 @@ +#ifndef MLS_ADVANCE_H +#define MLS_ADVANCE_H + +#include +#include +#include +#include +#include +#include + +namespace vcg { + namespace tri { + +class FrontEdge { + public: + int v0, v1, v2; //v0, v1 represent the FrontEdge, v2 the other vertex + //in the face this FrontEdge belongs to + int face; //index of the face + bool active; //keep tracks of wether it is in front or in deads + + //the loops in the front are mantained as a double linked list + std::list::iterator next; + std::list::iterator previous; + + FrontEdge() {} + FrontEdge(int _v0, int _v1, int _v2, int _face): + v0(_v0), v1(_v1), v2(_v2), face(_face), active(true) { + assert(v0 != v1 && v1 != v2 && v0 != v2); + } + + const bool operator==(const FrontEdge& f) const + { + return ((v0 == f.v0) && (v1 == f.v1) && (v2 == f.v2) && (face == f.face)); + } +}; + +template class AdvancingFront { + public: + + typedef typename MESH::VertexType VertexType; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::VertexType::CoordType Point3x; + + //class FrontEdgeLists + //{ + + //}; + + +// protected: + std::list front; + std::list deads; + std::vector nb; //number of fronts a vertex is into, + //this is used for the Visited and Border flags + //but adding topology may not be needed anymore + + public: + + MESH &mesh; //this structure will be filled by the algorithm + + AdvancingFront(MESH &_mesh): mesh(_mesh) { + + + UpdateFlags::FaceBorderFromNone(mesh); + UpdateFlags::VertexBorderFromFace(mesh); + + nb.clear(); + nb.resize(mesh.vert.size(), 0); + + CreateLoops(); + } + virtual ~AdvancingFront() {} + virtual ScalarType radi() { return 0; } + + void BuildMesh(CallBackPos call = NULL, int interval = 512) { + float finalfacesext = mesh.vert.size() * 2.0f; + if(call) call(0, "Advancing front"); + while(1) { + + for(int i = 0; i < interval; i++) { + if(!front.size() && !SeedFace()) return; + AddFace(); + if(call) + { + float rap = float(mesh.face.size()) / finalfacesext; + int perc = (int) (100.0f * rap); + (*call)(perc,"Adding Faces"); + } + } + } + } + +protected: + //Implement these functions in your subclass + enum ListID {FRONT,DEADS}; + typedef std::pair< ListID,std::list::iterator > ResultIterator; + virtual bool Seed(int &v0, int &v1, int &v2) = 0; + virtual int Place(FrontEdge &e, ResultIterator &touch) = 0; + + bool CheckFrontEdge(int v0, int v1) { + int tot = 0; + //HACK to speed up things until i can use a seach structure +// int i = mesh.face.size() - 4*(front.size()); +// if(front.size() < 100) i = mesh.face.size() - 100; + int i = 0; + if(i < 0) i = 0; + for(; i < (int)mesh.face.size(); i++) { + FaceType &f = mesh.face[i]; + for(int k = 0; k < 3; k++) { + if(v1== (int)f.V(k) && v0 == (int)f.V((k+1)%3)) ++tot; + else if(v0 == (int)f.V(k) && v1 == (int)f.V((k+1)%3)) { //orientation non constistent + return false; + } + } + if(tot >= 2) { //non manifold + return false; + } + } + return true; + } + + //create the FrontEdge loops from seed faces + void CreateLoops() { + VertexType *start = &*mesh.vert.begin(); + for(int i = 0; i < (int)mesh.face.size(); i++) { + FaceType &f = mesh.face[i]; + if(f.IsD()) continue; + + for(int k = 0; k < 3; k++) { + if(f.IsB(k)) { + NewEdge(FrontEdge(f.V0(k) - start, f.V1(k) - start, f.V2(k) - start, i)); + nb[f.V0(k)-start]++; + } + } + } + + for(std::list::iterator s = front.begin(); s != front.end(); s++) { + (*s).previous = front.end(); + (*s).next = front.end(); + } + //now create loops: + for(std::list::iterator s = front.begin(); s != front.end(); s++) { + for(std::list::iterator j = front.begin(); j != front.end(); j++) { + if(s == j) continue; + if((*s).v1 != (*j).v0) continue; + if((*j).previous != front.end()) continue; + (*s).next = j; + (*j).previous = s; + break; + } + } + for(std::list::iterator s = front.begin(); s != front.end(); s++) { + assert((*s).next != front.end()); + assert((*s).previous != front.end()); + } + } + + bool SeedFace() { + int v[3]; + bool success = Seed(v[0], v[1], v[2]); + if(!success) return false; + + nb.resize(mesh.vert.size(), 0); + + //create the border of the first face + std::list::iterator e = front.end(); + std::list::iterator last = e; + std::list::iterator first; + + for(int i = 0; i < 3; i++) { + int v0 = v[i]; + int v1 = v[((i+1)%3)]; + int v2 = v[((i+2)%3)]; + + mesh.vert[v0].SetB(); + nb[v[i]]++; + + e = front.insert(front.begin(), FrontEdge(v0, v1, v2, mesh.face.size())); + if(i != 0) { + (*last).next = e; + (*e).previous = last; + } else + first = e; + + last = e; + } + //connect last and first + (*last).next = first; + (*first).previous = last; + + AddFace(v[0], v[1], v[2]); + return true; + } + +public: + bool AddFace() { + if(!front.size()) return false; + + std::list::iterator ei = front.begin(); + FrontEdge ¤t = *ei; + FrontEdge &previous = *current.previous; + FrontEdge &next = *current.next; + + int v0 = current.v0, v1 = current.v1; + assert(nb[v0] < 10 && nb[v1] < 10); + + ResultIterator touch; + touch.first = FRONT; + touch.second = front.end(); + int v2 = Place(current, touch); + + if(v2 == -1) { + KillEdge(ei); + return false; + } + + assert(v2 != v0 && v2 != v1); + + if ((touch.first == FRONT) && (touch.second != front.end()) || + (touch.first == DEADS) && (touch.second != deads.end())) + + { + //check for orientation and manifoldness + + //touch == current.previous? + if(v2 == previous.v0) { + if(!CheckEdge(v2, v1)) { + KillEdge(ei); + return false; + } + /*touching previous FrontEdge (we reuse previous) + next + ------->v2 -----> v1------> + \ / + \ / + previous \ / current + \ / + v0 */ + + Detach(v0); + + std::list::iterator up = NewEdge(FrontEdge(v2, v1, v0, mesh.face.size())); + MoveFront(up); + (*up).previous = previous.previous; + (*up).next = current.next; + (*previous.previous).next = up; + next.previous = up; + Erase(current.previous); + Erase(ei); + Glue(up); + + //touch == (*current.next).next + } else if(v2 == next.v1) { + if(!CheckEdge(v0, v2)) { + KillEdge(ei); + return false; + } + /*touching next FrontEdge (we reuse next) + previous + ------->v0 -----> v2------> + \ / + \ / + \ / next + \ / + v1 */ + + Detach(v1); + std::list::iterator up = NewEdge(FrontEdge(v0, v2, v1, mesh.face.size())); + MoveFront(up); + (*up).previous = current.previous; + (*up).next = (*current.next).next; + previous.next = up; + (*next.next).previous = up; + Erase(current.next); + Erase(ei); + Glue(up); + } else { + if(!CheckEdge(v0, v2) || !CheckEdge(v2, v1)) { + KillEdge(ei); + return false; + } + //touching some loop: split (or merge it is local does not matter. + //like this + /* + left right + <--------v2-<------ + /|\ + / \ + up / \ down + / \ + / V + ----v0 - - - > v1--------- + current */ + std::list::iterator left = touch.second; + std::list::iterator right = (*touch.second).previous; + + //this would be a really bad join + if(v1 == (*right).v0 || v0 == (*left).v1) { + KillEdge(ei); + return false; + } + + nb[v2]++; + + std::list::iterator down = NewEdge(FrontEdge(v2, v1, v0, mesh.face.size())); + std::list::iterator up = NewEdge(FrontEdge(v0, v2, v1, mesh.face.size())); + + (*right).next = down; + (*down).previous = right; + + (*down).next = current.next; + next.previous = down; + + (*left).previous = up; + (*up).next = left; + + (*up).previous = current.previous; + previous.next = up; + Erase(ei); + } + + + } + else if ((touch.first == FRONT) && (touch.second == front.end()) || + (touch.first == DEADS) && (touch.second == deads.end())) + { +// assert(CheckEdge(v0, v2)); +// assert(CheckEdge(v2, v1)); + /* adding a new vertex + + v2 + /|\ + / \ + up / \ down + / \ + / V + ----v0 - - - > v1--------- */ + assert(!mesh.vert[v2].IsB()); //fatal error! a new point is already a border? + nb[v2]++; + mesh.vert[v2].SetB(); + + std::list::iterator down = NewEdge(FrontEdge(v2, v1, v0, mesh.face.size())); + std::list::iterator up = NewEdge(FrontEdge(v0, v2, v1, mesh.face.size())); + + (*down).previous = up; + (*up).next = down; + (*down).next = current.next; + next.previous = down; + (*up).previous = current.previous; + previous.next = up; + Erase(ei); + } + + AddFace(v0, v2, v1); + return false; + } + +protected: + void AddFace(int v0, int v1, int v2) { + assert(v0 < (int)mesh.vert.size() && v1 < (int)mesh.vert.size() && v2 < (int)mesh.vert.size()); + FaceType face; + face.V(0) = &mesh.vert[v0]; + face.V(1) = &mesh.vert[v1]; + face.V(2) = &mesh.vert[v2]; + ComputeNormalizedNormal(face); + mesh.face.push_back(face); + mesh.fn++; + } + + void AddVertex(VertexType &vertex) { + VertexType *oldstart = NULL; + if(mesh.vert.size()) oldstart = &*mesh.vert.begin(); + mesh.vert.push_back(vertex); + mesh.vn++; + VertexType *newstart = &*mesh.vert.begin(); + if(oldstart && oldstart != newstart) { + for(int i = 0; i < mesh.face.size(); i++) { + FaceType &face = mesh.face[i]; + for(int k = 0; k < 3; k++) + face.V(k) = newstart + (face.V(k) - oldstart); + } + } + nb.push_back(0); + } + + + bool CheckEdge(int v0, int v1) { + int tot = 0; + //HACK to speed up things until i can use a seach structure +/* int i = mesh.face.size() - 4*(front.size()); + if(front.size() < 100) i = mesh.face.size() - 100; + if(i < 0) i = 0;*/ + VertexType *vv0 = &(mesh.vert[v0]); + VertexType *vv1 = &(mesh.vert[v1]); + + for(int i = 0; i < (int)mesh.face.size(); i++) { + FaceType &f = mesh.face[i]; + for(int k = 0; k < 3; k++) { + if(vv0 == f.V0(k) && vv1 == f.V1(k)) //orientation non constistent + return false; + else if(vv1 == f.V0(k) && vv0 == f.V1(k)) ++tot; + } + if(tot >= 2) { //non manifold + return false; + } + } + return true; + } + //front management: + + //Add a new FrontEdge to the back of the queue + std::list::iterator NewEdge(FrontEdge e) { + return front.insert(front.end(), e); + } + + //move an Edge among the dead ones + void KillEdge(std::list::iterator e) + { + if (e->active) + { + (*e).active = false; + //std::list::iterator res = std::find(front.begin(),front.end(),e); + FrontEdge tmp = *e; + deads.splice(deads.end(), front, e); + std::list::iterator newe = std::find(deads.begin(),deads.end(),tmp); + tmp.previous->next = newe; + tmp.next->previous = newe; + } + + } + + void Erase(std::list::iterator e) { + if((*e).active) front.erase(e); + else deads.erase(e); + } + + //move an FrontEdge to the back of the queue + void MoveBack(std::list::iterator e) { + front.splice(front.end(), front, e); + } + + void MoveFront(std::list::iterator e) { + front.splice(front.begin(), front, e); + } + + //check if e can be sewed with one of oits neighbours + bool Glue(std::list::iterator e) { + return Glue((*e).previous, e) || Glue(e, (*e).next); + } + + //Glue toghether a and b (where a.next = b + bool Glue(std::list::iterator a, std::list::iterator b) { + if((*a).v0 != (*b).v1) return false; + + std::list::iterator previous = (*a).previous; + std::list::iterator next = (*b).next; + (*previous).next = next; + (*next).previous = previous; + Detach((*a).v1); + Detach((*a).v0); + Erase(a); + Erase(b); + return true; + } + + void Detach(int v) { + assert(nb[v] > 0); + if(--nb[v] == 0) { + mesh.vert[v].ClearB(); + } + } +}; + +template class AdvancingTest: public AdvancingFront { + public: + typedef typename MESH::VertexType VertexType; + typedef typename MESH::VertexIterator VertexIterator; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::FaceIterator FaceIterator; + + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::VertexType::CoordType Point3x; + + AdvancingTest(MESH &_mesh): AdvancingFront(_mesh) {} + + bool Seed(int &v0, int &v1, int &v2) { + VertexType v[3]; + v[0].P() = Point3x(0, 0, 0); + v[1].P() = Point3x(1, 0, 0); + v[2].P() = Point3x(0, 1, 0); + v[0].ClearFlags(); + v[1].ClearFlags(); + v[2].ClearFlags(); + + v0 = this->mesh.vert.size(); + AddVertex(v[0]); + v1 = this->mesh.vert.size(); + AddVertex(v[1]); + v2 = this->mesh.vert.size(); + AddVertex(v[2]); + return true; + } + + int Place(FrontEdge &e, typename AdvancingFront::ResultIterator &touch) + { + Point3f p[3]; + p[0] = this->mesh.vert[e.v0].P(); + p[1] = this->mesh.vert[e.v1].P(); + p[2] = this->mesh.vert[e.v2].P(); + Point3f point = p[0] + p[1] - p[2]; + + int vn = this->mesh.vert.size(); + for(int i = 0; i < this->mesh.vert.size(); i++) + { + if((this->mesh.vert[i].P() - point).Norm() < 0.1) + { + vn = i; + //find the border + assert(this->mesh.vert[i].IsB()); + for(std::list::iterator k = this->front.begin(); k != this->front.end(); k++) + if((*k).v0 == i) + { + touch.first = AdvancingFront::FRONT; + touch.second = k; + } + + for(std::list::iterator k = this->deads.begin(); k != this->deads.end(); k++) + if((*k).v0 == i) + if((*k).v0 == i) + { + touch.first = AdvancingFront::FRONT; + touch.second = k; + } + break; + } + } + if(vn == this->mesh.vert.size()) { + VertexType v; + v.P() = point; + v.ClearFlags(); + AddVertex(v); + } + return vn; + } +}; + +}//namespace tri +}//namespace vcg + +#endif diff --git a/vcg/complex/algorithms/create/ball_pivoting.h b/vcg/complex/algorithms/create/ball_pivoting.h new file mode 100644 index 00000000..6f71693e --- /dev/null +++ b/vcg/complex/algorithms/create/ball_pivoting.h @@ -0,0 +1,418 @@ +#ifndef BALL_PIVOTING_H +#define BALL_PIVOTING_H + +#include "advancing_front.h" +#include +#include + +/* Ball pivoting algorithm: + 1) the vertices used in the new mesh are marked as visited + 2) the border vertices of the new mesh are marked as border + 3) the vector nb is used to keep track of the number of borders a vertex belongs to + 4) usedBit flag is used to select the points in the mesh already processed + +*/ +namespace vcg { + namespace tri { + +template class BallPivoting: public AdvancingFront { + public: + typedef typename MESH::VertexType VertexType; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::VertexIterator VertexIterator; + typedef typename MESH::VertexType::CoordType Point3x; + typedef GridStaticPtr StaticGrid; + + float radius; //radius of the ball + float min_edge; //min lenght of an edge + float max_edge; //min lenght of an edge + float max_angle; //max angle between 2 faces (cos(angle) actually) + + public: + ScalarType radi() { return radius; } + + // if radius ==0 an autoguess for the ball pivoting radius is attempted + // otherwise the passed value (in absolute mesh units) is used. + + BallPivoting(MESH &_mesh, float _radius = 0, + float minr = 0.2, float angle = M_PI/2): + + AdvancingFront(_mesh), radius(_radius), + min_edge(minr), max_edge(1.8), max_angle(cos(angle)), + last_seed(-1) { + + //compute bbox + baricenter = Point3x(0, 0, 0); + UpdateBounding::Box(_mesh); + for(VertexIterator vi=this->mesh.vert.begin();vi!=this->mesh.vert.end();++vi) + if( !(*vi).IsD() ) baricenter += (*vi).P(); + + baricenter /= this->mesh.vn; + + assert(this->mesh.vn > 3); + if(radius == 0) // radius ==0 means that an auto guess should be attempted. + radius = sqrt((this->mesh.bbox.Diag()*this->mesh.bbox.Diag())/this->mesh.vn); + + + min_edge *= radius; + max_edge *= radius; + + //enlarging the bbox for out-of-box queries + Box3 BPbbox=this->mesh.bbox; + BPbbox.Offset(4*radius); + grid.Set(this->mesh.vert.begin(), this->mesh.vert.end(), BPbbox); + + //mark visited points + std::vector targets; + std::vector points; + std::vector dists; + + usedBit = VertexType::NewBitFlag(); + for(int i = 0; i < (int)this->mesh.vert.size(); i++) + this->mesh.vert[i].ClearUserBit(usedBit); + + UpdateFlags::VertexClearV(this->mesh); + + for(int i = 0; i < (int)this->mesh.face.size(); i++) { + FaceType &f = this->mesh.face[i]; + if(f.IsD()) continue; + for(int k = 0; k < 3; k++) { + f.V(k)->SetV(); + int n = tri::GetInSphereVertex(this->mesh, grid, f.V(k)->P(), min_edge, targets, dists, points); + for(int t = 0; t < n; t++) { + targets[t]->SetUserBit(usedBit); + assert(targets[t]->IsUserBit(usedBit)); + } + assert(f.V(k)->IsUserBit(usedBit)); + } + } + } + + ~BallPivoting() { + VertexType::DeleteBitFlag(usedBit); + } + + bool Seed(int &v0, int &v1, int &v2) { + //get a sphere of neighbours + std::vector targets; + std::vector points; + std::vector dists; + while(++last_seed < (int)(this->mesh.vert.size())) { + VertexType &seed = this->mesh.vert[last_seed]; + if(seed.IsD() || seed.IsUserBit(usedBit)) continue; + + seed.SetUserBit(usedBit); + + int n = tri::GetInSphereVertex(this->mesh, grid, seed.P(), 2*radius, targets, dists, points); + if(n < 3) { + continue; + } + + bool success = true; + //find the closest visited or boundary + for(int i = 0; i < n; i++) { + VertexType &v = *(targets[i]); + if(v.IsV()) { + success = false; + break; + } + } + if(!success) continue; + + VertexType *vv0, *vv1, *vv2; + success = false; + //find a triplet that does not contains any other point + Point3x center; + for(int i = 0; i < n; i++) { + vv0 = targets[i]; + if(vv0->IsD()) continue; + Point3x &p0 = vv0->P(); + + for(int k = i+1; k < n; k++) { + vv1 = targets[k]; + if(vv1->IsD()) continue; + Point3x &p1 = vv1->P(); + float d2 = (p1 - p0).Norm(); + if(d2 < min_edge || d2 > max_edge) continue; + + for(int j = k+1; j < n; j++) { + vv2 = targets[j]; + if(vv2->IsD()) continue; + Point3x &p2 = vv2->P(); + float d1 = (p2 - p0).Norm(); + if(d1 < min_edge || d1 > max_edge) continue; + float d0 = (p2 - p1).Norm(); + if(d0 < min_edge || d0 > max_edge) continue; + + Point3x normal = (p1 - p0)^(p2 - p0); + if(normal.dot(p0 - baricenter) < 0) continue; +/* if(use_normals) { + if(normal * vv0->N() < 0) continue; + if(normal * vv1->N() < 0) continue; + if(normal * vv2->N() < 0) continue; + }*/ + + if(!FindSphere(p0, p1, p2, center)) { + continue; + } + + //check no other point inside + int t; + for(t = 0; t < n; t++) { + if((center - targets[t]->P()).Norm() <= radius) + break; + } + if(t < n) { + continue; + } + + //check on the other side there is not a surface + Point3x opposite = center + normal*(((center - p0).dot(normal))*2/normal.SquaredNorm()); + for(t = 0; t < n; t++) { + VertexType &v = *(targets[t]); + if((v.IsV()) && (opposite - v.P()).Norm() <= radius) + break; + } + if(t < n) { + continue; + } + success = true; + i = k = j = n; + } + } + } + + if(!success) { //see bad luck above + continue; + } + Mark(vv0); + Mark(vv1); + Mark(vv2); + + v0 = vv0 - &*this->mesh.vert.begin(); + v1 = vv1 - &*this->mesh.vert.begin(); + v2 = vv2 - &*this->mesh.vert.begin(); + return true; + } + return false; + } + + //select a new vertex, mark as Visited and mark as usedBit all neighbours (less than min_edge) + int Place(FrontEdge &edge,typename AdvancingFront::ResultIterator &touch) { + Point3x v0 = this->mesh.vert[edge.v0].P(); + Point3x v1 = this->mesh.vert[edge.v1].P(); + Point3x v2 = this->mesh.vert[edge.v2].P(); + /* TODO why using the face normals everything goes wrong? should be + exactly the same................................................ + + Point3x &normal = mesh.face[edge.face].N(); ? + */ + + Point3x normal = ((v1 - v0)^(v2 - v0)).Normalize(); + Point3x middle = (v0 + v1)/2; + Point3x center; + + if(!FindSphere(v0, v1, v2, center)) { +// assert(0); + return -1; + } + + Point3x start_pivot = center - middle; + Point3x axis = (v1 - v0); + + ScalarType axis_len = axis.SquaredNorm(); + if(axis_len > 4*radius*radius) { + return -1; + } + axis.Normalize(); + + // r is the radius of the thorus of all possible spheres passing throug v0 and v1 + ScalarType r = sqrt(radius*radius - axis_len/4); + + std::vector targets; + std::vector dists; + std::vector points; + + tri::GetInSphereVertex(this->mesh, grid, middle, r + radius, targets, dists, points); + + if(targets.size() == 0) { + return -1; //this really would be strange but one never knows. + } + + VertexType *candidate = NULL; + ScalarType min_angle = M_PI; + + for(int i = 0; i < static_cast(targets.size()); i++) { + VertexType *v = targets[i]; + int id = v - &*this->mesh.vert.begin(); + if(v->IsD()) continue; + + // this should always be true IsB => IsV , IsV => IsU + if(v->IsB()) assert(v->IsV()); + if(v->IsV()) assert(v->IsUserBit(usedBit)); + + + if(v->IsUserBit(usedBit) && !(v->IsB())) continue; + if(id == edge.v0 || id == edge.v1 || id == edge.v2) continue; + + Point3x p = this->mesh.vert[id].P(); + + /* Find the sphere through v0, p, v1 (store center on end_pivot */ + if(!FindSphere(v0, p, v1, center)) { + continue; + } + + /* Angle between old center and new center */ + ScalarType alpha = Angle(start_pivot, center - middle, axis); + + /* adding a small bias to already chosen vertices. + doesn't solve numerical problems, but helps. */ + // if(this->mesh.vert[id].IsB()) alpha -= 0.001; + + /* Sometimes alpha might be little less then M_PI while it should be 0, + by numerical errors: happens for example pivoting + on the diagonal of a square. */ + +/* if(alpha > 2*M_PI - 0.8) { + // Angle between old center and new *point* + //TODO is this really overshooting? shouldbe enough to alpha -= 2*M_PI + Point3x proj = p - axis * (axis * p - axis * middle); + ScalarType beta = angle(start_pivot, proj - middle, axis); + + if(alpha > beta) alpha -= 2*M_PI; + } */ + if(candidate == NULL || alpha < min_angle) { + candidate = v; + min_angle = alpha; + } + } + if(min_angle >= M_PI - 0.1) { + return -1; + } + + if(candidate == NULL) { + return -1; + } + if(!candidate->IsB()) { + assert((candidate->P() - v0).Norm() > min_edge); + assert((candidate->P() - v1).Norm() > min_edge); + } + + int id = candidate - &*this->mesh.vert.begin(); + assert(id != edge.v0 && id != edge.v1); + + Point3x newnormal = ((candidate->P() - v0)^(v1 - v0)).Normalize(); + if(normal.dot(newnormal) < max_angle || this->nb[id] >= 2) { + return -1; + } + + //test if id is in some border (to return touch + for(std::list::iterator k = this->front.begin(); k != this->front.end(); k++) + { + if((*k).v0 == id) + { + touch.first = AdvancingFront::FRONT; + touch.second = k; + } + } + for(std::list::iterator k = this->deads.begin(); k != this->deads.end(); k++) + { + if((*k).v0 == id) + { + touch.first = AdvancingFront::DEADS; + touch.second = k; + } + } + + //mark vertices close to candidate + Mark(candidate); + return id; + } + + private: + int last_seed; //used for new seeds when front is empty + int usedBit; //use to detect if a vertex has been already processed. + Point3x baricenter;//used for the first seed. + + StaticGrid grid; //lookup grid for points + + + /* returns the sphere touching p0, p1, p2 of radius r such that + the normal of the face points toward the center of the sphere */ + + bool FindSphere(Point3x &p0, Point3x &p1, Point3x &p2, Point3x ¢er) { + //we want p0 to be always the smallest one. + Point3x p[3]; + + if(p0 < p1 && p0 < p2) { + p[0] = p0; + p[1] = p1; + p[2] = p2; + } else if(p1 < p0 && p1 < p2) { + p[0] = p1; + p[1] = p2; + p[2] = p0; + } else { + p[0] = p2; + p[1] = p0; + p[2] = p1; + } + Point3x q1 = p[1] - p[0]; + Point3x q2 = p[2] - p[0]; + + Point3x up = q1^q2; + ScalarType uplen = up.Norm(); + + //the three points are aligned + if(uplen < 0.001*q1.Norm()*q2.Norm()) { + return false; + } + up /= uplen; + + + ScalarType a11 = q1.dot(q1); + ScalarType a12 = q1.dot(q2); + ScalarType a22 = q2.dot(q2); + + ScalarType m = 4*(a11*a22 - a12*a12); + ScalarType l1 = 2*(a11*a22 - a22*a12)/m; + ScalarType l2 = 2*(a11*a22 - a12*a11)/m; + + center = q1*l1 + q2*l2; + ScalarType circle_r = center.Norm(); + if(circle_r > radius) { + return false; //need too big a sphere + } + + ScalarType height = sqrt(radius*radius - circle_r*circle_r); + center += p[0] + up*height; + + return true; + } + + /* compute angle from p to q, using axis for orientation */ + ScalarType Angle(Point3x p, Point3x q, Point3x &axis) { + p.Normalize(); + q.Normalize(); + Point3x vec = p^q; + ScalarType angle = acos(p.dot(q)); + if(vec.dot(axis) < 0) angle = -angle; + if(angle < 0) angle += 2*M_PI; + return angle; + } + + void Mark(VertexType *v) { + std::vector targets; + std::vector points; + std::vector dists; + int n = tri::GetInSphereVertex(this->mesh, grid, v->P(), min_edge, targets, dists, points); + for(int t = 0; t < n; t++) + targets[t]->SetUserBit(usedBit); + v->SetV(); + } +}; + +} //namespace tri +} //namespace vcg +#endif diff --git a/vcg/complex/algorithms/create/emc_lookup_table.h b/vcg/complex/algorithms/create/emc_lookup_table.h new file mode 100644 index 00000000..fde806dd --- /dev/null +++ b/vcg/complex/algorithms/create/emc_lookup_table.h @@ -0,0 +1,928 @@ +/*===========================================================================*\ +* * +* IsoEx * +* Copyright (C) 2002 by Computer Graphics Group, RWTH Aachen * +* www.rwth-graphics.de * +* * +*---------------------------------------------------------------------------* +* * +* License * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Library General Public License as published * +* by the Free Software Foundation, version 2. * +* * +* This library 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 * +* Library General Public License for more details. * +* * +* You should have received a copy of the GNU Library General Public * +* License along with this library; if not, write to the Free Software * +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +* * +\*===========================================================================*/ + +//== TABLES ================================================================== + +#ifndef __VCG_EMC_LOOK_UP_TABLE +#define __VCG_EMC_LOOK_UP_TABLE + +namespace vcg +{ + namespace tri + { + class EMCLookUpTable + { + public: + static const int EdgeTable(unsigned char cubetype) + { + static const int edgeTable[256]= + { + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + return edgeTable[cubetype]; + }; // end of EdgeTable + + //----------------------------------------------------------------------------- + + static int* TriTable(unsigned char cubetype, int u) + { + static int triTable[256][2][17] = + {{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 9, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 10, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 10, 9, 8, 3, 2 , -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 8, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 10 */ + {{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1, 9, 0, 2, 3,11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9, 8, 11, 2, 1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 11,10, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 11, 10, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 11,10, 9, 0, 3, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 15 */ + {{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 8, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 4, 7, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 7, 3, 1, 9, 4, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 20 */ + {{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1, 2,10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 3, 0, 4, 7, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2,10, 9, 0, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1, -1}, + {1, 6, 7, 3, 2,10, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 25 */ + {{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 2, 0, 4, 7,11,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1}}, + + {{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 1, 9, 11, 11,9,4,7, -1, -1, -1, -1, -1 ,-1}}, + + {{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 3, 11,10, 1, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1, -1}, + {1, 6, 1, 0, 4, 7,11,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 30 */ + {{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1, -1}, + {2, 3, 5, 4, 7, 8, 0, 3, 11, 10, 9, -1, -1, -1, -1, -1, -1}}, + + {{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 7,11,10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 1, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 35 */ + {{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 1, 5, 4, 8,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1, 2,10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1}}, + + {{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 0, 2,10, 5,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 10, 5, 3, 4, 8, 3, 5, -1, -1, -1, -1, -1, -1}}, + + /* 40 */ + {{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 0, 8, 11, 2, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 0, 1, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 5, 4, 8,11, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 3,11,10, 1, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}}, + + /* 45 */ + {{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1, -1}, + {2, 3, 5, 4, 9, 5, 1, 0, 8,11, 10, -1, -1, -1, -1, -1, -1}}, + + {{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1, -1}, + {1, 6, 5, 4, 0, 3,11, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5, 4, 8, 11, 10,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 7, 8, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5, 7, 3, 0, 9,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 50 */ + {{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 1, 5, 7, 8, 0,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 7, 8, 9, 5,10, 1, 2, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1, -1}, + { 2, 3, 5,10, 1, 2, 0, 9, 5, 7, 3,-1, -1, -1, -1, -1, -1}}, + + {{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1, -1}, + {1, 6, 2,10, 5, 7, 8, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 55 */ + {{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 2,10, 5, 7, 3,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 7, 8, 9, 5, 3,11, 2, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1, -1}, + {1, 6, 2, 0, 9, 5, 7,11, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 2, 3,11, 8, 0, 1, 5, 7, -1, -1, -1, -1, -1, -1}}, + + {{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5,11, 2, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 60 */ + {{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1, -1}, + {2, 4, 4, 3,11, 10, 1, 5, 7, 8, 9, -1, -1, -1, -1, -1, -1}}, + + {{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1, -1}, + {1, 7, 5, 7, 11,10, 1, 0, 9, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1, -1}, + {1, 7, 11,10,5, 7, 8, 0,3, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 5, 7, 11,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3,10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 65 */ + {{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 9, 8, 3, 5,10, 6, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 2, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 2, 6, 5, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}}, + + /* 70 */ + {{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 2, 6, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1, -1}, + {1, 6, 2, 6, 5, 9, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 2, 3,11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 0, 8, 11, 2, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}}, + + /* 75 */ + {{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 5,10, 6, 2, 1, 9, 8,11, -1, -1, -1, -1, -1, -1}}, + + {{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5, 1, 3, 11,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 0, 8,11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 3, 11, 6, 0, 5, 9, 0, 6, -1, -1, -1, -1}}, + + {{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 6, 5, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 80 */ + {{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 5,10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 4, 7, 3, 0, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 1, 9, 0, 5,10, 6, 8, 4, 7, -1, -1, -1, -1}}, + + {{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1, -1}, + { 2, 3, 5,10, 6, 5, 9, 4, 7, 3, 1,-1, -1, -1, -1, -1, -1}}, + + {{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 2, 6, 5, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}}, + + /* 85 */ + {{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 6, 5, 1, 3, 0, 4, 7, -1, -1, -1, -1, -1, -1}}, + + {{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 8, 4, 7, 5, 9, 0, 2, 6, -1, -1, -1, -1, -1, -1}}, + + {{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1, -1}, + {1, 7, 7, 3, 2, 6, 5, 9, 4,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 3,11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1}}, + + {{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 5,10, 6, 7,11, 2, 0, 4, -1, -1, -1, -1, -1, -1}}, + + /* 90 */ + {{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1}, + {4, 3, 3, 3, 3, 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6}}, + + {{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1, -1}, + {3, 4, 4, 3, 2, 1, 9,11, 4, 7, 11, 9, 5, 10, 6, -1, -1}}, + + {{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 8, 4, 7, 11, 6, 5, 1, 3, -1, -1, -1, -1, -1, -1}}, + + {{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1, -1}, + {1, 7, 5, 1, 0, 4, 7,11, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1, -1}, + {3, 4, 4, 3, 0, 6, 5, 9, 3, 11, 6, 0, 8, 4, 7, -1, -1}}, + + /* 95 */ + {{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 9, 4, 7, 11, 6, 5, 9, 11,-1, -1, -1, -1, -1, -1}}, + + {{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 4, 9, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 4, 9,10, 6, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 6, 4, 0, 1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1, -1}, + {1, 6, 1,10, 6, 4, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 100 */ + {{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 2, 6, 4, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1, -1}, + {2, 3, 5, 3, 0, 8, 9, 1, 2, 6, 4, -1, -1, -1, -1, -1, -1}}, + + {{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 3, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 10, 6, 4, 9,11, 2, 3, -1, -1, -1, -1, -1, -1, -1}}, + + /* 105 */ + {{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 11, 8, 0, 10, 6, 4, 9, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1, -1}, + {2, 3, 5, 3,11, 2, 1, 10,6, 4, 0, -1, -1, -1, -1, -1, -1}}, + + {{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1, -1}, + {1, 7, 6, 4, 8,11, 2, 1,10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1, -1}, + {1, 6, 3,11, 6, 4, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1, -1}, + {1, 7, 8,11, 6, 4, 9, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 110 */ + {{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3,11, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 8, 11, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 9,10, 6, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1, -1}, + {1, 6, 0, 9, 10, 6, 7, 3, -1,-1,-1, -1, -1, -1, -1, -1, -1}}, + + {{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1, -1}, + { 2, 4, 4, 8, 0, 1, 7, 10, 6, 7, 1,-1, -1, -1, -1, -1, -1}}, + + /* 115 */ + {{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 10, 6, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1, -1}, + {1, 6, 1, 2, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1, -1}, + {1, 7, 2, 6, 7, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 7, 8, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 7, 3, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 120 */ + {{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 2, 3,11, 6, 7, 8, 9,10, -1, -1, -1, -1, -1, -1}}, + + {{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1, -1}, + {1, 7, 2, 0, 9,10,6, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1, -1}, + {3, 4, 4, 3, 8, 0, 1, 7, 10, 6, 7, 1, 11, 2, 3, -1, -1}}, + + {{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1, -1}, + { 2, 4, 4, 11, 2, 1,7, 1, 10, 6, 7,-1, -1, -1, -1, -1, -1}}, + + {{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1, -1}, + {1, 7, 8, 9, 1, 3, 11, 6, 7,-1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 125 */ + {{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1, -1}, + {2, 4, 4, 0, 3,11, 6, 7, 8, 0, 6, -1, -1, -1, -1, -1, -1}}, + + {{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 130 */ + {{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 9, 8, 3,11, 7, 6, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 3,10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 1, 2,10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1}}, + + {{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2, 10, 9, 0, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}}, + + /* 135 */ + {{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1, -1}, + {2, 3, 5, 6, 11, 7, 3, 2,10, 9, 8, -1, -1, -1, -1, -1, -1}}, + + {{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 3, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 6, 2, 0, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2, 3, 7, 6, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1, -1}, + {1, 6, 6, 2, 1, 9, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 140 */ + {{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 1, 3, 7, 6,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1, -1}, + { 2, 4, 4, 10, 1, 7, 6, 8, 7, 1, 0,-1, -1, -1, -1, -1, -1}}, + + {{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1, -1}, + {1, 6,10, 9, 0, 3, 7, 6,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 7, 6, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 6, 11, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 145 */ + {{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 4, 6,11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 6,11, 8, 4, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1, -1}, + {1, 6, 6,11, 3, 1, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 6, 11, 8, 4, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 1, 2, 10,11, 3,0,4, 6, -1, -1, -1, -1, -1, -1}}, + + /* 150 */ + {{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 4, 6, 11, 8, 2,10, 9, 0, -1, -1, -1, -1, -1, -1}}, + + {{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1, -1}, + {1, 7, 10,9, 4, 6, 11, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 6, 2, 3, 8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 4, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1, -1}, + {2, 3, 5, 1, 9, 0, 3, 8, 4, 6, 2, -1, -1, -1, -1, -1, -1}}, + + /* 155 */ + {{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 1, 9, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1, -1}, + {1, 6, 1, 3, 8, 4, 6,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5,10, 1,0,4,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1, -1}, + {1, 7, 4, 6, 10, 9, 0,3, 8, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 4, 6, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 160 */ + {{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1}}, + + {{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 0, 1, 5, 4, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1, -1}, + { 2, 3, 5,11, 7, 6, 4, 8, 3, 1, 5,-1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 9, 5, 4,10, 1, 2, 7, 6, 11, -1, -1, -1, -1}}, + + /* 165 */ + {{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1, -1}, + {4, 3, 3, 3, 3, 6,11, 7, 1, 2,10, 0, 8, 3, 4, 9, 5}}, + + {{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1, -1}, + {2, 3, 5, 7, 6, 11, 10, 5, 4, 0, 2,-1, -1, -1, -1, -1, -1}}, + + {{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1, -1}, + {3, 4, 4, 3, 5, 3, 2,10, 4, 8, 3, 5, 6, 11, 7, 6, -1}}, + + {{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2, 3, 7, 6, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 9, 5, 4, 8, 7, 6, 2, 0, -1, -1, -1, -1, -1, -1}}, + + /* 170 */ + {{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1, -1}, + {2, 4, 4, 3, 7, 6, 2, 0, 1, 5, 4, -1, -1, -1, -1, -1, -1}}, + + {{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1, -1}, + {1, 7, 6, 2, 1, 5, 4, 8, 7,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 9, 5, 4, 6,10, 1, 3, 7,-1, -1, -1, -1, -1, -1}}, + + {{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1, -1}, + {3, 4, 4, 3, 0, 8, 7, 1, 6, 10, 1, 7, 9, 5, 4, -1, -1}}, + + {{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1, -1}, + {1, 7, 4, 0, 3, 7, 6, 10, 5, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 175 */ + {{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1, -1}, + {2, 4, 4, 4, 8, 10, 5, 7, 6,10, 8, -1, -1, -1, -1, -1, -1}}, + + {{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5,11, 8, 9, 5, 6,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1, -1}, + {2, 4, 4, 0, 9, 5, 6, 6,11, 3, 0, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1, -1}, + {1, 6, 0, 1, 5, 6,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 6,11, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /*180 */ + {{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 1, 2, 10, 5, 6,11, 8, 9, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1, -1}, + {3, 4, 4, 3, 11, 3,0, 6, 9, 5, 6, 0, 2, 10, 1, 2, 10}}, + + {{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1, -1}, + { 1, 7,11, 8, 0, 2,10, 5, 6,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1, -1}, + {2, 4, 4, 6,11, 3, 5, 10, 5, 3, 2, -1, -1, -1, -1, -1, -1}}, + + {{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1, -1}, + {1, 6, 2, 3, 8, 9, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 185 */ + {{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9, 5, 6, 2, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1, -1}, + {1, 7, 1, 5, 6, 2, 3, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 5, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1, -1}, + {1, 7, 1, 3, 8, 9, 5, 6,10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1, -1}, + { 2, 4, 4, 5, 6, 0, 9, 10, 1, 0, 6, -1, -1, -1, -1, -1, -1}}, + + /* 190 */ + {{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3,10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 5,10, 11, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 5,10,11, 7, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 5, 10, 11, 7, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}}, + + /* 195 */ + {{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1, -1}, + { 2, 4, 4, 10, 11, 7, 5, 1, 9, 8, 3, -1, -1, -1, -1, -1, -1}}, + + {{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 7, 5, 1, 2,11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 0, 8, 3, 2,11, 7, 5,1, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1, -1}, + {1, 6, 2,11, 7, 5, 9, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1, -1}, + {1, 7, 7, 5, 9, 8, 3, 2,11,-1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 200 */ + {{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 7, 5,10, 2,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1, -1}, + {1, 6, 5,10, 2, 0, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1, -1}, + {2, 3, 5, 9, 0, 1, 10, 2, 3, 7, 5, -1, -1, -1, -1, -1, -1}}, + + {{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1, -1}, + {1, 7, 9, 8, 7, 5,10, 2, 1,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 205 */ + {{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 8, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9, 0, 3, 7, 5,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 7, 5, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 10,11, 8, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1, -1}, + {1, 6, 0, 4, 5,10,11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 210 */ + {{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1, -1}, + {2, 3, 5, 0, 1, 9, 4, 5, 10, 11, 8, -1, -1, -1, -1, -1, -1}}, + + {{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1, -1}, + { 1, 7,10, 11, 3, 1, 9,4, 5,-1, -1, -1, -1, -1, -1, -1}}, + + {{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1, -1}, + {1, 6, 2,11, 8, 4, 5, 1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1, -1}, + {1, 7, 0, 4, 5, 1, 2, 11, 3,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1, -1}, + {1, 7, 0, 2,11, 8, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 215 */ + {{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 3, 5, 10, 4, 5, 3, 8,-1, -1, -1, -1, -1, -1}}, + + {{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5,10, 2, 0, 4,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1, -1}, + {3, 4, 4, 3, 3, 5, 10, 2, 8, 4, 5, 3, 0, 1, 9, -1, -1}}, + + {{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1, -1}, + {1, 6,10, 2, 1, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 220 */ + {{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 4, 5, 1, 3,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 4, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1, -1}, + {2, 4, 4, 0, 3, 5, 9, 8, 4, 5, 3, -1, -1, -1, -1, -1, -1}}, + + {{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9,10, 11, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 225 */ + {{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 0, 8, 3, 7, 4, 9, 10, 11, -1, -1, -1, -1, -1, -1}}, + + {{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1, -1}, + {1, 6, 1, 10,11, 7, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1, -1}, + {1, 7, 3, 1,10,11, 7, 4, 8, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 11, 9, 1, 4, 9, 11, 7, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1, -1}, + {3, 4, 4, 3, 1, 2, 11, 9, 7, 4, 9,11, 8, 3, 0, 8, 3}}, + + /* 230 */ + {{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 11, 7, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1, -1}, + { 2, 4, 4, 11, 7, 4, 2, 3, 2, 4, 8,-1, -1, -1, -1, -1, -1}}, + + {{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1, -1}, + {1, 6, 2, 3, 7, 4, 9,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1, -1}, + {1, 7, 9,10, 2, 0, 8, 7, 4,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1, -1}, + {1, 7, 3, 7, 4, 0, 1,10, 2, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 235 */ + {{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1,10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 9, 1, 3, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1, -1}, + {2, 4, 4, 8, 7, 1, 0, 4, 9, 1, 7, -1, -1, -1, -1, -1, -1}}, + + {{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 7, 4, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 240 */ + {{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 8, 9, 10,11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 0, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 1, 10,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 1,10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 1, 2, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 245 */ + {{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 2,11, 9, 1, 3, 0, 9, 11, -1, -1, -1, -1, -1,-1}}, + + {{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 2,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 2, 3, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 0, 9,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 250 */ + {{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 3, 8, 10, 1, 10, 8, 0, -1, -1, -1, -1, -1, -1}}, + + {{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 3, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}} + }; + return &triTable[cubetype][u][0]; + }; // end of TriTable + + + //----------------------------------------------------------------------------- + + + static const int PolyTable(unsigned int cubetype, int u) + { + static const int polyTable[8][16] = + { + {-1}, + {-1}, + {-1}, + {0, 1, 2, -1}, + {0, 1, 2, 2, 3, 0, -1}, + {0, 1, 2, 0, 2, 4, 4, 2, 3, -1}, + {0, 1, 2, 2, 3, 4, 4, 5, 0, 0, 2, 4, -1}, + {0, 1, 5, 0, 5, 6, 1, 2, 5, 4, 5, 3, 2, 3, 5, -1} + }; + return polyTable[cubetype][u]; + }; // end of PolyTable + + //============================================================================= + + }; //end of class EMCLookUpTable + }; // end of namespace tri +}; // end of namespace vcg +#endif // __VCG_EMC_LOOK_UP_TABLE diff --git a/vcg/complex/algorithms/create/extended_marching_cubes.h b/vcg/complex/algorithms/create/extended_marching_cubes.h new file mode 100644 index 00000000..b48c241f --- /dev/null +++ b/vcg/complex/algorithms/create/extended_marching_cubes.h @@ -0,0 +1,462 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/***************************************************************************/ + + +#ifndef __VCG_EXTENDED_MARCHING_CUBES +#define __VCG_EXTENDED_MARCHING_CUBES + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "emc_lookup_table.h" + +namespace vcg +{ + namespace tri + { + // Doxygen documentation + /** \addtogroup trimesh */ + /*@{*/ + + /* + * Cube description: + * 3 ________ 2 _____2__ + * /| /| / | /| + * / | / | 11/ 3 10/ | + * 7 /_______ / | /__6_|__ / |1 + * | | |6 | | | | + * | 0|__|_____|1 | |__|_0|__| + * | / | / 7 8/ 5 / + * | / | / | / | /9 + * |/_______|/ |/___4___|/ + * 4 5 + */ + + //! This class implements the Extended Marching Cubes algorithm. + /*! + * The implementation is enough generic: this class works only on one volume cell for each + * call to ProcessCell. Using the field value at the cell corners, it adds to the + * mesh the triangles set approximating the surface that cross that cell. + * @param TRIMESH_TYPE (Template parameter) the mesh type that will be constructed + * @param WALKER_TYPE (Template parameter) the class that implements the traversal ordering of the volume. + **/ + template + class ExtendedMarchingCubes + { + public: +#if defined(__GNUC__) + typedef unsigned int size_t; +#else +#ifdef _WIN64 + typedef unsigned __int64 size_t; +#else + typedef _W64 unsigned int size_t; +#endif +#endif + typedef typename vcg::tri::Allocator< TRIMESH_TYPE > AllocatorType; + typedef typename TRIMESH_TYPE::ScalarType ScalarType; + typedef typename TRIMESH_TYPE::VertexType VertexType; + typedef typename TRIMESH_TYPE::VertexPointer VertexPointer; + typedef typename TRIMESH_TYPE::VertexIterator VertexIterator; + typedef typename TRIMESH_TYPE::FaceType FaceType; + typedef typename TRIMESH_TYPE::FacePointer FacePointer; + typedef typename TRIMESH_TYPE::FaceIterator FaceIterator; + typedef typename TRIMESH_TYPE::CoordType CoordType; + typedef typename TRIMESH_TYPE::CoordType* CoordPointer; + + struct LightEdge + { + LightEdge(size_t _face, size_t _edge):face(_face), edge(_edge) { } + size_t face, edge; + }; + + /*! + * Constructor + * \param mesh The mesh that will be constructed + * \param volume The volume describing the field + * \param walker The class implementing the traversal policy + * \param angle The feature detection threshold misuring the sharpness of a feature(default is 30 degree) + */ + ExtendedMarchingCubes(TRIMESH_TYPE &mesh, WALKER_TYPE &walker, ScalarType angle=30) + { + _mesh = &mesh; + _walker = &walker; + _featureAngle = vcg::math::ToRad(angle); + _initialized = _finalized = false; + }; + + /*! + * Execute the initialiazation. + * This method must be executed before the first call to ApplyEMC + */ + void Initialize() + { + assert(!_initialized && !_finalized); + _featureFlag = VertexType::NewBitFlag(); + _initialized = true; + }; + + /*! + * + * This method must be executed after the last call to ApplyEMC + */ + void Finalize() + { + assert(_initialized && !_finalized); + FlipEdges(); + + VertexIterator v_iter = _mesh->vert.begin(); + VertexIterator v_end = _mesh->vert.end(); + for ( ; v_iter!=v_end; v_iter++) + v_iter->ClearUserBit( _featureFlag ); + VertexType::DeleteBitFlag( _featureFlag ); + _featureFlag = 0; + _mesh = NULL; + _walker = NULL; + _finalized = true; + }; + + /*! + * Apply the extended marching cubes algorithm to the volume cell identified by the two points min and max. + * All the three coordinates of the first point must be smaller than the respectives three coordinatas of the second point. + * \param min the first point + * \param max the second point + */ + void ProcessCell(const vcg::Point3i &min, const vcg::Point3i &max) + { + assert(_initialized && !_finalized); + assert(min[0]V(_corners[0].X(), _corners[0].Y(), _corners[0].Z())) >= 0) cubetype+= 1; + if ((_field[1] = _walker->V(_corners[1].X(), _corners[1].Y(), _corners[1].Z())) >= 0) cubetype+= 2; + if ((_field[2] = _walker->V(_corners[2].X(), _corners[2].Y(), _corners[2].Z())) >= 0) cubetype+= 4; + if ((_field[3] = _walker->V(_corners[3].X(), _corners[3].Y(), _corners[3].Z())) >= 0) cubetype+= 8; + if ((_field[4] = _walker->V(_corners[4].X(), _corners[4].Y(), _corners[4].Z())) >= 0) cubetype+= 16; + if ((_field[5] = _walker->V(_corners[5].X(), _corners[5].Y(), _corners[5].Z())) >= 0) cubetype+= 32; + if ((_field[6] = _walker->V(_corners[6].X(), _corners[6].Y(), _corners[6].Z())) >= 0) cubetype+= 64; + if ((_field[7] = _walker->V(_corners[7].X(), _corners[7].Y(), _corners[7].Z())) >= 0) cubetype+=128; + + if (cubetype==0 || cubetype==255) + return; + + size_t vertices_idx[12]; + memset(vertices_idx, -1, 12*sizeof(size_t)); + int code = EMCLookUpTable::EdgeTable(cubetype); + VertexPointer vp = NULL; + if ( 1&code ) { _walker->GetXIntercept(_corners[0], _corners[1], vp); vertices_idx[ 0] = vp - &_mesh->vert[0]; } + if ( 2&code ) { _walker->GetYIntercept(_corners[1], _corners[2], vp); vertices_idx[ 1] = vp - &_mesh->vert[0]; } + if ( 4&code ) { _walker->GetXIntercept(_corners[3], _corners[2], vp); vertices_idx[ 2] = vp - &_mesh->vert[0]; } + if ( 8&code ) { _walker->GetYIntercept(_corners[0], _corners[3], vp); vertices_idx[ 3] = vp - &_mesh->vert[0]; } + if ( 16&code ) { _walker->GetXIntercept(_corners[4], _corners[5], vp); vertices_idx[ 4] = vp - &_mesh->vert[0]; } + if ( 32&code ) { _walker->GetYIntercept(_corners[5], _corners[6], vp); vertices_idx[ 5] = vp - &_mesh->vert[0]; } + if ( 64&code ) { _walker->GetXIntercept(_corners[7], _corners[6], vp); vertices_idx[ 6] = vp - &_mesh->vert[0]; } + if ( 128&code ) { _walker->GetYIntercept(_corners[4], _corners[7], vp); vertices_idx[ 7] = vp - &_mesh->vert[0]; } + if ( 256&code ) { _walker->GetZIntercept(_corners[0], _corners[4], vp); vertices_idx[ 8] = vp - &_mesh->vert[0]; } + if ( 512&code ) { _walker->GetZIntercept(_corners[1], _corners[5], vp); vertices_idx[ 9] = vp - &_mesh->vert[0]; } + if (1024&code ) { _walker->GetZIntercept(_corners[2], _corners[6], vp); vertices_idx[10] = vp - &_mesh->vert[0]; } + if (2048&code ) { _walker->GetZIntercept(_corners[3], _corners[7], vp); vertices_idx[11] = vp - &_mesh->vert[0]; } + + int m, n, vertices_num; + int components = EMCLookUpTable::TriTable(cubetype, 1)[0]; //unsigned int components = triTable[cubetype][1][0]; + int *indices = &EMCLookUpTable::TriTable(cubetype, 1)[components+1]; //int *indices = &EMCLookUpTable::TriTable(cubetype, 1, components+1); + + std::vector< size_t > vertices_list; + for (m=1; m<=components; m++) + { + // current sheet contains vertices_num vertices + vertices_num = EMCLookUpTable::TriTable(cubetype, 1)[m]; //vertices_num = triTable[cubetype][1][m]; + + // collect vertices + vertices_list.clear(); + for (n=0; n create triangle fan around feature vertex + size_t feature_idx = feature - &_mesh->vert[0]; + size_t face_idx = _mesh->face.size(); + vertices_list.push_back( vertices_list[0] ); + AllocatorType::AddFaces(*_mesh, (int) vertices_num); + for (int j=0; jface[face_idx].V(0) = &_mesh->vert[ vertices_list[j ] ]; + _mesh->face[face_idx].V(1) = &_mesh->vert[ vertices_list[j+1] ]; + _mesh->face[face_idx].V(2) = &_mesh->vert[ feature_idx ]; + } + } + else + { + // no feature -> old marching cubes triangle table + for (int j=0; EMCLookUpTable::PolyTable(vertices_num, j) != -1; j+=3) //for (int j=0; polyTable[vertices_num][j] != -1; j+=3) + { + size_t face_idx = _mesh->face.size(); + AllocatorType::AddFaces(*_mesh, 1); + //_mesh->face[ face_idx].V(0) = &_mesh->vert[ vertices_idx[ indices[ polyTable[vertices_num][j ] ] ] ]; + //_mesh->face[ face_idx].V(1) = &_mesh->vert[ vertices_idx[ indices[ polyTable[vertices_num][j+1] ] ] ]; + //_mesh->face[ face_idx].V(2) = &_mesh->vert[ vertices_idx[ indices[ polyTable[vertices_num][j+2] ] ] ]; + _mesh->face[ face_idx].V(0) = &_mesh->vert[ vertices_idx[ indices[ EMCLookUpTable::PolyTable(vertices_num, j ) ] ] ]; + _mesh->face[ face_idx].V(1) = &_mesh->vert[ vertices_idx[ indices[ EMCLookUpTable::PolyTable(vertices_num, j+1) ] ] ]; + _mesh->face[ face_idx].V(2) = &_mesh->vert[ vertices_idx[ indices[ EMCLookUpTable::PolyTable(vertices_num, j+2) ] ] ]; + } + } + indices += vertices_num; + + } + }; // end of ApplyEMC + + private: + /*! + */ + WALKER_TYPE *_walker; + /*! + */ + TRIMESH_TYPE *_mesh; + /*! + */ + bool _initialized;; + /*! + */ + bool _finalized; + /*! + * The feature detection threshold misuring the sharpness of a feature + */ + ScalarType _featureAngle; + /*! + * The flag used for marking the feature vertices. + */ + int _featureFlag; + /*! + * Array of the 8 corners of the volume cell being processed + */ + vcg::Point3i _corners[8]; + /*! + * The field value at the cell corners + */ + ScalarType _field[8]; + + + /*! + * Tests if the surface patch crossing the current cell contains a sharp feature + * \param vertices_idx The list of vertex indices intersecting the edges of the current cell + * \return The pointer to the new Vertex if a feature is detected; NULL otherwise. + */ + VertexPointer FindFeature(const std::vector &vertices_idx) + { + unsigned int i, j, rank; + size_t vertices_num = (size_t) vertices_idx.size(); + + CoordType *points = new CoordType[ vertices_num ]; + CoordType *normals = new CoordType[ vertices_num ]; + Box3 bb; + for (i=0; ivert[ vertices_idx[i] ].P(); + normals[i].Import(_mesh->vert[ vertices_idx[i] ].N()); + bb.Add(points[i]); + } + + // move barycenter of points into (0, 0, 0) + CoordType center((ScalarType) 0.0, (ScalarType) 0.0, (ScalarType) 0.0); + for (i=0; i cos(_featureAngle)) + return NULL; // invalid vertex + + // ok, we have a feature: is it edge or corner, i.e. rank 2 or 3 ? + axis.Normalize(); + for (minC=1.0, maxC=-1.0, i=0; i maxC) maxC = c; + } + c = std::max< double >(fabs(minC), fabs(maxC)); + c = sqrt(1.0-c*c); + rank = (c > cos(_featureAngle) ? 2 : 3); + + // setup linear system (find intersection of tangent planes) + vcg::ndim::Matrix A((unsigned int) vertices_num, 3); + double *b = new double[ vertices_num ]; + for (i=0; i V(3, 3); + double *w = new double[vertices_num]; + vcg::SingularValueDecomposition< typename vcg::ndim::Matrix > (A, w, V, LeaveUnsorted, 100); + + // rank == 2 -> suppress smallest singular value + if (rank == 2) + { + double smin = DBL_MAX; // the max value, as defined in + unsigned int sminid = 0; + unsigned int srank = std::min< unsigned int >(vertices_num, 3u); + + for (i=0; i least squares, least norm solution x + double *x = new double[3]; + vcg::SingularValueBacksubstitution< vcg::ndim::Matrix >(A, w, V, x, b); + + // transform x to world coords + CoordType point((ScalarType) x[0], (ScalarType) x[1], (ScalarType) x[2]); + point += center; + + // Safety check if the feature point found by svd is + // out of the bbox of the vertices perhaps it is better to put it back in the center... + if(!bb.IsIn(point)) point = center; + + // insert the feature-point + VertexPointer mean_point = &*AllocatorType::AddVertices( *_mesh, 1); + mean_point->SetUserBit(_featureFlag); + mean_point->P() = point; + mean_point->N().SetZero(); + delete []x; + delete []points; + delete []normals; + return mean_point; + } // end of FindFeature + + /*! + * Postprocessing step performed during the finalization tha flip some of the mesh edges. + * The flipping criterion is quite simple: each edge is flipped if it will connect two + * feature samples after the flip. + */ + void FlipEdges() + { + size_t i; + std::vector< LightEdge > edges; + FaceIterator f_iter = _mesh->face.begin(); + FaceIterator f_end = _mesh->face.end(); + for (i=0; f_iter!=f_end; f_iter++, i++) + { + if (f_iter->V(1) > f_iter->V(0)) edges.push_back( LightEdge(i,0) ); + if (f_iter->V(2) > f_iter->V(1)) edges.push_back( LightEdge(i,1) ); + if (f_iter->V(0) > f_iter->V(2)) edges.push_back( LightEdge(i,2) ); + } + vcg::tri::UpdateTopology< TRIMESH_TYPE >::FaceFace( *_mesh ); + + // Select all the triangles that has a vertex shared with a non manifold edge. + int nonManifEdge = tri::Clean< TRIMESH_TYPE >::CountNonManifoldEdgeFF(*_mesh,true); + if(nonManifEdge >0) + tri::UpdateSelection< TRIMESH_TYPE >::FaceFromVertexLoose(*_mesh); + //qDebug("Got %i non manif edges",nonManifEdge); + + typename std::vector< LightEdge >::iterator e_it = edges.begin(); + typename std::vector< LightEdge >::iterator e_end = edges.end(); + + FacePointer g, f; + int w, z; + for( ; e_it!=e_end; e_it++) + { + f = &_mesh->face[e_it->face]; + z = (int) e_it->edge; + +// v2------v1 swap the diagonal only if v2 and v3 are feature and v0 and v1 are not. +// | / | +// | / | +// v0------v3 + if (!(f->IsS()) && vcg::face::CheckFlipEdge< FaceType >(*f, z)) + { + VertexPointer v0, v1, v2, v3; + v0 = f->V(z); + v1 = f->V1(z); + v2 = f->V2(z); + g = f->FFp(z); + w = f->FFi(z); + v3 = g->V2(w); + bool b0, b1, b2, b3; + b0 = !v0->IsUserBit(_featureFlag) ; + b1 = !v1->IsUserBit(_featureFlag) ; + b2 = v2->IsUserBit(_featureFlag) ; + b3 = v3->IsUserBit(_featureFlag) ; + if( b0 && b1 && b2 && b3) + vcg::face::FlipEdge< FaceType >(*f, z); + + } // end if (vcg::face::CheckFlipEdge< _Face >(*f, z)) + } // end for( ; e_it!=e_end; e_it++) + }; //end of FlipEdges + }; // end of class ExtendedMarchingCubes + // /*! @} */ + // end of Doxygen documentation + + } // end of namespace tri +}; // end of namespace vcg + +#endif // __VCG_EXTENDED_MARCHING_CUBES diff --git a/vcg/complex/algorithms/create/marching_cubes.h b/vcg/complex/algorithms/create/marching_cubes.h new file mode 100644 index 00000000..1d732ccc --- /dev/null +++ b/vcg/complex/algorithms/create/marching_cubes.h @@ -0,0 +1,730 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/***************************************************************************/ + +#ifndef __VCG_MARCHING_CUBES +#define __VCG_MARCHING_CUBES + +#include +#include +#include +#include "mc_lookup_table.h" + +namespace vcg +{ + namespace tri + { + // Doxygen documentation + /** \addtogroup trimesh */ + /*@{*/ + + /* + * Cube description: + * 3 ________ 2 _____2__ + * /| /| / | /| + * / | / | 11/ 3 10/ | + * 7 /_______ / | /__6_|__ / |1 + * | | |6 | | | | | + * | 0|__|_____|1 | |__|__0__| + * | / | / 7 8/ 5 / + * | / | / | / | /9 + * |/_______|/ |/___4___|/ + * 4 5 + */ + + //! This class implements the Marching Cubes algorithm. + /*! + * The implementation is enough generic: this class works only on one volume cell for each + * call to ProcessCell. Using the field value at the cell corners, it adds to the + * mesh the triangles set approximating the surface that cross that cell. The ambiguities + * are resolved using an enhanced topologically controlled lookup table. + * @param TRIMESH_TYPE (Template parameter) the mesh type that will be constructed + * @param WALKER_TYPE (Template parameter) the class that implement the traversal ordering of the volume + **/ + template + class MarchingCubes + { + public: + enum Dimension {X, Y, Z}; + +#if defined(__GNUC__) + typedef unsigned int size_t; +#else +#ifdef _WIN64 + typedef unsigned __int64 size_t; +#else + typedef _W64 unsigned int size_t; +#endif +#endif + typedef typename vcg::tri::Allocator< TRIMESH_TYPE > AllocatorType; + typedef typename TRIMESH_TYPE::ScalarType ScalarType; + typedef typename TRIMESH_TYPE::VertexType VertexType; + typedef typename TRIMESH_TYPE::VertexPointer VertexPointer; + typedef typename TRIMESH_TYPE::VertexIterator VertexIterator; + typedef typename TRIMESH_TYPE::FaceType FaceType; + typedef typename TRIMESH_TYPE::FacePointer FacePointer; + typedef typename TRIMESH_TYPE::FaceIterator FaceIterator; + typedef typename TRIMESH_TYPE::CoordType CoordType; + typedef typename TRIMESH_TYPE::CoordType* CoordPointer; + + /*! + * Constructor + * \param mesh the mesh that will be constructed + * \param walker the class implementing the traversal policy + */ + MarchingCubes(TRIMESH_TYPE &mesh, WALKER_TYPE &walker) + { + _mesh = &mesh; + _walker = &walker; + }; + + /*! + * Execute the initialiazation. + * This method must be executed before the first call to ApplyMC + */ + void Initialize() + { + _mesh->Clear(); + }; // end of Initialize() + + /*! + * + * This method must be executed after the last call to ApplyMC + */ + void Finalize() + { + _mesh = NULL; + _walker = NULL; + }; // end of Finalize() + + /*! + * Apply the marching cubes algorithm to the volume cell identified by the two points min and max. + * All the three coordinates of the first point must be smaller than the respectives three coordinatas of the second point. + * \param min the first point + * \param max the second point + */ + void ProcessCell(const vcg::Point3i &min, const vcg::Point3i &max) + { + _case = _subconfig = _config = -1; + assert(min[0]V( _corners[i].X(), _corners[i].Y(), _corners[i].Z() ); + + unsigned char cubetype = 0; + for (int i=0; i<8; i++) + if (_field[i]>0) cubetype += 1<= 0 ; // face and A invert signs + }; // end of TestFace + + + /*! + * Tests if the components of the tesselation of the cube should be connected + * through the interior of the cube + */ + inline bool TestInterior(signed char s) + { + ScalarType t, At=0, Bt=0, Ct=0, Dt=0, a, b ; + char test = 0 ; + char edge = -1 ; // reference edge of the triangulation + + switch( _case ) + { + case 4 : + case 10 : + { + a = (_field[4]-_field[0])*(_field[6]-_field[2]) - (_field[7]-_field[3])*(_field[5]-_field[1]); + b = _field[2]*(_field[4]-_field[0])+_field[0]*(_field[6]-_field[2])-_field[1]*(_field[7]-_field[3])-_field[3]*(_field[5]-_field[1]); + t = - b / (2*a) ; + if( t<0 || t>1 ) + return s>0 ; + + At = _field[0] + ( _field[4] - _field[0] ) * t ; + Bt = _field[3] + ( _field[7] - _field[3] ) * t ; + Ct = _field[2] + ( _field[6] - _field[2] ) * t ; + Dt = _field[1] + ( _field[5] - _field[1] ) * t ; + break ; + } + case 6 : + case 7 : + case 12 : + case 13 : + switch( _case ) + { + case 6 : edge = MCLookUpTable::Test6 (_config, 2) ; break ; + case 7 : edge = MCLookUpTable::Test7 (_config, 4) ; break ; + case 12 : edge = MCLookUpTable::Test12(_config, 3) ; break ; + case 13 : edge = MCLookUpTable::Tiling13_5_1(_config, _subconfig)[0] ; break ; + } + switch( edge ) + { + case 0 : + t = _field[0] / ( _field[0] - _field[1] ) ; + At = 0 ; + Bt = _field[3] + ( _field[2] - _field[3] ) * t ; + Ct = _field[7] + ( _field[6] - _field[7] ) * t ; + Dt = _field[4] + ( _field[5] - _field[4] ) * t ; + break ; + case 1 : + t = _field[1] / ( _field[1] - _field[2] ) ; + At = 0 ; + Bt = _field[0] + ( _field[3] - _field[0] ) * t ; + Ct = _field[4] + ( _field[7] - _field[4] ) * t ; + Dt = _field[5] + ( _field[6] - _field[5] ) * t ; + break ; + case 2 : + t = _field[2] / ( _field[2] - _field[3] ) ; + At = 0 ; + Bt = _field[1] + ( _field[0] - _field[1] ) * t ; + Ct = _field[5] + ( _field[4] - _field[5] ) * t ; + Dt = _field[6] + ( _field[7] - _field[6] ) * t ; + break ; + case 3 : + t = _field[3] / ( _field[3] - _field[0] ) ; + At = 0 ; + Bt = _field[2] + ( _field[1] - _field[2] ) * t ; + Ct = _field[6] + ( _field[5] - _field[6] ) * t ; + Dt = _field[7] + ( _field[4] - _field[7] ) * t ; + break ; + case 4 : + t = _field[4] / ( _field[4] - _field[5] ) ; + At = 0 ; + Bt = _field[7] + ( _field[6] - _field[7] ) * t ; + Ct = _field[3] + ( _field[2] - _field[3] ) * t ; + Dt = _field[0] + ( _field[1] - _field[0] ) * t ; + break ; + case 5 : + t = _field[5] / ( _field[5] - _field[6] ) ; + At = 0 ; + Bt = _field[4] + ( _field[7] - _field[4] ) * t ; + Ct = _field[0] + ( _field[3] - _field[0] ) * t ; + Dt = _field[1] + ( _field[2] - _field[1] ) * t ; + break ; + case 6 : + t = _field[6] / ( _field[6] - _field[7] ) ; + At = 0 ; + Bt = _field[5] + ( _field[4] - _field[5] ) * t ; + Ct = _field[1] + ( _field[0] - _field[1] ) * t ; + Dt = _field[2] + ( _field[3] - _field[2] ) * t ; + break ; + case 7 : + t = _field[7] / ( _field[7] - _field[4] ) ; + At = 0 ; + Bt = _field[6] + ( _field[5] - _field[6] ) * t ; + Ct = _field[2] + ( _field[1] - _field[2] ) * t ; + Dt = _field[3] + ( _field[0] - _field[3] ) * t ; + break ; + case 8 : + t = _field[0] / ( _field[0] - _field[4] ) ; + At = 0 ; + Bt = _field[3] + ( _field[7] - _field[3] ) * t ; + Ct = _field[2] + ( _field[6] - _field[2] ) * t ; + Dt = _field[1] + ( _field[5] - _field[1] ) * t ; + break ; + case 9 : + t = _field[1] / ( _field[1] - _field[5] ) ; + At = 0 ; + Bt = _field[0] + ( _field[4] - _field[0] ) * t ; + Ct = _field[3] + ( _field[7] - _field[3] ) * t ; + Dt = _field[2] + ( _field[6] - _field[2] ) * t ; + break ; + case 10 : + t = _field[2] / ( _field[2] - _field[6] ) ; + At = 0 ; + Bt = _field[1] + ( _field[5] - _field[1] ) * t ; + Ct = _field[0] + ( _field[4] - _field[0] ) * t ; + Dt = _field[3] + ( _field[7] - _field[3] ) * t ; + break ; + case 11 : + t = _field[3] / ( _field[3] - _field[7] ) ; + At = 0 ; + Bt = _field[2] + ( _field[6] - _field[2] ) * t ; + Ct = _field[1] + ( _field[5] - _field[1] ) * t ; + Dt = _field[0] + ( _field[4] - _field[0] ) * t ; + break ; + default: { assert(false); /* Invalid edge */ break ; } + } + break ; + + default : assert(false); /* Invalid ambiguous case */ break; + } + + if( At >= 0 ) test ++ ; + if( Bt >= 0 ) test += 2 ; + if( Ct >= 0 ) test += 4 ; + if( Dt >= 0 ) test += 8 ; + switch( test ) + { + case 0 : return s>0 ; + case 1 : return s>0 ; + case 2 : return s>0 ; + case 3 : return s>0 ; + case 4 : return s>0 ; + case 5 : if( At * Ct < Bt * Dt ) return s>0 ; break ; + case 6 : return s>0 ; + case 7 : return s<0 ; + case 8 : return s>0 ; + case 9 : return s>0 ; + case 10 : if( At * Ct >= Bt * Dt ) return s>0 ; break ; + case 11 : return s<0 ; + case 12 : return s>0 ; + case 13 : return s<0 ; + case 14 : return s<0 ; + case 15 : return s<0 ; + } + return s<0 ; + }; //end of TestInterior + + /*! + * Adds a vertex inside the current cube + * \param v The pointer to the new vertex along the edge + */ + inline void ComputeCVertex(VertexPointer &v12) + { + v12 = &*AllocatorType::AddVertices(*_mesh, 1); + v12->P() = CoordType(0.0, 0.0, 0.0); + + unsigned int count = 0; + VertexPointer v = NULL; + if (_walker->Exist(_corners[0], _corners[1], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[1], _corners[2], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[3], _corners[2], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[0], _corners[3], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[4], _corners[5], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[5], _corners[6], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[7], _corners[6], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[4], _corners[7], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[0], _corners[4], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[1], _corners[5], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[2], _corners[6], v) ) + { + count++; + v12->P() += v->P(); + } + if (_walker->Exist(_corners[3], _corners[7], v) ) + { + count++; + v12->P() += v->P(); + } + v12->P() /= (float) count; + } // end of AddCVertex + /*! + * Adds new triangles to the mesh + * \param vertices_list The list of vertex indices + * \param n The number of triangles that will be added to the mesh + * \param v12 The pointer to the vertex inside the current cell + */ + inline void AddTriangles(const char *vertices_list, char n, VertexPointer v12=NULL) + { + VertexPointer vp = NULL; + size_t face_idx = _mesh->face.size(); + size_t v12_idx = -1; + size_t vertices_idx[3]; + if (v12 != NULL) v12_idx = v12 - &_mesh->vert[0]; + AllocatorType::AddFaces(*_mesh, (int) n); + + for (int trig=0; trig<3*n; face_idx++ ) + { + vp = NULL; + memset(vertices_idx, -1, 3*sizeof(size_t)); + for (int vert=0; vert<3; vert++, trig++) //ok + { + + switch ( vertices_list[trig] ) + { + case 0: { _walker->GetXIntercept(_corners[0], _corners[1], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 1: { _walker->GetYIntercept(_corners[1], _corners[2], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 2: { _walker->GetXIntercept(_corners[3], _corners[2], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 3: { _walker->GetYIntercept(_corners[0], _corners[3], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 4: { _walker->GetXIntercept(_corners[4], _corners[5], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 5: { _walker->GetYIntercept(_corners[5], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 6: { _walker->GetXIntercept(_corners[7], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 7: { _walker->GetYIntercept(_corners[4], _corners[7], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 8: { _walker->GetZIntercept(_corners[0], _corners[4], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 9: { _walker->GetZIntercept(_corners[1], _corners[5], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 10: { _walker->GetZIntercept(_corners[2], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 11: { _walker->GetZIntercept(_corners[3], _corners[7], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } + case 12: { assert(v12 != NULL); vertices_idx[vert] = v12_idx; break; } + default: { assert(false); /* Invalid edge identifier */ } + } // end of switch + + // Note that vp can be zero if we are in case 12 and that vertices_idx is surely >0 so the following assert has to be corrected as below. + // assert((vp - &_mesh->vert[0])>=0 && vertices_idx[vert]<_mesh->vert.size()); + assert(vertices_idx[vert]<_mesh->vert.size()); + } // end for (int vert=0 ...) + + _mesh->face[face_idx].V(0) = &_mesh->vert[vertices_idx[0]]; + _mesh->face[face_idx].V(1) = &_mesh->vert[vertices_idx[1]]; + _mesh->face[face_idx].V(2) = &_mesh->vert[vertices_idx[2]]; + } // end for (int trig=0...) + }; // end of AddTriangles + + + }; // end of class MarchingCubes + + /*! @} */ + //end of Doxygen documentation + + }; // end of namespace tri +}; // end of namespace vcg + +#endif //__VCG_MARCHING_CUBES diff --git a/vcg/complex/algorithms/create/mc_lookup_table.h b/vcg/complex/algorithms/create/mc_lookup_table.h new file mode 100644 index 00000000..12669550 --- /dev/null +++ b/vcg/complex/algorithms/create/mc_lookup_table.h @@ -0,0 +1,2560 @@ +/** +* @file LookUpTable.h +* @author Thomas Lewiner +* @author Math Dept, PUC-Rio +* @version 0.2 +* @date 12/08/2002 +* +* @brief LookUpTable for the MarchingCubes 33 Algorithm +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) +* for more details. +*/ +//________________________________________________ + + +#ifndef __VCG_MC_LOOK_UP_TABLE +#define __VCG_MC_LOOK_UP_TABLE + +namespace vcg +{ + namespace tri + { + class MCLookUpTable + { + public: + //_____________________________________________________________________________ + /** + * \brief case mapping + * For each of the possible vertex states listed in this table there is a + * specific triangulation of the edge intersection points. The table lists + * all of them in the form of 0-5 edge triples with the list terminated by + * the invalid value -1. For example: case[3] list the 2 triangles + * formed when cube[0] and cube[1] are inside of the surface, but the rest of + * the cube is not. + * + * Cube description: + * 7 ________ 6 _____6__ ________ + * /| /| 7/| /| /| /| + * / | / | / | /5 | / 6 / | + * 4 /_______ / | /__4____ / 10 /_______3/ | + * | | |5 | | 11 | | | | | 2 | + * | 3|__|_____|2 | |__|__2__| | 4 |__|_____| + * | / | / 8 3/ 9 / | / | / + * | / | / | / | /1 | / 5 / + * |/_______|/ |/___0___|/ |/_1_____|/ + * 0 1 0 1 + */ + //----------------------------------------------------------------------------- + static char Cases(unsigned char cubetype, unsigned char u) + { + static const char cases[256][2] = { + /* 0: */ { 0, -1 }, + /* 1: 0, */ { 1, 0 }, + /* 2: 1, */ { 1, 1 }, + /* 3: 0, 1, */ { 2, 0 }, + /* 4: 2, */ { 1, 2 }, + /* 5: 0, 2, */ { 3, 0 }, + /* 6: 1, 2, */ { 2, 3 }, + /* 7: 0, 1, 2, */ { 5, 0 }, + /* 8: 3, */ { 1, 3 }, + /* 9: 0, 3, */ { 2, 1 }, + /* 10: 1, 3, */ { 3, 3 }, + /* 11: 0, 1, 3, */ { 5, 1 }, + /* 12: 2, 3, */ { 2, 5 }, + /* 13: 0, 2, 3, */ { 5, 4 }, + /* 14: 1, 2, 3, */ { 5, 9 }, + /* 15: 0, 1, 2, 3, */ { 8, 0 }, + /* 16: 4, */ { 1, 4 }, + /* 17: 0, 4, */ { 2, 2 }, + /* 18: 1, 4, */ { 3, 4 }, + /* 19: 0, 1, 4, */ { 5, 2 }, + /* 20: 2, 4, */ { 4, 2 }, + /* 21: 0, 2, 4, */ { 6, 2 }, + /* 22: 1, 2, 4, */ { 6, 9 }, + /* 23: 0, 1, 2, 4, */ { 11, 0 }, + /* 24: 3, 4, */ { 3, 8 }, + /* 25: 0, 3, 4, */ { 5, 5 }, + /* 26: 1, 3, 4, */ { 7, 3 }, + /* 27: 0, 1, 3, 4, */ { 9, 1 }, + /* 28: 2, 3, 4, */ { 6, 16 }, + /* 29: 0, 2, 3, 4, */ { 14, 3 }, + /* 30: 1, 2, 3, 4, */ { 12, 12 }, + /* 31: 0, 1, 2, 3, 4, */ { 5, 24 }, + /* 32: 5, */ { 1, 5 }, + /* 33: 0, 5, */ { 3, 1 }, + /* 34: 1, 5, */ { 2, 4 }, + /* 35: 0, 1, 5, */ { 5, 3 }, + /* 36: 2, 5, */ { 3, 6 }, + /* 37: 0, 2, 5, */ { 7, 0 }, + /* 38: 1, 2, 5, */ { 5, 10 }, + /* 39: 0, 1, 2, 5, */ { 9, 0 }, + /* 40: 3, 5, */ { 4, 3 }, + /* 41: 0, 3, 5, */ { 6, 4 }, + /* 42: 1, 3, 5, */ { 6, 11 }, + /* 43: 0, 1, 3, 5, */ { 14, 1 }, + /* 44: 2, 3, 5, */ { 6, 17 }, + /* 45: 0, 2, 3, 5, */ { 12, 4 }, + /* 46: 1, 2, 3, 5, */ { 11, 6 }, + /* 47: 0, 1, 2, 3, 5, */ { 5, 25 }, + /* 48: 4, 5, */ { 2, 8 }, + /* 49: 0, 4, 5, */ { 5, 7 }, + /* 50: 1, 4, 5, */ { 5, 12 }, + /* 51: 0, 1, 4, 5, */ { 8, 1 }, + /* 52: 2, 4, 5, */ { 6, 18 }, + /* 53: 0, 2, 4, 5, */ { 12, 5 }, + /* 54: 1, 2, 4, 5, */ { 14, 7 }, + /* 55: 0, 1, 2, 4, 5, */ { 5, 28 }, + /* 56: 3, 4, 5, */ { 6, 21 }, + /* 57: 0, 3, 4, 5, */ { 11, 4 }, + /* 58: 1, 3, 4, 5, */ { 12, 15 }, + /* 59: 0, 1, 3, 4, 5, */ { 5, 30 }, + /* 60: 2, 3, 4, 5, */ { 10, 5 }, + /* 61: 0, 2, 3, 4, 5, */ { 6, 32 }, + /* 62: 1, 2, 3, 4, 5, */ { 6, 39 }, + /* 63: 0, 1, 2, 3, 4, 5, */ { 2, 12 }, + /* 64: 6, */ { 1, 6 }, + /* 65: 0, 6, */ { 4, 0 }, + /* 66: 1, 6, */ { 3, 5 }, + /* 67: 0, 1, 6, */ { 6, 0 }, + /* 68: 2, 6, */ { 2, 6 }, + /* 69: 0, 2, 6, */ { 6, 3 }, + /* 70: 1, 2, 6, */ { 5, 11 }, + /* 71: 0, 1, 2, 6, */ { 14, 0 }, + /* 72: 3, 6, */ { 3, 9 }, + /* 73: 0, 3, 6, */ { 6, 5 }, + /* 74: 1, 3, 6, */ { 7, 4 }, + /* 75: 0, 1, 3, 6, */ { 12, 1 }, + /* 76: 2, 3, 6, */ { 5, 14 }, + /* 77: 0, 2, 3, 6, */ { 11, 3 }, + /* 78: 1, 2, 3, 6, */ { 9, 4 }, + /* 79: 0, 1, 2, 3, 6, */ { 5, 26 }, + /* 80: 4, 6, */ { 3, 10 }, + /* 81: 0, 4, 6, */ { 6, 6 }, + /* 82: 1, 4, 6, */ { 7, 5 }, + /* 83: 0, 1, 4, 6, */ { 12, 2 }, + /* 84: 2, 4, 6, */ { 6, 19 }, + /* 85: 0, 2, 4, 6, */ { 10, 1 }, + /* 86: 1, 2, 4, 6, */ { 12, 13 }, + /* 87: 0, 1, 2, 4, 6, */ { 6, 24 }, + /* 88: 3, 4, 6, */ { 7, 7 }, + /* 89: 0, 3, 4, 6, */ { 12, 9 }, + /* 90: 1, 3, 4, 6, */ { 13, 1 }, + /* 91: 0, 1, 3, 4, 6, */ { 7, 9 }, + /* 92: 2, 3, 4, 6, */ { 12, 20 }, + /* 93: 0, 2, 3, 4, 6, */ { 6, 33 }, + /* 94: 1, 2, 3, 4, 6, */ { 7, 13 }, + /* 95: 0, 1, 2, 3, 4, 6, */ { 3, 12 }, + /* 96: 5, 6, */ { 2, 10 }, + /* 97: 0, 5, 6, */ { 6, 7 }, + /* 98: 1, 5, 6, */ { 5, 13 }, + /* 99: 0, 1, 5, 6, */ { 11, 2 }, + /* 100: 2, 5, 6, */ { 5, 16 }, + /* 101: 0, 2, 5, 6, */ { 12, 7 }, + /* 102: 1, 2, 5, 6, */ { 8, 3 }, + /* 103: 0, 1, 2, 5, 6, */ { 5, 29 }, + /* 104: 3, 5, 6, */ { 6, 22 }, + /* 105: 0, 3, 5, 6, */ { 10, 2 }, + /* 106: 1, 3, 5, 6, */ { 12, 17 }, + /* 107: 0, 1, 3, 5, 6, */ { 6, 27 }, + /* 108: 2, 3, 5, 6, */ { 14, 9 }, + /* 109: 0, 2, 3, 5, 6, */ { 6, 34 }, + /* 110: 1, 2, 3, 5, 6, */ { 5, 39 }, + /* 111: 0, 1, 2, 3, 5, 6, */ { 2, 14 }, + /* 112: 4, 5, 6, */ { 5, 20 }, + /* 113: 0, 4, 5, 6, */ { 14, 5 }, + /* 114: 1, 4, 5, 6, */ { 9, 5 }, + /* 115: 0, 1, 4, 5, 6, */ { 5, 32 }, + /* 116: 2, 4, 5, 6, */ { 11, 10 }, + /* 117: 0, 2, 4, 5, 6, */ { 6, 35 }, + /* 118: 1, 2, 4, 5, 6, */ { 5, 41 }, + /* 119: 0, 1, 2, 4, 5, 6, */ { 2, 16 }, + /* 120: 3, 4, 5, 6, */ { 12, 23 }, + /* 121: 0, 3, 4, 5, 6, */ { 6, 37 }, + /* 122: 1, 3, 4, 5, 6, */ { 7, 14 }, + /* 123: 0, 1, 3, 4, 5, 6, */ { 3, 16 }, + /* 124: 2, 3, 4, 5, 6, */ { 6, 46 }, + /* 125: 0, 2, 3, 4, 5, 6, */ { 4, 6 }, + /* 126: 1, 2, 3, 4, 5, 6, */ { 3, 21 }, + /* 127: 0, 1, 2, 3, 4, 5, 6, */ { 1, 8 }, + /* 128: 7, */ { 1, 7 }, + /* 129: 0, 7, */ { 3, 2 }, + /* 130: 1, 7, */ { 4, 1 }, + /* 131: 0, 1, 7, */ { 6, 1 }, + /* 132: 2, 7, */ { 3, 7 }, + /* 133: 0, 2, 7, */ { 7, 1 }, + /* 134: 1, 2, 7, */ { 6, 10 }, + /* 135: 0, 1, 2, 7, */ { 12, 0 }, + /* 136: 3, 7, */ { 2, 7 }, + /* 137: 0, 3, 7, */ { 5, 6 }, + /* 138: 1, 3, 7, */ { 6, 12 }, + /* 139: 0, 1, 3, 7, */ { 11, 1 }, + /* 140: 2, 3, 7, */ { 5, 15 }, + /* 141: 0, 2, 3, 7, */ { 9, 2 }, + /* 142: 1, 2, 3, 7, */ { 14, 6 }, + /* 143: 0, 1, 2, 3, 7, */ { 5, 27 }, + /* 144: 4, 7, */ { 2, 9 }, + /* 145: 0, 4, 7, */ { 5, 8 }, + /* 146: 1, 4, 7, */ { 6, 13 }, + /* 147: 0, 1, 4, 7, */ { 14, 2 }, + /* 148: 2, 4, 7, */ { 6, 20 }, + /* 149: 0, 2, 4, 7, */ { 12, 6 }, + /* 150: 1, 2, 4, 7, */ { 10, 3 }, + /* 151: 0, 1, 2, 4, 7, */ { 6, 25 }, + /* 152: 3, 4, 7, */ { 5, 18 }, + /* 153: 0, 3, 4, 7, */ { 8, 2 }, + /* 154: 1, 3, 4, 7, */ { 12, 16 }, + /* 155: 0, 1, 3, 4, 7, */ { 5, 31 }, + /* 156: 2, 3, 4, 7, */ { 11, 9 }, + /* 157: 0, 2, 3, 4, 7, */ { 5, 34 }, + /* 158: 1, 2, 3, 4, 7, */ { 6, 40 }, + /* 159: 0, 1, 2, 3, 4, 7, */ { 2, 13 }, + /* 160: 5, 7, */ { 3, 11 }, + /* 161: 0, 5, 7, */ { 7, 2 }, + /* 162: 1, 5, 7, */ { 6, 14 }, + /* 163: 0, 1, 5, 7, */ { 12, 3 }, + /* 164: 2, 5, 7, */ { 7, 6 }, + /* 165: 0, 2, 5, 7, */ { 13, 0 }, + /* 166: 1, 2, 5, 7, */ { 12, 14 }, + /* 167: 0, 1, 2, 5, 7, */ { 7, 8 }, + /* 168: 3, 5, 7, */ { 6, 23 }, + /* 169: 0, 3, 5, 7, */ { 12, 10 }, + /* 170: 1, 3, 5, 7, */ { 10, 4 }, + /* 171: 0, 1, 3, 5, 7, */ { 6, 28 }, + /* 172: 2, 3, 5, 7, */ { 12, 21 }, + /* 173: 0, 2, 3, 5, 7, */ { 7, 10 }, + /* 174: 1, 2, 3, 5, 7, */ { 6, 41 }, + /* 175: 0, 1, 2, 3, 5, 7, */ { 3, 13 }, + /* 176: 4, 5, 7, */ { 5, 21 }, + /* 177: 0, 4, 5, 7, */ { 9, 3 }, + /* 178: 1, 4, 5, 7, */ { 11, 8 }, + /* 179: 0, 1, 4, 5, 7, */ { 5, 33 }, + /* 180: 2, 4, 5, 7, */ { 12, 22 }, + /* 181: 0, 2, 4, 5, 7, */ { 7, 11 }, + /* 182: 1, 2, 4, 5, 7, */ { 6, 42 }, + /* 183: 0, 1, 2, 4, 5, 7, */ { 3, 14 }, + /* 184: 3, 4, 5, 7, */ { 14, 11 }, + /* 185: 0, 3, 4, 5, 7, */ { 5, 36 }, + /* 186: 1, 3, 4, 5, 7, */ { 6, 44 }, + /* 187: 0, 1, 3, 4, 5, 7, */ { 2, 17 }, + /* 188: 2, 3, 4, 5, 7, */ { 6, 47 }, + /* 189: 0, 2, 3, 4, 5, 7, */ { 3, 18 }, + /* 190: 1, 2, 3, 4, 5, 7, */ { 4, 7 }, + /* 191: 0, 1, 2, 3, 4, 5, 7, */ { 1, 9 }, + /* 192: 6, 7, */ { 2, 11 }, + /* 193: 0, 6, 7, */ { 6, 8 }, + /* 194: 1, 6, 7, */ { 6, 15 }, + /* 195: 0, 1, 6, 7, */ { 10, 0 }, + /* 196: 2, 6, 7, */ { 5, 17 }, + /* 197: 0, 2, 6, 7, */ { 12, 8 }, + /* 198: 1, 2, 6, 7, */ { 11, 7 }, + /* 199: 0, 1, 2, 6, 7, */ { 6, 26 }, + /* 200: 3, 6, 7, */ { 5, 19 }, + /* 201: 0, 3, 6, 7, */ { 14, 4 }, + /* 202: 1, 3, 6, 7, */ { 12, 18 }, + /* 203: 0, 1, 3, 6, 7, */ { 6, 29 }, + /* 204: 2, 3, 6, 7, */ { 8, 4 }, + /* 205: 0, 2, 3, 6, 7, */ { 5, 35 }, + /* 206: 1, 2, 3, 6, 7, */ { 5, 40 }, + /* 207: 0, 1, 2, 3, 6, 7, */ { 2, 15 }, + /* 208: 4, 6, 7, */ { 5, 22 }, + /* 209: 0, 4, 6, 7, */ { 11, 5 }, + /* 210: 1, 4, 6, 7, */ { 12, 19 }, + /* 211: 0, 1, 4, 6, 7, */ { 6, 30 }, + /* 212: 2, 4, 6, 7, */ { 14, 10 }, + /* 213: 0, 2, 4, 6, 7, */ { 6, 36 }, + /* 214: 1, 2, 4, 6, 7, */ { 6, 43 }, + /* 215: 0, 1, 2, 4, 6, 7, */ { 4, 4 }, + /* 216: 3, 4, 6, 7, */ { 9, 7 }, + /* 217: 0, 3, 4, 6, 7, */ { 5, 37 }, + /* 218: 1, 3, 4, 6, 7, */ { 7, 15 }, + /* 219: 0, 1, 3, 4, 6, 7, */ { 3, 17 }, + /* 220: 2, 3, 4, 6, 7, */ { 5, 44 }, + /* 221: 0, 2, 3, 4, 6, 7, */ { 2, 19 }, + /* 222: 1, 2, 3, 4, 6, 7, */ { 3, 22 }, + /* 223: 0, 1, 2, 3, 4, 6, 7, */ { 1, 10 }, + /* 224: 5, 6, 7, */ { 5, 23 }, + /* 225: 0, 5, 6, 7, */ { 12, 11 }, + /* 226: 1, 5, 6, 7, */ { 14, 8 }, + /* 227: 0, 1, 5, 6, 7, */ { 6, 31 }, + /* 228: 2, 5, 6, 7, */ { 9, 6 }, + /* 229: 0, 2, 5, 6, 7, */ { 7, 12 }, + /* 230: 1, 2, 5, 6, 7, */ { 5, 42 }, + /* 231: 0, 1, 2, 5, 6, 7, */ { 3, 15 }, + /* 232: 3, 5, 6, 7, */ { 11, 11 }, + /* 233: 0, 3, 5, 6, 7, */ { 6, 38 }, + /* 234: 1, 3, 5, 6, 7, */ { 6, 45 }, + /* 235: 0, 1, 3, 5, 6, 7, */ { 4, 5 }, + /* 236: 2, 3, 5, 6, 7, */ { 5, 45 }, + /* 237: 0, 2, 3, 5, 6, 7, */ { 3, 19 }, + /* 238: 1, 2, 3, 5, 6, 7, */ { 2, 21 }, + /* 239: 0, 1, 2, 3, 5, 6, 7, */ { 1, 11 }, + /* 240: 4, 5, 6, 7, */ { 8, 5 }, + /* 241: 0, 4, 5, 6, 7, */ { 5, 38 }, + /* 242: 1, 4, 5, 6, 7, */ { 5, 43 }, + /* 243: 0, 1, 4, 5, 6, 7, */ { 2, 18 }, + /* 244: 2, 4, 5, 6, 7, */ { 5, 46 }, + /* 245: 0, 2, 4, 5, 6, 7, */ { 3, 20 }, + /* 246: 1, 2, 4, 5, 6, 7, */ { 2, 22 }, + /* 247: 0, 1, 2, 4, 5, 6, 7, */ { 1, 12 }, + /* 248: 3, 4, 5, 6, 7, */ { 5, 47 }, + /* 249: 0, 3, 4, 5, 6, 7, */ { 2, 20 }, + /* 250: 1, 3, 4, 5, 6, 7, */ { 3, 23 }, + /* 251: 0, 1, 3, 4, 5, 6, 7, */ { 1, 13 }, + /* 252: 2, 3, 4, 5, 6, 7, */ { 2, 23 }, + /* 253: 0, 2, 3, 4, 5, 6, 7, */ { 1, 14 }, + /* 254: 1, 2, 3, 4, 5, 6, 7, */ { 1, 15 }, + /* 255: 0, 1, 2, 3, 4, 5, 6, 7, */ { 0, -1 } + }; + return cases[cubetype][u]; + }; // end of Cases + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling1(unsigned char config) + { + static const char tiling1[16][3] = { + /* 1: 0, */ { 0, 8, 3 }, + /* 2: 1, */ { 0, 1, 9 }, + /* 4: 2, */ { 1, 2, 10 }, + /* 8: 3, */ { 3, 11, 2 }, + /* 16: 4, */ { 4, 7, 8 }, + /* 32: 5, */ { 9, 5, 4 }, + /* 64: 6, */ { 10, 6, 5 }, + /* 128: 7, */ { 7, 6, 11 }, + /* 127: 0, 1, 2, 3, 4, 5, 6, */ { 7, 11, 6 }, + /* 191: 0, 1, 2, 3, 4, 5, 7, */ { 10, 5, 6 }, + /* 223: 0, 1, 2, 3, 4, 6, 7, */ { 9, 4, 5 }, + /* 239: 0, 1, 2, 3, 5, 6, 7, */ { 4, 8, 7 }, + /* 247: 0, 1, 2, 4, 5, 6, 7, */ { 3, 2, 11 }, + /* 251: 0, 1, 3, 4, 5, 6, 7, */ { 1, 10, 2 }, + /* 253: 0, 2, 3, 4, 5, 6, 7, */ { 0, 9, 1 }, + /* 254: 1, 2, 3, 4, 5, 6, 7, */ { 0, 3, 8 } + }; + return &tiling1[config][0]; + }; // end of Tiling1 + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling2(unsigned char config) + { + static const char tiling2[24][6] = { + /* 3: 0, 1, */ { 1, 8, 3, 9, 8, 1 }, + /* 9: 0, 3, */ { 0, 11, 2, 8, 11, 0 }, + /* 17: 0, 4, */ { 4, 3, 0, 7, 3, 4 }, + /* 6: 1, 2, */ { 9, 2, 10, 0, 2, 9 }, + /* 34: 1, 5, */ { 0, 5, 4, 1, 5, 0 }, + /* 12: 2, 3, */ { 3, 10, 1, 11, 10, 3 }, + /* 68: 2, 6, */ { 1, 6, 5, 2, 6, 1 }, + /* 136: 3, 7, */ { 7, 2, 3, 6, 2, 7 }, + /* 48: 4, 5, */ { 9, 7, 8, 5, 7, 9 }, + /* 144: 4, 7, */ { 6, 8, 4, 11, 8, 6 }, + /* 96: 5, 6, */ { 10, 4, 9, 6, 4, 10 }, + /* 192: 6, 7, */ { 11, 5, 10, 7, 5, 11 }, + /* 63: 0, 1, 2, 3, 4, 5, */ { 11, 10, 5, 7, 11, 5 }, + /* 159: 0, 1, 2, 3, 4, 7, */ { 10, 9, 4, 6, 10, 4 }, + /* 111: 0, 1, 2, 3, 5, 6, */ { 6, 4, 8, 11, 6, 8 }, + /* 207: 0, 1, 2, 3, 6, 7, */ { 9, 8, 7, 5, 9, 7 }, + /* 119: 0, 1, 2, 4, 5, 6, */ { 7, 3, 2, 6, 7, 2 }, + /* 187: 0, 1, 3, 4, 5, 7, */ { 1, 5, 6, 2, 1, 6 }, + /* 243: 0, 1, 4, 5, 6, 7, */ { 3, 1, 10, 11, 3, 10 }, + /* 221: 0, 2, 3, 4, 6, 7, */ { 0, 4, 5, 1, 0, 5 }, + /* 249: 0, 3, 4, 5, 6, 7, */ { 9, 10, 2, 0, 9, 2 }, + /* 238: 1, 2, 3, 5, 6, 7, */ { 4, 0, 3, 7, 4, 3 }, + /* 246: 1, 2, 4, 5, 6, 7, */ { 0, 2, 11, 8, 0, 11 }, + /* 252: 2, 3, 4, 5, 6, 7, */ { 1, 3, 8, 9, 1, 8 } + }; + return &tiling2[config][0]; + }; // end of Tiling2 + //_____________________________________________________________________________ + + + + //_____________________________________________________________________________ + + + /** + * \brief test table for case 3 + * One face to test + * When the test on the specified face is positive : 4 first triangles + * When the test on the specified face is negative : 2 last triangles + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static char Test3(unsigned char config) + { + static const char test3[24] = { + /* 5: 0, 2, */ 5, + /* 33: 0, 5, */ 1, + /* 129: 0, 7, */ 4, + /* 10: 1, 3, */ 5, + /* 18: 1, 4, */ 1, + /* 66: 1, 6, */ 2, + /* 36: 2, 5, */ 2, + /* 132: 2, 7, */ 3, + /* 24: 3, 4, */ 4, + /* 72: 3, 6, */ 3, + /* 80: 4, 6, */ 6, + /* 160: 5, 7, */ 6, + /* 95: 0, 1, 2, 3, 4, 6, */ -6, + /* 175: 0, 1, 2, 3, 5, 7, */ -6, + /* 183: 0, 1, 2, 4, 5, 7, */ -3, + /* 231: 0, 1, 2, 5, 6, 7, */ -4, + /* 123: 0, 1, 3, 4, 5, 6, */ -3, + /* 219: 0, 1, 3, 4, 6, 7, */ -2, + /* 189: 0, 2, 3, 4, 5, 7, */ -2, + /* 237: 0, 2, 3, 5, 6, 7, */ -1, + /* 245: 0, 2, 4, 5, 6, 7, */ -5, + /* 126: 1, 2, 3, 4, 5, 6, */ -4, + /* 222: 1, 2, 3, 4, 6, 7, */ -1, + /* 250: 1, 3, 4, 5, 6, 7, */ -5 + }; + return test3[config]; + }; // end of Test3 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 3.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling3_1(unsigned char config) + { + static const char tiling3_1[24][6] = { + /* 5: 0, 2, */ { 0, 8, 3, 1, 2, 10 }, + /* 33: 0, 5, */ { 9, 5, 4, 0, 8, 3 }, + /* 129: 0, 7, */ { 3, 0, 8, 11, 7, 6 }, + /* 10: 1, 3, */ { 1, 9, 0, 2, 3, 11 }, + /* 18: 1, 4, */ { 0, 1, 9, 8, 4, 7 }, + /* 66: 1, 6, */ { 9, 0, 1, 5, 10, 6 }, + /* 36: 2, 5, */ { 1, 2, 10, 9, 5, 4 }, + /* 132: 2, 7, */ { 10, 1, 2, 6, 11, 7 }, + /* 24: 3, 4, */ { 8, 4, 7, 3, 11, 2 }, + /* 72: 3, 6, */ { 2, 3, 11, 10, 6, 5 }, + /* 80: 4, 6, */ { 5, 10, 6, 4, 7, 8 }, + /* 160: 5, 7, */ { 4, 9, 5, 7, 6, 11 }, + /* 95: 0, 1, 2, 3, 4, 6, */ { 5, 9, 4, 11, 6, 7 }, + /* 175: 0, 1, 2, 3, 5, 7, */ { 6, 10, 5, 8, 7, 4 }, + /* 183: 0, 1, 2, 4, 5, 7, */ { 11, 3, 2, 5, 6, 10 }, + /* 231: 0, 1, 2, 5, 6, 7, */ { 7, 4, 8, 2, 11, 3 }, + /* 123: 0, 1, 3, 4, 5, 6, */ { 2, 1, 10, 7, 11, 6 }, + /* 219: 0, 1, 3, 4, 6, 7, */ { 10, 2, 1, 4, 5, 9 }, + /* 189: 0, 2, 3, 4, 5, 7, */ { 1, 0, 9, 6, 10, 5 }, + /* 237: 0, 2, 3, 5, 6, 7, */ { 9, 1, 0, 7, 4, 8 }, + /* 245: 0, 2, 4, 5, 6, 7, */ { 0, 9, 1, 11, 3, 2 }, + /* 126: 1, 2, 3, 4, 5, 6, */ { 8, 0, 3, 6, 7, 11 }, + /* 222: 1, 2, 3, 4, 6, 7, */ { 4, 5, 9, 3, 8, 0 }, + /* 250: 1, 3, 4, 5, 6, 7, */ { 3, 8, 0, 10, 2, 1 } + }; + return &tiling3_1[config][0]; + }; // end of Tiling3_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 3.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling3_2(unsigned char config) + { + static const char tiling3_2[24][12] = { + /* 5: 0, 2, */ { 10, 3, 2, 10, 8, 3, 10, 1, 0, 8, 10, 0 }, + /* 33: 0, 5, */ { 3, 4, 8, 3, 5, 4, 3, 0, 9, 5, 3, 9 }, + /* 129: 0, 7, */ { 6, 8, 7, 6, 0, 8, 6, 11, 3, 0, 6, 3 }, + /* 10: 1, 3, */ { 11, 0, 3, 11, 9, 0, 11, 2, 1, 9, 11, 1 }, + /* 18: 1, 4, */ { 7, 9, 4, 7, 1, 9, 7, 8, 0, 1, 7, 0 }, + /* 66: 1, 6, */ { 6, 1, 10, 6, 0, 1, 9, 0, 6, 9, 6, 5 }, + /* 36: 2, 5, */ { 4, 10, 5, 4, 2, 10, 4, 9, 1, 2, 4, 1 }, + /* 132: 2, 7, */ { 7, 2, 11, 7, 1, 2, 7, 6, 10, 1, 7, 10 }, + /* 24: 3, 4, */ { 2, 7, 11, 2, 4, 7, 2, 3, 8, 4, 2, 8 }, + /* 72: 3, 6, */ { 5, 11, 6, 5, 3, 11, 5, 10, 2, 3, 5, 2 }, + /* 80: 4, 6, */ { 8, 6, 7, 8, 10, 6, 8, 4, 5, 10, 8, 5 }, + /* 160: 5, 7, */ { 11, 5, 6, 11, 9, 5, 11, 7, 4, 9, 11, 4 }, + /* 95: 0, 1, 2, 3, 4, 6, */ { 6, 5, 11, 5, 9, 11, 4, 7, 11, 4, 11, 9 }, + /* 175: 0, 1, 2, 3, 5, 7, */ { 7, 6, 8, 6, 10, 8, 5, 4, 8, 5, 8, 10 }, + /* 183: 0, 1, 2, 4, 5, 7, */ { 6, 11, 5, 11, 3, 5, 2, 10, 5, 2, 5, 3 }, + /* 231: 0, 1, 2, 5, 6, 7, */ { 11, 7, 2, 7, 4, 2, 8, 3, 2, 8, 2, 4 }, + /* 123: 0, 1, 3, 4, 5, 6, */ { 11, 2, 7, 2, 1, 7, 10, 6, 7, 10, 7, 1 }, + /* 219: 0, 1, 3, 4, 6, 7, */ { 5, 10, 4, 10, 2, 4, 1, 9, 4, 1, 4, 2 }, + /* 189: 0, 2, 3, 4, 5, 7, */ { 10, 1, 6, 1, 0, 6, 6, 0, 9, 5, 6, 9 }, + /* 237: 0, 2, 3, 5, 6, 7, */ { 4, 9, 7, 9, 1, 7, 0, 8, 7, 0, 7, 1 }, + /* 245: 0, 2, 4, 5, 6, 7, */ { 3, 0, 11, 0, 9, 11, 1, 2, 11, 1, 11, 9 }, + /* 126: 1, 2, 3, 4, 5, 6, */ { 7, 8, 6, 8, 0, 6, 3, 11, 6, 3, 6, 0 }, + /* 222: 1, 2, 3, 4, 6, 7, */ { 8, 4, 3, 4, 5, 3, 9, 0, 3, 9, 3, 5 }, + /* 250: 1, 3, 4, 5, 6, 7, */ { 2, 3, 10, 3, 8, 10, 0, 1, 10, 0, 10, 8 } + }; + return &tiling3_2[config][0]; + }; // end of Tiling3_2 + //_____________________________________________________________________________ + + + + //_____________________________________________________________________________ + /** + * \brief test table for case 4 + * Interior to test + * When the test on the interior is negative : 2 first triangles + * When the test on the interior is positive : 6 last triangles + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static char Test4(unsigned char config) + { + static const char test4[8] = { + /* 65: 0, 6, */ 7, + /* 130: 1, 7, */ 7, + /* 20: 2, 4, */ 7, + /* 40: 3, 5, */ 7, + /* 215: 0, 1, 2, 4, 6, 7, */ -7, + /* 235: 0, 1, 3, 5, 6, 7, */ -7, + /* 125: 0, 2, 3, 4, 5, 6, */ -7, + /* 190: 1, 2, 3, 4, 5, 7, */ -7 + }; + return test4[config]; + }; // end of Test4 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 4.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling4_1(unsigned char config) + { + static const char tiling4_1[8][6] = { + /* 65: 0, 6, */ { 0, 8, 3, 5, 10, 6 }, + /* 130: 1, 7, */ { 0, 1, 9, 11, 7, 6 }, + /* 20: 2, 4, */ { 1, 2, 10, 8, 4, 7 }, + /* 40: 3, 5, */ { 9, 5, 4, 2, 3, 11 }, + /* 215: 0, 1, 2, 4, 6, 7, */ { 4, 5, 9, 11, 3, 2 }, + /* 235: 0, 1, 3, 5, 6, 7, */ { 10, 2, 1, 7, 4, 8 }, + /* 125: 0, 2, 3, 4, 5, 6, */ { 9, 1, 0, 6, 7, 11 }, + /* 190: 1, 2, 3, 4, 5, 7, */ { 3, 8, 0, 6, 10, 5 } + }; + return &tiling4_1[config][0]; + }; // end of Tiling4_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 4.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling4_2(unsigned char config) + { + static const char tiling4_2[8][18] = { + /* 65: 0, 6, */ { 8, 5, 0, 5, 8, 6, 3, 6, 8, 6, 3, 10, 0, 10, 3, 10, 0, 5 }, + /* 130: 1, 7, */ { 9, 6, 1, 6, 9, 7, 0, 7, 9, 7, 0, 11, 1, 11, 0, 11, 1, 6 }, + /* 20: 2, 4, */ { 10, 7, 2, 7, 10, 4, 1, 4, 10, 4, 1, 8, 2, 8, 1, 8, 2, 7 }, + /* 40: 3, 5, */ { 11, 4, 3, 4, 11, 5, 2, 5, 11, 5, 2, 9, 3, 9, 2, 9, 3, 4 }, + /* 215: 0, 1, 2, 4, 6, 7, */ { 3, 4, 11, 5, 11, 4, 11, 5, 2, 9, 2, 5, 2, 9, 3, 4, 3, 9 }, + /* 235: 0, 1, 3, 5, 6, 7, */ { 2, 7, 10, 4, 10, 7, 10, 4, 1, 8, 1, 4, 1, 8, 2, 7, 2, 8 }, + /* 125: 0, 2, 3, 4, 5, 6, */ { 1, 6, 9, 7, 9, 6, 9, 7, 0, 11, 0, 7, 0, 11, 1, 6, 1, 11 }, + /* 190: 1, 2, 3, 4, 5, 7, */ { 0, 5, 8, 6, 8, 5, 8, 6, 3, 10, 3, 6, 3, 10, 0, 5, 0, 10 } + }; + return &tiling4_2[config][0]; + }; // end of Tiling4_2 + //_____________________________________________________________________________ + + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 5 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling5(unsigned char config) + { + static const char tiling5[48][9] = { + /* 7: 0, 1, 2, */ { 2, 8, 3, 2, 10, 8, 10, 9, 8 }, + /* 11: 0, 1, 3, */ { 1, 11, 2, 1, 9, 11, 9, 8, 11 }, + /* 19: 0, 1, 4, */ { 4, 1, 9, 4, 7, 1, 7, 3, 1 }, + /* 35: 0, 1, 5, */ { 8, 5, 4, 8, 3, 5, 3, 1, 5 }, + /* 13: 0, 2, 3, */ { 0, 10, 1, 0, 8, 10, 8, 11, 10 }, + /* 25: 0, 3, 4, */ { 11, 4, 7, 11, 2, 4, 2, 0, 4 }, + /* 137: 0, 3, 7, */ { 7, 0, 8, 7, 6, 0, 6, 2, 0 }, + /* 49: 0, 4, 5, */ { 9, 3, 0, 9, 5, 3, 5, 7, 3 }, + /* 145: 0, 4, 7, */ { 3, 6, 11, 3, 0, 6, 0, 4, 6 }, + /* 14: 1, 2, 3, */ { 3, 9, 0, 3, 11, 9, 11, 10, 9 }, + /* 38: 1, 2, 5, */ { 5, 2, 10, 5, 4, 2, 4, 0, 2 }, + /* 70: 1, 2, 6, */ { 9, 6, 5, 9, 0, 6, 0, 2, 6 }, + /* 50: 1, 4, 5, */ { 0, 7, 8, 0, 1, 7, 1, 5, 7 }, + /* 98: 1, 5, 6, */ { 10, 0, 1, 10, 6, 0, 6, 4, 0 }, + /* 76: 2, 3, 6, */ { 6, 3, 11, 6, 5, 3, 5, 1, 3 }, + /* 140: 2, 3, 7, */ { 10, 7, 6, 10, 1, 7, 1, 3, 7 }, + /* 100: 2, 5, 6, */ { 1, 4, 9, 1, 2, 4, 2, 6, 4 }, + /* 196: 2, 6, 7, */ { 11, 1, 2, 11, 7, 1, 7, 5, 1 }, + /* 152: 3, 4, 7, */ { 8, 2, 3, 8, 4, 2, 4, 6, 2 }, + /* 200: 3, 6, 7, */ { 2, 5, 10, 2, 3, 5, 3, 7, 5 }, + /* 112: 4, 5, 6, */ { 7, 10, 6, 7, 8, 10, 8, 9, 10 }, + /* 176: 4, 5, 7, */ { 6, 9, 5, 6, 11, 9, 11, 8, 9 }, + /* 208: 4, 6, 7, */ { 5, 8, 4, 5, 10, 8, 10, 11, 8 }, + /* 224: 5, 6, 7, */ { 4, 11, 7, 4, 9, 11, 9, 10, 11 }, + /* 31: 0, 1, 2, 3, 4, */ { 4, 7, 11, 4, 11, 9, 9, 11, 10 }, + /* 47: 0, 1, 2, 3, 5, */ { 5, 4, 8, 5, 8, 10, 10, 8, 11 }, + /* 79: 0, 1, 2, 3, 6, */ { 6, 5, 9, 6, 9, 11, 11, 9, 8 }, + /* 143: 0, 1, 2, 3, 7, */ { 7, 6, 10, 7, 10, 8, 8, 10, 9 }, + /* 55: 0, 1, 2, 4, 5, */ { 2, 10, 5, 2, 5, 3, 3, 5, 7 }, + /* 103: 0, 1, 2, 5, 6, */ { 8, 3, 2, 8, 2, 4, 4, 2, 6 }, + /* 59: 0, 1, 3, 4, 5, */ { 11, 2, 1, 11, 1, 7, 7, 1, 5 }, + /* 155: 0, 1, 3, 4, 7, */ { 1, 9, 4, 1, 4, 2, 2, 4, 6 }, + /* 115: 0, 1, 4, 5, 6, */ { 10, 6, 7, 10, 7, 1, 1, 7, 3 }, + /* 179: 0, 1, 4, 5, 7, */ { 6, 11, 3, 6, 3, 5, 5, 3, 1 }, + /* 157: 0, 2, 3, 4, 7, */ { 10, 1, 0, 10, 0, 6, 6, 0, 4 }, + /* 205: 0, 2, 3, 6, 7, */ { 0, 8, 7, 0, 7, 1, 1, 7, 5 }, + /* 185: 0, 3, 4, 5, 7, */ { 9, 5, 6, 9, 6, 0, 0, 6, 2 }, + /* 217: 0, 3, 4, 6, 7, */ { 5, 10, 2, 5, 2, 4, 4, 2, 0 }, + /* 241: 0, 4, 5, 6, 7, */ { 3, 0, 9, 3, 9, 11, 11, 9, 10 }, + /* 110: 1, 2, 3, 5, 6, */ { 3, 11, 6, 3, 6, 0, 0, 6, 4 }, + /* 206: 1, 2, 3, 6, 7, */ { 9, 0, 3, 9, 3, 5, 5, 3, 7 }, + /* 118: 1, 2, 4, 5, 6, */ { 7, 8, 0, 7, 0, 6, 6, 0, 2 }, + /* 230: 1, 2, 5, 6, 7, */ { 11, 7, 4, 11, 4, 2, 2, 4, 0 }, + /* 242: 1, 4, 5, 6, 7, */ { 0, 1, 10, 0, 10, 8, 8, 10, 11 }, + /* 220: 2, 3, 4, 6, 7, */ { 8, 4, 5, 8, 5, 3, 3, 5, 1 }, + /* 236: 2, 3, 5, 6, 7, */ { 4, 9, 1, 4, 1, 7, 7, 1, 3 }, + /* 244: 2, 4, 5, 6, 7, */ { 1, 2, 11, 1, 11, 9, 9, 11, 8 }, + /* 248: 3, 4, 5, 6, 7, */ { 2, 3, 8, 2, 8, 10, 10, 8, 9 } + }; + return &tiling5[config][0]; + }; // end of Tiling5 + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief test table for case 6 + * 1 face to test + eventually the interior + * When the test on the specified face is positive : 5 first triangles + * When the test on the specified face is negative : + * - if the test on the interior is negative : 3 middle triangles + * - if the test on the interior is positive : 8 last triangles + * The support edge for the interior test is marked as the 3rd column. + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static char Test6(unsigned char config, int u) + { + static const char test6[48][3] = { + /* 67: 0, 1, 6, */ { 2, 7, 10 }, + /* 131: 0, 1, 7, */ { 4, 7, 11 }, + /* 21: 0, 2, 4, */ { 5, 7, 1 }, + /* 69: 0, 2, 6, */ { 5, 7, 3 }, + /* 41: 0, 3, 5, */ { 1, 7, 9 }, + /* 73: 0, 3, 6, */ { 3, 7, 10 }, + /* 81: 0, 4, 6, */ { 6, 7, 5 }, + /* 97: 0, 5, 6, */ { 1, 7, 8 }, + /* 193: 0, 6, 7, */ { 4, 7, 8 }, + /* 22: 1, 2, 4, */ { 1, 7, 8 }, + /* 134: 1, 2, 7, */ { 3, 7, 11 }, + /* 42: 1, 3, 5, */ { 5, 7, 2 }, + /* 138: 1, 3, 7, */ { 5, 7, 0 }, + /* 146: 1, 4, 7, */ { 1, 7, 9 }, + /* 162: 1, 5, 7, */ { 6, 7, 6 }, + /* 194: 1, 6, 7, */ { 2, 7, 9 }, + /* 28: 2, 3, 4, */ { 4, 7, 8 }, + /* 44: 2, 3, 5, */ { 2, 7, 9 }, + /* 52: 2, 4, 5, */ { 2, 7, 10 }, + /* 84: 2, 4, 6, */ { 6, 7, 7 }, + /* 148: 2, 4, 7, */ { 3, 7, 10 }, + /* 56: 3, 4, 5, */ { 4, 7, 11 }, + /* 104: 3, 5, 6, */ { 3, 7, 11 }, + /* 168: 3, 5, 7, */ { 6, 7, 4 }, + /* 87: 0, 1, 2, 4, 6, */ { -6, -7, 4 }, + /* 151: 0, 1, 2, 4, 7, */ { -3, -7, 11 }, + /* 199: 0, 1, 2, 6, 7, */ { -4, -7, 11 }, + /* 107: 0, 1, 3, 5, 6, */ { -3, -7, 10 }, + /* 171: 0, 1, 3, 5, 7, */ { -6, -7, 7 }, + /* 203: 0, 1, 3, 6, 7, */ { -2, -7, 10 }, + /* 211: 0, 1, 4, 6, 7, */ { -2, -7, 9 }, + /* 227: 0, 1, 5, 6, 7, */ { -4, -7, 8 }, + /* 61: 0, 2, 3, 4, 5, */ { -2, -7, 9 }, + /* 93: 0, 2, 3, 4, 6, */ { -6, -7, 6 }, + /* 109: 0, 2, 3, 5, 6, */ { -1, -7, 9 }, + /* 117: 0, 2, 4, 5, 6, */ { -5, -7, 0 }, + /* 213: 0, 2, 4, 6, 7, */ { -5, -7, 2 }, + /* 121: 0, 3, 4, 5, 6, */ { -3, -7, 11 }, + /* 233: 0, 3, 5, 6, 7, */ { -1, -7, 8 }, + /* 62: 1, 2, 3, 4, 5, */ { -4, -7, 8 }, + /* 158: 1, 2, 3, 4, 7, */ { -1, -7, 8 }, + /* 174: 1, 2, 3, 5, 7, */ { -6, -7, 5 }, + /* 182: 1, 2, 4, 5, 7, */ { -3, -7, 10 }, + /* 214: 1, 2, 4, 6, 7, */ { -1, -7, 9 }, + /* 186: 1, 3, 4, 5, 7, */ { -5, -7, 3 }, + /* 234: 1, 3, 5, 6, 7, */ { -5, -7, 1 }, + /* 124: 2, 3, 4, 5, 6, */ { -4, -7, 11 }, + /* 188: 2, 3, 4, 5, 7, */ { -2, -7, 10 } + }; + return test6[config][u]; + }; // end of Test6 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 6.1.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling6_1_1(unsigned char config) + { + static const char tiling6_1_1[48][9] = { + /* 67: 0, 1, 6, */ { 6, 5, 10, 3, 1, 8, 9, 8, 1 }, + /* 131: 0, 1, 7, */ { 11, 7, 6, 9, 3, 1, 3, 9, 8 }, + /* 21: 0, 2, 4, */ { 1, 2, 10, 7, 0, 4, 0, 7, 3 }, + /* 69: 0, 2, 6, */ { 3, 0, 8, 5, 2, 6, 2, 5, 1 }, + /* 41: 0, 3, 5, */ { 5, 4, 9, 2, 0, 11, 8, 11, 0 }, + /* 73: 0, 3, 6, */ { 10, 6, 5, 8, 2, 0, 2, 8, 11 }, + /* 81: 0, 4, 6, */ { 10, 6, 5, 0, 4, 3, 7, 3, 4 }, + /* 97: 0, 5, 6, */ { 3, 0, 8, 6, 4, 10, 9, 10, 4 }, + /* 193: 0, 6, 7, */ { 8, 3, 0, 10, 7, 5, 7, 10, 11 }, + /* 22: 1, 2, 4, */ { 8, 4, 7, 10, 0, 2, 0, 10, 9 }, + /* 134: 1, 2, 7, */ { 7, 6, 11, 0, 2, 9, 10, 9, 2 }, + /* 42: 1, 3, 5, */ { 2, 3, 11, 4, 1, 5, 1, 4, 0 }, + /* 138: 1, 3, 7, */ { 0, 1, 9, 6, 3, 7, 3, 6, 2 }, + /* 146: 1, 4, 7, */ { 9, 0, 1, 11, 4, 6, 4, 11, 8 }, + /* 162: 1, 5, 7, */ { 11, 7, 6, 1, 5, 0, 4, 0, 5 }, + /* 194: 1, 6, 7, */ { 0, 1, 9, 7, 5, 11, 10, 11, 5 }, + /* 28: 2, 3, 4, */ { 4, 7, 8, 1, 3, 10, 11, 10, 3 }, + /* 44: 2, 3, 5, */ { 9, 5, 4, 11, 1, 3, 1, 11, 10 }, + /* 52: 2, 4, 5, */ { 10, 1, 2, 8, 5, 7, 5, 8, 9 }, + /* 84: 2, 4, 6, */ { 8, 4, 7, 2, 6, 1, 5, 1, 6 }, + /* 148: 2, 4, 7, */ { 1, 2, 10, 4, 6, 8, 11, 8, 6 }, + /* 56: 3, 4, 5, */ { 2, 3, 11, 5, 7, 9, 8, 9, 7 }, + /* 104: 3, 5, 6, */ { 11, 2, 3, 9, 6, 4, 6, 9, 10 }, + /* 168: 3, 5, 7, */ { 9, 5, 4, 3, 7, 2, 6, 2, 7 }, + /* 87: 0, 1, 2, 4, 6, */ { 4, 5, 9, 2, 7, 3, 7, 2, 6 }, + /* 151: 0, 1, 2, 4, 7, */ { 3, 2, 11, 4, 6, 9, 10, 9, 6 }, + /* 199: 0, 1, 2, 6, 7, */ { 11, 3, 2, 9, 7, 5, 7, 9, 8 }, + /* 107: 0, 1, 3, 5, 6, */ { 10, 2, 1, 8, 6, 4, 6, 8, 11 }, + /* 171: 0, 1, 3, 5, 7, */ { 7, 4, 8, 1, 6, 2, 6, 1, 5 }, + /* 203: 0, 1, 3, 6, 7, */ { 2, 1, 10, 7, 5, 8, 9, 8, 5 }, + /* 211: 0, 1, 4, 6, 7, */ { 4, 5, 9, 3, 1, 11, 10, 11, 1 }, + /* 227: 0, 1, 5, 6, 7, */ { 8, 7, 4, 10, 3, 1, 3, 10, 11 }, + /* 61: 0, 2, 3, 4, 5, */ { 9, 1, 0, 11, 5, 7, 5, 11, 10 }, + /* 93: 0, 2, 3, 4, 6, */ { 6, 7, 11, 0, 5, 1, 5, 0, 4 }, + /* 109: 0, 2, 3, 5, 6, */ { 1, 0, 9, 6, 4, 11, 8, 11, 4 }, + /* 117: 0, 2, 4, 5, 6, */ { 9, 1, 0, 7, 3, 6, 2, 6, 3 }, + /* 213: 0, 2, 4, 6, 7, */ { 11, 3, 2, 5, 1, 4, 0, 4, 1 }, + /* 121: 0, 3, 4, 5, 6, */ { 11, 6, 7, 9, 2, 0, 2, 9, 10 }, + /* 233: 0, 3, 5, 6, 7, */ { 7, 4, 8, 2, 0, 10, 9, 10, 0 }, + /* 62: 1, 2, 3, 4, 5, */ { 0, 3, 8, 5, 7, 10, 11, 10, 7 }, + /* 158: 1, 2, 3, 4, 7, */ { 8, 0, 3, 10, 4, 6, 4, 10, 9 }, + /* 174: 1, 2, 3, 5, 7, */ { 5, 6, 10, 3, 4, 0, 4, 3, 7 }, + /* 182: 1, 2, 4, 5, 7, */ { 5, 6, 10, 0, 2, 8, 11, 8, 2 }, + /* 214: 1, 2, 4, 6, 7, */ { 9, 4, 5, 11, 0, 2, 0, 11, 8 }, + /* 186: 1, 3, 4, 5, 7, */ { 8, 0, 3, 6, 2, 5, 1, 5, 2 }, + /* 234: 1, 3, 5, 6, 7, */ { 10, 2, 1, 4, 0, 7, 3, 7, 0 }, + /* 124: 2, 3, 4, 5, 6, */ { 6, 7, 11, 1, 3, 9, 8, 9, 3 }, + /* 188: 2, 3, 4, 5, 7, */ { 10, 5, 6, 8, 1, 3, 1, 8, 9 } + }; + return &tiling6_1_1[config][0]; + }; // end of Tiling6_1_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 6.1.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling6_1_2(unsigned char config) + { + static const char tiling6_1_2[48][21] = { + /* 67: 0, 1, 6, */ { 1, 10, 3, 6, 3, 10, 3, 6, 8, 5, 8, 6, 8, 5, 9, 1, 9, 5, 10, 1, 5 }, + /* 131: 0, 1, 7, */ { 1, 11, 3, 11, 1, 6, 9, 6, 1, 6, 9, 7, 8, 7, 9, 7, 8, 3, 7, 3, 11 }, + /* 21: 0, 2, 4, */ { 4, 1, 0, 1, 4, 10, 7, 10, 4, 10, 7, 2, 3, 2, 7, 2, 3, 0, 2, 0, 1 }, + /* 69: 0, 2, 6, */ { 6, 3, 2, 3, 6, 8, 5, 8, 6, 8, 5, 0, 1, 0, 5, 0, 1, 2, 0, 2, 3 }, + /* 41: 0, 3, 5, */ { 0, 9, 2, 5, 2, 9, 2, 5, 11, 4, 11, 5, 11, 4, 8, 0, 8, 4, 9, 0, 4 }, + /* 73: 0, 3, 6, */ { 0, 10, 2, 10, 0, 5, 8, 5, 0, 5, 8, 6, 11, 6, 8, 6, 11, 2, 6, 2, 10 }, + /* 81: 0, 4, 6, */ { 4, 5, 0, 10, 0, 5, 0, 10, 3, 6, 3, 10, 3, 6, 7, 4, 7, 6, 5, 4, 6 }, + /* 97: 0, 5, 6, */ { 4, 8, 6, 3, 6, 8, 6, 3, 10, 0, 10, 3, 10, 0, 9, 4, 9, 0, 8, 4, 0 }, + /* 193: 0, 6, 7, */ { 5, 8, 7, 8, 5, 0, 10, 0, 5, 0, 10, 3, 11, 3, 10, 3, 11, 7, 3, 7, 8 }, + /* 22: 1, 2, 4, */ { 2, 8, 0, 8, 2, 7, 10, 7, 2, 7, 10, 4, 9, 4, 10, 4, 9, 0, 4, 0, 8 }, + /* 134: 1, 2, 7, */ { 2, 11, 0, 7, 0, 11, 0, 7, 9, 6, 9, 7, 9, 6, 10, 2, 10, 6, 11, 2, 6 }, + /* 42: 1, 3, 5, */ { 5, 2, 1, 2, 5, 11, 4, 11, 5, 11, 4, 3, 0, 3, 4, 3, 0, 1, 3, 1, 2 }, + /* 138: 1, 3, 7, */ { 7, 0, 3, 0, 7, 9, 6, 9, 7, 9, 6, 1, 2, 1, 6, 1, 2, 3, 1, 3, 0 }, + /* 146: 1, 4, 7, */ { 6, 9, 4, 9, 6, 1, 11, 1, 6, 1, 11, 0, 8, 0, 11, 0, 8, 4, 0, 4, 9 }, + /* 162: 1, 5, 7, */ { 5, 6, 1, 11, 1, 6, 1, 11, 0, 7, 0, 11, 0, 7, 4, 5, 4, 7, 6, 5, 7 }, + /* 194: 1, 6, 7, */ { 5, 9, 7, 0, 7, 9, 7, 0, 11, 1, 11, 0, 11, 1, 10, 5, 10, 1, 9, 5, 1 }, + /* 28: 2, 3, 4, */ { 3, 8, 1, 4, 1, 8, 1, 4, 10, 7, 10, 4, 10, 7, 11, 3, 11, 7, 8, 3, 7 }, + /* 44: 2, 3, 5, */ { 3, 9, 1, 9, 3, 4, 11, 4, 3, 4, 11, 5, 10, 5, 11, 5, 10, 1, 5, 1, 9 }, + /* 52: 2, 4, 5, */ { 7, 10, 5, 10, 7, 2, 8, 2, 7, 2, 8, 1, 9, 1, 8, 1, 9, 5, 1, 5, 10 }, + /* 84: 2, 4, 6, */ { 6, 7, 2, 8, 2, 7, 2, 8, 1, 4, 1, 8, 1, 4, 5, 6, 5, 4, 7, 6, 4 }, + /* 148: 2, 4, 7, */ { 6, 10, 4, 1, 4, 10, 4, 1, 8, 2, 8, 1, 8, 2, 11, 6, 11, 2, 10, 6, 2 }, + /* 56: 3, 4, 5, */ { 7, 11, 5, 2, 5, 11, 5, 2, 9, 3, 9, 2, 9, 3, 8, 7, 8, 3, 11, 7, 3 }, + /* 104: 3, 5, 6, */ { 4, 11, 6, 11, 4, 3, 9, 3, 4, 3, 9, 2, 10, 2, 9, 2, 10, 6, 2, 6, 11 }, + /* 168: 3, 5, 7, */ { 7, 4, 3, 9, 3, 4, 3, 9, 2, 5, 2, 9, 2, 5, 6, 7, 6, 5, 4, 7, 5 }, + /* 87: 0, 1, 2, 4, 6, */ { 3, 4, 7, 4, 3, 9, 2, 9, 3, 9, 2, 5, 6, 5, 2, 5, 6, 7, 5, 7, 4 }, + /* 151: 0, 1, 2, 4, 7, */ { 6, 11, 4, 3, 4, 11, 4, 3, 9, 2, 9, 3, 9, 2, 10, 6, 10, 2, 11, 6, 2 }, + /* 199: 0, 1, 2, 6, 7, */ { 5, 11, 7, 11, 5, 2, 9, 2, 5, 2, 9, 3, 8, 3, 9, 3, 8, 7, 3, 7, 11 }, + /* 107: 0, 1, 3, 5, 6, */ { 4, 10, 6, 10, 4, 1, 8, 1, 4, 1, 8, 2, 11, 2, 8, 2, 11, 6, 2, 6, 10 }, + /* 171: 0, 1, 3, 5, 7, */ { 2, 7, 6, 7, 2, 8, 1, 8, 2, 8, 1, 4, 5, 4, 1, 4, 5, 6, 4, 6, 7 }, + /* 203: 0, 1, 3, 6, 7, */ { 5, 10, 7, 2, 7, 10, 7, 2, 8, 1, 8, 2, 8, 1, 9, 5, 9, 1, 10, 5, 1 }, + /* 211: 0, 1, 4, 6, 7, */ { 1, 9, 3, 4, 3, 9, 3, 4, 11, 5, 11, 4, 11, 5, 10, 1, 10, 5, 9, 1, 5 }, + /* 227: 0, 1, 5, 6, 7, */ { 1, 8, 3, 8, 1, 4, 10, 4, 1, 4, 10, 7, 11, 7, 10, 7, 11, 3, 7, 3, 8 }, + /* 61: 0, 2, 3, 4, 5, */ { 7, 9, 5, 9, 7, 0, 11, 0, 7, 0, 11, 1, 10, 1, 11, 1, 10, 5, 1, 5, 9 }, + /* 93: 0, 2, 3, 4, 6, */ { 1, 6, 5, 6, 1, 11, 0, 11, 1, 11, 0, 7, 4, 7, 0, 7, 4, 5, 7, 5, 6 }, + /* 109: 0, 2, 3, 5, 6, */ { 4, 9, 6, 1, 6, 9, 6, 1, 11, 0, 11, 1, 11, 0, 8, 4, 8, 0, 9, 4, 0 }, + /* 117: 0, 2, 4, 5, 6, */ { 3, 0, 7, 9, 7, 0, 7, 9, 6, 1, 6, 9, 6, 1, 2, 3, 2, 1, 0, 3, 1 }, + /* 213: 0, 2, 4, 6, 7, */ { 1, 2, 5, 11, 5, 2, 5, 11, 4, 3, 4, 11, 4, 3, 0, 1, 0, 3, 2, 1, 3 }, + /* 121: 0, 3, 4, 5, 6, */ { 0, 11, 2, 11, 0, 7, 9, 7, 0, 7, 9, 6, 10, 6, 9, 6, 10, 2, 6, 2, 11 }, + /* 233: 0, 3, 5, 6, 7, */ { 0, 8, 2, 7, 2, 8, 2, 7, 10, 4, 10, 7, 10, 4, 9, 0, 9, 4, 8, 0, 4 }, + /* 62: 1, 2, 3, 4, 5, */ { 7, 8, 5, 0, 5, 8, 5, 0, 10, 3, 10, 0, 10, 3, 11, 7, 11, 3, 8, 7, 3 }, + /* 158: 1, 2, 3, 4, 7, */ { 6, 8, 4, 8, 6, 3, 10, 3, 6, 3, 10, 0, 9, 0, 10, 0, 9, 4, 0, 4, 8 }, + /* 174: 1, 2, 3, 5, 7, */ { 0, 5, 4, 5, 0, 10, 3, 10, 0, 10, 3, 6, 7, 6, 3, 6, 7, 4, 6, 4, 5 }, + /* 182: 1, 2, 4, 5, 7, */ { 2, 10, 0, 5, 0, 10, 0, 5, 8, 6, 8, 5, 8, 6, 11, 2, 11, 6, 10, 2, 6 }, + /* 214: 1, 2, 4, 6, 7, */ { 2, 9, 0, 9, 2, 5, 11, 5, 2, 5, 11, 4, 8, 4, 11, 4, 8, 0, 4, 0, 9 }, + /* 186: 1, 3, 4, 5, 7, */ { 2, 3, 6, 8, 6, 3, 6, 8, 5, 0, 5, 8, 5, 0, 1, 2, 1, 0, 3, 2, 0 }, + /* 234: 1, 3, 5, 6, 7, */ { 0, 1, 4, 10, 4, 1, 4, 10, 7, 2, 7, 10, 7, 2, 3, 0, 3, 2, 1, 0, 2 }, + /* 124: 2, 3, 4, 5, 6, */ { 3, 11, 1, 6, 1, 11, 1, 6, 9, 7, 9, 6, 9, 7, 8, 3, 8, 7, 11, 3, 7 }, + /* 188: 2, 3, 4, 5, 7, */ { 3, 10, 1, 10, 3, 6, 8, 6, 3, 6, 8, 5, 9, 5, 8, 5, 9, 1, 5, 1, 10 } + }; + return &tiling6_1_2[config][0]; + }; // end of Tiling6_1_2 + + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 6.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling6_2(unsigned char config) + { + static const char tiling6_2[48][15] = { + /* 67: 0, 1, 6, */ { 1, 10, 3, 6, 3, 10, 3, 6, 8, 5, 8, 6, 8, 5, 9 }, + /* 131: 0, 1, 7, */ { 1, 11, 3, 11, 1, 6, 9, 6, 1, 6, 9, 7, 8, 7, 9 }, + /* 21: 0, 2, 4, */ { 4, 1, 0, 1, 4, 10, 7, 10, 4, 10, 7, 2, 3, 2, 7 }, + /* 69: 0, 2, 6, */ { 6, 3, 2, 3, 6, 8, 5, 8, 6, 8, 5, 0, 1, 0, 5 }, + /* 41: 0, 3, 5, */ { 0, 9, 2, 5, 2, 9, 2, 5, 11, 4, 11, 5, 11, 4, 8 }, + /* 73: 0, 3, 6, */ { 0, 10, 2, 10, 0, 5, 8, 5, 0, 5, 8, 6, 11, 6, 8 }, + /* 81: 0, 4, 6, */ { 4, 5, 0, 10, 0, 5, 0, 10, 3, 6, 3, 10, 3, 6, 7 }, + /* 97: 0, 5, 6, */ { 4, 8, 6, 3, 6, 8, 6, 3, 10, 0, 10, 3, 10, 0, 9 }, + /* 193: 0, 6, 7, */ { 5, 8, 7, 8, 5, 0, 10, 0, 5, 0, 10, 3, 11, 3, 10 }, + /* 22: 1, 2, 4, */ { 2, 8, 0, 8, 2, 7, 10, 7, 2, 7, 10, 4, 9, 4, 10 }, + /* 134: 1, 2, 7, */ { 2, 11, 0, 7, 0, 11, 0, 7, 9, 6, 9, 7, 9, 6, 10 }, + /* 42: 1, 3, 5, */ { 5, 2, 1, 2, 5, 11, 4, 11, 5, 11, 4, 3, 0, 3, 4 }, + /* 138: 1, 3, 7, */ { 7, 0, 3, 0, 7, 9, 6, 9, 7, 9, 6, 1, 2, 1, 6 }, + /* 146: 1, 4, 7, */ { 6, 9, 4, 9, 6, 1, 11, 1, 6, 1, 11, 0, 8, 0, 11 }, + /* 162: 1, 5, 7, */ { 5, 6, 1, 11, 1, 6, 1, 11, 0, 7, 0, 11, 0, 7, 4 }, + /* 194: 1, 6, 7, */ { 5, 9, 7, 0, 7, 9, 7, 0, 11, 1, 11, 0, 11, 1, 10 }, + /* 28: 2, 3, 4, */ { 3, 8, 1, 4, 1, 8, 1, 4, 10, 7, 10, 4, 10, 7, 11 }, + /* 44: 2, 3, 5, */ { 3, 9, 1, 9, 3, 4, 11, 4, 3, 4, 11, 5, 10, 5, 11 }, + /* 52: 2, 4, 5, */ { 7, 10, 5, 10, 7, 2, 8, 2, 7, 2, 8, 1, 9, 1, 8 }, + /* 84: 2, 4, 6, */ { 6, 7, 2, 8, 2, 7, 2, 8, 1, 4, 1, 8, 1, 4, 5 }, + /* 148: 2, 4, 7, */ { 6, 10, 4, 1, 4, 10, 4, 1, 8, 2, 8, 1, 8, 2, 11 }, + /* 56: 3, 4, 5, */ { 7, 11, 5, 2, 5, 11, 5, 2, 9, 3, 9, 2, 9, 3, 8 }, + /* 104: 3, 5, 6, */ { 4, 11, 6, 11, 4, 3, 9, 3, 4, 3, 9, 2, 10, 2, 9 }, + /* 168: 3, 5, 7, */ { 7, 4, 3, 9, 3, 4, 3, 9, 2, 5, 2, 9, 2, 5, 6 }, + /* 87: 0, 1, 2, 4, 6, */ { 3, 4, 7, 4, 3, 9, 2, 9, 3, 9, 2, 5, 6, 5, 2 }, + /* 151: 0, 1, 2, 4, 7, */ { 6, 11, 4, 3, 4, 11, 4, 3, 9, 2, 9, 3, 9, 2, 10 }, + /* 199: 0, 1, 2, 6, 7, */ { 5, 11, 7, 11, 5, 2, 9, 2, 5, 2, 9, 3, 8, 3, 9 }, + /* 107: 0, 1, 3, 5, 6, */ { 4, 10, 6, 10, 4, 1, 8, 1, 4, 1, 8, 2, 11, 2, 8 }, + /* 171: 0, 1, 3, 5, 7, */ { 2, 7, 6, 7, 2, 8, 1, 8, 2, 8, 1, 4, 5, 4, 1 }, + /* 203: 0, 1, 3, 6, 7, */ { 5, 10, 7, 2, 7, 10, 7, 2, 8, 1, 8, 2, 8, 1, 9 }, + /* 211: 0, 1, 4, 6, 7, */ { 1, 9, 3, 4, 3, 9, 3, 4, 11, 5, 11, 4, 11, 5, 10 }, + /* 227: 0, 1, 5, 6, 7, */ { 1, 8, 3, 8, 1, 4, 10, 4, 1, 4, 10, 7, 11, 7, 10 }, + /* 61: 0, 2, 3, 4, 5, */ { 7, 9, 5, 9, 7, 0, 11, 0, 7, 0, 11, 1, 10, 1, 11 }, + /* 93: 0, 2, 3, 4, 6, */ { 1, 6, 5, 6, 1, 11, 0, 11, 1, 11, 0, 7, 4, 7, 0 }, + /* 109: 0, 2, 3, 5, 6, */ { 4, 9, 6, 1, 6, 9, 6, 1, 11, 0, 11, 1, 11, 0, 8 }, + /* 117: 0, 2, 4, 5, 6, */ { 3, 0, 7, 9, 7, 0, 7, 9, 6, 1, 6, 9, 6, 1, 2 }, + /* 213: 0, 2, 4, 6, 7, */ { 1, 2, 5, 11, 5, 2, 5, 11, 4, 3, 4, 11, 4, 3, 0 }, + /* 121: 0, 3, 4, 5, 6, */ { 0, 11, 2, 11, 0, 7, 9, 7, 0, 7, 9, 6, 10, 6, 9 }, + /* 233: 0, 3, 5, 6, 7, */ { 0, 8, 2, 7, 2, 8, 2, 7, 10, 4, 10, 7, 10, 4, 9 }, + /* 62: 1, 2, 3, 4, 5, */ { 7, 8, 5, 0, 5, 8, 5, 0, 10, 3, 10, 0, 10, 3, 11 }, + /* 158: 1, 2, 3, 4, 7, */ { 6, 8, 4, 8, 6, 3, 10, 3, 6, 3, 10, 0, 9, 0, 10 }, + /* 174: 1, 2, 3, 5, 7, */ { 0, 5, 4, 5, 0, 10, 3, 10, 0, 10, 3, 6, 7, 6, 3 }, + /* 182: 1, 2, 4, 5, 7, */ { 2, 10, 0, 5, 0, 10, 0, 5, 8, 6, 8, 5, 8, 6, 11 }, + /* 214: 1, 2, 4, 6, 7, */ { 2, 9, 0, 9, 2, 5, 11, 5, 2, 5, 11, 4, 8, 4, 11 }, + /* 186: 1, 3, 4, 5, 7, */ { 2, 3, 6, 8, 6, 3, 6, 8, 5, 0, 5, 8, 5, 0, 1 }, + /* 234: 1, 3, 5, 6, 7, */ { 0, 1, 4, 10, 4, 1, 4, 10, 7, 2, 7, 10, 7, 2, 3 }, + /* 124: 2, 3, 4, 5, 6, */ { 3, 11, 1, 6, 1, 11, 1, 6, 9, 7, 9, 6, 9, 7, 8 }, + /* 188: 2, 3, 4, 5, 7, */ { 3, 10, 1, 10, 3, 6, 8, 6, 3, 6, 8, 5, 9, 5, 8 } + }; + return &tiling6_2[config][0]; + }; // end of Tiling6_2 + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief test table for case 7 + * 3 faces to test + eventually the interior + * When the tests on the 3 specified faces are positive : + * - if the test on the interior is positive : 5 first triangles + * - if the test on the interior is negative : 9 next triangles + * When the tests on the first and the second specified faces are positive : 9 next triangles + * When the tests on the first and the third specified faces are positive : 9 next triangles + * When the tests on the second and the third specified faces are positive : 9 next triangles + * When the test on the first specified face is positive : 5 next triangles + * When the test on the second specified face is positive : 5 next triangles + * When the test on the third specified face is positive : 5 next triangles + * When the tests on the 3 specified faces are negative : 3 last triangles + * The support edge for the interior test is marked as the 5th column. + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static char Test7(unsigned char config, int u) + { + static const char test7[16][5] = { + /* 37: 0, 2, 5, */ { 1, 2, 5, 7, 1 }, + /* 133: 0, 2, 7, */ { 3, 4, 5, 7, 3 }, + /* 161: 0, 5, 7, */ { 4, 1, 6, 7, 4 }, + /* 26: 1, 3, 4, */ { 4, 1, 5, 7, 0 }, + /* 74: 1, 3, 6, */ { 2, 3, 5, 7, 2 }, + /* 82: 1, 4, 6, */ { 1, 2, 6, 7, 5 }, + /* 164: 2, 5, 7, */ { 2, 3, 6, 7, 6 }, + /* 88: 3, 4, 6, */ { 3, 4, 6, 7, 7 }, + /* 167: 0, 1, 2, 5, 7, */ { -3, -4, -6, -7, 7 }, + /* 91: 0, 1, 3, 4, 6, */ { -2, -3, -6, -7, 6 }, + /* 173: 0, 2, 3, 5, 7, */ { -1, -2, -6, -7, 5 }, + /* 181: 0, 2, 4, 5, 7, */ { -2, -3, -5, -7, 2 }, + /* 229: 0, 2, 5, 6, 7, */ { -4, -1, -5, -7, 0 }, + /* 94: 1, 2, 3, 4, 6, */ { -4, -1, -6, -7, 4 }, + /* 122: 1, 3, 4, 5, 6, */ { -3, -4, -5, -7, 3 }, + /* 218: 1, 3, 4, 6, 7, */ { -1, -2, -5, -7, 1 } + }; + return test7[config][u]; + }; // end of Test7 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 7.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling7_1(unsigned char config) + { + static const char tiling7_1[16][9] = { + /* 37: 0, 2, 5, */ { 9, 5, 4, 10, 1, 2, 8, 3, 0 }, + /* 133: 0, 2, 7, */ { 11, 7, 6, 8, 3, 0, 10, 1, 2 }, + /* 161: 0, 5, 7, */ { 3, 0, 8, 5, 4, 9, 7, 6, 11 }, + /* 26: 1, 3, 4, */ { 8, 4, 7, 9, 0, 1, 11, 2, 3 }, + /* 74: 1, 3, 6, */ { 10, 6, 5, 11, 2, 3, 9, 0, 1 }, + /* 82: 1, 4, 6, */ { 0, 1, 9, 6, 5, 10, 4, 7, 8 }, + /* 164: 2, 5, 7, */ { 1, 2, 10, 7, 6, 11, 5, 4, 9 }, + /* 88: 3, 4, 6, */ { 2, 3, 11, 4, 7, 8, 6, 5, 10 }, + /* 167: 0, 1, 2, 5, 7, */ { 11, 3, 2, 8, 7, 4, 10, 5, 6 }, + /* 91: 0, 1, 3, 4, 6, */ { 10, 2, 1, 11, 6, 7, 9, 4, 5 }, + /* 173: 0, 2, 3, 5, 7, */ { 9, 1, 0, 10, 5, 6, 8, 7, 4 }, + /* 181: 0, 2, 4, 5, 7, */ { 5, 6, 10, 3, 2, 11, 1, 0, 9 }, + /* 229: 0, 2, 5, 6, 7, */ { 7, 4, 8, 1, 0, 9, 3, 2, 11 }, + /* 94: 1, 2, 3, 4, 6, */ { 8, 0, 3, 9, 4, 5, 11, 6, 7 }, + /* 122: 1, 3, 4, 5, 6, */ { 6, 7, 11, 0, 3, 8, 2, 1, 10 }, + /* 218: 1, 3, 4, 6, 7, */ { 4, 5, 9, 2, 1, 10, 0, 3, 8 } + }; + return &tiling7_1[config][0]; + }; // end of Tiling7_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 7.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling7_2(unsigned char config, int u) + { + static const char tiling7_2[16][3][15] = { + /* 37: 0, 2, 5, */ { + /* 1,0 */ { 1, 2, 10, 3, 4, 8, 4, 3, 5, 0, 5, 3, 5, 0, 9 }, + /* 0,1 */ { 3, 0, 8, 9, 1, 4, 2, 4, 1, 4, 2, 5, 10, 5, 2 }, + /* 1,1 */ { 9, 5, 4, 0, 10, 1, 10, 0, 8, 10, 8, 2, 3, 2, 8 } + }, + /* 133: 0, 2, 7, */ { + /* 1,0 */ { 3, 0, 8, 1, 6, 10, 6, 1, 7, 2, 7, 1, 7, 2, 11 }, + /* 0,1 */ { 1, 2, 10, 11, 3, 6, 0, 6, 3, 6, 0, 7, 8, 7, 0 }, + /* 1,1 */ { 11, 7, 6, 2, 8, 3, 8, 2, 10, 8, 10, 0, 1, 0, 10 } + }, + /* 161: 0, 5, 7, */ { + /* 1,0 */ { 9, 5, 4, 11, 3, 6, 0, 6, 3, 6, 0, 7, 8, 7, 0 }, + /* 0,1 */ { 11, 7, 6, 3, 4, 8, 4, 3, 5, 0, 5, 3, 5, 0, 9 }, + /* 1,1 */ { 3, 0, 8, 4, 9, 7, 11, 7, 9, 5, 11, 9, 11, 5, 6 } + }, + /* 26: 1, 3, 4, */ { + /* 1,0 */ { 0, 1, 9, 2, 7, 11, 7, 2, 4, 3, 4, 2, 4, 3, 8 }, + /* 0,1 */ { 2, 3, 11, 8, 0, 7, 1, 7, 0, 7, 1, 4, 9, 4, 1 }, + /* 1,1 */ { 8, 4, 7, 3, 9, 0, 9, 3, 11, 9, 11, 1, 2, 1, 11 } + }, + /* 74: 1, 3, 6, */ { + /* 1,0 */ { 2, 3, 11, 0, 5, 9, 5, 0, 6, 1, 6, 0, 6, 1, 10 }, + /* 0,1 */ { 0, 1, 9, 10, 2, 5, 3, 5, 2, 5, 3, 6, 11, 6, 3 }, + /* 1,1 */ { 6, 5, 10, 1, 11, 2, 11, 1, 9, 11, 9, 3, 0, 3, 9 } + }, + /* 82: 1, 4, 6, */ { + /* 1,0 */ { 6, 5, 10, 8, 0, 7, 1, 7, 0, 7, 1, 4, 9, 4, 1 }, + /* 0,1 */ { 8, 4, 7, 0, 5, 9, 5, 0, 6, 1, 6, 0, 6, 1, 10 }, + /* 1,1 */ { 0, 1, 9, 5, 10, 4, 8, 4, 10, 6, 8, 10, 8, 6, 7 } + }, + /* 164: 2, 5, 7, */ { + /* 1,0 */ { 11, 7, 6, 9, 1, 4, 2, 4, 1, 4, 2, 5, 10, 5, 2 }, + /* 0,1 */ { 9, 5, 4, 1, 6, 10, 6, 1, 7, 2, 7, 1, 7, 2, 11 }, + /* 1,1 */ { 1, 2, 10, 6, 11, 5, 9, 5, 11, 7, 9, 11, 9, 7, 4 } + }, + /* 88: 3, 4, 6, */ { + /* 1,0 */ { 8, 4, 7, 10, 2, 5, 3, 5, 2, 5, 3, 6, 11, 6, 3 }, + /* 0,1 */ { 6, 5, 10, 2, 7, 11, 7, 2, 4, 3, 4, 2, 4, 3, 8 }, + /* 1,1 */ { 2, 3, 11, 7, 8, 6, 10, 6, 8, 4, 10, 8, 10, 4, 5 } + }, + /* 167: 0, 1, 2, 5, 7, */ { + /* 1,0 */ { 7, 4, 8, 5, 2, 10, 2, 5, 3, 6, 3, 5, 3, 6, 11 }, + /* 0,1 */ { 10, 5, 6, 11, 7, 2, 4, 2, 7, 2, 4, 3, 8, 3, 4 }, + /* 1,1 */ { 11, 3, 2, 6, 8, 7, 8, 6, 10, 8, 10, 4, 5, 4, 10 } + }, + /* 91: 0, 1, 3, 4, 6, */ { + /* 1,0 */ { 6, 7, 11, 4, 1, 9, 1, 4, 2, 5, 2, 4, 2, 5, 10 }, + /* 0,1 */ { 4, 5, 9, 10, 6, 1, 7, 1, 6, 1, 7, 2, 11, 2, 7 }, + /* 1,1 */ { 10, 2, 1, 5, 11, 6, 11, 5, 9, 11, 9, 7, 4, 7, 9 } + }, + /* 173: 0, 2, 3, 5, 7, */ { + /* 1,0 */ { 10, 5, 6, 7, 0, 8, 0, 7, 1, 4, 1, 7, 1, 4, 9 }, + /* 0,1 */ { 7, 4, 8, 9, 5, 0, 6, 0, 5, 0, 6, 1, 10, 1, 6 }, + /* 1,1 */ { 9, 1, 0, 4, 10, 5, 10, 4, 8, 10, 8, 6, 7, 6, 8 } + }, + /* 181: 0, 2, 4, 5, 7, */ { + /* 1,0 */ { 11, 3, 2, 9, 5, 0, 6, 0, 5, 0, 6, 1, 10, 1, 6 }, + /* 0,1 */ { 9, 1, 0, 5, 2, 10, 2, 5, 3, 6, 3, 5, 3, 6, 11 }, + /* 1,1 */ { 10, 5, 6, 2, 11, 1, 9, 1, 11, 3, 9, 11, 9, 3, 0 } + }, + /* 229: 0, 2, 5, 6, 7, */ { + /* 1,0 */ { 9, 1, 0, 11, 7, 2, 4, 2, 7, 2, 4, 3, 8, 3, 4 }, + /* 0,1 */ { 11, 3, 2, 7, 0, 8, 0, 7, 1, 4, 1, 7, 1, 4, 9 }, + /* 1,1 */ { 7, 4, 8, 0, 9, 3, 11, 3, 9, 1, 11, 9, 11, 1, 2 } + }, + /* 94: 1, 2, 3, 4, 6, */ { + /* 1,0 */ { 4, 5, 9, 6, 3, 11, 3, 6, 0, 7, 0, 6, 0, 7, 8 }, + /* 0,1 */ { 6, 7, 11, 8, 4, 3, 5, 3, 4, 3, 5, 0, 9, 0, 5 }, + /* 1,1 */ { 8, 0, 3, 7, 9, 4, 9, 7, 11, 9, 11, 5, 6, 5, 11 } + }, + /* 122: 1, 3, 4, 5, 6, */ { + /* 1,0 */ { 8, 0, 3, 10, 6, 1, 7, 1, 6, 1, 7, 2, 11, 2, 7 }, + /* 0,1 */ { 10, 2, 1, 6, 3, 11, 3, 6, 0, 7, 0, 6, 0, 7, 8 }, + /* 1,1 */ { 6, 7, 11, 3, 8, 2, 10, 2, 8, 0, 10, 8, 10, 0, 1 } + }, + /* 218: 1, 3, 4, 6, 7, */ { + /* 1,0 */ { 10, 2, 1, 8, 4, 3, 5, 3, 4, 3, 5, 0, 9, 0, 5 }, + /* 0,1 */ { 8, 0, 3, 4, 1, 9, 1, 4, 2, 5, 2, 4, 2, 5, 10 }, + /* 1,1 */ { 4, 5, 9, 1, 10, 0, 8, 0, 10, 2, 8, 10, 8, 2, 3 } } + }; + return &tiling7_2[config][u][0]; + }; // end of Tiling7_2 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 7.3 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling7_3(unsigned char config, unsigned char u) + { + static const char tiling7_3[16][3][27] = { + /* 37: 0, 2, 5, */ { + /* 1,0 */ { 12, 2, 10, 12, 10, 5, 12, 5, 4, 12, 4, 8, 12, 8, 3, 12, 3, 0, 12, 0, 9, 12, 9, 1, 12, 1, 2 }, + /* 0,1 */ { 12, 5, 4, 12, 4, 8, 12, 8, 3, 12, 3, 2, 12, 2, 10, 12, 10, 1, 12, 1, 0, 12, 0, 9, 12, 9, 5 }, + /* 1,1 */ { 5, 4, 12, 10, 5, 12, 2, 10, 12, 3, 2, 12, 8, 3, 12, 0, 8, 12, 1, 0, 12, 9, 1, 12, 4, 9, 12 } + }, + /* 133: 0, 2, 7, */ { + /* 1,0 */ { 12, 0, 8, 12, 8, 7, 12, 7, 6, 12, 6, 10, 12, 10, 1, 12, 1, 2, 12, 2, 11, 12, 11, 3, 12, 3, 0 }, + /* 0,1 */ { 12, 7, 6, 12, 6, 10, 12, 10, 1, 12, 1, 0, 12, 0, 8, 12, 8, 3, 12, 3, 2, 12, 2, 11, 12, 11, 7 }, + /* 1,1 */ { 7, 6, 12, 8, 7, 12, 0, 8, 12, 1, 0, 12, 10, 1, 12, 2, 10, 12, 3, 2, 12, 11, 3, 12, 6, 11, 12 } + }, + /* 161: 0, 5, 7, */ { + /* 1,0 */ { 9, 5, 12, 0, 9, 12, 3, 0, 12, 11, 3, 12, 6, 11, 12, 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12 }, + /* 0,1 */ { 3, 0, 12, 11, 3, 12, 6, 11, 12, 5, 6, 12, 9, 5, 12, 4, 9, 12, 7, 4, 12, 8, 7, 12, 0, 8, 12 }, + /* 1,1 */ { 12, 3, 0, 12, 0, 9, 12, 9, 5, 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4, 12, 4, 8, 12, 8, 3 } + }, + /* 26: 1, 3, 4, */ { + /* 1,0 */ { 12, 1, 9, 12, 9, 4, 12, 4, 7, 12, 7, 11, 12, 11, 2, 12, 2, 3, 12, 3, 8, 12, 8, 0, 12, 0, 1 }, + /* 0,1 */ { 12, 4, 7, 12, 7, 11, 12, 11, 2, 12, 2, 1, 12, 1, 9, 12, 9, 0, 12, 0, 3, 12, 3, 8, 12, 8, 4 }, + /* 1,1 */ { 4, 7, 12, 9, 4, 12, 1, 9, 12, 2, 1, 12, 11, 2, 12, 3, 11, 12, 0, 3, 12, 8, 0, 12, 7, 8, 12 } + }, + /* 74: 1, 3, 6, */ { + /* 1,0 */ { 12, 3, 11, 12, 11, 6, 12, 6, 5, 12, 5, 9, 12, 9, 0, 12, 0, 1, 12, 1, 10, 12, 10, 2, 12, 2, 3 }, + /* 0,1 */ { 12, 6, 5, 12, 5, 9, 12, 9, 0, 12, 0, 3, 12, 3, 11, 12, 11, 2, 12, 2, 1, 12, 1, 10, 12, 10, 6 }, + /* 1,1 */ { 6, 5, 12, 11, 6, 12, 3, 11, 12, 0, 3, 12, 9, 0, 12, 1, 9, 12, 2, 1, 12, 10, 2, 12, 5, 10, 12 } + }, + /* 82: 1, 4, 6, */ { + /* 1,0 */ { 10, 6, 12, 1, 10, 12, 0, 1, 12, 8, 0, 12, 7, 8, 12, 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12 }, + /* 0,1 */ { 0, 1, 12, 8, 0, 12, 7, 8, 12, 6, 7, 12, 10, 6, 12, 5, 10, 12, 4, 5, 12, 9, 4, 12, 1, 9, 12 }, + /* 1,1 */ { 12, 0, 1, 12, 1, 10, 12, 10, 6, 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5, 12, 5, 9, 12, 9, 0 } + }, + /* 164: 2, 5, 7, */ { + /* 1,0 */ { 11, 7, 12, 2, 11, 12, 1, 2, 12, 9, 1, 12, 4, 9, 12, 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12 }, + /* 0,1 */ { 1, 2, 12, 9, 1, 12, 4, 9, 12, 7, 4, 12, 11, 7, 12, 6, 11, 12, 5, 6, 12, 10, 5, 12, 2, 10, 12 }, + /* 1,1 */ { 12, 1, 2, 12, 2, 11, 12, 11, 7, 12, 7, 4, 12, 4, 9, 12, 9, 5, 12, 5, 6, 12, 6, 10, 12, 10, 1 } + }, + /* 88: 3, 4, 6, */ { + /* 1,0 */ { 8, 4, 12, 3, 8, 12, 2, 3, 12, 10, 2, 12, 5, 10, 12, 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12 }, + /* 0,1 */ { 2, 3, 12, 10, 2, 12, 5, 10, 12, 4, 5, 12, 8, 4, 12, 7, 8, 12, 6, 7, 12, 11, 6, 12, 3, 11, 12 }, + /* 1,1 */ { 12, 2, 3, 12, 3, 8, 12, 8, 4, 12, 4, 5, 12, 5, 10, 12, 10, 6, 12, 6, 7, 12, 7, 11, 12, 11, 2 } + }, + /* 167: 0, 1, 2, 5, 7, */ { + /* 1,0 */ { 12, 4, 8, 12, 8, 3, 12, 3, 2, 12, 2, 10, 12, 10, 5, 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4 }, + /* 0,1 */ { 12, 3, 2, 12, 2, 10, 12, 10, 5, 12, 5, 4, 12, 4, 8, 12, 8, 7, 12, 7, 6, 12, 6, 11, 12, 11, 3 }, + /* 1,1 */ { 3, 2, 12, 8, 3, 12, 4, 8, 12, 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12, 11, 7, 12, 2, 11, 12 } + }, + /* 91: 0, 1, 3, 4, 6, */ { + /* 1,0 */ { 12, 7, 11, 12, 11, 2, 12, 2, 1, 12, 1, 9, 12, 9, 4, 12, 4, 5, 12, 5, 10, 12, 10, 6, 12, 6, 7 }, + /* 0,1 */ { 12, 2, 1, 12, 1, 9, 12, 9, 4, 12, 4, 7, 12, 7, 11, 12, 11, 6, 12, 6, 5, 12, 5, 10, 12, 10, 2 }, + /* 1,1 */ { 2, 1, 12, 11, 2, 12, 7, 11, 12, 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12, 10, 6, 12, 1, 10, 12 } + }, + /* 173: 0, 2, 3, 5, 7, */ { + /* 1,0 */ { 12, 6, 10, 12, 10, 1, 12, 1, 0, 12, 0, 8, 12, 8, 7, 12, 7, 4, 12, 4, 9, 12, 9, 5, 12, 5, 6 }, + /* 0,1 */ { 12, 1, 0, 12, 0, 8, 12, 8, 7, 12, 7, 6, 12, 6, 10, 12, 10, 5, 12, 5, 4, 12, 4, 9, 12, 9, 1 }, + /* 1,1 */ { 1, 0, 12, 10, 1, 12, 6, 10, 12, 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12, 9, 5, 12, 0, 9, 12 } + }, + /* 181: 0, 2, 4, 5, 7, */ { + /* 1,0 */ { 11, 3, 12, 6, 11, 12, 5, 6, 12, 9, 5, 12, 0, 9, 12, 1, 0, 12, 10, 1, 12, 2, 10, 12, 3, 2, 12 }, + /* 0,1 */ { 5, 6, 12, 9, 5, 12, 0, 9, 12, 3, 0, 12, 11, 3, 12, 2, 11, 12, 1, 2, 12, 10, 1, 12, 6, 10, 12 }, + /* 1,1 */ { 12, 5, 6, 12, 6, 11, 12, 11, 3, 12, 3, 0, 12, 0, 9, 12, 9, 1, 12, 1, 2, 12, 2, 10, 12, 10, 5 } + }, + /* 229: 0, 2, 5, 6, 7, */ { + /* 1,0 */ { 9, 1, 12, 4, 9, 12, 7, 4, 12, 11, 7, 12, 2, 11, 12, 3, 2, 12, 8, 3, 12, 0, 8, 12, 1, 0, 12 }, + /* 0,1 */ { 7, 4, 12, 11, 7, 12, 2, 11, 12, 1, 2, 12, 9, 1, 12, 0, 9, 12, 3, 0, 12, 8, 3, 12, 4, 8, 12 }, + /* 1,1 */ { 12, 7, 4, 12, 4, 9, 12, 9, 1, 12, 1, 2, 12, 2, 11, 12, 11, 3, 12, 3, 0, 12, 0, 8, 12, 8, 7 } + }, + /* 94: 1, 2, 3, 4, 6, */ { + /* 1,0 */ { 12, 5, 9, 12, 9, 0, 12, 0, 3, 12, 3, 11, 12, 11, 6, 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5 }, + /* 0,1 */ { 12, 0, 3, 12, 3, 11, 12, 11, 6, 12, 6, 5, 12, 5, 9, 12, 9, 4, 12, 4, 7, 12, 7, 8, 12, 8, 0 }, + /* 1,1 */ { 0, 3, 12, 9, 0, 12, 5, 9, 12, 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12, 8, 4, 12, 3, 8, 12 } + }, + /* 122: 1, 3, 4, 5, 6, */ { + /* 1,0 */ { 8, 0, 12, 7, 8, 12, 6, 7, 12, 10, 6, 12, 1, 10, 12, 2, 1, 12, 11, 2, 12, 3, 11, 12, 0, 3, 12 }, + /* 0,1 */ { 6, 7, 12, 10, 6, 12, 1, 10, 12, 0, 1, 12, 8, 0, 12, 3, 8, 12, 2, 3, 12, 11, 2, 12, 7, 11, 12 }, + /* 1,1 */ { 12, 6, 7, 12, 7, 8, 12, 8, 0, 12, 0, 1, 12, 1, 10, 12, 10, 2, 12, 2, 3, 12, 3, 11, 12, 11, 6 } + }, + /* 218: 1, 3, 4, 6, 7, */ { + /* 1,0 */ { 10, 2, 12, 5, 10, 12, 4, 5, 12, 8, 4, 12, 3, 8, 12, 0, 3, 12, 9, 0, 12, 1, 9, 12, 2, 1, 12 }, + /* 0,1 */ { 4, 5, 12, 8, 4, 12, 3, 8, 12, 2, 3, 12, 10, 2, 12, 1, 10, 12, 0, 1, 12, 9, 0, 12, 5, 9, 12 }, + /* 1,1 */ { 12, 4, 5, 12, 5, 10, 12, 10, 2, 12, 2, 3, 12, 3, 8, 12, 8, 0, 12, 0, 1, 12, 1, 9, 12, 9, 4 } } + }; + return &tiling7_3[config][u][0]; + }; // end of Tiling7_3 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 7.4.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling7_4_1(unsigned char config) + { + static const char tiling7_4_1[16][15] = { + /* 37: 0, 2, 5, */ { 3, 4, 8, 4, 3, 10, 2, 10, 3, 4, 10, 5, 9, 1, 0 }, + /* 133: 0, 2, 7, */ { 1, 6, 10, 6, 1, 8, 0, 8, 1, 6, 8, 7, 11, 3, 2 }, + /* 161: 0, 5, 7, */ { 11, 3, 6, 9, 6, 3, 6, 9, 5, 0, 9, 3, 7, 4, 8 }, + /* 26: 1, 3, 4, */ { 2, 7, 11, 7, 2, 9, 1, 9, 2, 7, 9, 4, 8, 0, 3 }, + /* 74: 1, 3, 6, */ { 0, 5, 9, 5, 0, 11, 3, 11, 0, 5, 11, 6, 10, 2, 1 }, + /* 82: 1, 4, 6, */ { 8, 0, 7, 10, 7, 0, 7, 10, 6, 1, 10, 0, 4, 5, 9 }, + /* 164: 2, 5, 7, */ { 9, 1, 4, 11, 4, 1, 4, 11, 7, 2, 11, 1, 5, 6, 10 }, + /* 88: 3, 4, 6, */ { 10, 2, 5, 8, 5, 2, 5, 8, 4, 3, 8, 2, 6, 7, 11 }, + /* 167: 0, 1, 2, 5, 7, */ { 5, 2, 10, 2, 5, 8, 4, 8, 5, 2, 8, 3, 11, 7, 6 }, + /* 91: 0, 1, 3, 4, 6, */ { 4, 1, 9, 1, 4, 11, 7, 11, 4, 1, 11, 2, 10, 6, 5 }, + /* 173: 0, 2, 3, 5, 7, */ { 7, 0, 8, 0, 7, 10, 6, 10, 7, 0, 10, 1, 9, 5, 4 }, + /* 181: 0, 2, 4, 5, 7, */ { 9, 5, 0, 11, 0, 5, 0, 11, 3, 6, 11, 5, 1, 2, 10 }, + /* 229: 0, 2, 5, 6, 7, */ { 11, 7, 2, 9, 2, 7, 2, 9, 1, 4, 9, 7, 3, 0, 8 }, + /* 94: 1, 2, 3, 4, 6, */ { 6, 3, 11, 3, 6, 9, 5, 9, 6, 3, 9, 0, 8, 4, 7 }, + /* 122: 1, 3, 4, 5, 6, */ { 10, 6, 1, 8, 1, 6, 1, 8, 0, 7, 8, 6, 2, 3, 11 }, + /* 218: 1, 3, 4, 6, 7, */ { 8, 4, 3, 10, 3, 4, 3, 10, 2, 5, 10, 4, 0, 1, 9 } + }; + return &tiling7_4_1[config][0]; + }; // end of Tiling7_4_1 + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 7.4.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling7_4_2(unsigned char config) + { + static const char tiling7_4_2[16][27] = { + /* 37: 0, 2, 5, */ { 9, 4, 8, 4, 9, 5, 10, 5, 9, 1, 10, 9, 10, 1, 2, 0, 2, 1, 2, 0, 3, 8, 3, 0, 9, 8, 0 }, + /* 133: 0, 2, 7, */ { 11, 6, 10, 6, 11, 7, 8, 7, 11, 3, 8, 11, 8, 3, 0, 2, 0, 3, 0, 2, 1, 10, 1, 2, 11, 10, 2 }, + /* 161: 0, 5, 7, */ { 11, 3, 8, 0, 8, 3, 8, 0, 9, 8, 9, 4, 5, 4, 9, 4, 5, 7, 6, 7, 5, 7, 6, 11, 7, 11, 8 }, + /* 26: 1, 3, 4, */ { 8, 7, 11, 7, 8, 4, 9, 4, 8, 0, 9, 8, 9, 0, 1, 3, 1, 0, 1, 3, 2, 11, 2, 3, 8, 11, 3 }, + /* 74: 1, 3, 6, */ { 10, 5, 9, 5, 10, 6, 11, 6, 10, 2, 11, 10, 11, 2, 3, 1, 3, 2, 3, 1, 0, 9, 0, 1, 10, 9, 1 }, + /* 82: 1, 4, 6, */ { 8, 0, 9, 1, 9, 0, 9, 1, 10, 9, 10, 5, 6, 5, 10, 5, 6, 4, 7, 4, 6, 4, 7, 8, 4, 8, 9 }, + /* 164: 2, 5, 7, */ { 9, 1, 10, 2, 10, 1, 10, 2, 11, 10, 11, 6, 7, 6, 11, 6, 7, 5, 4, 5, 7, 5, 4, 9, 5, 9, 10 }, + /* 88: 3, 4, 6, */ { 10, 2, 11, 3, 11, 2, 11, 3, 8, 11, 8, 7, 4, 7, 8, 7, 4, 6, 5, 6, 4, 6, 5, 10, 6, 10, 11 }, + /* 167: 0, 1, 2, 5, 7, */ { 11, 2, 10, 2, 11, 3, 8, 3, 11, 7, 8, 11, 8, 7, 4, 6, 4, 7, 4, 6, 5, 10, 5, 6, 11, 10, 6 }, + /* 91: 0, 1, 3, 4, 6, */ { 10, 1, 9, 1, 10, 2, 11, 2, 10, 6, 11, 10, 11, 6, 7, 5, 7, 6, 7, 5, 4, 9, 4, 5, 10, 9, 5 }, + /* 173: 0, 2, 3, 5, 7, */ { 9, 0, 8, 0, 9, 1, 10, 1, 9, 5, 10, 9, 10, 5, 6, 4, 6, 5, 6, 4, 7, 8, 7, 4, 9, 8, 4 }, + /* 181: 0, 2, 4, 5, 7, */ { 9, 5, 10, 6, 10, 5, 10, 6, 11, 10, 11, 2, 3, 2, 11, 2, 3, 1, 0, 1, 3, 1, 0, 9, 1, 9, 10 }, + /* 229: 0, 2, 5, 6, 7, */ { 11, 7, 8, 4, 8, 7, 8, 4, 9, 8, 9, 0, 1, 0, 9, 0, 1, 3, 2, 3, 1, 3, 2, 11, 3, 11, 8 }, + /* 94: 1, 2, 3, 4, 6, */ { 8, 3, 11, 3, 8, 0, 9, 0, 8, 4, 9, 8, 9, 4, 5, 7, 5, 4, 5, 7, 6, 11, 6, 7, 8, 11, 7 }, + /* 122: 1, 3, 4, 5, 6, */ { 10, 6, 11, 7, 11, 6, 11, 7, 8, 11, 8, 3, 0, 3, 8, 3, 0, 2, 1, 2, 0, 2, 1, 10, 2, 10, 11 }, + /* 218: 1, 3, 4, 6, 7, */ { 8, 4, 9, 5, 9, 4, 9, 5, 10, 9, 10, 1, 2, 1, 10, 1, 2, 0, 3, 0, 2, 0, 3, 8, 0, 8, 9 } + }; + return &tiling7_4_2[config][0]; + }; // end of Tiling7_4_2 + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 8 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling8(unsigned char config) + { + static const char tiling8[6][6] = { + /* 15: 0, 1, 2, 3, */ { 9, 8, 10, 10, 8, 11 }, + /* 51: 0, 1, 4, 5, */ { 1, 5, 3, 3, 5, 7 }, + /* 153: 0, 3, 4, 7, */ { 0, 4, 2, 4, 6, 2 }, + /* 102: 1, 2, 5, 6, */ { 0, 2, 4, 4, 2, 6 }, + /* 204: 2, 3, 6, 7, */ { 1, 3, 5, 3, 7, 5 }, + /* 240: 4, 5, 6, 7, */ { 9, 10, 8, 10, 11, 8 } + }; + return &tiling8[config][0]; + }; // end of Tiling8 + //_____________________________________________________________________________ + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 9 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling9(unsigned char config) + { + static const char tiling9[8][12] = { + /* 39: 0, 1, 2, 5, */ { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8 }, + /* 27: 0, 1, 3, 4, */ { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1 }, + /* 141: 0, 2, 3, 7, */ { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8 }, + /* 177: 0, 4, 5, 7, */ { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5 }, + /* 78: 1, 2, 3, 6, */ { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9 }, + /* 114: 1, 4, 5, 6, */ { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0 }, + /* 228: 2, 5, 6, 7, */ { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2 }, + /* 216: 3, 4, 6, 7, */ { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4 } + }; + return &tiling9[config][0]; + }; + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief test table for case 10 + * 2 faces to test + eventually the interior + * When the tests on both specified faces are positive : 4 middle triangles (1) + * When the test on the first specified face is positive : 8 first triangles + * When the test on the second specified face is positive : 8 next triangles + * When the tests on both specified faces are negative : + * - if the test on the interior is negative : 4 middle triangles + * - if the test on the interior is positive : 8 last triangles + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static char Test10(unsigned char config, int u) + { + static const char test10[6][3] = { + /* 195: 0, 1, 6, 7, */ { 2, 4, 7 }, + /* 85: 0, 2, 4, 6, */ { 5, 6, 7 }, + /* 105: 0, 3, 5, 6, */ { 1, 3, 7 }, + /* 150: 1, 2, 4, 7, */ { 1, 3, 7 }, + /* 170: 1, 3, 5, 7, */ { 5, 6, 7 }, + /* 60: 2, 3, 4, 5, */ { 2, 4, 7 } + }; + return test10[config][u]; + }; // end of Test10 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 10.1.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling10_1_1(unsigned char config) + { + static const char tiling10_1_1[6][12] = { + /* 195: 0, 1, 6, 7, */ { 5, 10, 7, 11, 7, 10, 8, 1, 9, 1, 8, 3 }, + /* 85: 0, 2, 4, 6, */ { 1, 2, 5, 6, 5, 2, 4, 3, 0, 3, 4, 7 }, + /* 105: 0, 3, 5, 6, */ { 11, 0, 8, 0, 11, 2, 4, 9, 6, 10, 6, 9 }, + /* 150: 1, 2, 4, 7, */ { 9, 0, 10, 2, 10, 0, 6, 8, 4, 8, 6, 11 }, + /* 170: 1, 3, 5, 7, */ { 7, 2, 3, 2, 7, 6, 0, 1, 4, 5, 4, 1 }, + /* 60: 2, 3, 4, 5, */ { 7, 9, 5, 9, 7, 8, 10, 1, 11, 3, 11, 1 } + }; + return &tiling10_1_1[config][0]; + }; // end of Tiling10_1_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 10.1.1 inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling10_1_1_(unsigned char config) + { + static const char tiling10_1_1_[6][12] = { + /* 195: 0, 1, 6, 7, */ { 5, 9, 7, 8, 7, 9, 11, 1, 10, 1, 11, 3 }, + /* 85: 0, 2, 4, 6, */ { 3, 2, 7, 6, 7, 2, 4, 1, 0, 1, 4, 5 }, + /* 105: 0, 3, 5, 6, */ { 10, 0, 9, 0, 10, 2, 4, 8, 6, 11, 6, 8 }, + /* 150: 1, 2, 4, 7, */ { 8, 0, 11, 2, 11, 0, 6, 9, 4, 9, 6, 10 }, + /* 170: 1, 3, 5, 7, */ { 5, 2, 1, 2, 5, 6, 0, 3, 4, 7, 4, 3 }, + /* 60: 2, 3, 4, 5, */ { 7, 10, 5, 10, 7, 11, 9, 1, 8, 3, 8, 1 } + }; + return &tiling10_1_1_[config][0]; + }; // end of Tiling10_1_1_ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 10.1.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling10_1_2(unsigned char config) + { + static const char tiling10_1_2[6][24] = { + /* 195: 0, 1, 6, 7, */ { 3, 11, 7, 3, 7, 8, 9, 8, 7, 5, 9, 7, 9, 5, 10, 9, 10, 1, 3, 1, 10, 11, 3, 10 }, + /* 85: 0, 2, 4, 6, */ { 7, 6, 5, 7, 5, 4, 0, 4, 5, 1, 0, 5, 0, 1, 2, 0, 2, 3, 7, 3, 2, 6, 7, 2 }, + /* 105: 0, 3, 5, 6, */ { 11, 2, 10, 6, 11, 10, 11, 6, 4, 11, 4, 8, 0, 8, 4, 9, 0, 4, 0, 9, 10, 0, 10, 2 }, + /* 150: 1, 2, 4, 7, */ { 11, 2, 10, 11, 10, 6, 4, 6, 10, 9, 4, 10, 4, 9, 0, 4, 0, 8, 11, 8, 0, 2, 11, 0 }, + /* 170: 1, 3, 5, 7, */ { 7, 6, 5, 4, 7, 5, 7, 4, 0, 7, 0, 3, 2, 3, 0, 1, 2, 0, 2, 1, 5, 2, 5, 6 }, + /* 60: 2, 3, 4, 5, */ { 7, 8, 3, 11, 7, 3, 7, 11, 10, 7, 10, 5, 9, 5, 10, 1, 9, 10, 9, 1, 3, 9, 3, 8 } + }; + return &tiling10_1_2[config][0]; + }; // end of Tiling10_1_2 + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 10.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling10_2(unsigned char config) + { + static const char tiling10_2[6][24] = { + /* 195: 0, 1, 6, 7, */ { 12, 5, 9, 12, 9, 8, 12, 8, 3, 12, 3, 1, 12, 1, 10, 12, 10, 11, 12, 11, 7, 12, 7, 5 }, + /* 85: 0, 2, 4, 6, */ { 12, 1, 0, 12, 0, 4, 12, 4, 7, 12, 7, 3, 12, 3, 2, 12, 2, 6, 12, 6, 5, 12, 5, 1 }, + /* 105: 0, 3, 5, 6, */ { 4, 8, 12, 6, 4, 12, 10, 6, 12, 9, 10, 12, 0, 9, 12, 2, 0, 12, 11, 2, 12, 8, 11, 12 }, + /* 150: 1, 2, 4, 7, */ { 12, 9, 4, 12, 4, 6, 12, 6, 11, 12, 11, 8, 12, 8, 0, 12, 0, 2, 12, 2, 10, 12, 10, 9 }, + /* 170: 1, 3, 5, 7, */ { 0, 3, 12, 4, 0, 12, 5, 4, 12, 1, 5, 12, 2, 1, 12, 6, 2, 12, 7, 6, 12, 3, 7, 12 }, + /* 60: 2, 3, 4, 5, */ { 10, 5, 12, 11, 10, 12, 3, 11, 12, 1, 3, 12, 9, 1, 12, 8, 9, 12, 7, 8, 12, 5, 7, 12 } + }; + return &tiling10_2[config][0]; + }; // end of Tiling10_2 + + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 10.2 inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling10_2_(unsigned char config) + { + static const char tiling10_2_[6][24] = { + /* 195: 0, 1, 6, 7, */ { 8, 7, 12, 9, 8, 12, 1, 9, 12, 3, 1, 12, 11, 3, 12, 10, 11, 12, 5, 10, 12, 7, 5, 12 }, + /* 85: 0, 2, 4, 6, */ { 4, 5, 12, 0, 4, 12, 3, 0, 12, 7, 3, 12, 6, 7, 12, 2, 6, 12, 1, 2, 12, 5, 1, 12 }, + /* 105: 0, 3, 5, 6, */ { 12, 11, 6, 12, 6, 4, 12, 4, 9, 12, 9, 10, 12, 10, 2, 12, 2, 0, 12, 0, 8, 12, 8, 11 }, + /* 150: 1, 2, 4, 7, */ { 6, 10, 12, 4, 6, 12, 8, 4, 12, 11, 8, 12, 2, 11, 12, 0, 2, 12, 9, 0, 12, 10, 9, 12 }, + /* 170: 1, 3, 5, 7, */ { 12, 7, 4, 12, 4, 0, 12, 0, 1, 12, 1, 5, 12, 5, 6, 12, 6, 2, 12, 2, 3, 12, 3, 7 }, + /* 60: 2, 3, 4, 5, */ { 12, 7, 11, 12, 11, 10, 12, 10, 1, 12, 1, 3, 12, 3, 8, 12, 8, 9, 12, 9, 5, 12, 5, 7 } + }; + return &tiling10_2_[config][0]; + }; // end of Tiling10_2_ + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 11 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling11(unsigned char config) + { + static const char tiling11[12][12] = { + /* 23: 0, 1, 2, 4, */ { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4 }, + /* 139: 0, 1, 3, 7, */ { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6 }, + /* 99: 0, 1, 5, 6, */ { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10 }, + /* 77: 0, 2, 3, 6, */ { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6 }, + /* 57: 0, 3, 4, 5, */ { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11 }, + /* 209: 0, 4, 6, 7, */ { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0 }, + /* 46: 1, 2, 3, 5, */ { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3 }, + /* 198: 1, 2, 6, 7, */ { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7 }, + /* 178: 1, 4, 5, 7, */ { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11 }, + /* 156: 2, 3, 4, 7, */ { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1 }, + /* 116: 2, 4, 5, 6, */ { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7 }, + /* 232: 3, 5, 6, 7, */ { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9 } + }; + return &tiling11[config][0]; + }; // end of tiling11 + //_____________________________________________________________________________ + + + + //_____________________________________________________________________________ + /** + * \brief test table for case 12 + * 2 faces to test + eventually the interior + * When the tests on both specified faces are positive : 4 middle triangles (1) + * When the test on the first specified face is positive : 8 first triangles + * When the test on the second specified face is positive : 8 next triangles + * When the tests on both specified faces are negative : + * - if the test on the interior is negative : 4 middle triangles + * - if the test on the interior is positive : 8 last triangles + * The support edge for the interior test is marked as the 4th column. + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static char Test12(unsigned char config, int u) + { + static const char test12[24][4] = { + /* 135: 0, 1, 2, 7, */ { 4, 3, 7, 11 }, + /* 75: 0, 1, 3, 6, */ { 3, 2, 7, 10 }, + /* 83: 0, 1, 4, 6, */ { 2, 6, 7, 5 }, + /* 163: 0, 1, 5, 7, */ { 6, 4, 7, 7 }, + /* 45: 0, 2, 3, 5, */ { 2, 1, 7, 9 }, + /* 53: 0, 2, 4, 5, */ { 5, 2, 7, 1 }, + /* 149: 0, 2, 4, 7, */ { 5, 3, 7, 2 }, + /* 101: 0, 2, 5, 6, */ { 5, 1, 7, 0 }, + /* 197: 0, 2, 6, 7, */ { 5, 4, 7, 3 }, + /* 89: 0, 3, 4, 6, */ { 6, 3, 7, 6 }, + /* 169: 0, 3, 5, 7, */ { 1, 6, 7, 4 }, + /* 225: 0, 5, 6, 7, */ { 1, 4, 7, 8 }, + /* 30: 1, 2, 3, 4, */ { 4, 1, 7, 8 }, + /* 86: 1, 2, 4, 6, */ { 6, 1, 7, 4 }, + /* 166: 1, 2, 5, 7, */ { 3, 6, 7, 6 }, + /* 58: 1, 3, 4, 5, */ { 4, 5, 7, 3 }, + /* 154: 1, 3, 4, 7, */ { 1, 5, 7, 0 }, + /* 106: 1, 3, 5, 6, */ { 3, 5, 7, 2 }, + /* 202: 1, 3, 6, 7, */ { 2, 5, 7, 1 }, + /* 210: 1, 4, 6, 7, */ { 1, 2, 7, 9 }, + /* 92: 2, 3, 4, 6, */ { 4, 6, 7, 7 }, + /* 172: 2, 3, 5, 7, */ { 6, 2, 7, 5 }, + /* 180: 2, 4, 5, 7, */ { 2, 3, 7, 10 }, + /* 120: 3, 4, 5, 6, */ { 3, 4, 7, 11 } + }; + return test12[config][u]; + }; + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 12.1.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling12_1_1(unsigned char config) + { + static const char tiling12_1_1[24][12] = { + /* 135: 0, 1, 2, 7, */ { 7, 6, 11, 10, 3, 2, 3, 10, 8, 9, 8, 10 }, + /* 75: 0, 1, 3, 6, */ { 6, 5, 10, 9, 2, 1, 2, 9, 11, 8, 11, 9 }, + /* 83: 0, 1, 4, 6, */ { 10, 6, 5, 7, 9, 4, 9, 7, 1, 3, 1, 7 }, + /* 163: 0, 1, 5, 7, */ { 7, 6, 11, 4, 8, 5, 3, 5, 8, 5, 3, 1 }, + /* 45: 0, 2, 3, 5, */ { 5, 4, 9, 8, 1, 0, 1, 8, 10, 11, 10, 8 }, + /* 53: 0, 2, 4, 5, */ { 1, 2, 10, 0, 9, 3, 5, 3, 9, 3, 5, 7 }, + /* 149: 0, 2, 4, 7, */ { 10, 1, 2, 0, 11, 3, 11, 0, 6, 4, 6, 0 }, + /* 101: 0, 2, 5, 6, */ { 8, 3, 0, 2, 9, 1, 9, 2, 4, 6, 4, 2 }, + /* 197: 0, 2, 6, 7, */ { 3, 0, 8, 2, 11, 1, 7, 1, 11, 1, 7, 5 }, + /* 89: 0, 3, 4, 6, */ { 6, 5, 10, 7, 11, 4, 2, 4, 11, 4, 2, 0 }, + /* 169: 0, 3, 5, 7, */ { 9, 5, 4, 6, 8, 7, 8, 6, 0, 2, 0, 6 }, + /* 225: 0, 5, 6, 7, */ { 8, 3, 0, 7, 4, 11, 9, 11, 4, 11, 9, 10 }, + /* 30: 1, 2, 3, 4, */ { 4, 7, 8, 11, 0, 3, 0, 11, 9, 10, 9, 11 }, + /* 86: 1, 2, 4, 6, */ { 4, 7, 8, 5, 9, 6, 0, 6, 9, 6, 0, 2 }, + /* 166: 1, 2, 5, 7, */ { 11, 7, 6, 4, 10, 5, 10, 4, 2, 0, 2, 4 }, + /* 58: 1, 3, 4, 5, */ { 11, 2, 3, 1, 8, 0, 8, 1, 7, 5, 7, 1 }, + /* 154: 1, 3, 4, 7, */ { 0, 1, 9, 3, 8, 2, 4, 2, 8, 2, 4, 6 }, + /* 106: 1, 3, 5, 6, */ { 2, 3, 11, 1, 10, 0, 6, 0, 10, 0, 6, 4 }, + /* 202: 1, 3, 6, 7, */ { 9, 0, 1, 3, 10, 2, 10, 3, 5, 7, 5, 3 }, + /* 210: 1, 4, 6, 7, */ { 9, 0, 1, 4, 5, 8, 10, 8, 5, 8, 10, 11 }, + /* 92: 2, 3, 4, 6, */ { 8, 4, 7, 5, 11, 6, 11, 5, 3, 1, 3, 5 }, + /* 172: 2, 3, 5, 7, */ { 5, 4, 9, 6, 10, 7, 1, 7, 10, 7, 1, 3 }, + /* 180: 2, 4, 5, 7, */ { 10, 1, 2, 5, 6, 9, 11, 9, 6, 9, 11, 8 }, + /* 120: 3, 4, 5, 6, */ { 11, 2, 3, 6, 7, 10, 8, 10, 7, 10, 8, 9 } + }; + return &tiling12_1_1[config][0]; + }; // end of Tiling12_1_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 12.1.1 inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling12_1_1_(unsigned char config) + { + static const char tiling12_1_1_[24][12] = { + /* 135: 0, 1, 2, 7, */ { 3, 2, 11, 10, 7, 6, 7, 10, 8, 9, 8, 10 }, + /* 75: 0, 1, 3, 6, */ { 2, 1, 10, 9, 6, 5, 6, 9, 11, 8, 11, 9 }, + /* 83: 0, 1, 4, 6, */ { 9, 4, 5, 7, 10, 6, 10, 7, 1, 3, 1, 7 }, + /* 163: 0, 1, 5, 7, */ { 7, 4, 8, 6, 11, 5, 3, 5, 11, 5, 3, 1 }, + /* 45: 0, 2, 3, 5, */ { 1, 0, 9, 8, 5, 4, 5, 8, 10, 11, 10, 8 }, + /* 53: 0, 2, 4, 5, */ { 1, 0, 9, 2, 10, 3, 5, 3, 10, 3, 5, 7 }, + /* 149: 0, 2, 4, 7, */ { 11, 3, 2, 0, 10, 1, 10, 0, 6, 4, 6, 0 }, + /* 101: 0, 2, 5, 6, */ { 9, 1, 0, 2, 8, 3, 8, 2, 4, 6, 4, 2 }, + /* 197: 0, 2, 6, 7, */ { 3, 2, 11, 0, 8, 1, 7, 1, 8, 1, 7, 5 }, + /* 89: 0, 3, 4, 6, */ { 6, 7, 11, 5, 10, 4, 2, 4, 10, 4, 2, 0 }, + /* 169: 0, 3, 5, 7, */ { 8, 7, 4, 6, 9, 5, 9, 6, 0, 2, 0, 6 }, + /* 225: 0, 5, 6, 7, */ { 8, 7, 4, 3, 0, 11, 9, 11, 0, 11, 9, 10 }, + /* 30: 1, 2, 3, 4, */ { 0, 3, 8, 11, 4, 7, 4, 11, 9, 10, 9, 11 }, + /* 86: 1, 2, 4, 6, */ { 4, 5, 9, 7, 8, 6, 0, 6, 8, 6, 0, 2 }, + /* 166: 1, 2, 5, 7, */ { 10, 5, 6, 4, 11, 7, 11, 4, 2, 0, 2, 4 }, + /* 58: 1, 3, 4, 5, */ { 8, 0, 3, 1, 11, 2, 11, 1, 7, 5, 7, 1 }, + /* 154: 1, 3, 4, 7, */ { 0, 3, 8, 1, 9, 2, 4, 2, 9, 2, 4, 6 }, + /* 106: 1, 3, 5, 6, */ { 2, 1, 10, 3, 11, 0, 6, 0, 11, 0, 6, 4 }, + /* 202: 1, 3, 6, 7, */ { 10, 2, 1, 3, 9, 0, 9, 3, 5, 7, 5, 3 }, + /* 210: 1, 4, 6, 7, */ { 9, 4, 5, 0, 1, 8, 10, 8, 1, 8, 10, 11 }, + /* 92: 2, 3, 4, 6, */ { 11, 6, 7, 5, 8, 4, 8, 5, 3, 1, 3, 5 }, + /* 172: 2, 3, 5, 7, */ { 5, 6, 10, 4, 9, 7, 1, 7, 9, 7, 1, 3 }, + /* 180: 2, 4, 5, 7, */ { 10, 5, 6, 1, 2, 9, 11, 9, 2, 9, 11, 8 }, + /* 120: 3, 4, 5, 6, */ { 11, 6, 7, 2, 3, 10, 8, 10, 3, 10, 8, 9 } + }; + return &tiling12_1_1_[config][0]; + }; // end of Tiling12_1_1_ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 12.1.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling12_1_2(unsigned char config) + { + static const char tiling12_1_2[24][24] = { + /* 135: 0, 1, 2, 7, */ { 7, 3, 11, 3, 7, 8, 9, 8, 7, 6, 9, 7, 9, 6, 10, 2, 10, 6, 11, 2, 6, 2, 11, 3 }, + /* 75: 0, 1, 3, 6, */ { 6, 2, 10, 2, 6, 11, 8, 11, 6, 5, 8, 6, 8, 5, 9, 1, 9, 5, 10, 1, 5, 1, 10, 2 }, + /* 83: 0, 1, 4, 6, */ { 10, 9, 5, 9, 10, 1, 3, 1, 10, 6, 3, 10, 3, 6, 7, 4, 7, 6, 5, 4, 6, 4, 5, 9 }, + /* 163: 0, 1, 5, 7, */ { 7, 8, 11, 3, 11, 8, 11, 3, 1, 11, 1, 6, 5, 6, 1, 6, 5, 4, 6, 4, 7, 8, 7, 4 }, + /* 45: 0, 2, 3, 5, */ { 5, 1, 9, 1, 5, 10, 11, 10, 5, 4, 11, 5, 11, 4, 8, 0, 8, 4, 9, 0, 4, 0, 9, 1 }, + /* 53: 0, 2, 4, 5, */ { 1, 9, 10, 5, 10, 9, 10, 5, 7, 10, 7, 2, 3, 2, 7, 2, 3, 0, 2, 0, 1, 9, 1, 0 }, + /* 149: 0, 2, 4, 7, */ { 10, 11, 2, 11, 10, 6, 4, 6, 10, 1, 4, 10, 4, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 11 }, + /* 101: 0, 2, 5, 6, */ { 8, 9, 0, 9, 8, 4, 6, 4, 8, 3, 6, 8, 6, 3, 2, 1, 2, 3, 0, 1, 3, 1, 0, 9 }, + /* 197: 0, 2, 6, 7, */ { 3, 11, 8, 7, 8, 11, 8, 7, 5, 8, 5, 0, 1, 0, 5, 0, 1, 2, 0, 2, 3, 11, 3, 2 }, + /* 89: 0, 3, 4, 6, */ { 6, 11, 10, 2, 10, 11, 10, 2, 0, 10, 0, 5, 4, 5, 0, 5, 4, 7, 5, 7, 6, 11, 6, 7 }, + /* 169: 0, 3, 5, 7, */ { 9, 8, 4, 8, 9, 0, 2, 0, 9, 5, 2, 9, 2, 5, 6, 7, 6, 5, 4, 7, 5, 7, 4, 8 }, + /* 225: 0, 5, 6, 7, */ { 8, 4, 0, 9, 0, 4, 0, 9, 10, 0, 10, 3, 11, 3, 10, 3, 11, 7, 3, 7, 8, 4, 8, 7 }, + /* 30: 1, 2, 3, 4, */ { 4, 0, 8, 0, 4, 9, 10, 9, 4, 7, 10, 4, 10, 7, 11, 3, 11, 7, 8, 3, 7, 3, 8, 0 }, + /* 86: 1, 2, 4, 6, */ { 4, 9, 8, 0, 8, 9, 8, 0, 2, 8, 2, 7, 6, 7, 2, 7, 6, 5, 7, 5, 4, 9, 4, 5 }, + /* 166: 1, 2, 5, 7, */ { 11, 10, 6, 10, 11, 2, 0, 2, 11, 7, 0, 11, 0, 7, 4, 5, 4, 7, 6, 5, 7, 5, 6, 10 }, + /* 58: 1, 3, 4, 5, */ { 11, 8, 3, 8, 11, 7, 5, 7, 11, 2, 5, 11, 5, 2, 1, 0, 1, 2, 3, 0, 2, 0, 3, 8 }, + /* 154: 1, 3, 4, 7, */ { 0, 8, 9, 4, 9, 8, 9, 4, 6, 9, 6, 1, 2, 1, 6, 1, 2, 3, 1, 3, 0, 8, 0, 3 }, + /* 106: 1, 3, 5, 6, */ { 2, 10, 11, 6, 11, 10, 11, 6, 4, 11, 4, 3, 0, 3, 4, 3, 0, 1, 3, 1, 2, 10, 2, 1 }, + /* 202: 1, 3, 6, 7, */ { 9, 10, 1, 10, 9, 5, 7, 5, 9, 0, 7, 9, 7, 0, 3, 2, 3, 0, 1, 2, 0, 2, 1, 10 }, + /* 210: 1, 4, 6, 7, */ { 9, 5, 1, 10, 1, 5, 1, 10, 11, 1, 11, 0, 8, 0, 11, 0, 8, 4, 0, 4, 9, 5, 9, 4 }, + /* 92: 2, 3, 4, 6, */ { 8, 11, 7, 11, 8, 3, 1, 3, 8, 4, 1, 8, 1, 4, 5, 6, 5, 4, 7, 6, 4, 6, 7, 11 }, + /* 172: 2, 3, 5, 7, */ { 5, 10, 9, 1, 9, 10, 9, 1, 3, 9, 3, 4, 7, 4, 3, 4, 7, 6, 4, 6, 5, 10, 5, 6 }, + /* 180: 2, 4, 5, 7, */ { 10, 6, 2, 11, 2, 6, 2, 11, 8, 2, 8, 1, 9, 1, 8, 1, 9, 5, 1, 5, 10, 6, 10, 5 }, + /* 120: 3, 4, 5, 6, */ { 11, 7, 3, 8, 3, 7, 3, 8, 9, 3, 9, 2, 10, 2, 9, 2, 10, 6, 2, 6, 11, 7, 11, 6 } + }; + return &tiling12_1_2[config][0]; + }; // end of Tiling12_1_2 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 12.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling12_2(unsigned char config) + { + static const char tiling12_2[24][24] = { + /* 135: 0, 1, 2, 7, */ { 9, 8, 12, 10, 9, 12, 2, 10, 12, 3, 2, 12, 11, 3, 12, 6, 11, 12, 7, 6, 12, 8, 7, 12 }, + /* 75: 0, 1, 3, 6, */ { 8, 11, 12, 9, 8, 12, 1, 9, 12, 2, 1, 12, 10, 2, 12, 5, 10, 12, 6, 5, 12, 11, 6, 12 }, + /* 83: 0, 1, 4, 6, */ { 3, 1, 12, 7, 3, 12, 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12, 10, 6, 12, 1, 10, 12 }, + /* 163: 0, 1, 5, 7, */ { 12, 3, 1, 12, 1, 5, 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4, 12, 4, 8, 12, 8, 3 }, + /* 45: 0, 2, 3, 5, */ { 11, 10, 12, 8, 11, 12, 0, 8, 12, 1, 0, 12, 9, 1, 12, 4, 9, 12, 5, 4, 12, 10, 5, 12 }, + /* 53: 0, 2, 4, 5, */ { 12, 5, 7, 12, 7, 3, 12, 3, 2, 12, 2, 10, 12, 10, 1, 12, 1, 0, 12, 0, 9, 12, 9, 5 }, + /* 149: 0, 2, 4, 7, */ { 4, 6, 12, 0, 4, 12, 1, 0, 12, 10, 1, 12, 2, 10, 12, 3, 2, 12, 11, 3, 12, 6, 11, 12 }, + /* 101: 0, 2, 5, 6, */ { 6, 4, 12, 2, 6, 12, 3, 2, 12, 8, 3, 12, 0, 8, 12, 1, 0, 12, 9, 1, 12, 4, 9, 12 }, + /* 197: 0, 2, 6, 7, */ { 12, 7, 5, 12, 5, 1, 12, 1, 0, 12, 0, 8, 12, 8, 3, 12, 3, 2, 12, 2, 11, 12, 11, 7 }, + /* 89: 0, 3, 4, 6, */ { 12, 2, 0, 12, 0, 4, 12, 4, 5, 12, 5, 10, 12, 10, 6, 12, 6, 7, 12, 7, 11, 12, 11, 2 }, + /* 169: 0, 3, 5, 7, */ { 2, 0, 12, 6, 2, 12, 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12, 9, 5, 12, 0, 9, 12 }, + /* 225: 0, 5, 6, 7, */ { 12, 9, 10, 12, 10, 11, 12, 11, 7, 12, 7, 4, 12, 4, 8, 12, 8, 3, 12, 3, 0, 12, 0, 9 }, + /* 30: 1, 2, 3, 4, */ { 10, 9, 12, 11, 10, 12, 7, 11, 12, 4, 7, 12, 8, 4, 12, 3, 8, 12, 0, 3, 12, 9, 0, 12 }, + /* 86: 1, 2, 4, 6, */ { 12, 0, 2, 12, 2, 6, 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5, 12, 5, 9, 12, 9, 0 }, + /* 166: 1, 2, 5, 7, */ { 0, 2, 12, 4, 0, 12, 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12, 11, 7, 12, 2, 11, 12 }, + /* 58: 1, 3, 4, 5, */ { 5, 7, 12, 1, 5, 12, 0, 1, 12, 8, 0, 12, 3, 8, 12, 2, 3, 12, 11, 2, 12, 7, 11, 12 }, + /* 154: 1, 3, 4, 7, */ { 12, 4, 6, 12, 6, 2, 12, 2, 3, 12, 3, 8, 12, 8, 0, 12, 0, 1, 12, 1, 9, 12, 9, 4 }, + /* 106: 1, 3, 5, 6, */ { 12, 6, 4, 12, 4, 0, 12, 0, 1, 12, 1, 10, 12, 10, 2, 12, 2, 3, 12, 3, 11, 12, 11, 6 }, + /* 202: 1, 3, 6, 7, */ { 7, 5, 12, 3, 7, 12, 2, 3, 12, 10, 2, 12, 1, 10, 12, 0, 1, 12, 9, 0, 12, 5, 9, 12 }, + /* 210: 1, 4, 6, 7, */ { 12, 10, 11, 12, 11, 8, 12, 8, 0, 12, 0, 1, 12, 1, 9, 12, 9, 4, 12, 4, 5, 12, 5, 10 }, + /* 92: 2, 3, 4, 6, */ { 1, 3, 12, 5, 1, 12, 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12, 8, 4, 12, 3, 8, 12 }, + /* 172: 2, 3, 5, 7, */ { 12, 1, 3, 12, 3, 7, 12, 7, 4, 12, 4, 9, 12, 9, 5, 12, 5, 6, 12, 6, 10, 12, 10, 1 }, + /* 180: 2, 4, 5, 7, */ { 12, 11, 8, 12, 8, 9, 12, 9, 1, 12, 1, 2, 12, 2, 10, 12, 10, 5, 12, 5, 6, 12, 6, 11 }, + /* 120: 3, 4, 5, 6, */ { 12, 8, 9, 12, 9, 10, 12, 10, 2, 12, 2, 3, 12, 3, 11, 12, 11, 6, 12, 6, 7, 12, 7, 8 } + }; + return &tiling12_2[config][0]; + }; // end of Tiling12_2 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 12.2 inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling12_2_(unsigned char config) + { + static const char tiling12_2_[24][24] = { + /* 135: 0, 1, 2, 7, */ { 12, 2, 11, 12, 11, 7, 12, 7, 6, 12, 6, 10, 12, 10, 9, 12, 9, 8, 12, 8, 3, 12, 3, 2 }, + /* 75: 0, 1, 3, 6, */ { 12, 1, 10, 12, 10, 6, 12, 6, 5, 12, 5, 9, 12, 9, 8, 12, 8, 11, 12, 11, 2, 12, 2, 1 }, + /* 83: 0, 1, 4, 6, */ { 12, 4, 5, 12, 5, 10, 12, 10, 6, 12, 6, 7, 12, 7, 3, 12, 3, 1, 12, 1, 9, 12, 9, 4 }, + /* 163: 0, 1, 5, 7, */ { 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12, 1, 5, 12, 3, 1, 12, 11, 3, 12, 6, 11, 12 }, + /* 45: 0, 2, 3, 5, */ { 12, 0, 9, 12, 9, 5, 12, 5, 4, 12, 4, 8, 12, 8, 11, 12, 11, 10, 12, 10, 1, 12, 1, 0 }, + /* 53: 0, 2, 4, 5, */ { 1, 2, 12, 9, 1, 12, 0, 9, 12, 3, 0, 12, 7, 3, 12, 5, 7, 12, 10, 5, 12, 2, 10, 12 }, + /* 149: 0, 2, 4, 7, */ { 12, 1, 2, 12, 2, 11, 12, 11, 3, 12, 3, 0, 12, 0, 4, 12, 4, 6, 12, 6, 10, 12, 10, 1 }, + /* 101: 0, 2, 5, 6, */ { 12, 3, 0, 12, 0, 9, 12, 9, 1, 12, 1, 2, 12, 2, 6, 12, 6, 4, 12, 4, 8, 12, 8, 3 }, + /* 197: 0, 2, 6, 7, */ { 3, 0, 12, 11, 3, 12, 2, 11, 12, 1, 2, 12, 5, 1, 12, 7, 5, 12, 8, 7, 12, 0, 8, 12 }, + /* 89: 0, 3, 4, 6, */ { 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12, 0, 4, 12, 2, 0, 12, 10, 2, 12, 5, 10, 12 }, + /* 169: 0, 3, 5, 7, */ { 12, 7, 4, 12, 4, 9, 12, 9, 5, 12, 5, 6, 12, 6, 2, 12, 2, 0, 12, 0, 8, 12, 8, 7 }, + /* 225: 0, 5, 6, 7, */ { 8, 7, 12, 0, 8, 12, 3, 0, 12, 11, 3, 12, 10, 11, 12, 9, 10, 12, 4, 9, 12, 7, 4, 12 }, + /* 30: 1, 2, 3, 4, */ { 12, 7, 8, 12, 8, 0, 12, 0, 3, 12, 3, 11, 12, 11, 10, 12, 10, 9, 12, 9, 4, 12, 4, 7 }, + /* 86: 1, 2, 4, 6, */ { 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12, 2, 6, 12, 0, 2, 12, 8, 0, 12, 7, 8, 12 }, + /* 166: 1, 2, 5, 7, */ { 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4, 12, 4, 0, 12, 0, 2, 12, 2, 10, 12, 10, 5 }, + /* 58: 1, 3, 4, 5, */ { 12, 0, 3, 12, 3, 11, 12, 11, 2, 12, 2, 1, 12, 1, 5, 12, 5, 7, 12, 7, 8, 12, 8, 0 }, + /* 154: 1, 3, 4, 7, */ { 0, 3, 12, 9, 0, 12, 1, 9, 12, 2, 1, 12, 6, 2, 12, 4, 6, 12, 8, 4, 12, 3, 8, 12 }, + /* 106: 1, 3, 5, 6, */ { 2, 1, 12, 11, 2, 12, 3, 11, 12, 0, 3, 12, 4, 0, 12, 6, 4, 12, 10, 6, 12, 1, 10, 12 }, + /* 202: 1, 3, 6, 7, */ { 12, 2, 1, 12, 1, 9, 12, 9, 0, 12, 0, 3, 12, 3, 7, 12, 7, 5, 12, 5, 10, 12, 10, 2 }, + /* 210: 1, 4, 6, 7, */ { 9, 0, 12, 5, 9, 12, 4, 5, 12, 8, 4, 12, 11, 8, 12, 10, 11, 12, 1, 10, 12, 0, 1, 12 }, + /* 92: 2, 3, 4, 6, */ { 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5, 12, 5, 1, 12, 1, 3, 12, 3, 11, 12, 11, 6 }, + /* 172: 2, 3, 5, 7, */ { 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12, 3, 7, 12, 1, 3, 12, 9, 1, 12, 4, 9, 12 }, + /* 180: 2, 4, 5, 7, */ { 10, 1, 12, 6, 10, 12, 5, 6, 12, 9, 5, 12, 8, 9, 12, 11, 8, 12, 2, 11, 12, 1, 2, 12 }, + /* 120: 3, 4, 5, 6, */ { 11, 2, 12, 7, 11, 12, 6, 7, 12, 10, 6, 12, 9, 10, 12, 8, 9, 12, 3, 8, 12, 2, 3, 12 } + }; + return &tiling12_2_[config][0]; + }; // end of Tiling12_2_ + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief test table for case 13 + * All faces are to be tested + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13: face test */ + static char Test13(unsigned char config, int u) + { + static const char test13[2][7] = { + /* 165: 0, 2, 5, 7, */ { 1,2,3,4,5,6,7 }, + /* 90: 1, 3, 4, 6, */ { 2,3,4,1,5,6,7 }, + }; + return test13[config][u]; + }; // end of Test13 + + //_____________________________________________________________________________ + /** + * \brief subconfiguration table for case 13 + * Hard-coded tests for the subconfiguration determination + * + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13: sub configs */ + static char Subconfig13(unsigned char config) + { + static const char subconfig13[64] = { + /* 0: 0,0,0,0,0,0 */ 0, + /* 1: 1,0,0,0,0,0 */ 1, + /* 2: 0,1,0,0,0,0 */ 2, + /* 3: 1,1,0,0,0,0 */ 7, + /* 4: 0,0,1,0,0,0 */ 3, + /* 5: 1,0,1,0,0,0 */ -1, + /* 6: 0,1,1,0,0,0 */ 11, + /* 7: 1,1,1,0,0,0 */ -1, + /* 8: 0,0,0,1,0,0 */ 4, + /* 9: 1,0,0,1,0,0 */ 8, + /* 10: 0,1,0,1,0,0 */ -1, + /* 11: 1,1,0,1,0,0 */ -1, + /* 12: 0,0,1,1,0,0 */ 14, + /* 13: 1,0,1,1,0,0 */ -1, + /* 14: 0,1,1,1,0,0 */ -1, + /* 15: 1,1,1,1,0,0 */ -1, + /* 16: 0,0,0,0,1,0 */ 5, + /* 17: 1,0,0,0,1,0 */ 9, + /* 18: 0,1,0,0,1,0 */ 12, + /* 19: 1,1,0,0,1,0 */ 23, + /* 20: 0,0,1,0,1,0 */ 15, + /* 21: 1,0,1,0,1,0 */ -1, + /* 22: 0,1,1,0,1,0 */ 21, + /* 23: 1,1,1,0,1,0 */ 38, + /* 24: 0,0,0,1,1,0 */ 17, + /* 25: 1,0,0,1,1,0 */ 20, + /* 26: 0,1,0,1,1,0 */ -1, + /* 27: 1,1,0,1,1,0 */ 36, + /* 28: 0,0,1,1,1,0 */ 26, + /* 29: 1,0,1,1,1,0 */ 33, + /* 30: 0,1,1,1,1,0 */ 30, + /* 31: 1,1,1,1,1,0 */ 44, + /* 32: 0,0,0,0,0,1 */ 6, + /* 33: 1,0,0,0,0,1 */ 10, + /* 34: 0,1,0,0,0,1 */ 13, + /* 35: 1,1,0,0,0,1 */ 19, + /* 36: 0,0,1,0,0,1 */ 16, + /* 37: 1,0,1,0,0,1 */ -1, + /* 38: 0,1,1,0,0,1 */ 25, + /* 39: 1,1,1,0,0,1 */ 37, + /* 40: 0,0,0,1,0,1 */ 18, + /* 41: 1,0,0,1,0,1 */ 24, + /* 42: 0,1,0,1,0,1 */ -1, + /* 43: 1,1,0,1,0,1 */ 35, + /* 44: 0,0,1,1,0,1 */ 22, + /* 45: 1,0,1,1,0,1 */ 32, + /* 46: 0,1,1,1,0,1 */ 29, + /* 47: 1,1,1,1,0,1 */ 43, + /* 48: 0,0,0,0,1,1 */ -1, + /* 49: 1,0,0,0,1,1 */ -1, + /* 50: 0,1,0,0,1,1 */ -1, + /* 51: 1,1,0,0,1,1 */ 34, + /* 52: 0,0,1,0,1,1 */ -1, + /* 53: 1,0,1,0,1,1 */ -1, + /* 54: 0,1,1,0,1,1 */ 28, + /* 55: 1,1,1,0,1,1 */ 42, + /* 56: 0,0,0,1,1,1 */ -1, + /* 57: 1,0,0,1,1,1 */ 31, + /* 58: 0,1,0,1,1,1 */ -1, + /* 59: 1,1,0,1,1,1 */ 41, + /* 60: 0,0,1,1,1,1 */ 27, + /* 61: 1,0,1,1,1,1 */ 40, + /* 62: 0,1,1,1,1,1 */ 39, + /* 63: 1,1,1,1,1,1 */ 45, + }; + return subconfig13[config]; + }; // end of Subconfig13 + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.1 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.1 */ + static const char* Tiling13_1(unsigned char config) + { + static const char tiling13_1[2][12] = { + /* 165: 0, 2, 5, 7, */ { 11, 7, 6, 1, 2, 10, 8, 3, 0, 9, 5, 4 }, + /* 90: 1, 3, 4, 6, */ { 8, 4, 7, 2, 3, 11, 9, 0, 1, 10, 6, 5 } + }; + return &tiling13_1[config][0]; + }; // end of Tiling13_1 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.1 inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.1 */ + static const char* Tiling13_1_(unsigned char config) + { + static const char tiling13_1_[2][12] = { + /* 165: 0, 2, 5, 7, */ { 7, 4, 8, 11, 3, 2, 1, 0, 9, 5, 6, 10 }, + /* 90: 1, 3, 4, 6, */ { 6, 7, 11, 10, 2, 1, 0, 3, 8, 4, 5, 9 } + }; + return &tiling13_1_[config][0]; + }; // end of Tiling13_1_ + + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.2 */ + static const char* Tiling13_2(unsigned char config, unsigned char u) + { + static const char tiling13_2[2][6][18] = { + /* 165: 0, 2, 5, 7, */ { + /* 1 */ { 1, 2, 10, 11, 7, 6, 3, 4, 8, 4, 3, 5, 0, 5, 3, 5, 0, 9 }, + /* 2 */ { 8, 3, 0, 11, 7, 6, 9, 1, 4, 2, 4, 1, 4, 2, 5, 10, 5, 2 }, + /* 3 */ { 9, 5, 4, 8, 3, 0, 1, 6, 10, 6, 1, 7, 2, 7, 1, 7, 2, 11 }, + /* 4 */ { 9, 5, 4, 1, 2, 10, 11, 3, 6, 0, 6, 3, 6, 0, 7, 8, 7, 0 }, + /* 5 */ { 9, 5, 4, 11, 7, 6, 0, 10, 1, 10, 0, 8, 10, 8, 2, 3, 2, 8 }, + /* 6 */ { 1, 2, 10, 3, 0, 8, 4, 9, 7, 11, 7, 9, 5, 11, 9, 11, 5, 6 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1 */ { 2, 3, 11, 8, 4, 7, 0, 5, 9, 5, 0, 6, 1, 6, 0, 6, 1, 10 }, + /* 2 */ { 9, 0, 1, 8, 4, 7, 10, 2, 5, 3, 5, 2, 5, 3, 6, 11, 6, 3 }, + /* 3 */ { 6, 5, 10, 9, 0, 1, 2, 7, 11, 7, 2, 4, 3, 4, 2, 4, 3, 8 }, + /* 4 */ { 6, 5, 10, 2, 3, 11, 8, 0, 7, 1, 7, 0, 7, 1, 4, 9, 4, 1 }, + /* 5 */ { 6, 5, 10, 8, 4, 7, 1, 11, 2, 11, 1, 9, 11, 9, 3, 0, 3, 9 }, + /* 6 */ { 2, 3, 11, 0, 1, 9, 5, 10, 4, 8, 4, 10, 6, 8, 10, 8, 6, 7 } + } }; + return &tiling13_2[config][u][0]; + }; // end of Tiling13_2 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.2 inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.2 */ + static const char* Tiling13_2_(unsigned char config, unsigned char u) + { + static const char tiling13_2_[2][6][18] = { + /* 165: 0, 2, 5, 7, */ { + /* 1 */ { 10, 5, 6, 11, 3, 2, 7, 0, 8, 0, 7, 1, 4, 1, 7, 1, 4, 9 }, + /* 2 */ { 11, 3, 2, 7, 4, 8, 9, 5, 0, 6, 0, 5, 0, 6, 1, 10, 1, 6 }, + /* 3 */ { 1, 0, 9, 7, 4, 8, 5, 2, 10, 2, 5, 3, 6, 3, 5, 3, 6, 11 }, + /* 4 */ { 10, 5, 6, 1, 0, 9, 11, 7, 2, 4, 2, 7, 2, 4, 3, 8, 3, 4 }, + /* 5 */ { 10, 5, 6, 7, 4, 8, 2, 11, 1, 9, 1, 11, 3, 9, 11, 9, 3, 0 }, + /* 6 */ { 11, 3, 2, 9, 1, 0, 4, 10, 5, 10, 4, 8, 10, 8, 6, 7, 6, 8 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1 */ { 6, 7, 11, 8, 0, 3, 4, 1, 9, 1, 4, 2, 5, 2, 4, 2, 5, 10 }, + /* 2 */ { 8, 0, 3, 4, 5, 9, 10, 6, 1, 7, 1, 6, 1, 7, 2, 11, 2, 7 }, + /* 3 */ { 2, 1, 10, 4, 5, 9, 6, 3, 11, 3, 6, 0, 7, 0, 6, 0, 7, 8 }, + /* 4 */ { 6, 7, 11, 2, 1, 10, 8, 4, 3, 5, 3, 4, 3, 5, 0, 9, 0, 5 }, + /* 5 */ { 6, 7, 11, 4, 5, 9, 3, 8, 2, 10, 2, 8, 0, 10, 8, 10, 0, 1 }, + /* 6 */ { 8, 0, 3, 10, 2, 1, 5, 11, 6, 11, 5, 9, 11, 9, 7, 4, 7, 9 } + } }; + return &tiling13_2_[config][u][0]; + }; // end of Tiling13_2_ + + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.3 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.3 */ + static const char* Tiling13_3(unsigned char config, unsigned int u) + { + static const char tiling13_3[2][12][30] = { + /* 165: 0, 2, 5, 7, */ { + /* 1,2 */ { 11, 7, 6, 12, 2, 10, 12, 10, 5, 12, 5, 4, 12, 4, 8, 12, 8, 3, 12, 3, 0, 12, 0, 9, 12, 9, 1, 12, 1, 2 }, + /* 1,4 */ { 1, 2, 10, 9, 5, 12, 0, 9, 12, 3, 0, 12, 11, 3, 12, 6, 11, 12, 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12 }, + /* 1,5 */ { 11, 7, 6, 12, 5, 4, 12, 4, 8, 12, 8, 3, 12, 3, 2, 12, 2, 10, 12, 10, 1, 12, 1, 0, 12, 0, 9, 12, 9, 5 }, + /* 1,6 */ { 1, 2, 10, 12, 3, 0, 12, 0, 9, 12, 9, 5, 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4, 12, 4, 8, 12, 8, 3 }, + /* 2,3 */ { 8, 3, 0, 11, 7, 12, 2, 11, 12, 1, 2, 12, 9, 1, 12, 4, 9, 12, 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12 }, + /* 2,5 */ { 11, 7, 6, 5, 4, 12, 10, 5, 12, 2, 10, 12, 3, 2, 12, 8, 3, 12, 0, 8, 12, 1, 0, 12, 9, 1, 12, 4, 9, 12 }, + /* 2,6 */ { 8, 3, 0, 1, 2, 12, 9, 1, 12, 4, 9, 12, 7, 4, 12, 11, 7, 12, 6, 11, 12, 5, 6, 12, 10, 5, 12, 2, 10, 12 }, + /* 3,4 */ { 9, 5, 4, 12, 0, 8, 12, 8, 7, 12, 7, 6, 12, 6, 10, 12, 10, 1, 12, 1, 2, 12, 2, 11, 12, 11, 3, 12, 3, 0 }, + /* 3,5 */ { 9, 5, 4, 12, 7, 6, 12, 6, 10, 12, 10, 1, 12, 1, 0, 12, 0, 8, 12, 8, 3, 12, 3, 2, 12, 2, 11, 12, 11, 7 }, + /* 3,6 */ { 8, 3, 0, 12, 1, 2, 12, 2, 11, 12, 11, 7, 12, 7, 4, 12, 4, 9, 12, 9, 5, 12, 5, 6, 12, 6, 10, 12, 10, 1 }, + /* 4,5 */ { 9, 5, 4, 7, 6, 12, 8, 7, 12, 0, 8, 12, 1, 0, 12, 10, 1, 12, 2, 10, 12, 3, 2, 12, 11, 3, 12, 6, 11, 12 }, + /* 4,6 */ { 1, 2, 10, 3, 0, 12, 11, 3, 12, 6, 11, 12, 5, 6, 12, 9, 5, 12, 4, 9, 12, 7, 4, 12, 8, 7, 12, 0, 8, 12 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1,2 */ { 8, 4, 7, 12, 3, 11, 12, 11, 6, 12, 6, 5, 12, 5, 9, 12, 9, 0, 12, 0, 1, 12, 1, 10, 12, 10, 2, 12, 2, 3 }, + /* 1,4 */ { 2, 3, 11, 10, 6, 12, 1, 10, 12, 0, 1, 12, 8, 0, 12, 7, 8, 12, 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12 }, + /* 1,5 */ { 8, 4, 7, 12, 6, 5, 12, 5, 9, 12, 9, 0, 12, 0, 3, 12, 3, 11, 12, 11, 2, 12, 2, 1, 12, 1, 10, 12, 10, 6 }, + /* 1,6 */ { 2, 3, 11, 12, 0, 1, 12, 1, 10, 12, 10, 6, 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5, 12, 5, 9, 12, 9, 0 }, + /* 2,3 */ { 0, 1, 9, 8, 4, 12, 3, 8, 12, 2, 3, 12, 10, 2, 12, 5, 10, 12, 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12 }, + /* 2,5 */ { 8, 4, 7, 6, 5, 12, 11, 6, 12, 3, 11, 12, 0, 3, 12, 9, 0, 12, 1, 9, 12, 2, 1, 12, 10, 2, 12, 5, 10, 12 }, + /* 2,6 */ { 9, 0, 1, 2, 3, 12, 10, 2, 12, 5, 10, 12, 4, 5, 12, 8, 4, 12, 7, 8, 12, 6, 7, 12, 11, 6, 12, 3, 11, 12 }, + /* 3,4 */ { 6, 5, 10, 12, 1, 9, 12, 9, 4, 12, 4, 7, 12, 7, 11, 12, 11, 2, 12, 2, 3, 12, 3, 8, 12, 8, 0, 12, 0, 1 }, + /* 3,5 */ { 6, 5, 10, 12, 4, 7, 12, 7, 11, 12, 11, 2, 12, 2, 1, 12, 1, 9, 12, 9, 0, 12, 0, 3, 12, 3, 8, 12, 8, 4 }, + /* 3,6 */ { 9, 0, 1, 12, 2, 3, 12, 3, 8, 12, 8, 4, 12, 4, 5, 12, 5, 10, 12, 10, 6, 12, 6, 7, 12, 7, 11, 12, 11, 2 }, + /* 4,5 */ { 6, 5, 10, 4, 7, 12, 9, 4, 12, 1, 9, 12, 2, 1, 12, 11, 2, 12, 3, 11, 12, 0, 3, 12, 8, 0, 12, 7, 8, 12 }, + /* 4,6 */ { 2, 3, 11, 0, 1, 12, 8, 0, 12, 7, 8, 12, 6, 7, 12, 10, 6, 12, 5, 10, 12, 4, 5, 12, 9, 4, 12, 1, 9, 12 } + } }; + return &tiling13_3[config][u][0]; + }; // end of Tiling13_3 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.3, inverted + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.3 */ + static const char* Tiling13_3_(unsigned char config, unsigned char u) + { + static const char tiling13_3_[2][12][30] = { + /* 165: 0, 2, 5, 7, */ { + /* 1,2 */ { 3, 2, 11, 8, 7, 12, 0, 8, 12, 1, 0, 12, 10, 1, 12, 6, 10, 12, 5, 6, 12, 9, 5, 12, 4, 9, 12, 7, 4, 12 }, + /* 1,4 */ { 5, 6, 10, 12, 2, 11, 12, 11, 7, 12, 7, 4, 12, 4, 9, 12, 9, 1, 12, 1, 0, 12, 0, 8, 12, 8, 3, 12, 3, 2 }, + /* 1,5 */ { 10, 5, 6, 12, 7, 4, 12, 4, 9, 12, 9, 1, 12, 1, 2, 12, 2, 11, 12, 11, 3, 12, 3, 0, 12, 0, 8, 12, 8, 7 }, + /* 1,6 */ { 11, 3, 2, 12, 1, 0, 12, 0, 8, 12, 8, 7, 12, 7, 6, 12, 6, 10, 12, 10, 5, 12, 5, 4, 12, 4, 9, 12, 9, 1 }, + /* 2,3 */ { 7, 4, 8, 11, 3, 12, 6, 11, 12, 5, 6, 12, 9, 5, 12, 0, 9, 12, 1, 0, 12, 10, 1, 12, 2, 10, 12, 3, 2, 12 }, + /* 2,5 */ { 7, 4, 8, 5, 6, 12, 9, 5, 12, 0, 9, 12, 3, 0, 12, 11, 3, 12, 2, 11, 12, 1, 2, 12, 10, 1, 12, 6, 10, 12 }, + /* 2,6 */ { 11, 3, 2, 1, 0, 12, 10, 1, 12, 6, 10, 12, 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12, 9, 5, 12, 0, 9, 12 }, + /* 3,4 */ { 1, 0, 9, 12, 4, 8, 12, 8, 3, 12, 3, 2, 12, 2, 10, 12, 10, 5, 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4 }, + /* 3,5 */ { 7, 4, 8, 12, 5, 6, 12, 6, 11, 12, 11, 3, 12, 3, 0, 12, 0, 9, 12, 9, 1, 12, 1, 2, 12, 2, 10, 12, 10, 5 }, + /* 3,6 */ { 1, 0, 9, 12, 3, 2, 12, 2, 10, 12, 10, 5, 12, 5, 4, 12, 4, 8, 12, 8, 7, 12, 7, 6, 12, 6, 11, 12, 11, 3 }, + /* 4,5 */ { 10, 5, 6, 7, 4, 12, 11, 7, 12, 2, 11, 12, 1, 2, 12, 9, 1, 12, 0, 9, 12, 3, 0, 12, 8, 3, 12, 4, 8, 12 }, + /* 4,6 */ { 9, 1, 0, 3, 2, 12, 8, 3, 12, 4, 8, 12, 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12, 11, 7, 12, 2, 11, 12 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1,2 */ { 0, 3, 8, 9, 4, 12, 1, 9, 12, 2, 1, 12, 11, 2, 12, 7, 11, 12, 6, 7, 12, 10, 6, 12, 5, 10, 12, 4, 5, 12 }, + /* 1,4 */ { 11, 6, 7, 12, 3, 8, 12, 8, 4, 12, 4, 5, 12, 5, 10, 12, 10, 2, 12, 2, 1, 12, 1, 9, 12, 9, 0, 12, 0, 3 }, + /* 1,5 */ { 6, 7, 11, 12, 4, 5, 12, 5, 10, 12, 10, 2, 12, 2, 3, 12, 3, 8, 12, 8, 0, 12, 0, 1, 12, 1, 9, 12, 9, 4 }, + /* 1,6 */ { 8, 0, 3, 12, 2, 1, 12, 1, 9, 12, 9, 4, 12, 4, 7, 12, 7, 11, 12, 11, 6, 12, 6, 5, 12, 5, 10, 12, 10, 2 }, + /* 2,3 */ { 4, 5, 9, 8, 0, 12, 7, 8, 12, 6, 7, 12, 10, 6, 12, 1, 10, 12, 2, 1, 12, 11, 2, 12, 3, 11, 12, 0, 3, 12 }, + /* 2,5 */ { 4, 5, 9, 6, 7, 12, 10, 6, 12, 1, 10, 12, 0, 1, 12, 8, 0, 12, 3, 8, 12, 2, 3, 12, 11, 2, 12, 7, 11, 12 }, + /* 2,6 */ { 8, 0, 3, 2, 1, 12, 11, 2, 12, 7, 11, 12, 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12, 10, 6, 12, 1, 10, 12 }, + /* 3,4 */ { 2, 1, 10, 12, 5, 9, 12, 9, 0, 12, 0, 3, 12, 3, 11, 12, 11, 6, 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5 }, + /* 3,5 */ { 4, 5, 9, 12, 6, 7, 12, 7, 8, 12, 8, 0, 12, 0, 1, 12, 1, 10, 12, 10, 2, 12, 2, 3, 12, 3, 11, 12, 11, 6 }, + /* 3,6 */ { 2, 1, 10, 12, 0, 3, 12, 3, 11, 12, 11, 6, 12, 6, 5, 12, 5, 9, 12, 9, 4, 12, 4, 7, 12, 7, 8, 12, 8, 0 }, + /* 4,5 */ { 6, 7, 11, 4, 5, 12, 8, 4, 12, 3, 8, 12, 2, 3, 12, 10, 2, 12, 1, 10, 12, 0, 1, 12, 9, 0, 12, 5, 9, 12 }, + /* 4,6 */ { 10, 2, 1, 0, 3, 12, 9, 0, 12, 5, 9, 12, 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12, 8, 4, 12, 3, 8, 12 } + } }; + return &tiling13_3_[config][u][0]; + }; // end of Tiling13_3_ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.4 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.4 */ + static const char* Tiling13_4(unsigned char config, unsigned int u) + { + static const char tiling13_4[2][4][36] = { + /* 165: 0, 2, 5, 7, */ { + /* 1,2,6 */ { 12, 2, 10, 12, 10, 5, 12, 5, 6, 12, 6, 11, 12, 11, 7, 12, 7, 4, 12, 4, 8, 12, 8, 3, 12, 3, 0, 12, 0, 9, 12, 9, 1, 12, 1, 2 }, + /* 1,4,5 */ { 11, 3, 12, 6, 11, 12, 7, 6, 12, 8, 7, 12, 4, 8, 12, 5, 4, 12, 9, 5, 12, 0, 9, 12, 1, 0, 12, 10, 1, 12, 2, 10, 12, 3, 2, 12 }, + /* 2,3,5 */ { 9, 1, 12, 4, 9, 12, 5, 4, 12, 10, 5, 12, 6, 10, 12, 7, 6, 12, 11, 7, 12, 2, 11, 12, 3, 2, 12, 8, 3, 12, 0, 8, 12, 1, 0, 12 }, + /* 3,4,6 */ { 12, 0, 8, 12, 8, 7, 12, 7, 4, 12, 4, 9, 12, 9, 5, 12, 5, 6, 12, 6, 10, 12, 10, 1, 12, 1, 2, 12, 2, 11, 12, 11, 3, 12, 3, 0 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1,2,6 */ { 12, 3, 11, 12, 11, 6, 12, 6, 7, 12, 7, 8, 12, 8, 4, 12, 4, 5, 12, 5, 9, 12, 9, 0, 12, 0, 1, 12, 1, 10, 12, 10, 2, 12, 2, 3 }, + /* 1,4,5 */ { 8, 0, 12, 7, 8, 12, 4, 7, 12, 9, 4, 12, 5, 9, 12, 6, 5, 12, 10, 6, 12, 1, 10, 12, 2, 1, 12, 11, 2, 12, 3, 11, 12, 0, 3, 12 }, + /* 2,3,5 */ { 10, 2, 12, 5, 10, 12, 6, 5, 12, 11, 6, 12, 7, 11, 12, 4, 7, 12, 8, 4, 12, 3, 8, 12, 0, 3, 12, 9, 0, 12, 1, 9, 12, 2, 1, 12 }, + /* 3,4,6 */ { 12, 1, 9, 12, 9, 4, 12, 4, 5, 12, 5, 10, 12, 10, 6, 12, 6, 7, 12, 7, 11, 12, 11, 2, 12, 2, 3, 12, 3, 8, 12, 8, 0, 12, 0, 1 } + } }; + return &tiling13_4[config][u][0]; + }; // end of Tiling13_4 + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.5.1 + * The support edge for the interior test is marked as the 1st column. + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.5.1 */ + static const char* Tiling13_5_1(unsigned char config, int u) + { + static const char tiling13_5_1[2][4][18] = { + /* 165: 0, 2, 5, 7, */ { + /* 1,2,5 */ { 7, 6, 11, 1, 0, 9, 10, 3, 2, 3, 10, 5, 3, 5, 8, 4, 8, 5 }, + /* 1,4,6 */ { 1, 2, 10, 7, 4, 8, 3, 0, 11, 6, 11, 0, 9, 6, 0, 6, 9, 5 }, + /* 2,3,6 */ { 3, 0, 8, 5, 6, 10, 1, 2, 9, 4, 9, 2, 11, 4, 2, 4, 11, 7 }, + /* 3,4,5 */ { 5, 4, 9, 3, 2, 11, 8, 1, 0, 1, 8, 7, 1, 7, 10, 6, 10, 7 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1,2,5 */ { 4, 7, 8, 2, 1, 10, 11, 0, 3, 0, 11, 6, 0, 6, 9, 5, 9, 6 }, + /* 1,4,6 */ { 2, 3, 11, 4, 5, 9, 0, 1, 8, 7, 8, 1, 10, 7, 1, 7, 10, 6 }, + /* 2,3,6 */ { 0, 1, 9, 6, 7, 11, 2, 3, 10, 5, 10, 3, 8, 5, 3, 5, 8, 4 }, + /* 3,4,5 */ { 6, 5, 10, 0, 3, 8, 9, 2, 1, 2, 9, 4, 2, 4, 11, 7, 11, 4 } + } }; + return &tiling13_5_1[config][u][0]; + }; // end of Tiling13_5_1 + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 13.5.2 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + /* 13.5.2 */ + static const char* Tiling13_5_2(unsigned char config, int u) + { + static const char tiling13_5_2[2][4][30] = { + /* 165: 0, 2, 5, 7, */ { + /* 1,2,5 */ { 1, 0, 9, 7, 4, 8, 7, 8, 3, 7, 3, 11, 2, 11, 3, 11, 2, 10, 11, 10, 6, 5, 6, 10, 6, 5, 7, 4, 7, 5 }, + /* 1,4,6 */ { 7, 4, 8, 11, 3, 2, 6, 11, 2, 10, 6, 2, 6, 10, 5, 9, 5, 10, 1, 9, 10, 9, 1, 0, 2, 0, 1, 0, 2, 3 }, + /* 2,3,6 */ { 5, 6, 10, 9, 1, 0, 4, 9, 0, 8, 4, 0, 4, 8, 7, 11, 7, 8, 3, 11, 8, 11, 3, 2, 0, 2, 3, 2, 0, 1 }, + /* 3,4,5 */ { 3, 2, 11, 5, 6, 10, 5, 10, 1, 5, 1, 9, 0, 9, 1, 9, 0, 8, 9, 8, 4, 4, 8, 7, 4, 7, 5, 6, 5, 7 } + }, + /* 90: 1, 3, 4, 6, */ { + /* 1,2,5 */ { 2, 1, 10, 4, 5, 9, 4, 9, 0, 4, 0, 8, 3, 8, 0, 8, 3, 11, 8, 11, 7, 6, 7, 11, 7, 6, 4, 5, 4, 6 }, + /* 1,4,6 */ { 4, 5, 9, 8, 0, 3, 7, 8, 3, 11, 7, 3, 7, 11, 6, 10, 6, 11, 2, 10, 11, 10, 2, 1, 3, 1, 2, 1, 3, 0 }, + /* 2,3,6 */ { 6, 7, 11, 10, 2, 1, 5, 10, 1, 9, 5, 1, 5, 9, 4, 8, 4, 9, 0, 8, 9, 8, 0, 3, 1, 3, 0, 3, 1, 2 }, + /* 3,4,5 */ { 0, 3, 8, 6, 7, 11, 6, 11, 2, 6, 2, 10, 1, 10, 2, 10, 1, 9, 10, 9, 5, 5, 9, 4, 5, 4, 6, 7, 6, 4 } + } }; + return &tiling13_5_2[config][u][0]; + }; // end of Tiling13_5_2 + //_____________________________________________________________________________ + + + //_____________________________________________________________________________ + /** + * \brief tiling table for case 14 + * For each of the case above, the specific triangulation of the edge + * intersection points is given. + * When a case is ambiguous, there is an auxiliary table that contains + * the face number to test and the tiling table contains the specific + * triangulations depending on the results + * A minus sign means to invert the result of the test. + */ + //----------------------------------------------------------------------------- + static const char* Tiling14(unsigned char config) + { + static const char tiling14[12][12] = { + /* 71: 0, 1, 2, 6, */ { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8 }, + /* 43: 0, 1, 3, 5, */ { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5 }, + /* 147: 0, 1, 4, 7, */ { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6 }, + /* 29: 0, 2, 3, 4, */ { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4 }, + /* 201: 0, 3, 6, 7, */ { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5 }, + /* 113: 0, 4, 5, 6, */ { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10 }, + /* 142: 1, 2, 3, 7, */ { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7 }, + /* 54: 1, 2, 4, 5, */ { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2 }, + /* 226: 1, 5, 6, 7, */ { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11 }, + /* 108: 2, 3, 5, 6, */ { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3 }, + /* 212: 2, 4, 6, 7, */ { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8 }, + /* 184: 3, 4, 5, 7, */ { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2 } + }; + return &tiling14[config][0]; + }; // end of Tiling14 + //_____________________________________________________________________________ + + + + //_____________________________________________________________________________ + /** + * \brief original Marching Cubes implementation + * For each of the possible vertex states listed in this table there is a + * specific triangulation of the edge intersection points. The table lists + * all of them in the form of 0-5 edge triples with the list terminated by + * the invalid value -1. For example: casesClassic[3] list the 2 triangles + * formed when cube[0] and cube[1] are inside of the surface, but the rest of + * the cube is not. + */ + //----------------------------------------------------------------------------- + static char CasesClassic(unsigned char u, unsigned char w) + { + static const char casesClassic[256][16] = { + /* 0: */ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 1: 0, */ { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 2: 1, */ { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 3: 0, 1, */ { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 4: 2, */ { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 5: 0, 2, */ { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 6: 1, 2, */ { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 7: 0, 1, 2, */ { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + /* 8: 3, */ { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 9: 0, 3, */ { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 10: 1, 3, */ { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 11: 0, 1, 3, */ { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 12: 2, 3, */ { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 13: 0, 2, 3, */ { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + /* 14: 1, 2, 3, */ { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + /* 15: 0, 1, 2, 3, */ { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 16: 4, */ { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 17: 0, 4, */ { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 18: 1, 4, */ { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 19: 0, 1, 4, */ { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + /* 20: 2, 4, */ { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 21: 0, 2, 4, */ { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, + /* 22: 1, 2, 4, */ { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 23: 0, 1, 2, 4, */ { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + /* 24: 3, 4, */ { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 25: 0, 3, 4, */ { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + /* 26: 1, 3, 4, */ { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 27: 0, 1, 3, 4, */ { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, + /* 28: 2, 3, 4, */ { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, + /* 29: 0, 2, 3, 4, */ { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, + /* 30: 1, 2, 3, 4, */ { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + /* 31: 0, 1, 2, 3, 4, */ { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + /* 32: 5, */ { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 33: 0, 5, */ { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 34: 1, 5, */ { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 35: 0, 1, 5, */ { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 36: 2, 5, */ { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 37: 0, 2, 5, */ { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 38: 1, 2, 5, */ { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + /* 39: 0, 1, 2, 5, */ { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, + /* 40: 3, 5, */ { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 41: 0, 3, 5, */ { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 42: 1, 3, 5, */ { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 43: 0, 1, 3, 5, */ { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, + /* 44: 2, 3, 5, */ { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, + /* 45: 0, 2, 3, 5, */ { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, + /* 46: 1, 2, 3, 5, */ { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + /* 47: 0, 1, 2, 3, 5, */ { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 48: 4, 5, */ { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 49: 0, 4, 5, */ { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + /* 50: 1, 4, 5, */ { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 51: 0, 1, 4, 5, */ { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 52: 2, 4, 5, */ { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, + /* 53: 0, 2, 4, 5, */ { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, + /* 54: 1, 2, 4, 5, */ { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, + /* 55: 0, 1, 2, 4, 5, */ { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 56: 3, 4, 5, */ { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, + /* 57: 0, 3, 4, 5, */ { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + /* 58: 1, 3, 4, 5, */ { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, + /* 59: 0, 1, 3, 4, 5, */ { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 60: 2, 3, 4, 5, */ { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, + /* 61: 0, 2, 3, 4, 5, */ { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, + /* 62: 1, 2, 3, 4, 5, */ { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, + /* 63: 0, 1, 2, 3, 4, 5, */ { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 64: 6, */ { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 65: 0, 6, */ { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 66: 1, 6, */ { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 67: 0, 1, 6, */ { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 68: 2, 6, */ { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 69: 0, 2, 6, */ { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, + /* 70: 1, 2, 6, */ { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 71: 0, 1, 2, 6, */ { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, + /* 72: 3, 6, */ { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 73: 0, 3, 6, */ { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 74: 1, 3, 6, */ { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 75: 0, 1, 3, 6, */ { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, + /* 76: 2, 3, 6, */ { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + /* 77: 0, 2, 3, 6, */ { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + /* 78: 1, 2, 3, 6, */ { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, + /* 79: 0, 1, 2, 3, 6, */ { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + /* 80: 4, 6, */ { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 81: 0, 4, 6, */ { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, + /* 82: 1, 4, 6, */ { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 83: 0, 1, 4, 6, */ { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + /* 84: 2, 4, 6, */ { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, + /* 85: 0, 2, 4, 6, */ { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, + /* 86: 1, 2, 4, 6, */ { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, + /* 87: 0, 1, 2, 4, 6, */ { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, + /* 88: 3, 4, 6, */ { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 89: 0, 3, 4, 6, */ { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + /* 90: 1, 3, 4, 6, */ { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, + /* 91: 0, 1, 3, 4, 6, */ { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, + /* 92: 2, 3, 4, 6, */ { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + /* 93: 0, 2, 3, 4, 6, */ { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, + /* 94: 1, 2, 3, 4, 6, */ { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, + /* 95: 0, 1, 2, 3, 4, 6, */ { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, + /* 96: 5, 6, */ { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 97: 0, 5, 6, */ { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, + /* 98: 1, 5, 6, */ { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + /* 99: 0, 1, 5, 6, */ { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + /* 100: 2, 5, 6, */ { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + /* 101: 0, 2, 5, 6, */ { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, + /* 102: 1, 2, 5, 6, */ { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 103: 0, 1, 2, 5, 6, */ { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 104: 3, 5, 6, */ { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, + /* 105: 0, 3, 5, 6, */ { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, + /* 106: 1, 3, 5, 6, */ { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + /* 107: 0, 1, 3, 5, 6, */ { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, + /* 108: 2, 3, 5, 6, */ { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, + /* 109: 0, 2, 3, 5, 6, */ { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, + /* 110: 1, 2, 3, 5, 6, */ { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + /* 111: 0, 1, 2, 3, 5, 6, */ { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 112: 4, 5, 6, */ { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + /* 113: 0, 4, 5, 6, */ { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, + /* 114: 1, 4, 5, 6, */ { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, + /* 115: 0, 1, 4, 5, 6, */ { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + /* 116: 2, 4, 5, 6, */ { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + /* 117: 0, 2, 4, 5, 6, */ { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, + /* 118: 1, 2, 4, 5, 6, */ { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + /* 119: 0, 1, 2, 4, 5, 6, */ { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 120: 3, 4, 5, 6, */ { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + /* 121: 0, 3, 4, 5, 6, */ { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, + /* 122: 1, 3, 4, 5, 6, */ { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, + /* 123: 0, 1, 3, 4, 5, 6, */ { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, + /* 124: 2, 3, 4, 5, 6, */ { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, + /* 125: 0, 2, 3, 4, 5, 6, */ { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 126: 1, 2, 3, 4, 5, 6, */ { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, + /* 127: 0, 1, 2, 3, 4, 5, 6, */ { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 128: 7, */ { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 129: 0, 7, */ { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 130: 1, 7, */ { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 131: 0, 1, 7, */ { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 132: 2, 7, */ { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 133: 0, 2, 7, */ { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 134: 1, 2, 7, */ { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 135: 0, 1, 2, 7, */ { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, + /* 136: 3, 7, */ { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 137: 0, 3, 7, */ { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + /* 138: 1, 3, 7, */ { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, + /* 139: 0, 1, 3, 7, */ { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, + /* 140: 2, 3, 7, */ { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 141: 0, 2, 3, 7, */ { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, + /* 142: 1, 2, 3, 7, */ { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, + /* 143: 0, 1, 2, 3, 7, */ { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + /* 144: 4, 7, */ { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 145: 0, 4, 7, */ { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 146: 1, 4, 7, */ { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, + /* 147: 0, 1, 4, 7, */ { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, + /* 148: 2, 4, 7, */ { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, + /* 149: 0, 2, 4, 7, */ { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, + /* 150: 1, 2, 4, 7, */ { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, + /* 151: 0, 1, 2, 4, 7, */ { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, + /* 152: 3, 4, 7, */ { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + /* 153: 0, 3, 4, 7, */ { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 154: 1, 3, 4, 7, */ { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, + /* 155: 0, 1, 3, 4, 7, */ { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 156: 2, 3, 4, 7, */ { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, + /* 157: 0, 2, 3, 4, 7, */ { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + /* 158: 1, 2, 3, 4, 7, */ { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, + /* 159: 0, 1, 2, 3, 4, 7, */ { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 160: 5, 7, */ { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 161: 0, 5, 7, */ { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + /* 162: 1, 5, 7, */ { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 163: 0, 1, 5, 7, */ { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, + /* 164: 2, 5, 7, */ { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 165: 0, 2, 5, 7, */ { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, + /* 166: 1, 2, 5, 7, */ { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, + /* 167: 0, 1, 2, 5, 7, */ { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, + /* 168: 3, 5, 7, */ { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, + /* 169: 0, 3, 5, 7, */ { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, + /* 170: 1, 3, 5, 7, */ { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, + /* 171: 0, 1, 3, 5, 7, */ { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, + /* 172: 2, 3, 5, 7, */ { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, + /* 173: 0, 2, 3, 5, 7, */ { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, + /* 174: 1, 2, 3, 5, 7, */ { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, + /* 175: 0, 1, 2, 3, 5, 7, */ { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, + /* 176: 4, 5, 7, */ { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + /* 177: 0, 4, 5, 7, */ { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, + /* 178: 1, 4, 5, 7, */ { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, + /* 179: 0, 1, 4, 5, 7, */ { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + /* 180: 2, 4, 5, 7, */ { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, + /* 181: 0, 2, 4, 5, 7, */ { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, + /* 182: 1, 2, 4, 5, 7, */ { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, + /* 183: 0, 1, 2, 4, 5, 7, */ { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, + /* 184: 3, 4, 5, 7, */ { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, + /* 185: 0, 3, 4, 5, 7, */ { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + /* 186: 1, 3, 4, 5, 7, */ { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, + /* 187: 0, 1, 3, 4, 5, 7, */ { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 188: 2, 3, 4, 5, 7, */ { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, + /* 189: 0, 2, 3, 4, 5, 7, */ { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, + /* 190: 1, 2, 3, 4, 5, 7, */ { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 191: 0, 1, 2, 3, 4, 5, 7, */ { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 192: 6, 7, */ { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 193: 0, 6, 7, */ { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, + /* 194: 1, 6, 7, */ { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, + /* 195: 0, 1, 6, 7, */ { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, + /* 196: 2, 6, 7, */ { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + /* 197: 0, 2, 6, 7, */ { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, + /* 198: 1, 2, 6, 7, */ { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, + /* 199: 0, 1, 2, 6, 7, */ { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, + /* 200: 3, 6, 7, */ { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 201: 0, 3, 6, 7, */ { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, + /* 202: 1, 3, 6, 7, */ { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, + /* 203: 0, 1, 3, 6, 7, */ { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, + /* 204: 2, 3, 6, 7, */ { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 205: 0, 2, 3, 6, 7, */ { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + /* 206: 1, 2, 3, 6, 7, */ { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + /* 207: 0, 1, 2, 3, 6, 7, */ { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 208: 4, 6, 7, */ { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + /* 209: 0, 4, 6, 7, */ { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, + /* 210: 1, 4, 6, 7, */ { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, + /* 211: 0, 1, 4, 6, 7, */ { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, + /* 212: 2, 4, 6, 7, */ { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, + /* 213: 0, 2, 4, 6, 7, */ { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, + /* 214: 1, 2, 4, 6, 7, */ { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, + /* 215: 0, 1, 2, 4, 6, 7, */ { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 216: 3, 4, 6, 7, */ { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, + /* 217: 0, 3, 4, 6, 7, */ { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + /* 218: 1, 3, 4, 6, 7, */ { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, + /* 219: 0, 1, 3, 4, 6, 7, */ { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, + /* 220: 2, 3, 4, 6, 7, */ { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + /* 221: 0, 2, 3, 4, 6, 7, */ { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 222: 1, 2, 3, 4, 6, 7, */ { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, + /* 223: 0, 1, 2, 3, 4, 6, 7, */ { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 224: 5, 6, 7, */ { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 225: 0, 5, 6, 7, */ { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, + /* 226: 1, 5, 6, 7, */ { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, + /* 227: 0, 1, 5, 6, 7, */ { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, + /* 228: 2, 5, 6, 7, */ { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, + /* 229: 0, 2, 5, 6, 7, */ { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, + /* 230: 1, 2, 5, 6, 7, */ { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + /* 231: 0, 1, 2, 5, 6, 7, */ { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, + /* 232: 3, 5, 6, 7, */ { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, + /* 233: 0, 3, 5, 6, 7, */ { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, + /* 234: 1, 3, 5, 6, 7, */ { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, + /* 235: 0, 1, 3, 5, 6, 7, */ { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 236: 2, 3, 5, 6, 7, */ { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + /* 237: 0, 2, 3, 5, 6, 7, */ { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, + /* 238: 1, 2, 3, 5, 6, 7, */ { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 239: 0, 1, 2, 3, 5, 6, 7, */ { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 240: 4, 5, 6, 7, */ { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 241: 0, 4, 5, 6, 7, */ { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + /* 242: 1, 4, 5, 6, 7, */ { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + /* 243: 0, 1, 4, 5, 6, 7, */ { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 244: 2, 4, 5, 6, 7, */ { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + /* 245: 0, 2, 4, 5, 6, 7, */ { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, + /* 246: 1, 2, 4, 5, 6, 7, */ { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 247: 0, 1, 2, 4, 5, 6, 7, */ { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 248: 3, 4, 5, 6, 7, */ { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + /* 249: 0, 3, 4, 5, 6, 7, */ { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 250: 1, 3, 4, 5, 6, 7, */ { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, + /* 251: 0, 1, 3, 4, 5, 6, 7, */ { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 252: 2, 3, 4, 5, 6, 7, */ { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 253: 0, 2, 3, 4, 5, 6, 7, */ { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 254: 1, 2, 3, 4, 5, 6, 7, */ { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + /* 255: 0, 1, 2, 3, 4, 5, 6, 7, */ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } + }; + return casesClassic[u][w]; + }; // end of CasesClassic + //_____________________________________________________________________________ + + }; // end of class EMCLookUpTable + }; // end of namespace tri +}; //end of namespace vcg + + + +#endif // __VCG_MC_LOOK_UP_TABLE diff --git a/vcg/complex/algorithms/create/mc_trivial_walker.h b/vcg/complex/algorithms/create/mc_trivial_walker.h new file mode 100644 index 00000000..d721f6d5 --- /dev/null +++ b/vcg/complex/algorithms/create/mc_trivial_walker.h @@ -0,0 +1,346 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2004-2009 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ +#ifndef __VCG_TRIVIAL_WALKER +#define __VCG_TRIVIAL_WALKER +#include + +namespace vcg { + +// Very simple volume class. +// just an example of the interface that the trivial walker expects + +template +class SimpleVolume +{ +public: + typedef VOX_TYPE VoxelType; + std::vector Vol; + + Point3i sz; /// Dimensioni griglia come numero di celle per lato + + const Point3i &ISize() {return sz;}; /// Dimensioni griglia come numero di celle per lato + + void Init(Point3i _sz) + { + sz=_sz; + Vol.resize(sz[0]*sz[1]*sz[2]); + } + + float Val(const int &x,const int &y,const int &z) const { + return cV(x,y,z).V(); + //else return numeric_limits::quiet_NaN( ); + } + + float &Val(const int &x,const int &y,const int &z) { + return V(x,y,z).V(); + //else return numeric_limits::quiet_NaN( ); + } + + VOX_TYPE &V(const int &x,const int &y,const int &z) { + return Vol[x+y*sz[0]+z*sz[0]*sz[1]]; + } + + const VOX_TYPE &cV(const int &x,const int &y,const int &z) const { + return Vol[x+y*sz[0]+z*sz[0]*sz[1]]; + } + + +typedef enum { XAxis=0,YAxis=1,ZAxis=2} VolumeAxis; + +template < class VertexPointerType, VolumeAxis AxisVal > + void GetIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr) +{ + float f1 = Val(p1.X(), p1.Y(), p1.Z())-thr; + float f2 = Val(p2.X(), p2.Y(), p2.Z())-thr; + float u = (float) f1/(f1-f2); + if(AxisVal==XAxis) v->P().X() = (float) p1.X()*(1-u) + u*p2.X(); + else v->P().X() = (float) p1.X(); + if(AxisVal==YAxis) v->P().Y() = (float) p1.Y()*(1-u) + u*p2.Y(); + else v->P().Y() = (float) p1.Y(); + if(AxisVal==ZAxis) v->P().Z() = (float) p1.Z()*(1-u) + u*p2.Z(); + else v->P().Z() = (float) p1.Z(); +} + +template < class VertexPointerType > + void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr) +{ GetIntercept(p1,p2,v,thr); } + +template < class VertexPointerType > + void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr) +{ GetIntercept(p1,p2,v,thr); } + +template < class VertexPointerType > + void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr) +{ GetIntercept(p1,p2,v,thr); } +}; +template +class RawVolumeImporter +{ +public: + enum DataType +{ + // Funzioni superiori + UNDEF=0, + BYTE=1, + SHORT=2, + FLOAT=3 +}; + +static bool Open(const char *filename, VolumeType &V, Point3i sz, DataType d) +{ +return true; +} +}; + +class SimpleVoxel +{ +private: + float _v; +public: + float &V() {return _v;}; + float V() const {return _v;}; +}; + + +namespace tri { + + +// La classe Walker implementa la politica di visita del volume; conoscendo l'ordine di visita del volume +// Ë conveniente che il Walker stesso si faccia carico del caching dei dati utilizzati durante l'esecuzione +// degli algoritmi MarchingCubes ed ExtendedMarchingCubes, in particolare il calcolo del volume ai vertici +// delle celle e delle intersezioni della superficie con le celle. In questo esempio il volume da processare +// viene suddiviso in fette; in questo modo se il volume ha dimensione h*l*w (rispettivamente altezza, +// larghezza e profondit‡), lo spazio richiesto per il caching dei vertici gi‡ allocati passa da O(h*l*w) +// a O(h*l). + +template +class TrivialWalker +{ +private: + typedef int VertexIndex; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::VertexPointer VertexPointer; + public: + + // bbox is the portion of the volume to be computed + // resolution determine the sampling step: + // should be a divisor of bbox size (e.g. if bbox size is 256^3 resolution could be 128,64, etc) + + + void Init(VolumeType &volume) + { + _bbox = Box3i(Point3i(0,0,0),volume.ISize()); + _slice_dimension = _bbox.DimX()*_bbox.DimZ(); + + _x_cs = new VertexIndex[ _slice_dimension ]; + _y_cs = new VertexIndex[ _slice_dimension ]; + _z_cs = new VertexIndex[ _slice_dimension ]; + _x_ns = new VertexIndex[ _slice_dimension ]; + _z_ns = new VertexIndex[ _slice_dimension ]; + + }; + + ~TrivialWalker() + {_thr=0;} + + template + void BuildMesh(MeshType &mesh, VolumeType &volume, EXTRACTOR_TYPE &extractor, const float threshold, vcg::CallBackPos * cb=0) + { + Init(volume); + _volume = &volume; + _mesh = &mesh; + _mesh->Clear(); + _thr=threshold; + vcg::Point3i p1, p2; + + Begin(); + extractor.Initialize(); + for (int j=_bbox.min.Y(); j<(_bbox.max.Y()-1)-1; j+=1) + { + + if(cb && ((j%10)==0) ) cb(j*_bbox.DimY()/100.0,"Marching volume"); + + for (int i=_bbox.min.X(); i<(_bbox.max.X()-1)-1; i+=1) + { + for (int k=_bbox.min.Z(); k<(_bbox.max.Z()-1)-1; k+=1) + { + p1.X()=i; p1.Y()=j; p1.Z()=k; + p2.X()=i+1; p2.Y()=j+1; p2.Z()=k+1; + extractor.ProcessCell(p1, p2); + } + } + NextSlice(); + } + extractor.Finalize(); + _volume = NULL; + _mesh = NULL; + }; + + float V(int pi, int pj, int pk) + { + return _volume->Val(pi, pj, pk)-_thr; + } + + bool Exist(const vcg::Point3i &p0, const vcg::Point3i &p1, VertexPointer &v) + { + int pos = p0.X()+p0.Z()*_bbox.max.X(); + int vidx; + + if (p0.X()!=p1.X()) // punti allineati lungo l'asse X + vidx = (p0.Y()==_current_slice) ? _x_cs[pos] : _x_ns[pos]; + else if (p0.Y()!=p1.Y()) // punti allineati lungo l'asse Y + vidx = _y_cs[pos]; + else if (p0.Z()!=p1.Z()) // punti allineati lungo l'asse Z + vidx = (p0.Y()==_current_slice)? _z_cs[pos] : _z_ns[pos]; + else + assert(false); + + v = (vidx!=-1)? &_mesh->vert[vidx] : NULL; + return v!=NULL; + } + + void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + int i = p1.X() - _bbox.min.X(); + int z = p1.Z() - _bbox.min.Z(); + VertexIndex index = i+z*_bbox.max.X(); + VertexIndex pos; + if (p1.Y()==_current_slice) + { + if ((pos=_x_cs[index])==-1) + { + _x_cs[index] = (VertexIndex) _mesh->vert.size(); + pos = _x_cs[index]; + Allocator::AddVertices( *_mesh, 1 ); + v = &_mesh->vert[pos]; + _volume->GetXIntercept(p1, p2, v, _thr); + return; + } + } + if (p1.Y()==_current_slice+1) + { + if ((pos=_x_ns[index])==-1) + { + _x_ns[index] = (VertexIndex) _mesh->vert.size(); + pos = _x_ns[index]; + Allocator::AddVertices( *_mesh, 1 ); + v = &_mesh->vert[pos]; + _volume->GetXIntercept(p1, p2, v,_thr); + return; + } + } + assert(pos >=0 && size_t(pos)< _mesh->vert.size()); + v = &_mesh->vert[pos]; + } + void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + int i = p1.X() - _bbox.min.X(); + int z = p1.Z() - _bbox.min.Z(); + VertexIndex index = i+z*_bbox.max.X(); + VertexIndex pos; + if ((pos=_y_cs[index])==-1) + { + _y_cs[index] = (VertexIndex) _mesh->vert.size(); + pos = _y_cs[index]; + Allocator::AddVertices( *_mesh, 1); + v = &_mesh->vert[ pos ]; + _volume->GetYIntercept(p1, p2, v,_thr); + } + v = &_mesh->vert[pos]; + } + void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + int i = p1.X() - _bbox.min.X(); + int z = p1.Z() - _bbox.min.Z(); + VertexIndex index = i+z*_bbox.max.X(); + VertexIndex pos; + if (p1.Y()==_current_slice) + { + if ((pos=_z_cs[index])==-1) + { + _z_cs[index] = (VertexIndex) _mesh->vert.size(); + pos = _z_cs[index]; + Allocator::AddVertices( *_mesh, 1 ); + v = &_mesh->vert[pos]; + _volume->GetZIntercept(p1, p2, v,_thr); + return; + } + } + if (p1.Y()==_current_slice+1) + { + if ((pos=_z_ns[index])==-1) + { + _z_ns[index] = (VertexIndex) _mesh->vert.size(); + pos = _z_ns[index]; + Allocator::AddVertices( *_mesh, 1 ); + v = &_mesh->vert[pos]; + _volume->GetZIntercept(p1, p2, v,_thr); + return; + } + } + v = &_mesh->vert[pos]; + } + +protected: + Box3i _bbox; + + int _slice_dimension; + int _current_slice; + + VertexIndex *_x_cs; // indici dell'intersezioni della superficie lungo gli Xedge della fetta corrente + VertexIndex *_y_cs; // indici dell'intersezioni della superficie lungo gli Yedge della fetta corrente + VertexIndex *_z_cs; // indici dell'intersezioni della superficie lungo gli Zedge della fetta corrente + VertexIndex *_x_ns; // indici dell'intersezioni della superficie lungo gli Xedge della prossima fetta + VertexIndex *_z_ns; // indici dell'intersezioni della superficie lungo gli Zedge della prossima fetta + + MeshType *_mesh; + VolumeType *_volume; + + float _thr; + void NextSlice() + { + memset(_x_cs, -1, _slice_dimension*sizeof(VertexIndex)); + memset(_y_cs, -1, _slice_dimension*sizeof(VertexIndex)); + memset(_z_cs, -1, _slice_dimension*sizeof(VertexIndex)); + + std::swap(_x_cs, _x_ns); + std::swap(_z_cs, _z_ns); + + _current_slice += 1; + } + + void Begin() + { + _current_slice = _bbox.min.Y(); + + memset(_x_cs, -1, _slice_dimension*sizeof(VertexIndex)); + memset(_y_cs, -1, _slice_dimension*sizeof(VertexIndex)); + memset(_z_cs, -1, _slice_dimension*sizeof(VertexIndex)); + memset(_x_ns, -1, _slice_dimension*sizeof(VertexIndex)); + memset(_z_ns, -1, _slice_dimension*sizeof(VertexIndex)); + + } +}; +} // end namespace +} // end namespace +#endif // __VCGTEST_WALKER diff --git a/vcg/complex/algorithms/create/platonic.h b/vcg/complex/algorithms/create/platonic.h new file mode 100644 index 00000000..3030dc5e --- /dev/null +++ b/vcg/complex/algorithms/create/platonic.h @@ -0,0 +1,829 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCGLIB_PLATONIC +#define __VCGLIB_PLATONIC + +#include +#include +#include +#include + +namespace vcg { +namespace tri { +/** \addtogroup trimesh */ +//@{ + /** + A set of functions that builds meshes + that represent surfaces of platonic solids, + and other simple shapes. + + The 1st parameter is the mesh that will + be filled with the solid. + */ +template +void Tetrahedron(TetraMeshType &in) +{ + typedef TetraMeshType MeshType; + typedef typename TetraMeshType::CoordType CoordType; + typedef typename TetraMeshType::VertexPointer VertexPointer; + typedef typename TetraMeshType::VertexIterator VertexIterator; + typedef typename TetraMeshType::FaceIterator FaceIterator; + + in.Clear(); + Allocator::AddVertices(in,4); + Allocator::AddFaces(in,4); + + VertexPointer ivp[4]; + VertexIterator vi=in.vert.begin(); + ivp[0]=&*vi;(*vi).P()=CoordType ( 1.0, 1.0, 1.0); ++vi; + ivp[1]=&*vi;(*vi).P()=CoordType (-1.0, 1.0,-1.0); ++vi; + ivp[2]=&*vi;(*vi).P()=CoordType (-1.0,-1.0, 1.0); ++vi; + ivp[3]=&*vi;(*vi).P()=CoordType ( 1.0,-1.0,-1.0); + + FaceIterator fi=in.face.begin(); + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[3]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[1]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1]; +} + + +/// builds a Dodecahedron, +/// (each pentagon is composed of 5 triangles) +template +void Dodecahedron(DodMeshType & in) +{ + typedef DodMeshType MeshType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::ScalarType ScalarType; + const int N_penta=12; + const int N_points=62; + + int penta[N_penta*3*3]= + {20,11, 18, 18, 11, 8, 8, 11, 4, + 13,23, 4, 4, 23, 8, 8, 23, 16, + 13, 4, 30, 30, 4, 28, 28, 4, 11, + 16,34, 8, 8, 34, 18, 18, 34, 36, + 11,20, 28, 28, 20, 45, 45, 20, 38, + 13,30, 23, 23, 30, 41, 41, 30, 47, + 16,23, 34, 34, 23, 50, 50, 23, 41, + 20,18, 38, 38, 18, 52, 52, 18, 36, + 30,28, 47, 47, 28, 56, 56, 28, 45, + 50,60, 34, 34, 60, 36, 36, 60, 52, + 45,38, 56, 56, 38, 60, 60, 38, 52, + 50,41, 60, 60, 41, 56, 56, 41, 47 }; + //A B E D C + const ScalarType p=(1.0 + math::Sqrt(5.0)) / 2.0; + const ScalarType p2=p*p; + const ScalarType p3=p*p*p; + ScalarType vv[N_points*3]= + { + 0, 0, 2*p2, p2, 0, p3, p, p2, p3, + 0, p, p3, -p, p2, p3, -p2, 0, p3, + -p, -p2, p3, 0, -p, p3, p, -p2, p3, + p3, p, p2, p2, p2, p2, 0, p3, p2, + -p2, p2, p2, -p3, p, p2, -p3, -p, p2, + -p2, -p2, p2, 0, -p3, p2, p2, -p2, p2, + p3, -p, p2, p3, 0, p, p2, p3, p, + -p2, p3, p, -p3, 0, p, -p2, -p3, p, + p2, -p3, p, 2*p2, 0, 0, p3, p2, 0, + p, p3, 0, 0, 2*p2, 0, -p, p3, 0, + -p3, p2, 0, -2*p2, 0, 0, -p3, -p2, 0, + -p, -p3, 0, 0, -2*p2, 0, p, -p3, 0, + p3, -p2, 0, p3, 0, -p, p2, p3, -p, + -p2, p3, -p, -p3, 0, -p, -p2, -p3, -p, + p2, -p3, -p, p3, p, -p2, p2, p2, -p2, + 0, p3, -p2, -p2, p2, -p2, -p3, p, -p2, + -p3, -p, -p2, -p2, -p2, -p2, 0, -p3, -p2, + p2, -p2, -p2, p3, -p, -p2, p2, 0, -p3, + p, p2, -p3, 0, p, -p3, -p, p2, -p3, + -p2, 0, -p3, -p, -p2, -p3, 0, -p, -p3, + p, -p2, -p3, 0, 0, -2*p2 + }; + in.Clear(); + //in.face.clear(); + Allocator::AddVertices(in,20+12); + Allocator::AddFaces(in, 5*12); // five pentagons, each made by 5 tri + + int h,i,j,m=0; + + bool used[N_points]; + for (i=0; i index(in.vn); + + for(j=0,vi=in.vert.begin();j +void Octahedron(OctMeshType &in) +{ + typedef OctMeshType MeshType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + in.Clear(); + Allocator::AddVertices(in,6); + Allocator::AddFaces(in,8); + + VertexPointer ivp[6]; + + VertexIterator vi=in.vert.begin(); + ivp[0]=&*vi;(*vi).P()=CoordType ( 1, 0, 0); ++vi; + ivp[1]=&*vi;(*vi).P()=CoordType ( 0, 1, 0); ++vi; + ivp[2]=&*vi;(*vi).P()=CoordType ( 0, 0, 1); ++vi; + ivp[3]=&*vi;(*vi).P()=CoordType (-1, 0, 0); ++vi; + ivp[4]=&*vi;(*vi).P()=CoordType ( 0,-1, 0); ++vi; + ivp[5]=&*vi;(*vi).P()=CoordType ( 0, 0,-1); + + FaceIterator fi=in.face.begin(); + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[4]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[5]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[1]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[5]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[4]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1]; +} + +template +void Icosahedron(IcoMeshType &in) +{ + typedef IcoMeshType MeshType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + ScalarType L=ScalarType((math::Sqrt(5.0)+1.0)/2.0); + CoordType vv[12]={ + CoordType ( 0, L, 1), + CoordType ( 0, L,-1), + CoordType ( 0,-L, 1), + CoordType ( 0,-L,-1), + + CoordType ( L, 1, 0), + CoordType ( L,-1, 0), + CoordType (-L, 1, 0), + CoordType (-L,-1, 0), + + CoordType ( 1, 0, L), + CoordType (-1, 0, L), + CoordType ( 1, 0,-L), + CoordType (-1, 0,-L) + }; + + int ff[20][3]={ + {1,0,4},{0,1,6},{2,3,5},{3,2,7}, + {4,5,10},{5,4,8},{6,7,9},{7,6,11}, + {8,9,2},{9,8,0},{10,11,1},{11,10,3}, + {0,8,4},{0,6,9},{1,4,10},{1,11,6}, + {2,5,8},{2,9,7},{3,10,5},{3,7,11} + }; + + + in.Clear(); + Allocator::AddVertices(in,12); + Allocator::AddFaces(in,20); + VertexPointer ivp[12]; + + VertexIterator vi; + int i; + for(i=0,vi=in.vert.begin();vi!=in.vert.end();++i,++vi){ + (*vi).P()=vv[i]; + ivp[i]=&*vi; + } + + FaceIterator fi; + for(i=0,fi=in.face.begin();fi!=in.face.end();++i,++fi){ + (*fi).V(0)=ivp[ff[i][0]]; + (*fi).V(1)=ivp[ff[i][1]]; + (*fi).V(2)=ivp[ff[i][2]]; + } +} + +template +void Hexahedron(MeshType &in) +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + in.Clear(); + Allocator::AddVertices(in,8); + Allocator::AddFaces(in,12); + + VertexPointer ivp[8]; + + VertexIterator vi=in.vert.begin(); + + ivp[7]=&*vi;(*vi).P()=CoordType (-1,-1,-1); ++vi; + ivp[6]=&*vi;(*vi).P()=CoordType ( 1,-1,-1); ++vi; + ivp[5]=&*vi;(*vi).P()=CoordType (-1, 1,-1); ++vi; + ivp[4]=&*vi;(*vi).P()=CoordType ( 1, 1,-1); ++vi; + ivp[3]=&*vi;(*vi).P()=CoordType (-1,-1, 1); ++vi; + ivp[2]=&*vi;(*vi).P()=CoordType ( 1,-1, 1); ++vi; + ivp[1]=&*vi;(*vi).P()=CoordType (-1, 1, 1); ++vi; + ivp[0]=&*vi;(*vi).P()=CoordType ( 1, 1, 1); + + FaceIterator fi=in.face.begin(); + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[4]; ++fi; + (*fi).V(0)=ivp[6]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[1]; ++fi; + (*fi).V(0)=ivp[5]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[4]; ++fi; + (*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[6]; ++fi; + (*fi).V(0)=ivp[4]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[5]; ++fi; + (*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[3]; ++fi; + (*fi).V(0)=ivp[2]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[6]; ++fi; + (*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[5]; ++fi; + (*fi).V(0)=ivp[1]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[3]; + + if (in.HasPerFaceFlags()) { + FaceIterator fi=in.face.begin(); + for (int k=0; k<12; k++) { + (*fi).SetF(1); fi++; + } + } + +} + +template +void Square(MeshType &in) +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + in.Clear(); + Allocator::AddVertices(in,4); + Allocator::AddFaces(in,2); + + VertexPointer ivp[4]; + + VertexIterator vi=in.vert.begin(); + ivp[0]=&*vi;(*vi).P()=CoordType ( 1, 0, 0); ++vi; + ivp[1]=&*vi;(*vi).P()=CoordType ( 0, 1, 0); ++vi; + ivp[2]=&*vi;(*vi).P()=CoordType (-1, 0, 0); ++vi; + ivp[3]=&*vi;(*vi).P()=CoordType ( 0,-1, 0); + + FaceIterator fi=in.face.begin(); + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[2]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[0]; + + if (in.HasPerFaceFlags()) { + FaceIterator fi=in.face.begin(); + for (int k=0; k<2; k++) { + (*fi).SetF(2); fi++; + } + } +} + +// this function build a sphere starting from a eventually not empty mesh. +// If the mesh is not empty it is 'spherified' and used as base for the subdivision process. +// otherwise an icosahedron is used. +template +void Sphere(MeshType &in, const int subdiv = 3 ) +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + if(in.vn==0 && in.fn==0) Icosahedron(in); + + VertexIterator vi; + for(vi = in.vert.begin(); vi!=in.vert.end();++vi) + vi->P().Normalize(); + + tri::UpdateFlags::FaceBorderFromNone(in); + tri::UpdateTopology::FaceFace(in); + + size_t lastsize = 0; + for(int i = 0 ; i < subdiv; ++i) + { + Refine< MeshType, MidPoint >(in, MidPoint(&in), 0); + + for(vi = in.vert.begin() + lastsize; vi != in.vert.end(); ++vi) + vi->P().Normalize(); + + lastsize = in.vert.size(); + } +} + + + /// r1 = raggio 1, r2 = raggio2, h = altezza (asse y) +template +void Cone( MeshType& in, + const typename MeshType::ScalarType r1, + const typename MeshType::ScalarType r2, + const typename MeshType::ScalarType h, + const int SubDiv = 36 ) +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + int i,b1,b2; + in.Clear(); + int VN,FN; + if(r1==0 || r2==0) { + VN=SubDiv+2; + FN=SubDiv*2; + } else { + VN=SubDiv*2+2; + FN=SubDiv*4; + } + + Allocator::AddVertices(in,VN); + Allocator::AddFaces(in,FN); + VertexPointer *ivp = new VertexPointer[VN]; + + VertexIterator vi=in.vert.begin(); + ivp[0]=&*vi;(*vi).P()=CoordType ( 0,-h/2.0,0 ); ++vi; + ivp[1]=&*vi;(*vi).P()=CoordType ( 0, h/2.0,0 ); ++vi; + + b1 = b2 = 2; + int cnt=2; + if(r1!=0) + { + for(i=0;i +void Box(MeshType &in, const typename MeshType::BoxType & bb ) +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + in.Clear(); + Allocator::AddVertices(in,8); + Allocator::AddFaces(in,12); + + VertexPointer ivp[8]; + + VertexIterator vi=in.vert.begin(); + ivp[0]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.min[1],bb.min[2]); ++vi; + ivp[1]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.min[1],bb.min[2]); ++vi; + ivp[2]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.max[1],bb.min[2]); ++vi; + ivp[3]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.max[1],bb.min[2]); ++vi; + ivp[4]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.min[1],bb.max[2]); ++vi; + ivp[5]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.min[1],bb.max[2]); ++vi; + ivp[6]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.max[1],bb.max[2]); ++vi; + ivp[7]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.max[1],bb.max[2]); + + FaceIterator fi=in.face.begin(); + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[4]; ++fi; + (*fi).V(0)=ivp[6]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[2]; ++fi; + (*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[1]; ++fi; + (*fi).V(0)=ivp[5]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[4]; ++fi; + (*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[6]; ++fi; + (*fi).V(0)=ivp[4]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[5]; ++fi; + (*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[3]; ++fi; + (*fi).V(0)=ivp[2]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[6]; ++fi; + (*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[5]; ++fi; + (*fi).V(0)=ivp[1]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[3]; + + if (in.HasPerFaceFlags()) { + FaceIterator fi=in.face.begin(); + for (int k=0; k<12; k++) { + (*fi).SetF(1); fi++; + } + } + +} + + +// this function build a mesh starting from a vector of generic coords (objects having a triple of float at their beginning) +// and a vector of faces (objects having a triple of ints at theri beginning). + + +template +void Build( MeshType & in, const V & v, const F & f) +{ + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + Allocator::AddVertices(in,v.size()); + Allocator::AddFaces(in,f.size()); + + typename V::const_iterator vi; + + typename MeshType::VertexType tv; + + for(int i=0;i index(in.vn); + VertexIterator j; + int k; + for(k=0,j=in.vert.begin();j!=in.vert.end();++j,++k) + index[k] = &*j; + + typename F::const_iterator fi; + + typename MeshType::FaceType ft; + + for(int i=0;i=0 ); + assert( ff[1]>=0 ); + assert( ff[2]>=0 ); + assert( ff[0] array +// Once generated the vertex positions it uses the FaceGrid function to generate the faces; + +template +void Grid(MeshType & in, int w, int h, float wl, float hl, float *data) +{ + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + in.Clear(); + Allocator::AddVertices(in,w*h); + + + float wld=wl/float(w); + float hld=hl/float(h); + + for(int i=0;i +void FaceGrid(MeshType & in, int w, int h) +{ + assert(in.vn == (int)in.vert.size()); // require a compact vertex vector + assert(in.vn >= w*h); // the number of vertices should match the number of expected grid vertices + + Allocator::AddFaces(in,(w-1)*(h-1)*2); + +// i+0,j+0 -- i+0,j+1 +// | \ | +// | \ | +// | \ | +// | \ | +// i+1,j+0 -- i+1,j+1 +// + for(int i=0;i +void FaceGrid(MeshType & in, const std::vector &grid, int w, int h) +{ + assert(in.vn == (int)in.vert.size()); // require a compact vertex vector + assert(in.vn <= w*h); // the number of vertices should match the number of expected grid vertices + +// V0 V1 +// i+0,j+0 -- i+0,j+1 +// | \ | +// | \ | +// | \ | +// | \ | +// i+1,j+0 -- i+1,j+1 +// V2 V3 + + + for(int i=0;i=0 && V1i>=0 && V2i>=0 && V3i>=0 ) && in.HasPerFaceFlags(); + + if(V0i>=0 && V2i>=0 && V3i>=0 ) + { + typename MeshType::FaceIterator f= Allocator::AddFaces(in,1); + f->V(0)=&(in.vert[V3i]); + f->V(1)=&(in.vert[V2i]); + f->V(2)=&(in.vert[V0i]); + if (quad) f->SetF(2); + ndone++; + } + if(V0i>=0 && V1i>=0 && V3i>=0 ) + { + typename MeshType::FaceIterator f= Allocator::AddFaces(in,1); + f->V(0)=&(in.vert[V0i]); + f->V(1)=&(in.vert[V1i]); + f->V(2)=&(in.vert[V3i]); + if (quad) f->SetF(2); + ndone++; + } + + if (ndone==0) { // try diag the other way + if(V2i>=0 && V0i>=0 && V1i>=0 ) + { + typename MeshType::FaceIterator f= Allocator::AddFaces(in,1); + f->V(0)=&(in.vert[V2i]); + f->V(1)=&(in.vert[V0i]); + f->V(2)=&(in.vert[V1i]); + ndone++; + } + if(V1i>=0 && V3i>=0 && V2i>=0 ) + { + typename MeshType::FaceIterator f= Allocator::AddFaces(in,1); + f->V(0)=&(in.vert[V1i]); + f->V(1)=&(in.vert[V3i]); + f->V(2)=&(in.vert[V2i]); + ndone++; + } + } + + + } +} + +template +void Cylinder(int slices, int stacks, MeshType & m){ + + typename MeshType::VertexIterator vi = vcg::tri::Allocator::AddVertices(m,slices*(stacks+1)); + for ( int i = 0; i < stacks+1; ++i) + for ( int j = 0; j < slices; ++j) + { + float x,y,h; + x = cos( 2.0 * M_PI / slices * j); + y = sin( 2.0 * M_PI / slices * j); + h = 2 * i / (float)(stacks) - 1; + + (*vi).P() = typename MeshType::CoordType(x,h,y); + ++vi; + } + + typename MeshType::FaceIterator fi ; + for ( int j = 0; j < stacks; ++j) + for ( int i = 0; i < slices; ++i) + { + int a,b,c,d; + a = (j+0)*slices + i; + b = (j+1)*slices + i; + c = (j+1)*slices + (i+1)%slices; + d = (j+0)*slices + (i+1)%slices; + if(((i+j)%2) == 0){ + fi = vcg::tri::Allocator::AddFaces(m,1); + (*fi).V(0) = &m.vert[ a ]; + (*fi).V(1) = &m.vert[ b ]; + (*fi).V(2) = &m.vert[ c ]; + + fi = vcg::tri::Allocator::AddFaces(m,1); + (*fi).V(0) = &m.vert[ c ]; + (*fi).V(1) = &m.vert[ d ]; + (*fi).V(2) = &m.vert[ a ]; + } + else{ + fi = vcg::tri::Allocator::AddFaces(m,1); + (*fi).V(0) = &m.vert[ b ]; + (*fi).V(1) = &m.vert[ c ]; + (*fi).V(2) = &m.vert[ d ]; + + fi = vcg::tri::Allocator::AddFaces(m,1); + (*fi).V(0) = &m.vert[ d ]; + (*fi).V(1) = &m.vert[ a ]; + (*fi).V(2) = &m.vert[ b ]; + + } + } + + if (m.HasPerFaceFlags()) { + for (typename MeshType::FaceIterator fi=m.face.begin(); fi!=m.face.end(); fi++) { + (*fi).SetF(2); + } + } + + +} + +template +void GenerateCameraMesh(MeshType &in){ + typedef typename MeshType::CoordType MV; + MV vv[52]={ + MV(-0.000122145 , -0.2 ,0.35), + MV(0.000122145 , -0.2 ,-0.35),MV(-0.000122145 , 0.2 ,0.35),MV(0.000122145 , 0.2 ,-0.35),MV(0.999878 , -0.2 ,0.350349),MV(1.00012 , -0.2 ,-0.349651),MV(0.999878 , 0.2 ,0.350349),MV(1.00012 , 0.2 ,-0.349651),MV(1.28255 , 0.1 ,0.754205),MV(1.16539 , 0.1 ,1.03705),MV(0.88255 , 0.1 ,1.15421), + MV(0.599707 , 0.1 ,1.03705),MV(0.48255 , 0.1 ,0.754205),MV(0.599707 , 0.1 ,0.471362),MV(0.88255 , 0.1 ,0.354205),MV(1.16539 , 0.1 ,0.471362),MV(1.28255 , -0.1 ,0.754205),MV(1.16539 , -0.1 ,1.03705),MV(0.88255 , -0.1 ,1.15421),MV(0.599707 , -0.1 ,1.03705),MV(0.48255 , -0.1 ,0.754205), + MV(0.599707 , -0.1 ,0.471362),MV(1.16539 , -0.1 ,0.471362),MV(0.88255 , -0.1 ,0.354205),MV(3.49164e-005 , 0 ,-0.1),MV(1.74582e-005 , -0.0866025 ,-0.05),MV(-1.74582e-005 , -0.0866025 ,0.05),MV(-3.49164e-005 , 8.74228e-009 ,0.1),MV(-1.74582e-005 , 0.0866025 ,0.05),MV(1.74582e-005 , 0.0866025 ,-0.05),MV(-0.399913 , 1.99408e-022 ,-0.25014), + MV(-0.399956 , -0.216506 ,-0.12514),MV(-0.400044 , -0.216506 ,0.12486),MV(-0.400087 , 2.18557e-008 ,0.24986),MV(-0.400044 , 0.216506 ,0.12486),MV(-0.399956 , 0.216506 ,-0.12514),MV(0.479764 , 0.1 ,0.754205),MV(0.362606 , 0.1 ,1.03705),MV(0.0797637 , 0.1 ,1.15421),MV(-0.203079 , 0.1 ,1.03705),MV(-0.320236 , 0.1 ,0.754205), + MV(-0.203079 , 0.1 ,0.471362),MV(0.0797637 , 0.1 ,0.354205),MV(0.362606 , 0.1 ,0.471362),MV(0.479764 , -0.1 ,0.754205),MV(0.362606 , -0.1 ,1.03705),MV(0.0797637 , -0.1 ,1.15421),MV(-0.203079 , -0.1 ,1.03705),MV(-0.320236 , -0.1 ,0.754205),MV(0.0797637 , -0.1 ,0.354205),MV(0.362606 , -0.1 ,0.471362), + MV(-0.203079 , -0.1 ,0.471362), }; + int ff[88][3]={ + {0,2,3}, + {3,1,0},{4,5,7},{7,6,4},{0,1,5},{5,4,0},{1,3,7},{7,5,1},{3,2,6},{6,7,3},{2,0,4}, + {4,6,2},{10,9,8},{10,12,11},{10,13,12},{10,14,13},{10,15,14},{10,8,15},{8,17,16},{8,9,17},{9,18,17}, + {9,10,18},{10,19,18},{10,11,19},{11,20,19},{11,12,20},{12,21,20},{12,13,21},{13,23,21},{13,14,23},{14,22,23}, + {14,15,22},{15,16,22},{15,8,16},{23,16,17},{23,17,18},{23,18,19},{23,19,20},{23,20,21},{23,22,16},{25,27,26}, + {25,28,27},{25,29,28},{25,24,29},{24,31,30},{24,25,31},{25,32,31},{25,26,32},{26,33,32},{26,27,33},{27,34,33}, + {27,28,34},{28,35,34},{28,29,35},{29,30,35},{29,24,30},{35,30,31},{35,31,32},{35,32,33},{35,33,34},{42,37,36}, + {42,38,37},{42,39,38},{42,40,39},{42,41,40},{42,36,43},{36,45,44},{36,37,45},{37,46,45},{37,38,46},{38,47,46}, + {38,39,47},{39,48,47},{39,40,48},{40,51,48},{40,41,51},{41,49,51},{41,42,49},{42,50,49},{42,43,50},{43,44,50}, + {43,36,44},{51,44,45},{51,45,46},{51,46,47},{51,47,48},{51,49,50},{51,50,44}, + }; + + in.Clear(); + Allocator::AddVertices(in,52); + Allocator::AddFaces(in,88); + + in.vn=52;in.fn=88; + int i,j; + for(i=0;i index(in.vn); + + typename MeshType::VertexIterator vi; + for(j=0,vi=in.vert.begin();j +#include +#include +#include +#include +#include +#include +#include + +namespace vcg { +namespace tri { + + +/** \addtogroup trimesh */ +/*@{*/ +/*@{*/ +/** Class Resampler. + This is class reasmpling a mesh using marching cubes methods + @param OLD_MESH_TYPE (Template Parameter) Specifies the type of mesh to be resampled + @param NEW_MESH_TYPE (Template Parameter) Specifies the type of output mesh. + */ + +template > + class Resampler : public BasicGrid +{ + typedef OLD_MESH_TYPE Old_Mesh; + typedef NEW_MESH_TYPE New_Mesh; + + //template + class Walker : BasicGrid + { + private: + typedef int VertexIndex; + typedef OLD_MESH_TYPE Old_Mesh; + typedef NEW_MESH_TYPE New_Mesh; + typedef typename New_Mesh::CoordType NewCoordType; + typedef typename New_Mesh::VertexType* VertexPointer; + typedef typename Old_Mesh::FaceContainer FaceCont; + typedef typename vcg::GridStaticPtr GridType; + + protected: + + int SliceSize; + int CurrentSlice; + typedef tri::FaceTmark MarkerFace; + MarkerFace markerFunctor; + + + VertexIndex *_x_cs; // indici dell'intersezioni della superficie lungo gli Xedge della fetta corrente + VertexIndex *_y_cs; // indici dell'intersezioni della superficie lungo gli Yedge della fetta corrente + VertexIndex *_z_cs; // indici dell'intersezioni della superficie lungo gli Zedge della fetta corrente + VertexIndex *_x_ns; // indici dell'intersezioni della superficie lungo gli Xedge della prossima fetta + VertexIndex *_z_ns; // indici dell'intersezioni della superficie lungo gli Zedge della prossima fetta + + //float *_v_cs;///values of distance fields for each direction in current slice + //float *_v_ns;///values of distance fields for each direction in next slice + + typedef typename std::pair field_value; + field_value* _v_cs; + field_value* _v_ns; + + New_Mesh *_newM; + Old_Mesh *_oldM; + GridType _g; + + public: + float max_dim; // the limit value of the search (that takes into account of the offset) + float offset; // an offset value that is always added to the returned value. Useful for extrarting isosurface at a different threshold + bool DiscretizeFlag; // if the extracted surface should be discretized or not. + bool MultiSampleFlag; + bool AbsDistFlag; // if true the Distance Field computed is no more a signed one. + Walker(const Box3f &_bbox, Point3i _siz ) + { + this->bbox= _bbox; + this->siz=_siz; + ComputeDimAndVoxel(); + + SliceSize = (this->siz.X()+1)*(this->siz.Z()+1); + CurrentSlice = 0; + offset=0; + DiscretizeFlag=false; + MultiSampleFlag=false; + AbsDistFlag=false; + + _x_cs = new VertexIndex[ SliceSize ]; + _y_cs = new VertexIndex[ SliceSize ]; + _z_cs = new VertexIndex[ SliceSize ]; + _x_ns = new VertexIndex[ SliceSize ]; + _z_ns = new VertexIndex[ SliceSize ]; + + _v_cs= new field_value[(this->siz.X()+1)*(this->siz.Z()+1)]; + _v_ns= new field_value[(this->siz.X()+1)*(this->siz.Z()+1)]; + + }; + + ~Walker() + {} + + + float V(const Point3i &p) + { + return V(p.V(0),p.V(1),p.V(2)); + } + + + std::pair VV(int x,int y,int z) + { + assert ((y==CurrentSlice)||(y==(CurrentSlice+1))); + + //test if it is outside the bb of the mesh + //vcg::Point3f test=vcg::Point3f((float)x,(float)y,(float)z); + /*if (!_oldM->bbox.IsIn(test)) + return (1.f);*/ + int index=GetSliceIndex(x,z); + + if (y==CurrentSlice) return _v_cs[index]; + else return _v_ns[index]; + } + + float V(int x,int y,int z) + { + if(DiscretizeFlag) return VV(x,y,z).second+offset<0?-1:1; + return VV(x,y,z).second+offset; + } + ///return true if the distance form the mesh is less than maxdim and return distance + field_value DistanceFromMesh(Point3f &pp,Old_Mesh */*mesh*/) + { + float dist; + typename Old_Mesh::FaceType *f=NULL; + const float max_dist = max_dim; + vcg::Point3f testPt; + this->IPfToPf(pp,testPt); + + vcg::Point3f closestNormV,closestNormF; + vcg::Point3f closestPt; + vcg::Point3f pip(-1,-1,-1); + + // Note that PointDistanceBaseFunctor does not require the edge and plane precomptued. + // while the PointDistanceFunctor requires them. + + DISTFUNCTOR PDistFunct; + f = _g.GetClosest(PDistFunct,markerFunctor,testPt,max_dist,dist,closestPt); + if (f==NULL) return field_value(false,0); + if(AbsDistFlag) return field_value(true,dist); + assert(!f->IsD()); + bool retIP; + + // To compute the interpolated normal we use the more robust function that require to know what is the most orhogonal direction of the face. + if((*f).Flags() & Old_Mesh::FaceType::NORMX) retIP=InterpolationParameters(*f,0,closestPt, pip); + else if((*f).Flags() & Old_Mesh::FaceType::NORMY) retIP=InterpolationParameters(*f,1,closestPt, pip); + else if((*f).Flags() & Old_Mesh::FaceType::NORMZ) retIP=InterpolationParameters(*f,2,closestPt, pip); + else assert(0); + assert(retIP); // this should happen only if the starting mesh has degenerate faces. + + const float InterpolationEpsilon = 0.00001f; + int zeroCnt=0; + if(pip[0]0) // we Not are in the middle of the face so the face normal is NOT reliable. + { + closestNormV = (f->V(0)->cN())*pip[0] + (f->V(1)->cN())*pip[1] + (f->V(2)->cN())*pip[2] ; + signBest = dir.dot(closestNormV) ; + } + else + { + closestNormF = f->cN() ; + signBest = dir.dot(closestNormF) ; + } + + if(signBest<0) dist=-dist; + + return field_value(true,dist); + } + + field_value MultiDistanceFromMesh(Point3f &pp, Old_Mesh */*mesh*/) + { + float distSum=0; + int positiveCnt=0; // positive results counter + const int MultiSample=7; + const Point3f delta[7]={Point3f(0,0,0), + Point3f( 0.2, -0.01, -0.02), + Point3f(-0.2, 0.01, 0.02), + Point3f( 0.01, 0.2, 0.01), + Point3f( 0.03, -0.2, -0.03), + Point3f(-0.02, -0.03, 0.2 ), + Point3f(-0.01, 0.01, -0.2 )}; + + for(int qq=0;qq0) positiveCnt ++; + } + if(positiveCnt<=MultiSample/2) distSum = -distSum; + return field_value(true, distSum/MultiSample); + } + + /// compute the values if an entire slice (per y) distances>dig of a cell are signed with double of + /// the distance of the bb + void ComputeSliceValues(int slice,field_value *slice_values) + { + for (int i=0; i<=this->siz.X(); i++) + { + for (int k=0; k<=this->siz.Z(); k++) + { + int index=GetSliceIndex(i,k); + Point3f pp(i,slice,k); + if(this->MultiSampleFlag) slice_values[index] = MultiDistanceFromMesh(pp,_oldM); + else slice_values[index] = DistanceFromMesh(pp,_oldM); + } + } + //ComputeConsensus(slice,slice_values); + } + + /* + For some reasons it can happens that the sign of the computed distance could not correct. + this function tries to correct these issues by flipping the isolated voxels with discordant sign + */ + void ComputeConsensus(int slice, field_value *slice_values) + { + float max_dist = min(min(this->voxel[0],this->voxel[1]),this->voxel[2]); + int flippedCnt=0; + int flippedTot=0; + int flippedTimes=0; + do + { + flippedCnt=0; + for (int i=0; i<=this->siz.X(); i++) + { + for (int k=0; k<=this->siz.Z(); k++) + { + int goodCnt=0; + int badCnt=0; + int index=GetSliceIndex(i,k); + int index_l,index_r,index_u,index_d; + if(slice_values[index].first) + { + float curVal= slice_values[index].second; + if(i > 0 ) index_l=GetSliceIndex(i-1,k); else index_l = index; + if(i < this->siz.X() ) index_r=GetSliceIndex(i+1,k); else index_r = index; + if(k > 0 ) index_d=GetSliceIndex(i,k-1); else index_d = index; + if(k < this->siz.Z() ) index_u=GetSliceIndex(i,k+1); else index_u = index; + + if(slice_values[index_l].first) { goodCnt++; if(fabs(slice_values[index_l].second - curVal) > max_dist) badCnt++; } + if(slice_values[index_r].first) { goodCnt++; if(fabs(slice_values[index_r].second - curVal) > max_dist) badCnt++; } + if(slice_values[index_u].first) { goodCnt++; if(fabs(slice_values[index_u].second - curVal) > max_dist) badCnt++; } + if(slice_values[index_d].first) { goodCnt++; if(fabs(slice_values[index_d].second - curVal) > max_dist) badCnt++; } + + if(badCnt >= goodCnt) { + slice_values[index].second *=-1.0f; + //slice_values[index].first = false; + flippedCnt++; + } + } + } + } + flippedTot+=flippedCnt; + flippedTimes++; + } while(flippedCnt>0); + + +#ifndef NO_QT + if(flippedTot>0) + qDebug("Flipped %i values in %i times",flippedTot,flippedTimes); +#endif + } + template + void ProcessSlice(EXTRACTOR_TYPE &extractor) + { + for (int i=0; isiz.X(); i++) + { + for (int k=0; ksiz.Z(); k++) + { + bool goodCell=true; + Point3i p1(i,CurrentSlice,k); + Point3i p2=p1+Point3i(1,1,1); + for(int ii=0;ii<2;++ii) + for(int jj=0;jj<2;++jj) + for(int kk=0;kk<2;++kk) + goodCell &= VV(p1[0]+ii,p1[1]+jj,p1[2]+kk).first; + + if(goodCell) extractor.ProcessCell(p1, p2); + } + } + } + + + template + void BuildMesh(Old_Mesh &old_mesh,New_Mesh &new_mesh,EXTRACTOR_TYPE &extractor,vcg::CallBackPos *cb) + { + _newM=&new_mesh; + _oldM=&old_mesh; + + // the following two steps are required to be sure that the point-face distance without precomputed data works well. + tri::UpdateNormals::PerFaceNormalized(old_mesh); + tri::UpdateNormals::PerVertexAngleWeighted(old_mesh); + tri::UpdateFlags::FaceProjection(old_mesh); + int _size=(int)old_mesh.fn*100; + + _g.Set(_oldM->face.begin(),_oldM->face.end(),_size); + markerFunctor.SetMesh(&old_mesh); + + _newM->Clear(); + + Begin(); + extractor.Initialize(); + for (int j=0; j<=this->siz.Y(); j++) + { + cb((100*j)/this->siz.Y(),"Marching "); + ProcessSlice(extractor);//find cells where there is the isosurface and examine it + NextSlice(); + } + extractor.Finalize(); + typename New_Mesh::VertexIterator vi; + for(vi=new_mesh.vert.begin();vi!=new_mesh.vert.end();++vi) + if(!(*vi).IsD()) + { + IPfToPf((*vi).cP(),(*vi).P()); + } + } + + //return the index of a vertex in slide as it was stored + int GetSliceIndex(int x,int z) + { + VertexIndex index = x+z*(this->siz.X()+1); + return (index); + } + + //swap slices , the initial value of distance fields ids set as double of bbox of space + void NextSlice() + { + + memset(_x_cs, -1, SliceSize*sizeof(VertexIndex)); + memset(_y_cs, -1, SliceSize*sizeof(VertexIndex)); + memset(_z_cs, -1, SliceSize*sizeof(VertexIndex)); + + + std::swap(_x_cs, _x_ns); + std::swap(_z_cs, _z_ns); + + std::swap(_v_cs, _v_ns); + + CurrentSlice ++; + + ComputeSliceValues(CurrentSlice + 1,_v_ns); + } + + //initialize data strucures , the initial value of distance fields ids set as double of bbox of space + void Begin() + { + + CurrentSlice = 0; + + memset(_x_cs, -1, SliceSize*sizeof(VertexIndex)); + memset(_y_cs, -1, SliceSize*sizeof(VertexIndex)); + memset(_z_cs, -1, SliceSize*sizeof(VertexIndex)); + memset(_x_ns, -1, SliceSize*sizeof(VertexIndex)); + memset(_z_ns, -1, SliceSize*sizeof(VertexIndex)); + + ComputeSliceValues(CurrentSlice,_v_cs); + ComputeSliceValues(CurrentSlice+1,_v_ns); + } + + + + + bool Exist(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + int i = p1.X();// - _bbox.min.X())/_cell_size.X(); + int z = p1.Z();// - _bbox.min.Z())/_cell_size.Z(); + VertexIndex index = i+z*this->siz.X(); + + //VertexIndex index =GetSliceIndex(// + int v_ind = 0; + if (p1.X()!=p2.X()) //intersezione della superficie con un Xedge + { + if (p1.Y()==CurrentSlice) + { + if (_x_cs[index]!=-1) + { + v_ind = _x_cs[index]; + v = &_newM->vert[v_ind]; + assert(!v->IsD()); + return true; + } + + } + else + { + if (_x_ns[index]!=-1) + { + v_ind = _x_ns[index]; + v = &_newM->vert[v_ind]; + assert(!v->IsD()); + return true; + } + } + v = NULL; + return false; + } + else if (p1.Y()!=p2.Y()) //intersezione della superficie con un Yedge + { + if (_y_cs[index]!=-1) + { + v_ind =_y_cs[index]; + v = &_newM->vert[v_ind]; + assert(!v->IsD()); + return true; + } + else + { + v = NULL; + return false; + } + + } + else if (p1.Z()!=p2.Z()) + //intersezione della superficie con un Zedge + { + if (p1.Y()==CurrentSlice) + { + if ( _z_cs[index]!=-1) + { + v_ind = _z_cs[index]; + v = &_newM->vert[v_ind]; + assert(!v->IsD()); + return true; + } + + } + else + { + if (_z_ns[index]!=-1) + { + v_ind = _z_ns[index]; + v = &_newM->vert[v_ind]; + assert(!v->IsD()); + return true; + } + } + v = NULL; + return false; + } + assert (0); + return false; + } + + ///interpolate + NewCoordType Interpolate(const vcg::Point3i &p1, const vcg::Point3i &p2,int dir) + { + float f1 = (float)V(p1); + float f2 = (float)V(p2); + float u = (float) f1/(f1-f2); + NewCoordType ret=vcg::Point3f((float)p1.V(0),(float)p1.V(1),(float)p1.V(2)); + ret.V(dir) = (float) p1.V(dir)*(1.f-u) + u*(float)p2.V(dir); + return (ret); + } + + ///if there is a vertex in z axis of a cell return the vertex or create it + void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + assert(p1.X()+1 == p2.X()); + assert(p1.Y() == p2.Y()); + assert(p1.Z() == p2.Z()); + + int i = p1.X();// (p1.X() - _bbox.min.X())/_cell_size.X(); + int z = p1.Z();//(p1.Z() - _bbox.min.Z())/_cell_size.Z(); + VertexIndex index = i+z*this->siz.X(); + VertexIndex pos=-1; + if (p1.Y()==CurrentSlice) + { + if ((pos=_x_cs[index])==-1) + { + _x_cs[index] = (VertexIndex) _newM->vert.size(); + pos = _x_cs[index]; + Allocator::AddVertices( *_newM, 1 ); + v = &_newM->vert[pos]; + v->P()=Interpolate(p1,p2,0); + return; + } + } + if (p1.Y()==CurrentSlice+1) + { + if ((pos=_x_ns[index])==-1) + { + _x_ns[index] = (VertexIndex) _newM->vert.size(); + pos = _x_ns[index]; + Allocator::AddVertices( *_newM, 1 ); + v = &_newM->vert[pos]; + v->P()=Interpolate(p1,p2,0); + return; + } + } + assert(pos>=0); + v = &_newM->vert[pos]; + } + + ///if there is a vertex in y axis of a cell return the vertex or create it + void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + assert(p1.X() == p2.X()); + assert(p1.Y()+1 == p2.Y()); + assert(p1.Z() == p2.Z()); + + int i = p1.X(); // (p1.X() - _bbox.min.X())/_cell_size.X(); + int z = p1.Z(); // (p1.Z() - _bbox.min.Z())/_cell_size.Z(); + VertexIndex index = i+z*this->siz.X(); + VertexIndex pos=-1; + if ((pos=_y_cs[index])==-1) + { + _y_cs[index] = (VertexIndex) _newM->vert.size(); + pos = _y_cs[index]; + Allocator::AddVertices( *_newM, 1); + v = &_newM->vert[ pos ]; + v->P()=Interpolate(p1,p2,1); + } + assert(pos>=0); + v = &_newM->vert[pos]; + } + + ///if there is a vertex in z axis of a cell return the vertex or create it + void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v) + { + assert(p1.X() == p2.X()); + assert(p1.Y() == p2.Y()); + assert(p1.Z()+1 == p2.Z()); + + int i = p1.X(); //(p1.X() - _bbox.min.X())/_cell_size.X(); + int z = p1.Z(); //(p1.Z() - _bbox.min.Z())/_cell_size.Z(); + VertexIndex index = i+z*this->siz.X(); + + VertexIndex pos=-1; + if (p1.Y()==CurrentSlice) + { + if ((pos=_z_cs[index])==-1) + { + _z_cs[index] = (VertexIndex) _newM->vert.size(); + pos = _z_cs[index]; + Allocator::AddVertices( *_newM, 1 ); + v = &_newM->vert[pos]; + v->P()=Interpolate(p1,p2,2); + return; + } + } + if (p1.Y()==CurrentSlice+1) + { + if ((pos=_z_ns[index])==-1) + { + _z_ns[index] = (VertexIndex) _newM->vert.size(); + pos = _z_ns[index]; + Allocator::AddVertices( *_newM, 1 ); + v = &_newM->vert[pos]; + v->P()=Interpolate(p1,p2,2); + return; + } + } + assert(pos>=0); + v = &_newM->vert[pos]; + } + + };//end class walker + +public: + +typedef Walker /*< Old_Mesh,New_Mesh>*/ MyWalker; + +typedef vcg::tri::MarchingCubes MyMarchingCubes; + +///resample the mesh using marching cube algorithm ,the accuracy is the dimension of one cell the parameter +static void Resample(Old_Mesh &old_mesh,New_Mesh &new_mesh, Box3f volumeBox, vcg::Point3 accuracy,float max_dist, float thr=0, bool DiscretizeFlag=false, bool MultiSampleFlag=false, bool AbsDistFlag=false, vcg::CallBackPos *cb=0 ) +{ + ///be sure that the bounding box is updated + vcg::tri::UpdateBounding::Box(old_mesh); + + MyWalker walker(volumeBox,accuracy); + + walker.max_dim=max_dist+fabs(thr); + walker.offset = - thr; + walker.DiscretizeFlag = DiscretizeFlag; + walker.MultiSampleFlag = MultiSampleFlag; + walker.AbsDistFlag = AbsDistFlag; + MyMarchingCubes mc(new_mesh, walker); + walker.BuildMesh(old_mesh,new_mesh,mc,cb); +} + + +};//end class resampler + +};//end namespace tri +};//end namespace vcg +#endif diff --git a/vcg/complex/algorithms/edge_collapse.h b/vcg/complex/algorithms/edge_collapse.h new file mode 100644 index 00000000..afde69f8 --- /dev/null +++ b/vcg/complex/algorithms/edge_collapse.h @@ -0,0 +1,372 @@ +/**************************************************************************** +* 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 $ + Revision 1.16 2006/10/07 15:04:25 cignoni + removed a useless include + + Revision 1.15 2005/10/12 10:36:26 cignoni + Removed unused local type Edge. Now it use the standard simplex edge. + + Revision 1.14 2004/12/10 01:04:42 cignoni + better comments + + Revision 1.13 2004/11/23 10:34:45 cignoni + passed parameters by reference in many funcs and gcc cleaning + + +****************************************************************************/ + +#ifndef __VCG_TETRA_TRI_COLLAPSE +#define __VCG_TETRA_TRI_COLLAPSE + + +#include +#include +#include + +namespace vcg{ +namespace tri{ + +/** \addtogroup trimesh */ +/*@{*/ +/** This a static utility class for the edge collapse. + It provides a common set of useful function for actually making an edge collapse over a trimesh. + See also the corresponding class in the local optimization framework called TriEdgeCollapse +**/ + +template +class EdgeCollapse +{ + public: + /// The tetrahedral mesh type + typedef TRI_MESH_TYPE TriMeshType; + /// The tetrahedron type + typedef typename TriMeshType::FaceType FaceType; + /// The vertex type + typedef typename FaceType::VertexType VertexType; + typedef typename FaceType::VertexPointer VertexPointer; + /// The vertex iterator type + typedef typename TriMeshType::VertexIterator VertexIterator; + /// The tetra iterator type + typedef typename TriMeshType::FaceIterator FaceIterator; + /// The coordinate type + typedef typename FaceType::VertexType::CoordType CoordType; + /// The scalar type + typedef typename TriMeshType::VertexType::ScalarType ScalarType; + ///the container of tetrahedron type + typedef typename TriMeshType::FaceContainer FaceContainer; + ///the container of vertex type + typedef typename TriMeshType::VertContainer VertContainer; + ///half edge type + typedef typename TriMeshType::FaceType::EdgeType EdgeType; + /// vector of pos + typedef typename std::vector EdgeVec; + ///of VFIterator + typedef typename vcg::face::VFIterator VFI; + /// vector of VFIterator + typedef typename std::vector > VFIVec; + + + + /// Default Constructor + EdgeCollapse() + { + }; + + ~EdgeCollapse() + { + }; + + static VFIVec & AV0(){static VFIVec av0; return av0;} + static VFIVec & AV1(){static VFIVec av1; return av1;} + static VFIVec & AV01(){static VFIVec av01; return av01;} + + + void FindSets(EdgeType &p) + { + VertexType * v0 = p.V(0); + VertexType * v1 = p.V(1); + + AV0().clear(); // Facce incidenti in v0 + AV1().clear(); // Facce incidenti in v1 + AV01().clear(); // Facce incidenti in v0 e v1 + + VFI x; + + for( x.f = v0->VFp(), x.z = v0->VFi(); x.f!=0; ++x) + { + int zv1 = -1; + + for(int j=0;j<3;++j) + if( x.f->V(j)==&*v1 ) { + zv1 = j; + break; + } + if(zv1==-1) AV0().push_back( x ); // la faccia x.f non ha il vertice v1 => e' incidente solo in v0 + else AV01().push_back( x ); + } + + for( x.f = v1->VFp(), x.z = v1->VFi(); x.f!=0; ++x ) + { + int zv0 = -1; + + for(int j=0;j<3;++j) + if( x.f->V(j)==&*v0 ) { + zv0 = j; + break; + } + if(zv0==-1) AV1().push_back( x ); // la faccia x.f non ha il vertice v1 => e' incidente solo in v0 + } +} +/* + Link Conditions test, as described in + + Topology Preserving Edge Contraction + T. Dey, H. Edelsbrunner, + Pub. Inst. Math. 1999 + + Lk (sigma) is the set of all the faces of the cofaces of sigma that are disjoint from sigma + + Lk(v0) inters Lk(v1) == Lk(v0-v1) + + To perform these tests using only the VF adjacency we resort to some virtual counters over + the vertices and the edges, we implement them as std::maps, and we increase these counters + by running over all the faces around each vertex of the collapsing edge. + + At the end (after adding dummy stuff) we should have + 2 for vertices not shared + 4 for vertices shared + 2 for edges shared + 1 for edges not shared. + + +*/ + bool LinkConditions(EdgeType pos) + { + typedef typename vcg::face::VFIterator VFIterator; + // at the end of the loop each vertex must be counted twice + // except for boundary vertex. + std::map VertCnt; + std::map,int> EdgeCnt; + + // the list of the boundary vertexes for the two endpoints + std::vector BoundaryVertexVec[2]; + + // Collect vertexes and edges of V0 and V1 + VFIterator vfi; + for(int i=0;i<2;++i) + { + vfi = VFIterator(pos.V(i)); + for( ;!vfi.End();++vfi) + { + ++ VertCnt[vfi.V1()]; + ++ VertCnt[vfi.V2()]; + if(vfi.V1()::iterator vcmit; + for(vcmit=VertCnt.begin();vcmit!=VertCnt.end();++vcmit) + { + if((*vcmit).second==1) // boundary vertexes are counted only once + BoundaryVertexVec[i].push_back((*vcmit).first); + } + if(BoundaryVertexVec[i].size()==2) + { // aha! one of the two vertex of the collapse is on the boundary + // so add dummy vertex and two dummy edges + VertCnt[0]+=2; + ++ EdgeCnt[std::make_pair(VertexPointer(0),BoundaryVertexVec[i][0]) ] ; + ++ EdgeCnt[std::make_pair(VertexPointer(0),BoundaryVertexVec[i][1]) ] ; + // remember to hide the boundaryness of the two boundary vertexes + ++VertCnt[BoundaryVertexVec[i][0]]; + ++VertCnt[BoundaryVertexVec[i][1]]; + } + } + + // Final loop to find cardinality of Lk( V0-V1 ) + // Note that Lk(edge) is only a set of vertices. + std::vector LkEdge; + + for( vfi = VFIterator(pos.V(0)); !vfi.End(); ++vfi) + { + if(vfi.V1() == pos.V(1) ) LkEdge.push_back(vfi.V2()); + if(vfi.V2() == pos.V(1) ) LkEdge.push_back(vfi.V1()); + } + + // if the collapsing edge was a boundary edge, we must add the dummy vertex. + // Note that this implies that Lk(edge) >=2; + if(LkEdge.size()==1) + { + LkEdge.push_back(0); + } + + // NOW COUNT!!! + size_t SharedEdgeCnt=0; + typename std::map, int>::iterator eci; + for(eci=EdgeCnt.begin();eci!=EdgeCnt.end();++eci) + if((*eci).second == 2) SharedEdgeCnt ++; + + if(SharedEdgeCnt>0) return false; + size_t SharedVertCnt=0; + typename std::map::iterator vci; + for(vci=VertCnt.begin();vci!=VertCnt.end();++vci) + if((*vci).second == 4) SharedVertCnt++; + + if(SharedVertCnt != LkEdge.size() ) return false; + + return true; + } + + + + bool LinkConditionsOld(EdgeType pos){ + + const int ADJ_1 = TriMeshType::VertexType::NewBitFlag(); + const int ADJ_E = TriMeshType::VertexType::NewBitFlag(); + //enum {ADJ_1= MeshType::VertexType::USER0, + // ADJ_E= MeshType::VertexType::USER0<<1} ; + // const int ALLADJ = ADJ_1|ADJ_E; + const int NOTALLADJ = ~(ADJ_1 | ADJ_E | TriMeshType::VertexType::VISITED); + const int NOTALLADJ1 = ~(ADJ_E | TriMeshType::VertexType::VISITED); + + //EdgePosB x; + typename vcg::face::VFIterator x; + // Clear visited and adj flag for all vertices adj to v0; + for(x.f = pos.V(0)->VFp(), x.z = pos.V(0)->VFi(); x.f!=0; ++x ) { + x.f->V1(x.z)->Flags() &= NOTALLADJ; + x.f->V2(x.z)->Flags() &= NOTALLADJ; + } + // Clear visited flag for all vertices adj to v1 and set them adj1 to v1; + for(x.f = pos.V(1)->VFp(), x.z = pos.V(1)->VFi(); x.f!=0; ++x ) { + x.f->V1(x.z)->Flags() &= NOTALLADJ1; + x.f->V2(x.z)->Flags() &= NOTALLADJ1; + } + // Mark vertices adj to v1 as ADJ_1 and adj1 to v1; + for(x.f = pos.V(1)->VFp(), x.z = pos.V(1)->VFi(); x.f!=0; ++x ) { + if(x.f->V1(x.z)==pos.V(0)) x.f->V2(x.z)->Flags() |= ADJ_E | ADJ_1; + else x.f->V2(x.z)->Flags() |= ADJ_1; + if(x.f->V2(x.z)==pos.V(0)) x.f->V1(x.z)->Flags() |= ADJ_E | ADJ_1; + else x.f->V1(x.z)->Flags() |= ADJ_1; + } + + // compute the number of: + int adj01=0; // vertices adjacents to both v0 and v1 + int adje=0; // vertices adjacents to an egde (usually 2) + for(x.f = pos.V(0)->VFp(), x.z = pos.V(0)->VFi(); x.f!=0; ++x ) { + if(!x.f->V1(x.z)->IsV()) { + x.f->V1(x.z)->SetV(); + if(x.f->V1(x.z)->Flags()&ADJ_1) ++adj01; + if(x.f->V1(x.z)->Flags()&ADJ_E) ++adje; + } + if(!x.f->V2(x.z)->IsV()) { + x.f->V2(x.z)->SetV(); + if(x.f->V2(x.z)->Flags()&ADJ_1) ++adj01; + if(x.f->V2(x.z)->Flags()&ADJ_E) ++adje; + } + } + + //bool val=TopoCheck2(); + //if(val != (adj01==adje)) printf("Wrong topo %i %i\n",adj01,adje); + TriMeshType::VertexType::DeleteBitFlag(ADJ_E); + TriMeshType::VertexType::DeleteBitFlag(ADJ_1); + + return (adj01==adje); + } + + + + int DoCollapse(TriMeshType &m, EdgeType & c, const Point3 &p) + { + FindSets(c); + typename VFIVec::iterator i; + int n_face_del =0 ; + + //set Face Face topology + if (TriMeshType::HasFFTopology()) + { + //int e0=c.z; + //int e1=c.f->FFi(c.z); //opposite edge + + //FaceType *f0=c.f; + //FaceType *f1=f0->FFp(c.z); + // + ////take right indexes + //FaceType *f00=f0->FFp((e0+1)%3); + //FaceType *f01=f0->FFp((e0+2)%3); + //int If00=f0->FFi((e0+1)%3); + //int If01=f0->FFi((e0+2)%3); + // + ////then attach faces + //f00->FFp(If00)=f01; + //f00->FFi(If00)=If01; + //f01->FFp(If01)=f00; + //f01->FFi(If01)=If00; + + ////and the ones of face f1 + + //f00=f1->FFp((e1+1)%3); + //f01=f1->FFp((e1+2)%3); + //If00=f1->FFi((e1+1)%3); + //If01=f1->FFi((e1+2)%3); + // + ////and attach faces + //f00->FFp(If00)=f01; + //f00->FFi(If00)=If01; + //f01->FFp(If01)=f00; + //f01->FFi(If01)=If00; + } + + for(i=AV01().begin();i!=AV01().end();++i) + { + FaceType & f = *((*i).f); + assert(f.V((*i).z) == c.V(0)); + vcg::face::VFDetach(f,((*i).z+1)%3); + vcg::face::VFDetach(f,((*i).z+2)%3); + Allocator::DeleteFace(m,f); + //n_face_del++; + } + + //set Vertex Face topology + for(i=AV0().begin();i!=AV0().end();++i) + { + (*i).f->V((*i).z) = c.V(1); // In tutte le facce incidenti in v0, si sostituisce v0 con v1 + (*i).f->VFp((*i).z) = (*i).f->V((*i).z)->VFp(); // e appendo la lista di facce incidenti in v1 a questa faccia + (*i).f->VFi((*i).z) = (*i).f->V((*i).z)->VFi(); + (*i).f->V((*i).z)->VFp() = (*i).f; + (*i).f->V((*i).z)->VFi() = (*i).z; + } + + Allocator::DeleteVertex(m,*(c.V(0))); + //c.V(0)->SetD(); + c.V(1)->P()=p; + return n_face_del; + } + +}; + +} +} +#endif diff --git a/vcg/complex/algorithms/geodesic.h b/vcg/complex/algorithms/geodesic.h new file mode 100644 index 00000000..4f4adc8e --- /dev/null +++ b/vcg/complex/algorithms/geodesic.h @@ -0,0 +1,393 @@ +/**************************************************************************** +* 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 $ + Revision 1.6 2008/01/12 19:07:05 ganovelli + Recompiled from previous out of date version. Still to revise but working + + Revision 1.5 2005/12/13 17:17:19 ganovelli + first importing from old version. NOT optimized! It works with VertexFace Adjacency even over non manifolds + + + *#**************************************************************************/ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +class for computing approximated geodesic distances on a mesh. + +basic example: farthest vertex from a specified one + MyMesh m; + MyMesh::VertexPointer seed,far; + MyMesh::ScalarType dist; + + vcg::Geo g; + g.FarthestVertex(m,seed,far,d); + +*/ +namespace vcg{ +namespace tri{ + +template +struct EuclideanDistance{ + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::ScalarType ScalarType; + + EuclideanDistance(){} + ScalarType operator()(const VertexType * v0, const VertexType * v1) const + {return vcg::Distance(v0->cP(),v1->cP());} +}; + +template > +class Geo{ + + public: + + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::ScalarType ScalarType; + + + + /* Auxiliary class for keeping the heap of vertices to visit and their estimated distance + */ + struct VertDist{ + VertDist(){} + VertDist(VertexPointer _v, ScalarType _d):v(_v),d(_d){} + VertexPointer v; + ScalarType d; + }; + + + /* Temporary data to associate to all the vertices: estimated distance and boolean flag + */ + struct TempData{ + TempData(){} + TempData(const ScalarType & d_){d=d_;source = NULL;} + ScalarType d; + VertexPointer source;//closest source + + }; + + typedef SimpleTempData, TempData > TempDataType; + //TempDataType * TD; + + + struct pred: public std::binary_function{ + pred(){}; + bool operator()(const VertDist& v0, const VertDist& v1) const + {return (v0.d > v1.d);} + }; + struct pred_addr: public std::binary_function{ + pred_addr(){}; + bool operator()(const VertDist& v0, const VertDist& v1) const + {return (v0.v > v1.v);} + }; + + //************** calcolo della distanza di pw in base alle distanze note di pw1 e curr + //************** sapendo che (curr,pw,pw1) e'una faccia della mesh + //************** (vedi figura in file distance.gif) + static ScalarType Distance(const VertexPointer &pw, + const VertexPointer &pw1, + const VertexPointer &curr, + const ScalarType &d_pw1, + const ScalarType &d_curr) + { + ScalarType curr_d=0; + + ScalarType ew_c = DistanceFunctor()(pw,curr); + ScalarType ew_w1 = DistanceFunctor()(pw,pw1); + ScalarType ec_w1 = DistanceFunctor()(pw1,curr); + CoordType w_c = (pw->cP()-curr->cP()).Normalize() * ew_c; + CoordType w_w1 = (pw->cP() - pw1->cP()).Normalize() * ew_w1; + CoordType w1_c = (pw1->cP() - curr->cP()).Normalize() * ec_w1; + + ScalarType alpha,alpha_, beta,beta_,theta,h,delta,s,a,b; + + alpha = acos((w_c.dot(w1_c))/(ew_c*ec_w1)); + s = (d_curr + d_pw1+ec_w1)/2; + a = s/ec_w1; + b = a*s; + alpha_ = 2*acos ( std::min(1.0,sqrt( (b- a* d_pw1)/d_curr))); + + if ( alpha+alpha_ > M_PI){ + curr_d = d_curr + ew_c; + }else + { + beta_ = 2*acos ( std::min(1.0,sqrt( (b- a* d_curr)/d_pw1))); + beta = acos((w_w1).dot(-w1_c)/(ew_w1*ec_w1)); + + if ( beta+beta_ > M_PI) + curr_d = d_pw1 + ew_w1; + else + { + theta = ScalarType(M_PI)-alpha-alpha_; + delta = cos(theta)* ew_c; + h = sin(theta)* ew_c; + curr_d = sqrt( pow(h,2)+ pow(d_curr + delta,2)); + } + } + return (curr_d); + } + + /* + starting from the seeds, it assign a distance value to each vertex. The distance of a vertex is its + approximated geodesic distance to the closest seeds. + This is function is not meant to be called (although is not prevented). Instead, it is invoked by + wrapping function. + */ + static VertexPointer Visit( + MeshType & m, + std::vector & seedVec, + ScalarType & max_distance, + bool farthestOnBorder = false, + ScalarType distance_threshold = std::numeric_limits::max(), + typename MeshType::template PerVertexAttributeHandle * sources = NULL + ) +{ + bool isLeaf; + std::vector frontier; + VertexIterator ii; + std::list children; + VertexPointer curr,farthest=0,pw1; + typename std::list::iterator is; + std::deque leaves; + std::vector _frontier; + ScalarType unreached = std::numeric_limits::max(); + + std::vector > expansion; + typename std::vector ::iterator ifr; + face::VFIterator x; + VertexPointer pw; + + //Requirements + assert(m.HasVFTopology()); + assert(!seedVec.empty()); + + TempDataType * TD; + TD = new TempDataType(m.vert,unreached); + + for(ifr = seedVec.begin(); ifr != seedVec.end(); ++ifr){ + (*TD)[(*ifr).v].d = 0.0; + (*ifr).d = 0.0; + (*TD)[(*ifr).v].source = (*ifr).v; + frontier.push_back(VertDist((*ifr).v,0.0)); + } + // initialize Heap + make_heap(frontier.begin(),frontier.end(),pred()); + + ScalarType curr_d,d_curr = 0.0,d_heap; + VertexPointer curr_s = NULL; + max_distance=0.0; + typename std::vector:: iterator iv; + + while(!frontier.empty() && max_distance < distance_threshold) + { + pop_heap(frontier.begin(),frontier.end(),pred()); + curr = (frontier.back()).v; + curr_s = (*TD)[curr].source; + if(sources!=NULL) + (*sources)[curr] = curr_s; + d_heap = (frontier.back()).d; + frontier.pop_back(); + + assert((*TD)[curr].d <= d_heap); + assert(curr_s != NULL); + if((*TD)[curr].d < d_heap )// a vertex whose distance has been improved after it was inserted in the queue + continue; + assert((*TD)[curr].d == d_heap); + + d_curr = (*TD)[curr].d; + + isLeaf = (!farthestOnBorder || curr->IsB()); + + face::VFIterator x;int k; + + for( x.f = curr->VFp(), x.z = curr->VFi(); x.f!=0; ++x ) + for(k=0;k<2;++k) + { + if(k==0) { + pw = x.f->V1(x.z); + pw1=x.f->V2(x.z); + } + else { + pw = x.f->V2(x.z); + pw1=x.f->V1(x.z); + } + + const ScalarType & d_pw1 = (*TD)[pw1].d; + { + + const ScalarType inter = DistanceFunctor()(curr,pw1);//(curr->P() - pw1->P()).Norm(); + const ScalarType tol = (inter + d_curr + d_pw1)*.0001f; + + if ( ((*TD)[pw1].source != (*TD)[curr].source)||// not the same source + (inter + d_curr < d_pw1 +tol ) || + (inter + d_pw1 < d_curr +tol ) || + (d_curr + d_pw1 < inter +tol ) // triangular inequality + ) + curr_d = d_curr + DistanceFunctor()(pw,curr);//(pw->P()-curr->P()).Norm(); + else + curr_d = Distance(pw,pw1,curr,d_pw1,d_curr); + + } + + if((*TD)[(pw)].d > curr_d){ + (*TD)[(pw)].d = curr_d; + (*TD)[pw].source = curr_s; + frontier.push_back(VertDist(pw,curr_d)); + push_heap(frontier.begin(),frontier.end(),pred()); + } + if(isLeaf){ + if(d_curr > max_distance){ + max_distance = d_curr; + farthest = curr; + } + } + } + }// end while + + // scrivi le distanze sul campo qualita' (nn: farlo parametrico) + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + (*vi).Q() = (*TD)[&(*vi)].d; + + delete TD; + assert(farthest); + return farthest; + + } + + +public: + /* + Given a mesh and a vector of pointers to vertices (sources), assigns the approximated geodesic + distance from the cloasest source to all the mesh vertices and returns the pointer to the farthest. + Note: update the field Q() of the vertices + */ + static bool FarthestVertex( MeshType & m, + std::vector & fro, + VertexPointer & farthest, + ScalarType & distance, + ScalarType distance_thr = std::numeric_limits::max(), + typename MeshType::template PerVertexAttributeHandle * sources = NULL){ + + typename std::vector::iterator fi; + std::vectorfr; + if(fro.empty()) return false; + + for( fi = fro.begin(); fi != fro.end() ; ++fi) + fr.push_back(VertDist(*fi,0.0)); + farthest = Visit(m,fr,distance,false,distance_thr,sources); + return true; + } + /* + Given a mesh and a pointers to a vertex-source (source), assigns the approximated geodesic + distance from the vertex-source to all the mesh vertices and returns the pointer to the farthest + Note: update the field Q() of the vertices + */ + static void FarthestVertex( MeshType & m, + VertexPointer seed, + VertexPointer & farthest, + ScalarType & distance, + ScalarType distance_thr = std::numeric_limits::max()){ + std::vector seedVec; + seedVec.push_back( seed ); + VertexPointer v0; + FarthestVertex(m,seedVec,v0,distance,distance_thr); + farthest = v0; +} + +/* + Same as FarthestPoint but the returned pointer is to a border vertex + Note: update the field Q() of the vertices +*/ + static void FarthestBVertex(MeshType & m, + std::vector & seedVec, + VertexPointer & farthest, + ScalarType & distance, + typename MeshType::template PerVertexAttributeHandle * sources = NULL + ){ + + typename std::vector::iterator fi; + std::vectorfr; + + for( fi = seedVec.begin(); fi != seedVec.end() ; ++fi) + fr.push_back(VertDist(*fi,-1)); + farthest = Visit(m,fr,distance,true,sources); +} +/* + Same as FarthestPoint but the returned pointer is to a border vertex + Note: update the field Q() of the vertices +*/ + static void FarthestBVertex( MeshType & m, + VertexPointer seed, + VertexPointer & farthest, + ScalarType & distance, + typename MeshType::template PerVertexAttributeHandle * sources = NULL){ + std::vector fro; + fro.push_back( seed ); + VertexPointer v0; + FarthestBVertex(m,fro,v0,distance,sources); + farthest = v0; + } + +/* + Assigns to each vertex of the mesh its distance to the closest vertex on the border + Note: update the field Q() of the vertices +*/ + static bool DistanceFromBorder( MeshType & m, + ScalarType & distance, + typename MeshType::template PerVertexAttributeHandle * sources = NULL + ){ + std::vector fro; + VertexIterator vi; + VertexPointer farthest; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if( (*vi).IsB()) + fro.push_back(&(*vi)); + if(fro.empty()) return false; + + tri::UpdateQuality::VertexConstant(m,0); + + return FarthestVertex(m,fro,farthest,distance,std::numeric_limits::max(),sources); +} + + }; +};// end namespace tri +};// end namespace vcg diff --git a/vcg/complex/algorithms/halfedge_quad_clean.h b/vcg/complex/algorithms/halfedge_quad_clean.h new file mode 100755 index 00000000..0d2c5198 --- /dev/null +++ b/vcg/complex/algorithms/halfedge_quad_clean.h @@ -0,0 +1,712 @@ +#ifndef HALFEDGEQUADCLEAN_H +#define HALFEDGEQUADCLEAN_H + +#include +#include +#include +#include +#include +#include + + +namespace vcg +{ + namespace tri + { + /*! + * \brief The class provides methods for detecting doublets and singlets and removing them + * + */ + + template class HalfedgeQuadClean + { + + protected: + + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::EdgePointer EdgePointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename MeshType::FacePointer FacePointer; + + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::EdgeIterator EdgeIterator; + typedef typename MeshType::HEdgeIterator HEdgeIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + /*! Adds to a queue all faces on the 1-ring of a given vertex + * + * \param q Queue of faces + * \param vp Pointer to the vertex + * + */ + static void add_faces(queue &q, VertexPointer vp) + { + vector faces = HalfEdgeTopology::get_incident_faces(vp); + + for(typename vector::iterator fi = faces.begin(); fi != faces.end(); ++fi) + { + if(*fi) + q.push(*fi); + } + } + + /*! Removes doublets from all the faces into a queue until the queue empties + * + * \param m Mesh + * \param faces Set of all faces modified by the removals + * \param q Queue of faces to clean + * + */ + static void remove_doublets(MeshType &m, set &faces, queue &q) + { + + while(!q.empty()) + { + FacePointer fp = q.front(); + q.pop(); + + if( !fp->IsD() ) + { + faces.insert(remove_doublet(m,fp, &q)); + } + } + } + + /*! Removes a doublet and all the other ones that the removal may have generated + * + * \param m Mesh + * \param fp Pointer to the face to clean from doublets + * \param Queue of faces to check for new doublets + * + * \return The new face generated after doublet removal + */ + static FacePointer remove_doublet(MeshType &m, FacePointer fp, queue *q = NULL) + { + vector hedges = HalfEdgeTopology::find_doublet_hedges_quad(fp); + + assert(hedges.size() <= 4); + + switch(hedges.size()) + { + // No doublets + case 0: + return NULL; + + // A single doublet + case 1: + if(q) + { + add_faces(*q, hedges[0]->HNp()->HVp()); + + add_faces(*q, hedges[0]->HPp()->HVp()); + } + + return HalfEdgeTopology::doublet_remove_quad(m, hedges[0]->HVp()); + + // Two doublets on the same face + case 2: + { + + if(q) + { + add_faces(*q, hedges[0]->HNp()->HVp()); + + add_faces(*q, hedges[0]->HPp()->HVp()); + } + + FacePointer fp1 = HalfEdgeTopology::doublet_remove_quad(m, hedges[0]->HVp()); + + // Removal of the doublet may generate a singlet + if(HalfEdgeTopology::is_singlet_quad(fp1)) + { + + HEdgePointer hp = HalfEdgeTopology::singlet_remove_quad(m, fp1); + + if(hp) + { + if(q) + { + if(hp->HFp()) + q->push(hp->HFp()); + if(hp->HOp()->HFp()) + q->push(hp->HOp()->HFp()); + } + + int valence1, valence2; + + valence1 = HalfEdgeTopology::vertex_valence(hp->HVp()); + valence2 = HalfEdgeTopology::vertex_valence(hp->HOp()->HVp()); + + // Check if the removal of the singlet generated other singlets, then iteratively remove them + while(valence1 == 1 || valence2 == 1) + { + + assert(! (valence1 == 1 && valence2 == 1)); + + FacePointer singlet_pointer; + + if(valence1 == 1 ) + singlet_pointer = hp->HFp(); + else + singlet_pointer = hp->HOp()->HFp(); + + hp = HalfEdgeTopology::singlet_remove_quad(m, singlet_pointer); + + if(!hp) + break; + + if(q) + { + if(hp->HFp()) + q->push(hp->HFp()); + if(hp->HOp()->HFp()) + q->push(hp->HOp()->HFp()); + } + + valence1 = HalfEdgeTopology::vertex_valence(hp->HVp()); + valence2 = HalfEdgeTopology::vertex_valence(hp->HOp()->HVp()); + + } + } + } + + return fp1; + } + + // Four doublets: simply remove one of the two faces + case 4: + HalfEdgeTopology::remove_face(m,fp->FHp()->HOp()->HFp()); + return fp; + + default: + assert(0); + } + } + + + + + public: + + /*! Removes doublets from all the faces on the 1-ring of the given vertices + * + * \param m Mesh + * \param Set of all faces modified by the removals + * \param vertices Vector of vertices that will be + * + */ + static void remove_doublets(MeshType &m, set &faces, vector vertices) + { + queue q; + + for(typename vector::iterator vi = vertices.begin(); vi != vertices.end(); ++vi) + { + vector inc_faces = HalfEdgeTopology::get_incident_faces(*vi); + + for(typename vector::iterator fi = inc_faces.begin(); fi != inc_faces.end(); ++fi) + { + if(*fi) + if( !((*fi)->IsD()) ) + q.push(*fi); + } + } + + remove_doublets(m, faces, q); + } + + /*! Removes doublets from all the faces on the 1-ring of the given vertices + * + * \param m Mesh + * \param vertices Vector of vertices that will be + * + */ + static void remove_doublets(MeshType &m, vector vertices) + { + set faces; + + remove_doublets(m,faces,vertices); + } + + /*! Removes doublets from a set of faces + * + * \param m Mesh + * \param set of faces to clean + * + */ + static void remove_doublets(MeshType &m, set &faces) + { + + queue q; + + for(typename set::iterator fi = faces.begin(); fi != faces.end(); ++fi) + { + if(*fi) + if( !((*fi)->IsD()) ) + q.push(*fi); + } + + remove_doublets(m, faces, q); + + } + + + /*! Removes all doublets from a mesh + * + * \param m Mesh + * + * \return Number of doublets removed + */ + static int remove_doublets(MeshType &m) + { + int count; + int removed = 0; + do + { + count = 0; + for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if( !((*fi).IsD()) ) + { + if(remove_doublet(m, &(*fi))) + count++; + } + } + + removed += count; + + }while(count != 0); + + return removed; + } + + /*! Removes all singlets from a mesh + * + * \param m Mesh + * + * \return Number of singlets removed + */ + static int remove_singlets(MeshType &m) + { + int removed = 0; + int count; + do + { + count = 0; + for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if( !((*fi).IsD()) ) + { + if( HalfEdgeTopology::is_singlet_quad(&(*fi)) ) + { + HalfEdgeTopology::singlet_remove_quad(m, &(*fi)); + count++; + } + } + } + + removed += count; + + }while(count != 0); + + return removed; + } + + /*! Checks if a mesh has singlets + * + * \param m Mesh + * + * \return Value indicating whether mesh has singlets + */ + static bool has_singlets(MeshType &m) + { + + for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if( !((*fi).IsD()) ) + { + if( HalfEdgeTopology::is_singlet_quad(&(*fi)) ) + return true; + } + } + + return false; + } + + /*! Checks if a mesh has doublets + * + * \param m Mesh + * + * \return Value indicating whether mesh has doublets + */ + static bool has_doublets(MeshType &m) + { + + for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if( !((*fi).IsD()) ) + { + if( HalfEdgeTopology::has_doublet_quad(&(*fi)) ) + return true; + } + } + + return false; + } + + /*! Performs rotation of selected edges computing the best rotation + * + * + * \param m Mesh + * \param hedges Vector of halfedges representing the edges to rotate + * \param faces Set of modified faces (every modified face will be inserted into this set) + * + */ + template + static void flip_edges(MeshType &m, vector &hedges, set &faces) + { + + for(typename vector::iterator hi = hedges.begin(); hi != hedges.end(); ++hi) + { + // edge must be shared by two faces + if((*hi)->HFp() && (*hi)->HOp()->HFp()) + { + if(!(*hi)->IsD() && !(*hi)->HFp()->IsD() && !(*hi)->HOp()->HFp()->IsD()) + { + typename PriorityType::FlipType fliptype = PriorityType::best_flip( *hi ); + + if(fliptype != PriorityType::NO_FLIP) + { + + vector vertices; + + // Record old vertices for future removal of doublets + vertices.push_back((*hi)->HVp()); + vertices.push_back((*hi)->HOp()->HVp()); + + // Add modified faces into the set of faces + faces.insert((*hi)->HFp()); + faces.insert((*hi)->HOp()->HFp()); + + bool cw = (fliptype == PriorityType::CW_FLIP); + HalfEdgeTopology::edge_rotate_quad((*hi),cw); + + // After a rotation doublets may have generated + remove_doublets(m, faces, vertices); + + } + } + } + } + } + + /*! Performs edge rotations on the entire mesh computing the best rotation for each edge + * + * + * \param m Mesh + * + */ + template + static int flip_edges(MeshType &m) + { + int count; + int ret=0; + do + { + count = 0; + for(typename MeshType::EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei) + { + + if( !(ei->IsD()) ) + { + HEdgePointer hp = ei->EHp(); + + if(hp->HFp() && hp->HOp()->HFp()) + { + typename PriorityType::FlipType fliptype = PriorityType::best_flip( hp ); + + if(fliptype != PriorityType::NO_FLIP) + { + vector vertices; + + // Record old vertices for future removal of doublets + vertices.push_back(hp->HVp()); + vertices.push_back(hp->HOp()->HVp()); + + bool cw = (fliptype == PriorityType::CW_FLIP); + HalfEdgeTopology::edge_rotate_quad(hp,cw); + + // After a rotation doublets may have generated + remove_doublets(m, vertices); + + count++; + } + } + + } + } + + ret+=count; + + }while(count != 0); + + return ret; + } + + }; + + + /*! + * \brief Generic priority for edge rotations + * + */ + template class EdgeFlipPriority + { + public: + typedef typename MeshType::HEdgePointer HEdgePointer; + + /// Possible types of rotation + enum FlipType { NO_FLIP, CW_FLIP, CCW_FLIP}; + + /*! + * Computes the best rotation to perform + * + * \param hp Pointer to an halfedge representing the edge to rotate + * + * \return The best type of rotation + */ + static FlipType best_flip( HEdgePointer hp); + }; + + /*! + * \brief Priority based on maximizing vertices regularity + * + */ + template class VertReg: public EdgeFlipPriority + { + + public: + + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::EdgePointer EdgePointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename MeshType::FacePointer FacePointer; + + typedef EdgeFlipPriority Base; + typedef typename Base::FlipType FlipType; + + /// Default Constructor + VertReg(){} + + ~VertReg(){} + + /*! + * Computes the best rotation to perform for maximizing vertices regularity + * + * \param hp Pointer to an halfedge representing the edge to rotate + * + * \return The best type of rotation + */ + static FlipType best_flip( HEdgePointer hp) + { + assert(hp); + assert(!hp->IsD()); + + vector vps1 = HalfEdgeTopology::getVertices(hp->HFp(), hp); + + vector vps2 = HalfEdgeTopology::getVertices(hp->HOp()->HFp(), hp->HOp()); + + valarray valences(6); + + /* + Indices of vertices into vector valences: + + 3-------2 + | | + | f1 | + | | + 0-------1 + | | + | f2 | + | | + 4-------5 + + */ + + // Compute valencies of vertices + for(int i=0; i<4; i++) + valences[i] = HalfEdgeTopology::vertex_valence(vps1[i]) - 4 ; + + valences[4] = HalfEdgeTopology::vertex_valence(vps2[2]) - 4; + valences[5] = HalfEdgeTopology::vertex_valence(vps2[3]) - 4; + + + // Vector containing sums of the valencies in all the possible cases: no rotation, ccw rotation, cw rotation + vector sums; + + // positions: + // sums[0]: now (No rotation) + // sums[1]: flipping ccw + // sums[2]: flipping cw + + // No rotation + sums.push_back( pow(valences, 2.0).sum() ); + + // CCW + valences[0]--; + valences[1]--; + valences[2]++; + valences[4]++; + + sums.push_back( pow(valences, 2.0).sum() ); + + // CW + valences[2]--; + valences[4]--; + valences[3]++; + valences[5]++; + + sums.push_back( pow(valences, 2.0).sum() ); + + + if( sums[2]<= sums[1] && sums[2]< sums[0] ) + return Base::CW_FLIP; + + else if( sums[1]< sums[2] && sums[1]< sums[0] ) + return Base::CCW_FLIP; + + return Base::NO_FLIP; + + } + + }; + + /*! + * \brief Priority based on minimizing homeometry + * + */ + template class Homeometry: public EdgeFlipPriority + { + + public: + + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::EdgePointer EdgePointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename MeshType::FacePointer FacePointer; + + + typedef EdgeFlipPriority Base; + typedef typename Base::FlipType FlipType; + + /// Default Constructor + Homeometry(){} + + ~Homeometry(){} + + /*! + * Computes the best rotation to perform for minimizing the distance from homeometry + * + * \param hp Pointer to an halfedge representing the edge to rotate + * + * \return The best type of rotation + */ + static FlipType best_flip( HEdgePointer hp) + { + assert(hp); + assert(!hp->IsD()); + + vector face1 = HalfEdgeTopology::getVertices(hp->HFp(), hp); + vector face2 = HalfEdgeTopology::getVertices(hp->HOp()->HFp(), hp->HOp()); + + // Vector containing sums of the valencies in all the possible cases: no rotation, ccw rotation, cw rotation + vector sums; + + // positions: + // sums[0]: now (No rotation) + // sums[1]: flipping ccw + // sums[2]: flipping cw + + // No rotation + sums.push_back( distance_from_homeometry(face1, face2, 0) ); + + // CCW + face1[1] = face2[2]; + face2[1] = face1[2]; + + sums.push_back( distance_from_homeometry(face1, face2, 1) ); + + // CW + face1[2] = face2[3]; + face2[2] = face1[3]; + + sums.push_back( distance_from_homeometry(face1, face2, 2) ); + + + if( sums[2]<= sums[1] && sums[2]< sums[0] ) + return Base::CW_FLIP; + + else if( sums[1]< sums[2] && sums[1]< sums[0] ) + return Base::CCW_FLIP; + + return Base::NO_FLIP; + + } + + protected: + + /*! + * Computes the area of a quad + * + * \param vertices Vector of the four vertices of the quad + * + * \return Area of the quad + */ + static float area(vector &vertices) + { + + assert(vertices.size() == 4); + + float tri1 = Norm( (vertices[1]->cP() - vertices[0]->cP()) ^ (vertices[2]->cP() - vertices[0]->cP()) ); + float tri2 = Norm( (vertices[2]->cP() - vertices[0]->cP()) ^ (vertices[3]->cP() - vertices[0]->cP()) ); + + return (tri1+tri2) / 2; + } + + /*! + * Computes the distance of two faces from being homeometirc + * + * \param face1 Vector of vertices belonging to the first face + * \param face2 Vector of vertices belonging to the second face + * \param i Index of the edge to compute + * + * \return The computed homeometry + */ + static float distance_from_homeometry(vector &face1, vector &face2, int i) + { + // Ideal edge length + float mu = sqrt( (area(face1) + area(face2)) / 2 ); + + // Length of the i-th edge (the edge changed with a rotation) + float edge_length = Distance( face1[i]->cP(), face1[i+1]->cP() ); + + // Length of the diagonals + valarray diagonals(4); + + diagonals[0] = Distance( face1[0]->cP(), face1[2]->cP() ); + diagonals[1] = Distance( face1[1]->cP(), face1[3]->cP() ); + diagonals[2] = Distance( face2[0]->cP(), face2[2]->cP() ); + diagonals[3] = Distance( face2[1]->cP(), face2[3]->cP() ); + + // Ideal diagonal length + float ideal_diag_length = SQRT_TWO*mu; + + float sum_diagonals = pow(diagonals - ideal_diag_length, 2.0).sum(); + + return (pow (edge_length - mu , static_cast(2.0)) + sum_diagonals); + } + + }; + + } +} +#endif // HALFEDGEQUADCLEAN_H diff --git a/vcg/complex/algorithms/hole.h b/vcg/complex/algorithms/hole.h new file mode 100644 index 00000000..2bdd0d02 --- /dev/null +++ b/vcg/complex/algorithms/hole.h @@ -0,0 +1,992 @@ +/**************************************************************************** +* 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 $ +Revision 1.34 2007/01/31 15:25:49 giec +Remove some usless code in Minimum Weight Triangulation. + +Revision 1.33 2007/01/31 11:46:12 giec +Bug fix + +Revision 1.32 2007/01/18 18:15:14 cignoni +added missing typenames + +Revision 1.31 2007/01/18 11:17:43 giec +The minimum weight algorithm keep the topology consistent. + +Revision 1.30 2007/01/10 12:07:54 giec +Bugfixed ComputeDihedralAngle function + +Revision 1.29 2006/12/27 15:09:52 giec +Bug fix on ComputeDihedralAngle function + +Revision 1.28 2006/12/12 11:14:51 cignoni +Commented some variant of the quality measure of weighted ears + +Revision 1.27 2006/12/07 00:40:18 cignoni +Added many this-> for gcc compiling + +Revision 1.26 2006/12/06 13:03:59 cignoni +Corrected bugs on selfintersection + +Revision 1.25 2006/12/06 00:12:53 cignoni +Heavily restructured and corrected. Now a single Close ear function +Corrected Hole search function, and management of double non manifold vertex in a hole +Changed priority strategy in the heap, now a mix of quality and dihedral angle. +Changed but still untested IntersectionEar + +Revision 1.24 2006/12/01 21:24:16 cignoni +Corrected bug in the search of holes. Removed output prints + +Revision 1.23 2006/12/01 08:53:55 cignoni +Corrected pop_heap vs pop_back issue in heap usage + +Revision 1.22 2006/12/01 00:11:17 cignoni +Added Callback, Corrected some spelling errors (adiacense -> adjacency). +Added Validity Check function for hole loops + +Revision 1.21 2006/11/30 11:49:20 cignoni +small gcc compiling issues + +Revision 1.20 2006/11/29 16:21:45 cignoni +Made static exposed funtions of the class + +Revision 1.19 2006/11/29 15:25:22 giec +Removed limit. + +Revision 1.18 2006/11/29 15:18:49 giec +Code refactory and bugfix. + +Revision 1.17 2006/11/24 10:42:39 mariolatronico +Now compiles on gcc under linux. + +Revision 1.16 2006/11/22 13:43:28 giec +Code refactory and added minimum weight triangolation. + +Revision 1.15 2006/11/13 10:11:38 giec +Clear some useless code + +Revision 1.14 2006/11/07 15:13:56 zifnab1974 +Necessary changes for compilation with gcc 3.4.6. Especially the hash function is a problem + +Revision 1.13 2006/11/07 11:47:11 cignoni +gcc compiling issues + +Revision 1.12 2006/11/07 07:56:43 cignoni +Added missing std:: + +Revision 1.11 2006/11/06 16:12:29 giec +Leipa ear now compute max dihedral angle. + +Revision 1.10 2006/10/31 11:30:41 ganovelli +changed access throught iterator with static call to comply 2005 compiler + +Revision 1.9 2006/10/20 07:44:45 cignoni +Added missing std:: + +Revision 1.8 2006/10/18 15:06:47 giec +New policy for compute quality in TrivialEar. +Bugfixed LeipaEar. +Added new algorithm "selfintersection" with test for self intersection. + +Revision 1.7 2006/10/10 09:12:02 giec +Bugfix and added a new type of ear (Liepa like) + +Revision 1.6 2006/10/09 10:07:07 giec +Optimized version of "EAR HOLE FILLING", the Ear is selected according to its dihedral angle. + +Revision 1.5 2006/10/06 15:28:14 giec +first working implementationof "EAR HOLE FILLING". + +Revision 1.4 2006/10/02 12:06:40 giec +BugFix + +Revision 1.3 2006/09/27 15:33:32 giec +It close one simple hole . . . + +Revision 1.2 2006/09/27 09:29:53 giec +Frist working release whit a few bugs. +It almost fills the hole ... + +Revision 1.1 2006/09/25 09:17:44 cignoni +First Non working Version + +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_HOLE +#define __VCG_TRI_UPDATE_HOLE + +#include +#include +#include +#include +#include +#include + +namespace vcg { + namespace tri { + + /* + Un ear e' identificato da due hedge pos. + i vertici dell'ear sono + e0.VFlip().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: + typedef typename MESH::FaceType FaceType; + typedef typename MESH::FacePointer FacePointer; + typedef typename face::Pos PosType; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::CoordType CoordType; + + PosType e0; + PosType e1; + CoordType n; // the normal of the face defined by the ear + const char * Dump() {return 0;} + const CoordType &cP(int i) const {return P(i);} + const CoordType &P(int i) const { + switch(i) { + case 0 : return e0.v->cP(); + case 1 : return e1.v->cP(); + case 2 : return e0.VFlip()->cP(); + default: assert(0); + } + return e0.v->cP(); + } + + ScalarType quality; + ScalarType angle; + //std::vector* vf; + TrivialEar(){} + TrivialEar(const PosType & ep) + { + e0=ep; + assert(e0.IsBorder()); + e1=e0; + e1.NextB(); + n=vcg::Normal(*this); + ComputeQuality(); + ComputeAngle(); + } + + /// Compute the angle of the two edges of the ear. + // it tries to make the computation in a precision safe way. + // the angle computation takes into account the case of reversed ears + void ComputeAngle() + { + angle=Angle(cP(2)-cP(0), cP(1)-cP(0)); + ScalarType flipAngle = n.dot(e0.v->N()); + if(flipAngle<0) angle = (2.0 *(float)M_PI) - angle; + } + + virtual inline bool operator < ( const TrivialEar & c ) const { return quality < c.quality; } + + bool IsNull(){return e0.IsNull() || e1.IsNull();} + void SetNull(){e0.SetNull();e1.SetNull();} + virtual void ComputeQuality() { quality = QualityFace(*this) ; }; + bool IsUpToDate() {return ( e0.IsBorder() && e1.IsBorder());}; + // An ear is degenerated if both of its two endpoints are non manifold. + bool IsDegen(const int nonManifoldBit) + { + if(e0.VFlip()->IsUserBit(nonManifoldBit) && e1.V()->IsUserBit(nonManifoldBit)) + return true; + else return false; + } + bool IsConcave() const {return(angle > (float)M_PI);} + + virtual bool Close(PosType &np0, PosType &np1, FaceType * f) + { + // simple topological check + if(e0.f==e1.f) { + //printf("Avoided bad ear"); + return false; + } + + //usato per generare una delle due nuove orecchie. + PosType ep=e0; ep.FlipV(); ep.NextB(); ep.FlipV(); // he precedente a e0 + PosType en=e1; en.NextB(); // he successivo a e1 + + (*f).V(0) = e0.VFlip(); + (*f).V(1) = e0.v; + (*f).V(2) = e1.v; + ComputeNormal(*f); + + (*f).FFp(0) = e0.f; + (*f).FFi(0) = e0.z; + (*f).FFp(1) = e1.f; + (*f).FFi(1) = e1.z; + (*f).FFp(2) = f; + (*f).FFi(2) = 2; + + e0.f->FFp(e0.z)=f; + e0.f->FFi(e0.z)=0; + + e1.f->FFp(e1.z)=f; + e1.f->FFi(e1.z)=1; + + // caso ear degenere per buco triangolare + if(ep==en) + { + //printf("Closing the last triangle"); + f->FFp(2)=en.f; + f->FFi(2)=en.z; + en.f->FFp(en.z)=f; + en.f->FFi(en.z)=2; + np0.SetNull(); + np1.SetNull(); + } + // Caso ear non manifold a + else if(ep.v==en.v) + { + //printf("Ear Non manif A\n"); + PosType enold=en; + en.NextB(); + f->FFp(2)=enold.f; + f->FFi(2)=enold.z; + enold.f->FFp(enold.z)=f; + enold.f->FFi(enold.z)=2; + np0=ep; + np1=en; + } + // Caso ear non manifold b + else if(ep.VFlip()==e1.v) + { + //printf("Ear Non manif B\n"); + PosType epold=ep; + ep.FlipV(); ep.NextB(); ep.FlipV(); + f->FFp(2)=epold.f; + f->FFi(2)=epold.z; + epold.f->FFp(epold.z)=f; + epold.f->FFi(epold.z)=2; + np0=ep; // assign the two new + np1=en; // pos that denote the ears + } + else // caso standard // Now compute the new ears; + { + np0=ep; + np1=PosType(f,2,e1.v); + } + + return true; + } + }; + + //Ear with FillHoleMinimumWeight's quality policy + template class MinimumWeightEar : public TrivialEar + { + public: + static float &DiedralWeight() { static float _dw=1.0; return _dw;} + typedef TrivialEar TE; + typename MESH::ScalarType dihedralRad; + typename MESH::ScalarType aspectRatio; + const char * Dump() { + static char buf[200]; + if(this->IsConcave()) sprintf(buf,"Dihedral -(deg) %6.2f Quality %6.2f\n",math::ToDeg(dihedralRad),aspectRatio); + else sprintf(buf,"Dihedral (deg) %6.2f Quality %6.2f\n",math::ToDeg(dihedralRad),aspectRatio); + return buf; + } + + MinimumWeightEar(){} + //MinimumWeightEar(const PosType & ep) : TrivialEar(ep) + MinimumWeightEar(const typename face::Pos& ep) : TrivialEar(ep) + { + ComputeQuality(); + } + + // In the heap, by default, we retrieve the LARGEST value, + // so if we need the ear with minimal dihedral angle, we must reverse the sign of the comparison. + // The concave elements must be all in the end of the heap, sorted accordingly, + // So if only one of the two ear is Concave that one is always the minimum one. + // the pow function is here just to give a way to play with different weighting schemas, balancing in a different way + + virtual inline bool operator < ( const MinimumWeightEar & c ) const + { + if(TE::IsConcave() == c.IsConcave()) + { + return (pow((float)dihedralRad,(float)DiedralWeight())/aspectRatio) > (pow((float)c.dihedralRad,(float)DiedralWeight())/c.aspectRatio); + } + if(TE::IsConcave()) return true; + // assert(c.IsConcave()); + return false; + } + + // the real core of the whole hole filling strategy. + virtual void ComputeQuality() + { + //compute quality by (dihedral ancgle, area/sum(edge^2) ) + typename MESH::CoordType n1=TE::e0.FFlip()->cN(); + typename MESH::CoordType n2=TE::e1.FFlip()->cN(); + + dihedralRad = std::max(Angle(TE::n,n1),Angle(TE::n,n2)); + aspectRatio = QualityFace(*this); + } + + }; + //Ear for selfintersection algorithm + template class SelfIntersectionEar : public MinimumWeightEar + { + public: + typedef typename MESH::FaceType FaceType; + typedef typename MESH::FacePointer FacePointer; + typedef typename face::Pos PosType; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::CoordType CoordType; + + static std::vector &AdjacencyRing() + { + static std::vector ar; + return ar; + } + + SelfIntersectionEar(){} + SelfIntersectionEar(const PosType & ep):MinimumWeightEar(ep){} + + virtual bool Close(PosType &np0, PosType &np1, FacePointer f) + { + PosType ep=this->e0; ep.FlipV(); ep.NextB(); ep.FlipV(); // he precedente a e0 + PosType en=this->e1; en.NextB(); // he successivo a e1 + //costruisco la faccia e poi testo, o copio o butto via. + (*f).V(0) = this->e0.VFlip(); + (*f).V(1) = this->e0.v; + (*f).V(2) = this->e1.v; + + (*f).FFp(0) = this->e0.f; + (*f).FFi(0) = this->e0.z; + (*f).FFp(1) = this->e1.f; + (*f).FFi(1) = this->e1.z; + (*f).FFp(2) = f; + (*f).FFi(2) = 2; + + int a1, a2; + a1= this->e0.z; + a2= this->e1.z; + + this->e0.f->FFp(this->e0.z)=f; + this->e0.f->FFi(this->e0.z)=0; + + this->e1.f->FFp(this->e1.z)=f; + this->e1.f->FFi(this->e1.z)=1; + typename std::vector< FacePointer >::iterator it; + for(it = this->AdjacencyRing().begin();it!= this->AdjacencyRing().end();++it) + { + if(!(*it)->IsD()) + if( tri::Clean::TestIntersection(&(*f),*it)) + { + this->e0.f->FFp(this->e0.z)= this->e0.f; + this->e0.f->FFi(this->e0.z)=a1; + + this->e1.f->FFp(this->e1.z)= this->e1.f; + this->e1.f->FFi(this->e1.z)=a2; + return false; + } + } + //return ((TrivialEar *)this)->Close(np0,np1,f); + this->e0.f->FFp(this->e0.z)= this->e0.f; + this->e0.f->FFi(this->e0.z)=a1; + + this->e1.f->FFp(this->e1.z)=this->e1.f; + this->e1.f->FFi(this->e1.z)=a2; + + bool ret=TrivialEar::Close(np0,np1,f); + if(ret) AdjacencyRing().push_back(f); + return ret; + } + }; + + // 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. + +template +class Hole +{ +public: + typedef typename MESH::VertexType VertexType; + typedef typename MESH::VertexPointer VertexPointer; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::FacePointer FacePointer; + typedef typename MESH::FaceIterator FaceIterator; + typedef typename MESH::CoordType CoordType; + typedef typename vcg::Box3 Box3Type; + typedef typename face::Pos PosType; + +public: + + class Info + { + public: + Info(){} + Info(PosType const &pHole, int const pHoleSize, Box3 &pHoleBB) + { + p=pHole; + size=pHoleSize; + bb=pHoleBB; + } + + PosType p; + int size; + Box3Type bb; + + bool operator < (const Info & hh) const {return size < hh.size;} + + ScalarType Perimeter() + { + ScalarType sum=0; + PosType ip = p; + do + { + sum+=Distance(ip.v->cP(),ip.VFlip()->cP()); + ip.NextB(); + } + while (ip != p); + return sum; + } + + // Support function to test the validity of a single hole loop + // for now it test only that all the edges are border; + // The real test should check if all non manifold vertices + // are touched only by edges belonging to this hole loop. + bool CheckValidity() + { + if(!p.IsBorder()) + return false; + PosType ip=p;ip.NextB(); + for(;ip!=p;ip.NextB()) + { + if(!ip.IsBorder()) + return false; + } + return true; + } + }; + +template + static void FillHoleEar(MESH &m, Info &h ,int UBIT, std::vector &app,std::vector *vf =0) + { + //Aggiungo le facce e aggiorno il puntatore alla faccia! + FaceIterator f = tri::Allocator::AddFaces(m, h.size-2, app); + assert(h.p.f >= &*m.face.begin()); + assert(h.p.f <= &m.face.back()); + assert(h.p.IsBorder());//test fondamentale altrimenti qualcosa s'e' rotto! + std::vector< EAR > H; + H.reserve(h.size); + int nmBit= VertexType::NewBitFlag(); // non manifoldness bit + + //First loops around the hole to mark non manifold vertices. + PosType ip = h.p; // Pos iterator + do{ + ip.V()->ClearUserBit(nmBit); + ip.V()->ClearV(); + ip.NextB(); + } while(ip!=h.p); + + ip = h.p; // Re init the pos iterator for another loop (useless if everithing is ok!!) + do{ + if(!ip.V()->IsV()) + ip.V()->SetV(); // All the vertexes that are visited more than once are non manifold + else ip.V()->SetUserBit(nmBit); + ip.NextB(); + } while(ip!=h.p); + + PosType fp = h.p; + do{ + EAR app = EAR(fp); + H.push_back( app ); + //printf("Adding ear %s ",app.Dump()); + fp.NextB(); + assert(fp.IsBorder()); + }while(fp!=h.p); + + int cnt=h.size; + + make_heap(H.begin(), H.end()); + + //finche' il buco non e' chiuso o non ci sono piu' orecchie da analizzare. + while( cnt > 2 && !H.empty() ) + { + //printf("Front of the heap is %s", H.front().Dump()); + pop_heap(H.begin(), H.end()); // retrieve the MAXIMUM value and put in the back; + PosType ep0,ep1; + EAR BestEar=H.back(); + H.pop_back(); + if(BestEar.IsUpToDate() && !BestEar.IsDegen(nmBit)) + { + if((*f).HasPolyInfo()) (*f).Alloc(3); + if(BestEar.Close(ep0,ep1,&*f)) + { + if(!ep0.IsNull()){ + H.push_back(EAR(ep0)); + push_heap( H.begin(), H.end()); + } + if(!ep1.IsNull()){ + H.push_back(EAR(ep1)); + push_heap( H.begin(), H.end()); + } + --cnt; + f->SetUserBit(UBIT); + if(vf != 0) (*vf).push_back(*f); + ++f; + } + }//is update() + }//fine del while principale. + //tolgo le facce non utilizzate. + while(f!=m.face.end()) + { + (*f).SetD(); + ++f; + m.fn--; + } + + VertexType::DeleteBitFlag(nmBit); // non manifoldness bit + } + + template + static int EarCuttingFill(MESH &m, int sizeHole,bool Selected = false, CallBackPos *cb=0) + { + std::vector< Info > vinfo; + int UBIT = GetInfo(m, Selected,vinfo); + + typename std::vector::iterator ith; + //Info app; + int indCb=0; + int holeCnt=0; + std::vector vfp; + for(ith = vinfo.begin(); ith!= vinfo.end(); ++ith) + vfp.push_back( &(*ith).p.f ); + + for(ith = vinfo.begin(); ith!= vinfo.end(); ++ith) + { + indCb++; + if(cb) (*cb)(indCb*10/vinfo.size(),"Closing Holes"); + if((*ith).size < sizeHole){ + holeCnt++; + FillHoleEar< EAR >(m, *ith,UBIT,vfp); + } + } + + FaceIterator fi; + for(fi = m.face.begin(); fi!=m.face.end(); ++fi) + { + if(!(*fi).IsD()) + (*fi).ClearUserBit(UBIT); + } + return holeCnt; + } + +// it returns the number of created holes. + +template + static int EarCuttingIntersectionFill(MESH &m, int sizeHole, bool Selected = false, CallBackPos *cb=0) + { + std::vector vinfo; + int UBIT = GetInfo(m, Selected,vinfo); + std::vector vf; + PosType sp; + PosType ap; + typename std::vector::iterator ith; + + // collect the face pointer that has to be updated by the various addfaces + std::vector vfpOrig; + for(ith = vinfo.begin(); ith!= vinfo.end(); ++ith) + vfpOrig.push_back( &(*ith).p.f ); + + int indCb=0; + int holeCnt=0; + for(ith = vinfo.begin(); ith!= vinfo.end(); ++ith) + { + indCb++; + if(cb) (*cb)(indCb*10/vinfo.size(),"Closing Holes"); + if((*ith).size < sizeHole){ + std::vector vfp; + holeCnt++; + vfp=vfpOrig; + EAR::AdjacencyRing().clear(); + //Loops around the hole to collect the races . + PosType ip = (*ith).p; + do + { + PosType inp = ip; + do + { + inp.FlipE(); + inp.FlipF(); + EAR::AdjacencyRing().push_back(inp.f); + } while(!inp.IsBorder()); + ip.NextB(); + }while(ip != (*ith).p); + + typename std::vector::iterator fpi; + for(fpi=EAR::AdjacencyRing().begin();fpi!=EAR::AdjacencyRing().end();++fpi) + vfp.push_back( &*fpi ); + + FillHoleEar(m, *ith,UBIT,vfp,&vf); + EAR::AdjacencyRing().clear(); + } + } + FaceIterator fi; + for(fi = m.face.begin(); fi!=m.face.end(); ++fi) + { + if(!(*fi).IsD()) + (*fi).ClearUserBit(UBIT); + } + return holeCnt; + } + + + + static int GetInfo(MESH &m,bool Selected ,std::vector& VHI) + { + FaceIterator fi; + int UBIT = FaceType::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 + { + for(int j =0; j<3 ; ++j) + { + if( face::IsBorder(*fi,j) && !(*fi).IsUserBit(UBIT) ) + {//Trovato una faccia di bordo non ancora visitata. + (*fi).SetUserBit(UBIT); + PosType sp(&*fi, j, (*fi).V(j)); + PosType fp=sp; + int holesize=0; + + Box3Type hbox; + hbox.Add(sp.v->cP()); + //printf("Looping %i : (face %i edge %i) \n", VHI.size(),sp.f-&*m.face.begin(),sp.z); + sp.f->SetUserBit(UBIT); + do + { + sp.f->SetUserBit(UBIT); + hbox.Add(sp.v->cP()); + ++holesize; + sp.NextB(); + sp.f->SetUserBit(UBIT); + assert(sp.IsBorder()); + }while(sp != fp); + + //ho recuperato l'inofrmazione su tutto il buco + VHI.push_back( Info(sp,holesize,hbox) ); + } + }//for sugli edge del triangolo + }//S & !S + }//!IsD() + }//for principale!!! + return UBIT; + } + + //Minimum Weight Algorithm + class Weight + { + public: + + Weight() { ang = 180; ar = FLT_MAX ;} + Weight( float An, float Ar ) { ang=An ; ar= Ar;} + ~Weight() {} + + float angle() const { return ang; } + float area() const { return ar; } + + Weight operator+( const Weight & other ) const {return Weight( std::max( angle(), other.angle() ), area() + other.area());} + bool operator<( const Weight & rhs ) const {return ( angle() < rhs.angle() ||(angle() == rhs.angle() && area() < rhs.area())); } + + private: + float ang; + float ar; + }; + + /* + \ / \/ + v1*---------*v4 + / \ / + / \ / + / \ / + /ear \ / +*---------*- +| v3 v2\ +*/ + + static float ComputeDihedralAngle(CoordType p1,CoordType p2,CoordType p3,CoordType p4) + { + CoordType n1 = NormalizedNormal(p1,p3,p2); + CoordType n2 = NormalizedNormal(p1,p2,p4); + return math::ToDeg(AngleN(n1,n2)); + } + + static bool existEdge(PosType pi,PosType pf) + { + PosType app = pi; + PosType appF = pi; + PosType tmp; + assert(pi.IsBorder()); + appF.NextB(); + appF.FlipV(); + do + { + tmp = app; + tmp.FlipV(); + if(tmp.v == pf.v) + return true; + app.FlipE(); + app.FlipF(); + + if(app == pi)return false; + }while(app != appF); + return false; + } + + static Weight computeWeight( int i, int j, int k, + std::vector pv, + std::vector< std::vector< int > > v) + { + PosType pi = pv[i]; + PosType pj = pv[j]; + PosType pk = pv[k]; + + //test complex edge + if(existEdge(pi,pj) || existEdge(pj,pk)|| existEdge(pk,pi) ) + { + return Weight(); + } + // Return an infinite weight, if one of the neighboring patches + // could not be created. + if(v[i][j] == -1){return Weight();} + if(v[j][k] == -1){return Weight();} + + //calcolo il massimo angolo diedrale, se esiste. + float angle = 0.0f; + PosType px; + if(i + 1 == j) + { + px = pj; + px.FlipE(); px.FlipV(); + angle = std::max(angle , ComputeDihedralAngle(pi.v->P(), pj.v->P(), pk.v->P(), px.v->P()) ); + } + else + { + angle = std::max( angle, ComputeDihedralAngle(pi.v->P(),pj.v->P(), pk.v->P(), pv[ v[i][j] ].v->P())); + } + + if(j + 1 == k) + { + px = pk; + px.FlipE(); px.FlipV(); + angle = std::max(angle , ComputeDihedralAngle(pj.v->P(), pk.v->P(), pi.v->P(), px.v->P()) ); + } + else + { + angle = std::max( angle, ComputeDihedralAngle(pj.v->P(),pk.v->P(), pi.v->P(), pv[ v[j][k] ].v->P())); + } + + if( i == 0 && k == (int)v.size() - 1) + { + px = pi; + px.FlipE(); px.FlipV(); + angle = std::max(angle , ComputeDihedralAngle(pk.v->P(), pi.v->P(), pj.v->P(),px.v->P() ) ); + } + + ScalarType area = ( (pj.v->P() - pi.v->P()) ^ (pk.v->P() - pi.v->P()) ).Norm() * 0.5; + + return Weight(angle, area); + } + + static void calculateMinimumWeightTriangulation(MESH &m, FaceIterator f,std::vector vv ) + { + std::vector< std::vector< Weight > > w; //matrice dei pesi minimali di ogni orecchio preso in conzideraione + std::vector< std::vector< int > > vi;//memorizza l'indice del terzo vertice del triangolo + + //hole size + int nv = vv.size(); + + w.clear(); + w.resize( nv, std::vector( nv, Weight() ) ); + + vi.resize( nv, std::vector( nv, 0 ) ); + + //inizializzo tutti i pesi possibili del buco + for ( int i = 0; i < nv-1; ++i ) + w[i][i+1] = Weight( 0, 0 ); + + //doppio ciclo for per calcolare di tutti i possibili triangoli i loro pesi. + for ( int j = 2; j < nv; ++j ) + { + for ( int i = 0; i + j < nv; ++i ) + { + //per ogni triangolazione mi mantengo il minimo valore del peso tra i triangoli possibili + Weight minval; + + //indice del vertice che da il peso minimo nella triangolazione corrente + int minIndex = -1; + + //ciclo tra i vertici in mezzo a i due prefissati + for ( int m = i + 1; m < i + j; ++m ) + { + Weight a = w[i][m]; + Weight b = w[m][i+j]; + Weight newval = a + b + computeWeight( i, m, i+j, vv, vi); + if ( newval < minval ) + { + minval = newval; + minIndex = m; + } + } + w[i][i+j] = minval; + vi[i][i+j] = minIndex; + } + } + + //Triangulate + int i, j; + i=0; j=nv-1; + + triangulate(m,f, i, j, vi, vv); + + while(f!=m.face.end()) + { + (*f).SetD(); + ++f; + m.fn--; + } + } + + + static void triangulate(MESH &m, FaceIterator &f,int i, int j, + std::vector< std::vector > vi, std::vector vv) + { + if(i + 1 == j){return;} + if(i==j)return; + + int k = vi[i][j]; + + if(k == -1) return; + + //Setto i vertici + f->V(0) = vv[i].v; + f->V(1) = vv[k].v; + f->V(2) = vv[j].v; + + f++; + triangulate(m,f,i,k,vi,vv); + triangulate(m,f,k,j,vi,vv); + } + + static void MinimumWeightFill(MESH &m, int holeSize, bool Selected) + { + FaceIterator fi; + std::vector vvi; + std::vector vfp; + + std::vector vinfo; + typename std::vector::iterator VIT; + GetInfo(m, Selected,vinfo); + + for(VIT = vinfo.begin(); VIT != vinfo.end();++VIT) + { + vvi.push_back(VIT->p); + } + + typename std::vector::iterator ith; + typename std::vector::iterator ithn; + typename std::vector::iterator itf; + + std::vector app; + PosType ps; + std::vector tr; + std::vector vf; + + for(ith = vvi.begin(); ith!= vvi.end(); ++ith) + { + tr.clear(); + vf.clear(); + app.clear(); + vfp.clear(); + + ps = *ith; + getBoundHole(ps,app); + + if(app.size() <= size_t(holeSize) ) + { + typename std::vector::iterator itP; + std::vector vfp; + + for(ithn = vvi.begin(); ithn!= vvi.end(); ++ithn) + vfp.push_back(&(ithn->f)); + + for(itP = app.begin (); itP != app.end ();++itP) + vfp.push_back( &(*itP).f ); + + //aggiungo le facce + FaceIterator f = tri::Allocator::AddFaces(m, (app.size()-2) , vfp); + + calculateMinimumWeightTriangulation(m,f, app); + } + } + + } + + static void getBoundHole (PosType sp,std::vector&ret) + { + PosType fp = sp; + //take vertex around the hole + do + { + assert(fp.IsBorder()); + ret.push_back(fp); + fp.NextB(); + }while(sp != fp); + } + +};//close class Hole + + } // end namespace +} +#endif diff --git a/vcg/complex/algorithms/inertia.h b/vcg/complex/algorithms/inertia.h new file mode 100644 index 00000000..d4841bef --- /dev/null +++ b/vcg/complex/algorithms/inertia.h @@ -0,0 +1,383 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2005 \/)\/ * +* 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 $ +Revision 1.3 2006/03/29 10:12:08 corsini +Add cast to avoid warning + +Revision 1.2 2005/12/12 12:08:30 cignoni +First working version + +Revision 1.1 2005/11/21 15:58:12 cignoni +First Release (not working!) + + +Revision 1.13 2005/11/17 00:42:03 cignoni +****************************************************************************/ +#ifndef _VCG_INERTIA_ +#define _VCG_INERTIA_ + +/* +The algorithm is based on a three step reduction of the volume integrals +to successively simpler integrals. The algorithm is designed to minimize +the numerical errors that can result from poorly conditioned alignment of +polyhedral faces. It is also designed for efficiency. All required volume +integrals of a polyhedron are computed together during a single walk over +the boundary of the polyhedron; exploiting common subexpressions reduces +floating point operations. + +For more information, check out: + +Brian Mirtich, +``Fast and Accurate Computation of Polyhedral Mass Properties,'' +journal of graphics tools, volume 1, number 2, 1996 + +*/ + +#include +#include + +#include +namespace vcg +{ + namespace tri + { +template +class Inertia +{ + typedef InertiaMeshType 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; + typedef typename MeshType::CoordType CoordType; + +private : + enum {X=0,Y=1,Z=2}; + inline ScalarType SQR(ScalarType &x) const { return x*x;} + inline ScalarType CUBE(ScalarType &x) const { return x*x*x;} + + int A; /* alpha */ + int B; /* beta */ + int C; /* gamma */ + +/* projection integrals */ + double P1, Pa, Pb, Paa, Pab, Pbb, Paaa, Paab, Pabb, Pbbb; + +/* face integrals */ + double Fa, Fb, Fc, Faa, Fbb, Fcc, Faaa, Fbbb, Fccc, Faab, Fbbc, Fcca; + +/* volume integrals */ + double T0, T1[3], T2[3], TP[3]; + +public: + +/* compute various integrations over projection of face */ + void compProjectionIntegrals(FaceType &f) +{ + double a0, a1, da; + double b0, b1, db; + double a0_2, a0_3, a0_4, b0_2, b0_3, b0_4; + double a1_2, a1_3, b1_2, b1_3; + double C1, Ca, Caa, Caaa, Cb, Cbb, Cbbb; + double Cab, Kab, Caab, Kaab, Cabb, Kabb; + int i; + + P1 = Pa = Pb = Paa = Pab = Pbb = Paaa = Paab = Pabb = Pbbb = 0.0; + + for (i = 0; i < 3; i++) { + a0 = f.V(i)->P()[A]; + b0 = f.V(i)->P()[B]; + a1 = f.V1(i)->P()[A]; + b1 = f.V1(i)->P()[B]; + da = a1 - a0; + db = b1 - b0; + a0_2 = a0 * a0; a0_3 = a0_2 * a0; a0_4 = a0_3 * a0; + b0_2 = b0 * b0; b0_3 = b0_2 * b0; b0_4 = b0_3 * b0; + a1_2 = a1 * a1; a1_3 = a1_2 * a1; + b1_2 = b1 * b1; b1_3 = b1_2 * b1; + + C1 = a1 + a0; + Ca = a1*C1 + a0_2; Caa = a1*Ca + a0_3; Caaa = a1*Caa + a0_4; + Cb = b1*(b1 + b0) + b0_2; Cbb = b1*Cb + b0_3; Cbbb = b1*Cbb + b0_4; + Cab = 3*a1_2 + 2*a1*a0 + a0_2; Kab = a1_2 + 2*a1*a0 + 3*a0_2; + Caab = a0*Cab + 4*a1_3; Kaab = a1*Kab + 4*a0_3; + Cabb = 4*b1_3 + 3*b1_2*b0 + 2*b1*b0_2 + b0_3; + Kabb = b1_3 + 2*b1_2*b0 + 3*b1*b0_2 + 4*b0_3; + + P1 += db*C1; + Pa += db*Ca; + Paa += db*Caa; + Paaa += db*Caaa; + Pb += da*Cb; + Pbb += da*Cbb; + Pbbb += da*Cbbb; + Pab += db*(b1*Cab + b0*Kab); + Paab += db*(b1*Caab + b0*Kaab); + Pabb += da*(a1*Cabb + a0*Kabb); + } + + P1 /= 2.0; + Pa /= 6.0; + Paa /= 12.0; + Paaa /= 20.0; + Pb /= -6.0; + Pbb /= -12.0; + Pbbb /= -20.0; + Pab /= 24.0; + Paab /= 60.0; + Pabb /= -60.0; +} + + +void CompFaceIntegrals(FaceType &f) +{ + Point3 n; + ScalarType w; + double k1, k2, k3, k4; + + compProjectionIntegrals(f); + + n = f.N(); + w = -f.V(0)->P()*n; + k1 = 1 / n[C]; k2 = k1 * k1; k3 = k2 * k1; k4 = k3 * k1; + + Fa = k1 * Pa; + Fb = k1 * Pb; + Fc = -k2 * (n[A]*Pa + n[B]*Pb + w*P1); + + Faa = k1 * Paa; + Fbb = k1 * Pbb; + Fcc = k3 * (SQR(n[A])*Paa + 2*n[A]*n[B]*Pab + SQR(n[B])*Pbb + + w*(2*(n[A]*Pa + n[B]*Pb) + w*P1)); + + Faaa = k1 * Paaa; + Fbbb = k1 * Pbbb; + Fccc = -k4 * (CUBE(n[A])*Paaa + 3*SQR(n[A])*n[B]*Paab + + 3*n[A]*SQR(n[B])*Pabb + CUBE(n[B])*Pbbb + + 3*w*(SQR(n[A])*Paa + 2*n[A]*n[B]*Pab + SQR(n[B])*Pbb) + + w*w*(3*(n[A]*Pa + n[B]*Pb) + w*P1)); + + Faab = k1 * Paab; + Fbbc = -k2 * (n[A]*Pabb + n[B]*Pbbb + w*Pbb); + Fcca = k3 * (SQR(n[A])*Paaa + 2*n[A]*n[B]*Paab + SQR(n[B])*Pabb + + w*(2*(n[A]*Paa + n[B]*Pab) + w*Pa)); +} + + +void Compute(MeshType &m) +{ + tri::UpdateNormals::PerFaceNormalized(m); + double nx, ny, nz; + + T0 = T1[X] = T1[Y] = T1[Z] + = T2[X] = T2[Y] = T2[Z] + = TP[X] = TP[Y] = TP[Z] = 0; + FaceIterator fi; + for (fi=m.face.begin(); fi!=m.face.end();++fi) if(!(*fi).IsD()) { + FaceType &f=(*fi); + + nx = fabs(f.N()[0]); + ny = fabs(f.N()[1]); + nz = fabs(f.N()[2]); + if (nx > ny && nx > nz) C = X; + else C = (ny > nz) ? Y : Z; + A = (C + 1) % 3; + B = (A + 1) % 3; + + CompFaceIntegrals(f); + + T0 += f.N()[X] * ((A == X) ? Fa : ((B == X) ? Fb : Fc)); + + T1[A] += f.N()[A] * Faa; + T1[B] += f.N()[B] * Fbb; + T1[C] += f.N()[C] * Fcc; + T2[A] += f.N()[A] * Faaa; + T2[B] += f.N()[B] * Fbbb; + T2[C] += f.N()[C] * Fccc; + TP[A] += f.N()[A] * Faab; + TP[B] += f.N()[B] * Fbbc; + TP[C] += f.N()[C] * Fcca; + } + + T1[X] /= 2; T1[Y] /= 2; T1[Z] /= 2; + T2[X] /= 3; T2[Y] /= 3; T2[Z] /= 3; + TP[X] /= 2; TP[Y] /= 2; TP[Z] /= 2; +} + +ScalarType Mass() +{ + return static_cast(T0); +} + +Point3 CenterOfMass() +{ + Point3 r; + r[X] = T1[X] / T0; + r[Y] = T1[Y] / T0; + r[Z] = T1[Z] / T0; + return r; +} +void InertiaTensor(Matrix33 &J ){ + Point3 r; + r[X] = T1[X] / T0; + r[Y] = T1[Y] / T0; + r[Z] = T1[Z] / T0; + /* compute inertia tensor */ + J[X][X] = (T2[Y] + T2[Z]); + J[Y][Y] = (T2[Z] + T2[X]); + J[Z][Z] = (T2[X] + T2[Y]); + J[X][Y] = J[Y][X] = - TP[X]; + J[Y][Z] = J[Z][Y] = - TP[Y]; + J[Z][X] = J[X][Z] = - TP[Z]; + + J[X][X] -= T0 * (r[Y]*r[Y] + r[Z]*r[Z]); + J[Y][Y] -= T0 * (r[Z]*r[Z] + r[X]*r[X]); + J[Z][Z] -= T0 * (r[X]*r[X] + r[Y]*r[Y]); + J[X][Y] = J[Y][X] += T0 * r[X] * r[Y]; + J[Y][Z] = J[Z][Y] += T0 * r[Y] * r[Z]; + J[Z][X] = J[X][Z] += T0 * r[Z] * r[X]; +} + +void InertiaTensor(Matrix44 &J ) +{ + J.SetIdentity(); + Point3 r; + r[X] = T1[X] / T0; + r[Y] = T1[Y] / T0; + r[Z] = T1[Z] / T0; + /* compute inertia tensor */ + J[X][X] = (T2[Y] + T2[Z]); + J[Y][Y] = (T2[Z] + T2[X]); + J[Z][Z] = (T2[X] + T2[Y]); + J[X][Y] = J[Y][X] = - TP[X]; + J[Y][Z] = J[Z][Y] = - TP[Y]; + J[Z][X] = J[X][Z] = - TP[Z]; + + J[X][X] -= T0 * (r[Y]*r[Y] + r[Z]*r[Z]); + J[Y][Y] -= T0 * (r[Z]*r[Z] + r[X]*r[X]); + J[Z][Z] -= T0 * (r[X]*r[X] + r[Y]*r[Y]); + J[X][Y] = J[Y][X] += T0 * r[X] * r[Y]; + J[Y][Z] = J[Z][Y] += T0 * r[Y] * r[Z]; + J[Z][X] = J[X][Z] += T0 * r[Z] * r[X]; +} + + +/** Compute eigenvalues and eigenvectors of inertia tensor. + The eigenvectors make a rotation matrix that aligns the mesh along the axes of min/max inertia + */ +void InertiaTensorEigen(Matrix44 &EV, Point4 &ev ) +{ + Matrix44 it; + InertiaTensor(it); + Matrix44d EVd,ITd;ITd.Import(it); + Point4d evd; + int n; + Jacobi(ITd,evd,EVd,n); + EV.Import(EVd); + ev.Import(evd); +} + +/** Compute covariance matrix of a mesh, i.e. the integral + int_{M} { (x-b)(x-b)^T }dx where b is the barycenter and x spans over the mesh M + */ +static void Covariance(const MeshType & m, vcg::Point3 & bary, vcg::Matrix33 &C){ + // find the barycenter + + ConstFaceIterator fi; + ScalarType area = 0.0; + bary.SetZero(); + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + bary += vcg::Barycenter( *fi )* vcg::DoubleArea(*fi); + area+=vcg::DoubleArea(*fi); + } + bary/=area; + + + C.SetZero(); + // C as covariance of triangle (0,0,0)(1,0,0)(0,1,0) + vcg::Matrix33 C0; + C0.SetZero(); + C0[0][0] = C0[1][1] = 2.0; + C0[0][1] = C0[1][0] = 1.0; + C0*=1/24.0; + + // integral of (x,y,0) in the same triangle + CoordType X(1/6.0,1/6.0,0); + vcg::Matrix33 A, // matrix that bring the vertices to (v1-v0,v2-v0,n) + DC; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + const CoordType &P0 = (*fi).P(0); + const CoordType &P1 = (*fi).P(1); + const CoordType &P2 = (*fi).P(2); + CoordType n = ((P1-P0)^(P2-P0)); + const float da = n.Norm(); + n/=da*da; + + #ifndef VCG_USE_EIGEN + A.SetColumn(0, P1-P0); + A.SetColumn(1, P2-P0); + A.SetColumn(2, n); + #else + A.col(0) = P1-P0; + A.col(1) = P2-P0; + A.col(2) = n; + #endif + CoordType delta = P0 - bary; + + /* DC is calculated as integral of (A*x+delta) * (A*x+delta)^T over the triangle, + where delta = v0-bary + */ + + DC.SetZero(); + DC+= A*C0*A.transpose(); + vcg::Matrix33 tmp; + tmp.OuterProduct(A*X,delta); + DC += tmp + tmp.transpose(); + DC+= tmp; + tmp.OuterProduct(delta,delta); + DC+=tmp*0.5; +// DC*=fabs(A.Determinant()); // the determinant of A is the jacobian of the change of variables A*x+delta + DC*=da; // the determinant of A is also the double area of *fi + C+=DC; + } + +} +}; // end class Inertia + + } // end namespace tri +} // end namespace vcg + + +#endif diff --git a/vcg/complex/algorithms/inside.h b/vcg/complex/algorithms/inside.h new file mode 100644 index 00000000..0827cf8c --- /dev/null +++ b/vcg/complex/algorithms/inside.h @@ -0,0 +1,115 @@ + +/**************************************************************************** +* IDOLib * +* Interactive Deformable Objects Library * +* http://idolib.sf.net * +* * +* Copyright(C) 2005 * +* 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 $ +Revision 1.3 2007/06/06 15:38:57 turini +Use the barycenter function from triangle3.h instead of +the one in face\base.h. + +Revision 1.2 2007/06/06 14:26:51 pietroni +compiling error resolved + + + +****************************************************************************/ + + + +#include +#include +#include + + +#ifndef VCG_INSIDE +#define VCG_INSIDE + + +/// This static funtion is used to see if one point is inside a triangular mesh or not... +/// First parameter is a spatial indexing structure (eg. a grid) used to perform research operation, initialized with faces of the triangular mesh of type TriMeshType + +namespace vcg { + + namespace tri { + + template + class Inside + { + + private: + + typedef typename FaceSpatialIndexing::CoordType CoordType; + typedef typename FaceSpatialIndexing::ScalarType ScalarType; + + public: + + /// Return true if the point is inside the mesh. + static bool Is_Inside( TriMeshType & m, FaceSpatialIndexing & _g_mesh, const CoordType & test ) + { + typedef typename TriMeshType::FaceType FaceType; + typedef typename TriMeshType::ScalarType ScalarType; + typedef typename TriMeshType::CoordType CoordType; + const ScalarType EPSILON = 0.000001; + /// First test if the element is inside the bounding box of the mesh. + if( !( m.bbox.IsIn(test) ) ) return false; + else + { + ScalarType dist; + CoordType Norm, ip, nearest; + FaceType *f = vcg::tri::GetClosestFace< TriMeshType, FaceSpatialIndexing >( m, _g_mesh, test, m.bbox.Diag(), dist, nearest, Norm, ip ); + assert( f != NULL ); /// Check if there is any face in the mesh + /// If the point is on the face is considered inside. + if( ( test - nearest ).Norm() <= EPSILON ) return true; + /// Check if the closest point is inside a face + if( ( ip.V(0) > EPSILON ) && ( ip.V(1) > EPSILON ) && ( ip.V(2) > EPSILON ) ) + { + /// Check if the test point is inside the mesh using the normal direction + vcg::Point3f debugn = f->N(); + if( ( f->N() * ( test - nearest ) ) < 0 ) return true; + else return false; + } + /// In this case we are not sure because hit an edge or a vertex. + /// So we use a ray that go until the barycenter of found face, then see normal value again + else + { + CoordType bary = vcg::Barycenter< FaceType >(*f); + /// Set ray : origin and direction + vcg::Ray3 r; r.Set( test, ( bary - test ) ); r.Normalize(); + FaceType *f1 = vcg::tri::DoRay< TriMeshType, FaceSpatialIndexing >( m, _g_mesh, r, m.bbox.Diag(), dist ); + assert( f1 != NULL ); + /// In this case normal direction is enough. + if( ( f1->N() * ( test - bary ) ) < 0 ) return true; + else return false; + } + + } + } + + }; // end class + } +} + +#endif \ No newline at end of file diff --git a/vcg/complex/algorithms/intersection.h b/vcg/complex/algorithms/intersection.h new file mode 100644 index 00000000..c3982a0a --- /dev/null +++ b/vcg/complex/algorithms/intersection.h @@ -0,0 +1,470 @@ +/**************************************************************************** +* 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 $ +Revision 1.11 2007/05/02 13:25:45 zifnab1974 +only use typename when necessary + +Revision 1.10 2007/04/10 22:46:57 pietroni +- line 152 changed call intersection to IntersectionPlaneTriangle because changing in function's name + +Revision 1.9 2007/01/03 15:51:28 pietroni +added initial define and included missing files + +Revision 1.8 2006/01/19 14:06:37 spinelli +add std:: namespace... + +Revision 1.7 2005/10/03 16:18:15 spinelli +add template parameter for spatialindexing struction + +Revision 1.6 2005/05/30 09:11:20 ganovelli +header added, error in include + +Revision 1.3 2005/05/17 21:19:37 ganovelli +some std::and typename missing (CRS4) + +Revision 1.2 2005/03/08 14:42:22 ganovelli +added vcg header + + +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __VCGLIB_INTERSECTION_TRI_MESH +#define __VCGLIB_INTERSECTION_TRI_MESH + +namespace vcg{ + +/** \addtogroup complex */ +/*@{*/ +/** + Function computing the intersection between a grid and a plane. It returns all the cells intersected +*/ +template < typename GridType,typename ScalarType> +bool Intersect( GridType & grid,Plane3 plane, std::vector &cells){ + Point3d p,_d; + Plane3d pl; + _d.Import(plane.Direction()); + pl.SetDirection(_d); + pl.SetOffset(plane.Offset()); + for( int ax = 0; ax <3; ++ax) + { int axis = ax; + int axis0 = (axis+1)%3; + int axis1 = (axis+2)%3; + int i,j; + Point3i pi; + + Segment3 seg; + seg.P0().Import(grid.bbox.min); + seg.P1().Import(grid.bbox.min); + seg.P1()[axis] = grid.bbox.max[axis]; + + for(i = 0 ; i <= grid.siz[axis0]; ++i){ + for(j = 0 ; j <= grid.siz[axis1]; ++j) + { + seg.P0()[axis0] = grid.bbox.min[axis0]+ (i+0.01) * grid.voxel[axis0] ; + seg.P1()[axis0] = grid.bbox.min[axis0]+ (i+0.01) * grid.voxel[axis0]; + seg.P0()[axis1] = grid.bbox.min[axis1]+ (j+0.01) * grid.voxel[axis1]; + seg.P1()[axis1] = grid.bbox.min[axis1]+ (j+0.01) * grid.voxel[axis1]; + if ( IntersectionPlaneSegmentEpsilon(pl,seg,p)) + { + pi[axis] = std::min(std::max(0,(int)floor((p[axis ]-grid.bbox.min[axis])/grid.voxel[axis])),grid.siz[axis]); + pi[axis0] = i; + pi[axis1] = j; + grid.Grid(pi,axis,cells); + } + } + } + } + sort(cells.begin(),cells.end()); + cells.erase(unique(cells.begin(),cells.end()),cells.end()); + + return false; + } + +/*@}*/ +/** + Basic Function computing the intersection between a trimesh and a plane, provided a pointer + to an space indexing data structure (e.g. a grid, an oct-tree..) +*/ + template < typename TriMeshType, typename EdgeMeshType, class ScalarType, class IndexingType > + bool Intersection( /*TriMeshType & m, */ + Plane3 pl, + EdgeMeshType & em, + double& ave_length, + IndexingType *grid, + typename std::vector< typename IndexingType::Cell* >& cells) +{ + typedef typename TriMeshType::FaceContainer FaceContainer; + typedef IndexingType GridType; + typename EdgeMeshType::VertexIterator vi; + typename TriMeshType::FaceIterator fi; + std::vector v; + v.clear(); + Intersect(*grid,pl,cells); + Segment3 seg; + ave_length = 0.0; + typename std::vector::iterator ic; + typename GridType::Cell fs,ls; + for(ic = cells.begin(); ic != cells.end();++ic) + { + grid->Grid(*ic,fs,ls); + typename GridType::Link * lk = fs; + while(lk != ls){ + typename TriMeshType::FaceType & face = *(lk->Elem()); + if(!face.IsS()) + { + face.SetS(); + v.push_back(&face); + if(vcg::IntersectionPlaneTriangle(pl,face,seg))// intersezione piano triangolo + { + face.SetS(); + // add to em + ave_length+=seg.Length(); + vcg::edg::Allocator::AddEdges(em,1); + vi = vcg::edg::Allocator::AddVertices(em,2); + (*vi).P() = seg.P0(); + em.edges.back().V(0) = &(*vi); + vi++; + (*vi).P() = seg.P1(); + em.edges.back().V(1) = &(*vi); + } + }//endif + lk++; + }//end while + } + ave_length/=em.en; + typename std::vector::iterator v_i; + for(v_i=v.begin(); v_i!=v.end(); ++v_i) (*v_i)->ClearS(); + + return true; +} + +/** \addtogroup complex */ +/*@{*/ +/** + Basic Function computing the intersection between a trimesh and a plane. It returns an EdgeMesh without needing anything else. + Note: This version always returns a segment for each triangle of the mesh which intersects with the plane. In other + words there are 2*n vertices where n is the number of segments fo the mesh. You can run vcg::edge:Unify to unify + the vertices closer that a given value epsilon. Note that, due to subtraction error during triangle plane intersection, + it is not safe to put epsilon to 0. +// TODO si dovrebbe considerare la topologia face-face della trimesh per derivare quella della edge mesh.. +*/ +template < typename TriMeshType, typename EdgeMeshType, class ScalarType > +bool Intersection(TriMeshType & m, + Plane3 pl, + EdgeMeshType & em) +{ + typename EdgeMeshType::VertexIterator vi; + typename TriMeshType::FaceIterator fi; + em.Clear(); + Segment3 seg; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + if(vcg::IntersectionPlaneTriangle(pl,*fi,seg))// intersezione piano triangolo + { + vcg::edg::Allocator::AddEdges(em,1); + vi = vcg::edg::Allocator::AddVertices(em,2); + (*vi).P() = seg.P0(); + em.edges.back().V(0) = &(*vi); + vi++; + (*vi).P() = seg.P1(); + em.edges.back().V(1) = &(*vi); + } + }//end for + + return true; +} + +/** \addtogroup complex */ +/*@{*/ +/** + Compute the intersection between a trimesh and a plane. + given a plane return the set of faces that are contained + into intersected cells. +*/ +template < typename TriMeshType, class ScalarType, class IndexingType > +bool Intersection(Plane3 pl, + IndexingType *grid, + typename std::vector &v) +{ + typedef typename TriMeshType::FaceContainer FaceContainer; + typedef IndexingType GridType; + typename TriMeshType::FaceIterator fi; + v.clear(); + typename std::vector< typename GridType::Cell* > cells; + Intersect(*grid,pl,cells); + typename std::vector::iterator ic; + typename GridType::Cell fs,ls; + + for(ic = cells.begin(); ic != cells.end();++ic) + { + grid->Grid(*ic,fs,ls); + typename GridType::Link * lk = fs; + while(lk != ls){ + typename TriMeshType::FaceType & face = *(lk->Elem()); + v.push_back(&face); + lk++; + }//end while + }//end for + return true; +} + +/** + Computes the intersection between a Ray and a Mesh. Returns a 3D Pointset. +*/ +template < typename TriMeshType, class ScalarType> +bool IntersectionRayMesh( + /* Input Mesh */ TriMeshType * m, + /* Ray */ const Line3 & ray, + /* Intersect Point */ Point3 & hitPoint) +{ + //typedef typename TriMeshType::FaceContainer FaceContainer; + typename TriMeshType::FaceIterator fi; + bool hit=false; + + if(m==0) return false; + + //TriMeshType::FaceIterator fi; + //std::vector::iterator fi; + + ScalarType bar1,bar2,dist; + Point3 p1; + Point3 p2; + Point3 p3; + for(fi = m->face.begin(); fi != m->face.end(); ++fi) + { + p1=vcg::Point3( (*fi).P(0).X() ,(*fi).P(0).Y(),(*fi).P(0).Z() ); + p2=vcg::Point3( (*fi).P(1).X() ,(*fi).P(1).Y(),(*fi).P(1).Z() ); + p3=vcg::Point3( (*fi).P(2).X() ,(*fi).P(2).Y(),(*fi).P(2).Z() ); + if(IntersectionLineTriangle(ray,p1,p2,p3,dist,bar1,bar2)) + { + hitPoint= p1*(1-bar1-bar2) + p2*bar1 + p3*bar2; + hit=true; + } + } + + return hit; +} + +/** + Computes the intersection between a Ray and a Mesh. Returns a 3D Pointset, baricentric's coordinates + and a pointer of intersected face. +*/ +template < typename TriMeshType, class ScalarType> +bool IntersectionRayMesh( + /* Input Mesh */ TriMeshType * m, + /* Ray */ const Line3 & ray, + /* Intersect Point */ Point3 & hitPoint, + /* Baricentric coord 1*/ ScalarType &bar1, + /* Baricentric coord 2*/ ScalarType &bar2, + /* Baricentric coord 3*/ ScalarType &bar3, + /* FacePointer */ typename TriMeshType::FacePointer fp + ) +{ + //typedef typename TriMeshType::FaceContainer FaceContainer; + typename TriMeshType::FaceIterator fi; + bool hit=false; + + if(m==0) return false; + + //TriMeshType::FaceIterator fi; + //std::vector::iterator fi; + + ScalarType dist; + Point3 p1; + Point3 p2; + Point3 p3; + for(fi = m->face.begin(); fi != m->face.end(); ++fi) + { + p1=vcg::Point3( (*fi).P(0).X() ,(*fi).P(0).Y(),(*fi).P(0).Z() ); + p2=vcg::Point3( (*fi).P(1).X() ,(*fi).P(1).Y(),(*fi).P(1).Z() ); + p3=vcg::Point3( (*fi).P(2).X() ,(*fi).P(2).Y(),(*fi).P(2).Z() ); + if(IntersectionLineTriangle(ray,p1,p2,p3,dist,bar1,bar2)) + { + bar3 = (1-bar1-bar2); + hitPoint= p1*bar3 + p2*bar1 + p3*bar2; + fp = &(*fi); + hit=true; + } + } + + return hit; +} + +/** + Compute the intersection between a mesh and a ball. + given a mesh return a new mesh made by a copy of all the faces entirely includeded in the ball plus + new faces created by refining the ones intersected by the ball border. + It works by recursively splitting the triangles that cross the border, as long as their area is greater than + a given value tol. If no value is provided, 1/10^5*2*pi*radius is used + NOTE: the returned mesh is a triangle soup +*/ +template < typename TriMeshType, class ScalarType> +void IntersectionBallMesh( TriMeshType & m, const vcg::Sphere3 &ball, TriMeshType & res, + float tol = 0){ + + typename TriMeshType::VertexIterator v0,v1,v2; + typename TriMeshType::FaceIterator fi; + std::vector closests; + vcg::Point3 witness; + std::pair info; + + if(tol == 0) tol = M_PI * ball.Radius() * ball.Radius() / 100000; + + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD() && IntersectionSphereTriangle(ball ,(*fi), witness , &info)) + closests.push_back(&(*fi)); + + res.Clear(); + SubSet(res,closests); + int i =0; + while(i(ball ,res.face[i], witness , &info) && !allIn){ + if(vcg::DoubleArea(res.face[i]) > tol) + { + // split the face res.face[i] in four, add the four new faces to the mesh and delete the face res.face[i] + v0 = vcg::tri::Allocator::AddVertices(res,3); + fi = vcg::tri::Allocator::AddFaces(res,4); + + v1 = v0; ++v1; + v2 = v1; ++v2; + (*v0).P() = (res.face[i].P(0) + res.face[i].P(1))*0.5; + (*v1).P() = (res.face[i].P(1) + res.face[i].P(2))*0.5; + (*v2).P() = (res.face[i].P(2) + res.face[i].P(0))*0.5; + + (*fi).V(0) = res.face[i].V(0); + (*fi).V(1) = &(*v0); + (*fi).V(2) = &(*v2); + ++fi; + + (*fi).V(0) = res.face[i].V(1); + (*fi).V(1) = &(*v1); + (*fi).V(2) = &(*v0); + ++fi; + + (*fi).V(0) = &(*v0); + (*fi).V(1) = &(*v1); + (*fi).V(2) = &(*v2); + ++fi; + + (*fi).V(0) = &(*v2); + (*fi).V(1) = &(*v1); + (*fi).V(2) = res.face[i].V(2) ; + + vcg::tri::Allocator::DeleteFace(res,res.face[i]); + } + }// there was no intersection with the boundary + + if(info.first > 0.0) // closest point - radius. If >0 is outside + vcg::tri::Allocator::DeleteFace(res,res.face[i]); + ++i; + } +} + + +template < typename TriMeshType, class ScalarType, class IndexingType> +void IntersectionBallMesh( IndexingType * grid, TriMeshType & m, const vcg::Sphere3 &ball, TriMeshType & res, + float tol = 0){ + + typename TriMeshType::VertexIterator v0,v1,v2; + typename std::vector::iterator cfi; + typename TriMeshType::FaceIterator fi; + std::vector closestsF,closests; + vcg::Point3 witness; + std::vector > witnesses; + std::vector distances; + std::pair info; + + if(tol == 0) tol = M_PI * ball.Radius() * ball.Radius() / 100000; + + vcg::tri::GetInSphereFace(m,*grid, ball.Center(), ball.Radius(),closestsF,distances,witnesses); + for(cfi =closestsF.begin(); cfi != closestsF.end(); ++cfi) + if(!(**cfi).IsD() && IntersectionSphereTriangle(ball ,(**cfi), witness , &info)) + closests.push_back(&(**cfi)); + + res.Clear(); + SubSet(res,closests); + int i =0; + while(i(ball ,res.face[i], witness , &info) && !allIn){ + if(vcg::DoubleArea(res.face[i]) > tol) + { + // split the face res.face[i] in four, add the four new faces to the mesh and delete the face res.face[i] + v0 = vcg::tri::Allocator::AddVertices(res,3); + fi = vcg::tri::Allocator::AddFaces(res,4); + + v1 = v0; ++v1; + v2 = v1; ++v2; + (*v0).P() = (res.face[i].P(0) + res.face[i].P(1))*0.5; + (*v1).P() = (res.face[i].P(1) + res.face[i].P(2))*0.5; + (*v2).P() = (res.face[i].P(2) + res.face[i].P(0))*0.5; + + (*fi).V(0) = res.face[i].V(0); + (*fi).V(1) = &(*v0); + (*fi).V(2) = &(*v2); + ++fi; + + (*fi).V(0) = res.face[i].V(1); + (*fi).V(1) = &(*v1); + (*fi).V(2) = &(*v0); + ++fi; + + (*fi).V(0) = &(*v0); + (*fi).V(1) = &(*v1); + (*fi).V(2) = &(*v2); + ++fi; + + (*fi).V(0) = &(*v2); + (*fi).V(1) = &(*v1); + (*fi).V(2) = res.face[i].V(2) ; + + vcg::tri::Allocator::DeleteFace(res,res.face[i]); + } + }// there was no intersection with the boundary + + if(info.first > 0.0) // closest point - radius. If >0 is outside + vcg::tri::Allocator::DeleteFace(res,res.face[i]); + ++i; + } +} + +/*@}*/ +} // end namespace vcg +#endif diff --git a/vcg/complex/algorithms/local_optimization.h b/vcg/complex/algorithms/local_optimization.h new file mode 100644 index 00000000..a567b476 --- /dev/null +++ b/vcg/complex/algorithms/local_optimization.h @@ -0,0 +1,398 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/**************************************************************************** + $Log: not supported by cvs2svn $ + Revision 1.20 2007/01/19 09:13:09 cignoni + Added Finalize() method to the interface + + Revision 1.19 2007/01/11 11:48:33 ganovelli + currMetric inizialied to heap.front() (it was heap.back()- wrong) + + Revision 1.18 2006/12/11 14:09:44 ganovelli + added missing initialization of currMetric + + Revision 1.17 2006/06/09 07:28:43 m_di_benedetto + Corrected ClearHeap(): iterator "hi" not decrementable if it was the first of the container. + + Revision 1.16 2005/11/10 15:38:46 cignoni + Added casts to remove warnings + + Revision 1.15 2005/10/02 23:23:52 cignoni + Changed the sense of the < operator for heap: it is reversed according to the stl where highest score elements must float in the heap + Completed TimeBudget Termination condition. + Parametrized the ClearHeap procedure now there is a HeapSimplexRatio param. Removed dirty printf. + + Revision 1.14 2005/04/14 11:34:33 ponchio + *** empty log message *** + + Revision 1.13 2005/01/19 10:33:50 cignoni + Improved ClearHeap management + + Revision 1.12 2004/12/10 01:02:48 cignoni + added an inline and removed loggng + + Revision 1.11 2004/12/03 21:14:39 ponchio + Fixed memory leak... + + Revision 1.10 2004/11/23 10:37:17 cignoni + Added a member with a cached copy of the floating Priority() value inside the HeapElem to optimize operator< in heap updating operator + + Revision 1.9 2004/11/05 10:03:47 fiorin + Added ModifierType::TriEdgeFlipOp + + Revision 1.8 2004/10/25 07:02:56 ganovelli + some inline function, logs on file (precompiler directive) + + Revision 1.7 2004/09/29 17:08:39 ganovelli + changed > to < in heapelem comparison + + Revision 1.6 2004/09/28 09:57:08 cignoni + Better Doxygen docs + + Revision 1.5 2004/09/15 10:40:20 ponchio + typedef LocalOptimization HeapType -> public: + + Revision 1.4 2004/09/08 15:10:59 ganovelli + *** empty log message *** + + Revision 1.3 2004/07/27 09:46:15 cignoni + First working version of the LocalOptimization/Simplification Framework + + Revision 1.1 2004/07/15 12:04:14 ganovelli + minor changes + + Revision 1.2 2004/07/09 10:22:56 ganovelli + working draft + + Revision 1.1 2004/07/08 08:25:15 ganovelli + first draft + +****************************************************************************/ + +#ifndef __VCGLIB_LOCALOPTIMIZATION +#define __VCGLIB_LOCALOPTIMIZATION +#include +#include +#include +#include +#include + +namespace vcg{ + +template +class LocalOptimization; + +enum ModifierType{ TetraEdgeCollapseOp, TriEdgeSwapOp, TriVertexSplitOp, + TriEdgeCollapseOp,TetraEdgeSpliOpt,TetraEdgeSwapOp, TriEdgeFlipOp, + QuadDiagCollapseOp, QuadEdgeCollapseOp}; +/** \addtogroup tetramesh */ +/*@{*/ +/// This abstract class define which functions a local modification to be used in the LocalOptimization. +template +class LocalModification +{ + public: + typedef typename LocalOptimization::HeapType HeapType; + typedef typename MeshType::ScalarType ScalarType; + + + inline LocalModification(){}; + virtual ~LocalModification(){}; + + /// return the type of operation + virtual ModifierType IsOfType() = 0 ; + + /// return true if the data have not changed since it was created + virtual bool IsUpToDate() = 0 ; + + /// return true if no constraint disallow this operation to be performed (ex: change of topology in edge collapses) + virtual bool IsFeasible() = 0; + + /// Compute the priority to be used in the heap + virtual ScalarType ComputePriority()=0; + + /// Return the priority to be used in the heap (implement static priority) + virtual ScalarType Priority() const =0; + + /// Perform the operation and return the variation in the number of simplicies (>0 is refinement, <0 is simplification) + virtual void Execute(MeshType &m)=0; + + /// perform initialization + static void Init(MeshType &m, HeapType&); + + /// An approximation of the size of the heap with respect of the number of simplex + /// of the mesh. When this number is exceeded a clear heap purging is performed. + /// so it is should be reasonably larger than the minimum expected size to avoid too frequent clear heap + /// For example for symmetric edge collapse a 5 is a good guess. + /// while for non symmetric edge collapse a larger number like 9 is a better choice + static float HeapSimplexRatio() {return 6.0f;} ; + + virtual const char *Info(MeshType &) {return 0;} + /// Update the heap as a consequence of this operation + virtual void UpdateHeap(HeapType&)=0; +}; //end class local modification + + +/// LocalOptimization: +/// This class implements the algorihms running on 0-1-2-3-simplicial complex that are based on local modification +/// The local modification can be and edge_collpase, or an edge_swap, a vertex plit...as far as they implement +/// the interface defined in LocalModification. +/// Implementation note: in order to keep the local modification itself indepented by its use in this class, they are not +/// really derived by LocalModification. Instead, a wrapper is done to this purpose (see vcg/complex/tetramesh/decimation/collapse.h) + +template +class LocalOptimization +{ +public: + LocalOptimization(MeshType &mm): m(mm){ ClearTermination();e=0.0;HeapSimplexRatio=5;} + + struct HeapElem; + // scalar type + typedef typename MeshType::ScalarType ScalarType; + // type of the heap + typedef typename std::vector HeapType; + // modification type + typedef LocalModification LocModType; + // modification Pointer type + typedef LocalModification * LocModPtrType; + + + + /// termination conditions + enum LOTermination { + LOnSimplices = 0x01, // test number of simplicies + LOnVertices = 0x02, // test number of verticies + LOnOps = 0x04, // test number of operations + LOMetric = 0x08, // test Metric (error, quality...instance dependent) + LOTime = 0x10 // test how much time is passed since the start + } ; + + int tf; + + int nPerfmormedOps, + nTargetOps, + nTargetSimplices, + nTargetVertices; + + float timeBudget; + int start; + ScalarType currMetric; + ScalarType targetMetric; + + // The ratio between Heap size and the number of simplices in the current mesh + // When this value is exceeded a ClearHeap Start; + + float HeapSimplexRatio; + + void SetTerminationFlag (int v){tf |= v;} + void ClearTerminationFlag (int v){tf &= ~v;} + bool IsTerminationFlag (int v){return ((tf & v)!=0);} + + void SetTargetSimplices (int ts ){nTargetSimplices = ts; SetTerminationFlag(LOnSimplices); } + void SetTargetVertices (int tv ){nTargetVertices = tv; SetTerminationFlag(LOnVertices); } + void SetTargetOperations(int to ){nTargetOps = to; SetTerminationFlag(LOnOps); } + + void SetTargetMetric (ScalarType tm ){targetMetric = tm; SetTerminationFlag(LOMetric); } + void SetTimeBudget (float tb ){timeBudget = tb; SetTerminationFlag(LOTime); } + + void ClearTermination() + { + tf=0; + nTargetSimplices=0; + nTargetOps=0; + targetMetric=0; + timeBudget=0; + nTargetVertices=0; + } + /// the mesh to optimize + MeshType & m; + + + + ///the heap of operations + HeapType h; + + ///the element of the heap + // it is just a wrapper of the pointer to the localMod. + // std heap does not work for + // pointers and we want pointers to have heterogenous heaps. + + struct HeapElem + { + inline HeapElem(){locModPtr = NULL;} + ~HeapElem(){} + + ///pointer to instance of local modifier + LocModPtrType locModPtr; + float pri; + + + inline HeapElem( LocModPtrType _locModPtr) + { + locModPtr = _locModPtr; + pri=float(locModPtr->Priority()); + }; + + /// STL heap has the largest element as the first one. + /// usually we mean priority as an error so we should invert the comparison + inline bool operator <(const HeapElem & h) const + { + return (pri > h.pri); + //return (locModPtr->Priority() < h.locModPtr->Priority()); + } + + bool IsUpToDate() + { + return locModPtr->IsUpToDate(); + } + }; + + + + /// Default distructor + ~LocalOptimization(){ + typename HeapType::iterator i; + for(i = h.begin(); i != h.end(); i++) + delete (*i).locModPtr; + }; + + double e; + + /// main cycle of optimization + bool DoOptimization() + { + start=clock(); + nPerfmormedOps =0; + while( !GoalReached() && !h.empty()) + { + if(h.size()> m.SimplexNumber()*HeapSimplexRatio ) ClearHeap(); + std::pop_heap(h.begin(),h.end()); + LocModPtrType locMod = h.back().locModPtr; + currMetric=h.back().pri; + h.pop_back(); + + if( locMod->IsUpToDate() ) + { + //printf("popped out: %s\n",locMod->Info(m)); + // check if it is feasible + if (locMod->IsFeasible()) + { + nPerfmormedOps++; + locMod->Execute(m); + locMod->UpdateHeap(h); + } + } + //else printf("popped out unfeasible\n"); + delete locMod; + } + return !(h.empty()); + } + +// It removes from the heap all the operations that are no more 'uptodate' +// (e.g. collapses that have some recently modified vertices) +// This function is called from time to time by the doOptimization (e.g. when the heap is larger than fn*3) +void ClearHeap() +{ + typename HeapType::iterator hi; + //int sz=h.size(); + for(hi=h.begin();hi!=h.end();) + { + if(!(*hi).locModPtr->IsUpToDate()) + { + delete (*hi).locModPtr; + *hi=h.back(); + if(&*hi==&h.back()) + { + hi=h.end(); + h.pop_back(); + break; + } + h.pop_back(); + continue; + } + ++hi; + } + //qDebug("\nReduced heap from %7i to %7i (fn %7i) ",sz,h.size(),m.fn); + make_heap(h.begin(),h.end()); +} + + ///initialize for all vertex the temporary mark must call only at the start of decimation + ///by default it takes the first element in the heap and calls Init (static funcion) of that type + ///of local modification. + template void Init() + { + vcg::tri::InitVertexIMark(m); + + // The expected size of heap depends on the type of the local modification we are using.. + HeapSimplexRatio = LocalModificationType::HeapSimplexRatio(); + + LocalModificationType::Init(m,h); + std::make_heap(h.begin(),h.end()); + if(!h.empty()) currMetric=h.front().pri; + } + + + template void Finalize() + { + LocalModificationType::Finalize(m,h); + } + + + /// say if the process is to end or not: the process ends when any of the termination conditions is verified + /// override this function to implemetn other tests + bool GoalReached(){ + assert ( ( ( tf & LOnSimplices )==0) || ( nTargetSimplices!= -1)); + assert ( ( ( tf & LOnVertices )==0) || ( nTargetVertices != -1)); + assert ( ( ( tf & LOnOps )==0) || ( nTargetOps != -1)); + assert ( ( ( tf & LOMetric )==0) || ( targetMetric != -1)); + assert ( ( ( tf & LOTime )==0) || ( timeBudget != -1)); + + if ( IsTerminationFlag(LOnSimplices) && ( m.SimplexNumber()<= nTargetSimplices)) return true; + if ( IsTerminationFlag(LOnVertices) && ( m.VertexNumber() <= nTargetVertices)) return true; + if ( IsTerminationFlag(LOnOps) && (nPerfmormedOps == nTargetOps)) return true; + if ( IsTerminationFlag(LOMetric) && ( currMetric > targetMetric)) return true; + if ( IsTerminationFlag(LOTime) && ( (clock()-start)/(float)CLOCKS_PER_SEC > timeBudget)) return true; + return false; + } + + + +///erase from the heap the operations that are out of date + void ClearHeapOld() + { + typename HeapType::iterator hi; + for(hi=h.begin();hi!=h.end();++hi) + if(!(*hi).locModPtr->IsUpToDate()) + { + *hi=h.back(); + h.pop_back(); + if(hi==h.end()) break; + } + //printf("\nReduced heap from %i to %i",sz,h.size()); + make_heap(h.begin(),h.end()); + } + +};//end class decimation + +}//end namespace +#endif diff --git a/vcg/complex/algorithms/local_optimization/quad_diag_collapse.h b/vcg/complex/algorithms/local_optimization/quad_diag_collapse.h new file mode 100755 index 00000000..fdeba144 --- /dev/null +++ b/vcg/complex/algorithms/local_optimization/quad_diag_collapse.h @@ -0,0 +1,627 @@ +#ifndef QUAD_DIAGONAL_COLLAPSE_H +#define QUAD_DIAGONAL_COLLAPSE_H + +#include +#include +#include + +#include + +#include + +#include + +namespace vcg{ + namespace tri{ + + + /*! + * \brief Generic class for checking feasibility of collapses + * + */ + template + class FeasibilityCheck + { + public: + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename TriMeshType::FaceType TriFaceType; + typedef typename vcg::GridStaticPtr GRID; + typedef typename TriMeshType::CoordType CoordType; + + static bool check_feasible(HEdgePointer hp, CoordType &V1, CoordType &V2, TriMeshType &tm, GRID &grid); + }; + + /*! + * \brief Generic class for weighting collapses + * + */ + template + class OperationWeight + { + public: + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename TriMeshType::FaceType TriFaceType; + typedef typename vcg::GridStaticPtr GRID; + typedef typename TriMeshType::CoordType CoordType; + + static float compute_weight(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid); + }; + + + /*! + * \brief Class that provides methods for checking and weighting collapses using fitmaps + * + */ + template + class FitmapsCollapse : public FeasibilityCheck, public OperationWeight + { + + protected: + + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + + typedef typename TriMeshType::FaceType TriFaceType; + typedef typename vcg::GridStaticPtr GRID; + + typedef typename TriMeshType::CoordType CoordType; + typedef typename TriMeshType::ScalarType ScalarType; + typedef typename TriMeshType::FacePointer FacePointer; + + typedef typename TriMeshType::template PerVertexAttributeHandle Fitmap_attr; + + public: + + /// Coefficient that will be multiplied with the value of the M-Fitmap + static float& Mfit_coeff() + { + static float coeff = 1.5; + return coeff; + } + + /*! Checks if an operation is feasible using M-Fitmap + * + * \param hp Pointer to an halfedge that identifies a diagonal + * \param V1 Coordinates of the first point of the diagonal + * \param V2 Coordinates of the second point of the diagonal + * \param tm Reference mesh + * \param grid Spatial index used for raycasting + * + * \return Value indicating whether diagnoal is collapsible + */ + static bool check_feasible(HEdgePointer hp, CoordType &V1, CoordType &V2, TriMeshType &tm, GRID &grid) + { + float lenght = Distance( V1, V2 ); + + Fitmap_attr M_Fit = tri::Allocator::template GetPerVertexAttribute(tm,"M-Fitmap"); + + CoordType P = (V1+V2)/2; + float fitmap = compute_fitmap(hp, P, tm, grid, M_Fit); + + return lenght <= fitmap/Mfit_coeff(); + } + + /*! Computes the weight of a diagonal using S-Fitmap + * + * \param hp Pointer to an halfedge that identifies a diagonal + * \param P Coordinates of the point on which fitmap will be computed + * \param tm Reference mesh + * \param grid Spatial index used for raycasting + * + * \return Computed weight + */ + static float compute_weight(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid) + { + Fitmap_attr S_Fit = tri::Allocator::template GetPerVertexAttribute(tm,"S-Fitmap"); + + return compute_fitmap(hp, P, tm, grid, S_Fit); + } + + protected: + + /*! + * Performs the computation of a fitmap on a given point + * + * \param hp Pointer to an halfedge that identifies a diagonal + * \param P Coordinates of the point on which fitmap will be computed + * \param tm Reference mesh + * \param grid Spatial index used for raycasting + * \param attr Fitmap type (S or M) + * + * \return Computed value of the fitmap + */ + static float compute_fitmap(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid, Fitmap_attr &attr) + { + CoordType N(0,0,0); + + vector vertices = HalfEdgeTopology::getVertices(hp->HFp()); + + assert(vertices.size() == 4); + + N += vcg::Normal(vertices[0]->cP(), vertices[1]->cP(), vertices[2]->cP()); + N += vcg::Normal(vertices[2]->cP(), vertices[3]->cP(), vertices[0]->cP()); + + N.Normalize(); + + CoordType C(0,0,0); + FacePointer T = getClosestFaceRay(tm, grid, P, N, &C, NULL); + + float result = 1.0; + + if(T) + { + float w0; + float w1; + float w2; + vcg::InterpolationParameters(*T, N, C, w0, w1, w2); + + float s0 = attr[T->V(0)]; + float s1 = attr[T->V(1)]; + float s2 = attr[T->V(2)]; + + result = (w0*s0 + w1*s1 + w2*s2)/(w0+w1+w2); + } + + return result; + } + + + static FacePointer getClosestFaceRay(TriMeshType &m, GRID &grid, CoordType P, CoordType raydir, CoordType* closest, ScalarType* minDist) + { + + ScalarType diag = m.bbox.Diag(); + + raydir.Normalize(); + + Ray3 ray; + + ray.SetOrigin(P); + ScalarType t; + + FacePointer f = 0; + FacePointer fr = 0; + + vector closests; + vector minDists; + vector faces; + + ray.SetDirection(-raydir); + + f = vcg::tri::DoRay(m, grid, ray, diag/4.0, t); + + if (f) + { + closests.push_back(ray.Origin() + ray.Direction()*t); + minDists.push_back(fabs(t)); + faces.push_back(f); + } + + ray.SetDirection(raydir); + + fr = vcg::tri::DoRay(m, grid, ray, diag/4.0, t); + + if (fr) + { + closests.push_back(ray.Origin() + ray.Direction()*t); + minDists.push_back(fabs(t)); + faces.push_back(fr); + } + + if (fr) + if (fr->N()*raydir<0) + fr=0; // discard: inverse normal; + + if (minDists.size() == 0) + { + if (closest) *closest=P; + if (minDist) *minDist=0; + f = 0; + } + else + { + int minI = std::min_element(minDists.begin(),minDists.end()) - minDists.begin(); + if (closest) *closest= closests[minI]; + if (minDist) *minDist= minDists[minI]; + f = faces[minI]; + } + + return f; + + } + + }; + + + + /*! + * \brief Class implementing simplification of quad meshes by diagonal collapses + * + */ + template + class QuadDiagonalCollapseBase: public LocalOptimization::LocModType + { + + protected: + + typedef Pos PosType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename LocalOptimization::HeapElem HeapElem; + typedef typename LocalOptimization::HeapType HeapType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename TriMeshType::FaceType TriFaceType; + typedef typename vcg::GridStaticPtr GRID; + + /// Vertex returned by the collapse + VertexPointer ret; + + /// Global mark for updating + static int& GlobalMark() + { + static int im=0; + return im; + } + + /// Local mark for updating + int localMark; + + /// Priority in the heap + ScalarType _priority; + + /// Set of modified faces + set faces; + + /// Halfedge that identifies the diagonal to collapse + HEdgePointer hp; + + public: + + /// Reference mesh used for smoothing + static TriMeshType* &refMesh() + { + static TriMeshType* m = NULL; + return m; + } + + /// Spatial index for smoothing + static GRID* &grid() + { + static GRID* grid = NULL; + return grid; + } + + /// Number of smoothing iterations to be performed + static unsigned int &smoothing_iterations() + { + static unsigned int iterations = 5; + return iterations; + } + + /// Default Constructor + QuadDiagonalCollapseBase(){} + + /*! + * Constructor + * + * \param he Pointer to an halfedge representing a diagonal + * \param mark Temporal mark of the operation + */ + QuadDiagonalCollapseBase(HEdgePointer he, int mark) + { + localMark = mark; + hp = he; + _priority = ComputePriority(); + } + + ~QuadDiagonalCollapseBase() + { + faces.clear(); + } + + /*! + * Computes priority of the operation as the length of the diagonal + * + * \return Priority + */ + ScalarType ComputePriority() + { + CoordType V1 = hp->HVp()->cP(); + CoordType V2 = hp->HNp()->HNp()->HVp()->cP(); + + _priority = Distance( V1, V2 ); + return _priority; + } + + virtual const char *Info(MeshType &m) + { + static char buf[60]; + sprintf(buf,"(%d - %d) %g\n", hp->HVp()-&m.vert[0], hp->HNp()->HNp()->HVp()-&m.vert[0], -_priority); + return buf; + } + + /*! + * Performs the collapse and the optimizations + * + * \param m Mesh + * + */ + inline void Execute(MeshType &m) + { + + // Collapse the diagonal + ret = HalfEdgeTopology::diagonal_collapse_quad(m,hp->HFp(), hp->HVp()); + + if(ret->VHp()) + { + set tmp = HalfEdgeTopology::getFaces(ret); + + vector incident_faces = HalfEdgeTopology::get_incident_faces(ret,ret->VHp()); + + faces.insert(incident_faces.begin(), incident_faces.end()); + + HalfedgeQuadClean::remove_doublets(m, faces); + + // Optimization by edge rotations + if(!ret->IsD()) + { + vector hedges = HalfEdgeTopology::get_incident_hedges(ret,ret->VHp()); + + HalfedgeQuadClean:: template flip_edges(m, hedges, faces); + } + + faces.insert(tmp.begin(), tmp.end()); + + // Set of all vertices to smooth + set vertices; + + for(typename set::iterator fi = faces.begin(); fi != faces.end(); ++fi) + { + if(*fi) + { + if( !((*fi)->IsD())) + { + vector aux = HalfEdgeTopology::getVertices(*fi); + + vertices.insert(aux.begin(),aux.end()); + } + } + } + + + // Smoothing + for(unsigned int i = 0; i < smoothing_iterations(); i++) + { + for(typename set::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi) + if(!HalfEdgeTopology::isBorderVertex(*vi)) + Smooth::VertexCoordLaplacianReproject(m,*grid(), *refMesh(),*vi); + } + + + // Add all faces modified by smoothing into the set of modified faces + for(typename set::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi) + { + vector tmp_faces = HalfEdgeTopology::get_incident_faces(*vi); + + faces.insert(tmp_faces.begin(), tmp_faces.end()); + } + + } + + + } + + /*! + * Updates the heap of operations. + * For each modified face inserts into the heap two consecutive halfedges representing the two diagonals + * + * \param h_ret Heap to be updated + * + */ + inline void UpdateHeap(HeapType & h_ret) + { + + GlobalMark()++; + + for(typename set::iterator fi = faces.begin(); fi != faces.end(); ++fi) + { + if(*fi) + { + if( !((*fi)->IsD())) + { + (*fi)->IMark() = GlobalMark(); + + HEdgePointer start_he = (*fi)->FHp(); + + h_ret.push_back( HeapElem( new MYTYPE( start_he, GlobalMark() ) ) ); + std::push_heap( h_ret.begin(),h_ret.end() ); + + h_ret.push_back( HeapElem( new MYTYPE( start_he->HNp(), GlobalMark() ) ) ); + std::push_heap( h_ret.begin(),h_ret.end() ); + } + } + } + } + + + ModifierType IsOfType() + { + return QuadDiagCollapseOp; + } + + /*! + * Checks if the operation can be done without generation of degenerate configurations + * + * \return Value indicating whether the operation can be performed + */ + inline bool IsFeasible() + { + FacePointer fp = hp->HFp(); + + if(!fp) + return false; + + if(fp->IsD() || fp->VN() !=4) + return false; + + return ( HalfEdgeTopology::check_diagonal_collapse_quad(hp)); + + } + + /*! + * Checks if the operation is up to date + * + * \return Value indicating whether operation is up to date + */ + inline bool IsUpToDate() + { + FacePointer fp = hp->HFp(); + + if(fp) + return (!hp->IsD() && localMark >= fp->IMark() ); + + return false; + + } + + /*! + * Gets the priority of the operation + * + * \return Value indicating the priority + */ + virtual ScalarType Priority() const + { + return _priority; + } + + /*! + * Initializes a heap with all the possible diagonal collapses of the mesh + * For each face inserts two consecutive halfedges representing the two diagonals + * + * \param m Mesh + * \param h_ret heap to be initialized + * + */ + static void Init(MeshType &m,HeapType &h_ret) + { + // Grid and reference mesh must be initialized + assert(grid()); + assert(refMesh()); + + assert(!HalfedgeQuadClean::has_doublets(m)); + assert(!HalfedgeQuadClean::has_singlets(m)); + + vcg::tri::InitFaceIMark(m); + + h_ret.clear(); + + typename MeshType::FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end();++fi) + { + if(!(*fi).IsD()) + { + + h_ret.push_back( HeapElem(new MYTYPE( (*fi).FHp(), IMark(m)))); + + h_ret.push_back( HeapElem(new MYTYPE( (*fi).FHp()->HNp(), IMark(m)))); + + } + } + + } + + + }; + + /*! + * \brief Class implementing simplification of quad meshes by diagonal collapses + * priority of the operations is weighted with a value computed by class WeightType + * Feasibility is checked with class CheckType + * + */ + template + class QuadDiagonalCollapse: public QuadDiagonalCollapseBase + { + + protected: + + typedef Pos PosType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename LocalOptimization::HeapElem HeapElem; + typedef typename LocalOptimization::HeapType HeapType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + + typedef typename TriMeshType::FaceType TriFaceType; + typedef typename vcg::GridStaticPtr GRID; + + public: + + /// Default constructor + QuadDiagonalCollapse(){} + + /*! + * Constructor + * + * \param he Pointer to an halfedge representing a diagonal + * \param mark Temporal mark of the operation + */ + QuadDiagonalCollapse(HEdgePointer he, int mark) + { + this->localMark = mark; + this->hp = he; + this->_priority = this->ComputePriority(); + } + + /*! + * Computes priority of the operation as length * weight + * + * \return Priority + */ + ScalarType ComputePriority() + { + CoordType V1 = this->hp->HVp()->cP(); + CoordType V2 = this->hp->HNp()->HNp()->HVp()->cP(); + + CoordType P = (V1+V2)/2; + float weight = WeightType::compute_weight(this->hp, P, *(this->refMesh()), *(this->grid())); + + this->_priority = Distance( V1, V2 ) * weight; + return this->_priority; + } + + /*! + * Checks if the operation can be done without generation of degenerate configurations + * + * \return Value indicating whether the operation can be performed + */ + bool IsFeasible() + { + FacePointer fp = this->hp->HFp(); + + if(!fp) + return false; + + if(fp->IsD() || fp->VN() !=4) + return false; + + if(!HalfEdgeTopology::check_diagonal_collapse_quad(this->hp)) + return false; + + CoordType V1 = this->hp->HVp()->cP(); + CoordType V2 = this->hp->HNp()->HNp()->HVp()->cP(); + + return CheckType::check_feasible(this->hp, V1, V2, *(this->refMesh()), *(this->grid())); + + } + + }; + + + }//end namespace tri +}//end namespace vcg + +#endif // QUAD_DIAGONAL_COLLAPSE_H diff --git a/vcg/complex/algorithms/local_optimization/tetra_edge_collapse.h b/vcg/complex/algorithms/local_optimization/tetra_edge_collapse.h new file mode 100644 index 00000000..abc0e1de --- /dev/null +++ b/vcg/complex/algorithms/local_optimization/tetra_edge_collapse.h @@ -0,0 +1,288 @@ +/**************************************************************************** +* 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 +****************************************************************************/ + +#ifndef __VCG_DECIMATION_COLLAPSE +#define __VCG_DECIMATION_COLLAPSE + +#include +#include +#include +#include + + +struct FAIL{ + static int VOL(){static int vol=0; return vol++;} + static int LKF(){static int lkf=0; return lkf++;} + static int LKE(){static int lke=0; return lke++;} + static int LKV(){static int lkv=0; return lkv++;} + static int OFD(){static int ofd=0; return ofd++;} + static int BOR(){static int bor=0; return bor++;} + +}; + +namespace vcg{ +namespace tetra{ + +/** \addtogroup tetramesh */ +/*@{*/ +/// This Class is specialization of LocalModification for the edge collapse +/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine. +/// Note that it has knowledge of the heap of the class LocalOptimization because +/// it is responsible of updating it after a collapse has been performed + +template +class TetraEdgeCollapse: public LocalOptimization::LocModType +{ + + /// The tetrahedral mesh type + //typedef typename TETRA_MESH_TYPE TETRA_MESH_TYPE; + /// The tetrahedron type + typedef typename TETRA_MESH_TYPE::TetraType TetraType; + /// The vertex type + typedef typename TetraType::VertexType VertexType; + /// The coordinate type + typedef typename TetraType::VertexType::CoordType CoordType; + /// The scalar type + typedef typename TETRA_MESH_TYPE::VertexType::ScalarType ScalarType; + /////the base type class + //typedef typename vcg::tri::LocalModification LocalMod; + /// The HEdgePos type + typedef Pos PosType; + /// The HEdgePos Loop type + typedef PosLoop PosLType; + /// definition of the heap element + typedef typename LocalOptimization::HeapElem HeapElem; +private: + +///the new point that substitute the edge +Point3 _NewPoint; +///the pointer to edge collapser method +vcg::tetra::EdgeCollapse _EC; +///mark for up_dating +static int& _Imark(){ static int im=0; return im;} +///the pos of collapse +PosType pos; +///pointer to vertex that remain +VertexType *vrem; +/// priority in the heap +ScalarType _priority; + +public: +/// Default Constructor + TetraEdgeCollapse() + {} + +///Constructor with postype + TetraEdgeCollapse(PosType p,int mark) + { + _Imark() = mark; + pos=p; + _priority = _AspectRatioMedia(p); + } + + ~TetraEdgeCollapse() + {} + +private: + +///Return the aspect Ratio media of the tetrahedrons +///that share the adge to collapse +ScalarType _AspectRatioMedia(PosType p) +{ + PosLType posl=PosLType(p.T(),p.F(),p.E(),p.V()); + posl.Reset(); + int num=0; + ScalarType ratio_media=0.f; + while(!posl.LoopEnd()) + { + ratio_media+=posl.T()->AspectRatio(); + posl.NextT(); + num++; + } + ratio_media=ratio_media/num; + return (ratio_media); +} + + +///Modify pos and alfa to obtain the collapse that minimize the error +ScalarType _VolumePreservingError(PosType &pos,CoordType &new_point,int nsteps) +{ + VertexType *ve0=(pos.T()->V(Tetra::VofE(pos.E(),0))); + VertexType *ve1=(pos.T()->V(Tetra::VofE(pos.E(),1))); + bool ext_v0=ve0->IsB(); + bool ext_v1=ve1->IsB(); + + ScalarType best_error=0.f; + if ((ext_v0)&&(!ext_v1)) + new_point=ve0->P(); + else + if ((!ext_v0)&&(ext_v1)) + new_point=ve1->P(); + else + if ((!ext_v0)&&(!ext_v1)) + {/*CoordType g; + g.SetZero(); + g+=ve0->cP(); + g+=ve1->cP(); + g/=2;*/ + new_point=(ve0->cP()+ve1->cP())/2.f; + } + else + if ((ext_v0)&&(ext_v1))//both are external vertex + { + ScalarType step=1.f/(nsteps-1); + ScalarType Vol_Original=_EC.VolumeOriginal(); + for (int i=0;icP()*alfatemp; + //g+=ve1->cP()*(1-alfatemp); + //CoordType newPTemp=g; + CoordType newPTemp=(ve0->cP()*alfatemp) +(ve1->cP()*(1.f-alfatemp)); + //the error is the absolute value of difference of volumes + ScalarType error=fabs(Vol_Original-_EC.VolumeSimulateCollapse(pos,newPTemp)); + if(error %i %f\n", pos.()-&m.vert[0], pos.VFlip()-&m.vert[0],_priority); + return buf; + } + + ScalarType ComputePriority() + { + return (_priority = _AspectRatioMedia(this->pos)); + } + + ScalarType ComputeError() + { + vrem=(pos.T()->V(Tetra::VofE(pos.E(),0))); + return (_VolumePreservingError(pos,_NewPoint,5));// magic number....parametrize! + } + + void Execute(TETRA_MESH_TYPE &tm) + { + // _EC.FindSets(pos); + assert(!vrem->IsD()); + int del=_EC.DoCollapse(pos,_NewPoint); + tm.tn-=del; + tm.vn-=1; + } + + void UpdateHeap(typename LocalOptimization::HeapType & h_ret) + { + assert(!vrem->IsD()); + _Imark()++; + VTIterator VTi(vrem->VTb(),vrem->VTi()); + while (!VTi.End()) + { + VTi.Vt()->ComputeVolume(); + for (int j=0;j<6;j++) + { + vcg::tetra::Pos p=Pos(VTi.Vt(),Tetra::FofE(j,0),j,Tetra::VofE(j,0)); + assert(!p.T()->V(p.V())->IsD()); + assert(!p.T()->IsD()); + h_ret.push_back(HeapElem(new TetraEdgeCollapse(p,_Imark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + // update the mark of the vertices + VTi.Vt()->V(Tetra::VofE(j,0))->IMark() = _Imark(); + } + ++VTi; + } + } + + /// return the type of operation + + ModifierType IsOfType(){ return TetraEdgeCollapseOp;} + + bool IsFeasible(){ + vcg::tetra::EdgeCollapse::Reset(); + _EC.FindSets(pos); + ComputeError(); + return(_EC.CheckPreconditions(pos,_NewPoint)); + } + + bool IsUpToDate(){ + if (!pos.T()->IsD()) + { + VertexType *v0=pos.T()->V(Tetra::VofE(pos.E(),0)); + VertexType *v1=pos.T()->V(Tetra::VofE(pos.E(),1)); + assert(!v0->IsD()); + assert(!v1->IsD()); + if(! (( (!v0->IsD()) && (!v1->IsD())) && + _Imark()>=v0->IMark() && + _Imark()>=v1->IMark())) + { + FAIL::OFD(); + return false; + } + else + return true; + } + else + return false; + } + + virtual ScalarType Priority() const { + return _priority; + } + + /// perform initialization + static void Init(TETRA_MESH_TYPE &m,typename LocalOptimization::HeapType& h_ret){ + h_ret.clear(); + typename TETRA_MESH_TYPE::TetraIterator ti; + for(ti = m.tetra.begin(); ti != m.tetra.end();++ti) + if(!(*ti).IsD()){ + (*ti).ComputeVolume(); + for (int j=0;j<6;j++) + { + PosType p=PosType(&*ti,Tetra::FofE(j,0),j,Tetra::VofE(j,0)); + assert(!p.T()->V(p.V())->IsD()); + assert(!p.T()->IsD()); + h_ret.push_back(HeapElem(new TetraEdgeCollapse(p,m.IMark))); + } + } + } + +}; +}//end namespace tetra +}//end namespace vcg +#endif diff --git a/vcg/complex/algorithms/local_optimization/tri_edge_collapse.h b/vcg/complex/algorithms/local_optimization/tri_edge_collapse.h new file mode 100644 index 00000000..1f7ab86c --- /dev/null +++ b/vcg/complex/algorithms/local_optimization/tri_edge_collapse.h @@ -0,0 +1,290 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/**************************************************************************** + $Log: not supported by cvs2svn $ + Revision 1.19 2006/10/15 07:31:21 cignoni + typenames and qualifiers for gcc compliance + + Revision 1.18 2006/10/09 20:09:40 cignoni + Changed some access to VertexFaceIterator to reflect the shorter new operators. + + Revision 1.17 2005/10/12 10:44:01 cignoni + Now creation of new edge use Ordered() constructor to comply the fact that the basic collapse is simmetric. + + Revision 1.16 2005/01/19 10:35:28 cignoni + Better management of symmetric/asymmetric edge collapses + + Revision 1.15 2004/12/10 01:03:53 cignoni + better comments and removed logging + + Revision 1.14 2004/11/23 10:34:23 cignoni + passed parameters by reference in many funcs and gcc cleaning + + Revision 1.13 2004/10/25 16:28:32 ganovelli + pos to edge + + Revision 1.12 2004/09/15 11:16:02 ganovelli + changed P() to cP() + + Revision 1.11 2004/09/09 13:23:01 ponchio + Header guards typo + + Revision 1.10 2004/09/09 13:01:12 ponchio + Linux compatible path in #include + + Revision 1.9 2004/09/08 15:13:29 ganovelli + changes for gc + + Revision 1.8 2004/09/08 14:33:31 ganovelli + *** empty log message *** + + +****************************************************************************/ + +#ifndef __VCG_DECIMATION_TRICOLLAPSE +#define __VCG_DECIMATION_TRICOLLAPSE + +#include +#include +#include + + +namespace vcg{ +namespace tri{ + +/** \addtogroup trimesh */ +/*@{*/ +/// This Class is specialization of LocalModification for the edge collapse. +/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine. +/// Note that it has knowledge of the heap of the class LocalOptimization because +/// it is responsible of updating it after a collapse has been performed; +/// This is the base class of all the specialized collapse classes like for example Quadric Edge Collapse. +/// Each derived class + +template +class TriEdgeCollapse: public LocalOptimization::LocModType , public EdgeCollapse +{ +public: + /// static data to gather statistical information about the reasons of collapse failures + class FailStat { + public: + static int &Volume() {static int vol=0; return vol;} + static int &LinkConditionFace(){static int lkf=0; return lkf;} + static int &LinkConditionEdge(){static int lke=0; return lke;} + static int &LinkConditionVert(){static int lkv=0; return lkv;} + static int &OutOfDate() {static int ofd=0; return ofd;} + static int &Border() {static int bor=0; return bor;} + static void Init() + { + Volume() =0; + LinkConditionFace()=0; + LinkConditionEdge()=0; + LinkConditionVert()=0; + OutOfDate() =0; + Border() =0; + } +}; +protected: + typedef typename TriMeshType::FaceType FaceType; + typedef typename TriMeshType::FaceType::VertexType VertexType; + typedef typename VertexType::EdgeType EdgeType; + typedef typename FaceType::VertexType::CoordType CoordType; + typedef typename TriMeshType::VertexType::ScalarType ScalarType; + typedef typename LocalOptimization::HeapElem HeapElem; + typedef typename LocalOptimization::HeapType HeapType; + + TriMeshType *mt; + ///the pair to collapse + EdgeType pos; + + ///mark for up_dating + static int& GlobalMark(){ static int im=0; return im;} + + ///mark for up_dating + int localMark; + + /// priority in the heap + ScalarType _priority; + + public: + /// Default Constructor + inline TriEdgeCollapse() + {} + ///Constructor with postype + inline TriEdgeCollapse(const EdgeType &p, int mark) + { + localMark = mark; + pos=p; + _priority = ComputePriority(); + } + + ~TriEdgeCollapse() + {} + +private: + + +public: + + + inline ScalarType ComputePriority() + { + _priority = Distance(pos.V(0)->cP(),pos.V(1)->cP()); + return _priority; + } + + virtual const char *Info(TriMeshType &m) { + mt = &m; + static char buf[60]; + sprintf(buf,"%i -> %i %g\n", int(pos.V(0)-&m.vert[0]), int(pos.V(1)-&m.vert[0]),-_priority); + return buf; + } + + inline void Execute(TriMeshType &m) + { + CoordType MidPoint=(pos.V(0)->P()+pos.V(1)->P())/2.0; + DoCollapse(m, pos, MidPoint); + } + + static bool IsSymmetric() { return true;} + + + // This function is called after an action to re-add in the heap elements whose priority could have been changed. + // in the plain case we just put again in the heap all the edges around the vertex resulting from the previous collapse: v[1]. + // if the collapse is not symmetric you should add also backward edges (because v0->v1 collapse could be different from v1->v0) + + inline void UpdateHeap(HeapType & h_ret) + { + GlobalMark()++; int nn=0; + VertexType *v[2]; + v[0]= pos.V(0);v[1]=pos.V(1); + v[1]->IMark() = GlobalMark(); + + // First loop around the remaining vertex to unmark visited flags + vcg::face::VFIterator vfi(v[1]); + while (!vfi.End()){ + vfi.V1()->ClearV(); + vfi.V2()->ClearV(); + ++vfi; + } + + // Second Loop: add all the outgoing edges around v[1] + // for each face add the two edges outgoing from v[1] and not visited. + vfi = face::VFIterator(v[1]); + while (!vfi.End()) + { + assert(!vfi.F()->IsD()); + for (int j=0;j<3;j++) + { + if( !(vfi.V1()->IsV()) && (vfi.V1()->IsRW())) + { + vfi.V1()->SetV(); + h_ret.push_back(HeapElem(new MYTYPE(EdgeType( vfi.V(),vfi.V1() ),GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + if(! this->IsSymmetric()){ + h_ret.push_back(HeapElem(new MYTYPE(EdgeType( vfi.V1(),vfi.V()),GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + } + } + if( !(vfi.V2()->IsV()) && (vfi.V2()->IsRW())) + { + vfi.V2()->SetV(); + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.F()->V(vfi.I()),vfi.F()->V2(vfi.I())),GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + if(! this->IsSymmetric()){ + h_ret.push_back(HeapElem(new MYTYPE(EdgeType (vfi.F()->V1(vfi.I()),vfi.F()->V(vfi.I())),GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + } + } + // if(vfi.V1()->IsRW() && vfi.V2()->IsRW() ) +// { +// h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V2()),this->GlobalMark()))); +// std::push_heap(h_ret.begin(),h_ret.end()); +// if(IsSymmetric()){ +// h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V1()), this->GlobalMark()))); +// std::push_heap(h_ret.begin(),h_ret.end()); +// } +// } + + } + ++vfi;nn++; + } +// printf("ADDED %d\n",nn); + } + + ModifierType IsOfType(){ return TriEdgeCollapseOp;} + + inline bool IsFeasible(){ + return LinkConditions(pos); + } + + inline bool IsUpToDate(){ + // if(pos.V(1)->IsD()) { + // ++FailStat::OutOfDate(); + // return false; + //} + // + // if(pos.V(1)->IsD()) { + // ++FailStat::OutOfDate(); + // return false; + //} + + VertexType *v0=pos.V(0); + VertexType *v1=pos.V(1); + + //if(! (( (!v0->IsD()) && (!v1->IsD())) && + // localMark>=v0->IMark() && + // localMark>=v1->IMark())) + if( v0->IsD() || v1->IsD() || + localMark < v0->IMark() || + localMark < v1->IMark() ) + { + ++FailStat::OutOfDate(); + return false; + } + return true; + } + + virtual ScalarType Priority() const { + return _priority; + } + + static void Init(TriMeshType&m,HeapType&h_ret){ + h_ret.clear(); + typename TriMeshType::FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end();++fi) + if(!(*fi).IsD()){ + for (int j=0;j<3;j++) + { + EdgeType p=EdgeType::OrderedEdge((*fi).V(j),(*fi).V((j+1)%3)); + h_ret.push_back(HeapElem(new MYTYPE(p, IMark(m)))); + //printf("Inserting in heap coll %3i ->%3i %f\n",p.V()-&m.vert[0],p.VFlip()-&m.vert[0],h_ret.back().locModPtr->Priority()); + } + } + } + +}; +}//end namespace tri +}//end namespace vcg + +#endif diff --git a/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h b/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h new file mode 100644 index 00000000..58afcaa3 --- /dev/null +++ b/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h @@ -0,0 +1,636 @@ +/**************************************************************************** +* 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 $ +Revision 1.14 2007/03/22 11:07:16 cignoni +Solved an issue related to different casting double-float between gcc 3 and gcc 4 + +Revision 1.13 2007/02/25 09:20:10 cignoni +Added Rad to the NormalThr Option and removed a bug in multiple exectuion of non optimal simplification (missing an isD check) + +Revision 1.12 2007/01/19 09:13:14 cignoni +Added Finalize() method to the interface, corrected minor bugs on border preserving and postsimplification cleanup +Avoided double make_heap (it is done only in the local_optimization init) + +Revision 1.11 2006/10/15 07:31:21 cignoni +typenames and qualifiers for gcc compliance + +Revision 1.10 2006/10/09 20:12:55 cignoni +Heavyly restructured for meshlab inclusion. Now the access to the quadric elements are mediated by a static helper class. + +Revision 1.9 2006/10/07 17:20:25 cignoni +Updated to the new style face->Normal() becomes Normal(face) + +Revision 1.8 2005/10/02 23:19:36 cignoni +Changed the sign of the priority of a collapse. Now it is its the error as it should (and not -error) + +Revision 1.7 2005/04/14 11:35:07 ponchio +*** empty log message *** + +Revision 1.6 2005/01/19 10:35:28 cignoni +Better management of symmetric/asymmetric edge collapses + +Revision 1.5 2004/12/10 01:07:15 cignoni +Moved param classes inside; added support for optimal placement and symmetric; added update heap also here (not only in the base class) + +Revision 1.4 2004/11/23 10:34:23 cignoni +passed parameters by reference in many funcs and gcc cleaning + +Revision 1.3 2004/10/25 07:07:56 ganovelli +A vcg.::Pos was used to implement the collapse type. CHanged +to vcg::Edge + +Revision 1.2 2004/09/29 17:08:16 ganovelli +corrected error in -error (see localoptimization) + + +****************************************************************************/ + + + + +#ifndef __VCG_TRIMESHCOLLAPSE_QUADRIC__ +#define __VCG_TRIMESHCOLLAPSE_QUADRIC__ + +#include +#include +#include +#include +#include +#include +#include + + +namespace vcg{ +namespace tri{ + + + + +/** + This class describe Quadric based collapse operation. + + Requirements: + + Vertex + must have: + incremental mark + VF topology + + must have: + members + + QuadricType Qd(); + + ScalarType W() const; + A per-vertex Weight that can be used in simplification + lower weight means that error is lowered, + standard: return W==1.0 + + void Merge(MESH_TYPE::vertex_type const & v); + Merges the attributes of the current vertex with the ones of v + (e.g. its weight with the one of the given vertex, the color ect). + Standard: void function; + + OtherWise the class should be templated with a static helper class that helps to retrieve these functions. + If the vertex class exposes these functions a default static helper class is provided. + +*/ + //**Helper CLASSES**// + template + class QInfoStandard + { + public: + QInfoStandard(){}; + static void Init(){}; + static math::Quadric &Qd(VERTEX_TYPE &v) {return v.Qd();} + static math::Quadric &Qd(VERTEX_TYPE *v) {return v->Qd();} + static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE */*v*/) {return 1.0;}; + static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE &/*v*/) {return 1.0;}; + static void Merge(VERTEX_TYPE & v_dest, VERTEX_TYPE const & v_del){}; + }; + + +class TriEdgeCollapseQuadricParameter +{ +public: + double QualityThr; // all + double BoundaryWeight; + double NormalThrRad; + double CosineThr; + double QuadricEpsilon; + double ScaleFactor; + bool UseArea; + bool UseVertexWeight; + bool NormalCheck; + bool QualityCheck; + bool OptimalPlacement; + bool MemoryLess; + bool QualityWeight; + bool ScaleIndependent; + //*********************** + bool QualityQuadric; // During the initialization manage all the edges as border edges adding a set of additional quadrics that are useful mostly for keeping face aspect ratio good. + bool PreserveTopology; + bool PreserveBoundary; + bool MarkComplex; + bool FastPreserveBoundary; + bool SafeHeapUpdate; +}; + + +template > +class TriEdgeCollapseQuadric: public TriEdgeCollapse< TriMeshType, MYTYPE> +{ +public: + typedef typename vcg::tri::TriEdgeCollapse< TriMeshType, MYTYPE > TEC; + typedef typename TEC::EdgeType EdgeType; + typedef typename TriEdgeCollapse::HeapType HeapType; + typedef typename TriEdgeCollapse::HeapElem HeapElem; + typedef typename TriMeshType::CoordType CoordType; + typedef typename TriMeshType::ScalarType ScalarType; + typedef math::Quadric< double > QuadricType; + typedef typename TriMeshType::FaceType FaceType; + typedef typename TriMeshType::VertexType VertexType; + typedef TriEdgeCollapseQuadricParameter QParameter; + typedef HelperType QH; + + static QParameter & Params(){ + static QParameter p; + return p; + } + enum Hint { + HNHasFFTopology = 0x0001, // La mesh arriva con la topologia ff gia'fatta + HNHasVFTopology = 0x0002, // La mesh arriva con la topologia bf gia'fatta + HNHasBorderFlag = 0x0004 // La mesh arriva con i flag di bordo gia' settati + }; + + static int & Hnt(){static int hnt; return hnt;} // the current hints + + static void SetHint(Hint hn) { Hnt() |= hn; } + static void ClearHint(Hint hn) { Hnt()&=(~hn);} + static bool IsSetHint(Hint hn) { return (Hnt()&hn)!=0; } + + // puntatori ai vertici che sono stati messi non-w per preservare il boundary + static std::vector & WV(){ + static std::vector _WV; return _WV; + }; + + inline TriEdgeCollapseQuadric(const EdgeType &p, int i) + //:TEC(p,i){} + { + this->localMark = i; + this->pos=p; + this->_priority = ComputePriority(); + } + + + inline bool IsFeasible(){ + bool res = ( !Params().PreserveTopology || LinkConditions(this->pos) ); + if(!res) ++( TriEdgeCollapse< TriMeshType,MYTYPE>::FailStat::LinkConditionEdge() ); + return res; + } + + void Execute(TriMeshType &m) + { CoordType newPos; + if(Params().OptimalPlacement) newPos= static_cast(this)->ComputeMinimal(); + else newPos=this->pos.V(1)->P(); + //this->pos.V(1)->Qd()+=this->pos.V(0)->Qd(); + QH::Qd(this->pos.V(1))+=QH::Qd(this->pos.V(0)); + //int FaceDel= + DoCollapse(m, this->pos, newPos); // v0 is deleted and v1 take the new position + //m.fn-=FaceDel; + //--m.vn; + } + + + + // Final Clean up after the end of the simplification process + static void Finalize(TriMeshType &m, HeapType& /*h_ret*/) + { + // if the mesh was prepared with precomputed borderflags + // correctly set them again. + if(IsSetHint(HNHasBorderFlag) ) + vcg::tri::UpdateFlags::FaceBorderFromVF(m); + + // If we had the boundary preservation we should clean up the writable flags + if(Params().FastPreserveBoundary) + { + typename TriMeshType::VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) (*vi).SetW(); + } + if(Params().PreserveBoundary) + { + typename std::vector::iterator wvi; + for(wvi=WV().begin();wvi!=WV().end();++wvi) + if(!(*wvi)->IsD()) (*wvi)->SetW(); + } + } + + static void Init(TriMeshType &m,HeapType&h_ret){ + + typename TriMeshType::VertexIterator vi; + typename TriMeshType::FaceIterator pf; + + EdgeType av0,av1,av01; + Params().CosineThr=cos(Params().NormalThrRad); + + if(!IsSetHint(HNHasVFTopology) ) vcg::tri::UpdateTopology::VertexFace(m); + + if(Params().MarkComplex) { + vcg::tri::UpdateTopology::FaceFace(m); + vcg::tri::UpdateFlags::FaceBorderFromFF(m); + vcg::tri::UpdateTopology::VertexFace(m); + } // e' un po' piu' lenta ma marca i vertici complex + else + if(!IsSetHint(HNHasBorderFlag) ) + vcg::tri::UpdateFlags::FaceBorderFromVF(m); + + if(Params().FastPreserveBoundary) + { + for(pf=m.face.begin();pf!=m.face.end();++pf) + if( !(*pf).IsD() && (*pf).IsW() ) + for(int j=0;j<3;++j) + if((*pf).IsB(j)) + { + (*pf).V(j)->ClearW(); + (*pf).V1(j)->ClearW(); + } + } + + if(Params().PreserveBoundary) + { + WV().clear(); + for(pf=m.face.begin();pf!=m.face.end();++pf) + if( !(*pf).IsD() && (*pf).IsW() ) + for(int j=0;j<3;++j) + if((*pf).IsB(j)) + { + if((*pf).V(j)->IsW()) {(*pf).V(j)->ClearW(); WV().push_back((*pf).V(j));} + if((*pf).V1(j)->IsW()) {(*pf).V1(j)->ClearW();WV().push_back((*pf).V1(j));} + } + } + + InitQuadric(m); + + // Initialize the heap with all the possible collapses + if(IsSymmetric()) + { // if the collapse is symmetric (e.g. u->v == v->u) + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && (*vi).IsRW()) + { + vcg::face::VFIterator x; + for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x){ + x.V1()->ClearV(); + x.V2()->ClearV(); + } + for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++x ) + { + assert(x.F()->V(x.I())==&(*vi)); + if((x.V0()IsRW() && !x.V1()->IsV()){ + x.V1()->SetV(); + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(x.V0(),x.V1()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark() ))); + } + if((x.V0()IsRW()&& !x.V2()->IsV()){ + x.V2()->SetV(); + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(x.V0(),x.V2()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark() ))); + } + } + } + } + else + { // if the collapse is A-symmetric (e.g. u->v != v->u) + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && (*vi).IsRW()) + { + vcg::face::VFIterator x; + UnMarkAll(m); + for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x) + { + assert(x.F()->V(x.I())==&(*vi)); + if(x.V()->IsRW() && x.V1()->IsRW() && !IsMarked(m,x.F()->V1(x.I()))){ + h_ret.push_back( HeapElem( new MYTYPE( EdgeType (x.V(),x.V1()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark()))); + } + if(x.V()->IsRW() && x.V2()->IsRW() && !IsMarked(m,x.F()->V2(x.I()))){ + h_ret.push_back( HeapElem( new MYTYPE( EdgeType (x.V(),x.V2()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark()))); + } + } + } + } +} + static float HeapSimplexRatio() {return IsSymmetric()?5.0f:9.0f;} + static bool IsSymmetric() {return Params().OptimalPlacement;} + static bool IsVertexStable() {return !Params().OptimalPlacement;} + static void SetDefaultParams(){ + Params().UseArea=true; + Params().UseVertexWeight=false; + Params().NormalCheck=false; + Params().NormalThrRad=M_PI/2; + Params().QualityCheck=true; + Params().QualityThr=.1; + Params().BoundaryWeight=.5; + Params().QualityQuadric=false; + Params().OptimalPlacement=true; + Params().ScaleIndependent=true; + Params().QualityWeight=false; + Params().QuadricEpsilon =1e-15; + Params().ScaleFactor=1.0; + + Params().PreserveTopology = false; + } + +///* +// Funzione principale di valutazione dell'errore del collasso. +// In pratica simula il collasso vero e proprio. +// +// Da ottimizzare il ciclo sulle normali (deve sparire on e si deve usare per face normals) +//*/ + ScalarType ComputePriority() { + ScalarType error; + typename vcg::face::VFIterator x; + std::vector on; // original normals + typename TriMeshType::VertexType * v[2]; + v[0] = this->pos.V(0); + v[1] = this->pos.V(1); + + if(Params().NormalCheck){ // Compute maximal normal variation + // store the old normals for non-collapsed face in v0 + for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(); x.F()!=0; ++x ) // for all faces in v0 + if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1 + on.push_back(NormalizedNormal(*x.F())); + // store the old normals for non-collapsed face in v1 + for(x.F() = v[1]->VFp(), x.I() = v[1]->VFi(); x.F()!=0; ++x ) // for all faces in v1 + if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0 + on.push_back(NormalizedNormal(*x.F())); + } + + //// Move the two vertexe into new position (storing the old ones) + CoordType OldPos0=v[0]->P(); + CoordType OldPos1=v[1]->P(); + if(Params().OptimalPlacement) { v[0]->P() = ComputeMinimal(); v[1]->P()=v[0]->P();} + else v[0]->P() = v[1]->P(); + + //// Rescan faces and compute quality and difference between normals + int i; + double ndiff,MinCos = 1e100; // minimo coseno di variazione di una normale della faccia + // (e.g. max angle) Mincos varia da 1 (normali coincidenti) a + // -1 (normali opposte); + double qt, MinQual = 1e100; + CoordType nn; + for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v0 + if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1 + { + if(Params().NormalCheck){ + nn=NormalizedNormal(*x.F()); + ndiff=nn.dot(on[i++]); + if(ndiffVFp(), x.I() = v[1]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v1 + if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0 + { + if(Params().NormalCheck){ + nn=NormalizedNormal(*x.F()); + ndiff=nn.dot(on[i++]); + if(ndiffP()); + double QuadErr = Params().ScaleFactor*qq.Apply(tpd); + + // All collapses involving triangles with quality larger than has no penalty; + if(MinQual>Params().QualityThr) MinQual=Params().QualityThr; + + if(Params().NormalCheck){ + // All collapses where the normal vary less than (e.g. more than CosineThr) + // have no penalty + if(MinCos>Params().CosineThr) MinCos=Params().CosineThr; + MinCos=(MinCos+1)/2.0; // Now it is in the range 0..1 with 0 very dangerous! + } + + if(QuadErrP()=OldPos0; + v[1]->P()=OldPos1; + this->_priority = error; + return this->_priority; + } + +// +//static double MaxError() {return 1e100;} +// + inline void UpdateHeap(HeapType & h_ret) + { + this->GlobalMark()++; + VertexType *v[2]; + v[0]= this->pos.V(0); + v[1]= this->pos.V(1); + v[1]->IMark() = this->GlobalMark(); + + // First loop around the remaining vertex to unmark visited flags + vcg::face::VFIterator vfi(v[1]); + while (!vfi.End()){ + vfi.V1()->ClearV(); + vfi.V2()->ClearV(); + ++vfi; + } + + // Second Loop + vfi = face::VFIterator(v[1]); + while (!vfi.End()) + { + assert(!vfi.F()->IsD()); + for (int j=0;j<3;j++) + { + if( !(vfi.V1()->IsV()) && vfi.V1()->IsRW()) + { + vfi.V1()->SetV(); + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V0(),vfi.V1()), this->GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + if(!IsSymmetric()){ + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V0()), this->GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + } + } + if( !(vfi.V2()->IsV()) && vfi.V2()->IsRW()) + { + vfi.V2()->SetV(); + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V0(),vfi.V2()),this->GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + if(!IsSymmetric()){ + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V0()), this->GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + } + } + if(Params().SafeHeapUpdate && vfi.V1()->IsRW() && vfi.V2()->IsRW() ) + { + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V2()),this->GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + if(!IsSymmetric()){ + h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V1()), this->GlobalMark()))); + std::push_heap(h_ret.begin(),h_ret.end()); + } + } + } + ++vfi; + } + + } + +static void InitQuadric(TriMeshType &m) +{ + typename TriMeshType::FaceIterator pf; + typename TriMeshType::VertexIterator pv; + int j; + QH::Init(); + // m.ClearFlags(); + for(pv=m.vert.begin();pv!=m.vert.end();++pv) // Azzero le quadriche + if( ! (*pv).IsD() && (*pv).IsW()) + QH::Qd(*pv).SetZero(); + + + for(pf=m.face.begin();pf!=m.face.end();++pf) + if( !(*pf).IsD() && (*pf).IsR() ) + if((*pf).V(0)->IsR() &&(*pf).V(1)->IsR() &&(*pf).V(2)->IsR()) + { + QuadricType q; + Plane3 p; + // Calcolo piano + p.SetDirection( ( (*pf).V(1)->cP() - (*pf).V(0)->cP() ) ^ ( (*pf).V(2)->cP() - (*pf).V(0)->cP() )); + // Se normalizzo non dipende dall'area + + if(!Params().UseArea) + p.Normalize(); + + p.SetOffset( p.Direction().dot((*pf).V(0)->cP())); + + // Calcolo quadrica delle facce + q.ByPlane(p); + + for(j=0;j<3;++j) + if( (*pf).V(j)->IsW() ) + { + if(Params().QualityWeight) + q*=(*pf).V(j)->Q(); + QH::Qd((*pf).V(j)) += q; // Sommo la quadrica ai vertici + } + + for(j=0;j<3;++j) + if( (*pf).IsB(j) || Params().QualityQuadric ) // Bordo! + { + Plane3 pb; // Piano di bordo + + // Calcolo la normale al piano di bordo e la sua distanza + // Nota che la lunghezza dell'edge DEVE essere Normalizzata + // poiche' la pesatura in funzione dell'area e'gia fatta in p.Direction() + // Senza la normalize il bordo e' pesato in funzione della grandezza della mesh (mesh grandi non decimano sul bordo) + pb.SetDirection(p.Direction() ^ ( (*pf).V1(j)->cP() - (*pf).V(j)->cP() ).normalized()); + if( (*pf).IsB(j) ) pb.SetDirection(pb.Direction()* (ScalarType)Params().BoundaryWeight); // amplify border planes + else pb.SetDirection(pb.Direction()* (ScalarType)(Params().BoundaryWeight/100.0)); // and consider much less quadric for quality + pb.SetOffset(pb.Direction().dot((*pf).V(j)->cP())); + q.ByPlane(pb); + + if( (*pf).V (j)->IsW() ) QH::Qd((*pf).V (j)) += q; // Sommo le quadriche + if( (*pf).V1(j)->IsW() ) QH::Qd((*pf).V1(j)) += q; + } + } + + if(Params().ScaleIndependent) + { + vcg::tri::UpdateBounding::Box(m); + //Make all quadric independent from mesh size + Params().ScaleFactor = 1e8*pow(1.0/m.bbox.Diag(),6); // scaling factor + //Params().ScaleFactor *=Params().ScaleFactor ; + //Params().ScaleFactor *=Params().ScaleFactor ; + //printf("Scale factor =%f\n",Params().ScaleFactor ); + //printf("bb (%5.2f %5.2f %5.2f)-(%5.2f %5.2f %5.2f) Diag %f\n",m.bbox.min[0],m.bbox.min[1],m.bbox.min[2],m.bbox.max[0],m.bbox.max[1],m.bbox.max[2],m.bbox.Diag()); + } +} + + + +// +// +// +// +// +// +//static void InitMesh(MESH_TYPE &m){ +// Params().CosineThr=cos(Params().NormalThr); +// InitQuadric(m); +// //m.Topology(); +// //OldInitQuadric(m,UseArea); +// } +// + CoordType ComputeMinimal() +{ + typename TriMeshType::VertexType * v[2]; + v[0] = this->pos.V(0); + v[1] = this->pos.V(1); + QuadricType q=QH::Qd(v[0]); + q+=QH::Qd(v[1]); + + Point3 x; + + bool rt=q.Minimum(x); + if(!rt) { // if the computation of the minimum fails we choose between the two edge points and the middle one. + Point3 x0=Point3d::Construct(v[0]->P()); + Point3 x1=Point3d::Construct(v[1]->P()); + x.Import((v[0]->P()+v[1]->P())/2); + double qvx=q.Apply(x); + double qv0=q.Apply(x0); + double qv1=q.Apply(x1); + if(qv0 +#include +#include + +namespace vcg +{ +namespace tri +{ +/** \addtogroup trimesh */ +/* @{ */ + +/*! + * This Class is specialization of LocalModification for the edge flip + * It wraps the atomic operation EdgeFlip to be used in a optimization routine. + * Note that it has knowledge of the heap of the class LocalOptimization because + * it is responsible of updating it after a flip has been performed + * This is the simplest edge flipping class. + * It flips an edge only if two adjacent faces are coplanar and the + * quality of the faces improves after the flip. + */ +template const & p0, + Point3 const & p1, + Point3 const & p2) = Quality> +class PlanarEdgeFlip : + public LocalOptimization< TRIMESH_TYPE>::LocModType +{ +protected: + typedef typename TRIMESH_TYPE::FaceType FaceType; + typedef typename TRIMESH_TYPE::FacePointer FacePointer; + typedef typename TRIMESH_TYPE::FaceIterator FaceIterator; + typedef typename TRIMESH_TYPE::VertexType VertexType; + typedef typename TRIMESH_TYPE::ScalarType ScalarType; + typedef typename TRIMESH_TYPE::VertexPointer VertexPointer; + typedef typename TRIMESH_TYPE::CoordType CoordType; + typedef vcg::face::Pos PosType; + typedef typename LocalOptimization::HeapElem HeapElem; + typedef typename LocalOptimization::HeapType HeapType; + + /*! + * the pos of the flipping + */ + PosType _pos; + + /*! + * priority in the heap + */ + ScalarType _priority; + + /*! + * Mark for updating + */ + int _localMark; + + /*! + * mark for up_dating + */ + static int& GlobalMark() + { + static int im = 0; + return im; + } + + static void Insert(HeapType& heap, PosType& p, int mark) + { + if(!p.IsBorder() && p.F()->IsW() && p.FFlip()->IsW()) { + MYTYPE* newflip = new MYTYPE(p, mark); + heap.push_back(HeapElem(newflip)); + std::push_heap(heap.begin(), heap.end()); + } + } + +public: + /*! + * Default constructor + */ + inline PlanarEdgeFlip() + { + } + + + /*! + * Constructor with pos type + */ + inline PlanarEdgeFlip(PosType pos, int mark) + { + _pos = pos; + _localMark = mark; + _priority = this->ComputePriority(); + } + + + /*! + * Copy Constructor + */ + inline PlanarEdgeFlip(const PlanarEdgeFlip &par) + { + _pos = par.GetPos(); + _localMark = par.GetMark(); + _priority = par.Priority(); + } + + + /*! + */ + ~PlanarEdgeFlip() + { + } + + + /*! + * Parameter + */ + static ScalarType &CoplanarAngleThresholdDeg() + { + static ScalarType _CoplanarAngleThresholdDeg = 0.01f; + return _CoplanarAngleThresholdDeg; + } + + inline PosType GetPos() const + { + return _pos; + } + + inline int GetMark()const + { + return _localMark; + } + + + /*! + * Return the LocalOptimization type + */ + ModifierType IsOfType() + { + return TriEdgeFlipOp; + } + + + /*! + * Check if the pos is updated + */ + bool IsUpToDate() + { + int lastMark = _pos.F()->V(0)->IMark(); + lastMark = std::max(lastMark, _pos.F()->V(1)->IMark()); + lastMark = std::max(lastMark, _pos.F()->V(2)->IMark()); + + return ( _localMark >= lastMark ); + } + + /*! + * + Check if this flipping operation can be performed. + It is a topological and geometrical check. + */ + virtual bool IsFeasible() + { + if(!vcg::face::CheckFlipEdge(*this->_pos.F(), this->_pos.E())) + return false; + + if( math::ToDeg( Angle(_pos.FFlip()->cN(), _pos.F()->cN()) ) > CoplanarAngleThresholdDeg() ) + return false; + + CoordType v0, v1, v2, v3; + int i = _pos.E(); + + v0 = _pos.F()->P0(i); + v1 = _pos.F()->P1(i); + v2 = _pos.F()->P2(i); + v3 = _pos.F()->FFp(i)->P2(_pos.F()->FFi(i)); + + // Take the parallelogram formed by the adjacent faces of edge + // If a corner of the parallelogram on extreme of edge to flip is >= 180 + // the flip produce two identical faces - avoid this + if( (Angle(v2 - v0, v1 - v0) + Angle(v3 - v0, v1 - v0) >= M_PI) || + (Angle(v2 - v1, v0 - v1) + Angle(v3 - v1, v0 - v1) >= M_PI)) + return false; + + // if any of two faces adj to edge in non writable, the flip is unfeasible + if(!_pos.F()->IsW() || !_pos.F()->FFp(i)->IsW()) + return false; + + return true; + } + + /*! + * Compute the priority of this optimization + */ + /* + 1 + /|\ + / | \ + 2 | 3 + \ | / + \|/ + 0 + */ + ScalarType ComputePriority() + { + CoordType v0, v1, v2, v3; + int i = _pos.E(); + v0 = _pos.F()->P0(i); + v1 = _pos.F()->P1(i); + v2 = _pos.F()->P2(i); + v3 = _pos.F()->FFp(i)->P2(_pos.F()->FFi(i)); + + ScalarType Qa = QualityFunc(v0, v1, v2); + ScalarType Qb = QualityFunc(v0, v3, v1); + + ScalarType QaAfter = QualityFunc(v1, v2, v3); + ScalarType QbAfter = QualityFunc(v0, v3, v2); + + // < 0 if the average quality of faces improves after flip + _priority = (Qa + Qb - QaAfter - QbAfter) / (ScalarType)2.0; + + return _priority; + } + + /*! + * Return the priority of this optimization + */ + virtual ScalarType Priority() const + { + return _priority; + } + + /*! + * Execute the flipping of the edge + */ + void Execute(TRIMESH_TYPE &m) + { + int i = _pos.E(); + int j = _pos.F()->FFi(i); + FacePointer f1 = _pos.F(); + FacePointer f2 = _pos.F()->FFp(i); + + vcg::face::FlipEdge(*_pos.F(), _pos.E()); + + // avoid texture coordinates swap after flip + if(tri::HasPerWedgeTexCoord(m)) { + f2->WT((j + 1) % 3) = f1->WT((i + 2) % 3); + f1->WT((i + 1) % 3) = f2->WT((j + 2) % 3); + } + } + + /*! + */ + const char* Info(TRIMESH_TYPE &m) + { + static char dump[60]; + sprintf(dump,"%d -> %d %g\n", _pos.F()->V(0)-&m.vert[0], _pos.F()->V(1)-&m.vert[0],-_priority); + return dump; + } + + /*! + */ + static void Init(TRIMESH_TYPE &mesh, HeapType &heap) + { + heap.clear(); + FaceIterator fi; + for(fi = mesh.face.begin(); fi != mesh.face.end(); ++fi) { + if(!(*fi).IsD() && (*fi).IsW()) { + for(unsigned int i = 0; i < 3; i++) { + if( !(*fi).IsB(i) && !((*fi).FFp(i)->IsD()) && (*fi).FFp(i)->IsW() ) { + if((*fi).V1(i) - (*fi).V0(i) > 0) { + PosType p(&*fi, i); + Insert(heap, p, IMark(mesh)); + } + //heap.push_back( HeapElem( new MYTYPE(PosType(&*fi, i), mesh.IMark() )) ); + } //endif + } //endfor + } + } //endfor + } + + /*! + */ + virtual void UpdateHeap(HeapType &heap) + { + GlobalMark()++; + + // after flip, the new edge just created is the next edge + int flipped = (_pos.E() + 1) % 3; + + PosType pos(_pos.F(), flipped); + + pos.F()->V(0)->IMark() = GlobalMark(); + pos.F()->V(1)->IMark() = GlobalMark(); + pos.F()->V(2)->IMark() = GlobalMark(); + pos.F()->FFp(flipped)->V2(pos.F()->FFi(flipped))->IMark() = GlobalMark(); + + pos.FlipF(); pos.FlipE(); + Insert(heap, pos, GlobalMark()); + + pos.FlipV(); pos.FlipE(); + Insert(heap, pos, GlobalMark()); + + pos.FlipV(); pos.FlipE(); + pos.FlipF(); pos.FlipE(); + Insert(heap, pos, GlobalMark()); + + pos.FlipV(); pos.FlipE(); + Insert(heap, pos, GlobalMark()); + } +}; // end of PlanarEdgeFlip class + + +template +class TriEdgeFlip : public PlanarEdgeFlip +{ +protected: + typedef typename TRIMESH_TYPE::FaceType FaceType; + typedef typename TRIMESH_TYPE::ScalarType ScalarType; + typedef typename TRIMESH_TYPE::CoordType CoordType; + typedef vcg::face::Pos PosType; + +public: + /*! + * Default constructor + */ + inline TriEdgeFlip() {} + + /*! + * Constructor with pos type + */ + inline TriEdgeFlip(const PosType pos, int mark) + { + this->_pos = pos; + this->_localMark = mark; + this->_priority = ComputePriority(); + } + + /*! + * Copy Constructor + */ + inline TriEdgeFlip(const TriEdgeFlip &par) + { + this->_pos = par.GetPos(); + this->_localMark = par.GetMark(); + this->_priority = par.Priority(); + } + + + ScalarType ComputePriority() + { + /* + 1 + /|\ + / | \ + 2 | 3 + \ | / + \|/ + 0 + */ + CoordType v0, v1, v2, v3; + int i = this->_pos.E(); + v0 = this->_pos.F()->P0(i); + v1 = this->_pos.F()->P1(i); + v2 = this->_pos.F()->P2(i); + v3 = this->_pos.F()->FFp(i)->P2(this->_pos.F()->FFi(i)); + + // if the sum of angles in v2 e v3 is > 180, then the triangle + // pair is not a delaunay triangulation + ScalarType alpha = math::Abs(Angle(v0 - v2, v1 - v2)); + ScalarType beta = math::Abs(Angle(v0 - v3, v1 - v3)); + this->_priority = 180 - math::ToDeg((alpha + beta)); + return this->_priority; + } +}; + + +// This kind of flip minimize the variance of number of incident faces +// on the vertices of two faces involved in the flip +template +class TopoEdgeFlip : public PlanarEdgeFlip +{ +protected: + typedef typename TRIMESH_TYPE::VertexPointer VertexPointer; + + typedef typename TRIMESH_TYPE::FaceType FaceType; + typedef typename TRIMESH_TYPE::FacePointer FacePointer; + typedef typename TRIMESH_TYPE::ScalarType ScalarType; + typedef typename TRIMESH_TYPE::CoordType CoordType; + typedef vcg::face::Pos PosType; + + typedef typename LocalOptimization::HeapElem HeapElem; + typedef typename LocalOptimization::HeapType HeapType; + + typedef typename TRIMESH_TYPE::FaceIterator FaceIterator; + typedef typename TRIMESH_TYPE::VertexIterator VertexIterator; + +public: + /*! + * Default constructor + */ + inline TopoEdgeFlip() {} + + /*! + * Constructor with pos type + */ + inline TopoEdgeFlip(const PosType pos, int mark) + { + this->_pos = pos; + this->_localMark = mark; + this->_priority = ComputePriority(); + } + + /*! + * Copy Constructor + */ + inline TopoEdgeFlip(const TopoEdgeFlip &par) + { + this->_pos = par.GetPos(); + this->_localMark = par.GetMark(); + this->_priority = par.Priority(); + } + + + ScalarType ComputePriority() + { + /* + 1 + /|\ + / | \ + 2 | 3 + \ | / + \|/ + 0 + */ + VertexPointer v0, v1, v2, v3; + int i = this->_pos.E(); + v0 = this->_pos.F()->V0(i); + v1 = this->_pos.F()->V1(i); + v2 = this->_pos.F()->V2(i); + v3 = this->_pos.F()->FFp(i)->V2(this->_pos.F()->FFi(i)); + + // This kind of flip minimize the variance of number of incident faces + // on the vertices of two faces involved in the flip + + ScalarType avg = (v0->Q() + v1->Q() + v2->Q() + v3->Q()) / 4.0; + + ScalarType varbefore = (powf(v0->Q() - avg, 2.0) + + powf(v1->Q() - avg, 2.0) + + powf(v2->Q() - avg, 2.0) + + powf(v3->Q() - avg, 2.0)) / 4.0; + + ScalarType varafter = (powf(v0->Q() - 1 - avg, 2.0) + + powf(v1->Q() - 1 - avg, 2.0) + + powf(v2->Q() + 1 - avg, 2.0) + + powf(v3->Q() + 1 - avg, 2.0)) / 4.0; + + this->_priority = varafter - varbefore; + return this->_priority; + } + + + /*! + * Execute the flipping of the edge + */ + void Execute(TRIMESH_TYPE &m) + { + int i = this->_pos.E(); + FacePointer f1 = this->_pos.F(); + FacePointer f2 = f1->FFp(i); + int j = f1->FFi(i); + + // update the number of faces adjacent to vertices + f1->V0(i)->Q()--; + f1->V1(i)->Q()--; + f1->V2(i)->Q()++; + f2->V2(j)->Q()++; + + // do the flip + vcg::face::FlipEdge(*this->_pos.F(), this->_pos.E()); + + // avoid texture coordinates swap after flip + if (tri::HasPerWedgeTexCoord(m)) { + f2->WT((j + 1) % 3) = f1->WT((i + 2) % 3); + f1->WT((i + 1) % 3) = f2->WT((j + 2) % 3); + } + } + + + static void Init(TRIMESH_TYPE &m, HeapType &heap) + { + // reset quality field for each vertex + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD()) + (*vi).Q() = 0; + + // for each vertex, put the number of incident faces in quality field + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + for(int i = 0; i < 3; i++) + (*fi).V(i)->Q()++; + + TriEdgeFlip::Init(m, heap); + } + + + void UpdateHeap(HeapType &heap) + { + this->GlobalMark()++; + + VertexPointer v0, v1, v2, v3; + int flipped = (this->_pos.E() + 1) % 3; + FacePointer f1 = this->_pos.F(); + FacePointer f2 = this->_pos.F()->FFp(flipped); + + v0 = f1->V0(flipped); + v1 = f1->V1(flipped); + v2 = f1->V2(flipped); + v3 = f2->V2(f1->FFi(flipped)); + + v0->IMark() = this->GlobalMark(); + v1->IMark() = this->GlobalMark(); + v2->IMark() = this->GlobalMark(); + v3->IMark() = this->GlobalMark(); + + // edges of the first face, except the flipped edge + for(int i = 0; i < 3; i++) if(i != flipped) { + PosType newpos(f1, i); + Insert(heap, newpos, this->GlobalMark()); + } + + // edges of the second face, except the flipped edge + for(int i = 0; i < 3; i++) if(i != f1->FFi(flipped)) { + PosType newpos(f2, i); + Insert(heap, newpos, this->GlobalMark()); + } + + // every edge with v0, v1 v3 of f1 + for(int i = 0; i < 3; i++) { + PosType startpos(f1, i); + PosType pos(startpos); + + do { // go to the first border (if there is one) + pos.NextE(); + } while(pos != startpos && !pos.IsBorder()); + + // if a border is reached, set startpos here + if(pos.IsBorder()) + startpos = pos; + + do { + VertexPointer v = pos.VFlip(); + if(v != v0 && v != v1 && v != v2 && v != v3) + Insert(heap, pos, this->GlobalMark()); + + pos.NextE(); + } while(pos != startpos && !pos.IsBorder()); + } + + PosType startpos(f2, (f1->FFi(flipped) + 2) % 3); + PosType pos(startpos); + + do { // go to the first border (if there is one) + pos.NextE(); + } while(pos != startpos && !pos.IsBorder()); + + // if a border is reached, set startpos here + if(pos.IsBorder()) + startpos = pos; + + do { + VertexPointer v = pos.VFlip(); + if(v != v0 && v != v1 && v != v2 && v != v3) + Insert(heap, pos, this->GlobalMark()); + + pos.NextE(); + } while(pos != startpos && !pos.IsBorder()); + } +}; + + +} // end of namespace tri +} // end of namespace vcg + +#endif diff --git a/vcg/complex/algorithms/nring.h b/vcg/complex/algorithms/nring.h new file mode 100644 index 00000000..5d1756f8 --- /dev/null +++ b/vcg/complex/algorithms/nring.h @@ -0,0 +1,157 @@ +#ifndef RINGWALKER_H +#define RINGWALKER_H + +/**************************************************************************** + * 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. * + * * + ****************************************************************************/ + +#include +#include +#include +#include + +namespace vcg +{ +namespace tri +{ + + /** \addtogroup trimesh */ + /*@{*/ + /*@{*/ + /** Class Mesh. + This is class for extracting n-ring of vertexes or faces, starting from a vertex of a mesh. + */ +template +class Nring +{ +public: + + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::CoordType CoordType; + + + std::vector allV; + std::vector allF; + + std::vector lastV; + std::vector lastF; + + MeshType* m; + + Nring(VertexType* v, MeshType* m) : m(m) + { + assert((unsigned)(v - &*m->vert.begin()) < m->vert.size()); + insertAndFlag(v); + + } + + ~Nring() + { + clear(); + } + + void insertAndFlag1Ring(VertexType* v) + { + insertAndFlag(v); + + typename face::Pos p(v->VFp(),v); + assert(p.V() == v); + + int count = 0; + face::Pos ori = p; + do + { + insertAndFlag(p.F()); + p.FlipF(); + p.FlipE(); + assert(count++ < 100); + } while (ori != p); + + } + + void insertAndFlag(FaceType* f) + { + if (!f->IsV()) + { + allF.push_back(f); + lastF.push_back(f); + f->SetV(); + insertAndFlag(f->V(0)); + insertAndFlag(f->V(1)); + insertAndFlag(f->V(2)); + } + } + + void insertAndFlag(VertexType* v) + { + if (!v->IsV()) + { + allV.push_back(v); + lastV.push_back(v); + v->SetV(); + } + } + + + static void clearFlags(MeshType* m) + { + tri::UpdateFlags::VertexClearV(*m); + tri::UpdateFlags::FaceClearV(*m); + } + + void clear() + { + for(unsigned i=0; i< allV.size(); ++i) + allV[i]->ClearV(); + for(unsigned i=0; i< allF.size(); ++i) + allF[i]->ClearV(); + + allV.clear(); + allF.clear(); + } + + void expand() + { + std::vector lastVtemp = lastV; + + lastV.clear(); + lastF.clear(); + + for(typename std::vector::iterator it = lastVtemp.begin(); it != lastVtemp.end(); ++it) + { + insertAndFlag1Ring(*it); + } + } + + void expand(int k) + { + for(int i=0;i +#include +#include +#include +#include + +#include + +using namespace std; +using namespace vcg; + +/** \brief This class provides a strategy to estimate the overlap percentage of two range maps/point clouds. + * + * This class can be used, for exemple, into an automatic alignment process to check the quality of the + * transformation found; the idea is that bad alignments should have a small overlap. Two points are + * considered 'overlapping in the righ way' if they are close (i.e distance is less then \c consensusDist) + * and at the same time points' normals match quite well (i.e the angle between them is less then + * \c consensusNormalsAngle). The test to compute the overlap is perfomed on a given number of points + * (2500 is the default) sampled in a normal equalized way (default) or uniformly. + * \author Francesco Tonarelli + */ +template class OverlapEstimation +{ + public: + + typedef MESH_TYPE MeshType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename vector::iterator VertexPointerIterator; + typedef GridStaticPtr MeshGrid; + typedef tri::VertTmark MarkerVertex; + + private: + /** Private simple class needed to perform sampling of pointers to vertexes. */ + class VertexPointerSampler + { + public: + + MeshType* m; //this is needed for advanced sampling (i.e poisson sampling) + + VertexPointerSampler(){ m = new MeshType(); m->Tr.SetIdentity(); m->sfn=0; } + ~VertexPointerSampler(){ if(m) delete m; } + vector sampleVec; + + void AddVert(VertexType &p){ sampleVec.push_back(&p); } //this function is the only we really need + void AddFace(const FaceType &f, const CoordType &p){} + void AddTextureSample(const FaceType &, const CoordType &, const Point2i &){} + }; + + public: + /** \brief Public class to hold parameters. Used to avoid endless list of parameters inside functions. + * \author Francesco Tonarelli + */ + class Parameters + { + public: + int samples; ///< Number of samples to check to compute the overlap. Higher values get more accurancy but requires more time. + int bestScore; ///< Score to overcome to paint \c mMov . If overlap estimation is called many times inside a loop, you can set this value in each iteration to paint \c mMov and see only the best overlap achived. + float consensusDist; ///< Consensus distance. Lower values should gat more accurancy; high values can lead to performance hit. + float consensusNormalsAngle; ///< Holds the the consensus angle for normals, in gradients. Lower values decrease accurancy, particulary for range maps with many peaks and high frequencies. + float threshold; ///< Consensus percentage requested to win consensus. Used to paint \c mMov. If the overlap overcames the \c threshold (and \c bestScore), \c mMov is painted. + bool normalEqualization; ///< Allows to use normal equalization sampling in consensus. If set to \c false uniform sampling is used instead. Uniform sampling is faster but less accurate. + bool paint; ///< Allows painting of \c mMov according to consensus. See Paint() for details. + void (*log)(int level, const char * f, ... ); ///< Pointer to a log function. + + /** Constructor with default values. */ + Parameters() + { + samples = 2500; + bestScore = 0; + consensusDist = 2.0f; + consensusNormalsAngle = 0.965f; //15 degrees. + threshold = 0.0f; + normalEqualization = true; + paint = false; + log = NULL; + } + }; + + private: + MeshType* mFix; /** Pointer to mesh \c mFix. */ + MeshType* mMov; /** Pointer to mesh \c mMov. */ + vector >* normBuckets; //structure to hold normals bucketing. Needed for normal equalized sampling during consensus + MeshGrid* gridFix; //variable to manage uniform grid + MarkerVertex markerFunctorFix; //variable to manage uniform grid + + public: + /** Default constructor. */ + OverlapEstimation() : normBuckets(NULL), gridFix(NULL){} + /** Default destructor. Deallocates structures. */ + ~OverlapEstimation(){ + if(normBuckets) delete normBuckets; + if(gridFix) delete gridFix; + } + /** Set the fix mesh \c mFix. */ + void SetFix(MeshType& m){ mFix = &m; } + /** Set the move mesh \c mMov. */ + void SetMove(MeshType& m){ mMov = &m; } + + /** Paint \c mMov according to the overlap estimation result. Works only if \c Compute() or \c Check() have + * been previously called with \c Parameters.paint=true .
Legend: \arg \e red: points overlaps correctly. + * \arg \e blue: points are too far to overlap. \arg \e yellow: points are close, but normals mismatch. + */ + void Paint() + { + for(VertexIterator vi=mMov->vert.begin(); vi!=mMov->vert.end(); vi++){ + if(!(*vi).IsD()){ + if((*vi).Q()==0.0) (*vi).C() = Color4b::Red; + if((*vi).Q()==1.0) (*vi).C() = Color4b::Yellow; + if((*vi).Q()==2.0) (*vi).C() = Color4b::Blue; + } + } + } + + /** Initializes structures. + * @param param A reference to a \c Parameter class containing all the desidered options to estimate overlap. + * \return \c true if everything goes right. + */ + bool Init(Parameters& param){ + //builds the uniform grid with mFix vertices + gridFix = new MeshGrid(); + SetupGrid(); + + //if requested, group normals of mMov into 30 buckets. Buckets are used for Vertex Normal Equalization + //in consensus. Bucketing is done here once for all to speed up consensus. + if(normBuckets) {normBuckets->clear(); delete normBuckets; } + if(param.normalEqualization){ + normBuckets = BucketVertexNormal(mMov->vert, 30); + assert(normBuckets); + } + return true; + } + + /** Compute the overlap estimation between \c mFix and \c mMov. + * @param param A reference to a \c Parameter class containing all the desidered options to estimate overlap. + * \return The percentage of overlap in the range \c [0..1] . + */ + float Compute(Parameters& param) + { + return Check(param)/float(param.samples); + } + + /** Compute the overlap estimation between \c mFix and \c mMov. + * @param param A reference to a \c Parameter class containing all the desidered options to estimate overlap. + * \return The number of points that overlap correctly. This number is in the range \c [0..param.samples] . + */ + //IMPORTANT: per vertex normals of mMov and mFix MUST BE PROVIDED YET NORMALIZED!!! + int Check(Parameters& param) + { + //pointer to a function to compute distance beetween points + vertex::PointDistanceFunctor PDistFunct; + + //if no buckets are provided get a vector of vertex pointers sampled uniformly + //else, get a vector of vertex pointers sampled in a normal equalized manner; used as query points + vector queryVert; + if(param.normalEqualization){ + assert(normBuckets); + for(unsigned int i=0; ivert.size(); i++) queryVert.push_back(&(mMov->vert[i]));//do a copy of pointers to vertexes + SampleVertNormalEqualized(queryVert, param.samples); + } + else{ + SampleVertUniform(*mMov, queryVert, param.samples); + } + assert(queryVert.size()!=0); + + //init variables for consensus + float consDist = param.consensusDist*(mMov->bbox.Diag()/100.0f); //consensus distance + int cons_succ = int(param.threshold*(param.samples/100.0f)); //score needed to pass consensus + int consensus = 0; //counts vertices in consensus + float dist; //holds the distance of the closest vertex found + VertexType* closestVertex = NULL; //pointer to the closest vertex + Point3 queryNrm; //the query point normal for consensus + CoordType queryPnt; //the query point for consensus + CoordType closestPnt; //the closest point found in consensus + Matrix33 inv33_matMov(mMov->Tr,3); //3x3 matrix needed to transform normals + Matrix33 inv33_matFix(Inverse(mFix->Tr),3); //3x3 matrix needed to transform normals + + //consensus loop + VertexPointerIterator vi; int i; + for(i=0, vi=queryVert.begin(); vi!=queryVert.end(); vi++, i++) + { + dist = -1.0f; + //set query point; vertex coord is transformed properly in fix mesh coordinates space; the same for normals + queryPnt = Inverse(mFix->Tr) * (mMov->Tr * (*vi)->P()); + queryNrm = inv33_matFix * (inv33_matMov * (*vi)->N()); + //if query point is bbox, the look for a vertex in cDist from the query point + if(mFix->bbox.IsIn(queryPnt)) closestVertex = gridFix->GetClosest(PDistFunct,markerFunctorFix,queryPnt,consDist,dist,closestPnt); + else closestVertex=NULL; //out of bbox, we consider the point not in consensus... + + if(closestVertex!=NULL && dist < consDist){ + assert(closestVertex->P()==closestPnt); //coord and vertex pointer returned by getClosest must be the same + + //point is in consensus distance, now we check if normals are near + if(queryNrm.dot(closestVertex->N())>param.consensusNormalsAngle) //15 degrees + { + consensus++; //got consensus + if(param.paint) (*vi)->Q() = 0.0f; //store 0 as quality + } + else{ + if(param.paint) (*vi)->Q() = 1.0f; //store 1 as quality + } + } + else{ + if(param.paint) (*vi)->Q() = 2.0f; //store 2 as quality + } + } + + //Paint the mesh only if required and if consensus is the best ever found. Colors have been stores as numbers into quality attribute + if(param.paint){ + if(consensus>=param.bestScore && consensus>=cons_succ) Paint(); + } + + return consensus; + } + + private: + /** Fill the vector \c vert with \c sampleNum pointers to vertexes sampled uniformly from mesh \c m . + * @param m Source mesh. + * @param vert Destination vector. + * @param sampleNum Requested number of vertexes. + */ + void SampleVertUniform(MESH_TYPE& m, vector& vert, int sampleNum) + { + VertexPointerSampler sampler; + tri::SurfaceSampling::VertexUniform(m, sampler, sampleNum); + for(unsigned int i=0; i >* BucketVertexNormal(typename MESH_TYPE::VertContainer& vert, int bucketDim = 30) + { + static vector NV; + if(NV.size()==0) GenNormal::Uniform(bucketDim,NV); + + vector >* BKT = new vector >(NV.size()); //NV size is greater then bucketDim, so don't change this! + + int ind; + for(int i=0;i::BestMatchingNormal(vert[i].N(),NV); + (*BKT)[ind].push_back(i); + } + + return BKT; + } + /** Samples the \c vert vector in a normal equalized way. + * \return \c SampleNum pointers to vertexes sampled in a normal equalized way. Pointers are stored in + * the \c vert (i.e it is an \c in/out parameter). + */ + bool SampleVertNormalEqualized(vector& vert, int SampleNum) + { + assert(normBuckets); + // vettore di contatori per sapere quanti punti ho gia' preso per ogni bucket + vector BKTpos(normBuckets->size(),0); + + if(SampleNum >= int(vert.size())) SampleNum= int(vert.size()-1); + + int ind; + for(int i=0;isize()); // Scelgo un Bucket + int &CURpos = BKTpos[ind]; + vector &CUR = (*normBuckets)[ind]; + + if(CURposSet(mFix->vert.begin(),mFix->vert.end()); + markerFunctorFix.SetMesh(mFix); + } +}; + +#endif // OVERLAP_ESTIMATION_H diff --git a/vcg/complex/algorithms/point_sampling.h b/vcg/complex/algorithms/point_sampling.h new file mode 100644 index 00000000..8a7ae328 --- /dev/null +++ b/vcg/complex/algorithms/point_sampling.h @@ -0,0 +1,1468 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/**************************************************************************** + +The sampling Class has a set of static functions, that you can call to sample the surface of a mesh. +Each function is templated on the mesh and on a Sampler object s. +Each function calls many time the sample object with the sampling point as parameter. + +Sampler Classes and Sampling algorithms are independent. +Sampler classes exploits the sample that are generated with various algorithms. +For example, you can compute Hausdorff distance (that is a sampler) using various +sampling strategies (montecarlo, stratified etc). + +****************************************************************************/ +#ifndef __VCGLIB_POINT_SAMPLING +#define __VCGLIB_POINT_SAMPLING + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcg +{ +namespace tri +{ + +/// Trivial Sampler, an example sampler object that show the required interface used by the sampling class. +/// Most of the sampling classes call the AddFace method with the face containing the sample and its barycentric coord. +/// Beside being an example of how to write a sampler it provides a simple way to use the various sampling classes. +// For example if you just want to get a vector with positions over the surface You have just to write +// +// vector myVec; +// TrivialSampler ts(myVec) +// SurfaceSampling >::Montecarlo(M, ts, SampleNum); +// +// + +template +class TrivialSampler +{ + public: + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::FaceType FaceType; + + TrivialSampler() + { + sampleVec = new std::vector(); + vectorOwner=true; + }; + + TrivialSampler(std::vector &Vec) + { + sampleVec = &Vec; + sampleVec->clear(); + vectorOwner=false; + }; + + ~TrivialSampler() + { + if(vectorOwner) delete sampleVec; + } + + private: + std::vector *sampleVec; + bool vectorOwner; + public: + + void AddVert(const VertexType &p) + { + sampleVec->push_back(p.cP()); + } + void AddFace(const FaceType &f, const CoordType &p) + { + sampleVec->push_back(f.P(0)*p[0] + f.P(1)*p[1] +f.P(2)*p[2] ); + } + + void AddTextureSample(const FaceType &, const CoordType &, const Point2i &, float ) + { + // Retrieve the color of the sample from the face f using the barycentric coord p + // and write that color in a texture image at position + // if edgeDist is > 0 then the corrisponding point is affecting face color even if outside the face area (in texture space) + } +}; // end class TrivialSampler + +template +class SurfaceSampling +{ + typedef typename MetroMesh::CoordType CoordType; + typedef typename MetroMesh::ScalarType ScalarType; + typedef typename MetroMesh::VertexType VertexType; + typedef typename MetroMesh::VertexPointer VertexPointer; + typedef typename MetroMesh::VertexIterator VertexIterator; + typedef typename MetroMesh::FacePointer FacePointer; + typedef typename MetroMesh::FaceIterator FaceIterator; + typedef typename MetroMesh::FaceType FaceType; + typedef typename MetroMesh::FaceContainer FaceContainer; + + typedef typename vcg::SpatialHashTable MeshSHT; + typedef typename vcg::SpatialHashTable::CellIterator MeshSHTIterator; + typedef typename vcg::SpatialHashTable MontecarloSHT; + typedef typename vcg::SpatialHashTable::CellIterator MontecarloSHTIterator; + typedef typename vcg::SpatialHashTable SampleSHT; + typedef typename vcg::SpatialHashTable::CellIterator SampleSHTIterator; + +public: + +static math::MarsenneTwisterRNG &SamplingRandomGenerator() +{ + static math::MarsenneTwisterRNG rnd; + return rnd; +} + +// Returns an integer random number in the [0,i-1] interval using the improve Marsenne-Twister method. +static unsigned int RandomInt(unsigned int i) +{ + return (SamplingRandomGenerator().generate(0) % i); +} + +// Returns a random number in the [0,1) real interval using the improved Marsenne-Twister method. +static double RandomDouble01() +{ + return SamplingRandomGenerator().generate01(); +} + +// Returns a random number in the [0,1] real interval using the improved Marsenne-Twister. +static double RandomDouble01closed() +{ + return SamplingRandomGenerator().generate01closed(); +} + +#define FAK_LEN 1024 +static double LnFac(int n) { + // Tabled log factorial function. gives natural logarithm of n! + + // define constants + static const double // coefficients in Stirling approximation + C0 = 0.918938533204672722, // ln(sqrt(2*pi)) + C1 = 1./12., + C3 = -1./360.; + // C5 = 1./1260., // use r^5 term if FAK_LEN < 50 + // C7 = -1./1680.; // use r^7 term if FAK_LEN < 20 + // static variables + static double fac_table[FAK_LEN]; // table of ln(n!): + static bool initialized = false; // remember if fac_table has been initialized + + + if (n < FAK_LEN) { + if (n <= 1) { + if (n < 0) assert(0);//("Parameter negative in LnFac function"); + return 0; + } + if (!initialized) { // first time. Must initialize table + // make table of ln(n!) + double sum = fac_table[0] = 0.; + for (int i=1; i= pois_bound) continue; // reject if outside valid range + k = (int)(x); + lf = k * pois_g - LnFac(k) - pois_f0; + if (lf >= u * (4.0 - u) - 3.0) break; // quick acceptance + if (u * (u - lf) > 1.0) continue; // quick rejection + if (2.0 * log(u) <= lf) break; // final acceptance + } + return k; +} + + +/** + algorithm poisson random number (Knuth): + init: + Let L ↠e^−λ, k ↠0 and p ↠1. + do: + k ↠k + 1. + Generate uniform random number u in [0,1] and let p ↠p × u. + while p > L. + return k − 1. + + */ +static int Poisson(double lambda) +{ + if(lambda>50) return PoissonRatioUniforms(lambda); + double L = exp(-lambda); + int k =0; + double p = 1.0; + do + { + k = k+1; + p = p*RandomDouble01(); + } while (p>L); + + return k -1; +} + + +static void AllVertex(MetroMesh & m, VertexSampler &ps) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + if(!(*vi).IsD()) + { + ps.AddVert(*vi); + } + } +} + +/// Sample the vertices in a weighted way. Each vertex has a probability of being chosen +/// that is proportional to its quality. +/// It assumes that you are asking a number of vertices smaller than nv; +/// Algorithm: +/// 1) normalize quality so that sum q == 1; +/// 2) shuffle vertices. +/// 3) for each vertices choose it if rand > thr; + +static void VertexWeighted(MetroMesh & m, VertexSampler &ps, int sampleNum) +{ + ScalarType qSum = 0; + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD()) + qSum += (*vi).Q(); + + ScalarType samplePerUnit = sampleNum/qSum; + ScalarType floatSampleNum =0; + std::vector vertVec; + FillAndShuffleVertexPointerVector(m,vertVec); + + std::vector vertUsed(m.vn,false); + + int i=0; int cnt=0; + while(cnt < sampleNum) + { + if(vertUsed[i]) + { + floatSampleNum += vertVec[i]->Q() * samplePerUnit; + int vertSampleNum = (int) floatSampleNum; + floatSampleNum -= (float) vertSampleNum; + + // for every sample p_i in T... + if(vertSampleNum > 1) + { + ps.AddVert(*vertVec[i]); + cnt++; + vertUsed[i]=true; + } + } + i = (i+1)%m.vn; + } +} + +/// Sample the vertices in a uniform way. Each vertex has a probability of being chosen +/// that is proportional to the area it represent. +static void VertexAreaUniform(MetroMesh & m, VertexSampler &ps, int sampleNum) +{ + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD()) + (*vi).Q() = 0; + + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + ScalarType areaThird = DoubleArea(*fi)/6.0; + (*fi).V(0).Q()+=areaThird; + (*fi).V(1).Q()+=areaThird; + (*fi).V(2).Q()+=areaThird; + } + + VertexWeighted(m,ps,sampleNum); +} + +static void FillAndShuffleFacePointerVector(MetroMesh & m, std::vector &faceVec) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) faceVec.push_back(&*fi); + + assert((int)faceVec.size()==m.fn); + + unsigned int (*p_myrandom)(unsigned int) = RandomInt; + std::random_shuffle(faceVec.begin(),faceVec.end(), p_myrandom); +} +static void FillAndShuffleVertexPointerVector(MetroMesh & m, std::vector &vertVec) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) vertVec.push_back(&*vi); + + assert((int)vertVec.size()==m.vn); + + unsigned int (*p_myrandom)(unsigned int) = RandomInt; + std::random_shuffle(vertVec.begin(),vertVec.end(), p_myrandom); +} + +/// Sample the vertices in a uniform way. Each vertex has the same probabiltiy of being chosen. +static void VertexUniform(MetroMesh & m, VertexSampler &ps, int sampleNum) +{ + if(sampleNum>=m.vn) { + AllVertex(m,ps); + return; + } + + std::vector vertVec; + FillAndShuffleVertexPointerVector(m,vertVec); + + for(int i =0; i< sampleNum; ++i) + ps.AddVert(*vertVec[i]); +} + + +static void FaceUniform(MetroMesh & m, VertexSampler &ps, int sampleNum) +{ + if(sampleNum>=m.fn) { + AllFace(m,ps); + return; + } + + std::vector faceVec; + FillAndShuffleFacePointerVector(m,faceVec); + + for(int i =0; i< sampleNum; ++i) + ps.AddFace(*faceVec[i],Barycenter(*faceVec[i])); +} + +static void AllFace(MetroMesh & m, VertexSampler &ps) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + ps.AddFace(*fi,Barycenter(*fi)); + } +} + + +static void AllEdge(MetroMesh & m, VertexSampler &ps) +{ + // Edge sampling. + typedef typename UpdateTopology::PEdge SimpleEdge; + std::vector< SimpleEdge > Edges; + typename std::vector< SimpleEdge >::iterator ei; + UpdateTopology::FillUniqueEdgeVector(m,Edges); + + for(ei=Edges.begin(); ei!=Edges.end(); ++ei) + { + Point3f interp(0,0,0); + interp[ (*ei).z ]=.5; + interp[((*ei).z+1)%3]=.5; + ps.AddFace(*(*ei).f,interp); + } +} + +// Regular Uniform Edge sampling +// Each edge is subdivided in a number of pieces proprtional to its lenght +// Sample are choosen without touching the vertices. + +static void EdgeUniform(MetroMesh & m, VertexSampler &ps,int sampleNum, bool sampleFauxEdge=true) +{ + typedef typename UpdateTopology::PEdge SimpleEdge; + std::vector< SimpleEdge > Edges; + UpdateTopology::FillUniqueEdgeVector(m,Edges,sampleFauxEdge); + // First loop compute total edge lenght; + float edgeSum=0; + typename std::vector< SimpleEdge >::iterator ei; + for(ei=Edges.begin(); ei!=Edges.end(); ++ei) + edgeSum+=Distance((*ei).v[0]->P(),(*ei).v[1]->P()); + + //qDebug("Edges %i edge sum %f",Edges.size(),edgeSum); + float sampleLen = edgeSum/sampleNum; + //qDebug("EdgesSamples %i Sampling Len %f",sampleNum,sampleLen); + float rest=0; + for(ei=Edges.begin(); ei!=Edges.end(); ++ei) + { + float len = Distance((*ei).v[0]->P(),(*ei).v[1]->P()); + float samplePerEdge = floor((len+rest)/sampleLen); + rest = (len+rest) - samplePerEdge * sampleLen; + float step = 1.0/(samplePerEdge+1); + for(int i=0;i 1.0) + { + interp[1] = 1.0 - interp[1]; + interp[2] = 1.0 - interp[2]; + } + + assert(interp[1] + interp[2] <= 1.0); + interp[0]=1.0-(interp[1] + interp[2]); + return interp; +} + +static void StratifiedMontecarlo(MetroMesh & m, VertexSampler &ps,int sampleNum) +{ + ScalarType area = Stat::ComputeMeshArea(m); + ScalarType samplePerAreaUnit = sampleNum/area; + //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); + // Montecarlo sampling. + double floatSampleNum = 0.0; + + FaceIterator fi; + for(fi=m.face.begin(); fi != m.face.end(); fi++) + if(!(*fi).IsD()) + { + // compute # samples in the current face (taking into account of the remainders) + floatSampleNum += 0.5*DoubleArea(*fi) * samplePerAreaUnit; + int faceSampleNum = (int) floatSampleNum; + + // for every sample p_i in T... + for(int i=0; i < faceSampleNum; i++) + ps.AddFace(*fi,RandomBaricentric()); + floatSampleNum -= (double) faceSampleNum; + } +} + +/** + This function compute montecarlo distribution with an approximate number of samples exploiting the poisson distribution approximation of the binomial distribution. + + For a given triangle t of area a_t, in a Mesh of area A, + if we take n_s sample over the mesh, the number of samples that falls in t + follows the poisson distribution of P(lambda ) with lambda = n_s * (a_t/A). + + To approximate the Binomial we use a Poisson distribution with parameter \lambda = np can be used as an approximation to B(n,p) (it works if n is sufficiently large and p is sufficiently small). + + */ + + +static void MontecarloPoisson(MetroMesh & m, VertexSampler &ps,int sampleNum) +{ + ScalarType area = Stat::ComputeMeshArea(m); + ScalarType samplePerAreaUnit = sampleNum/area; + + FaceIterator fi; + for(fi=m.face.begin(); fi != m.face.end(); fi++) + if(!(*fi).IsD()) + { + float areaT=DoubleArea(*fi) * 0.5f; + int faceSampleNum = Poisson(areaT*samplePerAreaUnit); + + // for every sample p_i in T... + for(int i=0; i < faceSampleNum; i++) + ps.AddFace(*fi,RandomBaricentric()); +// SampleNum -= (double) faceSampleNum; + } +} + +/** + This function computes a montecarlo distribution with an EXACT number of samples. + it works by generating a sequence of consecutive segments proportional to the triangle areas + and actually shooting sample over this line + */ + +static void Montecarlo(MetroMesh & m, VertexSampler &ps,int sampleNum) +{ + typedef std::pair IntervalType; + std::vector< IntervalType > intervals (m.fn+1); + FaceIterator fi; + int i=0; + intervals[i]=std::make_pair(0,FacePointer(0)); + // First loop: build a sequence of consecutive segments proportional to the triangle areas. + for(fi=m.face.begin(); fi != m.face.end(); fi++) + if(!(*fi).IsD()) + { + intervals[i+1]=std::make_pair(intervals[i].first+0.5*DoubleArea(*fi), &*fi); + ++i; + } + ScalarType meshArea = intervals.back().first; + for(i=0;i::iterator it = lower_bound(intervals.begin(),intervals.end(),std::make_pair(val,FacePointer(0)) ); + assert(it != intervals.end()); + assert(it != intervals.begin()); + assert( (*(it-1)).first = val); + ps.AddFace( *(*it).second, RandomBaricentric() ); + } +} + +static ScalarType WeightedArea(FaceType f) +{ + ScalarType averageQ = ( f.V(0)->Q() + f.V(1)->Q() + f.V(2)->Q() ) /3.0; + return DoubleArea(f)*averageQ/2.0; +} + +/// Compute a sampling of the surface that is weighted by the quality +/// the area of each face is multiplied by the average of the quality of the vertices. +/// So the a face with a zero quality on all its vertices is never sampled and a face with average quality 2 get twice the samples of a face with the same area but with an average quality of 1; +static void WeightedMontecarlo(MetroMesh & m, VertexSampler &ps, int sampleNum) +{ + assert(tri::HasPerVertexQuality(m)); + + ScalarType weightedArea = 0; + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + weightedArea += WeightedArea(*fi); + + ScalarType samplePerAreaUnit = sampleNum/weightedArea; + //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); + // Montecarlo sampling. + double floatSampleNum = 0.0; + for(fi=m.face.begin(); fi != m.face.end(); fi++) + if(!(*fi).IsD()) + { + // compute # samples in the current face (taking into account of the remainders) + floatSampleNum += WeightedArea(*fi) * samplePerAreaUnit; + int faceSampleNum = (int) floatSampleNum; + + // for every sample p_i in T... + for(int i=0; i < faceSampleNum; i++) + ps.AddFace(*fi,RandomBaricentric()); + + floatSampleNum -= (double) faceSampleNum; + } +} + + +// Subdivision sampling of a single face. +// return number of added samples + +static int SingleFaceSubdivision(int sampleNum, const CoordType & v0, const CoordType & v1, const CoordType & v2, VertexSampler &ps, FacePointer fp, bool randSample) +{ + // recursive face subdivision. + if(sampleNum == 1) + { + // ground case. + CoordType SamplePoint; + if(randSample) + { + CoordType rb=RandomBaricentric(); + SamplePoint=v0*rb[0]+v1*rb[1]+v2*rb[2]; + } + else SamplePoint=((v0+v1+v2)*(1.0f/3.0f)); + + ps.AddFace(*fp,SamplePoint); + return 1; + } + + int s0 = sampleNum /2; + int s1 = sampleNum-s0; + assert(s0>0); + assert(s1>0); + + ScalarType w0 = ScalarType(s1)/ScalarType(sampleNum); + ScalarType w1 = 1.0-w0; + // compute the longest edge. + ScalarType maxd01 = SquaredDistance(v0,v1); + ScalarType maxd12 = SquaredDistance(v1,v2); + ScalarType maxd20 = SquaredDistance(v2,v0); + int res; + if(maxd01 > maxd12) + if(maxd01 > maxd20) res = 0; + else res = 2; + else + if(maxd12 > maxd20) res = 1; + else res = 2; + + int faceSampleNum=0; + // break the input triangle along the midpoint of the longest edge. + CoordType pp; + switch(res) + { + case 0 : pp = v0*w0 + v1*w1; + faceSampleNum+=SingleFaceSubdivision(s0,v0,pp,v2,ps,fp,randSample); + faceSampleNum+=SingleFaceSubdivision(s1,pp,v1,v2,ps,fp,randSample); + break; + case 1 : pp = v1*w0 + v2*w1; + faceSampleNum+=SingleFaceSubdivision(s0,v0,v1,pp,ps,fp,randSample); + faceSampleNum+=SingleFaceSubdivision(s1,v0,pp,v2,ps,fp,randSample); + break; + case 2 : pp = v0*w0 + v2*w1; + faceSampleNum+=SingleFaceSubdivision(s0,v0,v1,pp,ps,fp,randSample); + faceSampleNum+=SingleFaceSubdivision(s1,pp,v1,v2,ps,fp,randSample); + break; + } + return faceSampleNum; +} + + +/// Compute a sampling of the surface where the points are regularly scattered over the face surface using a recursive longest-edge subdivision rule. +static void FaceSubdivision(MetroMesh & m, VertexSampler &ps,int sampleNum, bool randSample) +{ + + ScalarType area = Stat::ComputeMeshArea(m); + ScalarType samplePerAreaUnit = sampleNum/area; + //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); + std::vector faceVec; + FillAndShuffleFacePointerVector(m,faceVec); + vcg::tri::UpdateNormals::PerFaceNormalized(m); + vcg::tri::UpdateFlags::FaceProjection(m); + double floatSampleNum = 0.0; + int faceSampleNum; + // Subdivision sampling. + typename std::vector::iterator fi; + for(fi=faceVec.begin(); fi!=faceVec.end(); fi++) + { + const CoordType b0(1.0, 0.0, 0.0); + const CoordType b1(0.0, 1.0, 0.0); + const CoordType b2(0.0, 0.0, 1.0); + // compute # samples in the current face. + floatSampleNum += 0.5*DoubleArea(**fi) * samplePerAreaUnit; + faceSampleNum = (int) floatSampleNum; + if(faceSampleNum>0) + faceSampleNum = SingleFaceSubdivision(faceSampleNum,b0,b1,b2,ps,*fi,randSample); + floatSampleNum -= (double) faceSampleNum; + } +} +//--------- +// Subdivision sampling of a single face. +// return number of added samples + +static int SingleFaceSubdivisionOld(int sampleNum, const CoordType & v0, const CoordType & v1, const CoordType & v2, VertexSampler &ps, FacePointer fp, bool randSample) +{ + // recursive face subdivision. + if(sampleNum == 1) + { + // ground case. + CoordType SamplePoint; + if(randSample) + { + CoordType rb=RandomBaricentric(); + SamplePoint=v0*rb[0]+v1*rb[1]+v2*rb[2]; + } + else SamplePoint=((v0+v1+v2)*(1.0f/3.0f)); + + CoordType SampleBary; +// int axis; +// if(fp->Flags() & FaceType::NORMX ) axis = 0; +// else if(fp->Flags() & FaceType::NORMY ) axis = 1; +// else { +// assert(fp->Flags() & FaceType::NORMZ) ; +// axis =2; +// } +// InterpolationParameters(*fp,axis,SamplePoint,SampleBary); + InterpolationParameters(*fp,SamplePoint,SampleBary[0],SampleBary[1],SampleBary[2]); + ps.AddFace(*fp,SampleBary); + return 1; + } + + int s0 = sampleNum /2; + int s1 = sampleNum-s0; + assert(s0>0); + assert(s1>0); + + ScalarType w0 = ScalarType(s1)/ScalarType(sampleNum); + ScalarType w1 = 1.0-w0; + // compute the longest edge. + ScalarType maxd01 = SquaredDistance(v0,v1); + ScalarType maxd12 = SquaredDistance(v1,v2); + ScalarType maxd20 = SquaredDistance(v2,v0); + int res; + if(maxd01 > maxd12) + if(maxd01 > maxd20) res = 0; + else res = 2; + else + if(maxd12 > maxd20) res = 1; + else res = 2; + + int faceSampleNum=0; + // break the input triangle along the midpoint of the longest edge. + CoordType pp; + switch(res) + { + case 0 : pp = v0*w0 + v1*w1; + faceSampleNum+=SingleFaceSubdivision(s0,v0,pp,v2,ps,fp,randSample); + faceSampleNum+=SingleFaceSubdivision(s1,pp,v1,v2,ps,fp,randSample); + break; + case 1 : pp = v1*w0 + v2*w1; + faceSampleNum+=SingleFaceSubdivision(s0,v0,v1,pp,ps,fp,randSample); + faceSampleNum+=SingleFaceSubdivision(s1,v0,pp,v2,ps,fp,randSample); + break; + case 2 : pp = v0*w0 + v2*w1; + faceSampleNum+=SingleFaceSubdivision(s0,v0,v1,pp,ps,fp,randSample); + faceSampleNum+=SingleFaceSubdivision(s1,pp,v1,v2,ps,fp,randSample); + break; + } + return faceSampleNum; +} + + +/// Compute a sampling of the surface where the points are regularly scattered over the face surface using a recursive longest-edge subdivision rule. +static void FaceSubdivisionOld(MetroMesh & m, VertexSampler &ps,int sampleNum, bool randSample) +{ + + ScalarType area = Stat::ComputeMeshArea(m); + ScalarType samplePerAreaUnit = sampleNum/area; + //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); + std::vector faceVec; + FillAndShuffleFacePointerVector(m,faceVec); + tri::UpdateNormals::PerFaceNormalized(m); + tri::UpdateFlags::FaceProjection(m); + double floatSampleNum = 0.0; + int faceSampleNum; + // Subdivision sampling. + typename std::vector::iterator fi; + for(fi=faceVec.begin(); fi!=faceVec.end(); fi++) + { + // compute # samples in the current face. + floatSampleNum += 0.5*DoubleArea(**fi) * samplePerAreaUnit; + faceSampleNum = (int) floatSampleNum; + if(faceSampleNum>0) + faceSampleNum = SingleFaceSubdivision(faceSampleNum,(**fi).V(0)->cP(), (**fi).V(1)->cP(), (**fi).V(2)->cP(),ps,*fi,randSample); + floatSampleNum -= (double) faceSampleNum; + } +} + + +//--------- + +// Similar Triangles sampling. +// Skip vertex and edges +// Sample per edges includes vertexes, so here we should expect n_samples_per_edge >=4 + +static int SingleFaceSimilar(FacePointer fp, VertexSampler &ps, int n_samples_per_edge) +{ + int n_samples=0; + int i, j; + float segmentNum=n_samples_per_edge -1 ; + float segmentLen = 1.0/segmentNum; + // face sampling. + for(i=1; i < n_samples_per_edge-1; i++) + for(j=1; j < n_samples_per_edge-1-i; j++) + { + //AddSample( v0 + (V1*(double)i + V2*(double)j) ); + CoordType sampleBary(i*segmentLen,j*segmentLen, 1.0 - (i*segmentLen+j*segmentLen) ) ; + n_samples++; + ps.AddFace(*fp,sampleBary); + } + return n_samples; +} +static int SingleFaceSimilarDual(FacePointer fp, VertexSampler &ps, int n_samples_per_edge, bool randomFlag) +{ + int n_samples=0; + float i, j; + float segmentNum=n_samples_per_edge -1 ; + float segmentLen = 1.0/segmentNum; + // face sampling. + for(i=0; i < n_samples_per_edge-1; i++) + for(j=0; j < n_samples_per_edge-1-i; j++) + { + //AddSample( v0 + (V1*(double)i + V2*(double)j) ); + CoordType V0((i+0)*segmentLen,(j+0)*segmentLen, 1.0 - ((i+0)*segmentLen+(j+0)*segmentLen) ) ; + CoordType V1((i+1)*segmentLen,(j+0)*segmentLen, 1.0 - ((i+1)*segmentLen+(j+0)*segmentLen) ) ; + CoordType V2((i+0)*segmentLen,(j+1)*segmentLen, 1.0 - ((i+0)*segmentLen+(j+1)*segmentLen) ) ; + n_samples++; + if(randomFlag) { + CoordType rb=RandomBaricentric(); + ps.AddFace(*fp, V0*rb[0]+V1*rb[1]+V2*rb[2]); + } else ps.AddFace(*fp,(V0+V1+V2)/3.0); + + if( j < n_samples_per_edge-i-2 ) + { + CoordType V3((i+1)*segmentLen,(j+1)*segmentLen, 1.0 - ((i+1)*segmentLen+(j+1)*segmentLen) ) ; + n_samples++; + if(randomFlag) { + CoordType rb=RandomBaricentric(); + ps.AddFace(*fp, V3*rb[0]+V1*rb[1]+V2*rb[2]); + } else ps.AddFace(*fp,(V3+V1+V2)/3.0); + } + } + return n_samples; +} + +// Similar sampling +// Each triangle is subdivided into similar triangles following a generalization of the classical 1-to-4 splitting rule of triangles. +// According to the level of subdivision you get 1, 4 , 9, 16 , triangles. +// Depending on the kind of the sampling strategies we can have two different approach to choosing the sample points. +// 1) you have already sampled both edges and vertices +// 2) you are not going to take samples on edges and vertices. +// +// In the first case you have to consider only internal vertices of the subdivided triangles (to avoid multiple sampling of edges and vertices). +// Therefore the number of internal points is ((k-3)*(k-2))/2. where k is the number of points on an edge (vertex included) +// E.g. for k=4 you get 3 segments on each edges and the original triangle is subdivided +// into 9 smaller triangles and you get (1*2)/2 == 1 only a single internal point. +// So if you want N samples in a triangle you have to solve k^2 -5k +6 - 2N = 0 +// from which you get: +// +// 5 + sqrt( 1 + 8N ) +// k = ------------------- +// 2 +// +// In the second case if you are not interested to skip the sampling on edges and vertices you have to consider as sample number the number of triangles. +// So if you want N samples in a triangle, the number of points on an edge (vertex included) should be simply: +// k = 1 + sqrt(N) +// examples: +// N = 4 -> k = 3 +// N = 9 -> k = 4 + + + +//template +//void Sampling::SimilarFaceSampling() +static void FaceSimilar(MetroMesh & m, VertexSampler &ps,int sampleNum, bool dualFlag, bool randomFlag) +{ + ScalarType area = Stat::ComputeMeshArea(m); + ScalarType samplePerAreaUnit = sampleNum/area; + + // Similar Triangles sampling. + int n_samples_per_edge; + double n_samples_decimal = 0.0; + FaceIterator fi; + + for(fi=m.face.begin(); fi != m.face.end(); fi++) + { + // compute # samples in the current face. + n_samples_decimal += 0.5*DoubleArea(*fi) * samplePerAreaUnit; + int n_samples = (int) n_samples_decimal; + if(n_samples>0) + { + // face sampling. + if(dualFlag) + { + n_samples_per_edge = (int)((sqrt(1.0+8.0*(double)n_samples) +5.0)/2.0); // original for non dual case + n_samples = SingleFaceSimilar(&*fi,ps, n_samples_per_edge); + } else { + n_samples_per_edge = (int)(sqrt((double)n_samples) +1.0); + n_samples = SingleFaceSimilarDual(&*fi,ps, n_samples_per_edge,randomFlag); + } + } + n_samples_decimal -= (double) n_samples; + } +} + + + // Rasterization fuction + // Take a triangle + // T deve essere una classe funzionale che ha l'operatore () + // con due parametri x,y di tipo S esempio: + // class Foo { public void operator()(int x, int y ) { ??? } }; + +// This function does rasterization with a safety buffer area, thus accounting some points actually outside triangle area +// The safety area samples are generated according to face flag BORDER which should be true for texture space border edges +// Use correctSafePointsBaryCoords = true to map safety texels to closest point barycentric coords (on edge). + static void SingleFaceRaster(typename MetroMesh::FaceType &f, VertexSampler &ps, + const Point2 & v0, + const Point2 & v1, + const Point2 & v2, + bool correctSafePointsBaryCoords=true) + { + typedef typename MetroMesh::ScalarType S; + // Calcolo bounding box + Box2i bbox; + Box2 bboxf; + bboxf.Add(v0); + bboxf.Add(v1); + bboxf.Add(v2); + + bbox.min[0] = floor(bboxf.min[0]); + bbox.min[1] = floor(bboxf.min[1]); + bbox.max[0] = ceil(bboxf.max[0]); + bbox.max[1] = ceil(bboxf.max[1]); + + // Calcolo versori degli spigoli + Point2 d10 = v1 - v0; + Point2 d21 = v2 - v1; + Point2 d02 = v0 - v2; + + // Preparazione prodotti scalari + S b0 = (bbox.min[0]-v0[0])*d10[1] - (bbox.min[1]-v0[1])*d10[0]; + S b1 = (bbox.min[0]-v1[0])*d21[1] - (bbox.min[1]-v1[1])*d21[0]; + S b2 = (bbox.min[0]-v2[0])*d02[1] - (bbox.min[1]-v2[1])*d02[0]; + // Preparazione degli steps + S db0 = d10[1]; + S db1 = d21[1]; + S db2 = d02[1]; + // Preparazione segni + S dn0 = -d10[0]; + S dn1 = -d21[0]; + S dn2 = -d02[0]; + + //Calculating orientation + bool flipped = !(d02 * vcg::Point2(-d10[1], d10[0]) >= 0); + + // Calculating border edges + Segment2 borderEdges[3]; + S edgeLength[3]; + unsigned char edgeMask = 0; + + if (f.IsB(0)) { + borderEdges[0] = Segment2(v0, v1); + edgeLength[0] = borderEdges[0].Length(); + edgeMask |= 1; + } + if (f.IsB(1)) { + borderEdges[1] = Segment2(v1, v2); + edgeLength[1] = borderEdges[1].Length(); + edgeMask |= 2; + } + if (f.IsB(2)) { + borderEdges[2] = Segment2(v2, v0); + edgeLength[2] = borderEdges[2].Length(); + edgeMask |= 4; + } + + // Rasterizzazione + double de = v0[0]*v1[1]-v0[0]*v2[1]-v1[0]*v0[1]+v1[0]*v2[1]-v2[0]*v1[1]+v2[0]*v0[1]; + + for(int x=bbox.min[0]-1;x<=bbox.max[0]+1;++x) + { + bool in = false; + S n[3] = { b0-db0-dn0, b1-db1-dn1, b2-db2-dn2}; + for(int y=bbox.min[1]-1;y<=bbox.max[1]+1;++y) + { + if((n[0]>=0 && n[1]>=0 && n[2]>=0) || (n[0]<=0 && n[1]<=0 && n[2]<=0)) + { + typename MetroMesh::CoordType baryCoord; + baryCoord[0] = double(-y*v1[0]+v2[0]*y+v1[1]*x-v2[0]*v1[1]+v1[0]*v2[1]-x*v2[1])/de; + baryCoord[1] = -double( x*v0[1]-x*v2[1]-v0[0]*y+v0[0]*v2[1]-v2[0]*v0[1]+v2[0]*y)/de; + baryCoord[2] = 1-baryCoord[0]-baryCoord[1]; + + ps.AddTextureSample(f, baryCoord, Point2i(x,y), 0); + in = true; + } else { + // Check whether a pixel outside (on a border edge side) triangle affects color inside it + Point2 px(x, y); + Point2 closePoint; + int closeEdge = -1; + S minDst = FLT_MAX; + + // find the closest point (on some edge) that lies on the 2x2 squared neighborhood of the considered point + for (int i=0; i<3; ++i) + { + if (edgeMask & (1 << i)) + { + Point2 close; + S dst; + if ( ((!flipped) && (n[i]<0)) || + ( flipped && (n[i]>0)) ) + { + dst = ((close = ClosestPoint(borderEdges[i], px)) - px).Norm(); + if(dst < minDst && + close.X() > px.X()-1 && close.X() < px.X()+1 && + close.Y() > px.Y()-1 && close.Y() < px.Y()+1) + { + minDst = dst; + closePoint = close; + closeEdge = i; + } + } + } + } + + if (closeEdge >= 0) + { + typename MetroMesh::CoordType baryCoord; + if (correctSafePointsBaryCoords) + { + // Add x,y sample with closePoint barycentric coords (on edge) + baryCoord[closeEdge] = (closePoint - borderEdges[closeEdge].P(1)).Norm()/edgeLength[closeEdge]; + baryCoord[(closeEdge+1)%3] = 1 - baryCoord[closeEdge]; + baryCoord[(closeEdge+2)%3] = 0; + } else { + // Add x,y sample with his own barycentric coords (off edge) + baryCoord[0] = double(-y*v1[0]+v2[0]*y+v1[1]*x-v2[0]*v1[1]+v1[0]*v2[1]-x*v2[1])/de; + baryCoord[1] = -double( x*v0[1]-x*v2[1]-v0[0]*y+v0[0]*v2[1]-v2[0]*v0[1]+v2[0]*y)/de; + baryCoord[2] = 1-baryCoord[0]-baryCoord[1]; + } + ps.AddTextureSample(f, baryCoord, Point2i(x,y), minDst); + in = true; + } + } + n[0] += dn0; + n[1] += dn1; + n[2] += dn2; + } + b0 += db0; + b1 += db1; + b2 += db2; + } +} + +// Generate a random point in volume defined by a box with uniform distribution +static CoordType RandomBox(vcg::Box3 box) +{ + CoordType p = box.min; + p[0] += box.Dim()[0] * RandomDouble01(); + p[1] += box.Dim()[1] * RandomDouble01(); + p[2] += box.Dim()[2] * RandomDouble01(); + return p; +} + +// generate Poisson-disk sample using a set of pre-generated samples (with the Montecarlo algorithm) +// It always return a point. +static VertexPointer getPrecomputedMontecarloSample(Point3i &cell, MontecarloSHT & samplepool) +{ + MontecarloSHTIterator cellBegin; + MontecarloSHTIterator cellEnd; + samplepool.Grid(cell, cellBegin, cellEnd); + return *cellBegin; +} + +// check the radius constrain +static bool checkPoissonDisk(MetroMesh & vmesh, SampleSHT & sht, const Point3 & p, ScalarType radius) +{ + // get the samples closest to the given one + std::vector closests; + + typedef VertTmark MarkerVert; + static MarkerVert mv; + + Box3f bb(p-Point3f(radius,radius,radius),p+Point3f(radius,radius,radius)); + int nsamples = GridGetInBox(sht, mv, bb, closests); + + ScalarType r2 = radius*radius; + for(int i=0; icP()) < r2) + return false; + + return true; +} + +struct PoissonDiskParam +{ + PoissonDiskParam() + { + adaptiveRadiusFlag = false; + radiusVariance =1; + MAXLEVELS = 5; + invertQuality = false; + preGenFlag = false; + preGenMesh = NULL; + } + bool adaptiveRadiusFlag; + float radiusVariance; + bool invertQuality; + bool preGenFlag; // when generating a poisson distribution, you can initialize the set pof omputed points with ALL the vertices of another mesh. Usefull for building progressive refinements. + MetroMesh *preGenMesh; + int MAXLEVELS; +}; + +static ScalarType ComputePoissonDiskRadius(MetroMesh &origMesh, int sampleNum) +{ + ScalarType meshArea = Stat::ComputeMeshArea(origMesh); + // Manage approximately the PointCloud Case, use the half a area of the bbox. + // TODO: If you had the radius a much better approximation could be done. + if(meshArea ==0) + { + meshArea = (origMesh.bbox.DimX()*origMesh.bbox.DimY() + + origMesh.bbox.DimX()*origMesh.bbox.DimZ() + + origMesh.bbox.DimY()*origMesh.bbox.DimZ()); + } + ScalarType diskRadius = sqrt(meshArea / (0.7 * M_PI * sampleNum)); // 0.7 is a density factor + return diskRadius; +} + +static int ComputePoissonSampleNum(MetroMesh &origMesh, ScalarType diskRadius) +{ + ScalarType meshArea = Stat::ComputeMeshArea(origMesh); + int sampleNum = meshArea / (diskRadius*diskRadius *M_PI *0.7) ; // 0.7 is a density factor + return sampleNum; +} + +static void ComputePoissonSampleRadii(MetroMesh &sampleMesh, ScalarType diskRadius, ScalarType radiusVariance, bool invert) +{ + VertexIterator vi; + std::pair minmax = tri::Stat::ComputePerVertexQualityMinMax( sampleMesh); + float minRad = diskRadius / radiusVariance; + float maxRad = diskRadius * radiusVariance; + float deltaQ = minmax.second-minmax.first; + float deltaRad = maxRad-minRad; + for (vi = sampleMesh.vert.begin(); vi != sampleMesh.vert.end(); vi++) + { + (*vi).Q() = minRad + deltaRad*((invert ? minmax.second - (*vi).Q() : (*vi).Q() - minmax.first )/deltaQ); + } +} + +// Trivial approach that puts all the samples in a UG and removes all the ones that surely do not fit the +static void PoissonDiskPruning(MetroMesh &origMesh, VertexSampler &ps, MetroMesh &montecarloMesh, ScalarType diskRadius, const struct PoissonDiskParam pp=PoissonDiskParam()) +{ + // spatial index of montecarlo samples - used to choose a new sample to insert + MontecarloSHT montecarloSHT; + // initialize spatial hash table for searching + // radius is the radius of empty disk centered over the samples (e.g. twice of the empty space disk) + // This radius implies that when we pick a sample in a cell all that cell will not be touched again. + ScalarType cellsize = 2.0f* diskRadius / sqrt(3.0); + + // inflating + origMesh.bbox.Offset(cellsize); + + int sizeX = std::max(1.0f,origMesh.bbox.DimX() / cellsize); + int sizeY = std::max(1.0f,origMesh.bbox.DimY() / cellsize); + int sizeZ = std::max(1.0f,origMesh.bbox.DimZ() / cellsize); + Point3i gridsize(sizeX, sizeY, sizeZ); +#ifdef QT_VERSION + qDebug("PDS: radius %f Grid:(%i %i %i) ",diskRadius,sizeX,sizeY,sizeZ); + QTime tt; tt.start(); +#endif + + // if we are doing variable density sampling we have to prepare the random samples quality with the correct expected radii. + if(pp.adaptiveRadiusFlag) + ComputePoissonSampleRadii(montecarloMesh, diskRadius, pp.radiusVariance, pp.invertQuality); + + montecarloSHT.InitEmpty(origMesh.bbox, gridsize); + + for (VertexIterator vi = montecarloMesh.vert.begin(); vi != montecarloMesh.vert.end(); vi++) + montecarloSHT.Add(&(*vi)); + + montecarloSHT.UpdateAllocatedCells(); + + + unsigned int (*p_myrandom)(unsigned int) = RandomInt; + std::random_shuffle(montecarloSHT.AllocatedCells.begin(),montecarloSHT.AllocatedCells.end(), p_myrandom); + +#ifdef QT_VERSION + qDebug("PDS: Completed creation of activeCells, %i cells (%i msec)", (int)montecarloSHT.AllocatedCells.size(), tt.restart()); +#endif +int removedCnt=0; + if(pp.preGenFlag) + { + // Initial pass for pruning the Hashed grid with the an eventual pre initialized set of samples + for(VertexIterator vi =pp.preGenMesh->vert.begin(); vi!=pp.preGenMesh->vert.end();++vi) + { + ps.AddVert(*vi); + removedCnt += montecarloSHT.RemoveInSphere(vi->cP(),diskRadius); + } + montecarloSHT.UpdateAllocatedCells(); +#ifdef QT_VERSION + qDebug("Removed %i samples in %i",removedCnt,tt.restart()); +#endif + } + while(!montecarloSHT.AllocatedCells.empty()) + { + removedCnt=0; + for (size_t i = 0; i < montecarloSHT.AllocatedCells.size(); i++) + { + if( montecarloSHT.EmptyCell(montecarloSHT.AllocatedCells[i]) ) continue; + VertexPointer sp = getPrecomputedMontecarloSample(montecarloSHT.AllocatedCells[i], montecarloSHT); + ps.AddVert(*sp); + ScalarType sampleRadius = diskRadius; + if(pp.adaptiveRadiusFlag) sampleRadius = sp->Q(); + removedCnt += montecarloSHT.RemoveInSphere(sp->cP(),sampleRadius); + } + +#ifdef QT_VERSION + qDebug("Removed %i samples in %i",removedCnt,tt.restart()); +#endif + montecarloSHT.UpdateAllocatedCells(); + } +} + +/** Compute a Poisson-disk sampling of the surface. + * The radius of the disk is computed according to the estimated sampling density. + * + * This algorithm is an adaptation of the algorithm of White et al. : + * + * "Poisson Disk Point Set by Hierarchical Dart Throwing" + * K. B. White, D. Cline, P. K. Egbert, + * IEEE Symposium on Interactive Ray Tracing, 2007, + * 10-12 Sept. 2007, pp. 129-132. + */ +static void PoissonDisk(MetroMesh &origMesh, VertexSampler &ps, MetroMesh &montecarloMesh, ScalarType diskRadius, const struct PoissonDiskParam pp=PoissonDiskParam()) +{ + + // spatial index of montecarlo samples - used to choose a new sample to insert + MontecarloSHT montecarloSHTVec[5]; + + + + // initialize spatial hash table for searching + // radius is the radius of empty disk centered over the samples (e.g. twice of the empty space disk) + // This radius implies that when we pick a sample in a cell all that cell will not be touched again. + ScalarType cellsize = 2.0f* diskRadius / sqrt(3.0); + + // inflating + origMesh.bbox.Offset(cellsize); + + int sizeX = std::max(1.0f,origMesh.bbox.DimX() / cellsize); + int sizeY = std::max(1.0f,origMesh.bbox.DimY() / cellsize); + int sizeZ = std::max(1.0f,origMesh.bbox.DimZ() / cellsize); + Point3i gridsize(sizeX, sizeY, sizeZ); +#ifdef QT_VERSION + qDebug("PDS: radius %f Grid:(%i %i %i) ",diskRadius,sizeX,sizeY,sizeZ); + QTime tt; tt.start(); +#endif + + // spatial hash table of the generated samples - used to check the radius constrain + SampleSHT checkSHT; + checkSHT.InitEmpty(origMesh.bbox, gridsize); + + + // sampling algorithm + // ------------------ + // + // - generate millions of samples using montecarlo algorithm + // - extract a cell (C) from the active cell list (with probability proportional to cell's volume) + // - generate a sample inside C by choosing one of the contained pre-generated samples + // - if the sample violates the radius constrain discard it, and add the cell to the cells-to-subdivide list + // - iterate until the active cell list is empty or a pre-defined number of subdivisions is reached + // + + int level = 0; + + // initialize spatial hash to index pre-generated samples + montecarloSHTVec[0].InitEmpty(origMesh.bbox, gridsize); + // create active cell list + for (VertexIterator vi = montecarloMesh.vert.begin(); vi != montecarloMesh.vert.end(); vi++) + montecarloSHTVec[0].Add(&(*vi)); + montecarloSHTVec[0].UpdateAllocatedCells(); + + // if we are doing variable density sampling we have to prepare the random samples quality with the correct expected radii. + if(pp.adaptiveRadiusFlag) + ComputePoissonSampleRadii(montecarloMesh, diskRadius, pp.radiusVariance, pp.invertQuality); + + do + { + MontecarloSHT &montecarloSHT = montecarloSHTVec[level]; + + if(level>0) + {// initialize spatial hash with the remaining points + montecarloSHT.InitEmpty(origMesh.bbox, gridsize); + // create active cell list + for (typename MontecarloSHT::HashIterator hi = montecarloSHTVec[level-1].hash_table.begin(); hi != montecarloSHTVec[level-1].hash_table.end(); hi++) + montecarloSHT.Add((*hi).second); + montecarloSHT.UpdateAllocatedCells(); + } + // shuffle active cells + unsigned int (*p_myrandom)(unsigned int) = RandomInt; + std::random_shuffle(montecarloSHT.AllocatedCells.begin(),montecarloSHT.AllocatedCells.end(), p_myrandom); +#ifdef QT_VERSION + qDebug("PDS: Init of Hashing grid %i cells and %i samples (%i msec)", montecarloSHT.AllocatedCells.size(), montecarloSHT.hash_table.size(), tt.restart()); +#endif + + // generate a sample inside C by choosing one of the contained pre-generated samples + ////////////////////////////////////////////////////////////////////////////////////////// + int removedCnt=montecarloSHT.hash_table.size(); + int addedCnt=checkSHT.hash_table.size(); + for (int i = 0; i < montecarloSHT.AllocatedCells.size(); i++) + { + for(int j=0;j<4;j++) + { + if( montecarloSHT.EmptyCell(montecarloSHT.AllocatedCells[i]) ) continue; + + // generate a sample chosen from the pre-generated one + typename MontecarloSHT::HashIterator hi = montecarloSHT.hash_table.find(montecarloSHT.AllocatedCells[i]); + + if(hi==montecarloSHT.hash_table.end()) {break;} + VertexPointer sp = (*hi).second; + // vr spans between 3.0*r and r / 4.0 according to vertex quality + ScalarType sampleRadius = diskRadius; + if(pp.adaptiveRadiusFlag) sampleRadius = sp->Q(); + if (checkPoissonDisk(*ps.m, checkSHT, sp->cP(), sampleRadius)) + { + ps.AddVert(*sp); + montecarloSHT.RemoveCell(sp); + checkSHT.Add(sp); + break; + } + else + montecarloSHT.RemovePunctual(sp); + } + } + addedCnt = checkSHT.hash_table.size()-addedCnt; + removedCnt = removedCnt-montecarloSHT.hash_table.size(); + + // proceed to the next level of subdivision + // increase grid resolution + gridsize *= 2; + + // +#ifdef QT_VERSION + qDebug("PDS: Pruning %i added %i and removed %i samples (%i msec)",level,addedCnt, removedCnt,tt.restart()); +#endif + level++; + } while(level < 5); +} + +//template +//void Sampling::SimilarFaceSampling() + +// This function also generates samples outside faces if those affects faces in texture space. +// Use correctSafePointsBaryCoords = true to map safety texels to closest point barycentric coords (on edge) +// otherwise obtained samples will map to barycentric coord actually outside face +// +// If you don't need to get those extra points clear faces Border Flags +// vcg::tri::UpdateFlags::FaceClearB(m); +// +// Else make sure to update border flags from texture space FFadj +// vcg::tri::UpdateTopology::FaceFaceFromTexCoord(m); +// vcg::tri::UpdateFlags::FaceBorderFromFF(m); +static void Texture(MetroMesh & m, VertexSampler &ps, int textureWidth, int textureHeight, bool correctSafePointsBaryCoords=true) +{ + FaceIterator fi; + + printf("Similar Triangles face sampling\n"); + for(fi=m.face.begin(); fi != m.face.end(); fi++) + if (!fi->IsD()) + { + Point2f ti[3]; + for(int i=0;i<3;++i) + ti[i]=Point2f((*fi).WT(i).U() * textureWidth - 0.5, (*fi).WT(i).V() * textureHeight - 0.5); + // - 0.5 constants are used to obtain correct texture mapping + + SingleFaceRaster(*fi, ps, ti[0],ti[1],ti[2], correctSafePointsBaryCoords); + } +} + +typedef GridStaticPtr TriMeshGrid; + +class RRParam +{ +public: +float offset; +float minDiag; +tri::FaceTmark markerFunctor; +TriMeshGrid gM; +}; + +static void RegularRecursiveOffset(MetroMesh & m, std::vector &pvec, ScalarType offset, float minDiag) +{ + Box3 bb=m.bbox; + bb.Offset(offset*2.0); + + RRParam rrp; + + rrp.markerFunctor.SetMesh(&m); + + rrp.gM.Set(m.face.begin(),m.face.end(),bb); + + + rrp.offset=offset; + rrp.minDiag=minDiag; + SubdivideAndSample(m, pvec, bb, rrp, bb.Diag()); +} + +static void SubdivideAndSample(MetroMesh & m, std::vector &pvec, const Box3 bb, RRParam &rrp, float curDiag) +{ + Point3f startPt = bb.Center(); + + ScalarType dist; + // Compute mesh point nearest to bb center + FaceType *nearestF=0; + float dist_upper_bound = curDiag+rrp.offset; + Point3f closestPt; + vcg::face::PointDistanceBaseFunctor PDistFunct; + dist=dist_upper_bound; + nearestF = rrp.gM.GetClosest(PDistFunct,rrp.markerFunctor,startPt,dist_upper_bound,dist,closestPt); + curDiag /=2; + if(dist < dist_upper_bound) + { + if(curDiag/3 < rrp.minDiag) //store points only for the last level of recursion (?) + { + if(rrp.offset==0) + pvec.push_back(closestPt); + else + { + if(dist>rrp.offset) // points below the offset threshold cannot be displaced at the right offset distance, we can only make points nearer. + { + Point3f delta = startPt-closestPt; + pvec.push_back(closestPt+delta*(rrp.offset/dist)); + } + } + } + if(curDiag < rrp.minDiag) return; + Point3f hs = (bb.max-bb.min)/2; + for(int i=0;i<2;i++) + for(int j=0;j<2;j++) + for(int k=0;k<2;k++) + SubdivideAndSample(m,pvec, + Box3f(Point3f( bb.min[0]+i*hs[0], bb.min[1]+j*hs[1], bb.min[2]+k*hs[2]), + Point3f(startPt[0]+i*hs[0],startPt[1]+j*hs[1],startPt[2]+k*hs[2])),rrp,curDiag); + + } +} +}; // end class + + +} // end namespace tri +} // end namespace vcg + +#endif + diff --git a/vcg/complex/algorithms/polygon_support.h b/vcg/complex/algorithms/polygon_support.h new file mode 100644 index 00000000..0ef331c1 --- /dev/null +++ b/vcg/complex/algorithms/polygon_support.h @@ -0,0 +1,178 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCGLIB_POLYGON_SUPPORT +#define __VCGLIB_POLYGON_SUPPORT + +#include +#include +#include +#include + +namespace vcg +{ + namespace tri{ + /// \ingroup trimesh + + /// \headerfile polygon_support.h vcg/complex/trimesh/polygon_support.h + + /// \brief This class is used convert between polygonal meshes and triangular meshes + + /** + This class contains two members that allow to build a triangular mesh from a polygonal mesh + and viceversa. In a trimesh, the generic polygons with n sides are codified represented by tagging the internal edge of the face + with the SetF. + */ + + template + struct PolygonSupport{ + + /** + Import a trianglemesh from a polygon mesh + **/ + static void ImportFromPolyMesh(TriMeshType & tm, PolyMeshType & pm){ + std::vector points; + std::vector faces; + + // the vertices are the same, simply import them + typename PolyMeshType::VertexIterator vi; + typename TriMeshType::FaceIterator tfi,tfib ; + typename TriMeshType ::VertexIterator tvi = Allocator::AddVertices(tm,pm.vert.size()); + int cnt = 0; + for(tvi = tm.vert.begin(),vi = pm.vert.begin(); tvi != tm.vert.end(); ++tvi,++vi,++cnt) + if(!(*vi).IsD()) (*tvi).ImportData(*vi); else vcg::tri::Allocator::DeleteVertex(tm,(*tvi)); + + typename PolyMeshType::FaceIterator fi; + for(fi = pm.face.begin(); fi != pm.face.end(); ++fi) + if(!((*fi).IsD())){ + points.clear(); + for(int i = 0; i < (*fi).VN(); ++i) { + typename PolyMeshType::VertexType * v = (*fi).V(i); + points.push_back(v->P()); + } + faces.clear(); + TessellatePlanarPolygon3(points,faces); + tfib = tfi = Allocator::AddFaces(tm,faces.size()/3); + for(int i = 0; tfi != tm.face.end();++tfi){ + (*tfi).V(0) = &tm.vert[ (*fi).V( faces[i] ) - &(*pm.vert.begin())]; + (*tfi).V(1) = &tm.vert[ (*fi).V( faces[i+1]) - &(*pm.vert.begin())]; + (*tfi).V(2) = &tm.vert[ (*fi).V( faces[i+2]) - &(*pm.vert.begin())]; + // set the F flags + if( (faces[i]+1)%points.size() != faces[i+1]) (*tfi).SetF(0); + if( (faces[i+1]+1)%points.size() != faces[i+2]) (*tfi).SetF(1); + if( (faces[i+2]+1)%points.size() != faces[i]) (*tfi).SetF(2); + i+=3; + } + + } + } + + + /** + Import a polygon mesh from a triangle mesh + **/ + static void ImportFromTriMesh( PolyMeshType & pm, TriMeshType & tm){ + + // the vertices are the same, simply import them + int cnt = 0; + typename TriMeshType ::ConstVertexIterator tvi; + typename PolyMeshType::VertexIterator vi = vcg::tri::Allocator::AddVertices(pm,tm.vert.size()); + for(tvi = tm.vert.begin(); tvi != tm.vert.end(); ++tvi,++vi,++cnt) + if(!(*tvi).IsD())(*vi).ImportData(*tvi); else vcg::tri::Allocator ::DeleteVertex(pm,(*vi)); + + // convert the faces + typename TriMeshType::FaceIterator tfi; + vcg::face::JumpingPos p; + + for( tfi = tm.face.begin(); tfi != tm.face.end(); ++tfi) if(!(*tfi).IsD() && !(*tfi).IsV()) + { + std::vector vs;// vertices of the polygon + std::vector fs;// triangle faces corresponding to the polygon + + + // find a non tagged edge + int se = 0; + for(;se < 3;++se) if (!(*tfi).IsF(se)) break; + + // initialize a pos on the first non tagged edge + typename TriMeshType::VertexPointer v0 = (*tfi).V(se); + p.F() = &(*tfi); + p.E() = se; + p.V() = p.F()->V(p.F()->Next(se)); + p.FlipE(); + + vs.push_back(p.F()->V(se)); + + do{ + while(p.F()->IsF(p.E())) { fs.push_back(p.F()); p.FlipF(); p.FlipE(); p.F()->SetV();} + vs.push_back(p.F()->V(p.E())); + p.FlipV(); + p.FlipE(); + } while( p.V() != v0 ); + + //now vs contains all the vertices of the polygon (still in the trimesh) + typename PolyMeshType::FaceIterator pfi = vcg::tri::Allocator::AddFaces(pm,1); + (*pfi).Alloc(vs.size()); + for( int i = 0 ; i < vs.size(); ++i) + (*pfi).V(i) = ( typename PolyMeshType::VertexType*) & pm.vert[vs[i]-&(*tm.vert.begin())]; + // here handle the other compoenents of the face (how the conponents of the n triangle faces goes in the + // the property of the polygon (e.g. the normal, the color, the quality and so on) + // TODO + + } + } + + static void ExtractPolygon(typename TriMeshType::FacePointer tfi, std::vector &vs){ + vs.clear(); + // find a non tagged edge + int se = -1; + for(int i=0; i<3; i++) if (!( tfi->IsF(i))) { se = i; break;} + + assert(se!=-1); // else, all faux edges! + + // initialize a pos on the first non faux edge + typename TriMeshType::VertexPointer v0 = tfi->V(se); + + vcg::face::JumpingPos p; + + p.F() = tfi; + p.E() = se; + p.V() = p.F()->V(p.F()->Next(se)); + p.FlipE(); + + vs.push_back(p.F()->V(se)); + + int guard = 0; + do{ + while(p.F()->IsF(p.E())) { p.FlipF(); p.FlipE(); p.F()->SetV(); if (guard++>10) break;} + if (guard++>10) break; + vs.push_back(p.F()->V(p.E())); + p.FlipV(); + p.FlipE(); + } while( p.V() != v0 ); + } + +}; // end of struct +}} // end namespace vcg::tri + +#endif // __VCGLIB_TRI_CLIP diff --git a/vcg/complex/algorithms/refine.h b/vcg/complex/algorithms/refine.h new file mode 100644 index 00000000..49ee79e3 --- /dev/null +++ b/vcg/complex/algorithms/refine.h @@ -0,0 +1,924 @@ +/***********F***************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCGLIB_REFINE +#define __VCGLIB_REFINE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcg{ + +/* A very short intro about the generic refinement framework, + the main fuction is the + + template + bool RefineE(MESH_TYPE &m, MIDPOINT mid, EDGEPRED ep,bool RefineSelected=false, CallBackPos *cb = 0) + + You have to provide two functor objects to this, one for deciding what edge has to be spltted and one to decide position and new values for the attributes of the new point. + + for example the minimal EDGEPRED is + + template class EdgeLen + { + public: + FLT thr2; + bool operator()(face::Pos ep) const + { + return SquaredDistance(ep.f->V(ep.z)->P(), ep.f->V1(ep.z)->P())>thr2; + } + }; + + With a bit of patience you can customize to make also slicing operation. + +*/ + + +/* The table which encodes how to subdivide a triangle depending + on the splitted edges is organized as such: + + TriNum (the first number): encodes the number of triangles + TV (the following 4 triples): encodes the resulting triangles where + 0, 1, 2 are the original vertices of the triangles and 3, 4, 5 + (mp01, mp12, mp20) are the midpoints of the three edges. + + In the case two edges are splitted the triangle has 2 possible splittings: +we need to choose a diagonal of the resulting trapezoid. +'swap' encodes the two diagonals to test: if diag1 < diag2 we swap the diagonal +like this (140, 504 -> 150, 514) (the second vertex of each triangles is replaced + by the first vertex of the other one). + 2 + / \ + 5---4 + / \ + 0-------1 + +*/ + +class Split { +public: + int TriNum; // number of triangles + int TV[4][3]; // The triangles coded as the following convention + // 0..2 vertici originali del triangolo + // 3..5 mp01, mp12, mp20 midpoints of the three edges + int swap[2][2]; // the two diagonals to test for swapping + int TE[4][3]; // the edge-edge correspondence between refined triangles and the old one + // (3) means the edge of the new triangle is internal; +}; + +const Split SplitTab[8]={ +/* m20 m12 m01 */ +/* 0 0 0 */ {1, {{0,1,2},{0,0,0},{0,0,0},{0,0,0}}, {{0,0},{0,0}}, {{0,1,2},{0,0,0},{0,0,0},{0,0,0}} }, +/* 0 0 1 */ {2, {{0,3,2},{3,1,2},{0,0,0},{0,0,0}}, {{0,0},{0,0}}, {{0,3,2},{0,1,3},{0,0,0},{0,0,0}} }, +/* 0 1 0 */ {2, {{0,1,4},{0,4,2},{0,0,0},{0,0,0}}, {{0,0},{0,0}}, {{0,1,3},{3,1,2},{0,0,0},{0,0,0}} }, +/* 0 1 1 */ {3, {{3,1,4},{0,3,2},{4,2,3},{0,0,0}}, {{0,4},{3,2}}, {{0,1,3},{0,3,2},{1,3,3},{0,0,0}} }, +/* 1 0 0 */ {2, {{0,1,5},{5,1,2},{0,0,0},{0,0,0}}, {{0,0},{0,0}}, {{0,3,2},{3,1,2},{0,0,0},{0,0,0}} }, +/* 1 0 1 */ {3, {{0,3,5},{3,1,5},{2,5,1},{0,0,0}}, {{3,2},{5,1}}, {{0,3,2},{0,3,3},{2,3,1},{0,0,0}} }, +/* 1 1 0 */ {3, {{2,5,4},{0,1,5},{4,5,1},{0,0,0}}, {{0,4},{5,1}}, {{2,3,1},{0,3,2},{3,3,1},{0,0,0}} }, +/* 1 1 1 */ //{4, {{0,3,5},{3,1,4},{5,4,2},{3,4,5}}, {{0,0},{0,0}}, {{0,3,2},{0,1,3},{3,1,2},{3,3,3}} }, +/* 1 1 1 */ {4, {{3,4,5},{0,3,5},{3,1,4},{5,4,2}}, {{0,0},{0,0}}, {{3,3,3},{0,3,2},{0,1,3},{3,1,2}} }, +}; + +// Basic subdivision class +// This class must provide methods for finding the position of the newly created vertices +// In this implemenation we simply put the new vertex in the MidPoint position. +// Color and TexCoords are interpolated accordingly. +template +struct MidPoint : public std::unary_function , typename MESH_TYPE::CoordType > +{ + MidPoint(MESH_TYPE *_mp) { mp=_mp; } + + MESH_TYPE *mp; + + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep){ + assert(mp); + nv.P()= (ep.f->V(ep.z)->P()+ep.f->V1(ep.z)->P())/2.0; + + if( MESH_TYPE::HasPerVertexNormal()) + nv.N()= (ep.f->V(ep.z)->N()+ep.f->V1(ep.z)->N()).normalized(); + + if( MESH_TYPE::HasPerVertexColor()) + nv.C().lerp(ep.f->V(ep.z)->C(),ep.f->V1(ep.z)->C(),.5f); + + if( MESH_TYPE::HasPerVertexQuality()) + nv.Q() = ((ep.f->V(ep.z)->Q()+ep.f->V1(ep.z)->Q())) / 2.0; + + if( tri::HasPerVertexTexCoord(*mp)) + nv.T().P() = ((ep.f->V(ep.z)->T().P()+ep.f->V1(ep.z)->T().P())) / 2.0; + } + + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + assert(t0.n()== t1.n()); + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } +}; + + + +template +struct MidPointArc : public std::unary_function , typename MESH_TYPE::CoordType> +{ + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep) + { + const typename MESH_TYPE::ScalarType EPS =1e-10; + typename MESH_TYPE::CoordType vp = (ep.f->V(ep.z)->P()+ep.f->V1(ep.z)->P())/2.0; + typename MESH_TYPE::CoordType n = (ep.f->V(ep.z)->N()+ep.f->V1(ep.z)->N())/2.0; + typename MESH_TYPE::ScalarType w =n.Norm(); + if(wV(ep.z)->P()+ep.f->V1(ep.z)->P())/2.0; return;} + n/=w; + typename MESH_TYPE::CoordType d0 = ep.f->V(ep.z)->P() - vp; + typename MESH_TYPE::CoordType d1 = ep.f->V1(ep.z)->P()- vp; + typename MESH_TYPE::ScalarType d=Distance(ep.f->V(ep.z)->P(),ep.f->V1(ep.z)->P())/2.0; + + typename MESH_TYPE::CoordType nn = ep.f->V1(ep.z)->N() ^ ep.f->V(ep.z)->N(); + typename MESH_TYPE::CoordType np = n ^ d0; //vector perpendicular to the edge plane, normal is interpolated + np.Normalize(); + double sign=1; + if(np*nn<0) sign=-1; // se le normali non divergono sposta il punto nella direzione opposta + + typename MESH_TYPE::CoordType n0=ep.f->V(ep.z)->N() -np*(ep.f->V(ep.z)->N()*np); + n0.Normalize(); + typename MESH_TYPE::CoordType n1=ep.f->V1(ep.z)->N()-np*(ep.f->V1(ep.z)->N()*np); + assert(n1.Norm()>EPS); + n1.Normalize(); + typename MESH_TYPE::ScalarType cosa0=n0*n; + typename MESH_TYPE::ScalarType cosa1=n1*n; + if(2-cosa0-cosa1V(ep.z)->P()+ep.f->V1(ep.z)->P())/2.0;return;} + typename MESH_TYPE::ScalarType cosb0=(d0*n)/d; + typename MESH_TYPE::ScalarType cosb1=(d1*n)/d; + assert(1+cosa0>EPS); + assert(1+cosa1>EPS); + typename MESH_TYPE::ScalarType delta0=d*(cosb0 +sqrt( ((1-cosb0*cosb0)*(1-cosa0))/(1+cosa0)) ); + typename MESH_TYPE::ScalarType delta1=d*(cosb1 +sqrt( ((1-cosb1*cosb1)*(1-cosa1))/(1+cosa1)) ); + assert(delta0+delta1<2*d); + nv.P()=vp+n*sign*(delta0+delta1)/2.0; + return ; + } + + // Aggiunte in modo grezzo le due wedgeinterp + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + assert(t0.n()== t1.n()); + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } + +}; + +/* +Versione Della Midpoint basata sul paper: +S. Karbacher, S. Seeger, G. Hausler +A non linear subdivision scheme for triangle meshes + + Non funziona! + Almeno due problemi: + 1) il verso delle normali influenza il risultato (e.g. si funziona solo se le normali divergono) + Risolvibile controllando se le normali divergono + 2) gli archi vanno calcolati sul piano definito dalla normale interpolata e l'edge. + funziona molto meglio nelle zone di sella e non semplici. + +*/ +template +struct MidPointArcNaive : public std::unary_function< face::Pos , typename MESH_TYPE::CoordType> +{ + typename MESH_TYPE::CoordType operator()(face::Pos ep) + { + + typename MESH_TYPE::CoordType vp = (ep.f->V(ep.z)->P()+ep.f->V1(ep.z)->P())/2.0; + typename MESH_TYPE::CoordType n = (ep.f->V(ep.z)->N()+ep.f->V1(ep.z)->N())/2.0; + n.Normalize(); + typename MESH_TYPE::CoordType d0 = ep.f->V(ep.z)->P() - vp; + typename MESH_TYPE::CoordType d1 = ep.f->V1(ep.z)->P()- vp; + typename MESH_TYPE::ScalarType d=Distance(ep.f->V(ep.z)->P(),ep.f->V1(ep.z)->P())/2.0; + + typename MESH_TYPE::ScalarType cosa0=ep.f->V(ep.z)->N()*n; + typename MESH_TYPE::ScalarType cosa1=ep.f->V1(ep.z)->N()*n; + typename MESH_TYPE::ScalarType cosb0=(d0*n)/d; + typename MESH_TYPE::ScalarType cosb1=(d1*n)/d; + + typename MESH_TYPE::ScalarType delta0=d*(cosb0 +sqrt( ((1-cosb0*cosb0)*(1-cosa0))/(1+cosa0)) ); + typename MESH_TYPE::ScalarType delta1=d*(cosb1 +sqrt( ((1-cosb1*cosb1)*(1-cosa1))/(1+cosa1)) ); + + return vp+n*(delta0+delta1)/2.0; + } +}; + + +// Basic Predicate that tells if a given edge must be splitted. +// the constructure requires the threshold. +// VERY IMPORTANT REQUIREMENT: this function must be symmetric +// e.g. it must return the same value if the Pos is VFlipped. +// If this function is not symmetric the Refine can crash. + +template +class EdgeLen +{ + FLT squaredThr; +public: + EdgeLen(){}; + EdgeLen(FLT threshold) {setThr(threshold);} + void setThr(FLT threshold) {squaredThr = threshold*threshold; } + bool operator()(face::Pos ep) const + { + return SquaredDistance(ep.V()->P(), ep.VFlip()->P())>squaredThr; + } +}; + +/*********************************************************/ +/********************************************************* + +Given a mesh the following function refines it according to two functor objects: + +- a predicate that tells if a given edge must be splitted + +- a functor that gives you the new poistion of the created vertices (starting from an edge) + +If RefineSelected is true only selected faces are taken into account for being splitted. + +Requirement: FF Adjacency and Manifoldness + +**********************************************************/ +/*********************************************************/ +template +class RefinedFaceData + { + public: + RefinedFaceData(){ + ep[0]=0;ep[1]=0;ep[2]=0; + vp[0]=0;vp[1]=0;vp[2]=0; + } + bool ep[3]; + VertexPointer vp[3]; + }; + +template +bool RefineE(MESH_TYPE &m, MIDPOINT mid, EDGEPRED ep,bool RefineSelected=false, CallBackPos *cb = 0) +{ + // common typenames + typedef typename MESH_TYPE::VertexIterator VertexIterator; + typedef typename MESH_TYPE::FaceIterator FaceIterator; + typedef typename MESH_TYPE::VertexPointer VertexPointer; + typedef typename MESH_TYPE::FacePointer FacePointer; + typedef typename MESH_TYPE::FaceType FaceType; + typedef typename MESH_TYPE::FaceType::TexCoordType TexCoordType; + + typedef face::Pos PosType; + + int j,NewVertNum=0,NewFaceNum=0; + + typedef RefinedFaceData RFD; + typedef typename MESH_TYPE :: template PerFaceAttributeHandle HandleType; + HandleType RD = tri::Allocator:: template AddPerFaceAttribute (m,std::string("RefineData")); + + // Callback stuff + int step=0; + int PercStep=std::max(1,m.fn/33); + + // First Loop: We analyze the mesh to compute the number of the new faces and new vertices + FaceIterator fi; + for(fi=m.face.begin(),j=0;fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + if(cb && (++step%PercStep)==0) (*cb)(step/PercStep,"Refining..."); + // skip unselected faces if necessary + if(RefineSelected && !(*fi).IsS()) continue; + + for(j=0;j<3;j++) + { + if(RD[fi].ep[j]) continue; + + PosType edgeCur(&*fi,j); + if(RefineSelected && ! edgeCur.FFlip()->IsS()) continue; + if(!ep(edgeCur)) continue; + + RD[edgeCur.F()].ep[edgeCur.E()]=true; + ++NewFaceNum; + ++NewVertNum; + assert(edgeCur.IsManifold()); + if(!edgeCur.IsBorder()) + { + edgeCur.FlipF(); + edgeCur.F()->SetV(); + RD[edgeCur.F()].ep[edgeCur.E()]=true; + ++NewFaceNum; + } + } + + } // end face loop + + if(NewVertNum ==0 ) + { + tri::Allocator :: template DeletePerFaceAttribute > (m,RD); + return false; + } + VertexIterator lastv = tri::Allocator::AddVertices(m,NewVertNum); + + // Secondo loop: We initialize a edge->vertex map + + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + if(cb && (++step%PercStep)==0)(*cb)(step/PercStep,"Refining..."); + for(j=0;j<3;j++) + { + // skip unselected faces if necessary + if(RefineSelected && !(*fi).IsS()) continue; + for(j=0;j<3;j++) + { + PosType edgeCur(&*fi,j); + if(RefineSelected && ! edgeCur.FFlip()->IsS()) continue; + + if( RD[edgeCur.F()].ep[edgeCur.E()] && RD[edgeCur.F()].vp[edgeCur.E()] ==0 ) + { + RD[edgeCur.F()].vp[edgeCur.E()] = &*lastv; + mid(*lastv,edgeCur); + if(!edgeCur.IsBorder()) + { + edgeCur.FlipF(); + assert(RD[edgeCur.F()].ep[edgeCur.E()]); + RD[edgeCur.F()].vp[edgeCur.E()] = &*lastv; + } + ++lastv; + } + } + } + } + + assert(lastv==m.vert.end()); // critical assert: we MUST have used all the vertex that we forecasted we need + + FaceIterator lastf = tri::Allocator::AddFaces(m,NewFaceNum); + FaceIterator oldendf = lastf; + +/* + v0 + + + f0 + + mp01 f3 mp02 + + + f1 f2 + + v1 mp12 v2 + +*/ + + VertexPointer vv[6]; // The six vertices that arise in the single triangle splitting + // 0..2 Original triangle vertices + // 3..5 mp01, mp12, mp20 midpoints of the three edges + FacePointer nf[4]; // The (up to) four faces that are created. + + TexCoordType wtt[6]; // per ogni faccia sono al piu' tre i nuovi valori + // di texture per wedge (uno per ogni edge) + + int fca=0,fcn =0; + for(fi=m.face.begin();fi!=oldendf;++fi) if(!(*fi).IsD()) + { + if(cb && (++step%PercStep)==0)(*cb)(step/PercStep,"Refining..."); + fcn++; + vv[0]=(*fi).V(0); + vv[1]=(*fi).V(1); + vv[2]=(*fi).V(2); + vv[3] = RD[fi].vp[0]; + vv[4] = RD[fi].vp[1]; + vv[5] = RD[fi].vp[2]; + + int ind=((&*vv[3])?1:0)+((&*vv[4])?2:0)+((&*vv[5])?4:0); + + nf[0]=&*fi; + int i; + for(i=1;iC()=(*fi).cC(); + } + + + if(tri::HasPerWedgeTexCoord(m)) + for(i=0;i<3;++i) { + wtt[i]=(*fi).WT(i); + wtt[3+i]=mid.WedgeInterp((*fi).WT(i),(*fi).WT((i+1)%3)); + } + + int orgflag= (*fi).UberFlags(); + for(i=0;iP(),vv[SplitTab[ind].swap[0][1]]->P()) < + SquaredDistance(vv[SplitTab[ind].swap[1][0]]->P(),vv[SplitTab[ind].swap[1][1]]->P()) ) + { // swap the last two triangles + (*nf[2]).V(1)=(*nf[1]).V(0); + (*nf[1]).V(1)=(*nf[2]).V(0); + if(tri::HasPerWedgeTexCoord(m)){ //swap also textures coordinates + (*nf[2]).WT(1)=(*nf[1]).WT(0); + (*nf[1]).WT(1)=(*nf[2]).WT(0); + } + + if((*nf[1]).IsB(0)) (*nf[2]).SetB(1); else (*nf[2]).ClearB(1); + if((*nf[2]).IsB(0)) (*nf[1]).SetB(1); else (*nf[1]).ClearB(1); + (*nf[1]).ClearB(0); + (*nf[2]).ClearB(0); + } + } + + assert(lastf==m.face.end()); // critical assert: we MUST have used all the faces that we forecasted we need and that we previously allocated. + assert(!m.vert.empty()); + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()){ + assert((*fi).V(0)>=&*m.vert.begin() && (*fi).V(0)<=&m.vert.back() ); + assert((*fi).V(1)>=&*m.vert.begin() && (*fi).V(1)<=&m.vert.back() ); + assert((*fi).V(2)>=&*m.vert.begin() && (*fi).V(2)<=&m.vert.back() ); + } + tri::UpdateTopology::FaceFace(m); + + tri::Allocator :: template DeletePerFaceAttribute > (m,RD); + + return true; +} + +/*************************************************************************/ +// simple wrapper of the base refine for lazy coder that do not need a edge predicate + +template +bool Refine(MESH_TYPE &m, MIDPOINT mid, typename MESH_TYPE::ScalarType thr=0,bool RefineSelected=false, CallBackPos *cb = 0) +{ + EdgeLen ep(thr); + return RefineE(m,mid,ep,RefineSelected,cb); +} +/*************************************************************************/ + +/* +Modified Butterfly interpolation scheme, +as presented in +Zorin, Schroeder +Subdivision for modeling and animation +Siggraph 2000 Course Notes +*/ + +/* + + vul-------vu--------vur + \ / \ / + \ / \ / + \ / fu \ / + vl--------vr + / \ fd / \ + / \ / \ + / \ / \ + vdl-------vd--------vdr + +*/ + +template +struct MidPointButterfly : public std::unary_function , typename MESH_TYPE::CoordType> +{ + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep) + { + face::Pos he(ep.f,ep.z,ep.f->V(ep.z)); + typename MESH_TYPE::CoordType *vl,*vr; + typename MESH_TYPE::CoordType *vl0,*vr0; + typename MESH_TYPE::CoordType *vu,*vd,*vul,*vur,*vdl,*vdr; + vl=&he.v->P(); + he.FlipV(); + vr=&he.v->P(); + + if( MESH_TYPE::HasPerVertexColor()) + nv.C().lerp(ep.f->V(ep.z)->C(),ep.f->V1(ep.z)->C(),.5f); + + if(he.IsBorder()) + { + he.NextB(); + vr0=&he.v->P(); + he.FlipV(); + he.NextB(); + assert(&he.v->P()==vl); + he.NextB(); + vl0=&he.v->P(); + nv.P()=((*vl)+(*vr))*(9.0/16.0)-((*vl0)+(*vr0))/16.0 ; + } + else + { + he.FlipE();he.FlipV(); + vu=&he.v->P(); + he.FlipF();he.FlipE();he.FlipV(); + vur=&he.v->P(); + he.FlipV();he.FlipE();he.FlipF(); assert(&he.v->P()==vu); // back to vu (on the right) + he.FlipE(); + he.FlipF();he.FlipE();he.FlipV(); + vul=&he.v->P(); + he.FlipV();he.FlipE();he.FlipF(); assert(&he.v->P()==vu); // back to vu (on the left) + he.FlipV();he.FlipE();he.FlipF(); assert(&he.v->P()==vl);// again on vl (but under the edge) + he.FlipE();he.FlipV(); + vd=&he.v->P(); + he.FlipF();he.FlipE();he.FlipV(); + vdl=&he.v->P(); + he.FlipV();he.FlipE();he.FlipF(); assert(&he.v->P()==vd);// back to vd (on the right) + he.FlipE(); + he.FlipF();he.FlipE();he.FlipV(); + vdr=&he.v->P(); + + nv.P()=((*vl)+(*vr))/2.0+((*vu)+(*vd))/8.0 - ((*vul)+(*vur)+(*vdl)+(*vdr))/16.0; + } + } + + /// Aggiunte in modo grezzo le due wedge interp + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + assert(t0.n()== t1.n()); + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } +}; + + +#if 0 + int rule=0; + if(vr==vul) rule+=1; + if(vl==vur) rule+=2; + if(vl==vdr) rule+=4; + if(vr==vdl) rule+=8; + switch(rule){ +/* */ +/* */ case 0 : return ((*vl)+(*vr))/2.0+((*vu)+(*vd))/8.0 - ((*vul)+(*vur)+(*vdl)+(*vdr))/16.0; +/* ul */ case 1 : return (*vl*6 + *vr*10 + *vu + *vd*3 - *vur - *vdl -*vdr*2 )/16.0; +/* ur */ case 2 : return (*vr*6 + *vl*10 + *vu + *vd*3 - *vul - *vdr -*vdl*2 )/16.0; +/* dr */ case 4 : return (*vr*6 + *vl*10 + *vd + *vu*3 - *vdl - *vur -*vul*2 )/16.0; +/* dl */ case 8 : return (*vl*6 + *vr*10 + *vd + *vu*3 - *vdr - *vul -*vur*2 )/16.0; +/* ul,ur */ case 3 : return (*vl*4 + *vr*4 + *vd*2 + - *vdr - *vdl )/8.0; +/* dl,dr */ case 12 : return (*vl*4 + *vr*4 + *vu*2 + - *vur - *vul )/8.0; + +/* ul,dr */ case 5 : +/* ur,dl */ case 10 : + default: + return (*vl+ *vr)/2.0; + } + + + +#endif +/* + vul-------vu--------vur + \ / \ / + \ / \ / + \ / fu \ / + vl--------vr + / \ fd / \ + / \ / \ + / \ / \ + vdl-------vd--------vdr + +*/ + +// Versione modificata per tenere di conto in meniara corretta dei vertici con valenza alta + +template +struct MidPointButterfly2 : public std::unary_function , typename MESH_TYPE::CoordType> +{ + typename MESH_TYPE::CoordType operator()(face::Pos ep) + { +double Rules[11][10] = +{ + {.0}, // valenza 0 + {.0}, // valenza 1 + {.0}, // valenza 2 + { .4166666667, -.08333333333 , -.08333333333 }, // valenza 3 + { .375 , .0 , -0.125 , .0 }, // valenza 4 + { .35 , .03090169945 , -.08090169945 , -.08090169945, .03090169945 }, // valenza 5 + { .5 , .125 , -0.0625 , .0 , -0.0625 , 0.125 }, // valenza 6 + { .25 , .1088899050 , -.06042933822 , -.04846056675, -.04846056675, -.06042933822, .1088899050 }, // valenza 7 + { .21875 , .1196383476 , -.03125 , -.05713834763, -.03125 , -.05713834763, -.03125 ,.1196383476 }, // valenza 8 + { .1944444444, .1225409480 , -.00513312590 , -.05555555556, -.03407448880, -.03407448880, -.05555555556, -.00513312590, .1225409480 }, // valenza 9 + { .175 , .1213525492 , .01545084973 , -.04635254918, -.04045084973, -.025 , -.04045084973, -.04635254918, .01545084973, .1213525492 } // valenza 10 +}; + +face::Pos he(ep.f,ep.z,ep.f->V(ep.z)); + typename MESH_TYPE::CoordType *vl,*vr; + vl=&he.v->P(); + vr=&he.VFlip()->P(); + if(he.IsBorder()) + {he.FlipV(); + typename MESH_TYPE::CoordType *vl0,*vr0; + he.NextB(); + vr0=&he.v->P(); + he.FlipV(); + he.NextB(); + assert(&he.v->P()==vl); + he.NextB(); + vl0=&he.v->P(); + return ((*vl)+(*vr))*(9.0/16.0)-((*vl0)+(*vr0))/16.0 ; + } + + int kl=0,kr=0; // valence of left and right vertices + bool bl=false,br=false; // if left and right vertices are of border + face::Pos heStart=he;assert(he.v->P()==*vl); + do { // compute valence of left vertex + he.FlipE();he.FlipF(); + if(he.IsBorder()) bl=true; + ++kl; + } while(he!=heStart); + + he.FlipV();heStart=he;assert(he.v->P()==*vr); + do { // compute valence of right vertex + he.FlipE();he.FlipF(); + if(he.IsBorder()) br=true; + ++kr; + } while(he!=heStart); + if(br||bl) return MidPointButterfly()( ep ); + if(kr==6 && kl==6) return MidPointButterfly()( ep ); + // TRACE("odd vertex among valences of %i %i\n",kl,kr); + typename MESH_TYPE::CoordType newposl=*vl*.75, newposr=*vr*.75; + he.FlipV();heStart=he; assert(he.v->P()==*vl); + int i=0; + if(kl!=6) + do { // compute position of left vertex + newposl+= he.VFlip()->P() * Rules[kl][i]; + he.FlipE();he.FlipF(); + ++i; + } while(he!=heStart); + i=0;he.FlipV();heStart=he;assert(he.v->P()==*vr); + if(kr!=6) + do { // compute position of right vertex + newposr+=he.VFlip()->P()* Rules[kr][i]; + he.FlipE();he.FlipF(); + ++i; + } while(he!=heStart); + if(kr==6) return newposl; + if(kl==6) return newposr; + return newposl+newposr; + } +}; + +/* +// Nuovi punti (e.g. midpoint) +template +struct OddPointLoop : public std::unary_function , typename MESH_TYPE::CoordType> +{ + + +} + +// vecchi punti +template +struct EvenPointLoop : public std::unary_function , typename MESH_TYPE::CoordType> +{ +} +*/ + +template +struct MidPointPlane : public std::unary_function , typename MESH_TYPE::CoordType> +{ + Plane3 pl; + typedef Point3 Point3x; + + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep){ + Point3x &p0=ep.f->V0(ep.z)->P(); + Point3x &p1=ep.f->V1(ep.z)->P(); + double pp= Distance(p0,pl)/(Distance(p0,pl) - Distance(p1,pl)); + + nv.P()=p1*pp + p0*(1.0-pp); + } + + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + assert(t0.n()== t1.n()); + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } +}; + + +template +class EdgeSplPlane +{ + public: + Plane3 pl; + bool operator()(const Point3 &p0, const Point3 &p1) const + { + if(Distance(pl,p0)>0) { + if(Distance(pl,p1)>0) return false; + else return true; + } + else if(Distance(pl,p1)<=0) return false; + return true; + } +}; + + +template +struct MidPointSphere : public std::unary_function , typename MESH_TYPE::CoordType> +{ + Sphere3 sph; + typedef Point3 Point3x; + + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep){ + Point3x &p0=ep.f->V0(ep.z)->P(); + Point3x &p1=ep.f->V1(ep.z)->P(); + nv.P()= sph.c+((p0+p1)/2.0 - sph.c ).Normalize(); + } + + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + assert(t0.n()== t1.n()); + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } +}; + + +template +class EdgeSplSphere +{ + public: + Sphere3 sph; + bool operator()(const Point3 &p0, const Point3 &p1) const + { + if(Distance(sph,p0)>0) { + if(Distance(sph,p1)>0) return false; + else return true; + } + else if(Distance(sph,p1)<=0) return false; + return true; + } +}; + +/*! +* Triangle split +*/ + +template +struct CenterPoint : public std::unary_function +{ + typename TRIMESH_TYPE::CoordType operator()(typename TRIMESH_TYPE::FacePointer f){ + return vcg::Barycenter(*f); + } +}; + +template +void TriSplit(typename TRIMESH_TYPE::FacePointer f, + typename TRIMESH_TYPE::FacePointer f1,typename TRIMESH_TYPE::FacePointer f2, + typename TRIMESH_TYPE::VertexPointer vB, CenterPoint Center) +{ + vB->P() = Center(f); + + //i tre vertici della faccia da dividere + typename TRIMESH_TYPE::VertexType* V0,*V1,*V2; + V0 = f->V(0); + V1 = f->V(1); + V2 = f->V(2); + + //risistemo la faccia di partenza + (*f).V(2) = &(*vB); + //Faccia nuova #1 + (*f1).V(0) = &(*vB); + (*f1).V(1) = V1; + (*f1).V(2) = V2; + //Faccia nuova #2 + (*f2).V(0) = V0; + (*f2).V(1) = &(*vB); + (*f2).V(2) = V2; + + if(f->HasFFAdjacency()) + { + //adiacenza delle facce adiacenti a quelle aggiunte + f->FFp(1)->FFp(f->FFi(1)) = f1; + f->FFp(2)->FFp(f->FFi(2)) = f2; + + //adiacenza ff + typename TRIMESH_TYPE::FacePointer FF0,FF1,FF2; + FF0 = f->FFp(0); + FF1 = f->FFp(1); + FF2 = f->FFp(2); + + //Indici di adiacenza ff + char FFi0,FFi1,FFi2; + FFi0 = f->FFi(0); + FFi1 = f->FFi(1); + FFi2 = f->FFi(2); + + //adiacenza della faccia di partenza + (*f).FFp(1) = &(*f1); + (*f).FFi(1) = 0; + (*f).FFp(2) = &(*f2); + (*f).FFi(2) = 0; + + //adiacenza della faccia #1 + (*f1).FFp(0) = f; + (*f1).FFi(0) = 1; + + (*f1).FFp(1) = FF1; + (*f1).FFi(1) = FFi1; + + (*f1).FFp(2) = &(*f2); + (*f1).FFi(2) = 1; + + //adiacenza della faccia #2 + (*f2).FFp(0) = f; + (*f2).FFi(0) = 2; + + (*f2).FFp(1) = &(*f1); + (*f2).FFi(1) = 2; + + (*f2).FFp(2) = FF2; + (*f2).FFi(2) = FFi2; + } +} + + +} // namespace vcg + + + + +#endif diff --git a/vcg/complex/algorithms/refine_loop.h b/vcg/complex/algorithms/refine_loop.h new file mode 100644 index 00000000..180a540c --- /dev/null +++ b/vcg/complex/algorithms/refine_loop.h @@ -0,0 +1,623 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/*! \file refine_loop.h + * + * \brief This file contain code for Loop's subdivision scheme for triangular + * mesh and some similar method. + * + */ + +#ifndef __VCGLIB_REFINE_LOOP +#define __VCGLIB_REFINE_LOOP + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace vcg{ +namespace tri{ + +/* +Metodo di Loop dalla documentazione "Siggraph 2000 course on subdivision" + + d4------d3 d4------d3 + / \ / \ / \ / \ u + / \ / \ / e4--e3 \ / \ + / \/ \ / / \/ \ \ / \ +d5------d1------d2 -> d5--e5--d1--e2--d2 l--M--r + \ /\ / \ \ /\ / / \ / + \ / \ / \ e6--e7 / \ / + \ / \ / \ / \ / d + d6------d7 d6------d7 + +******************************************************* + +*/ + +/*! + * \brief Weight class for classical Loop's scheme. + * + * See Zorin, D. & Schröeder, P.: Subdivision for modeling and animation. Proc. ACM SIGGRAPH [Courses], 2000 + */ +template +struct LoopWeight { + inline SCALAR_TYPE beta(int k) { + return (k>3)?(5.0/8.0 - std::pow((3.0/8.0 + std::cos(2.0*M_PI/SCALAR_TYPE(k))/4.0),2))/SCALAR_TYPE(k):3.0/16.0; + } + + inline SCALAR_TYPE incidentRegular(int) { + return 3.0/8.0; + } + inline SCALAR_TYPE incidentIrregular(int) { + return 3.0/8.0; + } + inline SCALAR_TYPE opposite(int) { + return 1.0/8.0; + } +}; + +/*! + * \brief Modified Loop's weight to optimise continuity. + * + * See Barthe, L. & Kobbelt, L.: Subdivision scheme tuning around extraordinary vertices. Computer Aided Geometric Design, 2004, 21, 561-583 + */ +template +struct RegularLoopWeight { + inline SCALAR_TYPE beta(int k) { + static SCALAR_TYPE bkPolar[] = { + .32517, + .49954, + .59549, + .625, + .63873, + .64643, + .65127, + .67358, + .68678, + .69908 + }; + + return (k<=12)?(1.0-bkPolar[k-3])/k:LoopWeight().beta(k); + } + + inline SCALAR_TYPE incidentRegular(int k) { + return 1.0 - incidentIrregular(k) - opposite(k)*2; + } + inline SCALAR_TYPE incidentIrregular(int k) { + static SCALAR_TYPE bkPolar[] = { + .15658, + .25029, + .34547, + .375, + .38877, + .39644, + .40132, + .42198, + .43423, + .44579 + }; + + return (k<=12)?bkPolar[k-3]:LoopWeight().incidentIrregular(k); + } + inline SCALAR_TYPE opposite(int k) { + static SCALAR_TYPE bkPolar[] = { + .14427, + .12524, + .11182, + .125, + .14771, + .1768, + .21092, + .20354, + .20505, + .19828 + }; + + return (k<=12)?bkPolar[k-3]:LoopWeight().opposite(k); + } +}; + +template +struct ContinuityLoopWeight { + inline SCALAR_TYPE beta(int k) { + static SCALAR_TYPE bkPolar[] = { + .32517, + .50033, + .59464, + .625, + .63903, + .67821, + .6866, + .69248, + .69678, + .70014 + }; + + return (k<=12)?(1.0-bkPolar[k-3])/k:LoopWeight().beta(k); + } + + inline SCALAR_TYPE incidentRegular(int k) { + return 1.0 - incidentIrregular(k) - opposite(k)*2; + } + inline SCALAR_TYPE incidentIrregular(int k) { + static SCALAR_TYPE bkPolar[] = { + .15658, + .26721, + .33539, + .375, + .36909, + .25579, + .2521, + .24926, + .24706, + .2452 + }; + + return (k<=12)?bkPolar[k-3]:LoopWeight().incidentIrregular(k); + } + inline SCALAR_TYPE opposite(int k) { + static SCALAR_TYPE bkPolar[] = { + .14427, + .12495, + .11252, + .125, + .14673, + .16074, + .18939, + .2222, + .25894, + .29934 + }; + + return (k<=12)?bkPolar[k-3]:LoopWeight().opposite(k); + } +}; + +// Centroid and LS3Projection classes may be pettre placed in an other file. (which one ?) + +/*! + * \brief Allow to compute classical Loop subdivision surface with the same code than LS3. + */ +template +struct Centroid { + typedef typename MESH_TYPE::ScalarType Scalar; + typedef typename MESH_TYPE::CoordType Coord; + typedef LSCALAR_TYPE LScalar; + typedef vcg::Point3 LVector; + + LVector sumP; + LScalar sumW; + + Centroid() { reset(); } + inline void reset() { + sumP.SetZero(); + sumW = 0.; + } + inline void addVertex(const typename MESH_TYPE::VertexType &v, LScalar w) { + LVector p(v.cP().X(), v.cP().Y(), v.cP().Z()); + LVector n(v.cN().X(), v.cN().Y(), v.cN().Z()); + + sumP += p * w; + sumW += w; + } + inline void project(typename MESH_TYPE::VertexType &v) const { + LVector position = sumP / sumW; + v.P() = Coord(position.X(), position.Y(), position.Z()); + } +}; + +/*! Implementation of the APSS projection for the LS3 scheme. + * + * See Gael Guennebaud and Marcel Germann and Markus Gross + * Dynamic sampling and rendering of algebraic point set surfaces. + * Computer Graphics Forum (Proceedings of Eurographics 2008), 2008, 27, 653-662 + * and Simon Boye and Gael Guennebaud and Christophe Schlick + * Least squares subdivision surfaces + * Computer Graphics Forum, 2010 + */ +template +struct LS3Projection { + typedef typename MESH_TYPE::ScalarType Scalar; + typedef typename MESH_TYPE::CoordType Coord; + typedef LSCALAR_TYPE LScalar; + typedef vcg::Point3 LVector; + + Scalar beta; + + LVector sumP; + LVector sumN; + LScalar sumDotPN; + LScalar sumDotPP; + LScalar sumW; + + inline LS3Projection(Scalar beta = 1.0) : beta(beta) { reset(); } + inline void reset() { + sumP.SetZero(); + sumN.SetZero(); + sumDotPN = 0.; + sumDotPP = 0.; + sumW = 0.; + } + inline void addVertex(const typename MESH_TYPE::VertexType &v, LScalar w) { + LVector p(v.cP().X(), v.cP().Y(), v.cP().Z()); + LVector n(v.cN().X(), v.cN().Y(), v.cN().Z()); + + sumP += p * w; + sumN += n * w; + sumDotPN += w * n.dot(p); + sumDotPP += w * vcg::SquaredNorm(p); + sumW += w; + } + void project(typename MESH_TYPE::VertexType &v) const { + LScalar invSumW = Scalar(1)/sumW; + LScalar aux4 = beta * LScalar(0.5) * + (sumDotPN - invSumW*sumP.dot(sumN)) + /(sumDotPP - invSumW*vcg::SquaredNorm(sumP)); + LVector uLinear = (sumN-sumP*(Scalar(2)*aux4))*invSumW; + LScalar uConstant = -invSumW*(uLinear.dot(sumP) + sumDotPP*aux4); + LScalar uQuad = aux4; + LVector orig = sumP*invSumW; + + // finalize + LVector position; + LVector normal; + if (fabs(uQuad)>1e-7) + { + LScalar b = 1./uQuad; + LVector center = uLinear*(-0.5*b); + LScalar radius = sqrt( vcg::SquaredNorm(center) - b*uConstant ); + + normal = orig - center; + normal.Normalize(); + position = center + normal * radius; + + normal = uLinear + position * (LScalar(2) * uQuad); + normal.Normalize(); + } + else if (uQuad==0.) + { + LScalar s = LScalar(1)/vcg::Norm(uLinear); + assert(!vcg::math::IsNAN(s) && "normal should not have zero len!"); + uLinear *= s; + uConstant *= s; + + normal = uLinear; + position = orig - uLinear * (orig.dot(uLinear) + uConstant); + } + else + { + // normalize the gradient + LScalar f = 1./sqrt(vcg::SquaredNorm(uLinear) - Scalar(4)*uConstant*uQuad); + uConstant *= f; + uLinear *= f; + uQuad *= f; + + // Newton iterations + LVector grad; + LVector dir = uLinear+orig*(2.*uQuad); + LScalar ilg = 1./vcg::Norm(dir); + dir *= ilg; + LScalar ad = uConstant + uLinear.dot(orig) + uQuad * vcg::SquaredNorm(orig); + LScalar delta = -ad*std::min(ilg,1.); + LVector p = orig + dir*delta; + for (int i=0 ; i<2 ; ++i) + { + grad = uLinear+p*(2.*uQuad); + ilg = 1./vcg::Norm(grad); + delta = -(uConstant + uLinear.dot(p) + uQuad * vcg::SquaredNorm(p))*std::min(ilg,1.); + p += dir*delta; + } + position = p; + + normal = uLinear + position * (Scalar(2) * uQuad); + normal.Normalize(); + } + + v.P() = Coord(position.X(), position.Y(), position.Z()); + v.N() = Coord(normal.X(), normal.Y(), normal.Z()); + } +}; + +template, class WEIGHT_TYPE=LoopWeight > +struct OddPointLoopGeneric : public std::unary_function , typename MESH_TYPE::VertexType> +{ + typedef METHOD_TYPE Projection; + typedef WEIGHT_TYPE Weight; + typedef typename MESH_TYPE::template PerVertexAttributeHandle ValenceAttr; + + Projection proj; + Weight weight; + ValenceAttr *valence; + + inline OddPointLoopGeneric(Projection proj = Projection(), Weight weight = Weight()) : + proj(proj), weight(weight), valence(0) {} + + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep) { + proj.reset(); + + face::Pos he(ep.f,ep.z,ep.f->V(ep.z)); + typename MESH_TYPE::VertexType *l,*r,*u,*d; + l = he.v; + he.FlipV(); + r = he.v; + + if( MESH_TYPE::HasPerVertexColor()) + nv.C().lerp(ep.f->V(ep.z)->C(),ep.f->V1(ep.z)->C(),.5f); + + if (he.IsBorder()) { + proj.addVertex(*l, 0.5); + proj.addVertex(*r, 0.5); + proj.project(nv); + } + else { + he.FlipE(); he.FlipV(); + u = he.v; + he.FlipV(); he.FlipE(); + assert(he.v == r); // back to r + he.FlipF(); he.FlipE(); he.FlipV(); + d = he.v; + + if(valence && ((*valence)[l]==6 || (*valence)[r]==6)) { + int extra = ((*valence)[l]==6)?(*valence)[r]:(*valence)[l]; + proj.addVertex(*l, ((*valence)[l]==6)?weight.incidentRegular(extra):weight.incidentIrregular(extra)); + proj.addVertex(*r, ((*valence)[r]==6)?weight.incidentRegular(extra):weight.incidentIrregular(extra)); + proj.addVertex(*u, weight.opposite(extra)); + proj.addVertex(*d, weight.opposite(extra)); + } + // unhandled case that append only at first subdivision step: use Loop's weights + else { + proj.addVertex(*l, 3.0/8.0); + proj.addVertex(*r, 3.0/8.0); + proj.addVertex(*u, 1.0/8.0); + proj.addVertex(*d, 1.0/8.0); + } + proj.project(nv); + } + + } + + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } + + inline void setValenceAttr(ValenceAttr *valence) { + this->valence = valence; + } +}; + +template, class WEIGHT_TYPE=LoopWeight > +struct EvenPointLoopGeneric : public std::unary_function , typename MESH_TYPE::VertexType> +{ + typedef METHOD_TYPE Projection; + typedef WEIGHT_TYPE Weight; + typedef typename MESH_TYPE::template PerVertexAttributeHandle ValenceAttr; + + Projection proj; + Weight weight; + ValenceAttr *valence; + + inline EvenPointLoopGeneric(Projection proj = Projection(), Weight weight = Weight()) : + proj(proj), weight(weight), valence(0) {} + + void operator()(typename MESH_TYPE::VertexType &nv, face::Pos ep) { + proj.reset(); + + face::Pos he(ep.f,ep.z,ep.f->V(ep.z)); + typename MESH_TYPE::VertexType *r, *l, *curr; + curr = he.v; + face::Pos heStart = he; + + // compute valence of this vertex or find a border + int k = 0; + do { + he.NextE(); + k++; + } while(!he.IsBorder() && he != heStart); + + if (he.IsBorder()) { // Border rule + // consider valence of borders as if they are half+1 of an inner vertex. not perfect, but better than nothing. + if(valence) { + k = 0; + do { + he.NextE(); + k++; + } while(!he.IsBorder()); + (*valence)[he.V()] = std::max(2*(k-1), 3); +// (*valence)[he.V()] = 6; + } + + he.FlipV(); + r = he.v; + he.FlipV(); + he.NextB(); + l = he.v; + + proj.addVertex(*curr, 3.0/4.0); + proj.addVertex(*l, 1.0/8.0); + proj.addVertex(*r, 1.0/8.0); + proj.project(nv); + } + else { // Inner rule +// assert(!he.v->IsB()); border flag no longer updated (useless) + if(valence) + (*valence)[he.V()] = k; + + typename MESH_TYPE::ScalarType beta = weight.beta(k); + + proj.addVertex(*curr, 1.0 - (typename MESH_TYPE::ScalarType)(k) * beta); + do { + proj.addVertex(*he.VFlip(), beta); + he.NextE(); + } while(he != heStart); + + proj.project(nv); + } + } // end of operator() + + Color4 WedgeInterp(Color4 &c0, Color4 &c1) + { + Color4 cc; + return cc.lerp(c0,c1,0.5f); + } + Color4b WedgeInterp(Color4b &c0, Color4b &c1) + { + Color4b cc; + cc.lerp(c0,c1,0.5f); + return cc; + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1) + { + TexCoord2 tmp; + // assert(t0.n()== t1.n()); + tmp.n()=t0.n(); + tmp.t()=(t0.t()+t1.t())/2.0; + return tmp; + } + + inline void setValenceAttr(ValenceAttr *valence) { + this->valence = valence; + } +}; + +template +struct OddPointLoop : OddPointLoopGeneric > +{ +}; + +template +struct EvenPointLoop : EvenPointLoopGeneric > +{ +}; + +template +bool RefineOddEven(MESH_TYPE &m, ODD_VERT odd, EVEN_VERT even,float length, + bool RefineSelected=false, CallBackPos *cbOdd = 0, CallBackPos *cbEven = 0) +{ + EdgeLen ep(length); + return RefineOddEvenE(m, odd, even, ep, RefineSelected, cbOdd, cbEven); +} + +/*! + * \brief Perform diadic subdivision using given rules for odd and even vertices. + */ +template +bool RefineOddEvenE(MESH_TYPE &m, ODD_VERT odd, EVEN_VERT even, PREDICATE edgePred, + bool RefineSelected=false, CallBackPos *cbOdd = 0, CallBackPos *cbEven = 0) +{ + typedef typename MESH_TYPE::template PerVertexAttributeHandle ValenceAttr; + + // momentaneamente le callback sono identiche, almeno cbOdd deve essere passata + cbEven = cbOdd; + + // to mark visited vertices + int evenFlag = MESH_TYPE::VertexType::NewBitFlag(); + for (int i = 0; i < m.vn ; i++ ) { + m.vert[i].ClearUserBit(evenFlag); + } + + int j = 0; + // di texture per wedge (uno per ogni edge) + + ValenceAttr valence = vcg::tri::Allocator:: template AddPerVertexAttribute(m); + odd.setValenceAttr(&valence); + even.setValenceAttr(&valence); + + // store updated vertices + std::vector updatedList(m.vn, false); + std::vector newEven(m.vn); + + typename MESH_TYPE::VertexIterator vi; + typename MESH_TYPE::FaceIterator fi; + for (fi = m.face.begin(); fi != m.face.end(); fi++) if(!(*fi).IsD() && (!RefineSelected || (*fi).IsS())){ //itero facce + for (int i = 0; i < 3; i++) { //itero vert + if ( !(*fi).V(i)->IsUserBit(evenFlag) && ! (*fi).V(i)->IsD() ) { + (*fi).V(i)->SetUserBit(evenFlag); + // use face selection, not vertex selection, to be coherent with RefineE + //if (RefineSelected && !(*fi).V(i)->IsS() ) + // break; + face::Posaux (&(*fi),i); + if( MESH_TYPE::HasPerVertexColor() ) { + (*fi).V(i)->C().lerp((*fi).V0(i)->C() , (*fi).V1(i)->C(),0.5f); + } + + if (cbEven) { + (*cbEven)(int(100.0f * (float)j / (float)m.fn),"Refining"); + j++; + } + int index = tri::Index(m, (*fi).V(i)); + updatedList[index] = true; + even(newEven[index], aux); + } + } + } + + MESH_TYPE::VertexType::DeleteBitFlag(evenFlag); + + // refine dei vertici odd, crea dei nuovi vertici in coda + RefineE< MESH_TYPE, ODD_VERT > (m, odd, edgePred, RefineSelected, cbOdd); + + typename std::vector::iterator nei; + for (nei=newEven.begin(); nei!=newEven.end(); ++nei) { + if(updatedList[nei-newEven.begin()]) { + m.vert[nei-newEven.begin()].ImportData(*nei); + assert(m.vert[nei-newEven.begin()].N() == nei->N()); + } + } + + odd.setValenceAttr(0); + even.setValenceAttr(0); + + vcg::tri::Allocator::DeletePerVertexAttribute(m, valence); + + return true; +} + +} // namespace tri +} // namespace vcg + + + + +#endif + + + diff --git a/vcg/complex/algorithms/smooth.h b/vcg/complex/algorithms/smooth.h new file mode 100644 index 00000000..909f81a8 --- /dev/null +++ b/vcg/complex/algorithms/smooth.h @@ -0,0 +1,1331 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + + +#ifndef __VCGLIB__SMOOTH +#define __VCGLIB__SMOOTH + +#include +#include +#include +#include +#include +#include +#include + + +namespace vcg +{ +namespace tri +{ +/// +/** \addtogroup trimesh */ +/*@{*/ +/// Class of static functions to smooth and fair meshes and their attributes. + +template +class Smooth +{ + +public: + typedef SmoothMeshType MeshType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexType::CoordType CoordType; + 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::FaceContainer FaceContainer; + typedef typename vcg::Box3 Box3Type; + typedef typename vcg::face::VFIterator VFLocalIterator; + +class ScaleLaplacianInfo +{ +public: + CoordType PntSum; + ScalarType LenSum; +}; + +// This is precisely what curvature flow does. +// Curvature flow smoothes the surface by moving along the surface +// normal n with a speed equal to the mean curvature +void VertexCoordLaplacianCurvatureFlow(MeshType &m, int step, ScalarType delta) +{ + +} + +// Another Laplacian smoothing variant, +// here we sum the baricenter of the faces incidents on each vertex weighting them with the angle + +static void VertexCoordLaplacianAngleWeighted(MeshType &m, int step, ScalarType delta) +{ + ScaleLaplacianInfo lpz; + lpz.PntSum=CoordType(0,0,0); + lpz.LenSum=0; + SimpleTempData TD(m.vert,lpz); + FaceIterator fi; + for(int i=0;iP() + (*fi).V(1)->P() + (*fi).V(2)->P())/3.0; + CoordType e0=((*fi).V(0)->P() - (*fi).V(1)->P()).Normalize(); + CoordType e1=((*fi).V(1)->P() - (*fi).V(2)->P()).Normalize(); + CoordType e2=((*fi).V(2)->P() - (*fi).V(0)->P()).Normalize(); + + a[0]=AngleN(-e0,e2); + a[1]=AngleN(-e1,e0); + a[2]=AngleN(-e2,e1); + //assert(fabs(M_PI -a[0] -a[1] -a[2])<0.0000001); + + for(int j=0;j<3;++j){ + CoordType dir= (mp-(*fi).V(j)->P()).Normalize(); + TD[(*fi).V(j)].PntSum+=dir*a[j]; + TD[(*fi).V(j)].LenSum+=a[j]; // well, it should be named angleSum + } + } + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].LenSum>0 ) + (*vi).P() = (*vi).P() + (TD[*vi].PntSum/TD[*vi].LenSum ) * delta; + + } +}; + +// Scale dependent laplacian smoothing [Fujiwara 95] +// as described in +// Implicit Fairing of Irregular Meshes using Diffusion and Curvature Flow +// Mathieu Desbrun, Mark Meyer, Peter Schroeder, Alan H. Barr +// SIGGRAPH 99 +// REQUIREMENTS: Border Flags. +// +// Note the delta parameter is in a absolute unit +// to get stability it should be a small percentage of the shortest edge. + +static void VertexCoordScaleDependentLaplacian_Fujiwara(MeshType &m, int step, ScalarType delta) +{ + SimpleTempData TD(m.vert); + ScaleLaplacianInfo lpz; + lpz.PntSum=CoordType(0,0,0); + lpz.LenSum=0; + FaceIterator fi; + for(int i=0;iP() -(*fi).V(j)->P(); + ScalarType len=Norm(edge); + edge/=len; + TD[(*fi).V(j)].PntSum+=edge; + TD[(*fi).V1(j)].PntSum-=edge; + TD[(*fi).V(j)].LenSum+=len; + TD[(*fi).V1(j)].LenSum+=len; + } + + for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD()) + for(int j=0;j<3;++j) + // se l'edge j e' di bordo si riazzera tutto e si riparte + if((*fi).IsB(j)) { + TD[(*fi).V(j)].PntSum=CoordType(0,0,0); + TD[(*fi).V1(j)].PntSum=CoordType(0,0,0); + TD[(*fi).V(j)].LenSum=0; + TD[(*fi).V1(j)].LenSum=0; + } + + + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + CoordType edge= (*fi).V1(j)->P() -(*fi).V(j)->P(); + ScalarType len=Norm(edge); + edge/=len; + TD[(*fi).V(j)].PntSum+=edge; + TD[(*fi).V1(j)].PntSum-=edge; + TD[(*fi).V(j)].LenSum+=len; + TD[(*fi).V1(j)].LenSum+=len; + } + // The fundamental part: + // We move the new point of a quantity + // + // L(M) = 1/Sum(edgelen) * Sum(Normalized edges) + // + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].LenSum>0 ) + (*vi).P() = (*vi).P() + (TD[*vi].PntSum/TD[*vi].LenSum)*delta; + } +}; + + +class LaplacianInfo +{ +public: + LaplacianInfo(const CoordType &_p, const int _n):sum(_p),cnt(_n) {} + LaplacianInfo() {} + CoordType sum; + ScalarType cnt; +}; + +// Classical Laplacian Smoothing. Each vertex can be moved onto the average of the adjacent vertices. +// Can smooth only the selected vertices and weight the smoothing according to the quality +// In the latter case 0 means that the vertex is not moved and 1 means that the vertex is moved onto the computed position. +// +// From the Taubin definition "A signal proc approach to fair surface design" +// We define the discrete Laplacian of a discrete surface signal by weighted averages over the neighborhoods +// \delta xi = \Sum wij (xj - xi) ; +// where xj are the adjacent vertices of xi and wij is usually 1/n_adj +// +// This function simply accumulate over a TempData all the positions of the ajacent vertices +// +static void AccumulateLaplacianInfo(MeshType &m, SimpleTempData &TD) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + { + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if(!(*fi).IsB(j)) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->P(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->P(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + } + // si azzaera i dati per i vertici di bordo + for(fi=m.face.begin();fi!=m.face.end();++fi) + { + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V0(j)].sum=(*fi).P0(j); + TD[(*fi).V1(j)].sum=(*fi).P1(j); + TD[(*fi).V0(j)].cnt=1; + TD[(*fi).V1(j)].cnt=1; + } + } + + // se l'edge j e' di bordo si deve mediare solo con gli adiacenti + for(fi=m.face.begin();fi!=m.face.end();++fi) + { + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->P(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->P(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + } +} + +static void VertexCoordLaplacian(MeshType &m, int step, bool SmoothSelected=false, vcg::CallBackPos * cb=0) +{ + VertexIterator vi; + LaplacianInfo lpz(CoordType(0,0,0),0); + SimpleTempData TD(m.vert,lpz); + for(int i=0;i0 ) + { + if(!SmoothSelected || (*vi).IsS()) + (*vi).P() = ( (*vi).P() + TD[*vi].sum)/(TD[*vi].cnt+1); + } + } +} + +// Same of above but moves only the vertices that do not change FaceOrientation more that the given threshold +static void VertexCoordPlanarLaplacian(MeshType &m, int step, float AngleThrRad = math::ToRad(1.0), bool SmoothSelected=false, vcg::CallBackPos * cb=0) +{ + VertexIterator vi; + FaceIterator fi; + LaplacianInfo lpz(CoordType(0,0,0),0); + SimpleTempData TD(m.vert,lpz); + for(int i=0;i0 ) + { + if(!SmoothSelected || (*vi).IsS()) + TD[*vi].sum = ( (*vi).P() + TD[*vi].sum)/(TD[*vi].cnt+1); + } + + for(fi=m.face.begin();fi!=m.face.end();++fi){ + if(!(*fi).IsD()){ + for (int j = 0; j < 3; ++j) { + if(Angle( NormalizedNormal(TD[(*fi).V0(j)].sum, (*fi).P1(j), (*fi).P2(j) ), + NormalizedNormal( (*fi).P0(j) , (*fi).P1(j), (*fi).P2(j) ) ) > AngleThrRad ) + TD[(*fi).V0(j)].sum = (*fi).P0(j); + } + } + } + for(fi=m.face.begin();fi!=m.face.end();++fi){ + if(!(*fi).IsD()){ + for (int j = 0; j < 3; ++j) { + if(Angle( NormalizedNormal(TD[(*fi).V0(j)].sum, TD[(*fi).V1(j)].sum, (*fi).P2(j) ), + NormalizedNormal( (*fi).P0(j) , (*fi).P1(j), (*fi).P2(j) ) ) > AngleThrRad ) + { + TD[(*fi).V0(j)].sum = (*fi).P0(j); + TD[(*fi).V1(j)].sum = (*fi).P1(j); + } + } + } + } + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + (*vi).P()= TD[*vi].sum; + + + + }// end step + + +} + +static void VertexCoordLaplacianBlend(MeshType &m, int step, float alpha, bool SmoothSelected=false) +{ + VertexIterator vi; + LaplacianInfo lpz(CoordType(0,0,0),0); + assert (alpha<= 1.0); + SimpleTempData TD(m.vert); + + for(int i=0;i0 ) + { + if(!SmoothSelected || (*vi).IsS()) + { + CoordType Delta = TD[*vi].sum/TD[*vi].cnt - (*vi).P(); + (*vi).P() = (*vi).P() + Delta*alpha; + } + } + } +} + +/* a couple of notes about the lambda mu values +We assume that 0 < lambda , and mu is a negative scale factor such that mu < - lambda. +Holds mu+lambda < 0 (e.g in absolute value mu is greater) + +let kpb be the pass-band frequency, taubin says that: + kpb = 1/lambda + 1/mu >0 + +Values of kpb from 0.01 to 0.1 produce good results according to the original paper. + +kpb * mu - mu/lambda = 1 +mu = 1/(kpb-1/lambda ) + +So if +* lambda == 0.5, kpb==0.1 -> mu = 1/(0.1 - 2) = -0.526 +* lambda == 0.5, kpb==0.01 -> mu = 1/(0.01 - 2) = -0.502 +*/ + + +static void VertexCoordTaubin(MeshType &m, int step, float lambda, float mu, bool SmoothSelected=false, vcg::CallBackPos * cb=0) +{ + LaplacianInfo lpz(CoordType(0,0,0),0); + SimpleTempData TD(m.vert,lpz); + VertexIterator vi; + for(int i=0;i0 ) + { + if(!SmoothSelected || (*vi).IsS()) + { + CoordType Delta = TD[*vi].sum/TD[*vi].cnt - (*vi).P(); + (*vi).P() = (*vi).P() + Delta*lambda ; + } + } + TD.Init(lpz); + AccumulateLaplacianInfo(m,TD); + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + { + if(!SmoothSelected || (*vi).IsS()) + { + CoordType Delta = TD[*vi].sum/TD[*vi].cnt - (*vi).P(); + (*vi).P() = (*vi).P() + Delta*mu ; + } + } + } // end for step +} + + +static void VertexCoordLaplacianQuality(MeshType &m, int step, bool SmoothSelected=false) +{ + LaplacianInfo lpz; + lpz.sum=CoordType(0,0,0); + lpz.cnt=1; + SimpleTempData TD(m.vert,lpz); + for(int i=0;i0 ) + if(!SmoothSelected || (*vi).IsS()) + { + float q=(*vi).Q(); + (*vi).P()=(*vi).P()*q + (TD[*vi].sum/TD[*vi].cnt)*(1.0-q); + } + } // end for +}; + +/* + Improved Laplacian Smoothing of Noisy Surface Meshes + J. Vollmer, R. Mencl, and H. M�ller + EUROGRAPHICS Volume 18 (1999), Number 3 +*/ + +class HCSmoothInfo +{ +public: + CoordType dif; + CoordType sum; + int cnt; +}; + +static void VertexCoordLaplacianHC(MeshType &m, int step, bool SmoothSelected=false ) +{ + ScalarType beta=0.5; + HCSmoothInfo lpz; + lpz.sum=CoordType(0,0,0); + lpz.dif=CoordType(0,0,0); + lpz.cnt=0; + for(int i=0;i TD(m.vert,lpz); + // First Loop compute the laplacian + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD()) + { + for(int j=0;j<3;++j) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->P(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->P(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + // se l'edge j e' di bordo si deve sommare due volte + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->P(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->P(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + } + } + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + TD[*vi].sum/=(float)TD[*vi].cnt; + + // Second Loop compute average difference + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + for(int j=0;j<3;++j) + { + TD[(*fi).V(j)].dif +=TD[(*fi).V1(j)].sum-(*fi).V1(j)->P(); + TD[(*fi).V1(j)].dif+=TD[(*fi).V(j)].sum-(*fi).V(j)->P(); + // se l'edge j e' di bordo si deve sommare due volte + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].dif +=TD[(*fi).V1(j)].sum-(*fi).V1(j)->P(); + TD[(*fi).V1(j)].dif+=TD[(*fi).V(j)].sum-(*fi).V(j)->P(); + } + } + } + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + TD[*vi].dif/=(float)TD[*vi].cnt; + if(!SmoothSelected || (*vi).IsS()) + (*vi).P()= TD[*vi].sum - (TD[*vi].sum - (*vi).P())*beta + (TD[*vi].dif)*(1.f-beta); + } + } // end for step +}; + +// Laplacian smooth of the quality. + + +class ColorSmoothInfo +{ +public: + unsigned int r; + unsigned int g; + unsigned int b; + unsigned int a; + int cnt; +}; + +static void VertexColorLaplacian(MeshType &m, int step, bool SmoothSelected=false, vcg::CallBackPos * cb=0) +{ + ColorSmoothInfo csi; + csi.r=0; csi.g=0; csi.b=0; csi.cnt=0; + SimpleTempData TD(m.vert,csi); + + for(int i=0;iC()[0]; + TD[(*fi).V(j)].g+=(*fi).V1(j)->C()[1]; + TD[(*fi).V(j)].b+=(*fi).V1(j)->C()[2]; + TD[(*fi).V(j)].a+=(*fi).V1(j)->C()[3]; + + TD[(*fi).V1(j)].r+=(*fi).V(j)->C()[0]; + TD[(*fi).V1(j)].g+=(*fi).V(j)->C()[1]; + TD[(*fi).V1(j)].b+=(*fi).V(j)->C()[2]; + TD[(*fi).V1(j)].a+=(*fi).V(j)->C()[3]; + + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + // si azzaera i dati per i vertici di bordo + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)]=csi; + TD[(*fi).V1(j)]=csi; + } + + // se l'edge j e' di bordo si deve mediare solo con gli adiacenti + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].r+=(*fi).V1(j)->C()[0]; + TD[(*fi).V(j)].g+=(*fi).V1(j)->C()[1]; + TD[(*fi).V(j)].b+=(*fi).V1(j)->C()[2]; + TD[(*fi).V(j)].a+=(*fi).V1(j)->C()[3]; + + TD[(*fi).V1(j)].r+=(*fi).V(j)->C()[0]; + TD[(*fi).V1(j)].g+=(*fi).V(j)->C()[1]; + TD[(*fi).V1(j)].b+=(*fi).V(j)->C()[2]; + TD[(*fi).V1(j)].a+=(*fi).V(j)->C()[3]; + + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + if(!SmoothSelected || (*vi).IsS()) + { + (*vi).C()[0] = (unsigned int) ceil((double) (TD[*vi].r / TD[*vi].cnt)); + (*vi).C()[1] = (unsigned int) ceil((double) (TD[*vi].g / TD[*vi].cnt)); + (*vi).C()[2] = (unsigned int) ceil((double) (TD[*vi].b / TD[*vi].cnt)); + (*vi).C()[3] = (unsigned int) ceil((double) (TD[*vi].a / TD[*vi].cnt)); + } + } // end for step +}; + +static void FaceColorLaplacian(MeshType &m, int step, bool SmoothSelected=false, vcg::CallBackPos * cb=0) +{ + ColorSmoothInfo csi; + csi.r=0; csi.g=0; csi.b=0; csi.cnt=0; + SimpleTempData TD(m.face,csi); + + for(int i=0;iC()[0]; + TD[*fi].g+=(*fi).FFp(j)->C()[1]; + TD[*fi].b+=(*fi).FFp(j)->C()[2]; + TD[*fi].a+=(*fi).FFp(j)->C()[3]; + ++TD[*fi].cnt; + } + } + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD() && TD[*fi].cnt>0 ) + if(!SmoothSelected || (*fi).IsS()) + { + (*fi).C()[0] = (unsigned int) ceil((float) (TD[*fi].r / TD[*fi].cnt)); + (*fi).C()[1] = (unsigned int) ceil((float) (TD[*fi].g / TD[*fi].cnt)); + (*fi).C()[2] = (unsigned int) ceil((float) (TD[*fi].b / TD[*fi].cnt)); + (*fi).C()[3] = (unsigned int) ceil((float) (TD[*fi].a / TD[*fi].cnt)); + } + } // end for step +}; + +// Laplacian smooth of the quality. + +class QualitySmoothInfo +{ +public: + ScalarType sum; + int cnt; +}; + + +static void VertexQualityLaplacian(MeshType &m, int step=1, bool SmoothSelected=false) +{ + QualitySmoothInfo lpz; + lpz.sum=0; + lpz.cnt=0; + SimpleTempData TD(m.vert,lpz); + //TD.Start(lpz); + for(int i=0;iQ(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->Q(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + // si azzaera i dati per i vertici di bordo + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)]=lpz; + TD[(*fi).V1(j)]=lpz; + } + + // se l'edge j e' di bordo si deve mediare solo con gli adiacenti + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->Q(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->Q(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + //VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + if(!SmoothSelected || (*vi).IsS()) + (*vi).Q()=TD[*vi].sum/TD[*vi].cnt; + } + + //TD.Stop(); +}; + +static void VertexNormalLaplacian(MeshType &m, int step,bool SmoothSelected=false) +{ + LaplacianInfo lpz; + lpz.sum=CoordType(0,0,0); + lpz.cnt=0; + SimpleTempData TD(m.vert,lpz); + for(int i=0;iN(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->N(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + // si azzaera i dati per i vertici di bordo + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)]=lpz; + TD[(*fi).V1(j)]=lpz; + } + + // se l'edge j e' di bordo si deve mediare solo con gli adiacenti + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->N(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->N(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + //VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + if(!SmoothSelected || (*vi).IsS()) + (*vi).N()=TD[*vi].sum/TD[*vi].cnt; + } + +}; + +// Smooth solo lungo la direzione di vista + // alpha e' compreso fra 0(no smoot) e 1 (tutto smoot) + // Nota che se smootare il bordo puo far fare bandierine. +static void VertexCoordViewDepth(MeshType &m, + const CoordType & viewpoint, + const ScalarType alpha, + int step, bool SmoothBorder=false ) +{ + LaplacianInfo lpz; + lpz.sum=CoordType(0,0,0); + lpz.cnt=0; + SimpleTempData TD(m.vert,lpz); + for(int i=0;icP(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->cP(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + // si azzaera i dati per i vertici di bordo + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)]=lpz; + TD[(*fi).V1(j)]=lpz; + } + + // se l'edge j e' di bordo si deve mediare solo con gli adiacenti + if(SmoothBorder) + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)) + { + TD[(*fi).V(j)].sum+=(*fi).V1(j)->cP(); + TD[(*fi).V1(j)].sum+=(*fi).V(j)->cP(); + ++TD[(*fi).V(j)].cnt; + ++TD[(*fi).V1(j)].cnt; + } + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + { + CoordType np = TD[*vi].sum/TD[*vi].cnt; + CoordType d = (*vi).cP() - viewpoint; d.Normalize(); + ScalarType s = d .dot ( np - (*vi).cP() ); + (*vi).P() += d * (s*alpha); + } + } +} + + + +/****************************************************************************************************************/ +/****************************************************************************************************************/ +// Paso Double Smoothing +// The proposed +// approach is a two step method where in the first step the face normals +// are adjusted and then, in a second phase, the vertex positions are updated. +/****************************************************************************************************************/ +/****************************************************************************************************************/ +// Classi di info + +class PDVertInfo +{ +public: + CoordType np; +}; + + +class PDFaceInfo +{ +public: + CoordType m; +}; +/***************************************************************************/ +// Paso Doble Step 1 compute the smoothed normals +/***************************************************************************/ +// Requirements: +// VF Topology +// Normalized Face Normals +// +// This is the Normal Smoothing approach of Shen and Berner +// Fuzzy Vector Median-Based Surface Smoothing TVCG 2004 + + +void FaceNormalFuzzyVectorSB(MeshType &m, + SimpleTempData &TD, + ScalarType sigma) +{ + int i; + + FaceIterator fi; + + for(fi=m.face.begin();fi!=m.face.end();++fi) + { + CoordType bc=(*fi).Barycenter(); + // 1) Clear all the visited flag of faces that are vertex-adjacent to fi + for(i=0;i<3;++i) + { + vcg::face::VFIterator ep(&*fi,i); + while (!ep.End()) + { + ep.f->ClearV(); + ++ep; + } + } + + // 1) Effectively average the normals weighting them with + (*fi).SetV(); + CoordType mm=CoordType(0,0,0); + for(i=0;i<3;++i) + { + vcg::face::VFIterator ep(&*fi,i); + while (!ep.End()) + { + if(! (*ep.f).IsV() ) + { + if(sigma>0) + { + ScalarType dd=SquaredDistance(ep.f->Barycenter(),bc); + ScalarType ang=AngleN(ep.f->N(),(*fi).N()); + mm+=ep.f->N()*exp((-sigma)*ang*ang/dd); + } + else mm+=ep.f->N(); + (*ep.f).SetV(); + } + ++ep; + } + } + mm.Normalize(); + TD[*fi].m=mm; + } +} + +// Replace the normal of the face with the average of normals of the vertex adijacent faces. +// Normals are weighted with face area. +// It assumes that: +// Normals are normalized: +// VF adjacency is present. + +static void FaceNormalLaplacianVF(MeshType &m) +{ + SimpleTempData TDF(m.face); + + PDFaceInfo lpzf; + lpzf.m=CoordType(0,0,0); + + assert(tri::HasPerVertexVFAdjacency(m) && tri::HasPerFaceVFAdjacency(m) ); + TDF.Start(lpzf); + int i; + + FaceIterator fi; + + tri::UpdateNormals::AreaNormalizeFace(m); + + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + CoordType bc=Barycenter(*fi); + // 1) Clear all the visited flag of faces that are vertex-adjacent to fi + for(i=0;i<3;++i) + { + VFLocalIterator ep(&*fi,i); + for (;!ep.End();++ep) + ep.f->ClearV(); + } + + // 2) Effectively average the normals + CoordType normalSum=(*fi).N(); + + for(i=0;i<3;++i) + { + VFLocalIterator ep(&*fi,i); + for (;!ep.End();++ep) + { + if(! (*ep.f).IsV() ) + { + normalSum += ep.f->N(); + (*ep.f).SetV(); + } + } + } + normalSum.Normalize(); + TDF[*fi].m=normalSum; + } + for(fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).N()=TDF[*fi].m; + + tri::UpdateNormals::NormalizeFace(m); + + TDF.Stop(); +} + +// Replace the normal of the face with the average of normals of the face adijacent faces. +// Normals are weighted with face area. +// It assumes that: +// Normals are normalized: +// FF adjacency is present. + + +static void FaceNormalLaplacianFF(MeshType &m, int step=1, bool SmoothSelected=false ) +{ + PDFaceInfo lpzf; + lpzf.m=CoordType(0,0,0); + SimpleTempData TDF(m.face,lpzf); + assert(tri::HasFFAdjacency(m)); + + FaceIterator fi; + tri::UpdateNormals::AreaNormalizeFace(m); + for(int i=0;iN(); + + TDF[*fi].m=normalSum; + } + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!SmoothSelected || (*fi).IsS()) + (*fi).N()=TDF[*fi].m; + + tri::UpdateNormals::NormalizeFace(m); + } +} + + +/***************************************************************************/ +// Paso Doble Step 1 compute the smoothed normals +/***************************************************************************/ +// Requirements: +// VF Topology +// Normalized Face Normals +// +// This is the Normal Smoothing approach bsased on a angle thresholded weighting +// sigma is in the 0 .. 1 range, it represent the cosine of a threshold angle. +// sigma == 0 All the normals are averaged +// sigma == 1 Nothing is averaged. +// Only within the specified range are averaged toghether. The averagin is weighted with the + + +static void FaceNormalAngleThreshold(MeshType &m, + SimpleTempData &TD, + ScalarType sigma) +{ + int i; + + + FaceIterator fi; + + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + CoordType bc=Barycenter(*fi); + // 1) Clear all the visited flag of faces that are vertex-adjacent to fi + for(i=0;i<3;++i) + { + VFLocalIterator ep(&*fi,i); + for (;!ep.End();++ep) + ep.f->ClearV(); + } + + // 1) Effectively average the normals weighting them with the squared difference of the angle similarity + // sigma is the cosine of a threshold angle. sigma \in 0..1 + // sigma == 0 All the normals are averaged + // sigma == 1 Nothing is averaged. + // The averaging is weighted with the difference between normals. more similar the normal more important they are. + + CoordType normalSum=CoordType(0,0,0); + for(i=0;i<3;++i) + { + VFLocalIterator ep(&*fi,i); + for (;!ep.End();++ep) + { + if(! (*ep.f).IsV() ) + { + ScalarType cosang=ep.f->N().dot((*fi).N()); + // Note that if two faces form an angle larger than 90 deg, their contribution should be very very small. + // Without this clamping + cosang = math::Clamp(cosang,0.0001f,1.f); + if(cosang >= sigma) + { + ScalarType w = cosang-sigma; + normalSum += ep.f->N()*(w*w); // similar normals have a cosang very close to 1 so cosang - sigma is maximized + } + (*ep.f).SetV(); + } + } + } + normalSum.Normalize(); + TD[*fi].m=normalSum; + } + + for(fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).N()=TD[*fi].m; +} + +/****************************************************************************************************************/ +// Restituisce il gradiente dell'area del triangolo nel punto p. +// Nota che dovrebbe essere sempre un vettore che giace nel piano del triangolo e perpendicolare al lato opposto al vertice p. +// Ottimizzato con Maple e poi pesantemente a mano. + +CoordType TriAreaGradient(CoordType &p,CoordType &p0,CoordType &p1) +{ + CoordType dd = p1-p0; + CoordType d0 = p-p0; + CoordType d1 = p-p1; + CoordType grad; + + ScalarType t16 = d0[1]* d1[2] - d0[2]* d1[1]; + ScalarType t5 = -d0[2]* d1[0] + d0[0]* d1[2]; + ScalarType t4 = -d0[0]* d1[1] + d0[1]* d1[0]; + + ScalarType delta= sqrtf(t4*t4 + t5*t5 +t16*t16); + + grad[0]= (t5 * (-dd[2]) + t4 * ( dd[1]))/delta; + grad[1]= (t16 * (-dd[2]) + t4 * (-dd[0]))/delta; + grad[2]= (t16 * ( dd[1]) + t5 * ( dd[0]))/delta; + + return grad; +} + +template +CoordType CrossProdGradient(CoordType &p, CoordType &p0, CoordType &p1, CoordType &m) +{ + CoordType grad; + CoordType p00=p0-p; + CoordType p01=p1-p; + grad[0] = (-p00[2] + p01[2])*m[1] + (-p01[1] + p00[1])*m[2]; + grad[1] = (-p01[2] + p00[2])*m[0] + (-p00[0] + p01[0])*m[2]; + grad[2] = (-p00[1] + p01[1])*m[0] + (-p01[0] + p00[0])*m[1]; + + return grad; +} + +/* +Deve Calcolare il gradiente di +E(p) = A(p,p0,p1) (n - m)^2 = +A(...) (2-2nm) = +(p0-p)^(p1-p) +2A - 2A * ------------- m = +2A + +2A - 2 (p0-p)^(p1-p) * m +*/ + +CoordType FaceErrorGrad(CoordType &p,CoordType &p0,CoordType &p1, CoordType &m) +{ + return TriAreaGradient(p,p0,p1) *2.0f + - CrossProdGradient(p,p0,p1,m) *2.0f ; +} +/***************************************************************************/ +// Paso Doble Step 2 Fitta la mesh a un dato insieme di normali +/***************************************************************************/ + + +void FitMesh(MeshType &m, + SimpleTempData &TDV, + SimpleTempData &TDF, + float lambda) +{ + //vcg::face::Pos ep; + vcg::face::VFIterator ep; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + CoordType ErrGrad=CoordType(0,0,0); + + ep.f=(*vi).VFp(); + ep.z=(*vi).VFi(); + while (!ep.End()) + { + ErrGrad+=FaceErrorGrad(ep.f->V(ep.z)->P(),ep.f->V1(ep.z)->P(),ep.f->V2(ep.z)->P(),TDF[ep.f].m); + ++ep; + } + TDV[*vi].np=(*vi).P()-ErrGrad*(ScalarType)lambda; + } + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + (*vi).P()=TDV[*vi].np; + +} +/****************************************************************************************************************/ + + + +static void FastFitMesh(MeshType &m, + SimpleTempData &TDV, + //SimpleTempData &TDF, + bool OnlySelected=false) +{ + //vcg::face::Pos ep; + vcg::face::VFIterator ep; + VertexIterator vi; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + CoordType Sum(0,0,0); + ScalarType cnt=0; + VFLocalIterator ep(&*vi); + for (;!ep.End();++ep) + { + CoordType bc=Barycenter(*ep.F()); + Sum += ep.F()->N()*(ep.F()->N().dot(bc - (*vi).P())); + ++cnt; + } + TDV[*vi].np=(*vi).P()+ Sum*(1.0/cnt); + } + + if(OnlySelected) + { + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if((*vi).IsS()) (*vi).P()=TDV[*vi].np; + } + else + { + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + (*vi).P()=TDV[*vi].np; + } +} + + + +static void VertexCoordPasoDoble(MeshType &m, int step, typename MeshType::ScalarType Sigma=0, int FitStep=10, typename MeshType::ScalarType FitLambda=0.05) +{ + SimpleTempData< typename MeshType::VertContainer, PDVertInfo> TDV(m.vert); + SimpleTempData< typename MeshType::FaceContainer, PDFaceInfo> TDF(m.face); + PDVertInfo lpzv; + lpzv.np=CoordType(0,0,0); + PDFaceInfo lpzf; + lpzf.m=CoordType(0,0,0); + + assert(m.HasVFTopology()); + m.HasVFTopology(); + TDV.Start(lpzv); + TDF.Start(lpzf); + for(int j=0;j::PerFace(m); + FaceNormalAngleThreshold(m,TDF,Sigma); + for(int k=0;k TDV(m.vert,lpzv); + SimpleTempData< typename MeshType::FaceContainer, PDFaceInfo> TDF(m.face,lpzf); + + for(int j=0;j +static void VertexCoordLaplacianReproject(MeshType& m, GRID& grid, MeshTypeTri& gridmesh) +{ + typename MeshType::VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + if(! (*vi).IsD()) + VertexCoordLaplacianReproject(m,grid,gridmesh,&*vi); + } +} + + +template +static void VertexCoordLaplacianReproject(MeshType& m, GRID& grid, MeshTypeTri& gridmesh, typename MeshType::VertexType* vp) +{ + assert(MeshType::HEdgeType::HasHVAdjacency()); + + // compute barycenter + typedef std::vector VertexSet; + VertexSet verts; + verts = HalfEdgeTopology::getVertices(vp); + + typename MeshType::CoordType ct(0,0,0); + for(typename VertexSet::iterator it = verts.begin(); it != verts.end(); ++it) + { + ct += (*it)->P(); + } + ct /= verts.size(); + + // move vertex + vp->P() = ct; + + + vector faces2 = HalfEdgeTopology::get_incident_faces(vp); + + // estimate normal + typename MeshType::CoordType avgn(0,0,0); + + for(unsigned int i = 0;i< faces2.size();i++) + if(faces2[i]) + { + vector vertices = HalfEdgeTopology::getVertices(faces2[i]); + + assert(vertices.size() == 4); + + avgn += vcg::Normal(vertices[0]->cP(), vertices[1]->cP(), vertices[2]->cP()); + avgn += vcg::Normal(vertices[2]->cP(), vertices[3]->cP(), vertices[0]->cP()); + + } + avgn.Normalize(); + + // reproject + ScalarType diag = m.bbox.Diag(); + typename MeshType::CoordType raydir = avgn; + Ray3 ray; + + ray.SetOrigin(vp->P()); + ScalarType t; + typename MeshTypeTri::FaceType* f = 0; + typename MeshTypeTri::FaceType* fr = 0; + + vector closests; + vector minDists; + vector faces; + ray.SetDirection(-raydir); + f = vcg::tri::DoRay(gridmesh, grid, ray, diag/4.0, t); + + if (f) + { + closests.push_back(ray.Origin() + ray.Direction()*t); + minDists.push_back(fabs(t)); + faces.push_back(f); + } + ray.SetDirection(raydir); + fr = vcg::tri::DoRay(gridmesh, grid, ray, diag/4.0, t); + if (fr) + { + closests.push_back(ray.Origin() + ray.Direction()*t); + minDists.push_back(fabs(t)); + faces.push_back(fr); + } + + if (fr) if (fr->N()*raydir<0) fr=0; // discard: inverse normal; + typename MeshType::CoordType newPos; + if (minDists.size() == 0) + { + newPos = vp->P(); + f = 0; + } + else + { + int minI = std::min_element(minDists.begin(),minDists.end()) - minDists.begin(); + newPos = closests[minI]; + f = faces[minI]; + } + + if (f) + vp->P() = newPos; +} + +}; //end Smooth class + +} // End namespace tri +} // End namespace vcg + +#endif // VCG_SMOOTH diff --git a/vcg/complex/algorithms/stat.h b/vcg/complex/algorithms/stat.h new file mode 100644 index 00000000..06b9babe --- /dev/null +++ b/vcg/complex/algorithms/stat.h @@ -0,0 +1,246 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2006 \/)\/ * +* 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 $ +Revision 1.3 2007/01/11 10:12:19 cignoni +Removed useless and conflicting inclusion of face.h + +Revision 1.2 2006/05/25 09:39:09 cignoni +missing std and other gcc detected syntax errors + +Revision 1.1 2006/05/21 06:59:13 cignoni +Initial Commit + +****************************************************************************/ + +#ifndef __VCGLIB_TRIMESH_STAT +#define __VCGLIB_TRIMESH_STAT + +// Standard headers +// VCG headers + +#include +#include +#include +#include +#include +#include +#include + + +namespace vcg { + namespace tri{ +template +class Stat +{ + public: + typedef StatMeshType 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::FaceContainer FaceContainer; + typedef typename vcg::Box3 Box3Type; + + static std::pair ComputePerVertexQualityMinMax( MeshType & m) // V1.0 + { + std::pair minmax = std::make_pair(std::numeric_limits::max(),-std::numeric_limits::max()); + + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD()) + { + if( (*vi).Q() < minmax.first) minmax.first=(*vi).Q(); + if( (*vi).Q() > minmax.second) minmax.second=(*vi).Q(); + } + return minmax; + } + + static std::pair ComputePerFaceQualityMinMax( MeshType & m) // V1.0 + { + std::pair minmax = std::make_pair(std::numeric_limits::max(),-std::numeric_limits::max()); + + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + if( (*fi).Q() < minmax.first) minmax.first =(*fi).Q(); + if( (*fi).Q() > minmax.second) minmax.second=(*fi).Q(); + } + return minmax; + } + + /** + \short compute the barycenter of the surface thin-shell. + E.g. it assume a 'empty' model where all the mass is located on the surface and compute the barycenter of that thinshell. + Works for any triangulated model (no problem with open, nonmanifold selfintersecting models). + Useful for computing the barycenter of 2D planar figures. + */ + static Point3 ComputeShellBarycenter(MeshType & m) + { + Point3 barycenter(0,0,0); + ScalarType areaSum=0; + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + ScalarType area=DoubleArea(*fi); + barycenter += Barycenter(*fi)*area; + areaSum+=area; + } + return barycenter/areaSum; + } + + + static ScalarType ComputeMeshArea(MeshType & m) + { + ScalarType area=0; + + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + area += DoubleArea(*fi); + + return area/ScalarType(2.0); + } + + static void ComputePerVertexQualityDistribution( MeshType & m, Distribution &h, bool selectionOnly = false) // V1.0 + { + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD() && ((!selectionOnly) || (*vi).IsS()) ) + { + assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); + h.Add((*vi).Q()); + } + } + + static void ComputePerFaceQualityDistribution( MeshType & m, Distribution &h, bool selectionOnly = false) // V1.0 + { + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD() && ((!selectionOnly) || (*fi).IsS()) ) + { + assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); + h.Add((*fi).Q()); + } + } + + static void ComputePerFaceQualityHistogram( MeshType & m, Histogramf &h, bool selectionOnly=false,int HistSize=10000 ) + { + std::pair minmax = tri::Stat::ComputePerFaceQualityMinMax(m); + h.Clear(); + h.SetRange( minmax.first,minmax.second, HistSize ); + for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD() && ((!selectionOnly) || (*fi).IsS()) ){ + assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); + h.Add((*fi).Q()); + } + } + + static void ComputePerVertexQualityHistogram( MeshType & m, Histogramf &h, bool selectionOnly = false, int HistSize=10000 ) // V1.0 + { + VertexIterator vi; + std::pair minmax = ComputePerVertexQualityMinMax(m); + + h.Clear(); + h.SetRange( minmax.first,minmax.second, HistSize); + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD() && ((!selectionOnly) || (*vi).IsS()) ) + { + assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); + h.Add((*vi).Q()); + } + // Sanity check; If some very wrong value has happened in the Q value, + // the histogram is messed. If a significant percentage (20% )of the values are all in a single bin + // we should try to solve the problem. No easy solution here. + // We choose to compute the get the 1percentile and 99 percentile values as new mixmax ranges + // and just to be sure enlarge the Histogram. + + if(h.MaxCount() > HistSize/5) + { + std::vector QV; + QV.reserve(m.vn); + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD()) QV.push_back((*vi).Q()); + + std::nth_element(QV.begin(),QV.begin()+m.vn/100,QV.end()); + float newmin=*(QV.begin()+m.vn/100); + std::nth_element(QV.begin(),QV.begin()+m.vn-m.vn/100,QV.end()); + float newmax=*(QV.begin()+m.vn-m.vn/100); + + h.Clear(); + h.SetRange(newmin, newmax, HistSize*50); + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!(*vi).IsD() && ((!selectionOnly) || (*vi).IsS()) ) + h.Add((*vi).Q()); + } + } + + static int ComputeEdgeHistogram( MeshType & m, Histogramf &h) // V1.0 + { + ScalarType Diag = m.bbox.Diag(); + h.Clear(); + h.SetRange( 0, Diag, 10000); + FaceIterator fi;VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) (*vi).ClearV(); + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if(!(*fi).IsD()) + { + if( !(*fi).V(0)->IsV() && !(*fi).V(1)->IsV() ) + { + h.Add(Distance((*fi).V(0)->P(),(*fi).V(1)->P())); + (*fi).V(0)->SetV(); + (*fi).V(1)->SetV(); + } + if( !(*fi).V(1)->IsV() && !(*fi).V(2)->IsV()) + { + h.Add(Distance((*fi).V(1)->P(),(*fi).V(2)->P())); + (*fi).V(2)->SetV(); + (*fi).V(1)->SetV(); + } + if( !(*fi).V(2)->IsV() && !(*fi).V(0)->IsV()) + { + h.Add(Distance((*fi).V(2)->P(),(*fi).V(0)->P())); + (*fi).V(0)->SetV(); + (*fi).V(2)->SetV(); + } + } + } + + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)(*vi).ClearV(); + return 0; + } +}; // end class + + } //End Namespace tri +} // End Namespace vcg + +#endif + diff --git a/vcg/complex/algorithms/subset.h b/vcg/complex/algorithms/subset.h new file mode 100644 index 00000000..e43aa0a3 --- /dev/null +++ b/vcg/complex/algorithms/subset.h @@ -0,0 +1,171 @@ +/**************************************************************************** +* 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 $ +Revision 1.12 2007/05/31 09:39:55 cignoni +Small gcc compiling issues + +Revision 1.11 2006/07/06 12:30:32 ganovelli +misleading comment removed + +Revision 1.10 2005/12/14 17:14:13 pietroni +added assert on deleted flag condition + +Revision 1.9 2005/02/08 14:38:05 turini +Warnings Correction + +Revision 1.8 2004/05/17 08:26:28 turini +Changed : Parameters Order As In vcg::tetra::SubSet. + +Revision 1.7 2004/05/17 07:58:16 turini +Minor Changes To Compile Even Without using namespace std. + +Revision 1.6 2004/05/14 11:43:17 turini +Changed mesh ClearFlag call. + +Revision 1.5 2004/05/13 09:59:20 turini +Added typedef typename in InsertedV + +Revision 1.4 2004/05/07 10:06:46 turini +include Plane3 removed. + +Revision 1.3 2004/05/07 09:35:09 turini +Added History Info + +****************************************************************************/ + + +#ifndef __VCGLIB_TRISUBSET +#define __VCGLIB_TRISUBSET + +#include + +namespace vcg { +namespace tri { + + +template +struct InsertedV +{ + typedef I_MESH_TYPE IMeshType; + typedef typename IMeshType::VertexPointer VertexPointer; + typedef typename IMeshType::FacePointer FacePointer; + + InsertedV(VertexPointer _v, FacePointer _f, int _z) + : v(_v), f(_f), z(_z) + {} + + VertexPointer v; + FacePointer f; + int z; + + bool operator < (const InsertedV & o) const + { + return (v +void SubSet(S_MESH_TYPE & m, STL_CONT & subSet) +{ + std::vector< InsertedV > newVertices; + typename STL_CONT::const_iterator pfi; + typename S_MESH_TYPE::VertexIterator vi; + typename S_MESH_TYPE::FaceIterator fi; + typedef typename S_MESH_TYPE::VertexType S_VertexType; + std::vector redirect; + + + fi = vcg::tri::Allocator::AddFaces(m,subSet.size()); + for(pfi=subSet.begin(); pfi!=subSet.end(); ++pfi) + { + assert(!(*pfi)->IsD()); + (*fi).ImportData(**pfi); + for(int ii = 0 ; ii < (*fi).VN(); ++ii) + (*fi).V(ii) = (S_VertexType*)(void*)(*pfi)->V(ii); + ++fi; + } + + + for(fi=m.face.begin(); fi!=m.face.end(); ++fi) + for(int ii = 0 ; ii < (*fi).VN(); ++ii) + newVertices.push_back(InsertedV((*fi).V(ii), &(*fi),ii)); + + sort(newVertices.begin(), newVertices.end()); + + typename std::vector< InsertedV >::iterator curr, next; + int pos=0; + curr=next=newVertices.begin(); + while(next!=newVertices.end()) + { + if((*curr)!=(*next)) + pos++; + (*next).f->V((*next).z)=(typename S_MESH_TYPE::VertexPointer)pos; + curr=next; + next++; + } + + typename std::vector< InsertedV >::iterator newE=unique(newVertices.begin(), newVertices.end()); + + vi = vcg::tri::Allocator::AddVertices(m,newE-newVertices.begin()); + for(curr=newVertices.begin(); curr!=newE; ++curr,++vi) + (*vi).ImportData(*((*curr).v)); + + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) + redirect.push_back(&(*vi)); + + for(fi=m.face.begin(); fi!=m.face.end(); ++fi) + { + for(int ii = 0 ; ii < (*fi).VN(); ++ii) + (*fi).V(ii)=redirect[(size_t)(*fi).V(ii)]; + } + m.vn=(int)m.vert.size(); + m.fn=(int)m.face.size(); +} + + +} // End Namespace TriMesh +} // End Namespace vcg + + +#endif + + diff --git a/vcg/complex/algorithms/textcoord_optimization.h b/vcg/complex/algorithms/textcoord_optimization.h new file mode 100644 index 00000000..b0cc89d0 --- /dev/null +++ b/vcg/complex/algorithms/textcoord_optimization.h @@ -0,0 +1,453 @@ +/**************************************************************************** +* 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 $ +Revision 1.6 2007/02/02 04:11:00 tarini +added parameter theta (from conformal to equiareal) to AreaPresTextureOptimizer. +Improved feature lists (comments). + +Revision 1.5 2007/02/02 01:39:58 tarini +added three general-utility global functions for texture coordinates: SmoothTextureCoords, IsFoldFree, MarkFolds (see descriptions) + +Revision 1.4 2007/02/02 01:23:47 tarini +added a few general comments on AreaPreserving optimizer, recapping optimizer features. + +Revision 1.3 2007/02/02 01:18:15 tarini +First version: general virtual class for texture optimizers. A subclass for area preservation. + + +****************************************************************************/ + +#ifndef __VCGLIB__TEXTCOOORD_OPTIMIZATION +#define __VCGLIB__TEXTCOOORD_OPTIMIZATION + +#include + + +/* + +SINGLE PATCH TEXTURE OPTIMIZATIONS + +A set of classes to perform optimizations of disk->disk parametrization. + +Requires texture coords to be defined per vertex (replicate seams). + +*/ + + +namespace vcg +{ +namespace tri +{ + + +/* Base class for all Texture Optimizers*/ +template +class TextureOptimizer{ +protected: + MESH_TYPE &m; + SimpleTempData isFixed; +public: + + /* Tpyes */ + typedef MESH_TYPE MeshType; + typedef typename MESH_TYPE::VertexIterator VertexIterator; + typedef typename MESH_TYPE::FaceIterator FaceIterator; + typedef typename MESH_TYPE::VertexType VertexType; + typedef typename MESH_TYPE::FaceType FaceType; + typedef typename MESH_TYPE::ScalarType ScalarType; + + + /* Access functions */ + const MeshType & Mesh() const {return m;} + MeshType & Mesh() {return m;} + + /* Constructior */ + TextureOptimizer(MeshType &_m):m(_m),isFixed(_m.vert){ + assert(m.HasPerVertexTexture()); + } + + // initializes on current geometry + virtual void TargetCurrentGeometry()=0; + + // performs an interation. Returns largest movement. + virtual ScalarType Iterate()=0; + + // performs an iteration (faster, but it does not tell how close it is to stopping) + virtual void IterateBlind()=0; + + // performs iteration + virtual ScalarType IterateN(int step){ + for (int i=0; iIterateBlind(); + } + if (step>1) return this->Iterate(); else return 0; + } + + // performs iterations until convergence. + bool IterateUntilConvergence(ScalarType threshold=0.0001, int maxite=5000){ + int i; + while (Iterate()>threshold) { + if (i++>maxite) return false; + } + return true; + } + + // desctuctor: free temporary field + ~TextureOptimizer(){ + isFixed.Stop(); + }; + + // set the current border as fixed (forced to stay in position during text optimization) + void SetBorderAsFixed(){ + isFixed.Start(); + for (VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) { + isFixed[v]=(v->IsB())?1:0; + } + } + + // everything moves, no vertex must fixed during texture optimization) + void SetNothingAsFixed(){ + isFixed.Start(); + for (VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) { + isFixed[v]=0; + } + } + + // fix a given vertex + void FixVertex(const VertexType *v, bool fix=true){ + isFixed[v]=(fix)?1:0; + } + + +}; + + + +/* +AREA PRESERVING TEXTURE OPTIMIZATION + +as in: Degener, P., Meseth, J., Klein, R. + "An adaptable surface parameterization method." + Proc. of the 12th International Meshing oundtable, 201–213 [2003]. + +Features: + +:) - Balances angle and area distortions (best results!). +:) - Can choose how to balance area and angle preservation (see SetTheta) + theta=0 -> pure conformal (use MIPS instead!) + theta=3 -> good balance between area and angle preservation + theta>3 -> care more about area than about angles +:( - Slowest method. +:( - Requires a fixed boundary, else expands forever in texture space (unless theta=0). +:( - Diverges in presence of flipped faces (unless theta=0). +:( - Requires a speed parameter to be set. + Speed too large => when close, bounces back and forth around minimum, w/o getting any closer. + Lower speed => longer convercence times +*/ + +template +class AreaPreservingTextureOptimizer:public TextureOptimizer{ +public: + /* Types */ + typedef MESH_TYPE MeshType; + typedef typename MESH_TYPE::VertexIterator VertexIterator; + typedef typename MESH_TYPE::FaceIterator FaceIterator; + typedef typename MESH_TYPE::VertexType VertexType; + typedef typename MESH_TYPE::FaceType FaceType; + typedef typename MESH_TYPE::ScalarType ScalarType; + + +private: + typedef TextureOptimizer Super; // superclass (commodity) + + // extra data per face: [0..3] -> cotangents. [4] -> area*2 + SimpleTempData > data; + SimpleTempData > sum; + + ScalarType totArea; + ScalarType speed; + + int theta; + +public: + + // constructor and destructor + AreaPreservingTextureOptimizer(MeshType &_m):Super(_m),data(_m.face),sum(_m.vert){ + speed=0.001; + theta=3; + } + + ~AreaPreservingTextureOptimizer(){ + data.Stop(); + sum.Stop(); + Super::isFixed.Stop(); + } + + void SetSpeed(ScalarType _speed){ + speed=_speed; + } + + ScalarType GetSpeed(){ + return speed; + } + + // sets the parameter theta: + // good parameters are in 1..3 + // 0 = converge to pure conformal, ignore area preservation + // 3 = good balance between area and conformal + // >3 = area more important, angle preservation less important + void SetTheta(int _theta){ + theta=_theta; + } + + int GetTheta(){ + return theta; + } + + void IterateBlind(){ + /* todo: do as iterate, but without */ + Iterate(); + } + + ScalarType Iterate(){ + + ScalarType max; // max displacement + + #define v0 (f->V0(i)->T().P()) + #define v1 (f->V1(i)->T().P()) + #define v2 (f->V2(i)->T().P()) + for (VertexIterator v=Super::m.vert.begin(); v!=Super::m.vert.end(); v++) { + sum[v].SetZero(); + } + + ScalarType tot_proj_area=0; + for (FaceIterator f=Super::m.face.begin(); f!=Super::m.face.end(); f++) { + int i=0; + double area2 = ((v1-v0) ^ (v2-v0)); + tot_proj_area+=area2; + } + + double scale= 1.0; //tot_proj_area / tot_area ; + + for (FaceIterator f=Super::m.face.begin(); f!=Super::m.face.end(); f++) { + int i=0; ScalarType area2 = ((v1-v0) ^ (v2-v0)); + for (i=0; i<3; i++){ + ScalarType + a = (v1-v0).Norm(), + b = ((v1-v0) * (v2-v0))/a, + c = area2 / a, + + m0= data[f][i] / area2, + m1= data[f][(i+1)%3] / area2, + m2= data[f][(i+2)%3] / area2, + + mx= (b-a)/area2, + my= c/area2, // 1.0/a + mA= data[f][3]/area2 * scale, + e = m0*((b-a)*(b-a)+c*c) + m1*(b*b+c*c) + m2*a*a, // as obvious + M1= mA + 1.0/mA, + M2= mA - 1.0/mA, + px= e*my, + py=-e*mx, + qx= m1*b+ m2*a, + qy= m1*c, + + /* linear weightings + + dx= (OMEGA) * (my * M2) + + (1-OMEGA) * ( px - 2.0*qx), + dy= (OMEGA) * (-mx * M2) + + (1-OMEGA) * ( py - 2.0*qy),*/ + + // exponential weighting + // 2d gradient + + dx=// M1 + //*M1 // ^ theta-1 + pow(M1,theta-1) + *(px*(M1+ theta*M2) - 2.0*qx*M1), + dy=// M1 + //*M1 // ^ theta-1 + pow(M1,theta-1) + *(py*(M1+ theta*M2) - 2.0*qy*M1), + + gy= dy/c, + gx= (dx - gy*b) / a; + + // 3d gradient + + sum[f->V(i)]+= ( (v1-v0) * gx + (v2-v0) * gy ) * data[f][3]; + } + } + max=0; // max displacement + speed=0.001; + for (VertexIterator v=Super::m.vert.begin(); v!=Super::m.vert.end(); v++) + if ( !Super::isFixed[v] ) //if (!v->IsB()) + { + ScalarType n=sum[v].Norm(); + if ( n > 1 ) { sum[v]/=n; n=1.0;} + if ( n*speed<=0.1 ); { + v->T().P()-=(sum[v] * speed ) /** scale*/; + if (maxV(1)->P() - f->V(0)->P() )^(f->V(2)->P() - f->V(0)->P() )).Norm(); + totArea+=area2; + //if ( Super::isFixed[f->V1(0)] ) + for (int i=0; i<3; i++){ + data[f][i]=( + (f->V1(i)->P() - f->V0(i)->P() )*(f->V2(i)->P() - f->V0(i)->P() ) + )/area2; + data[f][3]=area2; + } + } + } + +}; + + + +/* texture coords general utility functions */ +/*++++++++++++++++++++++++++++++++++++++++++*/ + +// returns false if any fold is present (faster than MarkFolds) +template +bool IsFoldFree(MESH_TYPE &m){ + + assert(m.HasPerVertexTexture()); + + typedef typename MESH_TYPE::VertexType::TextureType::PointType PointType; + typedef typename MESH_TYPE::VertexType::TextureType::PointType::ScalarType ScalarType; + + ScalarType lastsign=0; + for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){ + ScalarType sign=((f->V(1)->T().P()-f->V(0)->T().P()) ^ (f->V(2)->T().P()-f->V(0)->T().P())); + if (sign!=0) { + if (sign*lastsign<0) return false; + lastsign=sign; + } + } + return true; +} + +// detects and marks folded faces, by setting their quality to 0 (or 1 otherwise) +// returns number of folded faces +template +int MarkFolds(MESH_TYPE &m){ + + assert(m.HasPerVertexTexture()); + assert(m.HasPerFaceQuality()); + + typedef typename MESH_TYPE::VertexType::TextureType::PointType PointType; + typedef typename MESH_TYPE::VertexType::TextureType::PointType::ScalarType ScalarType; + + SimpleTempData sign(m.face); + sign.Start(0); + + // first pass, determine predominant sign + int npos=0, nneg=0; + ScalarType lastsign=0; + for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){ + ScalarType fsign=((f->V(1)->T().P()-f->V(0)->T().P()) ^ (f->V(2)->T().P()-f->V(0)->T().P())); + if (fsign<0) { sign[f]=-1; nneg++; } + if (fsign>0) { sign[f]=+1; npos++; } + } + + // second pass, detect folded faces + int res=0; + short gsign= (nneg>npos)?-1:+1; + for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){ + if (sign[f]*gsign<0){ + res++; + f->Q()=0; + } else f->Q()=1; + } + + sign.Stop(); + + return res; +} + +// Smooths texture coords. +// (can be useful to remove folds, +// e.g. these created when obtaining tecture coordinates after projections) +template +void SmoothTextureCoords(MESH_TYPE &m){ + + assert(m.HasPerVertexTexture()); + + typedef typename MESH_TYPE::VertexType::TextureType::PointType PointType; + + SimpleTempData div(m.vert); + SimpleTempData sum(m.vert); + + div.Start(); + sum.Start(); + + for (typename MESH_TYPE::VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) { + sum[v].SetZero(); + div[v]=0; + } + + for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){ + div[f->V(0)] +=2; sum[f->V(0)] += f->V(2)->T().P(); sum[f->V(0)] += f->V(1)->T().P(); + div[f->V(1)] +=2; sum[f->V(1)] += f->V(0)->T().P(); sum[f->V(1)] += f->V(2)->T().P(); + div[f->V(2)] +=2; sum[f->V(2)] += f->V(1)->T().P(); sum[f->V(2)] += f->V(0)->T().P(); + } + + for (typename MESH_TYPE::VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) // if (!v->IsB()) + { + if (v->div>0) { + v->T().P() = sum[v]/div[v]; + } + } + + div.Stop(); + sum.Stop(); + +} + + + +} } // End namespace vcg::tri + +#endif // __VCGLIB__TEXTCOOORD_OPTIMIZATION diff --git a/vcg/complex/algorithms/update/bounding.h b/vcg/complex/algorithms/update/bounding.h new file mode 100644 index 00000000..9a1ca446 --- /dev/null +++ b/vcg/complex/algorithms/update/bounding.h @@ -0,0 +1,94 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +// marco: removed types FaceType, FacePointer, FaceIterator to allow the use of this method from vertex meshes + + +/**************************************************************************** + History + +$Log: not supported by cvs2svn $ +Revision 1.2 2004/09/15 11:16:27 ganovelli +changed P() to cP() + +Revision 1.1 2004/04/05 11:56:13 cignoni +First working version! + +Revision 1.2 2004/03/12 15:22:19 cignoni +Written some documentation and added to the trimes doxygen module + +Revision 1.1 2004/03/05 10:59:24 cignoni +Changed name from plural to singular (normals->normal) + +Revision 1.1 2004/03/04 00:05:50 cignoni +First working version! + +Revision 1.1 2004/02/19 13:11:06 cignoni +Initial commit + + +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_BOUNDING +#define __VCG_TRI_UPDATE_BOUNDING + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile bounding.h vcg/complex/trimesh/update/bounding.h + +/// \brief Management, updating and computation of per-vertex and per-face normals. +/** +This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh. +*/ + +template +class UpdateBounding +{ + +public: +typedef ComputeMeshType MeshType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; + +/// \brief Calculates the bounding box of the \code \endcode m + +static void Box(ComputeMeshType &m) +{ + m.bbox.SetNull(); + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if( !(*vi).IsD() ) m.bbox.Add((*vi).cP()); + +} + + +}; // end class + +} // End namespace +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/update/color.h b/vcg/complex/algorithms/update/color.h new file mode 100644 index 00000000..2a500eae --- /dev/null +++ b/vcg/complex/algorithms/update/color.h @@ -0,0 +1,878 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCG_TRI_UPDATE_COLOR +#define __VCG_TRI_UPDATE_COLOR +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile color.h vcg/complex/trimesh/update/color.h + +/// \brief Generation of per-vertex and per-face colors according to various strategy. +/** +This class is used to compute per face or per vertex color with respect to for example Border (UpdateColor::VertexBorderFlag), Selection (UpdateColor::FaceSelected), Quality . +*/ + +template +class UpdateColor +{ +public: +typedef UpdateMeshType MeshType; +typedef typename UpdateMeshType::VertexType VertexType; +typedef typename UpdateMeshType::VertexPointer VertexPointer; +typedef typename UpdateMeshType::VertexIterator VertexIterator; +typedef typename UpdateMeshType::FaceType FaceType; +typedef typename UpdateMeshType::FacePointer FacePointer; +typedef typename UpdateMeshType::FaceIterator FaceIterator; + + + + class ColorAvgInfo + { + public: + unsigned int r; + unsigned int g; + unsigned int b; + unsigned int a; + int cnt; + }; + + static void VertexFromFace( UpdateMeshType &m) + { + ColorAvgInfo csi; + csi.r=0; csi.g=0; csi.b=0; csi.cnt=0; + SimpleTempData TD(m.vert,csi); + + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + { + TD[(*fi).V(j)].r+=(*fi).C()[0]; + TD[(*fi).V(j)].g+=(*fi).C()[1]; + TD[(*fi).V(j)].b+=(*fi).C()[2]; + TD[(*fi).V(j)].a+=(*fi).C()[3]; + ++TD[(*fi).V(j)].cnt; + } + + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && TD[*vi].cnt>0 ) + { + (*vi).C()[0] = TD[*vi].r / TD[*vi].cnt; + (*vi).C()[1] = TD[*vi].g / TD[*vi].cnt; + (*vi).C()[2] = TD[*vi].b / TD[*vi].cnt; + (*vi).C()[3] = TD[*vi].a / TD[*vi].cnt; + } + } + + static void FaceFromVertex( UpdateMeshType &m) + { + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + Color4f avg = (Color4f::Construct((*fi).V(0)->C()) + + Color4f::Construct((*fi).V(1)->C()) + + Color4f::Construct((*fi).V(2)->C()) )/ 3.0; + (*fi).C().Import(avg); + } + } + + + +/// \brief Color the vertexes of the mesh that are on the border + +/** +It uses the information in the Vertex flags, and not any topology. +So it just require that you have correctly computed the flags; + + - vcg::tri::UpdateTopology::FaceFace(m.cm); + - vcg::tri::UpdateFlags::FaceBorderFromFF(m.cm); + - vcg::tri::UpdateFlags::VertexBorderFromFace (m.cm); + - vcg::tri::UpdateColor::VertexBorderFlag(m.cm); + +*/ +static void VertexBorderFlag( UpdateMeshType &m, Color4b BorderColor=Color4b::Blue, Color4b InternalColor=Color4b::White, Color4b MixColor=Color4b::Cyan) +{ + Color4b BaseColor = Color4b::Green; + + VertexConstant(m,BaseColor); + + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsB(j)){ + if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = BorderColor; + if( (*fi).V(j)->C() == InternalColor) (*fi).V(j)->C() = MixColor; + if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = BorderColor; + if( (*fi).V1(j)->C() == InternalColor) (*fi).V1(j)->C() = MixColor; + } else + { + if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = InternalColor; + if( (*fi).V(j)->C() == BorderColor) (*fi).V(j)->C() = MixColor; + if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = InternalColor; + if( (*fi).V1(j)->C() == BorderColor) (*fi).V1(j)->C() = MixColor; + } + +} + + +/// This function colores the face of a mesh randomly. +/// The faux bit is used to color polygonal faces uniformly +static void FaceRandomConnectedComponent( UpdateMeshType &m) +{ + std::vector< std::pair > CCV; + int ScatterSize= min (100,tri::Clean::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many. + + ConnectedIterator ci; + for(unsigned int i=0;i FPV; + for(ci.start(m,CCV[i].second);!ci.completed();++ci) + (*ci)->C()=BaseColor; + } +} + +/// This function colores the face of a mesh randomly. +/// The faux bit is used to color polygonal faces uniformly +static void MultiFaceRandom( UpdateMeshType &m) +{ + FaceIterator fi; + Color4b BaseColor = Color4b::Black; + FaceConstant(m,BaseColor); + int id_num=0; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + id_num++; + if((*fi).C() == BaseColor) (*fi).C() = Color4b::Scatter(50, id_num%50,.4f,.7f); + for(int j=0;j<3;++j) + if((*fi).IsF(j)) + { + assert(!IsBorder((*fi),j)); + (*fi).FFp(j)->C()= (*fi).C(); + } + } +} + +static void FaceBF( UpdateMeshType &m, Color4b vn=Color4b::White, Color4b vb=Color4b::Blue, + Color4b vc=Color4b::Red, Color4b vs=Color4b::LightBlue) +{ + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + (*fi).C() = vn; + + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + if((*fi).IsS()) + (*fi).C() = vs; + else + { + for(int j=0;j<3;++j) + if(*fi.IsManifold(j)){ + if((*fi).IsB(j)){ + (*fi).C() = vb; + (*fi).C() = vb; + } + } + else + { + (*fi).C() = vc; + (*fi).C() = vc; + } + } + } +} + + +static int FaceSelected(UpdateMeshType &m, Color4b vs=Color4b::LightBlue) +{ + int cnt=0; + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()){ + if((*fi).IsS()) { (*fi).C() = vs; ++cnt; } + else (*fi).C() = Color4b::White; + } + return cnt; +} + +static void FaceColorStrip(UpdateMeshType &m, std::vector &TStripF) +{ + vcg::Color4b cc[7]={ + vcg::Color4b::White , + vcg::Color4b::Red , + vcg::Color4b::Green , + vcg::Color4b::Blue , + vcg::Color4b::Cyan , + vcg::Color4b::Yellow , + vcg::Color4b::Magenta + }; + int cnt=0; + + typename std::vector::iterator fi; + for(fi=TStripF.begin();fi!=TStripF.end();++fi) + if(*fi) (**fi).C().ColorRamp(0,16,cnt); + else cnt=(cnt+1)%16; + // if(*fi) (**fi).C()=cc[cnt]; + // else cnt=(cnt+1)%7; + +} + + +static int VertexSelected(UpdateMeshType &m, Color4b vs=Color4b::LightBlue) +{ + int cnt=0; + typename UpdateMeshType::VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()){ + if((*vi).IsS()) {(*vi).C() = vs; ++cnt; } + else (*vi).C() = Color4b::White; + } + return cnt; +} + +static void VertexConstant(UpdateMeshType &m, Color4b c=Color4b::White) +{ + typename UpdateMeshType::VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).C()=c; +} + +static void FaceConstant(UpdateMeshType &m, Color4b c=Color4b::White) +{ + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).C()=c; +} + +static void VertexBorderManifoldFlag(UpdateMeshType &m, Color4b vn=Color4b::White, Color4b vb=Color4b::Blue, Color4b vc=Color4b::Red) +{ + typename UpdateMeshType::VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).C()=vn; + + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + for(int j=0;j<3;++j) + if((*fi).IsManifold(j)){ + if((*fi).IsB(j)){ + (*fi).V(j)->C()=vb; + (*fi).V1(j)->C()=vb; + } + } + else + { + (*fi).V(j)->C()=vc; + (*fi).V1(j)->C()=vc; + } +} + +static void FaceQualityGray(UpdateMeshType &m, float minq, float maxq) +{ + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + (*fi).C().SetGrayShade( ((*fi).Q()-minq)/(maxq-minq)); +} + +static void FaceQualityGray(UpdateMeshType &m) +{ + std::pair minmax = Stat::ComputePerFaceQualityMinMax(m); + FaceQualityGray(m,minmax.first,minmax.second); +} + +static void FaceQualityRamp(UpdateMeshType &m,bool selected=false) +{ + // step 1: find the range + typename UpdateMeshType::FaceIterator fi; + float minq=std::numeric_limits::max(), + maxq=-std::numeric_limits::max(); + + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + if(!selected || (*fi).IsS()) + { + minq=std::min(minq,(*fi).Q()); + maxq=std::max(maxq,(*fi).Q()); + } + + FaceQualityRamp(m,minq,maxq,selected); +} + +static void FaceQualityRamp(UpdateMeshType &m, float minq, float maxq,bool selected=false) +{ + typename UpdateMeshType::FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + if(!selected || (*fi).IsS()) + (*fi).C().ColorRamp(minq,maxq,(*fi).Q()); +} + +static void VertexQualityRamp(UpdateMeshType &m, float minq, float maxq) +{ + typename UpdateMeshType::VertexIterator vi; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) + (*vi).C().ColorRamp(minq,maxq,(*vi).Q()); +} + +static void VertexQualityRamp(UpdateMeshType &m) +{ + std::pair minmax = Stat::ComputePerVertexQualityMinMax(m); + VertexQualityRamp(m,minmax.first,minmax.second); +} + +static void VertexQualityGray(UpdateMeshType &m, const float minq, const float maxq) +{ + typename UpdateMeshType::VertexIterator vi; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) + (*vi).C().SetGrayShade( ((*vi).Q()-minq)/(maxq-minq)); +} + +static void VertexQualityGray(UpdateMeshType &m) +{ + std::pair minmax = Stat::ComputePerVertexQualityMinMax( m); + VertexQualityGray(m,minmax.first,minmax.second); +} + +//Fill the mesh with the selected color. +static int Filling(UpdateMeshType &m, Color4b c, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = c; + ++counter; + } + } + } + return counter; +} + +//Reduces the mesh to two colors according to a threshold. +static int Thresholding(UpdateMeshType &m, float threshold, Color4b c1 = Color4::Black, Color4b c2 = Color4::White, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + float value = ComputeLightness((*vi).C()); + + if(value<=threshold) (*vi).C() = c1; + else (*vi).C() = c2; + ++counter; + } + } + } + return counter; +} + +//Computes the lightness value for a specified color. lightness = 0.5*(Max(R,G,B)+Min(R,G,B)) +static float ComputeLightness(Color4b c) +{ + float min_rgb = (float)math::Min(c[0],c[1],c[2]); + float max_rgb = (float)math::Max(c[0],c[1],c[2]); + return (max_rgb + min_rgb)/2; +} + +//Apply the brightness filter, with the given amount, to the mesh. +static int Brighting(UpdateMeshType &m, float amount, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = Color4b( + math::Clamp(int((*vi).C()[0]+amount),0,255), + math::Clamp(int((*vi).C()[1]+amount),0,255), + math::Clamp(int((*vi).C()[2]+amount),0,255), + 255); + ++counter; + } + } + } + return counter; +} + +//Apply Contrast filter to the mesh with the given contrast factor. +static int Contrast(UpdateMeshType &m, float factor, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorMul((*vi).C(),factor); + ++counter; + } + } + } + return counter; +} + +//Performs contrast operations on color, i.e expands or compress the histogram around +//the midpoint value. NewValue = (OldValue - 128) â—Š factor + 128 +static Color4b ColorMul(Color4b c, float factor) +{ + return Color4b( ValueMul(c[0], factor), ValueMul(c[1], factor), ValueMul(c[2], factor), 1); +} + +static int ValueMul(int value, float factor) +{ + return math::Clamp((int)((value - 128)*factor + 128), 0, 255); +} + +//Apply Brightness and Contrast filter to the mesh, with the given contrast factor and brightness amount. +static int BrightnessContrast(UpdateMeshType &m, float brightness, float contrast, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorBrightnessContrast((*vi).C(),brightness,contrast); + ++counter; + } + } + } + return counter; +} + +//Performs contrast and brightness operations on color, i.e NewValue = (OldValue - 128) â—Š contrast + 128 + amount +//The result is clamped just one time after all computations; this get a more accurate result. +// The formula used here is the one of GIMP. +static Color4b ColorBrightnessContrast(Color4b c, float brightness, float contrast) +{ + return Color4b( ValueBrightnessContrast(c[0], brightness, contrast), + ValueBrightnessContrast(c[1], brightness, contrast), + ValueBrightnessContrast(c[2], brightness, contrast), 1 ); +} + +static int ValueBrightnessContrast(unsigned char ivalue, float brightness, float contrast) +{ + float value = float(ivalue)/255.0f; + if (brightness < 0.0) value = value * ( 1.0 + brightness); + else value = value + ((1.0 - value) * brightness); + value = (value - 0.5) * (tan ((contrast + 1) * M_PI/4) ) + 0.5; + return math::Clamp(255.0*value, 0, 255); +} + +//Invert the colors of the mesh. +static int Invert(UpdateMeshType &m, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorInvert((*vi).C()); + ++counter; + } + } + } + return counter; +} + +//Invert the rgb component of the color +static Color4b ColorInvert(Color4b c) +{ + return Color4b( ValueInvert(c[0]), ValueInvert(c[1]), ValueInvert(c[2]), 1); +} + +static int ValueInvert(int value) +{ + return 255-value; +} +//Apply the gamma correction filter, with the given gamma exponet, to the mesh. +static int Gamma(UpdateMeshType &m, float gamma, const bool ProcessSelected=false) +{ + int counter=0; + + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorPow((*vi).C(), 1/gamma); + ++counter; + } + } + } + return counter; +} + +//computes the standard gamma transformation on a given color, according to NewVal = OldVal^(1/gamma) +static Color4b ColorPow(Color4b c, float exponent) +{ + return vcg::Color4b( + math::Clamp((int)( ValuePow(float(c[0])/255, exponent)*255), 0, 255), + math::Clamp((int)( ValuePow(float(c[1])/255, exponent)*255), 0, 255), + math::Clamp((int)( ValuePow(float(c[2])/255, exponent)*255), 0, 255), + 255); +} + +static float ValuePow(float value, float exponent) +{ + return powf(value, exponent); +} + +//useful bit masks for RGB channels, used for Levels filter. +enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 }; + +//Adjusts color levels of the mesh. Filter can be applied to all RGB channels or to each channel separately. +//in_min, gamma and in_max are respectively the black point, the gray point and the white point. +//out_min and out_max are the output level for black and white respectively. +static int Levels(UpdateMeshType &m, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask); + ++counter; + } + } + } + return counter; +} + +//Performs levels transformation on each channel set to 1 in the rgbMask. +static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask) +{ + unsigned char r = c[0], g = c[1], b = c[2]; + if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max); + if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max); + if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max); + return Color4b(r, g, b, 255); +} + +//Transform on levels +static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max) +{ + float fvalue = value/255.0f; + // normalize + fvalue = math::Clamp(fvalue - in_min, 0.0f, 1.0f) / math::Clamp(in_max - in_min, 1.0f/255.0f, 1.0f); + // transform gamma + fvalue = powf(fvalue,1/gamma); + // rescale range + fvalue = fvalue * (out_max - out_min) + out_min; + //back in interval [0,255] and clamp + return math::Clamp((int)(fvalue * 255), 0, 255); +} + +//Colors the mesh. Color is blended to the mesh with the given intensity. +static int Colourisation(UpdateMeshType &m, Color4b c, float intensity, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorApplyDiff((*vi).C(), c, intensity); + ++counter; + } + } + } + return counter; +} + +//Perform colourisation operation. For each channel C: newC = origC + intensity * (newC - origC) +static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intensity) +{ + return Color4b( ValueApplyDiff(old_color[0],new_color[0],intensity), ValueApplyDiff(old_color[1],new_color[1],intensity), ValueApplyDiff(old_color[2], new_color[2],intensity), 255); +} + +static int ValueApplyDiff(int old_value, int new_value, float intensity) +{ + return math::Clamp((int)(old_value + intensity * (new_value - old_value)), 0, 255); +} + +//An useful ENUM to hold all desaturation methods. +enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2}; + +//Desaturates the mesh according the selected method. Method belongs to DesaturationMethods's ENUM. +static int Desaturation(UpdateMeshType &m, int method, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorDesaturate((*vi).C(), method); + ++counter; + } + } + } + return counter; +} + +//Desature the color. Ausiliary functions to calculate lightness/luminosity/average. +static Color4b ColorDesaturate(Color4b c, int method) +{ + switch(method){ + case M_LIGHTNESS:{ + int val = (int)ComputeLightness(c); + return Color4b( val, val, val, 255); + } + case M_AVERAGE:{ + int val = (int)ComputeAvgLightness(c); + return Color4b( val, val, val, 255); + } + case M_LUMINOSITY:{ + int val = (int)ComputeLuminosity(c); + return Color4b( val, val, val, 255); + } + default: assert(0); + } +} + +//ausiliary function to compute average lightness. value = (R+G+B)/3 +static float ComputeAvgLightness(Color4b c) +{ + return float(c[0]+c[1]+c[2])/3.0f; +} + +//ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B +static float ComputeLuminosity(Color4b c) +{ + return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]); +} + +//Equalize the histogram of colors. It can equalize any combination of rgb channels or +//it can work on lightness. +static int Equalize(UpdateMeshType &m, unsigned int rgbMask, const bool ProcessSelected=false) +{ + //declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness + Histogramf Hl, Hr, Hg, Hb; + Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear(); + Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255); + + int counter=0; + VertexIterator vi; + + //Scan the mesh to build the histograms + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms + { + float v = ComputeLightness((*vi).C())+0.5; //compute and round lightness value + Hl.Add(v); Hr.Add((float)(*vi).C()[0]); Hg.Add((float)(*vi).C()[1]); Hb.Add((float)(*vi).C()[2]); + } + } + } + + //for each histogram, compute the cumulative distribution function, and build a lookup table + int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256]; + cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0); + for(int i=1; i<256; i++){ + cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1]; + cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1]; + cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1]; + cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1]; + } + + //this loop aaplies the transformation to colors + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask); + ++counter; + } + } + } + return counter; +} + +//Applies equalization to the components of the color according to rgbmask +static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask) +{ + unsigned char r = c[0], g = c[1], b = c[2]; + if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness + { + int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]); + return Color4b(v, v, v, 255); //return the equalized gray color + } + if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red + if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green + if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue + return Color4b(r, g, b, 255); //return the equalized color +} + +//Compute the equalized value +static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax) +{ + return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f); +} + +//applies the white balance filter. It may works with an auto regulation of white, or based on a user +//color that is supposed to be white. +static int WhiteBalance(UpdateMeshType &m, bool automatic, Color4b userColor, const bool ProcessSelected=false) +{ + Color4b unbalancedWhite; + float lightness = 0; + int counter=0; + VertexIterator vi; + + if(!automatic) unbalancedWhite = userColor; //no auto regolation required, user has provided a color. + else //else, we need to scan the mesh and pick its lighter color... + { + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected... + { + //the lighter color is selected with an incremental approach... + float v = ComputeLightness((*vi).C()); + if( v > lightness){ + lightness = v; //save lightness + unbalancedWhite = (*vi).C(); //save the color + } + } + } + } + } + + //in this loop the transformation is applied to the mesh + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite); + ++counter; + } + } + } + return counter; +} + +//Balnce the white of the color, applying a correction factor based on the unbalancedWhite color. +static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite) +{ + //sanity check to avoid division by zero... + if(unbalancedWhite[0]==0) unbalancedWhite[0]=1; + if(unbalancedWhite[1]==0) unbalancedWhite[1]=1; + if(unbalancedWhite[2]==0) unbalancedWhite[2]=1; + + return Color4b( + math::Clamp((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255), + math::Clamp((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255), + math::Clamp((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255), + 255); +} + +static void PerlinColor(MeshType& m, Box3f bbox, float freq, Point3i channelOffsets) +{ + typedef typename MeshType::ScalarType ScalarType; + + Point3 p; + for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) + { + if(!(*vi).IsD()){ + p = bbox.GlobalToLocal(m.Tr * (*vi).P()); //actual vertex position scaled to bbox + (*vi).C() = Color4b( int(255*math::Perlin::Noise(channelOffsets[0]+p[0]*freq,channelOffsets[0]+p[1]*freq,channelOffsets[0]+p[2]*freq)), + int(255*math::Perlin::Noise(channelOffsets[1]+p[0]*freq,channelOffsets[1]+p[1]*freq,channelOffsets[1]+p[2]*freq)), + int(255*math::Perlin::Noise(channelOffsets[2]+p[0]*freq,channelOffsets[2]+p[1]*freq,channelOffsets[2]+p[2]*freq)), + 255 ); + } + } +} + +static void ColorNoise(MeshType& m, int noiseBits) +{ + if(noiseBits>8) noiseBits = 8; + if(noiseBits<1) return; + + math::SubtractiveRingRNG randomGen = math::SubtractiveRingRNG(time(NULL)); + for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) + { + if(!(*vi).IsD()){ + (*vi).C()[0] = math::Clamp((*vi).C()[0] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255); + (*vi).C()[1] = math::Clamp((*vi).C()[1] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255); + (*vi).C()[2] = math::Clamp((*vi).C()[2] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255); + } + } +} + +}; + +}// end namespace +}// end namespace +#endif diff --git a/vcg/complex/algorithms/update/curvature.h b/vcg/complex/algorithms/update/curvature.h new file mode 100644 index 00000000..e2efa151 --- /dev/null +++ b/vcg/complex/algorithms/update/curvature.h @@ -0,0 +1,688 @@ +/**************************************************************************** +* 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 $ +Revision 1.8 2008/05/14 10:03:29 ganovelli +Point3f->Coordtype + +Revision 1.7 2008/04/23 16:37:15 onnis +VertexCurvature method added. + +Revision 1.6 2008/04/04 10:26:12 cignoni +Cleaned up names, now Kg() gives back Gaussian Curvature (k1*k2), while Kh() gives back Mean Curvature 1/2(k1+k2) + +Revision 1.5 2008/03/25 11:00:56 ganovelli +fixed bugs sign of principal direction and mean curvature value + +Revision 1.4 2008/03/17 11:29:59 ganovelli +taubin and desbrun estimates added (-> see vcg/simplex/vertex/component.h [component_ocf.h|component_occ.h ] + +Revision 1.3 2006/02/27 18:02:57 ponchio +Area -> doublearea/2 + +added some typename + +Revision 1.2 2005/10/25 09:17:41 spinelli +correct IsBorder + +Revision 1.1 2005/02/22 16:40:29 ganovelli +created. This version writes the gaussian curvature on the Q() member of +the vertex + +****************************************************************************/ + +#ifndef VCGLIB_UPDATE_CURVATURE_ +#define VCGLIB_UPDATE_CURVATURE_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile curvature.h vcg/complex/trimesh/update/curvature.h + +/// \brief Management, updating and computation of per-vertex and per-face normals. +/** +This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh. +*/ + +template +class UpdateCurvature +{ + +public: + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::VertContainer VertContainer; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef vcg::face::VFIterator VFIteratorType; + typedef typename MeshType::CoordType CoordType; + typedef typename CoordType::ScalarType ScalarType; + + +private: + struct AdjVertex { + VertexType * vert; + float doubleArea; + bool isBorder; + }; + + +public: + /// \brief Compute principal direction and magniuto of curvature. + +/* + Compute principal direction and magniuto of curvature as describe in the paper: + @InProceedings{bb33922, + author = "G. Taubin", + title = "Estimating the Tensor of Curvature of a Surface from a + Polyhedral Approximation", + booktitle = "International Conference on Computer Vision", + year = "1995", + pages = "902--907", + URL = "http://dx.doi.org/10.1109/ICCV.1995.466840", + bibsource = "http://www.visionbib.com/bibliography/describe440.html#TT32253", + */ + static void PrincipalDirections(MeshType &m) { + + assert(m.HasVFTopology()); + + vcg::tri::UpdateNormals::PerVertexNormalized(m); + + VertexIterator vi; + for (vi =m.vert.begin(); vi !=m.vert.end(); ++vi) { + if ( ! (*vi).IsD() && (*vi).VFp() != NULL) { + + VertexType * central_vertex = &(*vi); + + std::vector weights; + std::vector vertices; + + vcg::face::JumpingPos pos((*vi).VFp(), central_vertex); + + // firstV is the first vertex of the 1ring neighboorhood + VertexType* firstV = pos.VFlip(); + VertexType* tempV; + float totalDoubleAreaSize = 0.0f; + + // compute the area of each triangle around the central vertex as well as their total area + do + { + // this bring the pos to the next triangle counterclock-wise + pos.FlipF(); + pos.FlipE(); + + // tempV takes the next vertex in the 1ring neighborhood + tempV = pos.VFlip(); + assert(tempV!=central_vertex); + AdjVertex v; + + v.isBorder = pos.IsBorder(); + v.vert = tempV; + v.doubleArea = vcg::DoubleArea(*pos.F()); + totalDoubleAreaSize += v.doubleArea; + + vertices.push_back(v); + } + while(tempV != firstV); + + // compute the weights for the formula computing matrix M + for (size_t i = 0; i < vertices.size(); ++i) { + if (vertices[i].isBorder) { + weights.push_back(vertices[i].doubleArea / totalDoubleAreaSize); + } else { + weights.push_back(0.5f * (vertices[i].doubleArea + vertices[(i-1)%vertices.size()].doubleArea) / totalDoubleAreaSize); + } + assert(weights.back() < 1.0f); + } + + // compute I-NN^t to be used for computing the T_i's + Matrix33 Tp; + for (int i = 0; i < 3; ++i) + Tp[i][i] = 1.0f - powf(central_vertex->cN()[i],2); + Tp[0][1] = Tp[1][0] = -1.0f * (central_vertex->N()[0] * central_vertex->cN()[1]); + Tp[1][2] = Tp[2][1] = -1.0f * (central_vertex->cN()[1] * central_vertex->cN()[2]); + Tp[0][2] = Tp[2][0] = -1.0f * (central_vertex->cN()[0] * central_vertex->cN()[2]); + + // for all neighbors vi compute the directional curvatures k_i and the T_i + // compute M by summing all w_i k_i T_i T_i^t + Matrix33 tempMatrix; + Matrix33 M; + M.SetZero(); + for (size_t i = 0; i < vertices.size(); ++i) { + CoordType edge = (central_vertex->cP() - vertices[i].vert->cP()); + float curvature = (2.0f * (central_vertex->cN().dot(edge)) ) / edge.SquaredNorm(); + CoordType T = (Tp*edge).normalized(); + tempMatrix.ExternalProduct(T,T); + M += tempMatrix * weights[i] * curvature ; + } + + // compute vector W for the Householder matrix + CoordType W; + CoordType e1(1.0f,0.0f,0.0f); + if ((e1 - central_vertex->cN()).SquaredNorm() > (e1 + central_vertex->cN()).SquaredNorm()) + W = e1 - central_vertex->cN(); + else + W = e1 + central_vertex->cN(); + W.Normalize(); + + // compute the Householder matrix I - 2WW^t + Matrix33 Q; + Q.SetIdentity(); + tempMatrix.ExternalProduct(W,W); + Q -= tempMatrix * 2.0f; + + // compute matrix Q^t M Q + Matrix33 QtMQ = (Q.transpose() * M * Q); + + CoordType bl = Q.GetColumn(0); + CoordType T1 = Q.GetColumn(1); + CoordType T2 = Q.GetColumn(2); + + // find sin and cos for the Givens rotation + float s,c; + // Gabriel Taubin hint and Valentino Fiorin impementation + float alpha = QtMQ[1][1]-QtMQ[2][2]; + float beta = QtMQ[2][1]; + + float h[2]; + float delta = sqrtf(4.0f*powf(alpha, 2) +16.0f*powf(beta, 2)); + h[0] = (2.0f*alpha + delta) / (2.0f*beta); + h[1] = (2.0f*alpha - delta) / (2.0f*beta); + + float t[2]; + float best_c, best_s; + float min_error = std::numeric_limits::infinity(); + for (int i=0; i<2; i++) + { + delta = sqrtf(powf(h[i], 2) + 4.0f); + t[0] = (h[i]+delta) / 2.0f; + t[1] = (h[i]-delta) / 2.0f; + + for (int j=0; j<2; j++) + { + float squared_t = powf(t[j], 2); + float denominator = 1.0f + squared_t; + s = (2.0f*t[j]) / denominator; + c = (1-squared_t) / denominator; + + float approximation = c*s*alpha + (powf(c, 2) - powf(s, 2))*beta; + float angle_similarity = fabs(acosf(c)/asinf(s)); + float error = fabs(1.0f-angle_similarity)+fabs(approximation); + if (error MeshGridType; + typedef vcg::GridStaticPtr PointsGridType; + + static void PrincipalDirectionsPCA(MeshType &m, ScalarType r, bool pointVSfaceInt = true,vcg::CallBackPos * cb = NULL) { + std::vector closests; + std::vector distances; + std::vector points; + VertexIterator vi; + ScalarType area; + MeshType tmpM; + typename std::vector::iterator ii; + vcg::tri::TrivialSampler vs; + + MeshGridType mGrid; + PointsGridType pGrid; + + // Fill the grid used + if(pointVSfaceInt){ + area = Stat::ComputeMeshArea(m); + vcg::tri::SurfaceSampling >::Montecarlo(m,vs,1000 * area / (2*M_PI*r*r )); + vi = vcg::tri::Allocator::AddVertices(tmpM,m.vert.size()); + for(size_t y = 0; y < m.vert.size(); ++y,++vi) (*vi).P() = m.vert[y].P(); + pGrid.Set(tmpM.vert.begin(),tmpM.vert.end()); + } else{ mGrid.Set(m.face.begin(),m.face.end()); } + int jj = 0; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi){ + vcg::Matrix33 A,eigenvectors; + vcg::Point3 bp,eigenvalues; + int nrot; + + // sample the neighborhood + if(pointVSfaceInt) + { + vcg::tri::GetInSphereVertex< + MeshType, + PointsGridType,std::vector, + std::vector, + std::vector >(tmpM,pGrid, (*vi).cP(),r ,closests,distances,points); + + A.Covariance(points,bp); + A*=area*area/1000; + } + else{ + IntersectionBallMesh( m ,vcg::Sphere3((*vi).cP(),r),tmpM ); + vcg::Point3 _bary; + vcg::tri::Inertia::Covariance(tmpM,_bary,A); + } + + Jacobi(A, eigenvalues , eigenvectors, nrot); + + // get the estimate of curvatures from eigenvalues and eigenvectors + // find the 2 most tangent eigenvectors (by finding the one closest to the normal) + int best = 0; ScalarType bestv = fabs( (*vi).cN().dot(eigenvectors.GetColumn(0).normalized()) ); + for(int i = 1 ; i < 3; ++i){ + ScalarType prod = fabs((*vi).cN().dot(eigenvectors.GetColumn(i).normalized())); + if( prod > bestv){bestv = prod; best = i;} + } + + (*vi).PD1() = eigenvectors.GetColumn( (best+1)%3).normalized(); + (*vi).PD2() = eigenvectors.GetColumn( (best+2)%3).normalized(); + + // project them to the plane identified by the normal + vcg::Matrix33 rot; + ScalarType angle = acos((*vi).PD1().dot((*vi).N())); + rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD1()^(*vi).N()); + (*vi).PD1() = rot*(*vi).PD1(); + angle = acos((*vi).PD2().dot((*vi).N())); + rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD2()^(*vi).N()); + (*vi).PD2() = rot*(*vi).PD2(); + + + // copmutes the curvature values + const ScalarType r5 = r*r*r*r*r; + const ScalarType r6 = r*r5; + (*vi).K1() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+2)%3]-45.0*eigenvalues[(best+1)%3])/(M_PI*r6); + (*vi).K2() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+1)%3]-45.0*eigenvalues[(best+2)%3])/(M_PI*r6); + if((*vi).K1() < (*vi).K2()) { std::swap((*vi).K1(),(*vi).K2()); + std::swap((*vi).PD1(),(*vi).PD2()); + if (cb) + { + (*cb)(int(100.0f * (float)jj / (float)m.vn),"Vertices Analysis"); + ++jj; + } } + } + + + } + /// \brief Computes the discrete gaussian curvature. + +/** For further details, please, refer to: \n + +- Discrete Differential-Geometry Operators for Triangulated 2-Manifolds Mark Meyer, + Mathieu Desbrun, Peter Schroder, Alan H. Barr VisMath '02, Berlin +*/ + static void MeanAndGaussian(MeshType & m) + { + assert(HasFFAdjacency(m)); + float area0, area1, area2, angle0, angle1, angle2; + FaceIterator fi; + VertexIterator vi; + typename MeshType::CoordType e01v ,e12v ,e20v; + + SimpleTempData TDAreaPtr(m.vert); + SimpleTempData TDContr(m.vert); + + vcg::tri::UpdateNormals::PerVertexNormalized(m); + //Compute AreaMix in H (vale anche per K) + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD()) + { + (TDAreaPtr)[*vi].A = 0.0; + (TDContr)[*vi] =typename MeshType::CoordType(0.0,0.0,0.0); + (*vi).Kh() = 0.0; + (*vi).Kg() = (float)(2.0 * M_PI); + } + + for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD()) + { + // angles + angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) )); + angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) )); + angle2 = M_PI-(angle0+angle1); + + if((angle0 < M_PI/2) && (angle1 < M_PI/2) && (angle2 < M_PI/2)) // triangolo non ottuso + { + float e01 = SquaredDistance( (*fi).V(1)->cP() , (*fi).V(0)->cP() ); + float e12 = SquaredDistance( (*fi).V(2)->cP() , (*fi).V(1)->cP() ); + float e20 = SquaredDistance( (*fi).V(0)->cP() , (*fi).V(2)->cP() ); + + area0 = ( e20*(1.0/tan(angle1)) + e01*(1.0/tan(angle2)) ) / 8.0; + area1 = ( e01*(1.0/tan(angle2)) + e12*(1.0/tan(angle0)) ) / 8.0; + area2 = ( e12*(1.0/tan(angle0)) + e20*(1.0/tan(angle1)) ) / 8.0; + + (TDAreaPtr)[(*fi).V(0)].A += area0; + (TDAreaPtr)[(*fi).V(1)].A += area1; + (TDAreaPtr)[(*fi).V(2)].A += area2; + + } + else // obtuse + { + if(angle0 >= M_PI/2) + { + (TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea((*fi)) / 4.0; + (TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea((*fi)) / 8.0; + (TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea((*fi)) / 8.0; + } + else if(angle1 >= M_PI/2) + { + (TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea((*fi)) / 8.0; + (TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea((*fi)) / 4.0; + (TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea((*fi)) / 8.0; + } + else + { + (TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea((*fi)) / 8.0; + (TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea((*fi)) / 8.0; + (TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea((*fi)) / 4.0; + } + } + } + + + for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD() ) + { + angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) )); + angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) )); + angle2 = M_PI-(angle0+angle1); + + // Skip degenerate triangles. + if(angle0==0 || angle1==0 || angle1==0) continue; + + e01v = ( (*fi).V(1)->cP() - (*fi).V(0)->cP() ) ; + e12v = ( (*fi).V(2)->cP() - (*fi).V(1)->cP() ) ; + e20v = ( (*fi).V(0)->cP() - (*fi).V(2)->cP() ) ; + + TDContr[(*fi).V(0)] += ( e20v * (1.0/tan(angle1)) - e01v * (1.0/tan(angle2)) ) / 4.0; + TDContr[(*fi).V(1)] += ( e01v * (1.0/tan(angle2)) - e12v * (1.0/tan(angle0)) ) / 4.0; + TDContr[(*fi).V(2)] += ( e12v * (1.0/tan(angle0)) - e20v * (1.0/tan(angle1)) ) / 4.0; + + (*fi).V(0)->Kg() -= angle0; + (*fi).V(1)->Kg() -= angle1; + (*fi).V(2)->Kg() -= angle2; + + + for(int i=0;i<3;i++) + { + if(vcg::face::IsBorder((*fi), i)) + { + CoordType e1,e2; + vcg::face::Pos hp(&*fi, i, (*fi).V(i)); + vcg::face::Pos hp1=hp; + + hp1.FlipV(); + e1=hp1.v->cP() - hp.v->cP(); + hp1.FlipV(); + hp1.NextB(); + e2=hp1.v->cP() - hp.v->cP(); + (*fi).V(i)->Kg() -= math::Abs(Angle(e1,e2)); + } + } + } + + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/) + { + if((TDAreaPtr)[*vi].A<=std::numeric_limits::epsilon()) + { + (*vi).Kh() = 0; + (*vi).Kg() = 0; + } + else + { + (*vi).Kh() = (((TDContr)[*vi].dot((*vi).cN())>0)?1.0:-1.0)*((TDContr)[*vi] / (TDAreaPtr) [*vi].A).Norm(); + (*vi).Kg() /= (TDAreaPtr)[*vi].A; + } + } + } + + + /// \brief Update the mean and the gaussian curvature of a vertex. + + /** + The function uses the VF adiacency to walk around the vertex. + \return It will return the voronoi area around the vertex. If (norm == true) the mean and the gaussian curvature are normalized. + Based on the paper "Optimizing 3d triangulations using discrete curvature analysis" + */ + + static float VertexCurvature(VertexPointer v, bool norm = true) + { + // VFAdjacency required! + assert(FaceType::HasVFAdjacency()); + assert(VertexType::HasVFAdjacency()); + + VFIteratorType vfi(v); + float A = 0; + + v->Kh() = 0; + v->Kg() = 2 * M_PI; + + while (!vfi.End()) { + if (!vfi.F()->IsD()) { + FacePointer f = vfi.F(); + int i = vfi.I(); + VertexPointer v0 = f->V0(i), v1 = f->V1(i), v2 = f->V2(i); + + float ang0 = math::Abs(Angle(v1->P() - v0->P(), v2->P() - v0->P() )); + float ang1 = math::Abs(Angle(v0->P() - v1->P(), v2->P() - v1->P() )); + float ang2 = M_PI - ang0 - ang1; + + float s01 = SquaredDistance(v1->P(), v0->P()); + float s02 = SquaredDistance(v2->P(), v0->P()); + + // voronoi cell of current vertex + if (ang0 >= M_PI/2) + A += (0.5f * DoubleArea(*f) - (s01 * tan(ang1) + s02 * tan(ang2)) / 8.0 ); + else if (ang1 >= M_PI/2) + A += (s01 * tan(ang0)) / 8.0; + else if (ang2 >= M_PI/2) + A += (s02 * tan(ang0)) / 8.0; + else // non obctuse triangle + A += ((s02 / tan(ang1)) + (s01 / tan(ang2))) / 8.0; + + // gaussian curvature update + v->Kg() -= ang0; + + // mean curvature update + ang1 = math::Abs(Angle(f->N(), v1->N())); + ang2 = math::Abs(Angle(f->N(), v2->N())); + v->Kh() += ( (math::Sqrt(s01) / 2.0) * ang1 + + (math::Sqrt(s02) / 2.0) * ang2 ); + } + + ++vfi; + } + + v->Kh() /= 4.0f; + + if(norm) { + if(A <= std::numeric_limits::epsilon()) { + v->Kh() = 0; + v->Kg() = 0; + } + else { + v->Kh() /= A; + v->Kg() /= A; + } + } + + return A; + } + + static void VertexCurvature(MeshType & m){ + + for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + VertexCurvature(&*vi,false); + } + + + +/* + Compute principal curvature directions and value with normal cycle: + @inproceedings{CohMor03, + author = {Cohen-Steiner, David and Morvan, Jean-Marie }, + booktitle = {SCG '03: Proceedings of the nineteenth annual symposium on Computational geometry}, + title - {Restricted delaunay triangulations and normal cycle} + year = {2003} +} + */ + + static void PrincipalDirectionsNormalCycles(MeshType & m){ + assert(VertexType::HasVFAdjacency()); + assert(FaceType::HasFFAdjacency()); + assert(FaceType::HasFaceNormal()); + + + typename MeshType::VertexIterator vi; + + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if(!((*vi).IsD())){ + vcg::Matrix33 m33;m33.SetZero(); + face::JumpingPos p((*vi).VFp(),&(*vi)); + p.FlipE(); + typename MeshType::VertexType * firstv = p.VFlip(); + assert(p.F()->V(p.VInd())==&(*vi)); + + + do{ + if( p.F() != p.FFlip()){ + Point3 normalized_edge = p.F()->V(p.F()->Next(p.VInd()))->cP() - (*vi).P(); + ScalarType edge_length = normalized_edge.Norm(); + normalized_edge/=edge_length; + Point3 n1 = p.F()->cN();n1.Normalize(); + Point3 n2 = p.FFlip()->cN();n2.Normalize(); + ScalarType n1n2 = (n1 ^ n2).dot(normalized_edge); + n1n2 = std::max(std::min( ScalarType(1.0),n1n2),ScalarType(-1.0)); + ScalarType beta = math::Asin(n1n2); + m33[0][0] += beta*edge_length*normalized_edge[0]*normalized_edge[0]; + m33[0][1] += beta*edge_length*normalized_edge[1]*normalized_edge[0]; + m33[1][1] += beta*edge_length*normalized_edge[1]*normalized_edge[1]; + m33[0][2] += beta*edge_length*normalized_edge[2]*normalized_edge[0]; + m33[1][2] += beta*edge_length*normalized_edge[2]*normalized_edge[1]; + m33[2][2] += beta*edge_length*normalized_edge[2]*normalized_edge[2]; + } + p.NextFE(); + }while(firstv != p.VFlip()); + + if(m33.Determinant()==0.0){ // degenerate case + (*vi).K1() = (*vi).K2() = 0.0; continue;} + + m33[1][0] = m33[0][1]; + m33[2][0] = m33[0][2]; + m33[2][1] = m33[1][2]; + + Point3 lambda; + Matrix33 vect; + int n_rot; + Jacobi(m33,lambda, vect,n_rot); + + vect.transposeInPlace(); + ScalarType normal = std::numeric_limits::min(); + int normI = 0; + for(int i = 0; i < 3; ++i) + if( fabs((*vi).N().Normalize().dot(vect.GetRow(i))) > normal ) + { + normal= fabs((*vi).N().Normalize().dot(vect.GetRow(i))); + normI = i; + } + int maxI = (normI+2)%3; + int minI = (normI+1)%3; + if(fabs(lambda[maxI]) < fabs(lambda[minI])) std::swap(maxI,minI); + + (*vi).PD1() = *(Point3*)(& vect[maxI][0]); + (*vi).PD2() = *(Point3*)(& vect[minI][0]); + (*vi).K1() = lambda[maxI]; + (*vi).K2() = lambda[minI]; + } + } +}; + +} +} +#endif diff --git a/vcg/complex/algorithms/update/curvature_fitting.h b/vcg/complex/algorithms/update/curvature_fitting.h new file mode 100644 index 00000000..f8cb9e9c --- /dev/null +++ b/vcg/complex/algorithms/update/curvature_fitting.h @@ -0,0 +1,707 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +/**************************************************************************** + +****************************************************************************/ + +#ifndef VCGLIB_UPDATE_CURVATURE_FITTING +#define VCGLIB_UPDATE_CURVATURE_FITTING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// GG include +#include +#include + + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile curvature_fitting.h vcg/complex/trimesh/update/curvature_fitting.h + +/// \brief Computation of per-vertex directions and values of curvature. +/** +This class is used to compute the per-vertex directions and values of curvature using a quadric fitting method. +*/ + +template +class UpdateCurvatureFitting +{ + +public: + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::VertContainer VertContainer; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexPointer VertexTypeP; + typedef vcg::face::VFIterator VFIteratorType; + typedef typename MeshType::CoordType CoordType; + typedef typename CoordType::ScalarType ScalarType; + +class Quadric +{ + public: + + Quadric(double av, double bv, double cv, double dv, double ev) + { + a() = av; + b() = bv; + c() = cv; + d() = dv; + e() = ev; + } + + double& a() { return data[0];} + double& b() { return data[1];} + double& c() { return data[2];} + double& d() { return data[3];} + double& e() { return data[4];} + + double data[5]; + + double evaluate(double u, double v) + { + return a*u*u + b*u*v + c*v*v + d*u + e*v; + } + + double du(double u, double v) + { + return 2.0*a()*u + b()*v + d(); + } + + double dv(double u, double v) + { + return 2.0*c()*v + b()*u + e(); + } + + double duv(double u, double v) + { + return b(); + } + + double duu(double u, double v) + { + return 2.0*a(); + } + + double dvv(double u, double v) + { + return 2.0*c(); + } + + static Quadric fit(std::vector VV) + { + assert(VV.size() >= 5); + Eigen::MatrixXf A(VV.size(),5); + Eigen::MatrixXf b(VV.size(),1); + Eigen::MatrixXf sol(VV.size(),1); + + for(unsigned int c=0; c < VV.size(); ++c) + { + double u = VV[c].X(); + double v = VV[c].Y(); + double n = VV[c].Z(); + + A(c,0) = u*u; + A(c,1) = u*v; + A(c,2) = v*v; + A(c,3) = u; + A(c,4) = v; + + b[c] = n; + } + + sol = ((A.transpose()*A).inverse()*A.transpose())*b; + return Quadric(sol[0],sol[1],sol[2],sol[3],sol[4]); + } +}; + + static CoordType project(VertexType* v, VertexType* vp) + { + return vp->P() - (v->N() * ((vp->P() - v->P()) * v->N())); + } + + + static std::vector computeReferenceFrames(VertexTypeP vi) + { + vcg::face::VFIterator vfi(vi); + + int i = (vfi.I()+1)%3; + VertexTypeP vp = vfi.F()->V(i); + + CoordType x = (project(&*vi,vp) - vi->P()).Normalize(); + + assert(fabs(x * vi->N()) < 0.1); + + std::vector res(3); + + res[0] = x; + res[1] = (vi->N() ^ res[0]).Normalize(); + res[2] = (vi->N())/(vi->N()).Norm(); + + return res; + } + + static std::set getSecondRing(VertexTypeP v) + { + std::set ris; + std::set coords; + + vcg::face::VFIterator vvi(v); + for(;!vvi.End();++vvi) + { + vcg::face::VFIterator vvi2(vvi.F()->V((vvi.I()+1)%3)); + for(;!vvi2.End();++vvi2) + { + ris.insert(vvi2.F()->V((vvi2.I()+1)%3)); + } + } + typename std::set::iterator it; + for(it = ris.begin(); it != ris.end(); ++it) + coords.insert((*it)->P()); + + return coords; + } + + static Quadric fitQuadric(VertexTypeP v, std::vector& ref) + { + std::set ring = getSecondRing(v); + if (ring.size() < 5) + return Quadric(1,1,1,1,1); + std::vector points; + + typename std::set::iterator b = ring.begin(); + typename std::set::iterator e = ring.end(); + + while(b != e) + { + // vtang non e` il v tangente!!! + CoordType vTang = *b - v->P(); + + double x = vTang * ref[0]; + double y = vTang * ref[1]; + double z = vTang * ref[2]; + points.push_back(CoordType(x,y,z)); + ++b; + } + return Quadric::fit(points); + } + + + static void computeCurvature(MeshType & m) + { + + assert(tri::HasPerVertexVFAdjacency(m) && tri::HasPerFaceVFAdjacency(m) ); + + vcg::tri::UpdateTopology::VertexFace(m); + + vcg::tri::UpdateNormals::PerVertexAngleWeighted(m); + vcg::tri::UpdateNormals::NormalizeVertex(m); + + + VertexIterator vi; + for(vi = m.vert.begin(); vi!=m.vert.end(); ++vi ) + { + std::vector ref = computeReferenceFrames(&*vi); + + Quadric q = fitQuadric(&*vi,ref); + double a = q.a(); + double b = q.b(); + double c = q.c(); + double d = q.d(); + double e = q.e(); + + double E = 1.0 + d*d; + double F = d*e; + double G = 1.0 + e*e; + + CoordType n = CoordType(-d,-e,1.0).Normalize(); + + vi->N() = ref[0] * n[0] + ref[1] * n[1] + ref[2] * n[2]; + + double L = 2.0 * a * n.Z(); + double M = b * n.Z(); + double N = 2 * c * n.Z(); + + // ----------------- Eigen stuff + Eigen::Matrix2d m; + m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F; + m = m / (E*G-F*F); + Eigen::SelfAdjointEigenSolver eig(m); + + Eigen::Vector2d c_val = eig.eigenvalues(); + Eigen::Matrix2d c_vec = eig.eigenvectors(); + + c_val = -c_val; + + CoordType v1, v2; + v1[0] = c_vec[0]; + v1[1] = c_vec[1]; + v1[2] = 0; + + v2[0] = c_vec[2]; + v2[1] = c_vec[3]; + v2[2] = 0; + + v1 = v1.Normalize(); + v2 = v2.Normalize(); + + v1 = v1 * c_val[0]; + v2 = v2 * c_val[1]; + + CoordType v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2]; + CoordType v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2]; + + v1global.Normalize(); + v2global.Normalize(); + + if (c_val[0] > c_val[1]) + { + (*vi).PD1() = v1global; + (*vi).PD2() = v2global; + (*vi).K1() = c_val[0]; + (*vi).K2() = c_val[1]; + } + else + { + (*vi).PD1() = v2global; + (*vi).PD2() = v1global; + (*vi).K1() = c_val[1]; + (*vi).K2() = c_val[0]; + } + // ---- end Eigen stuff + } + } + + // GG LOCAL CURVATURE + + class QuadricLocal + { + public: + + QuadricLocal () + { + a() = b() = c() = d() = e() = 1.0; + } + + QuadricLocal (double av, double bv, double cv, double dv, double ev) + { + a() = av; + b() = bv; + c() = cv; + d() = dv; + e() = ev; + } + + double& a() { return data[0];} + double& b() { return data[1];} + double& c() { return data[2];} + double& d() { return data[3];} + double& e() { return data[4];} + + double data[5]; + + double evaluate(double u, double v) + { + return a()*u*u + b()*u*v + c()*v*v + d()*u + e()*v; + } + + double du(double u, double v) + { + return 2.0*a()*u + b()*v + d(); + } + + double dv(double u, double v) + { + return 2.0*c()*v + b()*u + e(); + } + + double duv(double u, double v) + { + return b(); + } + + double duu(double u, double v) + { + return 2.0*a(); + } + + double dvv(double u, double v) + { + return 2.0*c(); + } + + + static QuadricLocal fit(std::vector &VV, bool svdRes, bool detCheck) + { + assert(VV.size() >= 5); + Eigen::MatrixXd A(VV.size(),5); + Eigen::MatrixXd b(VV.size(),1); + Eigen::MatrixXd sol(5,1); + + for(unsigned int c=0; c < VV.size(); ++c) + { + double u = VV[c].X(); + double v = VV[c].Y(); + double n = VV[c].Z(); + + A(c,0) = u*u; + A(c,1) = u*v; + A(c,2) = v*v; + A(c,3) = u; + A(c,4) = v; + + b[c] = n; + } + + + static int count = 0, index = 0; + double min = 0.000000000001; //1.0e-12 + /* + if (!count) + printf("GNE %e\n", min); + */ + + if (detCheck && ((A.transpose()*A).determinant() < min && (A.transpose()*A).determinant() > -min)) + { + //A.svd().solve(b, &sol); A.svd().solve(b, &sol); + //cout << sol << endl; + printf("Quadric: unsolvable vertex %d %d\n", count, ++index); + //return Quadric (1, 1, 1, 1, 1); + A.svd().solve(b, &sol); + return QuadricLocal(sol[0],sol[1],sol[2],sol[3],sol[4]); + } + count++; + + //for (int i = 0; i < 100; i++) + { + if (svdRes) + A.svd().solve(b, &sol); + else + sol = ((A.transpose()*A).inverse()*A.transpose())*b; + + } + + return QuadricLocal(sol[0],sol[1],sol[2],sol[3],sol[4]); + } + }; + + static void expandMaxLocal (MeshType & mesh, VertexType *v, int max, std::vector *vv) + { + Nring rw = Nring (v, &mesh); + do rw.expand (); while (rw.allV.size() < max+1); + if (rw.allV[0] != v) + printf ("rw.allV[0] != *v\n"); + vv->reserve ((size_t)max); + for (int i = 1; i < max+1; i++) + vv->push_back(rw.allV[i]); + + rw.clear(); + } + + + static void expandSphereLocal (MeshType & mesh, VertexType *v, float r, int min, std::vector *vv) + { + Nring rw = Nring (v, &mesh); + + bool isInside = true; + while (isInside) + { + rw.expand(); + vv->reserve(rw.allV.size()); + + typename std::vector::iterator b = rw.lastV.begin(); + typename std::vector::iterator e = rw.lastV.end(); + isInside = false; + while(b != e) + { + if (((*b)->P() - v->P()).Norm() < r) + { + vv->push_back(*b);; + isInside = true; + } + ++b; + } + } + //printf ("%d\n", vv->size()); + rw.clear(); + + if (vv->size() < min) + { + vv->clear(); + expandMaxLocal (mesh, v, min, vv); + } + } + + + static void getAverageNormal (VertexType *vp, std::vector &vv, CoordType *ppn) + { + *ppn = CoordType (0,0,0); + for (typename std::vector::iterator vpi = vv.begin(); vpi != vv.end(); ++vpi) + *ppn += (*vpi)->N(); + *ppn += (*vp).N(); + *ppn /= vv.size() + 1; + ppn->Normalize(); + } + + + static void applyProjOnPlane (CoordType ppn, std::vector &vin, std::vector *vout) + { + for (typename std::vector::iterator vpi = vin.begin(); vpi != vin.end(); ++vpi) + if ((*vpi)->N() * ppn > 0.0f) + vout->push_back (*vpi); + } + + static CoordType projectLocal(VertexType* v, VertexType* vp, CoordType ppn) + { + return vp->P() - (ppn * ((vp->P() - v->P()) * ppn)); + } + + + static void computeReferenceFramesLocal (VertexType *v, CoordType ppn, std::vector *ref) + { + vcg::face::VFIterator vfi (v); + + int i = (vfi.I() + 1) % 3; + VertexTypeP vp = vfi.F()->V(i); + + CoordType x = (projectLocal (v, vp, ppn) - v->P()).Normalize(); + + assert(fabs(x * ppn) < 0.1); + + *ref = std::vector(3); + + (*ref)[0] = x; + (*ref)[1] = (ppn ^ (*ref)[0]).Normalize(); + (*ref)[2] = ppn.Normalize(); //ppn / ppn.Norm(); + } + + + static void fitQuadricLocal (VertexType *v, std::vector ref, std::vector &vv, QuadricLocal *q) + { + bool svdResolution = false; + bool zeroDeterminantCheck = false; + + std::vector points; + points.reserve (vv.size()); + + typename std::vector::iterator b = vv.begin(); + typename std::vector::iterator e = vv.end(); + + while(b != e) + { + CoordType cp = (*b)->P(); + + // vtang non e` il v tangente!!! + CoordType vTang = cp - v->P(); + + double x = vTang * ref[0]; + double y = vTang * ref[1]; + double z = vTang * ref[2]; + points.push_back(CoordType(x,y,z)); + ++b; + } + + *q = QuadricLocal::fit (points, svdResolution, zeroDeterminantCheck); + } + + + static void finalEigenStuff (VertexType *v, std::vector ref, QuadricLocal q) + { + double a = q.a(); + double b = q.b(); + double c = q.c(); + double d = q.d(); + double e = q.e(); + + double E = 1.0 + d*d; + double F = d*e; + double G = 1.0 + e*e; + + CoordType n = CoordType(-d,-e,1.0).Normalize(); + + v->N() = ref[0] * n[0] + ref[1] * n[1] + ref[2] * n[2]; + + double L = 2.0 * a * n.Z(); + double M = b * n.Z(); + double N = 2 * c * n.Z(); + + // ----------------- Eigen stuff + Eigen::Matrix2d m; + m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F; + m = m / (E*G-F*F); + Eigen::SelfAdjointEigenSolver eig(m); + + Eigen::Vector2d c_val = eig.eigenvalues(); + Eigen::Matrix2d c_vec = eig.eigenvectors(); + + c_val = -c_val; + + CoordType v1, v2; + v1[0] = c_vec[0]; + v1[1] = c_vec[1]; + v1[2] = d * v1[0] + e * v1[1]; + + v2[0] = c_vec[2]; + v2[1] = c_vec[3]; + v2[2] = d * v2[0] + e * v2[1]; + + v1 = v1.Normalize(); + v2 = v2.Normalize(); + + CoordType v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2]; + CoordType v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2]; + + v1global.Normalize(); + v2global.Normalize(); + + v1global *= c_val[0]; + v2global *= c_val[1]; + + if (c_val[0] > c_val[1]) + { + (*v).PD1() = v1global; + (*v).PD2() = v2global; + (*v).K1() = c_val[0]; + (*v).K2() = c_val[1]; + } + else + { + (*v).PD1() = v2global; + (*v).PD2() = v1global; + (*v).K1() = c_val[1]; + (*v).K2() = c_val[0]; + } + // ---- end Eigen stuff + } + + + + static void updateCurvatureLocal (MeshType & mesh, float radiusSphere) + { + bool verbose = false; + bool projectionPlaneCheck = true; + int vertexesPerFit = 0; + + int i = 0; + VertexIterator vi; + for(vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi, i++) + { + std::vector vv; + std::vector vvtmp; + + int count; + if (verbose && !((count = (vi - mesh.vert.begin())) % 1000)) + printf ("vertex %d of %d\n",count,mesh.vert.size()); + + // if (kRing != 0) + // expandRing (&*vi, kRing, 5, &vv); + // else + expandSphereLocal (mesh, &*vi, radiusSphere, 5, &vv); + + assert (vv.size() >= 5); + + CoordType ppn; + // if (averageNormalMode) + // //ppn = (*vi).N(); + getAverageNormal (&*vi, vv, &ppn); + // else + // getProjPlaneNormal (&*vi, vv, &ppn); + + if (projectionPlaneCheck) + { + vvtmp.reserve (vv.size ()); + applyProjOnPlane (ppn, vv, &vvtmp); + if (vvtmp.size() >= 5) + vv = vvtmp; + } + + vvtmp.clear(); + + // if (montecarloMaxVertexNum) + // { + // //printf ("P: %d\n", vv.size()); + // vvtmp.reserve (vv.size ()); + // //printf ("TP: %d\n", vvtmp.size()); + // applyMontecarlo (montecarloMaxVertexNum, vv, &vvtmp); + // //printf ("TD: %d\n", vvtmp.size()); + // vv = vvtmp; + // //printf ("D: %d\n", vv.size()); + // //printf ("\n"); + // } + + assert (vv.size() >= 5); + + std::vector ref; + computeReferenceFramesLocal (&*vi, ppn, &ref); + + /* + printf ("%lf %lf %lf - %lf %lf %lf - %lf %lf %lf\n", + ref[0][0], ref[0][1], ref[0][2], + ref[1][0], ref[1][1], ref[1][2], + ref[2][0], ref[2][1], ref[2][2]); + */ + + vertexesPerFit += vv.size(); + //printf ("size: %d\n", vv.size()); + + QuadricLocal q; + fitQuadricLocal (&*vi, ref, vv, &q); + + finalEigenStuff (&*vi, ref, q); + + } + + //if (verbose) + //printf ("average vertex num in each fit: %f, total %d, vn %d\n", ((float) vertexesPerFit) / mesh.vn, vertexesPerFit, mesh.vn); + if (verbose) + printf ("average vertex num in each fit: %f\n", ((float) vertexesPerFit) / mesh.vn); + } + +}; + +} +} +#endif diff --git a/vcg/complex/algorithms/update/edges.h b/vcg/complex/algorithms/update/edges.h new file mode 100644 index 00000000..5d3ede6f --- /dev/null +++ b/vcg/complex/algorithms/update/edges.h @@ -0,0 +1,109 @@ +/**************************************************************************** +* 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 $ +Revision 1.4 2006/05/16 21:36:54 cignoni +Removed unused box function and rewrote initial comment. + +Revision 1.3 2006/05/15 13:12:36 pietroni +Updating of edge values id divided into 2 functions ( the first one update only a face...) added also resetting of edges flags.. (see first line of Set function) + +Revision 1.2 2004/05/12 18:52:35 ganovelli +removed call to ComputeRT and put its body here + +Revision 1.1 2004/05/12 10:39:45 ganovelli +created + +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_EDGES +#define __VCG_TRI_UPDATE_EDGES + +#include + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + + /// \headerfile edges.h vcg/complex/trimesh/update/edges.h + + /// \brief This class is used to compute or update the precomputed data used to efficiently compute point-face distances. + template + class UpdateEdges + { + + public: + typedef ComputeMeshType MeshType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::FaceType::CoordType::ScalarType ScalarType; + + static void Set(FaceType &f) + { + f.Flags() = f.Flags() & (~(FaceType::NORMX|FaceType::NORMY|FaceType::NORMZ)); + + // Primo calcolo degli edges + f.Edge(0) = f.V(1)->P(); f.Edge(0) -= f.V(0)->P(); + f.Edge(1) = f.V(2)->P(); f.Edge(1) -= f.V(1)->P(); + f.Edge(2) = f.V(0)->P(); f.Edge(2) -= f.V(2)->P(); + // Calcolo di plane + f.Plane().SetDirection(f.Edge(0)^f.Edge(1)); + f.Plane().SetOffset(f.Plane().Direction().dot(f.V(0)->P())); + f.Plane().Normalize(); + // Calcolo migliore proiezione + ScalarType nx = math::Abs(f.Plane().Direction()[0]); + ScalarType ny = math::Abs(f.Plane().Direction()[1]); + ScalarType nz = math::Abs(f.Plane().Direction()[2]); + ScalarType d; + if(nx>ny && nx>nz) { f.Flags() |= FaceType::NORMX; d = 1/f.Plane().Direction()[0]; } + else if(ny>nz) { f.Flags() |= FaceType::NORMY; d = 1/f.Plane().Direction()[1]; } + else { f.Flags() |= FaceType::NORMZ; d = 1/f.Plane().Direction()[2]; } + + // Scalatura spigoli + f.Edge(0)*=d; + f.Edge(1)*=d; + f.Edge(2)*=d; + } + + static void Set(ComputeMeshType &m) + { + FaceIterator f; + + for(f = m.face.begin(); f!=m.face.end(); ++f) + if(!(*f).IsD()) + Set(*f); + } + + }; // end class + +} // End namespace +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/update/fitmaps.h b/vcg/complex/algorithms/update/fitmaps.h new file mode 100644 index 00000000..a9a85464 --- /dev/null +++ b/vcg/complex/algorithms/update/fitmaps.h @@ -0,0 +1,488 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef FITMAPS_H +#define FITMAPS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vcg/complex/trimesh/update/curvature_fitting.h" + +#include +#include +#include +#include + +#include + +#include + +#include + + +using namespace Eigen; + +namespace vcg { namespace tri { + +template +class Fitmaps +{ +public: + + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::CoordType CoordType; + + typedef vcg::tri::Nring RingWalker; + + + class Bicubic + { + public: + + Bicubic() {}; + + Bicubic(vector& input) + { + data = input; + if (input.size() != 16) + { + assert(0); + } + } + +// (u3 u2 u 1) (v3 v2 v 1) +// +// a u3 v3 +// b u3 v2 +// c u3 v1 +// d u3 1 +// e u2 v3 +// f u2 v2 +// g u2 v1 +// h u2 1 +// i u1 v3 +// l u1 v2 +// m u1 v1 +// n u1 1 +// o 1 v3 +// p 1 v2 +// q 1 v1 +// r 1 1 + + double& a() { return data[0];} + double& b() { return data[1];} + double& c() { return data[2];} + double& d() { return data[3];} + double& e() { return data[4];} + double& f() { return data[5];} + double& g() { return data[6];} + double& h() { return data[7];} + double& i() { return data[8];} + double& l() { return data[9];} + double& m() { return data[10];} + double& n() { return data[11];} + double& o() { return data[12];} + double& p() { return data[13];} + double& q() { return data[14];} + double& r() { return data[15];} + + vector data; + + double evaluate(double u, double v) + { + + return + a() * u*u*u * v*v*v + + b() * u*u*u * v*v + + c() * u*u*u * v + + d() * u*u*u + + e() * u*u * v*v*v + + f() * u*u * v*v + + g() * u*u * v + + h() * u*u + + i() * u * v*v*v + + l() * u * v*v + + m() * u * v + + n() * u + + o() * v*v*v + + p() * v*v + + q() * v + + r(); + } + + + double distanceRMS(std::vector& VV) + { + double error = 0; + for(typename std::vector::iterator it = VV.begin(); it != VV.end(); ++it) + { + double u = it->X(); + double v = it->Y(); + double n = it->Z(); + + double temp = evaluate(u,v); + error += (n - temp)*(n - temp); + } + error /= (double) VV.size(); + return sqrt(error); + } + + static Bicubic fit(std::vector VV) + { + assert(VV.size() >= 16); + Eigen::MatrixXf A(VV.size(),16); + Eigen::MatrixXf b(VV.size(),1); + Eigen::MatrixXf sol(16,1); + + for(unsigned int c=0; c < VV.size(); ++c) + { + double u = VV[c].X(); + double v = VV[c].Y(); + double n = VV[c].Z(); + + A(c,0) = u*u*u * v*v*v; + A(c,1) = u*u*u * v*v; + A(c,2) = u*u*u * v; + A(c,3) = u*u*u; + A(c,4) = u*u * v*v*v; + A(c,5) = u*u * v*v; + A(c,6) = u*u * v; + A(c,7) = u*u; + A(c,8) = u * v*v*v; + A(c,9) = u * v*v; + A(c,10) = u * v; + A(c,11) = u; + A(c,12) = v*v*v; + A(c,13) = v*v; + A(c,14) = v; + A(c,15) = 1; + + + b[c] = n; + } + + A.svd().solve(b, &sol); + + vector r(16); + + for (int i=0; i < 16; ++i) + r.at(i) = sol[i]; + + return Bicubic(r); + } + }; + + Fitmaps() + {} + + class radSorter + { + public: + + radSorter(VertexType* v) + { + origin = v; + } + + VertexType* origin; + + bool operator() (VertexType* v1, VertexType* v2) + { + return (v1->P() - origin->P()).SquaredNorm() < (v2->P() - origin->P()).SquaredNorm(); + } + }; + + float getMeanCurvature(VertexType* vp) + { + return (vp->K1() + vp->K2())/2.0; + } + + static bool fitBicubicPoints(VertexType* v, std::vector& ref, Bicubic& ret, std::vector& points, std::vector& ring) + { + points.clear(); + + if (ring.size() < 16) + { + return false; + } + + typename std::vector::iterator b = ring.begin(); + typename std::vector::iterator e = ring.end(); + + while(b != e) + { + CoordType vT = (*b)->P() - v->P(); + + double x = vT * ref[0]; + double y = vT * ref[1]; + double z = vT * ref[2]; + + points.push_back(CoordType(x,y,z)); + ++b; + } + ret = Bicubic::fit(points); + return true; + } + + static double AverageEdgeLenght(MeshType& m) + { + double doubleA = 0; + for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) { + doubleA+=vcg::DoubleArea(*fi); + } + int nquads = m.fn / 2; + return sqrt( doubleA/(2*nquads) ); + } + + static void computeMFitmap(MeshType& m, float perc, int ringMax = 50) + { + vcg::tri::UpdateCurvatureFitting::computeCurvature(m); + vcg::tri::UpdateNormals::PerVertexAngleWeighted(m); + + vcg::tri::UpdateTopology::FaceFace(m); + vcg::tri::UpdateTopology::VertexFace(m); + + vcg::tri::UpdateBounding::Box(m); + + int countTemp = 0; + + RingWalker::clearFlags(&m); + + for(VertexIterator it=m.vert.begin(); it!=m.vert.end();++it) + { + if ((countTemp++ % 100) == 0) + cerr << countTemp << "/" << m.vert.size() << endl; + + RingWalker rw(&*it,&m); + + CoordType nor = it->N(); + + float okfaces = 0; + float flipfaces = 0; + + int count = 0; + do + { + count++; + rw.expand(); + for(unsigned i=0; iN(); + + vet1.Normalize(); + vet2.Normalize(); + + + double scal = vet1 * vet2; + if ((scal) > 0) + okfaces += (vcg::DoubleArea(*rw.lastF[i])); + else + flipfaces += (vcg::DoubleArea(*rw.lastF[i])); + } + } while ((((flipfaces)/(okfaces + flipfaces))*100.0 < perc) && (count < ringMax)); + + std::sort(rw.lastV.begin(),rw.lastV.end(),radSorter(&*it)); + + it->Q() = ((*rw.lastV.begin())->P() - it->P()).Norm(); + rw.clear(); + + } + + vcg::tri::Smooth::VertexQualityLaplacian(m,2); + } + + static vector gatherNeighsSurface(VertexType* vt, float sigma, MeshType& m) + { + vector current; + + RingWalker rw(vt,&m); + + bool exit = false; + + do + { + rw.expand(); + + exit = true; + + for(typename vector::iterator it = rw.lastV.begin(); it != rw.lastV.end(); ++it) + { + if (((*it)->P() - vt->P()).Norm() < sigma) + { + current.push_back(*it); + exit = false; + } + } + + } while (!exit); + + rw.clear(); + return current; + } + + static void computeSFitmap(MeshType& m)//, float target = 1000) + { + + vcg::tri::UpdateCurvatureFitting::computeCurvature(m); + vcg::tri::UpdateNormals::PerVertexAngleWeighted(m); + + vcg::tri::UpdateTopology::FaceFace(m); + vcg::tri::UpdateTopology::VertexFace(m); + + // update bounding box + vcg::tri::UpdateBounding::Box(m); + + int countTemp = 0; + + double e = AverageEdgeLenght(m); + + int iteraz = 5; //2.0 * sqrt(m.vert.size()/target); + + for(VertexIterator it=m.vert.begin(); it!=m.vert.end();++it) + { + if ((countTemp++ % 100) == 0) + cerr << countTemp << "/" << m.vert.size() << endl; + + vector oneX; + + + for (int iteration = 0; iteration ref(3); + ref[0] = it->PD1(); + ref[1] = it->PD2(); + ref[2] = it->PD1() ^ it->PD2(); + + ref[0].Normalize(); + ref[1].Normalize(); + ref[2].Normalize(); + + Bicubic b; + + RingWalker::clearFlags(&m); + + std::vector pointsGlobal = gatherNeighsSurface(&*it,oneX.at(iteraz-1),m); + + vector onedimensional; + + for (int iteration = 0; iteration points; // solo quelli nel raggio + + std::vector projected; // come sopra ma in coord locali + for (typename std::vector::iterator it2 = pointsGlobal.begin(); it2 != pointsGlobal.end(); ++it2) + { + if (((*it).P() - (*it2)->P()).Norm() < oneX.at(iteration)) + points.push_back(*it2); + } + + std::vector& pointsFitting = points; + + + if (!fitBicubicPoints(&*it, ref, b, projected,pointsFitting)) + { + onedimensional.push_back(0); + } + else + { + onedimensional.push_back(b.distanceRMS(projected)); + } + + } + + + // // vecchio fit ax^4 + Eigen::MatrixXf Am(onedimensional.size(),1); + Eigen::MatrixXf bm(onedimensional.size(),1); + Eigen::MatrixXf sol(1,1); + + for(unsigned int c=0; c < onedimensional.size(); ++c) + { + double x = oneX.at(c); + + Am(c,0) = pow(x,4); + bm[c] = onedimensional[c]; + } + + Am.svd().solve(bm, &sol); + + it->Q() = pow((double)sol[0],0.25); + + // // nuovo fit ax^4 + b + // Eigen::MatrixXf Am(onedimensional.size()+1,2); + // Eigen::MatrixXf bm(onedimensional.size()+1,1); + // Eigen::MatrixXf sol(2,1); + // + // Am(0,0) = 0; + // Am(0,1) = 0; + // bm[0] = 0; + // + // for(unsigned int c=0; c < onedimensional.size(); ++c) + // { + // double x = oneX.at(c); + // + // Am(c,0) = pow(x,4); + // Am(c,1) = 1; + // bm[c] = onedimensional[c]; + // } + // + // //sol = ((Am.transpose()*Am).inverse()*Am.transpose())*bm; + // Am.svd().solve(bm, &sol); + // + // cerr << "------" << sol[0] << " " << sol[1] << endl; + // if (sol[0] > 0) + // saliency[it] = pow((double)sol[0],0.25); + // else + // saliency[it] = 0; + + + } + + vcg::tri::Smooth::VertexQualityLaplacian(m,1); + } + + + ~Fitmaps(){}; + +}; + +}} // END NAMESPACES + +#endif // FITMAPS_H diff --git a/vcg/complex/algorithms/update/flag.h b/vcg/complex/algorithms/update/flag.h new file mode 100644 index 00000000..ca6b3591 --- /dev/null +++ b/vcg/complex/algorithms/update/flag.h @@ -0,0 +1,453 @@ +/**************************************************************************** +* 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 $ +Revision 1.20 2007/05/31 15:24:50 ponchio +FIxed off-by-one error on FaceBorderFromNone. + +Revision 1.19 2007/05/22 15:19:42 cignoni +Added VertexClear + +Revision 1.18 2007/01/30 18:49:23 tarini +aggiunta la VertexBorderFromNone (flag bordo per vertici senza richiedere nulla) + +Revision 1.17 2006/08/31 13:11:12 marfr960 +corrected bounds of a vector scan + +Revision 1.16 2006/08/30 12:59:49 marfr960 +Added missing std:: to swap + +Revision 1.15 2006/08/30 06:50:07 cignoni +Reverted to version 1.13. Version 1.14 was done on outdated version. + +Revision 1.13 2006/06/18 20:49:30 cignoni +Added missing IsD tests + +Revision 1.12 2006/05/03 21:23:25 cignoni +Corrected IsDeleted -> isD + +Revision 1.11 2005/12/02 00:09:12 cignoni +Added assert(HasFlags) everywhere.. + +Revision 1.10 2005/07/06 08:16:34 ganovelli +set VertexBorderFromFace as static + +Revision 1.9 2005/06/10 15:07:23 cignoni +Completed FaceBorderFromNone (and added a missing helper class) + +Revision 1.8 2005/04/01 13:04:55 fiorin +Minor changes + +Revision 1.7 2004/09/14 19:49:43 ganovelli +first compilation version + +Revision 1.6 2004/07/15 00:13:39 cignoni +Better doxigen documentation + +Revision 1.5 2004/07/06 06:27:02 cignoni +Added FaceBorderFromVF + +Revision 1.4 2004/05/13 15:58:55 ganovelli +function Clear added + +Revision 1.3 2004/03/12 15:22:19 cignoni +Written some documentation and added to the trimes doxygen module + +Revision 1.2 2004/03/10 00:46:10 cignoni +changed to the face::IsBorder() style + +Revision 1.1 2004/03/05 10:59:24 cignoni +Changed name from plural to singular (normals->normal) + +Revision 1.1 2004/03/04 00:37:56 cignoni +First working version! + + +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_FLAGS +#define __VCG_TRI_UPDATE_FLAGS + +#include + +namespace vcg { +namespace tri { +/// \ingroup trimesh + +/// \headerfile flag.h vcg/complex/trimesh/update/flag.h + +/// \brief Management, updating and computation of per-vertex and per-face flags (like border flags). + +/** +This class is used to compute or update some of the flags that can be stored in the mesh components. For now just Border flags (e.g. the flag that tells if a given edge of a face belong to a border of the mesh or not). +*/ + +template +class UpdateFlags +{ + +public: +typedef UpdateMeshType MeshType; +typedef vcg::face::Pos PosType; +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FacePointer FacePointer; +typedef typename MeshType::FaceIterator FaceIterator; + +/// \brief Reset all the mesh flags (both vertexes and faces) setting everithing to zero (the default value for flags) + +static void Clear(MeshType &m) +{ + assert(HasPerFaceFlags(m)); + FaceIterator fi; + VertexIterator vi; + for(fi=m.face.begin(); fi!=m.face.end(); ++fi) + (*fi).Flags() = 0; + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) + (*vi).Flags() = 0; +} + +static void VertexClear(MeshType &m, unsigned int FlagMask = 0xffffffff) +{ + VertexIterator vi; + int andMask = ~FlagMask; + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) + if(!(*vi).IsD()) (*vi).Flags() &= andMask ; +} + +static void FaceClear(MeshType &m, unsigned int FlagMask = 0xffffffff) +{ + FaceIterator fi; + int andMask = ~FlagMask; + for(fi=m.face.begin(); fi!=m.face.end(); ++fi) + if(!(*fi).IsD()) (*fi).Flags() &= andMask ; +} + +static void VertexSet(MeshType &m, unsigned int FlagMask) +{ + VertexIterator vi; + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) + if(!(*vi).IsD()) (*vi).Flags() |= FlagMask ; +} + +static void FaceSet(MeshType &m, unsigned int FlagMask) +{ + FaceIterator fi; + for(fi=m.face.begin(); fi!=m.face.end(); ++fi) + if(!(*fi).IsD()) (*fi).Flags() |= FlagMask ; +} + + + +static void VertexClearV(MeshType &m) { VertexClear(m,VertexType::VISITED);} +static void VertexClearB(MeshType &m) { VertexClear(m,VertexType::BORDER);} +static void FaceClearV(MeshType &m) { FaceClear(m,FaceType::VISITED);} +static void FaceClearB(MeshType &m) { FaceClear(m,FaceType::BORDER012);} +static void FaceClearF(MeshType &m) { FaceClear(m,FaceType::FAUX012);} + +static void VertexSetV(MeshType &m) { VertexSet(m,VertexType::VISITED);} +static void VertexSetB(MeshType &m) { VertexSet(m,VertexType::BORDER);} +static void FaceSetV(MeshType &m) { FaceSet(m,FaceType::VISITED);} +static void FaceSetB(MeshType &m) { FaceSet(m,FaceType::BORDER);} +static void FaceSetF(MeshType &m) { FaceSet(m,FaceType::FAUX012);} + +/// \brief Compute the border flags for the faces using the Face-Face Topology. + +/** + \warning Obviously it assumes that the topology has been correctly computed (see: UpdateTopology::FaceFace ) +*/ +static void FaceBorderFromFF(MeshType &m) +{ + assert(HasPerFaceFlags(m)); +// const int BORDERFLAG[3]={FaceType::BORDER0,FaceType::BORDER1,FaceType::BORDER2}; + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD()) + for(int j=0;j<3;++j) + { + //if(!(*fi).IsManifold(j)) (*fi).SetCF(j); + //else + if(face::IsBorder(*fi,j)) (*fi).SetB(j); + else (*fi).ClearB(j); + } +} + + +static void FaceBorderFromVF(MeshType &m) +{ + assert(HasPerFaceFlags(m)); + VertexIterator vi; + assert(m.HasVFTopology()); + + int visitedBit=VertexType::NewBitFlag(); + + // Calcolo dei bordi + // per ogni vertice vi si cercano i vertici adiacenti che sono toccati da una faccia sola + // (o meglio da un numero dispari di facce) + + const int BORDERFLAG[3]={FaceType::BORDER0, FaceType::BORDER1, FaceType::BORDER2}; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) + { + for(face::VFIterator vfi(&*vi) ; !vfi.End(); ++vfi ) + { + vfi.f->V1(vfi.z)->ClearUserBit(visitedBit); + vfi.f->V2(vfi.z)->ClearUserBit(visitedBit); + } + for(face::VFIterator vfi(&*vi) ; !vfi.End(); ++vfi ) + { + if(vfi.f->V1(vfi.z)->IsUserBit(visitedBit)) vfi.f->V1(vfi.z)->ClearUserBit(visitedBit); + else vfi.f->V1(vfi.z)->SetUserBit(visitedBit); + if(vfi.f->V2(vfi.z)->IsUserBit(visitedBit)) vfi.f->V2(vfi.z)->ClearUserBit(visitedBit); + else vfi.f->V2(vfi.z)->SetUserBit(visitedBit); + } + for(face::VFIterator vfi(&*vi) ; !vfi.End(); ++vfi ) + { + if(vfi.f->V(vfi.z)< vfi.f->V1(vfi.z) && vfi.f->V1(vfi.z)->IsUserBit(visitedBit)) + vfi.f->Flags() |= BORDERFLAG[vfi.z]; + if(vfi.f->V(vfi.z)< vfi.f->V2(vfi.z) && vfi.f->V2(vfi.z)->IsUserBit(visitedBit)) + vfi.f->Flags() |= BORDERFLAG[(vfi.z+2)%3]; + } + } + VertexType::DeleteBitFlag(VertexType::LastBitFlag()); +} + + +class EdgeSorter +{ +public: + + VertexPointer v[2]; // Puntatore ai due vertici (Ordinati) + FacePointer f; // Puntatore alla faccia generatrice + int z; // Indice dell'edge nella faccia + + EdgeSorter() {} // Nothing to do + + +void Set( const FacePointer pf, const int nz ) +{ + assert(pf!=0); + assert(nz>=0); + assert(nz<3); + + v[0] = pf->V(nz); + v[1] = pf->V((nz+1)%3); + assert(v[0] != v[1]); + + if( v[0] > v[1] ) std::swap(v[0],v[1]); + f = pf; + z = nz; +} + +inline bool operator < ( const EdgeSorter & pe ) const { + if( v[0]pe.v[0] ) return false; + else return v[1] < pe.v[1]; +} + +inline bool operator == ( const EdgeSorter & pe ) const +{ + return v[0]==pe.v[0] && v[1]==pe.v[1]; +} +inline bool operator != ( const EdgeSorter & pe ) const +{ + return v[0]!=pe.v[0] || v[1]!=pe.v[1]; +} + +}; + + +// versione minimale che non calcola i complex flag. +static void VertexBorderFromNone(MeshType &m) +{ + assert(HasPerVertexFlags(m)); + std::vector e; + typename UpdateMeshType::FaceIterator pf; + typename std::vector::iterator p; + + if( m.fn == 0 ) + return; + + e.resize(m.fn*3); // Alloco il vettore ausiliario + p = e.begin(); + for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce + if( ! (*pf).IsD() ) + for(int j=0;j<3;++j) + { + (*p).Set(&(*pf),j); + (*pf).ClearB(j); + ++p; + } + assert(p==e.end()); + sort(e.begin(), e.end()); // Lo ordino per vertici + + typename std::vector::iterator pe,ps; + for(ps = e.begin(), pe = e.begin(); pe < e.end(); ++pe) // Scansione vettore ausiliario + { + if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali + { + if(pe-ps==1) { + ps->v[0]->SetB(); + ps->v[1]->SetB(); + } else + if(pe-ps!=2) { // not twomanyfold! + for(;ps!=pe;++ps) { + ps->v[0]->SetB(); // Si settano border anche i complex. + ps->v[1]->SetB(); + } + } + ps = pe; + } + } +} + +/// This function fill the flags with the info on what is the best projection direction +/// for a given face. Used by the point-face distance function when do not exploiting pre-computed +/// per-face data (the so called edge component) +static void FaceProjection(MeshType &m) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) // Lo riempio con i dati delle facce + if( ! (*fi).IsD() ) + { + ScalarType nx = math::Abs((*fi).cN()[0]); + ScalarType ny = math::Abs((*fi).cN()[1]); + ScalarType nz = math::Abs((*fi).cN()[2]); + if(nx>ny && nx>nz) { (*fi).Flags() |= FaceType::NORMX; } + else if(ny>nz) { (*fi).Flags() |= FaceType::NORMY; } + else { (*fi).Flags() |= FaceType::NORMZ; } + } +} + +/// Computes per-face border flags without requiring any kind of topology +/// It has a O(fn log fn) complexity. +static void FaceBorderFromNone(MeshType &m) +{ + assert(HasPerFaceFlags(m)); + std::vector e; + typename UpdateMeshType::FaceIterator pf; + typename std::vector::iterator p; + + for(VertexIterator v=m.vert.begin();v!=m.vert.end();++v) + (*v).ClearB(); + + if( m.fn == 0 ) + return; + + FaceIterator fi; + int n_edges = 0; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN(); + e.resize(n_edges); + + p = e.begin(); + for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce + if( ! (*pf).IsD() ) + for(int j=0;j<(*pf).VN();++j) + { + (*p).Set(&(*pf),j); + (*pf).ClearB(j); + ++p; + } + assert(p==e.end()); + sort(e.begin(), e.end()); // Lo ordino per vertici + + typename std::vector::iterator pe,ps; + ps = e.begin();pe=e.begin(); + do + { + if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali + { + if(pe-ps==1) { + ps->f->SetB(ps->z); + } else + if(pe-ps!=2) { // Caso complex!! + for(;ps!=pe;++ps) + ps->f->SetB(ps->z); // Si settano border anche i complex. + } + ps = pe; + } + if(pe==e.end()) break; + ++pe; + } while(true); +// TRACE("found %i border (%i complex) on %i edges\n",nborder,ncomplex,ne); +} + +/// Compute the PerVertex Border flag deriving it from the faces +static void VertexBorderFromFace(MeshType &m) +{ + assert(HasPerFaceFlags(m)); + typename MeshType::VertexIterator v; + typename MeshType::FaceIterator f; + + for(v=m.vert.begin();v!=m.vert.end();++v) + (*v).ClearB(); + + for(f=m.face.begin();f!=m.face.end();++f) + if(!(*f).IsD()) + { + for(int z=0;z<(*f).VN();++z) + if( (*f).IsB(z) ) + { + (*f).V(z)->SetB(); + (*f).V((*f).Next(z))->SetB(); + } + } +} +// +static void FaceFauxCrease(MeshType &m,float AngleRad) +{ + assert(HasPerFaceFlags(m)); + assert(HasFFAdjacency(m)); + + typename MeshType::FaceIterator f; + + //initially everything is faux (e.g all internal) + FaceSetF(m); + for(f=m.face.begin();f!=m.face.end();++f) + { + if(!(*f).IsD()) + { + for(int z=0;z<(*f).VN();++z) + { + if( face::IsBorder(*f,z) ) (*f).ClearF(z); + else + { + if(Angle((*f).N(), (*f).FFp(z)->N()) > AngleRad) + (*f).ClearF(z); + } + } + } + } +} + +}; // end class + +} // End namespace tri +} // End namespace vcg + + +#endif diff --git a/vcg/complex/algorithms/update/halfedge_indexed.h b/vcg/complex/algorithms/update/halfedge_indexed.h new file mode 100644 index 00000000..e7ac1607 --- /dev/null +++ b/vcg/complex/algorithms/update/halfedge_indexed.h @@ -0,0 +1,666 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCGLIB_HALFEDGE_ +#define __VCGLIB_HALFEDGE_ + +#include +#include +#include +#include +#include +#include + +namespace vcg +{ + namespace tri{ + /// \ingroup trimesh + + /// \headerfile edge_support.h vcg/complex/trimesh/edge_support.h + + /// \brief This class is used to build edge based data structure from indexed data structure and viceversa + + /** + */ + + template + class UpdateHalfEdges{ + public: + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename MeshType::HEdgeType HEdgeType; + typedef typename MeshType::EdgePointer EdgePointer; + typedef typename MeshType::EdgeType EdgeType; + typedef typename MeshType::EdgeIterator EdgeIterator; + typedef typename MeshType::HEdgeIterator HEdgeIterator; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::FaceType FaceType; + + struct VertexPairEdgePtr{ + VertexPairEdgePtr(VertexPointer _v0,VertexPointer _v1,HEdgePointer _ep):v0(_v0),v1(_v1),ep(_ep){if(v0>v1) std::swap(v0,v1);} + const bool operator <(const VertexPairEdgePtr &o) const {return (v0 == o.v0)? (v1 BitVector; + + /** + build a half-edge data structure from an indexed data structure. Note that the half-edges are allocated here for the first time. + If you have a mesh where there are already edges, they will be removed and the data lost, so do not use this function + to just "update" the topology of half edges. + **/ + static void FromIndexed(MeshType & m){ + assert(HasFVAdjacency(m)); + assert(HasHOppAdjacency(m)); + assert(HasHNextAdjacency(m)); + + typename MeshType::template PerFaceAttributeHandle flagVisited = + vcg::tri::Allocator::template AddPerFaceAttribute(m,""); + std::vector borderEdges; + + // allocate all new half edges + FaceIterator fi; + unsigned int n_edges = 0; + + // count how many half edge to allocate + for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) + {n_edges+=(*fi).VN(); + for(int i = 0; i < (*fi).VN(); ++i) + if(vcg::face::IsBorder((*fi),(i))) + ++n_edges; + } + + m.hedge.clear(); + m.hn = 0; + + // allocate the half edges + typename MeshType::HEdgeIterator ei = vcg::tri::Allocator::AddHEdges(m,n_edges); + + + std::vector all; + int firstEdge = 0; + for(fi = m.face.begin(); fi != m.face.end(); ++fi)if(!(*fi).IsD()){ + assert((*fi).VN()>2); + if(flagVisited[*fi].empty()) {flagVisited[*fi].resize((*fi).VN());} + + for(int i = 0; i < (*fi).VN(); ++i,++ei) + { + (*ei).HVp() = (*fi).V(i); + (*ei).HNp() = &m.hedge[firstEdge + (i +1) % (*fi).VN()]; + if(MeshType::HEdgeType::HasHFAdjacency()) + (*ei).HFp() = &(*fi); + if( MeshType::FaceType::HasFHAdjacency()) + (*fi).FHp() = &(*ei); + if(MeshType::HEdgeType::HasHPrevAdjacency()) + (*ei).HPp() = &m.hedge[firstEdge + (i +(*fi).VN()-1) % (*fi).VN()]; + if(HasVHAdjacency(m)) + (*ei).HVp()->VHp() = &(*ei); + all.push_back(VertexPairEdgePtr((*fi).V(i), (*fi).V((*fi).Next(i)),&(*ei)));// it will be used to link the hedges + + if( vcg::face::IsBorder((*fi),(i))) + borderEdges.push_back(FacePtrInt(&(*fi),i)); + } + firstEdge += (*fi).VN(); + } + + // add all the border hedges + int borderLength; + typename std::vector::iterator ebi; + for( ebi = borderEdges.begin(); ebi != borderEdges.end(); ++ebi) + if( !flagVisited[(*ebi).f][(*ebi).i])// not already inserted + { + + borderLength = 0; + vcg::face::Pos bp((*ebi).f,(*ebi).i); + + //FaceType * start = (*ebi).f; + VertexType * start = ((*ebi).f)->V((*ebi).i); + do{ + all.push_back( VertexPairEdgePtr ( bp.f->V( bp.f->Next(bp.z) ),bp.f->V( bp.z ),&(*ei))); + (*ei).HVp() = bp.f->V(bp.f->Next(bp.z)) ; + flagVisited[bp.f][bp.z] = true; + ++ei; + bp.NextB(); + ++borderLength; + }while (bp.v != start); + //}while (bp.f != start); + + + // run over the border edges to link the adjacencies + for(int be = 0; be < borderLength; ++be) + { + if(MeshType::HEdgeType::HasHFAdjacency()) + m.hedge[firstEdge + be].HFp() = NULL; + + if(MeshType::HEdgeType::HasHPrevAdjacency()) + m.hedge[firstEdge + be].HPp() = &m.hedge[firstEdge + (be +borderLength-1) % borderLength]; + + m.hedge[firstEdge + be].HNp() = &m.hedge[firstEdge + (be +1) % borderLength]; + } + firstEdge+=borderLength; + } + + vcg::tri::Allocator:: template DeletePerFaceAttribute(m,flagVisited ); + + std::sort(all.begin(),all.end()); + assert(all.size() == n_edges); + + for(unsigned int i = 0 ; i < all.size(); ) + if(all[i] == all[i+1]) + { + all[i].ep->HOp() = all[i+1].ep; + all[i+1].ep->HOp() = all[i].ep; + i+=2; + } + else + { + all[i].ep->HOp() = all[i].ep; + i+=1; + } + + if(HasEHAdjacency(m) && HasHEAdjacency(m)) + { + assert(m.edge.size() == 0 || m.edge.size() == n_edges/2); + + if ( m.edge.size() == 0 ) + { + m.en = 0; + // allocate the edges + typename MeshType::EdgeIterator edge_i = vcg::tri::Allocator::AddEdges(m,n_edges/2); + + for(ei = m.hedge.begin(); ei != m.hedge.end(); ++ei) + { + if((*ei).HEp() == NULL) + { + (*ei).HEp() = &(*edge_i); + (*ei).HOp()->HEp() = &(*edge_i); + + (*edge_i).EHp() = &(*ei); + + ++edge_i; + } + } + + } + else + { + + if(HasEVAdjacency(m) && HasHEAdjacency(m) && HasEHAdjacency(m)) + { + //update edge relations + + for(typename MeshType::EdgeIterator ei1 = m.edge.begin(); ei1 != m.edge.end(); ++ei1 ) + { + vector hedges = HalfEdgeTopology::get_incident_hedges((*ei1).V(0)); + + for(typename vector::iterator hi = hedges.begin(); hi != hedges.end(); ++hi) + { + if((*hi)->HOp()->HVp() == (*ei1).V(1)) + { + + assert((*hi)->HEp() == NULL); + assert((*hi)->HOp()->HEp() == NULL); + + // EH + (*ei1).EHp() = *hi; + + // HE + (*hi)->HEp() = &(*ei1); + (*hi)->HOp()->HEp() = &(*ei1); + + break; + } + } + } + } + } + + } + + } + + /** + Checks pointers FHEp() are valid + **/ + static bool CheckConsistency_FHp(MeshType & m){ + assert(MeshType::FaceType::HasFHAdjacency()); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()){ + if((*fi).FHp() < &(*m.hedge.begin())) return false; + if((*fi).FHp() > &(m.hedge.back())) return false; + } + return true; + } + + /** + Checks that half edges and face relation are consistent + **/ + static bool CheckConsistency(MeshType & m){ + assert(MeshType::HEdgeType::HasHNextAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(MeshType::FaceType::HasFHAdjacency()); + + //bool hasHEF = ( MeshType::HEdgeType::HasHFAdjacency()); + bool hasHP = ( MeshType::HEdgeType::HasHPrevAdjacency()); + + FaceIterator fi; + HEdgePointer ep,ep1; + int cnt = 0; + + if( MeshType::HEdgeType::HasHFAdjacency() ) + { + int iDb = 0; + for(fi = m.face.begin(); fi != m.face.end(); ++fi,++iDb) + if(!(*fi).IsD()) + { + ep = ep1 = (*fi).FHp(); + + do{ + if(ep->IsD()) + return false; // the hedge should not be connected, it has been deleted + if( ! ep->HFp()) + return false; + if(ep->HFp() != &(*fi)) + return false;// hedge is not pointing to the rigth face + ep = ep->HNp(); + if(cnt++ > m.hn) + return false; // hedges are ill connected (HENp()) + + }while(ep!=ep1); + + } + } + + HEdgePointer epPrev; + HEdgeIterator hi; + //bool extEdge ; + for( hi = m.hedge.begin(); hi != m.hedge.end(); ++hi) + if(!(*hi).IsD()) + { + //cnt = 0; + epPrev = ep = ep1 = &(*hi); + //do{ + //extEdge = (ep->HFp()==NULL); + if(hasHP) + { + if( !ep->HPp()) + return false; + if( ep->HPp() == ep) + return false; // the previous of an edge cannot be the edge itself + if( ep->HNp()->HPp() != ep) + return false; // next and prev relation are not mutual + if( ep->HPp()->IsD()) + return false; // + } + + if( ! ep->HOp() ) + return false; + + if( ep->HOp() == ep) + return false; // opposite relation is not mutual + + if( ep->HOp()->IsD()) + return false; + + if( ep->HOp()->HOp() != ep) + return false; // opposite relation is not mutual + + if( HasHFAdjacency(m) ) + { + if(ep->HFp()) + { + if( ep->HFp()->IsD()) + return false; // pointed face must not be deleted + } + } + + if( HasHEAdjacency(m) && (m.en!=0)) + { + if( ! ep->HEp()) + return false; //halfedge must point to an edge + + if( ep->HEp()->IsD()) + return false; // pointed edge must not be deleted + + if(ep->HEp() != ep->HOp()->HEp()) + return false; // he and opposite he must point to the same edge + + if(ep->HEp()->EHp() != ep && ep->HEp()->EHp() != ep->HOp() ) + return false; // halfedge points to an edge not pointing it or its opposite + + } + + + if( !ep->HNp() ) + return false; + + if( ep->HNp() == ep ) + return false; // the next of an hedge cannot be the hedge itself + + if( ep->HNp()->IsD()) + return false; // + + if(hasHP) + if( ep->HNp()->HPp() != ep) + return false; // + + if( HasHVAdjacency(m) ) + { + if( ! ep->HVp() ) + return false; // halfedge must point to a vertex + + if( ep->HVp()->IsD() ) + return false; // pointed vertex must not be deleted + + if( HasVHAdjacency(m) ) + if( ! (ep->HVp()->VHp()) ) + return false; // halfedge points to a vertex pointing NULL + + } + + + ep = ep->HNp(); + if( ep->HVp() != epPrev->HOp()->HVp()) + return false; + + epPrev = ep; + + // if(cnt++ > m.hn) + // return false; // edges are ill connected (HENp()) + + //}while(ep!=ep1); + } + + if(HasEHAdjacency(m) && HasHEAdjacency(m)) + for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei) + { + if(!(*ei).IsD()) + { + if( !(*ei).EHp()) + return false; //edge must have a valid pointer to his halfedge + + if( (*ei).EHp()->HEp() != &(*ei) ) + return false; // edge's halfedge must point to the edge itself + + if( (*ei).EHp()->IsD()) + return false; + } + } + + if(HasVHAdjacency(m)) + for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + { + if( !(*vi).IsD() ) + if( (*vi).VHp() ) + { + if( (*vi).VHp()->HVp() != &(*vi) ) + return false; + if( (*vi).VHp()->IsD()) + return false; + } + } + + + return true; + } + + /** Set the relations HFp(), FHp() from a loop of edges to a face + */ + private: + static void SetRelationsLoopFace(HEdgeType * e0, FaceType * f){ + assert(HEdgeType::HasHNextAdjacency()); + assert(FaceType::HasFHAdjacency()); + + HEdgeType *e = e0; + assert(e!=NULL); + do{ e->HFp() = f; e = e->HNp(); } while(e != e0); + f->FHp() = e0; + } + + /** + Merge the two faces. This will probably become a class template or a functor + */ + static void MergeFaces(FaceType *, FaceType *){} + + /** + Find previous hedge in the loop + */ + static HEdgeType * PreviousEdge(HEdgeType * e0){ + HEdgeType * ep = e0; + do{ + if(ep->HNp() == e0) return ep; + ep = ep->HNp(); + }while(ep!=e0); + assert(0); // degenerate loop + return 0; + } + + public: + /** Adds an edge between the sources of e0 and e1 and set all the topology relations. + If the edges store the pointers to the faces then a new face is created. + <--- e1 ---- X <------e1_HEPp--- + ^ + || + ei0 || ei1 + || + v + ----e0_HEPp-> X ----- e0 ------> + */ + static void AddHEdge(MeshType &m, HEdgeType * e0, HEdgeType * e1){ + HEdgeType *iii =e0->HNp(); + assert(e1!=e0->HNp()); + assert(e0!=e1->HNp()); + HEdgePointer tmp; + bool hasP = MeshType::HEdgeType::HasHPrevAdjacency(); + assert(e0->HOp() != e1); // the hedge already exists + assert(e0!=e1->HNp()); + + std::vector toUpdate; + toUpdate.push_back(&e0); + toUpdate.push_back(&e1); + HEdgeIterator ei0 = vcg::tri::Allocator::AddHEdges(m,2,toUpdate); + + HEdgeIterator ei1 = ei0; ++ei1; + (*ei0).HNp() = e1;(*ei0).HVp() = e0->HVp(); + (*ei1).HNp() = e0;(*ei1).HVp() = e1->HVp(); + + HEdgePointer e0_HEPp = 0,e1_HEPp = 0,ep =0; + if(hasP){ + e0_HEPp = e0->HPp(); + e1_HEPp = e1->HPp(); + }else{// does not have pointer to previous, it must be computed + ep = e0; + do{ + if(ep->HNp() == e0) e0_HEPp = ep; + if(ep->HNp() == e1) e1_HEPp = ep; + ep = ep->HNp(); + }while(ep!=e0); + } + if(hasP){ + (*ei0).HPp() = e0->HPp(); + (*ei1).HPp() = e1->HPp(); + e0->HPp() = &(*ei1); + e1->HPp() = &(*ei0); + } + e0_HEPp -> HNp() = &(*ei0); + e1_HEPp -> HNp() = &(*ei1); + + (*ei0).HOp() = &(*ei1); + (*ei1).HOp() = &(*ei0); + + + if( HEdgeType::HasHFAdjacency() && FaceType::HasFHAdjacency()){ + FaceIterator fi0 = vcg::tri::Allocator::AddFaces(m,1); + m.face.back().ImportData(*e0->HFp()); + + SetRelationsLoopFace(&(*ei0),e1->HFp()); // one loop to the old face + SetRelationsLoopFace(&(*ei1),&m.face.back()); // the other to the new face + } + } + + /** Detach the topology relations of a given edge + <--- e->HENPp -X --- <---------eO_HEPp--- + ^ + || + e || e->HEOp() + || + v + ----e_HEPp--> X ----- e->HEOp->HENPp() ------> + + */ + static void RemoveHEdge(MeshType &m, HEdgeType * e){ + assert(MeshType::HEdgeType::HasHNextAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::FaceType::HasFHAdjacency()); + + bool hasP = MeshType::HEdgeType::HasHPrevAdjacency(); + HEdgePointer e_HEPp,eO_HEPp; + + if(hasP){ + e_HEPp = e->HPp(); + eO_HEPp = e->HOp()->HPp(); + }else{ + e_HEPp = PreviousEdge(e); + eO_HEPp = PreviousEdge(e->HOp()); + } + + assert(e_HEPp->HNp() == e); + assert(eO_HEPp->HNp() == e->HOp()); + e_HEPp->HNp() = e->HOp()->HNp(); + eO_HEPp->HNp() = e-> HNp(); + + if(hasP) { + e->HOp()->HNp()->HPp() = e_HEPp; + e->HNp()->HPp() = eO_HEPp; + + e->HPp() = NULL; + e-> HOp()->HPp() = NULL; + } + + + // take care of the faces + if(MeshType::HEdgeType::HasHFAdjacency()){ + MergeFaces(e_HEPp->HFp(),eO_HEPp->HFp()); + vcg::tri::Allocator::DeleteFace(m,*eO_HEPp->HFp()); + SetRelationsLoopFace(e_HEPp,e_HEPp->HFp()); + + } + vcg::tri::Allocator::DeleteHEdge(m,*e->HOp()); + vcg::tri::Allocator::DeleteHEdge(m,*e); + + } + + };// end class + template + struct UpdateIndexed{ + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename MeshType::HEdgeType HEdgeType; + typedef typename MeshType::HEdgeIterator HEdgeIterator; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::FaceType FaceType; + + struct VertexPairEdgePtr{ + VertexPairEdgePtr(VertexPointer _v0,VertexPointer _v1,HEdgePointer _ep):v0(_v0),v1(_v1),ep(_ep){if(v0>v1) std::swap(v0,v1);} + const bool operator <(const VertexPairEdgePtr &o) const {return (v0 == o.v0)? (v1 hV = Allocator::template AddPerHEdgeAttribute(m,""); + + + typename MeshType::HEdgeIterator ei; + typename MeshType::FacePointer fp; + typename MeshType::FaceIterator fi; + typename MeshType::HEdgePointer ep,epF; + //int vi = 0; + vcg::SimpleTempData hV(m.hedge); + + hasHEF = (MeshType::HEdgeType::HasHFAdjacency()); + assert( !hasHEF || (hasHEF && m.fn>0)); + + // if the edgetype has the pointer to face + // it is assumed the the edget2face pointer (HEFp) are correct + // and the faces are allocated + for ( ei = m.hedge.begin(); ei != m.hedge.end(); ++ei) + if(!(*ei).IsD()) // it has not been deleted + if(!hasHEF || ( hasHEF && (*ei).HFp()!=NULL)) // if it has a pointer to the face it is + // not null (i.e. it is not a border edge) + if(!hV[(*ei)] ) // it has not be visited yet + { + if(!hasHEF)// if it has + fp = &(* Allocator::AddFaces(m,1)); + else + fp = (*ei).HFp(); + + ep = epF = &(*ei); + std::vector vpts; + do{vpts.push_back((*ep).HVp()); ep=ep->HNp();}while(ep!=epF); + //int idbg =fp->VN(); + if(fp->VN() != vpts.size()){ + fp->Dealloc(); + fp ->Alloc(vpts.size()); + } + //int idbg1 =fp->VN(); + for(unsigned int i = 0; i < vpts.size();++i) fp ->V(i) = vpts[i];// set the pointer from face to vertex + + hV[(*ei)] = true; + } + //Allocator::DeletePerHEdgeAttribute(m,hV); + } + + }; + } // end namespace vcg +} +#endif // __VCGLIB_EDGE_SUPPORT diff --git a/vcg/complex/algorithms/update/halfedge_topology.h b/vcg/complex/algorithms/update/halfedge_topology.h new file mode 100644 index 00000000..11a7a8e3 --- /dev/null +++ b/vcg/complex/algorithms/update/halfedge_topology.h @@ -0,0 +1,1765 @@ +#ifndef VCG_HEDGE_TOPOLOGY +#define VCG_HEDGE_TOPOLOGY + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace vcg::hedge; +using namespace vcg::tri; + +namespace vcg +{ + namespace tri + { + + /*! + * \brief Class containing functions to modify the topology of a halfedge based mesh + * + */ + template class HalfEdgeTopology + { + public: + + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::EdgePointer EdgePointer; + typedef typename MeshType::HEdgePointer HEdgePointer; + typedef typename MeshType::FacePointer FacePointer; + + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::EdgeIterator EdgeIterator; + typedef typename MeshType::HEdgeIterator HEdgeIterator; + typedef typename MeshType::FaceIterator FaceIterator; + + + /*! + * Collpases an edge shared by two quads, generating only quads. + * Made by a series of a vertex rotation and a diagonal collapse. + * + * \param m Mesh + * \param ep Edge to be collapsed + * \param vp Vertex that will be rotated + * + * \return Pointer to the new vertex + */ + static VertexPointer edge_collapse_quad(MeshType &m, HEdgePointer hp, VertexPointer vp) + { + assert(vp); + assert(hp); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(hp->HVp() == vp || hp->HOp()->HVp() == vp); + assert(hp->HFp()->VN() == 4); + assert(hp->HOp()->HFp()->VN() == 4); + + + VertexPointer vp_opp; + FacePointer fp; + + if( hp->HVp() == vp ) + hp = hp->HOp(); + + //retrieve opposite vertex and right face for diagonal collapse + vp_opp = hp->HVp(); + fp = hp->HFp(); + + VertexPointer vp_rot = vertex_rotate_quad( vp ); + + assert(vp_rot == vp); + + return diagonal_collapse_quad( m, fp, vp ); + } + + /*! + * Collpases a diagonal in a quad. + * + * + * \param m Mesh + * \param fp Face where diagonal resides + * \param vp One of the two vertices of the diagonal + * + * \return Pointer to the new vertex + */ + static VertexPointer diagonal_collapse_quad(MeshType &m, FacePointer fp, VertexPointer vp) + { + + assert(MeshType::VertexType::HasVHAdjacency()); + assert(MeshType::FaceType::HasFHAdjacency()); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(MeshType::HEdgeType::HasHFAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::HEdgeType::HasHPrevAdjacency()); + + assert(fp); + assert(fp->FHp()); + assert(fp->VN() == 4); + assert(!fp->IsD()); + + assert( !has_doublet_quad(fp) ); + assert(!is_singlet_quad(fp)); + + bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); + bool HasEH = MeshType::EdgeType::HasEHAdjacency(); + + HEdgePointer hp; + + vector vps = getVertices(fp); + + assert(vps.size()==4); + + for(unsigned int i = 0; i< vps.size(); i++) + { + // all vertices must be different + assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); + } + + hp = fp->FHp(); + + while(hp->HVp() != vp) + hp= hp->HNp(); + + vector hps = getHEdges(fp,hp); + + assert(vp == hps[0]->HVp()); + + VertexPointer opposite_vertex = hps[2]->HVp(); + + vp->P() = (vp->P() + opposite_vertex->P() )/2; + change_vertex(opposite_vertex, vp); + + hps[0]->HOp()->HOp()=hps[1]->HOp(); + hps[1]->HOp()->HOp()=hps[0]->HOp(); + + hps[2]->HOp()->HOp()=hps[3]->HOp(); + hps[3]->HOp()->HOp()=hps[2]->HOp(); + + for(int i=0; i<4; i++) + { + if(hps[i]->HVp()->VHp() == hps[i]) + hps[i]->HVp()->VHp() = hps[(i+4-1)%4]->HOp(); + } + + if(HasHE) + { + hps[1]->HOp()->HEp()=hps[0]->HEp(); + hps[3]->HOp()->HEp()=hps[2]->HEp(); + } + + if(HasEH && HasHE) + { + for(int i=0; i<3; i+=2) + { + if(hps[i]->HEp()->EHp() == hps[i]) + hps[i]->HEp()->EHp() = hps[i]->HOp(); + } + } + + // there are no faces, remove hedges and edge + /* + /\ + hps[1]->HOp() / \ hps[0]->HOp() + / \ + ------ ------ + | \ / | + | \ / | + |_______\/_______| + */ + + bool b[2]; + + b[0] = (hps[0]->HOp()->HFp() == NULL && hps[1]->HOp()->HFp() == NULL); + b[1] = (hps[2]->HOp()->HFp() == NULL && hps[3]->HOp()->HFp() == NULL); + + for( int i=0, j=0; i < 4; i+=2, j++ ) + { + if(b[j]) + { + if(HasHE) + Allocator::DeleteEdge(m, *(hps[i]->HEp()) ); + + Allocator::DeleteHEdge(m, *(hps[i]->HOp()) ); + Allocator::DeleteHEdge(m, *(hps[i+1]->HOp()) ); + + hps[i+1]->HVp()->VHp() = NULL; + + if(!b[(j+1)%2]) + { + hps[i]->HOp()->HNp()->HPp() = hps[i+1]->HOp()->HPp(); + hps[i+1]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp(); + + if(vp->VHp() == hps[i+1]->HOp()) + vp->VHp() = hps[(i+3)%4]->HOp(); + } + + else + vp->VHp() = NULL; + + } + } + + + Allocator::DeleteFace(m, *(fp) ); + Allocator::DeleteVertex(m, *(opposite_vertex) ); + if(HasHE) + { + Allocator::DeleteEdge(m, *(hps[1]->HEp()) ); + Allocator::DeleteEdge(m, *(hps[3]->HEp()) ); + } + Allocator::DeleteHEdge(m, *(hps[0]) ); + Allocator::DeleteHEdge(m, *(hps[1]) ); + Allocator::DeleteHEdge(m, *(hps[2]) ); + Allocator::DeleteHEdge(m, *(hps[3]) ); + + return vp; + + } + + /*! + * Removes a doublet merging the two quads in one + * + * \param m Mesh + * \param vp Vertex shared by the two consecutive edges of the doublet + * + * \return Pointer to the new face + */ + static FacePointer doublet_remove_quad(MeshType &m, VertexPointer vp) + { + assert(vp); + + HEdgePointer hp = vp->VHp(); + + assert(hp); + + FacePointer fp1 = hp->HFp(); + FacePointer fp2 = hp->HOp()->HFp(); + + assert(!is_singlet_quad(fp1)); + assert(!is_singlet_quad(fp2)); + + + assert( fp1 ); + assert( fp2 ); + + assert( hp->HOp()->HNp()->HOp() == hp->HPp() ); + + assert( fp1->VN() == 4); + assert( fp2->VN() == 4); + + // end of check + + hp->HNp()->HPp() = hp->HOp()->HPp(); + hp->HOp()->HPp()->HNp() = hp->HNp(); + + hp->HPp()->HPp()->HNp() = hp->HOp()->HNp()->HNp(); + hp->HOp()->HNp()->HNp()->HPp() = hp->HPp()->HPp(); + + + if( hp->HOp()->HVp()->VHp() == hp->HOp() ) + hp->HOp()->HVp()->VHp() = hp->HNp(); + + if( hp->HPp()->HVp()->VHp() == hp->HPp() ) + hp->HPp()->HVp()->VHp() = hp->HPp()->HPp()->HNp(); + + + hp->HNp()->HPp()->HFp() = fp1; + hp->HOp()->HNp()->HNp()->HFp() = fp1; + + if(fp1->FHp() == hp || fp1->FHp() == hp->HPp()) + fp1->FHp() = hp->HNp(); + + + Allocator::DeleteVertex(m, *vp); + if(MeshType::HEdgeType::HasHEAdjacency()) + { + Allocator::DeleteEdge(m, *(hp->HEp()) ); + Allocator::DeleteEdge(m, *(hp->HPp()->HEp()) ); + } + Allocator::DeleteHEdge(m, *hp ); + Allocator::DeleteHEdge(m, *(hp->HOp()) ); + Allocator::DeleteHEdge(m, *(hp->HPp()) ); + Allocator::DeleteHEdge(m, *(hp->HPp()->HOp()) ); + Allocator::DeleteFace(m, *fp2 ); + + + return fp1; + + + } + + + /*! + * Removes a singlet replacing it with an edge + * + * \param m Mesh + * \param vp Vertex shared by the two consecutive edges inside the singlet + * + * \return Pointer to an halfdedge representing the new edge + */ + static HEdgePointer singlet_remove_quad(MeshType &m, FacePointer fp) + { + /* + 2 + / \ + / \ + | | + | | + | 1 | + | /\ | + \ | | / + \ \/ / + 3 + */ + + assert( is_singlet_quad(fp) ); + + bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); + bool HasEH = MeshType::EdgeType::HasEHAdjacency(); + + vector ext_hedges; + + vector int_hedges = getHEdges(fp); + + Allocator::DeleteFace( m, *(fp) ); + + for(typename vector::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi) + { + + if((*hi)->HOp()->HFp() != fp) + ext_hedges.push_back((*hi)->HOp()); + + else if(vertex_valence((*hi)->HVp()) == 1) + { + Allocator::DeleteVertex( m, *((*hi)->HVp()) ); + if(HasHE) + Allocator::DeleteEdge( m, *((*hi)->HEp()) ); + } + + } + + for(typename vector::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi) + Allocator::DeleteHEdge( m, *(*hi) ); + + assert(ext_hedges.size() == 2); + + + if(ext_hedges[0]->HFp() || ext_hedges[1]->HFp()) + { + ext_hedges[0]->HOp() = ext_hedges[1]; + ext_hedges[1]->HOp() = ext_hedges[0]; + + if(HasHE) + { + Allocator::DeleteEdge( m, *(ext_hedges[1]->HEp()) ); + + ext_hedges[1]->HEp() = ext_hedges[0]->HEp(); + + if(HasEH) + ext_hedges[0]->HEp()->EHp() = ext_hedges[0]; + } + + ext_hedges[0]->HVp()->VHp() = ext_hedges[0]; + ext_hedges[1]->HVp()->VHp() = ext_hedges[1]; + + return ext_hedges[0]; + } + + else + { + ext_hedges[0]->HVp()->VHp() = NULL; + ext_hedges[1]->HVp()->VHp() = NULL; + + if(HasHE) + { + Allocator::DeleteEdge( m, *( ext_hedges[0]->HEp()) ); + Allocator::DeleteEdge( m, *( ext_hedges[1]->HEp()) ); + } + Allocator::DeleteHEdge( m, *( ext_hedges[0]) ); + Allocator::DeleteHEdge( m, *( ext_hedges[1]) ); + + return NULL; + } + + } + + + /*! + * Rotates a non-border edge shared by two quads + * + * \param m Mesh + * \param ep Edge to be rotated + * \param cw flag denoting a clockwise or counter-clockwise rotation + * + * \return Pointer to the rotated edge + */ + static HEdgePointer edge_rotate_quad(HEdgePointer hp, bool cw) + { + + assert( MeshType::HEdgeType::HasHFAdjacency() ); + assert( MeshType::HEdgeType::HasHOppAdjacency() ); + assert( MeshType::FaceType::HasFHAdjacency() ); + + + FacePointer fp1 = hp->HFp(); + FacePointer fp2 = hp->HOp()->HFp(); + + + assert( fp1 ); + assert( fp1->VN() == 4 ); + + assert( fp2 ); + assert( fp2->VN() == 4 ); + + assert(!is_singlet_quad(fp1)); + assert(!is_singlet_quad(fp2)); + + assert(!has_doublet_quad(fp1)); + assert(!has_doublet_quad(fp2)); + + vector fps; + typedef vector hedge_vect; + vector hps; + + fps.push_back(fp1); + fps.push_back(fp2); + + + hps.push_back( getHEdges( fp1, hp ) ); + hps.push_back( getHEdges( fp2, hp->HOp() ) ); + + + for(int i=0; i< 2; i++) + { + + int j = (i+1)%2; + + // uguali sia per cw che per ccw + hps[i][1]->HPp() = hps[j][3]; + hps[j][3]->HNp() = hps[i][1]; + + if(hps[j][0]->HVp()->VHp() == hps[j][0]) + hps[j][0]->HVp()->VHp() = hps[i][1]; + + if(cw) + { + hps[i][0]->HNp() = hps[j][3]; + hps[j][3]->HPp() = hps[i][0]; + + hps[j][2]->HNp() = hps[j][0]; + hps[j][0]->HPp() = hps[j][2]; + + hps[j][0]->HVp() = hps[j][3]->HVp(); + + hps[j][3]->HFp() = fps[i]; + + if(fps[j]->FHp() == hps[j][3]) + fps[j]->FHp() = hps[j][0]; + } + else + { + hps[i][0]->HNp() = hps[i][2]; + hps[i][2]->HPp() = hps[i][0]; + + hps[i][1]->HNp() = hps[j][0]; + hps[j][0]->HPp() = hps[i][1]; + + hps[j][0]->HVp() = hps[i][2]->HVp(); + + hps[i][1]->HFp() = fps[j]; + + if(fps[i]->FHp() == hps[i][1]) + fps[i]->FHp() = hps[i][0]; + } + + } + + return hp; + + } + + + + /*! + * Rotates a non-border vertex shared by only quads + * + * \param m Mesh + * \param vp Vertex to be rotated + * + * \return Pointer to the rotated vertex + */ + static VertexPointer vertex_rotate_quad(VertexPointer vp) + { + + assert(MeshType::VertexType::HasVHAdjacency()); + assert( vp->VHp() ); + + Pos p(vp->VHp(), true); + + HEdgePointer hep = p.HE(); + + typedef vector hedge_vect; + vector hedges; + + do + { + assert( p.F() ); + assert( p.F()->VN() == 4); + + hedges.push_back(getHEdges(p.F(), p.HE())); + + p.FlipE(); + p.FlipF(); + + }while(p.HE() != hep); + + + int size = hedges.size(); + + for(int i=0; i< size; i++) + { + hedges[i][0]->HNp() = hedges[i][2]; + hedges[i][2]->HPp() = hedges[i][0]; + + assert(hedges[i][0]->HOp() == hedges[(i+size-1)%size][3]); + + hedges[i][2]->HNp() = hedges[(i+1)%size][1]; + hedges[(i+1)%size][1]->HPp() = hedges[i][2]; + + hedges[(i+1)%size][1]->HNp() = hedges[i][3]; + hedges[i][3]->HPp() = hedges[(i+1)%size][1]; + + hedges[(i+1)%size][1]->HFp() = hedges[i][3]->HFp(); + + if(hedges[i][3]->HVp()->VHp() == hedges[i][3]) + hedges[i][3]->HVp()->VHp() = hedges[(i+1)%size][1]; + + hedges[i][3]->HVp() = hedges[(i+1)%size][2]->HVp(); + + if(hedges[i][0]->HFp()->FHp() == hedges[i][1]) + hedges[i][0]->HFp()->FHp() = hedges[i][0]; + + } + return vp; + + } + + + /*! + * Collapses a generic edge + * + * \param m Mesh + * \param ep Edge to be collapsed + * \param vp Vertex to be deleted + * + * \return Pointer to the other vertex belonging to the collapsed edge + */ + static VertexPointer edge_collapse(MeshType &m, HEdgePointer hp, VertexPointer vp) + { + + assert(MeshType::VertexType::HasVHAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(MeshType::HEdgeType::HasHPrevAdjacency()); + + if( hp->HFp() ) + assert(hp->HFp()->VN() > 3); + + if( hp->HOp()->HFp()) + assert(hp->HOp()->HFp()->VN() > 3); + + assert(hp->HFp() || hp->HOp()->HFp()); + assert(hp->HVp() == vp || hp->HOp()->HVp() == vp); + + + HEdgePointer hopp = hp->HOp(); + + VertexPointer vp1; + + if( hp->HVp() == vp ) + vp1 = hopp->HVp(); + else + vp1 = hp->HVp(); + + change_vertex( vp, vp1); + + //HP + hp->HNp()->HPp() = hp->HPp(); + hopp->HNp()->HPp() = hopp->HPp(); + + //HN + hp->HPp()->HNp() = hp->HNp(); + hopp->HPp()->HNp() = hopp->HNp(); + + //FH + if( hp->HFp() ) + if( hp->HFp()->FHp() == hp ) + hp->HFp()->FHp() = hp->HNp(); + + if( hopp->HFp() ) + if( hopp->HFp()->FHp() == hopp ) + hopp->HFp()->FHp() = hopp->HNp(); + + // VH + if( vp1->VHp() == hopp ) + vp1->VHp() = hopp->HNp(); + + if(HasHEAdjacency(m)) + Allocator::DeleteEdge(m,*(hp->HEp())); + Allocator::DeleteHEdge(m,*hp); + Allocator::DeleteHEdge(m,*hopp); + Allocator::DeleteVertex(m,*vp); + + return vp1; + + } + + /*! + * Adds a face in a mesh, checking if the operation is possible. + * + * \param m Mesh + * \param vps Vector of vertices (in ccw order) that will belong to the new face + * + * \return Pointer to the new face if it has been inserted, NULL otherwise + */ + static FacePointer add_face(MeshType &m, vector &vps) + { + + assert(MeshType::VertexType::HasVHAdjacency()); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(MeshType::HEdgeType::HasHFAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::HEdgeType::HasHPrevAdjacency()); + + unsigned int size = vps.size(); + + assert(size >= 3); //there must be at least 3 vertices + + for(unsigned int i = 0; i< size; i++) + { + // all vertices must be different + assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); + } + + vector hps; + + while(hps.size() < size) + if( !can_add_hedge(vps, hps) ) + return NULL; + + vector non_manifold_vertices(size, false); + + return add_face_unsafe( m,vps, hps, non_manifold_vertices); + + } + + /*! + * Removes a face in a mesh, checking if the operation is possible + * + * \param m Mesh + * \param fp face to be removed + * + * \retval true if face has been removed + * \retval false otherwise + */ + static bool remove_face(MeshType &m, FacePointer fp) + { + + assert(MeshType::VertexType::HasVHAdjacency()); + assert(MeshType::FaceType::HasFHAdjacency()); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(MeshType::HEdgeType::HasHFAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::HEdgeType::HasHPrevAdjacency()); + + if( can_remove_face(fp) ) + { + remove_face_unsafe(m, fp); + return true; + } + + return false; + } + + protected: + + /*! + * Adds a face in a mesh without any check + * + * \param m Mesh + * \param vps Vector of vertices (in ccw order) that will belong to the new face + * + * \return Pointer to the new face + */ + static FacePointer add_face_unsafe(MeshType &m, vector &vps) + { + unsigned int size = vps.size(); + + vector hps; + vector non_manifold_vertices; + + while(hps.size() < size) + { + if( can_add_hedge(vps, hps) ) + non_manifold_vertices.push_back( false ); + else + non_manifold_vertices.push_back( hps.back() == NULL ); + } + + return add_face_unsafe(m,vps,hps, non_manifold_vertices); + } + + + /*! + * Adds a face in a mesh without any check + * + * \param m Mesh + * \param vps Vector of vertices (in ccw order) that will belong to the new face + * \param non_manifold_vertices Vector of booleans denoting on the i-th position if the i-th vertex is non-manifold + * + * \return Pointer to the new face + */ + static FacePointer add_face_unsafe(MeshType &m, vector &vps, vector &hps, vector &non_manifold_vertices) + { + + assert(MeshType::VertexType::HasVHAdjacency()); + assert(MeshType::HEdgeType::HasHVAdjacency()); + assert(MeshType::HEdgeType::HasHFAdjacency()); + assert(MeshType::HEdgeType::HasHOppAdjacency()); + assert(MeshType::HEdgeType::HasHPrevAdjacency()); + + unsigned int size = vps.size(); + + assert(size >= 3); //there must be at least 3 vertices + +// for(unsigned int i = 0; i< size; i++) +// { +// // all vertices must be different +// assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); +// } + + bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); + bool HasEH = MeshType::EdgeType::HasEHAdjacency(); + + HEdgeIterator hi; + + assert(hps.size() == size); + + HEdgePointer nullPointer = NULL; + int edge_n = count(hps.begin(), hps.end(), nullPointer); + + FacePointer fp; + + FaceIterator fi = Allocator::AddFaces(m,1); + (*fi).Alloc( size ); + fp = &(*fi); + + if(edge_n > 0) + { + + EdgeIterator ei; + + fp->SetD(); + + if(HasEH || HasHE) + { + ei = Allocator::AddEdges(m,edge_n); + for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1) + (*ei1).SetD(); + } + + typename Allocator::template PointerUpdater pu; + + if(m.hedge.empty()) + pu.oldBase = 0; + else + { + pu.oldBase = &*(m.hedge.begin()); + pu.oldEnd = &m.hedge.back()+1; + } + + hi = Allocator::AddHEdges(m,2*edge_n); + + pu.newBase = &*(m.hedge.begin()); + pu.newEnd = &m.hedge.back()+1; + + //undelete face + fp->ClearD(); + + //undelete edges + for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1) + (*ei1).ClearD(); + + // update hedge pointers (if needed) + if( pu.NeedUpdate() ) + for(typename vector::iterator hpsi = hps.begin(); hpsi != hps.end(); ++hpsi) + { + if((*hpsi)) + pu.Update(*hpsi); + } + + + HEdgeIterator hi1 = hi; + HEdgeIterator hi2 = hi; + + ++hi2; + + EdgeIterator ei1 = ei; + + for(; hi2 != m.hedge.end(); ++hi1, ++hi2) + { + // EH + if(HasEH) + (*ei1).EHp() = &(*hi1); + + // HE + if(HasHE) + { + (*hi1).HEp() = &(*ei1); + (*hi2).HEp() = &(*ei1); + } + + //HO + (*hi1).HOp() = &(*hi2); + (*hi2).HOp() = &(*hi1); + + // HF + (*hi1).HFp() = fp; + + ++hi1; + ++hi2; + } + } + + vector hps1; + + for(unsigned int i = 0; i < size; i++) + { + if(hps[i] == NULL) + { + hps1.push_back(&(*hi)); + ++hi; + ++hi; + } + else + hps1.push_back(hps[i]); + } + + assert( hps1.size() == size ); + + for(unsigned int i = 0; i < size; i++) + { + + int next = (i+1)%size; + + // hedge already exisitng + if(hps[i]) + { + hps1[i]->HFp() = fp; + + // next hedge was disconnected + if(!hps[next]) + { + + hps1[next]->HOp()->HNp() = hps1[i]->HNp(); + + hps1[i]->HNp()->HPp() = hps1[next]->HOp(); + + hps1[i]->HNp() = hps1[next]; + + hps1[next]->HPp() = hps1[i]; + } + } + + // hedge wasn't existing, vertex was disconnected + else + { + //HV + hps1[i]->HVp() = vps[i]; + hps1[i]->HOp()->HVp() = vps[next]; + + + hps1[i]->HNp() = hps1[next]; + + // next hedge was existing (vertex was disconnected) + if(hps[next]) + { + hps1[i]->HOp()->HPp() = hps1[next]->HPp(); + hps1[next]->HPp()->HNp() = hps1[i]->HOp(); + } + + //vertex was detached + else + { + // after face insertion vertex will become non-manifold + if(non_manifold_vertices[next]) + { + Pos p(vps[next]->VHp(), true); + + while(p.F()) + { + + p.FlipE(); + p.FlipF(); + + if(p.HE() == vps[next]->VHp()) + assert(0); //can't add a connection, there is no space + } + + + p.HE()->HPp()->HNp() = hps1[i]->HOp(); + hps1[i]->HOp()->HPp() = p.HE()->HPp(); + + p.HE()->HPp() = hps1[next]->HOp(); + hps1[next]->HOp()->HNp() = p.HE(); + + } + else + { + hps1[i]->HOp()->HPp() = hps1[next]->HOp(); + hps1[next]->HOp()->HNp() = hps1[i]->HOp(); + } + + } + + + hps1[next]->HPp() = hps1[i]; + + //VH + if( !vps[i]->VHp()) + vps[i]->VHp() = hps1[i]; + } + } + + //FH + fp->FHp() = hps1.front(); + + return fp; + + } + + /*! + * Removes a face in a mesh, without any check + * + * \param m Mesh + * \param fp Face to be removed + * + */ + static void remove_face_unsafe (MeshType &m, FacePointer fp) + { + + vector hps = getHEdges(fp); + + int size = hps.size(); + + for( int i = 0; i< size; i++ ) + { + if( hps[i]->HOp()->HFp() ) + { + hps[i]->HFp() = NULL; + + if( !hps[(i+size-1)%size]->HOp()->HFp() ) + { + // HP + hps[i]->HPp() = hps[(i+size-1)%size]->HOp()->HPp(); + hps[(i+size-1)%size]->HOp()->HPp()->HNp() = hps[i]; + } + + if( !hps[(i+1)%size]->HOp()->HFp() ) + { + // HN + hps[i]->HNp() = hps[(i+1)%size]->HOp()->HNp(); + hps[(i+1)%size]->HOp()->HNp()->HPp() = hps[i]; + } + } + else + { + Allocator::DeleteHEdge( m, *hps[i] ); + Allocator::DeleteHEdge( m, *(hps[i]->HOp()) ); + + if(MeshType::HEdgeType::HasHEAdjacency()) + Allocator::DeleteEdge( m, *(hps[i]->HEp()) ); + + if( !hps[(i+size-1)%size]->HOp()->HFp() ) + { + hps[i]->HOp()->HNp()->HPp() = hps[(i+size-1)%size]->HOp()->HPp(); + hps[(i+size-1)%size]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp(); + } + + } + + } + + for( int i = 0; i< size; i++ ) + { + if( hps[i]->HVp()->VHp()->IsD() ) + { + if( !hps[i]->IsD() ) + hps[i]->HVp()->VHp() = hps[i]; + + else if( !hps[(i+size-1)%size]->IsD() ) + hps[i]->HVp()->VHp() = hps[(i+size-1)%size]->HOp(); + + else //search for a hedge (hedge can be found only if the vertex is non-manifold) + { + bool manifold = true; + + Pos p(hps[i]->HVp()->VHp(), true); + + p.HE()->SetV(); + + p.FlipE(); + p.FlipF(); + + while( !p.HE()->IsV() ) + { + if( !p.HE()->IsD() ) + { + manifold = false; + hps[i]->HVp()->VHp() = p.HE(); + break; + } + + p.FlipE(); + p.FlipF(); + } + + p.HE()->ClearV(); + + if(manifold) + hps[i]->HVp()->VHp() = NULL; + + } + } + + } + + Allocator::DeleteFace(m,*fp); + + } + + /*! + * Checks if the next hedge can be inserted into hps. + * If true, inserts the hedge into hps. If false, inserts NULL. + * + * \param vps Vector of vertices (in ccw order) that will belong to the new face + * \param hps Vector of hedges already checked + * + * \retval true if hedge can be inserted + * \retval false otherwise + */ + static bool can_add_hedge( vector &vps, vector &hps ) + { + + unsigned int i = hps.size(); + + assert( i < vps.size() ); + + HEdgePointer he = vps[i]->VHp(); + + if(!he) //vertex is detached + { + hps.push_back(NULL); + return true; + } + else + { + bool disconnected = false; + + bool hasEdge = false; + + unsigned int size = vps.size(); + + Pos p(he, false); + + he->SetV(); + + while(p.V() != vps[(i+1)%size]) + { + if(!hasEdge) + hasEdge= ( find( vps.begin(), vps.end(), p.V()) != (vps.end() ) ); + + p.FlipV(); + + p.FlipE(); + p.FlipF(); + + p.FlipV(); + + if(p.HE()->IsV()) + { + disconnected = true; + break; + } + + } + + he->ClearV(); + + if(disconnected) // edge does not exist + { + hps.push_back(NULL); + + // if hasEdge is false after inserting the face there will be a non-manifold vertex + return hasEdge; + } + + else //edge already existing + { + // try to insert consecutve hedges if they will belong to the new face + while( (p.V() == vps[(i+1)%size]) && (i < size) ) + { + hps.push_back( p.HE() ); + + if(p.HE()->HFp() != NULL) + return false; + + i++; + p.FlipE(); + p.FlipV(); + } + return true; + } + } + } + + public: + /*! + * Checks if a face can be removed + * + * \param fp Face to check + * + * \retval true if the face can be removed + * \retval false otherwise + */ + static bool can_remove_face(FacePointer fp) + { + + assert(fp); + assert(!fp->IsD()); + + Pos p(fp->FHp(), true); + + do + { + vector incident_faces = get_incident_faces( p.V() ); + + unsigned int size = incident_faces.size(); + + if(size > 2) + { + for(unsigned int i = 0; i < size; i++) + { + if(incident_faces[i] == NULL) + if(incident_faces[(i+1)%size] != fp && incident_faces[((i+size)-1)%size] != fp ) + return false; + } + } + + p.FlipV(); + p.FlipE(); + + }while( p.HE() != fp->FHp() ); + + return true; + } + + /*! + * Checks if a diagonal can be collapsed + * + * \param vp Hedge whose vertex is one of the two vertices of the diagonal + * + * \retval true if diagonal can be collapsed + * \retval false if diagonal cannot be collapsed + */ + static bool check_diagonal_collapse_quad(HEdgePointer hp) + { + + assert(hp); + assert(hp->HFp()); + assert(hp->HFp()->VN() == 4); + assert(!hp->IsD()); + + vector faces; + + HEdgePointer hopp = hp->HNp()->HNp(); + vector faces1 = get_incident_faces(hp->HVp(), hp); + vector faces2 = get_incident_faces(hp->HNp()->HNp()->HVp(), hopp); + + faces.assign(faces1.begin()+1, faces1.end()); + faces.assign(faces2.begin()+1, faces2.end()); + + + // First check: + + unsigned int size = faces.size(); + bool null_face = false; + + + // if size <=2 check is ok + if(size > 2) + { + for(unsigned int i = 0; i < size; i++) + { + if(faces[i] == NULL) + { + if(faces[(i+1)%size] != NULL && faces[((i+size)-1)%size] != NULL ) + { + if(null_face) + return false; + + null_face=true; + } + } + } + } + + // End of first check + + + // Second check + + set set1; + set set2; + + vector vect1 = getVertices(hp->HVp()); + vector vect2 = getVertices(hp->HNp()->HNp()->HVp()); + + set1.insert(vect1.begin(), vect1.end()); + set2.insert(vect2.begin(), vect2.end()); + + size = vect1.size(); + if(vect2.size() < size) + size = vect2.size(); + + vector intersection(size); + + typename vector::iterator it; + it = set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), intersection.begin()); + + size = it- intersection.begin(); + + assert( size >= 2 ); + + return (size==2); + + // End of second check + + } + + /*! + * Checks if a vertex is non-manifold, comparing local and global information (slow) + * + * \param vp Vertex to check + * + * \retval true if vertex is non-manifold + * \retval false if verex is manifold + */ + static bool is_nonManifold_vertex(MeshType &m, VertexPointer vp) + { + assert(vp); + assert(!vp->IsD()); + + set set1; + for(HEdgeIterator hi = m.hedge.begin(); hi != m.hedge.end(); ++hi) + { + if(!(*hi).IsD() && (*hi).HVp() == vp) + set1.insert(&(*hi)); + } + + + vector vect2 = get_incident_hedges(vp); + + set set2; + set2.insert(vect2.begin(), vect2.end()); + + return !equal(set1.begin(), set1.end(), set2.begin()); + + } + + /*! + * Checks if a vertex is non-manifold, based only on local informations + * + * \param vp Vertex to check + * + * \retval true if vertex is non-manifold + * \retval false if verex is manifold + */ + static bool is_nonManifold_vertex(VertexPointer vp) + { + assert(vp); + assert(!vp->IsD()); + + vector faces = get_incident_faces(vp); + + unsigned int size = faces.size(); + int null_count = 0; + + if(size > 2) + { + for(unsigned int i = 0; i < size; i++) + { + if(faces[i] == NULL) + { + if(null_count > 0) + return true; + else + null_count++; + } + } + } + + return false; + + } + + /*! + * Shortcut to get the second vertex of an edge + * + * \param hp Hedge + * + * \return Opposite vertex + */ + static VertexPointer opp_vert(HEdgePointer hp) + { + return hp->HOp()->HVp(); + } + + /*! + * Gets vertices on the 1-ring of a vertex + * + * \param vp Vertex. It must be a non-border vertex. + * + * \return Vector containing vertices + */ + static vector getVertices(VertexPointer vp) + { + assert(vp); + assert(!vp->IsD()); + + HEdgePointer hp = vp->VHp(); + + vector ret; + + if( !hp ) + return ret; + + Pos p(hp); + + do + { + if(p.F()) + { + assert(!p.F()->IsD()); + + ret.push_back( opp_vert( p.HE() ) ); + + ret.push_back( opp_vert( p.HE()->HNp() ) ); + + + } + p.FlipE(); + p.FlipF(); + + }while( p.HE() != hp); + + return ret; + } + + + /*! + * Gets faces on the 1-ring of a vertex + * + * \param vp Vertex + * + * \return Set containing faces + */ + static set getFaces(VertexPointer vp) + { + assert(vp); + assert(!vp->IsD()); + + set ret; + + vector vertices = getVertices(vp); + + for(typename vector::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi) + { + vector incident_faces = get_incident_faces(*vi); + ret.insert(incident_faces.begin(), incident_faces.end()); + } + + return ret; + + } + + + /*! + * Checks if a face is a singlet + * + * \param fp Face to check + * + * \retval true if face is a singlet + * \retval false if face isn't a singlet + */ + static bool is_singlet_quad(FacePointer fp) + { + assert(fp); + assert(fp->FHp()); + assert(!fp->IsD()); + + Pos p( fp->FHp() ); + + do + { + if( vertex_valence(p.V()) == 1 ) + return true; + + p.FlipV(); + p.FlipE(); + + }while(p.HE() != fp->FHp()); + + return false; + + } + + /*! + * Gets all vertices incident to a face + * + * \param fp Face + * \param starting_he A hedge in the face from which to start + * + * \return Vector containing the incident vertices + */ + static vector getVertices(FacePointer fp, HEdgePointer starting_he = NULL) + { + assert(fp); + assert(!fp->IsD()); + + if(!starting_he) + starting_he = fp->FHp(); + + assert( starting_he->HFp() == fp ); + + Pos p( starting_he, true ); + + vector ret; + + + do + { + assert(!(p.V()->IsD())); + + ret.push_back( p.V() ); + + p.FlipV(); + p.FlipE(); + + assert(ret.size() <= (unsigned int)(fp->VN())); + + }while(p.HE() != starting_he); + + return ret; + + } + + protected: + /*! + * Gets all hedges incident to a face + * + * \param fp Face + * \param starting_he A hedge in the face from which to start + * + * \return Vector containing the incident hedges + */ + static vector getHEdges(FacePointer fp, HEdgePointer starting_he = NULL) + { + assert(fp); + assert(!fp->IsD()); + + if(starting_he) + assert( starting_he->HFp() == fp ); + else + starting_he = fp->FHp(); + + Pos p( starting_he, true ); + + vector ret; + + do + { + ret.push_back( p.HE() ); + + p.FlipV(); + p.FlipE(); + + assert(ret.size() <= (unsigned int) (fp->VN())); + + }while(p.HE() != starting_he); + + return ret; + + } + + public: + + /*! + * Gets all faces incident to a vertex + * + * \param vp Vertex + * \param starting_he A hedge from which to start + * + * \return Vector containing the incident faces + */ + static vector get_incident_faces(VertexPointer vp, HEdgePointer starting_he = NULL) + { + assert(vp); + assert(!vp->IsD()); + + if(starting_he) + assert( starting_he->HVp() == vp ); + else + starting_he = vp->VHp(); + + vector ret; + + if(!starting_he) + return ret; + + Pos p( starting_he, true ); + + do + { + ret.push_back( p.F() ); + + p.FlipE(); + p.FlipF(); + + }while(p.HE() != starting_he); + + return ret; + + } + + + static vector get_adjacent_faces(FacePointer fp) + { + assert(fp); + assert(!fp->IsD()); + + vector ret; + + Pos p( fp->FHp() ); + assert(p.F() == fp); + + do + { + p.FlipF(); + ret.push_back( p.F() ); + p.FlipF(); + + p.FlipV(); + p.FlipE(); + + } while(p.HE() != fp->FHp()); + + return ret; + + } + + /*! + * Gets all hedges incident to a vertex + * + * \param vp Vertex + * \param starting_he A hedge from which to start navigation + * + * \return Vector containing the incident hedges + */ + static vector get_incident_hedges(VertexPointer vp, HEdgePointer starting_he = NULL) + { + assert(vp); + assert(!vp->IsD()); + + if(starting_he) + assert( starting_he->HVp() == vp ); + else + starting_he = vp->VHp(); + + vector ret; + + if(!starting_he) + return ret; + + Pos p( starting_he, true ); + + do + { + assert(!p.HE()->IsD()); + + ret.push_back( p.HE() ); + + p.FlipE(); + p.FlipF(); + + + }while(p.HE() != starting_he); + + return ret; + + } + + /*! + * Checks if a face has doublets + * + * \param fp Face to check + * + * \retval true if face has at least a doublet + * \retval false if face hasn't any doublet + */ + static bool has_doublet_quad(FacePointer fp) + { + return ( !find_doublet_hedges_quad(fp).empty() ); + } + + /*! + * Gets all hedges whose vertex is into a doublet + * + * \param fp Face to check + * + * \return Vector containing the hedges + */ + static vector find_doublet_hedges_quad(FacePointer fp) + { + assert(fp); + assert(fp->FHp()); + assert(!fp->IsD()); + + vector ret; + + Pos p( fp->FHp(), true ); + + do + { + + if(vertex_valence(p.V()) == 2 && !isBorderVertex(p.V())) + ret.push_back(p.HE()); + + assert(ret.size() <= 4); + + p.FlipV(); + p.FlipE(); + + }while(p.HE() != fp->FHp()); + + return ret; + + } + + /*! + * Checks if a vertex is a border vertex + * + * \param vp Vertex to check + * + * \retval true if vertex is a border vertex + * \retval false if vertex isn't a border vertex + */ + static bool isBorderVertex(VertexPointer vp) + { + assert(vp); + assert(!vp->IsD()); + + if( !(vp->VHp()) ) + return true; + + Pos p( vp->VHp() ); + + do + { + if(!p.F()) + return true; + + p.FlipE(); + p.FlipF(); + + }while(p.HE() != vp->VHp()); + + return false; + } + + /*! + * Computes valence of a vertex + * + * \param vp Vertex + * + * \return Vertex valence + */ + static int vertex_valence(VertexPointer vp) + { + assert(vp); + assert(!vp->IsD()); + + if( !(vp->VHp()) ) + return 0; + + int ret = 0; + + Pos p( vp->VHp() ); + + do + { + assert(!p.HE()->IsD()); + ret++; + + p.FlipE(); + p.FlipF(); + + }while(p.HE() != vp->VHp()); + + return ret; + } + + /*! + * Connects to a new vertex all hedges incident to a vertex + * + * \param old_vp the old vertex to be disconnected + * \param new_vp the new vertex to be connected + * + */ + protected: + static void change_vertex(VertexPointer old_vp, VertexPointer new_vp) + { + assert(old_vp); + assert(new_vp); + assert(old_vp != new_vp); + assert(!old_vp->IsD()); + + Pos p(old_vp->VHp(),true); + + p.HE()->SetV(); + + do + { + p.HE()->HVp() = new_vp; + + p.FlipE(); + p.FlipF(); + + }while( !p.HE()->IsV() ); + + p.HE()->ClearV(); + + if( !new_vp->VHp() ) + new_vp->VHp() = old_vp->VHp(); + + } + + }; + + } +} + +#endif // VCG_HEDGE_TOPOLOGY + diff --git a/vcg/complex/algorithms/update/normal.h b/vcg/complex/algorithms/update/normal.h new file mode 100644 index 00000000..7be220e4 --- /dev/null +++ b/vcg/complex/algorithms/update/normal.h @@ -0,0 +1,398 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#ifndef __VCG_TRI_UPDATE_NORMALS +#define __VCG_TRI_UPDATE_NORMALS + +#include +#include +#include + + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile normal.h vcg/complex/trimesh/update/normal.h + +/// \brief Management, updating and computation of per-vertex and per-face normals. +/** +This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh. +*/ + +template +class UpdateNormals +{ +public: +typedef ComputeMeshType MeshType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::CoordType CoordType; +typedef typename VertexType::NormalType NormalType; +typedef typename VertexType::ScalarType ScalarType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FacePointer FacePointer; +typedef typename MeshType::FaceIterator FaceIterator; + +/** + Set to zero all the normals. Usued by all the face averaging algorithms. + by default it does not clear the normals of unreferenced vertices because they could be still useful + */ +static void PerVertexClear(ComputeMeshType &m, bool ClearAllVertNormal=false) +{ + assert(HasPerVertexNormal(m)); + if(ClearAllVertNormal) + UpdateFlags::VertexClearV(m); + else + { + UpdateFlags::VertexSetV(m); + for(FaceIterator f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() ) + for(int i=0;i<3;++i) (*f).V(i)->ClearV(); + } + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if( !(*vi).IsD() && (*vi).IsRW() && (!(*vi).IsV()) ) + (*vi).N() = NormalType((ScalarType)0,(ScalarType)0,(ScalarType)0); +} + +/// \brief Calculates the face normal (if stored in the current face type) + +static void PerFace(ComputeMeshType &m) +{ + if( !m.HasPerFaceNormal()) return; + FaceIterator f; + for(f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() ) face::ComputeNormal(*f); +} + +/// \brief Calculates the vertex normal. Exploiting or current face normals. +/** + The normal of a vertex v is the weigthed average of the normals of the faces incident on v. +*/ +static void PerVertexFromCurrentFaceNormal(ComputeMeshType &m) +{ + if( !m.HasPerVertexNormal()) return; + + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if( !(*vi).IsD() && (*vi).IsRW() ) + (*vi).N()=CoordType(0,0,0); + + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if( !(*fi).IsD()) + { + for(int j=0; j<3; ++j) + if( !(*fi).V(j)->IsD()) + (*fi).V(j)->N() += (*fi).cN(); + } +} +/// \brief Calculates the vertex normal. Exploiting or current face normals. +/** + The normal of a face f is the average of the normals of the vertices of f. +*/ +static void PerFaceFromCurrentVertexNormal(ComputeMeshType &m) +{ + for (FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi) + if( !(*fi).IsD()) + { + NormalType n; + n.SetZero(); + for(int j=0; j<3; ++j) + n += fi->V(j)->cN(); + n.Normalize(); + fi->N() = n; + } +} + + +/// \brief Calculates the vertex normal. Without exploiting or touching face normals. +/** + The normal of a vertex v computed as a weighted sum f the incident face normals. + The weight is simlply the angle of the involved wedge. Described in: + +G. Thurmer, C. A. Wuthrich +"Computing vertex normals from polygonal facets" +Journal of Graphics Tools, 1998 + */ + +static void PerVertexAngleWeighted(ComputeMeshType &m) +{ + assert(HasPerVertexNormal(m)); + PerVertexClear(m); + FaceIterator f; + for(f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() && (*f).IsR() ) + { + typename FaceType::NormalType t = vcg::NormalizedNormal(*f); + NormalType e0 = ((*f).V1(0)->cP()-(*f).V0(0)->cP()).Normalize(); + NormalType e1 = ((*f).V1(1)->cP()-(*f).V0(1)->cP()).Normalize(); + NormalType e2 = ((*f).V1(2)->cP()-(*f).V0(2)->cP()).Normalize(); + + (*f).V(0)->N() += t*AngleN(e0,-e2); + (*f).V(1)->N() += t*AngleN(-e0,e1); + (*f).V(2)->N() += t*AngleN(-e1,e2); + } +} + +/// \brief Calculates the vertex normal. Without exploiting or touching face normals. +/** + The normal of a vertex v is computed according to the formula described by Nelson Max in + Max, N., "Weights for Computing Vertex Normals from Facet Normals", Journal of Graphics Tools, 4(2) (1999) + + The weight for each wedge is the cross product of the two edge over the product of the square of the two edge lengths. + According to the original paper it is perfect only for spherical surface, but it should perform well... + */ +static void PerVertexWeighted(ComputeMeshType &m) +{ + assert(HasPerVertexNormal(m)); + + PerVertexClear(m); + + FaceIterator f; + for(f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() && (*f).IsR() ) + { + typename FaceType::NormalType t = vcg::Normal(*f); + ScalarType e0 = SquaredDistance((*f).V0(0)->cP(),(*f).V1(0)->cP()); + ScalarType e1 = SquaredDistance((*f).V0(1)->cP(),(*f).V1(1)->cP()); + ScalarType e2 = SquaredDistance((*f).V0(2)->cP(),(*f).V1(2)->cP()); + + (*f).V(0)->N() += t/(e0*e2); + (*f).V(1)->N() += t/(e0*e1); + (*f).V(2)->N() += t/(e1*e2); + } +} + +/// \brief Calculates the vertex normal. Without exploiting or touching face normals. +/** + The normal of a vertex v is the classical area weigthed average of the normals of the faces incident on v. + */ + +static void PerVertex(ComputeMeshType &m) +{ + assert(HasPerVertexNormal(m)); + + PerVertexClear(m); + + FaceIterator f; + for(f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() && (*f).IsR() ) + { + //typename FaceType::NormalType t = (*f).Normal(); + typename FaceType::NormalType t = vcg::Normal(*f); + + for(int j=0; j<3; ++j) + if( !(*f).V(j)->IsD() && (*f).V(j)->IsRW() ) + (*f).V(j)->N() += t; + } +} + + +/// \brief Calculates both vertex and face normals. +/** + The normal of a vertex v is the weigthed average of the normals of the faces incident on v. +*/ + +static void PerVertexPerFace(ComputeMeshType &m) +{ + if( !m.HasPerVertexNormal() || !m.HasPerFaceNormal()) return; + + PerFace(m); + PerVertexClear(m); + + FaceIterator f; + + for(f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() && (*f).IsR() ) + { + for(int j=0; j<3; ++j) + if( !(*f).V(j)->IsD() && (*f).V(j)->IsRW() ) + (*f).V(j)->N() += (*f).cN(); + } +} + +/// \brief Calculates both vertex and face normals. +/** + The normal of a vertex v is the weigthed average of the normals of the faces incident on v. +*/ + +static void PerVertexNormalizedPerFace(ComputeMeshType &m) +{ + PerVertexPerFace(m); + NormalizeVertex(m); +} + +/// \brief Normalize the lenght of the face normals. +static void NormalizeVertex(ComputeMeshType &m) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if( !(*vi).IsD() && (*vi).IsRW() ) + (*vi).N().Normalize(); +} + +/// \brief Normalize the lenght of the face normals. +static void NormalizeFace(ComputeMeshType &m) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if( !(*fi).IsD() ) (*fi).N().Normalize(); +} + +static void AreaNormalizeFace(ComputeMeshType &m) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if( !(*fi).IsD() ) + { + (*fi).N().Normalize(); + (*fi).N() = (*fi).N() * DoubleArea(*fi); + } +} + +static void PerVertexNormalizedPerFaceNormalized(ComputeMeshType &m) +{ + PerVertexNormalizedPerFace(m); + NormalizeFace(m); +} + +static void PerFaceRW(ComputeMeshType &m, bool normalize=false) +{ + if( !m.HasPerFaceNormal()) return; + + FaceIterator f; + bool cn = true; + + if(normalize) + { + for(f=m.m.face.begin();f!=m.m.face.end();++f) + if( !(*f).IsD() && (*f).IsRW() ) + { + for(int j=0; j<3; ++j) + if( !(*f).V(j)->IsR()) cn = false; + if( cn ) face::ComputeNormalizedNormal(*f); + cn = true; + } + } + else + { + for(f=m.m.face.begin();f!=m.m.face.end();++f) + if( !(*f).IsD() && (*f).IsRW() ) + { + for(int j=0; j<3; ++j) + if( !(*f).V(j)->IsR()) cn = false; + + if( cn ) + (*f).ComputeNormal(); + cn = true; + } + } +} + + +static void PerFaceNormalized(ComputeMeshType &m) +{ + if( !m.HasPerFaceNormal()) return; + FaceIterator f; + for(f=m.face.begin();f!=m.face.end();++f) + if( !(*f).IsD() ) face::ComputeNormalizedNormal(*f); +} + +static void PerBitQuadFaceNormalized(ComputeMeshType &m) +{ + if( !m.HasPerFaceNormal()) return; + PerFace(m); + + FaceIterator f; + for(f=m.face.begin();f!=m.face.end();++f) { + if( !(*f).IsD() ) { + for (int k=0; k<3; k++) if (f->IsF(k)) + if (&*f < f->FFp(k)) { + f->N() = f->FFp(k)->N() = (f->FFp(k)->N() + f->N()).Normalize(); + } + } + } +} + + +/// \brief Calculates the vertex normal. +static void PerVertexNormalized(ComputeMeshType &m) +{ + if( !m.HasPerVertexNormal()) return; + PerVertex(m); + for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) + if( !(*vi).IsD() && (*vi).IsRW() ) + (*vi).N().Normalize(); +} + +/// \brief Multiply the vertex normals by the matrix passed. By default, the scale component is removed. +static void PerVertexMatrix(ComputeMeshType &m, const Matrix44 &mat, bool remove_scaling= true){ + float scale; + + Matrix33 mat33(mat,3); + + if( !m.HasPerVertexNormal()) return; + + if(remove_scaling){ + scale = pow(mat33.Determinant(),(ScalarType)(1.0/3.0)); + mat33[0][0]/=scale; + mat33[1][1]/=scale; + mat33[2][2]/=scale; + } + + for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) + if( !(*vi).IsD() && (*vi).IsRW() ) + (*vi).N() = mat33*(*vi).N(); +} + +/// \brief Multiply the face normals by the matrix passed. By default, the scale component is removed. +static void PerFaceMatrix(ComputeMeshType &m, const Matrix44 &mat, bool remove_scaling= true){ + float scale; + + Matrix33 mat33(mat,3); + + if( !m.HasPerFaceNormal()) return; + + if(remove_scaling){ + scale = pow(mat33.Determinant(),ScalarType(1.0/3.0)); + mat33[0][0]/=scale; + mat33[1][1]/=scale; + mat33[2][2]/=scale; + } + + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + if( !(*fi).IsD() && (*fi).IsRW() ) + (*fi).N() = mat33* (*fi).N(); +} + +}; // end class + +} // End namespace +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/update/position.h b/vcg/complex/algorithms/update/position.h new file mode 100644 index 00000000..43482054 --- /dev/null +++ b/vcg/complex/algorithms/update/position.h @@ -0,0 +1,83 @@ +/**************************************************************************** +* 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 $ +Revision 1.1 2005/07/06 08:02:27 cignoni +Initial commit + + +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_POSITION +#define __VCG_TRI_UPDATE_POSITION + +#include "normal.h" + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile position.h vcg/complex/trimesh/update/position.h + +/// \brief This class is used to update vertex position according to a transformation matrix. +template +class UpdatePosition +{ + +public: +typedef ComputeMeshType MeshType; +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FacePointer FacePointer; +typedef typename MeshType::FaceIterator FaceIterator; + +/// \brief Multiply +static void Matrix(ComputeMeshType &m, const Matrix44 &M, bool update_also_normals = true) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) (*vi).P()=M*(*vi).cP(); + + if(update_also_normals){ + if(m.HasPerVertexNormal()){ + UpdateNormals::PerVertexMatrix(m,M); + } + if(m.HasPerFaceNormal()){ + UpdateNormals::PerFaceMatrix(m,M); + } + } +} + + +}; // end class + +} // End namespace +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/update/quality.h b/vcg/complex/algorithms/update/quality.h new file mode 100644 index 00000000..65d4c7db --- /dev/null +++ b/vcg/complex/algorithms/update/quality.h @@ -0,0 +1,351 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_QUALITY +#define __VCG_TRI_UPDATE_QUALITY +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcg { +namespace tri { +/// \ingroup trimesh + +/// \headerfile quality.h vcg/complex/trimesh/update/quality.h + +/// \brief Generation of per-vertex and per-face qualities. +/** + It works according to various strategy, like geodesic distance from the border (UpdateQuality::VertexGeodesicFromBorder) or curvature ecc. + This class is templated over the mesh and (like all other Update* classes) has only static members; Typical usage: +\code +MyMeshType m; +UpdateQuality::VertexGeodesicFromBorder(m); +\endcode +*/ + +template +class UpdateQuality +{ +public: + typedef UpdateMeshType MeshType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceIterator FaceIterator; + +class VQualityHeap +{ +public: + float q; + VertexPointer p; + inline VQualityHeap( VertexPointer np ) + { + q = np->Q(); + p = np; + } + // Attenzione il minore e' maggiore + inline bool operator < ( const VQualityHeap & vq ) const { return q > vq.q; } + inline bool operator == ( const VQualityHeap & vq ) const { return q == vq.q; } + inline bool operator > ( const VQualityHeap & vq ) const { return q < vq.q; } + inline bool operator != ( const VQualityHeap & vq ) const { return q != vq.q; } + inline bool operator <= ( const VQualityHeap & vq ) const { return q >= vq.q; } + inline bool operator >= ( const VQualityHeap & vq ) const { return q <= vq.q; } + inline bool is_valid() const { return q==p->Q(); } +}; + + + +// *** IMPORTANT REQUIREMENTS +// VF topology +// Border FLags +// tri::UpdateTopology::VertexFace(sm); +// tri::UpdateFlags::FaceBorderFromVF(sm); +// +// Calcola la qualita' come distanza geodesica dal bordo della mesh. +// Robusta funziona anche per mesh non manifold. +// La qualita' memorizzata indica la distanza assoluta dal bordo della mesh. +// Nota prima del 13/11/03 in alcuni casi rari SPT andava in loop perche' poteva capitare +// che per approx numeriche ben strane pw->Q() > pv->Q()+d ma durante la memorizzazione +// della nuova distanza essa rimanesse uguale a prima. Patchato rimettendo i vertici nello +// heap solo se migliorano la distanza di un epsilon == 1/100000 della mesh diag. + +/// \brief Compute, for each vertex of the mesh the geodesic distance from the border of the mesh itself. + +/** +It uses the classical Dijkstra Shortest Path Tree algorithm. +The geodesic distance is approximated by allowing to walk only along edges of the mesh. + +\warning VF topology, Per Vertex Quality and border flags already computed (see UpdateFlags::FaceBorderFromVF and UpdateTopology::VertexFace); + +*/ +static void VertexGeodesicFromBorder(MeshType &m) // R1 +{ + //Requirements + assert(m.HasVFTopology()); + assert(m.HasPerVertexQuality()); + + std::vector< VQualityHeap > heap; + VertexIterator v; + FaceIterator f; + int j; + + for(v=m.vert.begin();v!=m.vert.end();++v) + (*v).Q() = -1; + for(f=m.face.begin();f!=m.face.end();++f) // Inserisco nell'heap i v di bordo + if(!(*f).IsD()) + for(j=0;j<3;++j) + if( (*f).IsB(j) ) + { + for(int k=0;k<2;++k) + { + VertexPointer pv = (*f).V((j+k)%3); + if( pv->Q()==-1 ) + { + pv->Q() = 0; + heap.push_back(VQualityHeap(pv)); + } + } + } + + const ScalarType loc_eps=m.bbox.Diag()/ScalarType(100000); + while( heap.size()!=0 ) // Shortest path tree + { + VertexPointer pv; + std::pop_heap(heap.begin(),heap.end()); + if( ! heap.back().is_valid() ) + { + heap.pop_back(); + continue; + } + pv = heap.back().p; + heap.pop_back(); + + for(face::VFIterator vfi(pv) ; !vfi.End(); ++vfi ) + { + for(int k=0;k<2;++k) + { + VertexPointer pw; + float d; + if(k==0) pw = vfi.f->V1(vfi.z); + else pw = vfi.f->V2(vfi.z); + d = Distance(pv->P(),pw->P()); + if( pw->Q()==-1 || pw->Q() > pv->Q()+d + loc_eps) + { + pw->Q() = pv->Q()+d; + heap.push_back(VQualityHeap(pw)); + std::push_heap(heap.begin(),heap.end()); + } + } + } + } + + for(v=m.vert.begin();v!=m.vert.end();++v) + if(v->Q()==-1) + v->Q() = 0; +} + + +/** Assign to each vertex of the mesh a constant quality value. Useful for initialization. +*/ +static void VertexConstant(MeshType &m, float q) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).Q()=q; +} + +/** Clamp each vertex of the mesh with a range of values. +*/ +static void VertexClamp(MeshType &m, float qmin, float qmax) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).Q()=std::min(qmax, std::max(qmin,(*vi).Q())); +} + +/** Normalize the vertex quality so that it fits in the specified range. +*/ +static void VertexNormalize(MeshType &m, float qmin=0.0, float qmax=1.0) +{ + ScalarType deltaRange = qmax-qmin; + std::pair minmax = tri::Stat::ComputePerVertexQualityMinMax(m); + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + (*vi).Q() = qmin+deltaRange*((*vi).Q() - minmax.first)/(minmax.second - minmax.first); +} + +/** Normalize the face quality so that it fits in the specified range. +*/ +static void FaceNormalize(MeshType &m, float qmin=0.0, float qmax=1.0) +{ + ScalarType deltaRange = qmax-qmin; + std::pair minmax = tri::Stat::ComputePerFaceQualityMinMax(m); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + (*fi).Q() = qmin+deltaRange*((*fi).Q() - minmax.first)/(minmax.second - minmax.first); +} + +/** Assign to each face of the mesh a constant quality value. Useful for initialization. +*/ +static void FaceConstant(MeshType &m, float q) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).Q()=q; +} + + +static void VertexFromGaussianCurvature(MeshType &m) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).Q() = (*vi).Kg(); +} + +static void VertexFromMeanCurvature(MeshType &m) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).Q() = (*vi).Kh(); +} + +/* + * Absolute Curvature + * + * 2|H| if K >= 0 + * |k1| + |k2| = < + * 2 * sqrt(|H|^2-K) otherwise + * + * defs and formulas taken from + * + * Improved curvature estimation for watershed segmentation of 3-dimensional meshes + * S Pulla, A Razdan, G Farin - Arizona State University, Tech. Rep, 2001 + * and from + * Optimizing 3D triangulations using discrete curvature analysis + * N Dyn, K Hormann, SJ Kim, D Levin - Mathematical Methods for Curves and Surfaces: Oslo, 2000 + */ + +static void VertexFromAbsoluteCurvature(MeshType &m) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + { + if((*vi).Kg() >= 0) + (*vi).Q() = math::Abs( 2*(*vi).Kh() ); + else + (*vi).Q() = 2*math::Sqrt(math::Abs( (*vi).Kh()*(*vi).Kh() - (*vi).Kg())); + } +} + +/* + * RMS Curvature = sqrt(4H^2-2K) + * def and formula taken from + * + * Improved curvature estimation for watershed segmentation of 3-dimensional meshes + * S Pulla, A Razdan, G Farin - Arizona State University, Tech. Rep, 2001 + */ +static void VertexFromRMSCurvature(MeshType &m) +{ + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) + (*vi).Q() = math::Sqrt(math::Abs( 4*(*vi).Kh()*(*vi).Kh() - 2*(*vi).Kg())); +} + + + +/* + Saturate the vertex quality so that for each vertex the gradient of the quality is lower than the given threshold value (in absolute value) + The saturation is done in a conservative way (quality is always decreased and never increased) + + Note: requires VF adjacency. + */ +static void VertexSaturate(MeshType &m, ScalarType gradientThr=1.0) +{ + UpdateFlags::VertexClearV(m); + std::stack st; + + st.push(&*m.vert.begin()); + + while(!st.empty()) + { + VertexPointer vc = st.top(); // the center + //printf("Stack size %i\n",st.size()); + //printf("Pop elem %i %f\n",st.top() - &*m.vert.begin(), st.top()->Q()); + st.pop(); + vc->SetV(); + std::vector star; + typename std::vector::iterator vvi; + face::VVStarVF(vc,star); + for(vvi=star.begin();vvi!=star.end();++vvi ) + { + float &qi = (*vvi)->Q(); + float distGeom = Distance((*vvi)->cP(),vc->cP()) / gradientThr; + // Main test if the quality varies more than the geometric displacement we have to lower something. + if( distGeom < fabs(qi - vc->Q())) + { + // center = 0 other=10 -> other = + // center = 10 other=0 + if(vc->Q() > qi) // first case: the center of the star has to be lowered (and re-inserted in the queue). + { + //printf("Reinserting center %i \n",vc - &*m.vert.begin()); + vc->Q() = qi+distGeom-0.00001f; + assert( distGeom > fabs(qi - vc->Q())); + st.push(vc); + break; + } + else + { + // second case: you have to lower qi, the vertex under examination. + assert( distGeom < fabs(qi - vc->Q())); + assert(vc->Q() < qi); + float newQi = vc->Q() + distGeom -0.00001f; + assert(newQi <= qi); + assert(vc->Q() < newQi); + assert( distGeom > fabs(newQi - vc->Q()) ); +// printf("distGeom %f, qi %f, vc->Q() %f, fabs(qi - vc->Q()) %f\n",distGeom,qi,vc->Q(),fabs(qi - vc->Q())); + qi = newQi; + (*vvi)->ClearV(); + } + } + if(!(*vvi)->IsV()) + { + st.push( *vvi); +// printf("Reinserting side %i \n",*vvi - &*m.vert.begin()); + (*vvi)->SetV(); + } + } + } + } + + +}; //end class +} // end namespace +} // end namespace +#endif diff --git a/vcg/complex/algorithms/update/selection.h b/vcg/complex/algorithms/update/selection.h new file mode 100644 index 00000000..fc552e9b --- /dev/null +++ b/vcg/complex/algorithms/update/selection.h @@ -0,0 +1,446 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_SELECTION +#define __VCG_TRI_UPDATE_SELECTION + +#include +#include + +namespace vcg { +namespace tri { +/// \ingroup trimesh +/// \brief A stack for saving and restoring selection. +/** + This class is used to save the current selection onto a stack for later use. + \todo it should be generalized to other attributes with a templated approach. +*/ +template +class SelectionStack +{ + typedef typename ComputeMeshType::template PerVertexAttributeHandle< bool > vsHandle; + typedef typename ComputeMeshType::template PerFaceAttributeHandle< bool > fsHandle; + +public: + SelectionStack(ComputeMeshType &m) + { + _m=&m; + } + + bool push() + { + vsHandle vsH = Allocator::template AddPerVertexAttribute< bool >(*_m); + fsHandle fsH = Allocator::template AddPerFaceAttribute< bool > (*_m); + typename ComputeMeshType::VertexIterator vi; + for(vi = _m->vert.begin(); vi != _m->vert.end(); ++vi) + if( !(*vi).IsD() ) vsH[*vi] = (*vi).IsS() ; + + typename ComputeMeshType::FaceIterator fi; + for(fi = _m->face.begin(); fi != _m->face.end(); ++fi) + if( !(*fi).IsD() ) fsH[*fi] = (*fi).IsS() ; + + vsV.push_back(vsH); + fsV.push_back(fsH); + return true; + } + + bool pop() + { + if(vsV.empty()) return false; + vsHandle vsH = vsV.back(); + fsHandle fsH = fsV.back(); + if(! (Allocator::template IsValidHandle(*_m, vsH))) return false; + + typename ComputeMeshType::VertexIterator vi; + for(vi = _m->vert.begin(); vi != _m->vert.end(); ++vi) + if( !(*vi).IsD() ) + if(vsH[*vi]) (*vi).SetS() ; + else (*vi).ClearS() ; + + typename ComputeMeshType::FaceIterator fi; + for(fi = _m->face.begin(); fi != _m->face.end(); ++fi) + if( !(*fi).IsD() ) + if(fsH[*fi]) (*fi).SetS() ; + else (*fi).ClearS() ; + + Allocator::template DeletePerVertexAttribute(*_m,vsH); + Allocator::template DeletePerFaceAttribute(*_m,fsH); + vsV.pop_back(); + fsV.pop_back(); + return true; + } + +private: + ComputeMeshType *_m; + std::vector vsV; + std::vector fsV; + +}; + +/// \ingroup trimesh + +/// \headerfile selection.h vcg/complex/trimesh/update/selection.h + +/// \brief Management, updating and computation of per-vertex and per-face normals. +/** +This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh. +*/ + +template +class UpdateSelection +{ + +public: +typedef ComputeMeshType MeshType; +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FacePointer FacePointer; +typedef typename MeshType::FaceIterator FaceIterator; +typedef typename vcg::Box3 Box3Type; + +static size_t AllVertex(MeshType &m) +{ + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if( !(*vi).IsD() ) (*vi).SetS(); + return m.vn; +} + +static size_t AllFace(MeshType &m) +{ + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() ) (*fi).SetS(); + return m.fn; +} + +static size_t ClearVertex(MeshType &m) +{ + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if( !(*vi).IsD() ) (*vi).ClearS(); + return 0; +} + +static size_t ClearFace(MeshType &m) +{ + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() ) (*fi).ClearS(); + return 0; +} + +static void Clear(MeshType &m) +{ + ClearVertex(m); + ClearFace(m); +} + +static size_t CountFace(MeshType &m) +{ + size_t selCnt=0; + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS()) ++selCnt; + return selCnt; +} + +static size_t CountVertex(MeshType &m) +{ + size_t selCnt=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD() && (*vi).IsS()) ++selCnt; + return selCnt; +} + +static size_t InvertFace(MeshType &m) +{ + size_t selCnt=0; + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + if((*fi).IsS()) (*fi).ClearS(); + else { + (*fi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +static size_t InvertVertex(MeshType &m) +{ + size_t selCnt=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) + { + if((*vi).IsS()) (*vi).ClearS(); + else { + (*vi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +/// \brief Select all the vertices that are touched by at least a single selected faces +static size_t VertexFromFaceLoose(MeshType &m) +{ + size_t selCnt=0; + ClearVertex(m); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() && (*fi).IsS()) + { + if( !(*fi).V(0)->IsS()) { (*fi).V(0)->SetS(); ++selCnt; } + if( !(*fi).V(1)->IsS()) { (*fi).V(1)->SetS(); ++selCnt; } + if( !(*fi).V(2)->IsS()) { (*fi).V(2)->SetS(); ++selCnt; } + } + return selCnt; +} + +/// \brief Select ONLY the vertices that are touched ONLY by selected faces +/** In other words all the vertices having all the faces incident on them selected. + \warning Isolated vertices will not selected. +*/ +static size_t VertexFromFaceStrict(MeshType &m) +{ + VertexFromFaceLoose(m); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() && !(*fi).IsS()) + { + (*fi).V(0)->ClearS(); + (*fi).V(1)->ClearS(); + (*fi).V(2)->ClearS(); + } + return CountVertex(m); +} + +/// \brief Select ONLY the faces with ALL the vertices selected +static size_t FaceFromVertexStrict(MeshType &m) +{ + size_t selCnt=0; + ClearFace(m); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD()) + { + if((*fi).V(0)->IsS() && (*fi).V(1)->IsS() && (*fi).V(2)->IsS()) + { + (*fi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +/// \brief Select all the faces with at least one selected vertex +static size_t FaceFromVertexLoose(MeshType &m) +{ + size_t selCnt=0; + ClearFace(m); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() && !(*fi).IsS()) + { + if((*fi).V(0)->IsS() || (*fi).V(1)->IsS() || (*fi).V(2)->IsS()) + { + (*fi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +static size_t VertexFromBorderFlag(MeshType &m) +{ + size_t selCnt=0; + ClearVertex(m); + VertexIterator vi; + for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if( !(*vi).IsD() ) + { + if((*vi).IsB() ) + { + (*vi).SetS(); + ++selCnt; + } + } + return selCnt; +} + + +static size_t FaceFromBorderFlag(MeshType &m) +{ + size_t selCnt=0; + ClearFace(m); + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() ) + { + if((*fi).IsB(0) || (*fi).IsB(1) || (*fi).IsB(2)) + { + (*fi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +/// \brief This function select the faces that have an edge outside the given range. +static size_t FaceOutOfRangeEdge(MeshType &m, ScalarType MinEdgeThr=0, ScalarType MaxEdgeThr=(std::numeric_limits::max)()) +{ + FaceIterator fi; + size_t count_fd = 0; + MinEdgeThr=MinEdgeThr*MinEdgeThr; + MaxEdgeThr=MaxEdgeThr*MaxEdgeThr; + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + for(unsigned int i=0;i<3;++i) + { + const ScalarType squaredEdge=SquaredDistance((*fi).V0(i)->cP(),(*fi).V1(i)->cP()); + if((squaredEdge<=MinEdgeThr) || (squaredEdge>=MaxEdgeThr) ) + { + count_fd++; + (*fi).SetS(); + break; // skip the rest of the edges of the tri + } + } + } + return count_fd; +} + +/// \brief This function expand current selection to cover the whole connected component. +static size_t FaceConnectedFF(MeshType &m) +{ + // it also assumes that the FF adjacency is well computed. + assert (HasFFAdjacency(m)); + UpdateFlags::FaceClearV(m); + + std::deque visitStack; + size_t selCnt=0; + FaceIterator fi; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() && (*fi).IsS() && !(*fi).IsV() ) + visitStack.push_back(&*fi); + + while(!visitStack.empty()) + { + FacePointer fp = visitStack.front(); + visitStack.pop_front(); + assert(!fp->IsV()); + fp->SetV(); + for(int i=0;i<3;++i) { + FacePointer ff = fp->FFp(i); + if(! ff->IsS()) + { + ff->SetS(); + ++selCnt; + visitStack.push_back(ff); + assert(!ff->IsV()); + } + } + } + return selCnt; +} +/// \brief Select ONLY the faces whose quality is in the specified closed interval. +static size_t FaceFromQualityRange(MeshType &m,float minq, float maxq) +{ + size_t selCnt=0; + ClearFace(m); + FaceIterator fi; + assert(HasPerFaceQuality(m)); + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + if( (*fi).Q()>=minq && (*fi).Q()<=maxq ) + { + (*fi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +/// \brief Select ONLY the vertices whose quality is in the specified closed interval. +static size_t VertexFromQualityRange(MeshType &m,float minq, float maxq) +{ + size_t selCnt=0; + ClearVertex(m); + VertexIterator vi; + assert(HasPerVertexQuality(m)); + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if(!(*vi).IsD()) + { + if( (*vi).Q()>=minq && (*vi).Q()<=maxq ) + { + (*vi).SetS(); + ++selCnt; + } + } + return selCnt; +} + +static int VertexInBox( MeshType & m, const Box3Type &bb) +{ + int selCnt=0; + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) + { + if(bb.IsIn((*vi).cP()) ) { + (*vi).SetS(); + ++selCnt; + } + } + return selCnt; +} + + +void VertexNonManifoldEdges(MeshType &m) +{ + assert(HasFFTopology(m)); + + VertexClear(m); + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;++i) + if(!IsManifold(*fi,i)){ + (*fi).V0(i)->SetS(); + (*fi).V1(i)->SetS(); + } + } +} + +}; // end class + +} // End namespace +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/update/texture.h b/vcg/complex/algorithms/update/texture.h new file mode 100644 index 00000000..b5b1d20f --- /dev/null +++ b/vcg/complex/algorithms/update/texture.h @@ -0,0 +1,105 @@ +/**************************************************************************** +* 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: position.h,v $ +****************************************************************************/ + +#ifndef __VCG_TRI_UPDATE_TEXTURE +#define __VCG_TRI_UPDATE_TEXTURE + +//#include + +namespace vcg { +namespace tri { + +/// \ingroup trimesh + +/// \headerfile texture.h vcg/complex/trimesh/update/texture.h + +/// \brief This class is used to update vertex position according to a transformation matrix. +template +class UpdateTexture +{ + +public: +typedef ComputeMeshType MeshType; +typedef typename MeshType::ScalarType ScalarType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FacePointer FacePointer; +typedef typename MeshType::FaceIterator FaceIterator; + +static void WedgeTexFromPlanar(ComputeMeshType &m, Plane3 &pl) +{ + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + + } +} + +static void WedgeTexFromCamera(ComputeMeshType &m, Plane3 &pl) +{ + +} + + +/// Currently texture coords are kept for ALL the triangles of a mesh. The texture id is stored with each face. +/// if a given face should not have tex coord it has the default -1 value for texture ID. +/// This function will add an new fake texture, add that to the list of textures and change all the -1 id to that value. +static void WedgeTexRemoveNull(ComputeMeshType &m, const std::string &texturename) +{ + bool found=false; + + FaceIterator fi; + // first loop lets check that there are -1 indexed textured face + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) if((*fi).WT(0).N()==-1) found = true; + + if(!found) return; + m.textures.push_back(texturename); + + int nullId=m.textures.size()-1; + + for(fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) if((*fi).WT(0).N()==-1) + { + (*fi).WT(0).N() = nullId; + (*fi).WT(1).N() = nullId; + (*fi).WT(2).N() = nullId; + } + +} + +}; // end class + +} // End namespace +} // End namespace + + +#endif diff --git a/vcg/complex/algorithms/update/topology.h b/vcg/complex/algorithms/update/topology.h new file mode 100644 index 00000000..ff805e37 --- /dev/null +++ b/vcg/complex/algorithms/update/topology.h @@ -0,0 +1,489 @@ +/**************************************************************************** +* 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 $ +Revision 1.20 2008/04/04 10:27:34 cignoni +minor changes to the topology correctness checks + +Revision 1.19 2007/05/29 00:07:06 ponchio +VFi++ -> ++VFi + +Revision 1.18 2006/02/27 19:26:14 spinelli +minor bug in Face-Face topology loop fixed + +Revision 1.17 2006/02/27 11:56:48 spinelli +minor bug in Face-Face topology loop fixed + +Revision 1.16 2005/11/10 15:36:42 cignoni +Added clarifying comment in an assert + +Revision 1.15 2004/10/20 07:33:10 cignoni +removed FaceBorderFlags (already present in update/flags.h) + +Revision 1.14 2004/10/18 17:10:22 ganovelli +added ::FaceBorderFLags + +Revision 1.13 2004/10/01 15:58:00 ponchio +Added include + +Revision 1.12 2004/09/09 13:02:12 ponchio +Linux compatible path in #include + +Revision 1.11 2004/08/07 16:18:20 pietroni +addet testFFTopology and testVFTopology functions used to test the rispective topology.... + +Revision 1.10 2004/07/15 11:35:08 ganovelli +Vfb to VFp + +Revision 1.9 2004/07/15 00:13:39 cignoni +Better doxigen documentation + +Revision 1.8 2004/06/02 16:42:44 ganovelli +typename for gcc compilation + +Revision 1.7 2004/06/02 16:28:22 ganovelli +minor changes (swap =>> math::Swap) + +Revision 1.6 2004/05/10 15:23:43 cignoni +Changed a FV -> VF in VertexFace topology computation + +Revision 1.5 2004/05/06 15:24:38 pietroni +changed names to topology functions + +Revision 1.4 2004/03/31 14:44:43 cignoni +Added Vertex-Face Topology + +Revision 1.3 2004/03/12 15:22:19 cignoni +Written some documentation and added to the trimes doxygen module + +Revision 1.2 2004/03/05 21:49:21 cignoni +First working version for face face + +Revision 1.1 2004/03/04 00:53:24 cignoni +Initial commit + + +****************************************************************************/ +#ifndef __VCG_TRI_UPDATE_TOPOLOGY +#define __VCG_TRI_UPDATE_TOPOLOGY +#include +#include +#include +#include +namespace vcg { +namespace tri { +/// \ingroup trimesh + +/// \headerfile topology.h vcg/complex/trimesh/update/topology.h + +/// \brief Generation of per-vertex and per-face topological information. + +template +class UpdateTopology +{ + +public: +typedef UpdateMeshType MeshType; +typedef typename MeshType::VertexType VertexType; +typedef typename MeshType::VertexPointer VertexPointer; +typedef typename MeshType::VertexIterator VertexIterator; +typedef typename MeshType::FaceType FaceType; +typedef typename MeshType::FacePointer FacePointer; +typedef typename MeshType::FaceIterator FaceIterator; + + +/// \headerfile topology.h vcg/complex/trimesh/update/topology.h + +/// \brief Auxiliairy data structure for computing face face adjacency information. +/** +It identifies and edge storing two vertex pointer and a face pointer where it belong. +*/ + +class PEdge +{ +public: + + VertexPointer v[2]; // the two Vertex pointer are ordered! + FacePointer f; // the face where this edge belong + int z; // index in [0..2] of the edge of the face + + PEdge() {} + +void Set( FacePointer pf, const int nz ) +{ + assert(pf!=0); + assert(nz>=0); + assert(nzVN()); + + v[0] = pf->V(nz); + v[1] = pf->V(pf->Next(nz)); + assert(v[0] != v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes) + + if( v[0] > v[1] ) math::Swap(v[0],v[1]); + f = pf; + z = nz; +} + +inline bool operator < ( const PEdge & pe ) const +{ + if( v[0]pe.v[0] ) return false; + else return v[1] < pe.v[1]; +} + +inline bool operator == ( const PEdge & pe ) const +{ + return v[0]==pe.v[0] && v[1]==pe.v[1]; +} + +}; + +// Fill a vector with all the edges of the mesh. +// each edge is stored in the vector the number of times that it appears in the mesh, with the referring face. +// optionally it can skip the faux edges (to retrieve only the real edges of a triangulated polygonal mesh) + +static void FillEdgeVector(MeshType &m, std::vector &e, bool includeFauxEdge=true) +{ + FaceIterator pf; + typename std::vector::iterator p; + + // Alloco il vettore ausiliario + //e.resize(m.fn*3); + FaceIterator fi; + int n_edges = 0; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN(); + e.resize(n_edges); + + p = e.begin(); + for(pf=m.face.begin();pf!=m.face.end();++pf) + if( ! (*pf).IsD() ) + for(int j=0;j<(*pf).VN();++j) + if(includeFauxEdge || !(*pf).IsF(j)) + { + (*p).Set(&(*pf),j); + ++p; + } + + if(includeFauxEdge) assert(p==e.end()); + else e.resize(p-e.begin()); +} + +static void FillUniqueEdgeVector(MeshType &m, std::vector &Edges, bool includeFauxEdge=true) +{ + FillEdgeVector(m,Edges,includeFauxEdge); + sort(Edges.begin(), Edges.end()); // Lo ordino per vertici + + typename std::vector< PEdge>::iterator newEnd = std::unique(Edges.begin(), Edges.end()); + typename std::vector::iterator ei; + + Edges.resize(newEnd-Edges.begin()); +} + +/// \brief Update the Face-Face topological relation by allowing to retrieve for each face what other faces shares their edges. +static void FaceFace(MeshType &m) +{ + assert(HasFFAdjacency(m)); + if( m.fn == 0 ) return; + + std::vector e; + FillEdgeVector(m,e); + sort(e.begin(), e.end()); // Lo ordino per vertici + + int ne = 0; // Numero di edge reali + + typename std::vector::iterator pe,ps; + ps = e.begin();pe=e.begin(); + //for(ps = e.begin(),pe=e.begin();pe<=e.end();++pe) // Scansione vettore ausiliario + do + { + if( pe==e.end() || !(*pe == *ps) ) // Trovo blocco di edge uguali + { + typename std::vector::iterator q,q_next; + for (q=ps;q=0); + //assert((*q).z< 3); + q_next = q; + ++q_next; + assert((*q_next).z>=0); + assert((*q_next).z< (*q_next).f->VN()); + (*q).f->FFp(q->z) = (*q_next).f; // Collegamento in lista delle facce + (*q).f->FFi(q->z) = (*q_next).z; + } + assert((*q).z>=0); + assert((*q).z< (*q).f->VN()); + (*q).f->FFp((*q).z) = ps->f; + (*q).f->FFi((*q).z) = ps->z; + ps = pe; + ++ne; // Aggiorno il numero di edge + } + if(pe==e.end()) break; + ++pe; + } while(true); +} + + +/// \brief Update the Vertex-Face topological relation. +/** +The function allows to retrieve for each vertex the list of faces sharing this vertex. +*/ + +static void VertexFace(MeshType &m) +{ + if(!m.HasVFTopology()) return; + + VertexIterator vi; + FaceIterator fi; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + (*vi).VFp() = 0; + (*vi).VFi() = 0; + } + + for(fi=m.face.begin();fi!=m.face.end();++fi) + if( ! (*fi).IsD() ) + { + for(int j=0;j<(*fi).VN();++j) + { + (*fi).VFp(j) = (*fi).V(j)->VFp(); + (*fi).VFi(j) = (*fi).V(j)->VFi(); + (*fi).V(j)->VFp() = &(*fi); + (*fi).V(j)->VFi() = j; + } + } +} + + +/// \headerfile topology.h vcg/complex/trimesh/update/topology.h + +/// \brief Auxiliairy data structure for computing face face adjacency information. +/** +It identifies and edge storing two vertex pointer and a face pointer where it belong. +*/ + +class PEdgeTex +{ +public: + + typename FaceType::TexCoordType v[2]; // the two Vertex pointer are ordered! + FacePointer f; // the face where this edge belong + int z; // index in [0..2] of the edge of the face + + PEdgeTex() {} + +void Set( FacePointer pf, const int nz ) +{ + assert(pf!=0); + assert(nz>=0); + assert(nz<3); + + v[0] = pf->WT(nz); + v[1] = pf->WT(pf->Next(nz)); + assert(v[0] != v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes) + + if( v[1] < v[0] ) std::swap(v[0],v[1]); + f = pf; + z = nz; +} + +inline bool operator < ( const PEdgeTex & pe ) const +{ + if( v[0] e; + FaceIterator pf; + typename std::vector::iterator p; + + if( m.fn == 0 ) return; + +// e.resize(m.fn*3); // Alloco il vettore ausiliario + FaceIterator fi; + int n_edges = 0; + for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN(); + e.resize(n_edges); + + p = e.begin(); + for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce + if( ! (*pf).IsD() ) + for(int j=0;j<(*pf).VN();++j) + { + if( (*pf).WT(j) != (*pf).WT((*pf).Next(j))) + { + (*p).Set(&(*pf),j); + ++p; + } + } + + e.resize(p-e.begin()); // remove from the end of the edge vector the unitiailized ones + assert(p==e.end()); + sort(e.begin(), e.end()); + + int ne = 0; // number of real edges + typename std::vector::iterator pe,ps; + ps = e.begin();pe=e.begin(); + //for(ps = e.begin(),pe=e.begin();pe<=e.end();++pe) // Scansione vettore ausiliario + do + { + if( pe==e.end() || (*pe) != (*ps) ) // Trovo blocco di edge uguali + { + typename std::vector::iterator q,q_next; + for (q=ps;q=0); + assert((*q).z< 3); + q_next = q; + ++q_next; + assert((*q_next).z>=0); + assert((*q_next).z< (*q_next).f->VN()); + (*q).f->FFp(q->z) = (*q_next).f; // Collegamento in lista delle facce + (*q).f->FFi(q->z) = (*q_next).z; + } + assert((*q).z>=0); + assert((*q).z< (*q).f->VN()); + (*q).f->FFp((*q).z) = ps->f; + (*q).f->FFi((*q).z) = ps->z; + ps = pe; + ++ne; // Aggiorno il numero di edge + } + if(pe==e.end()) break; + ++pe; + } while(true); +} + + + + + +/// \brief Test correctness of VFtopology +static void TestVertexFace(MeshType &m) +{ + SimpleTempData numVertex(m.vert,0); + + if(!m.HasVFTopology()) return; + + FaceIterator fi; + for(fi=m.face.begin();fi!=m.face.end();++fi) + { + if (!(*fi).IsD()) + { + numVertex[(*fi).V0(0)]++; + numVertex[(*fi).V1(0)]++; + numVertex[(*fi).V2(0)]++; + } + } + + VertexIterator vi; + vcg::face::VFIterator VFi; + + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + { + if (!vi->IsD()) + if(vi->VFp()!=0) // unreferenced vertices MUST have VF == 0; + { + int num=0; + assert(vi->VFp() >= &*m.face.begin()); + assert(vi->VFp() <= &m.face.back()); + VFi.f=vi->VFp(); + VFi.z=vi->VFi(); + while (!VFi.End()) + { + num++; + assert(!VFi.F()->IsD()); + assert((VFi.F()->V(VFi.I()))==&(*vi)); + ++VFi; + } + int num1=numVertex[&(*vi)]; + assert(num==num1); + /*assert(num>1);*/ + } + } +} + +/// \brief Test correctness of FFtopology (only for 2Manifold Meshes!) +static void TestFaceFace(MeshType &m) +{ + if(!m.HasFFTopology()) return; + + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + if (!fi->IsD()) + { + for (int i=0;i<(*fi).VN();i++) + { + FaceType *ffpi=fi->FFp(i); + int e=fi->FFi(i); + //invariant property of FF topology for two manifold meshes + assert(ffpi->FFp(e) == &(*fi)); + assert(ffpi->FFi(e) == i); + + // Test that the two faces shares the same edge + // Vertices of the i-th edges of the first face + VertexPointer v0i= fi->V0(i); + VertexPointer v1i= fi->V1(i); + // Vertices of the corresponding edge on the other face + VertexPointer ffv0i= ffpi->V0(e); + VertexPointer ffv1i= ffpi->V1(e); + + assert( (ffv0i==v0i) || (ffv0i==v1i) ); + assert( (ffv1i==v0i) || (ffv1i==v1i) ); + } + + } + } +} + +}; // end class + +} // End namespace +} // End namespace + + +#endif