#include
#include
#include
#include
#include
#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();
}