填充多边形

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 直到你碰到边界。

算法要读写多边形的整个表面,不跨越自交点。对于大型表面,可能会有相当大的堆栈堆积(至少对于天真的实现),并且边界条件的灵活性降低是基于像素的(例如,当在多边形顶部绘制其他东西时,会涌入间隙)。从这个意义上说,这不是一个数学上正确的解决方案,但它适用于许多应用程序。