多边形的扫描转换算法——扫描线算法(计算机图形学)

  • 扫描线算法是扫描转换多边形的常用算法,它充分利用了相邻像素之间的连贯性,避免了逐点判断和反复求交计算,达到了减少计算量和提高算法效率的目的。

  • 处理对象:非自交多边形 (边与边之间除了顶点外无其它交点)。开发和利用相邻象素之间的连贯性是光栅图形学算法的重要技巧。

  • 扫描线算法综合利用了区域的连贯性、扫描线的连贯性和边的连贯性等三种形式的连贯性。

  • **区域的连贯性:**相邻两条扫描线构成一个水平长方形区域,并被多边形的边分割为若干梯形(一类位于多边形的内部;另一类在多边形的外部,且间隔排列)。只需知道该区域内任一梯形中一点关于多边形的内外关系,即可确定区域内所有梯形关于多边形的内外关系。

  • **扫描线的连贯性:**区域的连贯性在一条扫描线上的反映;

  • **边的连贯性:**某条边与当前扫描线相交,也可能与下一条扫描线相交。可通过与当前扫描线的交点计算与下一扫描线的交点(利用斜率)。(区域的连贯性在相邻两扫描线上的反映)

算法的具体实现步骤:

  1. **求交点:**求出扫描线与多边形所有边的交点
  2. **交点排序:**把这些交点按x坐标值以升序排列
  3. **配对:**对排序后的交点进行奇偶配对
  4. **填色:**对每一对交点间的区域进行填充

数据结构:

与当前扫描线相交的边称为活性边(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 < POINTNUM; i++) {
  if (polypoint[i].y > MaxY)
   MaxY = polypoint[i].y;
 }
 //选出最大的顶点所对应的y值
 AET* pAET = new AET;
 pAET->next = NULL;
 NET* pNET[1024];
 for (i = 0; i <= MaxY; i++) {
  pNET[i] = new NET;
  pNET[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 < POINTNUM; j++) {
   if (polypoint[j].y == i) {
    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;
    }
    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 <= MaxY; i++) {
  NET* p = pAET->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 <= p->next->x; j++) {
    glVertex2i(static_cast<int>(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();
}

你可能感兴趣的:(计算机图形学)