泊松圆盘采样(Poisson Disk Sampling)代码实现

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

参考资料
https://www.jianshu.com/p/4556ccad58d9
https://blog.csdn.net/qq_21476953/article/details/118440245
https://www.bilibili.com/video/av418836005

作用&性质

作用:可以在一个区域内生成均匀的采样点。
性质:做任意2点距离大于r。以每个点为圆作半径为r的圆,这些圆的并集可以基本覆盖到目标区域。

代码实现


class PoissonDisk {
    typedef pair<double, double> P;

    bool inCircle(P a, P b, double r) {
        a.first -= b.first;
        a.second -= b.second;
        return a.first*a.first+a.second*a.second< r*r;
    }

    bool isValid(double h, double w, double r,double cellSize,  P newP, vector<vector<int>> &grid, vector<P> &points) {
        if(newP.first<0 || newP.first>h || newP.second<0 || newP.second>w) return false; //出界

        // 计算出有可能发生碰撞五乘五格子的上下左右边界
        int up = max(0, int(newP.first/cellSize)-2);
        int down = min(int(grid.size()-1), int(newP.first/cellSize)+2);

        int left = max(0, int(newP.second/cellSize)-2);
        int right = min(int(grid[0].size()-1), int(newP.second/cellSize)+2);

        for(int i=up;i<=down;++i) {
            for(int j=left;j<=right;++j) {
                if(grid[i][j]<0)continue;
                if(inCircle(newP, points[grid[i][j]], r)) return false;
            }
        }

        return true;
    }
public:
    vector<P> sampling(double h, double w, double r, int numSamplesBeforeReject=30) {
        srandom(NULL);
        double cellSize = r/sqrt(2);
        vector<P> points;
        vector<P> spawnPoints;

        vector<vector<int>> grid(ceil(h/cellSize), vector<int>(ceil(w/cellSize),-1)); // 存储格子中是否有生成点,如果有就是大于0,默认-1;

        // 加入一个种子点
        spawnPoints.push_back({h/2, w/2});

        while(!spawnPoints.empty()) {
            bool candidateAccepted = false;
            int spawnIndex = rand()%spawnPoints.size();

            // 每次按种子为中心,从半径r到2r的地带随机选取一点。
            // 并判断是否可行,可行则退出,并加入到候选生成种子,否继续,直到采样次数用完。
            for(int i=0;i<numSamplesBeforeReject;++i) {
                double angle = rand()%360*1.0/360*2*M_PI;
                // cout<<"pi:"<<angle/M_2_PI<<endl;
                auto dir = P(sin(angle), cos(angle));

                auto newP = spawnPoints[spawnIndex];
                double rl = rand()%1000*1.0/1000*r;
                newP.first+=dir.first*(rl+r);
                newP.second+=dir.second*(rl+r);
                // cout<<newP.first<<"|a|"<<newP.second<<endl;

                bool res=isValid(h, w, r,cellSize, newP, grid, points);
                if(res) {
                    candidateAccepted = true;
                    grid[newP.first/cellSize][newP.second/cellSize] = points.size();
                    points.push_back(newP);
                    spawnPoints.push_back(newP);
                    break;
                }
            }

            // 采样次数用完,没有得到可行结果,说明该种不可用,直接删除。
            if(!candidateAccepted){
                if(spawnIndex!=spawnPoints.size()-1)
                    spawnPoints[spawnIndex] = spawnPoints[spawnPoints.size()-1];

                spawnPoints.pop_back();
            }
            // cout<<points.size()<<"|"<<spawnPoints.size()<<endl;
        }

        return points;
    }
};

效果


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值