1. 问题描述:
理解示范程序的整个框架结构;在示范程序“DrawPolygon”中实现DrawPolygonOpenGL函数,该函数用OpenGL绘制多边形方法绘制多边形。在示范程序“DrawPolygon”中实现DrawPolygon函数,该函数实现多边形扫描转换算法。
2. 算法描述:
1) DrawPolygonOpenGL算法:
下面展示一些 伪代码
。
void DrawPolygonOpenGL(int g_iCtrlPntNum, wcPt2D g_pCtrlPoints[])
{
赋予颜色,光滑明暗处理
glBegin(GL_POLYGON);
for (遍历g_pCtrlPoints数组){
glVertex2f(g_pCtrlPoints[i].x, g_pCtrlPoints[i].y);
}
glEnd();
glFlush();
}
2) DrawPolygon算法:
(1) 初始化ET:将多边形各条边按照该边的ymin 值存放至ymin 所对应的ET存储桶中。
(2) 初始化AET为空表。
(3) 将y值设置成ET中所列的最小y值,即第-一个非空存储桶的y值。
(4) 重复执行以下各步,直至AET和ET都为空。
① 当扫描线的y值开始大于或等于ET中某个y桶的值时,将该桶的所有结点加入AET中(同时要从ET中删去),并将AET中的记录按x值排序。
② 对于扫描线y,在一对交点之间填充所需要的像素值。
③ 删去AET中y>ymax的项。
④ 更新AET中所有剩余结点的x值,用x+1/m代替x.当一个结点是在本轮循环中才进入AET时,它记录的x值为xmin⑤对AET中的各结点按x值重新排序。
⑥ y增1后进入下一轮循环。
下面展示一些 伪代码
。
// 数据结构:
struct vertex {
float ver_x;
float ver_y;
};
typedef struct XET {
float x;
float dx, ymax;
XET* next;
}AET, NET;
vertex* ver;
// 函数:
void DrawPolygon(int g_iCtrlPntNum, wcPt2D g_pCtrlPoints[])
{
for(各条扫描线 i)
{
初始化 新边表头指针NET[i];
把y(min)=i 的边放进新边表NET[i];
}
y=最低扫描线号;
初始化活性边表AET为空;
for(各条扫描线i)
{
处理传入的顶点g_pCtrlPoints[],如果超出限定值则赋予限定值以保证点不会超出范围;
初始化AET表,即初始化活跃边表; 初始化NET表,即初始化边表;
把新边表NET[i]中的边结点用插入排序法插入活性边表AET中;
遍历活性边表AET,将新边表中的活性边按照从左到右的顺序排序,删掉扫描线高度等同于ymax的废弃点,并以 x 坐标的值升序排列;
对多边形进行填充;
}
}
测试结果:
1) DrawPolygonOpenGL算法:
实际运行结果:
2) DrawPolygon算法:
实际运行结果:
分析与评论:
1) DrawPolygonOpenGL算法:
DrawPolygonOpenGL算法的思路很简单,只需在glBegin(GL_POLYGON)后用glVertex2f(x, y)进行描点即可完成对多边形的填充,体现出了OpenGL库函数的强大于便利;因为调用了库函数,所以时间复杂度不明;
2) DrawPolygon算法:
对比OpenGL库函数,DrawPolygon算法才是真正考验对多边形扫描转换的理解是否到位;首先,要构建活性边表AET和新边表NET,然后初始化AET表,即初始化活跃边表; 初始化NET表,即初始化边表;把新边表NET[i]中的边结点用插入排序法插入活性边表AET中;遍历活性边表AET,将新边表中的活性边按照从左到右的顺序排序,删掉扫描线高度等同于ymax的废弃点,并以 x 坐标的值升序排列;对多边形进行填充;
从空间复杂度上看,仅使用了大小为n的ver数组;
从时间复杂度上看,O((ymax-ymin)*边数);
评论:
DrawPolygonOpenGL算法画不出凸多边形与自相交多边形,而DrawPolygon算法可以通过软件的特性,不用CPU就可以画出凸多边形与自相交多边形,但是本次实验我仅实现了非自交多边形的绘制;
附录: Source Code(in C)
链接: 多边形扫描转换实验.rar
(代码仅供参考)
没有币的同学来Gitee吧!记得留下Star噢~
Jack-lllll 的Gitee仓库