填充多边形
Filling a polygon
我创建了这个函数来绘制一个具有 n 个顶点的简单多边形:
void polygon (int n)
{
double pI = 3.141592653589;
double area = min(width / 2, height / 2);
int X = 0, Y = area - 1;
double offset = Y;
int lastx, lasty;
double radius = sqrt(X * X + Y * Y);
double quadrant = atan2(Y, X);
int i;
for (i = 1; i <= n; i++)
{
lastx = X; lasty = Y;
quadrant = quadrant + pI * 2.0 / n;
X = round((double)radius * cos(quadrant));
Y = round((double)radius * sin(quadrant));
setpen((i * 255) / n, 0, 0, 0.0, 1); // r(interval) g b, a, size
moveto(offset + lastx, offset + lasty); // Moves line offset
lineto(offset + X, offset + Y); // Draws a line from offset
}
}
如何用纯色填充它?
我不知道如何修改我的代码以绘制它。
无论如何,似乎我/自己又解决了/这个问题,在不依赖帮助(或任何尝试)的情况下
void polygon (int n)
{
double pI = 3.141592653589;
double area = min(width / 2, height / 2);
int X = 0, Y = area - 1;
double offset = Y;
int lastx, lasty;
while(Y-->0) {
double radius = sqrt(X * X + Y * Y);
double quadrant = atan2(Y, X);
int i;
for (i = 1; i <= n; i++)
{
lastx = X; lasty = Y;
quadrant = quadrant + pI * 2.0 / n;
X = round((double)radius * cos(quadrant));
Y = round((double)radius * sin(quadrant));
//setpen((i * 255) / n, 0, 0, 0.0, 1);
setpen(255, 0, 0, 0.0, 1); // just red
moveto(offset + lastx, offset + lasty);
lineto(offset + X, offset + Y);
} }
}
如您所见,它不是很复杂,这意味着它可能也不是最有效的解决方案..但它已经足够接近了。
它减小半径并凭借其较小半径的较小版本来填充它。
在这种情况下,精度起着重要作用,n 越高,填充的精度就越低。
填充形状的常用方法是找到多边形的边缘与每个 x 或每个 y 坐标相交的位置。通常使用y坐标,这样就可以用水平线来填充了。 (在像 VGA 这样的帧缓冲设备上,水平线比垂直线快,因为它们使用连续的 memory/framebuffer 地址。)
就此而言,
void fill_regular_polygon(int center_x, int center_y, int vertices, int radius)
{
const double a = 2.0 * 3.14159265358979323846 / (double)vertices;
int i = 1;
int y, px, py, nx, ny;
if (vertices < 3 || radius < 1)
return;
px = 0;
py = -radius;
nx = (int)(0.5 + radius * sin(a));
ny = (int)(0.5 - radius * cos(a));
y = -radius;
while (y <= ny || ny > py) {
const int x = px + (nx - px) * (y - py) / (ny - py);
if (center_y + y >= 0 && center_y + y < height) {
if (center_x - x >= 0)
moveto(center_x - x, center_y + y);
else
moveto(0, center_y + y);
if (center_x + x < width)
lineto(center_x + x, center_y + y);
else
lineto(width - 1, center_y + y);
}
y++;
while (y > ny) {
if (nx < 0)
return;
i++;
px = nx;
py = ny;
nx = (int)(0.5 + radius * sin(a * (double)i));
ny = (int)(0.5 - radius * cos(a * (double)i));
}
}
}
请注意,我只用一个简单的 SVG 生成器测试了上面的内容,并将绘制的线条与多边形进行了比较。似乎工作正常,但使用风险自负;没有保证。
对于一般形状,使用您最喜欢的搜索引擎查找 "polygon filling" 算法。例如,this, this, this, and this.
最有效的解决方案是将正多边形分解为梯形(以及一个或两个三角形)。
通过对称性,顶点垂直对齐,很容易找到极限横坐标(X + R cos(2πn/N)
和X + R cos(2π(+1)N))
。
您还有纵坐标(Y + R sin(2πn/N)
和 Y + R sin(2π(+1)N)
),它足以在两个顶点之间线性插值 Y = Y0 + (Y1 - Y0) (X - X0) / (X1 - X0)
。
水平填充稍微复杂一些,因为顶点可能没有水平对齐,而且梯形较多。
有两种不同的方法来实施解决方案:
扫描线
从顶部的坐标(最小y值)开始,继续逐行向下扫描(递增y),看看哪些边与线相交。
- 对于凸多边形,您找到 2 个点,(x1,y) 和 (x2,y)。只需在每条扫描线上画一条线。
- 对于凹多边形,这也可以是 2 的倍数。只需在每对之间画线即可。一对后,转到下一个 2 个坐标。这将在该扫描线上创建一个 filled/unfilled/filled/unfilled 模式,解析为正确的整体解决方案。
如果你有自相交的多边形,你也会发现坐标等于一些多边形点,你必须过滤掉它们。之后,你应该属于上述情况之一。
如果你在扫描排线时过滤掉了多边形点,别忘了把它们也画出来。
填充
另一种选择是使用洪水填充。它必须在每个像素的每个步骤中执行更多的工作来评估边界情况,因此这往往会成为一个较慢的版本。这个想法是在多边形内选择一个种子点,并且基本上递归地逐个像素地扩展 up/down/left/right 直到你碰到边界。
算法要读写多边形的整个表面,不跨越自交点。对于大型表面,可能会有相当大的堆栈堆积(至少对于天真的实现),并且边界条件的灵活性降低是基于像素的(例如,当在多边形顶部绘制其他东西时,会涌入间隙)。从这个意义上说,这不是一个数学上正确的解决方案,但它适用于许多应用程序。
我创建了这个函数来绘制一个具有 n 个顶点的简单多边形:
void polygon (int n)
{
double pI = 3.141592653589;
double area = min(width / 2, height / 2);
int X = 0, Y = area - 1;
double offset = Y;
int lastx, lasty;
double radius = sqrt(X * X + Y * Y);
double quadrant = atan2(Y, X);
int i;
for (i = 1; i <= n; i++)
{
lastx = X; lasty = Y;
quadrant = quadrant + pI * 2.0 / n;
X = round((double)radius * cos(quadrant));
Y = round((double)radius * sin(quadrant));
setpen((i * 255) / n, 0, 0, 0.0, 1); // r(interval) g b, a, size
moveto(offset + lastx, offset + lasty); // Moves line offset
lineto(offset + X, offset + Y); // Draws a line from offset
}
}
如何用纯色填充它? 我不知道如何修改我的代码以绘制它。
无论如何,似乎我/自己又解决了/这个问题,在不依赖帮助(或任何尝试)的情况下
void polygon (int n)
{
double pI = 3.141592653589;
double area = min(width / 2, height / 2);
int X = 0, Y = area - 1;
double offset = Y;
int lastx, lasty;
while(Y-->0) {
double radius = sqrt(X * X + Y * Y);
double quadrant = atan2(Y, X);
int i;
for (i = 1; i <= n; i++)
{
lastx = X; lasty = Y;
quadrant = quadrant + pI * 2.0 / n;
X = round((double)radius * cos(quadrant));
Y = round((double)radius * sin(quadrant));
//setpen((i * 255) / n, 0, 0, 0.0, 1);
setpen(255, 0, 0, 0.0, 1); // just red
moveto(offset + lastx, offset + lasty);
lineto(offset + X, offset + Y);
} }
}
如您所见,它不是很复杂,这意味着它可能也不是最有效的解决方案..但它已经足够接近了。 它减小半径并凭借其较小半径的较小版本来填充它。 在这种情况下,精度起着重要作用,n 越高,填充的精度就越低。
填充形状的常用方法是找到多边形的边缘与每个 x 或每个 y 坐标相交的位置。通常使用y坐标,这样就可以用水平线来填充了。 (在像 VGA 这样的帧缓冲设备上,水平线比垂直线快,因为它们使用连续的 memory/framebuffer 地址。)
就此而言,
void fill_regular_polygon(int center_x, int center_y, int vertices, int radius)
{
const double a = 2.0 * 3.14159265358979323846 / (double)vertices;
int i = 1;
int y, px, py, nx, ny;
if (vertices < 3 || radius < 1)
return;
px = 0;
py = -radius;
nx = (int)(0.5 + radius * sin(a));
ny = (int)(0.5 - radius * cos(a));
y = -radius;
while (y <= ny || ny > py) {
const int x = px + (nx - px) * (y - py) / (ny - py);
if (center_y + y >= 0 && center_y + y < height) {
if (center_x - x >= 0)
moveto(center_x - x, center_y + y);
else
moveto(0, center_y + y);
if (center_x + x < width)
lineto(center_x + x, center_y + y);
else
lineto(width - 1, center_y + y);
}
y++;
while (y > ny) {
if (nx < 0)
return;
i++;
px = nx;
py = ny;
nx = (int)(0.5 + radius * sin(a * (double)i));
ny = (int)(0.5 - radius * cos(a * (double)i));
}
}
}
请注意,我只用一个简单的 SVG 生成器测试了上面的内容,并将绘制的线条与多边形进行了比较。似乎工作正常,但使用风险自负;没有保证。
对于一般形状,使用您最喜欢的搜索引擎查找 "polygon filling" 算法。例如,this, this, this, and this.
最有效的解决方案是将正多边形分解为梯形(以及一个或两个三角形)。
通过对称性,顶点垂直对齐,很容易找到极限横坐标(X + R cos(2πn/N)
和X + R cos(2π(+1)N))
。
您还有纵坐标(Y + R sin(2πn/N)
和 Y + R sin(2π(+1)N)
),它足以在两个顶点之间线性插值 Y = Y0 + (Y1 - Y0) (X - X0) / (X1 - X0)
。
水平填充稍微复杂一些,因为顶点可能没有水平对齐,而且梯形较多。
有两种不同的方法来实施解决方案:
扫描线
从顶部的坐标(最小y值)开始,继续逐行向下扫描(递增y),看看哪些边与线相交。
- 对于凸多边形,您找到 2 个点,(x1,y) 和 (x2,y)。只需在每条扫描线上画一条线。
- 对于凹多边形,这也可以是 2 的倍数。只需在每对之间画线即可。一对后,转到下一个 2 个坐标。这将在该扫描线上创建一个 filled/unfilled/filled/unfilled 模式,解析为正确的整体解决方案。
如果你有自相交的多边形,你也会发现坐标等于一些多边形点,你必须过滤掉它们。之后,你应该属于上述情况之一。
如果你在扫描排线时过滤掉了多边形点,别忘了把它们也画出来。
填充
另一种选择是使用洪水填充。它必须在每个像素的每个步骤中执行更多的工作来评估边界情况,因此这往往会成为一个较慢的版本。这个想法是在多边形内选择一个种子点,并且基本上递归地逐个像素地扩展 up/down/left/right 直到你碰到边界。
算法要读写多边形的整个表面,不跨越自交点。对于大型表面,可能会有相当大的堆栈堆积(至少对于天真的实现),并且边界条件的灵活性降低是基于像素的(例如,当在多边形顶部绘制其他东西时,会涌入间隙)。从这个意义上说,这不是一个数学上正确的解决方案,但它适用于许多应用程序。