2011-04-01 18:25:49 +02:00
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
2016-06-13 07:29:25 +02:00
* Copyright ( C ) 2004 - 2016 \ / ) \ / *
2011-04-01 18:25:49 +02:00
* Visual Computing Lab / \ / | *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved . *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License ( http : //www.gnu.org/licenses/gpl.txt) *
* for more details . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/****************************************************************************
The sampling Class has a set of static functions , that you can call to sample the surface of a mesh .
2013-03-01 09:34:33 +01:00
Each function is templated on the mesh and on a Sampler object s .
2011-04-01 18:25:49 +02:00
Each function calls many time the sample object with the sampling point as parameter .
2013-03-01 09:34:33 +01:00
Sampler Classes and Sampling algorithms are independent .
2011-04-01 18:25:49 +02:00
Sampler classes exploits the sample that are generated with various algorithms .
2013-03-01 09:34:33 +01:00
For example , you can compute Hausdorff distance ( that is a sampler ) using various
2011-04-01 18:25:49 +02:00
sampling strategies ( montecarlo , stratified etc ) .
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifndef __VCGLIB_POINT_SAMPLING
# define __VCGLIB_POINT_SAMPLING
2012-07-02 18:31:52 +02:00
2011-04-01 18:25:49 +02:00
# include <vcg/math/random_generator.h>
2011-04-01 19:06:03 +02:00
# include <vcg/complex/algorithms/closest.h>
2011-04-01 18:25:49 +02:00
# include <vcg/space/index/spatial_hashing.h>
2014-05-15 12:35:08 +02:00
# include <vcg/complex/algorithms/hole.h>
2011-04-01 19:06:03 +02:00
# include <vcg/complex/algorithms/stat.h>
2013-07-23 09:32:12 +02:00
# include <vcg/complex/algorithms/create/platonic.h>
2011-04-01 19:06:03 +02:00
# include <vcg/complex/algorithms/update/normal.h>
2013-03-13 01:16:51 +01:00
# include <vcg/complex/algorithms/update/bounding.h>
2011-04-01 18:25:49 +02:00
# include <vcg/space/segment2.h>
2015-10-21 00:32:16 +02:00
# include <vcg/space/index/grid_static_ptr.h>
2017-09-11 14:47:32 +02:00
2011-04-01 18:25:49 +02:00
namespace vcg
{
namespace tri
{
2013-06-24 12:51:53 +02:00
/// \ingroup trimesh
/// \headerfile point_sampling.h vcg/complex/algorithms/point_sampling.h
2011-04-01 18:25:49 +02:00
2013-06-24 12:51:53 +02:00
/**
2014-03-04 01:39:27 +01:00
\ brief A basic sampler class that show the required interface used by the SurfaceSampling class .
2013-06-24 12:51:53 +02:00
Most of the methods of sampling classes call the AddFace method of this class with the face containing the sample and its barycentric coord .
Beside being an example of how to write a sampler it provides a simple way to use the various sampling classes .
For example if you just want to get a vector with positions over the surface You have just to write
vector < Point3f > myVec ;
2017-04-02 17:40:12 +02:00
SurfaceSampling < MyMesh , TrivialSampler < MyMesh > > : : Montecarlo ( M , TrivialSampler < MyMesh > ( myVec ) , SampleNum ) ;
2013-06-24 12:51:53 +02:00
* */
2011-04-01 18:25:49 +02:00
template < class MeshType >
class TrivialSampler
{
2013-06-24 12:51:53 +02:00
public :
2014-08-26 03:43:04 +02:00
typedef typename MeshType : : ScalarType ScalarType ;
2013-06-24 12:51:53 +02:00
typedef typename MeshType : : CoordType CoordType ;
typedef typename MeshType : : VertexType VertexType ;
2014-08-26 03:43:04 +02:00
typedef typename MeshType : : EdgeType EdgeType ;
2013-06-24 12:51:53 +02:00
typedef typename MeshType : : FaceType FaceType ;
2011-04-01 18:25:49 +02:00
2014-05-15 12:35:08 +02:00
void reset ( )
{
sampleVec - > clear ( ) ;
}
2013-06-24 12:51:53 +02:00
TrivialSampler ( )
{
sampleVec = new std : : vector < CoordType > ( ) ;
vectorOwner = true ;
}
2011-04-01 18:25:49 +02:00
2013-06-24 12:51:53 +02:00
TrivialSampler ( std : : vector < CoordType > & Vec )
{
sampleVec = & Vec ;
vectorOwner = false ;
2014-05-15 12:35:08 +02:00
reset ( ) ;
2013-06-24 12:51:53 +02:00
}
2011-04-01 18:25:49 +02:00
2013-06-24 12:51:53 +02:00
~ TrivialSampler ( )
{
if ( vectorOwner ) delete sampleVec ;
}
2013-03-01 09:34:33 +01:00
2013-06-24 12:51:53 +02:00
private :
std : : vector < CoordType > * sampleVec ;
bool vectorOwner ;
public :
2016-12-16 23:19:37 +01:00
std : : vector < CoordType > & SampleVec ( )
{
return * sampleVec ;
}
2013-03-01 09:34:33 +01:00
2013-06-24 12:51:53 +02:00
void AddVert ( const VertexType & p )
{
sampleVec - > push_back ( p . cP ( ) ) ;
}
2014-08-26 03:43:04 +02:00
void AddEdge ( const EdgeType & e , ScalarType u ) / / u = = 0 - > v ( 0 ) u = = 1 - > v ( 1 ) ;
{
sampleVec - > push_back ( e . cV ( 0 ) - > cP ( ) * ( 1.0 - u ) + e . cV ( 1 ) - > cP ( ) * u ) ;
}
2013-06-24 12:51:53 +02:00
void AddFace ( const FaceType & f , const CoordType & p )
{
sampleVec - > push_back ( f . cP ( 0 ) * p [ 0 ] + f . cP ( 1 ) * p [ 1 ] + f . cP ( 2 ) * p [ 2 ] ) ;
}
2013-03-01 09:34:33 +01:00
2013-06-24 12:51:53 +02:00
void AddTextureSample ( const FaceType & , const CoordType & , const Point2i & , float )
{
// Retrieve the color of the sample from the face f using the barycentric coord p
// and write that color in a texture image at position <tp[0], texHeight-tp[1]>
// if edgeDist is > 0 then the corrisponding point is affecting face color even if outside the face area (in texture space)
}
2011-04-01 18:25:49 +02:00
} ; // end class TrivialSampler
2014-04-17 11:51:48 +02:00
template < class MeshType >
class TrivialPointerSampler
{
public :
2017-09-05 00:37:45 +02:00
typedef typename MeshType : : ScalarType ScalarType ;
2014-04-17 11:51:48 +02:00
typedef typename MeshType : : CoordType CoordType ;
typedef typename MeshType : : VertexType VertexType ;
2017-09-05 00:37:45 +02:00
typedef typename MeshType : : EdgeType EdgeType ;
2014-04-17 11:51:48 +02:00
typedef typename MeshType : : FaceType FaceType ;
2013-06-24 12:51:53 +02:00
2014-04-17 11:51:48 +02:00
TrivialPointerSampler ( ) { }
~ TrivialPointerSampler ( ) { }
2013-07-23 09:32:12 +02:00
2014-05-15 12:35:08 +02:00
void reset ( )
{
sampleVec . clear ( ) ;
}
2014-04-17 11:51:48 +02:00
public :
std : : vector < VertexType * > sampleVec ;
2014-04-17 12:11:43 +02:00
void AddVert ( VertexType & p )
2014-04-17 11:51:48 +02:00
{
2014-04-17 12:11:43 +02:00
sampleVec . push_back ( & p ) ;
2014-04-17 11:51:48 +02:00
}
2017-09-05 00:37:45 +02:00
void AddEdge ( const EdgeType & e , ScalarType u ) / / u = = 0 - > v ( 0 ) u = = 1 - > v ( 1 ) ;
{
if ( u < 0.5 )
sampleVec . push_back ( e . cV ( 0 ) ) ;
else
sampleVec . push_back ( e . cV ( 1 ) ) ;
}
2014-04-17 11:51:48 +02:00
// This sampler should be used only for getting vertex pointers. Meaningless in other case.
void AddFace ( const FaceType & , const CoordType & ) { assert ( 0 ) ; }
void AddTextureSample ( const FaceType & , const CoordType & , const Point2i & , float ) { assert ( 0 ) ; }
} ; // end class TrivialSampler
2013-07-23 09:32:12 +02:00
template < class MeshType >
class MeshSampler
{
public :
typedef typename MeshType : : VertexType VertexType ;
typedef typename MeshType : : FaceType FaceType ;
typedef typename MeshType : : CoordType CoordType ;
2015-10-28 13:52:25 +01:00
MeshSampler ( MeshType & _m ) : m ( _m ) {
perFaceNormal = false ;
}
2013-07-23 09:32:12 +02:00
MeshType & m ;
2015-10-28 13:52:25 +01:00
bool perFaceNormal ; // default false; if true the sample normal is the face normal, otherwise it is interpolated
2014-05-15 12:35:08 +02:00
void reset ( )
{
m . Clear ( ) ;
}
2013-07-23 09:32:12 +02:00
void AddVert ( const VertexType & p )
{
tri : : Allocator < MeshType > : : AddVertices ( m , 1 ) ;
m . vert . back ( ) . ImportData ( p ) ;
}
void AddFace ( const FaceType & f , CoordType p )
{
tri : : Allocator < MeshType > : : AddVertices ( m , 1 ) ;
m . vert . back ( ) . P ( ) = f . cP ( 0 ) * p [ 0 ] + f . cP ( 1 ) * p [ 1 ] + f . cP ( 2 ) * p [ 2 ] ;
2015-10-28 13:52:25 +01:00
if ( perFaceNormal ) m . vert . back ( ) . N ( ) = f . cN ( ) ;
else m . vert . back ( ) . N ( ) = f . cV ( 0 ) - > N ( ) * p [ 0 ] + f . cV ( 1 ) - > N ( ) * p [ 1 ] + f . cV ( 2 ) - > N ( ) * p [ 2 ] ;
2013-09-11 13:09:29 +02:00
if ( tri : : HasPerVertexQuality ( m ) )
m . vert . back ( ) . Q ( ) = f . cV ( 0 ) - > Q ( ) * p [ 0 ] + f . cV ( 1 ) - > Q ( ) * p [ 1 ] + f . cV ( 2 ) - > Q ( ) * p [ 2 ] ;
2013-07-23 09:32:12 +02:00
}
2018-05-18 13:25:34 +02:00
} ; // end class MeshSampler
2013-07-23 09:32:12 +02:00
2015-10-21 00:32:16 +02:00
/* This sampler is used to perform compute the Hausdorff measuring.
* It keep internally the spatial indexing structure used to find the closest point
* and the partial integration results needed to compute the average and rms error values .
* Averaged values assume that the samples are equi - distributed ( e . g . a good unbiased montecarlo sampling of the surface ) .
*/
template < class MeshType >
class HausdorffSampler
{
typedef typename MeshType : : FaceType FaceType ;
typedef typename MeshType : : VertexType VertexType ;
typedef typename MeshType : : CoordType CoordType ;
typedef typename MeshType : : ScalarType ScalarType ;
typedef GridStaticPtr < FaceType , ScalarType > MetroMeshFaceGrid ;
typedef GridStaticPtr < VertexType , ScalarType > MetroMeshVertexGrid ;
public :
HausdorffSampler ( MeshType * _m , MeshType * _sampleMesh = 0 , MeshType * _closestMesh = 0 ) : markerFunctor ( _m )
{
m = _m ;
init ( _sampleMesh , _closestMesh ) ;
}
MeshType * m ; /// the mesh for which we search the closest points.
MeshType * samplePtMesh ; /// the mesh containing the sample points
MeshType * closestPtMesh ; /// the mesh containing the corresponding closest points that have been found
MetroMeshVertexGrid unifGridVert ;
MetroMeshFaceGrid unifGridFace ;
// Parameters
double min_dist ;
double max_dist ;
double mean_dist ;
double RMS_dist ; /// from the wikipedia defintion RMS DIST is sqrt(Sum(distances^2)/n), here we store Sum(distances^2)
double volume ;
double area_S1 ;
Histogramf hist ;
// globals parameters driving the samples.
int n_total_samples ;
int n_samples ;
bool useVertexSampling ;
ScalarType dist_upper_bound ; // samples that have a distance beyond this threshold distance are not considered.
typedef typename tri : : FaceTmark < MeshType > MarkerFace ;
MarkerFace markerFunctor ;
float getMeanDist ( ) const { return mean_dist / n_total_samples ; }
float getMinDist ( ) const { return min_dist ; }
float getMaxDist ( ) const { return max_dist ; }
float getRMSDist ( ) const { return sqrt ( RMS_dist / n_total_samples ) ; }
void init ( MeshType * _sampleMesh = 0 , MeshType * _closestMesh = 0 )
{
samplePtMesh = _sampleMesh ;
closestPtMesh = _closestMesh ;
if ( m )
{
tri : : UpdateNormal < MeshType > : : PerFaceNormalized ( * m ) ;
if ( m - > fn = = 0 ) useVertexSampling = true ;
else useVertexSampling = false ;
if ( useVertexSampling ) unifGridVert . Set ( m - > vert . begin ( ) , m - > vert . end ( ) ) ;
else unifGridFace . Set ( m - > face . begin ( ) , m - > face . end ( ) ) ;
markerFunctor . SetMesh ( m ) ;
hist . SetRange ( 0.0 , m - > bbox . Diag ( ) / 100.0 , 100 ) ;
}
min_dist = std : : numeric_limits < double > : : max ( ) ;
max_dist = 0 ;
mean_dist = 0 ;
RMS_dist = 0 ;
n_total_samples = 0 ;
}
void AddFace ( const FaceType & f , CoordType interp )
{
CoordType startPt = f . cP ( 0 ) * interp [ 0 ] + f . cP ( 1 ) * interp [ 1 ] + f . cP ( 2 ) * interp [ 2 ] ; // point to be sampled
CoordType startN = f . cV ( 0 ) - > cN ( ) * interp [ 0 ] + f . cV ( 1 ) - > cN ( ) * interp [ 1 ] + f . cV ( 2 ) - > cN ( ) * interp [ 2 ] ; // Normal of the interpolated point
AddSample ( startPt , startN ) ; // point to be sampled);
}
void AddVert ( VertexType & p )
{
p . Q ( ) = AddSample ( p . cP ( ) , p . cN ( ) ) ;
}
float AddSample ( const CoordType & startPt , const CoordType & startN )
{
// the results
CoordType closestPt ;
ScalarType dist = dist_upper_bound ;
// compute distance between startPt and the mesh S2
FaceType * nearestF = 0 ;
VertexType * nearestV = 0 ;
vcg : : face : : PointDistanceBaseFunctor < ScalarType > PDistFunct ;
dist = dist_upper_bound ;
if ( useVertexSampling )
nearestV = tri : : GetClosestVertex < MeshType , MetroMeshVertexGrid > ( * m , unifGridVert , startPt , dist_upper_bound , dist ) ;
else
nearestF = unifGridFace . GetClosest ( PDistFunct , markerFunctor , startPt , dist_upper_bound , dist , closestPt ) ;
// update distance measures
if ( dist = = dist_upper_bound )
return dist ;
if ( dist > max_dist ) max_dist = dist ; // L_inf
if ( dist < min_dist ) min_dist = dist ; // L_inf
mean_dist + = dist ; // L_1
RMS_dist + = dist * dist ; // L_2
n_total_samples + + ;
hist . Add ( ( float ) fabs ( dist ) ) ;
if ( samplePtMesh )
{
tri : : Allocator < MeshType > : : AddVertices ( * samplePtMesh , 1 ) ;
samplePtMesh - > vert . back ( ) . P ( ) = startPt ;
samplePtMesh - > vert . back ( ) . Q ( ) = dist ;
samplePtMesh - > vert . back ( ) . N ( ) = startN ;
}
if ( closestPtMesh )
{
tri : : Allocator < MeshType > : : AddVertices ( * closestPtMesh , 1 ) ;
closestPtMesh - > vert . back ( ) . P ( ) = closestPt ;
closestPtMesh - > vert . back ( ) . Q ( ) = dist ;
closestPtMesh - > vert . back ( ) . N ( ) = startN ;
}
return dist ;
}
} ; // end class HausdorffSampler
2013-07-23 09:32:12 +02:00
2016-02-02 16:09:54 +01:00
/* This sampler is used to transfer the detail of a mesh onto another one.
* It keep internally the spatial indexing structure used to find the closest point
*/
template < class MeshType >
class RedetailSampler
{
typedef typename MeshType : : FaceType FaceType ;
typedef typename MeshType : : VertexType VertexType ;
typedef typename MeshType : : CoordType CoordType ;
typedef typename MeshType : : ScalarType ScalarType ;
typedef GridStaticPtr < FaceType , ScalarType > MetroMeshGrid ;
typedef GridStaticPtr < VertexType , ScalarType > VertexMeshGrid ;
public :
RedetailSampler ( ) : m ( 0 ) { }
MeshType * m ; /// the source mesh for which we search the closest points (e.g. the mesh from which we take colors etc).
CallBackPos * cb ;
int sampleNum ; // the expected number of samples. Used only for the callback
int sampleCnt ;
MetroMeshGrid unifGridFace ;
VertexMeshGrid unifGridVert ;
bool useVertexSampling ;
// Parameters
typedef tri : : FaceTmark < MeshType > MarkerFace ;
MarkerFace markerFunctor ;
bool coordFlag ;
bool colorFlag ;
bool normalFlag ;
bool qualityFlag ;
bool selectionFlag ;
bool storeDistanceAsQualityFlag ;
float dist_upper_bound ;
void init ( MeshType * _m , CallBackPos * _cb = 0 , int targetSz = 0 )
{
coordFlag = false ;
colorFlag = false ;
qualityFlag = false ;
selectionFlag = false ;
storeDistanceAsQualityFlag = false ;
m = _m ;
tri : : UpdateNormal < MeshType > : : PerFaceNormalized ( * m ) ;
if ( m - > fn = = 0 ) useVertexSampling = true ;
else useVertexSampling = false ;
if ( useVertexSampling ) unifGridVert . Set ( m - > vert . begin ( ) , m - > vert . end ( ) ) ;
else unifGridFace . Set ( m - > face . begin ( ) , m - > face . end ( ) ) ;
markerFunctor . SetMesh ( m ) ;
// sampleNum and sampleCnt are used only for the progress callback.
cb = _cb ;
sampleNum = targetSz ;
sampleCnt = 0 ;
}
// this function is called for each vertex of the target mesh.
// and retrieve the closest point on the source mesh.
void AddVert ( VertexType & p )
{
assert ( m ) ;
// the results
CoordType closestPt , normf , bestq , ip ;
ScalarType dist = dist_upper_bound ;
const CoordType & startPt = p . cP ( ) ;
// compute distance between startPt and the mesh S2
if ( useVertexSampling )
{
VertexType * nearestV = 0 ;
nearestV = tri : : GetClosestVertex < MeshType , VertexMeshGrid > ( * m , unifGridVert , startPt , dist_upper_bound , dist ) ; //(PDistFunct,markerFunctor,startPt,dist_upper_bound,dist,closestPt);
if ( cb ) cb ( sampleCnt + + * 100 / sampleNum , " Resampling Vertex attributes " ) ;
if ( storeDistanceAsQualityFlag ) p . Q ( ) = dist ;
if ( dist = = dist_upper_bound ) return ;
if ( coordFlag ) p . P ( ) = nearestV - > P ( ) ;
if ( colorFlag ) p . C ( ) = nearestV - > C ( ) ;
if ( normalFlag ) p . N ( ) = nearestV - > N ( ) ;
if ( qualityFlag ) p . Q ( ) = nearestV - > Q ( ) ;
if ( selectionFlag ) if ( nearestV - > IsS ( ) ) p . SetS ( ) ;
}
else
{
FaceType * nearestF = 0 ;
vcg : : face : : PointDistanceBaseFunctor < ScalarType > PDistFunct ;
dist = dist_upper_bound ;
if ( cb ) cb ( sampleCnt + + * 100 / sampleNum , " Resampling Vertex attributes " ) ;
nearestF = unifGridFace . GetClosest ( PDistFunct , markerFunctor , startPt , dist_upper_bound , dist , closestPt ) ;
if ( dist = = dist_upper_bound ) return ;
CoordType interp ;
InterpolationParameters ( * nearestF , ( * nearestF ) . cN ( ) , closestPt , interp ) ;
interp [ 2 ] = 1.0 - interp [ 1 ] - interp [ 0 ] ;
if ( coordFlag ) p . P ( ) = closestPt ;
if ( colorFlag ) p . C ( ) . lerp ( nearestF - > V ( 0 ) - > C ( ) , nearestF - > V ( 1 ) - > C ( ) , nearestF - > V ( 2 ) - > C ( ) , interp ) ;
if ( normalFlag ) p . N ( ) = nearestF - > V ( 0 ) - > N ( ) * interp [ 0 ] + nearestF - > V ( 1 ) - > N ( ) * interp [ 1 ] + nearestF - > V ( 2 ) - > N ( ) * interp [ 2 ] ;
if ( qualityFlag ) p . Q ( ) = nearestF - > V ( 0 ) - > Q ( ) * interp [ 0 ] + nearestF - > V ( 1 ) - > Q ( ) * interp [ 1 ] + nearestF - > V ( 2 ) - > Q ( ) * interp [ 2 ] ;
if ( selectionFlag ) if ( nearestF - > IsS ( ) ) p . SetS ( ) ;
}
}
} ; // end class RedetailSampler
2013-06-24 12:51:53 +02:00
/**
\ brief Main Class of the Sampling framework .
This class allows you to perform various kind of random / procedural point sampling over a triangulated surface .
The class is templated over the PointSampler object that allows to customize the use of the generated samples .
* */
2014-05-15 12:35:08 +02:00
template < class MeshType , class VertexSampler = TrivialSampler < MeshType > >
2011-04-01 18:25:49 +02:00
class SurfaceSampling
{
2014-05-15 12:35:08 +02:00
typedef typename MeshType : : CoordType CoordType ;
2014-06-24 10:48:27 +02:00
typedef typename MeshType : : BoxType BoxType ;
2014-05-15 12:35:08 +02:00
typedef typename MeshType : : ScalarType ScalarType ;
typedef typename MeshType : : VertexType VertexType ;
typedef typename MeshType : : VertexPointer VertexPointer ;
typedef typename MeshType : : VertexIterator VertexIterator ;
2014-08-26 03:43:04 +02:00
typedef typename MeshType : : EdgeType EdgeType ;
typedef typename MeshType : : EdgeIterator EdgeIterator ;
typedef typename MeshType : : FaceType FaceType ;
2014-05-15 12:35:08 +02:00
typedef typename MeshType : : FacePointer FacePointer ;
typedef typename MeshType : : FaceIterator FaceIterator ;
typedef typename MeshType : : FaceContainer FaceContainer ;
2013-02-23 06:50:27 +01:00
typedef typename vcg : : SpatialHashTable < FaceType , ScalarType > MeshSHT ;
typedef typename vcg : : SpatialHashTable < FaceType , ScalarType > : : CellIterator MeshSHTIterator ;
typedef typename vcg : : SpatialHashTable < VertexType , ScalarType > MontecarloSHT ;
typedef typename vcg : : SpatialHashTable < VertexType , ScalarType > : : CellIterator MontecarloSHTIterator ;
typedef typename vcg : : SpatialHashTable < VertexType , ScalarType > SampleSHT ;
typedef typename vcg : : SpatialHashTable < VertexType , ScalarType > : : CellIterator SampleSHTIterator ;
2011-04-01 18:25:49 +02:00
2014-05-15 12:35:08 +02:00
typedef typename MeshType : : template PerVertexAttributeHandle < float > PerVertexFloatAttribute ;
2011-04-01 18:25:49 +02:00
public :
2013-03-01 09:34:33 +01:00
static math : : MarsenneTwisterRNG & SamplingRandomGenerator ( )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
static math : : MarsenneTwisterRNG rnd ;
return rnd ;
2011-04-01 18:25:49 +02:00
}
// Returns an integer random number in the [0,i-1] interval using the improve Marsenne-Twister method.
2014-04-18 10:27:38 +02:00
// this functor is needed for passing it to the std functions.
2011-04-01 18:25:49 +02:00
static unsigned int RandomInt ( unsigned int i )
{
2014-04-17 15:53:55 +02:00
return ( SamplingRandomGenerator ( ) . generate ( i ) ) ;
2011-04-01 18:25:49 +02:00
}
// Returns a random number in the [0,1) real interval using the improved Marsenne-Twister method.
static double RandomDouble01 ( )
{
2013-03-01 09:34:33 +01:00
return SamplingRandomGenerator ( ) . generate01 ( ) ;
2011-04-01 18:25:49 +02:00
}
# define FAK_LEN 1024
static double LnFac ( int n ) {
// Tabled log factorial function. gives natural logarithm of n!
// define constants
static const double // coefficients in Stirling approximation
C0 = 0.918938533204672722 , // ln(sqrt(2*pi))
C1 = 1. / 12. ,
C3 = - 1. / 360. ;
// C5 = 1./1260., // use r^5 term if FAK_LEN < 50
// C7 = -1./1680.; // use r^7 term if FAK_LEN < 20
// static variables
static double fac_table [ FAK_LEN ] ; // table of ln(n!):
static bool initialized = false ; // remember if fac_table has been initialized
if ( n < FAK_LEN ) {
if ( n < = 1 ) {
if ( n < 0 ) assert ( 0 ) ; //("Parameter negative in LnFac function");
return 0 ;
}
if ( ! initialized ) { // first time. Must initialize table
// make table of ln(n!)
double sum = fac_table [ 0 ] = 0. ;
for ( int i = 1 ; i < FAK_LEN ; i + + ) {
sum + = log ( double ( i ) ) ;
fac_table [ i ] = sum ;
}
initialized = true ;
}
return fac_table [ n ] ;
}
// not found in table. use Stirling approximation
double n1 , r ;
n1 = n ; r = 1. / n1 ;
return ( n1 + 0.5 ) * log ( n1 ) - n1 + C0 + r * ( C1 + r * r * C3 ) ;
}
static int PoissonRatioUniforms ( double L ) {
/*
This subfunction generates a integer with the poisson
distribution using the ratio - of - uniforms rejection method ( PRUAt ) .
This approach is STABLE even for large L ( e . g . it does not suffer from the overflow limit of the classical Knuth implementation )
Execution time does not depend on L , except that it matters whether
is within the range where ln ( n ! ) is tabulated .
Reference :
E . Stadlober
" The ratio of uniforms approach for generating discrete random variates " .
Journal of Computational and Applied Mathematics ,
vol . 31 , no . 1 , 1990 , pp . 181 - 189.
Partially adapted / inspired from some subfunctions of the Agner Fog stocc library ( www . agner . org / random )
Same licensing scheme .
*/
// constants
const double SHAT1 = 2.943035529371538573 ; // 8/e
const double SHAT2 = 0.8989161620588987408 ; // 3-sqrt(12/e)
double u ; // uniform random
double lf ; // ln(f(x))
double x ; // real sample
int k ; // integer sample
double pois_a = L + 0.5 ; // hat center
int mode = ( int ) L ; // mode
double pois_g = log ( L ) ;
double pois_f0 = mode * pois_g - LnFac ( mode ) ; // value at mode
double pois_h = sqrt ( SHAT1 * ( L + 0.5 ) ) + SHAT2 ; // hat width
double pois_bound = ( int ) ( pois_a + 6.0 * pois_h ) ; // safety-bound
while ( 1 ) {
u = RandomDouble01 ( ) ;
if ( u = = 0 ) continue ; // avoid division by 0
x = pois_a + pois_h * ( RandomDouble01 ( ) - 0.5 ) / u ;
if ( x < 0 | | x > = pois_bound ) continue ; // reject if outside valid range
k = ( int ) ( x ) ;
lf = k * pois_g - LnFac ( k ) - pois_f0 ;
if ( lf > = u * ( 4.0 - u ) - 3.0 ) break ; // quick acceptance
if ( u * ( u - lf ) > 1.0 ) continue ; // quick rejection
if ( 2.0 * log ( u ) < = lf ) break ; // final acceptance
}
return k ;
}
/**
algorithm poisson random number ( Knuth ) :
init :
Let L ← e ^ − λ , k ← 0 and p ← 1.
do :
k ← k + 1.
Generate uniform random number u in [ 0 , 1 ] and let p ← p × u .
while p > L .
return k − 1.
*/
static int Poisson ( double lambda )
{
if ( lambda > 50 ) return PoissonRatioUniforms ( lambda ) ;
double L = exp ( - lambda ) ;
int k = 0 ;
double p = 1.0 ;
do
{
k = k + 1 ;
p = p * RandomDouble01 ( ) ;
} while ( p > L ) ;
return k - 1 ;
}
2014-05-15 12:35:08 +02:00
static void AllVertex ( MeshType & m , VertexSampler & ps )
2011-04-01 18:25:49 +02:00
{
2015-10-29 14:44:25 +01:00
AllVertex ( m , ps , false ) ;
}
static void AllVertex ( MeshType & m , VertexSampler & ps , bool onlySelected )
{
VertexIterator vi ;
for ( vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
if ( ! ( * vi ) . IsD ( ) )
if ( ( ! onlySelected ) | | ( ( * vi ) . IsS ( ) ) )
{
ps . AddVert ( * vi ) ;
}
2011-04-01 18:25:49 +02:00
}
/// Sample the vertices in a weighted way. Each vertex has a probability of being chosen
2013-03-01 09:34:33 +01:00
/// that is proportional to its quality.
2011-04-01 18:25:49 +02:00
/// It assumes that you are asking a number of vertices smaller than nv;
2013-03-01 09:34:33 +01:00
/// Algorithm:
2011-04-01 18:25:49 +02:00
/// 1) normalize quality so that sum q == 1;
/// 2) shuffle vertices.
/// 3) for each vertices choose it if rand > thr;
2013-03-01 09:34:33 +01:00
2014-05-15 12:35:08 +02:00
static void VertexWeighted ( MeshType & m , VertexSampler & ps , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
ScalarType qSum = 0 ;
VertexIterator vi ;
for ( vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
if ( ! ( * vi ) . IsD ( ) )
qSum + = ( * vi ) . Q ( ) ;
ScalarType samplePerUnit = sampleNum / qSum ;
ScalarType floatSampleNum = 0 ;
std : : vector < VertexPointer > vertVec ;
FillAndShuffleVertexPointerVector ( m , vertVec ) ;
std : : vector < bool > vertUsed ( m . vn , false ) ;
int i = 0 ; int cnt = 0 ;
while ( cnt < sampleNum )
{
if ( vertUsed [ i ] )
{
floatSampleNum + = vertVec [ i ] - > Q ( ) * samplePerUnit ;
int vertSampleNum = ( int ) floatSampleNum ;
floatSampleNum - = ( float ) vertSampleNum ;
// for every sample p_i in T...
if ( vertSampleNum > 1 )
{
ps . AddVert ( * vertVec [ i ] ) ;
cnt + + ;
vertUsed [ i ] = true ;
}
}
i = ( i + 1 ) % m . vn ;
}
2011-04-01 18:25:49 +02:00
}
/// Sample the vertices in a uniform way. Each vertex has a probability of being chosen
2013-03-01 09:34:33 +01:00
/// that is proportional to the area it represent.
2014-05-15 12:35:08 +02:00
static void VertexAreaUniform ( MeshType & m , VertexSampler & ps , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
VertexIterator vi ;
for ( vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
if ( ! ( * vi ) . IsD ( ) )
( * vi ) . Q ( ) = 0 ;
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
if ( ! ( * fi ) . IsD ( ) )
{
ScalarType areaThird = DoubleArea ( * fi ) / 6.0 ;
( * fi ) . V ( 0 ) - > Q ( ) + = areaThird ;
( * fi ) . V ( 1 ) - > Q ( ) + = areaThird ;
( * fi ) . V ( 2 ) - > Q ( ) + = areaThird ;
}
VertexWeighted ( m , ps , sampleNum ) ;
2011-04-01 18:25:49 +02:00
}
2013-03-01 09:34:33 +01:00
2014-05-15 12:35:08 +02:00
static void FillAndShuffleFacePointerVector ( MeshType & m , std : : vector < FacePointer > & faceVec )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
2013-12-20 03:27:09 +01:00
if ( ! ( * fi ) . IsD ( ) ) faceVec . push_back ( & * fi ) ;
2013-03-01 09:34:33 +01:00
2013-12-20 03:27:09 +01:00
assert ( ( int ) faceVec . size ( ) = = m . fn ) ;
2013-03-01 09:34:33 +01:00
2013-12-20 03:27:09 +01:00
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( faceVec . begin ( ) , faceVec . end ( ) , p_myrandom ) ;
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
static void FillAndShuffleVertexPointerVector ( MeshType & m , std : : vector < VertexPointer > & vertVec )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
for ( VertexIterator vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
2013-12-20 03:27:09 +01:00
if ( ! ( * vi ) . IsD ( ) ) vertVec . push_back ( & * vi ) ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
assert ( ( int ) vertVec . size ( ) = = m . vn ) ;
2013-03-01 09:34:33 +01:00
2013-12-20 03:27:09 +01:00
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( vertVec . begin ( ) , vertVec . end ( ) , p_myrandom ) ;
2011-04-01 18:25:49 +02:00
}
2013-03-01 09:34:33 +01:00
/// Sample the vertices in a uniform way. Each vertex has the same probabiltiy of being chosen.
2015-10-29 14:44:25 +01:00
static void VertexUniform ( MeshType & m , VertexSampler & ps , int sampleNum , bool onlySelected )
2011-04-01 18:25:49 +02:00
{
2015-10-29 14:44:25 +01:00
if ( sampleNum > = m . vn ) {
AllVertex ( m , ps , onlySelected ) ;
return ;
}
std : : vector < VertexPointer > vertVec ;
FillAndShuffleVertexPointerVector ( m , vertVec ) ;
int added = 0 ;
for ( int i = 0 ; ( ( i < m . vn ) & & ( added < sampleNum ) ) ; + + i )
if ( ! ( * vertVec [ i ] ) . IsD ( ) )
if ( ( ! onlySelected ) | | ( * vertVec [ i ] ) . IsS ( ) )
{
ps . AddVert ( * vertVec [ i ] ) ;
added + + ;
}
}
2013-03-01 09:34:33 +01:00
2015-10-29 14:44:25 +01:00
static void VertexUniform ( MeshType & m , VertexSampler & ps , int sampleNum )
{
VertexUniform ( m , ps , sampleNum , false ) ;
2011-04-01 18:25:49 +02:00
}
2014-08-26 03:43:04 +02:00
2018-05-11 18:56:46 +02:00
///
/// \brief The EdgeSamplingStrategy enum determines the sampling strategy for edge meshes.
/// Given a sampling radius 'r', and the total length of the edge mesh 'L',
/// the number of generated samples is: op(L/r) (+ 1 if the mesh is not a loop)
/// where op is (floor | round | ceil)
///
enum EdgeSamplingStrategy
{
Floor = 0 ,
Round ,
Ceil ,
} ;
2014-09-19 19:07:02 +02:00
/// Perform an uniform sampling over an EdgeMesh.
///
/// It assumes that the mesh is 1-manifold.
/// each connected component is sampled in a independent way.
2018-05-07 20:24:39 +02:00
/// For each component of length <L> we place on it floor(L/radius)+1 samples.
2015-11-06 18:32:29 +01:00
/// (if conservative argument is false we place ceil(L/radius)+1 samples)
2014-08-26 03:43:04 +02:00
///
2018-05-11 19:17:22 +02:00
static void EdgeMeshUniform ( MeshType & m , VertexSampler & ps , float radius , EdgeSamplingStrategy strategy = Floor )
2014-08-26 03:43:04 +02:00
{
2015-11-06 00:35:44 +01:00
tri : : RequireEEAdjacency ( m ) ;
tri : : RequireCompactness ( m ) ;
tri : : RequirePerEdgeFlags ( m ) ;
tri : : RequirePerVertexFlags ( m ) ;
tri : : UpdateTopology < MeshType > : : EdgeEdge ( m ) ;
tri : : UpdateFlags < MeshType > : : EdgeClearV ( m ) ;
2017-09-11 14:47:32 +02:00
tri : : MeshAssert < MeshType > : : EEOneManifold ( m ) ;
2015-11-06 00:35:44 +01:00
for ( EdgeIterator ei = m . edge . begin ( ) ; ei ! = m . edge . end ( ) ; + + ei )
{
if ( ! ei - > IsV ( ) )
{
edge : : Pos < EdgeType > ep ( & * ei , 0 ) ;
2017-09-11 14:47:32 +02:00
edge : : Pos < EdgeType > startep = ep ;
2015-11-06 00:35:44 +01:00
do // first loop to search a boundary component.
{
ep . NextE ( ) ;
if ( ep . IsBorder ( ) )
break ;
} while ( startep ! = ep ) ;
if ( ! ep . IsBorder ( ) )
{
// it's a loop
2017-09-11 14:47:32 +02:00
assert ( ep = = startep ) ;
// to keep the uniform resampling order-independent:
// 1) start from the 'lowest' point...
edge : : Pos < EdgeType > altEp = ep ;
altEp . NextE ( ) ;
while ( altEp ! = startep ) {
if ( altEp . V ( ) - > cP ( ) < ep . V ( ) - > cP ( ) )
{
ep = altEp ;
}
altEp . NextE ( ) ;
}
// 2) ... with consistent direction
const auto dir0 = ep . VFlip ( ) - > cP ( ) - ep . V ( ) - > cP ( ) ;
ep . FlipE ( ) ;
const auto dir1 = ep . VFlip ( ) - > cP ( ) - ep . V ( ) - > cP ( ) ;
if ( dir0 < dir1 )
{
ep . FlipE ( ) ;
}
2015-11-06 00:35:44 +01:00
}
else
{
2017-09-11 14:47:32 +02:00
// not a loop
2015-11-06 00:35:44 +01:00
// to keep the uniform resampling order-independent
// start from the border with 'lowest' point
edge : : Pos < EdgeType > altEp = ep ;
do {
altEp . NextE ( ) ;
} while ( ! altEp . IsBorder ( ) ) ;
if ( altEp . V ( ) - > cP ( ) < ep . V ( ) - > cP ( ) )
{
ep = altEp ;
}
}
2014-09-19 19:07:02 +02:00
2015-11-06 00:35:44 +01:00
ScalarType totalLen = 0 ;
ep . FlipV ( ) ;
2017-09-11 14:47:32 +02:00
// second loop to compute the length of the chain.
2015-11-06 00:35:44 +01:00
do
{
ep . E ( ) - > SetV ( ) ;
totalLen + = Distance ( ep . V ( ) - > cP ( ) , ep . VFlip ( ) - > cP ( ) ) ;
ep . NextE ( ) ;
2017-09-11 14:47:32 +02:00
} while ( ! ep . E ( ) - > IsV ( ) & & ! ep . IsBorder ( ) ) ;
if ( ep . IsBorder ( ) )
{
ep . E ( ) - > SetV ( ) ;
totalLen + = Distance ( ep . V ( ) - > cP ( ) , ep . VFlip ( ) - > cP ( ) ) ;
}
VertexPointer startVertex = ep . V ( ) ;
2015-11-06 00:35:44 +01:00
2017-09-11 14:47:32 +02:00
// Third loop actually performs the sampling.
2018-05-11 18:56:46 +02:00
int sampleNum = - 1 ;
{
double div = double ( totalLen ) / radius ;
switch ( strategy ) {
case Round :
sampleNum = int ( round ( div ) ) ;
break ;
case Ceil :
sampleNum = int ( ceil ( div ) ) ;
break ;
default : // Floor
sampleNum = int ( floor ( div ) ) ;
break ;
} ;
}
assert ( sampleNum > = 0 ) ;
2015-11-06 18:32:29 +01:00
2015-11-06 00:35:44 +01:00
ScalarType sampleDist = totalLen / sampleNum ;
// printf("Found a chain of %f with %i samples every %f (%f)\n", totalLen, sampleNum, sampleDist, radius);
ScalarType curLen = 0 ;
int sampleCnt = 1 ;
ps . AddEdge ( * ( ep . E ( ) ) , ep . VInd ( ) = = 0 ? 0.0 : 1.0 ) ;
2017-09-11 14:47:32 +02:00
do {
2015-11-06 00:35:44 +01:00
ep . NextE ( ) ;
assert ( ep . E ( ) - > IsV ( ) ) ;
2017-09-11 14:47:32 +02:00
ScalarType edgeLen = Distance ( ep . VFlip ( ) - > cP ( ) , ep . V ( ) - > cP ( ) ) ;
2015-11-06 00:35:44 +01:00
ScalarType d0 = curLen ;
ScalarType d1 = d0 + edgeLen ;
while ( d1 > sampleCnt * sampleDist & & sampleCnt < sampleNum )
{
ScalarType off = ( sampleCnt * sampleDist - d0 ) / edgeLen ;
// printf("edgeLen %f off %f samplecnt %i\n", edgeLen, off, sampleCnt);
ps . AddEdge ( * ( ep . E ( ) ) , ep . VInd ( ) = = 0 ? 1.0 - off : off ) ;
sampleCnt + + ;
}
curLen + = edgeLen ;
} while ( ! ep . IsBorder ( ) & & ep . V ( ) ! = startVertex ) ;
if ( ep . V ( ) ! = startVertex )
2017-09-11 14:47:32 +02:00
{
2015-11-06 00:35:44 +01:00
ps . AddEdge ( * ( ep . E ( ) ) , ep . VInd ( ) = = 0 ? 0.0 : 1.0 ) ;
2017-09-11 14:47:32 +02:00
}
2015-11-06 00:35:44 +01:00
}
}
2014-08-26 03:43:04 +02:00
}
2013-10-03 12:00:53 +02:00
/// \brief Sample all the border corner vertices
///
2013-12-20 03:27:09 +01:00
/// It assumes that the border flag have been set over the mesh both for vertex and for faces.
2016-12-16 23:19:37 +01:00
/// All the vertices on the border where the edges of the boundary of the surface forms an angle smaller than the given threshold are sampled.
2017-04-02 17:40:12 +02:00
/// It assumes that the Per-Vertex border Flag has been set.
2017-01-27 12:20:31 +01:00
static void VertexBorderCorner ( MeshType & m , VertexSampler & ps , ScalarType angleRad )
2013-10-03 12:00:53 +02:00
{
2017-04-02 17:40:12 +02:00
vcg : : tri : : UpdateSelection < MeshType > : : VertexCornerBorder ( m , angleRad ) ;
2017-01-25 17:26:57 +01:00
for ( VertexIterator vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
2013-10-03 12:00:53 +02:00
{
2017-04-02 17:40:12 +02:00
if ( vi - > IsS ( ) ) ps . AddVert ( * vi ) ;
2013-10-03 12:00:53 +02:00
}
}
2013-12-20 03:27:09 +01:00
/// \brief Sample all the border vertices
///
/// It assumes that the border flag have been set over the mesh.
/// All the vertices on the border are sampled.
///
2014-05-15 12:35:08 +02:00
static void VertexBorder ( MeshType & m , VertexSampler & ps )
2013-12-20 03:27:09 +01:00
{
VertexBorderCorner ( m , ps , std : : numeric_limits < ScalarType > : : max ( ) ) ;
}
2013-06-24 12:51:53 +02:00
/// Sample all the crease vertices.
2013-10-03 12:00:53 +02:00
/// It assumes that the crease edges had been marked as non-faux edges
/// for example by using
/// tri::UpdateFlags<MeshType>::FaceFauxCrease(mesh,creaseAngleRad);
/// Then it chooses all the vertices where there are at least three non faux edges.
///
2014-05-15 12:35:08 +02:00
static void VertexCrease ( MeshType & m , VertexSampler & ps )
2013-06-24 12:51:53 +02:00
{
2014-05-15 12:35:08 +02:00
typedef typename UpdateTopology < MeshType > : : PEdge SimpleEdge ;
2013-06-24 12:51:53 +02:00
std : : vector < SimpleEdge > Edges ;
typename std : : vector < SimpleEdge > : : iterator ei ;
2014-05-15 12:35:08 +02:00
UpdateTopology < MeshType > : : FillUniqueEdgeVector ( m , Edges , false ) ;
2013-06-24 12:51:53 +02:00
2014-05-15 12:35:08 +02:00
typename MeshType : : template PerVertexAttributeHandle < int > hv = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < int > ( m ) ;
2013-06-24 12:51:53 +02:00
for ( ei = Edges . begin ( ) ; ei ! = Edges . end ( ) ; + + ei )
{
hv [ ei - > v [ 0 ] ] + + ;
hv [ ei - > v [ 1 ] ] + + ;
}
for ( VertexIterator vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
{
if ( hv [ vi ] > 2 )
ps . AddVert ( * vi ) ;
}
}
2011-04-01 18:25:49 +02:00
2014-05-15 12:35:08 +02:00
static void FaceUniform ( MeshType & m , VertexSampler & ps , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
if ( sampleNum > = m . fn ) {
AllFace ( m , ps ) ;
return ;
}
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
std : : vector < FacePointer > faceVec ;
FillAndShuffleFacePointerVector ( m , faceVec ) ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
for ( int i = 0 ; i < sampleNum ; + + i )
ps . AddFace ( * faceVec [ i ] , Barycenter ( * faceVec [ i ] ) ) ;
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
static void AllFace ( MeshType & m , VertexSampler & ps )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
if ( ! ( * fi ) . IsD ( ) )
{
ps . AddFace ( * fi , Barycenter ( * fi ) ) ;
}
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
static void AllEdge ( MeshType & m , VertexSampler & ps )
2011-04-01 18:25:49 +02:00
{
2013-06-24 12:51:53 +02:00
// Edge sampling.
2014-05-15 12:35:08 +02:00
typedef typename UpdateTopology < MeshType > : : PEdge SimpleEdge ;
2013-06-24 12:51:53 +02:00
std : : vector < SimpleEdge > Edges ;
typename std : : vector < SimpleEdge > : : iterator ei ;
2014-05-15 12:35:08 +02:00
UpdateTopology < MeshType > : : FillUniqueEdgeVector ( m , Edges ) ;
2013-06-24 12:51:53 +02:00
for ( ei = Edges . begin ( ) ; ei ! = Edges . end ( ) ; + + ei )
ps . AddFace ( * ( * ei ) . f , ei - > EdgeBarycentricToFaceBarycentric ( 0.5 ) ) ;
2011-04-01 18:25:49 +02:00
}
// Regular Uniform Edge sampling
2012-10-11 13:08:38 +02:00
// Each edge is subdivided in a number of pieces proprtional to its length
2011-04-01 18:25:49 +02:00
// Sample are choosen without touching the vertices.
2014-05-15 12:35:08 +02:00
static void EdgeUniform ( MeshType & m , VertexSampler & ps , int sampleNum , bool sampleFauxEdge = true )
2011-04-01 18:25:49 +02:00
{
2014-06-24 10:48:27 +02:00
typedef typename UpdateTopology < MeshType > : : PEdge SimpleEdge ;
std : : vector < SimpleEdge > Edges ;
UpdateTopology < MeshType > : : FillUniqueEdgeVector ( m , Edges , sampleFauxEdge ) ;
// First loop compute total edge length;
float edgeSum = 0 ;
typename std : : vector < SimpleEdge > : : iterator ei ;
for ( ei = Edges . begin ( ) ; ei ! = Edges . end ( ) ; + + ei )
edgeSum + = Distance ( ( * ei ) . v [ 0 ] - > P ( ) , ( * ei ) . v [ 1 ] - > P ( ) ) ;
float sampleLen = edgeSum / sampleNum ;
float rest = 0 ;
for ( ei = Edges . begin ( ) ; ei ! = Edges . end ( ) ; + + ei )
{
float len = Distance ( ( * ei ) . v [ 0 ] - > P ( ) , ( * ei ) . v [ 1 ] - > P ( ) ) ;
float samplePerEdge = floor ( ( len + rest ) / sampleLen ) ;
rest = ( len + rest ) - samplePerEdge * sampleLen ;
float step = 1.0 / ( samplePerEdge + 1 ) ;
for ( int i = 0 ; i < samplePerEdge ; + + i )
{
CoordType interp ( 0 , 0 , 0 ) ;
interp [ ( * ei ) . z ] = step * ( i + 1 ) ;
interp [ ( ( * ei ) . z + 1 ) % 3 ] = 1.0 - step * ( i + 1 ) ;
ps . AddFace ( * ( * ei ) . f , interp ) ;
}
}
2011-04-01 18:25:49 +02:00
}
2013-03-01 09:34:33 +01:00
// Generate the barycentric coords of a random point over a single face,
// with a uniform distribution over the triangle.
// It uses the parallelogram folding trick.
2012-10-22 20:42:11 +02:00
static CoordType RandomBarycentric ( )
2011-04-01 18:25:49 +02:00
{
2013-06-24 12:51:53 +02:00
return math : : GenerateBarycentricUniform < ScalarType > ( SamplingRandomGenerator ( ) ) ;
2011-04-01 18:25:49 +02:00
}
2012-10-22 20:42:11 +02:00
// Given a triangle return a random point over it
static CoordType RandomPointInTriangle ( const FaceType & f )
{
2013-12-20 03:27:09 +01:00
CoordType u = RandomBarycentric ( ) ;
return f . cP ( 0 ) * u [ 0 ] + f . cP ( 1 ) * u [ 1 ] + f . cP ( 2 ) * u [ 2 ] ;
2012-10-22 20:42:11 +02:00
}
2014-05-15 12:35:08 +02:00
static void StratifiedMontecarlo ( MeshType & m , VertexSampler & ps , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
ScalarType area = Stat < MeshType > : : ComputeMeshArea ( m ) ;
2013-12-20 03:27:09 +01:00
ScalarType samplePerAreaUnit = sampleNum / area ;
// Montecarlo sampling.
double floatSampleNum = 0.0 ;
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
if ( ! ( * fi ) . IsD ( ) )
{
// compute # samples in the current face (taking into account of the remainders)
floatSampleNum + = 0.5 * DoubleArea ( * fi ) * samplePerAreaUnit ;
int faceSampleNum = ( int ) floatSampleNum ;
// for every sample p_i in T...
for ( int i = 0 ; i < faceSampleNum ; i + + )
ps . AddFace ( * fi , RandomBarycentric ( ) ) ;
floatSampleNum - = ( double ) faceSampleNum ;
}
2011-04-01 18:25:49 +02:00
}
/**
2013-06-24 12:51:53 +02:00
This function compute montecarlo distribution with an approximate number of
samples exploiting the poisson distribution approximation of the binomial distribution .
2011-04-01 18:25:49 +02:00
For a given triangle t of area a_t , in a Mesh of area A ,
if we take n_s sample over the mesh , the number of samples that falls in t
follows the poisson distribution of P ( lambda ) with lambda = n_s * ( a_t / A ) .
2013-06-24 12:51:53 +02:00
To approximate the Binomial we use a Poisson distribution with parameter
\ lambda = np can be used as an approximation to B ( n , p )
( it works if n is sufficiently large and p is sufficiently small ) .
2011-04-01 18:25:49 +02:00
*/
2014-05-15 12:35:08 +02:00
static void MontecarloPoisson ( MeshType & m , VertexSampler & ps , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
ScalarType area = Stat < MeshType > : : ComputeMeshArea ( m ) ;
2011-04-01 18:25:49 +02:00
ScalarType samplePerAreaUnit = sampleNum / area ;
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
if ( ! ( * fi ) . IsD ( ) )
{
float areaT = DoubleArea ( * fi ) * 0.5f ;
int faceSampleNum = Poisson ( areaT * samplePerAreaUnit ) ;
// for every sample p_i in T...
for ( int i = 0 ; i < faceSampleNum ; i + + )
2012-10-22 20:42:11 +02:00
ps . AddFace ( * fi , RandomBarycentric ( ) ) ;
2011-04-01 18:25:49 +02:00
// SampleNum -= (double) faceSampleNum;
}
}
2013-06-24 12:51:53 +02:00
/**
This function computes a montecarlo distribution with an EXACT number of samples .
it works by generating a sequence of consecutive segments proportional to the triangle areas
and actually shooting sample over this line
*/
2014-05-15 12:35:08 +02:00
static void EdgeMontecarlo ( MeshType & m , VertexSampler & ps , int sampleNum , bool sampleAllEdges )
2013-06-24 12:51:53 +02:00
{
2014-05-15 12:35:08 +02:00
typedef typename UpdateTopology < MeshType > : : PEdge SimpleEdge ;
2013-06-24 12:51:53 +02:00
std : : vector < SimpleEdge > Edges ;
2014-05-15 12:35:08 +02:00
UpdateTopology < MeshType > : : FillUniqueEdgeVector ( m , Edges , sampleAllEdges ) ;
2013-06-24 12:51:53 +02:00
assert ( ! Edges . empty ( ) ) ;
typedef std : : pair < ScalarType , SimpleEdge * > IntervalType ;
std : : vector < IntervalType > intervals ( Edges . size ( ) + 1 ) ;
int i = 0 ;
intervals [ i ] = std : : make_pair ( 0 , ( SimpleEdge * ) ( 0 ) ) ;
// First loop: build a sequence of consecutive segments proportional to the edge lenghts.
typename std : : vector < SimpleEdge > : : iterator ei ;
for ( ei = Edges . begin ( ) ; ei ! = Edges . end ( ) ; ei + + )
{
intervals [ i + 1 ] = std : : make_pair ( intervals [ i ] . first + Distance ( ( * ei ) . v [ 0 ] - > P ( ) , ( * ei ) . v [ 1 ] - > P ( ) ) , & * ei ) ;
+ + i ;
}
// Second Loop get a point on the line 0...Sum(edgeLen) to pick a point;
ScalarType edgeSum = intervals . back ( ) . first ;
for ( i = 0 ; i < sampleNum ; + + i )
{
ScalarType val = edgeSum * RandomDouble01 ( ) ;
// lower_bound returns the furthermost iterator i in [first, last) such that, for every iterator j in [first, i), *j < value.
// E.g. An iterator pointing to the first element "not less than" val, or end() if every element is less than val.
typename std : : vector < IntervalType > : : iterator it = lower_bound ( intervals . begin ( ) , intervals . end ( ) , std : : make_pair ( val , ( SimpleEdge * ) ( 0 ) ) ) ;
assert ( it ! = intervals . end ( ) & & it ! = intervals . begin ( ) ) ;
assert ( ( ( * ( it - 1 ) ) . first < val ) & & ( ( * ( it ) ) . first > = val ) ) ;
SimpleEdge * ep = ( * it ) . second ;
ps . AddFace ( * ( ep - > f ) , ep - > EdgeBarycentricToFaceBarycentric ( RandomDouble01 ( ) ) ) ;
}
}
2011-04-01 18:25:49 +02:00
/**
This function computes a montecarlo distribution with an EXACT number of samples .
it works by generating a sequence of consecutive segments proportional to the triangle areas
and actually shooting sample over this line
*/
2014-05-15 12:35:08 +02:00
static void Montecarlo ( MeshType & m , VertexSampler & ps , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
typedef std : : pair < ScalarType , FacePointer > IntervalType ;
std : : vector < IntervalType > intervals ( m . fn + 1 ) ;
FaceIterator fi ;
int i = 0 ;
intervals [ i ] = std : : make_pair ( 0 , FacePointer ( 0 ) ) ;
// First loop: build a sequence of consecutive segments proportional to the triangle areas.
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
if ( ! ( * fi ) . IsD ( ) )
{
intervals [ i + 1 ] = std : : make_pair ( intervals [ i ] . first + 0.5 * DoubleArea ( * fi ) , & * fi ) ;
+ + i ;
}
ScalarType meshArea = intervals . back ( ) . first ;
for ( i = 0 ; i < sampleNum ; + + i )
{
ScalarType val = meshArea * RandomDouble01 ( ) ;
// lower_bound returns the furthermost iterator i in [first, last) such that, for every iterator j in [first, i), *j < value.
// E.g. An iterator pointing to the first element "not less than" val, or end() if every element is less than val.
typename std : : vector < IntervalType > : : iterator it = lower_bound ( intervals . begin ( ) , intervals . end ( ) , std : : make_pair ( val , FacePointer ( 0 ) ) ) ;
assert ( it ! = intervals . end ( ) ) ;
assert ( it ! = intervals . begin ( ) ) ;
assert ( ( * ( it - 1 ) ) . first < val ) ;
assert ( ( * ( it ) ) . first > = val ) ;
ps . AddFace ( * ( * it ) . second , RandomBarycentric ( ) ) ;
}
2011-04-01 18:25:49 +02:00
}
2013-03-01 09:34:33 +01:00
2014-05-15 12:35:08 +02:00
static ScalarType WeightedArea ( FaceType & f , PerVertexFloatAttribute & wH )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
ScalarType averageQ = ( wH [ f . V ( 0 ) ] + wH [ f . V ( 1 ) ] + wH [ f . V ( 2 ) ] ) / 3.0 ;
return averageQ * averageQ * DoubleArea ( f ) / 2.0 ;
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
/// Compute a sampling of the surface that is weighted by the quality and a variance
///
/// We use the quality as linear distortion of density.
/// We consider each triangle as scaled between 1 and 1/variance linearly according quality.
///
/// In practice with variance 2 the average distance between sample will double where the quality is maxima.
/// If you have two same area region A with q==-1 and B with q==1, if variance==2 the A will have 4 times more samples than B
///
static void WeightedMontecarlo ( MeshType & m , VertexSampler & ps , int sampleNum , float variance )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
tri : : RequirePerVertexQuality ( m ) ;
tri : : RequireCompactness ( m ) ;
PerVertexFloatAttribute rH = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < float > ( m , " radius " ) ;
InitRadiusHandleFromQuality ( m , rH , 1.0 , variance , true ) ;
ScalarType weightedArea = 0 ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
weightedArea + = WeightedArea ( * fi , rH ) ;
ScalarType samplePerAreaUnit = sampleNum / weightedArea ;
// Montecarlo sampling.
double floatSampleNum = 0.0 ;
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
2013-12-20 03:27:09 +01:00
{
2014-05-15 12:35:08 +02:00
// compute # samples in the current face (taking into account of the remainders)
floatSampleNum + = WeightedArea ( * fi , rH ) * samplePerAreaUnit ;
int faceSampleNum = ( int ) floatSampleNum ;
2013-12-20 03:27:09 +01:00
2014-05-15 12:35:08 +02:00
// for every sample p_i in T...
for ( int i = 0 ; i < faceSampleNum ; i + + )
ps . AddFace ( * fi , RandomBarycentric ( ) ) ;
2013-03-01 09:34:33 +01:00
2014-05-15 12:35:08 +02:00
floatSampleNum - = ( double ) faceSampleNum ;
2011-04-01 18:25:49 +02:00
}
}
2013-03-01 09:34:33 +01:00
// Subdivision sampling of a single face.
2011-04-01 18:25:49 +02:00
// return number of added samples
static int SingleFaceSubdivision ( int sampleNum , const CoordType & v0 , const CoordType & v1 , const CoordType & v2 , VertexSampler & ps , FacePointer fp , bool randSample )
{
// recursive face subdivision.
if ( sampleNum = = 1 )
{
// ground case.
CoordType SamplePoint ;
2013-03-01 09:34:33 +01:00
if ( randSample )
2011-04-01 18:25:49 +02:00
{
2012-10-22 20:42:11 +02:00
CoordType rb = RandomBarycentric ( ) ;
2011-04-01 18:25:49 +02:00
SamplePoint = v0 * rb [ 0 ] + v1 * rb [ 1 ] + v2 * rb [ 2 ] ;
}
else SamplePoint = ( ( v0 + v1 + v2 ) * ( 1.0f / 3.0f ) ) ;
ps . AddFace ( * fp , SamplePoint ) ;
return 1 ;
}
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
int s0 = sampleNum / 2 ;
int s1 = sampleNum - s0 ;
assert ( s0 > 0 ) ;
assert ( s1 > 0 ) ;
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
ScalarType w0 = ScalarType ( s1 ) / ScalarType ( sampleNum ) ;
ScalarType w1 = 1.0 - w0 ;
// compute the longest edge.
ScalarType maxd01 = SquaredDistance ( v0 , v1 ) ;
ScalarType maxd12 = SquaredDistance ( v1 , v2 ) ;
ScalarType maxd20 = SquaredDistance ( v2 , v0 ) ;
int res ;
if ( maxd01 > maxd12 )
if ( maxd01 > maxd20 ) res = 0 ;
else res = 2 ;
else
if ( maxd12 > maxd20 ) res = 1 ;
else res = 2 ;
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
int faceSampleNum = 0 ;
// break the input triangle along the midpoint of the longest edge.
CoordType pp ;
switch ( res )
{
case 0 : pp = v0 * w0 + v1 * w1 ;
faceSampleNum + = SingleFaceSubdivision ( s0 , v0 , pp , v2 , ps , fp , randSample ) ;
faceSampleNum + = SingleFaceSubdivision ( s1 , pp , v1 , v2 , ps , fp , randSample ) ;
break ;
case 1 : pp = v1 * w0 + v2 * w1 ;
faceSampleNum + = SingleFaceSubdivision ( s0 , v0 , v1 , pp , ps , fp , randSample ) ;
faceSampleNum + = SingleFaceSubdivision ( s1 , v0 , pp , v2 , ps , fp , randSample ) ;
break ;
case 2 : pp = v0 * w0 + v2 * w1 ;
faceSampleNum + = SingleFaceSubdivision ( s0 , v0 , v1 , pp , ps , fp , randSample ) ;
faceSampleNum + = SingleFaceSubdivision ( s1 , pp , v1 , v2 , ps , fp , randSample ) ;
break ;
}
return faceSampleNum ;
}
/// Compute a sampling of the surface where the points are regularly scattered over the face surface using a recursive longest-edge subdivision rule.
2014-05-15 12:35:08 +02:00
static void FaceSubdivision ( MeshType & m , VertexSampler & ps , int sampleNum , bool randSample )
2011-04-01 18:25:49 +02:00
{
2013-03-01 09:34:33 +01:00
2014-05-15 12:35:08 +02:00
ScalarType area = Stat < MeshType > : : ComputeMeshArea ( m ) ;
2013-12-20 03:27:09 +01:00
ScalarType samplePerAreaUnit = sampleNum / area ;
std : : vector < FacePointer > faceVec ;
FillAndShuffleFacePointerVector ( m , faceVec ) ;
2014-05-15 12:35:08 +02:00
vcg : : tri : : UpdateNormal < MeshType > : : PerFaceNormalized ( m ) ;
2013-12-20 03:27:09 +01:00
double floatSampleNum = 0.0 ;
int faceSampleNum ;
// Subdivision sampling.
typename std : : vector < FacePointer > : : iterator fi ;
for ( fi = faceVec . begin ( ) ; fi ! = faceVec . end ( ) ; fi + + )
{
const CoordType b0 ( 1.0 , 0.0 , 0.0 ) ;
const CoordType b1 ( 0.0 , 1.0 , 0.0 ) ;
const CoordType b2 ( 0.0 , 0.0 , 1.0 ) ;
// compute # samples in the current face.
floatSampleNum + = 0.5 * DoubleArea ( * * fi ) * samplePerAreaUnit ;
faceSampleNum = ( int ) floatSampleNum ;
if ( faceSampleNum > 0 )
faceSampleNum = SingleFaceSubdivision ( faceSampleNum , b0 , b1 , b2 , ps , * fi , randSample ) ;
floatSampleNum - = ( double ) faceSampleNum ;
}
2011-04-01 18:25:49 +02:00
}
//---------
// Subdivision sampling of a single face.
// return number of added samples
static int SingleFaceSubdivisionOld ( int sampleNum , const CoordType & v0 , const CoordType & v1 , const CoordType & v2 , VertexSampler & ps , FacePointer fp , bool randSample )
{
// recursive face subdivision.
if ( sampleNum = = 1 )
{
// ground case.
CoordType SamplePoint ;
if ( randSample )
{
2012-10-22 20:42:11 +02:00
CoordType rb = RandomBarycentric ( ) ;
2011-04-01 18:25:49 +02:00
SamplePoint = v0 * rb [ 0 ] + v1 * rb [ 1 ] + v2 * rb [ 2 ] ;
}
else SamplePoint = ( ( v0 + v1 + v2 ) * ( 1.0f / 3.0f ) ) ;
CoordType SampleBary ;
2011-04-05 23:53:12 +02:00
InterpolationParameters ( * fp , SamplePoint , SampleBary ) ;
2011-04-01 18:25:49 +02:00
ps . AddFace ( * fp , SampleBary ) ;
return 1 ;
}
int s0 = sampleNum / 2 ;
int s1 = sampleNum - s0 ;
assert ( s0 > 0 ) ;
assert ( s1 > 0 ) ;
ScalarType w0 = ScalarType ( s1 ) / ScalarType ( sampleNum ) ;
ScalarType w1 = 1.0 - w0 ;
// compute the longest edge.
ScalarType maxd01 = SquaredDistance ( v0 , v1 ) ;
ScalarType maxd12 = SquaredDistance ( v1 , v2 ) ;
ScalarType maxd20 = SquaredDistance ( v2 , v0 ) ;
int res ;
if ( maxd01 > maxd12 )
if ( maxd01 > maxd20 ) res = 0 ;
else res = 2 ;
else
if ( maxd12 > maxd20 ) res = 1 ;
else res = 2 ;
int faceSampleNum = 0 ;
// break the input triangle along the midpoint of the longest edge.
CoordType pp ;
switch ( res )
{
case 0 : pp = v0 * w0 + v1 * w1 ;
faceSampleNum + = SingleFaceSubdivision ( s0 , v0 , pp , v2 , ps , fp , randSample ) ;
faceSampleNum + = SingleFaceSubdivision ( s1 , pp , v1 , v2 , ps , fp , randSample ) ;
break ;
case 1 : pp = v1 * w0 + v2 * w1 ;
faceSampleNum + = SingleFaceSubdivision ( s0 , v0 , v1 , pp , ps , fp , randSample ) ;
faceSampleNum + = SingleFaceSubdivision ( s1 , v0 , pp , v2 , ps , fp , randSample ) ;
break ;
case 2 : pp = v0 * w0 + v2 * w1 ;
faceSampleNum + = SingleFaceSubdivision ( s0 , v0 , v1 , pp , ps , fp , randSample ) ;
faceSampleNum + = SingleFaceSubdivision ( s1 , pp , v1 , v2 , ps , fp , randSample ) ;
break ;
}
return faceSampleNum ;
}
/// Compute a sampling of the surface where the points are regularly scattered over the face surface using a recursive longest-edge subdivision rule.
2014-05-15 12:35:08 +02:00
static void FaceSubdivisionOld ( MeshType & m , VertexSampler & ps , int sampleNum , bool randSample )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
ScalarType area = Stat < MeshType > : : ComputeMeshArea ( m ) ;
2011-04-01 18:25:49 +02:00
ScalarType samplePerAreaUnit = sampleNum / area ;
std : : vector < FacePointer > faceVec ;
FillAndShuffleFacePointerVector ( m , faceVec ) ;
2014-05-15 12:35:08 +02:00
tri : : UpdateNormal < MeshType > : : PerFaceNormalized ( m ) ;
2011-04-01 18:25:49 +02:00
double floatSampleNum = 0.0 ;
int faceSampleNum ;
// Subdivision sampling.
typename std : : vector < FacePointer > : : iterator fi ;
for ( fi = faceVec . begin ( ) ; fi ! = faceVec . end ( ) ; fi + + )
{
// compute # samples in the current face.
floatSampleNum + = 0.5 * DoubleArea ( * * fi ) * samplePerAreaUnit ;
faceSampleNum = ( int ) floatSampleNum ;
if ( faceSampleNum > 0 )
faceSampleNum = SingleFaceSubdivision ( faceSampleNum , ( * * fi ) . V ( 0 ) - > cP ( ) , ( * * fi ) . V ( 1 ) - > cP ( ) , ( * * fi ) . V ( 2 ) - > cP ( ) , ps , * fi , randSample ) ;
floatSampleNum - = ( double ) faceSampleNum ;
}
}
//---------
// Similar Triangles sampling.
// Skip vertex and edges
2013-03-01 09:34:33 +01:00
// Sample per edges includes vertexes, so here we should expect n_samples_per_edge >=4
2011-04-01 18:25:49 +02:00
static int SingleFaceSimilar ( FacePointer fp , VertexSampler & ps , int n_samples_per_edge )
{
2013-03-01 09:34:33 +01:00
int n_samples = 0 ;
2011-04-01 18:25:49 +02:00
int i , j ;
float segmentNum = n_samples_per_edge - 1 ;
2013-03-01 09:34:33 +01:00
float segmentLen = 1.0 / segmentNum ;
// face sampling.
2011-04-01 18:25:49 +02:00
for ( i = 1 ; i < n_samples_per_edge - 1 ; i + + )
for ( j = 1 ; j < n_samples_per_edge - 1 - i ; j + + )
{
//AddSample( v0 + (V1*(double)i + V2*(double)j) );
2013-03-01 09:34:33 +01:00
CoordType sampleBary ( i * segmentLen , j * segmentLen , 1.0 - ( i * segmentLen + j * segmentLen ) ) ;
2011-04-01 18:25:49 +02:00
n_samples + + ;
2013-03-01 09:34:33 +01:00
ps . AddFace ( * fp , sampleBary ) ;
2011-04-01 18:25:49 +02:00
}
2013-03-01 09:34:33 +01:00
return n_samples ;
2011-04-01 18:25:49 +02:00
}
static int SingleFaceSimilarDual ( FacePointer fp , VertexSampler & ps , int n_samples_per_edge , bool randomFlag )
{
2013-03-01 09:34:33 +01:00
int n_samples = 0 ;
2011-04-01 18:25:49 +02:00
float i , j ;
float segmentNum = n_samples_per_edge - 1 ;
2013-03-01 09:34:33 +01:00
float segmentLen = 1.0 / segmentNum ;
// face sampling.
2011-04-01 18:25:49 +02:00
for ( i = 0 ; i < n_samples_per_edge - 1 ; i + + )
for ( j = 0 ; j < n_samples_per_edge - 1 - i ; j + + )
{
//AddSample( v0 + (V1*(double)i + V2*(double)j) );
2013-03-01 09:34:33 +01:00
CoordType V0 ( ( i + 0 ) * segmentLen , ( j + 0 ) * segmentLen , 1.0 - ( ( i + 0 ) * segmentLen + ( j + 0 ) * segmentLen ) ) ;
CoordType V1 ( ( i + 1 ) * segmentLen , ( j + 0 ) * segmentLen , 1.0 - ( ( i + 1 ) * segmentLen + ( j + 0 ) * segmentLen ) ) ;
CoordType V2 ( ( i + 0 ) * segmentLen , ( j + 1 ) * segmentLen , 1.0 - ( ( i + 0 ) * segmentLen + ( j + 1 ) * segmentLen ) ) ;
n_samples + + ;
if ( randomFlag ) {
CoordType rb = RandomBarycentric ( ) ;
ps . AddFace ( * fp , V0 * rb [ 0 ] + V1 * rb [ 1 ] + V2 * rb [ 2 ] ) ;
} else ps . AddFace ( * fp , ( V0 + V1 + V2 ) / 3.0 ) ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
if ( j < n_samples_per_edge - i - 2 )
{
CoordType V3 ( ( i + 1 ) * segmentLen , ( j + 1 ) * segmentLen , 1.0 - ( ( i + 1 ) * segmentLen + ( j + 1 ) * segmentLen ) ) ;
n_samples + + ;
if ( randomFlag ) {
CoordType rb = RandomBarycentric ( ) ;
ps . AddFace ( * fp , V3 * rb [ 0 ] + V1 * rb [ 1 ] + V2 * rb [ 2 ] ) ;
} else ps . AddFace ( * fp , ( V3 + V1 + V2 ) / 3.0 ) ;
}
}
return n_samples ;
2011-04-01 18:25:49 +02:00
}
2013-03-01 09:34:33 +01:00
// Similar sampling
// Each triangle is subdivided into similar triangles following a generalization of the classical 1-to-4 splitting rule of triangles.
// According to the level of subdivision <k> you get 1, 4 , 9, 16 , <k^2> triangles.
// Depending on the kind of the sampling strategies we can have two different approach to choosing the sample points.
2011-04-01 18:25:49 +02:00
// 1) you have already sampled both edges and vertices
2013-03-01 09:34:33 +01:00
// 2) you are not going to take samples on edges and vertices.
//
2011-04-01 18:25:49 +02:00
// In the first case you have to consider only internal vertices of the subdivided triangles (to avoid multiple sampling of edges and vertices).
// Therefore the number of internal points is ((k-3)*(k-2))/2. where k is the number of points on an edge (vertex included)
2013-03-01 09:34:33 +01:00
// E.g. for k=4 you get 3 segments on each edges and the original triangle is subdivided
2011-04-01 18:25:49 +02:00
// into 9 smaller triangles and you get (1*2)/2 == 1 only a single internal point.
2013-03-01 09:34:33 +01:00
// So if you want N samples in a triangle you have to solve k^2 -5k +6 - 2N = 0
2011-04-01 18:25:49 +02:00
// from which you get:
//
2013-03-01 09:34:33 +01:00
// 5 + sqrt( 1 + 8N )
// k = -------------------
2011-04-01 18:25:49 +02:00
// 2
//
2013-03-01 09:34:33 +01:00
// In the second case if you are not interested to skip the sampling on edges and vertices you have to consider as sample number the number of triangles.
2011-04-01 18:25:49 +02:00
// So if you want N samples in a triangle, the number <k> of points on an edge (vertex included) should be simply:
2013-03-01 09:34:33 +01:00
// k = 1 + sqrt(N)
// examples:
2011-04-01 18:25:49 +02:00
// N = 4 -> k = 3
2013-03-01 09:34:33 +01:00
// N = 9 -> k = 4
2011-04-01 18:25:49 +02:00
2014-05-15 12:35:08 +02:00
//template <class MeshType>
//void Sampling<MeshType>::SimilarFaceSampling()
static void FaceSimilar ( MeshType & m , VertexSampler & ps , int sampleNum , bool dualFlag , bool randomFlag )
2013-03-01 09:34:33 +01:00
{
2014-05-15 12:35:08 +02:00
ScalarType area = Stat < MeshType > : : ComputeMeshArea ( m ) ;
2013-12-20 03:27:09 +01:00
ScalarType samplePerAreaUnit = sampleNum / area ;
2011-04-01 18:25:49 +02:00
2013-03-01 09:34:33 +01:00
// Similar Triangles sampling.
2011-04-01 18:25:49 +02:00
int n_samples_per_edge ;
double n_samples_decimal = 0.0 ;
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
{
// compute # samples in the current face.
n_samples_decimal + = 0.5 * DoubleArea ( * fi ) * samplePerAreaUnit ;
int n_samples = ( int ) n_samples_decimal ;
if ( n_samples > 0 )
{
// face sampling.
2013-03-01 09:34:33 +01:00
if ( dualFlag )
{
n_samples_per_edge = ( int ) ( ( sqrt ( 1.0 + 8.0 * ( double ) n_samples ) + 5.0 ) / 2.0 ) ; // original for non dual case
n_samples = SingleFaceSimilar ( & * fi , ps , n_samples_per_edge ) ;
} else {
n_samples_per_edge = ( int ) ( sqrt ( ( double ) n_samples ) + 1.0 ) ;
n_samples = SingleFaceSimilarDual ( & * fi , ps , n_samples_per_edge , randomFlag ) ;
}
2011-04-01 18:25:49 +02:00
}
n_samples_decimal - = ( double ) n_samples ;
}
}
2013-12-20 03:27:09 +01:00
// Rasterization fuction
// Take a triangle
// T deve essere una classe funzionale che ha l'operatore ()
// con due parametri x,y di tipo S esempio:
// class Foo { public void operator()(int x, int y ) { ??? } };
2011-04-01 18:25:49 +02:00
// This function does rasterization with a safety buffer area, thus accounting some points actually outside triangle area
// The safety area samples are generated according to face flag BORDER which should be true for texture space border edges
// Use correctSafePointsBaryCoords = true to map safety texels to closest point barycentric coords (on edge).
2014-05-15 12:35:08 +02:00
static void SingleFaceRaster ( typename MeshType : : FaceType & f , VertexSampler & ps ,
const Point2 < typename MeshType : : ScalarType > & v0 ,
const Point2 < typename MeshType : : ScalarType > & v1 ,
const Point2 < typename MeshType : : ScalarType > & v2 ,
2011-04-01 18:25:49 +02:00
bool correctSafePointsBaryCoords = true )
{
2014-05-15 12:35:08 +02:00
typedef typename MeshType : : ScalarType S ;
2011-04-01 18:25:49 +02:00
// Calcolo bounding box
Box2i bbox ;
2013-03-01 09:34:33 +01:00
Box2 < S > bboxf ;
bboxf . Add ( v0 ) ;
bboxf . Add ( v1 ) ;
bboxf . Add ( v2 ) ;
2013-12-20 03:27:09 +01:00
bbox . min [ 0 ] = floor ( bboxf . min [ 0 ] ) ;
bbox . min [ 1 ] = floor ( bboxf . min [ 1 ] ) ;
bbox . max [ 0 ] = ceil ( bboxf . max [ 0 ] ) ;
bbox . max [ 1 ] = ceil ( bboxf . max [ 1 ] ) ;
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
// Calcolo versori degli spigoli
Point2 < S > d10 = v1 - v0 ;
Point2 < S > d21 = v2 - v1 ;
Point2 < S > d02 = v0 - v2 ;
// Preparazione prodotti scalari
S b0 = ( bbox . min [ 0 ] - v0 [ 0 ] ) * d10 [ 1 ] - ( bbox . min [ 1 ] - v0 [ 1 ] ) * d10 [ 0 ] ;
S b1 = ( bbox . min [ 0 ] - v1 [ 0 ] ) * d21 [ 1 ] - ( bbox . min [ 1 ] - v1 [ 1 ] ) * d21 [ 0 ] ;
S b2 = ( bbox . min [ 0 ] - v2 [ 0 ] ) * d02 [ 1 ] - ( bbox . min [ 1 ] - v2 [ 1 ] ) * d02 [ 0 ] ;
// Preparazione degli steps
S db0 = d10 [ 1 ] ;
S db1 = d21 [ 1 ] ;
S db2 = d02 [ 1 ] ;
// Preparazione segni
S dn0 = - d10 [ 0 ] ;
S dn1 = - d21 [ 0 ] ;
S dn2 = - d02 [ 0 ] ;
//Calculating orientation
bool flipped = ! ( d02 * vcg : : Point2 < S > ( - d10 [ 1 ] , d10 [ 0 ] ) > = 0 ) ;
// Calculating border edges
Segment2 < S > borderEdges [ 3 ] ;
S edgeLength [ 3 ] ;
unsigned char edgeMask = 0 ;
if ( f . IsB ( 0 ) ) {
borderEdges [ 0 ] = Segment2 < S > ( v0 , v1 ) ;
edgeLength [ 0 ] = borderEdges [ 0 ] . Length ( ) ;
edgeMask | = 1 ;
}
2013-03-01 09:34:33 +01:00
if ( f . IsB ( 1 ) ) {
2011-04-01 18:25:49 +02:00
borderEdges [ 1 ] = Segment2 < S > ( v1 , v2 ) ;
edgeLength [ 1 ] = borderEdges [ 1 ] . Length ( ) ;
edgeMask | = 2 ;
}
if ( f . IsB ( 2 ) ) {
borderEdges [ 2 ] = Segment2 < S > ( v2 , v0 ) ;
edgeLength [ 2 ] = borderEdges [ 2 ] . Length ( ) ;
edgeMask | = 4 ;
}
// Rasterizzazione
double de = v0 [ 0 ] * v1 [ 1 ] - v0 [ 0 ] * v2 [ 1 ] - v1 [ 0 ] * v0 [ 1 ] + v1 [ 0 ] * v2 [ 1 ] - v2 [ 0 ] * v1 [ 1 ] + v2 [ 0 ] * v0 [ 1 ] ;
for ( int x = bbox . min [ 0 ] - 1 ; x < = bbox . max [ 0 ] + 1 ; + + x )
{
bool in = false ;
2013-03-01 09:34:33 +01:00
S n [ 3 ] = { b0 - db0 - dn0 , b1 - db1 - dn1 , b2 - db2 - dn2 } ;
2011-04-01 18:25:49 +02:00
for ( int y = bbox . min [ 1 ] - 1 ; y < = bbox . max [ 1 ] + 1 ; + + y )
{
2012-10-11 13:08:38 +02:00
if ( ( ( n [ 0 ] > = 0 & & n [ 1 ] > = 0 & & n [ 2 ] > = 0 ) | | ( n [ 0 ] < = 0 & & n [ 1 ] < = 0 & & n [ 2 ] < = 0 ) ) & & ( de ! = 0 ) )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
typename MeshType : : CoordType baryCoord ;
2011-04-01 18:25:49 +02:00
baryCoord [ 0 ] = double ( - y * v1 [ 0 ] + v2 [ 0 ] * y + v1 [ 1 ] * x - v2 [ 0 ] * v1 [ 1 ] + v1 [ 0 ] * v2 [ 1 ] - x * v2 [ 1 ] ) / de ;
baryCoord [ 1 ] = - double ( x * v0 [ 1 ] - x * v2 [ 1 ] - v0 [ 0 ] * y + v0 [ 0 ] * v2 [ 1 ] - v2 [ 0 ] * v0 [ 1 ] + v2 [ 0 ] * y ) / de ;
baryCoord [ 2 ] = 1 - baryCoord [ 0 ] - baryCoord [ 1 ] ;
ps . AddTextureSample ( f , baryCoord , Point2i ( x , y ) , 0 ) ;
in = true ;
} else {
// Check whether a pixel outside (on a border edge side) triangle affects color inside it
Point2 < S > px ( x , y ) ;
Point2 < S > closePoint ;
int closeEdge = - 1 ;
S minDst = FLT_MAX ;
// find the closest point (on some edge) that lies on the 2x2 squared neighborhood of the considered point
for ( int i = 0 ; i < 3 ; + + i )
{
2013-03-01 09:34:33 +01:00
if ( edgeMask & ( 1 < < i ) )
{
Point2 < S > close ;
S dst ;
2011-04-01 18:25:49 +02:00
if ( ( ( ! flipped ) & & ( n [ i ] < 0 ) ) | |
( flipped & & ( n [ i ] > 0 ) ) )
{
dst = ( ( close = ClosestPoint ( borderEdges [ i ] , px ) ) - px ) . Norm ( ) ;
if ( dst < minDst & &
close . X ( ) > px . X ( ) - 1 & & close . X ( ) < px . X ( ) + 1 & &
close . Y ( ) > px . Y ( ) - 1 & & close . Y ( ) < px . Y ( ) + 1 )
{
minDst = dst ;
closePoint = close ;
closeEdge = i ;
}
}
2013-03-01 09:34:33 +01:00
}
2011-04-01 18:25:49 +02:00
}
if ( closeEdge > = 0 )
{
2014-05-15 12:35:08 +02:00
typename MeshType : : CoordType baryCoord ;
2011-04-01 18:25:49 +02:00
if ( correctSafePointsBaryCoords )
{
// Add x,y sample with closePoint barycentric coords (on edge)
2013-11-07 23:35:32 +01:00
baryCoord [ closeEdge ] = ( closePoint - borderEdges [ closeEdge ] . P1 ( ) ) . Norm ( ) / edgeLength [ closeEdge ] ;
2011-04-01 18:25:49 +02:00
baryCoord [ ( closeEdge + 1 ) % 3 ] = 1 - baryCoord [ closeEdge ] ;
baryCoord [ ( closeEdge + 2 ) % 3 ] = 0 ;
} else {
// Add x,y sample with his own barycentric coords (off edge)
baryCoord [ 0 ] = double ( - y * v1 [ 0 ] + v2 [ 0 ] * y + v1 [ 1 ] * x - v2 [ 0 ] * v1 [ 1 ] + v1 [ 0 ] * v2 [ 1 ] - x * v2 [ 1 ] ) / de ;
baryCoord [ 1 ] = - double ( x * v0 [ 1 ] - x * v2 [ 1 ] - v0 [ 0 ] * y + v0 [ 0 ] * v2 [ 1 ] - v2 [ 0 ] * v0 [ 1 ] + v2 [ 0 ] * y ) / de ;
baryCoord [ 2 ] = 1 - baryCoord [ 0 ] - baryCoord [ 1 ] ;
}
ps . AddTextureSample ( f , baryCoord , Point2i ( x , y ) , minDst ) ;
in = true ;
}
}
n [ 0 ] + = dn0 ;
n [ 1 ] + = dn1 ;
n [ 2 ] + = dn2 ;
}
b0 + = db0 ;
b1 + = db1 ;
b2 + = db2 ;
}
}
// check the radius constrain
2012-05-06 16:20:34 +02:00
static bool checkPoissonDisk ( SampleSHT & sht , const Point3 < ScalarType > & p , ScalarType radius )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
// get the samples closest to the given one
std : : vector < VertexType * > closests ;
2015-12-03 23:58:08 +01:00
typedef EmptyTMark < MeshType > MarkerVert ;
2011-10-05 17:04:40 +02:00
static MarkerVert mv ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
Box3f bb ( p - Point3f ( radius , radius , radius ) , p + Point3f ( radius , radius , radius ) ) ;
GridGetInBox ( sht , mv , bb , closests ) ;
2011-04-01 18:25:49 +02:00
2011-10-05 17:04:40 +02:00
ScalarType r2 = radius * radius ;
2013-03-01 09:34:33 +01:00
for ( int i = 0 ; i < closests . size ( ) ; + + i )
if ( SquaredDistance ( p , closests [ i ] - > cP ( ) ) < r2 )
return false ;
2011-04-01 18:25:49 +02:00
2013-03-01 09:34:33 +01:00
return true ;
2011-04-01 18:25:49 +02:00
}
struct PoissonDiskParam
{
2012-07-02 18:31:52 +02:00
PoissonDiskParam ( )
{
adaptiveRadiusFlag = false ;
2013-06-24 12:51:53 +02:00
bestSampleChoiceFlag = true ;
2013-09-10 12:50:10 +02:00
bestSamplePoolSize = 10 ;
2012-07-02 18:31:52 +02:00
radiusVariance = 1 ;
MAXLEVELS = 5 ;
invertQuality = false ;
2011-04-01 18:25:49 +02:00
preGenFlag = false ;
preGenMesh = NULL ;
2011-10-05 17:04:40 +02:00
geodesicDistanceFlag = false ;
2014-08-29 15:21:27 +02:00
randomSeed = 0 ;
2011-04-01 18:25:49 +02:00
}
2012-07-02 18:31:52 +02:00
struct Stat
{
int montecarloTime ;
int gridTime ;
int pruneTime ;
int totalTime ;
Point3i gridSize ;
int gridCellNum ;
2014-08-09 02:02:52 +02:00
size_t sampleNum ;
2012-07-02 18:31:52 +02:00
int montecarloSampleNum ;
} ;
2011-10-05 17:04:40 +02:00
bool geodesicDistanceFlag ;
2013-06-24 12:51:53 +02:00
bool bestSampleChoiceFlag ; // In poisson disk pruning when we choose a sample in a cell, we choose the sample that remove the minimal number of other samples. This previlege the "on boundary" samples.
2013-09-10 12:50:10 +02:00
int bestSamplePoolSize ;
2012-07-02 18:31:52 +02:00
bool adaptiveRadiusFlag ;
float radiusVariance ;
bool invertQuality ;
2014-05-15 12:35:08 +02:00
bool preGenFlag ; // when generating a poisson distribution, you can initialize the set of computed points with
// ALL the vertices of another mesh. Useful for building progressive//prioritize refinements.
MeshType * preGenMesh ; // There are two ways of passing the pregen vertexes to the pruning, 1) is with a mesh pointer
// 2) with a per vertex attribute.
2011-04-01 18:25:49 +02:00
int MAXLEVELS ;
2014-08-29 15:21:27 +02:00
int randomSeed ;
2012-07-02 18:31:52 +02:00
2014-05-15 12:35:08 +02:00
Stat pds ;
2011-04-01 18:25:49 +02:00
} ;
2013-09-10 12:50:10 +02:00
// generate Poisson-disk sample using a set of pre-generated samples (with the Montecarlo algorithm)
// It always return a point.
static VertexPointer getSampleFromCell ( Point3i & cell , MontecarloSHT & samplepool )
{
2013-12-20 03:27:09 +01:00
MontecarloSHTIterator cellBegin , cellEnd ;
samplepool . Grid ( cell , cellBegin , cellEnd ) ;
return * cellBegin ;
2013-09-10 12:50:10 +02:00
}
// Given a cell of the grid it search the point that remove the minimum number of other samples
// it linearly scan all the points of a cell.
static VertexPointer getBestPrecomputedMontecarloSample ( Point3i & cell , MontecarloSHT & samplepool , ScalarType diskRadius , const PoissonDiskParam & pp )
{
MontecarloSHTIterator cellBegin , cellEnd ;
samplepool . Grid ( cell , cellBegin , cellEnd ) ;
VertexPointer bestSample = 0 ;
int minRemoveCnt = std : : numeric_limits < int > : : max ( ) ;
std : : vector < typename MontecarloSHT : : HashIterator > inSphVec ;
int i = 0 ;
for ( MontecarloSHTIterator ci = cellBegin ; ci ! = cellEnd & & i < pp . bestSamplePoolSize ; + + ci , i + + )
{
VertexPointer sp = * ci ;
if ( pp . adaptiveRadiusFlag ) diskRadius = sp - > Q ( ) ;
int curRemoveCnt = samplepool . CountInSphere ( sp - > cP ( ) , diskRadius , inSphVec ) ;
if ( curRemoveCnt < minRemoveCnt )
{
bestSample = sp ;
minRemoveCnt = curRemoveCnt ;
}
}
return bestSample ;
}
2015-09-11 07:56:59 +02:00
/// \brief Estimate the radius r that you should give to get a certain number of samples in a Poissson Disk Distribution of radius r.
///
2014-05-15 12:35:08 +02:00
static ScalarType ComputePoissonDiskRadius ( MeshType & origMesh , int sampleNum )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
ScalarType meshArea = Stat < MeshType > : : ComputeMeshArea ( origMesh ) ;
2013-12-20 03:27:09 +01:00
// Manage approximately the PointCloud Case, use the half a area of the bbox.
// TODO: If you had the radius a much better approximation could be done.
if ( meshArea = = 0 )
{
meshArea = ( origMesh . bbox . DimX ( ) * origMesh . bbox . DimY ( ) +
origMesh . bbox . DimX ( ) * origMesh . bbox . DimZ ( ) +
origMesh . bbox . DimY ( ) * origMesh . bbox . DimZ ( ) ) ;
}
ScalarType diskRadius = sqrt ( meshArea / ( 0.7 * M_PI * sampleNum ) ) ; // 0.7 is a density factor
return diskRadius ;
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
static int ComputePoissonSampleNum ( MeshType & origMesh , ScalarType diskRadius )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
ScalarType meshArea = Stat < MeshType > : : ComputeMeshArea ( origMesh ) ;
2013-12-20 03:27:09 +01:00
int sampleNum = meshArea / ( diskRadius * diskRadius * M_PI * 0.7 ) ; // 0.7 is a density factor
return sampleNum ;
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
/// When performing an adptive pruning for each sample we expect a varying radius to be removed.
/// The radius is a PerVertex attribute that we compute from the current quality
///
/// the expected radius of the sample is computed so that
/// it linearly maps the quality between diskradius and diskradius*variance
/// in other words the radius
2013-07-23 09:32:12 +02:00
2014-05-15 12:35:08 +02:00
static void InitRadiusHandleFromQuality ( MeshType & sampleMesh , PerVertexFloatAttribute & rH , ScalarType diskRadius , ScalarType radiusVariance , bool invert )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
std : : pair < float , float > minmax = tri : : Stat < MeshType > : : ComputePerVertexQualityMinMax ( sampleMesh ) ;
float minRad = diskRadius ;
float maxRad = diskRadius * radiusVariance ;
float deltaQ = minmax . second - minmax . first ;
float deltaRad = maxRad - minRad ;
for ( VertexIterator vi = sampleMesh . vert . begin ( ) ; vi ! = sampleMesh . vert . end ( ) ; vi + + )
{
rH [ * vi ] = minRad + deltaRad * ( ( invert ? minmax . second - ( * vi ) . Q ( ) : ( * vi ) . Q ( ) - minmax . first ) / deltaQ ) ;
}
2011-04-01 18:25:49 +02:00
}
2013-09-10 15:41:16 +02:00
// initialize spatial hash table for searching
// radius is the radius of empty disk centered over the samples (e.g. twice of the empty space disk)
// This radius implies that when we pick a sample in a cell all that cell probably will not be touched again.
2013-09-10 16:21:48 +02:00
// Howvever we must ensure that we do not put too many vertices inside each hash cell
2011-04-01 18:25:49 +02:00
2014-05-15 12:35:08 +02:00
static void InitSpatialHashTable ( MeshType & montecarloMesh , MontecarloSHT & montecarloSHT , ScalarType diskRadius ,
struct PoissonDiskParam pp = PoissonDiskParam ( ) )
2013-09-10 15:41:16 +02:00
{
ScalarType cellsize = 2.0f * diskRadius / sqrt ( 3.0 ) ;
float occupancyRatio = 0 ;
do
{
2011-04-01 18:25:49 +02:00
// inflating
2013-03-01 09:34:33 +01:00
BoxType bb = montecarloMesh . bbox ;
assert ( ! bb . IsNull ( ) ) ;
2013-02-23 06:50:27 +01:00
bb . Offset ( cellsize ) ;
2011-04-01 18:25:49 +02:00
2014-06-18 12:38:15 +02:00
int sizeX = std : : max ( 1 , int ( bb . DimX ( ) / cellsize ) ) ;
int sizeY = std : : max ( 1 , int ( bb . DimY ( ) / cellsize ) ) ;
int sizeZ = std : : max ( 1 , int ( bb . DimZ ( ) / cellsize ) ) ;
2011-04-01 18:25:49 +02:00
Point3i gridsize ( sizeX , sizeY , sizeZ ) ;
2013-02-23 06:50:27 +01:00
montecarloSHT . InitEmpty ( bb , gridsize ) ;
2011-04-01 18:25:49 +02:00
for ( VertexIterator vi = montecarloMesh . vert . begin ( ) ; vi ! = montecarloMesh . vert . end ( ) ; vi + + )
2014-05-15 12:35:08 +02:00
if ( ! ( * vi ) . IsD ( ) )
{
montecarloSHT . Add ( & ( * vi ) ) ;
}
2014-01-21 15:59:45 +01:00
2011-04-01 18:25:49 +02:00
montecarloSHT . UpdateAllocatedCells ( ) ;
2014-05-15 12:35:08 +02:00
pp . pds . gridSize = gridsize ;
pp . pds . gridCellNum = ( int ) montecarloSHT . AllocatedCells . size ( ) ;
2013-09-10 15:41:16 +02:00
cellsize / = 2.0f ;
occupancyRatio = float ( montecarloMesh . vn ) / float ( montecarloSHT . AllocatedCells . size ( ) ) ;
2014-05-15 12:35:08 +02:00
// qDebug(" %i / %i = %6.3f", montecarloMesh.vn , montecarloSHT.AllocatedCells.size(),occupancyRatio);
2013-09-10 15:41:16 +02:00
}
while ( occupancyRatio > 100 ) ;
}
2011-04-01 18:25:49 +02:00
2014-05-15 12:35:08 +02:00
static void PoissonDiskPruningByNumber ( VertexSampler & ps , MeshType & m ,
2014-08-09 02:02:52 +02:00
size_t sampleNum , ScalarType & diskRadius ,
2014-05-15 12:35:08 +02:00
PoissonDiskParam & pp ,
float tolerance = 0.04 ,
int maxIter = 20 )
2013-09-10 15:41:16 +02:00
{
2014-05-15 12:35:08 +02:00
size_t sampleNumMin = int ( float ( sampleNum ) * ( 1.0f - tolerance ) ) ;
size_t sampleNumMax = int ( float ( sampleNum ) * ( 1.0f + tolerance ) ) ;
float RangeMinRad = m . bbox . Diag ( ) / 50.0 ;
float RangeMaxRad = m . bbox . Diag ( ) / 50.0 ;
size_t RangeMinRadNum ;
size_t RangeMaxRadNum ;
// Note RangeMinRad < RangeMaxRad
// but RangeMinRadNum > sampleNum > RangeMaxRadNum
do {
ps . reset ( ) ;
RangeMinRad / = 2.0f ;
PoissonDiskPruning ( ps , m , RangeMinRad , pp ) ;
RangeMinRadNum = pp . pds . sampleNum ;
// qDebug("PoissonDiskPruning Iteratin Min (%6.3f:%5i) instead of %i",RangeMinRad,RangeMinRadNum,sampleNum);
} while ( RangeMinRadNum < sampleNum ) ; // if the number of sample is still smaller you have to make radius larger.
do {
ps . reset ( ) ;
RangeMaxRad * = 2.0f ;
PoissonDiskPruning ( ps , m , RangeMaxRad , pp ) ;
RangeMaxRadNum = pp . pds . sampleNum ;
// qDebug("PoissonDiskPruning Iteratin Max (%6.3f:%5i) instead of %i",RangeMaxRad,RangeMaxRadNum,sampleNum);
} while ( RangeMaxRadNum > sampleNum ) ;
2014-08-28 03:53:58 +02:00
float curRadius = RangeMaxRad ;
2014-05-15 12:35:08 +02:00
int iterCnt = 0 ;
while ( iterCnt < maxIter & &
( pp . pds . sampleNum < sampleNumMin | | pp . pds . sampleNum > sampleNumMax ) )
{
iterCnt + + ;
ps . reset ( ) ;
curRadius = ( RangeMaxRad + RangeMinRad ) / 2.0f ;
PoissonDiskPruning ( ps , m , curRadius , pp ) ;
2014-09-19 19:07:02 +02:00
// qDebug("PoissonDiskPruning Iteratin (%6.3f:%5lu %6.3f:%5lu) Cur Radius %f -> %lu sample instead of %lu",RangeMinRad,RangeMinRadNum,RangeMaxRad,RangeMaxRadNum,curRadius,pp.pds.sampleNum,sampleNum);
2014-05-15 12:35:08 +02:00
if ( pp . pds . sampleNum > sampleNum ) {
RangeMinRad = curRadius ;
RangeMinRadNum = pp . pds . sampleNum ;
}
if ( pp . pds . sampleNum < sampleNum ) {
RangeMaxRad = curRadius ;
RangeMaxRadNum = pp . pds . sampleNum ;
}
}
diskRadius = curRadius ;
}
/// This is the main function that is used to build a poisson distribuition
2016-12-16 23:19:37 +01:00
/// starting from a dense sample cloud (the montecarloMesh) by 'pruning' it.
/// it puts all the samples in a hashed UG and randomly choose a sample
2014-05-15 12:35:08 +02:00
/// and remove all the points in the sphere centered on the chosen sample
2016-12-16 23:19:37 +01:00
///
/// You can impose some constraint: all the vertices in the montecarloMesh
/// that are marked with a bool attribute called "fixed" are surely chosen
/// (if you also set the preGenFlag option)
///
2014-05-15 12:35:08 +02:00
static void PoissonDiskPruning ( VertexSampler & ps , MeshType & montecarloMesh ,
ScalarType diskRadius , PoissonDiskParam & pp )
{
tri : : RequireCompactness ( montecarloMesh ) ;
2014-08-29 15:21:27 +02:00
if ( pp . randomSeed ) SamplingRandomGenerator ( ) . initialize ( pp . randomSeed ) ;
2014-05-15 12:35:08 +02:00
if ( pp . adaptiveRadiusFlag )
tri : : RequirePerVertexQuality ( montecarloMesh ) ;
int t0 = clock ( ) ;
2013-09-10 15:41:16 +02:00
// spatial index of montecarlo samples - used to choose a new sample to insert
MontecarloSHT montecarloSHT ;
InitSpatialHashTable ( montecarloMesh , montecarloSHT , diskRadius , pp ) ;
2014-05-15 12:35:08 +02:00
// if we are doing variable density sampling we have to prepare the handle that keeps the the random samples expected radii.
2013-09-10 15:41:16 +02:00
// At this point we just assume that there is the quality values as sampled from the base mesh
2014-05-15 12:35:08 +02:00
PerVertexFloatAttribute rH = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < float > ( montecarloMesh , " radius " ) ;
2013-09-10 15:41:16 +02:00
if ( pp . adaptiveRadiusFlag )
2014-05-15 12:35:08 +02:00
InitRadiusHandleFromQuality ( montecarloMesh , rH , diskRadius , pp . radiusVariance , pp . invertQuality ) ;
2011-04-01 18:25:49 +02:00
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( montecarloSHT . AllocatedCells . begin ( ) , montecarloSHT . AllocatedCells . end ( ) , p_myrandom ) ;
2012-07-02 18:31:52 +02:00
int t1 = clock ( ) ;
2014-05-15 12:35:08 +02:00
pp . pds . montecarloSampleNum = montecarloMesh . vn ;
pp . pds . sampleNum = 0 ;
2012-07-02 18:31:52 +02:00
int removedCnt = 0 ;
2014-05-15 12:35:08 +02:00
// Initial pass for pruning the Hashed grid with the an eventual pre initialized set of samples
if ( pp . preGenFlag )
2011-04-01 18:25:49 +02:00
{
2014-05-15 12:35:08 +02:00
if ( pp . preGenMesh = = 0 )
{
typename MeshType : : template PerVertexAttributeHandle < bool > fixed ;
fixed = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < bool > ( montecarloMesh , " fixed " ) ;
for ( VertexIterator vi = montecarloMesh . vert . begin ( ) ; vi ! = montecarloMesh . vert . end ( ) ; + + vi )
if ( fixed [ * vi ] ) {
pp . pds . sampleNum + + ;
2014-01-21 15:59:45 +01:00
ps . AddVert ( * vi ) ;
removedCnt + = montecarloSHT . RemoveInSphere ( vi - > cP ( ) , diskRadius ) ;
}
2014-05-15 12:35:08 +02:00
}
else
{
for ( VertexIterator vi = pp . preGenMesh - > vert . begin ( ) ; vi ! = pp . preGenMesh - > vert . end ( ) ; + + vi )
{
ps . AddVert ( * vi ) ;
pp . pds . sampleNum + + ;
removedCnt + = montecarloSHT . RemoveInSphere ( vi - > cP ( ) , diskRadius ) ;
}
}
2011-04-01 18:25:49 +02:00
montecarloSHT . UpdateAllocatedCells ( ) ;
2012-07-02 18:31:52 +02:00
}
2011-10-05 17:04:40 +02:00
vertex : : ApproximateGeodesicDistanceFunctor < VertexType > GDF ;
2011-04-01 18:25:49 +02:00
while ( ! montecarloSHT . AllocatedCells . empty ( ) )
{
removedCnt = 0 ;
for ( size_t i = 0 ; i < montecarloSHT . AllocatedCells . size ( ) ; i + + )
{
if ( montecarloSHT . EmptyCell ( montecarloSHT . AllocatedCells [ i ] ) ) continue ;
2013-06-24 12:51:53 +02:00
ScalarType currentRadius = diskRadius ;
VertexPointer sp ;
2013-09-10 12:50:10 +02:00
if ( pp . bestSampleChoiceFlag )
sp = getBestPrecomputedMontecarloSample ( montecarloSHT . AllocatedCells [ i ] , montecarloSHT , diskRadius , pp ) ;
else
sp = getSampleFromCell ( montecarloSHT . AllocatedCells [ i ] , montecarloSHT ) ;
if ( pp . adaptiveRadiusFlag )
2014-05-15 12:35:08 +02:00
currentRadius = rH [ sp ] ;
2013-09-10 12:50:10 +02:00
2011-04-01 18:25:49 +02:00
ps . AddVert ( * sp ) ;
2014-05-15 12:35:08 +02:00
pp . pds . sampleNum + + ;
2013-06-24 12:51:53 +02:00
if ( pp . geodesicDistanceFlag ) removedCnt + = montecarloSHT . RemoveInSphereNormal ( sp - > cP ( ) , sp - > cN ( ) , GDF , currentRadius ) ;
else removedCnt + = montecarloSHT . RemoveInSphere ( sp - > cP ( ) , currentRadius ) ;
2011-04-01 18:25:49 +02:00
}
montecarloSHT . UpdateAllocatedCells ( ) ;
}
2012-07-02 18:31:52 +02:00
int t2 = clock ( ) ;
2014-05-15 12:35:08 +02:00
pp . pds . gridTime = t1 - t0 ;
pp . pds . pruneTime = t2 - t1 ;
2011-04-01 18:25:49 +02:00
}
/** Compute a Poisson-disk sampling of the surface.
* The radius of the disk is computed according to the estimated sampling density .
*
* This algorithm is an adaptation of the algorithm of White et al . :
*
2013-03-01 09:34:33 +01:00
* " Poisson Disk Point Set by Hierarchical Dart Throwing "
2011-04-01 18:25:49 +02:00
* K . B . White , D . Cline , P . K . Egbert ,
* IEEE Symposium on Interactive Ray Tracing , 2007 ,
* 10 - 12 Sept . 2007 , pp . 129 - 132.
*/
2014-05-15 12:35:08 +02:00
static void HierarchicalPoissonDisk ( MeshType & origMesh , VertexSampler & ps , MeshType & montecarloMesh , ScalarType diskRadius , const struct PoissonDiskParam pp = PoissonDiskParam ( ) )
2011-04-01 18:25:49 +02:00
{
2012-12-02 16:07:17 +01:00
// int t0=clock();
2013-03-01 09:34:33 +01:00
// spatial index of montecarlo samples - used to choose a new sample to insert
2011-04-01 18:25:49 +02:00
MontecarloSHT montecarloSHTVec [ 5 ] ;
2013-03-01 09:34:33 +01:00
// initialize spatial hash table for searching
2011-04-01 18:25:49 +02:00
// radius is the radius of empty disk centered over the samples (e.g. twice of the empty space disk)
// This radius implies that when we pick a sample in a cell all that cell will not be touched again.
ScalarType cellsize = 2.0f * diskRadius / sqrt ( 3.0 ) ;
2013-12-20 03:27:09 +01:00
// inflating
BoxType bb = origMesh . bbox ;
bb . Offset ( cellsize ) ;
2011-04-01 18:25:49 +02:00
2013-02-23 06:50:27 +01:00
int sizeX = std : : max ( 1.0f , bb . DimX ( ) / cellsize ) ;
int sizeY = std : : max ( 1.0f , bb . DimY ( ) / cellsize ) ;
int sizeZ = std : : max ( 1.0f , bb . DimZ ( ) / cellsize ) ;
2013-03-01 09:34:33 +01:00
Point3i gridsize ( sizeX , sizeY , sizeZ ) ;
2011-04-01 18:25:49 +02:00
// spatial hash table of the generated samples - used to check the radius constrain
SampleSHT checkSHT ;
2013-02-23 06:50:27 +01:00
checkSHT . InitEmpty ( bb , gridsize ) ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
// sampling algorithm
// ------------------
//
// - generate millions of samples using montecarlo algorithm
// - extract a cell (C) from the active cell list (with probability proportional to cell's volume)
// - generate a sample inside C by choosing one of the contained pre-generated samples
// - if the sample violates the radius constrain discard it, and add the cell to the cells-to-subdivide list
// - iterate until the active cell list is empty or a pre-defined number of subdivisions is reached
//
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
int level = 0 ;
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
// initialize spatial hash to index pre-generated samples
2013-02-23 06:50:27 +01:00
montecarloSHTVec [ 0 ] . InitEmpty ( bb , gridsize ) ;
2011-04-01 18:25:49 +02:00
// create active cell list
for ( VertexIterator vi = montecarloMesh . vert . begin ( ) ; vi ! = montecarloMesh . vert . end ( ) ; vi + + )
montecarloSHTVec [ 0 ] . Add ( & ( * vi ) ) ;
montecarloSHTVec [ 0 ] . UpdateAllocatedCells ( ) ;
// if we are doing variable density sampling we have to prepare the random samples quality with the correct expected radii.
2014-05-15 12:35:08 +02:00
PerVertexFloatAttribute rH = tri : : Allocator < MeshType > : : template GetPerVertexAttribute < float > ( montecarloMesh , " radius " ) ;
2013-03-01 09:34:33 +01:00
if ( pp . adaptiveRadiusFlag )
2014-05-15 12:35:08 +02:00
InitRadiusHandleFromQuality ( montecarloMesh , rH , diskRadius , pp . radiusVariance , pp . invertQuality ) ;
2013-03-01 09:34:33 +01:00
2013-12-20 03:27:09 +01:00
do
{
MontecarloSHT & montecarloSHT = montecarloSHTVec [ level ] ;
2011-04-01 18:25:49 +02:00
if ( level > 0 )
{ // initialize spatial hash with the remaining points
2013-02-23 06:50:27 +01:00
montecarloSHT . InitEmpty ( bb , gridsize ) ;
2011-04-01 18:25:49 +02:00
// create active cell list
for ( typename MontecarloSHT : : HashIterator hi = montecarloSHTVec [ level - 1 ] . hash_table . begin ( ) ; hi ! = montecarloSHTVec [ level - 1 ] . hash_table . end ( ) ; hi + + )
montecarloSHT . Add ( ( * hi ) . second ) ;
montecarloSHT . UpdateAllocatedCells ( ) ;
}
2013-03-01 09:34:33 +01:00
// shuffle active cells
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( montecarloSHT . AllocatedCells . begin ( ) , montecarloSHT . AllocatedCells . end ( ) , p_myrandom ) ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
// generate a sample inside C by choosing one of the contained pre-generated samples
//////////////////////////////////////////////////////////////////////////////////////////
int removedCnt = montecarloSHT . hash_table . size ( ) ;
int addedCnt = checkSHT . hash_table . size ( ) ;
for ( int i = 0 ; i < montecarloSHT . AllocatedCells . size ( ) ; i + + )
{
for ( int j = 0 ; j < 4 ; j + + )
{
if ( montecarloSHT . EmptyCell ( montecarloSHT . AllocatedCells [ i ] ) ) continue ;
2011-04-01 18:25:49 +02:00
2013-03-01 09:34:33 +01:00
// generate a sample chosen from the pre-generated one
2011-04-01 18:25:49 +02:00
typename MontecarloSHT : : HashIterator hi = montecarloSHT . hash_table . find ( montecarloSHT . AllocatedCells [ i ] ) ;
if ( hi = = montecarloSHT . hash_table . end ( ) ) { break ; }
VertexPointer sp = ( * hi ) . second ;
2013-03-01 09:34:33 +01:00
// vr spans between 3.0*r and r / 4.0 according to vertex quality
ScalarType sampleRadius = diskRadius ;
2014-05-15 12:35:08 +02:00
if ( pp . adaptiveRadiusFlag ) sampleRadius = rH [ sp ] ;
2013-03-01 09:34:33 +01:00
if ( checkPoissonDisk ( checkSHT , sp - > cP ( ) , sampleRadius ) )
2011-04-01 18:25:49 +02:00
{
ps . AddVert ( * sp ) ;
montecarloSHT . RemoveCell ( sp ) ;
checkSHT . Add ( sp ) ;
break ;
}
else
montecarloSHT . RemovePunctual ( sp ) ;
}
2013-03-01 09:34:33 +01:00
}
2011-04-01 18:25:49 +02:00
addedCnt = checkSHT . hash_table . size ( ) - addedCnt ;
removedCnt = removedCnt - montecarloSHT . hash_table . size ( ) ;
2013-03-01 09:34:33 +01:00
// proceed to the next level of subdivision
2011-04-01 18:25:49 +02:00
// increase grid resolution
gridsize * = 2 ;
2013-12-20 03:27:09 +01:00
//
level + + ;
} while ( level < 5 ) ;
2011-04-01 18:25:49 +02:00
}
2014-05-15 12:35:08 +02:00
//template <class MeshType>
//void Sampling<MeshType>::SimilarFaceSampling()
2011-04-01 18:25:49 +02:00
// This function also generates samples outside faces if those affects faces in texture space.
// Use correctSafePointsBaryCoords = true to map safety texels to closest point barycentric coords (on edge)
// otherwise obtained samples will map to barycentric coord actually outside face
//
// If you don't need to get those extra points clear faces Border Flags
// vcg::tri::UpdateFlags<Mesh>::FaceClearB(m);
//
// Else make sure to update border flags from texture space FFadj
// vcg::tri::UpdateTopology<Mesh>::FaceFaceFromTexCoord(m);
// vcg::tri::UpdateFlags<Mesh>::FaceBorderFromFF(m);
2014-05-15 12:35:08 +02:00
static void Texture ( MeshType & m , VertexSampler & ps , int textureWidth , int textureHeight , bool correctSafePointsBaryCoords = true )
2011-04-01 18:25:49 +02:00
{
2014-06-24 10:48:27 +02:00
typedef Point2 < ScalarType > Point2x ;
2013-12-20 03:27:09 +01:00
printf ( " Similar Triangles face sampling \n " ) ;
2014-06-24 10:48:27 +02:00
for ( FaceIterator fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
2013-12-20 03:27:09 +01:00
if ( ! fi - > IsD ( ) )
{
2014-06-24 10:48:27 +02:00
Point2x ti [ 3 ] ;
2013-12-20 03:27:09 +01:00
for ( int i = 0 ; i < 3 ; + + i )
2014-06-24 10:48:27 +02:00
ti [ i ] = Point2x ( ( * fi ) . WT ( i ) . U ( ) * textureWidth - 0.5 , ( * fi ) . WT ( i ) . V ( ) * textureHeight - 0.5 ) ;
2013-12-20 03:27:09 +01:00
// - 0.5 constants are used to obtain correct texture mapping
2011-04-01 18:25:49 +02:00
SingleFaceRaster ( * fi , ps , ti [ 0 ] , ti [ 1 ] , ti [ 2 ] , correctSafePointsBaryCoords ) ;
}
}
typedef GridStaticPtr < FaceType , ScalarType > TriMeshGrid ;
class RRParam
{
public :
float offset ;
float minDiag ;
2014-05-15 12:35:08 +02:00
tri : : FaceTmark < MeshType > markerFunctor ;
2011-04-01 18:25:49 +02:00
TriMeshGrid gM ;
} ;
2014-06-24 10:48:27 +02:00
static void RegularRecursiveOffset ( MeshType & m , std : : vector < CoordType > & pvec , ScalarType offset , float minDiag )
2011-04-01 18:25:49 +02:00
{
2013-12-20 03:27:09 +01:00
Box3 < ScalarType > bb = m . bbox ;
bb . Offset ( offset * 2.0 ) ;
2013-03-01 09:34:33 +01:00
2013-12-20 03:27:09 +01:00
RRParam rrp ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
rrp . markerFunctor . SetMesh ( & m ) ;
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
rrp . gM . Set ( m . face . begin ( ) , m . face . end ( ) , bb ) ;
2013-03-01 09:34:33 +01:00
2011-04-01 18:25:49 +02:00
2013-12-20 03:27:09 +01:00
rrp . offset = offset ;
rrp . minDiag = minDiag ;
SubdivideAndSample ( m , pvec , bb , rrp , bb . Diag ( ) ) ;
2011-04-01 18:25:49 +02:00
}
2014-06-24 10:48:27 +02:00
static void SubdivideAndSample ( MeshType & m , std : : vector < CoordType > & pvec , const Box3 < ScalarType > bb , RRParam & rrp , float curDiag )
2011-04-01 18:25:49 +02:00
{
2014-06-24 10:48:27 +02:00
CoordType startPt = bb . Center ( ) ;
ScalarType dist ;
// Compute mesh point nearest to bb center
FaceType * nearestF = 0 ;
ScalarType dist_upper_bound = curDiag + rrp . offset ;
CoordType closestPt ;
vcg : : face : : PointDistanceBaseFunctor < ScalarType > PDistFunct ;
dist = dist_upper_bound ;
nearestF = rrp . gM . GetClosest ( PDistFunct , rrp . markerFunctor , startPt , dist_upper_bound , dist , closestPt ) ;
2011-04-01 18:25:49 +02:00
curDiag / = 2 ;
2014-06-24 10:48:27 +02:00
if ( dist < dist_upper_bound )
{
if ( curDiag / 3 < rrp . minDiag ) //store points only for the last level of recursion (?)
{
if ( rrp . offset = = 0 )
pvec . push_back ( closestPt ) ;
else
{
if ( dist > rrp . offset ) // points below the offset threshold cannot be displaced at the right offset distance, we can only make points nearer.
2013-03-01 09:34:33 +01:00
{
2014-06-24 10:48:27 +02:00
CoordType delta = startPt - closestPt ;
pvec . push_back ( closestPt + delta * ( rrp . offset / dist ) ) ;
2013-03-01 09:34:33 +01:00
}
2014-06-24 10:48:27 +02:00
}
}
if ( curDiag < rrp . minDiag ) return ;
CoordType hs = ( bb . max - bb . min ) / 2 ;
for ( int i = 0 ; i < 2 ; i + + )
for ( int j = 0 ; j < 2 ; j + + )
for ( int k = 0 ; k < 2 ; k + + )
SubdivideAndSample ( m , pvec ,
BoxType ( CoordType ( bb . min [ 0 ] + i * hs [ 0 ] , bb . min [ 1 ] + j * hs [ 1 ] , bb . min [ 2 ] + k * hs [ 2 ] ) ,
CoordType ( startPt [ 0 ] + i * hs [ 0 ] , startPt [ 1 ] + j * hs [ 1 ] , startPt [ 2 ] + k * hs [ 2 ] ) ) ,
rrp , curDiag
) ;
}
2013-03-01 09:34:33 +01:00
}
2014-03-04 01:39:27 +01:00
} ; // end sampling class
2014-05-15 12:35:08 +02:00
template < class MeshType >
typename MeshType : : ScalarType ComputePoissonDiskRadius ( MeshType & origMesh , int sampleNum )
{
typedef typename MeshType : : ScalarType ScalarType ;
ScalarType meshArea = Stat < MeshType > : : ComputeMeshArea ( origMesh ) ;
// Manage approximately the PointCloud Case, use the half a area of the bbox.
// TODO: If you had the radius a much better approximation could be done.
if ( meshArea = = 0 )
{
meshArea = ( origMesh . bbox . DimX ( ) * origMesh . bbox . DimY ( ) +
origMesh . bbox . DimX ( ) * origMesh . bbox . DimZ ( ) +
origMesh . bbox . DimY ( ) * origMesh . bbox . DimZ ( ) ) ;
}
ScalarType diskRadius = sqrt ( meshArea / ( 0.7 * M_PI * sampleNum ) ) ; // 0.7 is a density factor
return diskRadius ;
}
2014-03-04 01:39:27 +01:00
2011-04-01 18:25:49 +02:00
2013-07-23 09:32:12 +02:00
template < class MeshType >
void MontecarloSampling ( MeshType & m , // the mesh that has to be sampled
MeshType & mm , // the mesh that will contain the samples
int sampleNum ) // the desired number sample, if zero you must set the radius to the wanted value
{
typedef tri : : MeshSampler < MeshType > BaseSampler ;
MeshSampler < MeshType > mcSampler ( & mm ) ;
tri : : SurfaceSampling < MeshType , BaseSampler > : : Montecarlo ( m , mcSampler , sampleNum ) ;
}
2012-04-18 23:12:12 +02:00
2012-11-23 00:28:40 +01:00
template < class MeshType >
void MontecarloSampling ( MeshType & m , // the mesh that has to be sampled
std : : vector < Point3f > & montercarloSamples , // the vector that will contain the set of points
int sampleNum ) // the desired number sample, if zero you must set the radius to the wanted value
{
typedef tri : : TrivialSampler < MeshType > BaseSampler ;
BaseSampler mcSampler ( montercarloSamples ) ;
tri : : SurfaceSampling < MeshType , BaseSampler > : : Montecarlo ( m , mcSampler , sampleNum ) ;
}
2012-07-02 18:31:52 +02:00
// Yet another simpler wrapper for the generation of a poisson disk distribution over a mesh.
2012-04-18 23:12:12 +02:00
//
template < class MeshType >
void PoissonSampling ( MeshType & m , // the mesh that has to be sampled
2014-07-01 08:20:23 +02:00
std : : vector < typename MeshType : : CoordType > & poissonSamples , // the vector that will contain the set of points
2012-04-18 23:12:12 +02:00
int sampleNum , // the desired number sample, if zero you must set the radius to the wanted value
2014-07-01 08:20:23 +02:00
typename MeshType : : ScalarType & radius , // the Poisson Disk Radius (used if sampleNum==0, setted if sampleNum!=0)
2014-08-28 11:28:14 +02:00
typename MeshType : : ScalarType radiusVariance = 1 ,
2014-08-29 15:21:27 +02:00
typename MeshType : : ScalarType PruningByNumberTolerance = 0.04f ,
unsigned int randSeed = 0 )
2013-07-23 09:32:12 +02:00
2012-04-18 23:12:12 +02:00
{
typedef tri : : TrivialSampler < MeshType > BaseSampler ;
2013-07-23 09:32:12 +02:00
typedef tri : : MeshSampler < MeshType > MontecarloSampler ;
2012-07-02 18:31:52 +02:00
typename tri : : SurfaceSampling < MeshType , BaseSampler > : : PoissonDiskParam pp ;
int t0 = clock ( ) ;
2014-08-28 03:53:58 +02:00
// if(sampleNum>0) radius = tri::SurfaceSampling<MeshType,BaseSampler>::ComputePoissonDiskRadius(m,sampleNum);
2012-04-18 23:12:12 +02:00
if ( radius > 0 & & sampleNum = = 0 ) sampleNum = tri : : SurfaceSampling < MeshType , BaseSampler > : : ComputePoissonSampleNum ( m , radius ) ;
2014-05-15 12:35:08 +02:00
pp . pds . sampleNum = sampleNum ;
2014-08-29 15:21:27 +02:00
pp . randomSeed = randSeed ;
2012-04-18 23:12:12 +02:00
poissonSamples . clear ( ) ;
2013-07-23 09:32:12 +02:00
// std::vector<Point3f> MontecarloSamples;
2012-04-18 23:12:12 +02:00
MeshType MontecarloMesh ;
// First step build the sampling
2013-07-23 09:32:12 +02:00
MontecarloSampler mcSampler ( MontecarloMesh ) ;
2012-04-18 23:12:12 +02:00
BaseSampler pdSampler ( poissonSamples ) ;
2014-08-29 15:21:27 +02:00
if ( randSeed ) tri : : SurfaceSampling < MeshType , MontecarloSampler > : : SamplingRandomGenerator ( ) . initialize ( randSeed ) ;
2013-07-23 09:32:12 +02:00
tri : : SurfaceSampling < MeshType , MontecarloSampler > : : Montecarlo ( m , mcSampler , std : : max ( 10000 , sampleNum * 40 ) ) ;
2013-03-13 01:16:51 +01:00
tri : : UpdateBounding < MeshType > : : Box ( MontecarloMesh ) ;
2013-07-23 09:32:12 +02:00
// tri::Build(MontecarloMesh, MontecarloSamples);
2012-07-02 18:31:52 +02:00
int t1 = clock ( ) ;
2014-05-15 12:35:08 +02:00
pp . pds . montecarloTime = t1 - t0 ;
2012-04-18 23:12:12 +02:00
2013-07-23 09:32:12 +02:00
if ( radiusVariance ! = 1 )
{
pp . adaptiveRadiusFlag = true ;
pp . radiusVariance = radiusVariance ;
}
2014-08-28 03:53:58 +02:00
if ( sampleNum = = 0 ) tri : : SurfaceSampling < MeshType , BaseSampler > : : PoissonDiskPruning ( pdSampler , MontecarloMesh , radius , pp ) ;
2014-08-28 11:28:14 +02:00
else tri : : SurfaceSampling < MeshType , BaseSampler > : : PoissonDiskPruningByNumber ( pdSampler , MontecarloMesh , sampleNum , radius , pp , PruningByNumberTolerance ) ;
2012-07-02 18:31:52 +02:00
int t2 = clock ( ) ;
2014-05-15 12:35:08 +02:00
pp . pds . totalTime = t2 - t0 ;
2012-04-18 23:12:12 +02:00
}
2014-04-17 11:51:48 +02:00
2014-05-15 12:35:08 +02:00
/// \brief Low level wrapper for Poisson Disk Pruning
2014-04-17 11:51:48 +02:00
///
2014-05-15 12:35:08 +02:00
/// This function simply takes a mesh and a radius and returns a vector of vertex pointers listing the "surviving" points.
2014-04-17 11:51:48 +02:00
//
template < class MeshType >
void PoissonPruning ( MeshType & m , // the mesh that has to be pruned
std : : vector < typename MeshType : : VertexPointer > & poissonSamples , // the vector that will contain the chosen set of points
2014-05-15 12:35:08 +02:00
float radius , unsigned int randSeed = 0 )
2013-06-24 12:51:53 +02:00
{
2014-04-17 11:51:48 +02:00
typedef tri : : TrivialPointerSampler < MeshType > BaseSampler ;
typename tri : : SurfaceSampling < MeshType , BaseSampler > : : PoissonDiskParam pp ;
2014-08-29 15:21:27 +02:00
pp . randomSeed = randSeed ;
2014-04-17 11:51:48 +02:00
tri : : UpdateBounding < MeshType > : : Box ( m ) ;
BaseSampler pdSampler ;
tri : : SurfaceSampling < MeshType , BaseSampler > : : PoissonDiskPruning ( pdSampler , m , radius , pp ) ;
2018-05-18 13:25:34 +02:00
poissonSamples = pdSampler . sampleVec ;
2014-04-17 11:51:48 +02:00
}
2014-05-15 12:35:08 +02:00
/// \brief Low level wrapper for Poisson Disk Pruning
///
2016-12-16 23:19:37 +01:00
/// This function simply takes a mesh containing a point cloud to be pruned and a radius
/// It returns a vector of CoordType listing the "surviving" points.
///
2014-05-15 12:35:08 +02:00
template < class MeshType >
void PoissonPruning ( MeshType & m , // the mesh that has to be pruned
2016-12-16 23:19:37 +01:00
std : : vector < typename MeshType : : CoordType > & poissonSamples , // the vector that will contain the chosen set of points
2014-05-15 12:35:08 +02:00
float radius , unsigned int randSeed = 0 )
{
std : : vector < typename MeshType : : VertexPointer > poissonSamplesVP ;
PoissonPruning ( m , poissonSamplesVP , radius , randSeed ) ;
poissonSamples . resize ( poissonSamplesVP . size ( ) ) ;
2016-12-16 23:19:37 +01:00
for ( size_t i = 0 ; i < poissonSamplesVP . size ( ) ; + + i )
2014-05-15 12:35:08 +02:00
poissonSamples [ i ] = poissonSamplesVP [ i ] - > P ( ) ;
}
2014-04-17 11:51:48 +02:00
/// \brief Very simple wrapping for the Exact Poisson Disk Pruning
///
/// This function simply takes a mesh and an expected number of points and returns
/// vector of points. It performs multiple attempts with varius radii to correctly get the expected number of samples.
/// It is obviously much slower than the other versions...
template < class MeshType >
2014-05-15 12:35:08 +02:00
void PoissonPruningExact ( MeshType & m , /// the mesh that has to be pruned
std : : vector < typename MeshType : : VertexPointer > & poissonSamples , /// the vector that will contain the chosen set of points
2014-07-01 08:20:23 +02:00
typename MeshType : : ScalarType & radius ,
2014-04-17 11:51:48 +02:00
int sampleNum ,
float tolerance = 0.04 ,
2014-08-29 15:21:27 +02:00
int maxIter = 20 ,
unsigned int randSeed = 0 )
2014-04-17 11:51:48 +02:00
{
2014-05-23 17:05:16 +02:00
size_t sampleNumMin = int ( float ( sampleNum ) * ( 1.0f - tolerance ) ) ; // the expected values range.
size_t sampleNumMax = int ( float ( sampleNum ) * ( 1.0f + tolerance ) ) ; // e.g. any sampling in [sampleNumMin, sampleNumMax] is OK
2014-05-15 12:35:08 +02:00
float RangeMinRad = m . bbox . Diag ( ) / 10.0f ;
float RangeMaxRad = m . bbox . Diag ( ) / 10.0f ;
2014-05-23 17:05:16 +02:00
size_t RangeMinSampleNum ;
size_t RangeMaxSampleNum ;
2014-05-15 12:35:08 +02:00
std : : vector < typename MeshType : : VertexPointer > poissonSamplesTmp ;
do
{
RangeMinRad / = 2.0f ;
2014-08-29 15:21:27 +02:00
PoissonPruning ( m , poissonSamplesTmp , RangeMinRad , randSeed ) ;
2014-05-23 17:05:16 +02:00
RangeMinSampleNum = poissonSamplesTmp . size ( ) ;
} while ( RangeMinSampleNum < sampleNumMin ) ;
2014-05-15 12:35:08 +02:00
do
{
RangeMaxRad * = 2.0f ;
2014-08-29 15:21:27 +02:00
PoissonPruning ( m , poissonSamplesTmp , RangeMaxRad , randSeed ) ;
2014-05-23 17:05:16 +02:00
RangeMaxSampleNum = poissonSamplesTmp . size ( ) ;
} while ( RangeMaxSampleNum > sampleNumMax ) ;
2014-05-15 12:35:08 +02:00
2014-04-17 11:51:48 +02:00
float curRadius ;
int iterCnt = 0 ;
while ( iterCnt < maxIter & &
( poissonSamplesTmp . size ( ) < sampleNumMin | | poissonSamplesTmp . size ( ) > sampleNumMax ) )
{
2014-05-15 12:35:08 +02:00
curRadius = ( RangeMaxRad + RangeMinRad ) / 2.0f ;
2014-08-29 15:21:27 +02:00
PoissonPruning ( m , poissonSamplesTmp , curRadius , randSeed ) ;
2014-05-23 17:05:16 +02:00
//qDebug("(%6.3f:%5i %6.3f:%5i) Cur Radius %f -> %i sample instead of %i",RangeMinRad,RangeMinSampleNum,RangeMaxRad,RangeMaxSampleNum,curRadius,poissonSamplesTmp.size(),sampleNum);
2016-02-11 07:09:31 +01:00
if ( poissonSamplesTmp . size ( ) > size_t ( sampleNum ) )
2014-05-15 12:35:08 +02:00
RangeMinRad = curRadius ;
2016-02-11 07:09:31 +01:00
if ( poissonSamplesTmp . size ( ) < size_t ( sampleNum ) )
2014-05-15 12:35:08 +02:00
RangeMaxRad = curRadius ;
2014-04-17 11:51:48 +02:00
}
swap ( poissonSamples , poissonSamplesTmp ) ;
radius = curRadius ;
2013-06-24 12:51:53 +02:00
}
2011-04-01 18:25:49 +02:00
} // end namespace tri
} // end namespace vcg
# endif
2013-03-01 09:34:33 +01:00