From de718b91cd4bbc631c99a6ede5f96a15f326b1b5 Mon Sep 17 00:00:00 2001 From: Sergey Kosarevsky Date: Fri, 16 Feb 2024 21:26:34 -0800 Subject: [PATCH] Added .clang-format --- .clang-format | 75 ++++++ Poisson.cpp | 360 ++++++++++++++-------------- PoissonGenerator.h | 577 +++++++++++++++++++++------------------------ README.md | 2 +- 4 files changed, 515 insertions(+), 499 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d1d1a03 --- /dev/null +++ b/.clang-format @@ -0,0 +1,75 @@ +--- +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +# AlingOperands: true # Unsupported +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: false +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakStringLiterals: true +ColumnLimit: 140 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +# LanguageKind: Cpp # Unsupported +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +# ObjCBinPackProtocolList: Never # Unsupported +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 5 +PenaltyBreakBeforeFirstCallParameter: 10 +PenaltyBreakComment: 60 +PenaltyBreakFirstLessLess: 20 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +# SpaceBeforeCtorInitializerColon: false # Unsupported +# SpaceBeforeInheritanceColon: false # Unsupported +SpaceBeforeParens: ControlStatements +# SpaceBeforeRangeBasedForLoopColon: true # Unsupported +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 2 +UseTab: Never +... diff --git a/Poisson.cpp b/Poisson.cpp index 1efeaf8..f83ed66 100644 --- a/Poisson.cpp +++ b/Poisson.cpp @@ -4,23 +4,23 @@ * * Poisson Disk Points Generator example * - * \version 1.6.0 - * \date 29/05/2023 - * \author Sergey Kosarevsky, 2014-2023 + * \version 1.6.1 + * \date 16/02/2024 + * \author Sergey Kosarevsky, 2014-2024 * \author support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com */ /* - To compile: - gcc Poisson.cpp -std=c++17 -lstdc++ + To compile: + gcc Poisson.cpp -std=c++17 -lstdc++ */ -#include +#include #include #include -#include #include #include +#include #define POISSON_PROGRESS_INDICATOR 1 #include "PoissonGenerator.h" @@ -29,217 +29,205 @@ ///////////////// User selectable parameters /////////////////////////////// -const int kNumPointsDefaultPoisson = 20000; // default number of points to generate for Poisson disk -const int kNumPointsDefaultVogel = 2000; // default number of points to generate for Vogel disk -const int kNumPointsDefaultJittered = 2500; // default number of points to generate for jittered grid -const int kImageSize = 512; // generate RGB image [ImageSize x ImageSize] +const int kNumPointsDefaultPoisson = 20000; // default number of points to generate for Poisson disk +const int kNumPointsDefaultVogel = 2000; // default number of points to generate for Vogel disk +const int kNumPointsDefaultJittered = 2500; // default number of points to generate for jittered grid +const int kImageSize = 512; // generate RGB image [ImageSize x ImageSize] //////////////////////////////////////////////////////////////////////////// float* g_DensityMap = nullptr; -#if defined( __GNUC__ ) -# define GCC_PACK(n) __attribute__((packed,aligned(n))) +#if defined(__GNUC__) +#define GCC_PACK(n) __attribute__((packed, aligned(n))) #else -# define GCC_PACK(n) __declspec(align(n)) +#define GCC_PACK(n) __declspec(align(n)) #endif // __GNUC__ #pragma pack(push, 1) -struct GCC_PACK( 1 ) sBMPHeader -{ - // BITMAPFILEHEADER - unsigned short bfType; - uint32_t bfSize; - unsigned short bfReserved1; - unsigned short bfReserved2; - uint32_t bfOffBits; - // BITMAPINFOHEADER - uint32_t biSize; - uint32_t biWidth; - uint32_t biHeight; - unsigned short biPlanes; - unsigned short biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - uint32_t biXPelsPerMeter; - uint32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; +struct GCC_PACK(1) sBMPHeader { + // BITMAPFILEHEADER + unsigned short bfType; + uint32_t bfSize; + unsigned short bfReserved1; + unsigned short bfReserved2; + uint32_t bfOffBits; + // BITMAPINFOHEADER + uint32_t biSize; + uint32_t biWidth; + uint32_t biHeight; + unsigned short biPlanes; + unsigned short biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + uint32_t biXPelsPerMeter; + uint32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; }; #pragma pack(pop) -void SaveBMP( const char* FileName, const void* RawBGRImage, int Width, int Height ) -{ - sBMPHeader Header; - - int ImageSize = Width * Height * 3; - - Header.bfType = 0x4D * 256 + 0x42; - Header.bfSize = ImageSize + sizeof( sBMPHeader ); - Header.bfReserved1 = 0; - Header.bfReserved2 = 0; - Header.bfOffBits = 0x36; - Header.biSize = 40; - Header.biWidth = Width; - Header.biHeight = Height; - Header.biPlanes = 1; - Header.biBitCount = 24; - Header.biCompression = 0; - Header.biSizeImage = ImageSize; - Header.biXPelsPerMeter = 6000; - Header.biYPelsPerMeter = 6000; - Header.biClrUsed = 0; - Header.biClrImportant = 0; - - std::ofstream File( FileName, std::ios::out | std::ios::binary ); - - File.write( (const char*)&Header, sizeof( Header ) ); - File.write( (const char*)RawBGRImage, ImageSize ); - - std::cout << "Saved " << FileName << std::endl; +void SaveBMP(const char* FileName, const void* RawBGRImage, int Width, int Height) { + sBMPHeader Header; + + int ImageSize = Width * Height * 3; + + Header.bfType = 0x4D * 256 + 0x42; + Header.bfSize = ImageSize + sizeof(sBMPHeader); + Header.bfReserved1 = 0; + Header.bfReserved2 = 0; + Header.bfOffBits = 0x36; + Header.biSize = 40; + Header.biWidth = Width; + Header.biHeight = Height; + Header.biPlanes = 1; + Header.biBitCount = 24; + Header.biCompression = 0; + Header.biSizeImage = ImageSize; + Header.biXPelsPerMeter = 6000; + Header.biYPelsPerMeter = 6000; + Header.biClrUsed = 0; + Header.biClrImportant = 0; + + std::ofstream File(FileName, std::ios::out | std::ios::binary); + + File.write((const char*)&Header, sizeof(Header)); + File.write((const char*)RawBGRImage, ImageSize); + + std::cout << "Saved " << FileName << std::endl; } -unsigned char* LoadBMP( const char* FileName, int* OutWidth, int* OutHeight ) -{ - sBMPHeader Header; +unsigned char* LoadBMP(const char* FileName, int* OutWidth, int* OutHeight) { + sBMPHeader Header; - std::ifstream File( FileName, std::ifstream::binary ); + std::ifstream File(FileName, std::ifstream::binary); - File.read( (char*)&Header, sizeof( Header ) ); + File.read((char*)&Header, sizeof(Header)); - *OutWidth = Header.biWidth; - *OutHeight = Header.biHeight; + *OutWidth = Header.biWidth; + *OutHeight = Header.biHeight; - const size_t DataSize = 3 * Header.biWidth * Header.biHeight; + const size_t DataSize = 3 * Header.biWidth * Header.biHeight; - unsigned char* Img = new unsigned char[ DataSize ]; + unsigned char* Img = new unsigned char[DataSize]; - File.read( (char*)Img, DataSize ); + File.read((char*)Img, DataSize); - return Img; + return Img; } -void LoadDensityMap( const char* FileName ) -{ - std::cout << "Loading density map " << FileName << std::endl; +void LoadDensityMap(const char* FileName) { + std::cout << "Loading density map " << FileName << std::endl; - int W, H; - unsigned char* Data = LoadBMP( FileName, &W, &H ); + int W, H; + unsigned char* Data = LoadBMP(FileName, &W, &H); - std::cout << "Loaded ( " << W << " x " << H << " ) " << std::endl; + std::cout << "Loaded ( " << W << " x " << H << " ) " << std::endl; - if ( W != kImageSize || H != kImageSize ) - { - std::cout << "ERROR: density map should be " << kImageSize << " x " << kImageSize << std::endl; + if (W != kImageSize || H != kImageSize) { + std::cout << "ERROR: density map should be " << kImageSize << " x " << kImageSize << std::endl; - exit( 255 ); - } + exit(255); + } - g_DensityMap = new float[ W * H ]; + g_DensityMap = new float[W * H]; - for ( int y = 0; y != H; y++ ) - { - for ( int x = 0; x != W; x++ ) - { - g_DensityMap[ x + y * W ] = float( Data[ 3 * (x + y * W) ] ) / 255.0f; - } - } + for (int y = 0; y != H; y++) { + for (int x = 0; x != W; x++) { + g_DensityMap[x + y * W] = float(Data[3 * (x + y * W)]) / 255.0f; + } + } - delete[]( Data ); + delete[] (Data); } -void PrintBanner() -{ - std::cout << "Poisson disk points generator" << std::endl; - std::cout << "Version " << PoissonGenerator::Version << std::endl; - std::cout << "Sergey Kosarevsky, 2014-2023" << std::endl; - std::cout << "support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com" << std::endl; - std::cout << std::endl; - std::cout << "Usage: Poisson [density-map-rgb24.bmp] [--raw-points] [--num-points=] [--square] [--vogel-disk | --jittered-grid | --hammersley]" << std::endl; - std::cout << std::endl; +void PrintBanner() { + std::cout << "Poisson disk points generator" << std::endl; + std::cout << "Version " << PoissonGenerator::Version << std::endl; + std::cout << "Sergey Kosarevsky, 2014-2023" << std::endl; + std::cout << "support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com" << std::endl; + std::cout << std::endl; + std::cout << "Usage: Poisson [density-map-rgb24.bmp] [--raw-points] [--num-points=] [--square] [--vogel-disk | --jittered-grid | " + "--hammersley]" + << std::endl; + std::cout << std::endl; } -int main( int argc, char** argv ) -{ - PrintBanner(); - - argh::parser cmdl(argv); - - if (!cmdl[1].empty()) - { - LoadDensityMap(cmdl[1].c_str()); - } - - const bool cmdRawPointsOutput = cmdl[{"--raw-points"}]; - const bool cmdSquare = cmdl[{"--square"}]; - const bool cmdVogelDisk = cmdl[{"--vogel-disk"}]; - const bool cmdJitteredGrid = cmdl[{"--jittered-grid"}];; - const bool cmdHammersley = cmdl[{"--hammersley"}];; - - unsigned int numPoints; - cmdl("num-points", cmdVogelDisk ? kNumPointsDefaultVogel : (cmdJitteredGrid ? kNumPointsDefaultJittered : kNumPointsDefaultPoisson)) >> numPoints; - - std::cout << "NumPoints = " << numPoints << std::endl; - - PoissonGenerator::DefaultPRNG PRNG; - - const auto Points = cmdVogelDisk ? - PoissonGenerator::generateVogelPoints(numPoints) : cmdJitteredGrid ? - PoissonGenerator::generateJitteredGridPoints(numPoints, PRNG, !cmdSquare) : cmdHammersley ? - PoissonGenerator::generateHammersleyPoints(numPoints) : - PoissonGenerator::generatePoissonPoints(numPoints, PRNG, !cmdSquare); - - // prepare BGR image - const size_t DataSize = 3 * kImageSize * kImageSize; - - unsigned char* Img = new unsigned char[ DataSize ]; - - memset( Img, 0, DataSize ); - - for ( auto i = Points.begin(); i != Points.end(); i++ ) - { - int x = int( i->x * kImageSize ); - int y = int( i->y * kImageSize ); - if (x < 0 || y < 0 || x >= kImageSize || y >= kImageSize) - continue; - if ( g_DensityMap ) - { - // dice - float R = PRNG.randomFloat(); - float P = g_DensityMap[ x + y * kImageSize ]; - if ( R > P ) continue; - } - int Base = 3 * (x + y * kImageSize); - Img[ Base+0 ] = Img[ Base+1 ] = Img[ Base+2 ] = 255; - } - - SaveBMP( "Points.bmp", Img, kImageSize, kImageSize ); - - delete[]( Img ); - - // dump points to a text file - std::ofstream File("points.txt", std::ios::out); - - if (cmdRawPointsOutput) - { - File << "NumPoints = " << Points.size() << std::endl; - - for (const auto& p : Points) - { - File << p.x << " " << p.y << std::endl; - } - } - else - { - File << "const vec2 points[" << Points.size() << "]" << std::endl; - File << "{" << std::endl; - File << std::fixed << std::setprecision(6); - for (const auto& p : Points) - { - File << "\tvec2(" << p.x << "f, " << p.y << "f)," << std::endl; - } - File << "};" << std::endl; - } - - return 0; +int main(int argc, char** argv) { + PrintBanner(); + + argh::parser cmdl(argv); + + if (!cmdl[1].empty()) { + LoadDensityMap(cmdl[1].c_str()); + } + + const bool cmdRawPointsOutput = cmdl[{"--raw-points"}]; + const bool cmdSquare = cmdl[{"--square"}]; + const bool cmdVogelDisk = cmdl[{"--vogel-disk"}]; + const bool cmdJitteredGrid = cmdl[{"--jittered-grid"}]; + ; + const bool cmdHammersley = cmdl[{"--hammersley"}]; + ; + + unsigned int numPoints; + cmdl("num-points", cmdVogelDisk ? kNumPointsDefaultVogel : (cmdJitteredGrid ? kNumPointsDefaultJittered : kNumPointsDefaultPoisson)) >> + numPoints; + + std::cout << "NumPoints = " << numPoints << std::endl; + + PoissonGenerator::DefaultPRNG PRNG; + + const auto Points = cmdVogelDisk ? PoissonGenerator::generateVogelPoints(numPoints) + : cmdJitteredGrid ? PoissonGenerator::generateJitteredGridPoints(numPoints, PRNG, !cmdSquare) + : cmdHammersley ? PoissonGenerator::generateHammersleyPoints(numPoints) + : PoissonGenerator::generatePoissonPoints(numPoints, PRNG, !cmdSquare); + + // prepare BGR image + const size_t DataSize = 3 * kImageSize * kImageSize; + + unsigned char* Img = new unsigned char[DataSize]; + + memset(Img, 0, DataSize); + + for (auto i = Points.begin(); i != Points.end(); i++) { + int x = int(i->x * kImageSize); + int y = int(i->y * kImageSize); + if (x < 0 || y < 0 || x >= kImageSize || y >= kImageSize) + continue; + if (g_DensityMap) { + // dice + float R = PRNG.randomFloat(); + float P = g_DensityMap[x + y * kImageSize]; + if (R > P) + continue; + } + int Base = 3 * (x + y * kImageSize); + Img[Base + 0] = Img[Base + 1] = Img[Base + 2] = 255; + } + + SaveBMP("Points.bmp", Img, kImageSize, kImageSize); + + delete[] (Img); + + // dump points to a text file + std::ofstream File("points.txt", std::ios::out); + + if (cmdRawPointsOutput) { + File << "NumPoints = " << Points.size() << std::endl; + + for (const auto& p : Points) { + File << p.x << " " << p.y << std::endl; + } + } else { + File << "const vec2 points[" << Points.size() << "]" << std::endl; + File << "{" << std::endl; + File << std::fixed << std::setprecision(6); + for (const auto& p : Points) { + File << "\tvec2(" << p.x << "f, " << p.y << "f)," << std::endl; + } + File << "};" << std::endl; + } + + return 0; } diff --git a/PoissonGenerator.h b/PoissonGenerator.h index 181c8db..b2423a4 100644 --- a/PoissonGenerator.h +++ b/PoissonGenerator.h @@ -4,22 +4,22 @@ * * Poisson Disk Points Generator * - * \version 1.6.0 - * \date 29/05/2023 - * \author Sergey Kosarevsky, 2014-2023 + * \version 1.6.1 + * \date 16/02/2024 + * \author Sergey Kosarevsky, 2014-2024 * \author support@linderdaum.com http://www.linderdaum.com http://blog.linderdaum.com */ /* - Usage example: - - #define POISSON_PROGRESS_INDICATOR 1 - #include "PoissonGenerator.h" - ... - PoissonGenerator::DefaultPRNG PRNG; - const auto Points = PoissonGenerator::generatePoissonPoints( NumPoints, PRNG ); - ... - const auto Points = PoissonGenerator::generateVogelPoints( NumPoints ); + Usage example: + + #define POISSON_PROGRESS_INDICATOR 1 + #include "PoissonGenerator.h" + ... + PoissonGenerator::DefaultPRNG PRNG; + const auto Points = PoissonGenerator::generatePoissonPoints( NumPoints, PRNG ); + ... + const auto Points = PoissonGenerator::generateVogelPoints( NumPoints ); */ // Fast Poisson Disk Sampling in Arbitrary Dimensions @@ -28,6 +28,8 @@ // Implementation based on http://devmag.org.za/2009/05/03/poisson-disk-sampling/ /* Versions history: + * 1.6.1 Feb 16, 2024 Reformatted using .clang-format + * 1.6 May 29, 2023 Added generateHammersleyPoints() to generate Hammersley points * 1.5 Mar 26, 2022 Added generateJitteredGridPoints() to generate jittered grid points * 1.4.1 Dec 12, 2021 Replaced default Mersenne Twister and with fast and lightweight LCG * 1.4 Dec 5, 2021 Added generateVogelPoints() to generate Vogel disk points @@ -42,378 +44,329 @@ * 1.1.1 May 23, 2014 Initialize PRNG seed, fixed uninitialized fields * 1.1 May 7, 2014 Support of density maps * 1.0 May 6, 2014 -*/ + */ -#include #include +#include -namespace PoissonGenerator -{ - -const char* Version = "1.4.1 (12/12/2021)"; - -class DefaultPRNG -{ -public: - DefaultPRNG() = default; - explicit DefaultPRNG(unsigned int seed) :seed_(seed) {} - inline float randomFloat() - { - seed_ *= 521167; - uint32_t a = (seed_ & 0x007fffff) | 0x40000000; - // remap to 0..1 - return 0.5f * (*((float*)&a) - 2.0f); - } - inline uint32_t randomInt(uint32_t maxInt) - { - return uint32_t(randomFloat() * maxInt); - } - inline uint32_t getSeed() const { return seed_; } -private: - uint32_t seed_ = 7133167; +namespace PoissonGenerator { + +const char* Version = "1.6.1 (16/02/2024)"; + +class DefaultPRNG { + public: + DefaultPRNG() = default; + explicit DefaultPRNG(unsigned int seed) : seed_(seed) {} + inline float randomFloat() { + seed_ *= 521167; + uint32_t a = (seed_ & 0x007fffff) | 0x40000000; + // remap to 0..1 + return 0.5f * (*((float*)&a) - 2.0f); + } + inline uint32_t randomInt(uint32_t maxInt) { + return uint32_t(randomFloat() * maxInt); + } + inline uint32_t getSeed() const { + return seed_; + } + + private: + uint32_t seed_ = 7133167; }; -struct Point -{ - Point() = default; - Point( float X, float Y ) - : x( X ) - , y( Y ) - , valid_( true ) - {} - float x = 0.0f; - float y = 0.0f; - bool valid_ = false; - // - bool isInRectangle() const - { - return x >= 0 && y >= 0 && x <= 1 && y <= 1; - } - // - bool isInCircle() const - { - const float fx = x - 0.5f; - const float fy = y - 0.5f; - return ( fx*fx + fy*fy ) <= 0.25f; - } - Point& operator + (const Point& p) - { - x += p.x; - y += p.y; - return *this; - } - Point& operator - (const Point& p) - { - x -= p.x; - y -= p.y; - return *this; - } +struct Point { + Point() = default; + Point(float X, float Y) : x(X), y(Y), valid_(true) {} + float x = 0.0f; + float y = 0.0f; + bool valid_ = false; + // + bool isInRectangle() const { + return x >= 0 && y >= 0 && x <= 1 && y <= 1; + } + // + bool isInCircle() const { + const float fx = x - 0.5f; + const float fy = y - 0.5f; + return (fx * fx + fy * fy) <= 0.25f; + } + Point& operator+(const Point& p) { + x += p.x; + y += p.y; + return *this; + } + Point& operator-(const Point& p) { + x -= p.x; + y -= p.y; + return *this; + } }; -struct GridPoint -{ - GridPoint() = delete; - GridPoint( int X, int Y ) - : x( X ) - , y( Y ) - {} - int x; - int y; +struct GridPoint { + GridPoint() = delete; + GridPoint(int X, int Y) : x(X), y(Y) {} + int x; + int y; }; -float getDistance( const Point& P1, const Point& P2 ) -{ - return sqrt( ( P1.x - P2.x ) * ( P1.x - P2.x ) + ( P1.y - P2.y ) * ( P1.y - P2.y ) ); +float getDistance(const Point& P1, const Point& P2) { + return sqrt((P1.x - P2.x) * (P1.x - P2.x) + (P1.y - P2.y) * (P1.y - P2.y)); } -GridPoint imageToGrid( const Point& P, float cellSize ) -{ - return GridPoint( ( int )( P.x / cellSize ), ( int )( P.y / cellSize ) ); +GridPoint imageToGrid(const Point& P, float cellSize) { + return GridPoint((int)(P.x / cellSize), (int)(P.y / cellSize)); } -struct Grid -{ - Grid( int w, int h, float cellSize ) - : w_( w ) - , h_( h ) - , cellSize_( cellSize ) - { - grid_.resize( h_ ); - for ( auto i = grid_.begin(); i != grid_.end(); i++ ) { i->resize( w ); } - } - void insert( const Point& p ) - { - const GridPoint g = imageToGrid( p, cellSize_ ); - grid_[ g.x ][ g.y ] = p; - } - bool isInNeighbourhood( const Point& point, float minDist, float cellSize ) - { - const GridPoint g = imageToGrid( point, cellSize ); - - // number of adjucent cells to look for neighbour points - const int D = 5; - - // scan the neighbourhood of the point in the grid - for ( int i = g.x - D; i <= g.x + D; i++ ) - { - for ( int j = g.y - D; j <= g.y + D; j++ ) - { - if ( i >= 0 && i < w_ && j >= 0 && j < h_ ) - { - const Point P = grid_[ i ][ j ]; - - if ( P.valid_ && getDistance( P, point ) < minDist ) - return true; - } - } - } - - return false; - } - -private: - int w_; - int h_; - float cellSize_; - std::vector< std::vector > grid_; +struct Grid { + Grid(int w, int h, float cellSize) : w_(w), h_(h), cellSize_(cellSize) { + grid_.resize(h_); + for (auto i = grid_.begin(); i != grid_.end(); i++) { + i->resize(w); + } + } + void insert(const Point& p) { + const GridPoint g = imageToGrid(p, cellSize_); + grid_[g.x][g.y] = p; + } + bool isInNeighbourhood(const Point& point, float minDist, float cellSize) { + const GridPoint g = imageToGrid(point, cellSize); + + // number of adjucent cells to look for neighbour points + const int D = 5; + + // scan the neighbourhood of the point in the grid + for (int i = g.x - D; i <= g.x + D; i++) { + for (int j = g.y - D; j <= g.y + D; j++) { + if (i >= 0 && i < w_ && j >= 0 && j < h_) { + const Point P = grid_[i][j]; + + if (P.valid_ && getDistance(P, point) < minDist) + return true; + } + } + } + + return false; + } + + private: + int w_; + int h_; + float cellSize_; + std::vector> grid_; }; -template -Point popRandom( std::vector& points, PRNG& generator ) -{ - const int idx = generator.randomInt( static_cast(points.size())-1 ); - const Point p = points[ idx ]; - points.erase( points.begin() + idx ); - return p; +template +Point popRandom(std::vector& points, PRNG& generator) { + const int idx = generator.randomInt(static_cast(points.size()) - 1); + const Point p = points[idx]; + points.erase(points.begin() + idx); + return p; } -template -Point generateRandomPointAround( const Point& p, float minDist, PRNG& generator ) -{ - // start with non-uniform distribution - const float R1 = generator.randomFloat(); - const float R2 = generator.randomFloat(); +template +Point generateRandomPointAround(const Point& p, float minDist, PRNG& generator) { + // start with non-uniform distribution + const float R1 = generator.randomFloat(); + const float R2 = generator.randomFloat(); - // radius should be between MinDist and 2 * MinDist - const float radius = minDist * ( R1 + 1.0f ); + // radius should be between MinDist and 2 * MinDist + const float radius = minDist * (R1 + 1.0f); - // random angle - const float angle = 2 * 3.141592653589f * R2; + // random angle + const float angle = 2 * 3.141592653589f * R2; - // the new point is generated around the point (x, y) - const float x = p.x + radius * cos( angle ); - const float y = p.y + radius * sin( angle ); + // the new point is generated around the point (x, y) + const float x = p.x + radius * cos(angle); + const float y = p.y + radius * sin(angle); - return Point( x, y ); + return Point(x, y); } /** - Return a vector of generated points + Return a vector of generated points - NewPointsCount - refer to bridson-siggraph07-poissondisk.pdf for details (the value 'k') - Circle - 'true' to fill a circle, 'false' to fill a rectangle - MinDist - minimal distance estimator, use negative value for default + NewPointsCount - refer to bridson-siggraph07-poissondisk.pdf for details (the value 'k') + Circle - 'true' to fill a circle, 'false' to fill a rectangle + MinDist - minimal distance estimator, use negative value for default **/ -template -std::vector generatePoissonPoints( - uint32_t numPoints, - PRNG& generator, - bool isCircle = true, - uint32_t newPointsCount = 30, - float minDist = -1.0f -) -{ - numPoints *= 2; - - // if we want to generate a Poisson square shape, multiply the estimate number of points by PI/4 due to reduced shape area - if (!isCircle) - { - const double Pi_4 = 0.785398163397448309616; // PI/4 - numPoints = static_cast(Pi_4 * numPoints); - } - - if ( minDist < 0.0f ) - { - minDist = sqrt( float(numPoints) ) / float(numPoints); - } - - std::vector samplePoints; - std::vector processList; - - if (!numPoints) - return samplePoints; - - // create the grid - const float cellSize = minDist / sqrt( 2.0f ); - - const int gridW = ( int )ceil( 1.0f / cellSize ); - const int gridH = ( int )ceil( 1.0f / cellSize ); - - Grid grid( gridW, gridH, cellSize ); - - Point firstPoint; - do { - firstPoint = Point( generator.randomFloat(), generator.randomFloat() ); - } while (!(isCircle ? firstPoint.isInCircle() : firstPoint.isInRectangle())); - - // update containers - processList.push_back( firstPoint ); - samplePoints.push_back( firstPoint ); - grid.insert( firstPoint ); +template +std::vector generatePoissonPoints(uint32_t numPoints, + PRNG& generator, + bool isCircle = true, + uint32_t newPointsCount = 30, + float minDist = -1.0f) { + numPoints *= 2; + + // if we want to generate a Poisson square shape, multiply the estimate number of points by PI/4 due to reduced shape area + if (!isCircle) { + const double Pi_4 = 0.785398163397448309616; // PI/4 + numPoints = static_cast(Pi_4 * numPoints); + } + + if (minDist < 0.0f) { + minDist = sqrt(float(numPoints)) / float(numPoints); + } + + std::vector samplePoints; + std::vector processList; + + if (!numPoints) + return samplePoints; + + // create the grid + const float cellSize = minDist / sqrt(2.0f); + + const int gridW = (int)ceil(1.0f / cellSize); + const int gridH = (int)ceil(1.0f / cellSize); + + Grid grid(gridW, gridH, cellSize); + + Point firstPoint; + do { + firstPoint = Point(generator.randomFloat(), generator.randomFloat()); + } while (!(isCircle ? firstPoint.isInCircle() : firstPoint.isInRectangle())); + + // update containers + processList.push_back(firstPoint); + samplePoints.push_back(firstPoint); + grid.insert(firstPoint); #if POISSON_PROGRESS_INDICATOR - size_t progress = 0; + size_t progress = 0; #endif - // generate new points for each point in the queue - while ( !processList.empty() && samplePoints.size() <= numPoints ) - { + // generate new points for each point in the queue + while (!processList.empty() && samplePoints.size() <= numPoints) { #if POISSON_PROGRESS_INDICATOR - // a progress indicator, kind of - if ((samplePoints.size()) % 1000 == 0) - { - const size_t newProgress = 200 * (samplePoints.size() + processList.size()) / numPoints; - if (newProgress != progress) - { - progress = newProgress; - std::cout << "."; - } - } + // a progress indicator, kind of + if ((samplePoints.size()) % 1000 == 0) { + const size_t newProgress = 200 * (samplePoints.size() + processList.size()) / numPoints; + if (newProgress != progress) { + progress = newProgress; + std::cout << "."; + } + } #endif // POISSON_PROGRESS_INDICATOR - const Point point = popRandom( processList, generator ); + const Point point = popRandom(processList, generator); - for ( uint32_t i = 0; i < newPointsCount; i++ ) - { - const Point newPoint = generateRandomPointAround( point, minDist, generator ); - const bool canFitPoint = isCircle ? newPoint.isInCircle() : newPoint.isInRectangle(); + for (uint32_t i = 0; i < newPointsCount; i++) { + const Point newPoint = generateRandomPointAround(point, minDist, generator); + const bool canFitPoint = isCircle ? newPoint.isInCircle() : newPoint.isInRectangle(); - if ( canFitPoint && !grid.isInNeighbourhood( newPoint, minDist, cellSize ) ) - { - processList.push_back( newPoint ); - samplePoints.push_back( newPoint ); - grid.insert( newPoint ); - continue; - } - } - } + if (canFitPoint && !grid.isInNeighbourhood(newPoint, minDist, cellSize)) { + processList.push_back(newPoint); + samplePoints.push_back(newPoint); + grid.insert(newPoint); + continue; + } + } + } #if POISSON_PROGRESS_INDICATOR - std::cout << std::endl << std::endl; + std::cout << std::endl << std::endl; #endif // POISSON_PROGRESS_INDICATOR - return samplePoints; + return samplePoints; } -Point sampleVogelDisk(uint32_t idx, uint32_t numPoints, float phi) -{ - const float kGoldenAngle = 2.4f; +Point sampleVogelDisk(uint32_t idx, uint32_t numPoints, float phi) { + const float kGoldenAngle = 2.4f; - const float r = sqrtf(float(idx) + 0.5f) / sqrtf(float(numPoints)); - const float theta = idx * kGoldenAngle + phi; + const float r = sqrtf(float(idx) + 0.5f) / sqrtf(float(numPoints)); + const float theta = idx * kGoldenAngle + phi; - return Point(r * cosf(theta), r * sinf(theta)); + return Point(r * cosf(theta), r * sinf(theta)); } /** - Return a vector of generated points + Return a vector of generated points **/ -std::vector generateVogelPoints(uint32_t numPoints, bool isCircle = true, float phi = 0.0f, Point center = Point(0.5f, 0.5f)) -{ - std::vector samplePoints; +std::vector generateVogelPoints(uint32_t numPoints, bool isCircle = true, float phi = 0.0f, Point center = Point(0.5f, 0.5f)) { + std::vector samplePoints; - samplePoints.reserve(numPoints); + samplePoints.reserve(numPoints); - const uint32_t numSamples = isCircle ? 4 * numPoints : numPoints; + const uint32_t numSamples = isCircle ? 4 * numPoints : numPoints; - for (uint32_t i = 0; i != numPoints; i++) - { - const Point p = sampleVogelDisk(i, numSamples, phi * 3.141592653f / 180.0f) + center; - samplePoints.push_back(p); - } + for (uint32_t i = 0; i != numPoints; i++) { + const Point p = sampleVogelDisk(i, numSamples, phi * 3.141592653f / 180.0f) + center; + samplePoints.push_back(p); + } - return samplePoints; + return samplePoints; } /** - Return a vector of generated points + Return a vector of generated points **/ -template -std::vector generateJitteredGridPoints( - uint32_t numPoints, - PRNG& generator, - bool isCircle = false, - float jitterRadius = 0.004f, - Point center = Point(0.5f, 0.5f) -) -{ - std::vector samplePoints; - - samplePoints.reserve(numPoints); - - const uint32_t gridSize = uint32_t(sqrt(numPoints)); - - for (uint32_t x = 0; x != gridSize; x++) - { - for (uint32_t y = 0; y != gridSize; y++) - { - Point p; - do { - const Point offs = generateRandomPointAround(Point(0, 0), jitterRadius, generator) - center + Point(0.5f, 0.5f); - p = Point(float(x) / gridSize, float(y) / gridSize) + offs; - // generate a new point until it is within the boundaries - } while (!p.isInRectangle()); - - if (isCircle) - if (!p.isInCircle()) continue; - - samplePoints.push_back(p); - } - } - - return samplePoints; +template +std::vector generateJitteredGridPoints(uint32_t numPoints, + PRNG& generator, + bool isCircle = false, + float jitterRadius = 0.004f, + Point center = Point(0.5f, 0.5f)) { + std::vector samplePoints; + + samplePoints.reserve(numPoints); + + const uint32_t gridSize = uint32_t(sqrt(numPoints)); + + for (uint32_t x = 0; x != gridSize; x++) { + for (uint32_t y = 0; y != gridSize; y++) { + Point p; + do { + const Point offs = generateRandomPointAround(Point(0, 0), jitterRadius, generator) - center + Point(0.5f, 0.5f); + p = Point(float(x) / gridSize, float(y) / gridSize) + offs; + // generate a new point until it is within the boundaries + } while (!p.isInRectangle()); + + if (isCircle) + if (!p.isInCircle()) + continue; + + samplePoints.push_back(p); + } + } + + return samplePoints; } namespace { // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html -float radicalInverse_VdC(uint32_t bits) -{ - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(float(bits) * 2.3283064365386963e-10); // / 0x100000000 +float radicalInverse_VdC(uint32_t bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(float(bits) * 2.3283064365386963e-10); // / 0x100000000 } -Point hammersley2d(uint32_t i, uint32_t N) -{ - return Point(float(i)/float(N), radicalInverse_VdC(i)); +Point hammersley2d(uint32_t i, uint32_t N) { + return Point(float(i) / float(N), radicalInverse_VdC(i)); } } // namespace /** - Return a vector of generated points + Return a vector of generated points **/ -std::vector generateHammersleyPoints( - uint32_t numPoints -) -{ - std::vector samplePoints; +std::vector generateHammersleyPoints(uint32_t numPoints) { + std::vector samplePoints; - samplePoints.reserve(numPoints); + samplePoints.reserve(numPoints); - const uint32_t gridSize = uint32_t(sqrt(numPoints)); + const uint32_t gridSize = uint32_t(sqrt(numPoints)); - for (uint32_t i = 0; i != numPoints; i++) - { - Point p = hammersley2d(i, numPoints); + for (uint32_t i = 0; i != numPoints; i++) { + Point p = hammersley2d(i, numPoints); - samplePoints.push_back(p); - } - return samplePoints; + samplePoints.push_back(p); + } + return samplePoints; } } // namespace PoissonGenerator diff --git a/README.md b/README.md index 1121a45..197149f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ **Poisson Disk Points Generator** -(C) Sergey Kosarevsky, 2014-2023 +(C) Sergey Kosarevsky, 2014-2024 @corporateshark sk@linderdaum.com