如何迭代栅格网格中的环?

时间:2013-05-15 17:25:49

标签: c++ algorithm loops grid geometry

我们处于二维空间的任何给定位置。该空间分为二次单元格。我想按照它们与我们的距离顺序遍历给定距离内的所有单元格。例如,这可能发生在尺寸增大的环中。距离几乎相等的细胞顺序无关紧要。

如何以距离到给定位置的顺序循环这些单元格?

1 个答案:

答案 0 :(得分:2)

如果对整数网格坐标的简化是正确的(正如你在“几乎相等的距离”上所说的那样),你可以通过下面的代码逐渐增加距离逐个细胞。如果您的起点不同于(0,0),则只需将其添加到每个生成的点。关键的想法是:

  1. (d,0)开始,我们以d的逆时针方向迭代地寻找具有“相似”距离(整数部分= (0,0))的相邻点,直到我们到达起点
  2. 每个点都有(除了我们来自的邻居)最多一个直接邻居(通过边缘)具有“相似”距离。如果没有直接邻居,则只有一个对角线邻居具有“相似”距离(除了前一个邻居之外)。
  3. 到下一个邻居的向量“几乎”与到原点的向量正交。
  4. 以下是代码:

    #include <cmath>
    #include <vector>
    
    template <typename T> int sgn(T val) {
        return (T(0) < val) - (val < T(0));
    }
    
    int dist(double dx, double dy)
    {
        return (int)sqrt(dx*dx + dy*dy);
    }
    
    typedef std::pair<int,int> TPoint;
    typedef std::vector<TPoint> TPoints;
    
    void generateNeighbourRing(int d, TPoints& ring)
    {
        int dx = d;
        int dy = 0;
        do
        {
            ring.push_back(TPoint(dx,dy));
            int nx = -sgn(dy);
            int ny = sgn(dx);
            if (nx != 0 && dist(dx+nx, dy) == d)
                dx += nx;
            else if (ny != 0 && dist(dx, dy+ny) == d)
                dy += ny;
            else
            {
                dx += nx;
                dy += ny;
            }
        } while (dx != d || dy != 0);
    }
    
    int main()
    {
        TPoints points;
        const int d_max = 4;
        for (int d = 0; d <= d_max; ++d)
            generateNeighbourRing(d, points);
        printf("spiral for dmax=%d (%d points):\n", d_max, points.size());
        for (unsigned int i=0; i<points.size(); ++i)
            printf(" (%d,%d),", points[i].first, points[i].second);
        printf("\n");
    }
    

    正确性的合理性:

    让我们首先看一下图像,其中与中心单元格距离相等的单元格具有相同的颜色(一旦距离被截断,一旦距离被舍入):

    distance truncated - - - - - - distance rounded

    使用(dx,dy),我们以相等的距离迭代环的单元格; (nx,ny)是一种法向量,沿每个半轴和每个象限内是常数:

    enter image description here

    每个区域的黑色箭头显示(nx,ny);蓝色箭头表示首先搜索具有相等距离的(直接)邻居的方向。

    接下来,我们需要考虑具有相等距离的邻居的哪些配置是可能的。由于象限是旋转对称的,因此足以观察第一象限。两个直接邻居之间到中心小区的距离可以相差至多1;对角线朝向或远离中心,距离相差1或2:

    enter image description here。 。 。 enter image description here

    (这源于直接的不等式。)重要的结论是不能发生相等距离的2x2块;最多4个邻居可以有相同的距离形成“之字形”:

    no 2x2 block of equal distances。 。 。 max 4 neighbours

    另一个重要结论是每个细胞至少有2个具有相等距离的邻居,同样仅在某些配置中。由此可以推断,如果沿着蓝色箭头的邻居具有不同的距离,则沿着黑色箭头的邻居具有相同的距离。因此,放入变量ring的所有点都具有距离d。 (请注意,在第二个else - 分支中,不会检查距离。)

    接下来我们将终止do ... while - 循环。注意,对于每次迭代,线(0,0) - (dx,dy)与正x轴之间的角度增加。由于距离保持不变,我们最终将离开当前象限并进入下一个象限。因为沿着半轴,每一个距离都会出现,一旦我们最终到达起点(d,0)。

    由此可以得出两点都没有得到两点:在generateNeighbourRing的一次调用中,再次开始do ... while循环的迭代,同样的点再次导致无限循环,因此与终止。在generateNeighbourRing的几次调用中,由于到中心单元的距离不同,所有点都不同。

    查看具有相同距离的邻居的可能配置,还可以显示将收集给定距离d的所有点。