【计算机图形学 】扫描线多边形填充算法 | OpenGL+鼠标交互
文章目录
- 其他计算机图形学实验
- 前言
- 思路借鉴
- 步骤
- 1.点的结构体
- 2. AET 活性边表、NET新边表 的结构体
- 3. 扫描线算法实现
- 4. 改变鼠标响应函数
- 完整代码
- 总结
其他计算机图形学实验
传送门
前言
实现多边形扫描线填充算法,并和鼠标进行交互。
具体原理略过,会贴上完整代码,可直接运行。
环境:
vs2019,OpenGL的库(可以搜索如何用vs使用OpenGL的库,可以使用vs自带的插件或者其他方法,很方便)
要点:
1.NET和AET的创建,改动
2.改变鼠标点击和鼠标拖拽的响应事件。
最终效果:
用鼠标随意画顶点,然后展示填充过程
对应控制台会输出顶点坐标和个数
思路借鉴
文章1
文章2
步骤
1.点的结构体
struct point
{float x, y;point(){}point(int xx, int yy):x(xx), y(yy) {}
};
vector<point> vertice; //顶点
2. AET 活性边表、NET新边表 的结构体
typedef struct XET
{float x;float dx; // 从当前扫描线到下一条扫描线间x的增量,即斜率的倒数float ymax; //该边所交的最高扫描线的坐标值ymaxXET* next;
}AET, NET; //AET 活性边表; NET新边表
3. 扫描线算法实现
void PolyScan()
{/*得到最高点的y坐标*/int Max_Y = 0;for (int i = 0; i < vertice.size(); i++) /*Max_Y = max(Max_Y, vertice[i].y);*/if (vertice[i].y > Max_Y)Max_Y = vertice[i].y;//初始化AET表AET* pAET = new AET;pAET->next = NULL;//初始化NET表NET* pNET[800]; //吊桶for (int i = 0; i <= Max_Y; i++){pNET[i] = new NET;pNET[i]->next = NULL;;}//扫描并且建立NET表int len = vertice.size(); //顶点个数for (int i = 0; i <= Max_Y; i++){for (int j = 0; j < len; j++) //扫描每个点{if (i == vertice[j].y){//如果一个点和前一个点有一条边相连,则该点和后面一个点也相连//!这个式子 便于最后一个顶点和第一个点相连 和 防止出现负数//判断当前点的高低,使ymax、DX、DY的计算有变化if (vertice[(j - 1 + len) % len].y > vertice[j].y){//前一个点在当前点的上方NET* p = new NET;p->x = vertice[j].x;p->ymax = vertice[(j - 1 + len) % len].y;//与当前扫描线相交的活性边 的 最高点即为相邻顶点的yfloat DX = vertice[(j - 1 + len) % len].x - vertice[j].x;float DY = vertice[(j - 1 + len) % len].y - vertice[j].y;p->dx = DX / DY;//dx为直线斜率的倒数p->next = pNET[i]->next;pNET[i]->next = p;}if (vertice[(j + 1) % len].y > vertice[j].y){//后一个点在当前点的上方NET* p = new NET;p->x = vertice[j].x;p->ymax = vertice[(j + 1) % len].y;float DX = vertice[(j + 1) % len].x - vertice[j].x;float DY = vertice[(j + 1) % len].y - vertice[j].y;p->dx = DX / DY;//dx为直线斜率的倒数p->next = pNET[i]->next;pNET[i]->next = p;}}}}//建立并且更新活性边表AET//各条扫描线ifor (int i = 0; i <= Max_Y; i++){/*把新边表NET[i] 中的边结点用插入排序法插入AET表,使之按x坐标递增顺序排列*///计算每条扫描线上不同线产生的新的交点x,更新AETNET* p = pAET->next;while (p){p->x = p->x + p->dx; //更新x坐标p = p->next;}//断表排序,不再开辟空间 AET* tq = pAET;p = pAET->next;tq->next = NULL;while (p)//顺着链表往下走{//找到第一个比它大的数字tq->next->next->x,则从p->next到tq->next都是比p->x小的while (tq->next != NULL && tq->next->x <= p->x)tq = tq->next;//插入p到tq和tq->next之间NET* t = p->next;p->next = tq->next;tq->next = p;p = t;tq = pAET;//回到头}/*(改进算法) 取消求交,减少计算量*///先从AET表中删除ymax==i的结点****************************************///像素的取舍问题,保证多边形的“下闭上开”,避免填充扩大化(交点的个数应保证为偶数个)AET* q = pAET;p = q->next;while (p){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){while (q->next && p->x >= q->next->x)q = q->next;//插入pNET* t = p->next;p->next = q->next;q->next = p;p = t;q = pAET;//回到头}//配对后填充颜色p = pAET->next;while (p && p->next != NULL){for (float j = p->x; j <= p->next->x; j++){//扫描线画点draw_a_point(j, i);//cout << "(" << j << ", " << i << ")" << endl;}p = p->next->next;//考虑端点情况}}glFlush();
}
4. 改变鼠标响应函数
void mymouse(int button, int state, int x, int y)
{//左键if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){draw_a_point(x, window_height - y);point p(x, window_height - y);vertice.push_back(p);cout << "顶点" << vertice.size() << ": (" << x << ", " << window_height - y << ")" << endl;}//右键if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN){glClearColor(1, 1, 1, 1);//设置绘制窗口颜色为白色glColor3f(0, 1, 1);//绘制多边形glBegin(GL_LINES); for (int i = 0; i < vertice.size(); i++){if (i == vertice.size() - 1)//画完最后一个点,使其闭合{glVertex2f(vertice[0].x, vertice[0].y);glVertex2f(vertice[i].x, vertice[i].y);}else{glVertex2f(vertice[i].x, vertice[i].y);glVertex2f(vertice[i + 1].x, vertice[i + 1].y);}}glEnd();glFlush();}//鼠标中间if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN){//cout << "center: (" << x << ", " << y << ")" << endl;//BoundaryFill4(x, window_height - y);//BoundaryFill4_Stack(x, window_height - y);cout << "多边形顶点个数为" << vertice.size() << "。 " << "开始扫描线填充。" << endl;PolyScan();}
}
完整代码
//扫描线算法
#include<iostream>
#include<gl/glut.h>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
const int window_width = 800, window_height = 600;
const int maxn = 99999;struct point
{float x, y;point(){}point(int xx, int yy):x(xx), y(yy) {}
};
vector<point> vertice; //顶点typedef struct XET
{float x;float dx; // 从当前扫描线到下一条扫描线间x的增量,即斜率的倒数float ymax; //该边所交的最高扫描线的坐标值ymaxXET* next;
}AET, NET; //AET 活性边表; NET新边表void draw_a_point(int x, int y);
void PolyScan();
void mymouse(int button, int state, int x, int y);
void display();int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);glutInitWindowPosition(100, 50);glutInitWindowSize(window_width, window_height);glutCreateWindow("扫描线填充");glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0, window_width, 0, window_height);glClearColor(1, 1, 1, 1);glClear(GL_COLOR_BUFFER_BIT);glutMouseFunc(&mymouse);glutDisplayFunc(&display);glutMainLoop();return 0;
}//画点函数
void draw_a_point(int x, int y)
{glBegin(GL_POINTS);glColor3f(0, 1, 1);glVertex2f(x, y);glEnd();glFlush();
}void PolyScan()
{/*得到最高点的y坐标*/int Max_Y = 0;for (int i = 0; i < vertice.size(); i++) /*Max_Y = max(Max_Y, vertice[i].y);*/if (vertice[i].y > Max_Y)Max_Y = vertice[i].y;//初始化AET表AET* pAET = new AET;pAET->next = NULL;//初始化NET表NET* pNET[800]; //吊桶for (int i = 0; i <= Max_Y; i++){pNET[i] = new NET;pNET[i]->next = NULL;;}//扫描并且建立NET表int len = vertice.size(); //顶点个数for (int i = 0; i <= Max_Y; i++){for (int j = 0; j < len; j++) //扫描每个点{if (i == vertice[j].y){//如果一个点和前一个点有一条边相连,则该点和后面一个点也相连//!这个式子 便于最后一个顶点和第一个点相连 和 防止出现负数//判断当前点的高低,使ymax、DX、DY的计算有变化if (vertice[(j - 1 + len) % len].y > vertice[j].y){//前一个点在当前点的上方NET* p = new NET;p->x = vertice[j].x;p->ymax = vertice[(j - 1 + len) % len].y;//与当前扫描线相交的活性边 的 最高点即为相邻顶点的yfloat DX = vertice[(j - 1 + len) % len].x - vertice[j].x;float DY = vertice[(j - 1 + len) % len].y - vertice[j].y;p->dx = DX / DY;//dx为直线斜率的倒数p->next = pNET[i]->next;pNET[i]->next = p;}if (vertice[(j + 1) % len].y > vertice[j].y){//后一个点在当前点的上方NET* p = new NET;p->x = vertice[j].x;p->ymax = vertice[(j + 1) % len].y;float DX = vertice[(j + 1) % len].x - vertice[j].x;float DY = vertice[(j + 1) % len].y - vertice[j].y;p->dx = DX / DY;//dx为直线斜率的倒数p->next = pNET[i]->next;pNET[i]->next = p;}}}}//建立并且更新活性边表AET//各条扫描线ifor (int i = 0; i <= Max_Y; i++){/*把新边表NET[i] 中的边结点用插入排序法插入AET表,使之按x坐标递增顺序排列*///计算每条扫描线上不同线产生的新的交点x,更新AETNET* p = pAET->next;while (p){p->x = p->x + p->dx; //更新x坐标p = p->next;}//断表排序,不再开辟空间 AET* tq = pAET;p = pAET->next;tq->next = NULL;while (p)//顺着链表往下走{//找到第一个比它大的数字tq->next->next->x,则从p->next到tq->next都是比p->x小的while (tq->next != NULL && tq->next->x <= p->x)tq = tq->next;//插入p到tq和tq->next之间NET* t = p->next;p->next = tq->next;tq->next = p;p = t;tq = pAET;//回到头}/*(改进算法) 取消求交,减少计算量*///先从AET表中删除ymax==i的结点****************************************///像素的取舍问题,保证多边形的“下闭上开”,避免填充扩大化(交点的个数应保证为偶数个)AET* q = pAET;p = q->next;while (p){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){while (q->next && p->x >= q->next->x)q = q->next;//插入pNET* t = p->next;p->next = q->next;q->next = p;p = t;q = pAET;//回到头}//配对后填充颜色p = pAET->next;while (p && p->next != NULL){for (float j = p->x; j <= p->next->x; j++){//扫描线画点draw_a_point(j, i);//cout << "(" << j << ", " << i << ")" << endl;}p = p->next->next;//考虑端点情况}}glFlush();
}
void mymouse(int button, int state, int x, int y)
{//左键if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){draw_a_point(x, window_height - y);point p(x, window_height - y);vertice.push_back(p);cout << "顶点" << vertice.size() << ": (" << x << ", " << window_height - y << ")" << endl;}//右键if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN){glClearColor(1, 1, 1, 1);//设置绘制窗口颜色为白色glColor3f(0, 1, 1);//绘制多边形glBegin(GL_LINES); for (int i = 0; i < vertice.size(); i++){if (i == vertice.size() - 1)//画完最后一个点,使其闭合{glVertex2f(vertice[0].x, vertice[0].y);glVertex2f(vertice[i].x, vertice[i].y);}else{glVertex2f(vertice[i].x, vertice[i].y);glVertex2f(vertice[i + 1].x, vertice[i + 1].y);}}glEnd();glFlush();}//鼠标中间if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN){//cout << "center: (" << x << ", " << y << ")" << endl;//BoundaryFill4(x, window_height - y);//BoundaryFill4_Stack(x, window_height - y);cout << "多边形顶点个数为" << vertice.size() << "。 " << "开始扫描线填充。" << endl;PolyScan();}
}
void display()
{glClear(GL_COLOR_BUFFER_BIT);glColor3f(0.0, 0.4, 0.2);glPointSize(1);glBegin(GL_POINTS);PolyScan();glEnd();glFlush();
}
总结
扫描线算法部分,建立NET 和 建立并且更新活性边表AET 这两个地方比较复杂,可以带入图中多想
【计算机图形学 】扫描线多边形填充算法 | OpenGL+鼠标交互相关推荐
- 计算机图形学 ———— 扫描线多边形填充算法 (讲解)
一.基本原理 扫描线多边形区域填充算法是按扫描线顺序(由下到上),计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作. ...
- 计算机图形学:多边形填充算法(算法原理及代码实现)
一.实现方案 扫描线算法: 实现原理: 把图形的填充转换为扫描线从上往下扫描填充,这时我们只需要判断每一条扫描线与图形的交点,而我们可以根据扫描线的连贯性,对交点进行排序,第1个点与第2个点之间,第3 ...
- 【计算机图形学 】Cohen-Sutherland 直线裁剪算法 | OpenGL+鼠标交互
文章目录 其他计算机图形学实验 前言 代码借鉴 步骤 1.点的结构体 2. 创建用于裁剪的窗口并绘制 3.画点函数 4. Cohen-Sutherland 直线裁剪算法部分 4.1 判断点所在位置,生 ...
- 图形学初步----------多边形填充算法
参考博文: https://blog.csdn.net/xiaowei_cqu/article/details/7693985 https://blog.csdn.net/xiaowei_cqu/ar ...
- 计算机图形学 OpenGl-种子填充算法画红黄绿交通灯
一.实验原理 我就不多叙述了,课本上说的已经够多了,其次我自己也是借鉴别人的代码写出来的 二.上机环境 VS2010 三.代码运行效果 四.完整代码 #include <GL/glut.h> ...
- 计算机图形学绘制多边形代码_《GPU编程与CG语言之阳春白雪下里巴人》- 第二章(GPU 图形绘制管线)...
第二章 GPU 图形绘制管线 万事开头难,每门科学都是如此. ------ 马克思 图形绘制管线描述 GPU 渲染流程,即"给定视点.三维物体.光源.照明模式,和纹理等元素,如何绘制一幅二维 ...
- 多边形区域填充算法--扫描线种子填充算法
分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow http://bl ...
- 图形学初步--------种子填充算法
上篇博文讲到了填充算法的扫描线填充,这篇博文讲解另一大算法思路----------种子填充. 一.概念 种子填充算法假设在多边形或区域内部至少有一个像素是已知的.然后设法找到区域内所有其他像素,并对它 ...
- java实现种子填充算法,Java编写图形学的种子填充算法
用C写的图形学填充算法已经很多了,看到不少帖子都是在问关于如何用Java编写图形学的填充算法,说来也巧,我刚好要做一个这个方面的实验,用的是扫描线种子填充算法,由于时间仓促,代码质量可能不算很高,希望 ...
最新文章
- 那些学校计算机招不满,那些招不满人的985院校,请留意!
- 面试题目集锦 -- 排序算法
- 那些做中台的程序员,后来都怎么样了?
- java并行流 阻塞主线程_多线程入门案例与java8的并行流
- BizTalk开发系列(十二) Schema设计之Group与Order
- tl wn322g linux驱动下载,怎样才能装好tl_wn322G+V2.0版USB无线网卡的Linux驱动
- matlab的示波器保存figure图像
- zabbix邮件内容乱码与邮件内容为附件解决办法
- DWA论文解析(CurvatureVelovityMethod)(3)
- JMeter java.lang.OutOfMemoryError: PermGen space错误
- 在Linux下基于路由策略的IP地址控制
- 3台机器配置hadoop集群_复制Hadoop集群之后无法访问端口50070的问题
- R7000刷梅林固件一个小结(变砖解决)
- Qt5设置应用程序图标报错Debug Error 1
- 商业创新奇才,巧用大数据分析带你穿越古代当首富
- ecshop二次开发手册【基本结构】
- JavaScript Canvas2D实现SpriteSheet角色动画
- 美国撞击小行星的宇宙飞船成功改变行星轨道
- python自动化 html 翻译转 pdf
- html恋爱纪念页面,HTML5适合的情人节礼物有纪念日期功能