让我们把它变成一个简单的点序列。
我们知道我们正在朝着四个方向之一前进。
var steps = new (int dx, int dy)[] { (1, 0), (0, 1), (-1, 0), (0, -1) };
为了创建螺旋,我们先向第一个方向移动一次,然后向第二个方向移动一次;然后接下来的两个两次,然后接下来的两个三次,然后接下来的两个四次,等等。如果我们到达列表的末尾,我们就会循环回到开头。所以如果我们从n = 0
然后我们重复每个方向n / 2 + 1
次(知道这是整数数学)。
这是我的Spiral
生成器方法:
public IEnumerable<Point> Spiral(int x, int y)
{
yield return new Point(x, y);
var steps = new(int dx, int dy)[] { (1, 0), (0, 1), (-1, 0), (0, -1) };
var i = 0;
var n = 0;
while (true)
{
for (var j = 0; j < n / 2 + 1; j++)
{
var (sx, sy) = steps[i];
x += sx;
y += sy;
yield return new Point(x, y);
}
if (++i >= steps.Length)
i = 0;
n++;
}
}
前 50 分(即Spiral(0, 0).Take(50)
)那么是这样的:
(0, 0), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, - 1), (1, -1), (2, -1), (2, 0), (2, 1), (2, 2), (1, 2), (0, 2), (-1 , 2), (-2, 2), (-2, 1), (-2, 0), (-2, -1), (-2, -2), (-1, -2), ( 0, -2), (1, -2), (2, -2), (3, -2), (3, -1), (3, 0), (3, 1), (3, 2) ), (3, 3), (2, 3), (1, 3), (0, 3), (-1, 3), (-2, 3), (-3, 3), (-3 , 2), (-3, 1), (-3, 0), (-3, -1), (-3, -2), (-3, -3), (-2, -3), (-1, -3), (0, -3), (1, -3), (2, -3), (3, -3), (4, -3)
现在,生成您想要的螺旋非常容易。
如果我假设您从屏幕中间开始,那么就是这样:
IEnumerable<Point> query =
Spiral(1920 / 2, 1080 / 2)
.Where(z => z.X >= 0 && z.X < 1920)
.Where(z => z.Y >= 0 && z.Y < 1080)
.Take(1920 * 1080);
如果我们想使用您问题中给出的较小屏幕进行验证,则如下所示:
IEnumerable<Point> query =
Spiral(0, 0)
.Where(z => z.Y >= -1 && z.Y <= 1)
.Where(z => z.X >= -2 && z.X <= 2)
.Take(5 * 3);
这给出:
(0, 0), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, - 1), (1, -1), (2, -1), (2, 0), (2, 1), (-2, 1), (-2, 0), (-2, -1)
这里它被封装在一个方法中:
public static void Spiral()
{
IEnumerable<Point> SpiralPoints(int x, int y)
{
yield return new Point(x, y);
var steps = new(int dx, int dy)[] { (1, 0), (0, 1), (-1, 0), (0, -1) };
var i = 0;
var n = 0;
while (true)
{
for (var j = 0; j < n / 2 + 1; j++)
{
var (sx, sy) = steps[i];
x += sx;
y += sy;
yield return new Point(x, y);
}
if (++i >= steps.Length)
i = 0;
n++;
}
}
var w = Screen.PrimaryScreen.Bounds.Width;
var h = Screen.PrimaryScreen.Bounds.Height;
var l = Screen.PrimaryScreen.Bounds.Left;
var r = Screen.PrimaryScreen.Bounds.Right;
var t = Screen.PrimaryScreen.Bounds.Top;
var b = Screen.PrimaryScreen.Bounds.Bottom;
foreach (Point point in SpiralPoints(w / 2, h / 2)
.Where(z => z.X >= l && z.X < r)
.Where(z => z.Y >= t && z.Y < b)
.Take(w * h))
{
/* Do Stuff With Each Point Here */
}
}