📜  在C中使用OPENGL进行扫描线多边形填充

📅  最后修改于: 2021-04-23 21:50:33             🧑  作者: Mango

可以使用多边形在计算机屏幕上绘制图形。要用颜色填充这些图形,我们需要开发一些算法。为此目的,有两种著名的算法:边界填充和扫描线填充算法。

边界填充需要大量处理,因此实时遇到的问题很少。因此,可行的选择是扫描线填充,因为它本质上非常坚固。本文讨论如何使用Scanline填充算法来填充图像中的颜色。

扫描线多边形填充算法

扫描线填充基本上是使用水平线或扫描线填充多边形。 SLPF算法的目的是仅给图形的顶点填充(着色)多边形的内部像素。要了解Scanline,请考虑用一支笔从左下角开始,一直到右端绘制图像,仅绘制图像中存在点的点,并且当线完成时,从下一行开始,然后继续。
该算法通过将扫描线与多边形边缘相交并填充交点对之间的多边形来工作。

多边形顶点的特殊情况:

  1. 如果在顶点处相交的两条线都在扫描线的同一侧,则将其视为两个点。
  2. 如果在顶点处相交的线在扫描线的相对侧,则将其视为仅一个点。

多边形填充的组成部分:

  1. 边桶:包含边的信息。边缘存储区的条目根据您使用的数据结构而有所不同。在下面的示例中,有三个边缘存储区,分别是:ymax,xofymin,
    斜率逆。
  2. 边缘表:它由几个边缘列表组成->保存构成图形的所有边缘。创建边缘时,需要从左到右对边缘的顶点进行排序,并且这些边缘将以yMin递增的顺序进行维护。一旦从ET移除了所有边缘,填充就完成了
  3. 活动列表: IT维护当前用于填充多边形的边。当边的yMin等于当前正在处理的扫描线时,将边从边表推入AL中。
    每次通过后,活动列表将重新排序。

数据结构:
扫描线多边形填充1

Algorithm:

1. We will process the polygon edge after edge, and store in the edge Table.
2. Storing is done by storing the edge in the same scanline edge tuple as 
   the lowermost point's y-coordinate value of the edge.
3. After addition of any edge in an edge tuple, the tuple is 
   sorted using insertion sort, according to its xofymin value.
4. After the whole polygon is added to the edge table, 
   the figure is now filled.
5. Filling is started from the first scanline at the bottom,
   and continued till the top.
6. Now the active edge table is taken and the following things 
   are repeated for each scanline:
       i. Copy all edge buckets of the designated scanline 
          to the active edge tuple
       ii. Perform an insertion sort according
          to the xofymin values
       iii. Remove all edge buckets whose ymax is equal 
            or greater than the scanline
       iv. Fillup pairs of edges in active tuple, if any vertex is got, 
           follow these instructions:
            o If both lines intersecting at the vertex are on
              the same side of the scanline, consider it as two points.
            o If lines intersecting at the vertex are at 
              opposite sides of the scanline, consider it as only one point.
       v. Update the xofymin by adding slopeinverse for each bucket.
   

样本图片

我们以多边形恐龙为例。将以下内容粘贴到与可执行文件相同的文件夹中的文本文件中,并将其重命名为PolyDino.txt。
链接:PolyDino.txt

// CPP program to illustrate
// Scanline Polygon fill Algorithm
  
#include 
#include 
#include 
#define maxHt 800
#define maxWd 600
#define maxVer 10000
  
FILE *fp;
  
// Start from lower left corner
typedef struct edgebucket 
{
    int ymax;   //max y-coordinate of edge
    float xofymin;  //x-coordinate of lowest edge point updated only in aet
    float slopeinverse;
}EdgeBucket;
  
typedef struct edgetabletup
{
    // the array will give the scanline number
    // The edge table (ET) with edges entries sorted 
    // in increasing y and x of the lower end
      
    int countEdgeBucket;    //no. of edgebuckets
    EdgeBucket buckets[maxVer];
}EdgeTableTuple;
  
EdgeTableTuple EdgeTable[maxHt], ActiveEdgeTuple;
  
  
// Scanline Function
void initEdgeTable()
{
    int i;
    for (i=0; icountEdgeBucket)
        printf("\nCount %d-----\n",tup->countEdgeBucket);
          
        for (j=0; jcountEdgeBucket; j++)
        { 
            printf(" %d+%.2f+%.2f",
            tup->buckets[j].ymax, tup->buckets[j].xofymin,tup->buckets[j].slopeinverse);
        }
}
  
void printTable()
{
    int i,j;
      
    for (i=0; icountEdgeBucket; i++) 
    {
        temp.ymax = ett->buckets[i].ymax;
        temp.xofymin = ett->buckets[i].xofymin;
        temp.slopeinverse = ett->buckets[i].slopeinverse;
        j = i - 1;
  
    while ((temp.xofymin < ett->buckets[j].xofymin) && (j >= 0)) 
    {
        ett->buckets[j + 1].ymax = ett->buckets[j].ymax;
        ett->buckets[j + 1].xofymin = ett->buckets[j].xofymin;
        ett->buckets[j + 1].slopeinverse = ett->buckets[j].slopeinverse;
        j = j - 1;
    }
    ett->buckets[j + 1].ymax = temp.ymax;
    ett->buckets[j + 1].xofymin = temp.xofymin;
    ett->buckets[j + 1].slopeinverse = temp.slopeinverse;
    }
}
  
  
void storeEdgeInTuple (EdgeTableTuple *receiver,int ym,int xm,float slopInv)
{
    // both used for edgetable and active edge table..
    // The edge tuple sorted in increasing ymax and x of the lower end.
    (receiver->buckets[(receiver)->countEdgeBucket]).ymax = ym;
    (receiver->buckets[(receiver)->countEdgeBucket]).xofymin = (float)xm;
    (receiver->buckets[(receiver)->countEdgeBucket]).slopeinverse = slopInv;
              
    // sort the buckets
    insertionSort(receiver);
          
    (receiver->countEdgeBucket)++; 
      
      
}
  
void storeEdgeInTable (int x1,int y1, int x2, int y2)
{
    float m,minv;
    int ymaxTS,xwithyminTS, scanline; //ts stands for to store
      
    if (x2==x1)
    {
        minv=0.000000;
    }
    else
    {
    m = ((float)(y2-y1))/((float)(x2-x1));
      
    // horizontal lines are not stored in edge table
    if (y2==y1)
        return;
          
    minv = (float)1.0/m;
    printf("\nSlope string for %d %d & %d %d: %f",x1,y1,x2,y2,minv);
    }
      
    if (y1>y2)
    {
        scanline=y2;
        ymaxTS=y1;
        xwithyminTS=x2;
    }
    else
    {
        scanline=y1;
        ymaxTS=y2;
        xwithyminTS=x1;     
    }
    // the assignment part is done..now storage..
    storeEdgeInTuple(&EdgeTable[scanline],ymaxTS,xwithyminTS,minv);
      
      
}
  
void removeEdgeByYmax(EdgeTableTuple *Tup,int yy)
{
    int i,j;
    for (i=0; i< Tup->countEdgeBucket; i++)
    {
        if (Tup->buckets[i].ymax == yy)
        {
            printf("\nRemoved at %d",yy);
              
            for ( j = i ; j < Tup->countEdgeBucket -1 ; j++ )
                {
                Tup->buckets[j].ymax =Tup->buckets[j+1].ymax;
                Tup->buckets[j].xofymin =Tup->buckets[j+1].xofymin;
                Tup->buckets[j].slopeinverse = Tup->buckets[j+1].slopeinverse;
                }
                Tup->countEdgeBucket--;
            i--;
        }
    }
}     
  
  
void updatexbyslopeinv(EdgeTableTuple *Tup)
{
    int i;
      
    for (i=0; icountEdgeBucket; i++)
    {
        (Tup->buckets[i]).xofymin =(Tup->buckets[i]).xofymin + (Tup->buckets[i]).slopeinverse;
    }
}
  
  
void ScanlineFill()
{
    /* Follow the following rules:
    1. Horizontal edges: Do not include in edge table
    2. Horizontal edges: Drawn either on the bottom or on the top.
    3. Vertices: If local max or min, then count twice, else count
        once.
    4. Either vertices at local minima or at local maxima are drawn.*/
  
  
    int i, j, x1, ymax1, x2, ymax2, FillFlag = 0, coordCount;
      
    // we will start from scanline 0; 
    // Repeat until last scanline:
    for (i=0; i2)
        {
            x1 = x2;
            y1 = y2;
            count=2;
        }
        if (count==1)
        {
            fscanf(fp, "%d,%d", &x1, &y1);
        }
        else
        {
            fscanf(fp, "%d,%d", &x2, &y2);
            printf("\n%d,%d", x2, y2);
            glBegin(GL_LINES);
                glVertex2i( x1, y1);
                glVertex2i( x2, y2);
            glEnd();
            storeEdgeInTable(x1, y1, x2, y2);//storage of edges in edge table.
              
              
            glFlush();
        }
    }
          
          
}
  
void drawDino(void)
{
    initEdgeTable();
    drawPolyDino();
    printf("\nTable");
    printTable();
      
    ScanlineFill();//actual calling of scanline filling..
}
  
void main(int argc, char** argv)
{
    fp=fopen ("PolyDino.txt","r");
    if ( fp == NULL )
    {
        printf( "Could not open file" ) ;
        return;
    }
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); 
    glutInitWindowSize(maxHt,maxWd);
    glutInitWindowPosition(100, 150);
    glutCreateWindow("Scanline filled dinosaur");
    myInit();
    glutDisplayFunc(drawDino);
      
    glutMainLoop();
    fclose(fp);
}

输出:
填满的恐龙:
填满恐龙