扫描线算法是扫描转换多边形的常用算法,它充分利用了相邻像素之间的连贯性,避免了逐点判断和反复求交计算,达到了减少计算量和提高算法效率的目的。
处理对象:非自交多边形 (边与边之间除了顶点外无其它交点)。开发和利用相邻象素之间的连贯性是光栅图形学算法的重要技巧。
扫描线算法综合利用了区域的连贯性、扫描线的连贯性和边的连贯性等三种形式的连贯性。
**区域的连贯性:**相邻两条扫描线构成一个水平长方形区域,并被多边形的边分割为若干梯形(一类位于多边形的内部;另一类在多边形的外部,且间隔排列)。只需知道该区域内任一梯形中一点关于多边形的内外关系,即可确定区域内所有梯形关于多边形的内外关系。
**扫描线的连贯性:**区域的连贯性在一条扫描线上的反映;
**边的连贯性:**某条边与当前扫描线相交,也可能与下一条扫描线相交。可通过与当前扫描线的交点计算与下一扫描线的交点(利用斜率)。(区域的连贯性在相邻两扫描线上的反映)
算法的具体实现步骤: **求交点:**求出扫描线与多边形所有边的交点 **交点排序:**把这些交点按x坐标值以升序排列 **配对:**对排序后的交点进行奇偶配对 **填色:**对每一对交点间的区域进行填充 数据结构:与当前扫描线相交的边称为活性边(Active Edge),把它们按与扫描线交点x坐标递增的顺序存入一个链表中,称为活性边表AET (AEL, Active Edge List)。它记录了多边形边沿扫描线的交点序列。
新边表(NET)存放在一条扫描线中第一次出现的边。若某边的较低端点为ymin,则该边就放在扫描线ymin的新边表中。
AET,NET存放信息:
ymax:边所交的最高扫描线;
x:当前扫描线与边的交点;
Δx:从当前扫描线到下一条扫描线之间的x增量
扫描转换如下三角形:顶点为:(80,120),(20,30),(150,50)。(扫描线算法)
代码来源:多边形的扫描转换算法
#include
#include
#include
const int POINTNUM = 3;
//链表的实现
typedef struct XET {
float x;
float dx, ymax;
XET* next;
}AET, NET;
//结构体对于某一个点的实现
struct point {
float x;
float y;
}
void PolyScan() {
int MaxY = 0;
int i;
for (i = 0; i MaxY)
MaxY = polypoint[i].y;
}
//选出最大的顶点所对应的y值
AET* pAET = new AET;
pAET->next = NULL;
NET* pNET[1024];
for (i = 0; i next = NULL;
}//初始化扫描边的活性边表
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_POINTS);
//一个点跟前面的点形成一条线段,同时跟后面一个点形成线段
for (i = 0; i < MaxY; i++) {
for (int j = 0; j polypoint[j].y) {
NET* p = new NET;
p->x = polypoint[j].x;
p->ymax = polypoint[(j - 1 + POINTNUM) % POINTNUM].y;
p->dx = (polypoint[(j - 1 + POINTNUM) % POINTNUM].x - polypoint[j].x) / (polypoint[(j - 1 + POINTNUM) % POINTNUM].y - polypoint[j].y);
p->next = pNET[i]->next;
pNET[i]->next = p;
}
if (polypoint[(j + 1 + POINTNUM) % POINTNUM].y > polypoint[j].y) {
NET* p = new NET;
p->x = polypoint[j].x;
p->ymax = polypoint[(j + 1 + POINTNUM) % POINTNUM].y;
p->dx = (polypoint[(j + 1 + POINTNUM) % POINTNUM].x - polypoint[j].x) / (polypoint[(j + 1 + POINTNUM) % POINTNUM].y - polypoint[j].y);
p->next = pNET[i]->next;
pNET[i]->next = p;
}
}
}
}
//把新边表net[i]中的边节点用插入排序法插入AET表,使之按照x的坐标递增顺序排序
for (i = 0; i next;
while (p) {
p->x = p->x + p->dx;
p = p->next;
}
AET* tq = pAET;
p = pAET->next;
tq->next = NULL;
while (p) {
while (tq->next && p->x >= tq->next->x)//重新排序
tq = tq->next;
NET* s = p->next;
p->next = tq->next;
tq->next = p;
p = s;
tq = pAET;
}
//遍历AET表,把配对交点的区间(左闭右开)上的像素(x,y)
AET* q = pAET;
p = q->next;
while (p) {
if (p->ymax == i) {
q->next = p->next;
delete p;
p = q->next;
}
else {
q = q->next;
p = q->next;
}
}
p = pNET[i]->next;
q = pAET;
while (p) {
while (q->next && p->x >= q->next->x)
q = q->next;
NET* s = p->next;
p->next = q->next;
q->next = p;
p = s;
q = pAET;
}
p = pAET->next;
while (p && p->next) {
for (float j = p->x; j next->x; j++) {
glVertex2i(static_cast(j), i);//改写像素的颜色值
}
p = p->next->next;
}
}
glEnd();
glFlush();
}
void main(int argc, char* argv) {
glutInit(&argc, &argv);//窗口的初始化
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//窗口谋模式的设定
glutInitWindowPosition(50, 100);//窗口位置的设定
glutInitWindowSize(400, 300);//窗口大小的设定
glutCreateWindow("多边形扫描转换算法");
glClearColor(1.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0, 600.0, 0.0, 450.0);
glutDisplayFunc(PolyScan);//调用函数
glutMainLoop();
}