具有特定最小距离的特定正方形(不是单位正方形)中的 2D 泊松盘采样

Posted

技术标签:

【中文标题】具有特定最小距离的特定正方形(不是单位正方形)中的 2D 泊松盘采样【英文标题】:2D Poisson-disk sampling in a specific square (not a unit square) with specific minimum distance 【发布时间】:2017-09-11 12:42:39 【问题描述】:

有什么办法可以修改泊松点生成器找到here。我需要使用textfile.txt 中点的坐标生成新的泊松点以改善分布。下面是单位平方泊松盘采样的c++代码。

泊松生成器.h:

#include <vector>
#include <random>
#include <stdint.h>
#include <time.h>


namespace PoissoGenerator



class DefaultPRNG

public:
    DefaultPRNG()
        : m_Gen(std::random_device()())
        , m_Dis(0.0f, 1.f)
    
        // prepare PRNG
        m_Gen.seed(time(nullptr));
    

    explicit DefaultPRNG(unsigned short seed)
        : m_Gen(seed)
        , m_Dis(0.0f, 1.f)
    

    

    double RandomDouble()
    
        return static_cast <double>(m_Dis(m_Gen));
    

    int RandomInt(int Max)
    
        std::uniform_int_distribution<> DisInt(0, Max);
        return DisInt(m_Gen);
    

private:
    std::mt19937 m_Gen;
    std::uniform_real_distribution<double> m_Dis;
;



struct sPoint

    sPoint()
    : x(0)
    , y(0)
    , m_valid(false)
    sPoint(double X, double Y)
        : x(X)
        , y(Y)
        , m_valid(true)
    double x;
    double y;
    bool m_valid;
    //
    bool IsInRectangle() const
    
        return x >= 0 && y >= 0 && x <= 1 && y <= 1;
    
    //
    bool IsInCircle() const
    
        double fx = x - 0.5f;
        double fy = y - 0.5f;
        return (fx*fx + fy*fy) <= 0.25f;

    


;

struct sGridPoint

    sGridPoint(int X, int Y)
    : x(X)
    , y(Y)
    
    int x;
    int y;
;

double GetDistance(const sPoint& P1, const sPoint& P2)

    return sqrt((P1.x - P2.x)*(P1.x - P2.x) + (P1.y - P2.y)*(P1.y - P2.y));


sGridPoint ImageToGrid(const sPoint& P, double CellSize)

    return sGridPoint((int)(P.x / CellSize), (int)(P.y / CellSize));


struct  sGrid

    sGrid(int W, int H, double CellSize)
    : m_W(W)
    , m_H(H)
    , m_CellSize(CellSize)
    
        m_Grid.resize((m_H));

        for (auto i = m_Grid.begin(); i != m_Grid.end(); i++) i->resize(m_W); 
    

    void Insert(const sPoint& P)
    
        sGridPoint G = ImageToGrid(P, m_CellSize);
        m_Grid[G.x][G.y] = P;
    
    bool IsInNeighbourhood(sPoint Point, double MinDist, double CellSize)
    
        sGridPoint G = ImageToGrid(Point, CellSize);

        //number of adjacent cell 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 < m_W && j >= 0 && j < m_H)
                
                    sPoint P = m_Grid[i][j];
                    if (P.m_valid && GetDistance(P, Point) < MinDist) return true; 
                
            
        

        return false;
    

private:
    int m_H;
    int m_W;
    double m_CellSize;

    std::vector< std::vector< sPoint> > m_Grid;
;

template <typename PRNG>
sPoint PopRandom(std::vector<sPoint>& Points, PRNG& Generator)

    const int Idx = Generator.RandomInt(Points.size() - 1);
    const sPoint P = Points[Idx];
    Points.erase(Points.begin() + Idx);
    return P;


template <typename PRNG>
sPoint GenerateRandomPointAround(const sPoint& P, double MinDist, PRNG& Generator)


    // Start with non-uniform distribution
    double R1 = Generator.RandomDouble();
    double R2 = Generator.RandomDouble();

    // radius should be between MinDist and 2 * MinDist
    double Radius = MinDist * (R1 + 1.0f);

    //random angle
    double Angle = 2 * 3.141592653589f * R2;

    // the new point is generated around the point (x, y)
    double X = P.x + Radius * cos(Angle);
    double Y = P.y + Radius * sin(Angle);

    return sPoint(X, Y);


// 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

template <typename PRNG = DefaultPRNG>
std::vector<sPoint> GeneratePoissonPoints(rsize_t NumPoints, PRNG& Generator, int NewPointsCount = 30,
    bool Circle = true, double MinDist = -1.0f)

    if (MinDist < 0.0f)
    
        MinDist = sqrt(double(NumPoints)) / double(NumPoints);
    

    std::vector <sPoint> SamplePoints;
    std::vector <sPoint> ProcessList;

    // create the grid
    double CellSize = MinDist / sqrt(2.0f);

    int GridW = (int)(ceil)(1.0f / CellSize);
    int GridH = (int)(ceil)(1.0f / CellSize);

    sGrid Grid(GridW, GridH, CellSize);



    sPoint FirstPoint;
    do
    
        FirstPoint = sPoint(Generator.RandomDouble(), Generator.RandomDouble());

     while (!(Circle ? FirstPoint.IsInCircle() : FirstPoint.IsInRectangle()));

    //Update containers

    ProcessList.push_back(FirstPoint);
    SamplePoints.push_back(FirstPoint);
    Grid.Insert(FirstPoint);


    // 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() % 100 == 0) std::cout << ".";
             #endif // POISSON_PROGRESS_INDICATOR

        sPoint Point = PopRandom<PRNG>(ProcessList, Generator);

        for (int i = 0; i < NewPointsCount; i++)
        
            sPoint NewPoint = GenerateRandomPointAround(Point, MinDist, Generator);
            bool Fits = Circle ? NewPoint.IsInCircle() : NewPoint.IsInRectangle();

            if (Fits && !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;
        #endif // POISSON_PROGRESS_INDICATOR

    return SamplePoints;


主程序是:

泊松.cpp

 #include "stdafx.h"
 #include <vector>
 #include <iostream>
 #include <fstream>
 #include <memory.h>


 #define POISSON_PROGRESS_INDICATOR 1
 #include "PoissonGenerator.h"


  const int   NumPoints = 20000;    // minimal number of points to generate


int main()


 PoissonGenerator::DefaultPRNG PRNG;

 const auto Points =
 PoissonGenerator::GeneratePoissonPoints(NumPoints,PRNG);


std::ofstream File("Poisson.txt", std::ios::out);

File << "NumPoints = " << Points.size() << std::endl;
for (const auto& p : Points)

File << "       " << p.x << "       " << p.y << std::endl;


system("PAUSE");
return 0;
  

【问题讨论】:

你应该试试the code review stack exchange site @akshayk07 代码审查专门用于改进工作代码 【参考方案1】:

假设您在空间[0,1] x [0,1] 中有一个点,形式为std::pair&lt;double, double&gt;,但希望在空间[x,y] x [w,z] 中有点。

函数对象

struct ProjectTo 
    double x, y, w, z;
    std::pair<double, double> operator(std::pair<double, double> in)
    
        return std::make_pair(in.first * (y - x) + x, in.second * (z - w) + w);
    
;

会将这样的输入点转换为所需的输出点。

进一步假设您有一个std::vector&lt;std::pair&lt;double, double&gt;&gt; points,全部来自输入分布。

std::copy(points.begin(), points.end(), points.begin(), ProjectTo x, y, w, z );

现在您在输出空间中有一个点向量。

【讨论】:

以上是关于具有特定最小距离的特定正方形(不是单位正方形)中的 2D 泊松盘采样的主要内容,如果未能解决你的问题,请参考以下文章

算法将点放入具有最大最小距离的正方形

如何跟踪具有特定颜色的矩形

如何有效地找到具有特定大小的开放矩形?

用给定的坐标和以米为单位的距离计算新坐标

poj3301--Texas Trip(最小正方形覆盖)

数组中的距离