在没有重复索引的情况下循环遍历圆形数组
Loop through a array in circle shape without repeat indexes
我需要遍历一个小半径圆弧形的数组(比如一个像素一个像素地画一个圆),但是我试过的所有算法都会检查数组的重复索引(它有相同的 x 和 y几次)。
我的半径为 3,圆圈形式为 28 个元素(未填充),但算法迭代 360 次。我可以在做某事之前检查 x 或 y 是否改变,但它很蹩脚。
我现在的代码:
for (int radius = 1; radius < 6; radius++)
{
for (double i = 0; i < 360; i += 1)
{
double angle = i * System.Math.PI / 180;
int x = (int)(radius * System.Math.Cos(angle)) + centerX;
int y = (int)(radius * System.Math.Sin(angle)) + centerY;
// do something
// if (array[x, y]) ....
}
}
PS: 我不能使用中点圆,因为我需要从 2 开始增加半径直到 6,而不是每个索引都得到,因为他的圆不是真实的(根据三角学)
编辑:
我真正需要的是从中心开始逐边扫描整个圆。
360步(获取所有坐标):
Full scan
for (int radius = 2; radius <= 7; radius++)
{
for (double i = 0; i <= 360; i += 1)
{
double angle = i * System.Math.PI / 180;
int x = (int)(radius * System.Math.Cos(angle));
int y = (int)(radius * System.Math.Sin(angle));
print(x, y, "X");
}
}
使用中点圆或其他算法跳过步骤(缺少坐标):
Midpoint Circle Algorithm
for (int radius = 2; radius <= 7; radius++)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y)
{
print(x, y, "X");
print(y, x, "X");
print(-y, x, "X");
print(-y, x, "X");
print(-x, y, "X");
print(-x, -y, "X");
print(-y, -x, "X");
print(y, -x, "X");
print(x, -y, "X");
y += 1;
err += 1 + 2 * y;
if (2 * (err - x) + 1 > 0)
{
x -= 1;
err += 1 - 2 * x;
}
}
}
这里有两种算法思想:一种是光栅化一个圆。 OP 代码在这方面提供了几个改进机会:(a) 无需对整个 360 度圆进行采样,意识到圆在两个轴上是对称的。 (x,y) 可以在其他三个象限中反映为 (-x,y)、(-x,-y) 和 (x,-y)。 (b) 循环上的步骤应与曲率有关。一个简单的启发式方法是使用半径作为步长。所以...
let step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
add (x,y) to results
reflect into quadrants 2,3,4 and add to results
}
通过这几项改进,您可能不再关心生成的重复样本。如果你仍然这样做,那么第二个想法,独立于圆圈,是如何散列一对整数。这里有一篇很好的文章:Mapping two integers to one, in a unique and deterministic way.
简而言之,我们从保证映射唯一的 x,y 对计算一个 int,然后检查是否有重复...
cantor(x, y) = 1/2(x + y)(x + y + 1) + y
这仅适用于 x,y 的正值,这正是您所需要的,因为我们只在第一象限中计算(然后反映)。对于每一对,检查它们是否是唯一的
let s = an empty set
int step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
generate (x,y)
let c = cantor(x,y)
if (not(s contains c)) {
add (x,y) to results
reflect into quadrants 2,3,4 and add to results
add c to s
}
}
知道了!
它不漂亮,但对我有用。
int maxRadius = 7;
for (int radius = 1; radius <= maxRadius; radius++)
{
x = position.X - radius;
y = position.Y - radius;
x2 = position.X + radius;
y2 = position.Y + radius;
for (int i = 0; i <= radius * 2; i++)
{
if (InCircle(position.X, position.Y, x + i, y, maxRadius)) // Top X
myArray[position, x + i, y]; // check array
if (InCircle(position.X, position.Y, x + i, y2, maxRadius)) // Bottom X
myArray[position, x + i, y2]; // check array
if (i > 0 && i < radius * 2)
{
if (InCircle(position.X, position.Y, x, y + i, maxRadius)) // Left Y
myArray[position, x, y + i]; // check array
if (InCircle(position.X, position.Y, x2, y + i, maxRadius)) // Right Y
myArray[position, x2, y + i]; // check array
}
}
}
public static bool InCircle(int originX, int originY, int x, int y, int radius)
{
int dx = Math.Abs(x - originX);
if (dx > radius) return false;
int dy = Math.Abs(y - originY);
if (dy > radius) return false;
if (dx + dy <= radius) return true;
return (dx * dx + dy * dy <= radius * radius);
}
我需要遍历一个小半径圆弧形的数组(比如一个像素一个像素地画一个圆),但是我试过的所有算法都会检查数组的重复索引(它有相同的 x 和 y几次)。 我的半径为 3,圆圈形式为 28 个元素(未填充),但算法迭代 360 次。我可以在做某事之前检查 x 或 y 是否改变,但它很蹩脚。
我现在的代码:
for (int radius = 1; radius < 6; radius++)
{
for (double i = 0; i < 360; i += 1)
{
double angle = i * System.Math.PI / 180;
int x = (int)(radius * System.Math.Cos(angle)) + centerX;
int y = (int)(radius * System.Math.Sin(angle)) + centerY;
// do something
// if (array[x, y]) ....
}
}
PS: 我不能使用中点圆,因为我需要从 2 开始增加半径直到 6,而不是每个索引都得到,因为他的圆不是真实的(根据三角学)
编辑: 我真正需要的是从中心开始逐边扫描整个圆。
360步(获取所有坐标):
Full scan
for (int radius = 2; radius <= 7; radius++)
{
for (double i = 0; i <= 360; i += 1)
{
double angle = i * System.Math.PI / 180;
int x = (int)(radius * System.Math.Cos(angle));
int y = (int)(radius * System.Math.Sin(angle));
print(x, y, "X");
}
}
使用中点圆或其他算法跳过步骤(缺少坐标):
Midpoint Circle Algorithm
for (int radius = 2; radius <= 7; radius++)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y)
{
print(x, y, "X");
print(y, x, "X");
print(-y, x, "X");
print(-y, x, "X");
print(-x, y, "X");
print(-x, -y, "X");
print(-y, -x, "X");
print(y, -x, "X");
print(x, -y, "X");
y += 1;
err += 1 + 2 * y;
if (2 * (err - x) + 1 > 0)
{
x -= 1;
err += 1 - 2 * x;
}
}
}
这里有两种算法思想:一种是光栅化一个圆。 OP 代码在这方面提供了几个改进机会:(a) 无需对整个 360 度圆进行采样,意识到圆在两个轴上是对称的。 (x,y) 可以在其他三个象限中反映为 (-x,y)、(-x,-y) 和 (x,-y)。 (b) 循环上的步骤应与曲率有关。一个简单的启发式方法是使用半径作为步长。所以...
let step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
add (x,y) to results
reflect into quadrants 2,3,4 and add to results
}
通过这几项改进,您可能不再关心生成的重复样本。如果你仍然这样做,那么第二个想法,独立于圆圈,是如何散列一对整数。这里有一篇很好的文章:Mapping two integers to one, in a unique and deterministic way.
简而言之,我们从保证映射唯一的 x,y 对计算一个 int,然后检查是否有重复...
cantor(x, y) = 1/2(x + y)(x + y + 1) + y
这仅适用于 x,y 的正值,这正是您所需要的,因为我们只在第一象限中计算(然后反映)。对于每一对,检查它们是否是唯一的
let s = an empty set
int step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
generate (x,y)
let c = cantor(x,y)
if (not(s contains c)) {
add (x,y) to results
reflect into quadrants 2,3,4 and add to results
add c to s
}
}
知道了!
它不漂亮,但对我有用。
int maxRadius = 7;
for (int radius = 1; radius <= maxRadius; radius++)
{
x = position.X - radius;
y = position.Y - radius;
x2 = position.X + radius;
y2 = position.Y + radius;
for (int i = 0; i <= radius * 2; i++)
{
if (InCircle(position.X, position.Y, x + i, y, maxRadius)) // Top X
myArray[position, x + i, y]; // check array
if (InCircle(position.X, position.Y, x + i, y2, maxRadius)) // Bottom X
myArray[position, x + i, y2]; // check array
if (i > 0 && i < radius * 2)
{
if (InCircle(position.X, position.Y, x, y + i, maxRadius)) // Left Y
myArray[position, x, y + i]; // check array
if (InCircle(position.X, position.Y, x2, y + i, maxRadius)) // Right Y
myArray[position, x2, y + i]; // check array
}
}
}
public static bool InCircle(int originX, int originY, int x, int y, int radius)
{
int dx = Math.Abs(x - originX);
if (dx > radius) return false;
int dy = Math.Abs(y - originY);
if (dy > radius) return false;
if (dx + dy <= radius) return true;
return (dx * dx + dy * dy <= radius * radius);
}