vcglib/vcg/complex/algorithms/clustering.h

362 lines
12 KiB
C
Raw Normal View History

2011-04-01 18:25:49 +02:00
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004-2016 \/)\/ *
* 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. *
* *
****************************************************************************/
2011-04-01 18:25:49 +02:00
#ifndef __VCGLIB_CLUSTERING
#define __VCGLIB_CLUSTERING
#include<vcg/complex/complex.h>
#include <vcg/complex/algorithms/clean.h>
2011-04-01 18:25:49 +02:00
#include<vcg/space/triangle3.h>
#include<vcg/space/index/grid_util.h>
#include <iostream>
#include <math.h>
#include <unordered_set>
#include <unordered_map>
2011-04-01 18:25:49 +02:00
namespace std
{
template<>
struct hash<vcg::Point3i>
{
typedef vcg::Point3i argument_type;
2011-04-01 18:25:49 +02:00
std::size_t operator()(const vcg::Point3i & s) const
{
return std::hash<int>()(s[0]) ^ std::hash<int>()(s[1]) ^ std::hash<int>()(s[2]);
}
};
}
2011-04-01 18:25:49 +02:00
namespace vcg{
namespace tri{
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;
2011-04-01 18:25:49 +02:00
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
2011-04-01 18:25:49 +02:00
{
assert(valid);
return bestPos;
2011-04-01 18:25:49 +02:00
}
Color4b Col() const {return Color4b::White;}
CoordType N() const {return bestN;}
VertexType * Ptr() const {return orig;}
2011-04-01 18:25:49 +02:00
};
template<class MeshType>
class AverageColorCell
{
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::VertexType VertexType;
2011-04-01 18:25:49 +02:00
typedef BasicGrid<typename MeshType::ScalarType> GridType;
2011-04-01 18:25:49 +02:00
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]);
2011-04-01 18:25:49 +02:00
// 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();
if(tri::HasPerVertexColor(m))
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;
2011-04-01 18:25:49 +02:00
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; }
2011-04-01 18:25:49 +02:00
};
/*
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
2011-04-01 18:25:49 +02:00
{
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]);
}
2011-04-01 18:25:49 +02:00
// 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!
}
bool operator ==(const SimpleTri &pt) const
{
return (pt.v[0] == v[0])
&& (pt.v[1] == v[1])
&& (pt.v[2] == v[2]);
}
2011-04-01 18:25:49 +02:00
// Hashing Function;
size_t operator () (const SimpleTri &pt) const
2011-04-01 18:25:49 +02:00
{
// return (ii(0)*HASH_P0 ^ ii(1)*HASH_P1 ^ ii(2)*HASH_P2);
return std::hash<CellType*>()(pt.v[0]) ^ std::hash<CellType*>()(pt.v[1]) ^ std::hash<CellType*>()(pt.v[2]);
2011-04-01 18:25:49 +02:00
}
};
// 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
2012-10-08 11:05:11 +02:00
// _cellsize is the absolute length of the edge of the grid cell.
2011-04-01 18:25:49 +02:00
// 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
2012-10-08 11:05:11 +02:00
// during the simplification (e.g. half of the cell edge length)
2011-04-01 18:25:49 +02:00
void Init(Box3<ScalarType> _mbb, int _size, ScalarType _cellsize=0)
{
GridCell.clear();
TriSet.clear();
2011-04-01 18:25:49 +02:00
Grid.bbox=_mbb;
///inflate the bb calculated
2011-04-01 18:25:49 +02:00
ScalarType infl = (_cellsize == (ScalarType)0) ? (Grid.bbox.Diag() / _size) : (_cellsize);
Grid.bbox.min-=CoordType(infl,infl,infl);
Grid.bbox.max+=CoordType(infl,infl,infl);
2011-04-01 18:25:49 +02:00
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];
2011-04-01 18:25:49 +02:00
}
BasicGrid<ScalarType> Grid;
std::unordered_set<SimpleTri,SimpleTri> TriSet;
typedef typename std::unordered_set<SimpleTri,SimpleTri>::iterator TriHashSetIterator;
std::unordered_map<Point3i,CellType> GridCell;
2011-04-01 18:25:49 +02:00
void AddPointSet(MeshType &m, bool UseOnlySelected=false)
{
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD())
if(!UseOnlySelected || (*vi).IsS())
{
Point3i pi;
Grid.PToIP((*vi).cP(), pi );
GridCell[pi].AddVertex(m,Grid,pi,*(vi));
}
}
2011-04-01 18:25:49 +02:00
void AddMesh(MeshType &m)
{
FaceIterator fi;
for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
{
Point3i 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();
2011-04-01 18:25:49 +02:00
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());
}
2011-04-01 18:25:49 +02:00
}
int CountPointSet() {return GridCell.size(); }
void SelectPointSet(MeshType &m)
{
typename std::unordered_map<Point3i,CellType>::iterator gi;
UpdateSelection<MeshType>::VertexClear(m);
for(gi=GridCell.begin();gi!=GridCell.end();++gi)
2011-04-01 18:25:49 +02:00
{
VertexType *ptr=(*gi).second.Ptr();
if(ptr && ( ptr >= &*m.vert.begin() ) && ( ptr <= &*(m.vert.end() - 1) ) )
ptr->SetS();
}
2011-04-01 18:25:49 +02:00
}
void ExtractPointSet(MeshType &m)
{
m.Clear();
if (GridCell.empty()) return;
2011-04-01 18:25:49 +02:00
Allocator<MeshType>::AddVertices(m,GridCell.size());
typename std::unordered_map<Point3i,CellType>::iterator gi;
2011-04-01 18:25:49 +02:00
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();
if(HasPerVertexColor(m))
m.vert[i].C()=(*gi).second.Col();
++i;
2011-04-01 18:25:49 +02:00
}
2011-04-01 18:25:49 +02:00
}
void ExtractMesh(MeshType &m)
{
m.Clear();
if (GridCell.empty()) return;
2011-04-01 18:25:49 +02:00
Allocator<MeshType>::AddVertices(m,GridCell.size());
typename std::unordered_map<Point3i,CellType>::iterator gi;
2011-04-01 18:25:49 +02:00
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();
if(HasPerVertexColor(m))
2011-04-01 18:25:49 +02:00
m.vert[i].C()=(*gi).second.Col();
(*gi).second.id=i;
++i;
}
2011-04-01 18:25:49 +02:00
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=TriangleNormal(m.face[i]);
2011-04-01 18:25:49 +02:00
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