文章目录

  • 其他计算机图形学实验
  • 前言
  • 思路借鉴
  • 步骤
    • 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. 计算机图形学:多边形填充算法(算法原理及代码实现)

    一.实现方案 扫描线算法: 实现原理: 把图形的填充转换为扫描线从上往下扫描填充,这时我们只需要判断每一条扫描线与图形的交点,而我们可以根据扫描线的连贯性,对交点进行排序,第1个点与第2个点之间,第3 ...

  3. 【计算机图形学 】Cohen-Sutherland 直线裁剪算法 | OpenGL+鼠标交互

    文章目录 其他计算机图形学实验 前言 代码借鉴 步骤 1.点的结构体 2. 创建用于裁剪的窗口并绘制 3.画点函数 4. Cohen-Sutherland 直线裁剪算法部分 4.1 判断点所在位置,生 ...

  4. 图形学初步----------多边形填充算法

    参考博文: https://blog.csdn.net/xiaowei_cqu/article/details/7693985 https://blog.csdn.net/xiaowei_cqu/ar ...

  5. 计算机图形学 OpenGl-种子填充算法画红黄绿交通灯

    一.实验原理 我就不多叙述了,课本上说的已经够多了,其次我自己也是借鉴别人的代码写出来的 二.上机环境 VS2010 三.代码运行效果 四.完整代码 #include <GL/glut.h> ...

  6. 计算机图形学绘制多边形代码_《GPU编程与CG语言之阳春白雪下里巴人》- 第二章(GPU 图形绘制管线)...

    第二章 GPU 图形绘制管线 万事开头难,每门科学都是如此. ------ 马克思 图形绘制管线描述 GPU 渲染流程,即"给定视点.三维物体.光源.照明模式,和纹理等元素,如何绘制一幅二维 ...

  7. 多边形区域填充算法--扫描线种子填充算法

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow http://bl ...

  8. 图形学初步--------种子填充算法

    上篇博文讲到了填充算法的扫描线填充,这篇博文讲解另一大算法思路----------种子填充. 一.概念 种子填充算法假设在多边形或区域内部至少有一个像素是已知的.然后设法找到区域内所有其他像素,并对它 ...

  9. java实现种子填充算法,Java编写图形学的种子填充算法

    用C写的图形学填充算法已经很多了,看到不少帖子都是在问关于如何用Java编写图形学的填充算法,说来也巧,我刚好要做一个这个方面的实验,用的是扫描线种子填充算法,由于时间仓促,代码质量可能不算很高,希望 ...

最新文章

  1. 那些学校计算机招不满,那些招不满人的985院校,请留意!
  2. 面试题目集锦 -- 排序算法
  3. 那些做中台的程序员,后来都怎么样了?
  4. java并行流 阻塞主线程_多线程入门案例与java8的并行流
  5. BizTalk开发系列(十二) Schema设计之Group与Order
  6. tl wn322g linux驱动下载,怎样才能装好tl_wn322G+V2.0版USB无线网卡的Linux驱动
  7. matlab的示波器保存figure图像
  8. zabbix邮件内容乱码与邮件内容为附件解决办法
  9. DWA论文解析(CurvatureVelovityMethod)(3)
  10. JMeter java.lang.OutOfMemoryError: PermGen space错误
  11. 在Linux下基于路由策略的IP地址控制
  12. 3台机器配置hadoop集群_复制Hadoop集群之后无法访问端口50070的问题
  13. R7000刷梅林固件一个小结(变砖解决)
  14. Qt5设置应用程序图标报错Debug Error 1
  15. 商业创新奇才,巧用大数据分析带你穿越古代当首富
  16. ecshop二次开发手册【基本结构】
  17. JavaScript Canvas2D实现SpriteSheet角色动画
  18. 美国撞击小行星的宇宙飞船成功改变行星轨道
  19. python自动化 html 翻译转 pdf
  20. html恋爱纪念页面,HTML5适合的情人节礼物有纪念日期功能

热门文章

  1. 拼多多开店不做推广能行吗?
  2. 【Python-Keras】keras.layers.BatchNormalization解析与使用
  3. OPC (OLE for Process Control)
  4. java简单程序彩票系统!
  5. 洪柱森老师介绍--沪师经纪-刘建
  6. 本地虚拟机搭建nginx web服务器
  7. Java----映射 map
  8. 【微信开发】基于微信公众号的早起签到程序
  9. 傅里叶变换的解释与推导
  10. 一个癌症病人的美国求医经历:活人死人如果都得不到尊重,病人也很难被尊重