2011-10-21 00:26:46 +02:00
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright ( C ) 2005 \ / ) \ / *
* Visual Computing Lab / \ / | *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved . *
* *
2013-07-05 16:46:48 +02:00
* This program is free software ; you can redistribute it and / or modify *
2011-10-21 00:26:46 +02:00
* 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 VORONOI_PROCESSING_H
# define VORONOI_PROCESSING_H
# include <vcg/complex/algorithms/geodesic.h>
# include <vcg/complex/algorithms/update/color.h>
namespace vcg
{
namespace tri
{
template < class MeshType >
class ClusteringSampler
2013-07-05 16:46:48 +02:00
{
public :
typedef typename MeshType : : VertexType VertexType ;
ClusteringSampler ( std : : vector < VertexType * > & _vec ) : sampleVec ( _vec )
{
sampleVec = _vec ;
}
std : : vector < VertexType * > & sampleVec ;
void AddVert ( const VertexType & p )
{
sampleVec . push_back ( ( VertexType * ) ( & p ) ) ;
}
} ; // end class ClusteringSampler
2011-10-21 00:26:46 +02:00
template < class MeshType >
class VoronoiProcessing
{
2013-07-05 16:46:48 +02:00
typedef typename MeshType : : CoordType CoordType ;
2011-10-21 00:26:46 +02:00
typedef typename MeshType : : ScalarType ScalarType ;
2013-07-05 16:46:48 +02:00
typedef typename MeshType : : VertexType VertexType ;
2011-10-21 00:26:46 +02:00
typedef typename MeshType : : VertexPointer VertexPointer ;
typedef typename MeshType : : VertexIterator VertexIterator ;
typedef typename MeshType : : FacePointer FacePointer ;
typedef typename MeshType : : FaceIterator FaceIterator ;
typedef typename MeshType : : FaceType FaceType ;
typedef typename MeshType : : FaceContainer FaceContainer ;
2013-07-05 16:46:48 +02:00
public :
2011-10-21 00:26:46 +02:00
// Given a vector of point3f it finds the closest vertices on the mesh.
static void SeedToVertexConversion ( MeshType & m , std : : vector < CoordType > & seedPVec , std : : vector < VertexType * > & seedVVec )
{
typedef typename vcg : : SpatialHashTable < VertexType , ScalarType > HashVertexGrid ;
seedVVec . clear ( ) ;
HashVertexGrid HG ;
HG . Set ( m . vert . begin ( ) , m . vert . end ( ) ) ;
const float dist_upper_bound = m . bbox . Diag ( ) / 10.0 ;
typename std : : vector < CoordType > : : iterator pi ;
for ( pi = seedPVec . begin ( ) ; pi ! = seedPVec . end ( ) ; + + pi )
{
float dist ;
VertexPointer vp ;
vp = tri : : GetClosestVertex < MeshType , HashVertexGrid > ( m , HG , * pi , dist_upper_bound , dist ) ;
if ( vp )
{
seedVVec . push_back ( vp ) ;
}
}
}
typedef typename MeshType : : template PerVertexAttributeHandle < VertexPointer > PerVertexPointerHandle ;
2012-07-02 18:41:28 +02:00
typedef typename MeshType : : template PerFaceAttributeHandle < VertexPointer > PerFacePointerHandle ;
2011-10-21 00:26:46 +02:00
static void ComputePerVertexSources ( MeshType & m , std : : vector < VertexType * > & seedVec )
{
tri : : Allocator < MeshType > : : DeletePerVertexAttribute ( m , " sources " ) ; // delete any conflicting handle regardless of the type...
2012-07-02 18:41:28 +02:00
PerVertexPointerHandle vertexSources = tri : : Allocator < MeshType > : : template AddPerVertexAttribute < VertexPointer > ( m , " sources " ) ;
2012-11-07 02:07:08 +01:00
2012-07-02 18:41:28 +02:00
tri : : Allocator < MeshType > : : DeletePerFaceAttribute ( m , " sources " ) ; // delete any conflicting handle regardless of the type...
PerFacePointerHandle faceSources = tri : : Allocator < MeshType > : : template AddPerFaceAttribute < VertexPointer > ( m , " sources " ) ;
2012-11-07 02:07:08 +01:00
2012-07-02 18:41:28 +02:00
assert ( tri : : Allocator < MeshType > : : IsValidHandle ( m , vertexSources ) ) ;
2012-11-07 02:07:08 +01:00
tri : : Geodesic < MeshType > : : Compute ( m , seedVec , std : : numeric_limits < ScalarType > : : max ( ) , 0 , & vertexSources ) ;
2011-10-21 00:26:46 +02:00
}
static void VoronoiColoring ( MeshType & m , std : : vector < VertexType * > & seedVec , bool frontierFlag = true )
{
PerVertexPointerHandle sources = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < VertexPointer > ( m , " sources " ) ;
assert ( tri : : Allocator < MeshType > : : IsValidHandle ( m , sources ) ) ;
2012-11-07 02:07:08 +01:00
tri : : Geodesic < MeshType > g ;
2011-10-21 00:26:46 +02:00
VertexPointer farthest ;
2013-07-05 16:46:48 +02:00
if ( frontierFlag )
{
//static_cast<VertexPointer>(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers.
//The error should have been removed from MSVS2012
std : : pair < float , VertexPointer > zz ( 0.0f , static_cast < VertexPointer > ( NULL ) ) ;
std : : vector < std : : pair < float , VertexPointer > > regionArea ( m . vert . size ( ) , zz ) ;
std : : vector < VertexPointer > borderVec ;
GetAreaAndFrontier ( m , sources , regionArea , borderVec ) ;
tri : : Geodesic < MeshType > : : Compute ( m , borderVec ) ;
}
2011-10-21 00:26:46 +02:00
2013-07-05 16:46:48 +02:00
tri : : UpdateColor < MeshType > : : PerVertexQualityRamp ( m ) ;
2011-10-21 00:26:46 +02:00
}
2012-07-03 11:14:17 +02:00
// It associates the faces with a given vertex according to the vertex associations
//
// It READS the PerVertex attribute 'sources'
// It WRITES the PerFace attribute 'sources'
2012-07-02 18:41:28 +02:00
static void FaceAssociateRegion ( MeshType & m )
2011-10-21 00:26:46 +02:00
{
2012-07-02 18:41:28 +02:00
PerFacePointerHandle faceSources = tri : : Allocator < MeshType > : : template GetPerFaceAttribute < VertexPointer > ( m , " sources " ) ;
PerVertexPointerHandle vertexSources = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < VertexPointer > ( m , " sources " ) ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
{
faceSources [ fi ] = 0 ;
std : : vector < VertexPointer > vp ( 3 ) ;
for ( int i = 0 ; i < 3 ; + + i ) vp [ i ] = vertexSources [ fi - > V ( i ) ] ;
2012-07-03 11:14:17 +02:00
for ( int i = 0 ; i < 3 ; + + i ) // First try to associate to the most reached vertex
2012-07-02 18:41:28 +02:00
{
if ( vp [ 0 ] = = vp [ 1 ] & & vp [ 0 ] = = vp [ 2 ] ) faceSources [ fi ] = vp [ 0 ] ;
else
{
if ( vp [ 0 ] = = vp [ 1 ] & & vp [ 0 ] - > Q ( ) < vp [ 2 ] - > Q ( ) ) faceSources [ fi ] = vp [ 0 ] ;
if ( vp [ 0 ] = = vp [ 2 ] & & vp [ 0 ] - > Q ( ) < vp [ 1 ] - > Q ( ) ) faceSources [ fi ] = vp [ 0 ] ;
if ( vp [ 1 ] = = vp [ 2 ] & & vp [ 1 ] - > Q ( ) < vp [ 0 ] - > Q ( ) ) faceSources [ fi ] = vp [ 1 ] ;
}
}
}
tri : : UpdateTopology < MeshType > : : FaceFace ( m ) ;
int unassCnt = 0 ;
do
{
unassCnt = 0 ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
{
if ( faceSources [ fi ] = = 0 )
{
std : : vector < VertexPointer > vp ( 3 ) ;
for ( int i = 0 ; i < 3 ; + + i )
vp [ i ] = faceSources [ fi - > FFp ( i ) ] ;
if ( vp [ 0 ] ! = 0 & & ( vp [ 0 ] = = vp [ 1 ] | | vp [ 0 ] = = vp [ 2 ] ) )
faceSources [ fi ] = vp [ 0 ] ;
else if ( vp [ 1 ] ! = 0 & & ( vp [ 1 ] = = vp [ 2 ] ) )
faceSources [ fi ] = vp [ 1 ] ;
else
faceSources [ fi ] = std : : max ( vp [ 0 ] , std : : max ( vp [ 1 ] , vp [ 2 ] ) ) ;
if ( faceSources [ fi ] = = 0 ) unassCnt + + ;
}
}
}
while ( unassCnt > 0 ) ;
}
2012-07-03 11:14:17 +02:00
// Select all the faces with a given source vertex <vp>
// It reads the PerFace attribute 'sources'
2012-07-02 18:41:28 +02:00
static int FaceSelectAssociateRegion ( MeshType & m , VertexPointer vp )
{
2013-01-30 18:18:55 +01:00
PerFacePointerHandle sources = tri : : Allocator < MeshType > : : template FindPerFaceAttribute < VertexPointer > ( m , " sources " ) ;
2011-10-21 00:26:46 +02:00
assert ( tri : : Allocator < MeshType > : : IsValidHandle ( m , sources ) ) ;
2012-07-03 11:14:17 +02:00
tri : : UpdateSelection < MeshType > : : Clear ( m ) ;
2012-07-02 18:41:28 +02:00
int selCnt = 0 ;
2011-10-21 00:26:46 +02:00
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
{
2012-07-02 18:41:28 +02:00
if ( sources [ fi ] = = vp )
{
2011-10-21 00:26:46 +02:00
fi - > SetS ( ) ;
2012-07-02 18:41:28 +02:00
+ + selCnt ;
}
2011-10-21 00:26:46 +02:00
}
2012-07-02 18:41:28 +02:00
return selCnt ;
}
2011-10-21 00:26:46 +02:00
2012-07-03 11:14:17 +02:00
// Given a seed <vp>, it selects all the faces that have the minimal distance vertex sourced by the given <vp>.
// <vp> can be null (it search for unreached faces...)
2012-07-02 18:41:28 +02:00
// returns the number of selected faces;
2012-07-03 11:14:17 +02:00
//
// It reads the PerVertex attribute 'sources'
2012-07-02 18:41:28 +02:00
static int FaceSelectRegion ( MeshType & m , VertexPointer vp )
{
PerVertexPointerHandle sources = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < VertexPointer > ( m , " sources " ) ;
assert ( tri : : Allocator < MeshType > : : IsValidHandle ( m , sources ) ) ;
2012-07-03 11:14:17 +02:00
tri : : UpdateSelection < MeshType > : : Clear ( m ) ;
2012-07-02 18:41:28 +02:00
int selCnt = 0 ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
{
int minInd = 0 ; float minVal = std : : numeric_limits < float > : : max ( ) ;
for ( int i = 0 ; i < 3 ; + + i )
{
if ( ( * fi ) . V ( i ) - > Q ( ) < minVal )
{
minInd = i ;
minVal = ( * fi ) . V ( i ) - > Q ( ) ;
}
}
if ( sources [ ( * fi ) . V ( minInd ) ] = = vp )
{
fi - > SetS ( ) ;
selCnt + + ;
}
}
return selCnt ;
2011-10-21 00:26:46 +02:00
}
// find the vertexes of frontier faces
// and compute Area of all the regions
static void GetAreaAndFrontier ( MeshType & m , PerVertexPointerHandle & sources ,
std : : vector < std : : pair < float , VertexPointer > > & regionArea ,
std : : vector < VertexPointer > & borderVec )
{
2013-07-05 16:46:48 +02:00
tri : : UpdateFlags < MeshType > : : VertexClearV ( m ) ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
{
if ( sources [ ( * fi ) . V ( 0 ) ] ! = sources [ ( * fi ) . V ( 1 ) ] | |
sources [ ( * fi ) . V ( 0 ) ] ! = sources [ ( * fi ) . V ( 2 ) ] )
{
for ( int i = 0 ; i < 3 ; + + i )
{
( * fi ) . V ( i ) - > SetV ( ) ;
( * fi ) . V ( i ) - > C ( ) = Color4b : : Black ;
}
}
else // the face belongs to a single region; accumulate area;
{
if ( sources [ ( * fi ) . V ( 0 ) ] ! = 0 )
{
int seedIndex = sources [ ( * fi ) . V ( 0 ) ] - & * m . vert . begin ( ) ;
regionArea [ seedIndex ] . first + = DoubleArea ( * fi ) ;
regionArea [ seedIndex ] . second = sources [ ( * fi ) . V ( 0 ) ] ;
}
}
}
// Collect the frontier vertexes
borderVec . clear ( ) ;
for ( VertexIterator vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
if ( ( * vi ) . IsV ( ) ) borderVec . push_back ( & * vi ) ;
2011-10-21 00:26:46 +02:00
}
static void VoronoiRelaxing ( MeshType & m , std : : vector < VertexType * > & seedVec , int relaxIter , int /*percentileClamping*/ , vcg : : CallBackPos * cb = 0 )
2013-07-05 16:46:48 +02:00
{
tri : : RequireVFAdjacency ( m ) ;
typename MeshType : : template PerVertexAttributeHandle < VertexPointer > sources ;
sources = tri : : Allocator < MeshType > : : template AddPerVertexAttribute < VertexPointer > ( m , " sources " ) ;
for ( int iter = 0 ; iter < relaxIter ; + + iter )
{
if ( cb ) cb ( iter * 100 / relaxIter , " Voronoi Lloyd Relaxation: First Partitioning " ) ;
// first run: find for each point what is the closest to one of the seeds.
tri : : Geodesic < MeshType > : : Compute ( m , seedVec , std : : numeric_limits < ScalarType > : : max ( ) , 0 , & sources ) ;
// Delete all the (hopefully) small regions that have not been reached by the seeds;
tri : : UpdateFlags < MeshType > : : VertexClearV ( m ) ;
for ( int i = 0 ; i < m . vert . size ( ) ; + + i )
if ( sources [ i ] = = 0 ) m . vert [ i ] . SetV ( ) ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
if ( fi - > V ( 0 ) - > IsV ( ) | | fi - > V ( 1 ) - > IsV ( ) | | fi - > V ( 2 ) - > IsV ( ) )
{
face : : VFDetach ( * fi ) ;
tri : : Allocator < MeshType > : : DeleteFace ( m , * fi ) ;
}
// qDebug("Deleted faces not reached: %i -> %i",int(m.face.size()),m.fn);
tri : : Clean < MeshType > : : RemoveUnreferencedVertex ( m ) ;
tri : : Allocator < MeshType > : : CompactFaceVector ( m ) ;
tri : : Allocator < MeshType > : : CompactVertexVector ( m ) ;
//static_cast<VertexPointer>(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers.
//The error should have been removed from MSVS2012
std : : pair < float , VertexPointer > zz ( 0.0f , static_cast < VertexPointer > ( NULL ) ) ;
std : : vector < std : : pair < float , VertexPointer > > regionArea ( m . vert . size ( ) , zz ) ;
std : : vector < VertexPointer > borderVec ;
GetAreaAndFrontier ( m , sources , regionArea , borderVec ) ;
// Smaller area region are discarded
Distribution < float > H ;
2011-10-21 00:26:46 +02:00
for ( size_t i = 0 ; i < regionArea . size ( ) ; + + i )
2013-07-05 16:46:48 +02:00
if ( regionArea [ i ] . second ) H . Add ( regionArea [ i ] . first ) ;
float areaThreshold ;
if ( iter = = 0 ) areaThreshold = H . Percentile ( .1f ) ;
else areaThreshold = H . Percentile ( .001f ) ;
//qDebug("We have found %i regions range (%f %f), avg area is %f, Variance is %f 10perc is %f",(int)seedVec.size(),H.Min(),H.Max(),H.Avg(),H.StandardDeviation(),areaThreshold);
if ( cb ) cb ( iter * 100 / relaxIter , " Voronoi Lloyd Relaxation: Searching New Seeds " ) ;
tri : : Geodesic < MeshType > : : Compute ( m , borderVec ) ;
tri : : UpdateColor < MeshType > : : PerVertexQualityRamp ( m ) ;
// Search the local maxima for each region and use them as new seeds
std : : vector < std : : pair < float , VertexPointer > > seedMaxima ( m . vert . size ( ) , zz ) ;
for ( VertexIterator vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
{
int seedIndex = tri : : Index ( m , sources [ vi ] ) ;
if ( seedMaxima [ seedIndex ] . first < ( * vi ) . Q ( ) )
{
seedMaxima [ seedIndex ] . first = ( * vi ) . Q ( ) ;
seedMaxima [ seedIndex ] . second = & * vi ;
}
}
std : : vector < VertexPointer > newSeeds ;
2011-10-21 00:26:46 +02:00
for ( size_t i = 0 ; i < seedMaxima . size ( ) ; + + i )
2013-07-05 16:46:48 +02:00
if ( seedMaxima [ i ] . second )
{
seedMaxima [ i ] . second - > C ( ) = Color4b : : Gray ;
if ( regionArea [ i ] . first > = areaThreshold )
newSeeds . push_back ( seedMaxima [ i ] . second ) ;
}
tri : : UpdateColor < MeshType > : : PerVertexQualityRamp ( m ) ;
2011-10-21 00:26:46 +02:00
for ( size_t i = 0 ; i < seedVec . size ( ) ; + + i )
2013-07-05 16:46:48 +02:00
seedVec [ i ] - > C ( ) = Color4b : : Black ;
2011-10-21 00:26:46 +02:00
for ( size_t i = 0 ; i < borderVec . size ( ) ; + + i )
2013-07-05 16:46:48 +02:00
borderVec [ i ] - > C ( ) = Color4b : : Gray ;
swap ( newSeeds , seedVec ) ;
2011-10-21 00:26:46 +02:00
for ( size_t i = 0 ; i < seedVec . size ( ) ; + + i )
2013-07-05 16:46:48 +02:00
seedVec [ i ] - > C ( ) = Color4b : : White ;
}
tri : : Allocator < MeshType > : : DeletePerVertexAttribute ( m , " sources " ) ;
2011-10-21 00:26:46 +02:00
}
2013-07-05 16:46:48 +02:00
2011-10-21 00:26:46 +02:00
// Base vertex voronoi coloring algorithm.
2013-07-05 16:46:48 +02:00
// it assumes VF adjacency. No attempt of computing real geodesic distnace is done. Just a BFS visit starting from the seeds.
2011-10-21 00:26:46 +02:00
static void TopologicalVertexColoring ( MeshType & m , std : : vector < VertexType * > & seedVec )
2013-07-05 16:46:48 +02:00
{
std : : queue < VertexPointer > VQ ;
2011-10-21 00:26:46 +02:00
2013-07-05 16:46:48 +02:00
tri : : UpdateQuality < MeshType > : : VertexConstant ( m , 0 ) ;
for ( size_t i = 0 ; i < seedVec . size ( ) ; + + i )
{
VQ . push ( seedVec [ i ] ) ;
seedVec [ i ] - > Q ( ) = i + 1 ;
}
while ( ! VQ . empty ( ) )
{
VertexPointer vp = VQ . front ( ) ;
VQ . pop ( ) ;
std : : vector < VertexPointer > vertStar ;
vcg : : face : : VVStarVF < FaceType > ( vp , vertStar ) ;
for ( typename std : : vector < VertexPointer > : : iterator vv = vertStar . begin ( ) ; vv ! = vertStar . end ( ) ; + + vv )
{
if ( ( * vv ) - > Q ( ) = = 0 )
{
( * vv ) - > Q ( ) = vp - > Q ( ) ;
VQ . push ( * vv ) ;
}
}
} // end while(!VQ.empty())
}
2011-10-21 00:26:46 +02:00
2012-07-03 11:14:17 +02:00
// Drastic Simplification algorithm.
// Similar in philosopy to the classic grid clustering but using a voronoi partition instead of the regular grid.
//
// This function assumes that in the mOld mesh, for each vertex you have a quality that denotes the index of the cluster
// mNew is created by collasping onto a single vertex all the vertices that lies in the same cluster.
// Non degenerate triangles are preserved.
2013-07-05 16:46:48 +02:00
static void VoronoiClustering ( MeshType & mOld , MeshType & mNew , std : : vector < VertexType * > & seedVec )
2011-10-21 00:26:46 +02:00
{
std : : set < Point3i > clusteredFace ;
FaceIterator fi ;
for ( fi = mOld . face . begin ( ) ; fi ! = mOld . face . end ( ) ; + + fi )
{
2013-07-05 16:46:48 +02:00
if ( ( fi - > V ( 0 ) - > Q ( ) ! = fi - > V ( 1 ) - > Q ( ) ) & &
2011-10-21 00:26:46 +02:00
( fi - > V ( 0 ) - > Q ( ) ! = fi - > V ( 2 ) - > Q ( ) ) & &
( fi - > V ( 1 ) - > Q ( ) ! = fi - > V ( 2 ) - > Q ( ) ) )
2013-07-05 16:46:48 +02:00
clusteredFace . insert ( Point3i ( int ( fi - > V ( 0 ) - > Q ( ) ) , int ( fi - > V ( 1 ) - > Q ( ) ) , int ( fi - > V ( 2 ) - > Q ( ) ) ) ) ;
}
2011-10-21 00:26:46 +02:00
tri : : Allocator < MeshType > : : AddVertices ( mNew , seedVec . size ( ) ) ;
for ( size_t i = 0 ; i < seedVec . size ( ) ; + + i )
2013-07-05 16:46:48 +02:00
mNew . vert [ i ] . ImportData ( * ( seedVec [ i ] ) ) ;
2011-10-21 00:26:46 +02:00
tri : : Allocator < MeshType > : : AddFaces ( mNew , clusteredFace . size ( ) ) ;
std : : set < Point3i > : : iterator fsi ; ;
for ( fi = mNew . face . begin ( ) , fsi = clusteredFace . begin ( ) ; fsi ! = clusteredFace . end ( ) ; + + fsi , + + fi )
{
( * fi ) . V ( 0 ) = & mNew . vert [ ( int ) ( fsi - > V ( 0 ) - 1 ) ] ;
( * fi ) . V ( 1 ) = & mNew . vert [ ( int ) ( fsi - > V ( 1 ) - 1 ) ] ;
( * fi ) . V ( 2 ) = & mNew . vert [ ( int ) ( fsi - > V ( 2 ) - 1 ) ] ;
}
}
2012-07-03 11:14:17 +02:00
} ; // end class VoronoiProcessing
2011-10-21 00:26:46 +02:00
} // end namespace tri
} // end namespace vcg
# endif