计算机图形学(三)扫描线多边形填充算法讲解与源代码

原创 2015年06月22日 16:28:22

如果喜欢转载请标明出处:

并非菜鸟菜鸟的博客

源代码下载:点击打开链接

在这里先说下算法的实现过程

本人觉得这个算法实现起来还是有点难度的!很多人都不愿意去看太多描述性的文字,所以对这个算法的过程是什么大概也不知道,那么我在这里简要的说一些!

算法实现过程中应用两个数据结构:

1、边表ET:Edge Table)

用来对除水平边外的所有边进行登记,来建立边的记录。边的记录定义为:

扫描线 y 对应的ET表

  第一项:某边的最大y值(ymax)。注意要进行奇异点处理:对于非极值点应该ymax=ymax-1。
  第二项:某边的最小的y对应的x值。
  第三项:某边斜率的倒数:1/m
  第四项:指针。用来指向同一条扫描线相交的其它边,如果其它边不存在,则该项置空。

2、活动边表AET:Active Edge Table)

ET表建立以后,就可以开始扫描转换了。对不同的扫描线,与之相交的边线也是不同的,当对某一条扫描线进行扫描转换时,我们只需要考虑与它相交的那些边线,为此需要建立一个只与当前扫描线相交的边记录链表,称之为活动边表。

下边说下算法的实现:

    1、根据给定的多边形顶点坐标,建立ET 表。

  2、AET表初始化,每个桶置空。

  3、for(y=ymin;y<= ymax;y++)

     {
      合并当前扫描线y的ET表;

      将y桶中每个记录按x项升序排列;

      在当前y值下,将两两记录的x值之间的象素进行填充;

      删除y=ymax的边记录;

      修改边记录x=x+1/m

     }

那么先来一个效果图

那么现在来看下关键的代码部分

先看下两关键的数据结构

<span style="white-space:pre">	</span>struct Edge
	{
		double Ymax;
		double X;
		double Dx;
	};
	struct EDGE
	{
		CPoint Up;
		CPoint Down;
		Edge EG;
	};

这两个数据结构是我们要用到的

看一下我们怎么讲多边形存储起来的

void CPolyFill::BuildEDGEs()
{
	if(m_pEDGEs) 
	{
		delete[] m_pEDGEs; m_pEDGEs = NULL;
	}
	m_pEDGEs = new EDGE[m_PtNum];
	
	for(int i = 0; i < m_PtNum-1; i++)
	{
		if (m_Pts[i].y > m_Pts[i+1].y)
		{
			m_pEDGEs[i].Up   = m_Pts[i];
			m_pEDGEs[i].Down = m_Pts[i+1];			
		}
		else
		{
			m_pEDGEs[i].Up   = m_Pts[i+1];
			m_pEDGEs[i].Down = m_Pts[i];
		}	   
		m_pEDGEs[i].EG.Ymax = m_pEDGEs[i].Up.y ;
		m_pEDGEs[i].EG.X = m_pEDGEs[i].Down.x;
		m_pEDGEs[i].EG.Dx = double((m_pEDGEs[i].Up.x - m_pEDGEs[i].Down.x))/(m_pEDGEs[i].Up.y - m_pEDGEs[i].Down.y);
	}
	
	if (m_Pts[0].y > m_Pts[m_PtNum-1].y)
	{
		m_pEDGEs[m_PtNum-1].Up   = m_Pts[0];
		m_pEDGEs[m_PtNum-1].Down = m_Pts[m_PtNum-1];
	}
	else
	{
		m_pEDGEs[m_PtNum-1].Up   = m_Pts[m_PtNum-1];
		m_pEDGEs[m_PtNum-1].Down = m_Pts[0];
	}	
	m_pEDGEs[m_PtNum-1].EG.Ymax = m_pEDGEs[m_PtNum-1].Up.y ;
	m_pEDGEs[m_PtNum-1].EG.X = m_pEDGEs[m_PtNum-1].Down.x;
	m_pEDGEs[m_PtNum-1].EG.Dx = double((m_pEDGEs[m_PtNum-1].Up.x - m_pEDGEs[m_PtNum-1].Down.x))/(m_pEDGEs[m_PtNum-1].Up.y - m_pEDGEs[m_PtNum-1].Down.y);
}
现在多边形已经被存储起来了

那么接下来该做的就是建立边表

void CPolyFill::CreateET()
{
	GetMinMaxY(MinY,MaxY);
	if(m_pET) 
	{
		delete [] m_pET;
		m_pET = NULL;
	}
	m_pET = new CArray <Edge,Edge>[MaxY- MinY+1];
    // Add EDGE to ET
	for(int i = 0; i < m_PtNum; i++)
	{
		int scanline = m_pEDGEs[i].Down.y - MinY;
		m_pET[scanline].Add(m_pEDGEs[i].EG); 			
	}
	// 多边形的边排序: Sort according to Xmin
	for (int n = MinY; n < MaxY; n++) 
	{
		int index = n-MinY;
		int sz = m_pET[index].GetSize();
		for (int i = 0; i < sz-1; i++)
		{
			for (int k = i+1; k < sz; k++)
			{
				if (m_pET[index][i].X > m_pET[index][k].X)
				{
					Edge t = m_pET[index][i];  
					m_pET[index][i] = m_pET[index][k];
					m_pET[index][k] = t;
				}
			}
		}
	}
}

然后建立活动边表
void CPolyFill::InitAET()
{
	if(m_pAET) 
	{
		delete [] m_pAET;
		m_pAET = NULL;
	}
	m_pAET = new CArray<Edge,Edge>[MaxY- MinY+1];	
}

现在准备工作已经做好了,开始进入算法的主要部分

void CPolyFill::FillPolygon(CDC *pDC)
{
	int nRand = rand();
	float fMap = (float)255/RAND_MAX;
	int ColorR = (UINT)(float)nRand*fMap + 0.5f;
	nRand = rand();
	fMap = (float)255/RAND_MAX;
	int ColorG = (UINT)(float)nRand*fMap + 0.5f;

	for (m_CurrentScanLine = MinY; m_CurrentScanLine < MaxY; m_CurrentScanLine++)
	{
		MoveNewEdgeFromET();   // 加入新边
		RemoveEdges();		   // 删除旧边
		SortAET();             // 按照边的交点的X值排列
		FillScanLine(pDC);     // 按照配对填充当前扫描行 
		UpdateDelteX();	       // 更新下条扫描线的交点X坐标
	}	
}

一些关键的函数如下

// 加入新边
void CPolyFill::MoveNewEdgeFromET()
{
	int index = m_CurrentScanLine - MinY;
	for (int i = 0; i < m_pET[index].GetSize(); i++) 
	{
		m_pAET[index].Add(m_pET[index][i]);
	}    
}

// 删除旧边
void CPolyFill::RemoveEdges()
{
	int index = m_CurrentScanLine- MinY;
	for(int i = 0; i < m_pAET[index].GetSize(); i++) 
	{
		if (m_CurrentScanLine == m_pAET[index][i].Ymax) 
		{
			m_pAET[index].RemoveAt(i); 
			i--;
		}
	}
}

// 排序
void CPolyFill::SortAET()
{
	// Sort according to Xmin
	int index = m_CurrentScanLine-MinY; 
	int sz = m_pAET[index].GetSize();
	for (int i = 0; i < sz-1; i++)
	{
		for (int k = i+1; k < sz; k++)
		{
			if (m_pAET[index][i].X > m_pAET[index][k].X)
			{
				Edge t = m_pAET[index][i];  
				m_pAET[index][i] = m_pAET[index][k];
				m_pAET[index][k] = t;
			}
		}
	}
}



// 配对填充当前行
void CPolyFill::FillScanLine(CDC *pDC)
{   
	int mY;
	mY =MaxY-MinY;    
	
	int index = m_CurrentScanLine-MinY;
    for (int i = 0; i < m_pAET[index].GetSize()-1; i += 2)
	{
	//	int i =0;
		for (int x0 = m_pAET[index][i].X+0.99; x0 < int(m_pAET[index][i+1].X); x0++)
		{
			
			pDC->SetPixel(x0,m_CurrentScanLine, RGB(ColorR, ColorG,255-MulDiv(m_CurrentScanLine, 255, mY)));
		}
	}
}
// 更新交点横坐标。
void CPolyFill::UpdateDelteX()
{
	// Sort according to Xmin
	int index = m_CurrentScanLine-MinY; 
	int sz = m_pAET[index].GetSize();
	for (int i = 0; i < sz; i++)
	{
		m_pAET[index][i].X += m_pAET[index][i].Dx;
		m_pAET[index+1].Add(m_pAET[index][i]);
	}
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

扫描线填充多边形算法详解与代码

扫描线填充多边形算法详解与代码首先给出已知信息:多边形结构体里面包含三个信息:顶点个数,顶点和颜色class MyPolygon { public: int m_VerticeNumber;...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

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

二、扫描线算法(Scan-Line Filling)         扫描线算法适合对矢量图形进行区域填充,只需要直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合...

计算机图形学 学习笔记(二):多边形扫描转换:X扫描线算法 和 改进的X扫描线算法

接上文 计算机图形学 学习笔记(一):概述,光栅图形学算法:直线扫描算法(DDA,中点画线算法,Bresenham算法)光栅图形学算法2.4 多边形扫描转换-X扫描线算法多边形的扫描转换和区域填充...
  • Jurbo
  • Jurbo
  • 2017年02月23日 19:46
  • 2234

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

算法系列之十二:多边形区域填充算法--扫描线填充算法(有序边表法)

二、扫描线算法(Scan-Line Filling)        扫描线算法适合对矢量图形进行区域填充,只需要直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合使用,比如电...
  • orbit
  • orbit
  • 2012年03月19日 14:57
  • 66188

种子填充算法(计算机图形学)

#include #include using namespace std; //种子填充算法四联通算法 int BoundaryFill(int x, int y) { int c...

MFC下实现图形学之多边形扫描转化填充算法

//*************************//获取点中y坐标最大值//*************************int CPolygonFillView::GetMaxY(){ i...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:计算机图形学(三)扫描线多边形填充算法讲解与源代码
举报原因:
原因补充:

(最多只允许输入30个字)