计算机图形学——扫描线填充算法(有序边表)代码简单易懂可运行

//多边形扫描线填充算法
#include <stdlib.h> 
#include <windows.h> 
#include <glut.h>
#define NULL 0  //C++中没有NULL这个符号常量,这里用宏定义
#define WINDOW_HEIGHT 400   //定义窗口高为400
#define WINDOW_WIDTH 400    //定义窗口宽为400

struct dcPt { //定义一个点的结构体
    int x;
    int y;
};

void setPixel(GLint x, GLint y)  //用OpenGL函数改写setPixel
{
    glBegin(GL_POINTS);
    glVertex2i(x, y);
    glEnd();
   
}
//边数据结构
typedef struct tEdge {
    int yUpper;//边的最大y坐标
    float xIntersect, dxPerScan;//与当前扫描线的交点x坐标,边所在的直线的斜率的倒数
    struct tEdge* next;//指向下一条边
} Edge;


/* Inserts edge into list in order of increasing xIntersect field.
*/
void insertEdge(Edge* list, Edge* edge)
{
    Edge* p, * q = list;
    p = q->next;
    while (p != NULL) {
        if (edge->xIntersect < p->xIntersect)
            p = NULL;
        else {
            q = p;
            p = p->next;
        }
    }
    edge->next = q->next;
    q->next = edge;
}

int yNext(int k, int cnt, dcPt* pts)
{
    int j;

    if ((k + 1) > (cnt - 1))
        j = 0;
    else
        j = k + 1;
    while (pts[k].y == pts[j].y)
        if ((j + 1) > (cnt - 1))
            j = 0;
        else
            j++;
    return (pts[j].y);
}

void makeEdgeRec(dcPt lower, dcPt upper, int yComp, Edge* edge, Edge* edges[])
{
    edge->dxPerScan = (float)(upper.x - lower.x) / (upper.y - lower.y);
    edge->xIntersect = lower.x;
    if (upper.y < yComp)
        edge->yUpper = upper.y - 1;
    else
        edge->yUpper = upper.y;
    insertEdge(edges[lower.y], edge);
}
//建立边表
void buildEdgeList(int cnt, dcPt* pts, Edge* edges[])
{
    Edge* edge;
    dcPt v1, v2;
    int i, yPrev = pts[cnt - 2].y;

    v1.x = pts[cnt - 1].x; v1.y = pts[cnt - 1].y;
    for (i = 0; i < cnt; i++) {
        v2 = pts[i];
        if (v1.y != v2.y) {              
            edge = (Edge*)malloc(sizeof(Edge));
            if (v1.y < v2.y)                
                makeEdgeRec(v1, v2, yNext(i, cnt, pts), edge, edges);
            else                           
                makeEdgeRec(v2, v1, yPrev, edge, edges);
        }
        yPrev = v1.y;
        v1 = v2;
    }
}
//建立活化边表
void buildActiveList(int scan, Edge* active, Edge* edges[])
{
    Edge* p, * q;

    p = edges[scan]->next;
    while (p) {
        q = p->next;
        insertEdge(active, p);
        p = q;
    }
}
void fillScan(int scan, Edge* active)
{
    Edge* p1, * p2;
    int i;

    p1 = active->next;
    while (p1) {
        p2 = p1->next;
        for (i = p1->xIntersect; i < p2->xIntersect; i++)
        {
 setPixel((int)i, scan);

           
        }
           
        p1 = p2->next;
    }
}
void deleteAfter(Edge* q)
{
    Edge* p = q->next;

    q->next = p->next;
    free(p);
}

/*
删除完成的边。更新'xIntersect'
*/
void updateActiveList(int scan, Edge* active)
{
    Edge* q = active, * p = active->next;

    while (p)
        if (scan >= p->yUpper) {
            p = p->next;
            deleteAfter(q);
        }
        else {
            p->xIntersect = p->xIntersect + p->dxPerScan;
            q = p;
            p = p->next;
        }
}
/*   */
void resortActiveList(Edge* active)
{
    Edge* q, * p = active->next;

    active->next = NULL;
    while (p) {
        q = p->next;
        insertEdge(active, p);
        p = q;
    }
}
//填充过程
void scanFill(int cnt, dcPt* pts) //cnt为顶点个数
{
    Edge* edges[WINDOW_HEIGHT], * active;
    int i, scan;

    for (i = 0; i < WINDOW_HEIGHT; i++) {
        edges[i] = (Edge*)malloc(sizeof(Edge));
        if (edges[i] != NULL) {//在堆区申请内存成功了才这么干!

            edges[i]->next = NULL;
        }
    }
    buildEdgeList(cnt, pts, edges);
    active = (Edge*)malloc(sizeof(Edge));
    if (active != NULL) {//在堆区申请内存成功了才这么干!
      
        active->next = NULL;
    }
   

    for (scan = 0; scan < WINDOW_HEIGHT; scan++) {
   
        buildActiveList(scan, active, edges);
        if (active->next) {
            fillScan(scan, active);
            updateActiveList(scan, active);
            resortActiveList(active);
            Sleep(1); //为了放慢填充速度,便于观看填充过程,每填充一行停顿1毫秒,Sleep函数包含在头文件windows.h里面
        }
    }
}


void init(void)    //将所有初始化和有关的一次性设定参数的函数都放在init中。
{
    glClearColor(1.0, 1.0, 1.0, 0.0);  //显示窗口的颜色设置成白色
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);
}

void myDraw(void)//画六边形
{
    dcPt pts[] = { //pts是表示填充图元的顶点数组,这里定义了一个六边形
    50, 50,
    300, 20,
    300, 300,
    200,100,
    150,350,
   
    };

    glClear(GL_COLOR_BUFFER_BIT); //清除显示窗口
    glColor3f(0.0, 1.0, 1.0);//将线的颜色设置成蓝色
    scanFill(5, pts); // 第一个参数为填充图元的顶点数,第二个参数为顶点坐标数组
    glFlush();
}

//主函数
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(50, 100);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutCreateWindow("多边形的扫描线填充");

    init();
    glutDisplayFunc(myDraw);
    glutMainLoop();
    return 0;
}

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小yuan不搜题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值