This commit is contained in:
ganovelli 2011-04-01 16:25:49 +00:00
parent 3262c530c8
commit 1ad23912db
59 changed files with 34805 additions and 0 deletions

View File

@ -0,0 +1,369 @@
#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

@ -0,0 +1,677 @@
#ifndef _4PCS_
#define _4PCS_
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/**
implementation of the 4PCS method from the paper:
"4-Points Congruent Sets for Robust Pairwise Surface Registration"
D.Aiger, N.Mitra D.Cohen-Or, SIGGRAPH 2008
ps: the name of the variables are out of vcg standard but like the one
used in the paper pseudocode.
*/
#include <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

@ -0,0 +1,788 @@
#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

@ -0,0 +1,404 @@
#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

@ -0,0 +1,160 @@
/****************************************************************************
* 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

@ -0,0 +1,563 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.27 2006/12/06 12:59:13 pietroni
added max distance to rayIterator
Revision 1.26 2006/11/21 16:06:54 ponchio
passing VDistFunct() to functions wanting a reference, not a value
(why a reference btw?)
Revision 1.25 2006/11/13 13:13:49 ponchio
Added usual typename.
Revision 1.24 2006/11/12 02:41:03 pietroni
added normalization of normal in DoRay functions
Revision 1.23 2006/11/10 11:41:49 pietroni
added DoRayFuntion that return interpolated normal
Revision 1.22 2006/09/20 17:18:26 ponchio
VDistFunct() at line 292 was passed as a temporary.
Invalid under g++. Fixed.
Revision 1.21 2006/02/09 08:38:04 pietroni
sintax error corrected
Revision 1.20 2006/02/08 17:02:41 pietroni
commented one GetClosestFace function ... the code is the same then getClosest that return barycentric coordinates
Revision 1.19 2006/01/10 13:31:54 pietroni
correct pass of variable closest_pt by reference in getclosestFace function
Revision 1.18 2005/12/02 00:13:34 cignoni
Added and removed typenames for gcc compiling.
removed also some template arguments specifcation that gcc disliked...
commented out GetInSphereFace and SetMesh that are probably never used and i didnt succeed in compile
Revision 1.17 2005/10/05 17:02:52 pietroni
corrected bugs on GEtKClosestVert and GetInSphereVert
Revision 1.16 2005/10/03 16:19:07 spinelli
fixed some bugs
Revision 1.15 2005/10/03 13:59:39 pietroni
added GetInSphere and GetInBox functions
rensmed Functions respectively with Face suffix or Vertex suffix for query on vertex or faces
Revision 1.14 2005/09/30 13:10:37 pietroni
used functor defined in face/distance.h for distance point-face
used functor defined in intersection3.h for ray-triangle intersection
added GetKClosest and DoRay Functions
Revision 1.13 2005/09/28 08:30:48 cignoni
changed name of include, removed use of an undefined type (scalar instead of Scalar)
removed unused code portions (the old closest code)
Revision 1.12 2005/09/21 09:24:30 pietroni
Added RayIterators.
Added ClosestIterators on Triangles and Vertices.
Added Closest Functions on triangles and Vertices.
Revision 1.11 2005/09/19 13:36:24 pietroni
added ray iterator of faces
Revision 1.10 2005/09/16 11:53:51 cignoni
Small gcc compliling issues
Revision 1.9 2005/09/15 13:16:10 spinelli
fixed bugs
Revision 1.8 2005/09/15 11:15:00 pietroni
minor changes
Revision 1.7 2005/09/14 12:56:47 pietroni
used closest function from grid
Revision 1.6 2005/08/26 09:12:48 cignoni
changed typedef A2UGridLink da 'GridStaticPtr<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

@ -0,0 +1,436 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2006 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.11 2006/06/08 13:55:16 cignoni
Added ColorPreserving Cellbase template.
Revision 1.10 2006/05/26 10:18:11 cignoni
Re-adapted to ms compilers
Revision 1.9 2006/05/25 09:37:14 cignoni
Many changes for the different interpretation of hash_set between gcc and .net. Probably to be completed.
Revision 1.8 2006/05/24 16:42:22 m_di_benedetto
Corrected bbox inflation amount in case of _cellsize != 0
Revision 1.7 2006/05/24 15:16:01 cignoni
better comment to the init parameters
Revision 1.6 2006/05/24 08:54:04 cignoni
Added missing std:: to swap
Revision 1.5 2006/05/21 06:40:31 cignoni
Added DoubleFace management
Revision 1.4 2006/05/19 20:49:03 m_di_benedetto
Added check for empty generated mesh (prevent call to mesh allocator with zero vertices or faces).
Revision 1.3 2006/05/18 22:20:53 m_di_benedetto
added check for deleted faces and modified/added std namespace qualifier.
Revision 1.2 2006/05/18 13:59:20 cignoni
Some minor optimizations
Revision 1.1 2006/05/16 21:56:06 cignoni
First working Version
****************************************************************************/
#ifndef __VCGLIB_CLUSTERING
#define __VCGLIB_CLUSTERING
#include<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

@ -0,0 +1,147 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2008 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_CREASE_CUT
#define __VCG_CREASE_CUT
#include<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

@ -0,0 +1,551 @@
#ifndef MLS_ADVANCE_H
#define MLS_ADVANCE_H
#include <iostream>
#include <list>
#include <wrap/callback.h>
#include <vcg/complex/trimesh/update/topology.h>
#include <vcg/complex/trimesh/update/flag.h>
#include <map>
namespace vcg {
namespace tri {
class FrontEdge {
public:
int v0, v1, v2; //v0, v1 represent the FrontEdge, v2 the other vertex
//in the face this FrontEdge belongs to
int face; //index of the face
bool active; //keep tracks of wether it is in front or in deads
//the loops in the front are mantained as a double linked list
std::list<FrontEdge>::iterator next;
std::list<FrontEdge>::iterator previous;
FrontEdge() {}
FrontEdge(int _v0, int _v1, int _v2, int _face):
v0(_v0), v1(_v1), v2(_v2), face(_face), active(true) {
assert(v0 != v1 && v1 != v2 && v0 != v2);
}
const bool operator==(const FrontEdge& f) const
{
return ((v0 == f.v0) && (v1 == f.v1) && (v2 == f.v2) && (face == f.face));
}
};
template <class MESH> class AdvancingFront {
public:
typedef typename MESH::VertexType VertexType;
typedef typename MESH::FaceType FaceType;
typedef typename MESH::ScalarType ScalarType;
typedef typename MESH::VertexType::CoordType Point3x;
//class FrontEdgeLists
//{
//};
// protected:
std::list<FrontEdge> front;
std::list<FrontEdge> deads;
std::vector<int> nb; //number of fronts a vertex is into,
//this is used for the Visited and Border flags
//but adding topology may not be needed anymore
public:
MESH &mesh; //this structure will be filled by the algorithm
AdvancingFront(MESH &_mesh): mesh(_mesh) {
UpdateFlags<MESH>::FaceBorderFromNone(mesh);
UpdateFlags<MESH>::VertexBorderFromFace(mesh);
nb.clear();
nb.resize(mesh.vert.size(), 0);
CreateLoops();
}
virtual ~AdvancingFront() {}
virtual ScalarType radi() { return 0; }
void BuildMesh(CallBackPos call = NULL, int interval = 512) {
float finalfacesext = mesh.vert.size() * 2.0f;
if(call) call(0, "Advancing front");
while(1) {
for(int i = 0; i < interval; i++) {
if(!front.size() && !SeedFace()) return;
AddFace();
if(call)
{
float rap = float(mesh.face.size()) / finalfacesext;
int perc = (int) (100.0f * rap);
(*call)(perc,"Adding Faces");
}
}
}
}
protected:
//Implement these functions in your subclass
enum ListID {FRONT,DEADS};
typedef std::pair< ListID,std::list<FrontEdge>::iterator > ResultIterator;
virtual bool Seed(int &v0, int &v1, int &v2) = 0;
virtual int Place(FrontEdge &e, ResultIterator &touch) = 0;
bool CheckFrontEdge(int v0, int v1) {
int tot = 0;
//HACK to speed up things until i can use a seach structure
// int i = mesh.face.size() - 4*(front.size());
// if(front.size() < 100) i = mesh.face.size() - 100;
int i = 0;
if(i < 0) i = 0;
for(; i < (int)mesh.face.size(); i++) {
FaceType &f = mesh.face[i];
for(int k = 0; k < 3; k++) {
if(v1== (int)f.V(k) && v0 == (int)f.V((k+1)%3)) ++tot;
else if(v0 == (int)f.V(k) && v1 == (int)f.V((k+1)%3)) { //orientation non constistent
return false;
}
}
if(tot >= 2) { //non manifold
return false;
}
}
return true;
}
//create the FrontEdge loops from seed faces
void CreateLoops() {
VertexType *start = &*mesh.vert.begin();
for(int i = 0; i < (int)mesh.face.size(); i++) {
FaceType &f = mesh.face[i];
if(f.IsD()) continue;
for(int k = 0; k < 3; k++) {
if(f.IsB(k)) {
NewEdge(FrontEdge(f.V0(k) - start, f.V1(k) - start, f.V2(k) - start, i));
nb[f.V0(k)-start]++;
}
}
}
for(std::list<FrontEdge>::iterator s = front.begin(); s != front.end(); s++) {
(*s).previous = front.end();
(*s).next = front.end();
}
//now create loops:
for(std::list<FrontEdge>::iterator s = front.begin(); s != front.end(); s++) {
for(std::list<FrontEdge>::iterator j = front.begin(); j != front.end(); j++) {
if(s == j) continue;
if((*s).v1 != (*j).v0) continue;
if((*j).previous != front.end()) continue;
(*s).next = j;
(*j).previous = s;
break;
}
}
for(std::list<FrontEdge>::iterator s = front.begin(); s != front.end(); s++) {
assert((*s).next != front.end());
assert((*s).previous != front.end());
}
}
bool SeedFace() {
int v[3];
bool success = Seed(v[0], v[1], v[2]);
if(!success) return false;
nb.resize(mesh.vert.size(), 0);
//create the border of the first face
std::list<FrontEdge>::iterator e = front.end();
std::list<FrontEdge>::iterator last = e;
std::list<FrontEdge>::iterator first;
for(int i = 0; i < 3; i++) {
int v0 = v[i];
int v1 = v[((i+1)%3)];
int v2 = v[((i+2)%3)];
mesh.vert[v0].SetB();
nb[v[i]]++;
e = front.insert(front.begin(), FrontEdge(v0, v1, v2, mesh.face.size()));
if(i != 0) {
(*last).next = e;
(*e).previous = last;
} else
first = e;
last = e;
}
//connect last and first
(*last).next = first;
(*first).previous = last;
AddFace(v[0], v[1], v[2]);
return true;
}
public:
bool AddFace() {
if(!front.size()) return false;
std::list<FrontEdge>::iterator ei = front.begin();
FrontEdge &current = *ei;
FrontEdge &previous = *current.previous;
FrontEdge &next = *current.next;
int v0 = current.v0, v1 = current.v1;
assert(nb[v0] < 10 && nb[v1] < 10);
ResultIterator touch;
touch.first = FRONT;
touch.second = front.end();
int v2 = Place(current, touch);
if(v2 == -1) {
KillEdge(ei);
return false;
}
assert(v2 != v0 && v2 != v1);
if ((touch.first == FRONT) && (touch.second != front.end()) ||
(touch.first == DEADS) && (touch.second != deads.end()))
{
//check for orientation and manifoldness
//touch == current.previous?
if(v2 == previous.v0) {
if(!CheckEdge(v2, v1)) {
KillEdge(ei);
return false;
}
/*touching previous FrontEdge (we reuse previous)
next
------->v2 -----> v1------>
\ /
\ /
previous \ / current
\ /
v0 */
Detach(v0);
std::list<FrontEdge>::iterator up = NewEdge(FrontEdge(v2, v1, v0, mesh.face.size()));
MoveFront(up);
(*up).previous = previous.previous;
(*up).next = current.next;
(*previous.previous).next = up;
next.previous = up;
Erase(current.previous);
Erase(ei);
Glue(up);
//touch == (*current.next).next
} else if(v2 == next.v1) {
if(!CheckEdge(v0, v2)) {
KillEdge(ei);
return false;
}
/*touching next FrontEdge (we reuse next)
previous
------->v0 -----> v2------>
\ /
\ /
\ / next
\ /
v1 */
Detach(v1);
std::list<FrontEdge>::iterator up = NewEdge(FrontEdge(v0, v2, v1, mesh.face.size()));
MoveFront(up);
(*up).previous = current.previous;
(*up).next = (*current.next).next;
previous.next = up;
(*next.next).previous = up;
Erase(current.next);
Erase(ei);
Glue(up);
} else {
if(!CheckEdge(v0, v2) || !CheckEdge(v2, v1)) {
KillEdge(ei);
return false;
}
//touching some loop: split (or merge it is local does not matter.
//like this
/*
left right
<--------v2-<------
/|\
/ \
up / \ down
/ \
/ V
----v0 - - - > v1---------
current */
std::list<FrontEdge>::iterator left = touch.second;
std::list<FrontEdge>::iterator right = (*touch.second).previous;
//this would be a really bad join
if(v1 == (*right).v0 || v0 == (*left).v1) {
KillEdge(ei);
return false;
}
nb[v2]++;
std::list<FrontEdge>::iterator down = NewEdge(FrontEdge(v2, v1, v0, mesh.face.size()));
std::list<FrontEdge>::iterator up = NewEdge(FrontEdge(v0, v2, v1, mesh.face.size()));
(*right).next = down;
(*down).previous = right;
(*down).next = current.next;
next.previous = down;
(*left).previous = up;
(*up).next = left;
(*up).previous = current.previous;
previous.next = up;
Erase(ei);
}
}
else if ((touch.first == FRONT) && (touch.second == front.end()) ||
(touch.first == DEADS) && (touch.second == deads.end()))
{
// assert(CheckEdge(v0, v2));
// assert(CheckEdge(v2, v1));
/* adding a new vertex
v2
/|\
/ \
up / \ down
/ \
/ V
----v0 - - - > v1--------- */
assert(!mesh.vert[v2].IsB()); //fatal error! a new point is already a border?
nb[v2]++;
mesh.vert[v2].SetB();
std::list<FrontEdge>::iterator down = NewEdge(FrontEdge(v2, v1, v0, mesh.face.size()));
std::list<FrontEdge>::iterator up = NewEdge(FrontEdge(v0, v2, v1, mesh.face.size()));
(*down).previous = up;
(*up).next = down;
(*down).next = current.next;
next.previous = down;
(*up).previous = current.previous;
previous.next = up;
Erase(ei);
}
AddFace(v0, v2, v1);
return false;
}
protected:
void AddFace(int v0, int v1, int v2) {
assert(v0 < (int)mesh.vert.size() && v1 < (int)mesh.vert.size() && v2 < (int)mesh.vert.size());
FaceType face;
face.V(0) = &mesh.vert[v0];
face.V(1) = &mesh.vert[v1];
face.V(2) = &mesh.vert[v2];
ComputeNormalizedNormal(face);
mesh.face.push_back(face);
mesh.fn++;
}
void AddVertex(VertexType &vertex) {
VertexType *oldstart = NULL;
if(mesh.vert.size()) oldstart = &*mesh.vert.begin();
mesh.vert.push_back(vertex);
mesh.vn++;
VertexType *newstart = &*mesh.vert.begin();
if(oldstart && oldstart != newstart) {
for(int i = 0; i < mesh.face.size(); i++) {
FaceType &face = mesh.face[i];
for(int k = 0; k < 3; k++)
face.V(k) = newstart + (face.V(k) - oldstart);
}
}
nb.push_back(0);
}
bool CheckEdge(int v0, int v1) {
int tot = 0;
//HACK to speed up things until i can use a seach structure
/* int i = mesh.face.size() - 4*(front.size());
if(front.size() < 100) i = mesh.face.size() - 100;
if(i < 0) i = 0;*/
VertexType *vv0 = &(mesh.vert[v0]);
VertexType *vv1 = &(mesh.vert[v1]);
for(int i = 0; i < (int)mesh.face.size(); i++) {
FaceType &f = mesh.face[i];
for(int k = 0; k < 3; k++) {
if(vv0 == f.V0(k) && vv1 == f.V1(k)) //orientation non constistent
return false;
else if(vv1 == f.V0(k) && vv0 == f.V1(k)) ++tot;
}
if(tot >= 2) { //non manifold
return false;
}
}
return true;
}
//front management:
//Add a new FrontEdge to the back of the queue
std::list<FrontEdge>::iterator NewEdge(FrontEdge e) {
return front.insert(front.end(), e);
}
//move an Edge among the dead ones
void KillEdge(std::list<FrontEdge>::iterator e)
{
if (e->active)
{
(*e).active = false;
//std::list<FrontEdge>::iterator res = std::find(front.begin(),front.end(),e);
FrontEdge tmp = *e;
deads.splice(deads.end(), front, e);
std::list<FrontEdge>::iterator newe = std::find(deads.begin(),deads.end(),tmp);
tmp.previous->next = newe;
tmp.next->previous = newe;
}
}
void Erase(std::list<FrontEdge>::iterator e) {
if((*e).active) front.erase(e);
else deads.erase(e);
}
//move an FrontEdge to the back of the queue
void MoveBack(std::list<FrontEdge>::iterator e) {
front.splice(front.end(), front, e);
}
void MoveFront(std::list<FrontEdge>::iterator e) {
front.splice(front.begin(), front, e);
}
//check if e can be sewed with one of oits neighbours
bool Glue(std::list<FrontEdge>::iterator e) {
return Glue((*e).previous, e) || Glue(e, (*e).next);
}
//Glue toghether a and b (where a.next = b
bool Glue(std::list<FrontEdge>::iterator a, std::list<FrontEdge>::iterator b) {
if((*a).v0 != (*b).v1) return false;
std::list<FrontEdge>::iterator previous = (*a).previous;
std::list<FrontEdge>::iterator next = (*b).next;
(*previous).next = next;
(*next).previous = previous;
Detach((*a).v1);
Detach((*a).v0);
Erase(a);
Erase(b);
return true;
}
void Detach(int v) {
assert(nb[v] > 0);
if(--nb[v] == 0) {
mesh.vert[v].ClearB();
}
}
};
template <class MESH> class AdvancingTest: public AdvancingFront<MESH> {
public:
typedef typename MESH::VertexType VertexType;
typedef typename MESH::VertexIterator VertexIterator;
typedef typename MESH::FaceType FaceType;
typedef typename MESH::FaceIterator FaceIterator;
typedef typename MESH::ScalarType ScalarType;
typedef typename MESH::VertexType::CoordType Point3x;
AdvancingTest(MESH &_mesh): AdvancingFront<MESH>(_mesh) {}
bool Seed(int &v0, int &v1, int &v2) {
VertexType v[3];
v[0].P() = Point3x(0, 0, 0);
v[1].P() = Point3x(1, 0, 0);
v[2].P() = Point3x(0, 1, 0);
v[0].ClearFlags();
v[1].ClearFlags();
v[2].ClearFlags();
v0 = this->mesh.vert.size();
AddVertex(v[0]);
v1 = this->mesh.vert.size();
AddVertex(v[1]);
v2 = this->mesh.vert.size();
AddVertex(v[2]);
return true;
}
int Place(FrontEdge &e, typename AdvancingFront<MESH>::ResultIterator &touch)
{
Point3f p[3];
p[0] = this->mesh.vert[e.v0].P();
p[1] = this->mesh.vert[e.v1].P();
p[2] = this->mesh.vert[e.v2].P();
Point3f point = p[0] + p[1] - p[2];
int vn = this->mesh.vert.size();
for(int i = 0; i < this->mesh.vert.size(); i++)
{
if((this->mesh.vert[i].P() - point).Norm() < 0.1)
{
vn = i;
//find the border
assert(this->mesh.vert[i].IsB());
for(std::list<FrontEdge>::iterator k = this->front.begin(); k != this->front.end(); k++)
if((*k).v0 == i)
{
touch.first = AdvancingFront<MESH>::FRONT;
touch.second = k;
}
for(std::list<FrontEdge>::iterator k = this->deads.begin(); k != this->deads.end(); k++)
if((*k).v0 == i)
if((*k).v0 == i)
{
touch.first = AdvancingFront<MESH>::FRONT;
touch.second = k;
}
break;
}
}
if(vn == this->mesh.vert.size()) {
VertexType v;
v.P() = point;
v.ClearFlags();
AddVertex(v);
}
return vn;
}
};
}//namespace tri
}//namespace vcg
#endif

View File

@ -0,0 +1,418 @@
#ifndef BALL_PIVOTING_H
#define BALL_PIVOTING_H
#include "advancing_front.h"
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/complex/trimesh/closest.h>
/* Ball pivoting algorithm:
1) the vertices used in the new mesh are marked as visited
2) the border vertices of the new mesh are marked as border
3) the vector nb is used to keep track of the number of borders a vertex belongs to
4) usedBit flag is used to select the points in the mesh already processed
*/
namespace vcg {
namespace tri {
template <class MESH> class BallPivoting: public AdvancingFront<MESH> {
public:
typedef typename MESH::VertexType VertexType;
typedef typename MESH::FaceType FaceType;
typedef typename MESH::ScalarType ScalarType;
typedef typename MESH::VertexIterator VertexIterator;
typedef typename MESH::VertexType::CoordType Point3x;
typedef GridStaticPtr<typename MESH::VertexType, typename MESH::ScalarType > StaticGrid;
float radius; //radius of the ball
float min_edge; //min lenght of an edge
float max_edge; //min lenght of an edge
float max_angle; //max angle between 2 faces (cos(angle) actually)
public:
ScalarType radi() { return radius; }
// if radius ==0 an autoguess for the ball pivoting radius is attempted
// otherwise the passed value (in absolute mesh units) is used.
BallPivoting(MESH &_mesh, float _radius = 0,
float minr = 0.2, float angle = M_PI/2):
AdvancingFront<MESH>(_mesh), radius(_radius),
min_edge(minr), max_edge(1.8), max_angle(cos(angle)),
last_seed(-1) {
//compute bbox
baricenter = Point3x(0, 0, 0);
UpdateBounding<MESH>::Box(_mesh);
for(VertexIterator vi=this->mesh.vert.begin();vi!=this->mesh.vert.end();++vi)
if( !(*vi).IsD() ) baricenter += (*vi).P();
baricenter /= this->mesh.vn;
assert(this->mesh.vn > 3);
if(radius == 0) // radius ==0 means that an auto guess should be attempted.
radius = sqrt((this->mesh.bbox.Diag()*this->mesh.bbox.Diag())/this->mesh.vn);
min_edge *= radius;
max_edge *= radius;
//enlarging the bbox for out-of-box queries
Box3<ScalarType> BPbbox=this->mesh.bbox;
BPbbox.Offset(4*radius);
grid.Set(this->mesh.vert.begin(), this->mesh.vert.end(), BPbbox);
//mark visited points
std::vector<VertexType *> targets;
std::vector<Point3x> points;
std::vector<ScalarType> dists;
usedBit = VertexType::NewBitFlag();
for(int i = 0; i < (int)this->mesh.vert.size(); i++)
this->mesh.vert[i].ClearUserBit(usedBit);
UpdateFlags<MESH>::VertexClearV(this->mesh);
for(int i = 0; i < (int)this->mesh.face.size(); i++) {
FaceType &f = this->mesh.face[i];
if(f.IsD()) continue;
for(int k = 0; k < 3; k++) {
f.V(k)->SetV();
int n = tri::GetInSphereVertex(this->mesh, grid, f.V(k)->P(), min_edge, targets, dists, points);
for(int t = 0; t < n; t++) {
targets[t]->SetUserBit(usedBit);
assert(targets[t]->IsUserBit(usedBit));
}
assert(f.V(k)->IsUserBit(usedBit));
}
}
}
~BallPivoting() {
VertexType::DeleteBitFlag(usedBit);
}
bool Seed(int &v0, int &v1, int &v2) {
//get a sphere of neighbours
std::vector<VertexType *> targets;
std::vector<Point3x> points;
std::vector<ScalarType> dists;
while(++last_seed < (int)(this->mesh.vert.size())) {
VertexType &seed = this->mesh.vert[last_seed];
if(seed.IsD() || seed.IsUserBit(usedBit)) continue;
seed.SetUserBit(usedBit);
int n = tri::GetInSphereVertex(this->mesh, grid, seed.P(), 2*radius, targets, dists, points);
if(n < 3) {
continue;
}
bool success = true;
//find the closest visited or boundary
for(int i = 0; i < n; i++) {
VertexType &v = *(targets[i]);
if(v.IsV()) {
success = false;
break;
}
}
if(!success) continue;
VertexType *vv0, *vv1, *vv2;
success = false;
//find a triplet that does not contains any other point
Point3x center;
for(int i = 0; i < n; i++) {
vv0 = targets[i];
if(vv0->IsD()) continue;
Point3x &p0 = vv0->P();
for(int k = i+1; k < n; k++) {
vv1 = targets[k];
if(vv1->IsD()) continue;
Point3x &p1 = vv1->P();
float d2 = (p1 - p0).Norm();
if(d2 < min_edge || d2 > max_edge) continue;
for(int j = k+1; j < n; j++) {
vv2 = targets[j];
if(vv2->IsD()) continue;
Point3x &p2 = vv2->P();
float d1 = (p2 - p0).Norm();
if(d1 < min_edge || d1 > max_edge) continue;
float d0 = (p2 - p1).Norm();
if(d0 < min_edge || d0 > max_edge) continue;
Point3x normal = (p1 - p0)^(p2 - p0);
if(normal.dot(p0 - baricenter) < 0) continue;
/* if(use_normals) {
if(normal * vv0->N() < 0) continue;
if(normal * vv1->N() < 0) continue;
if(normal * vv2->N() < 0) continue;
}*/
if(!FindSphere(p0, p1, p2, center)) {
continue;
}
//check no other point inside
int t;
for(t = 0; t < n; t++) {
if((center - targets[t]->P()).Norm() <= radius)
break;
}
if(t < n) {
continue;
}
//check on the other side there is not a surface
Point3x opposite = center + normal*(((center - p0).dot(normal))*2/normal.SquaredNorm());
for(t = 0; t < n; t++) {
VertexType &v = *(targets[t]);
if((v.IsV()) && (opposite - v.P()).Norm() <= radius)
break;
}
if(t < n) {
continue;
}
success = true;
i = k = j = n;
}
}
}
if(!success) { //see bad luck above
continue;
}
Mark(vv0);
Mark(vv1);
Mark(vv2);
v0 = vv0 - &*this->mesh.vert.begin();
v1 = vv1 - &*this->mesh.vert.begin();
v2 = vv2 - &*this->mesh.vert.begin();
return true;
}
return false;
}
//select a new vertex, mark as Visited and mark as usedBit all neighbours (less than min_edge)
int Place(FrontEdge &edge,typename AdvancingFront<MESH>::ResultIterator &touch) {
Point3x v0 = this->mesh.vert[edge.v0].P();
Point3x v1 = this->mesh.vert[edge.v1].P();
Point3x v2 = this->mesh.vert[edge.v2].P();
/* TODO why using the face normals everything goes wrong? should be
exactly the same................................................
Point3x &normal = mesh.face[edge.face].N(); ?
*/
Point3x normal = ((v1 - v0)^(v2 - v0)).Normalize();
Point3x middle = (v0 + v1)/2;
Point3x center;
if(!FindSphere(v0, v1, v2, center)) {
// assert(0);
return -1;
}
Point3x start_pivot = center - middle;
Point3x axis = (v1 - v0);
ScalarType axis_len = axis.SquaredNorm();
if(axis_len > 4*radius*radius) {
return -1;
}
axis.Normalize();
// r is the radius of the thorus of all possible spheres passing throug v0 and v1
ScalarType r = sqrt(radius*radius - axis_len/4);
std::vector<VertexType *> targets;
std::vector<ScalarType> dists;
std::vector<Point3x> points;
tri::GetInSphereVertex(this->mesh, grid, middle, r + radius, targets, dists, points);
if(targets.size() == 0) {
return -1; //this really would be strange but one never knows.
}
VertexType *candidate = NULL;
ScalarType min_angle = M_PI;
for(int i = 0; i < static_cast<int>(targets.size()); i++) {
VertexType *v = targets[i];
int id = v - &*this->mesh.vert.begin();
if(v->IsD()) continue;
// this should always be true IsB => IsV , IsV => IsU
if(v->IsB()) assert(v->IsV());
if(v->IsV()) assert(v->IsUserBit(usedBit));
if(v->IsUserBit(usedBit) && !(v->IsB())) continue;
if(id == edge.v0 || id == edge.v1 || id == edge.v2) continue;
Point3x p = this->mesh.vert[id].P();
/* Find the sphere through v0, p, v1 (store center on end_pivot */
if(!FindSphere(v0, p, v1, center)) {
continue;
}
/* Angle between old center and new center */
ScalarType alpha = Angle(start_pivot, center - middle, axis);
/* adding a small bias to already chosen vertices.
doesn't solve numerical problems, but helps. */
// if(this->mesh.vert[id].IsB()) alpha -= 0.001;
/* Sometimes alpha might be little less then M_PI while it should be 0,
by numerical errors: happens for example pivoting
on the diagonal of a square. */
/* if(alpha > 2*M_PI - 0.8) {
// Angle between old center and new *point*
//TODO is this really overshooting? shouldbe enough to alpha -= 2*M_PI
Point3x proj = p - axis * (axis * p - axis * middle);
ScalarType beta = angle(start_pivot, proj - middle, axis);
if(alpha > beta) alpha -= 2*M_PI;
} */
if(candidate == NULL || alpha < min_angle) {
candidate = v;
min_angle = alpha;
}
}
if(min_angle >= M_PI - 0.1) {
return -1;
}
if(candidate == NULL) {
return -1;
}
if(!candidate->IsB()) {
assert((candidate->P() - v0).Norm() > min_edge);
assert((candidate->P() - v1).Norm() > min_edge);
}
int id = candidate - &*this->mesh.vert.begin();
assert(id != edge.v0 && id != edge.v1);
Point3x newnormal = ((candidate->P() - v0)^(v1 - v0)).Normalize();
if(normal.dot(newnormal) < max_angle || this->nb[id] >= 2) {
return -1;
}
//test if id is in some border (to return touch
for(std::list<FrontEdge>::iterator k = this->front.begin(); k != this->front.end(); k++)
{
if((*k).v0 == id)
{
touch.first = AdvancingFront<MESH>::FRONT;
touch.second = k;
}
}
for(std::list<FrontEdge>::iterator k = this->deads.begin(); k != this->deads.end(); k++)
{
if((*k).v0 == id)
{
touch.first = AdvancingFront<MESH>::DEADS;
touch.second = k;
}
}
//mark vertices close to candidate
Mark(candidate);
return id;
}
private:
int last_seed; //used for new seeds when front is empty
int usedBit; //use to detect if a vertex has been already processed.
Point3x baricenter;//used for the first seed.
StaticGrid grid; //lookup grid for points
/* returns the sphere touching p0, p1, p2 of radius r such that
the normal of the face points toward the center of the sphere */
bool FindSphere(Point3x &p0, Point3x &p1, Point3x &p2, Point3x &center) {
//we want p0 to be always the smallest one.
Point3x p[3];
if(p0 < p1 && p0 < p2) {
p[0] = p0;
p[1] = p1;
p[2] = p2;
} else if(p1 < p0 && p1 < p2) {
p[0] = p1;
p[1] = p2;
p[2] = p0;
} else {
p[0] = p2;
p[1] = p0;
p[2] = p1;
}
Point3x q1 = p[1] - p[0];
Point3x q2 = p[2] - p[0];
Point3x up = q1^q2;
ScalarType uplen = up.Norm();
//the three points are aligned
if(uplen < 0.001*q1.Norm()*q2.Norm()) {
return false;
}
up /= uplen;
ScalarType a11 = q1.dot(q1);
ScalarType a12 = q1.dot(q2);
ScalarType a22 = q2.dot(q2);
ScalarType m = 4*(a11*a22 - a12*a12);
ScalarType l1 = 2*(a11*a22 - a22*a12)/m;
ScalarType l2 = 2*(a11*a22 - a12*a11)/m;
center = q1*l1 + q2*l2;
ScalarType circle_r = center.Norm();
if(circle_r > radius) {
return false; //need too big a sphere
}
ScalarType height = sqrt(radius*radius - circle_r*circle_r);
center += p[0] + up*height;
return true;
}
/* compute angle from p to q, using axis for orientation */
ScalarType Angle(Point3x p, Point3x q, Point3x &axis) {
p.Normalize();
q.Normalize();
Point3x vec = p^q;
ScalarType angle = acos(p.dot(q));
if(vec.dot(axis) < 0) angle = -angle;
if(angle < 0) angle += 2*M_PI;
return angle;
}
void Mark(VertexType *v) {
std::vector<VertexType *> targets;
std::vector<Point3x> points;
std::vector<ScalarType> dists;
int n = tri::GetInSphereVertex(this->mesh, grid, v->P(), min_edge, targets, dists, points);
for(int t = 0; t < n; t++)
targets[t]->SetUserBit(usedBit);
v->SetV();
}
};
} //namespace tri
} //namespace vcg
#endif

View File

@ -0,0 +1,928 @@
/*===========================================================================*\
* *
* IsoEx *
* Copyright (C) 2002 by Computer Graphics Group, RWTH Aachen *
* www.rwth-graphics.de *
* *
*---------------------------------------------------------------------------*
* *
* License *
* *
* This library is free software; you can redistribute it and/or modify it *
* under the terms of the GNU Library General Public License as published *
* by the Free Software Foundation, version 2. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
\*===========================================================================*/
//== TABLES ==================================================================
#ifndef __VCG_EMC_LOOK_UP_TABLE
#define __VCG_EMC_LOOK_UP_TABLE
namespace vcg
{
namespace tri
{
class EMCLookUpTable
{
public:
static const int EdgeTable(unsigned char cubetype)
{
static const int edgeTable[256]=
{
0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0
};
return edgeTable[cubetype];
}; // end of EdgeTable
//-----------------------------------------------------------------------------
static int* TriTable(unsigned char cubetype, int u)
{
static int triTable[256][2][17] =
{{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 1, 9, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 2, 10, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 10, 9, 8, 3, 2 , -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 0, 8, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 10 */
{{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 1, 9, 0, 2, 3,11, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 9, 8, 11, 2, 1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 3, 11,10, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 8, 11, 10, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 11,10, 9, 0, 3, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 15 */
{{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 8, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 4, 7, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 7, 3, 1, 9, 4, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 20 */
{{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 1, 2,10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 3, 0, 4, 7, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}},
{{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 2,10, 9, 0, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}},
{{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1, -1},
{1, 6, 7, 3, 2,10, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 25 */
{{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5, 2, 0, 4, 7,11,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1}},
{{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 1, 9, 11, 11,9,4,7, -1, -1, -1, -1, -1 ,-1}},
{{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 3, 11,10, 1, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}},
{{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1, -1},
{1, 6, 1, 0, 4, 7,11,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 30 */
{{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1, -1},
{2, 3, 5, 4, 7, 8, 0, 3, 11, 10, 9, -1, -1, -1, -1, -1, -1}},
{{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 4, 7,11,10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 0, 1, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 35 */
{{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 3, 1, 5, 4, 8,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 1, 2,10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1}},
{{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 4, 0, 2,10, 5,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 10, 5, 3, 4, 8, 3, 5, -1, -1, -1, -1, -1, -1}},
/* 40 */
{{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 0, 8, 11, 2, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}},
{{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 0, 1, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}},
{{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1, -1},
{1, 6, 2, 1, 5, 4, 8,11, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 4, 3, 3,11,10, 1, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}},
/* 45 */
{{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1, -1},
{2, 3, 5, 4, 9, 5, 1, 0, 8,11, 10, -1, -1, -1, -1, -1, -1}},
{{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1, -1},
{1, 6, 5, 4, 0, 3,11, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 5, 4, 8, 11, 10,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 7, 8, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 5, 7, 3, 0, 9,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 50 */
{{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 1, 5, 7, 8, 0,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 3, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 7, 8, 9, 5,10, 1, 2, -1, -1, -1, -1, -1, -1, -1}},
{{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1, -1},
{ 2, 3, 5,10, 1, 2, 0, 9, 5, 7, 3,-1, -1, -1, -1, -1, -1}},
{{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1, -1},
{1, 6, 2,10, 5, 7, 8, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 55 */
{{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 2,10, 5, 7, 3,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 7, 8, 9, 5, 3,11, 2, -1, -1, -1, -1, -1, -1, -1}},
{{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1, -1},
{1, 6, 2, 0, 9, 5, 7,11, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1, -1},
{2, 3, 5, 2, 3,11, 8, 0, 1, 5, 7, -1, -1, -1, -1, -1, -1}},
{{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5,11, 2, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 60 */
{{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1, -1},
{2, 4, 4, 3,11, 10, 1, 5, 7, 8, 9, -1, -1, -1, -1, -1, -1}},
{{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1, -1},
{1, 7, 5, 7, 11,10, 1, 0, 9, -1, -1, -1, -1, -1, -1, -1, -1}},
{{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1, -1},
{1, 7, 11,10,5, 7, 8, 0,3, -1, -1, -1, -1, -1, -1, -1, -1}},
{{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 5, 7, 11,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 3,10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 65 */
{{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 1, 9, 8, 3, 5,10, 6, -1, -1, -1, -1, -1, -1, -1}},
{{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 1, 2, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 1, 2, 6, 5, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}},
/* 70 */
{{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 0, 2, 6, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1, -1},
{1, 6, 2, 6, 5, 9, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 2, 3,11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}},
{{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 4, 3, 0, 8, 11, 2, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}},
{{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}},
/* 75 */
{{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1, -1},
{2, 3, 5, 5,10, 6, 2, 1, 9, 8,11, -1, -1, -1, -1, -1, -1}},
{{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 5, 1, 3, 11,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1, -1},
{1, 6, 5, 1, 0, 8,11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1, -1},
{2, 4, 4, 3, 11, 6, 0, 5, 9, 0, 6, -1, -1, -1, -1}},
{{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 6, 5, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 80 */
{{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 5,10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 4, 7, 3, 0, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}},
{{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 1, 9, 0, 5,10, 6, 8, 4, 7, -1, -1, -1, -1}},
{{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1, -1},
{ 2, 3, 5,10, 6, 5, 9, 4, 7, 3, 1,-1, -1, -1, -1, -1, -1}},
{{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 1, 2, 6, 5, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}},
/* 85 */
{{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 6, 5, 1, 3, 0, 4, 7, -1, -1, -1, -1, -1, -1}},
{{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1, -1},
{2, 3, 5, 8, 4, 7, 5, 9, 0, 2, 6, -1, -1, -1, -1, -1, -1}},
{{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1, -1},
{1, 7, 7, 3, 2, 6, 5, 9, 4,-1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 3,11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1}},
{{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1, -1},
{2, 3, 5, 5,10, 6, 7,11, 2, 0, 4, -1, -1, -1, -1, -1, -1}},
/* 90 */
{{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1},
{4, 3, 3, 3, 3, 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6}},
{{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1, -1},
{3, 4, 4, 3, 2, 1, 9,11, 4, 7, 11, 9, 5, 10, 6, -1, -1}},
{{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1, -1},
{2, 3, 5, 8, 4, 7, 11, 6, 5, 1, 3, -1, -1, -1, -1, -1, -1}},
{{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1, -1},
{1, 7, 5, 1, 0, 4, 7,11, 6, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1, -1},
{3, 4, 4, 3, 0, 6, 5, 9, 3, 11, 6, 0, 8, 4, 7, -1, -1}},
/* 95 */
{{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1, -1},
{2, 4, 4, 9, 4, 7, 11, 6, 5, 9, 11,-1, -1, -1, -1, -1, -1}},
{{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 4, 9, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 4, 9,10, 6, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}},
{{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5, 6, 4, 0, 1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1, -1},
{1, 6, 1,10, 6, 4, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 100 */
{{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 2, 6, 4, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1, -1},
{2, 3, 5, 3, 0, 8, 9, 1, 2, 6, 4, -1, -1, -1, -1, -1, -1}},
{{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 2, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 8, 3, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 4, 3, 10, 6, 4, 9,11, 2, 3, -1, -1, -1, -1, -1, -1, -1}},
/* 105 */
{{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 11, 8, 0, 10, 6, 4, 9, -1, -1, -1, -1, -1, -1}},
{{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1, -1},
{2, 3, 5, 3,11, 2, 1, 10,6, 4, 0, -1, -1, -1, -1, -1, -1}},
{{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1, -1},
{1, 7, 6, 4, 8,11, 2, 1,10, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1, -1},
{1, 6, 3,11, 6, 4, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1, -1},
{1, 7, 8,11, 6, 4, 9, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 110 */
{{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 3,11, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 8, 11, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 8, 9,10, 6, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1, -1},
{1, 6, 0, 9, 10, 6, 7, 3, -1,-1,-1, -1, -1, -1, -1, -1, -1}},
{{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1, -1},
{ 2, 4, 4, 8, 0, 1, 7, 10, 6, 7, 1,-1, -1, -1, -1, -1, -1}},
/* 115 */
{{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5, 10, 6, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1, -1},
{1, 6, 1, 2, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1, -1},
{1, 7, 2, 6, 7, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 7, 8, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 7, 3, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 120 */
{{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1, -1},
{2, 3, 5, 2, 3,11, 6, 7, 8, 9,10, -1, -1, -1, -1, -1, -1}},
{{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1, -1},
{1, 7, 2, 0, 9,10,6, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1, -1},
{3, 4, 4, 3, 8, 0, 1, 7, 10, 6, 7, 1, 11, 2, 3, -1, -1}},
{{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1, -1},
{ 2, 4, 4, 11, 2, 1,7, 1, 10, 6, 7,-1, -1, -1, -1, -1, -1}},
{{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1, -1},
{1, 7, 8, 9, 1, 3, 11, 6, 7,-1, -1, -1, -1, -1, -1, -1, -1}},
/* 125 */
{{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1, -1},
{2, 4, 4, 0, 3,11, 6, 7, 8, 0, 6, -1, -1, -1, -1, -1, -1}},
{{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 130 */
{{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 1, 9, 8, 3,11, 7, 6, -1, -1, -1, -1, -1, -1, -1}},
{{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 3, 3,10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 1, 2,10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1}},
{{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 2, 10, 9, 0, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}},
/* 135 */
{{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1, -1},
{2, 3, 5, 6, 11, 7, 3, 2,10, 9, 8, -1, -1, -1, -1, -1, -1}},
{{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 2, 3, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 6, 2, 0, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 2, 3, 7, 6, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}},
{{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1, -1},
{1, 6, 6, 2, 1, 9, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 140 */
{{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5, 1, 3, 7, 6,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1, -1},
{ 2, 4, 4, 10, 1, 7, 6, 8, 7, 1, 0,-1, -1, -1, -1, -1, -1}},
{{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1, -1},
{1, 6,10, 9, 0, 3, 7, 6,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 7, 6, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 6, 11, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 145 */
{{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 0, 4, 6,11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 6,11, 8, 4, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1, -1},
{1, 6, 6,11, 3, 1, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 6, 11, 8, 4, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1, -1},
{2, 3, 5, 1, 2, 10,11, 3,0,4, 6, -1, -1, -1, -1, -1, -1}},
/* 150 */
{{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1, -1},
{2, 4, 4, 4, 6, 11, 8, 2,10, 9, 0, -1, -1, -1, -1, -1, -1}},
{{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1, -1},
{1, 7, 10,9, 4, 6, 11, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 4, 6, 2, 3, 8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 4, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1, -1},
{2, 3, 5, 1, 9, 0, 3, 8, 4, 6, 2, -1, -1, -1, -1, -1, -1}},
/* 155 */
{{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 1, 9, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1, -1},
{1, 6, 1, 3, 8, 4, 6,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5,10, 1,0,4,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1, -1},
{1, 7, 4, 6, 10, 9, 0,3, 8, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 4, 6, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 160 */
{{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1}},
{{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 0, 1, 5, 4, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}},
{{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1, -1},
{ 2, 3, 5,11, 7, 6, 4, 8, 3, 1, 5,-1, -1, -1, -1, -1, -1}},
{{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{3, 3, 3, 3, 9, 5, 4,10, 1, 2, 7, 6, 11, -1, -1, -1, -1}},
/* 165 */
{{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1, -1},
{4, 3, 3, 3, 3, 6,11, 7, 1, 2,10, 0, 8, 3, 4, 9, 5}},
{{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1, -1},
{2, 3, 5, 7, 6, 11, 10, 5, 4, 0, 2,-1, -1, -1, -1, -1, -1}},
{{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1, -1},
{3, 4, 4, 3, 5, 3, 2,10, 4, 8, 3, 5, 6, 11, 7, 6, -1}},
{{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 4, 3, 2, 3, 7, 6, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}},
{{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1, -1},
{2, 3, 5, 9, 5, 4, 8, 7, 6, 2, 0, -1, -1, -1, -1, -1, -1}},
/* 170 */
{{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1, -1},
{2, 4, 4, 3, 7, 6, 2, 0, 1, 5, 4, -1, -1, -1, -1, -1, -1}},
{{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1, -1},
{1, 7, 6, 2, 1, 5, 4, 8, 7,-1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1, -1},
{2, 3, 5, 9, 5, 4, 6,10, 1, 3, 7,-1, -1, -1, -1, -1, -1}},
{{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1, -1},
{3, 4, 4, 3, 0, 8, 7, 1, 6, 10, 1, 7, 9, 5, 4, -1, -1}},
{{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1, -1},
{1, 7, 4, 0, 3, 7, 6, 10, 5, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 175 */
{{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1, -1},
{2, 4, 4, 4, 8, 10, 5, 7, 6,10, 8, -1, -1, -1, -1, -1, -1}},
{{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5,11, 8, 9, 5, 6,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1, -1},
{2, 4, 4, 0, 9, 5, 6, 6,11, 3, 0, -1, -1, -1, -1, -1, -1}},
{{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1, -1},
{1, 6, 0, 1, 5, 6,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 6,11, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/*180 */
{{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1, -1},
{2, 3, 5, 1, 2, 10, 5, 6,11, 8, 9, -1, -1, -1, -1, -1, -1}},
{{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1, -1},
{3, 4, 4, 3, 11, 3,0, 6, 9, 5, 6, 0, 2, 10, 1, 2, 10}},
{{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1, -1},
{ 1, 7,11, 8, 0, 2,10, 5, 6,-1, -1, -1, -1, -1, -1, -1, -1}},
{{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1, -1},
{2, 4, 4, 6,11, 3, 5, 10, 5, 3, 2, -1, -1, -1, -1, -1, -1}},
{{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1, -1},
{1, 6, 2, 3, 8, 9, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 185 */
{{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 9, 5, 6, 2, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1, -1},
{1, 7, 1, 5, 6, 2, 3, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 1, 5, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1, -1},
{1, 7, 1, 3, 8, 9, 5, 6,10, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1, -1},
{ 2, 4, 4, 5, 6, 0, 9, 10, 1, 0, 6, -1, -1, -1, -1, -1, -1}},
/* 190 */
{{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1}},
{{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 3,10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 5,10, 11, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 4, 3, 5,10,11, 7, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}},
{{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 4, 3, 5, 10, 11, 7, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}},
/* 195 */
{{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1, -1},
{ 2, 4, 4, 10, 11, 7, 5, 1, 9, 8, 3, -1, -1, -1, -1, -1, -1}},
{{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5, 7, 5, 1, 2,11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1, -1},
{2, 3, 5, 0, 8, 3, 2,11, 7, 5,1, -1, -1, -1, -1, -1, -1}},
{{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1, -1},
{1, 6, 2,11, 7, 5, 9, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1, -1},
{1, 7, 7, 5, 9, 8, 3, 2,11,-1, -1, -1, -1, -1, -1, -1, -1}},
/* 200 */
{{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 3, 7, 5,10, 2,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1, -1},
{1, 6, 5,10, 2, 0, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1, -1},
{2, 3, 5, 9, 0, 1, 10, 2, 3, 7, 5, -1, -1, -1, -1, -1, -1}},
{{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1, -1},
{1, 7, 9, 8, 7, 5,10, 2, 1,-1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 3, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 205 */
{{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 0, 8, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 9, 0, 3, 7, 5,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 7, 5, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 10,11, 8, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1, -1},
{1, 6, 0, 4, 5,10,11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 210 */
{{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1, -1},
{2, 3, 5, 0, 1, 9, 4, 5, 10, 11, 8, -1, -1, -1, -1, -1, -1}},
{{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1, -1},
{ 1, 7,10, 11, 3, 1, 9,4, 5,-1, -1, -1, -1, -1, -1, -1}},
{{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1, -1},
{1, 6, 2,11, 8, 4, 5, 1,-1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1, -1},
{1, 7, 0, 4, 5, 1, 2, 11, 3,-1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1, -1},
{1, 7, 0, 2,11, 8, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 215 */
{{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 3, 5, 10, 4, 5, 3, 8,-1, -1, -1, -1, -1, -1}},
{{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 5,10, 2, 0, 4,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1, -1},
{3, 4, 4, 3, 3, 5, 10, 2, 8, 4, 5, 3, 0, 1, 9, -1, -1}},
{{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1, -1},
{1, 6,10, 2, 1, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 220 */
{{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 8, 4, 5, 1, 3,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 0, 4, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1, -1},
{2, 4, 4, 0, 3, 5, 9, 8, 4, 5, 3, -1, -1, -1, -1, -1, -1}},
{{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 9,10, 11, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 225 */
{{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1, -1},
{2, 3, 5, 0, 8, 3, 7, 4, 9, 10, 11, -1, -1, -1, -1, -1, -1}},
{{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1, -1},
{1, 6, 1, 10,11, 7, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1, -1},
{1, 7, 3, 1,10,11, 7, 4, 8, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 11, 9, 1, 4, 9, 11, 7, -1, -1, -1, -1, -1, -1}},
{{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1, -1},
{3, 4, 4, 3, 1, 2, 11, 9, 7, 4, 9,11, 8, 3, 0, 8, 3}},
/* 230 */
{{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 5, 11, 7, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1, -1},
{ 2, 4, 4, 11, 7, 4, 2, 3, 2, 4, 8,-1, -1, -1, -1, -1, -1}},
{{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1, -1},
{1, 6, 2, 3, 7, 4, 9,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1, -1},
{1, 7, 9,10, 2, 0, 8, 7, 4,-1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1, -1},
{1, 7, 3, 7, 4, 0, 1,10, 2, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 235 */
{{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{2, 3, 3, 1,10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 4, 9, 1, 3, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1, -1},
{2, 4, 4, 8, 7, 1, 0, 4, 9, 1, 7, -1, -1, -1, -1, -1, -1}},
{{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 3, 7, 4, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 240 */
{{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 8, 9, 10,11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 3, 0, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 0, 1, 10,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 3, 1,10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 1, 2, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 245 */
{{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1, -1},
{2, 4, 4, 2,11, 9, 1, 3, 0, 9, 11, -1, -1, -1, -1, -1,-1}},
{{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 0, 2,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 5, 2, 3, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 2, 0, 9,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
/* 250 */
{{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1, -1},
{2, 4, 4, 2, 3, 8, 10, 1, 10, 8, 0, -1, -1, -1, -1, -1, -1}},
{{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 4, 1, 3, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{1, 3, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}
};
return &triTable[cubetype][u][0];
}; // end of TriTable
//-----------------------------------------------------------------------------
static const int PolyTable(unsigned int cubetype, int u)
{
static const int polyTable[8][16] =
{
{-1},
{-1},
{-1},
{0, 1, 2, -1},
{0, 1, 2, 2, 3, 0, -1},
{0, 1, 2, 0, 2, 4, 4, 2, 3, -1},
{0, 1, 2, 2, 3, 4, 4, 5, 0, 0, 2, 4, -1},
{0, 1, 5, 0, 5, 6, 1, 2, 5, 4, 5, 3, 2, 3, 5, -1}
};
return polyTable[cubetype][u];
}; // end of PolyTable
//=============================================================================
}; //end of class EMCLookUpTable
}; // end of namespace tri
}; // end of namespace vcg
#endif // __VCG_EMC_LOOK_UP_TABLE

View File

@ -0,0 +1,462 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/***************************************************************************/
#ifndef __VCG_EXTENDED_MARCHING_CUBES
#define __VCG_EXTENDED_MARCHING_CUBES
#include <float.h>
#include <assert.h>
#include <vector>
#include <vcg/math/base.h>
#include <vcg/math/matrix.h>
#include <vcg/math/lin_algebra.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/complex/trimesh/update/edges.h>
#include <vcg/complex/trimesh/update/normal.h>
#include <vcg/complex/trimesh/update/topology.h>
#include <vcg/complex/trimesh/allocate.h>
#include <vcg/complex/trimesh/clean.h>
#include <vcg/space/point3.h>
#include "emc_lookup_table.h"
namespace vcg
{
namespace tri
{
// Doxygen documentation
/** \addtogroup trimesh */
/*@{*/
/*
* Cube description:
* 3 ________ 2 _____2__
* /| /| / | /|
* / | / | 11/ 3 10/ |
* 7 /_______ / | /__6_|__ / |1
* | | |6 | | | |
* | 0|__|_____|1 | |__|_0|__|
* | / | / 7 8/ 5 /
* | / | / | / | /9
* |/_______|/ |/___4___|/
* 4 5
*/
//! This class implements the Extended Marching Cubes algorithm.
/*!
* The implementation is enough generic: this class works only on one volume cell for each
* call to <CODE>ProcessCell</CODE>. Using the field value at the cell corners, it adds to the
* mesh the triangles set approximating the surface that cross that cell.
* @param TRIMESH_TYPE (Template parameter) the mesh type that will be constructed
* @param WALKER_TYPE (Template parameter) the class that implements the traversal ordering of the volume.
**/
template<class TRIMESH_TYPE, class WALKER_TYPE>
class ExtendedMarchingCubes
{
public:
#if defined(__GNUC__)
typedef unsigned int size_t;
#else
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif
#endif
typedef typename vcg::tri::Allocator< TRIMESH_TYPE > AllocatorType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::VertexType VertexType;
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::VertexIterator VertexIterator;
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef typename TRIMESH_TYPE::CoordType* CoordPointer;
struct LightEdge
{
LightEdge(size_t _face, size_t _edge):face(_face), edge(_edge) { }
size_t face, edge;
};
/*!
* Constructor
* \param mesh The mesh that will be constructed
* \param volume The volume describing the field
* \param walker The class implementing the traversal policy
* \param angle The feature detection threshold misuring the sharpness of a feature(default is 30 degree)
*/
ExtendedMarchingCubes(TRIMESH_TYPE &mesh, WALKER_TYPE &walker, ScalarType angle=30)
{
_mesh = &mesh;
_walker = &walker;
_featureAngle = vcg::math::ToRad(angle);
_initialized = _finalized = false;
};
/*!
* Execute the initialiazation.
* This method must be executed before the first call to <CODE>ApplyEMC</CODE>
*/
void Initialize()
{
assert(!_initialized && !_finalized);
_featureFlag = VertexType::NewBitFlag();
_initialized = true;
};
/*!
*
* This method must be executed after the last call to <CODE>ApplyEMC</CODE>
*/
void Finalize()
{
assert(_initialized && !_finalized);
FlipEdges();
VertexIterator v_iter = _mesh->vert.begin();
VertexIterator v_end = _mesh->vert.end();
for ( ; v_iter!=v_end; v_iter++)
v_iter->ClearUserBit( _featureFlag );
VertexType::DeleteBitFlag( _featureFlag );
_featureFlag = 0;
_mesh = NULL;
_walker = NULL;
_finalized = true;
};
/*!
* Apply the <I>extended marching cubes</I> algorithm to the volume cell identified by the two points <CODE>min</CODE> and <CODE>max</CODE>.
* All the three coordinates of the first point must be smaller than the respectives three coordinatas of the second point.
* \param min the first point
* \param max the second point
*/
void ProcessCell(const vcg::Point3i &min, const vcg::Point3i &max)
{
assert(_initialized && !_finalized);
assert(min[0]<max[0] && min[1]<max[1] && min[2]<max[2]);
_corners[0].X()=min.X(); _corners[0].Y()=min.Y(); _corners[0].Z()=min.Z();
_corners[1].X()=max.X(); _corners[1].Y()=min.Y(); _corners[1].Z()=min.Z();
_corners[2].X()=max.X(); _corners[2].Y()=max.Y(); _corners[2].Z()=min.Z();
_corners[3].X()=min.X(); _corners[3].Y()=max.Y(); _corners[3].Z()=min.Z();
_corners[4].X()=min.X(); _corners[4].Y()=min.Y(); _corners[4].Z()=max.Z();
_corners[5].X()=max.X(); _corners[5].Y()=min.Y(); _corners[5].Z()=max.Z();
_corners[6].X()=max.X(); _corners[6].Y()=max.Y(); _corners[6].Z()=max.Z();
_corners[7].X()=min.X(); _corners[7].Y()=max.Y(); _corners[7].Z()=max.Z();
unsigned char cubetype = 0;
if ((_field[0] = _walker->V(_corners[0].X(), _corners[0].Y(), _corners[0].Z())) >= 0) cubetype+= 1;
if ((_field[1] = _walker->V(_corners[1].X(), _corners[1].Y(), _corners[1].Z())) >= 0) cubetype+= 2;
if ((_field[2] = _walker->V(_corners[2].X(), _corners[2].Y(), _corners[2].Z())) >= 0) cubetype+= 4;
if ((_field[3] = _walker->V(_corners[3].X(), _corners[3].Y(), _corners[3].Z())) >= 0) cubetype+= 8;
if ((_field[4] = _walker->V(_corners[4].X(), _corners[4].Y(), _corners[4].Z())) >= 0) cubetype+= 16;
if ((_field[5] = _walker->V(_corners[5].X(), _corners[5].Y(), _corners[5].Z())) >= 0) cubetype+= 32;
if ((_field[6] = _walker->V(_corners[6].X(), _corners[6].Y(), _corners[6].Z())) >= 0) cubetype+= 64;
if ((_field[7] = _walker->V(_corners[7].X(), _corners[7].Y(), _corners[7].Z())) >= 0) cubetype+=128;
if (cubetype==0 || cubetype==255)
return;
size_t vertices_idx[12];
memset(vertices_idx, -1, 12*sizeof(size_t));
int code = EMCLookUpTable::EdgeTable(cubetype);
VertexPointer vp = NULL;
if ( 1&code ) { _walker->GetXIntercept(_corners[0], _corners[1], vp); vertices_idx[ 0] = vp - &_mesh->vert[0]; }
if ( 2&code ) { _walker->GetYIntercept(_corners[1], _corners[2], vp); vertices_idx[ 1] = vp - &_mesh->vert[0]; }
if ( 4&code ) { _walker->GetXIntercept(_corners[3], _corners[2], vp); vertices_idx[ 2] = vp - &_mesh->vert[0]; }
if ( 8&code ) { _walker->GetYIntercept(_corners[0], _corners[3], vp); vertices_idx[ 3] = vp - &_mesh->vert[0]; }
if ( 16&code ) { _walker->GetXIntercept(_corners[4], _corners[5], vp); vertices_idx[ 4] = vp - &_mesh->vert[0]; }
if ( 32&code ) { _walker->GetYIntercept(_corners[5], _corners[6], vp); vertices_idx[ 5] = vp - &_mesh->vert[0]; }
if ( 64&code ) { _walker->GetXIntercept(_corners[7], _corners[6], vp); vertices_idx[ 6] = vp - &_mesh->vert[0]; }
if ( 128&code ) { _walker->GetYIntercept(_corners[4], _corners[7], vp); vertices_idx[ 7] = vp - &_mesh->vert[0]; }
if ( 256&code ) { _walker->GetZIntercept(_corners[0], _corners[4], vp); vertices_idx[ 8] = vp - &_mesh->vert[0]; }
if ( 512&code ) { _walker->GetZIntercept(_corners[1], _corners[5], vp); vertices_idx[ 9] = vp - &_mesh->vert[0]; }
if (1024&code ) { _walker->GetZIntercept(_corners[2], _corners[6], vp); vertices_idx[10] = vp - &_mesh->vert[0]; }
if (2048&code ) { _walker->GetZIntercept(_corners[3], _corners[7], vp); vertices_idx[11] = vp - &_mesh->vert[0]; }
int m, n, vertices_num;
int components = EMCLookUpTable::TriTable(cubetype, 1)[0]; //unsigned int components = triTable[cubetype][1][0];
int *indices = &EMCLookUpTable::TriTable(cubetype, 1)[components+1]; //int *indices = &EMCLookUpTable::TriTable(cubetype, 1, components+1);
std::vector< size_t > vertices_list;
for (m=1; m<=components; m++)
{
// current sheet contains vertices_num vertices
vertices_num = EMCLookUpTable::TriTable(cubetype, 1)[m]; //vertices_num = triTable[cubetype][1][m];
// collect vertices
vertices_list.clear();
for (n=0; n<vertices_num; ++n)
vertices_list.push_back( vertices_idx[ indices[n] ] );
VertexPointer feature = FindFeature( vertices_list );
if (feature != NULL) // i.e. is a valid vertex
{
// feature -> create triangle fan around feature vertex
size_t feature_idx = feature - &_mesh->vert[0];
size_t face_idx = _mesh->face.size();
vertices_list.push_back( vertices_list[0] );
AllocatorType::AddFaces(*_mesh, (int) vertices_num);
for (int j=0; j<vertices_num; ++j, face_idx++)
{
_mesh->face[face_idx].V(0) = &_mesh->vert[ vertices_list[j ] ];
_mesh->face[face_idx].V(1) = &_mesh->vert[ vertices_list[j+1] ];
_mesh->face[face_idx].V(2) = &_mesh->vert[ feature_idx ];
}
}
else
{
// no feature -> old marching cubes triangle table
for (int j=0; EMCLookUpTable::PolyTable(vertices_num, j) != -1; j+=3) //for (int j=0; polyTable[vertices_num][j] != -1; j+=3)
{
size_t face_idx = _mesh->face.size();
AllocatorType::AddFaces(*_mesh, 1);
//_mesh->face[ face_idx].V(0) = &_mesh->vert[ vertices_idx[ indices[ polyTable[vertices_num][j ] ] ] ];
//_mesh->face[ face_idx].V(1) = &_mesh->vert[ vertices_idx[ indices[ polyTable[vertices_num][j+1] ] ] ];
//_mesh->face[ face_idx].V(2) = &_mesh->vert[ vertices_idx[ indices[ polyTable[vertices_num][j+2] ] ] ];
_mesh->face[ face_idx].V(0) = &_mesh->vert[ vertices_idx[ indices[ EMCLookUpTable::PolyTable(vertices_num, j ) ] ] ];
_mesh->face[ face_idx].V(1) = &_mesh->vert[ vertices_idx[ indices[ EMCLookUpTable::PolyTable(vertices_num, j+1) ] ] ];
_mesh->face[ face_idx].V(2) = &_mesh->vert[ vertices_idx[ indices[ EMCLookUpTable::PolyTable(vertices_num, j+2) ] ] ];
}
}
indices += vertices_num;
}
}; // end of ApplyEMC
private:
/*!
*/
WALKER_TYPE *_walker;
/*!
*/
TRIMESH_TYPE *_mesh;
/*!
*/
bool _initialized;;
/*!
*/
bool _finalized;
/*!
* The feature detection threshold misuring the sharpness of a feature
*/
ScalarType _featureAngle;
/*!
* The flag used for marking the feature vertices.
*/
int _featureFlag;
/*!
* Array of the 8 corners of the volume cell being processed
*/
vcg::Point3i _corners[8];
/*!
* The field value at the cell corners
*/
ScalarType _field[8];
/*!
* Tests if the surface patch crossing the current cell contains a sharp feature
* \param vertices_idx The list of vertex indices intersecting the edges of the current cell
* \return The pointer to the new Vertex if a feature is detected; NULL otherwise.
*/
VertexPointer FindFeature(const std::vector<size_t> &vertices_idx)
{
unsigned int i, j, rank;
size_t vertices_num = (size_t) vertices_idx.size();
CoordType *points = new CoordType[ vertices_num ];
CoordType *normals = new CoordType[ vertices_num ];
Box3<ScalarType> bb;
for (i=0; i<vertices_num; i++)
{
points[i] = _mesh->vert[ vertices_idx[i] ].P();
normals[i].Import(_mesh->vert[ vertices_idx[i] ].N());
bb.Add(points[i]);
}
// move barycenter of points into (0, 0, 0)
CoordType center((ScalarType) 0.0, (ScalarType) 0.0, (ScalarType) 0.0);
for (i=0; i<vertices_num; ++i)
center += points[i];
center /= (ScalarType) vertices_num;
for (i=0; i<vertices_num; ++i)
points[i] -= center;
// normal angle criterion
double c, minC, maxC;
CoordType axis;
for (minC=1.0, i=0; i<vertices_num-1; ++i)
{
for (j=i+1; j<vertices_num; ++j)
{
c = normals[i]*normals[j];
if (c < minC)
{
minC = c;
axis = normals[i] ^ normals[j];
}
}
} //end for (minC=1.0, i=0; i<vertNumber; ++i)
if (minC > cos(_featureAngle))
return NULL; // invalid vertex
// ok, we have a feature: is it edge or corner, i.e. rank 2 or 3 ?
axis.Normalize();
for (minC=1.0, maxC=-1.0, i=0; i<vertices_num; ++i)
{
c = axis * normals[i];
if (c < minC) minC = c;
if (c > maxC) maxC = c;
}
c = std::max< double >(fabs(minC), fabs(maxC));
c = sqrt(1.0-c*c);
rank = (c > cos(_featureAngle) ? 2 : 3);
// setup linear system (find intersection of tangent planes)
vcg::ndim::Matrix<double> A((unsigned int) vertices_num, 3);
double *b = new double[ vertices_num ];
for (i=0; i<vertices_num; ++i)
{
A[i][0] = normals[i][0];
A[i][1] = normals[i][1];
A[i][2] = normals[i][2];
b[i] = (points[i] * normals[i]);
}
// SVD of matrix A
vcg::ndim::Matrix<double> V(3, 3);
double *w = new double[vertices_num];
vcg::SingularValueDecomposition< typename vcg::ndim::Matrix<double> > (A, w, V, LeaveUnsorted, 100);
// rank == 2 -> suppress smallest singular value
if (rank == 2)
{
double smin = DBL_MAX; // the max value, as defined in <float.h>
unsigned int sminid = 0;
unsigned int srank = std::min< unsigned int >(vertices_num, 3u);
for (i=0; i<srank; ++i)
{
if (w[i] < smin)
{
smin = w[i];
sminid = i;
}
}
w[sminid] = 0.0;
}
// SVD backsubstitution -> least squares, least norm solution x
double *x = new double[3];
vcg::SingularValueBacksubstitution< vcg::ndim::Matrix<double> >(A, w, V, x, b);
// transform x to world coords
CoordType point((ScalarType) x[0], (ScalarType) x[1], (ScalarType) x[2]);
point += center;
// Safety check if the feature point found by svd is
// out of the bbox of the vertices perhaps it is better to put it back in the center...
if(!bb.IsIn(point)) point = center;
// insert the feature-point
VertexPointer mean_point = &*AllocatorType::AddVertices( *_mesh, 1);
mean_point->SetUserBit(_featureFlag);
mean_point->P() = point;
mean_point->N().SetZero();
delete []x;
delete []points;
delete []normals;
return mean_point;
} // end of FindFeature
/*!
* Postprocessing step performed during the finalization tha flip some of the mesh edges.
* The flipping criterion is quite simple: each edge is flipped if it will connect two
* feature samples after the flip.
*/
void FlipEdges()
{
size_t i;
std::vector< LightEdge > edges;
FaceIterator f_iter = _mesh->face.begin();
FaceIterator f_end = _mesh->face.end();
for (i=0; f_iter!=f_end; f_iter++, i++)
{
if (f_iter->V(1) > f_iter->V(0)) edges.push_back( LightEdge(i,0) );
if (f_iter->V(2) > f_iter->V(1)) edges.push_back( LightEdge(i,1) );
if (f_iter->V(0) > f_iter->V(2)) edges.push_back( LightEdge(i,2) );
}
vcg::tri::UpdateTopology< TRIMESH_TYPE >::FaceFace( *_mesh );
// Select all the triangles that has a vertex shared with a non manifold edge.
int nonManifEdge = tri::Clean< TRIMESH_TYPE >::CountNonManifoldEdgeFF(*_mesh,true);
if(nonManifEdge >0)
tri::UpdateSelection< TRIMESH_TYPE >::FaceFromVertexLoose(*_mesh);
//qDebug("Got %i non manif edges",nonManifEdge);
typename std::vector< LightEdge >::iterator e_it = edges.begin();
typename std::vector< LightEdge >::iterator e_end = edges.end();
FacePointer g, f;
int w, z;
for( ; e_it!=e_end; e_it++)
{
f = &_mesh->face[e_it->face];
z = (int) e_it->edge;
// v2------v1 swap the diagonal only if v2 and v3 are feature and v0 and v1 are not.
// | / |
// | / |
// v0------v3
if (!(f->IsS()) && vcg::face::CheckFlipEdge< FaceType >(*f, z))
{
VertexPointer v0, v1, v2, v3;
v0 = f->V(z);
v1 = f->V1(z);
v2 = f->V2(z);
g = f->FFp(z);
w = f->FFi(z);
v3 = g->V2(w);
bool b0, b1, b2, b3;
b0 = !v0->IsUserBit(_featureFlag) ;
b1 = !v1->IsUserBit(_featureFlag) ;
b2 = v2->IsUserBit(_featureFlag) ;
b3 = v3->IsUserBit(_featureFlag) ;
if( b0 && b1 && b2 && b3)
vcg::face::FlipEdge< FaceType >(*f, z);
} // end if (vcg::face::CheckFlipEdge< _Face >(*f, z))
} // end for( ; e_it!=e_end; e_it++)
}; //end of FlipEdges
}; // end of class ExtendedMarchingCubes
// /*! @} */
// end of Doxygen documentation
} // end of namespace tri
}; // end of namespace vcg
#endif // __VCG_EXTENDED_MARCHING_CUBES

View File

@ -0,0 +1,730 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/***************************************************************************/
#ifndef __VCG_MARCHING_CUBES
#define __VCG_MARCHING_CUBES
#include <assert.h>
#include <vcg/space/point3.h>
#include <vcg/complex/trimesh/allocate.h>
#include "mc_lookup_table.h"
namespace vcg
{
namespace tri
{
// Doxygen documentation
/** \addtogroup trimesh */
/*@{*/
/*
* Cube description:
* 3 ________ 2 _____2__
* /| /| / | /|
* / | / | 11/ 3 10/ |
* 7 /_______ / | /__6_|__ / |1
* | | |6 | | | | |
* | 0|__|_____|1 | |__|__0__|
* | / | / 7 8/ 5 /
* | / | / | / | /9
* |/_______|/ |/___4___|/
* 4 5
*/
//! This class implements the Marching Cubes algorithm.
/*!
* The implementation is enough generic: this class works only on one volume cell for each
* call to <CODE>ProcessCell</CODE>. Using the field value at the cell corners, it adds to the
* mesh the triangles set approximating the surface that cross that cell. The ambiguities
* are resolved using an enhanced topologically controlled lookup table.
* @param TRIMESH_TYPE (Template parameter) the mesh type that will be constructed
* @param WALKER_TYPE (Template parameter) the class that implement the traversal ordering of the volume
**/
template<class TRIMESH_TYPE, class WALKER_TYPE>
class MarchingCubes
{
public:
enum Dimension {X, Y, Z};
#if defined(__GNUC__)
typedef unsigned int size_t;
#else
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif
#endif
typedef typename vcg::tri::Allocator< TRIMESH_TYPE > AllocatorType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::VertexType VertexType;
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::VertexIterator VertexIterator;
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef typename TRIMESH_TYPE::CoordType* CoordPointer;
/*!
* Constructor
* \param mesh the mesh that will be constructed
* \param walker the class implementing the traversal policy
*/
MarchingCubes(TRIMESH_TYPE &mesh, WALKER_TYPE &walker)
{
_mesh = &mesh;
_walker = &walker;
};
/*!
* Execute the initialiazation.
* This method must be executed before the first call to <CODE>ApplyMC</CODE>
*/
void Initialize()
{
_mesh->Clear();
}; // end of Initialize()
/*!
*
* This method must be executed after the last call to <CODE>ApplyMC</CODE>
*/
void Finalize()
{
_mesh = NULL;
_walker = NULL;
}; // end of Finalize()
/*!
* Apply the <I>marching cubes</I> algorithm to the volume cell identified by the two points <CODE>min</CODE> and <CODE>max</CODE>.
* All the three coordinates of the first point must be smaller than the respectives three coordinatas of the second point.
* \param min the first point
* \param max the second point
*/
void ProcessCell(const vcg::Point3i &min, const vcg::Point3i &max)
{
_case = _subconfig = _config = -1;
assert(min[0]<max[0] && min[1]<max[1] && min[2]<max[2]);
_corners[0].X()=min.X(); _corners[0].Y()=min.Y(); _corners[0].Z()=min.Z();
_corners[1].X()=max.X(); _corners[1].Y()=min.Y(); _corners[1].Z()=min.Z();
_corners[2].X()=max.X(); _corners[2].Y()=max.Y(); _corners[2].Z()=min.Z();
_corners[3].X()=min.X(); _corners[3].Y()=max.Y(); _corners[3].Z()=min.Z();
_corners[4].X()=min.X(); _corners[4].Y()=min.Y(); _corners[4].Z()=max.Z();
_corners[5].X()=max.X(); _corners[5].Y()=min.Y(); _corners[5].Z()=max.Z();
_corners[6].X()=max.X(); _corners[6].Y()=max.Y(); _corners[6].Z()=max.Z();
_corners[7].X()=min.X(); _corners[7].Y()=max.Y(); _corners[7].Z()=max.Z();
for (int i=0; i<8; i++)
_field[i] = _walker->V( _corners[i].X(), _corners[i].Y(), _corners[i].Z() );
unsigned char cubetype = 0;
for (int i=0; i<8; i++)
if (_field[i]>0) cubetype += 1<<i;
_case = MCLookUpTable::Cases(cubetype, 0); //_case = cases[cubetype][0];
_config = MCLookUpTable::Cases(cubetype, 1); //_config = cases[cubetype][1];
_subconfig = 0;
VertexPointer v12 = NULL;
switch( _case )
{
case 0 : { break ; }
case 1 : { AddTriangles( MCLookUpTable::Tiling1(_config), 1 ); break; } //case 1 : { AddTriangles( tiling1[_config], 1 ); break; }
case 2 : { AddTriangles( MCLookUpTable::Tiling2(_config), 2 ); break; } //case 2 : { AddTriangles( tiling2[_config], 2 ); break; }
case 3 :
{
//if( TestFace( test3[_config]) ) AddTriangles( tiling3_2[_config], 4 ) ; // 3.2
if( TestFace( MCLookUpTable::Test3(_config)) )
AddTriangles( MCLookUpTable::Tiling3_2(_config), 4 ) ; // 3.2
else
AddTriangles( MCLookUpTable::Tiling3_1(_config), 2 ) ; // 3.1
break ;
}
case 4 :
{
//if( TestInterior( test4[_config]) ) AddTriangles( tiling4_1[_config], 2 ) ; // 4.1.1
if( TestInterior( MCLookUpTable::Test4(_config) ) )
AddTriangles( MCLookUpTable::Tiling4_1(_config), 2 ) ; // 4.1.1
else
AddTriangles( MCLookUpTable::Tiling4_2(_config), 6 ) ; // 4.1.2
break ;
}
case 5 : { AddTriangles( MCLookUpTable::Tiling5(_config), 3 ); break; }
case 6 :
{
//if( TestFace( test6[_config][0]) )
if( TestFace( MCLookUpTable::Test6(_config, 0)) )
AddTriangles( MCLookUpTable::Tiling6_2(_config), 5 ) ; // 6.2
else
{
if( TestInterior( MCLookUpTable::Test6(_config, 1)) )
AddTriangles( MCLookUpTable::Tiling6_1_1(_config), 3 ) ; // 6.1.1
else
AddTriangles( MCLookUpTable::Tiling6_1_2(_config), 7 ) ; // 6.1.2
}
break ;
}
case 7 :
{
//if( TestFace( test7[_config][0] ) ) _subconfig += 1 ;
//if( TestFace( test7[_config][1] ) ) _subconfig += 2 ;
//if( TestFace( test7[_config][2] ) ) _subconfig += 4 ;
if( TestFace( MCLookUpTable::Test7(_config, 0) ) ) _subconfig += 1 ;
if( TestFace( MCLookUpTable::Test7(_config, 1) ) ) _subconfig += 2 ;
if( TestFace( MCLookUpTable::Test7(_config, 2) ) ) _subconfig += 4 ;
switch( _subconfig )
{
case 0 : { AddTriangles( MCLookUpTable::Tiling7_1(_config), 3 ) ; break; }
case 1 : { AddTriangles( MCLookUpTable::Tiling7_2(_config,0), 5 ) ; break; }
case 2 : { AddTriangles( MCLookUpTable::Tiling7_2(_config,1), 5 ) ; break; }
case 3 : { ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling7_3(_config,0), 9, v12 ) ; break ; }
case 4 : { AddTriangles( MCLookUpTable::Tiling7_2(_config, 2), 5 ) ; break ;}
case 5 : { ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling7_3(_config,1), 9, v12 ) ; break ; }
case 6 : { ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling7_3(_config,2), 9, v12 ) ; break ; }
case 7 :
{
if( TestInterior( MCLookUpTable::Test7(_config, 3) ) )
AddTriangles( MCLookUpTable::Tiling7_4_2(_config), 9 ) ;
else
AddTriangles( MCLookUpTable::Tiling7_4_1(_config), 5 ) ;
break ;
}
};
break ;
} // end of case 7
case 8 : { AddTriangles( MCLookUpTable::Tiling8(_config), 2 ) ; break ;}
case 9 : { AddTriangles( MCLookUpTable::Tiling9(_config), 4 ) ; break ;}
case 10 :
{
if( TestFace( MCLookUpTable::Test10(_config, 0)) ) //if( TestFace( test10[_config][0]) )
{
if( TestFace( MCLookUpTable::Test10(_config,1) ) )
AddTriangles( MCLookUpTable::Tiling10_1_1_(_config), 4 ) ; // 10.1.1
else
{
ComputeCVertex(v12);
AddTriangles( MCLookUpTable::Tiling10_2(_config), 8, v12 ) ; // 10.2
}
}
else
{
if( TestFace( MCLookUpTable::Test10(_config, 1) ) )
{
ComputeCVertex(v12) ;
AddTriangles( MCLookUpTable::Tiling10_2_(_config), 8, v12 ) ; // 10.2
}
else
{
if( TestInterior( MCLookUpTable::Test10(_config, 2) ) )
AddTriangles( MCLookUpTable::Tiling10_1_1(_config), 4 ) ; // 10.1.1
else
AddTriangles( MCLookUpTable::Tiling10_1_2(_config), 8 ) ; // 10.1.2
}
}
break ;
} // end of case 10
case 11 : { AddTriangles( MCLookUpTable::Tiling11(_config), 4 ) ; break ; }
case 12 :
{
if( TestFace( MCLookUpTable::Test12(_config, 0) ) ) //if( TestFace( test12[_config][0]) )
{
if( TestFace( MCLookUpTable::Test12(_config, 1) ) )
AddTriangles( MCLookUpTable::Tiling12_1_1_(_config), 4 ) ; // 12.1.1
else
{
ComputeCVertex(v12) ;
AddTriangles( MCLookUpTable::Tiling12_2(_config), 8, v12 ) ; // 12.2
}
}
else
{
if( TestFace( MCLookUpTable::Test12(_config, 1) ) )
{
ComputeCVertex(v12) ;
AddTriangles( MCLookUpTable::Tiling12_2_(_config), 8, v12 ) ; // 12.2
}
else
{
if( TestInterior( MCLookUpTable::Test12(_config, 2) ) )
AddTriangles( MCLookUpTable::Tiling12_1_1(_config), 4 ) ; // 12.1.1
else
AddTriangles( MCLookUpTable::Tiling12_1_2(_config), 8 ) ; // 12.1.2
}
}
break ;
} // end of case 12
case 13 :
{
//if( TestFace( test13[_config][0] ) ) _subconfig += 1 ;
//if( TestFace( test13[_config][1] ) ) _subconfig += 2 ;
//if( TestFace( test13[_config][2] ) ) _subconfig += 4 ;
//if( TestFace( test13[_config][3] ) ) _subconfig += 8 ;
//if( TestFace( test13[_config][4] ) ) _subconfig += 16 ;
//if( TestFace( test13[_config][5] ) ) _subconfig += 32 ;
if( TestFace( MCLookUpTable::Test13(_config, 0) ) ) _subconfig += 1 ;
if( TestFace( MCLookUpTable::Test13(_config, 1) ) ) _subconfig += 2 ;
if( TestFace( MCLookUpTable::Test13(_config, 2) ) ) _subconfig += 4 ;
if( TestFace( MCLookUpTable::Test13(_config, 3) ) ) _subconfig += 8 ;
if( TestFace( MCLookUpTable::Test13(_config, 4) ) ) _subconfig += 16 ;
if( TestFace( MCLookUpTable::Test13(_config, 5) ) ) _subconfig += 32 ;
switch( MCLookUpTable::Subconfig13(_subconfig) ) //switch( subconfig13[_subconfig] )
{
case 0 : { /* 13.1 */ AddTriangles( MCLookUpTable::Tiling13_1(_config) , 4 ) ; break ; }
case 1 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 0), 6 ) ; break ; }
case 2 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 1), 6 ) ; break ; }
case 3 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 2), 6 ) ; break ; }
case 4 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 3), 6 ) ; break ; }
case 5 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 4), 6 ) ; break ; }
case 6 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 5), 6 ) ; break ; }
case 7 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 0), 10, v12 ) ; break ; }
case 8 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 1), 10, v12 ) ; break ; }
case 9 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 2), 10, v12 ) ; break ; }
case 10 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 3), 10, v12 ) ; break ; }
case 11 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 4), 10, v12 ) ; break ; }
case 12 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 5), 10, v12 ) ; break ; }
case 13 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 6), 10, v12 ) ; break ; }
case 14 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 7), 10, v12 ) ; break ; }
case 15 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 8), 10, v12 ) ; break ; }
case 16 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 9), 10, v12 ) ; break ; }
case 17 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config,10), 10, v12 ) ; break ; }
case 18 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config,11), 10, v12 ) ; break ; }
case 19 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 0), 12, v12 ) ; break ; }
case 20 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 1), 12, v12 ) ; break ; }
case 21 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 2), 12, v12 ) ; break ; }
case 22 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 3), 12, v12 ) ; break ; }
case 23 :
{ /* 13.5 */
_subconfig = 0 ;
if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 0), 6 ) ;
else
AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 0), 10 ) ;
break ;
}
case 24 :
{ /* 13.5 */
_subconfig = 1 ;
if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 1), 6 ) ;
else
AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 1), 10 ) ;
break ;
}
case 25 :
{/* 13.5 */
_subconfig = 2 ;
if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 2), 6 ) ;
else
AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 2), 10 ) ;
break ;
}
case 26 :
{/* 13.5 */
_subconfig = 3 ;
if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 3), 6 ) ;
else
AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 3), 10 ) ;
break ;
}
case 27 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 0), 10, v12 ) ; break ; }
case 28 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 1), 10, v12 ) ; break ; }
case 29 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 2), 10, v12 ) ; break ; }
case 30 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 3), 10, v12 ) ; break ; }
case 31 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 4), 10, v12 ) ; break ; }
case 32 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 5), 10, v12 ) ; break ; }
case 33 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 6), 10, v12 ) ; break ; }
case 34 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 7), 10, v12 ) ; break ; }
case 35 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 8), 10, v12 ) ; break ; }
case 36 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 9), 10, v12 ) ; break ; }
case 37 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config,10), 10, v12 ) ; break ; }
case 38 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config,11), 10, v12 ) ; break ; }
case 39 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,0), 6 ) ; break ; }
case 40 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,1), 6 ) ; break ; }
case 41 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,2), 6 ) ; break ; }
case 42 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,3), 6 ) ; break ; }
case 43 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,4), 6 ) ; break ; }
case 44 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,5), 6 ) ; break ; }
case 45 : { /* 13.1 */ AddTriangles( MCLookUpTable::Tiling13_1_(_config) , 4 ) ; break ; }
default : { /*Impossible case 13*/ assert(false); }
}
break ;
} // end of case 13
case 14 : { AddTriangles( MCLookUpTable::Tiling14(_config), 4 ) ; }
break ;
} //end of switch (_case)
}; // end of ApplyMC
private:
/*!
*/
WALKER_TYPE *_walker;
/*!
*/
TRIMESH_TYPE *_mesh;
/*!
* The field value at the cell corners
*/
ScalarType _field[8];
/*!
* Array of the 8 corners of the volume cell being processed
*/
vcg::Point3i _corners[8];
/*!
* Case of the volume cell being processed
*/
unsigned char _case;
/*!
* Configuration of the volume cell being processed
*/
unsigned char _config;
/*!
* Subconfiguration of the volume cell being processed
*/
unsigned char _subconfig;
/*!
* Tests if the components of the tesselation of the cube should be connected
* by the interior of an ambiguous face
*/
inline bool TestFace(signed char face)
{
ScalarType A,B,C,D ;
switch( face )
{
case -1 : case 1 : A = _field[0] ; B = _field[4] ; C = _field[5] ; D = _field[1] ; break ;
case -2 : case 2 : A = _field[1] ; B = _field[5] ; C = _field[6] ; D = _field[2] ; break ;
case -3 : case 3 : A = _field[2] ; B = _field[6] ; C = _field[7] ; D = _field[3] ; break ;
case -4 : case 4 : A = _field[3] ; B = _field[7] ; C = _field[4] ; D = _field[0] ; break ;
case -5 : case 5 : A = _field[0] ; B = _field[3] ; C = _field[2] ; D = _field[1] ; break ;
case -6 : case 6 : A = _field[4] ; B = _field[7] ; C = _field[6] ; D = _field[5] ; break ;
default : assert(false); // Invalid face code
};
return face * A * ( A*C - B*D ) >= 0 ; // face and A invert signs
}; // end of TestFace
/*!
* Tests if the components of the tesselation of the cube should be connected
* through the interior of the cube
*/
inline bool TestInterior(signed char s)
{
ScalarType t, At=0, Bt=0, Ct=0, Dt=0, a, b ;
char test = 0 ;
char edge = -1 ; // reference edge of the triangulation
switch( _case )
{
case 4 :
case 10 :
{
a = (_field[4]-_field[0])*(_field[6]-_field[2]) - (_field[7]-_field[3])*(_field[5]-_field[1]);
b = _field[2]*(_field[4]-_field[0])+_field[0]*(_field[6]-_field[2])-_field[1]*(_field[7]-_field[3])-_field[3]*(_field[5]-_field[1]);
t = - b / (2*a) ;
if( t<0 || t>1 )
return s>0 ;
At = _field[0] + ( _field[4] - _field[0] ) * t ;
Bt = _field[3] + ( _field[7] - _field[3] ) * t ;
Ct = _field[2] + ( _field[6] - _field[2] ) * t ;
Dt = _field[1] + ( _field[5] - _field[1] ) * t ;
break ;
}
case 6 :
case 7 :
case 12 :
case 13 :
switch( _case )
{
case 6 : edge = MCLookUpTable::Test6 (_config, 2) ; break ;
case 7 : edge = MCLookUpTable::Test7 (_config, 4) ; break ;
case 12 : edge = MCLookUpTable::Test12(_config, 3) ; break ;
case 13 : edge = MCLookUpTable::Tiling13_5_1(_config, _subconfig)[0] ; break ;
}
switch( edge )
{
case 0 :
t = _field[0] / ( _field[0] - _field[1] ) ;
At = 0 ;
Bt = _field[3] + ( _field[2] - _field[3] ) * t ;
Ct = _field[7] + ( _field[6] - _field[7] ) * t ;
Dt = _field[4] + ( _field[5] - _field[4] ) * t ;
break ;
case 1 :
t = _field[1] / ( _field[1] - _field[2] ) ;
At = 0 ;
Bt = _field[0] + ( _field[3] - _field[0] ) * t ;
Ct = _field[4] + ( _field[7] - _field[4] ) * t ;
Dt = _field[5] + ( _field[6] - _field[5] ) * t ;
break ;
case 2 :
t = _field[2] / ( _field[2] - _field[3] ) ;
At = 0 ;
Bt = _field[1] + ( _field[0] - _field[1] ) * t ;
Ct = _field[5] + ( _field[4] - _field[5] ) * t ;
Dt = _field[6] + ( _field[7] - _field[6] ) * t ;
break ;
case 3 :
t = _field[3] / ( _field[3] - _field[0] ) ;
At = 0 ;
Bt = _field[2] + ( _field[1] - _field[2] ) * t ;
Ct = _field[6] + ( _field[5] - _field[6] ) * t ;
Dt = _field[7] + ( _field[4] - _field[7] ) * t ;
break ;
case 4 :
t = _field[4] / ( _field[4] - _field[5] ) ;
At = 0 ;
Bt = _field[7] + ( _field[6] - _field[7] ) * t ;
Ct = _field[3] + ( _field[2] - _field[3] ) * t ;
Dt = _field[0] + ( _field[1] - _field[0] ) * t ;
break ;
case 5 :
t = _field[5] / ( _field[5] - _field[6] ) ;
At = 0 ;
Bt = _field[4] + ( _field[7] - _field[4] ) * t ;
Ct = _field[0] + ( _field[3] - _field[0] ) * t ;
Dt = _field[1] + ( _field[2] - _field[1] ) * t ;
break ;
case 6 :
t = _field[6] / ( _field[6] - _field[7] ) ;
At = 0 ;
Bt = _field[5] + ( _field[4] - _field[5] ) * t ;
Ct = _field[1] + ( _field[0] - _field[1] ) * t ;
Dt = _field[2] + ( _field[3] - _field[2] ) * t ;
break ;
case 7 :
t = _field[7] / ( _field[7] - _field[4] ) ;
At = 0 ;
Bt = _field[6] + ( _field[5] - _field[6] ) * t ;
Ct = _field[2] + ( _field[1] - _field[2] ) * t ;
Dt = _field[3] + ( _field[0] - _field[3] ) * t ;
break ;
case 8 :
t = _field[0] / ( _field[0] - _field[4] ) ;
At = 0 ;
Bt = _field[3] + ( _field[7] - _field[3] ) * t ;
Ct = _field[2] + ( _field[6] - _field[2] ) * t ;
Dt = _field[1] + ( _field[5] - _field[1] ) * t ;
break ;
case 9 :
t = _field[1] / ( _field[1] - _field[5] ) ;
At = 0 ;
Bt = _field[0] + ( _field[4] - _field[0] ) * t ;
Ct = _field[3] + ( _field[7] - _field[3] ) * t ;
Dt = _field[2] + ( _field[6] - _field[2] ) * t ;
break ;
case 10 :
t = _field[2] / ( _field[2] - _field[6] ) ;
At = 0 ;
Bt = _field[1] + ( _field[5] - _field[1] ) * t ;
Ct = _field[0] + ( _field[4] - _field[0] ) * t ;
Dt = _field[3] + ( _field[7] - _field[3] ) * t ;
break ;
case 11 :
t = _field[3] / ( _field[3] - _field[7] ) ;
At = 0 ;
Bt = _field[2] + ( _field[6] - _field[2] ) * t ;
Ct = _field[1] + ( _field[5] - _field[1] ) * t ;
Dt = _field[0] + ( _field[4] - _field[0] ) * t ;
break ;
default: { assert(false); /* Invalid edge */ break ; }
}
break ;
default : assert(false); /* Invalid ambiguous case */ break;
}
if( At >= 0 ) test ++ ;
if( Bt >= 0 ) test += 2 ;
if( Ct >= 0 ) test += 4 ;
if( Dt >= 0 ) test += 8 ;
switch( test )
{
case 0 : return s>0 ;
case 1 : return s>0 ;
case 2 : return s>0 ;
case 3 : return s>0 ;
case 4 : return s>0 ;
case 5 : if( At * Ct < Bt * Dt ) return s>0 ; break ;
case 6 : return s>0 ;
case 7 : return s<0 ;
case 8 : return s>0 ;
case 9 : return s>0 ;
case 10 : if( At * Ct >= Bt * Dt ) return s>0 ; break ;
case 11 : return s<0 ;
case 12 : return s>0 ;
case 13 : return s<0 ;
case 14 : return s<0 ;
case 15 : return s<0 ;
}
return s<0 ;
}; //end of TestInterior
/*!
* Adds a vertex inside the current cube
* \param v The pointer to the new vertex along the edge
*/
inline void ComputeCVertex(VertexPointer &v12)
{
v12 = &*AllocatorType::AddVertices(*_mesh, 1);
v12->P() = CoordType(0.0, 0.0, 0.0);
unsigned int count = 0;
VertexPointer v = NULL;
if (_walker->Exist(_corners[0], _corners[1], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[1], _corners[2], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[3], _corners[2], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[0], _corners[3], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[4], _corners[5], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[5], _corners[6], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[7], _corners[6], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[4], _corners[7], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[0], _corners[4], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[1], _corners[5], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[2], _corners[6], v) )
{
count++;
v12->P() += v->P();
}
if (_walker->Exist(_corners[3], _corners[7], v) )
{
count++;
v12->P() += v->P();
}
v12->P() /= (float) count;
} // end of AddCVertex
/*!
* Adds new triangles to the mesh
* \param vertices_list The list of vertex indices
* \param n The number of triangles that will be added to the mesh
* \param v12 The pointer to the vertex inside the current cell
*/
inline void AddTriangles(const char *vertices_list, char n, VertexPointer v12=NULL)
{
VertexPointer vp = NULL;
size_t face_idx = _mesh->face.size();
size_t v12_idx = -1;
size_t vertices_idx[3];
if (v12 != NULL) v12_idx = v12 - &_mesh->vert[0];
AllocatorType::AddFaces(*_mesh, (int) n);
for (int trig=0; trig<3*n; face_idx++ )
{
vp = NULL;
memset(vertices_idx, -1, 3*sizeof(size_t));
for (int vert=0; vert<3; vert++, trig++) //ok
{
switch ( vertices_list[trig] )
{
case 0: { _walker->GetXIntercept(_corners[0], _corners[1], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 1: { _walker->GetYIntercept(_corners[1], _corners[2], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 2: { _walker->GetXIntercept(_corners[3], _corners[2], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 3: { _walker->GetYIntercept(_corners[0], _corners[3], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 4: { _walker->GetXIntercept(_corners[4], _corners[5], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 5: { _walker->GetYIntercept(_corners[5], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 6: { _walker->GetXIntercept(_corners[7], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 7: { _walker->GetYIntercept(_corners[4], _corners[7], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 8: { _walker->GetZIntercept(_corners[0], _corners[4], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 9: { _walker->GetZIntercept(_corners[1], _corners[5], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 10: { _walker->GetZIntercept(_corners[2], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 11: { _walker->GetZIntercept(_corners[3], _corners[7], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; }
case 12: { assert(v12 != NULL); vertices_idx[vert] = v12_idx; break; }
default: { assert(false); /* Invalid edge identifier */ }
} // end of switch
// Note that vp can be zero if we are in case 12 and that vertices_idx is surely >0 so the following assert has to be corrected as below.
// assert((vp - &_mesh->vert[0])>=0 && vertices_idx[vert]<_mesh->vert.size());
assert(vertices_idx[vert]<_mesh->vert.size());
} // end for (int vert=0 ...)
_mesh->face[face_idx].V(0) = &_mesh->vert[vertices_idx[0]];
_mesh->face[face_idx].V(1) = &_mesh->vert[vertices_idx[1]];
_mesh->face[face_idx].V(2) = &_mesh->vert[vertices_idx[2]];
} // end for (int trig=0...)
}; // end of AddTriangles
}; // end of class MarchingCubes
/*! @} */
//end of Doxygen documentation
}; // end of namespace tri
}; // end of namespace vcg
#endif //__VCG_MARCHING_CUBES

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,346 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004-2009 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_TRIVIAL_WALKER
#define __VCG_TRIVIAL_WALKER
#include <wrap/callback.h>
namespace vcg {
// Very simple volume class.
// just an example of the interface that the trivial walker expects
template <class VOX_TYPE>
class SimpleVolume
{
public:
typedef VOX_TYPE VoxelType;
std::vector<VoxelType> Vol;
Point3i sz; /// Dimensioni griglia come numero di celle per lato
const Point3i &ISize() {return sz;}; /// Dimensioni griglia come numero di celle per lato
void Init(Point3i _sz)
{
sz=_sz;
Vol.resize(sz[0]*sz[1]*sz[2]);
}
float Val(const int &x,const int &y,const int &z) const {
return cV(x,y,z).V();
//else return numeric_limits<float>::quiet_NaN( );
}
float &Val(const int &x,const int &y,const int &z) {
return V(x,y,z).V();
//else return numeric_limits<float>::quiet_NaN( );
}
VOX_TYPE &V(const int &x,const int &y,const int &z) {
return Vol[x+y*sz[0]+z*sz[0]*sz[1]];
}
const VOX_TYPE &cV(const int &x,const int &y,const int &z) const {
return Vol[x+y*sz[0]+z*sz[0]*sz[1]];
}
typedef enum { XAxis=0,YAxis=1,ZAxis=2} VolumeAxis;
template < class VertexPointerType, VolumeAxis AxisVal >
void GetIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr)
{
float f1 = Val(p1.X(), p1.Y(), p1.Z())-thr;
float f2 = Val(p2.X(), p2.Y(), p2.Z())-thr;
float u = (float) f1/(f1-f2);
if(AxisVal==XAxis) v->P().X() = (float) p1.X()*(1-u) + u*p2.X();
else v->P().X() = (float) p1.X();
if(AxisVal==YAxis) v->P().Y() = (float) p1.Y()*(1-u) + u*p2.Y();
else v->P().Y() = (float) p1.Y();
if(AxisVal==ZAxis) v->P().Z() = (float) p1.Z()*(1-u) + u*p2.Z();
else v->P().Z() = (float) p1.Z();
}
template < class VertexPointerType >
void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr)
{ GetIntercept<VertexPointerType,XAxis>(p1,p2,v,thr); }
template < class VertexPointerType >
void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr)
{ GetIntercept<VertexPointerType,YAxis>(p1,p2,v,thr); }
template < class VertexPointerType >
void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, const float thr)
{ GetIntercept<VertexPointerType,ZAxis>(p1,p2,v,thr); }
};
template <class VolumeType>
class RawVolumeImporter
{
public:
enum DataType
{
// Funzioni superiori
UNDEF=0,
BYTE=1,
SHORT=2,
FLOAT=3
};
static bool Open(const char *filename, VolumeType &V, Point3i sz, DataType d)
{
return true;
}
};
class SimpleVoxel
{
private:
float _v;
public:
float &V() {return _v;};
float V() const {return _v;};
};
namespace tri {
// La classe Walker implementa la politica di visita del volume; conoscendo l'ordine di visita del volume
// Ë conveniente che il Walker stesso si faccia carico del caching dei dati utilizzati durante l'esecuzione
// degli algoritmi MarchingCubes ed ExtendedMarchingCubes, in particolare il calcolo del volume ai vertici
// delle celle e delle intersezioni della superficie con le celle. In questo esempio il volume da processare
// viene suddiviso in fette; in questo modo se il volume ha dimensione h*l*w (rispettivamente altezza,
// larghezza e profondit‡), lo spazio richiesto per il caching dei vertici gi‡ allocati passa da O(h*l*w)
// a O(h*l).
template <class MeshType, class VolumeType>
class TrivialWalker
{
private:
typedef int VertexIndex;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexPointer VertexPointer;
public:
// bbox is the portion of the volume to be computed
// resolution determine the sampling step:
// should be a divisor of bbox size (e.g. if bbox size is 256^3 resolution could be 128,64, etc)
void Init(VolumeType &volume)
{
_bbox = Box3i(Point3i(0,0,0),volume.ISize());
_slice_dimension = _bbox.DimX()*_bbox.DimZ();
_x_cs = new VertexIndex[ _slice_dimension ];
_y_cs = new VertexIndex[ _slice_dimension ];
_z_cs = new VertexIndex[ _slice_dimension ];
_x_ns = new VertexIndex[ _slice_dimension ];
_z_ns = new VertexIndex[ _slice_dimension ];
};
~TrivialWalker()
{_thr=0;}
template<class EXTRACTOR_TYPE>
void BuildMesh(MeshType &mesh, VolumeType &volume, EXTRACTOR_TYPE &extractor, const float threshold, vcg::CallBackPos * cb=0)
{
Init(volume);
_volume = &volume;
_mesh = &mesh;
_mesh->Clear();
_thr=threshold;
vcg::Point3i p1, p2;
Begin();
extractor.Initialize();
for (int j=_bbox.min.Y(); j<(_bbox.max.Y()-1)-1; j+=1)
{
if(cb && ((j%10)==0) ) cb(j*_bbox.DimY()/100.0,"Marching volume");
for (int i=_bbox.min.X(); i<(_bbox.max.X()-1)-1; i+=1)
{
for (int k=_bbox.min.Z(); k<(_bbox.max.Z()-1)-1; k+=1)
{
p1.X()=i; p1.Y()=j; p1.Z()=k;
p2.X()=i+1; p2.Y()=j+1; p2.Z()=k+1;
extractor.ProcessCell(p1, p2);
}
}
NextSlice();
}
extractor.Finalize();
_volume = NULL;
_mesh = NULL;
};
float V(int pi, int pj, int pk)
{
return _volume->Val(pi, pj, pk)-_thr;
}
bool Exist(const vcg::Point3i &p0, const vcg::Point3i &p1, VertexPointer &v)
{
int pos = p0.X()+p0.Z()*_bbox.max.X();
int vidx;
if (p0.X()!=p1.X()) // punti allineati lungo l'asse X
vidx = (p0.Y()==_current_slice) ? _x_cs[pos] : _x_ns[pos];
else if (p0.Y()!=p1.Y()) // punti allineati lungo l'asse Y
vidx = _y_cs[pos];
else if (p0.Z()!=p1.Z()) // punti allineati lungo l'asse Z
vidx = (p0.Y()==_current_slice)? _z_cs[pos] : _z_ns[pos];
else
assert(false);
v = (vidx!=-1)? &_mesh->vert[vidx] : NULL;
return v!=NULL;
}
void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
int i = p1.X() - _bbox.min.X();
int z = p1.Z() - _bbox.min.Z();
VertexIndex index = i+z*_bbox.max.X();
VertexIndex pos;
if (p1.Y()==_current_slice)
{
if ((pos=_x_cs[index])==-1)
{
_x_cs[index] = (VertexIndex) _mesh->vert.size();
pos = _x_cs[index];
Allocator<MeshType>::AddVertices( *_mesh, 1 );
v = &_mesh->vert[pos];
_volume->GetXIntercept(p1, p2, v, _thr);
return;
}
}
if (p1.Y()==_current_slice+1)
{
if ((pos=_x_ns[index])==-1)
{
_x_ns[index] = (VertexIndex) _mesh->vert.size();
pos = _x_ns[index];
Allocator<MeshType>::AddVertices( *_mesh, 1 );
v = &_mesh->vert[pos];
_volume->GetXIntercept(p1, p2, v,_thr);
return;
}
}
assert(pos >=0 && size_t(pos)< _mesh->vert.size());
v = &_mesh->vert[pos];
}
void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
int i = p1.X() - _bbox.min.X();
int z = p1.Z() - _bbox.min.Z();
VertexIndex index = i+z*_bbox.max.X();
VertexIndex pos;
if ((pos=_y_cs[index])==-1)
{
_y_cs[index] = (VertexIndex) _mesh->vert.size();
pos = _y_cs[index];
Allocator<MeshType>::AddVertices( *_mesh, 1);
v = &_mesh->vert[ pos ];
_volume->GetYIntercept(p1, p2, v,_thr);
}
v = &_mesh->vert[pos];
}
void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
int i = p1.X() - _bbox.min.X();
int z = p1.Z() - _bbox.min.Z();
VertexIndex index = i+z*_bbox.max.X();
VertexIndex pos;
if (p1.Y()==_current_slice)
{
if ((pos=_z_cs[index])==-1)
{
_z_cs[index] = (VertexIndex) _mesh->vert.size();
pos = _z_cs[index];
Allocator<MeshType>::AddVertices( *_mesh, 1 );
v = &_mesh->vert[pos];
_volume->GetZIntercept(p1, p2, v,_thr);
return;
}
}
if (p1.Y()==_current_slice+1)
{
if ((pos=_z_ns[index])==-1)
{
_z_ns[index] = (VertexIndex) _mesh->vert.size();
pos = _z_ns[index];
Allocator<MeshType>::AddVertices( *_mesh, 1 );
v = &_mesh->vert[pos];
_volume->GetZIntercept(p1, p2, v,_thr);
return;
}
}
v = &_mesh->vert[pos];
}
protected:
Box3i _bbox;
int _slice_dimension;
int _current_slice;
VertexIndex *_x_cs; // indici dell'intersezioni della superficie lungo gli Xedge della fetta corrente
VertexIndex *_y_cs; // indici dell'intersezioni della superficie lungo gli Yedge della fetta corrente
VertexIndex *_z_cs; // indici dell'intersezioni della superficie lungo gli Zedge della fetta corrente
VertexIndex *_x_ns; // indici dell'intersezioni della superficie lungo gli Xedge della prossima fetta
VertexIndex *_z_ns; // indici dell'intersezioni della superficie lungo gli Zedge della prossima fetta
MeshType *_mesh;
VolumeType *_volume;
float _thr;
void NextSlice()
{
memset(_x_cs, -1, _slice_dimension*sizeof(VertexIndex));
memset(_y_cs, -1, _slice_dimension*sizeof(VertexIndex));
memset(_z_cs, -1, _slice_dimension*sizeof(VertexIndex));
std::swap(_x_cs, _x_ns);
std::swap(_z_cs, _z_ns);
_current_slice += 1;
}
void Begin()
{
_current_slice = _bbox.min.Y();
memset(_x_cs, -1, _slice_dimension*sizeof(VertexIndex));
memset(_y_cs, -1, _slice_dimension*sizeof(VertexIndex));
memset(_z_cs, -1, _slice_dimension*sizeof(VertexIndex));
memset(_x_ns, -1, _slice_dimension*sizeof(VertexIndex));
memset(_z_ns, -1, _slice_dimension*sizeof(VertexIndex));
}
};
} // end namespace
} // end namespace
#endif // __VCGTEST_WALKER

View File

@ -0,0 +1,829 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCGLIB_PLATONIC
#define __VCGLIB_PLATONIC
#include<vcg/math/base.h>
#include<vcg/complex/trimesh/allocate.h>
#include<vcg/complex/trimesh/refine.h>
#include<vcg/complex/trimesh/update/flag.h>
namespace vcg {
namespace tri {
/** \addtogroup trimesh */
//@{
/**
A set of functions that builds meshes
that represent surfaces of platonic solids,
and other simple shapes.
The 1st parameter is the mesh that will
be filled with the solid.
*/
template <class TetraMeshType>
void Tetrahedron(TetraMeshType &in)
{
typedef TetraMeshType MeshType;
typedef typename TetraMeshType::CoordType CoordType;
typedef typename TetraMeshType::VertexPointer VertexPointer;
typedef typename TetraMeshType::VertexIterator VertexIterator;
typedef typename TetraMeshType::FaceIterator FaceIterator;
in.Clear();
Allocator<TetraMeshType>::AddVertices(in,4);
Allocator<TetraMeshType>::AddFaces(in,4);
VertexPointer ivp[4];
VertexIterator vi=in.vert.begin();
ivp[0]=&*vi;(*vi).P()=CoordType ( 1.0, 1.0, 1.0); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType (-1.0, 1.0,-1.0); ++vi;
ivp[2]=&*vi;(*vi).P()=CoordType (-1.0,-1.0, 1.0); ++vi;
ivp[3]=&*vi;(*vi).P()=CoordType ( 1.0,-1.0,-1.0);
FaceIterator fi=in.face.begin();
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[3]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[1]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1];
}
/// builds a Dodecahedron,
/// (each pentagon is composed of 5 triangles)
template <class DodMeshType>
void Dodecahedron(DodMeshType & in)
{
typedef DodMeshType MeshType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::ScalarType ScalarType;
const int N_penta=12;
const int N_points=62;
int penta[N_penta*3*3]=
{20,11, 18, 18, 11, 8, 8, 11, 4,
13,23, 4, 4, 23, 8, 8, 23, 16,
13, 4, 30, 30, 4, 28, 28, 4, 11,
16,34, 8, 8, 34, 18, 18, 34, 36,
11,20, 28, 28, 20, 45, 45, 20, 38,
13,30, 23, 23, 30, 41, 41, 30, 47,
16,23, 34, 34, 23, 50, 50, 23, 41,
20,18, 38, 38, 18, 52, 52, 18, 36,
30,28, 47, 47, 28, 56, 56, 28, 45,
50,60, 34, 34, 60, 36, 36, 60, 52,
45,38, 56, 56, 38, 60, 60, 38, 52,
50,41, 60, 60, 41, 56, 56, 41, 47 };
//A B E D C
const ScalarType p=(1.0 + math::Sqrt(5.0)) / 2.0;
const ScalarType p2=p*p;
const ScalarType p3=p*p*p;
ScalarType vv[N_points*3]=
{
0, 0, 2*p2, p2, 0, p3, p, p2, p3,
0, p, p3, -p, p2, p3, -p2, 0, p3,
-p, -p2, p3, 0, -p, p3, p, -p2, p3,
p3, p, p2, p2, p2, p2, 0, p3, p2,
-p2, p2, p2, -p3, p, p2, -p3, -p, p2,
-p2, -p2, p2, 0, -p3, p2, p2, -p2, p2,
p3, -p, p2, p3, 0, p, p2, p3, p,
-p2, p3, p, -p3, 0, p, -p2, -p3, p,
p2, -p3, p, 2*p2, 0, 0, p3, p2, 0,
p, p3, 0, 0, 2*p2, 0, -p, p3, 0,
-p3, p2, 0, -2*p2, 0, 0, -p3, -p2, 0,
-p, -p3, 0, 0, -2*p2, 0, p, -p3, 0,
p3, -p2, 0, p3, 0, -p, p2, p3, -p,
-p2, p3, -p, -p3, 0, -p, -p2, -p3, -p,
p2, -p3, -p, p3, p, -p2, p2, p2, -p2,
0, p3, -p2, -p2, p2, -p2, -p3, p, -p2,
-p3, -p, -p2, -p2, -p2, -p2, 0, -p3, -p2,
p2, -p2, -p2, p3, -p, -p2, p2, 0, -p3,
p, p2, -p3, 0, p, -p3, -p, p2, -p3,
-p2, 0, -p3, -p, -p2, -p3, 0, -p, -p3,
p, -p2, -p3, 0, 0, -2*p2
};
in.Clear();
//in.face.clear();
Allocator<DodMeshType>::AddVertices(in,20+12);
Allocator<DodMeshType>::AddFaces(in, 5*12); // five pentagons, each made by 5 tri
int h,i,j,m=0;
bool used[N_points];
for (i=0; i<N_points; i++) used[i]=false;
int reindex[20+12 *10];
ScalarType xx,yy,zz, sx,sy,sz;
int order[5]={0,1,8,6,2};
int added[12];
VertexIterator vi=in.vert.begin();
for (i=0; i<12; i++) {
sx=sy=sz=0;
for (int j=0; j<5; j++) {
h= penta[ i*9 + order[j] ]-1;
xx=vv[h*3];yy=vv[h*3+1];zz=vv[h*3+2]; sx+=xx; sy+=yy; sz+=zz;
if (!used[h]) {
(*vi).P()=CoordType( xx, yy, zz ); vi++;
used[h]=true;
reindex[ h ] = m++;
}
}
(*vi).P()=CoordType( sx/5.0, sy/5.0, sz/5.0 ); vi++;
added[ i ] = m++;
}
std::vector<VertexPointer> index(in.vn);
for(j=0,vi=in.vert.begin();j<in.vn;++j,++vi) index[j] = &(*vi);
FaceIterator fi=in.face.begin();
for (i=0; i<12; i++) {
for (j=0; j<5; j++){
(*fi).V(0)=index[added[i] ];
(*fi).V(1)=index[reindex[penta[i*9 + order[j ] ] -1 ] ];
(*fi).V(2)=index[reindex[penta[i*9 + order[(j+1)%5] ] -1 ] ];
if (in.HasPerFaceFlags()) {
// tag faux edges
(*fi).SetF(0);
(*fi).SetF(2);
}
fi++;
}
}
}
template <class OctMeshType>
void Octahedron(OctMeshType &in)
{
typedef OctMeshType MeshType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
in.Clear();
Allocator<OctMeshType>::AddVertices(in,6);
Allocator<OctMeshType>::AddFaces(in,8);
VertexPointer ivp[6];
VertexIterator vi=in.vert.begin();
ivp[0]=&*vi;(*vi).P()=CoordType ( 1, 0, 0); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType ( 0, 1, 0); ++vi;
ivp[2]=&*vi;(*vi).P()=CoordType ( 0, 0, 1); ++vi;
ivp[3]=&*vi;(*vi).P()=CoordType (-1, 0, 0); ++vi;
ivp[4]=&*vi;(*vi).P()=CoordType ( 0,-1, 0); ++vi;
ivp[5]=&*vi;(*vi).P()=CoordType ( 0, 0,-1);
FaceIterator fi=in.face.begin();
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[4]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[5]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[1]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[5]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[4]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1];
}
template <class IcoMeshType>
void Icosahedron(IcoMeshType &in)
{
typedef IcoMeshType MeshType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
ScalarType L=ScalarType((math::Sqrt(5.0)+1.0)/2.0);
CoordType vv[12]={
CoordType ( 0, L, 1),
CoordType ( 0, L,-1),
CoordType ( 0,-L, 1),
CoordType ( 0,-L,-1),
CoordType ( L, 1, 0),
CoordType ( L,-1, 0),
CoordType (-L, 1, 0),
CoordType (-L,-1, 0),
CoordType ( 1, 0, L),
CoordType (-1, 0, L),
CoordType ( 1, 0,-L),
CoordType (-1, 0,-L)
};
int ff[20][3]={
{1,0,4},{0,1,6},{2,3,5},{3,2,7},
{4,5,10},{5,4,8},{6,7,9},{7,6,11},
{8,9,2},{9,8,0},{10,11,1},{11,10,3},
{0,8,4},{0,6,9},{1,4,10},{1,11,6},
{2,5,8},{2,9,7},{3,10,5},{3,7,11}
};
in.Clear();
Allocator<IcoMeshType>::AddVertices(in,12);
Allocator<IcoMeshType>::AddFaces(in,20);
VertexPointer ivp[12];
VertexIterator vi;
int i;
for(i=0,vi=in.vert.begin();vi!=in.vert.end();++i,++vi){
(*vi).P()=vv[i];
ivp[i]=&*vi;
}
FaceIterator fi;
for(i=0,fi=in.face.begin();fi!=in.face.end();++i,++fi){
(*fi).V(0)=ivp[ff[i][0]];
(*fi).V(1)=ivp[ff[i][1]];
(*fi).V(2)=ivp[ff[i][2]];
}
}
template <class MeshType>
void Hexahedron(MeshType &in)
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
in.Clear();
Allocator<MeshType>::AddVertices(in,8);
Allocator<MeshType>::AddFaces(in,12);
VertexPointer ivp[8];
VertexIterator vi=in.vert.begin();
ivp[7]=&*vi;(*vi).P()=CoordType (-1,-1,-1); ++vi;
ivp[6]=&*vi;(*vi).P()=CoordType ( 1,-1,-1); ++vi;
ivp[5]=&*vi;(*vi).P()=CoordType (-1, 1,-1); ++vi;
ivp[4]=&*vi;(*vi).P()=CoordType ( 1, 1,-1); ++vi;
ivp[3]=&*vi;(*vi).P()=CoordType (-1,-1, 1); ++vi;
ivp[2]=&*vi;(*vi).P()=CoordType ( 1,-1, 1); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType (-1, 1, 1); ++vi;
ivp[0]=&*vi;(*vi).P()=CoordType ( 1, 1, 1);
FaceIterator fi=in.face.begin();
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[4]; ++fi;
(*fi).V(0)=ivp[6]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[1]; ++fi;
(*fi).V(0)=ivp[5]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[4]; ++fi;
(*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[6]; ++fi;
(*fi).V(0)=ivp[4]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[5]; ++fi;
(*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[3]; ++fi;
(*fi).V(0)=ivp[2]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[6]; ++fi;
(*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[5]; ++fi;
(*fi).V(0)=ivp[1]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[3];
if (in.HasPerFaceFlags()) {
FaceIterator fi=in.face.begin();
for (int k=0; k<12; k++) {
(*fi).SetF(1); fi++;
}
}
}
template <class MeshType>
void Square(MeshType &in)
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
in.Clear();
Allocator<MeshType>::AddVertices(in,4);
Allocator<MeshType>::AddFaces(in,2);
VertexPointer ivp[4];
VertexIterator vi=in.vert.begin();
ivp[0]=&*vi;(*vi).P()=CoordType ( 1, 0, 0); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType ( 0, 1, 0); ++vi;
ivp[2]=&*vi;(*vi).P()=CoordType (-1, 0, 0); ++vi;
ivp[3]=&*vi;(*vi).P()=CoordType ( 0,-1, 0);
FaceIterator fi=in.face.begin();
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[2]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[0];
if (in.HasPerFaceFlags()) {
FaceIterator fi=in.face.begin();
for (int k=0; k<2; k++) {
(*fi).SetF(2); fi++;
}
}
}
// this function build a sphere starting from a eventually not empty mesh.
// If the mesh is not empty it is 'spherified' and used as base for the subdivision process.
// otherwise an icosahedron is used.
template <class MeshType>
void Sphere(MeshType &in, const int subdiv = 3 )
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
if(in.vn==0 && in.fn==0) Icosahedron(in);
VertexIterator vi;
for(vi = in.vert.begin(); vi!=in.vert.end();++vi)
vi->P().Normalize();
tri::UpdateFlags<MeshType>::FaceBorderFromNone(in);
tri::UpdateTopology<MeshType>::FaceFace(in);
size_t lastsize = 0;
for(int i = 0 ; i < subdiv; ++i)
{
Refine< MeshType, MidPoint<MeshType> >(in, MidPoint<MeshType>(&in), 0);
for(vi = in.vert.begin() + lastsize; vi != in.vert.end(); ++vi)
vi->P().Normalize();
lastsize = in.vert.size();
}
}
/// r1 = raggio 1, r2 = raggio2, h = altezza (asse y)
template <class MeshType>
void Cone( MeshType& in,
const typename MeshType::ScalarType r1,
const typename MeshType::ScalarType r2,
const typename MeshType::ScalarType h,
const int SubDiv = 36 )
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
int i,b1,b2;
in.Clear();
int VN,FN;
if(r1==0 || r2==0) {
VN=SubDiv+2;
FN=SubDiv*2;
} else {
VN=SubDiv*2+2;
FN=SubDiv*4;
}
Allocator<MeshType>::AddVertices(in,VN);
Allocator<MeshType>::AddFaces(in,FN);
VertexPointer *ivp = new VertexPointer[VN];
VertexIterator vi=in.vert.begin();
ivp[0]=&*vi;(*vi).P()=CoordType ( 0,-h/2.0,0 ); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType ( 0, h/2.0,0 ); ++vi;
b1 = b2 = 2;
int cnt=2;
if(r1!=0)
{
for(i=0;i<SubDiv;++i)
{
double a = math::ToRad(i*360.0/SubDiv);
ivp[cnt]=&*vi; (*vi).P()= CoordType(r1*cos(a), -h/2.0, r1*sin(a)); ++vi;++cnt;
}
b2 += SubDiv;
}
if(r2!=0)
{
for(i=0;i<SubDiv;++i)
{
double a = math::ToRad(i*360.0/SubDiv);
ivp[cnt]=&*vi; (*vi).P()= CoordType( r2*cos(a), h/2.0, r2*sin(a)); ++vi;++cnt;
}
}
FaceIterator fi=in.face.begin();
if(r1!=0) for(i=0;i<SubDiv;++i,++fi) {
(*fi).V(0)=ivp[0];
(*fi).V(1)=ivp[b1+i];
(*fi).V(2)=ivp[b1+(i+1)%SubDiv];
}
if(r2!=0) for(i=0;i<SubDiv;++i,++fi) {
(*fi).V(0)=ivp[1];
(*fi).V(2)=ivp[b2+i];
(*fi).V(1)=ivp[b2+(i+1)%SubDiv];
}
if(r1==0) for(i=0;i<SubDiv;++i,++fi)
{
(*fi).V(0)=ivp[0];
(*fi).V(1)=ivp[b2+i];
(*fi).V(2)=ivp[b2+(i+1)%SubDiv];
}
if(r2==0) for(i=0;i<SubDiv;++i,++fi){
(*fi).V(0)=ivp[1];
(*fi).V(2)=ivp[b1+i];
(*fi).V(1)=ivp[b1+(i+1)%SubDiv];
}
if(r1!=0 && r2!=0)for(i=0;i<SubDiv;++i)
{
(*fi).V(0)=ivp[b1+i];
(*fi).V(1)=ivp[b2+i];
(*fi).V(2)=ivp[b2+(i+1)%SubDiv];
++fi;
(*fi).V(0)=ivp[b1+i];
(*fi).V(1)=ivp[b2+(i+1)%SubDiv];
(*fi).V(2)=ivp[b1+(i+1)%SubDiv];
++fi;
}
}
template <class MeshType >
void Box(MeshType &in, const typename MeshType::BoxType & bb )
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
in.Clear();
Allocator<MeshType>::AddVertices(in,8);
Allocator<MeshType>::AddFaces(in,12);
VertexPointer ivp[8];
VertexIterator vi=in.vert.begin();
ivp[0]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.min[1],bb.min[2]); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.min[1],bb.min[2]); ++vi;
ivp[2]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.max[1],bb.min[2]); ++vi;
ivp[3]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.max[1],bb.min[2]); ++vi;
ivp[4]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.min[1],bb.max[2]); ++vi;
ivp[5]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.min[1],bb.max[2]); ++vi;
ivp[6]=&*vi;(*vi).P()=CoordType (bb.min[0],bb.max[1],bb.max[2]); ++vi;
ivp[7]=&*vi;(*vi).P()=CoordType (bb.max[0],bb.max[1],bb.max[2]);
FaceIterator fi=in.face.begin();
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[3]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[1]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[2]; (*fi).V(2)=ivp[4]; ++fi;
(*fi).V(0)=ivp[6]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[2]; ++fi;
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[4]; (*fi).V(2)=ivp[1]; ++fi;
(*fi).V(0)=ivp[5]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[4]; ++fi;
(*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[6]; ++fi;
(*fi).V(0)=ivp[4]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[5]; ++fi;
(*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[6]; (*fi).V(2)=ivp[3]; ++fi;
(*fi).V(0)=ivp[2]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[6]; ++fi;
(*fi).V(0)=ivp[7]; (*fi).V(1)=ivp[3]; (*fi).V(2)=ivp[5]; ++fi;
(*fi).V(0)=ivp[1]; (*fi).V(1)=ivp[5]; (*fi).V(2)=ivp[3];
if (in.HasPerFaceFlags()) {
FaceIterator fi=in.face.begin();
for (int k=0; k<12; k++) {
(*fi).SetF(1); fi++;
}
}
}
// this function build a mesh starting from a vector of generic coords (objects having a triple of float at their beginning)
// and a vector of faces (objects having a triple of ints at theri beginning).
template <class MeshType,class V, class F >
void Build( MeshType & in, const V & v, const F & f)
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
Allocator<MeshType>::AddVertices(in,v.size());
Allocator<MeshType>::AddFaces(in,f.size());
typename V::const_iterator vi;
typename MeshType::VertexType tv;
for(int i=0;i<v.size();++i)
{
float *vv=(float *)(&v[i]);
in.vert[i].P() = CoordType( vv[0],vv[1],vv[2]);
}
std::vector<VertexPointer> index(in.vn);
VertexIterator j;
int k;
for(k=0,j=in.vert.begin();j!=in.vert.end();++j,++k)
index[k] = &*j;
typename F::const_iterator fi;
typename MeshType::FaceType ft;
for(int i=0;i<f.size();++i)
{
int * ff=(int *)(&f[i]);
assert( ff[0]>=0 );
assert( ff[1]>=0 );
assert( ff[2]>=0 );
assert( ff[0]<in.vn );
assert( ff[1]<in.vn );
assert( ff[2]<in.vn );
in.face[i].V(0) = &in.vert[ ff[0] ];
in.face[i].V(1) = &in.vert[ ff[0] ];
in.face[i].V(2) = &in.vert[ ff[0] ];
}
}
// Build a regular grid mesh as a typical height field mesh
// x y are the position on the grid scaled by wl and hl (at the end x is in the range 0..wl and y is in 0..hl)
// z is taken from the <data> array
// Once generated the vertex positions it uses the FaceGrid function to generate the faces;
template <class MeshType>
void Grid(MeshType & in, int w, int h, float wl, float hl, float *data)
{
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
in.Clear();
Allocator<MeshType>::AddVertices(in,w*h);
float wld=wl/float(w);
float hld=hl/float(h);
for(int i=0;i<h;++i)
for(int j=0;j<w;++j)
in.vert[i*w+j].P()=CoordType ( j*wld, i*hld, data[i*w+j]);
FaceGrid(in,w,h);
}
// Build a regular grid mesh of faces as a typical height field mesh
// Vertexes are assumed to be already be allocated.
template <class MeshType>
void FaceGrid(MeshType & in, int w, int h)
{
assert(in.vn == (int)in.vert.size()); // require a compact vertex vector
assert(in.vn >= w*h); // the number of vertices should match the number of expected grid vertices
Allocator<MeshType>::AddFaces(in,(w-1)*(h-1)*2);
// i+0,j+0 -- i+0,j+1
// | \ |
// | \ |
// | \ |
// | \ |
// i+1,j+0 -- i+1,j+1
//
for(int i=0;i<h-1;++i)
for(int j=0;j<w-1;++j)
{
in.face[2*(i*(w-1)+j)+0].V(0) = &(in.vert[(i+1)*w+j+1]);
in.face[2*(i*(w-1)+j)+0].V(1) = &(in.vert[(i+0)*w+j+1]);
in.face[2*(i*(w-1)+j)+0].V(2) = &(in.vert[(i+0)*w+j+0]);
in.face[2*(i*(w-1)+j)+1].V(0) = &(in.vert[(i+0)*w+j+0]);
in.face[2*(i*(w-1)+j)+1].V(1) = &(in.vert[(i+1)*w+j+0]);
in.face[2*(i*(w-1)+j)+1].V(2) = &(in.vert[(i+1)*w+j+1]);
}
if (in.HasPerFaceFlags()) {
for (int k=0; k<(h-1)*(w-1)*2; k++) {
in.face[k].SetF(2);
}
}
}
// Build a regular grid mesh of faces as a typical height field mesh
// Vertexes are assumed to be already be allocated, but not oll the grid vertexes are present.
// For this purpos a grid of indexes is also passed. negative indexes means that there is no vertex.
template <class MeshType>
void FaceGrid(MeshType & in, const std::vector<int> &grid, int w, int h)
{
assert(in.vn == (int)in.vert.size()); // require a compact vertex vector
assert(in.vn <= w*h); // the number of vertices should match the number of expected grid vertices
// V0 V1
// i+0,j+0 -- i+0,j+1
// | \ |
// | \ |
// | \ |
// | \ |
// i+1,j+0 -- i+1,j+1
// V2 V3
for(int i=0;i<h-1;++i)
for(int j=0;j<w-1;++j)
{
int V0i= grid[(i+0)*w+j+0];
int V1i= grid[(i+0)*w+j+1];
int V2i= grid[(i+1)*w+j+0];
int V3i= grid[(i+1)*w+j+1];
int ndone=0;
bool quad = (V0i>=0 && V1i>=0 && V2i>=0 && V3i>=0 ) && in.HasPerFaceFlags();
if(V0i>=0 && V2i>=0 && V3i>=0 )
{
typename MeshType::FaceIterator f= Allocator<MeshType>::AddFaces(in,1);
f->V(0)=&(in.vert[V3i]);
f->V(1)=&(in.vert[V2i]);
f->V(2)=&(in.vert[V0i]);
if (quad) f->SetF(2);
ndone++;
}
if(V0i>=0 && V1i>=0 && V3i>=0 )
{
typename MeshType::FaceIterator f= Allocator<MeshType>::AddFaces(in,1);
f->V(0)=&(in.vert[V0i]);
f->V(1)=&(in.vert[V1i]);
f->V(2)=&(in.vert[V3i]);
if (quad) f->SetF(2);
ndone++;
}
if (ndone==0) { // try diag the other way
if(V2i>=0 && V0i>=0 && V1i>=0 )
{
typename MeshType::FaceIterator f= Allocator<MeshType>::AddFaces(in,1);
f->V(0)=&(in.vert[V2i]);
f->V(1)=&(in.vert[V0i]);
f->V(2)=&(in.vert[V1i]);
ndone++;
}
if(V1i>=0 && V3i>=0 && V2i>=0 )
{
typename MeshType::FaceIterator f= Allocator<MeshType>::AddFaces(in,1);
f->V(0)=&(in.vert[V1i]);
f->V(1)=&(in.vert[V3i]);
f->V(2)=&(in.vert[V2i]);
ndone++;
}
}
}
}
template <class MeshType>
void Cylinder(int slices, int stacks, MeshType & m){
typename MeshType::VertexIterator vi = vcg::tri::Allocator<MeshType>::AddVertices(m,slices*(stacks+1));
for ( int i = 0; i < stacks+1; ++i)
for ( int j = 0; j < slices; ++j)
{
float x,y,h;
x = cos( 2.0 * M_PI / slices * j);
y = sin( 2.0 * M_PI / slices * j);
h = 2 * i / (float)(stacks) - 1;
(*vi).P() = typename MeshType::CoordType(x,h,y);
++vi;
}
typename MeshType::FaceIterator fi ;
for ( int j = 0; j < stacks; ++j)
for ( int i = 0; i < slices; ++i)
{
int a,b,c,d;
a = (j+0)*slices + i;
b = (j+1)*slices + i;
c = (j+1)*slices + (i+1)%slices;
d = (j+0)*slices + (i+1)%slices;
if(((i+j)%2) == 0){
fi = vcg::tri::Allocator<MeshType>::AddFaces(m,1);
(*fi).V(0) = &m.vert[ a ];
(*fi).V(1) = &m.vert[ b ];
(*fi).V(2) = &m.vert[ c ];
fi = vcg::tri::Allocator<MeshType>::AddFaces(m,1);
(*fi).V(0) = &m.vert[ c ];
(*fi).V(1) = &m.vert[ d ];
(*fi).V(2) = &m.vert[ a ];
}
else{
fi = vcg::tri::Allocator<MeshType>::AddFaces(m,1);
(*fi).V(0) = &m.vert[ b ];
(*fi).V(1) = &m.vert[ c ];
(*fi).V(2) = &m.vert[ d ];
fi = vcg::tri::Allocator<MeshType>::AddFaces(m,1);
(*fi).V(0) = &m.vert[ d ];
(*fi).V(1) = &m.vert[ a ];
(*fi).V(2) = &m.vert[ b ];
}
}
if (m.HasPerFaceFlags()) {
for (typename MeshType::FaceIterator fi=m.face.begin(); fi!=m.face.end(); fi++) {
(*fi).SetF(2);
}
}
}
template <class MeshType>
void GenerateCameraMesh(MeshType &in){
typedef typename MeshType::CoordType MV;
MV vv[52]={
MV(-0.000122145 , -0.2 ,0.35),
MV(0.000122145 , -0.2 ,-0.35),MV(-0.000122145 , 0.2 ,0.35),MV(0.000122145 , 0.2 ,-0.35),MV(0.999878 , -0.2 ,0.350349),MV(1.00012 , -0.2 ,-0.349651),MV(0.999878 , 0.2 ,0.350349),MV(1.00012 , 0.2 ,-0.349651),MV(1.28255 , 0.1 ,0.754205),MV(1.16539 , 0.1 ,1.03705),MV(0.88255 , 0.1 ,1.15421),
MV(0.599707 , 0.1 ,1.03705),MV(0.48255 , 0.1 ,0.754205),MV(0.599707 , 0.1 ,0.471362),MV(0.88255 , 0.1 ,0.354205),MV(1.16539 , 0.1 ,0.471362),MV(1.28255 , -0.1 ,0.754205),MV(1.16539 , -0.1 ,1.03705),MV(0.88255 , -0.1 ,1.15421),MV(0.599707 , -0.1 ,1.03705),MV(0.48255 , -0.1 ,0.754205),
MV(0.599707 , -0.1 ,0.471362),MV(1.16539 , -0.1 ,0.471362),MV(0.88255 , -0.1 ,0.354205),MV(3.49164e-005 , 0 ,-0.1),MV(1.74582e-005 , -0.0866025 ,-0.05),MV(-1.74582e-005 , -0.0866025 ,0.05),MV(-3.49164e-005 , 8.74228e-009 ,0.1),MV(-1.74582e-005 , 0.0866025 ,0.05),MV(1.74582e-005 , 0.0866025 ,-0.05),MV(-0.399913 , 1.99408e-022 ,-0.25014),
MV(-0.399956 , -0.216506 ,-0.12514),MV(-0.400044 , -0.216506 ,0.12486),MV(-0.400087 , 2.18557e-008 ,0.24986),MV(-0.400044 , 0.216506 ,0.12486),MV(-0.399956 , 0.216506 ,-0.12514),MV(0.479764 , 0.1 ,0.754205),MV(0.362606 , 0.1 ,1.03705),MV(0.0797637 , 0.1 ,1.15421),MV(-0.203079 , 0.1 ,1.03705),MV(-0.320236 , 0.1 ,0.754205),
MV(-0.203079 , 0.1 ,0.471362),MV(0.0797637 , 0.1 ,0.354205),MV(0.362606 , 0.1 ,0.471362),MV(0.479764 , -0.1 ,0.754205),MV(0.362606 , -0.1 ,1.03705),MV(0.0797637 , -0.1 ,1.15421),MV(-0.203079 , -0.1 ,1.03705),MV(-0.320236 , -0.1 ,0.754205),MV(0.0797637 , -0.1 ,0.354205),MV(0.362606 , -0.1 ,0.471362),
MV(-0.203079 , -0.1 ,0.471362), };
int ff[88][3]={
{0,2,3},
{3,1,0},{4,5,7},{7,6,4},{0,1,5},{5,4,0},{1,3,7},{7,5,1},{3,2,6},{6,7,3},{2,0,4},
{4,6,2},{10,9,8},{10,12,11},{10,13,12},{10,14,13},{10,15,14},{10,8,15},{8,17,16},{8,9,17},{9,18,17},
{9,10,18},{10,19,18},{10,11,19},{11,20,19},{11,12,20},{12,21,20},{12,13,21},{13,23,21},{13,14,23},{14,22,23},
{14,15,22},{15,16,22},{15,8,16},{23,16,17},{23,17,18},{23,18,19},{23,19,20},{23,20,21},{23,22,16},{25,27,26},
{25,28,27},{25,29,28},{25,24,29},{24,31,30},{24,25,31},{25,32,31},{25,26,32},{26,33,32},{26,27,33},{27,34,33},
{27,28,34},{28,35,34},{28,29,35},{29,30,35},{29,24,30},{35,30,31},{35,31,32},{35,32,33},{35,33,34},{42,37,36},
{42,38,37},{42,39,38},{42,40,39},{42,41,40},{42,36,43},{36,45,44},{36,37,45},{37,46,45},{37,38,46},{38,47,46},
{38,39,47},{39,48,47},{39,40,48},{40,51,48},{40,41,51},{41,49,51},{41,42,49},{42,50,49},{42,43,50},{43,44,50},
{43,36,44},{51,44,45},{51,45,46},{51,46,47},{51,47,48},{51,49,50},{51,50,44},
};
in.Clear();
Allocator<MeshType>::AddVertices(in,52);
Allocator<MeshType>::AddFaces(in,88);
in.vn=52;in.fn=88;
int i,j;
for(i=0;i<in.vn;i++)
in.vert[i].P()=vv[i];;
std::vector<typename MeshType::VertexPointer> index(in.vn);
typename MeshType::VertexIterator vi;
for(j=0,vi=in.vert.begin();j<in.vn;++j,++vi) index[j] = &*vi;
for(j=0;j<in.fn;++j)
{
in.face[j].V(0)=index[ff[j][0]];
in.face[j].V(1)=index[ff[j][1]];
in.face[j].V(2)=index[ff[j][2]];
}
}
//@}
} // End Namespace TriMesh
} // End Namespace vcg
#endif

View File

@ -0,0 +1,102 @@
MARCHING CUBES & EXTENDED MARCHING CUBES
===================================================================================
In breve le classi coinvolte sono 3 e sono:
* MerchingCubes ed ExtendedMarchingCubes
processano una cella alla volta, aggiungendo per ogni chiamata a ProcessCell
l'insieme di triangoli approssimante la superficie che interseca la cella
* Walker
gestisce l'attraversamento del volume, servendo le chiamate effettuate dagli
algoritmi di estrazione della superficie al volume e cachandone i risultato
* Volume
conosce come calcolare il campo scalare all'interno del volume da processare
e come calcolare le intersezioni superficie/segmenti.
DESCRIZIONE
====================================================================================
Le classi che implementano gli algoritmi MarchingCubes ed ExtendedMarchingCubes
sono state implementate così da risultare quanto più generiche possibile: ogni chiamata
al metodo ProcessCell(Point3i p1, Point3i p2) analizza la cella del volume individuata
dai due punti p1 e p2 e l'analisi di questa cella si conclude esattamente al ritorno da questa
chiamata: nel caso infatti la superficie da estrarre attraversi questa cella, all'interno
di questa stessa chiamata la mesh viene aggiornata con un opportuno insieme di triangoli.
L'assunzione alla base di questa astrazione è l'esistenza di altri due entità, il Walker
ed il Volume; sebbene sulla loro implementazione è lasciata la più completa libertà, è utile
precisare in quale relazione essi stiano rispetto agli algoritmi di estrazione di superfici.
Un esempio che riassume quanto qui esposto è incluso nella libreria: vd. vcg/apps/test/extractors.
VOLUME
====================================================================================
Gli algoritmi di estrazione di superfici risalgono alla superficie utilizzando i valori
di un campo scalare definito sul volume da processare campionato sui vertici di una qualche
griglia. Questo campo scalare sarà generalmente diverso a seconda del tipo di applicazione.
Il Volume è appunto quella classe che racchiude il campo scalare e di cui ne conosce le proprietà.
In realtà, all'interno dell'algoritmo di estrazione di superfici, non esiste alcun collegamento
esplicito con il Volume: tutte le sue chiamate sono rivolte al Walker. Questo perché
(per motivi che saranno esposti successivamente a proposito del Walker) il Walker potrebbe già
possedere il valore del campo calcolato in un dato punto, che evita così di farselo ricalcolare
nuovamente dal Volume: Similmente, quando viene chiesto di calcolare il punto di intersezione
della superficie con un segmento, se il Walker dispone già di questa informazione, la restituisce
all'algoritmo di estrazione di superfici, altrimenti il calcolo verrà effettuato dal Volume: il
punto ottenuto verrà restituito al Walker che potrà quindi soddisfare la richiesta iniziale.
Il motivo per cui si è scelto di frapporre un Walker fra gli algoritmi di estrazione di superfici
ed il Volume è esclusivamente di ottimizzazione. L'idea di fondo è che il Walker è quell'oggetto
attrverso cui avviene la visita del volume: per ogni cella del volume, esso effettua la chiamata
ProcessCell. Conoscendo l'ordine di visita, il Walker è anche la classe candidata ad implementare
le politiche di caching, in quanto sa esattamente da che momento può essere utile una certa
informazione e per quanto a lungo può essere conveniente mantenerla prima di liberarsene.
WALKER
====================================================================================
Poiché la politica di visita del volume è realizzata all'interno del walker,
è opportuno che sempre all'interno del walker vengano realizzate le politiche
di caching rivolte ad ottimizzare l'esecuzione degli algoritmi MC ed EMC. Durante
il processing di ogni cella questi algoritmi possono chiamare le seguenti funzioni
del Walker:
MC EMC
------------------------------------------
V(i, j, k) X X
GetXIntercept(p1, p2, v) X X
GetYIntercept(p1, p2, v) X X
GetZIntercept(p1, p2, v) X X
Exist(p1, p2, v) X
const float V(int i, int j, int k) const
La superficie che attraversa ogni cella viene ricavata dall'algoritmo di estrazione
analizzando il valore del campo sugli otto spigoli di ogni voxel del volume;
per ogni voxel, il valore del campo sui suoi otto spigoli vengono richiesti
dall'algoritmo di estrazione al walker: se questo valore è già stato calcolato
e cachato, il walker restituisce direttamente tale valore; altrimenti il valore
del campo in questo spigolo viene calcolato (eventualmente cachato) e restituito
al walker. In questo modo il valoro del campo ad ogni punto viene calcolato una
sola volta anziché 8, questo puo' essere molto utile nel caso si utilizzi dataset
volumetrici mantenuti implicitamente.
void GetXIntercept(Point3i p1, Point3i p2, VertexPointer v)
void GetYIntercept(Point3i p1, Point3i p2, VertexPointer v)
void GetZIntercept(Point3i p1, Point3i p2, VertexPointer v)
Dall'analisi del valore del campo agli spigoli di un dato voxel, l'algoritmo di
estrazione ha rilevato che la superficie interseca lo spigolo avente estremi p1
e p2(a seconda dell'orientazione di questo spigolo, viene chiamato uno dei tre
metodi): al termine di una di queste chiamate, v deve puntare al vertice della
mesh (di coordinate comprese fra p1 e p2) attraverso cui passa la superficie.
Se questo vertice è stato già calcolato ed inserito nella mesh, il walker deve
risalire a tale vertice e memorizzarne in v il suo puntatore. Altrimenti deve provvedere
ad aggiugnere un nuovo vertice ed a calcolare la sua posizione; v deve puntare
in questo caso al vertice appena inserito.
Il motivo per cui questo calcolo non viene implementato direttamente negli algoritmi
di estrazione (possibile per es. attraverso interpolazione lineare del valore
del campo nei punti p1 e p2) è che questo calcolo può essere fatto in maniere
molto più precisa conoscendo come il campo viene calcolato, essendo infatti ciò
dipendente dall'applicazione.
bool Exist(Point3i p1, Point3i p2, VertexPointer v)
Questo metodo viene chiamato solamente all'interno dell'algoritmo MarchingCubes.
A differenza dei tre motodi precedenti, in questo caso si vuole sapere solamente
se esiste già un vertice tra i punti p1 e p2: nel caso tale vertice esista, Exist
deve resituire true ed v deve puntare a tale vertice; se invece tale vertice non
esiste, Exist deve restituire false e v deve prendere il valore NULL.
NB: nel caso in cui il vertice non esiste, alla mesh non deve essere
aggiunto alcun nuovo vertice.

View File

@ -0,0 +1,629 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_MESH_RESAMPLER
#define __VCG_MESH_RESAMPLER
#include <vcg/complex/trimesh/update/normal.h>
#include <vcg/complex/trimesh/update/flag.h>
#include <vcg/complex/trimesh/update/bounding.h>
#include <vcg/complex/trimesh/update/edges.h>
#include <vcg/complex/trimesh/create/marching_cubes.h>
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/complex/trimesh/closest.h>
#include <vcg/space/box3.h>
namespace vcg {
namespace tri {
/** \addtogroup trimesh */
/*@{*/
/*@{*/
/** Class Resampler.
This is class reasmpling a mesh using marching cubes methods
@param OLD_MESH_TYPE (Template Parameter) Specifies the type of mesh to be resampled
@param NEW_MESH_TYPE (Template Parameter) Specifies the type of output mesh.
*/
template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR = vcg::face::PointDistanceBaseFunctor<typename OLD_MESH_TYPE::ScalarType > >
class Resampler : public BasicGrid<FLT>
{
typedef OLD_MESH_TYPE Old_Mesh;
typedef NEW_MESH_TYPE New_Mesh;
//template <class OLD_MESH_TYPE,class NEW_MESH_TYPE>
class Walker : BasicGrid<float>
{
private:
typedef int VertexIndex;
typedef OLD_MESH_TYPE Old_Mesh;
typedef NEW_MESH_TYPE New_Mesh;
typedef typename New_Mesh::CoordType NewCoordType;
typedef typename New_Mesh::VertexType* VertexPointer;
typedef typename Old_Mesh::FaceContainer FaceCont;
typedef typename vcg::GridStaticPtr<typename Old_Mesh::FaceType> GridType;
protected:
int SliceSize;
int CurrentSlice;
typedef tri::FaceTmark<Old_Mesh> MarkerFace;
MarkerFace markerFunctor;
VertexIndex *_x_cs; // indici dell'intersezioni della superficie lungo gli Xedge della fetta corrente
VertexIndex *_y_cs; // indici dell'intersezioni della superficie lungo gli Yedge della fetta corrente
VertexIndex *_z_cs; // indici dell'intersezioni della superficie lungo gli Zedge della fetta corrente
VertexIndex *_x_ns; // indici dell'intersezioni della superficie lungo gli Xedge della prossima fetta
VertexIndex *_z_ns; // indici dell'intersezioni della superficie lungo gli Zedge della prossima fetta
//float *_v_cs;///values of distance fields for each direction in current slice
//float *_v_ns;///values of distance fields for each direction in next slice
typedef typename std::pair<bool,float> field_value;
field_value* _v_cs;
field_value* _v_ns;
New_Mesh *_newM;
Old_Mesh *_oldM;
GridType _g;
public:
float max_dim; // the limit value of the search (that takes into account of the offset)
float offset; // an offset value that is always added to the returned value. Useful for extrarting isosurface at a different threshold
bool DiscretizeFlag; // if the extracted surface should be discretized or not.
bool MultiSampleFlag;
bool AbsDistFlag; // if true the Distance Field computed is no more a signed one.
Walker(const Box3f &_bbox, Point3i _siz )
{
this->bbox= _bbox;
this->siz=_siz;
ComputeDimAndVoxel();
SliceSize = (this->siz.X()+1)*(this->siz.Z()+1);
CurrentSlice = 0;
offset=0;
DiscretizeFlag=false;
MultiSampleFlag=false;
AbsDistFlag=false;
_x_cs = new VertexIndex[ SliceSize ];
_y_cs = new VertexIndex[ SliceSize ];
_z_cs = new VertexIndex[ SliceSize ];
_x_ns = new VertexIndex[ SliceSize ];
_z_ns = new VertexIndex[ SliceSize ];
_v_cs= new field_value[(this->siz.X()+1)*(this->siz.Z()+1)];
_v_ns= new field_value[(this->siz.X()+1)*(this->siz.Z()+1)];
};
~Walker()
{}
float V(const Point3i &p)
{
return V(p.V(0),p.V(1),p.V(2));
}
std::pair<bool,float> VV(int x,int y,int z)
{
assert ((y==CurrentSlice)||(y==(CurrentSlice+1)));
//test if it is outside the bb of the mesh
//vcg::Point3f test=vcg::Point3f((float)x,(float)y,(float)z);
/*if (!_oldM->bbox.IsIn(test))
return (1.f);*/
int index=GetSliceIndex(x,z);
if (y==CurrentSlice) return _v_cs[index];
else return _v_ns[index];
}
float V(int x,int y,int z)
{
if(DiscretizeFlag) return VV(x,y,z).second+offset<0?-1:1;
return VV(x,y,z).second+offset;
}
///return true if the distance form the mesh is less than maxdim and return distance
field_value DistanceFromMesh(Point3f &pp,Old_Mesh */*mesh*/)
{
float dist;
typename Old_Mesh::FaceType *f=NULL;
const float max_dist = max_dim;
vcg::Point3f testPt;
this->IPfToPf(pp,testPt);
vcg::Point3f closestNormV,closestNormF;
vcg::Point3f closestPt;
vcg::Point3f pip(-1,-1,-1);
// Note that PointDistanceBaseFunctor does not require the edge and plane precomptued.
// while the PointDistanceFunctor requires them.
DISTFUNCTOR PDistFunct;
f = _g.GetClosest(PDistFunct,markerFunctor,testPt,max_dist,dist,closestPt);
if (f==NULL) return field_value(false,0);
if(AbsDistFlag) return field_value(true,dist);
assert(!f->IsD());
bool retIP;
// To compute the interpolated normal we use the more robust function that require to know what is the most orhogonal direction of the face.
if((*f).Flags() & Old_Mesh::FaceType::NORMX) retIP=InterpolationParameters(*f,0,closestPt, pip);
else if((*f).Flags() & Old_Mesh::FaceType::NORMY) retIP=InterpolationParameters(*f,1,closestPt, pip);
else if((*f).Flags() & Old_Mesh::FaceType::NORMZ) retIP=InterpolationParameters(*f,2,closestPt, pip);
else assert(0);
assert(retIP); // this should happen only if the starting mesh has degenerate faces.
const float InterpolationEpsilon = 0.00001f;
int zeroCnt=0;
if(pip[0]<InterpolationEpsilon) ++zeroCnt;
if(pip[1]<InterpolationEpsilon) ++zeroCnt;
if(pip[2]<InterpolationEpsilon) ++zeroCnt;
assert(zeroCnt<3);
Point3f dir=(testPt-closestPt).Normalize();
// Note that the two signs could be discordant.
// Always choose the best one according to where the nearest point falls.
float signBest;
// Compute test if the point see the surface normal from inside or outside
// Surface normal for improved robustness is computed both by face and interpolated from vertices.
if(zeroCnt>0) // we Not are in the middle of the face so the face normal is NOT reliable.
{
closestNormV = (f->V(0)->cN())*pip[0] + (f->V(1)->cN())*pip[1] + (f->V(2)->cN())*pip[2] ;
signBest = dir.dot(closestNormV) ;
}
else
{
closestNormF = f->cN() ;
signBest = dir.dot(closestNormF) ;
}
if(signBest<0) dist=-dist;
return field_value(true,dist);
}
field_value MultiDistanceFromMesh(Point3f &pp, Old_Mesh */*mesh*/)
{
float distSum=0;
int positiveCnt=0; // positive results counter
const int MultiSample=7;
const Point3f delta[7]={Point3f(0,0,0),
Point3f( 0.2, -0.01, -0.02),
Point3f(-0.2, 0.01, 0.02),
Point3f( 0.01, 0.2, 0.01),
Point3f( 0.03, -0.2, -0.03),
Point3f(-0.02, -0.03, 0.2 ),
Point3f(-0.01, 0.01, -0.2 )};
for(int qq=0;qq<MultiSample;++qq)
{
Point3f pp2=pp+delta[qq];
field_value ff= DistanceFromMesh(pp2,_oldM);
if(ff.first==false) return field_value(false,0);
distSum += fabs(ff.second);
if(ff.second>0) positiveCnt ++;
}
if(positiveCnt<=MultiSample/2) distSum = -distSum;
return field_value(true, distSum/MultiSample);
}
/// compute the values if an entire slice (per y) distances>dig of a cell are signed with double of
/// the distance of the bb
void ComputeSliceValues(int slice,field_value *slice_values)
{
for (int i=0; i<=this->siz.X(); i++)
{
for (int k=0; k<=this->siz.Z(); k++)
{
int index=GetSliceIndex(i,k);
Point3f pp(i,slice,k);
if(this->MultiSampleFlag) slice_values[index] = MultiDistanceFromMesh(pp,_oldM);
else slice_values[index] = DistanceFromMesh(pp,_oldM);
}
}
//ComputeConsensus(slice,slice_values);
}
/*
For some reasons it can happens that the sign of the computed distance could not correct.
this function tries to correct these issues by flipping the isolated voxels with discordant sign
*/
void ComputeConsensus(int slice, field_value *slice_values)
{
float max_dist = min(min(this->voxel[0],this->voxel[1]),this->voxel[2]);
int flippedCnt=0;
int flippedTot=0;
int flippedTimes=0;
do
{
flippedCnt=0;
for (int i=0; i<=this->siz.X(); i++)
{
for (int k=0; k<=this->siz.Z(); k++)
{
int goodCnt=0;
int badCnt=0;
int index=GetSliceIndex(i,k);
int index_l,index_r,index_u,index_d;
if(slice_values[index].first)
{
float curVal= slice_values[index].second;
if(i > 0 ) index_l=GetSliceIndex(i-1,k); else index_l = index;
if(i < this->siz.X() ) index_r=GetSliceIndex(i+1,k); else index_r = index;
if(k > 0 ) index_d=GetSliceIndex(i,k-1); else index_d = index;
if(k < this->siz.Z() ) index_u=GetSliceIndex(i,k+1); else index_u = index;
if(slice_values[index_l].first) { goodCnt++; if(fabs(slice_values[index_l].second - curVal) > max_dist) badCnt++; }
if(slice_values[index_r].first) { goodCnt++; if(fabs(slice_values[index_r].second - curVal) > max_dist) badCnt++; }
if(slice_values[index_u].first) { goodCnt++; if(fabs(slice_values[index_u].second - curVal) > max_dist) badCnt++; }
if(slice_values[index_d].first) { goodCnt++; if(fabs(slice_values[index_d].second - curVal) > max_dist) badCnt++; }
if(badCnt >= goodCnt) {
slice_values[index].second *=-1.0f;
//slice_values[index].first = false;
flippedCnt++;
}
}
}
}
flippedTot+=flippedCnt;
flippedTimes++;
} while(flippedCnt>0);
#ifndef NO_QT
if(flippedTot>0)
qDebug("Flipped %i values in %i times",flippedTot,flippedTimes);
#endif
}
template<class EXTRACTOR_TYPE>
void ProcessSlice(EXTRACTOR_TYPE &extractor)
{
for (int i=0; i<this->siz.X(); i++)
{
for (int k=0; k<this->siz.Z(); k++)
{
bool goodCell=true;
Point3i p1(i,CurrentSlice,k);
Point3i p2=p1+Point3i(1,1,1);
for(int ii=0;ii<2;++ii)
for(int jj=0;jj<2;++jj)
for(int kk=0;kk<2;++kk)
goodCell &= VV(p1[0]+ii,p1[1]+jj,p1[2]+kk).first;
if(goodCell) extractor.ProcessCell(p1, p2);
}
}
}
template<class EXTRACTOR_TYPE>
void BuildMesh(Old_Mesh &old_mesh,New_Mesh &new_mesh,EXTRACTOR_TYPE &extractor,vcg::CallBackPos *cb)
{
_newM=&new_mesh;
_oldM=&old_mesh;
// the following two steps are required to be sure that the point-face distance without precomputed data works well.
tri::UpdateNormals<Old_Mesh>::PerFaceNormalized(old_mesh);
tri::UpdateNormals<Old_Mesh>::PerVertexAngleWeighted(old_mesh);
tri::UpdateFlags<Old_Mesh>::FaceProjection(old_mesh);
int _size=(int)old_mesh.fn*100;
_g.Set(_oldM->face.begin(),_oldM->face.end(),_size);
markerFunctor.SetMesh(&old_mesh);
_newM->Clear();
Begin();
extractor.Initialize();
for (int j=0; j<=this->siz.Y(); j++)
{
cb((100*j)/this->siz.Y(),"Marching ");
ProcessSlice<EXTRACTOR_TYPE>(extractor);//find cells where there is the isosurface and examine it
NextSlice();
}
extractor.Finalize();
typename New_Mesh::VertexIterator vi;
for(vi=new_mesh.vert.begin();vi!=new_mesh.vert.end();++vi)
if(!(*vi).IsD())
{
IPfToPf((*vi).cP(),(*vi).P());
}
}
//return the index of a vertex in slide as it was stored
int GetSliceIndex(int x,int z)
{
VertexIndex index = x+z*(this->siz.X()+1);
return (index);
}
//swap slices , the initial value of distance fields ids set as double of bbox of space
void NextSlice()
{
memset(_x_cs, -1, SliceSize*sizeof(VertexIndex));
memset(_y_cs, -1, SliceSize*sizeof(VertexIndex));
memset(_z_cs, -1, SliceSize*sizeof(VertexIndex));
std::swap(_x_cs, _x_ns);
std::swap(_z_cs, _z_ns);
std::swap(_v_cs, _v_ns);
CurrentSlice ++;
ComputeSliceValues(CurrentSlice + 1,_v_ns);
}
//initialize data strucures , the initial value of distance fields ids set as double of bbox of space
void Begin()
{
CurrentSlice = 0;
memset(_x_cs, -1, SliceSize*sizeof(VertexIndex));
memset(_y_cs, -1, SliceSize*sizeof(VertexIndex));
memset(_z_cs, -1, SliceSize*sizeof(VertexIndex));
memset(_x_ns, -1, SliceSize*sizeof(VertexIndex));
memset(_z_ns, -1, SliceSize*sizeof(VertexIndex));
ComputeSliceValues(CurrentSlice,_v_cs);
ComputeSliceValues(CurrentSlice+1,_v_ns);
}
bool Exist(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
int i = p1.X();// - _bbox.min.X())/_cell_size.X();
int z = p1.Z();// - _bbox.min.Z())/_cell_size.Z();
VertexIndex index = i+z*this->siz.X();
//VertexIndex index =GetSliceIndex(//
int v_ind = 0;
if (p1.X()!=p2.X()) //intersezione della superficie con un Xedge
{
if (p1.Y()==CurrentSlice)
{
if (_x_cs[index]!=-1)
{
v_ind = _x_cs[index];
v = &_newM->vert[v_ind];
assert(!v->IsD());
return true;
}
}
else
{
if (_x_ns[index]!=-1)
{
v_ind = _x_ns[index];
v = &_newM->vert[v_ind];
assert(!v->IsD());
return true;
}
}
v = NULL;
return false;
}
else if (p1.Y()!=p2.Y()) //intersezione della superficie con un Yedge
{
if (_y_cs[index]!=-1)
{
v_ind =_y_cs[index];
v = &_newM->vert[v_ind];
assert(!v->IsD());
return true;
}
else
{
v = NULL;
return false;
}
}
else if (p1.Z()!=p2.Z())
//intersezione della superficie con un Zedge
{
if (p1.Y()==CurrentSlice)
{
if ( _z_cs[index]!=-1)
{
v_ind = _z_cs[index];
v = &_newM->vert[v_ind];
assert(!v->IsD());
return true;
}
}
else
{
if (_z_ns[index]!=-1)
{
v_ind = _z_ns[index];
v = &_newM->vert[v_ind];
assert(!v->IsD());
return true;
}
}
v = NULL;
return false;
}
assert (0);
return false;
}
///interpolate
NewCoordType Interpolate(const vcg::Point3i &p1, const vcg::Point3i &p2,int dir)
{
float f1 = (float)V(p1);
float f2 = (float)V(p2);
float u = (float) f1/(f1-f2);
NewCoordType ret=vcg::Point3f((float)p1.V(0),(float)p1.V(1),(float)p1.V(2));
ret.V(dir) = (float) p1.V(dir)*(1.f-u) + u*(float)p2.V(dir);
return (ret);
}
///if there is a vertex in z axis of a cell return the vertex or create it
void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
assert(p1.X()+1 == p2.X());
assert(p1.Y() == p2.Y());
assert(p1.Z() == p2.Z());
int i = p1.X();// (p1.X() - _bbox.min.X())/_cell_size.X();
int z = p1.Z();//(p1.Z() - _bbox.min.Z())/_cell_size.Z();
VertexIndex index = i+z*this->siz.X();
VertexIndex pos=-1;
if (p1.Y()==CurrentSlice)
{
if ((pos=_x_cs[index])==-1)
{
_x_cs[index] = (VertexIndex) _newM->vert.size();
pos = _x_cs[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,0);
return;
}
}
if (p1.Y()==CurrentSlice+1)
{
if ((pos=_x_ns[index])==-1)
{
_x_ns[index] = (VertexIndex) _newM->vert.size();
pos = _x_ns[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,0);
return;
}
}
assert(pos>=0);
v = &_newM->vert[pos];
}
///if there is a vertex in y axis of a cell return the vertex or create it
void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
assert(p1.X() == p2.X());
assert(p1.Y()+1 == p2.Y());
assert(p1.Z() == p2.Z());
int i = p1.X(); // (p1.X() - _bbox.min.X())/_cell_size.X();
int z = p1.Z(); // (p1.Z() - _bbox.min.Z())/_cell_size.Z();
VertexIndex index = i+z*this->siz.X();
VertexIndex pos=-1;
if ((pos=_y_cs[index])==-1)
{
_y_cs[index] = (VertexIndex) _newM->vert.size();
pos = _y_cs[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1);
v = &_newM->vert[ pos ];
v->P()=Interpolate(p1,p2,1);
}
assert(pos>=0);
v = &_newM->vert[pos];
}
///if there is a vertex in z axis of a cell return the vertex or create it
void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
{
assert(p1.X() == p2.X());
assert(p1.Y() == p2.Y());
assert(p1.Z()+1 == p2.Z());
int i = p1.X(); //(p1.X() - _bbox.min.X())/_cell_size.X();
int z = p1.Z(); //(p1.Z() - _bbox.min.Z())/_cell_size.Z();
VertexIndex index = i+z*this->siz.X();
VertexIndex pos=-1;
if (p1.Y()==CurrentSlice)
{
if ((pos=_z_cs[index])==-1)
{
_z_cs[index] = (VertexIndex) _newM->vert.size();
pos = _z_cs[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,2);
return;
}
}
if (p1.Y()==CurrentSlice+1)
{
if ((pos=_z_ns[index])==-1)
{
_z_ns[index] = (VertexIndex) _newM->vert.size();
pos = _z_ns[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,2);
return;
}
}
assert(pos>=0);
v = &_newM->vert[pos];
}
};//end class walker
public:
typedef Walker /*< Old_Mesh,New_Mesh>*/ MyWalker;
typedef vcg::tri::MarchingCubes<New_Mesh, MyWalker> MyMarchingCubes;
///resample the mesh using marching cube algorithm ,the accuracy is the dimension of one cell the parameter
static void Resample(Old_Mesh &old_mesh,New_Mesh &new_mesh, Box3f volumeBox, vcg::Point3<int> accuracy,float max_dist, float thr=0, bool DiscretizeFlag=false, bool MultiSampleFlag=false, bool AbsDistFlag=false, vcg::CallBackPos *cb=0 )
{
///be sure that the bounding box is updated
vcg::tri::UpdateBounding<Old_Mesh>::Box(old_mesh);
MyWalker walker(volumeBox,accuracy);
walker.max_dim=max_dist+fabs(thr);
walker.offset = - thr;
walker.DiscretizeFlag = DiscretizeFlag;
walker.MultiSampleFlag = MultiSampleFlag;
walker.AbsDistFlag = AbsDistFlag;
MyMarchingCubes mc(new_mesh, walker);
walker.BuildMesh(old_mesh,new_mesh,mc,cb);
}
};//end class resampler
};//end namespace tri
};//end namespace vcg
#endif

View File

@ -0,0 +1,372 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.16 2006/10/07 15:04:25 cignoni
removed a useless include
Revision 1.15 2005/10/12 10:36:26 cignoni
Removed unused local type Edge. Now it use the standard simplex edge.
Revision 1.14 2004/12/10 01:04:42 cignoni
better comments
Revision 1.13 2004/11/23 10:34:45 cignoni
passed parameters by reference in many funcs and gcc cleaning
****************************************************************************/
#ifndef __VCG_TETRA_TRI_COLLAPSE
#define __VCG_TETRA_TRI_COLLAPSE
#include<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

@ -0,0 +1,393 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/*#**************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.6 2008/01/12 19:07:05 ganovelli
Recompiled from previous out of date version. Still to revise but working
Revision 1.5 2005/12/13 17:17:19 ganovelli
first importing from old version. NOT optimized! It works with VertexFace Adjacency even over non manifolds
*#**************************************************************************/
#include <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

@ -0,0 +1,712 @@
#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

@ -0,0 +1,992 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.34 2007/01/31 15:25:49 giec
Remove some usless code in Minimum Weight Triangulation.
Revision 1.33 2007/01/31 11:46:12 giec
Bug fix
Revision 1.32 2007/01/18 18:15:14 cignoni
added missing typenames
Revision 1.31 2007/01/18 11:17:43 giec
The minimum weight algorithm keep the topology consistent.
Revision 1.30 2007/01/10 12:07:54 giec
Bugfixed ComputeDihedralAngle function
Revision 1.29 2006/12/27 15:09:52 giec
Bug fix on ComputeDihedralAngle function
Revision 1.28 2006/12/12 11:14:51 cignoni
Commented some variant of the quality measure of weighted ears
Revision 1.27 2006/12/07 00:40:18 cignoni
Added many this-> for gcc compiling
Revision 1.26 2006/12/06 13:03:59 cignoni
Corrected bugs on selfintersection
Revision 1.25 2006/12/06 00:12:53 cignoni
Heavily restructured and corrected. Now a single Close ear function
Corrected Hole search function, and management of double non manifold vertex in a hole
Changed priority strategy in the heap, now a mix of quality and dihedral angle.
Changed but still untested IntersectionEar
Revision 1.24 2006/12/01 21:24:16 cignoni
Corrected bug in the search of holes. Removed output prints
Revision 1.23 2006/12/01 08:53:55 cignoni
Corrected pop_heap vs pop_back issue in heap usage
Revision 1.22 2006/12/01 00:11:17 cignoni
Added Callback, Corrected some spelling errors (adiacense -> adjacency).
Added Validity Check function for hole loops
Revision 1.21 2006/11/30 11:49:20 cignoni
small gcc compiling issues
Revision 1.20 2006/11/29 16:21:45 cignoni
Made static exposed funtions of the class
Revision 1.19 2006/11/29 15:25:22 giec
Removed limit.
Revision 1.18 2006/11/29 15:18:49 giec
Code refactory and bugfix.
Revision 1.17 2006/11/24 10:42:39 mariolatronico
Now compiles on gcc under linux.
Revision 1.16 2006/11/22 13:43:28 giec
Code refactory and added minimum weight triangolation.
Revision 1.15 2006/11/13 10:11:38 giec
Clear some useless code
Revision 1.14 2006/11/07 15:13:56 zifnab1974
Necessary changes for compilation with gcc 3.4.6. Especially the hash function is a problem
Revision 1.13 2006/11/07 11:47:11 cignoni
gcc compiling issues
Revision 1.12 2006/11/07 07:56:43 cignoni
Added missing std::
Revision 1.11 2006/11/06 16:12:29 giec
Leipa ear now compute max dihedral angle.
Revision 1.10 2006/10/31 11:30:41 ganovelli
changed access throught iterator with static call to comply 2005 compiler
Revision 1.9 2006/10/20 07:44:45 cignoni
Added missing std::
Revision 1.8 2006/10/18 15:06:47 giec
New policy for compute quality in TrivialEar.
Bugfixed LeipaEar.
Added new algorithm "selfintersection" with test for self intersection.
Revision 1.7 2006/10/10 09:12:02 giec
Bugfix and added a new type of ear (Liepa like)
Revision 1.6 2006/10/09 10:07:07 giec
Optimized version of "EAR HOLE FILLING", the Ear is selected according to its dihedral angle.
Revision 1.5 2006/10/06 15:28:14 giec
first working implementationof "EAR HOLE FILLING".
Revision 1.4 2006/10/02 12:06:40 giec
BugFix
Revision 1.3 2006/09/27 15:33:32 giec
It close one simple hole . . .
Revision 1.2 2006/09/27 09:29:53 giec
Frist working release whit a few bugs.
It almost fills the hole ...
Revision 1.1 2006/09/25 09:17:44 cignoni
First Non working Version
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_HOLE
#define __VCG_TRI_UPDATE_HOLE
#include <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

@ -0,0 +1,383 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2005 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.3 2006/03/29 10:12:08 corsini
Add cast to avoid warning
Revision 1.2 2005/12/12 12:08:30 cignoni
First working version
Revision 1.1 2005/11/21 15:58:12 cignoni
First Release (not working!)
Revision 1.13 2005/11/17 00:42:03 cignoni
****************************************************************************/
#ifndef _VCG_INERTIA_
#define _VCG_INERTIA_
/*
The algorithm is based on a three step reduction of the volume integrals
to successively simpler integrals. The algorithm is designed to minimize
the numerical errors that can result from poorly conditioned alignment of
polyhedral faces. It is also designed for efficiency. All required volume
integrals of a polyhedron are computed together during a single walk over
the boundary of the polyhedron; exploiting common subexpressions reduces
floating point operations.
For more information, check out:
Brian Mirtich,
``Fast and Accurate Computation of Polyhedral Mass Properties,''
journal of graphics tools, volume 1, number 2, 1996
*/
#include <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

@ -0,0 +1,115 @@
/****************************************************************************
* IDOLib *
* Interactive Deformable Objects Library *
* http://idolib.sf.net *
* *
* Copyright(C) 2005 *
* Visual Computing Lab *
* ISTI - Italian National Research Council *
* *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.3 2007/06/06 15:38:57 turini
Use the barycenter function from triangle3.h instead of
the one in face\base.h.
Revision 1.2 2007/06/06 14:26:51 pietroni
compiling error resolved
****************************************************************************/
#include <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

@ -0,0 +1,470 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.11 2007/05/02 13:25:45 zifnab1974
only use typename when necessary
Revision 1.10 2007/04/10 22:46:57 pietroni
- line 152 changed call intersection to IntersectionPlaneTriangle because changing in function's name
Revision 1.9 2007/01/03 15:51:28 pietroni
added initial define and included missing files
Revision 1.8 2006/01/19 14:06:37 spinelli
add std:: namespace...
Revision 1.7 2005/10/03 16:18:15 spinelli
add template parameter for spatialindexing struction
Revision 1.6 2005/05/30 09:11:20 ganovelli
header added, error in include
Revision 1.3 2005/05/17 21:19:37 ganovelli
some std::and typename missing (CRS4)
Revision 1.2 2005/03/08 14:42:22 ganovelli
added vcg header
****************************************************************************/
#include<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

@ -0,0 +1,398 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
$Log: not supported by cvs2svn $
Revision 1.20 2007/01/19 09:13:09 cignoni
Added Finalize() method to the interface
Revision 1.19 2007/01/11 11:48:33 ganovelli
currMetric inizialied to heap.front() (it was heap.back()- wrong)
Revision 1.18 2006/12/11 14:09:44 ganovelli
added missing initialization of currMetric
Revision 1.17 2006/06/09 07:28:43 m_di_benedetto
Corrected ClearHeap(): iterator "hi" not decrementable if it was the first of the container.
Revision 1.16 2005/11/10 15:38:46 cignoni
Added casts to remove warnings
Revision 1.15 2005/10/02 23:23:52 cignoni
Changed the sense of the < operator for heap: it is reversed according to the stl where highest score elements must float in the heap
Completed TimeBudget Termination condition.
Parametrized the ClearHeap procedure now there is a HeapSimplexRatio param. Removed dirty printf.
Revision 1.14 2005/04/14 11:34:33 ponchio
*** empty log message ***
Revision 1.13 2005/01/19 10:33:50 cignoni
Improved ClearHeap management
Revision 1.12 2004/12/10 01:02:48 cignoni
added an inline and removed loggng
Revision 1.11 2004/12/03 21:14:39 ponchio
Fixed memory leak...
Revision 1.10 2004/11/23 10:37:17 cignoni
Added a member with a cached copy of the floating Priority() value inside the HeapElem to optimize operator< in heap updating operator
Revision 1.9 2004/11/05 10:03:47 fiorin
Added ModifierType::TriEdgeFlipOp
Revision 1.8 2004/10/25 07:02:56 ganovelli
some inline function, logs on file (precompiler directive)
Revision 1.7 2004/09/29 17:08:39 ganovelli
changed > to < in heapelem comparison
Revision 1.6 2004/09/28 09:57:08 cignoni
Better Doxygen docs
Revision 1.5 2004/09/15 10:40:20 ponchio
typedef LocalOptimization HeapType -> public:
Revision 1.4 2004/09/08 15:10:59 ganovelli
*** empty log message ***
Revision 1.3 2004/07/27 09:46:15 cignoni
First working version of the LocalOptimization/Simplification Framework
Revision 1.1 2004/07/15 12:04:14 ganovelli
minor changes
Revision 1.2 2004/07/09 10:22:56 ganovelli
working draft
Revision 1.1 2004/07/08 08:25:15 ganovelli
first draft
****************************************************************************/
#ifndef __VCGLIB_LOCALOPTIMIZATION
#define __VCGLIB_LOCALOPTIMIZATION
#include<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

@ -0,0 +1,627 @@
#ifndef QUAD_DIAGONAL_COLLAPSE_H
#define QUAD_DIAGONAL_COLLAPSE_H
#include<vcg/connectors/halfedge_pos.h>
#include<vcg/complex/local_optimization.h>
#include<vcg/complex/trimesh/smooth.h>
#include<set>
#include<vcg/space/ray3.h>
#include<vcg/complex/trimesh/halfedge_quad_clean.h>
namespace vcg{
namespace tri{
/*!
* \brief Generic class for checking feasibility of collapses
*
*/
template<class MeshType, class TriMeshType >
class FeasibilityCheck
{
public:
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
typedef typename TriMeshType::CoordType CoordType;
static bool check_feasible(HEdgePointer hp, CoordType &V1, CoordType &V2, TriMeshType &tm, GRID &grid);
};
/*!
* \brief Generic class for weighting collapses
*
*/
template<class MeshType, class TriMeshType >
class OperationWeight
{
public:
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
typedef typename TriMeshType::CoordType CoordType;
static float compute_weight(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid);
};
/*!
* \brief Class that provides methods for checking and weighting collapses using fitmaps
*
*/
template<class MeshType, class TriMeshType >
class FitmapsCollapse : public FeasibilityCheck<MeshType, TriMeshType>, public OperationWeight<MeshType, TriMeshType>
{
protected:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
typedef typename TriMeshType::CoordType CoordType;
typedef typename TriMeshType::ScalarType ScalarType;
typedef typename TriMeshType::FacePointer FacePointer;
typedef typename TriMeshType::template PerVertexAttributeHandle<float> Fitmap_attr;
public:
/// Coefficient that will be multiplied with the value of the M-Fitmap
static float& Mfit_coeff()
{
static float coeff = 1.5;
return coeff;
}
/*! Checks if an operation is feasible using M-Fitmap
*
* \param hp Pointer to an halfedge that identifies a diagonal
* \param V1 Coordinates of the first point of the diagonal
* \param V2 Coordinates of the second point of the diagonal
* \param tm Reference mesh
* \param grid Spatial index used for raycasting
*
* \return Value indicating whether diagnoal is collapsible
*/
static bool check_feasible(HEdgePointer hp, CoordType &V1, CoordType &V2, TriMeshType &tm, GRID &grid)
{
float lenght = Distance( V1, V2 );
Fitmap_attr M_Fit = tri::Allocator<TriMeshType>::template GetPerVertexAttribute<float>(tm,"M-Fitmap");
CoordType P = (V1+V2)/2;
float fitmap = compute_fitmap(hp, P, tm, grid, M_Fit);
return lenght <= fitmap/Mfit_coeff();
}
/*! Computes the weight of a diagonal using S-Fitmap
*
* \param hp Pointer to an halfedge that identifies a diagonal
* \param P Coordinates of the point on which fitmap will be computed
* \param tm Reference mesh
* \param grid Spatial index used for raycasting
*
* \return Computed weight
*/
static float compute_weight(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid)
{
Fitmap_attr S_Fit = tri::Allocator<TriMeshType>::template GetPerVertexAttribute<float>(tm,"S-Fitmap");
return compute_fitmap(hp, P, tm, grid, S_Fit);
}
protected:
/*!
* Performs the computation of a fitmap on a given point
*
* \param hp Pointer to an halfedge that identifies a diagonal
* \param P Coordinates of the point on which fitmap will be computed
* \param tm Reference mesh
* \param grid Spatial index used for raycasting
* \param attr Fitmap type (S or M)
*
* \return Computed value of the fitmap
*/
static float compute_fitmap(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid, Fitmap_attr &attr)
{
CoordType N(0,0,0);
vector<VertexPointer> vertices = HalfEdgeTopology<MeshType>::getVertices(hp->HFp());
assert(vertices.size() == 4);
N += vcg::Normal<typename MeshType::CoordType>(vertices[0]->cP(), vertices[1]->cP(), vertices[2]->cP());
N += vcg::Normal<typename MeshType::CoordType>(vertices[2]->cP(), vertices[3]->cP(), vertices[0]->cP());
N.Normalize();
CoordType C(0,0,0);
FacePointer T = getClosestFaceRay(tm, grid, P, N, &C, NULL);
float result = 1.0;
if(T)
{
float w0;
float w1;
float w2;
vcg::InterpolationParameters(*T, N, C, w0, w1, w2);
float s0 = attr[T->V(0)];
float s1 = attr[T->V(1)];
float s2 = attr[T->V(2)];
result = (w0*s0 + w1*s1 + w2*s2)/(w0+w1+w2);
}
return result;
}
static FacePointer getClosestFaceRay(TriMeshType &m, GRID &grid, CoordType P, CoordType raydir, CoordType* closest, ScalarType* minDist)
{
ScalarType diag = m.bbox.Diag();
raydir.Normalize();
Ray3<typename GRID::ScalarType> ray;
ray.SetOrigin(P);
ScalarType t;
FacePointer f = 0;
FacePointer fr = 0;
vector<CoordType> closests;
vector<ScalarType> minDists;
vector<FacePointer> faces;
ray.SetDirection(-raydir);
f = vcg::tri::DoRay<TriMeshType,GRID>(m, grid, ray, diag/4.0, t);
if (f)
{
closests.push_back(ray.Origin() + ray.Direction()*t);
minDists.push_back(fabs(t));
faces.push_back(f);
}
ray.SetDirection(raydir);
fr = vcg::tri::DoRay<TriMeshType,GRID>(m, grid, ray, diag/4.0, t);
if (fr)
{
closests.push_back(ray.Origin() + ray.Direction()*t);
minDists.push_back(fabs(t));
faces.push_back(fr);
}
if (fr)
if (fr->N()*raydir<0)
fr=0; // discard: inverse normal;
if (minDists.size() == 0)
{
if (closest) *closest=P;
if (minDist) *minDist=0;
f = 0;
}
else
{
int minI = std::min_element(minDists.begin(),minDists.end()) - minDists.begin();
if (closest) *closest= closests[minI];
if (minDist) *minDist= minDists[minI];
f = faces[minI];
}
return f;
}
};
/*!
* \brief Class implementing simplification of quad meshes by diagonal collapses
*
*/
template<class MeshType, class MYTYPE, class TriMeshType, class OptimizationType>
class QuadDiagonalCollapseBase: public LocalOptimization<MeshType>::LocModType
{
protected:
typedef Pos<MeshType> PosType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename LocalOptimization<MeshType>::HeapElem HeapElem;
typedef typename LocalOptimization<MeshType>::HeapType HeapType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
/// Vertex returned by the collapse
VertexPointer ret;
/// Global mark for updating
static int& GlobalMark()
{
static int im=0;
return im;
}
/// Local mark for updating
int localMark;
/// Priority in the heap
ScalarType _priority;
/// Set of modified faces
set<FacePointer> faces;
/// Halfedge that identifies the diagonal to collapse
HEdgePointer hp;
public:
/// Reference mesh used for smoothing
static TriMeshType* &refMesh()
{
static TriMeshType* m = NULL;
return m;
}
/// Spatial index for smoothing
static GRID* &grid()
{
static GRID* grid = NULL;
return grid;
}
/// Number of smoothing iterations to be performed
static unsigned int &smoothing_iterations()
{
static unsigned int iterations = 5;
return iterations;
}
/// Default Constructor
QuadDiagonalCollapseBase(){}
/*!
* Constructor
*
* \param he Pointer to an halfedge representing a diagonal
* \param mark Temporal mark of the operation
*/
QuadDiagonalCollapseBase(HEdgePointer he, int mark)
{
localMark = mark;
hp = he;
_priority = ComputePriority();
}
~QuadDiagonalCollapseBase()
{
faces.clear();
}
/*!
* Computes priority of the operation as the length of the diagonal
*
* \return Priority
*/
ScalarType ComputePriority()
{
CoordType V1 = hp->HVp()->cP();
CoordType V2 = hp->HNp()->HNp()->HVp()->cP();
_priority = Distance( V1, V2 );
return _priority;
}
virtual const char *Info(MeshType &m)
{
static char buf[60];
sprintf(buf,"(%d - %d) %g\n", hp->HVp()-&m.vert[0], hp->HNp()->HNp()->HVp()-&m.vert[0], -_priority);
return buf;
}
/*!
* Performs the collapse and the optimizations
*
* \param m Mesh
*
*/
inline void Execute(MeshType &m)
{
// Collapse the diagonal
ret = HalfEdgeTopology<MeshType>::diagonal_collapse_quad(m,hp->HFp(), hp->HVp());
if(ret->VHp())
{
set<FacePointer> tmp = HalfEdgeTopology<MeshType>::getFaces(ret);
vector<FacePointer> incident_faces = HalfEdgeTopology<MeshType>::get_incident_faces(ret,ret->VHp());
faces.insert(incident_faces.begin(), incident_faces.end());
HalfedgeQuadClean<MeshType>::remove_doublets(m, faces);
// Optimization by edge rotations
if(!ret->IsD())
{
vector<HEdgePointer> hedges = HalfEdgeTopology<MeshType>::get_incident_hedges(ret,ret->VHp());
HalfedgeQuadClean<MeshType>:: template flip_edges<OptimizationType>(m, hedges, faces);
}
faces.insert(tmp.begin(), tmp.end());
// Set of all vertices to smooth
set<VertexPointer> vertices;
for(typename set<FacePointer>::iterator fi = faces.begin(); fi != faces.end(); ++fi)
{
if(*fi)
{
if( !((*fi)->IsD()))
{
vector<VertexPointer> aux = HalfEdgeTopology<MeshType>::getVertices(*fi);
vertices.insert(aux.begin(),aux.end());
}
}
}
// Smoothing
for(unsigned int i = 0; i < smoothing_iterations(); i++)
{
for(typename set<VertexPointer>::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi)
if(!HalfEdgeTopology<MeshType>::isBorderVertex(*vi))
Smooth<MeshType>::VertexCoordLaplacianReproject(m,*grid(), *refMesh(),*vi);
}
// Add all faces modified by smoothing into the set of modified faces
for(typename set<VertexPointer>::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi)
{
vector<FacePointer> tmp_faces = HalfEdgeTopology<MeshType>::get_incident_faces(*vi);
faces.insert(tmp_faces.begin(), tmp_faces.end());
}
}
}
/*!
* Updates the heap of operations.
* For each modified face inserts into the heap two consecutive halfedges representing the two diagonals
*
* \param h_ret Heap to be updated
*
*/
inline void UpdateHeap(HeapType & h_ret)
{
GlobalMark()++;
for(typename set<FacePointer>::iterator fi = faces.begin(); fi != faces.end(); ++fi)
{
if(*fi)
{
if( !((*fi)->IsD()))
{
(*fi)->IMark() = GlobalMark();
HEdgePointer start_he = (*fi)->FHp();
h_ret.push_back( HeapElem( new MYTYPE( start_he, GlobalMark() ) ) );
std::push_heap( h_ret.begin(),h_ret.end() );
h_ret.push_back( HeapElem( new MYTYPE( start_he->HNp(), GlobalMark() ) ) );
std::push_heap( h_ret.begin(),h_ret.end() );
}
}
}
}
ModifierType IsOfType()
{
return QuadDiagCollapseOp;
}
/*!
* Checks if the operation can be done without generation of degenerate configurations
*
* \return Value indicating whether the operation can be performed
*/
inline bool IsFeasible()
{
FacePointer fp = hp->HFp();
if(!fp)
return false;
if(fp->IsD() || fp->VN() !=4)
return false;
return ( HalfEdgeTopology<MeshType>::check_diagonal_collapse_quad(hp));
}
/*!
* Checks if the operation is up to date
*
* \return Value indicating whether operation is up to date
*/
inline bool IsUpToDate()
{
FacePointer fp = hp->HFp();
if(fp)
return (!hp->IsD() && localMark >= fp->IMark() );
return false;
}
/*!
* Gets the priority of the operation
*
* \return Value indicating the priority
*/
virtual ScalarType Priority() const
{
return _priority;
}
/*!
* Initializes a heap with all the possible diagonal collapses of the mesh
* For each face inserts two consecutive halfedges representing the two diagonals
*
* \param m Mesh
* \param h_ret heap to be initialized
*
*/
static void Init(MeshType &m,HeapType &h_ret)
{
// Grid and reference mesh must be initialized
assert(grid());
assert(refMesh());
assert(!HalfedgeQuadClean<MeshType>::has_doublets(m));
assert(!HalfedgeQuadClean<MeshType>::has_singlets(m));
vcg::tri::InitFaceIMark(m);
h_ret.clear();
typename MeshType::FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end();++fi)
{
if(!(*fi).IsD())
{
h_ret.push_back( HeapElem(new MYTYPE( (*fi).FHp(), IMark(m))));
h_ret.push_back( HeapElem(new MYTYPE( (*fi).FHp()->HNp(), IMark(m))));
}
}
}
};
/*!
* \brief Class implementing simplification of quad meshes by diagonal collapses
* priority of the operations is weighted with a value computed by class WeightType
* Feasibility is checked with class CheckType
*
*/
template<class MeshType, class MYTYPE, class TriMeshType, class OptimizationType, class WeightType, class CheckType >
class QuadDiagonalCollapse: public QuadDiagonalCollapseBase<MeshType, MYTYPE, TriMeshType, OptimizationType>
{
protected:
typedef Pos<MeshType> PosType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename LocalOptimization<MeshType>::HeapElem HeapElem;
typedef typename LocalOptimization<MeshType>::HeapType HeapType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
public:
/// Default constructor
QuadDiagonalCollapse(){}
/*!
* Constructor
*
* \param he Pointer to an halfedge representing a diagonal
* \param mark Temporal mark of the operation
*/
QuadDiagonalCollapse(HEdgePointer he, int mark)
{
this->localMark = mark;
this->hp = he;
this->_priority = this->ComputePriority();
}
/*!
* Computes priority of the operation as length * weight
*
* \return Priority
*/
ScalarType ComputePriority()
{
CoordType V1 = this->hp->HVp()->cP();
CoordType V2 = this->hp->HNp()->HNp()->HVp()->cP();
CoordType P = (V1+V2)/2;
float weight = WeightType::compute_weight(this->hp, P, *(this->refMesh()), *(this->grid()));
this->_priority = Distance( V1, V2 ) * weight;
return this->_priority;
}
/*!
* Checks if the operation can be done without generation of degenerate configurations
*
* \return Value indicating whether the operation can be performed
*/
bool IsFeasible()
{
FacePointer fp = this->hp->HFp();
if(!fp)
return false;
if(fp->IsD() || fp->VN() !=4)
return false;
if(!HalfEdgeTopology<MeshType>::check_diagonal_collapse_quad(this->hp))
return false;
CoordType V1 = this->hp->HVp()->cP();
CoordType V2 = this->hp->HNp()->HNp()->HVp()->cP();
return CheckType::check_feasible(this->hp, V1, V2, *(this->refMesh()), *(this->grid()));
}
};
}//end namespace tri
}//end namespace vcg
#endif // QUAD_DIAGONAL_COLLAPSE_H

View File

@ -0,0 +1,288 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
****************************************************************************/
#ifndef __VCG_DECIMATION_COLLAPSE
#define __VCG_DECIMATION_COLLAPSE
#include<vcg/complex/local_optimization.h>
#include<vcg/simplex/tetrahedron/pos.h>
#include<vcg/complex/tetramesh/edge_collapse.h>
#include<vcg/space/point3.h>
struct FAIL{
static int VOL(){static int vol=0; return vol++;}
static int LKF(){static int lkf=0; return lkf++;}
static int LKE(){static int lke=0; return lke++;}
static int LKV(){static int lkv=0; return lkv++;}
static int OFD(){static int ofd=0; return ofd++;}
static int BOR(){static int bor=0; return bor++;}
};
namespace vcg{
namespace tetra{
/** \addtogroup tetramesh */
/*@{*/
/// This Class is specialization of LocalModification for the edge collapse
/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine.
/// Note that it has knowledge of the heap of the class LocalOptimization because
/// it is responsible of updating it after a collapse has been performed
template<class TETRA_MESH_TYPE>
class TetraEdgeCollapse: public LocalOptimization<TETRA_MESH_TYPE>::LocModType
{
/// The tetrahedral mesh type
//typedef typename TETRA_MESH_TYPE TETRA_MESH_TYPE;
/// The tetrahedron type
typedef typename TETRA_MESH_TYPE::TetraType TetraType;
/// The vertex type
typedef typename TetraType::VertexType VertexType;
/// The coordinate type
typedef typename TetraType::VertexType::CoordType CoordType;
/// The scalar type
typedef typename TETRA_MESH_TYPE::VertexType::ScalarType ScalarType;
/////the base type class
//typedef typename vcg::tri::LocalModification LocalMod;
/// The HEdgePos type
typedef Pos<TetraType> PosType;
/// The HEdgePos Loop type
typedef PosLoop<TetraType> PosLType;
/// definition of the heap element
typedef typename LocalOptimization<TETRA_MESH_TYPE>::HeapElem HeapElem;
private:
///the new point that substitute the edge
Point3<ScalarType> _NewPoint;
///the pointer to edge collapser method
vcg::tetra::EdgeCollapse<TETRA_MESH_TYPE> _EC;
///mark for up_dating
static int& _Imark(){ static int im=0; return im;}
///the pos of collapse
PosType pos;
///pointer to vertex that remain
VertexType *vrem;
/// priority in the heap
ScalarType _priority;
public:
/// Default Constructor
TetraEdgeCollapse()
{}
///Constructor with postype
TetraEdgeCollapse(PosType p,int mark)
{
_Imark() = mark;
pos=p;
_priority = _AspectRatioMedia(p);
}
~TetraEdgeCollapse()
{}
private:
///Return the aspect Ratio media of the tetrahedrons
///that share the adge to collapse
ScalarType _AspectRatioMedia(PosType p)
{
PosLType posl=PosLType(p.T(),p.F(),p.E(),p.V());
posl.Reset();
int num=0;
ScalarType ratio_media=0.f;
while(!posl.LoopEnd())
{
ratio_media+=posl.T()->AspectRatio();
posl.NextT();
num++;
}
ratio_media=ratio_media/num;
return (ratio_media);
}
///Modify pos and alfa to obtain the collapse that minimize the error
ScalarType _VolumePreservingError(PosType &pos,CoordType &new_point,int nsteps)
{
VertexType *ve0=(pos.T()->V(Tetra::VofE(pos.E(),0)));
VertexType *ve1=(pos.T()->V(Tetra::VofE(pos.E(),1)));
bool ext_v0=ve0->IsB();
bool ext_v1=ve1->IsB();
ScalarType best_error=0.f;
if ((ext_v0)&&(!ext_v1))
new_point=ve0->P();
else
if ((!ext_v0)&&(ext_v1))
new_point=ve1->P();
else
if ((!ext_v0)&&(!ext_v1))
{/*CoordType g;
g.SetZero();
g+=ve0->cP();
g+=ve1->cP();
g/=2;*/
new_point=(ve0->cP()+ve1->cP())/2.f;
}
else
if ((ext_v0)&&(ext_v1))//both are external vertex
{
ScalarType step=1.f/(nsteps-1);
ScalarType Vol_Original=_EC.VolumeOriginal();
for (int i=0;i<nsteps;i++)
{
best_error=1000000.f;
ScalarType alfatemp=step*((ScalarType)i);
//CoordType g;
// g.SetZero();
//g+=ve0->cP()*alfatemp;
//g+=ve1->cP()*(1-alfatemp);
//CoordType newPTemp=g;
CoordType newPTemp=(ve0->cP()*alfatemp) +(ve1->cP()*(1.f-alfatemp));
//the error is the absolute value of difference of volumes
ScalarType error=fabs(Vol_Original-_EC.VolumeSimulateCollapse(pos,newPTemp));
if(error<best_error)
{
new_point=newPTemp;
best_error=error;
}
}
}
return (best_error);
}
public:
virtual const char *Info(TETRA_MESH_TYPE &m) {
static char buf[60];
//sprintf(buf,"collapse %i -> %i %f\n", pos.()-&m.vert[0], pos.VFlip()-&m.vert[0],_priority);
return buf;
}
ScalarType ComputePriority()
{
return (_priority = _AspectRatioMedia(this->pos));
}
ScalarType ComputeError()
{
vrem=(pos.T()->V(Tetra::VofE(pos.E(),0)));
return (_VolumePreservingError(pos,_NewPoint,5));// magic number....parametrize!
}
void Execute(TETRA_MESH_TYPE &tm)
{
// _EC.FindSets(pos);
assert(!vrem->IsD());
int del=_EC.DoCollapse(pos,_NewPoint);
tm.tn-=del;
tm.vn-=1;
}
void UpdateHeap(typename LocalOptimization<TETRA_MESH_TYPE>::HeapType & h_ret)
{
assert(!vrem->IsD());
_Imark()++;
VTIterator<TetraType> VTi(vrem->VTb(),vrem->VTi());
while (!VTi.End())
{
VTi.Vt()->ComputeVolume();
for (int j=0;j<6;j++)
{
vcg::tetra::Pos<TetraType> p=Pos<TetraType>(VTi.Vt(),Tetra::FofE(j,0),j,Tetra::VofE(j,0));
assert(!p.T()->V(p.V())->IsD());
assert(!p.T()->IsD());
h_ret.push_back(HeapElem(new TetraEdgeCollapse<TETRA_MESH_TYPE>(p,_Imark())));
std::push_heap(h_ret.begin(),h_ret.end());
// update the mark of the vertices
VTi.Vt()->V(Tetra::VofE(j,0))->IMark() = _Imark();
}
++VTi;
}
}
/// return the type of operation
ModifierType IsOfType(){ return TetraEdgeCollapseOp;}
bool IsFeasible(){
vcg::tetra::EdgeCollapse<TETRA_MESH_TYPE>::Reset();
_EC.FindSets(pos);
ComputeError();
return(_EC.CheckPreconditions(pos,_NewPoint));
}
bool IsUpToDate(){
if (!pos.T()->IsD())
{
VertexType *v0=pos.T()->V(Tetra::VofE(pos.E(),0));
VertexType *v1=pos.T()->V(Tetra::VofE(pos.E(),1));
assert(!v0->IsD());
assert(!v1->IsD());
if(! (( (!v0->IsD()) && (!v1->IsD())) &&
_Imark()>=v0->IMark() &&
_Imark()>=v1->IMark()))
{
FAIL::OFD();
return false;
}
else
return true;
}
else
return false;
}
virtual ScalarType Priority() const {
return _priority;
}
/// perform initialization
static void Init(TETRA_MESH_TYPE &m,typename LocalOptimization<TETRA_MESH_TYPE>::HeapType& h_ret){
h_ret.clear();
typename TETRA_MESH_TYPE::TetraIterator ti;
for(ti = m.tetra.begin(); ti != m.tetra.end();++ti)
if(!(*ti).IsD()){
(*ti).ComputeVolume();
for (int j=0;j<6;j++)
{
PosType p=PosType(&*ti,Tetra::FofE(j,0),j,Tetra::VofE(j,0));
assert(!p.T()->V(p.V())->IsD());
assert(!p.T()->IsD());
h_ret.push_back(HeapElem(new TetraEdgeCollapse<TETRA_MESH_TYPE>(p,m.IMark)));
}
}
}
};
}//end namespace tetra
}//end namespace vcg
#endif

View File

@ -0,0 +1,290 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
$Log: not supported by cvs2svn $
Revision 1.19 2006/10/15 07:31:21 cignoni
typenames and qualifiers for gcc compliance
Revision 1.18 2006/10/09 20:09:40 cignoni
Changed some access to VertexFaceIterator to reflect the shorter new operators.
Revision 1.17 2005/10/12 10:44:01 cignoni
Now creation of new edge use Ordered() constructor to comply the fact that the basic collapse is simmetric.
Revision 1.16 2005/01/19 10:35:28 cignoni
Better management of symmetric/asymmetric edge collapses
Revision 1.15 2004/12/10 01:03:53 cignoni
better comments and removed logging
Revision 1.14 2004/11/23 10:34:23 cignoni
passed parameters by reference in many funcs and gcc cleaning
Revision 1.13 2004/10/25 16:28:32 ganovelli
pos to edge
Revision 1.12 2004/09/15 11:16:02 ganovelli
changed P() to cP()
Revision 1.11 2004/09/09 13:23:01 ponchio
Header guards typo
Revision 1.10 2004/09/09 13:01:12 ponchio
Linux compatible path in #include
Revision 1.9 2004/09/08 15:13:29 ganovelli
changes for gc
Revision 1.8 2004/09/08 14:33:31 ganovelli
*** empty log message ***
****************************************************************************/
#ifndef __VCG_DECIMATION_TRICOLLAPSE
#define __VCG_DECIMATION_TRICOLLAPSE
#include<vcg/complex/trimesh/edge_collapse.h>
#include<vcg/simplex/face/pos.h>
#include<vcg/complex/local_optimization.h>
namespace vcg{
namespace tri{
/** \addtogroup trimesh */
/*@{*/
/// This Class is specialization of LocalModification for the edge collapse.
/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine.
/// Note that it has knowledge of the heap of the class LocalOptimization because
/// it is responsible of updating it after a collapse has been performed;
/// This is the base class of all the specialized collapse classes like for example Quadric Edge Collapse.
/// Each derived class
template<class TriMeshType, class MYTYPE>
class TriEdgeCollapse: public LocalOptimization<TriMeshType>::LocModType , public EdgeCollapse<TriMeshType>
{
public:
/// static data to gather statistical information about the reasons of collapse failures
class FailStat {
public:
static int &Volume() {static int vol=0; return vol;}
static int &LinkConditionFace(){static int lkf=0; return lkf;}
static int &LinkConditionEdge(){static int lke=0; return lke;}
static int &LinkConditionVert(){static int lkv=0; return lkv;}
static int &OutOfDate() {static int ofd=0; return ofd;}
static int &Border() {static int bor=0; return bor;}
static void Init()
{
Volume() =0;
LinkConditionFace()=0;
LinkConditionEdge()=0;
LinkConditionVert()=0;
OutOfDate() =0;
Border() =0;
}
};
protected:
typedef typename TriMeshType::FaceType FaceType;
typedef typename TriMeshType::FaceType::VertexType VertexType;
typedef typename VertexType::EdgeType EdgeType;
typedef typename FaceType::VertexType::CoordType CoordType;
typedef typename TriMeshType::VertexType::ScalarType ScalarType;
typedef typename LocalOptimization<TriMeshType>::HeapElem HeapElem;
typedef typename LocalOptimization<TriMeshType>::HeapType HeapType;
TriMeshType *mt;
///the pair to collapse
EdgeType pos;
///mark for up_dating
static int& GlobalMark(){ static int im=0; return im;}
///mark for up_dating
int localMark;
/// priority in the heap
ScalarType _priority;
public:
/// Default Constructor
inline TriEdgeCollapse()
{}
///Constructor with postype
inline TriEdgeCollapse(const EdgeType &p, int mark)
{
localMark = mark;
pos=p;
_priority = ComputePriority();
}
~TriEdgeCollapse()
{}
private:
public:
inline ScalarType ComputePriority()
{
_priority = Distance(pos.V(0)->cP(),pos.V(1)->cP());
return _priority;
}
virtual const char *Info(TriMeshType &m) {
mt = &m;
static char buf[60];
sprintf(buf,"%i -> %i %g\n", int(pos.V(0)-&m.vert[0]), int(pos.V(1)-&m.vert[0]),-_priority);
return buf;
}
inline void Execute(TriMeshType &m)
{
CoordType MidPoint=(pos.V(0)->P()+pos.V(1)->P())/2.0;
DoCollapse(m, pos, MidPoint);
}
static bool IsSymmetric() { return true;}
// This function is called after an action to re-add in the heap elements whose priority could have been changed.
// in the plain case we just put again in the heap all the edges around the vertex resulting from the previous collapse: v[1].
// if the collapse is not symmetric you should add also backward edges (because v0->v1 collapse could be different from v1->v0)
inline void UpdateHeap(HeapType & h_ret)
{
GlobalMark()++; int nn=0;
VertexType *v[2];
v[0]= pos.V(0);v[1]=pos.V(1);
v[1]->IMark() = GlobalMark();
// First loop around the remaining vertex to unmark visited flags
vcg::face::VFIterator<FaceType> vfi(v[1]);
while (!vfi.End()){
vfi.V1()->ClearV();
vfi.V2()->ClearV();
++vfi;
}
// Second Loop: add all the outgoing edges around v[1]
// for each face add the two edges outgoing from v[1] and not visited.
vfi = face::VFIterator<FaceType>(v[1]);
while (!vfi.End())
{
assert(!vfi.F()->IsD());
for (int j=0;j<3;j++)
{
if( !(vfi.V1()->IsV()) && (vfi.V1()->IsRW()))
{
vfi.V1()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType( vfi.V(),vfi.V1() ),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(! this->IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType( vfi.V1(),vfi.V()),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
if( !(vfi.V2()->IsV()) && (vfi.V2()->IsRW()))
{
vfi.V2()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.F()->V(vfi.I()),vfi.F()->V2(vfi.I())),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(! this->IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType (vfi.F()->V1(vfi.I()),vfi.F()->V(vfi.I())),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
// if(vfi.V1()->IsRW() && vfi.V2()->IsRW() )
// {
// h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V2()),this->GlobalMark())));
// std::push_heap(h_ret.begin(),h_ret.end());
// if(IsSymmetric()){
// h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V1()), this->GlobalMark())));
// std::push_heap(h_ret.begin(),h_ret.end());
// }
// }
}
++vfi;nn++;
}
// printf("ADDED %d\n",nn);
}
ModifierType IsOfType(){ return TriEdgeCollapseOp;}
inline bool IsFeasible(){
return LinkConditions(pos);
}
inline bool IsUpToDate(){
// if(pos.V(1)->IsD()) {
// ++FailStat::OutOfDate();
// return false;
//}
//
// if(pos.V(1)->IsD()) {
// ++FailStat::OutOfDate();
// return false;
//}
VertexType *v0=pos.V(0);
VertexType *v1=pos.V(1);
//if(! (( (!v0->IsD()) && (!v1->IsD())) &&
// localMark>=v0->IMark() &&
// localMark>=v1->IMark()))
if( v0->IsD() || v1->IsD() ||
localMark < v0->IMark() ||
localMark < v1->IMark() )
{
++FailStat::OutOfDate();
return false;
}
return true;
}
virtual ScalarType Priority() const {
return _priority;
}
static void Init(TriMeshType&m,HeapType&h_ret){
h_ret.clear();
typename TriMeshType::FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end();++fi)
if(!(*fi).IsD()){
for (int j=0;j<3;j++)
{
EdgeType p=EdgeType::OrderedEdge((*fi).V(j),(*fi).V((j+1)%3));
h_ret.push_back(HeapElem(new MYTYPE(p, IMark(m))));
//printf("Inserting in heap coll %3i ->%3i %f\n",p.V()-&m.vert[0],p.VFlip()-&m.vert[0],h_ret.back().locModPtr->Priority());
}
}
}
};
}//end namespace tri
}//end namespace vcg
#endif

View File

@ -0,0 +1,636 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.14 2007/03/22 11:07:16 cignoni
Solved an issue related to different casting double-float between gcc 3 and gcc 4
Revision 1.13 2007/02/25 09:20:10 cignoni
Added Rad to the NormalThr Option and removed a bug in multiple exectuion of non optimal simplification (missing an isD check)
Revision 1.12 2007/01/19 09:13:14 cignoni
Added Finalize() method to the interface, corrected minor bugs on border preserving and postsimplification cleanup
Avoided double make_heap (it is done only in the local_optimization init)
Revision 1.11 2006/10/15 07:31:21 cignoni
typenames and qualifiers for gcc compliance
Revision 1.10 2006/10/09 20:12:55 cignoni
Heavyly restructured for meshlab inclusion. Now the access to the quadric elements are mediated by a static helper class.
Revision 1.9 2006/10/07 17:20:25 cignoni
Updated to the new style face->Normal() becomes Normal(face)
Revision 1.8 2005/10/02 23:19:36 cignoni
Changed the sign of the priority of a collapse. Now it is its the error as it should (and not -error)
Revision 1.7 2005/04/14 11:35:07 ponchio
*** empty log message ***
Revision 1.6 2005/01/19 10:35:28 cignoni
Better management of symmetric/asymmetric edge collapses
Revision 1.5 2004/12/10 01:07:15 cignoni
Moved param classes inside; added support for optimal placement and symmetric; added update heap also here (not only in the base class)
Revision 1.4 2004/11/23 10:34:23 cignoni
passed parameters by reference in many funcs and gcc cleaning
Revision 1.3 2004/10/25 07:07:56 ganovelli
A vcg.::Pos was used to implement the collapse type. CHanged
to vcg::Edge
Revision 1.2 2004/09/29 17:08:16 ganovelli
corrected error in -error (see localoptimization)
****************************************************************************/
#ifndef __VCG_TRIMESHCOLLAPSE_QUADRIC__
#define __VCG_TRIMESHCOLLAPSE_QUADRIC__
#include<vcg/math/quadric.h>
#include<vcg/simplex/face/pos.h>
#include<vcg/complex/trimesh/update/flag.h>
#include<vcg/complex/trimesh/update/topology.h>
#include<vcg/complex/trimesh/update/bounding.h>
#include<vcg/complex/local_optimization/tri_edge_collapse.h>
#include<vcg/complex/local_optimization.h>
namespace vcg{
namespace tri{
/**
This class describe Quadric based collapse operation.
Requirements:
Vertex
must have:
incremental mark
VF topology
must have:
members
QuadricType Qd();
ScalarType W() const;
A per-vertex Weight that can be used in simplification
lower weight means that error is lowered,
standard: return W==1.0
void Merge(MESH_TYPE::vertex_type const & v);
Merges the attributes of the current vertex with the ones of v
(e.g. its weight with the one of the given vertex, the color ect).
Standard: void function;
OtherWise the class should be templated with a static helper class that helps to retrieve these functions.
If the vertex class exposes these functions a default static helper class is provided.
*/
//**Helper CLASSES**//
template <class VERTEX_TYPE>
class QInfoStandard
{
public:
QInfoStandard(){};
static void Init(){};
static math::Quadric<double> &Qd(VERTEX_TYPE &v) {return v.Qd();}
static math::Quadric<double> &Qd(VERTEX_TYPE *v) {return v->Qd();}
static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE */*v*/) {return 1.0;};
static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE &/*v*/) {return 1.0;};
static void Merge(VERTEX_TYPE & v_dest, VERTEX_TYPE const & v_del){};
};
class TriEdgeCollapseQuadricParameter
{
public:
double QualityThr; // all
double BoundaryWeight;
double NormalThrRad;
double CosineThr;
double QuadricEpsilon;
double ScaleFactor;
bool UseArea;
bool UseVertexWeight;
bool NormalCheck;
bool QualityCheck;
bool OptimalPlacement;
bool MemoryLess;
bool QualityWeight;
bool ScaleIndependent;
//***********************
bool QualityQuadric; // During the initialization manage all the edges as border edges adding a set of additional quadrics that are useful mostly for keeping face aspect ratio good.
bool PreserveTopology;
bool PreserveBoundary;
bool MarkComplex;
bool FastPreserveBoundary;
bool SafeHeapUpdate;
};
template<class TriMeshType,class MYTYPE, class HelperType = QInfoStandard<typename TriMeshType::VertexType> >
class TriEdgeCollapseQuadric: public TriEdgeCollapse< TriMeshType, MYTYPE>
{
public:
typedef typename vcg::tri::TriEdgeCollapse< TriMeshType, MYTYPE > TEC;
typedef typename TEC::EdgeType EdgeType;
typedef typename TriEdgeCollapse<TriMeshType, MYTYPE>::HeapType HeapType;
typedef typename TriEdgeCollapse<TriMeshType, MYTYPE>::HeapElem HeapElem;
typedef typename TriMeshType::CoordType CoordType;
typedef typename TriMeshType::ScalarType ScalarType;
typedef math::Quadric< double > QuadricType;
typedef typename TriMeshType::FaceType FaceType;
typedef typename TriMeshType::VertexType VertexType;
typedef TriEdgeCollapseQuadricParameter QParameter;
typedef HelperType QH;
static QParameter & Params(){
static QParameter p;
return p;
}
enum Hint {
HNHasFFTopology = 0x0001, // La mesh arriva con la topologia ff gia'fatta
HNHasVFTopology = 0x0002, // La mesh arriva con la topologia bf gia'fatta
HNHasBorderFlag = 0x0004 // La mesh arriva con i flag di bordo gia' settati
};
static int & Hnt(){static int hnt; return hnt;} // the current hints
static void SetHint(Hint hn) { Hnt() |= hn; }
static void ClearHint(Hint hn) { Hnt()&=(~hn);}
static bool IsSetHint(Hint hn) { return (Hnt()&hn)!=0; }
// puntatori ai vertici che sono stati messi non-w per preservare il boundary
static std::vector<typename TriMeshType::VertexPointer> & WV(){
static std::vector<typename TriMeshType::VertexPointer> _WV; return _WV;
};
inline TriEdgeCollapseQuadric(const EdgeType &p, int i)
//:TEC(p,i){}
{
this->localMark = i;
this->pos=p;
this->_priority = ComputePriority();
}
inline bool IsFeasible(){
bool res = ( !Params().PreserveTopology || LinkConditions(this->pos) );
if(!res) ++( TriEdgeCollapse< TriMeshType,MYTYPE>::FailStat::LinkConditionEdge() );
return res;
}
void Execute(TriMeshType &m)
{ CoordType newPos;
if(Params().OptimalPlacement) newPos= static_cast<MYTYPE*>(this)->ComputeMinimal();
else newPos=this->pos.V(1)->P();
//this->pos.V(1)->Qd()+=this->pos.V(0)->Qd();
QH::Qd(this->pos.V(1))+=QH::Qd(this->pos.V(0));
//int FaceDel=
DoCollapse(m, this->pos, newPos); // v0 is deleted and v1 take the new position
//m.fn-=FaceDel;
//--m.vn;
}
// Final Clean up after the end of the simplification process
static void Finalize(TriMeshType &m, HeapType& /*h_ret*/)
{
// if the mesh was prepared with precomputed borderflags
// correctly set them again.
if(IsSetHint(HNHasBorderFlag) )
vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m);
// If we had the boundary preservation we should clean up the writable flags
if(Params().FastPreserveBoundary)
{
typename TriMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD()) (*vi).SetW();
}
if(Params().PreserveBoundary)
{
typename std::vector<typename TriMeshType::VertexPointer>::iterator wvi;
for(wvi=WV().begin();wvi!=WV().end();++wvi)
if(!(*wvi)->IsD()) (*wvi)->SetW();
}
}
static void Init(TriMeshType &m,HeapType&h_ret){
typename TriMeshType::VertexIterator vi;
typename TriMeshType::FaceIterator pf;
EdgeType av0,av1,av01;
Params().CosineThr=cos(Params().NormalThrRad);
if(!IsSetHint(HNHasVFTopology) ) vcg::tri::UpdateTopology<TriMeshType>::VertexFace(m);
if(Params().MarkComplex) {
vcg::tri::UpdateTopology<TriMeshType>::FaceFace(m);
vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromFF(m);
vcg::tri::UpdateTopology<TriMeshType>::VertexFace(m);
} // e' un po' piu' lenta ma marca i vertici complex
else
if(!IsSetHint(HNHasBorderFlag) )
vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m);
if(Params().FastPreserveBoundary)
{
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( !(*pf).IsD() && (*pf).IsW() )
for(int j=0;j<3;++j)
if((*pf).IsB(j))
{
(*pf).V(j)->ClearW();
(*pf).V1(j)->ClearW();
}
}
if(Params().PreserveBoundary)
{
WV().clear();
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( !(*pf).IsD() && (*pf).IsW() )
for(int j=0;j<3;++j)
if((*pf).IsB(j))
{
if((*pf).V(j)->IsW()) {(*pf).V(j)->ClearW(); WV().push_back((*pf).V(j));}
if((*pf).V1(j)->IsW()) {(*pf).V1(j)->ClearW();WV().push_back((*pf).V1(j));}
}
}
InitQuadric(m);
// Initialize the heap with all the possible collapses
if(IsSymmetric())
{ // if the collapse is symmetric (e.g. u->v == v->u)
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD() && (*vi).IsRW())
{
vcg::face::VFIterator<FaceType> x;
for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x){
x.V1()->ClearV();
x.V2()->ClearV();
}
for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++x )
{
assert(x.F()->V(x.I())==&(*vi));
if((x.V0()<x.V1()) && x.V1()->IsRW() && !x.V1()->IsV()){
x.V1()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(x.V0(),x.V1()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark() )));
}
if((x.V0()<x.V2()) && x.V2()->IsRW()&& !x.V2()->IsV()){
x.V2()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(x.V0(),x.V2()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark() )));
}
}
}
}
else
{ // if the collapse is A-symmetric (e.g. u->v != v->u)
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD() && (*vi).IsRW())
{
vcg::face::VFIterator<FaceType> x;
UnMarkAll(m);
for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x)
{
assert(x.F()->V(x.I())==&(*vi));
if(x.V()->IsRW() && x.V1()->IsRW() && !IsMarked(m,x.F()->V1(x.I()))){
h_ret.push_back( HeapElem( new MYTYPE( EdgeType (x.V(),x.V1()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark())));
}
if(x.V()->IsRW() && x.V2()->IsRW() && !IsMarked(m,x.F()->V2(x.I()))){
h_ret.push_back( HeapElem( new MYTYPE( EdgeType (x.V(),x.V2()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark())));
}
}
}
}
}
static float HeapSimplexRatio() {return IsSymmetric()?5.0f:9.0f;}
static bool IsSymmetric() {return Params().OptimalPlacement;}
static bool IsVertexStable() {return !Params().OptimalPlacement;}
static void SetDefaultParams(){
Params().UseArea=true;
Params().UseVertexWeight=false;
Params().NormalCheck=false;
Params().NormalThrRad=M_PI/2;
Params().QualityCheck=true;
Params().QualityThr=.1;
Params().BoundaryWeight=.5;
Params().QualityQuadric=false;
Params().OptimalPlacement=true;
Params().ScaleIndependent=true;
Params().QualityWeight=false;
Params().QuadricEpsilon =1e-15;
Params().ScaleFactor=1.0;
Params().PreserveTopology = false;
}
///*
// Funzione principale di valutazione dell'errore del collasso.
// In pratica simula il collasso vero e proprio.
//
// Da ottimizzare il ciclo sulle normali (deve sparire on e si deve usare per face normals)
//*/
ScalarType ComputePriority() {
ScalarType error;
typename vcg::face::VFIterator<FaceType> x;
std::vector<CoordType> on; // original normals
typename TriMeshType::VertexType * v[2];
v[0] = this->pos.V(0);
v[1] = this->pos.V(1);
if(Params().NormalCheck){ // Compute maximal normal variation
// store the old normals for non-collapsed face in v0
for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(); x.F()!=0; ++x ) // for all faces in v0
if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1
on.push_back(NormalizedNormal(*x.F()));
// store the old normals for non-collapsed face in v1
for(x.F() = v[1]->VFp(), x.I() = v[1]->VFi(); x.F()!=0; ++x ) // for all faces in v1
if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0
on.push_back(NormalizedNormal(*x.F()));
}
//// Move the two vertexe into new position (storing the old ones)
CoordType OldPos0=v[0]->P();
CoordType OldPos1=v[1]->P();
if(Params().OptimalPlacement) { v[0]->P() = ComputeMinimal(); v[1]->P()=v[0]->P();}
else v[0]->P() = v[1]->P();
//// Rescan faces and compute quality and difference between normals
int i;
double ndiff,MinCos = 1e100; // minimo coseno di variazione di una normale della faccia
// (e.g. max angle) Mincos varia da 1 (normali coincidenti) a
// -1 (normali opposte);
double qt, MinQual = 1e100;
CoordType nn;
for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v0
if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1
{
if(Params().NormalCheck){
nn=NormalizedNormal(*x.F());
ndiff=nn.dot(on[i++]);
if(ndiff<MinCos) MinCos=ndiff;
}
if(Params().QualityCheck){
qt= QualityFace(*x.F());
if(qt<MinQual) MinQual=qt;
}
}
for(x.F() = v[1]->VFp(), x.I() = v[1]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v1
if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0
{
if(Params().NormalCheck){
nn=NormalizedNormal(*x.F());
ndiff=nn.dot(on[i++]);
if(ndiff<MinCos) MinCos=ndiff;
}
if(Params().QualityCheck){
qt= QualityFace(*x.F());
if(qt<MinQual) MinQual=qt;
}
}
QuadricType qq=QH::Qd(v[0]);
qq+=QH::Qd(v[1]);
Point3d tpd=Point3d::Construct(v[1]->P());
double QuadErr = Params().ScaleFactor*qq.Apply(tpd);
// All collapses involving triangles with quality larger than <QualityThr> has no penalty;
if(MinQual>Params().QualityThr) MinQual=Params().QualityThr;
if(Params().NormalCheck){
// All collapses where the normal vary less than <NormalThr> (e.g. more than CosineThr)
// have no penalty
if(MinCos>Params().CosineThr) MinCos=Params().CosineThr;
MinCos=(MinCos+1)/2.0; // Now it is in the range 0..1 with 0 very dangerous!
}
if(QuadErr<Params().QuadricEpsilon) QuadErr=Params().QuadricEpsilon;
if( Params().UseVertexWeight ) QuadErr *= (QH::W(v[1])+QH::W(v[0]))/2;
if(!Params().QualityCheck && !Params().NormalCheck) error = (ScalarType)(QuadErr);
if( Params().QualityCheck && !Params().NormalCheck) error = (ScalarType)(QuadErr / MinQual);
if(!Params().QualityCheck && Params().NormalCheck) error = (ScalarType)(QuadErr / MinCos);
if( Params().QualityCheck && Params().NormalCheck) error = (ScalarType)(QuadErr / (MinQual*MinCos));
//Rrestore old position of v0 and v1
v[0]->P()=OldPos0;
v[1]->P()=OldPos1;
this->_priority = error;
return this->_priority;
}
//
//static double MaxError() {return 1e100;}
//
inline void UpdateHeap(HeapType & h_ret)
{
this->GlobalMark()++;
VertexType *v[2];
v[0]= this->pos.V(0);
v[1]= this->pos.V(1);
v[1]->IMark() = this->GlobalMark();
// First loop around the remaining vertex to unmark visited flags
vcg::face::VFIterator<FaceType> vfi(v[1]);
while (!vfi.End()){
vfi.V1()->ClearV();
vfi.V2()->ClearV();
++vfi;
}
// Second Loop
vfi = face::VFIterator<FaceType>(v[1]);
while (!vfi.End())
{
assert(!vfi.F()->IsD());
for (int j=0;j<3;j++)
{
if( !(vfi.V1()->IsV()) && vfi.V1()->IsRW())
{
vfi.V1()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V0(),vfi.V1()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(!IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V0()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
if( !(vfi.V2()->IsV()) && vfi.V2()->IsRW())
{
vfi.V2()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V0(),vfi.V2()),this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(!IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V0()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
if(Params().SafeHeapUpdate && vfi.V1()->IsRW() && vfi.V2()->IsRW() )
{
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V2()),this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(!IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V1()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
}
++vfi;
}
}
static void InitQuadric(TriMeshType &m)
{
typename TriMeshType::FaceIterator pf;
typename TriMeshType::VertexIterator pv;
int j;
QH::Init();
// m.ClearFlags();
for(pv=m.vert.begin();pv!=m.vert.end();++pv) // Azzero le quadriche
if( ! (*pv).IsD() && (*pv).IsW())
QH::Qd(*pv).SetZero();
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( !(*pf).IsD() && (*pf).IsR() )
if((*pf).V(0)->IsR() &&(*pf).V(1)->IsR() &&(*pf).V(2)->IsR())
{
QuadricType q;
Plane3<ScalarType,false> p;
// Calcolo piano
p.SetDirection( ( (*pf).V(1)->cP() - (*pf).V(0)->cP() ) ^ ( (*pf).V(2)->cP() - (*pf).V(0)->cP() ));
// Se normalizzo non dipende dall'area
if(!Params().UseArea)
p.Normalize();
p.SetOffset( p.Direction().dot((*pf).V(0)->cP()));
// Calcolo quadrica delle facce
q.ByPlane(p);
for(j=0;j<3;++j)
if( (*pf).V(j)->IsW() )
{
if(Params().QualityWeight)
q*=(*pf).V(j)->Q();
QH::Qd((*pf).V(j)) += q; // Sommo la quadrica ai vertici
}
for(j=0;j<3;++j)
if( (*pf).IsB(j) || Params().QualityQuadric ) // Bordo!
{
Plane3<ScalarType,false> pb; // Piano di bordo
// Calcolo la normale al piano di bordo e la sua distanza
// Nota che la lunghezza dell'edge DEVE essere Normalizzata
// poiche' la pesatura in funzione dell'area e'gia fatta in p.Direction()
// Senza la normalize il bordo e' pesato in funzione della grandezza della mesh (mesh grandi non decimano sul bordo)
pb.SetDirection(p.Direction() ^ ( (*pf).V1(j)->cP() - (*pf).V(j)->cP() ).normalized());
if( (*pf).IsB(j) ) pb.SetDirection(pb.Direction()* (ScalarType)Params().BoundaryWeight); // amplify border planes
else pb.SetDirection(pb.Direction()* (ScalarType)(Params().BoundaryWeight/100.0)); // and consider much less quadric for quality
pb.SetOffset(pb.Direction().dot((*pf).V(j)->cP()));
q.ByPlane(pb);
if( (*pf).V (j)->IsW() ) QH::Qd((*pf).V (j)) += q; // Sommo le quadriche
if( (*pf).V1(j)->IsW() ) QH::Qd((*pf).V1(j)) += q;
}
}
if(Params().ScaleIndependent)
{
vcg::tri::UpdateBounding<TriMeshType>::Box(m);
//Make all quadric independent from mesh size
Params().ScaleFactor = 1e8*pow(1.0/m.bbox.Diag(),6); // scaling factor
//Params().ScaleFactor *=Params().ScaleFactor ;
//Params().ScaleFactor *=Params().ScaleFactor ;
//printf("Scale factor =%f\n",Params().ScaleFactor );
//printf("bb (%5.2f %5.2f %5.2f)-(%5.2f %5.2f %5.2f) Diag %f\n",m.bbox.min[0],m.bbox.min[1],m.bbox.min[2],m.bbox.max[0],m.bbox.max[1],m.bbox.max[2],m.bbox.Diag());
}
}
//
//
//
//
//
//
//static void InitMesh(MESH_TYPE &m){
// Params().CosineThr=cos(Params().NormalThr);
// InitQuadric(m);
// //m.Topology();
// //OldInitQuadric(m,UseArea);
// }
//
CoordType ComputeMinimal()
{
typename TriMeshType::VertexType * v[2];
v[0] = this->pos.V(0);
v[1] = this->pos.V(1);
QuadricType q=QH::Qd(v[0]);
q+=QH::Qd(v[1]);
Point3<QuadricType::ScalarType> x;
bool rt=q.Minimum(x);
if(!rt) { // if the computation of the minimum fails we choose between the two edge points and the middle one.
Point3<QuadricType::ScalarType> x0=Point3d::Construct(v[0]->P());
Point3<QuadricType::ScalarType> x1=Point3d::Construct(v[1]->P());
x.Import((v[0]->P()+v[1]->P())/2);
double qvx=q.Apply(x);
double qv0=q.Apply(x0);
double qv1=q.Apply(x1);
if(qv0<qvx) x=x0;
if(qv1<qvx && qv1<qv0) x=x1;
}
return CoordType::Construct(x);
}
//
//
};
} // namespace tri
} // namespace vcg
#endif

View File

@ -0,0 +1,609 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_DECIMATION_TRIFLIP
#define __VCG_DECIMATION_TRIFLIP
#include <vcg/complex/local_optimization.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/space/triangle3.h>
namespace vcg
{
namespace tri
{
/** \addtogroup trimesh */
/* @{ */
/*!
* This Class is specialization of LocalModification for the edge flip
* It wraps the atomic operation EdgeFlip to be used in a optimization routine.
* Note that it has knowledge of the heap of the class LocalOptimization because
* it is responsible of updating it after a flip has been performed
* This is the simplest edge flipping class.
* It flips an edge only if two adjacent faces are coplanar and the
* quality of the faces improves after the flip.
*/
template <class TRIMESH_TYPE, class MYTYPE,
typename TRIMESH_TYPE::ScalarType (*QualityFunc)(
Point3<typename TRIMESH_TYPE::ScalarType> const & p0,
Point3<typename TRIMESH_TYPE::ScalarType> const & p1,
Point3<typename TRIMESH_TYPE::ScalarType> const & p2) = Quality>
class PlanarEdgeFlip :
public LocalOptimization< TRIMESH_TYPE>::LocModType
{
protected:
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::VertexType VertexType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapElem HeapElem;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapType HeapType;
/*!
* the pos of the flipping
*/
PosType _pos;
/*!
* priority in the heap
*/
ScalarType _priority;
/*!
* Mark for updating
*/
int _localMark;
/*!
* mark for up_dating
*/
static int& GlobalMark()
{
static int im = 0;
return im;
}
static void Insert(HeapType& heap, PosType& p, int mark)
{
if(!p.IsBorder() && p.F()->IsW() && p.FFlip()->IsW()) {
MYTYPE* newflip = new MYTYPE(p, mark);
heap.push_back(HeapElem(newflip));
std::push_heap(heap.begin(), heap.end());
}
}
public:
/*!
* Default constructor
*/
inline PlanarEdgeFlip()
{
}
/*!
* Constructor with <I>pos</I> type
*/
inline PlanarEdgeFlip(PosType pos, int mark)
{
_pos = pos;
_localMark = mark;
_priority = this->ComputePriority();
}
/*!
* Copy Constructor
*/
inline PlanarEdgeFlip(const PlanarEdgeFlip &par)
{
_pos = par.GetPos();
_localMark = par.GetMark();
_priority = par.Priority();
}
/*!
*/
~PlanarEdgeFlip()
{
}
/*!
* Parameter
*/
static ScalarType &CoplanarAngleThresholdDeg()
{
static ScalarType _CoplanarAngleThresholdDeg = 0.01f;
return _CoplanarAngleThresholdDeg;
}
inline PosType GetPos() const
{
return _pos;
}
inline int GetMark()const
{
return _localMark;
}
/*!
* Return the LocalOptimization type
*/
ModifierType IsOfType()
{
return TriEdgeFlipOp;
}
/*!
* Check if the pos is updated
*/
bool IsUpToDate()
{
int lastMark = _pos.F()->V(0)->IMark();
lastMark = std::max<int>(lastMark, _pos.F()->V(1)->IMark());
lastMark = std::max<int>(lastMark, _pos.F()->V(2)->IMark());
return ( _localMark >= lastMark );
}
/*!
*
Check if this flipping operation can be performed.
It is a topological and geometrical check.
*/
virtual bool IsFeasible()
{
if(!vcg::face::CheckFlipEdge(*this->_pos.F(), this->_pos.E()))
return false;
if( math::ToDeg( Angle(_pos.FFlip()->cN(), _pos.F()->cN()) ) > CoplanarAngleThresholdDeg() )
return false;
CoordType v0, v1, v2, v3;
int i = _pos.E();
v0 = _pos.F()->P0(i);
v1 = _pos.F()->P1(i);
v2 = _pos.F()->P2(i);
v3 = _pos.F()->FFp(i)->P2(_pos.F()->FFi(i));
// Take the parallelogram formed by the adjacent faces of edge
// If a corner of the parallelogram on extreme of edge to flip is >= 180
// the flip produce two identical faces - avoid this
if( (Angle(v2 - v0, v1 - v0) + Angle(v3 - v0, v1 - v0) >= M_PI) ||
(Angle(v2 - v1, v0 - v1) + Angle(v3 - v1, v0 - v1) >= M_PI))
return false;
// if any of two faces adj to edge in non writable, the flip is unfeasible
if(!_pos.F()->IsW() || !_pos.F()->FFp(i)->IsW())
return false;
return true;
}
/*!
* Compute the priority of this optimization
*/
/*
1
/|\
/ | \
2 | 3
\ | /
\|/
0
*/
ScalarType ComputePriority()
{
CoordType v0, v1, v2, v3;
int i = _pos.E();
v0 = _pos.F()->P0(i);
v1 = _pos.F()->P1(i);
v2 = _pos.F()->P2(i);
v3 = _pos.F()->FFp(i)->P2(_pos.F()->FFi(i));
ScalarType Qa = QualityFunc(v0, v1, v2);
ScalarType Qb = QualityFunc(v0, v3, v1);
ScalarType QaAfter = QualityFunc(v1, v2, v3);
ScalarType QbAfter = QualityFunc(v0, v3, v2);
// < 0 if the average quality of faces improves after flip
_priority = (Qa + Qb - QaAfter - QbAfter) / (ScalarType)2.0;
return _priority;
}
/*!
* Return the priority of this optimization
*/
virtual ScalarType Priority() const
{
return _priority;
}
/*!
* Execute the flipping of the edge
*/
void Execute(TRIMESH_TYPE &m)
{
int i = _pos.E();
int j = _pos.F()->FFi(i);
FacePointer f1 = _pos.F();
FacePointer f2 = _pos.F()->FFp(i);
vcg::face::FlipEdge(*_pos.F(), _pos.E());
// avoid texture coordinates swap after flip
if(tri::HasPerWedgeTexCoord(m)) {
f2->WT((j + 1) % 3) = f1->WT((i + 2) % 3);
f1->WT((i + 1) % 3) = f2->WT((j + 2) % 3);
}
}
/*!
*/
const char* Info(TRIMESH_TYPE &m)
{
static char dump[60];
sprintf(dump,"%d -> %d %g\n", _pos.F()->V(0)-&m.vert[0], _pos.F()->V(1)-&m.vert[0],-_priority);
return dump;
}
/*!
*/
static void Init(TRIMESH_TYPE &mesh, HeapType &heap)
{
heap.clear();
FaceIterator fi;
for(fi = mesh.face.begin(); fi != mesh.face.end(); ++fi) {
if(!(*fi).IsD() && (*fi).IsW()) {
for(unsigned int i = 0; i < 3; i++) {
if( !(*fi).IsB(i) && !((*fi).FFp(i)->IsD()) && (*fi).FFp(i)->IsW() ) {
if((*fi).V1(i) - (*fi).V0(i) > 0) {
PosType p(&*fi, i);
Insert(heap, p, IMark(mesh));
}
//heap.push_back( HeapElem( new MYTYPE(PosType(&*fi, i), mesh.IMark() )) );
} //endif
} //endfor
}
} //endfor
}
/*!
*/
virtual void UpdateHeap(HeapType &heap)
{
GlobalMark()++;
// after flip, the new edge just created is the next edge
int flipped = (_pos.E() + 1) % 3;
PosType pos(_pos.F(), flipped);
pos.F()->V(0)->IMark() = GlobalMark();
pos.F()->V(1)->IMark() = GlobalMark();
pos.F()->V(2)->IMark() = GlobalMark();
pos.F()->FFp(flipped)->V2(pos.F()->FFi(flipped))->IMark() = GlobalMark();
pos.FlipF(); pos.FlipE();
Insert(heap, pos, GlobalMark());
pos.FlipV(); pos.FlipE();
Insert(heap, pos, GlobalMark());
pos.FlipV(); pos.FlipE();
pos.FlipF(); pos.FlipE();
Insert(heap, pos, GlobalMark());
pos.FlipV(); pos.FlipE();
Insert(heap, pos, GlobalMark());
}
}; // end of PlanarEdgeFlip class
template <class TRIMESH_TYPE, class MYTYPE>
class TriEdgeFlip : public PlanarEdgeFlip<TRIMESH_TYPE, MYTYPE>
{
protected:
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType;
public:
/*!
* Default constructor
*/
inline TriEdgeFlip() {}
/*!
* Constructor with <I>pos</I> type
*/
inline TriEdgeFlip(const PosType pos, int mark)
{
this->_pos = pos;
this->_localMark = mark;
this->_priority = ComputePriority();
}
/*!
* Copy Constructor
*/
inline TriEdgeFlip(const TriEdgeFlip &par)
{
this->_pos = par.GetPos();
this->_localMark = par.GetMark();
this->_priority = par.Priority();
}
ScalarType ComputePriority()
{
/*
1
/|\
/ | \
2 | 3
\ | /
\|/
0
*/
CoordType v0, v1, v2, v3;
int i = this->_pos.E();
v0 = this->_pos.F()->P0(i);
v1 = this->_pos.F()->P1(i);
v2 = this->_pos.F()->P2(i);
v3 = this->_pos.F()->FFp(i)->P2(this->_pos.F()->FFi(i));
// if the sum of angles in v2 e v3 is > 180, then the triangle
// pair is not a delaunay triangulation
ScalarType alpha = math::Abs(Angle(v0 - v2, v1 - v2));
ScalarType beta = math::Abs(Angle(v0 - v3, v1 - v3));
this->_priority = 180 - math::ToDeg((alpha + beta));
return this->_priority;
}
};
// This kind of flip minimize the variance of number of incident faces
// on the vertices of two faces involved in the flip
template <class TRIMESH_TYPE, class MYTYPE>
class TopoEdgeFlip : public PlanarEdgeFlip<TRIMESH_TYPE, MYTYPE>
{
protected:
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapElem HeapElem;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapType HeapType;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::VertexIterator VertexIterator;
public:
/*!
* Default constructor
*/
inline TopoEdgeFlip() {}
/*!
* Constructor with <I>pos</I> type
*/
inline TopoEdgeFlip(const PosType pos, int mark)
{
this->_pos = pos;
this->_localMark = mark;
this->_priority = ComputePriority();
}
/*!
* Copy Constructor
*/
inline TopoEdgeFlip(const TopoEdgeFlip &par)
{
this->_pos = par.GetPos();
this->_localMark = par.GetMark();
this->_priority = par.Priority();
}
ScalarType ComputePriority()
{
/*
1
/|\
/ | \
2 | 3
\ | /
\|/
0
*/
VertexPointer v0, v1, v2, v3;
int i = this->_pos.E();
v0 = this->_pos.F()->V0(i);
v1 = this->_pos.F()->V1(i);
v2 = this->_pos.F()->V2(i);
v3 = this->_pos.F()->FFp(i)->V2(this->_pos.F()->FFi(i));
// This kind of flip minimize the variance of number of incident faces
// on the vertices of two faces involved in the flip
ScalarType avg = (v0->Q() + v1->Q() + v2->Q() + v3->Q()) / 4.0;
ScalarType varbefore = (powf(v0->Q() - avg, 2.0) +
powf(v1->Q() - avg, 2.0) +
powf(v2->Q() - avg, 2.0) +
powf(v3->Q() - avg, 2.0)) / 4.0;
ScalarType varafter = (powf(v0->Q() - 1 - avg, 2.0) +
powf(v1->Q() - 1 - avg, 2.0) +
powf(v2->Q() + 1 - avg, 2.0) +
powf(v3->Q() + 1 - avg, 2.0)) / 4.0;
this->_priority = varafter - varbefore;
return this->_priority;
}
/*!
* Execute the flipping of the edge
*/
void Execute(TRIMESH_TYPE &m)
{
int i = this->_pos.E();
FacePointer f1 = this->_pos.F();
FacePointer f2 = f1->FFp(i);
int j = f1->FFi(i);
// update the number of faces adjacent to vertices
f1->V0(i)->Q()--;
f1->V1(i)->Q()--;
f1->V2(i)->Q()++;
f2->V2(j)->Q()++;
// do the flip
vcg::face::FlipEdge(*this->_pos.F(), this->_pos.E());
// avoid texture coordinates swap after flip
if (tri::HasPerWedgeTexCoord(m)) {
f2->WT((j + 1) % 3) = f1->WT((i + 2) % 3);
f1->WT((i + 1) % 3) = f2->WT((j + 2) % 3);
}
}
static void Init(TRIMESH_TYPE &m, HeapType &heap)
{
// reset quality field for each vertex
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if(!(*vi).IsD())
(*vi).Q() = 0;
// for each vertex, put the number of incident faces in quality field
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if(!(*fi).IsD())
for(int i = 0; i < 3; i++)
(*fi).V(i)->Q()++;
TriEdgeFlip<TRIMESH_TYPE, MYTYPE>::Init(m, heap);
}
void UpdateHeap(HeapType &heap)
{
this->GlobalMark()++;
VertexPointer v0, v1, v2, v3;
int flipped = (this->_pos.E() + 1) % 3;
FacePointer f1 = this->_pos.F();
FacePointer f2 = this->_pos.F()->FFp(flipped);
v0 = f1->V0(flipped);
v1 = f1->V1(flipped);
v2 = f1->V2(flipped);
v3 = f2->V2(f1->FFi(flipped));
v0->IMark() = this->GlobalMark();
v1->IMark() = this->GlobalMark();
v2->IMark() = this->GlobalMark();
v3->IMark() = this->GlobalMark();
// edges of the first face, except the flipped edge
for(int i = 0; i < 3; i++) if(i != flipped) {
PosType newpos(f1, i);
Insert(heap, newpos, this->GlobalMark());
}
// edges of the second face, except the flipped edge
for(int i = 0; i < 3; i++) if(i != f1->FFi(flipped)) {
PosType newpos(f2, i);
Insert(heap, newpos, this->GlobalMark());
}
// every edge with v0, v1 v3 of f1
for(int i = 0; i < 3; i++) {
PosType startpos(f1, i);
PosType pos(startpos);
do { // go to the first border (if there is one)
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
// if a border is reached, set startpos here
if(pos.IsBorder())
startpos = pos;
do {
VertexPointer v = pos.VFlip();
if(v != v0 && v != v1 && v != v2 && v != v3)
Insert(heap, pos, this->GlobalMark());
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
}
PosType startpos(f2, (f1->FFi(flipped) + 2) % 3);
PosType pos(startpos);
do { // go to the first border (if there is one)
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
// if a border is reached, set startpos here
if(pos.IsBorder())
startpos = pos;
do {
VertexPointer v = pos.VFlip();
if(v != v0 && v != v1 && v != v2 && v != v3)
Insert(heap, pos, this->GlobalMark());
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
}
};
} // end of namespace tri
} // end of namespace vcg
#endif

View File

@ -0,0 +1,157 @@
#ifndef RINGWALKER_H
#define RINGWALKER_H
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include <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

@ -0,0 +1,327 @@
/****************************************************************************
* 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

@ -0,0 +1,178 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCGLIB_POLYGON_SUPPORT
#define __VCGLIB_POLYGON_SUPPORT
#include <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

@ -0,0 +1,924 @@
/***********F*****************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCGLIB_REFINE
#define __VCGLIB_REFINE
#include <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

@ -0,0 +1,623 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/*! \file refine_loop.h
*
* \brief This file contain code for Loop's subdivision scheme for triangular
* mesh and some similar method.
*
*/
#ifndef __VCGLIB_REFINE_LOOP
#define __VCGLIB_REFINE_LOOP
#include <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

@ -0,0 +1,246 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2006 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.3 2007/01/11 10:12:19 cignoni
Removed useless and conflicting inclusion of face.h
Revision 1.2 2006/05/25 09:39:09 cignoni
missing std and other gcc detected syntax errors
Revision 1.1 2006/05/21 06:59:13 cignoni
Initial Commit
****************************************************************************/
#ifndef __VCGLIB_TRIMESH_STAT
#define __VCGLIB_TRIMESH_STAT
// Standard headers
// VCG headers
#include <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

@ -0,0 +1,171 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.12 2007/05/31 09:39:55 cignoni
Small gcc compiling issues
Revision 1.11 2006/07/06 12:30:32 ganovelli
misleading comment removed
Revision 1.10 2005/12/14 17:14:13 pietroni
added assert on deleted flag condition
Revision 1.9 2005/02/08 14:38:05 turini
Warnings Correction
Revision 1.8 2004/05/17 08:26:28 turini
Changed : Parameters Order As In vcg::tetra::SubSet.
Revision 1.7 2004/05/17 07:58:16 turini
Minor Changes To Compile Even Without using namespace std.
Revision 1.6 2004/05/14 11:43:17 turini
Changed mesh ClearFlag call.
Revision 1.5 2004/05/13 09:59:20 turini
Added typedef typename in InsertedV
Revision 1.4 2004/05/07 10:06:46 turini
include Plane3 removed.
Revision 1.3 2004/05/07 09:35:09 turini
Added History Info
****************************************************************************/
#ifndef __VCGLIB_TRISUBSET
#define __VCGLIB_TRISUBSET
#include <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

View File

@ -0,0 +1,453 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.6 2007/02/02 04:11:00 tarini
added parameter theta (from conformal to equiareal) to AreaPresTextureOptimizer.
Improved feature lists (comments).
Revision 1.5 2007/02/02 01:39:58 tarini
added three general-utility global functions for texture coordinates: SmoothTextureCoords, IsFoldFree, MarkFolds (see descriptions)
Revision 1.4 2007/02/02 01:23:47 tarini
added a few general comments on AreaPreserving optimizer, recapping optimizer features.
Revision 1.3 2007/02/02 01:18:15 tarini
First version: general virtual class for texture optimizers. A subclass for area preservation.
****************************************************************************/
#ifndef __VCGLIB__TEXTCOOORD_OPTIMIZATION
#define __VCGLIB__TEXTCOOORD_OPTIMIZATION
#include <vcg/container/simple_temporary_data.h>
/*
SINGLE PATCH TEXTURE OPTIMIZATIONS
A set of classes to perform optimizations of disk->disk parametrization.
Requires texture coords to be defined per vertex (replicate seams).
*/
namespace vcg
{
namespace tri
{
/* Base class for all Texture Optimizers*/
template<class MESH_TYPE>
class TextureOptimizer{
protected:
MESH_TYPE &m;
SimpleTempData<typename MESH_TYPE::VertContainer, int > isFixed;
public:
/* Tpyes */
typedef MESH_TYPE MeshType;
typedef typename MESH_TYPE::VertexIterator VertexIterator;
typedef typename MESH_TYPE::FaceIterator FaceIterator;
typedef typename MESH_TYPE::VertexType VertexType;
typedef typename MESH_TYPE::FaceType FaceType;
typedef typename MESH_TYPE::ScalarType ScalarType;
/* Access functions */
const MeshType & Mesh() const {return m;}
MeshType & Mesh() {return m;}
/* Constructior */
TextureOptimizer(MeshType &_m):m(_m),isFixed(_m.vert){
assert(m.HasPerVertexTexture());
}
// initializes on current geometry
virtual void TargetCurrentGeometry()=0;
// performs an interation. Returns largest movement.
virtual ScalarType Iterate()=0;
// performs an iteration (faster, but it does not tell how close it is to stopping)
virtual void IterateBlind()=0;
// performs <steps> iteration
virtual ScalarType IterateN(int step){
for (int i=0; i<step-1; i++) {
this->IterateBlind();
}
if (step>1) return this->Iterate(); else return 0;
}
// performs iterations until convergence.
bool IterateUntilConvergence(ScalarType threshold=0.0001, int maxite=5000){
int i;
while (Iterate()>threshold) {
if (i++>maxite) return false;
}
return true;
}
// desctuctor: free temporary field
~TextureOptimizer(){
isFixed.Stop();
};
// set the current border as fixed (forced to stay in position during text optimization)
void SetBorderAsFixed(){
isFixed.Start();
for (VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) {
isFixed[v]=(v->IsB())?1:0;
}
}
// everything moves, no vertex must fixed during texture optimization)
void SetNothingAsFixed(){
isFixed.Start();
for (VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) {
isFixed[v]=0;
}
}
// fix a given vertex
void FixVertex(const VertexType *v, bool fix=true){
isFixed[v]=(fix)?1:0;
}
};
/*
AREA PRESERVING TEXTURE OPTIMIZATION
as in: Degener, P., Meseth, J., Klein, R.
"An adaptable surface parameterization method."
Proc. of the 12th International Meshing oundtable, 201213 [2003].
Features:
:) - Balances angle and area distortions (best results!).
:) - Can choose how to balance area and angle preservation (see SetTheta)
theta=0 -> pure conformal (use MIPS instead!)
theta=3 -> good balance between area and angle preservation
theta>3 -> care more about area than about angles
:( - Slowest method.
:( - Requires a fixed boundary, else expands forever in texture space (unless theta=0).
:( - Diverges in presence of flipped faces (unless theta=0).
:( - Requires a speed parameter to be set.
Speed too large => when close, bounces back and forth around minimum, w/o getting any closer.
Lower speed => longer convercence times
*/
template<class MESH_TYPE>
class AreaPreservingTextureOptimizer:public TextureOptimizer<MESH_TYPE>{
public:
/* Types */
typedef MESH_TYPE MeshType;
typedef typename MESH_TYPE::VertexIterator VertexIterator;
typedef typename MESH_TYPE::FaceIterator FaceIterator;
typedef typename MESH_TYPE::VertexType VertexType;
typedef typename MESH_TYPE::FaceType FaceType;
typedef typename MESH_TYPE::ScalarType ScalarType;
private:
typedef TextureOptimizer<MESH_TYPE> Super; // superclass (commodity)
// extra data per face: [0..3] -> cotangents. [4] -> area*2
SimpleTempData<typename MESH_TYPE::FaceContainer, Point4<ScalarType> > data;
SimpleTempData<typename MESH_TYPE::VertContainer, Point2<ScalarType> > sum;
ScalarType totArea;
ScalarType speed;
int theta;
public:
// constructor and destructor
AreaPreservingTextureOptimizer(MeshType &_m):Super(_m),data(_m.face),sum(_m.vert){
speed=0.001;
theta=3;
}
~AreaPreservingTextureOptimizer(){
data.Stop();
sum.Stop();
Super::isFixed.Stop();
}
void SetSpeed(ScalarType _speed){
speed=_speed;
}
ScalarType GetSpeed(){
return speed;
}
// sets the parameter theta:
// good parameters are in 1..3
// 0 = converge to pure conformal, ignore area preservation
// 3 = good balance between area and conformal
// >3 = area more important, angle preservation less important
void SetTheta(int _theta){
theta=_theta;
}
int GetTheta(){
return theta;
}
void IterateBlind(){
/* todo: do as iterate, but without */
Iterate();
}
ScalarType Iterate(){
ScalarType max; // max displacement
#define v0 (f->V0(i)->T().P())
#define v1 (f->V1(i)->T().P())
#define v2 (f->V2(i)->T().P())
for (VertexIterator v=Super::m.vert.begin(); v!=Super::m.vert.end(); v++) {
sum[v].SetZero();
}
ScalarType tot_proj_area=0;
for (FaceIterator f=Super::m.face.begin(); f!=Super::m.face.end(); f++) {
int i=0;
double area2 = ((v1-v0) ^ (v2-v0));
tot_proj_area+=area2;
}
double scale= 1.0; //tot_proj_area / tot_area ;
for (FaceIterator f=Super::m.face.begin(); f!=Super::m.face.end(); f++) {
int i=0; ScalarType area2 = ((v1-v0) ^ (v2-v0));
for (i=0; i<3; i++){
ScalarType
a = (v1-v0).Norm(),
b = ((v1-v0) * (v2-v0))/a,
c = area2 / a,
m0= data[f][i] / area2,
m1= data[f][(i+1)%3] / area2,
m2= data[f][(i+2)%3] / area2,
mx= (b-a)/area2,
my= c/area2, // 1.0/a
mA= data[f][3]/area2 * scale,
e = m0*((b-a)*(b-a)+c*c) + m1*(b*b+c*c) + m2*a*a, // as obvious
M1= mA + 1.0/mA,
M2= mA - 1.0/mA,
px= e*my,
py=-e*mx,
qx= m1*b+ m2*a,
qy= m1*c,
/* linear weightings
dx= (OMEGA) * (my * M2) +
(1-OMEGA) * ( px - 2.0*qx),
dy= (OMEGA) * (-mx * M2) +
(1-OMEGA) * ( py - 2.0*qy),*/
// exponential weighting
// 2d gradient
dx=// M1
//*M1 // ^ theta-1
pow(M1,theta-1)
*(px*(M1+ theta*M2) - 2.0*qx*M1),
dy=// M1
//*M1 // ^ theta-1
pow(M1,theta-1)
*(py*(M1+ theta*M2) - 2.0*qy*M1),
gy= dy/c,
gx= (dx - gy*b) / a;
// 3d gradient
sum[f->V(i)]+= ( (v1-v0) * gx + (v2-v0) * gy ) * data[f][3];
}
}
max=0; // max displacement
speed=0.001;
for (VertexIterator v=Super::m.vert.begin(); v!=Super::m.vert.end(); v++)
if ( !Super::isFixed[v] ) //if (!v->IsB())
{
ScalarType n=sum[v].Norm();
if ( n > 1 ) { sum[v]/=n; n=1.0;}
if ( n*speed<=0.1 ); {
v->T().P()-=(sum[v] * speed ) /** scale*/;
if (max<n) max=n;
}
//else rejected++;
}
return max;
#undef v0
#undef v1
#undef v2
//printf("rejected %d\n",rejected);
}
void TargetCurrentGeometry(){
Super::isFixed.Start();
data.Start();
sum.Start();
totArea=0;
for (FaceIterator f=Super::m.face.begin(); f!=Super::m.face.end(); f++) {
double area2 = ((f->V(1)->P() - f->V(0)->P() )^(f->V(2)->P() - f->V(0)->P() )).Norm();
totArea+=area2;
//if ( Super::isFixed[f->V1(0)] )
for (int i=0; i<3; i++){
data[f][i]=(
(f->V1(i)->P() - f->V0(i)->P() )*(f->V2(i)->P() - f->V0(i)->P() )
)/area2;
data[f][3]=area2;
}
}
}
};
/* texture coords general utility functions */
/*++++++++++++++++++++++++++++++++++++++++++*/
// returns false if any fold is present (faster than MarkFolds)
template<class MESH_TYPE>
bool IsFoldFree(MESH_TYPE &m){
assert(m.HasPerVertexTexture());
typedef typename MESH_TYPE::VertexType::TextureType::PointType PointType;
typedef typename MESH_TYPE::VertexType::TextureType::PointType::ScalarType ScalarType;
ScalarType lastsign=0;
for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){
ScalarType sign=((f->V(1)->T().P()-f->V(0)->T().P()) ^ (f->V(2)->T().P()-f->V(0)->T().P()));
if (sign!=0) {
if (sign*lastsign<0) return false;
lastsign=sign;
}
}
return true;
}
// detects and marks folded faces, by setting their quality to 0 (or 1 otherwise)
// returns number of folded faces
template<class MESH_TYPE>
int MarkFolds(MESH_TYPE &m){
assert(m.HasPerVertexTexture());
assert(m.HasPerFaceQuality());
typedef typename MESH_TYPE::VertexType::TextureType::PointType PointType;
typedef typename MESH_TYPE::VertexType::TextureType::PointType::ScalarType ScalarType;
SimpleTempData<typename MESH_TYPE::FaceContainer, short> sign(m.face);
sign.Start(0);
// first pass, determine predominant sign
int npos=0, nneg=0;
ScalarType lastsign=0;
for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){
ScalarType fsign=((f->V(1)->T().P()-f->V(0)->T().P()) ^ (f->V(2)->T().P()-f->V(0)->T().P()));
if (fsign<0) { sign[f]=-1; nneg++; }
if (fsign>0) { sign[f]=+1; npos++; }
}
// second pass, detect folded faces
int res=0;
short gsign= (nneg>npos)?-1:+1;
for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){
if (sign[f]*gsign<0){
res++;
f->Q()=0;
} else f->Q()=1;
}
sign.Stop();
return res;
}
// Smooths texture coords.
// (can be useful to remove folds,
// e.g. these created when obtaining tecture coordinates after projections)
template<class MESH_TYPE>
void SmoothTextureCoords(MESH_TYPE &m){
assert(m.HasPerVertexTexture());
typedef typename MESH_TYPE::VertexType::TextureType::PointType PointType;
SimpleTempData<typename MESH_TYPE::VertContainer, int> div(m.vert);
SimpleTempData<typename MESH_TYPE::VertContainer, PointType > sum(m.vert);
div.Start();
sum.Start();
for (typename MESH_TYPE::VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) {
sum[v].SetZero();
div[v]=0;
}
for (typename MESH_TYPE::FaceIterator f=m.face.begin(); f!=m.face.end(); f++){
div[f->V(0)] +=2; sum[f->V(0)] += f->V(2)->T().P(); sum[f->V(0)] += f->V(1)->T().P();
div[f->V(1)] +=2; sum[f->V(1)] += f->V(0)->T().P(); sum[f->V(1)] += f->V(2)->T().P();
div[f->V(2)] +=2; sum[f->V(2)] += f->V(1)->T().P(); sum[f->V(2)] += f->V(0)->T().P();
}
for (typename MESH_TYPE::VertexIterator v=m.vert.begin(); v!=m.vert.end(); v++) // if (!v->IsB())
{
if (v->div>0) {
v->T().P() = sum[v]/div[v];
}
}
div.Stop();
sum.Stop();
}
} } // End namespace vcg::tri
#endif // __VCGLIB__TEXTCOOORD_OPTIMIZATION

View File

@ -0,0 +1,94 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
// marco: removed types FaceType, FacePointer, FaceIterator to allow the use of this method from vertex meshes
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.2 2004/09/15 11:16:27 ganovelli
changed P() to cP()
Revision 1.1 2004/04/05 11:56:13 cignoni
First working version!
Revision 1.2 2004/03/12 15:22:19 cignoni
Written some documentation and added to the trimes doxygen module
Revision 1.1 2004/03/05 10:59:24 cignoni
Changed name from plural to singular (normals->normal)
Revision 1.1 2004/03/04 00:05:50 cignoni
First working version!
Revision 1.1 2004/02/19 13:11:06 cignoni
Initial commit
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_BOUNDING
#define __VCG_TRI_UPDATE_BOUNDING
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile bounding.h vcg/complex/trimesh/update/bounding.h
/// \brief Management, updating and computation of per-vertex and per-face normals.
/**
This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh.
*/
template <class ComputeMeshType>
class UpdateBounding
{
public:
typedef ComputeMeshType MeshType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
/// \brief Calculates the bounding box of the \code <ComputeMeshType> \endcode m
static void Box(ComputeMeshType &m)
{
m.bbox.SetNull();
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if( !(*vi).IsD() ) m.bbox.Add((*vi).cP());
}
}; // end class
} // End namespace
} // End namespace
#endif

View File

@ -0,0 +1,878 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_COLOR
#define __VCG_TRI_UPDATE_COLOR
#include <limits>
#include <math.h>
#include <time.h>
#include <vcg/space/color4.h>
#include <vcg/math/histogram.h>
#include <vcg/complex/trimesh/stat.h>
#include <vcg/math/perlin_noise.h>
#include <vcg/math/random_generator.h>
#include <vcg/complex/trimesh/clean.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile color.h vcg/complex/trimesh/update/color.h
/// \brief Generation of per-vertex and per-face colors according to various strategy.
/**
This class is used to compute per face or per vertex color with respect to for example Border (UpdateColor::VertexBorderFlag), Selection (UpdateColor::FaceSelected), Quality .
*/
template <class UpdateMeshType>
class UpdateColor
{
public:
typedef UpdateMeshType MeshType;
typedef typename UpdateMeshType::VertexType VertexType;
typedef typename UpdateMeshType::VertexPointer VertexPointer;
typedef typename UpdateMeshType::VertexIterator VertexIterator;
typedef typename UpdateMeshType::FaceType FaceType;
typedef typename UpdateMeshType::FacePointer FacePointer;
typedef typename UpdateMeshType::FaceIterator FaceIterator;
class ColorAvgInfo
{
public:
unsigned int r;
unsigned int g;
unsigned int b;
unsigned int a;
int cnt;
};
static void VertexFromFace( UpdateMeshType &m)
{
ColorAvgInfo csi;
csi.r=0; csi.g=0; csi.b=0; csi.cnt=0;
SimpleTempData<typename UpdateMeshType::VertContainer, ColorAvgInfo> TD(m.vert,csi);
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
for(int j=0;j<3;++j)
{
TD[(*fi).V(j)].r+=(*fi).C()[0];
TD[(*fi).V(j)].g+=(*fi).C()[1];
TD[(*fi).V(j)].b+=(*fi).C()[2];
TD[(*fi).V(j)].a+=(*fi).C()[3];
++TD[(*fi).V(j)].cnt;
}
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD() && TD[*vi].cnt>0 )
{
(*vi).C()[0] = TD[*vi].r / TD[*vi].cnt;
(*vi).C()[1] = TD[*vi].g / TD[*vi].cnt;
(*vi).C()[2] = TD[*vi].b / TD[*vi].cnt;
(*vi).C()[3] = TD[*vi].a / TD[*vi].cnt;
}
}
static void FaceFromVertex( UpdateMeshType &m)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
{
Color4f avg = (Color4f::Construct((*fi).V(0)->C()) +
Color4f::Construct((*fi).V(1)->C()) +
Color4f::Construct((*fi).V(2)->C()) )/ 3.0;
(*fi).C().Import(avg);
}
}
/// \brief Color the vertexes of the mesh that are on the border
/**
It uses the information in the Vertex flags, and not any topology.
So it just require that you have correctly computed the flags;
- vcg::tri::UpdateTopology<Mesh>::FaceFace(m.cm);
- vcg::tri::UpdateFlags<Mesh>::FaceBorderFromFF(m.cm);
- vcg::tri::UpdateFlags<Mesh>::VertexBorderFromFace (m.cm);
- vcg::tri::UpdateColor<Mesh>::VertexBorderFlag(m.cm);
*/
static void VertexBorderFlag( UpdateMeshType &m, Color4b BorderColor=Color4b::Blue, Color4b InternalColor=Color4b::White, Color4b MixColor=Color4b::Cyan)
{
Color4b BaseColor = Color4b::Green;
VertexConstant(m,BaseColor);
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
for(int j=0;j<3;++j)
if((*fi).IsB(j)){
if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = BorderColor;
if( (*fi).V(j)->C() == InternalColor) (*fi).V(j)->C() = MixColor;
if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = BorderColor;
if( (*fi).V1(j)->C() == InternalColor) (*fi).V1(j)->C() = MixColor;
} else
{
if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = InternalColor;
if( (*fi).V(j)->C() == BorderColor) (*fi).V(j)->C() = MixColor;
if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = InternalColor;
if( (*fi).V1(j)->C() == BorderColor) (*fi).V1(j)->C() = MixColor;
}
}
/// This function colores the face of a mesh randomly.
/// The faux bit is used to color polygonal faces uniformly
static void FaceRandomConnectedComponent( UpdateMeshType &m)
{
std::vector< std::pair<int, typename UpdateMeshType::FacePointer> > CCV;
int ScatterSize= min (100,tri::Clean<UpdateMeshType>::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many.
ConnectedIterator<MeshType> ci;
for(unsigned int i=0;i<CCV.size();++i)
{
Color4b BaseColor = Color4b::Scatter(ScatterSize, i%ScatterSize,.4f,.7f);
std::vector<typename MeshType::FacePointer> FPV;
for(ci.start(m,CCV[i].second);!ci.completed();++ci)
(*ci)->C()=BaseColor;
}
}
/// This function colores the face of a mesh randomly.
/// The faux bit is used to color polygonal faces uniformly
static void MultiFaceRandom( UpdateMeshType &m)
{
FaceIterator fi;
Color4b BaseColor = Color4b::Black;
FaceConstant(m,BaseColor);
int id_num=0;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
{
id_num++;
if((*fi).C() == BaseColor) (*fi).C() = Color4b::Scatter(50, id_num%50,.4f,.7f);
for(int j=0;j<3;++j)
if((*fi).IsF(j))
{
assert(!IsBorder((*fi),j));
(*fi).FFp(j)->C()= (*fi).C();
}
}
}
static void FaceBF( UpdateMeshType &m, Color4b vn=Color4b::White, Color4b vb=Color4b::Blue,
Color4b vc=Color4b::Red, Color4b vs=Color4b::LightBlue)
{
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
(*fi).C() = vn;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
{
if((*fi).IsS())
(*fi).C() = vs;
else
{
for(int j=0;j<3;++j)
if(*fi.IsManifold(j)){
if((*fi).IsB(j)){
(*fi).C() = vb;
(*fi).C() = vb;
}
}
else
{
(*fi).C() = vc;
(*fi).C() = vc;
}
}
}
}
static int FaceSelected(UpdateMeshType &m, Color4b vs=Color4b::LightBlue)
{
int cnt=0;
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD()){
if((*fi).IsS()) { (*fi).C() = vs; ++cnt; }
else (*fi).C() = Color4b::White;
}
return cnt;
}
static void FaceColorStrip(UpdateMeshType &m, std::vector<FacePointer> &TStripF)
{
vcg::Color4b cc[7]={
vcg::Color4b::White ,
vcg::Color4b::Red ,
vcg::Color4b::Green ,
vcg::Color4b::Blue ,
vcg::Color4b::Cyan ,
vcg::Color4b::Yellow ,
vcg::Color4b::Magenta
};
int cnt=0;
typename std::vector<FacePointer>::iterator fi;
for(fi=TStripF.begin();fi!=TStripF.end();++fi)
if(*fi) (**fi).C().ColorRamp(0,16,cnt);
else cnt=(cnt+1)%16;
// if(*fi) (**fi).C()=cc[cnt];
// else cnt=(cnt+1)%7;
}
static int VertexSelected(UpdateMeshType &m, Color4b vs=Color4b::LightBlue)
{
int cnt=0;
typename UpdateMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD()){
if((*vi).IsS()) {(*vi).C() = vs; ++cnt; }
else (*vi).C() = Color4b::White;
}
return cnt;
}
static void VertexConstant(UpdateMeshType &m, Color4b c=Color4b::White)
{
typename UpdateMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).C()=c;
}
static void FaceConstant(UpdateMeshType &m, Color4b c=Color4b::White)
{
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
(*fi).C()=c;
}
static void VertexBorderManifoldFlag(UpdateMeshType &m, Color4b vn=Color4b::White, Color4b vb=Color4b::Blue, Color4b vc=Color4b::Red)
{
typename UpdateMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).C()=vn;
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
for(int j=0;j<3;++j)
if((*fi).IsManifold(j)){
if((*fi).IsB(j)){
(*fi).V(j)->C()=vb;
(*fi).V1(j)->C()=vb;
}
}
else
{
(*fi).V(j)->C()=vc;
(*fi).V1(j)->C()=vc;
}
}
static void FaceQualityGray(UpdateMeshType &m, float minq, float maxq)
{
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
(*fi).C().SetGrayShade( ((*fi).Q()-minq)/(maxq-minq));
}
static void FaceQualityGray(UpdateMeshType &m)
{
std::pair<float,float> minmax = Stat<UpdateMeshType>::ComputePerFaceQualityMinMax(m);
FaceQualityGray(m,minmax.first,minmax.second);
}
static void FaceQualityRamp(UpdateMeshType &m,bool selected=false)
{
// step 1: find the range
typename UpdateMeshType::FaceIterator fi;
float minq=std::numeric_limits<float>::max(),
maxq=-std::numeric_limits<float>::max();
for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
if(!selected || (*fi).IsS())
{
minq=std::min(minq,(*fi).Q());
maxq=std::max(maxq,(*fi).Q());
}
FaceQualityRamp(m,minq,maxq,selected);
}
static void FaceQualityRamp(UpdateMeshType &m, float minq, float maxq,bool selected=false)
{
typename UpdateMeshType::FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
if(!selected || (*fi).IsS())
(*fi).C().ColorRamp(minq,maxq,(*fi).Q());
}
static void VertexQualityRamp(UpdateMeshType &m, float minq, float maxq)
{
typename UpdateMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD())
(*vi).C().ColorRamp(minq,maxq,(*vi).Q());
}
static void VertexQualityRamp(UpdateMeshType &m)
{
std::pair<float,float> minmax = Stat<UpdateMeshType>::ComputePerVertexQualityMinMax(m);
VertexQualityRamp(m,minmax.first,minmax.second);
}
static void VertexQualityGray(UpdateMeshType &m, const float minq, const float maxq)
{
typename UpdateMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD())
(*vi).C().SetGrayShade( ((*vi).Q()-minq)/(maxq-minq));
}
static void VertexQualityGray(UpdateMeshType &m)
{
std::pair<float,float> minmax = Stat<UpdateMeshType>::ComputePerVertexQualityMinMax( m);
VertexQualityGray(m,minmax.first,minmax.second);
}
//Fill the mesh with the selected color.
static int Filling(UpdateMeshType &m, Color4b c, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = c;
++counter;
}
}
}
return counter;
}
//Reduces the mesh to two colors according to a threshold.
static int Thresholding(UpdateMeshType &m, float threshold, Color4b c1 = Color4<unsigned char>::Black, Color4b c2 = Color4<unsigned char>::White, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
float value = ComputeLightness((*vi).C());
if(value<=threshold) (*vi).C() = c1;
else (*vi).C() = c2;
++counter;
}
}
}
return counter;
}
//Computes the lightness value for a specified color. lightness = 0.5*(Max(R,G,B)+Min(R,G,B))
static float ComputeLightness(Color4b c)
{
float min_rgb = (float)math::Min(c[0],c[1],c[2]);
float max_rgb = (float)math::Max(c[0],c[1],c[2]);
return (max_rgb + min_rgb)/2;
}
//Apply the brightness filter, with the given amount, to the mesh.
static int Brighting(UpdateMeshType &m, float amount, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = Color4b(
math::Clamp(int((*vi).C()[0]+amount),0,255),
math::Clamp(int((*vi).C()[1]+amount),0,255),
math::Clamp(int((*vi).C()[2]+amount),0,255),
255);
++counter;
}
}
}
return counter;
}
//Apply Contrast filter to the mesh with the given contrast factor.
static int Contrast(UpdateMeshType &m, float factor, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorMul((*vi).C(),factor);
++counter;
}
}
}
return counter;
}
//Performs contrast operations on color, i.e expands or compress the histogram around
//the midpoint value. NewValue = (OldValue - 128) ◊ factor + 128
static Color4b ColorMul(Color4b c, float factor)
{
return Color4b( ValueMul(c[0], factor), ValueMul(c[1], factor), ValueMul(c[2], factor), 1);
}
static int ValueMul(int value, float factor)
{
return math::Clamp<int>((int)((value - 128)*factor + 128), 0, 255);
}
//Apply Brightness and Contrast filter to the mesh, with the given contrast factor and brightness amount.
static int BrightnessContrast(UpdateMeshType &m, float brightness, float contrast, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorBrightnessContrast((*vi).C(),brightness,contrast);
++counter;
}
}
}
return counter;
}
//Performs contrast and brightness operations on color, i.e NewValue = (OldValue - 128) ◊ contrast + 128 + amount
//The result is clamped just one time after all computations; this get a more accurate result.
// The formula used here is the one of GIMP.
static Color4b ColorBrightnessContrast(Color4b c, float brightness, float contrast)
{
return Color4b( ValueBrightnessContrast(c[0], brightness, contrast),
ValueBrightnessContrast(c[1], brightness, contrast),
ValueBrightnessContrast(c[2], brightness, contrast), 1 );
}
static int ValueBrightnessContrast(unsigned char ivalue, float brightness, float contrast)
{
float value = float(ivalue)/255.0f;
if (brightness < 0.0) value = value * ( 1.0 + brightness);
else value = value + ((1.0 - value) * brightness);
value = (value - 0.5) * (tan ((contrast + 1) * M_PI/4) ) + 0.5;
return math::Clamp<int>(255.0*value, 0, 255);
}
//Invert the colors of the mesh.
static int Invert(UpdateMeshType &m, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorInvert((*vi).C());
++counter;
}
}
}
return counter;
}
//Invert the rgb component of the color
static Color4b ColorInvert(Color4b c)
{
return Color4b( ValueInvert(c[0]), ValueInvert(c[1]), ValueInvert(c[2]), 1);
}
static int ValueInvert(int value)
{
return 255-value;
}
//Apply the gamma correction filter, with the given gamma exponet, to the mesh.
static int Gamma(UpdateMeshType &m, float gamma, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorPow((*vi).C(), 1/gamma);
++counter;
}
}
}
return counter;
}
//computes the standard gamma transformation on a given color, according to NewVal = OldVal^(1/gamma)
static Color4b ColorPow(Color4b c, float exponent)
{
return vcg::Color4b(
math::Clamp((int)( ValuePow(float(c[0])/255, exponent)*255), 0, 255),
math::Clamp((int)( ValuePow(float(c[1])/255, exponent)*255), 0, 255),
math::Clamp((int)( ValuePow(float(c[2])/255, exponent)*255), 0, 255),
255);
}
static float ValuePow(float value, float exponent)
{
return powf(value, exponent);
}
//useful bit masks for RGB channels, used for Levels filter.
enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 };
//Adjusts color levels of the mesh. Filter can be applied to all RGB channels or to each channel separately.
//in_min, gamma and in_max are respectively the black point, the gray point and the white point.
//out_min and out_max are the output level for black and white respectively.
static int Levels(UpdateMeshType &m, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask);
++counter;
}
}
}
return counter;
}
//Performs levels transformation on each channel set to 1 in the rgbMask.
static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask)
{
unsigned char r = c[0], g = c[1], b = c[2];
if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max);
if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max);
if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max);
return Color4b(r, g, b, 255);
}
//Transform on levels
static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max)
{
float fvalue = value/255.0f;
// normalize
fvalue = math::Clamp<float>(fvalue - in_min, 0.0f, 1.0f) / math::Clamp<float>(in_max - in_min, 1.0f/255.0f, 1.0f);
// transform gamma
fvalue = powf(fvalue,1/gamma);
// rescale range
fvalue = fvalue * (out_max - out_min) + out_min;
//back in interval [0,255] and clamp
return math::Clamp<int>((int)(fvalue * 255), 0, 255);
}
//Colors the mesh. Color is blended to the mesh with the given intensity.
static int Colourisation(UpdateMeshType &m, Color4b c, float intensity, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorApplyDiff((*vi).C(), c, intensity);
++counter;
}
}
}
return counter;
}
//Perform colourisation operation. For each channel C: newC = origC + intensity * (newC - origC)
static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intensity)
{
return Color4b( ValueApplyDiff(old_color[0],new_color[0],intensity), ValueApplyDiff(old_color[1],new_color[1],intensity), ValueApplyDiff(old_color[2], new_color[2],intensity), 255);
}
static int ValueApplyDiff(int old_value, int new_value, float intensity)
{
return math::Clamp<int>((int)(old_value + intensity * (new_value - old_value)), 0, 255);
}
//An useful ENUM to hold all desaturation methods.
enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2};
//Desaturates the mesh according the selected method. Method belongs to DesaturationMethods's ENUM.
static int Desaturation(UpdateMeshType &m, int method, const bool ProcessSelected=false)
{
int counter=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C() = ColorDesaturate((*vi).C(), method);
++counter;
}
}
}
return counter;
}
//Desature the color. Ausiliary functions to calculate lightness/luminosity/average.
static Color4b ColorDesaturate(Color4b c, int method)
{
switch(method){
case M_LIGHTNESS:{
int val = (int)ComputeLightness(c);
return Color4b( val, val, val, 255);
}
case M_AVERAGE:{
int val = (int)ComputeAvgLightness(c);
return Color4b( val, val, val, 255);
}
case M_LUMINOSITY:{
int val = (int)ComputeLuminosity(c);
return Color4b( val, val, val, 255);
}
default: assert(0);
}
}
//ausiliary function to compute average lightness. value = (R+G+B)/3
static float ComputeAvgLightness(Color4b c)
{
return float(c[0]+c[1]+c[2])/3.0f;
}
//ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B
static float ComputeLuminosity(Color4b c)
{
return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]);
}
//Equalize the histogram of colors. It can equalize any combination of rgb channels or
//it can work on lightness.
static int Equalize(UpdateMeshType &m, unsigned int rgbMask, const bool ProcessSelected=false)
{
//declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness
Histogramf Hl, Hr, Hg, Hb;
Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear();
Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255);
int counter=0;
VertexIterator vi;
//Scan the mesh to build the histograms
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms
{
float v = ComputeLightness((*vi).C())+0.5; //compute and round lightness value
Hl.Add(v); Hr.Add((float)(*vi).C()[0]); Hg.Add((float)(*vi).C()[1]); Hb.Add((float)(*vi).C()[2]);
}
}
}
//for each histogram, compute the cumulative distribution function, and build a lookup table
int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256];
cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0);
for(int i=1; i<256; i++){
cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1];
cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1];
cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1];
cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1];
}
//this loop aaplies the transformation to colors
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask);
++counter;
}
}
}
return counter;
}
//Applies equalization to the components of the color according to rgbmask
static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask)
{
unsigned char r = c[0], g = c[1], b = c[2];
if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness
{
int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]);
return Color4b(v, v, v, 255); //return the equalized gray color
}
if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red
if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green
if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue
return Color4b(r, g, b, 255); //return the equalized color
}
//Compute the equalized value
static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax)
{
return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f);
}
//applies the white balance filter. It may works with an auto regulation of white, or based on a user
//color that is supposed to be white.
static int WhiteBalance(UpdateMeshType &m, bool automatic, Color4b userColor, const bool ProcessSelected=false)
{
Color4b unbalancedWhite;
float lightness = 0;
int counter=0;
VertexIterator vi;
if(!automatic) unbalancedWhite = userColor; //no auto regolation required, user has provided a color.
else //else, we need to scan the mesh and pick its lighter color...
{
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected...
{
//the lighter color is selected with an incremental approach...
float v = ComputeLightness((*vi).C());
if( v > lightness){
lightness = v; //save lightness
unbalancedWhite = (*vi).C(); //save the color
}
}
}
}
}
//in this loop the transformation is applied to the mesh
for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
{
if(!(*vi).IsD()) //if it has not been deleted...
{
if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
{
(*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite);
++counter;
}
}
}
return counter;
}
//Balnce the white of the color, applying a correction factor based on the unbalancedWhite color.
static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite)
{
//sanity check to avoid division by zero...
if(unbalancedWhite[0]==0) unbalancedWhite[0]=1;
if(unbalancedWhite[1]==0) unbalancedWhite[1]=1;
if(unbalancedWhite[2]==0) unbalancedWhite[2]=1;
return Color4b(
math::Clamp<int>((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255),
math::Clamp<int>((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255),
math::Clamp<int>((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255),
255);
}
static void PerlinColor(MeshType& m, Box3f bbox, float freq, Point3i channelOffsets)
{
typedef typename MeshType::ScalarType ScalarType;
Point3<ScalarType> p;
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
{
if(!(*vi).IsD()){
p = bbox.GlobalToLocal(m.Tr * (*vi).P()); //actual vertex position scaled to bbox
(*vi).C() = Color4b( int(255*math::Perlin::Noise(channelOffsets[0]+p[0]*freq,channelOffsets[0]+p[1]*freq,channelOffsets[0]+p[2]*freq)),
int(255*math::Perlin::Noise(channelOffsets[1]+p[0]*freq,channelOffsets[1]+p[1]*freq,channelOffsets[1]+p[2]*freq)),
int(255*math::Perlin::Noise(channelOffsets[2]+p[0]*freq,channelOffsets[2]+p[1]*freq,channelOffsets[2]+p[2]*freq)),
255 );
}
}
}
static void ColorNoise(MeshType& m, int noiseBits)
{
if(noiseBits>8) noiseBits = 8;
if(noiseBits<1) return;
math::SubtractiveRingRNG randomGen = math::SubtractiveRingRNG(time(NULL));
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
{
if(!(*vi).IsD()){
(*vi).C()[0] = math::Clamp<int>((*vi).C()[0] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
(*vi).C()[1] = math::Clamp<int>((*vi).C()[1] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
(*vi).C()[2] = math::Clamp<int>((*vi).C()[2] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
}
}
}
};
}// end namespace
}// end namespace
#endif

View File

@ -0,0 +1,688 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.8 2008/05/14 10:03:29 ganovelli
Point3f->Coordtype
Revision 1.7 2008/04/23 16:37:15 onnis
VertexCurvature method added.
Revision 1.6 2008/04/04 10:26:12 cignoni
Cleaned up names, now Kg() gives back Gaussian Curvature (k1*k2), while Kh() gives back Mean Curvature 1/2(k1+k2)
Revision 1.5 2008/03/25 11:00:56 ganovelli
fixed bugs sign of principal direction and mean curvature value
Revision 1.4 2008/03/17 11:29:59 ganovelli
taubin and desbrun estimates added (-> see vcg/simplex/vertex/component.h [component_ocf.h|component_occ.h ]
Revision 1.3 2006/02/27 18:02:57 ponchio
Area -> doublearea/2
added some typename
Revision 1.2 2005/10/25 09:17:41 spinelli
correct IsBorder
Revision 1.1 2005/02/22 16:40:29 ganovelli
created. This version writes the gaussian curvature on the Q() member of
the vertex
****************************************************************************/
#ifndef VCGLIB_UPDATE_CURVATURE_
#define VCGLIB_UPDATE_CURVATURE_
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/math/base.h>
#include <vcg/math/matrix.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/simplex/face/pos.h>
#include <vcg/simplex/face/jumping_pos.h>
#include <vcg/container/simple_temporary_data.h>
#include <vcg/complex/trimesh/update/normal.h>
#include <vcg/complex/trimesh/point_sampling.h>
#include <vcg/complex/trimesh/append.h>
#include <vcg/complex/intersection.h>
#include <vcg/complex/trimesh/inertia.h>
#include <vcg/math/matrix33.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile curvature.h vcg/complex/trimesh/update/curvature.h
/// \brief Management, updating and computation of per-vertex and per-face normals.
/**
This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh.
*/
template <class MeshType>
class UpdateCurvature
{
public:
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::VertContainer VertContainer;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef vcg::face::VFIterator<FaceType> VFIteratorType;
typedef typename MeshType::CoordType CoordType;
typedef typename CoordType::ScalarType ScalarType;
private:
struct AdjVertex {
VertexType * vert;
float doubleArea;
bool isBorder;
};
public:
/// \brief Compute principal direction and magniuto of curvature.
/*
Compute principal direction and magniuto of curvature as describe in the paper:
@InProceedings{bb33922,
author = "G. Taubin",
title = "Estimating the Tensor of Curvature of a Surface from a
Polyhedral Approximation",
booktitle = "International Conference on Computer Vision",
year = "1995",
pages = "902--907",
URL = "http://dx.doi.org/10.1109/ICCV.1995.466840",
bibsource = "http://www.visionbib.com/bibliography/describe440.html#TT32253",
*/
static void PrincipalDirections(MeshType &m) {
assert(m.HasVFTopology());
vcg::tri::UpdateNormals<MeshType>::PerVertexNormalized(m);
VertexIterator vi;
for (vi =m.vert.begin(); vi !=m.vert.end(); ++vi) {
if ( ! (*vi).IsD() && (*vi).VFp() != NULL) {
VertexType * central_vertex = &(*vi);
std::vector<float> weights;
std::vector<AdjVertex> vertices;
vcg::face::JumpingPos<FaceType> pos((*vi).VFp(), central_vertex);
// firstV is the first vertex of the 1ring neighboorhood
VertexType* firstV = pos.VFlip();
VertexType* tempV;
float totalDoubleAreaSize = 0.0f;
// compute the area of each triangle around the central vertex as well as their total area
do
{
// this bring the pos to the next triangle counterclock-wise
pos.FlipF();
pos.FlipE();
// tempV takes the next vertex in the 1ring neighborhood
tempV = pos.VFlip();
assert(tempV!=central_vertex);
AdjVertex v;
v.isBorder = pos.IsBorder();
v.vert = tempV;
v.doubleArea = vcg::DoubleArea(*pos.F());
totalDoubleAreaSize += v.doubleArea;
vertices.push_back(v);
}
while(tempV != firstV);
// compute the weights for the formula computing matrix M
for (size_t i = 0; i < vertices.size(); ++i) {
if (vertices[i].isBorder) {
weights.push_back(vertices[i].doubleArea / totalDoubleAreaSize);
} else {
weights.push_back(0.5f * (vertices[i].doubleArea + vertices[(i-1)%vertices.size()].doubleArea) / totalDoubleAreaSize);
}
assert(weights.back() < 1.0f);
}
// compute I-NN^t to be used for computing the T_i's
Matrix33<ScalarType> Tp;
for (int i = 0; i < 3; ++i)
Tp[i][i] = 1.0f - powf(central_vertex->cN()[i],2);
Tp[0][1] = Tp[1][0] = -1.0f * (central_vertex->N()[0] * central_vertex->cN()[1]);
Tp[1][2] = Tp[2][1] = -1.0f * (central_vertex->cN()[1] * central_vertex->cN()[2]);
Tp[0][2] = Tp[2][0] = -1.0f * (central_vertex->cN()[0] * central_vertex->cN()[2]);
// for all neighbors vi compute the directional curvatures k_i and the T_i
// compute M by summing all w_i k_i T_i T_i^t
Matrix33<ScalarType> tempMatrix;
Matrix33<ScalarType> M;
M.SetZero();
for (size_t i = 0; i < vertices.size(); ++i) {
CoordType edge = (central_vertex->cP() - vertices[i].vert->cP());
float curvature = (2.0f * (central_vertex->cN().dot(edge)) ) / edge.SquaredNorm();
CoordType T = (Tp*edge).normalized();
tempMatrix.ExternalProduct(T,T);
M += tempMatrix * weights[i] * curvature ;
}
// compute vector W for the Householder matrix
CoordType W;
CoordType e1(1.0f,0.0f,0.0f);
if ((e1 - central_vertex->cN()).SquaredNorm() > (e1 + central_vertex->cN()).SquaredNorm())
W = e1 - central_vertex->cN();
else
W = e1 + central_vertex->cN();
W.Normalize();
// compute the Householder matrix I - 2WW^t
Matrix33<ScalarType> Q;
Q.SetIdentity();
tempMatrix.ExternalProduct(W,W);
Q -= tempMatrix * 2.0f;
// compute matrix Q^t M Q
Matrix33<ScalarType> QtMQ = (Q.transpose() * M * Q);
CoordType bl = Q.GetColumn(0);
CoordType T1 = Q.GetColumn(1);
CoordType T2 = Q.GetColumn(2);
// find sin and cos for the Givens rotation
float s,c;
// Gabriel Taubin hint and Valentino Fiorin impementation
float alpha = QtMQ[1][1]-QtMQ[2][2];
float beta = QtMQ[2][1];
float h[2];
float delta = sqrtf(4.0f*powf(alpha, 2) +16.0f*powf(beta, 2));
h[0] = (2.0f*alpha + delta) / (2.0f*beta);
h[1] = (2.0f*alpha - delta) / (2.0f*beta);
float t[2];
float best_c, best_s;
float min_error = std::numeric_limits<ScalarType>::infinity();
for (int i=0; i<2; i++)
{
delta = sqrtf(powf(h[i], 2) + 4.0f);
t[0] = (h[i]+delta) / 2.0f;
t[1] = (h[i]-delta) / 2.0f;
for (int j=0; j<2; j++)
{
float squared_t = powf(t[j], 2);
float denominator = 1.0f + squared_t;
s = (2.0f*t[j]) / denominator;
c = (1-squared_t) / denominator;
float approximation = c*s*alpha + (powf(c, 2) - powf(s, 2))*beta;
float angle_similarity = fabs(acosf(c)/asinf(s));
float error = fabs(1.0f-angle_similarity)+fabs(approximation);
if (error<min_error)
{
min_error = error;
best_c = c;
best_s = s;
}
}
}
c = best_c;
s = best_s;
vcg::ndim::MatrixMNf minor2x2 (2,2);
vcg::ndim::MatrixMNf S (2,2);
// diagonalize M
minor2x2[0][0] = QtMQ[1][1];
minor2x2[0][1] = QtMQ[1][2];
minor2x2[1][0] = QtMQ[2][1];
minor2x2[1][1] = QtMQ[2][2];
S[0][0] = S[1][1] = c;
S[0][1] = s;
S[1][0] = -1.0f * s;
vcg::ndim::MatrixMNf StMS(S.transpose() * minor2x2 * S);
// compute curvatures and curvature directions
float Principal_Curvature1 = (3.0f * StMS[0][0]) - StMS[1][1];
float Principal_Curvature2 = (3.0f * StMS[1][1]) - StMS[0][0];
CoordType Principal_Direction1 = T1 * c - T2 * s;
CoordType Principal_Direction2 = T1 * s + T2 * c;
(*vi).PD1() = Principal_Direction1;
(*vi).PD2() = Principal_Direction2;
(*vi).K1() = Principal_Curvature1;
(*vi).K2() = Principal_Curvature2;
}
}
}
class AreaData
{
public:
float A;
};
/** Curvature meseaure as described in the paper:
Robust principal curvatures on Multiple Scales, Yong-Liang Yang, Yu-Kun Lai, Shi-Min Hu Helmut Pottmann
SGP 2004
If pointVSfaceInt==true the covariance is computed by montecarlo sampling on the mesh (faster)
If pointVSfaceInt==false the covariance is computed by (analytic)integration over the surface (slower)
*/
typedef vcg::GridStaticPtr <FaceType, ScalarType > MeshGridType;
typedef vcg::GridStaticPtr <VertexType, ScalarType > PointsGridType;
static void PrincipalDirectionsPCA(MeshType &m, ScalarType r, bool pointVSfaceInt = true,vcg::CallBackPos * cb = NULL) {
std::vector<VertexType*> closests;
std::vector<ScalarType> distances;
std::vector<CoordType> points;
VertexIterator vi;
ScalarType area;
MeshType tmpM;
typename std::vector<CoordType>::iterator ii;
vcg::tri::TrivialSampler<MeshType> vs;
MeshGridType mGrid;
PointsGridType pGrid;
// Fill the grid used
if(pointVSfaceInt){
area = Stat<MeshType>::ComputeMeshArea(m);
vcg::tri::SurfaceSampling<MeshType,vcg::tri::TrivialSampler<MeshType> >::Montecarlo(m,vs,1000 * area / (2*M_PI*r*r ));
vi = vcg::tri::Allocator<MeshType>::AddVertices(tmpM,m.vert.size());
for(size_t y = 0; y < m.vert.size(); ++y,++vi) (*vi).P() = m.vert[y].P();
pGrid.Set(tmpM.vert.begin(),tmpM.vert.end());
} else{ mGrid.Set(m.face.begin(),m.face.end()); }
int jj = 0;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi){
vcg::Matrix33<ScalarType> A,eigenvectors;
vcg::Point3<ScalarType> bp,eigenvalues;
int nrot;
// sample the neighborhood
if(pointVSfaceInt)
{
vcg::tri::GetInSphereVertex<
MeshType,
PointsGridType,std::vector<VertexType*>,
std::vector<ScalarType>,
std::vector<CoordType> >(tmpM,pGrid, (*vi).cP(),r ,closests,distances,points);
A.Covariance(points,bp);
A*=area*area/1000;
}
else{
IntersectionBallMesh<MeshType,ScalarType>( m ,vcg::Sphere3<ScalarType>((*vi).cP(),r),tmpM );
vcg::Point3<ScalarType> _bary;
vcg::tri::Inertia<MeshType>::Covariance(tmpM,_bary,A);
}
Jacobi(A, eigenvalues , eigenvectors, nrot);
// get the estimate of curvatures from eigenvalues and eigenvectors
// find the 2 most tangent eigenvectors (by finding the one closest to the normal)
int best = 0; ScalarType bestv = fabs( (*vi).cN().dot(eigenvectors.GetColumn(0).normalized()) );
for(int i = 1 ; i < 3; ++i){
ScalarType prod = fabs((*vi).cN().dot(eigenvectors.GetColumn(i).normalized()));
if( prod > bestv){bestv = prod; best = i;}
}
(*vi).PD1() = eigenvectors.GetColumn( (best+1)%3).normalized();
(*vi).PD2() = eigenvectors.GetColumn( (best+2)%3).normalized();
// project them to the plane identified by the normal
vcg::Matrix33<ScalarType> rot;
ScalarType angle = acos((*vi).PD1().dot((*vi).N()));
rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD1()^(*vi).N());
(*vi).PD1() = rot*(*vi).PD1();
angle = acos((*vi).PD2().dot((*vi).N()));
rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD2()^(*vi).N());
(*vi).PD2() = rot*(*vi).PD2();
// copmutes the curvature values
const ScalarType r5 = r*r*r*r*r;
const ScalarType r6 = r*r5;
(*vi).K1() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+2)%3]-45.0*eigenvalues[(best+1)%3])/(M_PI*r6);
(*vi).K2() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+1)%3]-45.0*eigenvalues[(best+2)%3])/(M_PI*r6);
if((*vi).K1() < (*vi).K2()) { std::swap((*vi).K1(),(*vi).K2());
std::swap((*vi).PD1(),(*vi).PD2());
if (cb)
{
(*cb)(int(100.0f * (float)jj / (float)m.vn),"Vertices Analysis");
++jj;
} }
}
}
/// \brief Computes the discrete gaussian curvature.
/** For further details, please, refer to: \n
- <em> Discrete Differential-Geometry Operators for Triangulated 2-Manifolds Mark Meyer,
Mathieu Desbrun, Peter Schroder, Alan H. Barr VisMath '02, Berlin </em>
*/
static void MeanAndGaussian(MeshType & m)
{
assert(HasFFAdjacency(m));
float area0, area1, area2, angle0, angle1, angle2;
FaceIterator fi;
VertexIterator vi;
typename MeshType::CoordType e01v ,e12v ,e20v;
SimpleTempData<VertContainer, AreaData> TDAreaPtr(m.vert);
SimpleTempData<VertContainer, typename MeshType::CoordType> TDContr(m.vert);
vcg::tri::UpdateNormals<MeshType>::PerVertexNormalized(m);
//Compute AreaMix in H (vale anche per K)
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD())
{
(TDAreaPtr)[*vi].A = 0.0;
(TDContr)[*vi] =typename MeshType::CoordType(0.0,0.0,0.0);
(*vi).Kh() = 0.0;
(*vi).Kg() = (float)(2.0 * M_PI);
}
for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD())
{
// angles
angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
angle2 = M_PI-(angle0+angle1);
if((angle0 < M_PI/2) && (angle1 < M_PI/2) && (angle2 < M_PI/2)) // triangolo non ottuso
{
float e01 = SquaredDistance( (*fi).V(1)->cP() , (*fi).V(0)->cP() );
float e12 = SquaredDistance( (*fi).V(2)->cP() , (*fi).V(1)->cP() );
float e20 = SquaredDistance( (*fi).V(0)->cP() , (*fi).V(2)->cP() );
area0 = ( e20*(1.0/tan(angle1)) + e01*(1.0/tan(angle2)) ) / 8.0;
area1 = ( e01*(1.0/tan(angle2)) + e12*(1.0/tan(angle0)) ) / 8.0;
area2 = ( e12*(1.0/tan(angle0)) + e20*(1.0/tan(angle1)) ) / 8.0;
(TDAreaPtr)[(*fi).V(0)].A += area0;
(TDAreaPtr)[(*fi).V(1)].A += area1;
(TDAreaPtr)[(*fi).V(2)].A += area2;
}
else // obtuse
{
if(angle0 >= M_PI/2)
{
(TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
(TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
(TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
}
else if(angle1 >= M_PI/2)
{
(TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
(TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
(TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
}
else
{
(TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
(TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
(TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
}
}
}
for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD() )
{
angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
angle2 = M_PI-(angle0+angle1);
// Skip degenerate triangles.
if(angle0==0 || angle1==0 || angle1==0) continue;
e01v = ( (*fi).V(1)->cP() - (*fi).V(0)->cP() ) ;
e12v = ( (*fi).V(2)->cP() - (*fi).V(1)->cP() ) ;
e20v = ( (*fi).V(0)->cP() - (*fi).V(2)->cP() ) ;
TDContr[(*fi).V(0)] += ( e20v * (1.0/tan(angle1)) - e01v * (1.0/tan(angle2)) ) / 4.0;
TDContr[(*fi).V(1)] += ( e01v * (1.0/tan(angle2)) - e12v * (1.0/tan(angle0)) ) / 4.0;
TDContr[(*fi).V(2)] += ( e12v * (1.0/tan(angle0)) - e20v * (1.0/tan(angle1)) ) / 4.0;
(*fi).V(0)->Kg() -= angle0;
(*fi).V(1)->Kg() -= angle1;
(*fi).V(2)->Kg() -= angle2;
for(int i=0;i<3;i++)
{
if(vcg::face::IsBorder((*fi), i))
{
CoordType e1,e2;
vcg::face::Pos<FaceType> hp(&*fi, i, (*fi).V(i));
vcg::face::Pos<FaceType> hp1=hp;
hp1.FlipV();
e1=hp1.v->cP() - hp.v->cP();
hp1.FlipV();
hp1.NextB();
e2=hp1.v->cP() - hp.v->cP();
(*fi).V(i)->Kg() -= math::Abs(Angle(e1,e2));
}
}
}
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/)
{
if((TDAreaPtr)[*vi].A<=std::numeric_limits<ScalarType>::epsilon())
{
(*vi).Kh() = 0;
(*vi).Kg() = 0;
}
else
{
(*vi).Kh() = (((TDContr)[*vi].dot((*vi).cN())>0)?1.0:-1.0)*((TDContr)[*vi] / (TDAreaPtr) [*vi].A).Norm();
(*vi).Kg() /= (TDAreaPtr)[*vi].A;
}
}
}
/// \brief Update the mean and the gaussian curvature of a vertex.
/**
The function uses the VF adiacency to walk around the vertex.
\return It will return the voronoi area around the vertex. If (norm == true) the mean and the gaussian curvature are normalized.
Based on the paper <a href="http://www2.in.tu-clausthal.de/~hormann/papers/Dyn.2001.OTU.pdf"> <em> "Optimizing 3d triangulations using discrete curvature analysis" </em> </a>
*/
static float VertexCurvature(VertexPointer v, bool norm = true)
{
// VFAdjacency required!
assert(FaceType::HasVFAdjacency());
assert(VertexType::HasVFAdjacency());
VFIteratorType vfi(v);
float A = 0;
v->Kh() = 0;
v->Kg() = 2 * M_PI;
while (!vfi.End()) {
if (!vfi.F()->IsD()) {
FacePointer f = vfi.F();
int i = vfi.I();
VertexPointer v0 = f->V0(i), v1 = f->V1(i), v2 = f->V2(i);
float ang0 = math::Abs(Angle(v1->P() - v0->P(), v2->P() - v0->P() ));
float ang1 = math::Abs(Angle(v0->P() - v1->P(), v2->P() - v1->P() ));
float ang2 = M_PI - ang0 - ang1;
float s01 = SquaredDistance(v1->P(), v0->P());
float s02 = SquaredDistance(v2->P(), v0->P());
// voronoi cell of current vertex
if (ang0 >= M_PI/2)
A += (0.5f * DoubleArea(*f) - (s01 * tan(ang1) + s02 * tan(ang2)) / 8.0 );
else if (ang1 >= M_PI/2)
A += (s01 * tan(ang0)) / 8.0;
else if (ang2 >= M_PI/2)
A += (s02 * tan(ang0)) / 8.0;
else // non obctuse triangle
A += ((s02 / tan(ang1)) + (s01 / tan(ang2))) / 8.0;
// gaussian curvature update
v->Kg() -= ang0;
// mean curvature update
ang1 = math::Abs(Angle(f->N(), v1->N()));
ang2 = math::Abs(Angle(f->N(), v2->N()));
v->Kh() += ( (math::Sqrt(s01) / 2.0) * ang1 +
(math::Sqrt(s02) / 2.0) * ang2 );
}
++vfi;
}
v->Kh() /= 4.0f;
if(norm) {
if(A <= std::numeric_limits<float>::epsilon()) {
v->Kh() = 0;
v->Kg() = 0;
}
else {
v->Kh() /= A;
v->Kg() /= A;
}
}
return A;
}
static void VertexCurvature(MeshType & m){
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
VertexCurvature(&*vi,false);
}
/*
Compute principal curvature directions and value with normal cycle:
@inproceedings{CohMor03,
author = {Cohen-Steiner, David and Morvan, Jean-Marie },
booktitle = {SCG '03: Proceedings of the nineteenth annual symposium on Computational geometry},
title - {Restricted delaunay triangulations and normal cycle}
year = {2003}
}
*/
static void PrincipalDirectionsNormalCycles(MeshType & m){
assert(VertexType::HasVFAdjacency());
assert(FaceType::HasFFAdjacency());
assert(FaceType::HasFaceNormal());
typename MeshType::VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if(!((*vi).IsD())){
vcg::Matrix33<ScalarType> m33;m33.SetZero();
face::JumpingPos<typename MeshType::FaceType> p((*vi).VFp(),&(*vi));
p.FlipE();
typename MeshType::VertexType * firstv = p.VFlip();
assert(p.F()->V(p.VInd())==&(*vi));
do{
if( p.F() != p.FFlip()){
Point3<ScalarType> normalized_edge = p.F()->V(p.F()->Next(p.VInd()))->cP() - (*vi).P();
ScalarType edge_length = normalized_edge.Norm();
normalized_edge/=edge_length;
Point3<ScalarType> n1 = p.F()->cN();n1.Normalize();
Point3<ScalarType> n2 = p.FFlip()->cN();n2.Normalize();
ScalarType n1n2 = (n1 ^ n2).dot(normalized_edge);
n1n2 = std::max(std::min( ScalarType(1.0),n1n2),ScalarType(-1.0));
ScalarType beta = math::Asin(n1n2);
m33[0][0] += beta*edge_length*normalized_edge[0]*normalized_edge[0];
m33[0][1] += beta*edge_length*normalized_edge[1]*normalized_edge[0];
m33[1][1] += beta*edge_length*normalized_edge[1]*normalized_edge[1];
m33[0][2] += beta*edge_length*normalized_edge[2]*normalized_edge[0];
m33[1][2] += beta*edge_length*normalized_edge[2]*normalized_edge[1];
m33[2][2] += beta*edge_length*normalized_edge[2]*normalized_edge[2];
}
p.NextFE();
}while(firstv != p.VFlip());
if(m33.Determinant()==0.0){ // degenerate case
(*vi).K1() = (*vi).K2() = 0.0; continue;}
m33[1][0] = m33[0][1];
m33[2][0] = m33[0][2];
m33[2][1] = m33[1][2];
Point3<ScalarType> lambda;
Matrix33<ScalarType> vect;
int n_rot;
Jacobi(m33,lambda, vect,n_rot);
vect.transposeInPlace();
ScalarType normal = std::numeric_limits<ScalarType>::min();
int normI = 0;
for(int i = 0; i < 3; ++i)
if( fabs((*vi).N().Normalize().dot(vect.GetRow(i))) > normal )
{
normal= fabs((*vi).N().Normalize().dot(vect.GetRow(i)));
normI = i;
}
int maxI = (normI+2)%3;
int minI = (normI+1)%3;
if(fabs(lambda[maxI]) < fabs(lambda[minI])) std::swap(maxI,minI);
(*vi).PD1() = *(Point3<ScalarType>*)(& vect[maxI][0]);
(*vi).PD2() = *(Point3<ScalarType>*)(& vect[minI][0]);
(*vi).K1() = lambda[maxI];
(*vi).K2() = lambda[minI];
}
}
};
}
}
#endif

View File

@ -0,0 +1,707 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
****************************************************************************/
#ifndef VCGLIB_UPDATE_CURVATURE_FITTING
#define VCGLIB_UPDATE_CURVATURE_FITTING
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/math/base.h>
#include <vcg/math/matrix.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/simplex/face/pos.h>
#include <vcg/simplex/face/jumping_pos.h>
#include <vcg/container/simple_temporary_data.h>
#include <vcg/complex/trimesh/update/normal.h>
#include <vcg/complex/trimesh/point_sampling.h>
#include <vcg/complex/trimesh/append.h>
#include <vcg/complex/intersection.h>
#include <vcg/complex/trimesh/inertia.h>
#include <vcg/math/matrix33.h>
#include <vcg/Eigen/Core>
#include <vcg/Eigen/QR>
#include <vcg/Eigen/LU>
#include <vcg/Eigen/SVD>
// GG include
#include <vector>
#include <vcg/complex/trimesh/nring.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile curvature_fitting.h vcg/complex/trimesh/update/curvature_fitting.h
/// \brief Computation of per-vertex directions and values of curvature.
/**
This class is used to compute the per-vertex directions and values of curvature using a quadric fitting method.
*/
template <class MeshType>
class UpdateCurvatureFitting
{
public:
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::VertContainer VertContainer;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexPointer VertexTypeP;
typedef vcg::face::VFIterator<FaceType> VFIteratorType;
typedef typename MeshType::CoordType CoordType;
typedef typename CoordType::ScalarType ScalarType;
class Quadric
{
public:
Quadric(double av, double bv, double cv, double dv, double ev)
{
a() = av;
b() = bv;
c() = cv;
d() = dv;
e() = ev;
}
double& a() { return data[0];}
double& b() { return data[1];}
double& c() { return data[2];}
double& d() { return data[3];}
double& e() { return data[4];}
double data[5];
double evaluate(double u, double v)
{
return a*u*u + b*u*v + c*v*v + d*u + e*v;
}
double du(double u, double v)
{
return 2.0*a()*u + b()*v + d();
}
double dv(double u, double v)
{
return 2.0*c()*v + b()*u + e();
}
double duv(double u, double v)
{
return b();
}
double duu(double u, double v)
{
return 2.0*a();
}
double dvv(double u, double v)
{
return 2.0*c();
}
static Quadric fit(std::vector<CoordType> VV)
{
assert(VV.size() >= 5);
Eigen::MatrixXf A(VV.size(),5);
Eigen::MatrixXf b(VV.size(),1);
Eigen::MatrixXf sol(VV.size(),1);
for(unsigned int c=0; c < VV.size(); ++c)
{
double u = VV[c].X();
double v = VV[c].Y();
double n = VV[c].Z();
A(c,0) = u*u;
A(c,1) = u*v;
A(c,2) = v*v;
A(c,3) = u;
A(c,4) = v;
b[c] = n;
}
sol = ((A.transpose()*A).inverse()*A.transpose())*b;
return Quadric(sol[0],sol[1],sol[2],sol[3],sol[4]);
}
};
static CoordType project(VertexType* v, VertexType* vp)
{
return vp->P() - (v->N() * ((vp->P() - v->P()) * v->N()));
}
static std::vector<CoordType> computeReferenceFrames(VertexTypeP vi)
{
vcg::face::VFIterator<FaceType> vfi(vi);
int i = (vfi.I()+1)%3;
VertexTypeP vp = vfi.F()->V(i);
CoordType x = (project(&*vi,vp) - vi->P()).Normalize();
assert(fabs(x * vi->N()) < 0.1);
std::vector<CoordType> res(3);
res[0] = x;
res[1] = (vi->N() ^ res[0]).Normalize();
res[2] = (vi->N())/(vi->N()).Norm();
return res;
}
static std::set<CoordType> getSecondRing(VertexTypeP v)
{
std::set<VertexTypeP> ris;
std::set<CoordType> coords;
vcg::face::VFIterator<FaceType> vvi(v);
for(;!vvi.End();++vvi)
{
vcg::face::VFIterator<FaceType> vvi2(vvi.F()->V((vvi.I()+1)%3));
for(;!vvi2.End();++vvi2)
{
ris.insert(vvi2.F()->V((vvi2.I()+1)%3));
}
}
typename std::set<VertexTypeP>::iterator it;
for(it = ris.begin(); it != ris.end(); ++it)
coords.insert((*it)->P());
return coords;
}
static Quadric fitQuadric(VertexTypeP v, std::vector<CoordType>& ref)
{
std::set<CoordType> ring = getSecondRing(v);
if (ring.size() < 5)
return Quadric(1,1,1,1,1);
std::vector<CoordType> points;
typename std::set<CoordType>::iterator b = ring.begin();
typename std::set<CoordType>::iterator e = ring.end();
while(b != e)
{
// vtang non e` il v tangente!!!
CoordType vTang = *b - v->P();
double x = vTang * ref[0];
double y = vTang * ref[1];
double z = vTang * ref[2];
points.push_back(CoordType(x,y,z));
++b;
}
return Quadric::fit(points);
}
static void computeCurvature(MeshType & m)
{
assert(tri::HasPerVertexVFAdjacency(m) && tri::HasPerFaceVFAdjacency(m) );
vcg::tri::UpdateTopology<MeshType>::VertexFace(m);
vcg::tri::UpdateNormals<MeshType>::PerVertexAngleWeighted(m);
vcg::tri::UpdateNormals<MeshType>::NormalizeVertex(m);
VertexIterator vi;
for(vi = m.vert.begin(); vi!=m.vert.end(); ++vi )
{
std::vector<CoordType> ref = computeReferenceFrames(&*vi);
Quadric q = fitQuadric(&*vi,ref);
double a = q.a();
double b = q.b();
double c = q.c();
double d = q.d();
double e = q.e();
double E = 1.0 + d*d;
double F = d*e;
double G = 1.0 + e*e;
CoordType n = CoordType(-d,-e,1.0).Normalize();
vi->N() = ref[0] * n[0] + ref[1] * n[1] + ref[2] * n[2];
double L = 2.0 * a * n.Z();
double M = b * n.Z();
double N = 2 * c * n.Z();
// ----------------- Eigen stuff
Eigen::Matrix2d m;
m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F;
m = m / (E*G-F*F);
Eigen::SelfAdjointEigenSolver<Eigen::Matrix2d> eig(m);
Eigen::Vector2d c_val = eig.eigenvalues();
Eigen::Matrix2d c_vec = eig.eigenvectors();
c_val = -c_val;
CoordType v1, v2;
v1[0] = c_vec[0];
v1[1] = c_vec[1];
v1[2] = 0;
v2[0] = c_vec[2];
v2[1] = c_vec[3];
v2[2] = 0;
v1 = v1.Normalize();
v2 = v2.Normalize();
v1 = v1 * c_val[0];
v2 = v2 * c_val[1];
CoordType v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2];
CoordType v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2];
v1global.Normalize();
v2global.Normalize();
if (c_val[0] > c_val[1])
{
(*vi).PD1() = v1global;
(*vi).PD2() = v2global;
(*vi).K1() = c_val[0];
(*vi).K2() = c_val[1];
}
else
{
(*vi).PD1() = v2global;
(*vi).PD2() = v1global;
(*vi).K1() = c_val[1];
(*vi).K2() = c_val[0];
}
// ---- end Eigen stuff
}
}
// GG LOCAL CURVATURE
class QuadricLocal
{
public:
QuadricLocal ()
{
a() = b() = c() = d() = e() = 1.0;
}
QuadricLocal (double av, double bv, double cv, double dv, double ev)
{
a() = av;
b() = bv;
c() = cv;
d() = dv;
e() = ev;
}
double& a() { return data[0];}
double& b() { return data[1];}
double& c() { return data[2];}
double& d() { return data[3];}
double& e() { return data[4];}
double data[5];
double evaluate(double u, double v)
{
return a()*u*u + b()*u*v + c()*v*v + d()*u + e()*v;
}
double du(double u, double v)
{
return 2.0*a()*u + b()*v + d();
}
double dv(double u, double v)
{
return 2.0*c()*v + b()*u + e();
}
double duv(double u, double v)
{
return b();
}
double duu(double u, double v)
{
return 2.0*a();
}
double dvv(double u, double v)
{
return 2.0*c();
}
static QuadricLocal fit(std::vector<CoordType> &VV, bool svdRes, bool detCheck)
{
assert(VV.size() >= 5);
Eigen::MatrixXd A(VV.size(),5);
Eigen::MatrixXd b(VV.size(),1);
Eigen::MatrixXd sol(5,1);
for(unsigned int c=0; c < VV.size(); ++c)
{
double u = VV[c].X();
double v = VV[c].Y();
double n = VV[c].Z();
A(c,0) = u*u;
A(c,1) = u*v;
A(c,2) = v*v;
A(c,3) = u;
A(c,4) = v;
b[c] = n;
}
static int count = 0, index = 0;
double min = 0.000000000001; //1.0e-12
/*
if (!count)
printf("GNE %e\n", min);
*/
if (detCheck && ((A.transpose()*A).determinant() < min && (A.transpose()*A).determinant() > -min))
{
//A.svd().solve(b, &sol); A.svd().solve(b, &sol);
//cout << sol << endl;
printf("Quadric: unsolvable vertex %d %d\n", count, ++index);
//return Quadric (1, 1, 1, 1, 1);
A.svd().solve(b, &sol);
return QuadricLocal(sol[0],sol[1],sol[2],sol[3],sol[4]);
}
count++;
//for (int i = 0; i < 100; i++)
{
if (svdRes)
A.svd().solve(b, &sol);
else
sol = ((A.transpose()*A).inverse()*A.transpose())*b;
}
return QuadricLocal(sol[0],sol[1],sol[2],sol[3],sol[4]);
}
};
static void expandMaxLocal (MeshType & mesh, VertexType *v, int max, std::vector<VertexType*> *vv)
{
Nring<MeshType> rw = Nring<MeshType> (v, &mesh);
do rw.expand (); while (rw.allV.size() < max+1);
if (rw.allV[0] != v)
printf ("rw.allV[0] != *v\n");
vv->reserve ((size_t)max);
for (int i = 1; i < max+1; i++)
vv->push_back(rw.allV[i]);
rw.clear();
}
static void expandSphereLocal (MeshType & mesh, VertexType *v, float r, int min, std::vector<VertexType*> *vv)
{
Nring<MeshType> rw = Nring<MeshType> (v, &mesh);
bool isInside = true;
while (isInside)
{
rw.expand();
vv->reserve(rw.allV.size());
typename std::vector<VertexType*>::iterator b = rw.lastV.begin();
typename std::vector<VertexType*>::iterator e = rw.lastV.end();
isInside = false;
while(b != e)
{
if (((*b)->P() - v->P()).Norm() < r)
{
vv->push_back(*b);;
isInside = true;
}
++b;
}
}
//printf ("%d\n", vv->size());
rw.clear();
if (vv->size() < min)
{
vv->clear();
expandMaxLocal (mesh, v, min, vv);
}
}
static void getAverageNormal (VertexType *vp, std::vector<VertexType*> &vv, CoordType *ppn)
{
*ppn = CoordType (0,0,0);
for (typename std::vector<VertexType*>::iterator vpi = vv.begin(); vpi != vv.end(); ++vpi)
*ppn += (*vpi)->N();
*ppn += (*vp).N();
*ppn /= vv.size() + 1;
ppn->Normalize();
}
static void applyProjOnPlane (CoordType ppn, std::vector<VertexType*> &vin, std::vector<VertexType*> *vout)
{
for (typename std::vector<VertexType*>::iterator vpi = vin.begin(); vpi != vin.end(); ++vpi)
if ((*vpi)->N() * ppn > 0.0f)
vout->push_back (*vpi);
}
static CoordType projectLocal(VertexType* v, VertexType* vp, CoordType ppn)
{
return vp->P() - (ppn * ((vp->P() - v->P()) * ppn));
}
static void computeReferenceFramesLocal (VertexType *v, CoordType ppn, std::vector<CoordType> *ref)
{
vcg::face::VFIterator<FaceType> vfi (v);
int i = (vfi.I() + 1) % 3;
VertexTypeP vp = vfi.F()->V(i);
CoordType x = (projectLocal (v, vp, ppn) - v->P()).Normalize();
assert(fabs(x * ppn) < 0.1);
*ref = std::vector<CoordType>(3);
(*ref)[0] = x;
(*ref)[1] = (ppn ^ (*ref)[0]).Normalize();
(*ref)[2] = ppn.Normalize(); //ppn / ppn.Norm();
}
static void fitQuadricLocal (VertexType *v, std::vector<CoordType> ref, std::vector<VertexType*> &vv, QuadricLocal *q)
{
bool svdResolution = false;
bool zeroDeterminantCheck = false;
std::vector<CoordType> points;
points.reserve (vv.size());
typename std::vector<VertexType*>::iterator b = vv.begin();
typename std::vector<VertexType*>::iterator e = vv.end();
while(b != e)
{
CoordType cp = (*b)->P();
// vtang non e` il v tangente!!!
CoordType vTang = cp - v->P();
double x = vTang * ref[0];
double y = vTang * ref[1];
double z = vTang * ref[2];
points.push_back(CoordType(x,y,z));
++b;
}
*q = QuadricLocal::fit (points, svdResolution, zeroDeterminantCheck);
}
static void finalEigenStuff (VertexType *v, std::vector<CoordType> ref, QuadricLocal q)
{
double a = q.a();
double b = q.b();
double c = q.c();
double d = q.d();
double e = q.e();
double E = 1.0 + d*d;
double F = d*e;
double G = 1.0 + e*e;
CoordType n = CoordType(-d,-e,1.0).Normalize();
v->N() = ref[0] * n[0] + ref[1] * n[1] + ref[2] * n[2];
double L = 2.0 * a * n.Z();
double M = b * n.Z();
double N = 2 * c * n.Z();
// ----------------- Eigen stuff
Eigen::Matrix2d m;
m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F;
m = m / (E*G-F*F);
Eigen::SelfAdjointEigenSolver<Eigen::Matrix2d> eig(m);
Eigen::Vector2d c_val = eig.eigenvalues();
Eigen::Matrix2d c_vec = eig.eigenvectors();
c_val = -c_val;
CoordType v1, v2;
v1[0] = c_vec[0];
v1[1] = c_vec[1];
v1[2] = d * v1[0] + e * v1[1];
v2[0] = c_vec[2];
v2[1] = c_vec[3];
v2[2] = d * v2[0] + e * v2[1];
v1 = v1.Normalize();
v2 = v2.Normalize();
CoordType v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2];
CoordType v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2];
v1global.Normalize();
v2global.Normalize();
v1global *= c_val[0];
v2global *= c_val[1];
if (c_val[0] > c_val[1])
{
(*v).PD1() = v1global;
(*v).PD2() = v2global;
(*v).K1() = c_val[0];
(*v).K2() = c_val[1];
}
else
{
(*v).PD1() = v2global;
(*v).PD2() = v1global;
(*v).K1() = c_val[1];
(*v).K2() = c_val[0];
}
// ---- end Eigen stuff
}
static void updateCurvatureLocal (MeshType & mesh, float radiusSphere)
{
bool verbose = false;
bool projectionPlaneCheck = true;
int vertexesPerFit = 0;
int i = 0;
VertexIterator vi;
for(vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi, i++)
{
std::vector<VertexType*> vv;
std::vector<VertexType*> vvtmp;
int count;
if (verbose && !((count = (vi - mesh.vert.begin())) % 1000))
printf ("vertex %d of %d\n",count,mesh.vert.size());
// if (kRing != 0)
// expandRing (&*vi, kRing, 5, &vv);
// else
expandSphereLocal (mesh, &*vi, radiusSphere, 5, &vv);
assert (vv.size() >= 5);
CoordType ppn;
// if (averageNormalMode)
// //ppn = (*vi).N();
getAverageNormal (&*vi, vv, &ppn);
// else
// getProjPlaneNormal (&*vi, vv, &ppn);
if (projectionPlaneCheck)
{
vvtmp.reserve (vv.size ());
applyProjOnPlane (ppn, vv, &vvtmp);
if (vvtmp.size() >= 5)
vv = vvtmp;
}
vvtmp.clear();
// if (montecarloMaxVertexNum)
// {
// //printf ("P: %d\n", vv.size());
// vvtmp.reserve (vv.size ());
// //printf ("TP: %d\n", vvtmp.size());
// applyMontecarlo (montecarloMaxVertexNum, vv, &vvtmp);
// //printf ("TD: %d\n", vvtmp.size());
// vv = vvtmp;
// //printf ("D: %d\n", vv.size());
// //printf ("\n");
// }
assert (vv.size() >= 5);
std::vector<CoordType> ref;
computeReferenceFramesLocal (&*vi, ppn, &ref);
/*
printf ("%lf %lf %lf - %lf %lf %lf - %lf %lf %lf\n",
ref[0][0], ref[0][1], ref[0][2],
ref[1][0], ref[1][1], ref[1][2],
ref[2][0], ref[2][1], ref[2][2]);
*/
vertexesPerFit += vv.size();
//printf ("size: %d\n", vv.size());
QuadricLocal q;
fitQuadricLocal (&*vi, ref, vv, &q);
finalEigenStuff (&*vi, ref, q);
}
//if (verbose)
//printf ("average vertex num in each fit: %f, total %d, vn %d\n", ((float) vertexesPerFit) / mesh.vn, vertexesPerFit, mesh.vn);
if (verbose)
printf ("average vertex num in each fit: %f\n", ((float) vertexesPerFit) / mesh.vn);
}
};
}
}
#endif

View File

@ -0,0 +1,109 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.4 2006/05/16 21:36:54 cignoni
Removed unused box function and rewrote initial comment.
Revision 1.3 2006/05/15 13:12:36 pietroni
Updating of edge values id divided into 2 functions ( the first one update only a face...) added also resetting of edges flags.. (see first line of Set function)
Revision 1.2 2004/05/12 18:52:35 ganovelli
removed call to ComputeRT and put its body here
Revision 1.1 2004/05/12 10:39:45 ganovelli
created
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_EDGES
#define __VCG_TRI_UPDATE_EDGES
#include <vcg/space/plane3.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile edges.h vcg/complex/trimesh/update/edges.h
/// \brief This class is used to compute or update the precomputed data used to efficiently compute point-face distances.
template <class ComputeMeshType>
class UpdateEdges
{
public:
typedef ComputeMeshType MeshType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::FaceType::CoordType::ScalarType ScalarType;
static void Set(FaceType &f)
{
f.Flags() = f.Flags() & (~(FaceType::NORMX|FaceType::NORMY|FaceType::NORMZ));
// Primo calcolo degli edges
f.Edge(0) = f.V(1)->P(); f.Edge(0) -= f.V(0)->P();
f.Edge(1) = f.V(2)->P(); f.Edge(1) -= f.V(1)->P();
f.Edge(2) = f.V(0)->P(); f.Edge(2) -= f.V(2)->P();
// Calcolo di plane
f.Plane().SetDirection(f.Edge(0)^f.Edge(1));
f.Plane().SetOffset(f.Plane().Direction().dot(f.V(0)->P()));
f.Plane().Normalize();
// Calcolo migliore proiezione
ScalarType nx = math::Abs(f.Plane().Direction()[0]);
ScalarType ny = math::Abs(f.Plane().Direction()[1]);
ScalarType nz = math::Abs(f.Plane().Direction()[2]);
ScalarType d;
if(nx>ny && nx>nz) { f.Flags() |= FaceType::NORMX; d = 1/f.Plane().Direction()[0]; }
else if(ny>nz) { f.Flags() |= FaceType::NORMY; d = 1/f.Plane().Direction()[1]; }
else { f.Flags() |= FaceType::NORMZ; d = 1/f.Plane().Direction()[2]; }
// Scalatura spigoli
f.Edge(0)*=d;
f.Edge(1)*=d;
f.Edge(2)*=d;
}
static void Set(ComputeMeshType &m)
{
FaceIterator f;
for(f = m.face.begin(); f!=m.face.end(); ++f)
if(!(*f).IsD())
Set(*f);
}
}; // end class
} // End namespace
} // End namespace
#endif

View File

@ -0,0 +1,488 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef FITMAPS_H
#define FITMAPS_H
#include <vcg/math/histogram.h>
#include <vcg/simplex/face/jumping_pos.h>
#include <vcg/complex/trimesh/allocate.h>
#include <vcg/complex/trimesh/update/flag.h>
#include <vector>
#include <set>
#include <vcg/complex/trimesh/update/normal.h>
#include <vcg/complex/trimesh/update/curvature.h>
#include <vcg/complex/trimesh/update/topology.h>
#include <vcg/complex/trimesh/update/bounding.h>
#include "vcg/complex/trimesh/update/curvature_fitting.h"
#include <vcg/Eigen/Core>
#include <vcg/Eigen/QR>
#include <vcg/Eigen/LU>
#include <vcg/Eigen/SVD>
#include <algorithm>
#include <vcg/complex/trimesh/nring.h>
#include <vcg/complex/trimesh/smooth.h>
using namespace Eigen;
namespace vcg { namespace tri {
template<class MeshType>
class Fitmaps
{
public:
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::CoordType CoordType;
typedef vcg::tri::Nring<MeshType> RingWalker;
class Bicubic
{
public:
Bicubic() {};
Bicubic(vector<double>& input)
{
data = input;
if (input.size() != 16)
{
assert(0);
}
}
// (u3 u2 u 1) (v3 v2 v 1)
//
// a u3 v3
// b u3 v2
// c u3 v1
// d u3 1
// e u2 v3
// f u2 v2
// g u2 v1
// h u2 1
// i u1 v3
// l u1 v2
// m u1 v1
// n u1 1
// o 1 v3
// p 1 v2
// q 1 v1
// r 1 1
double& a() { return data[0];}
double& b() { return data[1];}
double& c() { return data[2];}
double& d() { return data[3];}
double& e() { return data[4];}
double& f() { return data[5];}
double& g() { return data[6];}
double& h() { return data[7];}
double& i() { return data[8];}
double& l() { return data[9];}
double& m() { return data[10];}
double& n() { return data[11];}
double& o() { return data[12];}
double& p() { return data[13];}
double& q() { return data[14];}
double& r() { return data[15];}
vector<double> data;
double evaluate(double u, double v)
{
return
a() * u*u*u * v*v*v +
b() * u*u*u * v*v +
c() * u*u*u * v +
d() * u*u*u +
e() * u*u * v*v*v +
f() * u*u * v*v +
g() * u*u * v +
h() * u*u +
i() * u * v*v*v +
l() * u * v*v +
m() * u * v +
n() * u +
o() * v*v*v +
p() * v*v +
q() * v +
r();
}
double distanceRMS(std::vector<CoordType>& VV)
{
double error = 0;
for(typename std::vector<CoordType>::iterator it = VV.begin(); it != VV.end(); ++it)
{
double u = it->X();
double v = it->Y();
double n = it->Z();
double temp = evaluate(u,v);
error += (n - temp)*(n - temp);
}
error /= (double) VV.size();
return sqrt(error);
}
static Bicubic fit(std::vector<CoordType> VV)
{
assert(VV.size() >= 16);
Eigen::MatrixXf A(VV.size(),16);
Eigen::MatrixXf b(VV.size(),1);
Eigen::MatrixXf sol(16,1);
for(unsigned int c=0; c < VV.size(); ++c)
{
double u = VV[c].X();
double v = VV[c].Y();
double n = VV[c].Z();
A(c,0) = u*u*u * v*v*v;
A(c,1) = u*u*u * v*v;
A(c,2) = u*u*u * v;
A(c,3) = u*u*u;
A(c,4) = u*u * v*v*v;
A(c,5) = u*u * v*v;
A(c,6) = u*u * v;
A(c,7) = u*u;
A(c,8) = u * v*v*v;
A(c,9) = u * v*v;
A(c,10) = u * v;
A(c,11) = u;
A(c,12) = v*v*v;
A(c,13) = v*v;
A(c,14) = v;
A(c,15) = 1;
b[c] = n;
}
A.svd().solve(b, &sol);
vector<double> r(16);
for (int i=0; i < 16; ++i)
r.at(i) = sol[i];
return Bicubic(r);
}
};
Fitmaps()
{}
class radSorter
{
public:
radSorter(VertexType* v)
{
origin = v;
}
VertexType* origin;
bool operator() (VertexType* v1, VertexType* v2)
{
return (v1->P() - origin->P()).SquaredNorm() < (v2->P() - origin->P()).SquaredNorm();
}
};
float getMeanCurvature(VertexType* vp)
{
return (vp->K1() + vp->K2())/2.0;
}
static bool fitBicubicPoints(VertexType* v, std::vector<CoordType>& ref, Bicubic& ret, std::vector<CoordType>& points, std::vector<VertexType*>& ring)
{
points.clear();
if (ring.size() < 16)
{
return false;
}
typename std::vector<VertexType*>::iterator b = ring.begin();
typename std::vector<VertexType*>::iterator e = ring.end();
while(b != e)
{
CoordType vT = (*b)->P() - v->P();
double x = vT * ref[0];
double y = vT * ref[1];
double z = vT * ref[2];
points.push_back(CoordType(x,y,z));
++b;
}
ret = Bicubic::fit(points);
return true;
}
static double AverageEdgeLenght(MeshType& m)
{
double doubleA = 0;
for (FaceIterator fi = m.face.begin(); fi!=m.face.end(); fi++) if (!fi->IsD()) {
doubleA+=vcg::DoubleArea(*fi);
}
int nquads = m.fn / 2;
return sqrt( doubleA/(2*nquads) );
}
static void computeMFitmap(MeshType& m, float perc, int ringMax = 50)
{
vcg::tri::UpdateCurvatureFitting<MeshType>::computeCurvature(m);
vcg::tri::UpdateNormals<MeshType>::PerVertexAngleWeighted(m);
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
vcg::tri::UpdateTopology<MeshType>::VertexFace(m);
vcg::tri::UpdateBounding<MeshType>::Box(m);
int countTemp = 0;
RingWalker::clearFlags(&m);
for(VertexIterator it=m.vert.begin(); it!=m.vert.end();++it)
{
if ((countTemp++ % 100) == 0)
cerr << countTemp << "/" << m.vert.size() << endl;
RingWalker rw(&*it,&m);
CoordType nor = it->N();
float okfaces = 0;
float flipfaces = 0;
int count = 0;
do
{
count++;
rw.expand();
for(unsigned i=0; i<rw.lastF.size();++i)
{
CoordType vet1 = nor;
CoordType vet2 = rw.lastF[i]->N();
vet1.Normalize();
vet2.Normalize();
double scal = vet1 * vet2;
if ((scal) > 0)
okfaces += (vcg::DoubleArea(*rw.lastF[i]));
else
flipfaces += (vcg::DoubleArea(*rw.lastF[i]));
}
} while ((((flipfaces)/(okfaces + flipfaces))*100.0 < perc) && (count < ringMax));
std::sort(rw.lastV.begin(),rw.lastV.end(),radSorter(&*it));
it->Q() = ((*rw.lastV.begin())->P() - it->P()).Norm();
rw.clear();
}
vcg::tri::Smooth<MeshType>::VertexQualityLaplacian(m,2);
}
static vector<VertexType*> gatherNeighsSurface(VertexType* vt, float sigma, MeshType& m)
{
vector<VertexType*> current;
RingWalker rw(vt,&m);
bool exit = false;
do
{
rw.expand();
exit = true;
for(typename vector<VertexType*>::iterator it = rw.lastV.begin(); it != rw.lastV.end(); ++it)
{
if (((*it)->P() - vt->P()).Norm() < sigma)
{
current.push_back(*it);
exit = false;
}
}
} while (!exit);
rw.clear();
return current;
}
static void computeSFitmap(MeshType& m)//, float target = 1000)
{
vcg::tri::UpdateCurvatureFitting<MeshType>::computeCurvature(m);
vcg::tri::UpdateNormals<MeshType>::PerVertexAngleWeighted(m);
vcg::tri::UpdateTopology<MeshType>::FaceFace(m);
vcg::tri::UpdateTopology<MeshType>::VertexFace(m);
// update bounding box
vcg::tri::UpdateBounding<MeshType>::Box(m);
int countTemp = 0;
double e = AverageEdgeLenght(m);
int iteraz = 5; //2.0 * sqrt(m.vert.size()/target);
for(VertexIterator it=m.vert.begin(); it!=m.vert.end();++it)
{
if ((countTemp++ % 100) == 0)
cerr << countTemp << "/" << m.vert.size() << endl;
vector<float> oneX;
for (int iteration = 0; iteration<iteraz; ++iteration)
{
oneX.push_back((iteration+1)*(e));
}
std::vector<CoordType> ref(3);
ref[0] = it->PD1();
ref[1] = it->PD2();
ref[2] = it->PD1() ^ it->PD2();
ref[0].Normalize();
ref[1].Normalize();
ref[2].Normalize();
Bicubic b;
RingWalker::clearFlags(&m);
std::vector<VertexType*> pointsGlobal = gatherNeighsSurface(&*it,oneX.at(iteraz-1),m);
vector<float> onedimensional;
for (int iteration = 0; iteration<iteraz; ++iteration)
{
std::vector<VertexType*> points; // solo quelli nel raggio
std::vector<CoordType> projected; // come sopra ma in coord locali
for (typename std::vector<VertexType*>::iterator it2 = pointsGlobal.begin(); it2 != pointsGlobal.end(); ++it2)
{
if (((*it).P() - (*it2)->P()).Norm() < oneX.at(iteration))
points.push_back(*it2);
}
std::vector<VertexType*>& pointsFitting = points;
if (!fitBicubicPoints(&*it, ref, b, projected,pointsFitting))
{
onedimensional.push_back(0);
}
else
{
onedimensional.push_back(b.distanceRMS(projected));
}
}
// // vecchio fit ax^4
Eigen::MatrixXf Am(onedimensional.size(),1);
Eigen::MatrixXf bm(onedimensional.size(),1);
Eigen::MatrixXf sol(1,1);
for(unsigned int c=0; c < onedimensional.size(); ++c)
{
double x = oneX.at(c);
Am(c,0) = pow(x,4);
bm[c] = onedimensional[c];
}
Am.svd().solve(bm, &sol);
it->Q() = pow((double)sol[0],0.25);
// // nuovo fit ax^4 + b
// Eigen::MatrixXf Am(onedimensional.size()+1,2);
// Eigen::MatrixXf bm(onedimensional.size()+1,1);
// Eigen::MatrixXf sol(2,1);
//
// Am(0,0) = 0;
// Am(0,1) = 0;
// bm[0] = 0;
//
// for(unsigned int c=0; c < onedimensional.size(); ++c)
// {
// double x = oneX.at(c);
//
// Am(c,0) = pow(x,4);
// Am(c,1) = 1;
// bm[c] = onedimensional[c];
// }
//
// //sol = ((Am.transpose()*Am).inverse()*Am.transpose())*bm;
// Am.svd().solve(bm, &sol);
//
// cerr << "------" << sol[0] << " " << sol[1] << endl;
// if (sol[0] > 0)
// saliency[it] = pow((double)sol[0],0.25);
// else
// saliency[it] = 0;
}
vcg::tri::Smooth<MeshType>::VertexQualityLaplacian(m,1);
}
~Fitmaps(){};
};
}} // END NAMESPACES
#endif // FITMAPS_H

View File

@ -0,0 +1,453 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.20 2007/05/31 15:24:50 ponchio
FIxed off-by-one error on FaceBorderFromNone.
Revision 1.19 2007/05/22 15:19:42 cignoni
Added VertexClear
Revision 1.18 2007/01/30 18:49:23 tarini
aggiunta la VertexBorderFromNone (flag bordo per vertici senza richiedere nulla)
Revision 1.17 2006/08/31 13:11:12 marfr960
corrected bounds of a vector scan
Revision 1.16 2006/08/30 12:59:49 marfr960
Added missing std:: to swap
Revision 1.15 2006/08/30 06:50:07 cignoni
Reverted to version 1.13. Version 1.14 was done on outdated version.
Revision 1.13 2006/06/18 20:49:30 cignoni
Added missing IsD tests
Revision 1.12 2006/05/03 21:23:25 cignoni
Corrected IsDeleted -> isD
Revision 1.11 2005/12/02 00:09:12 cignoni
Added assert(HasFlags) everywhere..
Revision 1.10 2005/07/06 08:16:34 ganovelli
set VertexBorderFromFace as static
Revision 1.9 2005/06/10 15:07:23 cignoni
Completed FaceBorderFromNone (and added a missing helper class)
Revision 1.8 2005/04/01 13:04:55 fiorin
Minor changes
Revision 1.7 2004/09/14 19:49:43 ganovelli
first compilation version
Revision 1.6 2004/07/15 00:13:39 cignoni
Better doxigen documentation
Revision 1.5 2004/07/06 06:27:02 cignoni
Added FaceBorderFromVF
Revision 1.4 2004/05/13 15:58:55 ganovelli
function Clear added
Revision 1.3 2004/03/12 15:22:19 cignoni
Written some documentation and added to the trimes doxygen module
Revision 1.2 2004/03/10 00:46:10 cignoni
changed to the face::IsBorder() style
Revision 1.1 2004/03/05 10:59:24 cignoni
Changed name from plural to singular (normals->normal)
Revision 1.1 2004/03/04 00:37:56 cignoni
First working version!
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_FLAGS
#define __VCG_TRI_UPDATE_FLAGS
#include <vcg/simplex/face/pos.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile flag.h vcg/complex/trimesh/update/flag.h
/// \brief Management, updating and computation of per-vertex and per-face flags (like border flags).
/**
This class is used to compute or update some of the flags that can be stored in the mesh components. For now just Border flags (e.g. the flag that tells if a given edge of a face belong to a border of the mesh or not).
*/
template <class UpdateMeshType>
class UpdateFlags
{
public:
typedef UpdateMeshType MeshType;
typedef vcg::face::Pos<typename UpdateMeshType::FaceType> PosType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
/// \brief Reset all the mesh flags (both vertexes and faces) setting everithing to zero (the default value for flags)
static void Clear(MeshType &m)
{
assert(HasPerFaceFlags(m));
FaceIterator fi;
VertexIterator vi;
for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
(*fi).Flags() = 0;
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi)
(*vi).Flags() = 0;
}
static void VertexClear(MeshType &m, unsigned int FlagMask = 0xffffffff)
{
VertexIterator vi;
int andMask = ~FlagMask;
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi)
if(!(*vi).IsD()) (*vi).Flags() &= andMask ;
}
static void FaceClear(MeshType &m, unsigned int FlagMask = 0xffffffff)
{
FaceIterator fi;
int andMask = ~FlagMask;
for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
if(!(*fi).IsD()) (*fi).Flags() &= andMask ;
}
static void VertexSet(MeshType &m, unsigned int FlagMask)
{
VertexIterator vi;
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi)
if(!(*vi).IsD()) (*vi).Flags() |= FlagMask ;
}
static void FaceSet(MeshType &m, unsigned int FlagMask)
{
FaceIterator fi;
for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
if(!(*fi).IsD()) (*fi).Flags() |= FlagMask ;
}
static void VertexClearV(MeshType &m) { VertexClear(m,VertexType::VISITED);}
static void VertexClearB(MeshType &m) { VertexClear(m,VertexType::BORDER);}
static void FaceClearV(MeshType &m) { FaceClear(m,FaceType::VISITED);}
static void FaceClearB(MeshType &m) { FaceClear(m,FaceType::BORDER012);}
static void FaceClearF(MeshType &m) { FaceClear(m,FaceType::FAUX012);}
static void VertexSetV(MeshType &m) { VertexSet(m,VertexType::VISITED);}
static void VertexSetB(MeshType &m) { VertexSet(m,VertexType::BORDER);}
static void FaceSetV(MeshType &m) { FaceSet(m,FaceType::VISITED);}
static void FaceSetB(MeshType &m) { FaceSet(m,FaceType::BORDER);}
static void FaceSetF(MeshType &m) { FaceSet(m,FaceType::FAUX012);}
/// \brief Compute the border flags for the faces using the Face-Face Topology.
/**
\warning Obviously it assumes that the topology has been correctly computed (see: UpdateTopology::FaceFace )
*/
static void FaceBorderFromFF(MeshType &m)
{
assert(HasPerFaceFlags(m));
// const int BORDERFLAG[3]={FaceType::BORDER0,FaceType::BORDER1,FaceType::BORDER2};
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)if(!(*fi).IsD())
for(int j=0;j<3;++j)
{
//if(!(*fi).IsManifold(j)) (*fi).SetCF(j);
//else
if(face::IsBorder(*fi,j)) (*fi).SetB(j);
else (*fi).ClearB(j);
}
}
static void FaceBorderFromVF(MeshType &m)
{
assert(HasPerFaceFlags(m));
VertexIterator vi;
assert(m.HasVFTopology());
int visitedBit=VertexType::NewBitFlag();
// Calcolo dei bordi
// per ogni vertice vi si cercano i vertici adiacenti che sono toccati da una faccia sola
// (o meglio da un numero dispari di facce)
const int BORDERFLAG[3]={FaceType::BORDER0, FaceType::BORDER1, FaceType::BORDER2};
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD())
{
for(face::VFIterator<FaceType> vfi(&*vi) ; !vfi.End(); ++vfi )
{
vfi.f->V1(vfi.z)->ClearUserBit(visitedBit);
vfi.f->V2(vfi.z)->ClearUserBit(visitedBit);
}
for(face::VFIterator<FaceType> vfi(&*vi) ; !vfi.End(); ++vfi )
{
if(vfi.f->V1(vfi.z)->IsUserBit(visitedBit)) vfi.f->V1(vfi.z)->ClearUserBit(visitedBit);
else vfi.f->V1(vfi.z)->SetUserBit(visitedBit);
if(vfi.f->V2(vfi.z)->IsUserBit(visitedBit)) vfi.f->V2(vfi.z)->ClearUserBit(visitedBit);
else vfi.f->V2(vfi.z)->SetUserBit(visitedBit);
}
for(face::VFIterator<FaceType> vfi(&*vi) ; !vfi.End(); ++vfi )
{
if(vfi.f->V(vfi.z)< vfi.f->V1(vfi.z) && vfi.f->V1(vfi.z)->IsUserBit(visitedBit))
vfi.f->Flags() |= BORDERFLAG[vfi.z];
if(vfi.f->V(vfi.z)< vfi.f->V2(vfi.z) && vfi.f->V2(vfi.z)->IsUserBit(visitedBit))
vfi.f->Flags() |= BORDERFLAG[(vfi.z+2)%3];
}
}
VertexType::DeleteBitFlag(VertexType::LastBitFlag());
}
class EdgeSorter
{
public:
VertexPointer v[2]; // Puntatore ai due vertici (Ordinati)
FacePointer f; // Puntatore alla faccia generatrice
int z; // Indice dell'edge nella faccia
EdgeSorter() {} // Nothing to do
void Set( const FacePointer pf, const int nz )
{
assert(pf!=0);
assert(nz>=0);
assert(nz<3);
v[0] = pf->V(nz);
v[1] = pf->V((nz+1)%3);
assert(v[0] != v[1]);
if( v[0] > v[1] ) std::swap(v[0],v[1]);
f = pf;
z = nz;
}
inline bool operator < ( const EdgeSorter & pe ) const {
if( v[0]<pe.v[0] ) return true;
else if( v[0]>pe.v[0] ) return false;
else return v[1] < pe.v[1];
}
inline bool operator == ( const EdgeSorter & pe ) const
{
return v[0]==pe.v[0] && v[1]==pe.v[1];
}
inline bool operator != ( const EdgeSorter & pe ) const
{
return v[0]!=pe.v[0] || v[1]!=pe.v[1];
}
};
// versione minimale che non calcola i complex flag.
static void VertexBorderFromNone(MeshType &m)
{
assert(HasPerVertexFlags(m));
std::vector<EdgeSorter> e;
typename UpdateMeshType::FaceIterator pf;
typename std::vector<EdgeSorter>::iterator p;
if( m.fn == 0 )
return;
e.resize(m.fn*3); // Alloco il vettore ausiliario
p = e.begin();
for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce
if( ! (*pf).IsD() )
for(int j=0;j<3;++j)
{
(*p).Set(&(*pf),j);
(*pf).ClearB(j);
++p;
}
assert(p==e.end());
sort(e.begin(), e.end()); // Lo ordino per vertici
typename std::vector<EdgeSorter>::iterator pe,ps;
for(ps = e.begin(), pe = e.begin(); pe < e.end(); ++pe) // Scansione vettore ausiliario
{
if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali
{
if(pe-ps==1) {
ps->v[0]->SetB();
ps->v[1]->SetB();
} else
if(pe-ps!=2) { // not twomanyfold!
for(;ps!=pe;++ps) {
ps->v[0]->SetB(); // Si settano border anche i complex.
ps->v[1]->SetB();
}
}
ps = pe;
}
}
}
/// This function fill the flags with the info on what is the best projection direction
/// for a given face. Used by the point-face distance function when do not exploiting pre-computed
/// per-face data (the so called edge component)
static void FaceProjection(MeshType &m)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi) // Lo riempio con i dati delle facce
if( ! (*fi).IsD() )
{
ScalarType nx = math::Abs((*fi).cN()[0]);
ScalarType ny = math::Abs((*fi).cN()[1]);
ScalarType nz = math::Abs((*fi).cN()[2]);
if(nx>ny && nx>nz) { (*fi).Flags() |= FaceType::NORMX; }
else if(ny>nz) { (*fi).Flags() |= FaceType::NORMY; }
else { (*fi).Flags() |= FaceType::NORMZ; }
}
}
/// Computes per-face border flags without requiring any kind of topology
/// It has a O(fn log fn) complexity.
static void FaceBorderFromNone(MeshType &m)
{
assert(HasPerFaceFlags(m));
std::vector<EdgeSorter> e;
typename UpdateMeshType::FaceIterator pf;
typename std::vector<EdgeSorter>::iterator p;
for(VertexIterator v=m.vert.begin();v!=m.vert.end();++v)
(*v).ClearB();
if( m.fn == 0 )
return;
FaceIterator fi;
int n_edges = 0;
for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN();
e.resize(n_edges);
p = e.begin();
for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce
if( ! (*pf).IsD() )
for(int j=0;j<(*pf).VN();++j)
{
(*p).Set(&(*pf),j);
(*pf).ClearB(j);
++p;
}
assert(p==e.end());
sort(e.begin(), e.end()); // Lo ordino per vertici
typename std::vector<EdgeSorter>::iterator pe,ps;
ps = e.begin();pe=e.begin();
do
{
if( pe==e.end() || *pe != *ps ) // Trovo blocco di edge uguali
{
if(pe-ps==1) {
ps->f->SetB(ps->z);
} else
if(pe-ps!=2) { // Caso complex!!
for(;ps!=pe;++ps)
ps->f->SetB(ps->z); // Si settano border anche i complex.
}
ps = pe;
}
if(pe==e.end()) break;
++pe;
} while(true);
// TRACE("found %i border (%i complex) on %i edges\n",nborder,ncomplex,ne);
}
/// Compute the PerVertex Border flag deriving it from the faces
static void VertexBorderFromFace(MeshType &m)
{
assert(HasPerFaceFlags(m));
typename MeshType::VertexIterator v;
typename MeshType::FaceIterator f;
for(v=m.vert.begin();v!=m.vert.end();++v)
(*v).ClearB();
for(f=m.face.begin();f!=m.face.end();++f)
if(!(*f).IsD())
{
for(int z=0;z<(*f).VN();++z)
if( (*f).IsB(z) )
{
(*f).V(z)->SetB();
(*f).V((*f).Next(z))->SetB();
}
}
}
//
static void FaceFauxCrease(MeshType &m,float AngleRad)
{
assert(HasPerFaceFlags(m));
assert(HasFFAdjacency(m));
typename MeshType::FaceIterator f;
//initially everything is faux (e.g all internal)
FaceSetF(m);
for(f=m.face.begin();f!=m.face.end();++f)
{
if(!(*f).IsD())
{
for(int z=0;z<(*f).VN();++z)
{
if( face::IsBorder(*f,z) ) (*f).ClearF(z);
else
{
if(Angle((*f).N(), (*f).FFp(z)->N()) > AngleRad)
(*f).ClearF(z);
}
}
}
}
}
}; // end class
} // End namespace tri
} // End namespace vcg
#endif

View File

@ -0,0 +1,666 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCGLIB_HALFEDGE_
#define __VCGLIB_HALFEDGE_
#include <vector>
#include <vcg/complex/trimesh/allocate.h>
#include <vcg/complex/trimesh/clean.h>
#include <vcg/complex/trimesh/update/topology.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/update/halfedge_topology.h>
namespace vcg
{
namespace tri{
/// \ingroup trimesh
/// \headerfile edge_support.h vcg/complex/trimesh/edge_support.h
/// \brief This class is used to build edge based data structure from indexed data structure and viceversa
/**
*/
template <class MeshType >
class UpdateHalfEdges{
public:
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::HEdgeType HEdgeType;
typedef typename MeshType::EdgePointer EdgePointer;
typedef typename MeshType::EdgeType EdgeType;
typedef typename MeshType::EdgeIterator EdgeIterator;
typedef typename MeshType::HEdgeIterator HEdgeIterator;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::FaceType FaceType;
struct VertexPairEdgePtr{
VertexPairEdgePtr(VertexPointer _v0,VertexPointer _v1,HEdgePointer _ep):v0(_v0),v1(_v1),ep(_ep){if(v0>v1) std::swap(v0,v1);}
const bool operator <(const VertexPairEdgePtr &o) const {return (v0 == o.v0)? (v1<o.v1):(v0<o.v0);}
const bool operator ==(const VertexPairEdgePtr &o) const {return (v0 == o.v0)&& (v1==o.v1);}
VertexPointer v0,v1;
HEdgePointer ep;
};
struct FacePtrInt{
FacePtrInt ( FaceType * _f,int _i):f(_f),i(_i){}
FaceType * f;
int i;
};
typedef std::vector<bool> BitVector;
/**
build a half-edge data structure from an indexed data structure. Note that the half-edges are allocated here for the first time.
If you have a mesh where there are already edges, they will be removed and the data lost, so do not use this function
to just "update" the topology of half edges.
**/
static void FromIndexed(MeshType & m){
assert(HasFVAdjacency(m));
assert(HasHOppAdjacency(m));
assert(HasHNextAdjacency(m));
typename MeshType::template PerFaceAttributeHandle<BitVector> flagVisited =
vcg::tri::Allocator<MeshType>::template AddPerFaceAttribute<BitVector>(m,"");
std::vector<FacePtrInt > borderEdges;
// allocate all new half edges
FaceIterator fi;
unsigned int n_edges = 0;
// count how many half edge to allocate
for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD())
{n_edges+=(*fi).VN();
for(int i = 0; i < (*fi).VN(); ++i)
if(vcg::face::IsBorder<FaceType>((*fi),(i)))
++n_edges;
}
m.hedge.clear();
m.hn = 0;
// allocate the half edges
typename MeshType::HEdgeIterator ei = vcg::tri::Allocator<MeshType>::AddHEdges(m,n_edges);
std::vector<VertexPairEdgePtr> all;
int firstEdge = 0;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)if(!(*fi).IsD()){
assert((*fi).VN()>2);
if(flagVisited[*fi].empty()) {flagVisited[*fi].resize((*fi).VN());}
for(int i = 0; i < (*fi).VN(); ++i,++ei)
{
(*ei).HVp() = (*fi).V(i);
(*ei).HNp() = &m.hedge[firstEdge + (i +1) % (*fi).VN()];
if(MeshType::HEdgeType::HasHFAdjacency())
(*ei).HFp() = &(*fi);
if( MeshType::FaceType::HasFHAdjacency())
(*fi).FHp() = &(*ei);
if(MeshType::HEdgeType::HasHPrevAdjacency())
(*ei).HPp() = &m.hedge[firstEdge + (i +(*fi).VN()-1) % (*fi).VN()];
if(HasVHAdjacency(m))
(*ei).HVp()->VHp() = &(*ei);
all.push_back(VertexPairEdgePtr((*fi).V(i), (*fi).V((*fi).Next(i)),&(*ei)));// it will be used to link the hedges
if( vcg::face::IsBorder<FaceType>((*fi),(i)))
borderEdges.push_back(FacePtrInt(&(*fi),i));
}
firstEdge += (*fi).VN();
}
// add all the border hedges
int borderLength;
typename std::vector<FacePtrInt >::iterator ebi;
for( ebi = borderEdges.begin(); ebi != borderEdges.end(); ++ebi)
if( !flagVisited[(*ebi).f][(*ebi).i])// not already inserted
{
borderLength = 0;
vcg::face::Pos<FaceType> bp((*ebi).f,(*ebi).i);
//FaceType * start = (*ebi).f;
VertexType * start = ((*ebi).f)->V((*ebi).i);
do{
all.push_back( VertexPairEdgePtr ( bp.f->V( bp.f->Next(bp.z) ),bp.f->V( bp.z ),&(*ei)));
(*ei).HVp() = bp.f->V(bp.f->Next(bp.z)) ;
flagVisited[bp.f][bp.z] = true;
++ei;
bp.NextB();
++borderLength;
}while (bp.v != start);
//}while (bp.f != start);
// run over the border edges to link the adjacencies
for(int be = 0; be < borderLength; ++be)
{
if(MeshType::HEdgeType::HasHFAdjacency())
m.hedge[firstEdge + be].HFp() = NULL;
if(MeshType::HEdgeType::HasHPrevAdjacency())
m.hedge[firstEdge + be].HPp() = &m.hedge[firstEdge + (be +borderLength-1) % borderLength];
m.hedge[firstEdge + be].HNp() = &m.hedge[firstEdge + (be +1) % borderLength];
}
firstEdge+=borderLength;
}
vcg::tri::Allocator<MeshType>:: template DeletePerFaceAttribute<BitVector>(m,flagVisited );
std::sort(all.begin(),all.end());
assert(all.size() == n_edges);
for(unsigned int i = 0 ; i < all.size(); )
if(all[i] == all[i+1])
{
all[i].ep->HOp() = all[i+1].ep;
all[i+1].ep->HOp() = all[i].ep;
i+=2;
}
else
{
all[i].ep->HOp() = all[i].ep;
i+=1;
}
if(HasEHAdjacency(m) && HasHEAdjacency(m))
{
assert(m.edge.size() == 0 || m.edge.size() == n_edges/2);
if ( m.edge.size() == 0 )
{
m.en = 0;
// allocate the edges
typename MeshType::EdgeIterator edge_i = vcg::tri::Allocator<MeshType>::AddEdges(m,n_edges/2);
for(ei = m.hedge.begin(); ei != m.hedge.end(); ++ei)
{
if((*ei).HEp() == NULL)
{
(*ei).HEp() = &(*edge_i);
(*ei).HOp()->HEp() = &(*edge_i);
(*edge_i).EHp() = &(*ei);
++edge_i;
}
}
}
else
{
if(HasEVAdjacency(m) && HasHEAdjacency(m) && HasEHAdjacency(m))
{
//update edge relations
for(typename MeshType::EdgeIterator ei1 = m.edge.begin(); ei1 != m.edge.end(); ++ei1 )
{
vector<HEdgePointer> hedges = HalfEdgeTopology<MeshType>::get_incident_hedges((*ei1).V(0));
for(typename vector<HEdgePointer>::iterator hi = hedges.begin(); hi != hedges.end(); ++hi)
{
if((*hi)->HOp()->HVp() == (*ei1).V(1))
{
assert((*hi)->HEp() == NULL);
assert((*hi)->HOp()->HEp() == NULL);
// EH
(*ei1).EHp() = *hi;
// HE
(*hi)->HEp() = &(*ei1);
(*hi)->HOp()->HEp() = &(*ei1);
break;
}
}
}
}
}
}
}
/**
Checks pointers FHEp() are valid
**/
static bool CheckConsistency_FHp(MeshType & m){
assert(MeshType::FaceType::HasFHAdjacency());
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if(!(*fi).IsD()){
if((*fi).FHp() < &(*m.hedge.begin())) return false;
if((*fi).FHp() > &(m.hedge.back())) return false;
}
return true;
}
/**
Checks that half edges and face relation are consistent
**/
static bool CheckConsistency(MeshType & m){
assert(MeshType::HEdgeType::HasHNextAdjacency());
assert(MeshType::HEdgeType::HasHOppAdjacency());
assert(MeshType::HEdgeType::HasHVAdjacency());
assert(MeshType::FaceType::HasFHAdjacency());
//bool hasHEF = ( MeshType::HEdgeType::HasHFAdjacency());
bool hasHP = ( MeshType::HEdgeType::HasHPrevAdjacency());
FaceIterator fi;
HEdgePointer ep,ep1;
int cnt = 0;
if( MeshType::HEdgeType::HasHFAdjacency() )
{
int iDb = 0;
for(fi = m.face.begin(); fi != m.face.end(); ++fi,++iDb)
if(!(*fi).IsD())
{
ep = ep1 = (*fi).FHp();
do{
if(ep->IsD())
return false; // the hedge should not be connected, it has been deleted
if( ! ep->HFp())
return false;
if(ep->HFp() != &(*fi))
return false;// hedge is not pointing to the rigth face
ep = ep->HNp();
if(cnt++ > m.hn)
return false; // hedges are ill connected (HENp())
}while(ep!=ep1);
}
}
HEdgePointer epPrev;
HEdgeIterator hi;
//bool extEdge ;
for( hi = m.hedge.begin(); hi != m.hedge.end(); ++hi)
if(!(*hi).IsD())
{
//cnt = 0;
epPrev = ep = ep1 = &(*hi);
//do{
//extEdge = (ep->HFp()==NULL);
if(hasHP)
{
if( !ep->HPp())
return false;
if( ep->HPp() == ep)
return false; // the previous of an edge cannot be the edge itself
if( ep->HNp()->HPp() != ep)
return false; // next and prev relation are not mutual
if( ep->HPp()->IsD())
return false; //
}
if( ! ep->HOp() )
return false;
if( ep->HOp() == ep)
return false; // opposite relation is not mutual
if( ep->HOp()->IsD())
return false;
if( ep->HOp()->HOp() != ep)
return false; // opposite relation is not mutual
if( HasHFAdjacency(m) )
{
if(ep->HFp())
{
if( ep->HFp()->IsD())
return false; // pointed face must not be deleted
}
}
if( HasHEAdjacency(m) && (m.en!=0))
{
if( ! ep->HEp())
return false; //halfedge must point to an edge
if( ep->HEp()->IsD())
return false; // pointed edge must not be deleted
if(ep->HEp() != ep->HOp()->HEp())
return false; // he and opposite he must point to the same edge
if(ep->HEp()->EHp() != ep && ep->HEp()->EHp() != ep->HOp() )
return false; // halfedge points to an edge not pointing it or its opposite
}
if( !ep->HNp() )
return false;
if( ep->HNp() == ep )
return false; // the next of an hedge cannot be the hedge itself
if( ep->HNp()->IsD())
return false; //
if(hasHP)
if( ep->HNp()->HPp() != ep)
return false; //
if( HasHVAdjacency(m) )
{
if( ! ep->HVp() )
return false; // halfedge must point to a vertex
if( ep->HVp()->IsD() )
return false; // pointed vertex must not be deleted
if( HasVHAdjacency(m) )
if( ! (ep->HVp()->VHp()) )
return false; // halfedge points to a vertex pointing NULL
}
ep = ep->HNp();
if( ep->HVp() != epPrev->HOp()->HVp())
return false;
epPrev = ep;
// if(cnt++ > m.hn)
// return false; // edges are ill connected (HENp())
//}while(ep!=ep1);
}
if(HasEHAdjacency(m) && HasHEAdjacency(m))
for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
{
if(!(*ei).IsD())
{
if( !(*ei).EHp())
return false; //edge must have a valid pointer to his halfedge
if( (*ei).EHp()->HEp() != &(*ei) )
return false; // edge's halfedge must point to the edge itself
if( (*ei).EHp()->IsD())
return false;
}
}
if(HasVHAdjacency(m))
for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
{
if( !(*vi).IsD() )
if( (*vi).VHp() )
{
if( (*vi).VHp()->HVp() != &(*vi) )
return false;
if( (*vi).VHp()->IsD())
return false;
}
}
return true;
}
/** Set the relations HFp(), FHp() from a loop of edges to a face
*/
private:
static void SetRelationsLoopFace(HEdgeType * e0, FaceType * f){
assert(HEdgeType::HasHNextAdjacency());
assert(FaceType::HasFHAdjacency());
HEdgeType *e = e0;
assert(e!=NULL);
do{ e->HFp() = f; e = e->HNp(); } while(e != e0);
f->FHp() = e0;
}
/**
Merge the two faces. This will probably become a class template or a functor
*/
static void MergeFaces(FaceType *, FaceType *){}
/**
Find previous hedge in the loop
*/
static HEdgeType * PreviousEdge(HEdgeType * e0){
HEdgeType * ep = e0;
do{
if(ep->HNp() == e0) return ep;
ep = ep->HNp();
}while(ep!=e0);
assert(0); // degenerate loop
return 0;
}
public:
/** Adds an edge between the sources of e0 and e1 and set all the topology relations.
If the edges store the pointers to the faces then a new face is created.
<--- e1 ---- X <------e1_HEPp---
^
||
ei0 || ei1
||
v
----e0_HEPp-> X ----- e0 ------>
*/
static void AddHEdge(MeshType &m, HEdgeType * e0, HEdgeType * e1){
HEdgeType *iii =e0->HNp();
assert(e1!=e0->HNp());
assert(e0!=e1->HNp());
HEdgePointer tmp;
bool hasP = MeshType::HEdgeType::HasHPrevAdjacency();
assert(e0->HOp() != e1); // the hedge already exists
assert(e0!=e1->HNp());
std::vector<typename MeshType::HEdgePointer* > toUpdate;
toUpdate.push_back(&e0);
toUpdate.push_back(&e1);
HEdgeIterator ei0 = vcg::tri::Allocator<MeshType>::AddHEdges(m,2,toUpdate);
HEdgeIterator ei1 = ei0; ++ei1;
(*ei0).HNp() = e1;(*ei0).HVp() = e0->HVp();
(*ei1).HNp() = e0;(*ei1).HVp() = e1->HVp();
HEdgePointer e0_HEPp = 0,e1_HEPp = 0,ep =0;
if(hasP){
e0_HEPp = e0->HPp();
e1_HEPp = e1->HPp();
}else{// does not have pointer to previous, it must be computed
ep = e0;
do{
if(ep->HNp() == e0) e0_HEPp = ep;
if(ep->HNp() == e1) e1_HEPp = ep;
ep = ep->HNp();
}while(ep!=e0);
}
if(hasP){
(*ei0).HPp() = e0->HPp();
(*ei1).HPp() = e1->HPp();
e0->HPp() = &(*ei1);
e1->HPp() = &(*ei0);
}
e0_HEPp -> HNp() = &(*ei0);
e1_HEPp -> HNp() = &(*ei1);
(*ei0).HOp() = &(*ei1);
(*ei1).HOp() = &(*ei0);
if( HEdgeType::HasHFAdjacency() && FaceType::HasFHAdjacency()){
FaceIterator fi0 = vcg::tri::Allocator<MeshType>::AddFaces(m,1);
m.face.back().ImportData(*e0->HFp());
SetRelationsLoopFace(&(*ei0),e1->HFp()); // one loop to the old face
SetRelationsLoopFace(&(*ei1),&m.face.back()); // the other to the new face
}
}
/** Detach the topology relations of a given edge
<--- e->HENPp -X --- <---------eO_HEPp---
^
||
e || e->HEOp()
||
v
----e_HEPp--> X ----- e->HEOp->HENPp() ------>
*/
static void RemoveHEdge(MeshType &m, HEdgeType * e){
assert(MeshType::HEdgeType::HasHNextAdjacency());
assert(MeshType::HEdgeType::HasHOppAdjacency());
assert(MeshType::FaceType::HasFHAdjacency());
bool hasP = MeshType::HEdgeType::HasHPrevAdjacency();
HEdgePointer e_HEPp,eO_HEPp;
if(hasP){
e_HEPp = e->HPp();
eO_HEPp = e->HOp()->HPp();
}else{
e_HEPp = PreviousEdge(e);
eO_HEPp = PreviousEdge(e->HOp());
}
assert(e_HEPp->HNp() == e);
assert(eO_HEPp->HNp() == e->HOp());
e_HEPp->HNp() = e->HOp()->HNp();
eO_HEPp->HNp() = e-> HNp();
if(hasP) {
e->HOp()->HNp()->HPp() = e_HEPp;
e->HNp()->HPp() = eO_HEPp;
e->HPp() = NULL;
e-> HOp()->HPp() = NULL;
}
// take care of the faces
if(MeshType::HEdgeType::HasHFAdjacency()){
MergeFaces(e_HEPp->HFp(),eO_HEPp->HFp());
vcg::tri::Allocator<MeshType>::DeleteFace(m,*eO_HEPp->HFp());
SetRelationsLoopFace(e_HEPp,e_HEPp->HFp());
}
vcg::tri::Allocator<MeshType>::DeleteHEdge(m,*e->HOp());
vcg::tri::Allocator<MeshType>::DeleteHEdge(m,*e);
}
};// end class
template <class MeshType >
struct UpdateIndexed{
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::HEdgeType HEdgeType;
typedef typename MeshType::HEdgeIterator HEdgeIterator;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::FaceType FaceType;
struct VertexPairEdgePtr{
VertexPairEdgePtr(VertexPointer _v0,VertexPointer _v1,HEdgePointer _ep):v0(_v0),v1(_v1),ep(_ep){if(v0>v1) std::swap(v0,v1);}
const bool operator <(const VertexPairEdgePtr &o) const {return (v0 == o.v0)? (v1<o.v1):(v0<o.v0);}
const bool operator ==(const VertexPairEdgePtr &o) const {return (v0 == o.v0)&& (v1==o.v1);}
VertexPointer v0,v1;
HEdgePointer ep;
};
/**
builds an indexed data structure from a half-edge data structure.
Note: if the half edge have the pointer to face
their relation FV (face-vertex) will be computed and the data possibly stored in the
face will be preserved.
**/
static void FromHalfEdges( MeshType & m ){
assert(HasFVAdjacency(m));
assert(MeshType::HEdgeType::HasHNextAdjacency());
assert(MeshType::HEdgeType::HasHVAdjacency());
assert(MeshType::HEdgeType::HasHOppAdjacency());
assert(MeshType::FaceType::HasFHAdjacency());
bool hasHEF;
//bool createFace,hasHEF,hasFHE;
// typename MeshType::template PerHEdgeAttributeHandle<bool> hV = Allocator<MeshType>::template AddPerHEdgeAttribute<bool>(m,"");
typename MeshType::HEdgeIterator ei;
typename MeshType::FacePointer fp;
typename MeshType::FaceIterator fi;
typename MeshType::HEdgePointer ep,epF;
//int vi = 0;
vcg::SimpleTempData<typename MeshType::HEdgeContainer,bool> hV(m.hedge);
hasHEF = (MeshType::HEdgeType::HasHFAdjacency());
assert( !hasHEF || (hasHEF && m.fn>0));
// if the edgetype has the pointer to face
// it is assumed the the edget2face pointer (HEFp) are correct
// and the faces are allocated
for ( ei = m.hedge.begin(); ei != m.hedge.end(); ++ei)
if(!(*ei).IsD()) // it has not been deleted
if(!hasHEF || ( hasHEF && (*ei).HFp()!=NULL)) // if it has a pointer to the face it is
// not null (i.e. it is not a border edge)
if(!hV[(*ei)] ) // it has not be visited yet
{
if(!hasHEF)// if it has
fp = &(* Allocator<MeshType>::AddFaces(m,1));
else
fp = (*ei).HFp();
ep = epF = &(*ei);
std::vector<VertexPointer> vpts;
do{vpts.push_back((*ep).HVp()); ep=ep->HNp();}while(ep!=epF);
//int idbg =fp->VN();
if(fp->VN() != vpts.size()){
fp->Dealloc();
fp ->Alloc(vpts.size());
}
//int idbg1 =fp->VN();
for(unsigned int i = 0; i < vpts.size();++i) fp ->V(i) = vpts[i];// set the pointer from face to vertex
hV[(*ei)] = true;
}
//Allocator<MeshType>::DeletePerHEdgeAttribute(m,hV);
}
};
} // end namespace vcg
}
#endif // __VCGLIB_EDGE_SUPPORT

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,398 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_NORMALS
#define __VCG_TRI_UPDATE_NORMALS
#include <vcg/space/triangle3.h>
#include <vcg/math/matrix33.h>
#include <vcg/complex/trimesh/update/flag.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile normal.h vcg/complex/trimesh/update/normal.h
/// \brief Management, updating and computation of per-vertex and per-face normals.
/**
This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh.
*/
template <class ComputeMeshType>
class UpdateNormals
{
public:
typedef ComputeMeshType MeshType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::CoordType CoordType;
typedef typename VertexType::NormalType NormalType;
typedef typename VertexType::ScalarType ScalarType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
/**
Set to zero all the normals. Usued by all the face averaging algorithms.
by default it does not clear the normals of unreferenced vertices because they could be still useful
*/
static void PerVertexClear(ComputeMeshType &m, bool ClearAllVertNormal=false)
{
assert(HasPerVertexNormal(m));
if(ClearAllVertNormal)
UpdateFlags<ComputeMeshType>::VertexClearV(m);
else
{
UpdateFlags<ComputeMeshType>::VertexSetV(m);
for(FaceIterator f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() )
for(int i=0;i<3;++i) (*f).V(i)->ClearV();
}
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if( !(*vi).IsD() && (*vi).IsRW() && (!(*vi).IsV()) )
(*vi).N() = NormalType((ScalarType)0,(ScalarType)0,(ScalarType)0);
}
/// \brief Calculates the face normal (if stored in the current face type)
static void PerFace(ComputeMeshType &m)
{
if( !m.HasPerFaceNormal()) return;
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() ) face::ComputeNormal(*f);
}
/// \brief Calculates the vertex normal. Exploiting or current face normals.
/**
The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
*/
static void PerVertexFromCurrentFaceNormal(ComputeMeshType &m)
{
if( !m.HasPerVertexNormal()) return;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if( !(*vi).IsD() && (*vi).IsRW() )
(*vi).N()=CoordType(0,0,0);
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if( !(*fi).IsD())
{
for(int j=0; j<3; ++j)
if( !(*fi).V(j)->IsD())
(*fi).V(j)->N() += (*fi).cN();
}
}
/// \brief Calculates the vertex normal. Exploiting or current face normals.
/**
The normal of a face f is the average of the normals of the vertices of f.
*/
static void PerFaceFromCurrentVertexNormal(ComputeMeshType &m)
{
for (FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi)
if( !(*fi).IsD())
{
NormalType n;
n.SetZero();
for(int j=0; j<3; ++j)
n += fi->V(j)->cN();
n.Normalize();
fi->N() = n;
}
}
/// \brief Calculates the vertex normal. Without exploiting or touching face normals.
/**
The normal of a vertex v computed as a weighted sum f the incident face normals.
The weight is simlply the angle of the involved wedge. Described in:
G. Thurmer, C. A. Wuthrich
"Computing vertex normals from polygonal facets"
Journal of Graphics Tools, 1998
*/
static void PerVertexAngleWeighted(ComputeMeshType &m)
{
assert(HasPerVertexNormal(m));
PerVertexClear(m);
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() && (*f).IsR() )
{
typename FaceType::NormalType t = vcg::NormalizedNormal(*f);
NormalType e0 = ((*f).V1(0)->cP()-(*f).V0(0)->cP()).Normalize();
NormalType e1 = ((*f).V1(1)->cP()-(*f).V0(1)->cP()).Normalize();
NormalType e2 = ((*f).V1(2)->cP()-(*f).V0(2)->cP()).Normalize();
(*f).V(0)->N() += t*AngleN(e0,-e2);
(*f).V(1)->N() += t*AngleN(-e0,e1);
(*f).V(2)->N() += t*AngleN(-e1,e2);
}
}
/// \brief Calculates the vertex normal. Without exploiting or touching face normals.
/**
The normal of a vertex v is computed according to the formula described by Nelson Max in
Max, N., "Weights for Computing Vertex Normals from Facet Normals", Journal of Graphics Tools, 4(2) (1999)
The weight for each wedge is the cross product of the two edge over the product of the square of the two edge lengths.
According to the original paper it is perfect only for spherical surface, but it should perform well...
*/
static void PerVertexWeighted(ComputeMeshType &m)
{
assert(HasPerVertexNormal(m));
PerVertexClear(m);
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() && (*f).IsR() )
{
typename FaceType::NormalType t = vcg::Normal(*f);
ScalarType e0 = SquaredDistance((*f).V0(0)->cP(),(*f).V1(0)->cP());
ScalarType e1 = SquaredDistance((*f).V0(1)->cP(),(*f).V1(1)->cP());
ScalarType e2 = SquaredDistance((*f).V0(2)->cP(),(*f).V1(2)->cP());
(*f).V(0)->N() += t/(e0*e2);
(*f).V(1)->N() += t/(e0*e1);
(*f).V(2)->N() += t/(e1*e2);
}
}
/// \brief Calculates the vertex normal. Without exploiting or touching face normals.
/**
The normal of a vertex v is the classical area weigthed average of the normals of the faces incident on v.
*/
static void PerVertex(ComputeMeshType &m)
{
assert(HasPerVertexNormal(m));
PerVertexClear(m);
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() && (*f).IsR() )
{
//typename FaceType::NormalType t = (*f).Normal();
typename FaceType::NormalType t = vcg::Normal(*f);
for(int j=0; j<3; ++j)
if( !(*f).V(j)->IsD() && (*f).V(j)->IsRW() )
(*f).V(j)->N() += t;
}
}
/// \brief Calculates both vertex and face normals.
/**
The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
*/
static void PerVertexPerFace(ComputeMeshType &m)
{
if( !m.HasPerVertexNormal() || !m.HasPerFaceNormal()) return;
PerFace(m);
PerVertexClear(m);
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() && (*f).IsR() )
{
for(int j=0; j<3; ++j)
if( !(*f).V(j)->IsD() && (*f).V(j)->IsRW() )
(*f).V(j)->N() += (*f).cN();
}
}
/// \brief Calculates both vertex and face normals.
/**
The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
*/
static void PerVertexNormalizedPerFace(ComputeMeshType &m)
{
PerVertexPerFace(m);
NormalizeVertex(m);
}
/// \brief Normalize the lenght of the face normals.
static void NormalizeVertex(ComputeMeshType &m)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if( !(*vi).IsD() && (*vi).IsRW() )
(*vi).N().Normalize();
}
/// \brief Normalize the lenght of the face normals.
static void NormalizeFace(ComputeMeshType &m)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if( !(*fi).IsD() ) (*fi).N().Normalize();
}
static void AreaNormalizeFace(ComputeMeshType &m)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if( !(*fi).IsD() )
{
(*fi).N().Normalize();
(*fi).N() = (*fi).N() * DoubleArea(*fi);
}
}
static void PerVertexNormalizedPerFaceNormalized(ComputeMeshType &m)
{
PerVertexNormalizedPerFace(m);
NormalizeFace(m);
}
static void PerFaceRW(ComputeMeshType &m, bool normalize=false)
{
if( !m.HasPerFaceNormal()) return;
FaceIterator f;
bool cn = true;
if(normalize)
{
for(f=m.m.face.begin();f!=m.m.face.end();++f)
if( !(*f).IsD() && (*f).IsRW() )
{
for(int j=0; j<3; ++j)
if( !(*f).V(j)->IsR()) cn = false;
if( cn ) face::ComputeNormalizedNormal(*f);
cn = true;
}
}
else
{
for(f=m.m.face.begin();f!=m.m.face.end();++f)
if( !(*f).IsD() && (*f).IsRW() )
{
for(int j=0; j<3; ++j)
if( !(*f).V(j)->IsR()) cn = false;
if( cn )
(*f).ComputeNormal();
cn = true;
}
}
}
static void PerFaceNormalized(ComputeMeshType &m)
{
if( !m.HasPerFaceNormal()) return;
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f)
if( !(*f).IsD() ) face::ComputeNormalizedNormal(*f);
}
static void PerBitQuadFaceNormalized(ComputeMeshType &m)
{
if( !m.HasPerFaceNormal()) return;
PerFace(m);
FaceIterator f;
for(f=m.face.begin();f!=m.face.end();++f) {
if( !(*f).IsD() ) {
for (int k=0; k<3; k++) if (f->IsF(k))
if (&*f < f->FFp(k)) {
f->N() = f->FFp(k)->N() = (f->FFp(k)->N() + f->N()).Normalize();
}
}
}
}
/// \brief Calculates the vertex normal.
static void PerVertexNormalized(ComputeMeshType &m)
{
if( !m.HasPerVertexNormal()) return;
PerVertex(m);
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
if( !(*vi).IsD() && (*vi).IsRW() )
(*vi).N().Normalize();
}
/// \brief Multiply the vertex normals by the matrix passed. By default, the scale component is removed.
static void PerVertexMatrix(ComputeMeshType &m, const Matrix44<ScalarType> &mat, bool remove_scaling= true){
float scale;
Matrix33<ScalarType> mat33(mat,3);
if( !m.HasPerVertexNormal()) return;
if(remove_scaling){
scale = pow(mat33.Determinant(),(ScalarType)(1.0/3.0));
mat33[0][0]/=scale;
mat33[1][1]/=scale;
mat33[2][2]/=scale;
}
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
if( !(*vi).IsD() && (*vi).IsRW() )
(*vi).N() = mat33*(*vi).N();
}
/// \brief Multiply the face normals by the matrix passed. By default, the scale component is removed.
static void PerFaceMatrix(ComputeMeshType &m, const Matrix44<ScalarType> &mat, bool remove_scaling= true){
float scale;
Matrix33<ScalarType> mat33(mat,3);
if( !m.HasPerFaceNormal()) return;
if(remove_scaling){
scale = pow(mat33.Determinant(),ScalarType(1.0/3.0));
mat33[0][0]/=scale;
mat33[1][1]/=scale;
mat33[2][2]/=scale;
}
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
if( !(*fi).IsD() && (*fi).IsRW() )
(*fi).N() = mat33* (*fi).N();
}
}; // end class
} // End namespace
} // End namespace
#endif

View File

@ -0,0 +1,83 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.1 2005/07/06 08:02:27 cignoni
Initial commit
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_POSITION
#define __VCG_TRI_UPDATE_POSITION
#include "normal.h"
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile position.h vcg/complex/trimesh/update/position.h
/// \brief This class is used to update vertex position according to a transformation matrix.
template <class ComputeMeshType>
class UpdatePosition
{
public:
typedef ComputeMeshType MeshType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
/// \brief Multiply
static void Matrix(ComputeMeshType &m, const Matrix44<ScalarType> &M, bool update_also_normals = true)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD()) (*vi).P()=M*(*vi).cP();
if(update_also_normals){
if(m.HasPerVertexNormal()){
UpdateNormals<ComputeMeshType>::PerVertexMatrix(m,M);
}
if(m.HasPerFaceNormal()){
UpdateNormals<ComputeMeshType>::PerFaceMatrix(m,M);
}
}
}
}; // end class
} // End namespace
} // End namespace
#endif

View File

@ -0,0 +1,351 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_QUALITY
#define __VCG_TRI_UPDATE_QUALITY
#include <vcg/simplex/face/pos.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/complex/trimesh/update/flag.h>
#include <vcg/complex/trimesh/stat.h>
#include <algorithm>
#include <vector>
#include <stack>
#include <assert.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile quality.h vcg/complex/trimesh/update/quality.h
/// \brief Generation of per-vertex and per-face qualities.
/**
It works according to various strategy, like geodesic distance from the border (UpdateQuality::VertexGeodesicFromBorder) or curvature ecc.
This class is templated over the mesh and (like all other Update* classes) has only static members; Typical usage:
\code
MyMeshType m;
UpdateQuality<MyMeshType>::VertexGeodesicFromBorder(m);
\endcode
*/
template <class UpdateMeshType>
class UpdateQuality
{
public:
typedef UpdateMeshType MeshType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
class VQualityHeap
{
public:
float q;
VertexPointer p;
inline VQualityHeap( VertexPointer np )
{
q = np->Q();
p = np;
}
// Attenzione il minore e' maggiore
inline bool operator < ( const VQualityHeap & vq ) const { return q > vq.q; }
inline bool operator == ( const VQualityHeap & vq ) const { return q == vq.q; }
inline bool operator > ( const VQualityHeap & vq ) const { return q < vq.q; }
inline bool operator != ( const VQualityHeap & vq ) const { return q != vq.q; }
inline bool operator <= ( const VQualityHeap & vq ) const { return q >= vq.q; }
inline bool operator >= ( const VQualityHeap & vq ) const { return q <= vq.q; }
inline bool is_valid() const { return q==p->Q(); }
};
// *** IMPORTANT REQUIREMENTS
// VF topology
// Border FLags
// tri::UpdateTopology<SMesh>::VertexFace(sm);
// tri::UpdateFlags<SMesh>::FaceBorderFromVF(sm);
//
// Calcola la qualita' come distanza geodesica dal bordo della mesh.
// Robusta funziona anche per mesh non manifold.
// La qualita' memorizzata indica la distanza assoluta dal bordo della mesh.
// Nota prima del 13/11/03 in alcuni casi rari SPT andava in loop perche' poteva capitare
// che per approx numeriche ben strane pw->Q() > pv->Q()+d ma durante la memorizzazione
// della nuova distanza essa rimanesse uguale a prima. Patchato rimettendo i vertici nello
// heap solo se migliorano la distanza di un epsilon == 1/100000 della mesh diag.
/// \brief Compute, for each vertex of the mesh the geodesic distance from the border of the mesh itself.
/**
It uses the classical Dijkstra Shortest Path Tree algorithm.
The geodesic distance is approximated by allowing to walk only along edges of the mesh.
\warning VF topology, Per Vertex Quality and border flags already computed (see UpdateFlags::FaceBorderFromVF and UpdateTopology::VertexFace);
*/
static void VertexGeodesicFromBorder(MeshType &m) // R1
{
//Requirements
assert(m.HasVFTopology());
assert(m.HasPerVertexQuality());
std::vector< VQualityHeap > heap;
VertexIterator v;
FaceIterator f;
int j;
for(v=m.vert.begin();v!=m.vert.end();++v)
(*v).Q() = -1;
for(f=m.face.begin();f!=m.face.end();++f) // Inserisco nell'heap i v di bordo
if(!(*f).IsD())
for(j=0;j<3;++j)
if( (*f).IsB(j) )
{
for(int k=0;k<2;++k)
{
VertexPointer pv = (*f).V((j+k)%3);
if( pv->Q()==-1 )
{
pv->Q() = 0;
heap.push_back(VQualityHeap(pv));
}
}
}
const ScalarType loc_eps=m.bbox.Diag()/ScalarType(100000);
while( heap.size()!=0 ) // Shortest path tree
{
VertexPointer pv;
std::pop_heap(heap.begin(),heap.end());
if( ! heap.back().is_valid() )
{
heap.pop_back();
continue;
}
pv = heap.back().p;
heap.pop_back();
for(face::VFIterator<FaceType> vfi(pv) ; !vfi.End(); ++vfi )
{
for(int k=0;k<2;++k)
{
VertexPointer pw;
float d;
if(k==0) pw = vfi.f->V1(vfi.z);
else pw = vfi.f->V2(vfi.z);
d = Distance(pv->P(),pw->P());
if( pw->Q()==-1 || pw->Q() > pv->Q()+d + loc_eps)
{
pw->Q() = pv->Q()+d;
heap.push_back(VQualityHeap(pw));
std::push_heap(heap.begin(),heap.end());
}
}
}
}
for(v=m.vert.begin();v!=m.vert.end();++v)
if(v->Q()==-1)
v->Q() = 0;
}
/** Assign to each vertex of the mesh a constant quality value. Useful for initialization.
*/
static void VertexConstant(MeshType &m, float q)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).Q()=q;
}
/** Clamp each vertex of the mesh with a range of values.
*/
static void VertexClamp(MeshType &m, float qmin, float qmax)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).Q()=std::min(qmax, std::max(qmin,(*vi).Q()));
}
/** Normalize the vertex quality so that it fits in the specified range.
*/
static void VertexNormalize(MeshType &m, float qmin=0.0, float qmax=1.0)
{
ScalarType deltaRange = qmax-qmin;
std::pair<ScalarType,ScalarType> minmax = tri::Stat<MeshType>::ComputePerVertexQualityMinMax(m);
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
(*vi).Q() = qmin+deltaRange*((*vi).Q() - minmax.first)/(minmax.second - minmax.first);
}
/** Normalize the face quality so that it fits in the specified range.
*/
static void FaceNormalize(MeshType &m, float qmin=0.0, float qmax=1.0)
{
ScalarType deltaRange = qmax-qmin;
std::pair<ScalarType,ScalarType> minmax = tri::Stat<MeshType>::ComputePerFaceQualityMinMax(m);
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
(*fi).Q() = qmin+deltaRange*((*fi).Q() - minmax.first)/(minmax.second - minmax.first);
}
/** Assign to each face of the mesh a constant quality value. Useful for initialization.
*/
static void FaceConstant(MeshType &m, float q)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
(*fi).Q()=q;
}
static void VertexFromGaussianCurvature(MeshType &m)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).Q() = (*vi).Kg();
}
static void VertexFromMeanCurvature(MeshType &m)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).Q() = (*vi).Kh();
}
/*
* Absolute Curvature
*
* 2|H| if K >= 0
* |k1| + |k2| = <
* 2 * sqrt(|H|^2-K) otherwise
*
* defs and formulas taken from
*
* Improved curvature estimation for watershed segmentation of 3-dimensional meshes
* S Pulla, A Razdan, G Farin - Arizona State University, Tech. Rep, 2001
* and from
* Optimizing 3D triangulations using discrete curvature analysis
* N Dyn, K Hormann, SJ Kim, D Levin - Mathematical Methods for Curves and Surfaces: Oslo, 2000
*/
static void VertexFromAbsoluteCurvature(MeshType &m)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
{
if((*vi).Kg() >= 0)
(*vi).Q() = math::Abs( 2*(*vi).Kh() );
else
(*vi).Q() = 2*math::Sqrt(math::Abs( (*vi).Kh()*(*vi).Kh() - (*vi).Kg()));
}
}
/*
* RMS Curvature = sqrt(4H^2-2K)
* def and formula taken from
*
* Improved curvature estimation for watershed segmentation of 3-dimensional meshes
* S Pulla, A Razdan, G Farin - Arizona State University, Tech. Rep, 2001
*/
static void VertexFromRMSCurvature(MeshType &m)
{
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
(*vi).Q() = math::Sqrt(math::Abs( 4*(*vi).Kh()*(*vi).Kh() - 2*(*vi).Kg()));
}
/*
Saturate the vertex quality so that for each vertex the gradient of the quality is lower than the given threshold value (in absolute value)
The saturation is done in a conservative way (quality is always decreased and never increased)
Note: requires VF adjacency.
*/
static void VertexSaturate(MeshType &m, ScalarType gradientThr=1.0)
{
UpdateFlags<MeshType>::VertexClearV(m);
std::stack<VertexPointer> st;
st.push(&*m.vert.begin());
while(!st.empty())
{
VertexPointer vc = st.top(); // the center
//printf("Stack size %i\n",st.size());
//printf("Pop elem %i %f\n",st.top() - &*m.vert.begin(), st.top()->Q());
st.pop();
vc->SetV();
std::vector<VertexPointer> star;
typename std::vector<VertexPointer>::iterator vvi;
face::VVStarVF<FaceType>(vc,star);
for(vvi=star.begin();vvi!=star.end();++vvi )
{
float &qi = (*vvi)->Q();
float distGeom = Distance((*vvi)->cP(),vc->cP()) / gradientThr;
// Main test if the quality varies more than the geometric displacement we have to lower something.
if( distGeom < fabs(qi - vc->Q()))
{
// center = 0 other=10 -> other =
// center = 10 other=0
if(vc->Q() > qi) // first case: the center of the star has to be lowered (and re-inserted in the queue).
{
//printf("Reinserting center %i \n",vc - &*m.vert.begin());
vc->Q() = qi+distGeom-0.00001f;
assert( distGeom > fabs(qi - vc->Q()));
st.push(vc);
break;
}
else
{
// second case: you have to lower qi, the vertex under examination.
assert( distGeom < fabs(qi - vc->Q()));
assert(vc->Q() < qi);
float newQi = vc->Q() + distGeom -0.00001f;
assert(newQi <= qi);
assert(vc->Q() < newQi);
assert( distGeom > fabs(newQi - vc->Q()) );
// printf("distGeom %f, qi %f, vc->Q() %f, fabs(qi - vc->Q()) %f\n",distGeom,qi,vc->Q(),fabs(qi - vc->Q()));
qi = newQi;
(*vvi)->ClearV();
}
}
if(!(*vvi)->IsV())
{
st.push( *vvi);
// printf("Reinserting side %i \n",*vvi - &*m.vert.begin());
(*vvi)->SetV();
}
}
}
}
}; //end class
} // end namespace
} // end namespace
#endif

View File

@ -0,0 +1,446 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_SELECTION
#define __VCG_TRI_UPDATE_SELECTION
#include <queue>
#include <vcg/complex/trimesh/update/flag.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \brief A stack for saving and restoring selection.
/**
This class is used to save the current selection onto a stack for later use.
\todo it should be generalized to other attributes with a templated approach.
*/
template <class ComputeMeshType>
class SelectionStack
{
typedef typename ComputeMeshType::template PerVertexAttributeHandle< bool > vsHandle;
typedef typename ComputeMeshType::template PerFaceAttributeHandle< bool > fsHandle;
public:
SelectionStack(ComputeMeshType &m)
{
_m=&m;
}
bool push()
{
vsHandle vsH = Allocator<ComputeMeshType>::template AddPerVertexAttribute< bool >(*_m);
fsHandle fsH = Allocator<ComputeMeshType>::template AddPerFaceAttribute< bool > (*_m);
typename ComputeMeshType::VertexIterator vi;
for(vi = _m->vert.begin(); vi != _m->vert.end(); ++vi)
if( !(*vi).IsD() ) vsH[*vi] = (*vi).IsS() ;
typename ComputeMeshType::FaceIterator fi;
for(fi = _m->face.begin(); fi != _m->face.end(); ++fi)
if( !(*fi).IsD() ) fsH[*fi] = (*fi).IsS() ;
vsV.push_back(vsH);
fsV.push_back(fsH);
return true;
}
bool pop()
{
if(vsV.empty()) return false;
vsHandle vsH = vsV.back();
fsHandle fsH = fsV.back();
if(! (Allocator<ComputeMeshType>::template IsValidHandle(*_m, vsH))) return false;
typename ComputeMeshType::VertexIterator vi;
for(vi = _m->vert.begin(); vi != _m->vert.end(); ++vi)
if( !(*vi).IsD() )
if(vsH[*vi]) (*vi).SetS() ;
else (*vi).ClearS() ;
typename ComputeMeshType::FaceIterator fi;
for(fi = _m->face.begin(); fi != _m->face.end(); ++fi)
if( !(*fi).IsD() )
if(fsH[*fi]) (*fi).SetS() ;
else (*fi).ClearS() ;
Allocator<ComputeMeshType>::template DeletePerVertexAttribute<bool>(*_m,vsH);
Allocator<ComputeMeshType>::template DeletePerFaceAttribute<bool>(*_m,fsH);
vsV.pop_back();
fsV.pop_back();
return true;
}
private:
ComputeMeshType *_m;
std::vector<vsHandle> vsV;
std::vector<fsHandle> fsV;
};
/// \ingroup trimesh
/// \headerfile selection.h vcg/complex/trimesh/update/selection.h
/// \brief Management, updating and computation of per-vertex and per-face normals.
/**
This class is used to compute or update the normals that can be stored in the vertex or face component of a mesh.
*/
template <class ComputeMeshType>
class UpdateSelection
{
public:
typedef ComputeMeshType MeshType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename vcg::Box3<ScalarType> Box3Type;
static size_t AllVertex(MeshType &m)
{
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if( !(*vi).IsD() ) (*vi).SetS();
return m.vn;
}
static size_t AllFace(MeshType &m)
{
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() ) (*fi).SetS();
return m.fn;
}
static size_t ClearVertex(MeshType &m)
{
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if( !(*vi).IsD() ) (*vi).ClearS();
return 0;
}
static size_t ClearFace(MeshType &m)
{
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() ) (*fi).ClearS();
return 0;
}
static void Clear(MeshType &m)
{
ClearVertex(m);
ClearFace(m);
}
static size_t CountFace(MeshType &m)
{
size_t selCnt=0;
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD() && (*fi).IsS()) ++selCnt;
return selCnt;
}
static size_t CountVertex(MeshType &m)
{
size_t selCnt=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD() && (*vi).IsS()) ++selCnt;
return selCnt;
}
static size_t InvertFace(MeshType &m)
{
size_t selCnt=0;
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
{
if((*fi).IsS()) (*fi).ClearS();
else {
(*fi).SetS();
++selCnt;
}
}
return selCnt;
}
static size_t InvertVertex(MeshType &m)
{
size_t selCnt=0;
VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD())
{
if((*vi).IsS()) (*vi).ClearS();
else {
(*vi).SetS();
++selCnt;
}
}
return selCnt;
}
/// \brief Select all the vertices that are touched by at least a single selected faces
static size_t VertexFromFaceLoose(MeshType &m)
{
size_t selCnt=0;
ClearVertex(m);
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() && (*fi).IsS())
{
if( !(*fi).V(0)->IsS()) { (*fi).V(0)->SetS(); ++selCnt; }
if( !(*fi).V(1)->IsS()) { (*fi).V(1)->SetS(); ++selCnt; }
if( !(*fi).V(2)->IsS()) { (*fi).V(2)->SetS(); ++selCnt; }
}
return selCnt;
}
/// \brief Select ONLY the vertices that are touched ONLY by selected faces
/** In other words all the vertices having all the faces incident on them selected.
\warning Isolated vertices will not selected.
*/
static size_t VertexFromFaceStrict(MeshType &m)
{
VertexFromFaceLoose(m);
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() && !(*fi).IsS())
{
(*fi).V(0)->ClearS();
(*fi).V(1)->ClearS();
(*fi).V(2)->ClearS();
}
return CountVertex(m);
}
/// \brief Select ONLY the faces with ALL the vertices selected
static size_t FaceFromVertexStrict(MeshType &m)
{
size_t selCnt=0;
ClearFace(m);
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD())
{
if((*fi).V(0)->IsS() && (*fi).V(1)->IsS() && (*fi).V(2)->IsS())
{
(*fi).SetS();
++selCnt;
}
}
return selCnt;
}
/// \brief Select all the faces with at least one selected vertex
static size_t FaceFromVertexLoose(MeshType &m)
{
size_t selCnt=0;
ClearFace(m);
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() && !(*fi).IsS())
{
if((*fi).V(0)->IsS() || (*fi).V(1)->IsS() || (*fi).V(2)->IsS())
{
(*fi).SetS();
++selCnt;
}
}
return selCnt;
}
static size_t VertexFromBorderFlag(MeshType &m)
{
size_t selCnt=0;
ClearVertex(m);
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if( !(*vi).IsD() )
{
if((*vi).IsB() )
{
(*vi).SetS();
++selCnt;
}
}
return selCnt;
}
static size_t FaceFromBorderFlag(MeshType &m)
{
size_t selCnt=0;
ClearFace(m);
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() )
{
if((*fi).IsB(0) || (*fi).IsB(1) || (*fi).IsB(2))
{
(*fi).SetS();
++selCnt;
}
}
return selCnt;
}
/// \brief This function select the faces that have an edge outside the given range.
static size_t FaceOutOfRangeEdge(MeshType &m, ScalarType MinEdgeThr=0, ScalarType MaxEdgeThr=(std::numeric_limits<ScalarType>::max)())
{
FaceIterator fi;
size_t count_fd = 0;
MinEdgeThr=MinEdgeThr*MinEdgeThr;
MaxEdgeThr=MaxEdgeThr*MaxEdgeThr;
for(fi=m.face.begin(); fi!=m.face.end();++fi)
if(!(*fi).IsD())
{
for(unsigned int i=0;i<3;++i)
{
const ScalarType squaredEdge=SquaredDistance((*fi).V0(i)->cP(),(*fi).V1(i)->cP());
if((squaredEdge<=MinEdgeThr) || (squaredEdge>=MaxEdgeThr) )
{
count_fd++;
(*fi).SetS();
break; // skip the rest of the edges of the tri
}
}
}
return count_fd;
}
/// \brief This function expand current selection to cover the whole connected component.
static size_t FaceConnectedFF(MeshType &m)
{
// it also assumes that the FF adjacency is well computed.
assert (HasFFAdjacency(m));
UpdateFlags<MeshType>::FaceClearV(m);
std::deque<FacePointer> visitStack;
size_t selCnt=0;
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if( !(*fi).IsD() && (*fi).IsS() && !(*fi).IsV() )
visitStack.push_back(&*fi);
while(!visitStack.empty())
{
FacePointer fp = visitStack.front();
visitStack.pop_front();
assert(!fp->IsV());
fp->SetV();
for(int i=0;i<3;++i) {
FacePointer ff = fp->FFp(i);
if(! ff->IsS())
{
ff->SetS();
++selCnt;
visitStack.push_back(ff);
assert(!ff->IsV());
}
}
}
return selCnt;
}
/// \brief Select ONLY the faces whose quality is in the specified closed interval.
static size_t FaceFromQualityRange(MeshType &m,float minq, float maxq)
{
size_t selCnt=0;
ClearFace(m);
FaceIterator fi;
assert(HasPerFaceQuality(m));
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
{
if( (*fi).Q()>=minq && (*fi).Q()<=maxq )
{
(*fi).SetS();
++selCnt;
}
}
return selCnt;
}
/// \brief Select ONLY the vertices whose quality is in the specified closed interval.
static size_t VertexFromQualityRange(MeshType &m,float minq, float maxq)
{
size_t selCnt=0;
ClearVertex(m);
VertexIterator vi;
assert(HasPerVertexQuality(m));
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD())
{
if( (*vi).Q()>=minq && (*vi).Q()<=maxq )
{
(*vi).SetS();
++selCnt;
}
}
return selCnt;
}
static int VertexInBox( MeshType & m, const Box3Type &bb)
{
int selCnt=0;
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD())
{
if(bb.IsIn((*vi).cP()) ) {
(*vi).SetS();
++selCnt;
}
}
return selCnt;
}
void VertexNonManifoldEdges(MeshType &m)
{
assert(HasFFTopology(m));
VertexClear(m);
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD())
{
for(int i=0;i<3;++i)
if(!IsManifold(*fi,i)){
(*fi).V0(i)->SetS();
(*fi).V1(i)->SetS();
}
}
}
}; // end class
} // End namespace
} // End namespace
#endif

View File

@ -0,0 +1,105 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: position.h,v $
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_TEXTURE
#define __VCG_TRI_UPDATE_TEXTURE
//#include <vcg/space/plane.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile texture.h vcg/complex/trimesh/update/texture.h
/// \brief This class is used to update vertex position according to a transformation matrix.
template <class ComputeMeshType>
class UpdateTexture
{
public:
typedef ComputeMeshType MeshType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
static void WedgeTexFromPlanar(ComputeMeshType &m, Plane3<ScalarType> &pl)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD())
{
}
}
static void WedgeTexFromCamera(ComputeMeshType &m, Plane3<ScalarType> &pl)
{
}
/// Currently texture coords are kept for ALL the triangles of a mesh. The texture id is stored with each face.
/// if a given face should not have tex coord it has the default -1 value for texture ID.
/// This function will add an new fake texture, add that to the list of textures and change all the -1 id to that value.
static void WedgeTexRemoveNull(ComputeMeshType &m, const std::string &texturename)
{
bool found=false;
FaceIterator fi;
// first loop lets check that there are -1 indexed textured face
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD()) if((*fi).WT(0).N()==-1) found = true;
if(!found) return;
m.textures.push_back(texturename);
int nullId=m.textures.size()-1;
for(fi=m.face.begin();fi!=m.face.end();++fi)
if(!(*fi).IsD()) if((*fi).WT(0).N()==-1)
{
(*fi).WT(0).N() = nullId;
(*fi).WT(1).N() = nullId;
(*fi).WT(2).N() = nullId;
}
}
}; // end class
} // End namespace
} // End namespace
#endif

View File

@ -0,0 +1,489 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.20 2008/04/04 10:27:34 cignoni
minor changes to the topology correctness checks
Revision 1.19 2007/05/29 00:07:06 ponchio
VFi++ -> ++VFi
Revision 1.18 2006/02/27 19:26:14 spinelli
minor bug in Face-Face topology loop fixed
Revision 1.17 2006/02/27 11:56:48 spinelli
minor bug in Face-Face topology loop fixed
Revision 1.16 2005/11/10 15:36:42 cignoni
Added clarifying comment in an assert
Revision 1.15 2004/10/20 07:33:10 cignoni
removed FaceBorderFlags (already present in update/flags.h)
Revision 1.14 2004/10/18 17:10:22 ganovelli
added ::FaceBorderFLags
Revision 1.13 2004/10/01 15:58:00 ponchio
Added include <vector>
Revision 1.12 2004/09/09 13:02:12 ponchio
Linux compatible path in #include
Revision 1.11 2004/08/07 16:18:20 pietroni
addet testFFTopology and testVFTopology functions used to test the rispective topology....
Revision 1.10 2004/07/15 11:35:08 ganovelli
Vfb to VFp
Revision 1.9 2004/07/15 00:13:39 cignoni
Better doxigen documentation
Revision 1.8 2004/06/02 16:42:44 ganovelli
typename for gcc compilation
Revision 1.7 2004/06/02 16:28:22 ganovelli
minor changes (swap =>> math::Swap)
Revision 1.6 2004/05/10 15:23:43 cignoni
Changed a FV -> VF in VertexFace topology computation
Revision 1.5 2004/05/06 15:24:38 pietroni
changed names to topology functions
Revision 1.4 2004/03/31 14:44:43 cignoni
Added Vertex-Face Topology
Revision 1.3 2004/03/12 15:22:19 cignoni
Written some documentation and added to the trimes doxygen module
Revision 1.2 2004/03/05 21:49:21 cignoni
First working version for face face
Revision 1.1 2004/03/04 00:53:24 cignoni
Initial commit
****************************************************************************/
#ifndef __VCG_TRI_UPDATE_TOPOLOGY
#define __VCG_TRI_UPDATE_TOPOLOGY
#include <algorithm>
#include <vector>
#include <vcg/simplex/face/pos.h>
#include <vcg/complex/trimesh/base.h>
namespace vcg {
namespace tri {
/// \ingroup trimesh
/// \headerfile topology.h vcg/complex/trimesh/update/topology.h
/// \brief Generation of per-vertex and per-face topological information.
template <class UpdateMeshType>
class UpdateTopology
{
public:
typedef UpdateMeshType MeshType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
/// \headerfile topology.h vcg/complex/trimesh/update/topology.h
/// \brief Auxiliairy data structure for computing face face adjacency information.
/**
It identifies and edge storing two vertex pointer and a face pointer where it belong.
*/
class PEdge
{
public:
VertexPointer v[2]; // the two Vertex pointer are ordered!
FacePointer f; // the face where this edge belong
int z; // index in [0..2] of the edge of the face
PEdge() {}
void Set( FacePointer pf, const int nz )
{
assert(pf!=0);
assert(nz>=0);
assert(nz<pf->VN());
v[0] = pf->V(nz);
v[1] = pf->V(pf->Next(nz));
assert(v[0] != v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes)
if( v[0] > v[1] ) math::Swap(v[0],v[1]);
f = pf;
z = nz;
}
inline bool operator < ( const PEdge & pe ) const
{
if( v[0]<pe.v[0] ) return true;
else if( v[0]>pe.v[0] ) return false;
else return v[1] < pe.v[1];
}
inline bool operator == ( const PEdge & pe ) const
{
return v[0]==pe.v[0] && v[1]==pe.v[1];
}
};
// Fill a vector with all the edges of the mesh.
// each edge is stored in the vector the number of times that it appears in the mesh, with the referring face.
// optionally it can skip the faux edges (to retrieve only the real edges of a triangulated polygonal mesh)
static void FillEdgeVector(MeshType &m, std::vector<PEdge> &e, bool includeFauxEdge=true)
{
FaceIterator pf;
typename std::vector<PEdge>::iterator p;
// Alloco il vettore ausiliario
//e.resize(m.fn*3);
FaceIterator fi;
int n_edges = 0;
for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN();
e.resize(n_edges);
p = e.begin();
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( ! (*pf).IsD() )
for(int j=0;j<(*pf).VN();++j)
if(includeFauxEdge || !(*pf).IsF(j))
{
(*p).Set(&(*pf),j);
++p;
}
if(includeFauxEdge) assert(p==e.end());
else e.resize(p-e.begin());
}
static void FillUniqueEdgeVector(MeshType &m, std::vector<PEdge> &Edges, bool includeFauxEdge=true)
{
FillEdgeVector(m,Edges,includeFauxEdge);
sort(Edges.begin(), Edges.end()); // Lo ordino per vertici
typename std::vector< PEdge>::iterator newEnd = std::unique(Edges.begin(), Edges.end());
typename std::vector<PEdge>::iterator ei;
Edges.resize(newEnd-Edges.begin());
}
/// \brief Update the Face-Face topological relation by allowing to retrieve for each face what other faces shares their edges.
static void FaceFace(MeshType &m)
{
assert(HasFFAdjacency(m));
if( m.fn == 0 ) return;
std::vector<PEdge> e;
FillEdgeVector(m,e);
sort(e.begin(), e.end()); // Lo ordino per vertici
int ne = 0; // Numero di edge reali
typename std::vector<PEdge>::iterator pe,ps;
ps = e.begin();pe=e.begin();
//for(ps = e.begin(),pe=e.begin();pe<=e.end();++pe) // Scansione vettore ausiliario
do
{
if( pe==e.end() || !(*pe == *ps) ) // Trovo blocco di edge uguali
{
typename std::vector<PEdge>::iterator q,q_next;
for (q=ps;q<pe-1;++q) // Scansione facce associate
{
assert((*q).z>=0);
//assert((*q).z< 3);
q_next = q;
++q_next;
assert((*q_next).z>=0);
assert((*q_next).z< (*q_next).f->VN());
(*q).f->FFp(q->z) = (*q_next).f; // Collegamento in lista delle facce
(*q).f->FFi(q->z) = (*q_next).z;
}
assert((*q).z>=0);
assert((*q).z< (*q).f->VN());
(*q).f->FFp((*q).z) = ps->f;
(*q).f->FFi((*q).z) = ps->z;
ps = pe;
++ne; // Aggiorno il numero di edge
}
if(pe==e.end()) break;
++pe;
} while(true);
}
/// \brief Update the Vertex-Face topological relation.
/**
The function allows to retrieve for each vertex the list of faces sharing this vertex.
*/
static void VertexFace(MeshType &m)
{
if(!m.HasVFTopology()) return;
VertexIterator vi;
FaceIterator fi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
{
(*vi).VFp() = 0;
(*vi).VFi() = 0;
}
for(fi=m.face.begin();fi!=m.face.end();++fi)
if( ! (*fi).IsD() )
{
for(int j=0;j<(*fi).VN();++j)
{
(*fi).VFp(j) = (*fi).V(j)->VFp();
(*fi).VFi(j) = (*fi).V(j)->VFi();
(*fi).V(j)->VFp() = &(*fi);
(*fi).V(j)->VFi() = j;
}
}
}
/// \headerfile topology.h vcg/complex/trimesh/update/topology.h
/// \brief Auxiliairy data structure for computing face face adjacency information.
/**
It identifies and edge storing two vertex pointer and a face pointer where it belong.
*/
class PEdgeTex
{
public:
typename FaceType::TexCoordType v[2]; // the two Vertex pointer are ordered!
FacePointer f; // the face where this edge belong
int z; // index in [0..2] of the edge of the face
PEdgeTex() {}
void Set( FacePointer pf, const int nz )
{
assert(pf!=0);
assert(nz>=0);
assert(nz<3);
v[0] = pf->WT(nz);
v[1] = pf->WT(pf->Next(nz));
assert(v[0] != v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes)
if( v[1] < v[0] ) std::swap(v[0],v[1]);
f = pf;
z = nz;
}
inline bool operator < ( const PEdgeTex & pe ) const
{
if( v[0]<pe.v[0] ) return true;
else if( pe.v[0]<v[0] ) return false;
else return v[1] < pe.v[1];
}
inline bool operator == ( const PEdgeTex & pe ) const
{
return (v[0]==pe.v[0]) && (v[1]==pe.v[1]);
}
inline bool operator != ( const PEdgeTex & pe ) const
{
return (v[0]!=pe.v[0]) || (v[1]!=pe.v[1]);
}
};
/// \brief Update the Face-Face topological relation
/**
The function allows to retrieve for each face what other faces shares their edges.
*/
static void FaceFaceFromTexCoord(MeshType &m)
{
// assert(HasFFTopology(m));
assert(HasPerWedgeTexCoord(m));
std::vector<PEdgeTex> e;
FaceIterator pf;
typename std::vector<PEdgeTex>::iterator p;
if( m.fn == 0 ) return;
// e.resize(m.fn*3); // Alloco il vettore ausiliario
FaceIterator fi;
int n_edges = 0;
for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(! (*fi).IsD()) n_edges+=(*fi).VN();
e.resize(n_edges);
p = e.begin();
for(pf=m.face.begin();pf!=m.face.end();++pf) // Lo riempio con i dati delle facce
if( ! (*pf).IsD() )
for(int j=0;j<(*pf).VN();++j)
{
if( (*pf).WT(j) != (*pf).WT((*pf).Next(j)))
{
(*p).Set(&(*pf),j);
++p;
}
}
e.resize(p-e.begin()); // remove from the end of the edge vector the unitiailized ones
assert(p==e.end());
sort(e.begin(), e.end());
int ne = 0; // number of real edges
typename std::vector<PEdgeTex>::iterator pe,ps;
ps = e.begin();pe=e.begin();
//for(ps = e.begin(),pe=e.begin();pe<=e.end();++pe) // Scansione vettore ausiliario
do
{
if( pe==e.end() || (*pe) != (*ps) ) // Trovo blocco di edge uguali
{
typename std::vector<PEdgeTex>::iterator q,q_next;
for (q=ps;q<pe-1;++q) // Scansione facce associate
{
assert((*q).z>=0);
assert((*q).z< 3);
q_next = q;
++q_next;
assert((*q_next).z>=0);
assert((*q_next).z< (*q_next).f->VN());
(*q).f->FFp(q->z) = (*q_next).f; // Collegamento in lista delle facce
(*q).f->FFi(q->z) = (*q_next).z;
}
assert((*q).z>=0);
assert((*q).z< (*q).f->VN());
(*q).f->FFp((*q).z) = ps->f;
(*q).f->FFi((*q).z) = ps->z;
ps = pe;
++ne; // Aggiorno il numero di edge
}
if(pe==e.end()) break;
++pe;
} while(true);
}
/// \brief Test correctness of VFtopology
static void TestVertexFace(MeshType &m)
{
SimpleTempData<typename MeshType::VertContainer, int > numVertex(m.vert,0);
if(!m.HasVFTopology()) return;
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi)
{
if (!(*fi).IsD())
{
numVertex[(*fi).V0(0)]++;
numVertex[(*fi).V1(0)]++;
numVertex[(*fi).V2(0)]++;
}
}
VertexIterator vi;
vcg::face::VFIterator<FaceType> VFi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
{
if (!vi->IsD())
if(vi->VFp()!=0) // unreferenced vertices MUST have VF == 0;
{
int num=0;
assert(vi->VFp() >= &*m.face.begin());
assert(vi->VFp() <= &m.face.back());
VFi.f=vi->VFp();
VFi.z=vi->VFi();
while (!VFi.End())
{
num++;
assert(!VFi.F()->IsD());
assert((VFi.F()->V(VFi.I()))==&(*vi));
++VFi;
}
int num1=numVertex[&(*vi)];
assert(num==num1);
/*assert(num>1);*/
}
}
}
/// \brief Test correctness of FFtopology (only for 2Manifold Meshes!)
static void TestFaceFace(MeshType &m)
{
if(!m.HasFFTopology()) return;
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
{
if (!fi->IsD())
{
for (int i=0;i<(*fi).VN();i++)
{
FaceType *ffpi=fi->FFp(i);
int e=fi->FFi(i);
//invariant property of FF topology for two manifold meshes
assert(ffpi->FFp(e) == &(*fi));
assert(ffpi->FFi(e) == i);
// Test that the two faces shares the same edge
// Vertices of the i-th edges of the first face
VertexPointer v0i= fi->V0(i);
VertexPointer v1i= fi->V1(i);
// Vertices of the corresponding edge on the other face
VertexPointer ffv0i= ffpi->V0(e);
VertexPointer ffv1i= ffpi->V1(e);
assert( (ffv0i==v0i) || (ffv0i==v1i) );
assert( (ffv1i==v0i) || (ffv1i==v1i) );
}
}
}
}
}; // end class
} // End namespace
} // End namespace
#endif