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