填充三角形算法

Filling a Triangle Algorithm

本文关键字:算法 三角形 填充      更新时间:2023-10-16

我目前正在尝试完成一个填充绘制三角形的算法。我一直在这样做的方法是遍历形状并绘制单线。我有一个几乎完美的算法,除了一个小问题。当我有水平面时,填充失败。

这是我目前的填充算法。我应该注意,称为 origin、coor2 和 coor3 的多维数组表示为我的三角形的顶点(origin[0][0] = x of origin, origin[ 0][1 ]= y of origin)。坐标是典型的窗口,(0,0) 位于左上角。此外,gc 只是我需要在窗口中绘制的内容。

void triangle::drawFilled(GraphicsContext* gc)
{
// color
gc->setColor(colorRGB);
// algorithm variables
double ax = origin[0][0];
double bx = coor2[0][0];
double cx = coor3[0][0];
double ay = origin[1][0];
double by = coor2[1][0];
double cy = coor3[1][0];
// sort vertices by y
if (ay > by)
{
    std::swap(ay, by);
    std::swap(ax, bx);
}
if (ay > cy)
{
    std::swap(ay, cy);
    std::swap(ax, cx);
}
if (by > cy)
{
    std::swap(by, cy);
    std::swap(bx, cx);
}
// define more algorithm variables
double dx1 = (cx-ax)/(cy-ay);
double dx2 = (bx-ax)/(by-ay);
double dx3 = (cx-bx)/(cy-by);
double x1 = ax;
double x2 = ax;
// loop through coordinates
for(int y = ay; y < by; y++)
{
    gc->drawLine(x1,y,x2,y);
    x1 += dx1;
    x2 += dx2;
}
// loop through coordinates
for(int y = by; y < cy; y++)
{
    gc->drawLine(x1,y,x2,y);
    x1 += dx1;
    x2 += dx3;
}
}

这是没有水平边时的结果示例

这是当有一个水平面时

请注意轮廓和填充如何不对齐。

我意识到问题可能在于 y 顶点的排序,因此不考虑 x。我可以蛮力处理水平和垂直边缘的所有情况,但这似乎效率很低。我宁愿学习如何解决我的困境,而不是试图解决它。

问题是您的代码取决于第一个循环设置的副作用,x2 bx 。 当dx2是无穷大时,无论如何都行不通,甚至不尝试。

因此,在第一个循环之后,只需设置x2=bx;

大多数时候,额外的步骤是多余的,但它是微不足道的,当顶部是水平的时,它是必要的。

我相信你不再需要这个,但你的问题相对简单。 对于三角形顶部平坦而底部平坦的条件,您需要条件语句

if(TopIsFlat){
}else if(BottomIsFlat){
}else{
    pre existing code
}

对于顶部和底部,您必须弄清楚您将从哪个 X 坐标开始。 并在必要时交换 如果您交换,您还必须切换斜坡。

还要打印点之间的坡度,以确保坡度正确;

使用结构的示例

struct Vector2D{
    int x, y,
}
struct Triangle{
   Vector2D p[3];
}
void Fill(Triangle ySwap){
    
    if(ySwap.p[0].y > ySwap.p[1].y) std::swap(ySwap.p[0], ySwap.p[1]);
    if(ySwap.p[1].y > ySwap.p[2].y) std::swap(ySwap.p[1], ySwap.p[2]);
    if(ySwap.p[0].y > ySwap.p[1].y) std::swap(ySwap.p[0], ySwap.p[1]);
    
    float slope1 = ((float)(ySwap.p[1].x-ySwap.p[0].x)/(ySwap.p[1].y-ySwap.p[0].y));
    float slope2 = ((float)(ySwap.p[2].x-ySwap.p[1].x)/(ySwap.p[2].y-ySwap.p[1].y));
    float slope3 = ((float)(ySwap.p[2].x-ySwap.p[0].x)/(ySwap.p[2].y-ySwap.p[0].y));
    
    float x1; float x2;
    if(ySwap.p[0].y == ySwap.p[1].y){
        
        if(ySwap.p[1].x > ySwap.p[0].x){
            x1 = ySwap.p[0].x;
            x2 = ySwap.p[1].x;
        }else{
            x1 = ySwap.p[1].x;
            x2 = ySwap.p[0].x;
            std::swap(slope2, slope3);
        }
        for(int y = ySwap.p[0].y; y < ySwap.p[2].y; y++){
            DrawLine(x1, y, x2, y, 'T');
            x1 += slope3;
            x2 += slope2;
        }
        
    }else if(ySwap.p[1].y == ySwap.p[2].y){
        
        if(ySwap.p[2].x > ySwap.p[1].x){
            x1 = ySwap.p[0].x;
            x2 = ySwap.p[2].x;
        }else{
            x1 = ySwap.p[2].x;
            x2 = ySwap.p[0].x;
            std::swap(slope1, slope3);
        }
        for(int y = ySwap.p[0].y; y < ySwap.p[2].y; y++){
            DrawLine(x1, y, x2, y, 'B');
            x1 += slope3;
            x2 += slope1;
        }
        
    }else{
        
        x1 = ySwap.p[0].x;
        x2 = ySwap.p[0].x;
        
        for(int y = ySwap.p[0].y ; y < ySwap.p[1].y; y++){
            DrawLine(x1, y, x2, y, '+');
            x1 += slope3;
            x2 += slope1;
        }
        
        for(int y = ySwap.p[1].y; y < ySwap.p[2].y; y++){
            DrawLine(x1, y, x2, y, '-');
            x1 += slope3;
            x2 += slope2;
        }
    }
    
    screen[ySwap.p[0].x][ySwap.p[0].y] = '*';
    screen[ySwap.p[1].x][ySwap.p[1].y] = '*';
    screen[ySwap.p[2].x][ySwap.p[2].y] = '*';
    
    DrawTriangle(ySwap.p[0].x, ySwap.p[0].y, ySwap.p[1].x, ySwap.p[1].y, ySwap.p[2].x, ySwap.p[2].y, '*');
}

您的代码实际上允许我完成我的三角形绘制函数,该函数有一些错误,这应该可以更好地工作,并且此代码将允许您看到每个角边缘和不同的三角形拆分,尽管您必须将其连接到您自己的线条绘制函数以及打印的字符数组。