区域填充算法
一.目的:
掌握有序编表法的基本原理与实现,了解简单的交互技术。
二.要求:
1. 边界种子交互输入;
2. 实例包含有凹凸多边形类型;
3. 支持颜色选择,图案编辑。
三.内容:
1.实现有序编表法;
求出扫描线和多边形每条边的交点(x1,y1)的横纵坐标的最大最小值Xmin,Xmax,Ymax,Ymin。利用求出的每条边交点横纵坐标的最大最小值编成一张从小到大排列的表(float m_yMax[N],m_yMin[N],m_Xa[N],m_Dx[N]),然后根据 这张表进行多边形填充。其中的一些函数功能说明:
void CCgFQlineDemoView::pFillScan(CDC* pDC)
void FillPolygon(int n, CPoint *points, CDC *pDC)//编表法实现
void pLoadPolygon(int pNumbers,CPoint *points)//多边形加载
void pInsertLine(float x1, float y1,float x2, float y2)//求交点
void pInclude();
void pUpdateXvalue();
void pXsort(intBegin, int i)//建立有序表,即将各交点大小排序
2. 实现种子填充算法:
算法原理:
(1)栈顶象素出栈;
(2)沿扫描线对出栈象素的左右象素进行填充,直至遇到边界象素为止,即每出栈一个像素,就对包含该象素的整个区间进行填充;
(3)上述区间内最左,最右的象素分别记为x1和x2;
(4)在区间[x1,x2]中检查与当前扫描线相邻的上下两条扫描线的有关象素是否全为边界象素或已填充的象素,若存在非边界,未填充的象素,则把每一区间的最右象素取作种子象素入栈。
该算法的实现函数为void ScanLineFill(CDC* pDC, CPoint point, int color0)
四.关键代码:
(1)void CCgWP_FQlineDemoView::OnMouseMove(UINT nFlags,CPoint point)void CCgWP_FQlineDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (m_pNumbers) {
m_pDC->SetROP2(2);
m_pDC->MoveTo(m_pAccord[m_pNumbers-1]);
m_pDC->LineTo(m_mousePoint);
m_mousePoint = point;
m_pDC->MoveTo(m_pAccord[m_pNumbers-1]);
m_pDC->LineTo(m_mousePoint);
}
CView::OnMouseMove(nFlags, point);
}
(2)void CCgWP_FQlineDemoView::OnLButtonDown(UINT nFlags,CPoint point)
void CCgWP_FQlineDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CCgWP_FQlineDemoDoc* pDoc = (CCgWP_FQlineDemoDoc*) GetDocument();
if( pDoc->SIGN){
if (m_pNumbers < N) {
m_pAccord[m_pNumbers] = point;
m_pNumbers++;
m_mousePoint = point;
}
}
else{
m_tempPoint = point;
if( pDoc->SEEDFillMode)
CCgWP_FQlineDemoView::ScanLineFill(m_pDC, m_tempPoint,RGB(0,0,255));
}
CView::OnLButtonDown(nFlags, point);
}
(3)void CCgWP_FQlineDemoView::OnLButtonDblClk(UINT nFlags, CPointpoint)
void CCgWP_FQlineDemoView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CCgWP_FQlineDemoDoc* pDoc = (CCgWP_FQlineDemoDoc*) GetDocument();
if(pDoc->SIGN)
{
m_pDC->MoveTo(m_pAccord[m_pNumbers-1]);
m_pDC->LineTo(m_pAccord[0]);
m_pAccord[m_pNumbers] = m_pAccord[0];
m_pNumbers++; //此时的值为顶点数加上1
sprintf( stringnumber, "%d", m_pNumbers-1 );
m_pDC->TextOut(100,200,stringnumber);
FillPolygon(m_pNumbers, m_pAccord, m_pDC);
m_pNumbers = 0; // 防止鼠标拖动时划线
CView::OnLButtonDblClk(nFlags, point);
}
}
(4)void CCgWP_FQlineDemoView::FillPolygon(int n, CPoint *points, CDC*pDC)
void CCgWP_FQlineDemoView::FillPolygon(int n, CPoint *points, CDC *pDC)
{
CCgWP_FQlineDemoDoc* pDoc = (CCgWP_FQlineDemoDoc*) GetDocument();
m_edgeNumbers = 0;
pLoadPolygon(n, points);//加载多边形,通过记录m_yMax[N],m_yMin[N],m_Xa[N],m_Dx[N]来记录每一条边
m_Begin = m_End = 0;
m_Scan = (int)m_yMax[0];
pInclude(); // 求交
pUpdateXvalue();//改变m_Xa[N]的值
while (m_Begin != m_End) {
pFillScan(pDC);
m_Scan--;
pInclude();
pUpdateXvalue();
}//当已经求交边数和已经删除的边数不相等时,进入循环
}
(5)void CCgWP_FQlineDemoView::pLoadPolygon(intpNumbers,CPoint *points)
void CCgWP_FQlineDemoView::pLoadPolygon(int pNumbers,CPoint *points)
{
float x1,y1,x2,y2;
x1 = points[0].x; y1 = points[0].y+0.5;
for (int i = 1; i < pNumbers; i++) {
x2 = points[i].x; y2 = points[i].y+0.5;
if( y1-y2 )
pInsertLine(x1,y1,x2,y2);
x1 = x2; y1 = y2;
}
}
(6) void CCgWP_FQlineDemoView::pInsertLine(floatx1, float y1,float x2, float y2)
void CCgWP_FQlineDemoView::pInsertLine(float x1, float y1,
float x2, float y2)
{
int i;
float Ymax,Ymin;
Ymax = (y2 > y1) ? y2 : y1;
Ymin = (y2 < y1) ? y2 : y1;
i = m_edgeNumbers;
while (i > 0 && Ymax > m_yMax[i-1]) {
m_yMax[i] = m_yMax[i-1];
m_yMin[i] = m_yMin[i-1];
m_Dx[i] = m_Dx[i-1];
m_Xa[i] = m_Xa[i-1];
i--;
}
m_yMax[i] = Ymax;
m_yMin[i] = Ymin;
if (y2 > y1) m_Xa[i] = x2;
else m_Xa[i] = x1;
m_Dx[i] = (x1 - x2) / (y2 - y1);
m_edgeNumbers++;
}
(7) void CCgWP_FQlineDemoView::pInclude()
void CCgWP_FQlineDemoView::pInclude()
{
while (m_End < m_edgeNumbers && m_yMax[m_End] > m_Scan) {
m_Xa[m_End] = m_Xa[m_End] - 0.5 * m_Dx[m_End];
m_End++;
}
}
(8) void CCgWP_FQlineDemoView::pUpdateXvalue()
void CCgWP_FQlineDemoView::pUpdateXvalue()
{
int i,start = m_Begin;
for (i = start; i < m_End; i++) {
if (m_Scan > m_yMin[i]) {
m_Xa[i] += m_Dx[i];
pXsort(m_Begin, i);
} else {
for (int j = i; j > m_Begin; j--)
{
m_yMin[j] = m_yMin[j-1];
m_Xa[j] = m_Xa[j-1];
m_Dx[j] = m_Dx[j-1];
}
m_Begin++;
}
}
}
(9) void CCgWP_FQlineDemoView::pXsort(intBegin, int i)
void CCgWP_FQlineDemoView::pXsort(int Begin, int i)
{
float temp;
while (i > Begin && m_Xa[i] < m_Xa[i-1]) {
temp = m_Xa[i];
m_Xa[i] = m_Xa[i-1];
m_Xa[i-1] = temp;
temp = m_yMin[i];
m_yMin[i] = m_yMin[i-1];
m_yMin[i-1] = temp;
temp = m_Dx[i];
m_Dx[i] = m_Dx[i-1];
m_Dx[i-1] = temp;
i--;
}
}
(10)voidCCgWP_FQlineDemoView::pFillScan(CDC* pDC)
void CCgWP_FQlineDemoView::pFillScan(CDC* pDC)
{
int x=0,y=0;
CCgWP_FQlineDemoDoc* pDoc = (CCgWP_FQlineDemoDoc*) GetDocument();
if( pDoc->SEEDFillMode == 0 ){
pDC->SetROP2(10);
for (int i = m_Begin; i < m_End; i += 2) {
if (pDoc->SOLIDFillMode) {
pDC->MoveTo(m_Xa[i], m_Scan);
pDC->LineTo(m_Xa[i+1], m_Scan);
} else {
y = m_Scan;
for (int x = m_Xa[i]; x < m_Xa[i+1]; x++)
if (m_patternData[y%7][x%6])
pDC->SetPixel(x, y, RGB(0,0,255));
}
}
}
}
(11) void CCgWP_FQlineDemoView::OnRButtonDown(UINTnFlags, CPoint point)
void CCgWP_FQlineDemoView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CCgWP_FQlineDemoDoc* pDoc = (CCgWP_FQlineDemoDoc*) GetDocument();
pDoc->SIGN = !pDoc->SIGN;
CView::OnRButtonDown(nFlags, point);
}
(12) void CCgWP_FQlineDemoView::ScanLineFill(CDC*pDC, CPoint point, int color0)
void CCgWP_FQlineDemoView::ScanLineFill(CDC* pDC, CPoint point, int color0)
{
CCgWP_FQlineDemoDoc* pDoc = (CCgWP_FQlineDemoDoc*) GetDocument();
int clr,color;
int x,y,x0,y0,xl,xr,flag,xnextspan;
iStack=0;
pDC->SetROP2(10);
x=point.x;
y=point.y;
y0=y;
push(x,y);
color=pDC->SetPixel(x,y,color0);
while(iStack >0)
{
pop(x,y);
clr=pDC->SetPixel(x,y,RGB(255,0,255));
x0=x+1;
while(pDC->GetPixel(x0,y) !=color)
{
pDC->SetPixel(x0,y,RGB(255,0,255));
x0++;
}
xr=x0-1; // 最右像素
x0=x-1;
while(pDC->GetPixel(x0,y) !=color)
{
pDC->SetPixel(x0,y,RGB(255,0,255));
x0--;
}
xl=x0+1; // 最左像素
x0=xl;
y=y+1;
while(x0<=xr)
{
flag=0;
while(pDC->GetPixel(x0,y) !=color &&
pDC->GetPixel(x0,y) !=clr && x0<xr)
{
if(flag==0) flag=1;
x0++;
}
if(flag==1)
{
if(x0==xr && pDC->GetPixel(x0,y) !=color &&
pDC->GetPixel(x0,y) !=clr)
push(x0,y);
else
push(x0-1,y);
flag=0;
}
xnextspan=x0;
while(pDC->GetPixel(x0,y) ==color ||
pDC->GetPixel(x0,y) ==clr && x0<=xr)
x0++;
if(xnextspan==x0) x0++;
}
x0=xl;
y=y-2;
while(x0<=xr)
{
flag=0;
while(pDC->GetPixel(x0,y) !=color &&
pDC->GetPixel(x0,y) !=clr && x0<xr)
{
if(flag==0) flag=1;
x0++;
}
if(flag==1)
{
if(x0==xr && pDC->GetPixel(x0,y) !=color &&
pDC->GetPixel(x0,y) !=clr)
push(x0,y);
else
push(x0-1,y);
flag=0;
}
xnextspan=x0;
while(pDC->GetPixel(x0,y) ==color ||
pDC->GetPixel(x0,y) ==clr && x0<=xr)
x0++;
if(xnextspan==x0) x0++;
}
}
}
(13)voidCCgWP_FQlineDemoView::push(int x, int y)
void CCgWP_FQlineDemoView::push(int x, int y)
{
if(iStack>1000)
return;
else
{
iStack++;
stackX[iStack]=x;
stackY[iStack]=y;
}
}
(14)intCCgWP_FQlineDemoView::pop(int &x, int &y)
int CCgWP_FQlineDemoView::pop(int &x, int &y)
{
if(iStack<=0) return -1;
x=stackX[iStack];
y=stackY[iStack];
iStack--;
return iStack;
}<strong>
</strong>
五.结果及分析:
1. 有序编表法:
2.种子线扫描法:
生成多边形:
通过比较可知:有序编表法效率更高,种子填充算法更灵活。