Ongoing Rearrangement of filepath

delete old trimesh content
This commit is contained in:
ganovelli 2011-04-01 17:16:29 +00:00
parent 2506364fc4
commit f4a5512500
28 changed files with 0 additions and 16627 deletions

View File

@ -1,369 +0,0 @@
#ifndef VCG_TRI_ATTRIBUTE_SEAM_H
#define VCG_TRI_ATTRIBUTE_SEAM_H
#include <vector>
#include <vcg/complex/trimesh/allocate.h>
/*
// 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 <typename src_trimesh_t, typename dst_trimesh_t>
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 <typename dst_trimesh_t>
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 <typename src_trimesh_t, typename extract_wedge_attribs_t, typename compare_vertex_attribs_t>
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_trimesh_t> src_mesh_allocator_t;
typedef typename src_mesh_allocator_t :: template PointerUpdater<typename src_trimesh_t::VertexPointer> 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<int> 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 <typename src_trimesh_t, typename dst_trimesh_t, typename extract_wedge_attribs_t, typename compare_vertex_attribs_t, typename copy_vertex_t>
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_trimesh_t> 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<typename dst_trimesh_t::VertexPointer> dst_pointer_updater_t;
if (reinterpret_cast<const void *>(&src) == reinterpret_cast<const void *>(&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<int> 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<dst_vertex_t *>(idx);
}
fi++;
}
{
std::vector<int> 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<const int>(vf.V(0));
vf.V(1) = vstart + reinterpret_cast<const int>(vf.V(1));
vf.V(2) = vstart + reinterpret_cast<const int>(vf.V(2));
}
dst_mesh_allocator_t::DeleteVertex(dst, *vtx);
return true;
}
};
} // end namespace tri
} // end namespace vcg
#endif // VCG_TRI_ATTRIBUTE_SEAM_H

View File

@ -1,677 +0,0 @@
#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 <vcg/space/point3.h>
#include <vcg/space/point4.h>
#include <vcg/space/line3.h>
#include <vcg/space/plane3.h>
#include <vcg/math/base.h>
#include <vcg/math/point_matching.h>
#include <vcg/math/matrix44.h>
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/complex/trimesh/closest.h>
#include <vcg/complex/trimesh/update/bounding.h>
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/face/base.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/stat.h>
#include <wrap/io_trimesh/export_ply.h>
// note: temporary (callback.h should be moved inside vcg)
typedef bool AACb( const int pos,const char * str );
namespace vcg{
namespace tri{
template <class MeshType>
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<PVertex>::template AsVertexType,
vcg::Use<PFace >::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<PVertex>, std::vector<PFace> > {};
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<ScalarType> > FourPoints;
typedef vcg::GridStaticPtr<typename PMesh::VertexType, ScalarType > 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<int,int>{
Couple(const int & i, const int & j, float d):std::pair<int,int>(i,j),dist(d){}
Couple(float d):std::pair<int,int>(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<ScalarType>_T):p(_p),T(_T){}
FourPoints p;
vcg::Matrix44<ScalarType> 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<int> 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<FourPoints> bases; // used bases
ScalarType side; // side
std::vector<VertexType*> ExtB[4]; // selection of vertices "close" to the four point
std::vector<VertexType*> subsetP; // random selection on P
ScalarType radius;
ScalarType Bangle;
std::vector<Couple > R1/*,R2*/;
ScalarType r1,r2;
// class for the point 'ei'
struct EPoint{
EPoint(vcg::Point3<ScalarType> _p, int _i):pos(_p),pi(_i){}
vcg::Point3<ScalarType> pos;
int pi; //index to R[1|2]
void GetBBox(vcg::Box3<ScalarType> & b){b.Add(pos);}
};
GridType *ugrid; // griglia
vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridQ;
vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > 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<ScalarType> & 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<vcg::Matrix44f> 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 <class MeshType>
void
FourPCS<MeshType>:: 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<MeshType>::ComputeMeshArea(*P);
// float areaQ = vcg::tri::Stat<MeshType>::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<ScalarType > dists;
std::vector<VertexType* > ress;
vcg::tri::GetKClosestVertex<
MeshType,
vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType>,
std::vector<VertexType*>,
std::vector<ScalarType>,
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 <class MeshType>
bool
FourPCS<MeshType>::SelectCoplanarBase(){
vcg::tri::UpdateBounding<MeshType>::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<float>::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<typename MeshType::VertexType*> closests;
std::vector<ScalarType> distances;
std::vector<CoordType> points;
vcg::tri::GetInSphereVertex<
MeshType,
vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
std::vector<typename MeshType::VertexType*>,
std::vector<ScalarType>,
std::vector<CoordType>
>(*P,ugridP,B4,radius,closests,distances,points);
if(closests.empty())
return false;
best = -1; bestv=std::numeric_limits<float>::max();
for(i = 0; i <closests.size(); ++i){
ScalarType angle = fabs((closests[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<ScalarType > dists;
for(int i = 0 ; i< 4; ++i){
vcg::tri::GetKClosestVertex<
MeshType,
vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
std::vector<VertexType*>,
std::vector<ScalarType>,
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 <class MeshType>
bool
FourPCS<MeshType>::IsTransfCongruent(FourPoints fp,vcg::Matrix44<ScalarType> & mat, float & trerr){
std::vector<vcg::Point3<ScalarType> > fix;
std::vector<vcg::Point3<ScalarType> > 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<ScalarType> 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<ScalarType>::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 <class MeshType>
void
FourPCS<MeshType>::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 <class MeshType>
bool
FourPCS<MeshType>::FindCongruent() { // of base B, on Q, with approximation delta
bool done = false;
std::vector<EPoint> 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<Couple>::iterator bR1,eR1,bR2,eR2,ite,cite;
bR1 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d1-prs.delta*2.0));
eR1 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d1+prs.delta*2.0));
bR2 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d2-prs.delta*2.0));
eR2 = std::lower_bound<typename std::vector<Couple>::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<PMesh>::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<int> id = vcg::tri::Allocator<PMesh>::template AddPerVertexAttribute<int>(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<PMesh>::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<typename PMesh::VertexType*> closests;
std::vector<ScalarType> distances;
std::vector<CoordType> points;
// for each point in R2inv get all the points in R1 closer than prs.delta
vcg::Matrix44<ScalarType> 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<PMesh,GridType,std::vector<typename PMesh::VertexType*> >
(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<PMesh>::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 <class MeshType>
int FourPCS<MeshType>::EvaluateSample(CandiType & fp, CoordType & tp, CoordType & np, const float & angle){
VertexType* v;
ScalarType dist ;
radius = prs.delta;
tp = fp.T * tp;
vcg::Point4<ScalarType> np4;
np4 = fp.T * vcg::Point4<ScalarType>(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<typename MeshType::VertexType, ScalarType >
// >(*Q,ugridQ,tp,radius, dist );
typename MeshType::VertexType vq;
vq.P() = tp;
vq.N() = np;
v = vcg::tri::GetClosestVertexNormal<
MeshType,
vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >
>(*Q,ugridQ,vq,radius, dist );
if(v!=0)
if( v->N().dot(np) -angle >0) return 1; else return -1;
}
template <class MeshType>
void
FourPCS<MeshType>::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 <class MeshType>
void
FourPCS<MeshType>::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 <class MeshType>
bool
FourPCS<MeshType>:: 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<float>::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

View File

@ -1,788 +0,0 @@
#include <vcg/complex/trimesh/bitquad_support.h>
#include <vcg/complex/trimesh/allocate.h>
/** 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 _MeshType,
class Interpolator = GeometricInterpolator<typename _MeshType::VertexType> >
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<MeshType> BQ; // static class to make basic quad operations
// helper function:
// given a triangle, merge it with its best neightboord to form a quad
template <bool override>
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 <bool override> // override previous decisions?
static void MakeDominantPass(MeshType &m){
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
selectBestDiag<override>(&(*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<typename MeshType::FaceType *, typename MeshType::VertexType *> 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<MeshType>::AddFaces(m,1);
if(newVert==0) {
newVert=&*tri::Allocator<MeshType>::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<MeshType>::AddVertices(m,1);
(*vnew).P()=((*fi).P0(k)+(*fi).P1(k))/2.0;
FaceIterator fnew=tri::Allocator<MeshType>::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<MeshType>::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<MeshType>::AddFaces(m,ef);
VertexIterator nvi = tri::Allocator<MeshType>::AddVertices(m,ev);
tri::UpdateFlags<MeshType>::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<MeshType>::AddFaces(m,nsplit);
VertexIterator nvi = tri::Allocator<MeshType>::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<FaceType*> stack;
int stackPos=0;
stack.push_back(startF);
while ( stackPos<int(stack.size())) {
FaceType *f = stack[stackPos++];
for (int k=0; k<3; k++) {
assert(FFCorrectness(*f,k));
FaceType *fk = f->FFp(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 (scoreABC<bestScore) {
bestScore = scoreABC;
edge = k;
mustDoFlip = !(scoreB == scoreABC || scoreC == scoreABC);
}
}
if (done) break;
// use that edge to proceed
if (mustDoFlip) {
BQ::FlipDiag( *(tb->FFp(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<false> (m);
if (level>0) MakeDominantPass<true> (m);
if (level>1) MakeDominantPass<true> (m);
if (level>0) MakeDominantPass<false> (m);
}
};
}} // end namespace vcg::tri
#endif

View File

@ -1,404 +0,0 @@
#ifndef _BITQUAD_OPTIMIZATION
#define _BITQUAD_OPTIMIZATION
namespace vcg{namespace tri{
template <class BQ>
class BitQuadOptimization{
typedef typename BQ::MeshType MeshType;
typedef typename BQ::Pos Pos;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FaceType* FaceTypeP;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::VertexIterator VertexIterator;
//typedef BitQuad<MeshType> BQ; // static class to make basic quad operatins
public:
// helper function: mark a quadface, setting Q at 0, and neight at .75, 0.5...
static void MarkFace(FaceType* f, MeshType &m){
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
fi->Q() = 1;
}
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) f->FFp(i)->FFp(j)->Q() = 0.75;
}
for (int i=0; i<3; i++) {
f->FFp(i)->Q() = 0.50;
}
f->Q() = 0;
}
// helper function: mark a quadface, setting Q at 0, and neight at .75, 0.5...
static void MarkVertex(FaceType* f, int wedge, MeshType &m){
VertexType *v = f->V(wedge);
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
if (fi->V0(0)==v || fi->V1(0)==v ||fi->V2(0)==v ) fi->Q() = 0;
// else fi->Q() = 1;
}
}
static bool MarkSmallestEdge(MeshType &m, bool perform)
{
ScalarType min = std::numeric_limits<ScalarType>::max();
FaceType *fa=NULL; int w=0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD())
for (int k=0; k<3; k++) {
FaceType *f=&*fi;
if (f->IsF(k)) continue;
if (f->FFp(k) == f ) continue; // skip borders
ScalarType score;
score = (f->P0(k) - f->P1(k)).Norm();
if (score<min) {
min=score;
fa = f;
w = k;
}
}
if (fa) {
if (perform) {
return BQ::CollapseEdge(*fa,w,m);
} else {
fa->Q()=0.0;
fa->FFp(w)->Q()=0.0;
return true;
}
}
return false;
}
static ScalarType Importance(const CoordType &p){
//return ::proceduralImportance(p);
return 1;
}
// returns: 0 if fail. 1 if edge. 2 if diag.
static int MarkSmallestEdgeOrDiag(MeshType &m, ScalarType edgeMult, bool perform, Pos* affected=NULL)
{
ScalarType min = std::numeric_limits<ScalarType>::max();
FaceType *fa=NULL; int w=0; bool counterDiag = false;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD())
for (int k=0; k<3; k++) {
FaceType *f=&*fi;
if (f->FFp(k) >= f ) continue; // skip borders (==), and do it one per edge
ScalarType score;
score = (f->P0(k) - f->P1(k)).Norm();
ScalarType imp = Importance( (f->P0(k) + f->P1(k))/2 );
score /= imp;
if (!f->IsF(k)) score*=edgeMult; // edges are supposed to be smaller!
if (score<min) {
min=score;
fa = f;
w = k;
counterDiag=false;
}
if (f->IsF(k)) { // for diag faces, test counterdiag too
score = BQ::CounterDiag(f).Norm();
score /= imp;
if (score<min) {
min=score;
fa = f;
w = k;
counterDiag=true;
}
}
}
if (fa) {
if (perform) {
if (fa->IsF(w)) {
if (counterDiag) {
if (BQ::CollapseCounterDiag(*fa, BQ::PosOnDiag(*fa,true), m , affected)) return 2;
} else {
if (BQ::CollapseDiag(*fa, BQ::PosOnDiag(*fa,false), m ,affected)) return 2;
}
} else {
if (BQ::CollapseEdge(*fa,w,m, affected)) return 1;
}
} else {
fa->Q()=0.0;
fa->FFp(w)->Q()=0.0;
if (fa->IsF(w)) return 2; else return 1;
}
}
return 0;
}
static void MarkSmallestDiag(MeshType &m)
{
ScalarType min = std::numeric_limits<ScalarType>::max();
FaceType *fa=NULL;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
FaceType *f=&*fi;
ScalarType score;
score = BQ::Diag(f).Norm();
if (score<min) {
min=score;
fa = f;
}
score = BQ::CounterDiag(f).Norm();
if (score<min) {
min=score;
fa = f;
}
}
if (fa) {
fa->Q()=0.0;
fa->FFp(BQ::FauxIndex(fa))->Q()=0.0;
}
}
static bool IdentifyAndCollapseSmallestDiag(MeshType &m){
ScalarType min = std::numeric_limits<ScalarType>::max();
FaceType *fa=NULL; bool flip;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
FaceType *f=&*fi;
ScalarType score;
score = BQ::Diag(f).Norm();
if (score<min) {
min=score;
fa = f;
flip = false;
}
score = BQ::CounterDiag(f).Norm();
if (score<min) {
min=score;
fa = f;
flip = true;
}
}
if (!fa) return false;
if (BQ::TestAndRemoveDoublet(*fa,0,m)) { return true; }
if (BQ::TestAndRemoveDoublet(*fa,1,m)) { return true; }
if (BQ::TestAndRemoveDoublet(*fa,2,m)) { return true; }
int k = BQ::FauxIndex(fa);
if (BQ::TestAndRemoveDoublet( *fa->FFp(k),(fa->FFi(k)+2)%3, m )) return true;
if (flip) {
if (!BQ::CheckFlipDiag(*fa) ) {
// I can't collapse (why?)
MarkFace(fa,m);
return false;
} else
BQ::CollapseCounterDiag(*fa, BQ::PosOnDiag(*fa,true), m );
}
else {
BQ::CollapseDiag(*fa, BQ::PosOnDiag(*fa,false), m );
}
return true;
}
/*
seeks and removes all doublets (a pair of quads sharing two consecutive edges)
by merging them into a single quad (thus removing one vertex and two tri faces)-
Returns number of removed Doublets
*/
static int RemoveDoublets(MeshType &m, Pos *p=NULL)
{
int res=0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
fi->Q()=1;
for (int k=0; k<3; k++) {
if ( BQ::IsDoublet(*fi,k) ){
res++;
BQ::RemoveDoublet(*fi,k,m,p);
if (fi->IsD()) break; // break wedge circle, if face disappeard
if (p) return res;
}
}
}
return res;
}
/*
marks (Quality=0) and approx. counts profitable vertex rotations
(vertex rotations which make edge shorter
*/
template <bool perform>
static int MarkVertexRotations(MeshType &m, Pos *affected=NULL)
{
int res=0;
for (VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); vi++) if (!vi->IsD()) vi->ClearV();
if (!perform)
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->Q()=1.0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
for (int k=0; k<3; k++) {
if (fi->V(k)->IsV()) continue;
if (BQ::TestVertexRotation(*fi,k)) {
res++;
fi->V(k)->SetV();
if (!perform) {
res++; MarkVertex(&*fi, k, m); //fi->Q()=0;
}
else {
if (BQ::RotateVertex(*fi, k, m, affected)) res++; //fi->Q()=0;
if (affected) return res; // uncomment for only one rotation
}
}
}
}
return res;
}
// mark (and count) all edges that are worth rotating
// if perform == true, actually rotate them
template <bool perform>
static int MarkEdgeRotations(MeshType &m, Pos *p=NULL)
{
int count = 0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) fi->Q()=1;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
//if (count>0) break;
for (int k=0; k<3; k++) {
if (fi->IsF(k)) continue;
if (fi->FFp(k)<= &*fi) continue; // only once per real (non faux) edge, and only for non border ones
int best = BQ::TestEdgeRotation(*fi, k);
if (perform) {
if (best==+1) if (BQ::template RotateEdge< true>(*fi, k, m, p)) count++;
if (best==-1) if (BQ::template RotateEdge<false>(*fi, k, m, p)) count++;
if (p) if (count>0) return count;
}
else {
if (best!=0) { fi->Q()=0; fi->FFp(k)->Q()=0; count++; }
}
}
}
return count;
}
/*
marks (Quality=0) and approx. counts doublets (a pair of quads sharing two consecutive edges)
*/
static int MarkDoublets(MeshType &m)
{
int res=0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
fi->Q()=1;
for (int k=0; k<3; k++) {
if ( BQ::IsDoublet(*fi,k) ){
res++;
if (fi->IsF((k+1)%3)) res++; // counts for a quad
fi->Q()=0;
}
}
}
assert (res%2==0);
return res/4; // return doublet pairs (approx, as a quad could be a part of many pairs)
}
/*
marks (Quality=0) and counts singlets (vertex B in an A-B-A-C quad)
*/
static int MarkSinglets(MeshType &m)
{
int res=0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
fi->Q()=1;
for (int k=0; k<3; k++) {
if ( BQ::IsSinglet(*fi,k) ){
res++;
fi->Q()=0;
}
}
}
assert (res%2==0);
return res/2; // return number of singlet pairs
}
/*
deletes singlets, reutrns number of
*/
static int RemoveSinglets(MeshType &m, Pos *p=NULL)
{
int res=0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
for (int k=0; k<3; k++) {
if ( BQ::IsSinglet(*fi,k) ){
res++;
BQ::RemoveSinglet(*fi,k,m, p);
if (p) return res;
break;
}
}
}
return res; // return singlet pairs (approx, as a quad could be a part of many pairs)
}
/* returns average quad quality, and assigns it to triangle quality
*/
static ScalarType MeasureQuality(MeshType &m)
{
assert(MeshType::HasPerFaceFlags());
ScalarType res = 0;
int div = 0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
if (fi->IsAnyF()) {
ScalarType q = BQ::quadQuality( &*fi, BQ::FauxIndex(&*fi) );
if (MeshType::HasPerFaceQuality()) fi->Q() = q;
res += q;
div++;
}
}
if (!div) return 0; else return res / div;
}
};
}} // end namespace vcg::tri
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,160 +0,0 @@
/****************************************************************************
* 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 2007/07/31 12:31:34 ganovelli
added
****************************************************************************/
#ifndef __VCG_TETRA_TRI_CONVERTER
#define __VCG_TETRA_TRI_CONVERTER
#include <map>
#include <vector>
#include <vcg/space/tetra3.h>
#include <vcg/complex/tetramesh/allocate.h>
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 <class TetraContainer, class TriangleMeshType>
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();ti<tetra.end();ti++)
{
if (!(ti->IsD()))
{
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 <class TriVertexType >
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<o.v);
}
const bool operator ==(const InsertedV & o){
return (v==o.v);
}
const bool operator !=(const InsertedV & o){
return (v!=o.v);
}
};
/// this function build a triangle mesh using new pointers to the tetrahedral mesh vertex
template <class TetraContainer, class TriangleMeshType>
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<InsertedV<TriVertexType> > newVertices;
typename vector<InsertedV<TriVertexType> >::iterator curr,next;
TriVertexIterator vi;
vector<TriVertexType*> redirect;
OfTetramesh(tetra,trim);
FaceIterator fi;
for(fi = trim.face.begin(); fi != trim.face.end(); ++fi){
newVertices.push_back(InsertedV<TriVertexType>( (*fi).V(0),&(*fi),0));
newVertices.push_back(InsertedV<TriVertexType>( (*fi).V(1),&(*fi),1));
newVertices.push_back(InsertedV<TriVertexType>( (*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<InsertedV<TriVertexType> >::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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,563 +0,0 @@
/****************************************************************************
* 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<MESH::FaceContainer,double>::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 <math.h>
#include <vcg/space/point3.h>
#include <vcg/space/box3.h>
#include <vcg/space/point4.h>
#include <vcg/math/base.h>
#include <vcg/simplex/face/distance.h>
#include <vcg/simplex/vertex/distance.h>
#include <vcg/space/intersection3.h>
#include <vcg/space/index/space_iterators.h>
#include <vcg/complex/trimesh/base.h>
namespace vcg {
namespace tri {
//**MARKER CLASSES**//
template <class MESH_TYPE,class OBJ_TYPE>
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 MESH_TYPE>
class FaceTmark:public Tmark<MESH_TYPE,typename MESH_TYPE::FaceType>
{
public:
FaceTmark() {}
FaceTmark(MESH_TYPE *m) {this->SetMesh(m);}
};
template <class MESH_TYPE>
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<MESH::FaceContainer >, ma non sono riuscito a definirlo implicitamente
template <class MESH, class GRID>
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<ScalarType> Point3x;
typedef FaceTmark<MESH> MarkerFace;
MarkerFace mf(&mesh);
vcg::face::PointDistanceFunctor<ScalarType> 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<typename MESH::FaceType,typename MESH::ScalarType>(*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 <class MESH, class GRID>
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<typename GRID::ScalarType> _ip;
typedef FaceTmark<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
vcg::face::PointDistanceFunctor FDistFunct;
typename MESH::FaceType* bestf= gr.GetClosest(FDistFunct,mf,_p,_maxDist,_minDist,_closestPt) );
}*/
template <class MESH, class GRID>
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<ScalarType> Point3x;
typedef FaceTmark<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
vcg::face::PointDistanceFunctor<ScalarType> PDistFunct;
_minDist=_maxDist;
return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt));
}
template <class MESH, class GRID>
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<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
typedef vcg::face::PointNormalDistanceFunctor<typename MESH::VertexType> PDistFunct;
PDistFunct fn;
_minDist=_maxDist;
//return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt.P()));
return (gr.template GetClosest <PDistFunct,MarkerFace>(fn,mf,_p,_maxDist,_minDist,_closestPt));
}
template <class MESH, class GRID>
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<ScalarType> Point3x;
typedef VertTmark<MESH> MarkerVert;
MarkerVert mv;
mv.SetMesh(&mesh);
typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
VDistFunct fn;
_minDist=_maxDist;
Point3x _closestPt;
return (gr.template GetClosest<VDistFunct,MarkerVert>(fn,mv,_p,_maxDist,_minDist,_closestPt));
}
template <class MESH, class GRID>
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<ScalarType> Point3x;
typedef VertTmark<MESH> MarkerVert;
MarkerVert mv;
mv.SetMesh(&mesh);
typedef vcg::vertex::PointNormalDistanceFunctor<typename MESH::VertexType> VDistFunct;
VDistFunct fn;
_minDist=_maxDist;
Point3x _closestPt;
return (gr.template GetClosest <VDistFunct,MarkerVert>(fn,mv,_p,_maxDist,_minDist,_closestPt));
}
template <class MESH, class GRID, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
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<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
vcg::face::PointDistanceFunctor<typename MESH::ScalarType> FDistFunct;
return (gr.GetKClosest /*<vcg::face::PointDistanceFunctor, MarkerFace,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
(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 <class MESH, class GRID, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
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<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
vcg::face::PointDistanceBaseFunctor<typename MESH::ScalarType> FDistFunct;
return (gr.GetKClosest /*<vcg::face::PointDistanceFunctor, MarkerFace,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
(FDistFunct,mf,_k,_p,_maxDist,_objectPtrs,_distances,_points));
}
template <class MESH, class GRID, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
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<MESH> MarkerVert;
MarkerVert mv;
mv.SetMesh(&mesh);
typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
VDistFunct distFunct;
return (gr.GetKClosest/* <VDistFunct,MarkerVert,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
(distFunct,mv,_k,_p,_maxDist,_objectPtrs,_distances,_points));
}
template <class MESH, class GRID, class OBJPTRCONTAINER, class DISTCONTAINER, class POINTCONTAINER>
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<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
typedef vcg::face::PointDistanceFunctor<typename MESH::ScalarType> FDistFunct;
return (gr.GetInSphere/*<FDistFunct,MarkerFace,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
(FDistFunct(),mf,_p,_r,_objectPtrs,_distances,_points));
}
template <class MESH, class GRID, class OBJPTRCONTAINER, class DISTCONTAINER, class POINTCONTAINER>
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<MESH> MarkerVert;
MarkerVert mv;
mv.SetMesh(&mesh);
typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
VDistFunct fn;
return (gr.GetInSphere/*<VDistFunct,MarkerVert,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
(fn, mv,_p,_r,_objectPtrs,_distances,_points));
}
template <class MESH, class GRID, class OBJPTRCONTAINER>
unsigned int GetInBoxFace(MESH & mesh,
GRID & gr,
const vcg::Box3<typename GRID::ScalarType> _bbox,
OBJPTRCONTAINER & _objectPtrs)
{
typedef FaceTmark<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
return(gr.GetInBox/*<MarkerFace,OBJPTRCONTAINER>*/(mf,_bbox,_objectPtrs));
}
template <class MESH, class GRID, class OBJPTRCONTAINER>
unsigned int GetInBoxVertex(MESH & mesh,
GRID & gr,
const vcg::Box3<typename GRID::ScalarType> _bbox,
OBJPTRCONTAINER & _objectPtrs)
{
typedef VertTmark<MESH> MarkerVert;
MarkerVert mv;
mv.SetMesh(&mesh);
return(gr.GetInBox/*<MarkerVert,OBJPTRCONTAINER>*/(mv,_bbox,_objectPtrs));
}
template <class MESH, class GRID>
typename GRID::ObjPtr DoRay(MESH & mesh,GRID & gr, const Ray3<typename GRID::ScalarType> & _ray,
const typename GRID::ScalarType & _maxDist, typename GRID::ScalarType & _t)
{
typedef typename MESH::FaceType FaceType;
typedef typename MESH::ScalarType ScalarType;
typedef FaceTmark<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
Ray3<typename GRID::ScalarType> _ray1=_ray;
_ray1.Normalize();
typedef vcg::RayTriangleIntersectionFunctor<true> FintFunct;
FintFunct ff;
return(gr.DoRay(ff,mf,_ray1,_maxDist,_t));
}
template <class MESH, class GRID>
typename GRID::ObjPtr DoRay(MESH & mesh,GRID & gr, const Ray3<typename GRID::ScalarType> & _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<MESH> MarkerFace;
MarkerFace mf;
mf.SetMesh(&mesh);
typedef vcg::RayTriangleIntersectionFunctor<true> FintFunct;
FintFunct fintfunct;
Ray3<typename GRID::ScalarType> _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<FaceType,ScalarType>(*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 <class MESH, class GRID, class OBJPTRCONTAINER, class COORDCONTAINER>
void RaySpherical(MESH & mesh,GRID & gr, const Ray3<typename GRID::ScalarType> & _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<typename GRID::ScalarType> 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<FaceType,ScalarType>(*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 GRID,class MESH>
class ClosestFaceIterator:public vcg::ClosestIterator<GRID,
vcg::face::PointDistanceFunctor<typename MESH::ScalarType>, FaceTmark<MESH> >
{
public:
typedef GRID GridType;
typedef MESH MeshType;
typedef FaceTmark<MESH> MarkerFace;
typedef vcg::face::PointDistanceFunctor<typename MESH::ScalarType> PDistFunct;
typedef vcg::ClosestIterator<GRID,PDistFunct, FaceTmark<MESH> > ClosestBaseType;
typedef typename MESH::FaceType FaceType;
typedef typename MESH::ScalarType ScalarType;
//ClosestFaceIterator(GridType &_Si):ClosestBaseType(_Si,PDistFunct<FaceType,ScalarType>()){}
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 GRID,class MESH>
class ClosestVertexIterator:public vcg::ClosestIterator<GRID, vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType>, VertTmark<MESH> >
{
public:
typedef GRID GridType;
typedef MESH MeshType;
typedef VertTmark<MESH> MarkerVert;
typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
typedef vcg::ClosestIterator<GRID, VDistFunct, VertTmark<MESH> > 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 GRID,class MESH>
class TriRayIterator:public vcg::RayIterator<GRID,vcg::RayTriangleIntersectionFunctor<true>,FaceTmark<MESH> >
{
public:
typedef GRID GridType;
typedef MESH MeshType;
typedef FaceTmark<MESH> MarkerFace;
typedef vcg::RayTriangleIntersectionFunctor<true> FintFunct;
typedef vcg::RayIterator<GRID,FintFunct, FaceTmark<MESH> > 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

View File

@ -1,436 +0,0 @@
/****************************************************************************
* 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<vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/clean.h>
#include<vcg/space/triangle3.h>
#include<vcg/complex/trimesh/update/topology.h>
#include<vcg/space/index/grid_util.h>
#include <iostream>
#include <math.h>
#include <limits>
// some stuff for portable hashes...
#ifdef WIN32
#ifndef __MINGW32__
#include <hash_map>
#include <hash_set>
#define STDEXT stdext
#else
#include <ext/hash_map>
#include <ext/hash_set>
#define STDEXT __gnu_cxx
#endif
#else
#include <ext/hash_map>
#include <ext/hash_set>
#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<vcg::tri::HashedPoint3i>{
inline size_t operator ()(const vcg::tri::HashedPoint3i &p) const {return size_t(p);}
};
} namespace vcg{ namespace tri{
#endif
//
template<class MeshType >
class NearestToCenter
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::FaceType FaceType;
typedef BasicGrid<typename MeshType::ScalarType> 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 MeshType>
class AverageColorCell
{
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::VertexType VertexType;
typedef BasicGrid<typename MeshType::ScalarType> 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 MeshType, class CellType>
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]<p.v[2]):
(v[1]!=p.v[1])?(v[1]<p.v[1]):
(v[0]<p.v[0]);
}
// Sort the vertex of the face maintaining the original face orientation (it only ensure that v0 is the minimum)
void sortOrient()
{
if(v[1] < v[0] && v[1] < v[2] ) { std::swap(v[0],v[1]); std::swap(v[1],v[2]); return; } // v1 was the minimum
if(v[2] < v[0] && v[2] < v[1] ) { std::swap(v[0],v[2]); std::swap(v[1],v[2]); return; } // v2 was the minimum
return; // v0 was the minimum;
}
void sort()
{
if(v[0] > 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<ScalarType> _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<ScalarType> Grid;
#ifdef _MSC_VER
STDEXT::hash_set<SimpleTri> TriSet;
typedef typename STDEXT::hash_set<SimpleTri>::iterator TriHashSetIterator;
#else
struct SimpleTriHashFunc{
inline size_t operator ()(const SimpleTri &p) const {return size_t(p);}
};
STDEXT::hash_set<SimpleTri,SimpleTriHashFunc> TriSet;
typedef typename STDEXT::hash_set<SimpleTri,SimpleTriHashFunc>::iterator TriHashSetIterator;
#endif
STDEXT::hash_map<HashedPoint3i,CellType> 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<HashedPoint3i,CellType>::iterator gi;
UpdateSelection<MeshType>::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<MeshType>::AddVertices(m,GridCell.size());
typename STDEXT::hash_map<HashedPoint3i,CellType>::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<MeshType>::AddVertices(m,GridCell.size());
typename STDEXT::hash_map<HashedPoint3i,CellType>::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<MeshType>::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

View File

@ -1,147 +0,0 @@
/****************************************************************************
* 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<vcg/simplex/face/jumping_pos.h>
#include<vcg/complex/trimesh/append.h>
#include<vcg/complex/trimesh/update/normal.h>
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 <angle> (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<class MESH_TYPE>
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<MESH_TYPE>::CompactVertexVector(m);
tri::Allocator<MESH_TYPE>::CompactFaceVector(m);
tri::UpdateNormals<MESH_TYPE>::NormalizeFace(m);
assert(tri::HasFFAdjacency(m));
typename MESH_TYPE::ScalarType cosangle=math::Cos(angleRad);
tri::UpdateFlags<MESH_TYPE>::VertexClearV(m);
std::vector<int> 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<FaceType> 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<FaceType> 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(dotProd<cosangle) break;
} while (startPos!=iPos);
startPos=iPos; // the found crease become the new starting pos.
}
int locCreaseCounter=0;
int curVertexCounter =vertInd;
do { // The real Loop
ScalarType dotProd=iPos.FFlip()->cN().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(dotProd<cosangle)
{ //qDebug(" Crease FOUND");
++locCreaseCounter;
curVertexCounter=newVertexCounter;
newVertexCounter++;
}
iPos.NextFE();
} while (startPos!=iPos);
if(locCreaseCounter>0 && (!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<MESH_TYPE>::AddVertices(m,newVertexCounter-m.vn);
tri::UpdateFlags<MESH_TYPE>::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<MESH_TYPE>::PerVertexFromCurrentFaceNormal(m);
}
} // end namespace tri
} // end namespace vcg
#endif

View File

@ -1,372 +0,0 @@
/****************************************************************************
* 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<vcg/simplex/face/pos.h>
#include<vcg/simplex/face/topology.h>
#include<vcg/complex/trimesh/allocate.h>
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 TRI_MESH_TYPE>
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<EdgeType> EdgeVec;
///of VFIterator
typedef typename vcg::face::VFIterator<FaceType> VFI;
/// vector of VFIterator
typedef typename std::vector<vcg::face::VFIterator<FaceType> > 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<FaceType> VFIterator;
// at the end of the loop each vertex must be counted twice
// except for boundary vertex.
std::map<VertexPointer,int> VertCnt;
std::map<std::pair<VertexPointer,VertexPointer>,int> EdgeCnt;
// the list of the boundary vertexes for the two endpoints
std::vector<VertexPointer> 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()<vfi.V2()) ++EdgeCnt[std::make_pair(vfi.V1(),vfi.V2())];
else ++EdgeCnt[std::make_pair(vfi.V2(),vfi.V1())];
}
// Now a loop to add dummy stuff: add the dummy vertex and two dummy edges
// (and remember to increase the counters for the two boundary vertexes involved)
typename std::map<VertexPointer,int>::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<VertexPointer> 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<std::pair<VertexPointer,VertexPointer>, 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<VertexPointer,int>::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<MeshType::face_type::face_base> x;
typename vcg::face::VFIterator<FaceType> 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<ScalarType> &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<TriMeshType>::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<TriMeshType>::DeleteVertex(m,*(c.V(0)));
//c.V(0)->SetD();
c.V(1)->P()=p;
return n_face_del;
}
};
}
}
#endif

View File

@ -1,393 +0,0 @@
/****************************************************************************
* 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 <assert.h>
#include <vcg/math/base.h>
#include <vcg/container/simple_temporary_data.h>
#include <vcg/simplex/face/pos.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/complex/trimesh/update/quality.h>
#include <deque>
#include <vector>
#include <list>
#include <functional>
/*
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<MyMesh> g;
g.FarthestVertex(m,seed,far,d);
*/
namespace vcg{
namespace tri{
template <class MeshType>
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 MeshType, class DistanceFunctor = EuclideanDistance<MeshType> >
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<std::vector<VertexType>, TempData > TempDataType;
//TempDataType * TD;
struct pred: public std::binary_function<VertDist,VertDist,bool>{
pred(){};
bool operator()(const VertDist& v0, const VertDist& v1) const
{return (v0.d > v1.d);}
};
struct pred_addr: public std::binary_function<VertDist,VertDist,bool>{
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<ScalarType>(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<ScalarType>(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<VertDist> & seedVec,
ScalarType & max_distance,
bool farthestOnBorder = false,
ScalarType distance_threshold = std::numeric_limits<ScalarType>::max(),
typename MeshType::template PerVertexAttributeHandle<VertexPointer> * sources = NULL
)
{
bool isLeaf;
std::vector<VertDist> frontier;
VertexIterator ii;
std::list<VertexPointer> children;
VertexPointer curr,farthest=0,pw1;
typename std::list<VertexPointer>::iterator is;
std::deque<VertexPointer> leaves;
std::vector<VertDist> _frontier;
ScalarType unreached = std::numeric_limits<ScalarType>::max();
std::vector <std::pair<VertexPointer,ScalarType> > expansion;
typename std::vector <VertDist >::iterator ifr;
face::VFIterator<FaceType> 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<VertDist >:: 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<FaceType> 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<VertexPointer> & fro,
VertexPointer & farthest,
ScalarType & distance,
ScalarType distance_thr = std::numeric_limits<ScalarType>::max(),
typename MeshType::template PerVertexAttributeHandle<VertexPointer> * sources = NULL){
typename std::vector<VertexPointer>::iterator fi;
std::vector<VertDist>fr;
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<ScalarType>::max()){
std::vector<VertexPointer> 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<VertexPointer> & seedVec,
VertexPointer & farthest,
ScalarType & distance,
typename MeshType::template PerVertexAttributeHandle<VertexPointer> * sources = NULL
){
typename std::vector<VertexPointer>::iterator fi;
std::vector<VertDist>fr;
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<VertexPointer> * sources = NULL){
std::vector<VertexPointer> 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<VertexPointer> * sources = NULL
){
std::vector<VertexPointer> 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<CMeshO>::VertexConstant(m,0);
return FarthestVertex(m,fro,farthest,distance,std::numeric_limits<ScalarType>::max(),sources);
}
};
};// end namespace tri
};// end namespace vcg

View File

@ -1,712 +0,0 @@
#ifndef HALFEDGEQUADCLEAN_H
#define HALFEDGEQUADCLEAN_H
#include <vcg/complex/trimesh/update/halfedge_topology.h>
#include <queue>
#include <set>
#include<vcg/math/base.h>
#include<valarray>
#include<cmath>
namespace vcg
{
namespace tri
{
/*!
* \brief The class provides methods for detecting doublets and singlets and removing them
*
*/
template<class MeshType> 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<FacePointer> &q, VertexPointer vp)
{
vector<FacePointer> faces = HalfEdgeTopology<MeshType>::get_incident_faces(vp);
for(typename vector<FacePointer>::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<FacePointer> &faces, queue<FacePointer> &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<FacePointer> *q = NULL)
{
vector<HEdgePointer> hedges = HalfEdgeTopology<MeshType>::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<MeshType>::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<MeshType>::doublet_remove_quad(m, hedges[0]->HVp());
// Removal of the doublet may generate a singlet
if(HalfEdgeTopology<MeshType>::is_singlet_quad(fp1))
{
HEdgePointer hp = HalfEdgeTopology<MeshType>::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<MeshType>::vertex_valence(hp->HVp());
valence2 = HalfEdgeTopology<MeshType>::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<MeshType>::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<MeshType>::vertex_valence(hp->HVp());
valence2 = HalfEdgeTopology<MeshType>::vertex_valence(hp->HOp()->HVp());
}
}
}
return fp1;
}
// Four doublets: simply remove one of the two faces
case 4:
HalfEdgeTopology<MeshType>::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<FacePointer> &faces, vector<VertexPointer> vertices)
{
queue<FacePointer> q;
for(typename vector<VertexPointer>::iterator vi = vertices.begin(); vi != vertices.end(); ++vi)
{
vector<FacePointer> inc_faces = HalfEdgeTopology<MeshType>::get_incident_faces(*vi);
for(typename vector<FacePointer>::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<VertexPointer> vertices)
{
set<FacePointer> 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<FacePointer> &faces)
{
queue<FacePointer> q;
for(typename set<FacePointer>::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<MeshType>::is_singlet_quad(&(*fi)) )
{
HalfEdgeTopology<MeshType>::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<MeshType>::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<MeshType>::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<class PriorityType>
static void flip_edges(MeshType &m, vector<HEdgePointer> &hedges, set<FacePointer> &faces)
{
for(typename vector<HEdgePointer>::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<VertexPointer> 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<MeshType>::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 <class PriorityType>
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<VertexPointer> 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<MeshType>::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 MeshType> 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 MeshType> class VertReg: public EdgeFlipPriority<MeshType>
{
public:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::EdgePointer EdgePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::FacePointer FacePointer;
typedef EdgeFlipPriority<MeshType> 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<VertexPointer> vps1 = HalfEdgeTopology<MeshType>::getVertices(hp->HFp(), hp);
vector<VertexPointer> vps2 = HalfEdgeTopology<MeshType>::getVertices(hp->HOp()->HFp(), hp->HOp());
valarray<double> 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<MeshType>::vertex_valence(vps1[i]) - 4 ;
valences[4] = HalfEdgeTopology<MeshType>::vertex_valence(vps2[2]) - 4;
valences[5] = HalfEdgeTopology<MeshType>::vertex_valence(vps2[3]) - 4;
// Vector containing sums of the valencies in all the possible cases: no rotation, ccw rotation, cw rotation
vector<int> 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 MeshType> class Homeometry: public EdgeFlipPriority<MeshType>
{
public:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::EdgePointer EdgePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::FacePointer FacePointer;
typedef EdgeFlipPriority<MeshType> 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<VertexPointer> face1 = HalfEdgeTopology<MeshType>::getVertices(hp->HFp(), hp);
vector<VertexPointer> face2 = HalfEdgeTopology<MeshType>::getVertices(hp->HOp()->HFp(), hp->HOp());
// Vector containing sums of the valencies in all the possible cases: no rotation, ccw rotation, cw rotation
vector<int> 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<VertexPointer> &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<VertexPointer> &face1, vector<VertexPointer> &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<float> 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<float>(2.0)) + sum_diagonals);
}
};
}
}
#endif // HALFEDGEQUADCLEAN_H

View File

@ -1,992 +0,0 @@
/****************************************************************************
* 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 <wrap/callback.h>
#include <vcg/math/base.h>
#include <vcg/complex/trimesh/clean.h>
#include <vcg/space/point3.h>
#include <vector>
#include <float.h>
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 MESH> class TrivialEar
{
public:
typedef typename MESH::FaceType FaceType;
typedef typename MESH::FacePointer FacePointer;
typedef typename face::Pos<FaceType> 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<typename MESH::FaceType>* vf;
TrivialEar(){}
TrivialEar(const PosType & ep)
{
e0=ep;
assert(e0.IsBorder());
e1=e0;
e1.NextB();
n=vcg::Normal<TrivialEar>(*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 MESH> class MinimumWeightEar : public TrivialEar<MESH>
{
public:
static float &DiedralWeight() { static float _dw=1.0; return _dw;}
typedef TrivialEar<MESH> 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<MESH>(ep)
MinimumWeightEar(const typename face::Pos<typename MESH::FaceType>& ep) : TrivialEar<MESH>(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 MESH> class SelfIntersectionEar : public MinimumWeightEar<MESH>
{
public:
typedef typename MESH::FaceType FaceType;
typedef typename MESH::FacePointer FacePointer;
typedef typename face::Pos<FaceType> PosType;
typedef typename MESH::ScalarType ScalarType;
typedef typename MESH::CoordType CoordType;
static std::vector<FacePointer> &AdjacencyRing()
{
static std::vector<FacePointer> ar;
return ar;
}
SelfIntersectionEar(){}
SelfIntersectionEar(const PosType & ep):MinimumWeightEar<MESH>(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<MESH>::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<MESH> *)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<MESH>::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 MESH>
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<ScalarType> Box3Type;
typedef typename face::Pos<FaceType> PosType;
public:
class Info
{
public:
Info(){}
Info(PosType const &pHole, int const pHoleSize, Box3<ScalarType> &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<class EAR>
static void FillHoleEar(MESH &m, Info &h ,int UBIT, std::vector<FacePointer *> &app,std::vector<FaceType > *vf =0)
{
//Aggiungo le facce e aggiorno il puntatore alla faccia!
FaceIterator f = tri::Allocator<MESH>::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<class EAR>
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<Info >::iterator ith;
//Info app;
int indCb=0;
int holeCnt=0;
std::vector<FacePointer *> 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<class EAR>
static int EarCuttingIntersectionFill(MESH &m, int sizeHole, bool Selected = false, CallBackPos *cb=0)
{
std::vector<Info > vinfo;
int UBIT = GetInfo(m, Selected,vinfo);
std::vector<FaceType > vf;
PosType sp;
PosType ap;
typename std::vector<Info >::iterator ith;
// collect the face pointer that has to be updated by the various addfaces
std::vector<FacePointer *> 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<FacePointer *> 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<FacePointer>::iterator fpi;
for(fpi=EAR::AdjacencyRing().begin();fpi!=EAR::AdjacencyRing().end();++fpi)
vfp.push_back( &*fpi );
FillHoleEar<EAR >(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<Info >& 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<PosType > 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<float>(angle , ComputeDihedralAngle(pi.v->P(), pj.v->P(), pk.v->P(), px.v->P()) );
}
else
{
angle = std::max<float>( 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<float>(angle , ComputeDihedralAngle(pj.v->P(), pk.v->P(), pi.v->P(), px.v->P()) );
}
else
{
angle = std::max<float>( 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<float>(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<PosType > 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<Weight>( nv, Weight() ) );
vi.resize( nv, std::vector<int>( 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<int> > vi, std::vector<PosType > 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<PosType > vvi;
std::vector<FacePointer * > vfp;
std::vector<Info > vinfo;
typename std::vector<Info >::iterator VIT;
GetInfo(m, Selected,vinfo);
for(VIT = vinfo.begin(); VIT != vinfo.end();++VIT)
{
vvi.push_back(VIT->p);
}
typename std::vector<PosType >::iterator ith;
typename std::vector<PosType >::iterator ithn;
typename std::vector<VertexPointer >::iterator itf;
std::vector<PosType > app;
PosType ps;
std::vector<FaceType > tr;
std::vector<VertexPointer > 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<PosType >::iterator itP;
std::vector<FacePointer *> 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<MESH>::AddFaces(m, (app.size()-2) , vfp);
calculateMinimumWeightTriangulation(m,f, app);
}
}
}
static void getBoundHole (PosType sp,std::vector<PosType >&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

View File

@ -1,383 +0,0 @@
/****************************************************************************
* 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 <vcg/math/matrix33.h>
#include <vcg/math/lin_algebra.h>
#include <vcg/complex/trimesh/update/normal.h>
namespace vcg
{
namespace tri
{
template <class InertiaMeshType>
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<ScalarType> 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<MeshType>::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<ScalarType>(T0);
}
Point3<ScalarType> CenterOfMass()
{
Point3<ScalarType> r;
r[X] = T1[X] / T0;
r[Y] = T1[Y] / T0;
r[Z] = T1[Z] / T0;
return r;
}
void InertiaTensor(Matrix33<ScalarType> &J ){
Point3<ScalarType> 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<ScalarType> &J )
{
J.SetIdentity();
Point3<ScalarType> 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<ScalarType> &EV, Point4<ScalarType> &ev )
{
Matrix44<ScalarType> 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<ScalarType> & bary, vcg::Matrix33<ScalarType> &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<ScalarType> 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<ScalarType> 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<ScalarType> 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

View File

@ -1,115 +0,0 @@
/****************************************************************************
* 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 <vcg/space/ray3.h>
#include <vcg/space/box3.h>
#include <vcg/space/triangle3.h>
#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 FaceSpatialIndexing,class TriMeshType>
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<ScalarType> 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

View File

@ -1,470 +0,0 @@
/****************************************************************************
* 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<vector>
#include <algorithm>
#include<vcg/space/point3.h>
#include<vcg/space/plane3.h>
#include<vcg/space/segment3.h>
#include<vcg/space/intersection3.h>
#include<vcg/complex/edgemesh/allocate.h>
#include<vcg/complex/trimesh/allocate.h>
#include<vcg/complex/trimesh/subset.h>
#include<vcg/complex/trimesh/closest.h>
#include<vcg/complex/trimesh/base.h>
#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<ScalarType> plane, std::vector<typename GridType::Cell *> &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<double> 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<ScalarType> 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<typename TriMeshType::FaceType*> v;
v.clear();
Intersect(*grid,pl,cells);
Segment3<ScalarType> seg;
ave_length = 0.0;
typename std::vector<typename GridType::Cell*>::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<EdgeMeshType>::AddEdges(em,1);
vi = vcg::edg::Allocator<EdgeMeshType>::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<typename TriMeshType::FaceType*>::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<ScalarType> pl,
EdgeMeshType & em)
{
typename EdgeMeshType::VertexIterator vi;
typename TriMeshType::FaceIterator fi;
em.Clear();
Segment3<ScalarType> 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<EdgeMeshType>::AddEdges(em,1);
vi = vcg::edg::Allocator<EdgeMeshType>::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<ScalarType> pl,
IndexingType *grid,
typename std::vector<typename TriMeshType::FaceType*> &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<typename GridType::Cell*>::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<ScalarType> & ray,
/* Intersect Point */ Point3<ScalarType> & hitPoint)
{
//typedef typename TriMeshType::FaceContainer FaceContainer;
typename TriMeshType::FaceIterator fi;
bool hit=false;
if(m==0) return false;
//TriMeshType::FaceIterator fi;
//std::vector<TriMeshType::FaceType*>::iterator fi;
ScalarType bar1,bar2,dist;
Point3<ScalarType> p1;
Point3<ScalarType> p2;
Point3<ScalarType> p3;
for(fi = m->face.begin(); fi != m->face.end(); ++fi)
{
p1=vcg::Point3<ScalarType>( (*fi).P(0).X() ,(*fi).P(0).Y(),(*fi).P(0).Z() );
p2=vcg::Point3<ScalarType>( (*fi).P(1).X() ,(*fi).P(1).Y(),(*fi).P(1).Z() );
p3=vcg::Point3<ScalarType>( (*fi).P(2).X() ,(*fi).P(2).Y(),(*fi).P(2).Z() );
if(IntersectionLineTriangle<ScalarType>(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<ScalarType> & ray,
/* Intersect Point */ Point3<ScalarType> & 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<TriMeshType::FaceType*>::iterator fi;
ScalarType dist;
Point3<ScalarType> p1;
Point3<ScalarType> p2;
Point3<ScalarType> p3;
for(fi = m->face.begin(); fi != m->face.end(); ++fi)
{
p1=vcg::Point3<ScalarType>( (*fi).P(0).X() ,(*fi).P(0).Y(),(*fi).P(0).Z() );
p2=vcg::Point3<ScalarType>( (*fi).P(1).X() ,(*fi).P(1).Y(),(*fi).P(1).Z() );
p3=vcg::Point3<ScalarType>( (*fi).P(2).X() ,(*fi).P(2).Y(),(*fi).P(2).Z() );
if(IntersectionLineTriangle<ScalarType>(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<ScalarType> &ball, TriMeshType & res,
float tol = 0){
typename TriMeshType::VertexIterator v0,v1,v2;
typename TriMeshType::FaceIterator fi;
std::vector<typename TriMeshType:: FaceType*> closests;
vcg::Point3<ScalarType> witness;
std::pair<ScalarType, ScalarType> 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<ScalarType>(ball ,(*fi), witness , &info))
closests.push_back(&(*fi));
res.Clear();
SubSet(res,closests);
int i =0;
while(i<res.fn){
bool allIn = ( ball.IsIn(res.face[i].P(0)) && ball.IsIn(res.face[i].P(1))&&ball.IsIn(res.face[i].P(2)));
if( IntersectionSphereTriangle<ScalarType>(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<TriMeshType>::AddVertices(res,3);
fi = vcg::tri::Allocator<TriMeshType>::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<TriMeshType>::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<TriMeshType>::DeleteFace(res,res.face[i]);
++i;
}
}
template < typename TriMeshType, class ScalarType, class IndexingType>
void IntersectionBallMesh( IndexingType * grid, TriMeshType & m, const vcg::Sphere3<ScalarType> &ball, TriMeshType & res,
float tol = 0){
typename TriMeshType::VertexIterator v0,v1,v2;
typename std::vector<typename TriMeshType::FacePointer >::iterator cfi;
typename TriMeshType::FaceIterator fi;
std::vector<typename TriMeshType:: FaceType*> closestsF,closests;
vcg::Point3<ScalarType> witness;
std::vector<vcg::Point3<ScalarType> > witnesses;
std::vector<ScalarType> distances;
std::pair<ScalarType, ScalarType> 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<ScalarType>(ball ,(**cfi), witness , &info))
closests.push_back(&(**cfi));
res.Clear();
SubSet(res,closests);
int i =0;
while(i<res.fn){
bool allIn = ( ball.IsIn(res.face[i].P(0)) && ball.IsIn(res.face[i].P(1))&&ball.IsIn(res.face[i].P(2)));
if( IntersectionSphereTriangle<ScalarType>(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<TriMeshType>::AddVertices(res,3);
fi = vcg::tri::Allocator<TriMeshType>::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<TriMeshType>::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<TriMeshType>::DeleteFace(res,res.face[i]);
++i;
}
}
/*@}*/
} // end namespace vcg
#endif

View File

@ -1,398 +0,0 @@
/****************************************************************************
* 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<vector>
#include<algorithm>
#include<time.h>
#include<math.h>
#include<vcg/complex/trimesh/base.h>
namespace vcg{
template<class MeshType>
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 MeshType>
class LocalModification
{
public:
typedef typename LocalOptimization<MeshType>::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 MeshType>
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<HeapElem> HeapType;
// modification type
typedef LocalModification <MeshType> LocModType;
// modification Pointer type
typedef LocalModification <MeshType> * 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 <class LocalModificationType> 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 <class LocalModificationType> 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

View File

@ -1,157 +0,0 @@
#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 <vcg/simplex/face/jumping_pos.h>
#include <vcg/complex/trimesh/allocate.h>
#include <vcg/complex/trimesh/update/flag.h>
#include <vector>
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 MeshType>
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<VertexType*> allV;
std::vector<FaceType*> allF;
std::vector<VertexType*> lastV;
std::vector<FaceType*> 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<FaceType> p(v->VFp(),v);
assert(p.V() == v);
int count = 0;
face::Pos<FaceType> 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<MeshType>::VertexClearV(*m);
tri::UpdateFlags<MeshType>::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<VertexType*> lastVtemp = lastV;
lastV.clear();
lastF.clear();
for(typename std::vector<VertexType*>::iterator it = lastVtemp.begin(); it != lastVtemp.end(); ++it)
{
insertAndFlag1Ring(*it);
}
}
void expand(int k)
{
for(int i=0;i<k;++i)
expand();
}
};
}} // end namespace NAMESPACE
#endif // RINGWALKER_H

View File

@ -1,327 +0,0 @@
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2007 \/)\/ *
* 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 OVERLAP_ESTIMATION_H
#define OVERLAP_ESTIMATION_H
#include <vcg/math/gen_normal.h>
#include <vcg/math/random_generator.h>
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/complex/trimesh/closest.h>
#include <vcg/complex/trimesh/point_sampling.h>
#include <qdatetime.h>
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 MESH_TYPE> 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<VertexPointer>::iterator VertexPointerIterator;
typedef GridStaticPtr<VertexType, ScalarType > MeshGrid;
typedef tri::VertTmark<MeshType> 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<VertexType*> 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<vector<int> >* 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 .<br>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<ScalarType> 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<VertexPointer> queryVert;
if(param.normalEqualization){
assert(normBuckets);
for(unsigned int i=0; i<mMov->vert.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<ScalarType> queryNrm; //the query point normal for consensus
CoordType queryPnt; //the query point for consensus
CoordType closestPnt; //the closest point found in consensus
Matrix33<ScalarType> inv33_matMov(mMov->Tr,3); //3x3 matrix needed to transform normals
Matrix33<ScalarType> 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<typename MESH_TYPE::VertexPointer>& vert, int sampleNum)
{
VertexPointerSampler sampler;
tri::SurfaceSampling<MeshType, VertexPointerSampler>::VertexUniform(m, sampler, sampleNum);
for(unsigned int i=0; i<sampler.sampleVec.size(); i++) vert.push_back(sampler.sampleVec[i]);
}
/** Buckets normals of the vertexes contained in \c vert .
* \return A vector of vectors containing indexes to \c vert .
*/
vector<vector<int> >* BucketVertexNormal(typename MESH_TYPE::VertContainer& vert, int bucketDim = 30)
{
static vector<Point3f> NV;
if(NV.size()==0) GenNormal<float>::Uniform(bucketDim,NV);
vector<vector<int> >* BKT = new vector<vector<int> >(NV.size()); //NV size is greater then bucketDim, so don't change this!
int ind;
for(int i=0;i<vert.size();++i){
ind=GenNormal<float>::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<typename MESH_TYPE::VertexPointer>& vert, int SampleNum)
{
assert(normBuckets);
// vettore di contatori per sapere quanti punti ho gia' preso per ogni bucket
vector<int> BKTpos(normBuckets->size(),0);
if(SampleNum >= int(vert.size())) SampleNum= int(vert.size()-1);
int ind;
for(int i=0;i<SampleNum;){
ind=LocRnd(normBuckets->size()); // Scelgo un Bucket
int &CURpos = BKTpos[ind];
vector<int> &CUR = (*normBuckets)[ind];
if(CURpos<int(CUR.size())){
swap(CUR[CURpos], CUR[ CURpos + LocRnd((*normBuckets)[ind].size()-CURpos)]);
swap(vert[i],vert[CUR[CURpos]]);
++BKTpos[ind];
++i;
}
}
vert.resize(SampleNum);
return true;
}
/** Function to retrieve a static random number generator object.
* \return A \c SubtractiveRingRNG object.
*/
static math::SubtractiveRingRNG &LocRnd(){
static math::SubtractiveRingRNG myrnd(time(NULL));
return myrnd;
}
/** Gets a random number in the interval \c [0..n] . Number is
* produced by a \c SubtractiveRingRNG object initialized once for all.
* \return A random number in the interval \c [0..n] .
*/
static int LocRnd(int n){
return LocRnd().generate(n);
}
/** Put \c mFix into a grid. */
inline void SetupGrid()
{
gridFix->Set(mFix->vert.begin(),mFix->vert.end());
markerFunctorFix.SetMesh(mFix);
}
};
#endif // OVERLAP_ESTIMATION_H

File diff suppressed because it is too large Load Diff

View File

@ -1,178 +0,0 @@
/****************************************************************************
* 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 <vector>
#include <vcg/complex/trimesh/subset.h>
#include <vcg/simplex/face/jumping_pos.h>
#include <vcg/space/planar_polygon_tessellation.h>
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 <class TriMeshType,class PolyMeshType >
struct PolygonSupport{
/**
Import a trianglemesh from a polygon mesh
**/
static void ImportFromPolyMesh(TriMeshType & tm, PolyMeshType & pm){
std::vector<typename PolyMeshType::CoordType> points;
std::vector<int> faces;
// the vertices are the same, simply import them
typename PolyMeshType::VertexIterator vi;
typename TriMeshType::FaceIterator tfi,tfib ;
typename TriMeshType ::VertexIterator tvi = Allocator<TriMeshType>::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<TriMeshType>::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<TriMeshType>::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<PolyMeshType>::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<PolyMeshType> ::DeleteVertex(pm,(*vi));
// convert the faces
typename TriMeshType::FaceIterator tfi;
vcg::face::JumpingPos<typename TriMeshType::FaceType> p;
for( tfi = tm.face.begin(); tfi != tm.face.end(); ++tfi) if(!(*tfi).IsD() && !(*tfi).IsV())
{
std::vector<typename TriMeshType::VertexPointer> vs;// vertices of the polygon
std::vector<typename TriMeshType::FacePointer> 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<PolyMeshType>::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<typename TriMeshType::VertexPointer> &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<typename TriMeshType::FaceType> 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

View File

@ -1,924 +0,0 @@
/***********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 <functional>
#include <map>
#include <vector>
#include <vcg/space/sphere3.h>
#include <vcg/space/plane3.h>
#include <vcg/space/texcoord2.h>
#include <vcg/space/color4.h>
#include <vcg/simplex/face/pos.h>
#include<vcg/complex/trimesh/allocate.h>
#include<vcg/complex/trimesh/update/topology.h>
#include<wrap/callback.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/space/triangle3.h>
namespace vcg{
/* A very short intro about the generic refinement framework,
the main fuction is the
template<class MESH_TYPE,class MIDPOINT, class EDGEPRED>
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 MESH_TYPE, class FLT> class EdgeLen
{
public:
FLT thr2;
bool operator()(face::Pos<typename MESH_TYPE::FaceType> 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<class MESH_TYPE>
struct MidPoint : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType >
{
MidPoint(MESH_TYPE *_mp) { mp=_mp; }
MESH_TYPE *mp;
void operator()(typename MESH_TYPE::VertexType &nv, face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> cc;
return cc.lerp(c0,c1,0.5f);
}
template<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> tmp;
assert(t0.n()== t1.n());
tmp.n()=t0.n();
tmp.t()=(t0.t()+t1.t())/2.0;
return tmp;
}
};
template<class MESH_TYPE>
struct MidPointArc : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
void operator()(typename MESH_TYPE::VertexType &nv, face::Pos<typename MESH_TYPE::FaceType> 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(w<EPS) { nv.P()=(ep.f->V(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-cosa1<EPS) {nv.P()=(ep.f->V(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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> cc;
return cc.lerp(c0,c1,0.5f);
}
template<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> 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<class MESH_TYPE>
struct MidPointArcNaive : public std::unary_function< face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
typename MESH_TYPE::CoordType operator()(face::Pos<typename MESH_TYPE::FaceType> 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 MESH_TYPE, class FLT>
class EdgeLen
{
FLT squaredThr;
public:
EdgeLen(){};
EdgeLen(FLT threshold) {setThr(threshold);}
void setThr(FLT threshold) {squaredThr = threshold*threshold; }
bool operator()(face::Pos<typename MESH_TYPE::FaceType> 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 VertexPointer>
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<class MESH_TYPE,class MIDPOINT, class EDGEPRED>
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<FaceType> PosType;
int j,NewVertNum=0,NewFaceNum=0;
typedef RefinedFaceData<VertexPointer> RFD;
typedef typename MESH_TYPE :: template PerFaceAttributeHandle<RFD> HandleType;
HandleType RD = tri::Allocator<MESH_TYPE>:: template AddPerFaceAttribute<RFD> (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<MESH_TYPE> :: template DeletePerFaceAttribute<RefinedFaceData<VertexPointer> > (m,RD);
return false;
}
VertexIterator lastv = tri::Allocator<MESH_TYPE>::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<MESH_TYPE>::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;i<SplitTab[ind].TriNum;++i){
nf[i]=&*lastf; ++lastf; fca++;
if(RefineSelected || (*fi).IsS()) (*nf[i]).SetS();
if(tri::HasPerFaceColor(m))
nf[i]->C()=(*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;i<SplitTab[ind].TriNum;++i)
for(j=0;j<3;++j){
(*nf[i]).V(j)=&*vv[SplitTab[ind].TV[i][j]];
if(tri::HasPerWedgeTexCoord(m)) //analogo ai vertici...
(*nf[i]).WT(j)=wtt[SplitTab[ind].TV[i][j]];
assert((*nf[i]).V(j)!=0);
if(SplitTab[ind].TE[i][j]!=3){
if(orgflag & (MESH_TYPE::FaceType::BORDER0<<(SplitTab[ind].TE[i][j])))
(*nf[i]).SetB(j);
else
(*nf[i]).ClearB(j);
}
else (*nf[i]).ClearB(j);
}
if(SplitTab[ind].TriNum==3 &&
SquaredDistance(vv[SplitTab[ind].swap[0][0]]->P(),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<MESH_TYPE>::FaceFace(m);
tri::Allocator<MESH_TYPE> :: template DeletePerFaceAttribute<RefinedFaceData<VertexPointer> > (m,RD);
return true;
}
/*************************************************************************/
// simple wrapper of the base refine for lazy coder that do not need a edge predicate
template<class MESH_TYPE,class MIDPOINT>
bool Refine(MESH_TYPE &m, MIDPOINT mid, typename MESH_TYPE::ScalarType thr=0,bool RefineSelected=false, CallBackPos *cb = 0)
{
EdgeLen <MESH_TYPE, typename MESH_TYPE::ScalarType> 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<class MESH_TYPE>
struct MidPointButterfly : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
void operator()(typename MESH_TYPE::VertexType &nv, face::Pos<typename MESH_TYPE::FaceType> ep)
{
face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> cc;
return cc.lerp(c0,c1,0.5f);
}
template<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> 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<class MESH_TYPE>
struct MidPointButterfly2 : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
typename MESH_TYPE::CoordType operator()(face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::FaceType> 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<MESH_TYPE>()( ep );
if(kr==6 && kl==6) return MidPointButterfly<MESH_TYPE>()( 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<class MESH_TYPE>
struct OddPointLoop : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
}
// vecchi punti
template<class MESH_TYPE>
struct EvenPointLoop : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
}
*/
template<class MESH_TYPE>
struct MidPointPlane : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
Plane3<typename MESH_TYPE::ScalarType> pl;
typedef Point3<typename MESH_TYPE::ScalarType> Point3x;
void operator()(typename MESH_TYPE::VertexType &nv, face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> cc;
return cc.lerp(c0,c1,0.5f);
}
template<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> tmp;
assert(t0.n()== t1.n());
tmp.n()=t0.n();
tmp.t()=(t0.t()+t1.t())/2.0;
return tmp;
}
};
template <class FLT>
class EdgeSplPlane
{
public:
Plane3<FLT> pl;
bool operator()(const Point3<FLT> &p0, const Point3<FLT> &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<class MESH_TYPE>
struct MidPointSphere : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::CoordType>
{
Sphere3<typename MESH_TYPE::ScalarType> sph;
typedef Point3<typename MESH_TYPE::ScalarType> Point3x;
void operator()(typename MESH_TYPE::VertexType &nv, face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> cc;
return cc.lerp(c0,c1,0.5f);
}
template<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> tmp;
assert(t0.n()== t1.n());
tmp.n()=t0.n();
tmp.t()=(t0.t()+t1.t())/2.0;
return tmp;
}
};
template <class FLT>
class EdgeSplSphere
{
public:
Sphere3<FLT> sph;
bool operator()(const Point3<FLT> &p0, const Point3<FLT> &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<class TRIMESH_TYPE>
struct CenterPoint : public std::unary_function<typename TRIMESH_TYPE::FacePointer, typename TRIMESH_TYPE::CoordType>
{
typename TRIMESH_TYPE::CoordType operator()(typename TRIMESH_TYPE::FacePointer f){
return vcg::Barycenter<typename TRIMESH_TYPE::FaceType>(*f);
}
};
template<class TRIMESH_TYPE, class CenterPoint>
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

View File

@ -1,623 +0,0 @@
/****************************************************************************
* 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 <cmath>
#include <vcg/space/point3.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/refine.h>
#include <vcg/space/color4.h>
#include <vcg/container/simple_temporary_data.h>
#include <vcg/complex/trimesh/update/flag.h>
#include <vcg/complex/trimesh/update/color.h>
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<class SCALAR_TYPE>
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<class SCALAR_TYPE>
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<SCALAR_TYPE>().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<SCALAR_TYPE>().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<SCALAR_TYPE>().opposite(k);
}
};
template<class SCALAR_TYPE>
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<SCALAR_TYPE>().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<SCALAR_TYPE>().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<SCALAR_TYPE>().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<class MESH_TYPE, class LSCALAR_TYPE = typename MESH_TYPE::ScalarType>
struct Centroid {
typedef typename MESH_TYPE::ScalarType Scalar;
typedef typename MESH_TYPE::CoordType Coord;
typedef LSCALAR_TYPE LScalar;
typedef vcg::Point3<LScalar> 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<class MESH_TYPE, class LSCALAR_TYPE = typename MESH_TYPE::ScalarType>
struct LS3Projection {
typedef typename MESH_TYPE::ScalarType Scalar;
typedef typename MESH_TYPE::CoordType Coord;
typedef LSCALAR_TYPE LScalar;
typedef vcg::Point3<LScalar> 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<Scalar>(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<Scalar>(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 MESH_TYPE, class METHOD_TYPE=Centroid<MESH_TYPE>, class WEIGHT_TYPE=LoopWeight<typename MESH_TYPE::ScalarType> >
struct OddPointLoopGeneric : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::VertexType>
{
typedef METHOD_TYPE Projection;
typedef WEIGHT_TYPE Weight;
typedef typename MESH_TYPE::template PerVertexAttributeHandle<int> 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<typename MESH_TYPE::FaceType> ep) {
proj.reset();
face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> cc;
return cc.lerp(c0,c1,0.5f);
}
template<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> 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 MESH_TYPE, class METHOD_TYPE=Centroid<MESH_TYPE>, class WEIGHT_TYPE=LoopWeight<typename MESH_TYPE::ScalarType> >
struct EvenPointLoopGeneric : public std::unary_function<face::Pos<typename MESH_TYPE::FaceType> , typename MESH_TYPE::VertexType>
{
typedef METHOD_TYPE Projection;
typedef WEIGHT_TYPE Weight;
typedef typename MESH_TYPE::template PerVertexAttributeHandle<int> 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<typename MESH_TYPE::FaceType> ep) {
proj.reset();
face::Pos<typename MESH_TYPE::FaceType> he(ep.f,ep.z,ep.f->V(ep.z));
typename MESH_TYPE::VertexType *r, *l, *curr;
curr = he.v;
face::Pos<typename MESH_TYPE::FaceType> 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<typename MESH_TYPE::ScalarType> WedgeInterp(Color4<typename MESH_TYPE::ScalarType> &c0, Color4<typename MESH_TYPE::ScalarType> &c1)
{
Color4<typename MESH_TYPE::ScalarType> 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<class FL_TYPE>
TexCoord2<FL_TYPE,1> WedgeInterp(TexCoord2<FL_TYPE,1> &t0, TexCoord2<FL_TYPE,1> &t1)
{
TexCoord2<FL_TYPE,1> 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<class MESH_TYPE>
struct OddPointLoop : OddPointLoopGeneric<MESH_TYPE, Centroid<MESH_TYPE> >
{
};
template<class MESH_TYPE>
struct EvenPointLoop : EvenPointLoopGeneric<MESH_TYPE, Centroid<MESH_TYPE> >
{
};
template<class MESH_TYPE,class ODD_VERT, class EVEN_VERT>
bool RefineOddEven(MESH_TYPE &m, ODD_VERT odd, EVEN_VERT even,float length,
bool RefineSelected=false, CallBackPos *cbOdd = 0, CallBackPos *cbEven = 0)
{
EdgeLen <MESH_TYPE, typename MESH_TYPE::ScalarType> ep(length);
return RefineOddEvenE(m, odd, even, ep, RefineSelected, cbOdd, cbEven);
}
/*!
* \brief Perform diadic subdivision using given rules for odd and even vertices.
*/
template<class MESH_TYPE, class ODD_VERT, class EVEN_VERT, class PREDICATE>
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<int> 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<MESH_TYPE>:: template AddPerVertexAttribute<int>(m);
odd.setValenceAttr(&valence);
even.setValenceAttr(&valence);
// store updated vertices
std::vector<bool> updatedList(m.vn, false);
std::vector<typename MESH_TYPE::VertexType> 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::Pos<typename MESH_TYPE::FaceType>aux (&(*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<typename MESH_TYPE::VertexType>::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<MESH_TYPE>::DeletePerVertexAttribute(m, valence);
return true;
}
} // namespace tri
} // namespace vcg
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,246 +0,0 @@
/****************************************************************************
* 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 <vcg/math/histogram.h>
#include <vcg/simplex/face/pos.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/closest.h>
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/complex/trimesh/allocate.h>
namespace vcg {
namespace tri{
template <class StatMeshType>
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<ScalarType> Box3Type;
static std::pair<float,float> ComputePerVertexQualityMinMax( MeshType & m) // V1.0
{
std::pair<float,float> minmax = std::make_pair(std::numeric_limits<float>::max(),-std::numeric_limits<float>::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<float,float> ComputePerFaceQualityMinMax( MeshType & m) // V1.0
{
std::pair<float,float> minmax = std::make_pair(std::numeric_limits<float>::max(),-std::numeric_limits<float>::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<ScalarType> ComputeShellBarycenter(MeshType & m)
{
Point3<ScalarType> 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<float> &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<float> &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<float,float> minmax = tri::Stat<MeshType>::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<float,float> 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<float> 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<float>((*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<float>((*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<float>((*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

View File

@ -1,171 +0,0 @@
/****************************************************************************
* 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 <vcg/complex/trimesh/update/flag.h>
namespace vcg {
namespace tri {
template <class I_MESH_TYPE>
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<o.v);
}
bool operator ==(const InsertedV & o)
{
return (v==o.v);
}
bool operator !=(const InsertedV & o)
{
return (v!=o.v);
}
};
// This function build a nesh from a subset of faces of another.
// @param : subSet, stl vector of face poitners.
// m, output mesh mesh.
template <class S_MESH_TYPE, class STL_CONT>
void SubSet(S_MESH_TYPE & m, STL_CONT & subSet)
{
std::vector< InsertedV<S_MESH_TYPE> > 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<typename S_MESH_TYPE::VertexPointer> redirect;
fi = vcg::tri::Allocator<S_MESH_TYPE>::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<S_MESH_TYPE>((*fi).V(ii), &(*fi),ii));
sort(newVertices.begin(), newVertices.end());
typename std::vector< InsertedV<S_MESH_TYPE> >::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<S_MESH_TYPE> >::iterator newE=unique(newVertices.begin(), newVertices.end());
vi = vcg::tri::Allocator<S_MESH_TYPE>::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