【问题标题】:Filling a polygon填充多边形
【发布时间】:2015-08-04 00:53:56
【问题描述】:

我创建了这个函数来绘制一个具有 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
    }
}

如何用纯色填充它? 我不知道如何修改我的代码以绘制填充。

【问题讨论】:

  • 你用什么库来画画?这不是标准的 C。猜测 fill(x,y,color),但在不知道库的情况下,这是 100% 的猜测。
  • 这里不是要求代码增强的地方。尝试代码审查或论坛网站。
  • @John3136 不,这完全是标准 C 语言,并且不涉及任何库。我正在使用标准的、简单的、基本的、已知的绘图函数的简单实现来执行 VGA 编程。
  • @Olaf 增强功能 ??这是你第二次变得非常不可能。无法填充绘图是一个严重解决方案的问题。如果不是正确的填充方式,此功能将无法正常工作。它被视为无效代码。绝对不是为了代码审查。
  • 您在询问如何扩展代码以填充创建的多边形。这是一个扩展(“增强是错误的术语 - 承认)。看看你过去的帖子,你似乎把它用作教程网站,而不是自己做一些研究。我称之为“社交黑客”。(哦,显然,我非常“可靠”)。

标签: c graphics geometry drawing trigonometry


【解决方案1】:

填充形状的常用方法是查找多边形的边缘与每个 x 或每个 y 坐标相交的位置。通常使用y坐标,这样填充就可以用水平线来完成。 (在像 VGA 这样的帧缓冲设备上,水平线比垂直线快,因为它们使用连续的内存/帧缓冲地址。)

在这种情况下,

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 生成器测试了上述内容,并将绘制的线条与多边形进行了比较。似乎可以正常工作,但使用风险自负;没有保证。

对于一般形状,请使用您最喜欢的搜索引擎来查找“多边形填充”算法。例如,thisthisthisthis

【讨论】:

  • 它也适用于我。我现在将测量哪个更快并使用它。同样是的,水平分配应该稍微快一些,尽管我总是使用table[yOffset + iterator][xOffset] 来访问要在垂直上分配的每个像素。它比光栅扫描更快。
  • 你的方法就像我怀疑的那样快得多。
  • @Malina:请注意,要将其变为实心圆圈,您可以使用例如 const int x = (int)(0.5 + sqrt((double)(radius*radius) - (double)(y*y))); 并省略 pxpynxny 及其相关完全代码。这是因为y(x)=±sqrt(r*r-x*x)x(y)=±sqrt(r*r-y*y) 描述了与x=r*cos(t), y=r*sin(t) 相同的圆圈。或者只是循环x=-radius..radiusy=-radius..radius,并填充像素x+center_xy+center_y当且仅当x*x+y*y &lt;= radius*radius
【解决方案2】:

有两种不同的方式来实施解决方案:

扫描线

从位于顶部的坐标(最小 y 值)开始,继续逐行向下扫描(递增 y)并查看哪些边与线相交。

  • 对于凸多边形,您会找到 2 个点,(x1,y) 和 (x2,y)。只需在每条扫描线上的线之间画一条线。
  • 对于凹多边形,这也可以是 2 的倍数。只需在每对之间画线即可。一对之后,转到接下来的 2 个坐标。这将在该扫描线上创建填充/未填充/填充/未填充图案,从而解析为正确的整体解决方案。

如果您有自相交的多边形,您还会找到等于某些多边形点的坐标,您必须将它们过滤掉。之后,您应该处于上述情况之一。

如果您在扫描衬里期间过滤掉了多边形点,请不要忘记将它们也绘制出来。

洪水填充

另一种选择是使用洪水填充。它必须在每个像素的每一步执行更多的评估边界情况的工作,因此这往往会成为一个较慢的版本。这个想法是在多边形内选择一个种子点,并且基本上递归地逐个像素地向上/向下/向左/向右扩展,直到碰到边界。

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

【讨论】:

    【解决方案3】:

    最有效的解决方案是将正多边形分解为梯形(以及一两个三角形)。

    通过对称,顶点垂直对齐,很容易找到限制横坐标(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) 在两个顶点之间进行线性插值即可。

    填充水平运行有点复杂,因为顶点可能没有水平对齐并且梯形更多。

    【讨论】:

    • @malina:也是梯形分解。
    • 准确地说,这意味着您的答案更适合作为对他答案的评论。
    【解决方案4】:

    无论如何,在不依赖帮助(或任何尝试)的情况下,似乎我/自己又解决了/这个问题

    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 越高,填充的精度就越低。

    【讨论】: