OpenGL多边形填充AET算法

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

  

猜你喜欢

转载自www.cnblogs.com/annie666/p/11918002.html