我们处于二维空间中的任何给定位置。该空间被划分为二次单元。我想遍历给定距离内的所有单元格,按照它们与我们的距离顺序。例如,这可能发生在尺寸增加的环中。距离几乎相等的单元格的顺序无关紧要。
如何按到给定位置的距离顺序遍历这些单元格?
如果简化为整数网格坐标是可以的(正如您对“几乎相等距离”的评论所暗示的那样),您可以使用下面的代码随着距离的增加逐个遍历单元格。如果您的起点与 (0,0) 不同,则只需将其添加到每个生成的点。关键思想是:
(d,0)
迭代地寻找具有“相似”距离(整数部分 = )的相邻点,直到我们到达起点。d
(0,0)
这是代码:
#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");
}
让我们首先看一下图像,其中与中心单元距离相等的单元具有相同的颜色(一旦距离被截断,一旦距离被四舍五入):
-- -- -- -- -- --
随着(dx,dy)
我们迭代等距离的环的单元格;(nx,ny)
是一种法向量,沿每个半轴和每个象限内是恒定的:
黑色箭头显示(nx,ny)
每个区域;蓝色箭头表示首先搜索具有相等距离的(直接)邻居的方向。
接下来我们需要考虑哪些具有相等距离的邻居配置是可能的。由于象限是旋转对称的,因此看第一象限就足够了。两个直接邻居之间到中心像元的距离最多相差 1;斜向或远离中心,距离相差 1 或 2:
. . .
(这是从直接的不等式得出的。)重要的结论是不可能发生 2x2 等距离的块;最多 4 个邻居可以有相同的距离,形成一个“之字形”:
. . .
另一个重要的结论是每个单元至少有两个距离相等的邻居,同样仅在某些配置中。由此可以推断,如果沿蓝色箭头的邻居具有不同的距离,则沿黑色箭头的邻居具有相同的距离。因此,放入变量中的所有点ring
都有距离d
。(请注意,在第二个else
分支中,未检查距离。)
接下来我们去终止do ... while
-loop。请注意,随着每次迭代,线 (0,0)-(dx,dy) 与正 x 轴之间的角度会增加。由于距离保持不变,我们最终将离开当前象限并进入下一个象限。而且由于沿着半轴,每个距离恰好出现一次,我们最终将到达起点 (d,0)。
由此还得出,没有点被取两次:在一次调用中,再次使用相同的点generateNeighbourRing
开始循环迭代do ... while
将导致无限循环,因此与终止相矛盾。generateNeighbourRing
由于到中心单元的距离不同,所有点的多次调用都不同。
查看具有相同距离的邻居的可能配置也可以表明d
将收集具有给定距离的所有点。