参考文献:
https://blog.csdn.net/lhs322/article/details/79729356
https://blog.csdn.net/u013044116/article/details/49737585/
https://blog.csdn.net/zjccoder/article/details/41146259
主要功能
要OpenGL 实现
通过橡皮筋交互输入不同颜色、大小的多边形
清屏重置多边形,撤销多边形,对多个多边形进行填充,改变多边形线条的粗细,颜色
实现了存储多边形,以及显示存储的多边形
实现代码如下
#include<stdio.h>
#include<gl/glut.h>
#include"datastruct.h"
static const int screenwidth = 1000; //自定义窗口宽度
static const int screenheight = 1000; //自定义窗口高度
vector<point> p; //多边形点集向量
vector<polygon> s; //多边形类向量,用来保存已经画完的多边形
int move_x,move_y; //鼠标当前坐标值,在鼠标移动动态画线时使用
bool select = false, exam=true,functiondemo=false; //多边形封闭状态判断变量,当为true时即按下鼠标右键,将多边形封闭
float red=1.0,green=0.0,blue=0.0; //用此来改变线段的颜色
float lineWidth;
int MaxY=0,MinY=1000;
int POINTNUM;
int colorNum=0;
/**
使用说明:
按上键表示增加线条的宽度,下键减小线条的宽度
F2: 对多边形填充颜色
F1: 清屏
F3: 撤销刚画的一条边
F4: 撤销刚画的一个三角形
CTRL+a :改变多边形填充的颜色 红色
CTRL+z : 改变多边形填充的颜色 蓝色
CTRL+x : 改变多边形填充的颜色 绿色
**/
void init()
{
glClearColor(1.0,1.0,1.0,0.0);
glMatrixMode(GL_PROJECTION); //表示给物体加物理贴图,或透视效果
gluOrtho2D(0.0,screenwidth,0.0,screenheight);
}
void fillKColor(int mn){
int i;
for(int k=0;k<mn;k++){ //k表示多变行的个数
POINTNUM = s[k].p.size();
for( i = 0;i < POINTNUM; i++ )
{
if( s[k].p[i].y > MaxY )
{
MaxY = s[k].p[i].y;
}
if( s[k].p[i].y < MinY )
{
MinY = s[k].p[i].y;
}
}
/*******初始化AET表,即初始化活跃边表***********************************************************/
AET *pAET = new AET;
pAET->next = NULL;
/******初始化NET表,即初始化边表************************************************************/
NET *pNET[1024];
for( i = MinY;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);
/******扫描并建立NET表,即建立边表*********************************************************/
for( i = MinY; i <= MaxY; i++ )
{
for( int j = 0;j < POINTNUM; j++ )
{
if( s[k].p[j].y == i)
{
if( s[k].p[ (j-1+POINTNUM) % POINTNUM ].y > s[k].p[j].y )
{
NET *p=new NET;
p->x = s[k].p[j].x;
p->ymax = s[k].p[ (j-1+POINTNUM) % POINTNUM ].y; //下面表示的是斜率需要用浮点型来计算float()
p->dx = ( float(s[k].p[ (j-1+POINTNUM)%POINTNUM ].x-s[k].p[j].x )) /float( ( s[k].p[ (j-1+POINTNUM) % POINTNUM ].y - s[k].p[j].y ));
p->next = pNET[i]->next;
pNET[i]->next = p;
}
if( s[k].p[ (j+1+POINTNUM ) % POINTNUM].y > s[k].p[j].y )
{
NET *p = new NET;
p->x = s[k].p[j].x;
p->ymax = s[k].p[ (j+1+POINTNUM) % POINTNUM ].y;
p->dx = ( float(s[k].p[(j+1+POINTNUM) % POINTNUM ].x-s[k].p[j].x )) / float( s[k].p[ (j+1+POINTNUM) % POINTNUM ].y- s[k].p[j].y );
p->next = pNET[i]->next;
pNET[i]->next = p;
}
}
}
}
/******建立并更新活性边表AET*****************************************************/
for( i = MinY; i <= MaxY; i++ ) // for循环中按下面的流程而不是《计算机图形学》徐长青第二版P38中Polygonfill算法中的while循环中的流程,
{ // 这样可以处理书中的边界问题,无需开始时进行边缩短
//计算新的交点x,更新AET********************************************************/
NET *p = pAET->next;
while( p != NULL )
{
p->x = p->x + p->dx;
p = p->next;
}
//更新后新AET先排序*************************************************************/
//断表排序,不再开辟空间
AET *tq = pAET;
p = pAET->next;
tq->next = NULL;
while( p != NULL )
{
while( tq->next != NULL && p->x >= tq->next->x )
{
tq = tq->next;
}
NET *s = p->next;
p->next = tq->next;
tq->next = p;
p = s;
tq = pAET;
}
//(改进算法)先从AET表中删除ymax==i的结点****************************************/
AET *q = pAET;
p = q->next;
while( p != NULL )
{
if( p->ymax == i)
{
q->next = p->next;
delete p;
p = q->next;
}
else
{
q = q->next;
p = q->next;
}
}
//将NET中的新点加入AET,并用插入法按X值递增排序**********************************/
p = pNET[i]->next;
q = pAET;
while( p != NULL )
{
while( q->next != NULL && 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 != NULL && p->next != NULL )
{
for(float j = p->x;j <= p->next->x; j++)
{
//glVertex2i(j,i);
glVertex2i(static_cast<int>(j),i);
//pDC.SetPixel( static_cast<int>(j), i, RGB(255,0,0) );
} // pDC.MoveTo( static_cast<int>(p->x), i ); 用画直线来替换上面的设置像素点颜色,速度更快
// pDC.LineTo( static_cast<int>(p->next->x), i );
p = p->next->next;//考虑端点情况
}
}
glEnd();
exam = false;
NET *phead = NULL;
NET *pnext = NULL;
//释放边表
//释放活跃边表
phead = pAET;
while( phead != NULL )
{
pnext = phead->next;
delete phead;
phead = pnext;
}
}
glFlush();
}
void lineSegment()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(red,green,blue); //设定颜色,既是线段颜色也是填充色
int i, j; //两个循环控制变量,在下面的向量循环和数组循环中将被多次调用。
//if(exam){
if(!s.empty()) //看多边形类向量是否为空,即判断除了当前正在画的多边形是否还有曾经已经画好的多边形
{
for( i = 0; i < s.size(); i++) //对多边形类向量循环,该向量中的每个元素代表一个多边形
{
int k = s[i].p.size(); //将一个多边形的点的个数,后面划线会用到
// s[i].line(); //生成多边形的边
for(j = 0; j < s[i].p.size(); j++) //画多边形
{
glBegin(GL_LINES); //将当前的点与后一个点连线
glVertex2i(s[i].p[j].x, s[i].p[j].y);
glVertex2i(s[i].p[(j+1)%k].x, s[i].p[(j+1)%k].y);//,通过取模操作来避免越界问题,该思路来源于循环队列.
glEnd();
}
// paint(s[i].edge); //为当前的多边形填充颜色
// s[i].edge.clear(); //清空当前多边形的边向量
}
}
i = 0;
j = p.size() - 1;
while(i < j) //循环画图,将当前正在画的多边形
{
glBegin(GL_LINES); //将已经确定的点连接起来
glVertex2i(p[i].x, p[i].y);
glVertex2i(p[i+1].x, p[i+1].y);
glEnd();
i++;
}
if(!p.empty())
{
// int i = p.size() - 1; //将确定的最后一个点与当前鼠标所在位置连线,即动态画线
glBegin(GL_LINES);
glVertex2i(p[j].x, p[j].y);
glVertex2i(move_x, move_y);
glEnd();
}else{ //按下F1以后将线进行重新绘制,便展现了清屏的效果
glBegin(GL_LINES);
glVertex2i(0, 0);
glVertex2i(0, 0);
glEnd();
}
//}
if(colorNum>0)
fillKColor(colorNum);
if(select) //判断右键是否被点下
{
select = false; //将状态值置为假
if(!p.empty())
{
glBegin(GL_LINES); //将多边形封闭
glVertex2i(p[j].x, p[j].y);
glVertex2i(p[0].x, p[0].y);
glEnd();
polygon sq;
//将封闭了的多边形保存到多边形类中
for(i = 0; i < p.size(); i++)
sq.p.push_back(p[i]);
s.push_back(sq); //将绘成的多边形存入多边形类向量中
// paint(sq.edge); //给当前画完的多边形填色
}
p.clear();
}
glFlush();
}
void myMouse(int button, int state ,int x, int y) //鼠标点击事件响应函数
{
if(state == GLUT_DOWN && button == GLUT_LEFT_BUTTON)//当鼠标左键被点击
{
point v; //申请一个点类变量,点类为自定义类,在zl.h中定义
v.x = x; //将点击处的点坐标,即x和y的值存入v中
v.y = screenheight - y;
p.push_back(v); //将点信息存入多边形点集向量p中
exam = true;
glutPostRedisplay(); //重绘窗口
}
if(state == GLUT_DOWN && button == GLUT_RIGHT_BUTTON) //当鼠标右键被点击
{
select = true;
glutPostRedisplay();
}
}
void myPassiveMotion(int x, int y) //鼠标移动事件响应函数
{
move_x = x; //保存当前鼠标所在的坐标的值
move_y = screenheight - y;
glutPostRedisplay();
}
void processNormalKey(unsigned char key, int x, int y) //使用组合键改变线段的颜色
{
int mod = glutGetModifiers();
if(mod ==GLUT_ACTIVE_CTRL)
{
if(key == 1) //对应CTRL+a
{
red = 1.0;
green = 0.0;
blue = 0.0;
}
if(key == 26) //对应CTRL+z
{
red = 0.0;
green = 1.0;
blue = 0.0;
}
else if(key == 24) //对应CTRL+x
{
red = 0.0;
green = 0.0;
blue = 1.0;
}
else
{
red = 1.0;
}
}
}
void processSpqcialKeys(int key, int x, int y){ //按下按键改变线条的颜色
if(key == 27){
exit(0);
}
else if(key == GLUT_KEY_UP)
{
lineWidth = lineWidth+2;
glLineWidth(lineWidth);
}
else if(key == GLUT_KEY_DOWN)
{
lineWidth = lineWidth-2;
glLineWidth(lineWidth);
}
else if(key == GLUT_KEY_F2){ //按下F2键将多边形填充
if(!s.empty()){
//PolyScan();
//fillKColor();
if(s.size()>colorNum)
colorNum = s.size();
}
}
else if(key == GLUT_KEY_F1) //按下F1键清除屏幕
{
printf("%d ",s.size()); //将的多边形全部删除
while(!s.empty()){
s.pop_back();
}
printf("%d",p.size());
while(!p.empty()){ //将最后一个多变形删除
p.pop_back();
}
exam = true;
colorNum =0;
}else if(key == GLUT_KEY_F3){ //按下F3键撤销一个刚画的边
p.pop_back();
}else if(key == GLUT_KEY_F4){ //直接撤销一个三角形
s.pop_back();
}
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(50,100);
glutInitWindowSize(screenwidth,screenheight);
glutCreateWindow("扫描线填充算法");
init();
glutMouseFunc(myMouse); //鼠标点击消息监控,即监控鼠标是否被点击,若被点击就调用myMouse函数
glutDisplayFunc(lineSegment);
glutPassiveMotionFunc(myPassiveMotion); //鼠标移动消息监控,即监控鼠标是否移动,若移动就调用myPassiveMotion函数
glutKeyboardFunc(processNormalKey); //通过按键改变线段的颜色
glutSpecialFunc(processSpqcialKeys); //通过按特殊按键改变多边形线条的大小
glutMainLoop();
return 0;
}
头文件 datastruct.h
#ifndef DATA_H_
#define DATA_H_
#include<vector>
using namespace std;
class point //点类,存储了一个点的两坐标值
{
public:
int x;
int y;
};
class polygon //多边形类,存了一个多边形
{
public:
vector<point> p; //多边形的顶点
};
#endif
typedef struct XET
{
float x;
float dx,ymax;
struct XET* next;
}AET,NET;
实现效果