2008-05-27 23:38:37 +02:00
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright ( C ) 2004 \ / ) \ / *
* Visual Computing Lab / \ / | *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved . *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License ( http : //www.gnu.org/licenses/gpl.txt) *
* for more details . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/****************************************************************************
The sampling Class has a set of static functions , that you can call to sample the surface of a mesh .
Each function is templated on the mesh and on a Sampler object s .
Each function calls many time the sample object with the sampling point as parameter .
2009-01-12 17:11:37 +01:00
Sampler Classes and Sampling algorithms are independent .
2009-01-12 17:01:49 +01:00
Sampler classes exploits the sample that are generated with various algorithms .
2009-01-12 17:11:37 +01:00
For example , you can compute Hausdorff distance ( that is a sampler ) using various
sampling strategies ( montecarlo , stratified etc ) .
2009-01-12 17:01:49 +01:00
2008-05-27 23:38:37 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifndef __VCGLIB_POINT_SAMPLING
# define __VCGLIB_POINT_SAMPLING
2008-12-18 01:30:52 +01:00
# include <vcg/math/random_generator.h>
2009-01-09 18:05:10 +01:00
# include <vcg/complex/trimesh/closest.h>
# include <vcg/space/index/spatial_hashing.h>
2008-05-27 23:38:37 +02:00
# include <vcg/complex/trimesh/stat.h>
2008-07-20 16:34:26 +02:00
# include <vcg/complex/trimesh/update/topology.h>
# include <vcg/space/box2.h>
2008-05-27 23:38:37 +02:00
namespace vcg
{
namespace tri
{
2008-06-20 17:11:30 +02:00
/// Trivial Sampler, an example sampler object that show the required interface used by the sampling class.
2009-01-11 16:43:58 +01:00
/// Most of the sampling classes call the AddFace method with the face containing the sample and its barycentric coord.
2008-06-20 17:11:30 +02:00
template < class MeshType >
class TrivialSampler
{
public :
typedef typename MeshType : : CoordType CoordType ;
typedef typename MeshType : : VertexType VertexType ;
typedef typename MeshType : : FaceType FaceType ;
TrivialSampler ( ) { } ;
std : : vector < CoordType > sampleVec ;
void AddVert ( const VertexType & p )
{
sampleVec . push_back ( p . cP ( ) ) ;
}
2008-07-03 23:39:37 +02:00
void AddFace ( const FaceType & f , const CoordType & p )
2008-06-20 17:11:30 +02:00
{
sampleVec . push_back ( f . P ( 0 ) * p [ 0 ] + f . P ( 1 ) * p [ 1 ] + f . P ( 2 ) * p [ 2 ] ) ;
}
2008-06-21 01:33:24 +02:00
2008-07-03 23:39:37 +02:00
void AddTextureSample ( const FaceType & , const CoordType & , const Point2i & )
2008-06-21 01:33:24 +02:00
{
2009-01-11 16:43:58 +01:00
// Retrieve the color of the sample from the face f using the barycentric coord p
2008-06-21 01:33:24 +02:00
// and write that color in a texture image at position tp[0],tp[1]
}
2008-06-20 17:11:30 +02:00
} ; // end class TrivialSampler
2008-05-27 23:38:37 +02:00
template < class MetroMesh , class VertexSampler >
class SurfaceSampling
{
2009-01-11 16:43:58 +01:00
typedef typename MetroMesh : : CoordType CoordType ;
typedef typename MetroMesh : : ScalarType ScalarType ;
2008-05-27 23:38:37 +02:00
typedef typename MetroMesh : : VertexType VertexType ;
2009-01-11 16:43:58 +01:00
typedef typename MetroMesh : : VertexPointer VertexPointer ;
typedef typename MetroMesh : : VertexIterator VertexIterator ;
typedef typename MetroMesh : : FacePointer FacePointer ;
typedef typename MetroMesh : : FaceIterator FaceIterator ;
typedef typename MetroMesh : : FaceType FaceType ;
typedef typename MetroMesh : : FaceContainer FaceContainer ;
2009-01-16 12:30:19 +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 ;
2009-01-11 16:43:58 +01:00
public :
2009-01-09 15:35:46 +01:00
static math : : MarsenneTwisterRNG & SamplingRandomGenerator ( )
2008-12-18 01:30:52 +01:00
{
2009-01-18 20:33:50 +01:00
static math : : MarsenneTwisterRNG rnd ;
2008-12-18 01:30:52 +01:00
return rnd ;
}
2008-05-27 23:38:37 +02:00
2009-01-09 15:35:46 +01:00
// Returns an integer random number in the [0,i-1] interval using the improve Marsenne-Twister method.
static unsigned int RandomInt ( unsigned int i )
{
return ( SamplingRandomGenerator ( ) . generate ( 0 ) % i ) ;
}
// Returns a random number in the [0,1) real interval using the improved Marsenne-Twister method.
static double RandomDouble01 ( )
{
return SamplingRandomGenerator ( ) . generate01 ( ) ;
}
// Returns a random number in the [0,1] real interval using the improved Marsenne-Twister.
static double RandomDouble01closed ( )
{
return SamplingRandomGenerator ( ) . generate01closed ( ) ;
}
2008-12-18 01:30:52 +01:00
static void AllVertex ( MetroMesh & m , VertexSampler & ps )
2008-05-27 23:38:37 +02:00
{
2009-01-09 15:35:46 +01:00
VertexIterator vi ;
for ( vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
{
if ( ! ( * vi ) . IsD ( ) )
{
ps . AddVert ( * vi ) ;
}
}
2008-05-27 23:38:37 +02:00
}
2008-06-04 15:29:04 +02:00
2009-01-09 15:35:46 +01:00
/// Sample the vertices in a weighted way. Each vertex has a probability of being chosen
2008-06-04 15:29:04 +02:00
/// that is proportional to its quality.
/// It assumes that you are asking a number of vertices smaller than nv;
/// Algorithm:
/// 1) normalize quality so that sum q == 1;
/// 2) shuffle vertices.
/// 3) for each vertices choose it if rand > thr;
static void VertexWeighted ( MetroMesh & m , VertexSampler & ps , int sampleNum )
2008-05-27 23:38:37 +02:00
{
2008-06-04 15:29:04 +02: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 ;
2008-05-27 23:38:37 +02:00
std : : vector < VertexPointer > vertVec ;
2008-11-01 08:22:29 +01:00
FillAndShuffleVertexPointerVector ( m , vertVec ) ;
2008-06-04 15:29:04 +02:00
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 ;
}
}
2008-11-20 18:00:30 +01:00
/// Sample the vertices in a uniform way. Each vertex has a probability of being chosen
2008-06-04 15:29:04 +02:00
/// that is proportional to the area it represent.
static void VertexAreaUniform ( MetroMesh & m , VertexSampler & ps , int sampleNum )
{
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 ;
}
2008-05-27 23:38:37 +02:00
2008-06-04 15:29:04 +02:00
VertexWeighted ( m , ps , sampleNum ) ;
}
2008-11-01 08:22:29 +01:00
static void FillAndShuffleFacePointerVector ( MetroMesh & m , std : : vector < FacePointer > & faceVec )
{
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
if ( ! ( * fi ) . IsD ( ) ) faceVec . push_back ( & * fi ) ;
assert ( ( int ) faceVec . size ( ) = = m . fn ) ;
2008-12-18 01:30:52 +01:00
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( faceVec . begin ( ) , faceVec . end ( ) , p_myrandom ) ;
2008-11-01 08:22:29 +01:00
}
static void FillAndShuffleVertexPointerVector ( MetroMesh & m , std : : vector < VertexPointer > & vertVec )
2008-06-04 15:29:04 +02:00
{
2008-05-27 23:38:37 +02:00
VertexIterator vi ;
for ( vi = m . vert . begin ( ) ; vi ! = m . vert . end ( ) ; + + vi )
if ( ! ( * vi ) . IsD ( ) ) vertVec . push_back ( & * vi ) ;
2008-07-01 11:34:43 +02:00
assert ( ( int ) vertVec . size ( ) = = m . vn ) ;
2008-05-27 23:38:37 +02:00
2008-12-18 01:30:52 +01:00
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( vertVec . begin ( ) , vertVec . end ( ) , p_myrandom ) ;
2008-06-04 15:29:04 +02:00
}
2008-12-18 01:30:52 +01:00
2008-06-04 15:29:04 +02:00
/// Sample the vertices in a uniform way. Each vertex has the same probabiltiy of being chosen.
static void VertexUniform ( MetroMesh & m , VertexSampler & ps , int sampleNum )
{
2008-12-18 01:30:52 +01:00
if ( sampleNum > = m . vn ) {
2008-06-04 15:29:04 +02:00
AllVertex ( m , ps ) ;
return ;
}
std : : vector < VertexPointer > vertVec ;
2008-11-01 08:22:29 +01:00
FillAndShuffleVertexPointerVector ( m , vertVec ) ;
2008-05-27 23:38:37 +02:00
for ( int i = 0 ; i < sampleNum ; + + i )
ps . AddVert ( * vertVec [ i ] ) ;
}
2008-12-18 01:30:52 +01:00
static void FaceUniform ( MetroMesh & m , VertexSampler & ps , int sampleNum )
{
if ( sampleNum > = m . fn ) {
AllFace ( m , ps ) ;
return ;
}
std : : vector < FacePointer > faceVec ;
FillAndShuffleFacePointerVector ( m , faceVec ) ;
for ( int i = 0 ; i < sampleNum ; + + i )
ps . AddFace ( * faceVec [ i ] , Barycenter ( * faceVec [ i ] ) ) ;
}
2008-05-27 23:38:37 +02:00
static void AllFace ( MetroMesh & m , VertexSampler & ps )
{
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
if ( ! ( * fi ) . IsD ( ) )
{
ps . AddFace ( * fi , Barycenter ( * fi ) ) ;
}
}
static void AllEdge ( MetroMesh & m , VertexSampler & ps )
{
// Edge sampling.
typedef typename UpdateTopology < MetroMesh > : : PEdge SimpleEdge ;
std : : vector < SimpleEdge > Edges ;
2009-01-14 16:56:07 +01:00
typename std : : vector < SimpleEdge > : : iterator ei ;
UpdateTopology < MetroMesh > : : FillUniqueEdgeVector ( m , Edges ) ;
2008-05-27 23:38:37 +02:00
for ( ei = Edges . begin ( ) ; ei ! = Edges . end ( ) ; + + ei )
{
Point3f interp ( 0 , 0 , 0 ) ;
interp [ ( * ei ) . z ] = .5 ;
interp [ ( ( * ei ) . z + 1 ) % 3 ] = .5 ;
ps . AddFace ( * ( * ei ) . f , interp ) ;
}
}
2009-01-14 16:56:07 +01:00
2009-01-15 02:25:15 +01:00
// Regular Uniform Edge sampling
// Each edge is subdivided in a number of pieces proprtional to its lenght
// Sample are choosen without touching the vertices.
2009-01-14 16:56:07 +01:00
static void EdgeUniform ( MetroMesh & m , VertexSampler & ps , int sampleNum )
{
typedef typename UpdateTopology < MetroMesh > : : PEdge SimpleEdge ;
std : : vector < SimpleEdge > Edges ;
UpdateTopology < MetroMesh > : : FillUniqueEdgeVector ( m , Edges ) ;
// First loop compute total edge lenght;
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 ( ) ) ;
2009-01-20 00:18:10 +01:00
//qDebug("Edges %i edge sum %f",Edges.size(),edgeSum);
2009-01-14 16:56:07 +01:00
float sampleLen = edgeSum / sampleNum ;
2009-01-20 00:18:10 +01:00
//qDebug("EdgesSamples %i Sampling Len %f",sampleNum,sampleLen);
2009-01-14 16:56:07 +01:00
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 )
{
Point3f 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 ) ;
}
}
}
2009-01-09 15:35:46 +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.
2008-05-27 23:38:37 +02:00
static CoordType RandomBaricentric ( )
{
2009-01-09 15:35:46 +01:00
CoordType interp ;
interp [ 1 ] = RandomDouble01 ( ) ;
interp [ 2 ] = RandomDouble01 ( ) ;
if ( interp [ 1 ] + interp [ 2 ] > 1.0 )
{
interp [ 1 ] = 1.0 - interp [ 1 ] ;
interp [ 2 ] = 1.0 - interp [ 2 ] ;
}
assert ( interp [ 1 ] + interp [ 2 ] < = 1.0 ) ;
interp [ 0 ] = 1.0 - ( interp [ 1 ] + interp [ 2 ] ) ;
return interp ;
2008-05-27 23:38:37 +02:00
}
2008-11-01 08:22:29 +01:00
static void StratifiedMontecarlo ( MetroMesh & m , VertexSampler & ps , int sampleNum )
2008-05-27 23:38:37 +02:00
{
ScalarType area = Stat < MetroMesh > : : ComputeMeshArea ( m ) ;
ScalarType samplePerAreaUnit = sampleNum / area ;
2008-07-03 23:39:37 +02:00
//qDebug("samplePerAreaUnit %f",samplePerAreaUnit);
2008-05-27 23:38:37 +02:00
// Montecarlo sampling.
double floatSampleNum = 0.0 ;
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
if ( ! ( * fi ) . IsD ( ) )
2009-01-09 15:35:46 +01:00
{
2008-05-27 23:38:37 +02:00
// 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 , RandomBaricentric ( ) ) ;
floatSampleNum - = ( double ) faceSampleNum ;
2009-01-09 15:35:46 +01:00
}
2008-05-27 23:38:37 +02:00
}
2009-01-09 18:05:10 +01:00
2008-11-01 08:22:29 +01:00
static void Montecarlo ( MetroMesh & m , VertexSampler & ps , int sampleNum )
{
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 ) ) ;
2009-01-09 15:35:46 +01:00
// First loop: build a sequence of consecutive segments proportional to the triangle areas.
2008-11-01 08:22:29 +01:00
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
if ( ! ( * fi ) . IsD ( ) )
2009-01-09 15:35:46 +01:00
{
2008-11-01 08:22:29 +01:00
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 )
{
2009-01-09 15:35:46 +01:00
ScalarType val = meshArea * RandomDouble01 ( ) ;
2008-11-01 08:22:29 +01:00
// 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.
2008-11-20 14:26:26 +01:00
typename std : : vector < IntervalType > : : iterator it = lower_bound ( intervals . begin ( ) , intervals . end ( ) , std : : make_pair ( val , FacePointer ( 0 ) ) ) ;
2008-11-01 08:22:29 +01:00
assert ( it ! = intervals . end ( ) ) ;
assert ( it ! = intervals . begin ( ) ) ;
assert ( ( * ( it - 1 ) ) . first < val ) ;
assert ( ( * ( it ) ) . first > = val ) ;
ps . AddFace ( * ( * it ) . second , RandomBaricentric ( ) ) ;
}
}
2008-05-29 08:17:09 +02:00
static ScalarType WeightedArea ( FaceType f )
{
ScalarType averageQ = ( f . V ( 0 ) - > Q ( ) + f . V ( 1 ) - > Q ( ) + f . V ( 2 ) - > Q ( ) ) / 3.0 ;
return DoubleArea ( f ) * averageQ / 2.0 ;
}
/// Compute a sampling of the surface that is weighted by the quality
/// the area of each face is multiplied by the average of the quality of the vertices.
/// So the a face with a zero quality on all its vertices is never sampled and a face with average quality 2 get twice the samples of a face with the same area but with an average quality of 1;
static void WeightedMontecarlo ( MetroMesh & m , VertexSampler & ps , int sampleNum )
{
assert ( tri : : HasPerVertexQuality ( m ) ) ;
ScalarType weightedArea = 0 ;
FaceIterator fi ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; + + fi )
if ( ! ( * fi ) . IsD ( ) )
weightedArea + = WeightedArea ( * fi ) ;
ScalarType samplePerAreaUnit = sampleNum / weightedArea ;
2008-07-03 23:39:37 +02:00
//qDebug("samplePerAreaUnit %f",samplePerAreaUnit);
2008-05-29 08:17:09 +02:00
// Montecarlo sampling.
double floatSampleNum = 0.0 ;
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 + = WeightedArea ( * fi ) * samplePerAreaUnit ;
int faceSampleNum = ( int ) floatSampleNum ;
// for every sample p_i in T...
for ( int i = 0 ; i < faceSampleNum ; i + + )
ps . AddFace ( * fi , RandomBaricentric ( ) ) ;
floatSampleNum - = ( double ) faceSampleNum ;
}
}
2008-05-27 23:38:37 +02:00
2008-06-20 17:11:30 +02:00
// Subdivision sampling of a single face.
// return number of added samples
2008-05-27 23:38:37 +02:00
2008-11-01 08:22:29 +01:00
static int SingleFaceSubdivision ( int sampleNum , const CoordType & v0 , const CoordType & v1 , const CoordType & v2 , VertexSampler & ps , FacePointer fp , bool randSample )
2008-05-27 23:38:37 +02:00
{
// recursive face subdivision.
2008-11-01 08:22:29 +01:00
if ( sampleNum = = 1 )
2008-05-27 23:38:37 +02:00
{
// ground case.
2008-11-01 08:22:29 +01:00
CoordType SamplePoint ;
if ( randSample )
{
CoordType rb = RandomBaricentric ( ) ;
SamplePoint = v0 * rb [ 0 ] + v1 * rb [ 1 ] + v2 * rb [ 2 ] ;
}
else SamplePoint = ( ( v0 + v1 + v2 ) / 3.0f ) ;
CoordType SampleBary ;
InterpolationParameters ( * fp , SamplePoint , SampleBary [ 0 ] , SampleBary [ 1 ] , SampleBary [ 2 ] ) ;
ps . AddFace ( * fp , SampleBary ) ;
return 1 ;
2008-05-27 23:38:37 +02:00
}
2008-11-01 08:22:29 +01:00
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 ;
2008-05-27 23:38:37 +02:00
// compute the longest edge.
double maxd01 = SquaredDistance ( v0 , v1 ) ;
double maxd12 = SquaredDistance ( v1 , v2 ) ;
double 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 ;
2008-06-20 17:11:30 +02:00
int faceSampleNum = 0 ;
// break the input triangle along the midpoint of the longest edge.
CoordType pp ;
2008-05-27 23:38:37 +02:00
switch ( res )
{
2008-11-01 08:22:29 +01:00
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 ) ;
2008-05-27 23:38:37 +02:00
break ;
2008-11-01 08:22:29 +01:00
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 ) ;
2008-05-27 23:38:37 +02:00
break ;
2008-11-01 08:22:29 +01:00
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 ) ;
2008-05-27 23:38:37 +02:00
break ;
}
2008-06-20 17:11:30 +02:00
return faceSampleNum ;
2008-05-27 23:38:37 +02:00
}
2008-06-20 17:11:30 +02:00
/// Compute a sampling of the surface where the points are regularly scattered over the face surface using a recursive longest-edge subdivision rule.
2008-11-01 08:22:29 +01:00
static void FaceSubdivision ( MetroMesh & m , VertexSampler & ps , int sampleNum , bool randSample )
2008-05-27 23:38:37 +02:00
{
2008-06-20 17:11:30 +02:00
ScalarType area = Stat < MetroMesh > : : ComputeMeshArea ( m ) ;
ScalarType samplePerAreaUnit = sampleNum / area ;
2008-07-03 23:39:37 +02:00
//qDebug("samplePerAreaUnit %f",samplePerAreaUnit);
2008-11-01 08:22:29 +01:00
std : : vector < FacePointer > faceVec ;
FillAndShuffleFacePointerVector ( m , faceVec ) ;
2008-05-27 23:38:37 +02:00
2008-06-20 17:11:30 +02:00
double floatSampleNum = 0.0 ;
int faceSampleNum ;
// Subdivision sampling.
2008-11-01 08:22:29 +01:00
typename std : : vector < FacePointer > : : iterator fi ;
for ( fi = faceVec . begin ( ) ; fi ! = faceVec . end ( ) ; fi + + )
2008-05-27 23:38:37 +02:00
{
// compute # samples in the current face.
2008-11-01 08:22:29 +01:00
floatSampleNum + = 0.5 * DoubleArea ( * * fi ) * samplePerAreaUnit ;
2008-06-20 17:11:30 +02:00
faceSampleNum = ( int ) floatSampleNum ;
if ( faceSampleNum > 0 )
2008-11-01 08:22:29 +01:00
faceSampleNum = SingleFaceSubdivision ( faceSampleNum , ( * * fi ) . V ( 0 ) - > cP ( ) , ( * * fi ) . V ( 1 ) - > cP ( ) , ( * * fi ) . V ( 2 ) - > cP ( ) , ps , * fi , randSample ) ;
2008-06-20 17:11:30 +02:00
floatSampleNum - = ( double ) faceSampleNum ;
2008-05-27 23:38:37 +02:00
}
}
// Similar Triangles sampling.
2008-06-20 17:11:30 +02:00
// Skip vertex and edges
// Sample per edges includes vertexes, so here we should expect n_samples_per_edge >=4
static int SingleFaceSimilar ( FacePointer fp , VertexSampler & ps , int n_samples_per_edge )
2008-05-27 23:38:37 +02:00
{
2008-06-20 17:11:30 +02:00
int n_samples = 0 ;
2008-05-27 23:38:37 +02:00
int i , j ;
2008-06-20 17:11:30 +02:00
float segmentNum = n_samples_per_edge - 1 ;
float segmentLen = 1.0 / segmentNum ;
// face sampling.
2008-05-27 23:38:37 +02:00
for ( i = 1 ; i < n_samples_per_edge - 1 ; i + + )
for ( j = 1 ; j < n_samples_per_edge - 1 - i ; j + + )
{
2008-06-20 17:11:30 +02:00
//AddSample( v0 + (V1*(double)i + V2*(double)j) );
CoordType sampleBary ( i * segmentLen , j * segmentLen , 1.0 - ( i * segmentLen + j * segmentLen ) ) ;
2008-05-27 23:38:37 +02:00
n_samples + + ;
2008-06-20 17:11:30 +02:00
ps . AddFace ( * fp , sampleBary ) ;
2008-05-27 23:38:37 +02:00
}
2008-06-20 17:11:30 +02:00
return n_samples ;
2008-05-27 23:38:37 +02:00
}
2009-03-19 23:04:52 +01:00
static int SingleFaceSimilarDual ( FacePointer fp , VertexSampler & ps , int n_samples_per_edge , bool randomFlag )
{
int n_samples = 0 ;
float i , j ;
float segmentNum = n_samples_per_edge - 1 ;
float segmentLen = 1.0 / segmentNum ;
// face sampling.
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) );
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 = RandomBaricentric ( ) ;
ps . AddFace ( * fp , V0 * rb [ 0 ] + V1 * rb [ 1 ] + V2 * rb [ 2 ] ) ;
} else ps . AddFace ( * fp , ( V0 + V1 + V2 ) / 3.0 ) ;
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 = RandomBaricentric ( ) ;
ps . AddFace ( * fp , V3 * rb [ 0 ] + V1 * rb [ 1 ] + V2 * rb [ 2 ] ) ;
} else ps . AddFace ( * fp , ( V3 + V1 + V2 ) / 3.0 ) ;
}
}
return n_samples ;
}
2008-05-27 23:38:37 +02:00
2009-03-19 23:04:52 +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.
// 1) you have already sampled both edges and vertices
// 2) you are not going to take samples on edges and vertices.
//
// 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)
// E.g. for k=4 you get 3 segments on each edges and the original triangle is subdivided
// into 9 smaller triangles and you get (1*2)/2 == 1 only a single internal point.
// So if you want N samples in a triangle you have to solve k^2 -5k +6 - 2N = 0
// from which you get:
//
2008-06-20 17:11:30 +02:00
// 5 + sqrt( 1 + 8N )
// k = -------------------
// 2
2009-03-19 23:04:52 +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.
// So if you want N samples in a triangle, the number <k> of points on an edge (vertex included) should be simply:
// k = 1 + sqrt(N)
// examples:
// N = 4 -> k = 3
// N = 9 -> k = 4
2008-06-20 17:11:30 +02:00
//template <class MetroMesh>
//void Sampling<MetroMesh>::SimilarFaceSampling()
2009-03-19 23:04:52 +01:00
static void FaceSimilar ( MetroMesh & m , VertexSampler & ps , int sampleNum , bool dualFlag , bool randomFlag )
{
2008-06-20 17:11:30 +02:00
ScalarType area = Stat < MetroMesh > : : ComputeMeshArea ( m ) ;
ScalarType samplePerAreaUnit = sampleNum / area ;
// Similar Triangles sampling.
2008-07-01 11:34:43 +02:00
int n_samples_per_edge ;
2008-05-27 23:38:37 +02:00
double n_samples_decimal = 0.0 ;
FaceIterator fi ;
2008-06-20 17:11:30 +02:00
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
2008-05-27 23:38:37 +02:00
{
// compute # samples in the current face.
2008-06-20 17:11:30 +02:00
n_samples_decimal + = 0.5 * DoubleArea ( * fi ) * samplePerAreaUnit ;
int n_samples = ( int ) n_samples_decimal ;
2009-03-19 23:04:52 +01:00
if ( n_samples > 0 )
2008-05-27 23:38:37 +02:00
{
// face sampling.
2009-03-19 23:04:52 +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 ( n_samples ) + 1.0 ) ;
n_samples = SingleFaceSimilarDual ( & * fi , ps , n_samples_per_edge , randomFlag ) ;
}
2008-05-27 23:38:37 +02:00
}
n_samples_decimal - = ( double ) n_samples ;
}
}
2008-06-20 17:11:30 +02: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 ) { ??? } };
2008-06-21 01:33:24 +02:00
static void SingleFaceRaster ( FaceType & f , VertexSampler & ps , const Point2 < ScalarType > & v0 , const Point2 < ScalarType > & v1 , const Point2 < ScalarType > & v2 )
2008-06-20 17:11:30 +02:00
{
2008-06-21 01:33:24 +02:00
typedef ScalarType S ;
2008-06-20 17:11:30 +02:00
// Calcolo bounding box
Box2i bbox ;
2008-12-09 12:36:34 +01:00
if ( v0 [ 0 ] < v1 [ 0 ] ) { bbox . min [ 0 ] = int ( v0 [ 0 ] ) ; bbox . max [ 0 ] = int ( v1 [ 0 ] ) ; }
else { bbox . min [ 0 ] = int ( v1 [ 0 ] ) ; bbox . max [ 0 ] = int ( v0 [ 0 ] ) ; }
if ( v0 [ 1 ] < v1 [ 1 ] ) { bbox . min [ 1 ] = int ( v0 [ 1 ] ) ; bbox . max [ 1 ] = int ( v1 [ 1 ] ) ; }
else { bbox . min [ 1 ] = int ( v1 [ 1 ] ) ; bbox . max [ 1 ] = int ( v0 [ 1 ] ) ; }
if ( bbox . min [ 0 ] > int ( v2 [ 0 ] ) ) bbox . min [ 0 ] = int ( v2 [ 0 ] ) ;
else if ( bbox . max [ 0 ] < int ( v2 [ 0 ] ) ) bbox . max [ 0 ] = int ( v2 [ 0 ] ) ;
if ( bbox . min [ 1 ] > int ( v2 [ 1 ] ) ) bbox . min [ 1 ] = int ( v2 [ 1 ] ) ;
else if ( bbox . max [ 1 ] < int ( v2 [ 1 ] ) ) bbox . max [ 1 ] = int ( v2 [ 1 ] ) ;
2008-06-20 17:11:30 +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 ] ;
// 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 ] ; x < = bbox . max [ 0 ] ; + + x )
{
bool in = false ;
S n0 = b0 ;
S n1 = b1 ;
S n2 = b2 ;
for ( int y = bbox . min [ 1 ] ; y < = bbox . max [ 1 ] ; + + y )
{
if ( ( n0 > = 0 & & n1 > = 0 & & n2 > = 0 ) | | ( n0 < = 0 & & n1 < = 0 & & n2 < = 0 ) )
{
2008-06-21 01:33:24 +02:00
CoordType baryCoord ;
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 ] ;
2008-06-20 17:11:30 +02:00
2008-06-21 01:33:24 +02:00
ps . AddTextureSample ( f , baryCoord , Point2i ( x , y ) ) ;
2008-06-20 17:11:30 +02:00
in = true ;
} else if ( in ) break ;
n0 + = dn0 ;
n1 + = dn1 ;
n2 + = dn2 ;
}
b0 + = db0 ;
b1 + = db1 ;
b2 + = db2 ;
}
2008-05-27 23:38:37 +02:00
}
2008-06-20 17:11:30 +02:00
2009-01-15 18:03:08 +01:00
// Generate a random point in volume defined by a box with uniform distribution
static CoordType RandomBox ( vcg : : Box3 < ScalarType > box )
{
CoordType p = box . min ;
p [ 0 ] + = box . Dim ( ) [ 0 ] * RandomDouble01 ( ) ;
p [ 1 ] + = box . Dim ( ) [ 1 ] * RandomDouble01 ( ) ;
p [ 2 ] + = box . Dim ( ) [ 2 ] * RandomDouble01 ( ) ;
return p ;
}
2009-01-17 18:37:06 +01:00
// generate Poisson-disk sample using a set of pre-generated samples (with the Montecarlo algorithm)
2009-01-20 12:06:12 +01:00
// It always return a point.
2009-01-28 00:54:47 +01:00
static VertexPointer getPrecomputedMontecarloSample ( Point3i * cell , MontecarloSHT & samplepool )
2009-01-15 16:32:10 +01:00
{
2009-01-16 12:30:19 +01:00
MontecarloSHTIterator cellBegin ;
MontecarloSHTIterator cellEnd ;
samplepool . Grid ( * cell , cellBegin , cellEnd ) ;
2009-01-28 00:54:47 +01:00
return * cellBegin ;
2009-01-15 16:32:10 +01:00
}
2009-01-14 18:12:12 +01:00
// check the radius constrain
2009-01-28 00:54:47 +01:00
static bool checkPoissonDisk ( MetroMesh & vmesh , SampleSHT & sht , const Point3 < ScalarType > & p , ScalarType radius )
2009-01-14 18:12:12 +01:00
{
// get the samples closest to the given one
2009-01-17 18:37:06 +01:00
std : : vector < VertexType * > closests ;
std : : vector < ScalarType > distances ;
std : : vector < CoordType > points ;
2009-01-20 00:18:10 +01:00
typedef VertTmark < MetroMesh > MarkerVert ;
MarkerVert mv ;
mv . SetMesh ( & vmesh ) ;
typedef vcg : : vertex : : PointDistanceFunctor < ScalarType > VDistFunct ;
VDistFunct fn ;
Box3f bb ( p - Point3f ( radius , radius , radius ) , p + Point3f ( radius , radius , radius ) ) ;
int nsamples = GridGetInBox ( sht , mv , bb , closests ) ;
ScalarType r2 = radius * radius ;
2009-01-21 16:40:16 +01:00
for ( int i = 0 ; i < closests . size ( ) ; + + i )
if ( SquaredDistance ( p , closests [ i ] - > cP ( ) ) < r2 )
return false ;
return true ;
2009-01-14 18:12:12 +01:00
}
2009-01-28 00:54:47 +01:00
struct PoissonDiskParam
{
PoissonDiskParam ( )
{
adaptiveRadiusFlag = false ;
2009-01-29 01:16:00 +01:00
radiusVariance = 1 ;
2009-01-28 00:54:47 +01:00
MAXLEVELS = 5 ;
2009-03-25 09:22:03 +01:00
invertQuality = false ;
2009-01-28 00:54:47 +01:00
}
bool adaptiveRadiusFlag ;
2009-01-29 01:16:00 +01:00
float radiusVariance ;
2009-03-25 09:22:03 +01:00
bool invertQuality ;
2009-01-28 00:54:47 +01:00
int MAXLEVELS ;
} ;
2009-01-29 01:16:00 +01:00
static ScalarType ComputePoissonDiskRadius ( MetroMesh & origMesh , int sampleNum )
{
ScalarType meshArea = Stat < MetroMesh > : : ComputeMeshArea ( origMesh ) ;
2009-03-27 16:09:14 +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
2009-01-29 01:16:00 +01:00
return diskRadius ;
}
static int ComputePoissonSampleNum ( MetroMesh & origMesh , ScalarType diskRadius )
{
ScalarType meshArea = Stat < MetroMesh > : : ComputeMeshArea ( origMesh ) ;
int sampleNum = meshArea / ( diskRadius * diskRadius * M_PI * 0.7 ) ; // 0.7 is a density factor
return sampleNum ;
}
2009-03-25 09:22:03 +01:00
static void ComputePoissonSampleRadii ( MetroMesh & sampleMesh , ScalarType diskRadius , ScalarType radiusVariance , bool invert )
2009-01-29 01:16:00 +01:00
{
VertexIterator vi ;
std : : pair < float , float > minmax = tri : : Stat < MetroMesh > : : ComputePerVertexQualityMinMax ( sampleMesh ) ;
float minRad = diskRadius / radiusVariance ;
float maxRad = diskRadius * radiusVariance ;
float deltaQ = minmax . second - minmax . first ;
float deltaRad = maxRad - minRad ;
for ( vi = sampleMesh . vert . begin ( ) ; vi ! = sampleMesh . vert . end ( ) ; vi + + )
{
2009-03-25 09:22:03 +01:00
( * vi ) . Q ( ) = minRad + deltaRad * ( ( invert ? minmax . second - ( * vi ) . Q ( ) : ( * vi ) . Q ( ) - minmax . first ) / deltaQ ) ;
2009-01-29 01:16:00 +01:00
}
}
2009-01-28 00:54:47 +01:00
2009-01-11 16:43:58 +01: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 . :
*
* " Poisson Disk Point Set by Hierarchical Dart Throwing "
* K . B . White , D . Cline , P . K . Egbert ,
* IEEE Symposium on Interactive Ray Tracing , 2007 ,
* 10 - 12 Sept . 2007 , pp . 129 - 132.
*/
2009-01-29 01:16:00 +01:00
static void Poissondisk ( MetroMesh & origMesh , VertexSampler & ps , MetroMesh & montecarloMesh , ScalarType diskRadius , const struct PoissonDiskParam pp = PoissonDiskParam ( ) )
2009-01-11 16:43:58 +01:00
{
2009-01-16 16:01:46 +01:00
int cellusedcounter [ 20 ] ; // cells used for each level
int cellstosubdividecounter [ 20 ] ; // cells to subdivide for each level
int samplesgenerated [ 20 ] ; // samples generated for each level
int samplesaccepted [ 20 ] ; // samples accepted for each level
int verticescounter [ 20 ] ; // vertices added to the spatial hash table
2009-01-14 18:12:12 +01:00
2009-01-16 16:01:46 +01:00
for ( int i = 0 ; i < 20 ; i + + )
{
cellusedcounter [ i ] = 0 ;
cellstosubdividecounter [ i ] = 0 ;
samplesgenerated [ i ] = 0 ;
samplesaccepted [ i ] = 0 ;
verticescounter [ i ] = 0 ;
}
2009-01-18 20:33:50 +01:00
MetroMesh supportMesh ;
2009-01-16 12:30:19 +01:00
// spatial index of montecarlo samples - used to choose a new sample to insert
MontecarloSHT montecarloSHT ;
2009-01-14 18:12:12 +01:00
// spatial hash table of the generated samples - used to check the radius constrain
2009-01-16 12:30:19 +01:00
SampleSHT checkSHT ;
2009-01-11 16:43:58 +01:00
2009-01-14 18:12:12 +01:00
// initialize spatial hash table for searching
2009-01-28 00:54:47 +01:00
ScalarType cellsize = diskRadius / sqrt ( 3.0 ) ;
2008-11-17 13:52:17 +01:00
2009-01-21 16:40:16 +01:00
// inflating
2009-01-28 00:54:47 +01:00
origMesh . bbox . Offset ( cellsize ) ;
2009-01-21 16:40:16 +01:00
2009-01-29 11:49:34 +01:00
int sizeX = vcg : : math : : Max ( 1.0f , origMesh . bbox . DimX ( ) / cellsize ) ;
int sizeY = vcg : : math : : Max ( 1.0f , origMesh . bbox . DimY ( ) / cellsize ) ;
int sizeZ = vcg : : math : : Max ( 1.0f , origMesh . bbox . DimZ ( ) / cellsize ) ;
2009-01-14 18:12:12 +01:00
Point3i gridsize ( sizeX , sizeY , sizeZ ) ;
2009-03-19 23:04:52 +01:00
# ifdef QT_VERSION
2009-01-28 00:54:47 +01:00
qDebug ( " PDS: radius %f Grid:(%i %i %i) " , diskRadius , sizeX , sizeY , sizeZ ) ;
2009-01-29 12:12:57 +01:00
# endif
2009-01-16 12:30:19 +01:00
// initialize spatial hash to index pre-generated samples
VertexIterator vi ;
2009-01-16 16:01:46 +01:00
montecarloSHT . InitEmpty ( origMesh . bbox , gridsize ) ;
for ( vi = montecarloMesh . vert . begin ( ) ; vi ! = montecarloMesh . vert . end ( ) ; vi + + )
{
2009-01-16 12:30:19 +01:00
montecarloSHT . Add ( & ( * vi ) ) ;
2009-01-16 16:01:46 +01:00
verticescounter [ 0 ] + + ;
}
2009-03-19 23:04:52 +01:00
# ifdef QT_VERSION
2009-01-20 00:18:10 +01:00
qDebug ( " PDS: Completed montercarloSHT, inserted %i vertex in %i cells " , montecarloMesh . vn , montecarloSHT . AllocatedCells . size ( ) ) ;
2009-01-29 12:12:57 +01:00
# endif
2009-01-16 12:30:19 +01:00
// initialize spatial hash table for check poisson-disk radius constrain
2009-01-16 16:01:46 +01:00
checkSHT . InitEmpty ( origMesh . bbox , gridsize ) ;
2009-01-11 16:43:58 +01:00
2009-01-17 18:37:06 +01:00
2009-01-16 12:30:19 +01:00
// sampling algorithm
// ------------------
2009-01-11 16:43:58 +01:00
//
2009-01-16 12:30:19 +01:00
// - generate millions of samples using montecarlo algorithm
2009-01-12 17:01:49 +01:00
// - extract a cell (C) from the active cell list (with probability proportional to cell's volume)
2009-01-21 18:39:40 +01:00
// - generate a sample inside C by choosing one of the contained pre-generated samples
2009-01-14 18:12:12 +01:00
// - if the sample violates the radius constrain discard it, and add the cell to the cells-to-subdivide list
2009-01-11 16:43:58 +01:00
// - iterate until the active cell list is empty or a pre-defined number of subdivisions is reached
//
2009-01-12 15:22:03 +01:00
2009-01-14 18:12:12 +01:00
std : : vector < Point3i * > activeCells ;
2009-01-21 18:39:40 +01:00
std : : vector < VertexType * > nextPoints ;
typename std : : vector < VertexType * > : : iterator nextPointsIt ;
2009-01-16 12:30:19 +01:00
2009-01-15 09:04:12 +01:00
typename std : : vector < Point3i > : : iterator it ;
2009-01-15 16:32:10 +01:00
Point3i * currentCell ;
vcg : : Box3 < ScalarType > currentBox ;
2009-01-14 18:12:12 +01:00
int level = 0 ;
2009-01-29 01:16:00 +01:00
// if we are doing variable density sampling we have to prepare the random samples quality with the correct expected radii.
if ( pp . adaptiveRadiusFlag )
2009-03-25 09:22:03 +01:00
ComputePoissonSampleRadii ( montecarloMesh , diskRadius , pp . radiusVariance , pp . invertQuality ) ;
2009-01-29 01:16:00 +01:00
2009-01-12 15:22:03 +01:00
do
{
2009-01-14 18:12:12 +01:00
// extract a cell (C) from the active cell list (with probability proportional to cell's volume)
///////////////////////////////////////////////////////////////////////////////////////////////////
2009-01-12 15:22:03 +01:00
2009-01-21 16:40:16 +01:00
supportMesh . vert . reserve ( montecarloMesh . vn ) ;
2009-01-14 18:12:12 +01:00
// create active cell list
2009-01-16 12:30:19 +01:00
for ( it = montecarloSHT . AllocatedCells . begin ( ) ; it ! = montecarloSHT . AllocatedCells . end ( ) ; it + + )
2009-01-12 15:22:03 +01:00
{
2009-01-14 18:12:12 +01:00
activeCells . push_back ( & ( * it ) ) ;
}
2009-01-12 15:22:03 +01:00
2009-01-14 18:12:12 +01:00
int ncell = static_cast < int > ( activeCells . size ( ) ) ;
2009-01-16 16:01:46 +01:00
cellusedcounter [ level ] = ncell ;
2009-01-28 00:54:47 +01:00
// shuffle active cells
// int index,index2;
// Point3i *temp;
// for (int i = 0; i < ncell/2; i++)
// {
// index = RandomInt(ncell);
// index2 = RandomInt(ncell);
// temp = activeCells[index];
// activeCells[index] = activeCells[index2];
// activeCells[index2] = temp;
// }
unsigned int ( * p_myrandom ) ( unsigned int ) = RandomInt ;
std : : random_shuffle ( activeCells . begin ( ) , activeCells . end ( ) , p_myrandom ) ;
2009-01-12 15:22:03 +01:00
2009-01-17 18:37:06 +01:00
// generate a sample inside C by choosing one of the contained pre-generated samples
2009-01-14 18:12:12 +01:00
//////////////////////////////////////////////////////////////////////////////////////////
2009-01-12 15:22:03 +01:00
2009-01-14 18:12:12 +01:00
for ( int i = 0 ; i < ncell ; i + + )
{
2009-01-15 16:32:10 +01:00
currentCell = activeCells [ i ] ;
2009-01-28 00:54:47 +01:00
//vcg::Point3<ScalarType > s; // current sample
2009-01-15 16:32:10 +01:00
2009-01-16 12:30:19 +01:00
// generate a sample chosen from the pre-generated one
2009-01-28 00:54:47 +01:00
VertexPointer sp = getPrecomputedMontecarloSample ( currentCell , montecarloSHT ) ;
2009-01-16 16:01:46 +01:00
samplesgenerated [ level ] + + ;
2009-01-28 00:54:47 +01:00
2009-01-21 18:39:40 +01:00
// vr spans between 3.0*r and r / 4.0 according to vertex quality
2009-01-28 00:54:47 +01:00
ScalarType sampleRadius = diskRadius ;
2009-01-29 01:16:00 +01:00
if ( pp . adaptiveRadiusFlag ) sampleRadius = sp - > Q ( ) ;
2009-01-21 18:39:40 +01:00
2009-01-28 00:54:47 +01:00
if ( checkPoissonDisk ( * ps . m , checkSHT , sp - > cP ( ) , sampleRadius ) )
2009-01-15 16:32:10 +01:00
{
// add sample
2009-01-18 20:33:50 +01:00
tri : : Allocator < MetroMesh > : : AddVertices ( supportMesh , 1 ) ;
2009-01-28 00:54:47 +01:00
supportMesh . vert . back ( ) . P ( ) = sp - > P ( ) ;
supportMesh . vert . back ( ) . Q ( ) = sampleRadius ;
2009-03-26 11:51:44 +01:00
//ps.AddVert(supportMesh.vert.back()); Small change, we should call the sampler class with the input mesh.
ps . AddVert ( * sp ) ;
2009-01-15 16:32:10 +01:00
// add to control spatial index
2009-01-18 20:33:50 +01:00
checkSHT . Add ( & supportMesh . vert . back ( ) ) ;
2009-01-16 16:01:46 +01:00
samplesaccepted [ level ] + + ;
2009-01-15 16:32:10 +01:00
}
else
{
2009-01-16 12:30:19 +01:00
// subdivide this cell
2009-01-16 16:01:46 +01:00
///////////////////////////////////////////////////////////////////////
// pre-generated samples for the next level of subdivision
2009-01-28 00:54:47 +01:00
MontecarloSHTIterator ptBegin , ptEnd , ptIt ;
2009-01-16 16:01:46 +01:00
montecarloSHT . Grid ( * currentCell , ptBegin , ptEnd ) ;
2009-01-16 17:14:16 +01:00
for ( ptIt = ptBegin ; ptIt ! = ptEnd ; + + ptIt )
2009-01-16 16:01:46 +01:00
{
2009-01-21 18:39:40 +01:00
nextPoints . push_back ( * ptIt ) ;
2009-01-16 16:01:46 +01:00
}
cellstosubdividecounter [ level ] + + ;
2009-01-15 16:32:10 +01:00
}
2009-01-14 18:12:12 +01:00
}
2009-01-12 15:22:03 +01:00
2009-01-14 18:12:12 +01:00
activeCells . clear ( ) ;
2009-01-12 15:22:03 +01:00
2009-01-14 18:12:12 +01:00
// proceed to the next level of subdivision
2009-01-16 12:30:19 +01:00
///////////////////////////////////////////////////////////////////////////
// cleaning spatial index data structures
montecarloSHT . Clear ( ) ;
// increase grid resolution
2009-01-14 18:12:12 +01:00
gridsize [ 0 ] * = 2 ;
gridsize [ 1 ] * = 2 ;
gridsize [ 2 ] * = 2 ;
2009-01-15 16:32:10 +01:00
2009-01-17 18:37:06 +01:00
montecarloSHT . InitEmpty ( origMesh . bbox , gridsize ) ;
2009-01-14 18:12:12 +01:00
2009-01-16 16:01:46 +01:00
for ( nextPointsIt = nextPoints . begin ( ) ; nextPointsIt ! = nextPoints . end ( ) ; nextPointsIt + + )
2009-01-16 12:30:19 +01:00
{
2009-01-16 16:01:46 +01:00
montecarloSHT . Add ( * nextPointsIt ) ;
verticescounter [ level + 1 ] + + ;
2009-01-16 12:30:19 +01:00
}
2009-01-16 16:01:46 +01:00
nextPoints . clear ( ) ;
2009-03-19 23:04:52 +01:00
# ifdef QT_VERSION
2009-01-20 00:18:10 +01:00
qDebug ( " PDS: Completed Level %i, added %i samples " , level , samplesaccepted [ level ] ) ;
2009-01-29 12:12:57 +01:00
# endif
2009-01-14 18:12:12 +01:00
level + + ;
2009-01-12 15:22:03 +01:00
2009-01-29 01:16:00 +01:00
} while ( level < pp . MAXLEVELS ) ;
2009-01-16 16:01:46 +01:00
2009-03-19 23:04:52 +01:00
# ifdef DEBUG_DUMP_STAT
2009-01-16 16:01:46 +01:00
// write some statistics
2009-01-17 18:37:06 +01:00
QFile outfile ( " C:/temp/poissondisk_statistics.txt " ) ;
if ( outfile . open ( QFile : : WriteOnly | QFile : : Truncate ) )
2009-01-16 16:01:46 +01:00
{
2009-01-18 20:33:50 +01:00
QTextStream out ( & outfile ) ;
2009-01-16 16:01:46 +01:00
2009-01-29 01:16:00 +01:00
for ( int k = 0 ; k < pp . MAXLEVELS ; k + + )
2009-01-16 16:01:46 +01:00
out < < " Cells used for level " < < k < < " : " < < cellusedcounter [ k ] < < endl ;
2009-01-29 01:16:00 +01:00
for ( int k = 0 ; k < pp . MAXLEVELS ; k + + )
2009-01-16 16:01:46 +01:00
out < < " Cells to subdivide for level " < < k < < " : " < < cellstosubdividecounter [ k ] < < endl ;
2009-01-29 01:16:00 +01:00
for ( int k = 0 ; k < pp . MAXLEVELS ; k + + )
2009-01-16 16:01:46 +01:00
out < < " Vertices counter for level " < < k < < " : " < < verticescounter [ k ] < < endl ;
2009-01-29 01:16:00 +01:00
for ( int k = 0 ; k < pp . MAXLEVELS ; k + + )
2009-01-16 16:01:46 +01:00
out < < " Samples generated for level " < < k < < " : " < < samplesgenerated [ k ] < < endl ;
2009-01-29 01:16:00 +01:00
for ( int k = 0 ; k < pp . MAXLEVELS ; k + + )
2009-01-16 16:01:46 +01:00
out < < " Samples accepted for level " < < k < < " : " < < samplesaccepted [ k ] < < endl ;
}
2009-01-11 16:43:58 +01:00
2009-01-17 18:37:06 +01:00
outfile . close ( ) ;
2009-01-29 12:12:57 +01:00
# endif
2008-11-17 13:52:17 +01:00
}
2008-06-20 17:11:30 +02:00
2008-06-21 01:33:24 +02:00
//template <class MetroMesh>
//void Sampling<MetroMesh>::SimilarFaceSampling()
2008-11-14 16:12:02 +01:00
static void Texture ( MetroMesh & m , VertexSampler & ps , int textureWidth , int textureHeight )
2008-06-21 01:33:24 +02:00
{
2008-11-17 13:52:17 +01:00
FaceIterator fi ;
2008-06-21 01:33:24 +02:00
2008-11-17 13:52:17 +01:00
printf ( " Similar Triangles face sampling \n " ) ;
for ( fi = m . face . begin ( ) ; fi ! = m . face . end ( ) ; fi + + )
2008-06-21 01:33:24 +02:00
{
Point2f ti [ 3 ] ;
for ( int i = 0 ; i < 3 ; + + i )
2008-11-14 16:12:02 +01:00
ti [ i ] = Point2f ( ( * fi ) . WT ( i ) . U ( ) * textureWidth , ( * fi ) . WT ( i ) . V ( ) * textureHeight ) ;
2008-06-21 01:33:24 +02:00
SingleFaceRaster ( * fi , ps , ti [ 0 ] , ti [ 1 ] , ti [ 2 ] ) ;
}
}
2009-04-22 12:27:44 +02:00
typedef GridStaticPtr < FaceType , ScalarType > TriMeshGrid ;
2008-06-21 01:33:24 +02:00
2009-04-22 12:27:44 +02:00
class RRParam
{
public :
float offset ;
float minDiag ;
tri : : FaceTmark < MetroMesh > markerFunctor ;
TriMeshGrid gM ;
} ;
static void RegularRecursiveOffset ( MetroMesh & m , std : : vector < Point3f > & pvec , ScalarType offset , float minDiag )
{
Box3 < ScalarType > bb = m . bbox ;
bb . Offset ( offset * 2.0 ) ;
RRParam rrp ;
rrp . markerFunctor . SetMesh ( & m ) ;
rrp . gM . Set ( m . face . begin ( ) , m . face . end ( ) , bb ) ;
rrp . offset = offset ;
rrp . minDiag = minDiag ;
SubdivideAndSample ( m , pvec , bb , rrp , bb . Diag ( ) ) ;
}
2008-06-21 01:33:24 +02:00
2009-04-22 12:27:44 +02:00
static void SubdivideAndSample ( MetroMesh & m , std : : vector < Point3f > & pvec , const Box3 < ScalarType > bb , RRParam & rrp , float curDiag )
{
Point3f startPt = bb . Center ( ) ;
ScalarType bound ;
ScalarType dist ;
// Compute mesh point nearest to bb center
FaceType * nearestF = 0 ;
float dist_upper_bound = curDiag + rrp . offset ;
Point3f closestPt ;
vcg : : face : : PointDistanceBaseFunctor < ScalarType > PDistFunct ;
dist = dist_upper_bound ;
nearestF = rrp . gM . GetClosest ( PDistFunct , rrp . markerFunctor , startPt , dist_upper_bound , dist , closestPt ) ;
curDiag / = 2 ;
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.
{
Point3f delta = startPt - closestPt ;
pvec . push_back ( closestPt + delta * ( rrp . offset / dist ) ) ;
}
}
}
if ( curDiag < rrp . minDiag ) return ;
Point3f 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 ,
Box3f ( Point3f ( bb . min [ 0 ] + i * hs [ 0 ] , bb . min [ 1 ] + j * hs [ 1 ] , bb . min [ 2 ] + k * hs [ 2 ] ) ,
Point3f ( startPt [ 0 ] + i * hs [ 0 ] , startPt [ 1 ] + j * hs [ 1 ] , startPt [ 2 ] + k * hs [ 2 ] ) ) , rrp , curDiag ) ;
}
}
2008-06-21 01:33:24 +02:00
} ; // end class
2008-06-20 17:11:30 +02:00
} // end namespace tri
} // end namespace vcg
2008-05-27 23:38:37 +02:00
# endif
2009-03-19 23:04:52 +01:00