#include <stdio.h> #include<math.h> #include <malloc.h> #include <gl/glut.h> #include <Windows.h> #define EPSILON 0.000001 //最小浮点数 //点结构体 struct Point { int x; //x坐标 int y; //y坐标 }; //线结构体 struct Line { Point high_point; //高端点 Point low_point; //低端点 int is_active; //是否为有效边水平边0非水平边1 double inverse_k; //斜率k的倒数 }; //边结点 struct EdgeNode { double x; //扫描线与边交点的x坐标边的低端点的x坐标 int y_max; //边的高端点的y坐标ymax double inverse_k; //斜率k的倒数 EdgeNode* next; //下一个边结点的指针 }; //有效边表 struct ActiveEdgeTable { int y; //扫描线y EdgeNode* head; //边链表的头指针 }; //桶结点 typedef struct Bucket { int y; //扫描线y EdgeNode* head; //边链表的头指针 Bucket* next; //下一个桶的指针 } EdgeTable; int compare(Point p1, Point p2); Line* create_lines(Point points[], int n); Point get_lowest_point(Line lines[], int n); Point get_highest_point(Line lines[], int n); void swap(Line& l1, Line& l2); void sort(Line lines[], int n); EdgeTable* create_edge_table(Line lines[], int n); ActiveEdgeTable* init_active_table(EdgeTable* edge_table); void delete_edge(ActiveEdgeTable* active_table, int y_max); void add_edge(ActiveEdgeTable* active_table, EdgeNode edge); ActiveEdgeTable* update_active_table(ActiveEdgeTable* active_table, EdgeTable* edge_table); void DrawPolygon(Point points, int n); void DrawGrid(int x, int y); void Fill(Point points[], int n); void Initial(); void Display(); int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(400, 300); glutInitWindowPosition(100, 120); glutCreateWindow("有效边表填充算法"); glutDisplayFunc(Display); Initial(); glutMainLoop(); return 0; } //比较2个点的高度 int compare(Point p1, Point p2) { if (p1.y > p2.y) return 1; else if (p1.y == p2.y) return 0; return -1; } //由点数组生成线段数组 Line* create_lines(Point points[], int n) { Line* lines = (Line*)malloc(n * sizeof(Line)); for (int i = 0; i < n; ++i) { Point p1 = points[i]; Point p2 = points[(i + 1) % n]; int result = compare(p1, p2); if (result == 0) lines[i].is_active = 0; //水平边 else lines[i].is_active = 1; lines[i].high_point = result > 0 ? p1 : p2; lines[i].low_point = result < 0 ? p1 : p2; lines[i].inverse_k = (double)(p2.x - p1.x) / (double)(p2.y - p1.y); } return lines; } //获取线数组中最低的端点 Point get_lowest_point(Line lines[], int n) { Point lowest_point = lines[0].low_point; for (int i = 1; i < n; ++i) { Point low_point = lines[i].low_point; if (compare(lowest_point, low_point) > 0) lowest_point = low_point; } return lowest_point; } //获取线数组中最高的端点 Point get_highest_point(Line lines[], int n) { Point highest_point = lines[0].high_point; for (int i = 1; i < n; ++i) { Point high_point = lines[i].high_point; if (compare(highest_point, high_point) < 0) highest_point = high_point; } return highest_point; } //交换2个Line对象 void swap(Line& l1, Line& l2) { Line temp = l1; l1 = l2; l2 = temp; } //对线数组进行排序 void sort(Line lines[], int n) { //先按低端点的y坐标进行升序排序 for (int i = 0; i < n; ++i) { int min_index = i; for (int j = i + 1; j < n; ++j) { if (lines[j].low_point.y < lines[min_index].low_point.y) min_index = j; } swap(lines[i], lines[min_index]); } //再将有序数组按低端点的x坐标升序排列若x坐标相等按inverse_k升序 for (int i = 0; i < n; ++i) { int min_index = i; for (int j = i + 1; lines[j].low_point.y == lines[i].low_point.y; ++j) { if (lines[j].low_point.x < lines[min_index].low_point.x) min_index = j; } swap(lines[i], lines[min_index]); if (i > 0 && lines[i].low_point.x == lines[i - 1].low_point.x) { if (lines[i].is_active == 1 && lines[i - 1].is_active == 1) { if (lines[i].inverse_k < lines[i - 1].inverse_k) swap(lines[i], lines[i - 1]); } } } } //创建一个边表 EdgeTable* create_edge_table(Line lines[], int n) { EdgeTable* edge_table = (EdgeTable*)malloc(sizeof(EdgeTable));//创建边表的头结点 edge_table->head = NULL; edge_table->next = NULL; sort(lines, n); Point lowest_point = get_lowest_point(lines, n); Point highest_point = get_highest_point(lines, n); EdgeTable* s = edge_table; for (int i = lowest_point.y; i <= highest_point.y; ++i) {//构造一个纵向链表,链表的长度等于多边形所占的最大扫描线的线数 Bucket* bucket = (Bucket*)malloc(sizeof(Bucket)); bucket->y = i;//将每条边的信息装入该边最小y坐标对应的桶中 bucket->next = NULL; bucket->head = (EdgeNode*)malloc(sizeof(EdgeNode));//每条边的数据形成一个结点 bucket->head->next = NULL; EdgeNode* p = bucket->head;//p为桶头结点 for (int j = 0; j < n; ++j) { if (lines[j].is_active == 0) continue; if (lines[j].low_point.y == i) {//桶中插入新的边 EdgeNode* q = (EdgeNode*)malloc(sizeof(EdgeNode)); q->x = lines[j].low_point.x; q->y_max = lines[j].high_point.y; q->inverse_k = lines[j].inverse_k; q->next = NULL; p->next = q;//桶的头结点 p = q;//将头结点指向当前节点 } } s->next = bucket;//链表s在尾部插入桶 s = bucket;//将链表指针后移 } return edge_table; } //从边表中取出第一个不为空的桶初始化有效边表 ActiveEdgeTable* init_active_table(EdgeTable* edge_table) { ActiveEdgeTable* active_table = (ActiveEdgeTable*)malloc(sizeof(ActiveEdgeTable));//有效边表头节点 active_table->y = edge_table->next->y; active_table->head = (EdgeNode*)malloc(sizeof(EdgeNode));//创建AET的头结点,初始化y和head active_table->head->next = NULL; EdgeNode* p = edge_table->next->head;//指向边链表的第一个桶的头指针 EdgeNode* q = active_table->head;//有效边表 while (p->next != NULL) //若边表桶非空,将桶中所有边节点拷贝到有效边表中 { EdgeNode* s = (EdgeNode*)malloc(sizeof(EdgeNode));//新建一个边节点 s->x = p->next->x; //边节点的三个值置为p指向的下一个节点的x s->y_max = p->next->y_max; s->inverse_k = p->next->inverse_k; s->next = NULL; // q->next = s; //将边节点s插入有效边表这个链表中 q = s;//使q指向有效边表最后一个节点 p = p->next;//使p指向下一个边节点 } return active_table; } //从有效边表中删除指定y_max的边结点 void delete_edge(ActiveEdgeTable* active_table, int y_max) { EdgeNode* p = active_table->head;//获取有效边表头指针 while (p->next != NULL)//若没有到边表尾 { EdgeNode* q = p->next; if (q->y_max == y_max) { p->next = q->next; free(q); } else p = p->next; } } //将一个边结点按次序添加到有效边表中 void add_edge(ActiveEdgeTable* active_table, EdgeNode edge) { EdgeNode* t = (EdgeNode*)malloc(sizeof(EdgeNode));//分配一个边节点 t->x = edge.x; t->y_max = edge.y_max; t->inverse_k = edge.inverse_k; t->next = NULL; EdgeNode* p = active_table->head; while (p->next != NULL) { EdgeNode* q = p->next; if ((edge.x < q->x) || (edge.x == q->x && edge.inverse_k < q->inverse_k)) {//指针后移 //直到找到有效边表中对应的插入位置 p->next = t; t->next = q; return; } p = p->next; } p->next = t; } //更新有效边表并与边表中对应的桶合并 ActiveEdgeTable* update_active_table(ActiveEdgeTable* active_table, EdgeTable* edge_table) { //更新扫描线y ++active_table->y; //删除y=ymax的边 delete_edge(active_table, active_table->y); //更新边结点的数据 EdgeNode* p = active_table->head->next; while (p != NULL) {//将活性边表中所有x递增/k p->x += p->inverse_k; p = p->next; } //找到边表中对应的桶(y相等) EdgeTable* q = edge_table; while ((q = q->next) != NULL && q->y != active_table->y); //如果找到则进行合并 if (q != NULL) { EdgeNode* s = q->head; while ((s = s->next) != NULL) { add_edge(active_table, *s); } } return active_table; } //画出多边形的边框 void DrawPolygon(Point points[], int n) { glBegin(GL_LINE_LOOP); for (int i = 0; i < n; ++i) glVertex2i(points[i].x, points[i].y); glEnd(); } //画出x * y的网格 void DrawGrid(int x, int y) { glBegin(GL_LINES); //横线 for (int i = 0; i <= y; ++i)//y个网格y+1条线 { glVertex2i(0, i);//32位整数 glVertex2i(x, i); } //竖线 for (int i = 0; i <= x; ++i) { glVertex2d(i, 0); glVertex2d(i, y); } glEnd(); } //用指定的像素大小填充多边形 void Fill(Point points[], int n) { Line* lines = create_lines(points, n);//根据点数组生成线数组 EdgeTable* edge_table = create_edge_table(lines, n);//根据线数组初始化边表 ActiveEdgeTable* active_table = init_active_table(edge_table);//根据边表初始化有效边表 while (active_table->head->next != NULL) { EdgeNode* p = active_table->head->next;//指向活性边首部 int b = 1;//初始为在边界内 while (p->next != NULL)//有效边表一直向后扫 { //由AET表中取出交点进行填充 if (b>0)//在边界内,左值为当前边的x,右值为下一条边的x { int left = ceil(p->x);//左端点向上取整 int right = p->next->x;//右端点向下取整 //如果不是局部最低点则进行边界处理(左闭右开) if (!(p->x - p->next->x >= -EPSILON && p->x - p->next->x <= EPSILON)) { //处理左边界 //当左边界为整数时 //比如左端点为2,left=2 /*if (p->x - left >= -EPSILON && p->x - left <= EPSILON) left -= 1;*/ //处理右边界 //当右边界为整数时 //比如右端点为4,right=3,因为右开 if (p->next->x - right >= -EPSILON && p->next->x - right <= EPSILON) right -= 1; } for (int i = left; i <= right; ++i) { glBegin(GL_POINTS); glVertex2d(i, active_table->y); glEnd(); glFlush(); Sleep(50); } } p = p->next; b = -b; } active_table = update_active_table(active_table, edge_table);//更新有效边表并与边表中对应的桶合并 } } //初始化窗口x和y指定窗口的坐标大小 void Initial() { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glMatrixMode(GL_PROJECTION); gluOrtho2D(0.0, 20.0, 0.0, 15.0); } //窗口的显示回调函数 void Display() { //使用当前背景色填充窗口 glClear(GL_COLOR_BUFFER_BIT); //使用灰色画出网格线 glColor3f(0.75f, 0.75f, 0.75f); DrawGrid(20, 14); //glFlush(); //多边形的顶点坐标 Point points[] = { { 3, 1 },{ 6, 5 },{ 8, 1 },{ 12, 9 },{ 7, 8 },{ 3, 12 },{ 1, 7 } }; //计算顶点个数 int n = sizeof(points) / sizeof(Point); //使用黑色画出多边形的边框 glColor3f(0.0f, 0.0f, 0.0f); DrawPolygon(points, n); //glFlush(); //指定点大小 glPointSize(6.0f); //使用红色填充多边形 glColor3f(1.0f, 0.0f, 1.0f); Fill(points, n); glFlush(); }