Coarse C++ implementation for "Fast Poisson disk sampling in arbitrary dimensions, R. Bridson, ACM SIGGRAPH 2007 Sketches Program".
在公司实在闲的蛋疼,于是做了个快速泊松碟采样的实现。
2D FPDS Code
/**
* Copyright (c) Bo Zhou<bo.schwarzstein@gmail.com>
* An 2D implementation of "Fast Poisson Disk Sampling in Arbitrary Dimensions, R. Bridson, ACM SIGGRAPH 2007 Sketches Program".
* Anybody could use this code freely, if you feel it good or helpful, please tell me, thank you very much.
*/
#ifndef FPDS_HPP
#define FPDS_HPP
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <math.h>
#include <boost/multi_array.hpp>
// Define our basis data structure.
typedef std::pair<int,int> Index2D;
typedef std::pair<float,float> Point2D;
// Use C standard rand() to get uniform random number in (0,1)
inline float URand()
{
return (float)rand() / (float)RAND_MAX;
}
// Calculate distance between 2 points.
inline float LengthSqrt(const Point2D& X, const Point2D& Y)
{
float a = X.first - Y.first;
float b = X.second - Y.second;
return sqrtf( a*a + b*b );
}
float FastPoissonDiskSampling2D(const float r, std::vector<Point2D>& PointList)
{
PointList.clear();
const int N = 2;
const float SqrtN = sqrtf((float)N);
const float R = r*SqrtN; // This R should be used to draw circle.
const float RDivSqrtN = R / SqrtN;
const int K = 30; // Paper suggested.
const float CellSize = (float)N*RDivSqrtN;
const int GridSize = (int)ceilf(1.0f/CellSize);
const int INVALID_IDX = -1;
// Allocate a grid to accelerate.
// Each cell could have one and only one sampler.
boost::multi_array<int,N> Grid( boost::extents[GridSize][GridSize] );
memset( Grid.data(), INVALID_IDX, sizeof(int)*GridSize*GridSize );
std::vector<int> ActiveList;
Point2D P0;
Index2D I0;
P0.first = URand();
P0.second = URand();
I0.first = (int)floorf( P0.first*GridSize );
I0.second = (int)floorf( P0.second*GridSize );
ActiveList.push_back(0);
PointList.push_back(P0);
Grid[I0.first][I0.second] = 0;
int Done = 1;
while( ActiveList.size() != 0 )
{
// Initialize a sampler.
size_t MagicIdx = (size_t)floorf( URand() * ActiveList.size() );
size_t StartIdx = ActiveList[MagicIdx];
Point2D StartPoint = PointList[ StartIdx ];
bool Found = false;
for( size_t i=0; i<K; ++i )
{
// Generate point in radius (R,2R)
float t = URand()*M_PI*2.0;
float r = URand()*R+R;
float X = r*cosf( t );
float Y = r*sinf( t );
// Move to current center
Point2D CurrentPoint;
CurrentPoint.first = X+StartPoint.first;
CurrentPoint.second = Y+StartPoint.second;
// Discard if out of domain
if( CurrentPoint.first < 0.0f || CurrentPoint.first >= 1.0f )
continue;
if( CurrentPoint.second < 0.0f || CurrentPoint.second >= 1.0f )
continue;
// Which cell the test point located in
Index2D TargetCell;
TargetCell.first = (int)floorf( CurrentPoint.first / CellSize );
TargetCell.second = (int)floorf( CurrentPoint.second / CellSize );
if( TargetCell.first < 0 || TargetCell.first >= GridSize )
continue;
if( TargetCell.second < 0 || TargetCell.second >= GridSize )
continue;
if( Grid[TargetCell.first][TargetCell.second] != INVALID_IDX )
continue;
Index2D TCLeft( TargetCell );
Index2D TCRight( TargetCell );
Index2D TCDown( TargetCell );
Index2D TCUp( TargetCell );
Index2D TCLU( TargetCell );
Index2D TCRU( TargetCell );
Index2D TCLD( TargetCell );
Index2D TCRD( TargetCell );
bool A = false, B = false, C = false, D = false;
bool E = false, F = false, G = false, H = false;
TCLeft.first--;
if( TCLeft.first > -1 )
{
int Idx2 = Grid[TCLeft.first][TCLeft.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
A = true;
}
}else
{
A = true;
}
}else
{
A = true;
}
TCRight.first++;
if( TCRight.first < GridSize )
{
int Idx2 = Grid[TCRight.first][TCRight.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
B = true;
}
}else
{
B = true;
}
}else
{
B = true;
}
TCDown.second--;
if( TCDown.second > -1 )
{
int Idx2 = Grid[TCDown.first][TCDown.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
C = true;
}
}else
{
C = true;
}
}else
{
C = true;
}
TCUp.second++;
if( TCUp.second < GridSize )
{
int Idx2 = Grid[TCUp.first][TCUp.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
D = true;
}
}else
{
D = true;
}
}else
{
D = true;
}
// 4 Corner
// Left Up
TCLU.first--;TCLU.second++;
if( TCLU.first > -1 && TCLU.second < GridSize )
{
int Idx2 = Grid[TCLU.first][TCLU.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
E = true;
}
}else
{
E = true;
}
}else
{
E = true;
}
// Right Up
TCRU.first++;TCRU.second++;
if( TCRU.first < GridSize && TCRU.second < GridSize )
{
int Idx2 = Grid[TCRU.first][TCRU.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
F = true;
}
}else
{
F = true;
}
}else
{
F = true;
}
// Left Bottom
TCLD.first--;TCLD.second--;
if( TCLD.first > -1 && TCLD.second > -1 )
{
int Idx2 = Grid[TCLD.first][TCLD.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
G = true;
}
}else
{
G = true;
}
}else
{
G = true;
}
// Right Bottom
TCRD.first++;TCRD.second--;
if( TCRD.first < GridSize && TCRD.second > -1 )
{
int Idx2 = Grid[TCRD.first][TCRD.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
H = true;
}
}else
{
H = true;
}
}else
{
H = true;
}
if( A&B&C&D&E&F&G&H )
{
Grid[TargetCell.first][TargetCell.second] = Done;
PointList.push_back(CurrentPoint);
ActiveList.push_back(Done);
++Done;
Found = true;
break;
}
}
// We have to remove this test sampler from active list.
if( Found == false && ActiveList.size() > 0 )
ActiveList.erase( ActiveList.begin()+MagicIdx );
}
return R;
}
#endif
/**
* Copyright (c) Bo Zhou<bo.schwarzstein@gmail.com>
* An 2D implementation of "Fast Poisson Disk Sampling in Arbitrary Dimensions, R. Bridson, ACM SIGGRAPH 2007 Sketches Program".
* Anybody could use this code freely, if you feel it good or helpful, please tell me, thank you very much.
*/
#ifndef FPDS_HPP
#define FPDS_HPP
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <math.h>
#include <boost/multi_array.hpp>
// Define our basis data structure.
typedef std::pair<int,int> Index2D;
typedef std::pair<float,float> Point2D;
// Use C standard rand() to get uniform random number in (0,1)
inline float URand()
{
return (float)rand() / (float)RAND_MAX;
}
// Calculate distance between 2 points.
inline float LengthSqrt(const Point2D& X, const Point2D& Y)
{
float a = X.first - Y.first;
float b = X.second - Y.second;
return sqrtf( a*a + b*b );
}
float FastPoissonDiskSampling2D(const float r, std::vector<Point2D>& PointList)
{
PointList.clear();
const int N = 2;
const float SqrtN = sqrtf((float)N);
const float R = r*SqrtN; // This R should be used to draw circle.
const float RDivSqrtN = R / SqrtN;
const int K = 30; // Paper suggested.
const float CellSize = (float)N*RDivSqrtN;
const int GridSize = (int)ceilf(1.0f/CellSize);
const int INVALID_IDX = -1;
// Allocate a grid to accelerate.
// Each cell could have one and only one sampler.
boost::multi_array<int,N> Grid( boost::extents[GridSize][GridSize] );
memset( Grid.data(), INVALID_IDX, sizeof(int)*GridSize*GridSize );
std::vector<int> ActiveList;
Point2D P0;
Index2D I0;
P0.first = URand();
P0.second = URand();
I0.first = (int)floorf( P0.first*GridSize );
I0.second = (int)floorf( P0.second*GridSize );
ActiveList.push_back(0);
PointList.push_back(P0);
Grid[I0.first][I0.second] = 0;
int Done = 1;
while( ActiveList.size() != 0 )
{
// Initialize a sampler.
size_t MagicIdx = (size_t)floorf( URand() * ActiveList.size() );
size_t StartIdx = ActiveList[MagicIdx];
Point2D StartPoint = PointList[ StartIdx ];
bool Found = false;
for( size_t i=0; i<K; ++i )
{
// Generate point in radius (R,2R)
float t = URand()*M_PI*2.0;
float r = URand()*R+R;
float X = r*cosf( t );
float Y = r*sinf( t );
// Move to current center
Point2D CurrentPoint;
CurrentPoint.first = X+StartPoint.first;
CurrentPoint.second = Y+StartPoint.second;
// Discard if out of domain
if( CurrentPoint.first < 0.0f || CurrentPoint.first >= 1.0f )
continue;
if( CurrentPoint.second < 0.0f || CurrentPoint.second >= 1.0f )
continue;
// Which cell the test point located in
Index2D TargetCell;
TargetCell.first = (int)floorf( CurrentPoint.first / CellSize );
TargetCell.second = (int)floorf( CurrentPoint.second / CellSize );
if( TargetCell.first < 0 || TargetCell.first >= GridSize )
continue;
if( TargetCell.second < 0 || TargetCell.second >= GridSize )
continue;
if( Grid[TargetCell.first][TargetCell.second] != INVALID_IDX )
continue;
Index2D TCLeft( TargetCell );
Index2D TCRight( TargetCell );
Index2D TCDown( TargetCell );
Index2D TCUp( TargetCell );
Index2D TCLU( TargetCell );
Index2D TCRU( TargetCell );
Index2D TCLD( TargetCell );
Index2D TCRD( TargetCell );
bool A = false, B = false, C = false, D = false;
bool E = false, F = false, G = false, H = false;
TCLeft.first--;
if( TCLeft.first > -1 )
{
int Idx2 = Grid[TCLeft.first][TCLeft.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
A = true;
}
}else
{
A = true;
}
}else
{
A = true;
}
TCRight.first++;
if( TCRight.first < GridSize )
{
int Idx2 = Grid[TCRight.first][TCRight.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
B = true;
}
}else
{
B = true;
}
}else
{
B = true;
}
TCDown.second--;
if( TCDown.second > -1 )
{
int Idx2 = Grid[TCDown.first][TCDown.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
C = true;
}
}else
{
C = true;
}
}else
{
C = true;
}
TCUp.second++;
if( TCUp.second < GridSize )
{
int Idx2 = Grid[TCUp.first][TCUp.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
D = true;
}
}else
{
D = true;
}
}else
{
D = true;
}
// 4 Corner
// Left Up
TCLU.first--;TCLU.second++;
if( TCLU.first > -1 && TCLU.second < GridSize )
{
int Idx2 = Grid[TCLU.first][TCLU.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
E = true;
}
}else
{
E = true;
}
}else
{
E = true;
}
// Right Up
TCRU.first++;TCRU.second++;
if( TCRU.first < GridSize && TCRU.second < GridSize )
{
int Idx2 = Grid[TCRU.first][TCRU.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
F = true;
}
}else
{
F = true;
}
}else
{
F = true;
}
// Left Bottom
TCLD.first--;TCLD.second--;
if( TCLD.first > -1 && TCLD.second > -1 )
{
int Idx2 = Grid[TCLD.first][TCLD.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
G = true;
}
}else
{
G = true;
}
}else
{
G = true;
}
// Right Bottom
TCRD.first++;TCRD.second--;
if( TCRD.first < GridSize && TCRD.second > -1 )
{
int Idx2 = Grid[TCRD.first][TCRD.second];
if( Idx2 >= 0 )
{
if( LengthSqrt( PointList[Idx2],CurrentPoint ) > R )
{
H = true;
}
}else
{
H = true;
}
}else
{
H = true;
}
if( A&B&C&D&E&F&G&H )
{
Grid[TargetCell.first][TargetCell.second] = Done;
PointList.push_back(CurrentPoint);
ActiveList.push_back(Done);
++Done;
Found = true;
break;
}
}
// We have to remove this test sampler from active list.
if( Found == false && ActiveList.size() > 0 )
ActiveList.erase( ActiveList.begin()+MagicIdx );
}
return R;
}
#endif