//多边形扫描线填充算法
#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;
}