关闭

扫描线填充算法(2)

449人阅读 评论(0) 收藏 举报
分类:

扫描线填充算法(2)
活动边表示扫描线填充算法的核心,整个算法都是围绕着这张表进行处理的。要完整的定义“活动边表”,需要定义边的数据结构。每条边都和扫描线有个交点,扫描线填充算法只关注交点的x坐标。每当处理下一条扫描线时,根据xi+1/m直接计算出扫描线与边的交点x坐标,可以避免复杂的求交计算。一条边不会一直待在“活动边表”中,当扫描线与之没有交点时,要将其从“活动边表”中删除,判断是否有交点的依据就是看扫描线y是否大于这条边两个端点的y坐标值,为此,需要记录边的y坐标的最大值。

// FillPolygon.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <cstdio>
#include <malloc.h>
#include <cmath>
#include <vector>
#include <GL/glut.h>
#include <iostream>
#include <list>
using namespace std;

#pragma comment(linker,"/entry:\"mainCRTStartup\"")
#define MAX_VERTECES 100
#define WINDOWWIDTH 400
#define WINDOWHEIGHT 400
#define ACCURACY 0.1
#define max(x,y) (x)>(y)?(x):(y)
#define min(x,y) (x)<(y)?(x):(y)
#define drawLine(x1,y1,x2,y2) glBegin(GL_LINES);glVertex2i((x1),(y1));glVertex2i((x2),(y2));glEnd();

typedef struct Edge{
    int ymax;
    float xi, dx;
}Edge;

typedef struct Point{
    GLint x, y;
}Point;

typedef struct Polygon{
    Point pts[MAX_VERTECES];
    int polycnt;
}Polygon;

int ymin, ymax;

GLint verteces[MAX_VERTECES][2];
GLint pointn = 0;
GLint windowwidth = WINDOWWIDTH, windowheight = WINDOWHEIGHT;
GLint finish_picking_points = GL_FALSE;
GLint begin_to_draw = GL_FALSE;

list<Edge> slNet[WINDOWHEIGHT];
list<Edge> aet;

Polygon py;

void newPloygon()
{
    py.polycnt = pointn;
    ymin = 10000000;
    ymax = 0;
    for (int i = 0; i < pointn; i++)
    {
        py.pts[i].x = verteces[i][0];
        py.pts[i].y = verteces[i][1];
        ymin = min(ymin, verteces[i][1]);
        ymax = max(ymax, verteces[i][1]);
    }
}

void initScanLineNewEdgeTable()
{
    for (int i = 0; i < WINDOWHEIGHT; i++)
    {
        slNet[i].clear();
    }
    Edge e;

    for (int i = 0; i < py.polycnt; i++)
    {
        const Point & ps = py.pts[i];
        const Point & pe = py.pts[(i + 1) % py.polycnt];
        const Point & pee = py.pts[(i + 2) % py.polycnt];
        const Point & pss = py.pts[(i - 1 + py.polycnt) % py.polycnt];

        if (pe.y != ps.y)
        {
            e.dx = (double)(pe.x - ps.x) / (double)(pe.y - ps.y);
            if (pe.y > ps.y)
            {
                e.xi = ps.x;
                if (pee.y >= pe.y)
                {
                    e.ymax = pe.y - 1;
                }
                else
                {
                    e.ymax = pe.y;
                }
                slNet[ps.y].push_front(e);
            }
            else
            {
                e.xi = pe.x;
                if (pss.y >= ps.y)
                {
                    e.ymax = ps.y - 1;
                }
                else
                {
                    e.ymax = ps.y;
                }
                slNet[pe.y].push_front(e);
            }
        }
    }
}

void HorizonEdgeFill()
{
    int id1, id2;
    int x1, x2, y1, y2;
    for (int i = 0; i < py.polycnt; i++)
    {
        id1 = i;
        id2 = (i + 1) % py.polycnt;
        if (py.pts[id1].y == py.pts[id2].y)
        {
            x1 = py.pts[id1].x;
            y1 = py.pts[id1].y;
            x2 = py.pts[id2].x;
            y2 = py.pts[id2].y;
            drawLine(x1, y1, x2, y2);
        }
    }
    glFlush();
}
bool EdgeXiComparator(Edge& e1, Edge& e2)
{
    return e1.xi < e2.xi;
}
void InsertNetListToAet(int y)
{
    for (list<Edge>::iterator it1 = slNet[y].begin(); it1 != slNet[y].end(); ++it1)
    {
        //cout << it1->xi << " " << it1->dx << " " << it1->ymax << endl; 
        aet.push_back(*it1);
    }
    aet.sort(EdgeXiComparator);
}

void FillAetScanLine(int y)
{
    bool flag;
    flag = true;
    list<Edge>::iterator it1;
    for (list<Edge>::iterator it = aet.begin(); it != aet.end(); it++)
    {
        it1 = it;
        it1++;
        if (it1 == aet.end())
        {
            break;
        }
        if (flag)
        {
            drawLine(it->xi, y, it1->xi, y);
        }
        flag = !flag;
    }
    glFlush();
}

//bool IsEdgeOutOfActive(Edge e, int y)
//{
//  return (e.ymax == y);
//}

void ProcessScanLineFill()
{
    aet.clear();
    for (int y = ymin; y <= ymax; y++)
    {
        InsertNetListToAet(y);
        FillAetScanLine(y);
        //删除非活动边
        for (list<Edge>::iterator it = aet.begin(); it != aet.end();)
        {
            if (it->ymax == y)
            {
                it = aet.erase(it);
            }
            else
            {
                it++;
            }
        }

        //更新活动边表中每项的xi值,并根据xi重新排序
        //更新xi  
        for (list<Edge>::iterator iter = aet.begin(); iter != aet.end(); iter++)
            iter->xi += iter->dx;
        //根据xi从小到大重新排序    
        aet.sort(EdgeXiComparator);
    }
}
void draw()
{
    initScanLineNewEdgeTable();
    HorizonEdgeFill();
    ProcessScanLineFill();
}
void init()
{
    //glClearColor(0.0, 0.0, 0.0, 0.0);
    //glShadeModel(GL_FLAT);
}
void display()
{
    int k;
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(0.0, 1.0, 0.0);
    glBegin(GL_POINTS);
    for (k = 0; k < pointn; k++)
    {
        glVertex2i(verteces[k][0], verteces[k][1]);
    }
    glEnd();
    if (begin_to_draw == GL_TRUE)
    {
        glColor3f(0.0, 1.0, 1.0);
        draw();
    }
    glutSwapBuffers();
    //glFlush();
}
void keyboard(unsigned char key, int x, int y)
{
    switch (key){
    case 'r':
    case 'R':
        pointn = 0;
        finish_picking_points = GL_FALSE;
        begin_to_draw = GL_FALSE;
        break;
    case 'b':
    case 'B':
        begin_to_draw = GL_FALSE;
        break;
    case 'n':
    case 'N':
        begin_to_draw = GL_TRUE;
        break;
    };
    glutPostRedisplay();
}


//测试使用
//int xx[4] = { 0, 4, 10 };
//int yy[4] = { 0, 6, 4 };


void mouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
        if (finish_picking_points == GL_FALSE)      //如果是按下鼠标左键
        {
            verteces[pointn][0] = x;                //记录按下的点的x坐标       
            verteces[pointn][1] = windowheight - y; //记录按下的点的y坐标,由于坐标系原点一个是在左上角,一个是在左下角,所以要用windowheight-y。
            /*verteces[pointn][0] = xx[pointn];
            verteces[pointn][1] = yy[pointn];*/
            pointn++;
        }
    }
    else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) //如果是按下鼠标右键
    {
        newPloygon();   //构建多边形
        finish_picking_points = GL_TRUE; //停止画点
        begin_to_draw = GL_TRUE; //开始填充
    }
    glutPostRedisplay();
}

void reshape(int w, int h)
{
    windowwidth = w;
    windowheight = h;
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
}

int main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(windowwidth, windowheight);
    glutInitWindowPosition(300, 300);
    glutCreateWindow("扫描线算法");
    //init();
    glutDisplayFunc(display);
    glutMouseFunc(mouse);
    glutKeyboardFunc(keyboard);
    glutReshapeFunc(reshape);
    glutMainLoop();

    return 0;
}

这里写图片描述

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:28059次
    • 积分:1205
    • 等级:
    • 排名:千里之外
    • 原创:84篇
    • 转载:9篇
    • 译文:7篇
    • 评论:2条
    文章分类