OpenGL扫描线填充算法详解

本文实例为大家分享了OpenGL扫描线填充算法,供大家参考,具体内容如下

说明

把最近一系列的图形学经典算法实现了一下。课业繁忙,关于该系列的推导随后再写。但是在注释里已经有较为充分的分析。

分情况讨论

注意对于横线需要特别讨论,但是对于垂直线却不必特别讨论。想一想为什么?

代码

#include <iostream>
#include <GLUT/GLUT.h>
#include <map>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
int hmin,hmax;         //记录扫描线开始和结束的位置
struct Line {         //定义线段的结构体
 float dx,x,y,ym;       //不用记录K直接记录dx和x即可
 Line(float x1,float y1,float x2,float y2) {
  if(y1==y2){        //单独讨论横直线的情况
   this->y = y1;
   this->ym = y1;
   if(x1 < x2){
    dx = x1; x = x2;
   }else{
    dx =x2;x = x1;}
  }else if(y2<y1){      //选择靠上者的x值
   this -> x = x2;     //记录上方的x值一方便处理关键时刻(用于插入AET排序)
   this ->y = y2;      //记录上方的y值,用于排序
   this -> ym = y1;     //靠下者ym
  }else{
   this -> x = x1;
   this ->y = y1;
   this -> ym = y2;
  }
  dx = (x2-x1)/(y2-y1);
 }
};
typedef list<Line> TESTLIST;
vector<vector<Line>> con; //记录重要事件表(有序),当然这个也可以使用优先队列
list<Line> AET;     //滚动记录活动边表,这里将
           //该边表完整存储的意义不大所以采用滚动存储的方式
map<int, int> mapper;   //用于数据(y值)离散化处理
int x1,y1,x2,y2;      //描述构成直线的两个端点
int x0,y0;       //记录图形开始位置
float h_min,h_max;    //画线开始和结束的位置
int flag = 1;       //用于记录用户点击的次数,单次画点,双次画线。
int if_drawable = 1;    //当用户再次点击鼠标时不在更改信息
int window_size=600;   //这是我们显示界面的大小
vector<vector<Line>> con2;
int level = 1;
/*
 操作说明:算法没有严格的图形绘制检查。仅为了图形学算法的演示。
 您使用鼠标【左键】进行绘制点,请您保证没有线是交叉的。
 当您点击鼠标【右键】绘制最后一个点。系统会自动将其与起始点相连。
 整体思路描述:使用map将y的值离散化,用有序表记录“关键事件”主要
 是加入边(一条或者两条)删除边操作。在用一个滚动的活动边表进行遍历画线。
 */
void show_v(Line a){
 /*
  函数说明:显示点的信息
  */
 cout << "(" <<a.x << "," << a.y <<")";
 cout << " (" <<a.dx<<")" << "下限:"<<a.ym;
 cout << "  --  "<<endl;
}
bool higher(const vector<Line> & l1, const vector<Line>& l2) {
 //将关键事件表中的line按照y值进行排序;
 //注意我们的画布是从上到下不断递增从左到右不断递增
 return l1[0].y < l2[0].y;//可以保证一定至少有一个不然map不会映射到
}
bool AET_lefter(const Line & l1, const Line & l2) {
 //将AET表中的line按照x值进行排序;
 return l1.x < l2.x;//可以保证一定至少有一个不然map不会映射到
}
bool lefter(const Line & l1, const Line & l2) {
 /*
  函数说明:将关键事件表中的line按照x值以及dx进行排序;
 */
 if(l1.x < l2.x){
  return 1;
 }else if (l1.x == l2.x){
  if(l1.dx<0&&l2.dx>0)
   return 1;
  else
   return 0;
 }else
  return 0;
}
void sort_con(){
 /*
  函数说明:对关键事件表进行排序处理
  其中y从小到大递增,x方向按照斜率和x大小由左到右排序
  */
 for (int i = 0 ; i < con.size(); i++)
  if (con[i].size()>=2)
   sort(con[i].begin(),con[i].end(),lefter);
 for (int i = 0;i < con.size(); i++) {
  vector<Line> a;
  for (int j =0; j < con[i].size(); j++)
   a.push_back(con[i][j]);
  con2.push_back(a);     //这里将事件表进行拷贝,另一种方式是将map的映射对应改变
 }
 sort(con.begin(), con.end(), higher);
}
void draw_lines(float x1,float y1,float x2,float y2){
 glBegin(GL_LINES);
 glColor3f(1.0,1.0,0.0);
 glVertex2f(x1,window_size-y1);
 glVertex2f(x2,window_size-y2);
 glEnd();
 glFlush();
}
void show_con(){
 //输出排序后的关键事件表
 for (int i = 0; i < con.size(); i++) {
  cout <<"number : "<<i <<endl;
  for (int j = 0; j < con[i].size(); j++) {
   vector<Line> a = con[i];
   show_v (a[j]);
  }cout <<"================"<<endl;
 }
}
void lines_filling(){       //真正的扫描线填充过程
 if (con.empty())       //为了展示过程细节,部分功能没有使用函数ti
  return;
 int h_leveler = 0;       //高度遍历器
 map<int,int>::iterator iter;    //定义一个迭代指针iter
 for(h_leveler = h_min;h_leveler <= h_max;h_leveler++){//开始扫描
  int id = mapper[h_leveler];
  if (!id) {         //说明没有到达关键节点,我们只需要进行绘制和更新即可;
   float xx = 0.0; flag = 1;  //flag用于控制每两组画一次线
   for(list<Line> ::iterator it=AET.begin();it!=AET.end();)
   { if (flag%2==0) {   //该画线了!
     draw_lines(xx, h_leveler,it->x,h_leveler);
     for (TESTLIST::iterator pl = AET.begin(); pl != AET.end();)
      if (pl->ym == h_leveler)
       AET.erase(pl++);
      else
       pl++;    //这个负责删除的for循环在画线后执行可以避免留白情况
     it->x = it->x +it->dx;
    }else{
     if (it->y == it->ym) {
      xx = x1;
     }else{
     xx =it->x;
     it->x = it->x +it->dx;
     }
    }flag++;it++;}
  }else{         //如果到了关键事件,那么加线、去线
   list<Line> ::iterator it;
   float xx = 0.0;int counter = 1;
   for(it=AET.begin();it!=AET.end();it++)
   { Line temp= *it;
    if (counter%2==0)   //该画线了!
     draw_lines(xx, h_leveler,temp.x,h_leveler);
    else
     xx =temp.x;     //删除边前先画好线避免留白
    counter++;
   }
   for (TESTLIST::iterator it = AET.begin(); it != AET.end();)
    if (it->ym == h_leveler)
     AET.erase(it++);
    else
     it++;       //关键时间删除边
   for (int i =0 ; i < con2[id-1].size(); i++)
    if (con2[id-1][i].y == con2[id-1][i].ym)
     continue;     //如果是横线直接不用添加该横线
    else
     AET.push_back(con2[id-1][i]);
   AET.sort(AET_lefter);   //维持滚动活动边表的有序性
  }}}
void InitEnvironment()      //对环境进行初始化操作
{ glClearColor(0.0,0.0,0.0,0);
 glClear(GL_COLOR_BUFFER_BIT);
 glPointSize(7);
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 gluOrtho2D(0,window_size,0,window_size);
}
void myDisplay(void)
{ glClear(GL_COLOR_BUFFER_BIT);
 glFlush();
}
void OnMouse(int button,int state,int x,int y)
/*
 函数说明:进行用户交互的操作
 每两个点一组进行操作。设置左键、右键手势动作
 */
{if(button==GLUT_LEFT_BUTTON&&state==GLUT_DOWN&&if_drawable)
 {if (flag ==1 &&if_drawable) {
   glColor3f(1,0,0);
   glBegin(GL_POINTS);
   glVertex2f(x,window_size-y);
   x0 = x;y0 =y;
   x1 = x;y1 = y;
   h_min = y0;
   h_max = y0;
   glEnd();
   glFlush();
   flag++;
  }else{
   glColor3f(1,0,0);
   glBegin(GL_POINTS);
   glVertex2f(x,window_size-y);
   glEnd();
   x2 = x;y2 = y;
   glBegin(GL_LINES);
   glColor3f(1.0,0.0,0.0);
   glVertex2f(x1,window_size-y1);
   glVertex2f(x2,window_size-y2);
   if (y1 !=y2) {
    Line a(x1,y1,x2,y2);
   int r_y = min (y1,y2);
    if (y1 < h_min)
     h_min = y1;
    if (y2 < h_min)
     h_min = y2;
    if (y1 > h_max)
     h_max = y1;
    if (y2 >h_max)
     h_max = y2;
   int pos = mapper[r_y];
   if (pos==0) {   //说明该变量还没有离散化
    mapper[r_y] = level++;
    vector<Line> lines;
    lines.push_back(a);
    con.push_back(lines);}
   else
    con[pos-1].push_back(a);
   }
   x1 = x2; y1 = y2;
   glEnd();
   glFlush();
  }
 }
 if(button==GLUT_RIGHT_BUTTON&&state==GLUT_DOWN&&if_drawable)
 { //点击右键
  glColor3f(1,0,0);
  glBegin(GL_POINTS);
  glVertex2f(x,window_size-y);
  glEnd();
  x2 = x;y2 = y;
  glBegin(GL_LINES);
  glColor3f(1.0,0.0,0.0);
  glVertex2f(x1,window_size-y1);
  glVertex2f(x2,window_size-y2);
  Line a(x1,y1,x2,y2);
  int r_y = min (y1,y2);
  int pos = mapper[r_y];
  if (pos==0) {   //说明该变量还没有离散化
   mapper[r_y] = level++;
   vector<Line> lines;
   lines.push_back(a);
   con.push_back(lines);}
  else
   con[pos-1].push_back(a);
  glEnd();
  glFlush();
  glBegin(GL_LINES);
  glColor3f(1.0,0.0,0.0);
  glVertex2f(x0,window_size-y0);
  glVertex2f(x2,window_size-y2);
  glEnd();
  glFlush();
  Line aa(x0,y0,x2,y2);
  r_y = min (y0,y2);
  pos = mapper[r_y];
  if (pos==0) {   //说明该变量还没有离散化
   mapper[r_y] = level++;
   vector<Line> lines;
   lines.push_back(aa);
   con.push_back(lines);}
  else
   con[pos-1].push_back(aa);
  sort_con();
  lines_filling();
  if_drawable = 0;
 }
}

int main(int argc, char *argv[])
{ glutInit(&argc, argv); //初始化GLUT
 glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
 glutInitWindowPosition(300, 100);
 glutInitWindowSize(window_size, window_size);
 glutCreateWindow("hw2_filling_line");
 InitEnvironment(); //初始化
 glutMouseFunc(&OnMouse); //注册鼠标事件
 glutDisplayFunc(&myDisplay); //回调函数
 glutMainLoop(); //持续显示,当窗口改变会重新绘制图形
 return 0;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • OpenGL实现边缘填充算法

    边缘填充的思想:枚举每一条边,对这条边右边的像素进行求补操作,就是有填充则去掉,无填充就进行填充 #include <GL/gl.h> #include <GL/glut.h> #include <iostream> #include <cmath> #include <cstring> #include <string> using namespace std; int x[]= {10,70,70,60,40,30,20}; in

  • OpenGL扫描线填充算法详解

    本文实例为大家分享了OpenGL扫描线填充算法,供大家参考,具体内容如下 说明 把最近一系列的图形学经典算法实现了一下.课业繁忙,关于该系列的推导随后再写.但是在注释里已经有较为充分的分析. 分情况讨论 注意对于横线需要特别讨论,但是对于垂直线却不必特别讨论.想一想为什么? 代码 #include <iostream> #include <GLUT/GLUT.h> #include <map> #include <vector> #include <l

  • python扫描线填充算法详解

    本文实例为大家分享了python扫描线填充算法,供大家参考,具体内容如下 介绍 1.用水平扫描线从上到下扫描由点线段构成的多段构成的多边形. 2.每根扫描线与多边形各边产生一系列交点.将这些交点按照x坐标进行分类,将分类后的交点成对取出,作为两个端点,以所填的色彩画水平直线. 3.多边形被扫描完毕后,填色也就完成. 数据结构 活性边表: 新边表: 代码(使用数组) import numpy as np from PIL import Image from PIL import ImageDraw

  • OpenCV图像分割之分水岭算法与图像金字塔算法详解

    目录 前言 一.使用分水岭算法分割图像 1.cv2.distanceTransform()函数 2.cv2.connectedComponents()函数 3.cv2.watershed()函数 二.图像金字塔 1.高斯金字塔向下采样 2.高斯金字塔向上采样 3.拉普拉斯金字塔 4.应用图像金字塔实现图像的分割和融合 前言 主要介绍OpenCV中的分水岭算法.图像金字塔对图像进行分割的方法. 一.使用分水岭算法分割图像 分水岭算法的基本原理为:将任意的灰度图像视为地形图表面,其中灰度值高的部分表

  • Java求最小生成树的两种算法详解

    目录 1 最小生成树的概述 2 普里姆算法(Prim) 2.1 原理 2.2 案例分析 3 克鲁斯卡尔算法(Kruskal) 3.1 原理 3.2 案例分析 4 邻接矩阵加权图实现 5 邻接表加权图实现 6 总结 介绍了图的最小生成树的概念,然后介绍了求最小生成树的两种算法:Prim算法和Kruskal算法的原理,最后提供了基于邻接矩阵和邻接链表的图对两种算法的Java实现. 阅读本文需要一定的图的基础,如果对于图不是太明白的可以看看这篇文章:Java数据结构之图的原理与实现. 1 最小生成树的

  • 可能是你看过最全的十大排序算法详解(完整版代码)

    目录 前言 交集排序 冒泡 简单 快速排序 插入排序 直接插入排序 希尔排序 选择排序 简单选择排序 堆排序 归并排序 二路 多路 非比较类 计数排序 桶排序 基数排序 最后 前言 兄弟们,应上篇数据结构的各位要求,今天我开始工作了,开始肝算法,剑指offer还在路上,我真想开车去接它,奈何码神没有驾照的开车,算了,弄排序算法吧,有点长,耐心看啊,原创不易,你们懂的,先上一张图 可以看出排序算法,还是比较多的,算了,不多说了,你我肝完就是出门自带4年实习经验的! 交集排序 冒泡 冒泡我一般也将它

  • python算法演练_One Rule 算法(详解)

    这样某一个特征只有0和1两种取值,数据集有三个类别.当取0的时候,假如类别A有20个这样的个体,类别B有60个这样的个体,类别C有20个这样的个体.所以,这个特征为0时,最有可能的是类别B,但是,还是有40个个体不在B类别中,所以,将这个特征为0分到类别B中的错误率是40%.然后,将所有的特征统计完,计算所有的特征错误率,再选择错误率最低的特征作为唯一的分类准则--这就是OneR. 现在用代码来实现算法. # OneR算法实现 import numpy as np from sklearn.da

  • python实现决策树C4.5算法详解(在ID3基础上改进)

    一.概论 C4.5主要是在ID3的基础上改进,ID3选择(属性)树节点是选择信息增益值最大的属性作为节点.而C4.5引入了新概念"信息增益率",C4.5是选择信息增益率最大的属性作为树节点. 二.信息增益 以上公式是求信息增益率(ID3的知识点) 三.信息增益率 信息增益率是在求出信息增益值在除以. 例如下面公式为求属性为"outlook"的值: 四.C4.5的完整代码 from numpy import * from scipy import * from mat

  • java 中归并排序算法详解

    java 中归并排序算法详解 归并排序算法,顾名思义,是一种先分再合的算法,其算法思想是将要排序的数组分解为单个的元素,每个元素就是一个单个的个体,然后将相邻的两个元素进行从小到大或从大到小的顺序排序组成一个整体,每个整体包含一到两个元素,然后对相邻的整体继续"合"并,因为每个整体都是排过序的,因而可以采用一定的算法对其进行合并,合并之后每个整体包含三到四个元素,继续对相邻的整体进行合并,直到所有的整体都合并为一个整体,最终得到的整体就是将原数组进行排序之后的结果. 对于相邻的整体,其

  • python中实现k-means聚类算法详解

    算法优缺点: 优点:容易实现 缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢 使用数据类型:数值型数据 算法思想 k-means算法实际上就是通过计算不同样本间的距离来判断他们的相近关系的,相近的就会放到同一个类别中去. 1.首先我们需要选择一个k值,也就是我们希望把数据分成多少类,这里k值的选择对结果的影响很大,Ng的课说的选择方法有两种一种是elbow method,简单的说就是根据聚类的结果和k的函数关系判断k为多少的时候效果最好.另一种则是根据具体的需求确定,比如说进行衬衫尺寸的聚

  • Python编程实现蚁群算法详解

    简介 蚁群算法(ant colony optimization, ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型算法.它由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为.蚁群算法是一种模拟进化算法,初步的研究表明该算法具有许多优良的性质.针对PID控制器参数优化设计问题,将蚁群算法设计的结果与遗传算法设计的结果进行了比较,数值仿真结果表明,蚁群算法具有一种新的模拟进化优化方法的有效性和应用价值. 定义 各个蚂蚁在没有事先告诉

随机推荐