什么是多边形?

  • 多边形是由折线段组成的封闭图形

多边形的表示方法有哪些?

  • 顶点表示法:使用顶点进行描述,但此时多边形仅仅是一些封闭线段,内部是空的,且不能直接进行填充上色
  • 点阵表示法:使用大量的点进行描述,描述完成之后,得到的就是完整形态的多边形,内部已被填充,可直接针对点来进行上色

多边形的扫描转换就是从顶点表示法转换到点阵表示法的过程。

基础的填充多边形方式:

  • 检查光栅上的每一个像素是否位于多边形内

光栅究竟是什么?

  • 由大量等宽等间距的平行狭缝构成的光学器件称为光栅,这是专业且准确的方法,然而明显不是给人看的(观众:???)
  • 光栅是连接帧缓冲和具体的电脑屏幕的桥梁(这是很老的大头显示器上的,现在的液晶显示器不存在光栅,它的成像依靠的是电场,液晶,滤光膜等,所以我们暂且把这里说的的光栅理解为像素)

光栅化究竟是什么?

  • https://blog.csdn.net/waitforfree/article/details/10066547
  • 光栅化是一切屏幕成像的基础,没有它,就没有图像
  • 光栅化不依赖于光栅,它依赖于CPU和GPU的交互和运算

有效边表填充算法:

  • 基本原理:按照扫描线从小到大的移动顺序,计算当前扫描线与有效边的交点,然后把这些交点按x的值递增顺序进行排序,配对,以确定填充去间,最后用指定颜色填充区间内的所有像素,即完成填充工作
  • 优势:通过维护边表和有效边表,避开了扫描线与多边形所有边求交的复杂运算,性能提升巨大
  • 边界像素处理原则:对于边界像素,采用“左闭右开”和“下闭上开”的原则
  • 有效边:多边形与当前扫描线相交的边
  • 有效边表:把有效边按照与扫描线交点x坐标递增的顺序存放在一个链表中,称为有效边表
  • 桶表:按照扫描线顺序管理边的数据结构

算法实现:

将VC 6.0 调整到ClassView视图

创建有效边表和桶表类

将VC 6.0调整到FileView视图,对两个类进行定义

AET.h

class CAET
{
public:CAET();virtual ~CAET();
public:double  x;//当前扫描线与有效边交点的x坐标int     yMax;//边的最大y值double  k;//斜率的倒数(x的增量)CAET   *pNext;
};

Bucket.h

#include "AET.h"
#include "Bucket.h"class CBucket
{
public:CBucket();virtual ~CBucket();
public:int     ScanLine;     //扫描线CAET    *pET;         //桶上的边表指针CBucket *pNext;
};

回到ClassView视图,像之前那样创建CFill类,再回到FileView视图,对CFill类进行定义

Fill.h

#include "AET.h"
#include "Bucket.h"class CFill
{
public:CFill();virtual ~CFill();void SetPoint(CPoint *p,int);//初始化顶点和顶点数目void CreateBucket();//创建桶void CreateEdge();//边表void AddET(CAET *);//合并ET表void ETOrder();//ET表排序void Gouraud(CDC *);//填充多边形void ClearMemory();//清理内存void DeleteAETChain(CAET* pAET);//删除边表
protected:int     PNum;//顶点个数CPoint    *P;//顶点坐标动态数组CAET    *pHeadE,*pCurrentE,*pEdge; //有效边表结点指针CBucket *pHeadB,*pCurrentB;        //桶表结点指针
};

Fill.cpp

// Fill.cpp: implementation of the CFill class.
//
//#include "stdafx.h"
#include "Study3.h"
#include "Fill.h"
#include "AET.h"
#include "Bucket.h"#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//
// Construction/Destruction
//
CFill::CFill()
{PNum=0;P=NULL;pEdge=NULL;pHeadB=NULL;pHeadE=NULL;pCurrentB = NULL;pCurrentE = NULL;
}CFill::~CFill()
{if(P!=NULL){delete[] P;P=NULL;}ClearMemory();
}void CFill::SetPoint(CPoint *p,int m)初始化顶点和顶点数目
{P=new CPoint[m];//创建一维动态数组,转储CTestView类中P数组中的顶点for(int i=0;i<m;i++){P[i]=p[i];  }PNum=m;//记录顶点数量
}void CFill::CreateBucket()//创建桶表
{int yMin,yMax;yMin = yMax = P[0].y;for(int i = 0;i<PNum;i++){if(P[i].y<yMin){yMin = P[i].y;}if(P[i].y>yMax){yMax = P[i].y;}}for(int y = yMin;y<=yMax;y++){if(yMin == y){pHeadB = new CBucket;pCurrentB = pHeadB;pCurrentB->ScanLine = yMin;pCurrentB->pET = NULL;pCurrentB->pNext = NULL;}else{pCurrentB->pNext = new CBucket;pCurrentB=pCurrentB->pNext;pCurrentB->ScanLine = y;pCurrentB->pET=NULL;pCurrentB->pNext=NULL;}}
}void CFill::CreateEdge()//创建边表,即将各边链入到相应的桶节点
{for(int i = 0;i<PNum;i++){pCurrentB=pHeadB;int j = (i+1)%PNum;if(P[i].y<P[j].y){pEdge = new CAET;pEdge->x=P[i].x;pEdge->yMax=P[j].y;pEdge->k = (double)(P[j].x-P[i].x)/((double)(P[j].y-P[i].y));pEdge->pNext = NULL;while(pCurrentB->ScanLine!=P[i].y){pCurrentB=pCurrentB->pNext;}}if(P[j].y<P[i].y){pEdge=new CAET;pEdge->x=P[j].x;pEdge->yMax=P[i].y;pEdge->k = (double)(P[i].x-P[j].x)/((double)(P[i].y-P[j].y));pEdge->pNext = NULL;while(pCurrentB->ScanLine!=P[j].y){pCurrentB=pCurrentB->pNext;}}if(P[j].y!=P[i].y){pCurrentE=pCurrentB->pET;if(pCurrentE==NULL){pCurrentE=pEdge;pCurrentB->pET=pCurrentE;}else{while(NULL!=pCurrentE->pNext){pCurrentE=pCurrentE->pNext;}pCurrentE->pNext=pEdge;}}}
}
void CFill::AddET(CAET *pNewEdge)//合并ET表
{CAET *pCE=pHeadE;//边表头结点if(pCE==NULL)//若边表为空,则pNewEdge作为边表头结点{pHeadE=pNewEdge;pCE=pHeadE;}else//将pNewEdge链接到边表末尾(未排序){while(pCE->pNext!=NULL){pCE=pCE->pNext;}pCE->pNext=pNewEdge;}
}void CFill::ETOrder()//边表的冒泡排序算法
{CAET *pT1,*pT2;int Count=1;pT1=pHeadE;if(pT1==NULL)//没有边,不需要排序{return;}if(pT1->pNext==NULL)//如果该ET表没有再连ET表{return;//只有一条边,不需要排序}while(pT1->pNext!=NULL)//统计边结点的个数{Count++;pT1=pT1->pNext;}for(int i=0;i<Count-1;i++)//冒泡排序{CAET **pPre=&pHeadE;//pPre记录当面两个节点的前面一个节点,第一次为头节点pT1=pHeadE;for (int j=0;j<Count-1-i;j++){pT2=pT1->pNext;if ((pT1->x>pT2->x)||((pT1->x==pT2->x)&&(pT1->k>pT2->k)))//满足条件,则交换当前两个边结点的位置{pT1->pNext=pT2->pNext;pT2->pNext=pT1;*pPre=pT2;pPre=&(pT2->pNext);//调整位置为下次遍历准备}else//不交换当前两个边结点的位置,更新pPre和pT1{pPre=&(pT1->pNext);pT1=pT1->pNext;}}}
}void CFill::Gouraud(CDC *pDC)//填充多边形
{CAET *pT1=NULL,*pT2=NULL;pHeadE=NULL;for(pCurrentB=pHeadB;pCurrentB!=NULL;pCurrentB=pCurrentB->pNext){for(pCurrentE=pCurrentB->pET;pCurrentE!=NULL;pCurrentE=pCurrentE->pNext){pEdge=new CAET;pEdge->x=pCurrentE->x;pEdge->yMax=pCurrentE->yMax;pEdge->k=pCurrentE->k;pEdge->pNext=NULL;AddET(pEdge);}ETOrder();pT1=pHeadE;if(pT1==NULL){return ;}while(pCurrentB->ScanLine>=pT1->yMax){CAET *pAETTEmp = pT1;pT1=pT1->pNext;delete pAETTEmp;pHeadE=pT1;if(pHeadE==NULL){return;}}if(pT1->pNext!=NULL){pT2=pT1;pT1=pT2->pNext;}while(pT1!=NULL){if(pCurrentB->ScanLine>=pT1->yMax){CAET *pAETTemp=pT1;pT2->pNext=pT1->pNext;pT1=pT2->pNext;delete pAETTemp;}else{pT2=pT1;pT1=pT2->pNext;}}BOOL In = FALSE;int xb,xe;for(pT1=pHeadE;pT1!=NULL;pT1=pT1->pNext){if(FALSE==In){xb = (int)pT1->x;In=TRUE;}else{xe = (int)pT1->x;for(int x = xb;x<xe;x++){pDC->SetPixel(x,pCurrentB->ScanLine,RGB(0,0,255));}In = FALSE;}}for(pT1=pHeadE;pT1!=NULL;pT1=pT1->pNext){pT1->x=pT1->x+pT1->k;}}
}void CFill::ClearMemory()//安全删除所有桶与桶上连接的边
{DeleteAETChain(pHeadE);//删除边表CBucket *pBucket=pHeadB;while (pBucket!=NULL)//针对每一个桶{CBucket *pBucketTemp=pBucket->pNext;DeleteAETChain(pBucket->pET);//删除桶上面的边delete pBucket;pBucket=pBucketTemp;}pHeadB=NULL;pHeadE=NULL;
}void CFill::DeleteAETChain(CAET *pAET)//删除边表
{while (pAET!=NULL){CAET *pAETTemp=pAET->pNext;delete pAET;pAET=pAETTemp;}
}

一切准备就绪,进入CXXXView.cpp(“XXX”为你项目名称)

给OnDraw函数添加以下语句

 CRect rect;GetClientRect(&rect);pDC->SetMapMode(MM_ANISOTROPIC);pDC->SetWindowExt(rect.Width(),rect.Height());pDC->SetViewportExt(rect.Width(),-rect.Height());pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);rect.OffsetRect(-rect.Width()/2,-rect.Height()/2);//声明Fill类CFill *cFill = new CFill;//声明多边形的七个顶点CPoint points[7] = {CPoint(50,70),CPoint(-150,270),CPoint(-250,20),CPoint(-150,-280),CPoint(0,-80),CPoint(100,-280),CPoint(300,120)};//设置顶点cFill->SetPoint(points,7);//创建桶表cFill->CreateBucket();//创建边表cFill->CreateEdge();//填充多边形cFill->Gouraud(pDC);

运行结果

大家对于填充算法要多看看,很重要

图形学篇:多边形有效边表填充算法相关推荐

  1. 图形学复习-有效边表填充算法

    有效边表填充算法 前言 算法的填充原理 边界像素处理原则 1.考虑像素面积大小的影响问题 2.考虑边界像素的影响问题 不同点的处理原则 1.普通连接点的处理原则 2.局部最低点的处理原则 3.局部最高 ...

  2. Java边缘填充_任意画一个多边形,用边缘填充算法填充

    任意画一个多边形,并用边(缘)填充算法进行填充.(多边形的顶点坐标存放在数组中,坐标值由键盘输入) #include #include //边缘填充 void draw(int a,int b){ f ...

  3. 计算机图形学有序边表作业,《计算机图形学》有序边表填充算法.docx

    实验报告 实验目的 1.掌握有序边表算法填充多边形区域; 2.理解多边形填充算法的意义: 3.增强C语言编程能力. 算法原理介绍 根据多边形内部点的连续性知:一条扫描线与多边形的交点中,入点和出点之间 ...

  4. 活性边表算法c语言,《计算机图形学》有序边表填充算法.doc

    PAGE PAGE 8 实 验 报 告 实验目的 掌握有序边表算法填充多边形区域: 理解多边形填充算法的意义: 增强C语言编程能力. 算法原理介绍 根据多边形内部点的连续性知:一条扫描线与多边形的交点 ...

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

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

  6. 多边形区域填充算法--扫描线填充算法(有序边表法)

    来源:https://blog.csdn.net/u013044116/article/details/49737585 二.扫描线算法(Scan-Line Filling) 扫描线算法适合对矢量图形 ...

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

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

  8. 【计算机图形学 】扫描线多边形填充算法 | OpenGL+鼠标交互

    文章目录 其他计算机图形学实验 前言 思路借鉴 步骤 1.点的结构体 2. AET 活性边表.NET新边表 的结构体 3. 扫描线算法实现 4. 改变鼠标响应函数 完整代码 总结 其他计算机图形学实验 ...

  9. 计算机图形学:多边形填充算法(算法原理及代码实现)

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

最新文章

  1. 深入卷积神经网络背后的数学原理 | 技术头条
  2. android 动态添加元素,动态添加项目到Android中的NavigationView
  3. 黄忠---忠心不二主
  4. IIS7.5 部署WCF项目问题集锦
  5. python自建模块导入_Python模块的使用及自建模块的导入方法举例
  6. 纪念第一次青海湖之行泡汤
  7. Bitmovin视频开发者报告回顾
  8. 大型ERP等数据库系统常见几种设计------(转)
  9. git根据用户过滤提交记录
  10. 2019 CCPC - 网络选拔赛 A题^^
  11. Hive记录-Hive on Spark环境部署
  12. unity字符串换行符_unity如何在中文文本换行时实现排版换行,避免标点符号出现在行首等比较丑的情况?...
  13. 基于Lumisoft.NET组件的POP3邮件接收和删除操作
  14. mysql 中文 phpmyadmin_mysql中文乱码问题,phpmyadmin操作解决方法
  15. EasyUI框架分页实现
  16. 2022最新版VMware虚拟机及CentOS-7安装教程
  17. oracle数据库管理和日常维护,oracle数据库管理与维护
  18. html5_滑条等其他标签
  19. cleardevice
  20. Python-打印指定范围内的全部回文素数(高教社,《Python编程基础及应用》习题8-7) (10分) 回文素数是指一个数既是素数又是回文数,例如131既是素数又是回文数。

热门文章

  1. python怎么启动mne_mne-python 安装大法
  2. python控制qq添加好友_QQ增粉秘籍:QQ添加好友被限制 突破规则日增粉1000+
  3. 数据分析神器Alteryx
  4. Vue3.0笔记(B站天禹老师)
  5. JSjavascript获取B站bilibili哔哩哔哩分P播放列表并以excel文件保存本地
  6. 使用GCD(转自唐巧的技术博客)
  7. LaTeX升级打怪路之自定义指令篇1
  8. 7.7 Introduce Foreign Method 引入外部方法
  9. python定位二维码_图像中二维码的检测和定位
  10. python+pyecharts画地图