QT+OpenGL开发实例:扫描线填充算法

##      最近要做一个课程设计:扫描线填充算法。扫描线填充的具体实现要依靠OpenGL了,也算是用用计算机图形学这门课程所学的知识了。同时为了做出菜单、点击按钮这样的图形化界面,开始着手看QT OpenGL编程。本博客发布的代码,在Window 7 + VS2010 + QT 5.4 下编译通过。
         需要在QT中创建OpenGL应用程序,通常需要从QGLWidget类继承出自己的类。QGLWidget从QWidget派生,提供了在QT应用程序中显示OpenGL图形的能力。QGLWidget类提供了三个虚函数来完成OpenGL的绘图任务。initializeGL()完成OpenGL环境的初始化,paintGL()绘制OpenGL图形,resizeGL()在窗口发生改变时运行。本博客发布的代码利用此方法,可通过橡皮筋算法在窗口绘制多边形,还要通过扫描线填充算法对多边形进行填充。 ##
//main.cpp
#include "glwidget.h"
#include"MainWindow.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow win;
    win.show();
    return a.exec();
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtWidgets\QMainWindow>

class QAction;
class QLabel;
class QMenu;
class QScrollArea;

class GLWidget;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();



private:

    void createMenus();
    void createActions();

    void openFile();
    void clean();

    QWidget *centralWidget;
    QScrollArea *glWidgetArea;
    GLWidget *glWidget;


    QMenu *fileMenu;
    QMenu *helpMenu;

    QAction *renderIntoPixmapAction;
    QAction *grabFrameBufferAction;
    QAction *clearPixmapAction;
    QAction *exitAction;
    QAction *aboutAction;
    QAction *aboutQtAction;

    QToolBar *filetoolBar;
    QToolBar *cleantoolBar;
    QToolBar *texturefilltoolBar;
    QAction  *openAction;
    QAction  *cleanAction;
    QAction  *texturefillAction;
};

#endif // MAINWINDOW_H
//mainwindow.cpp
#include <QtOpenGL\QtOpenGL>

#include <QtWidgets\QAction>
#include <QtWidgets\QMenu>
#include <QtWidgets\QScrollArea>
#include <QtWidgets\QMenuBar>
#include <QtWidgets\QApplication>

#include "mainwindow.h"
#include "glwidget.h"
#include<vector>
#include<list>
using namespace std;

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{
    centralWidget = new QWidget;
    setCentralWidget(centralWidget);//设置这个窗口的中央窗口部件为centralWidget。这个中央窗口部件被上、下、左、右锚接区域环绕。菜单条在上锚接区域的上面。

    glWidget = new GLWidget;

    glWidgetArea = new QScrollArea;
    glWidgetArea->setWidget(glWidget);
    glWidgetArea->setWidgetResizable(true);
    glWidgetArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    glWidgetArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    glWidgetArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    glWidgetArea->setMinimumSize(50, 50);

    QGridLayout *centralLayout = new QGridLayout;
    centralLayout->addWidget(glWidgetArea, 0, 0);

    centralWidget->setLayout(centralLayout);

    createActions();
    createMenus();

    setWindowTitle(QStringLiteral("扫描线算法"));
    resize(800, 600);
}

void MainWindow::createActions()
{
    openAction = new QAction(QStringLiteral("打开"),this);
    connect(openAction, &QAction::triggered, this, &MainWindow::openFile);

    cleanAction = new QAction(QStringLiteral("清除"),this);
    connect(cleanAction, &QAction::triggered, this, &MainWindow::clean);

}

void MainWindow::createMenus()
{
    QToolBar *filetoolBar = addToolBar(QStringLiteral("打开"));
    filetoolBar->addAction(openAction);

    QToolBar *cleantoolBar = addToolBar(QStringLiteral("清除"));
    cleantoolBar->addAction(cleanAction);

}

void MainWindow::openFile()
{
    QString path = QFileDialog::getOpenFileName(this,tr("Open File"),".",tr("Text Files(*.txt)"));
    if(!path.isEmpty()) {
        string str = path.toStdString();
        glWidget->openFile(str);

    } else {
        QMessageBox::warning(this, tr("Path"),
            tr("You did not select any file."));
    }
}

void MainWindow::clean()
{

    glWidget->clean();
}

MainWindow::~MainWindow()
{ 

}
//MyPolygon.h
#pragma once
#include <string>
#include<vector>
#include<list>
#include <iostream>
using namespace std;

struct Point{
    int x; 
    int y;
};

struct Edge{
    double xi;
    double dx;
    int ymax;
    bool operator<(const Edge &e) const
    {
        return xi<e.xi;
    }
};

class MyPolygon
{
public:
    vector<Point> PT;       //多边形各个顶点
    vector<Edge> AET;         //活化边表
    vector<Edge> NEdge[1024];//新边表
    int maxy;
    int miny;
public:
    MyPolygon();
    void deletePolygon();
};
//MyPolygon.cpp
#include<iostream>
#include"MyPolygon.h"
using namespace std;

MyPolygon::MyPolygon()
{
}
void MyPolygon::deletePolygon()
{
    this->PT.clear();
    this->AET.clear();
    for (int i = miny; i <= maxy; i++) 
    {
        NEdge[i].clear();
    }
}
//glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QtOpenGL\QGLWidget>
#include<string>
#include <MyPolygon.h>

class GLWidget : public QGLWidget
{
    Q_OBJECT

public:
    explicit GLWidget(QWidget *parent = 0);
    ~GLWidget();
protected:
    void initializeGL();
    void paintGL();
    void resizeGL(int w, int h);

    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);

public:
    vector <MyPolygon>* polygon;
    MyPolygon tempPolygon;
public:
    void getPeakY();
    void scanLineFilling(MyPolygon &tempPolygon);
    void initNewEdgeTable(MyPolygon& tempPolygon);
    void clean();
    void openFile(string str);
};

#endif // GLWIDGET_H
//glwidget.cpp
#include <QtGui\QtGui>
#include <math.h>
#include<fstream>
#include "glwidget.h"
#include"MyPolygon.h"
using namespace std;

int windowheight;       //窗口高度
Edge edge;              //边 
Point temp;             //临时存放鼠标移动到的点,响应鼠标move

GLWidget::GLWidget(QWidget *parent) :
QGLWidget(parent)
{
    temp.x=-1;
    temp.y=-1;
    this->setMouseTracking(true);          // 鼠标追踪
    polygon = new vector <MyPolygon>();    //存放已经绘制好的多边形
}
GLWidget::~GLWidget() 
{
    delete polygon;
    polygon = nullptr;
}
void GLWidget::initializeGL()
{
    glShadeModel(GL_SMOOTH);  
    glClearColor(1.0, 1.0, 1.0, 0.0); 
    glClearDepth(1.0);  
    glEnable(GL_DEPTH_TEST);  
    glDepthFunc(GL_LEQUAL);  
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);  
    glEnableClientState(GL_VERTEX_ARRAY);  
}

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
    int len;
    for(int j=0;j<polygon->size();j++)
    {
        glColor3f(0.0, 0.0, 1.0);    //蓝色
        glBegin(GL_LINE_LOOP);
        for(int i=0;i<polygon->at(j).PT.size();i++)
        {
            glVertex2i(polygon->at(j).PT[i].x, polygon->at(j).PT[i].y);
        }
        glEnd();
        scanLineFilling(polygon->at(j));    
    }
    glColor3f(0.0, 0.0, 1.0);//蓝色
    if(tempPolygon.PT.size()>0)
    {
        glBegin(GL_LINE_STRIP);
        for(int i=0;i<tempPolygon.PT.size();i++)
        {
            glVertex2i(tempPolygon.PT[i].x, tempPolygon.PT[i].y);
        }
        if(temp.x!=-1&&temp.y!=-1)
        { 
            glVertex2i(temp.x,temp.y);
        }
        glEnd();
    }
    glFlush();

}
void GLWidget::resizeGL(GLint w, GLint h)
{
    glViewport (0, 0, (GLsizei) w, (GLsizei) h);
    glLoadIdentity ();
    glOrtho (0.0, (GLdouble) w, 0.0, (GLdouble) h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    windowheight = h;
}
void GLWidget::getPeakY()
{
    //1.计算ymax、ymin
    tempPolygon.maxy=tempPolygon.PT[0].y;
    tempPolygon.miny=tempPolygon.PT[0].y;
    for(int i=0;i<tempPolygon.PT.size();i++)
    {
        if(tempPolygon.PT[i].y>tempPolygon.maxy)
        {
            tempPolygon.maxy=tempPolygon.PT[i].y;
        }
        if(tempPolygon.PT[i].y<tempPolygon.miny)
        {
            tempPolygon.miny=tempPolygon.PT[i].y;
        }
    }
}
void GLWidget::initNewEdgeTable(MyPolygon& tempPolygon)//初始化新边表
{
    getPeakY();
    Point P1,P2;
    GLint psize=tempPolygon.PT.size();
    for(GLint i=0;i<psize;i++)
    {
        P1=tempPolygon.PT[i];
        P2=tempPolygon.PT[(i+1)%psize];
        if(P1.y!=P2.y)//不处理水平线
        {
            edge.dx=GLdouble(P2.x-P1.x)/(P2.y-P1.y);
            //上顶点减去一个像素
            if(P2.y > P1.y)
            {
                edge.ymax=P2.y-1;
                edge.xi=P1.x;
                tempPolygon.NEdge[P1.y].push_back(edge);        
            }
            else
            {
                edge.ymax=P1.y-1;
                edge.xi = P2.x;
                tempPolygon.NEdge[P2.y].push_back(edge);
            }
        }
    }
}

void GLWidget::scanLineFilling(MyPolygon &tempPolygon)//填充算法
{
    glColor3f(1.0, 0.0, 0.0);
    glBegin(GL_LINES);
    for(int i=tempPolygon.miny;i<=tempPolygon.maxy;i++)
    {
        //依次将NEdge中的边取出放到NET里面并在链表内部按照x坐标排序,还要随时更新AET
        vector<Edge>::iterator it = tempPolygon.AET.begin();
        while(it != tempPolygon.AET.end()) {
            it->xi = it->xi + it->dx;

            if(i > it->ymax){
                it = tempPolygon.AET.erase(it);
            }
            else {
                it ++;
            }
        }
        vector<Edge>::iterator iter = tempPolygon.NEdge[i].begin();
        while(iter != tempPolygon.NEdge[i].end()) {         
            tempPolygon.AET.push_back(*iter);
            iter ++;
        }
        int size = tempPolygon.AET.size();
        sort(tempPolygon.AET.begin(), tempPolygon.AET.end());
        //两两配对,绘制直线
        vector<Edge>::iterator edgeiter=tempPolygon.AET.begin();
        while(edgeiter!=tempPolygon.AET.end())
        {
            glVertex2d((*edgeiter).xi,i);
            ++edgeiter;

            glVertex2d((*(edgeiter)).xi,i);
            ++edgeiter;
        }
    }
    glEnd();
    glFlush();
}

void GLWidget::clean(){
    for(int j=0;j<polygon->size();j++)
    {
        polygon->at(j).deletePolygon();
    }
    tempPolygon.deletePolygon();
    updateGL();
}

void GLWidget::openFile(string str){
    // 数据文件名假设:data.txt,和程序在同一个目录下 
    ifstream ifile(str);
    // 打开文件成功 
    if(ifile)
    {
        //一行中的两个数据存到两个变量中
//      tempPolygon.PT.clear();
        double a,b;
        Point p;
        //clean();
        while(ifile>>a>>b)
        {
            p.x = a;
            p.y = b;
            tempPolygon.PT.push_back(p);
        }
        initNewEdgeTable(tempPolygon);
        polygon->push_back(tempPolygon);
        tempPolygon.deletePolygon();
        ifile.close(); 
    }
    /****打开文件失败怎么办***/
    updateGL();
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->buttons() == Qt::LeftButton)
    {   
        Point dot;
        dot.x = event->x();
        dot.y = windowheight-event->y();

        tempPolygon.PT.push_back(dot);

        updateGL();
    }
    else if(event->buttons() == Qt::RightButton&&tempPolygon.PT.size()!=0 )
    {
        ofstream ofile;                        //定义输出文件
        ofile.open("output.txt",ios::app);     //作为输出文件打开
        for(int j=0;j<polygon->size();j++)
        {

            for(int i=0;i<polygon->at(j).PT.size();i++)
            {
                ofile<<polygon->at(j).PT[i].x<<" "<<polygon->at(j).PT[i].y<<endl;
            }
            ofile<<-1<<" "<<-1<<endl;   
        }

        for(int i=0;i<tempPolygon.PT.size();i++)
        {
            ofile<<tempPolygon.PT[i].x<<" "<<tempPolygon.PT[i].y<<endl;
        }
        initNewEdgeTable(tempPolygon);//初始化新边表

        polygon->push_back(tempPolygon);

        tempPolygon.deletePolygon();
        updateGL();
    }
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    temp.x = event->x();
    temp.y = windowheight-event->y();
    updateGL();
}
 运行程序,就可以在窗口绘图,点击右键便可通过扫描线填充算法进行填充,同时通过菜单上的按钮可以浏览文件中的点信息绘制多边形并进行填充。
 [VS+QT开发环境的搭建参考:](http://qiusuoge.com/11375.html)
 [扫描线填充算法理解](http://blog.csdn.net/orbit/article/details/7368996)
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值