如果喜欢转载请标明出处:
并非菜鸟菜鸟的博客
源代码下载:点击打开链接
在这里先说下算法的实现过程
本人觉得这个算法实现起来还是有点难度的!很多人都不愿意去看太多描述性的文字,所以对这个算法的过程是什么大概也不知道,那么我在这里简要的说一些!
算法实现过程中应用两个数据结构:
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]); -
} -
}