区域的填充可以根据区域的填充,采用不同的填充算法,而其中有扫描线类算法和种子填充算法。这里,先介绍扫描线类算法之有序边表的扫描线算法。其他什么种子填充、边界标志算法、4连通区域的递归算法、8连通区域的递归算法、扫描线种子填充算法比较简单。
其实有序边表其实领会了也好理解,关键是将思想转化为代码。
先介绍算法思想:
1.根据给出的多边形顶点坐标,建立ET表(同时计算ymax和ymin)
2.初始化AET表,使之为空
3.使用扫描线yi值作为循环变量,使之初始值为ymin
重复做以下操作:
(1)如果ET表中yi桶非空,则合并到AET中
(2)对AET表中记录按x值从小到大排序
(3)依次取出表中记录,两两配对填充
(4)如果AET表中某记录的ymax=yi,则删除该记录
(5)对仍在AET中的记录,修改x值,x=x+1/m (m为斜率)
现在,关键要细节的处理与代码实现。
1.顶点坐标,由于无法确定顶点数,所以我们用一个数组保存。创建一个填充函数
void regionfill(HDC hdc,Point1 *p,int len,int ymin,int ymax)
我们需要创建EdgeTable类,通过分析,这个结构很像我们数据结构的图(邻接表表示)
代码如下:
/*
*author qyl
*date 2012/4/7
*purpose 边表结构
*version 1.1
*/
#include"LList.h"
#include"Point1.h"
class Edge{
public:
float x;
float increment;
float ymax;
Edge(){x=-1;increment=0;ymax=-1;}
Edge(float x0,float incr,float y0)
{
x=x0;
increment=incr;
ymax=y0;
}
friend bool operator<(Edge &a,Link &b)
{
return a.x(Edge &a,Edge &b)
{
return a.x>b.x;
}
};
class EdgeTable
{
private:
int numberLine;
int numberEdge;
LList **vertex;
public:
EdgeTable(int numLine)
{
int i;
numberLine=numLine;
numberEdge=0;
vertex=(LList**) new LList*[numberLine];
for(i=0;i();
}
~EdgeTable()
{
for(int i=0;iisEmpty();
}
LList* getValue(int pos)
{
Edge it;
LList* l=new LList();
for(vertex[pos]->setStart();vertex[pos]->getValue(it);vertex[pos]->next())
{
l->insert(it);
}
return l;
}
void setEdge(int pos,Edge e)
{
Edge curr;
for(vertex[pos]->setStart();vertex[pos]->getValue(curr);vertex[pos]->next())
{
if(curr.x<=e.x)
break;
}
numberEdge++;
vertex[pos]->insert(e);
}
void delEdge(int pos,Edge e)
{
Edge curr;
for(vertex[pos]->setStart();vertex[pos]->getValue(curr);vertex[pos]->next())
{
if(curr.ymax==e.ymax)
{
vertex[pos]->remove(curr);
numberEdge--;
}
}
}
void deleteAll(int pos)
{
numberEdge-=vertex[pos]->removeAll();
}
void createTableEdge(Point1 * p,int len,int ymin,int ymax)
{
Edge* e;
/*version 1.2
*改进版本
*/
//计算各个顶点是否为奇异点,对每个顶点进行标记
/*
int *flag=new int[len];
for(int i=0;ip[0].getY() && p[len-1].getY()>p[0].getY())
*(flag+i)=2;
else if(p[1].getY()p[0].getY())
*(flag+i)=1;
else if(p[(i+1)%len].getY()>p[i].getY() && p[len-1].getY()
p[i].getY() && p[(i-1)%len].getY()>p[i].getY())
*(flag+i)=2;
else if(p[(i+1)%len].getY()
p[i].getY())
*(flag+i)=1;
else if(p[(i+1)%len].getY()>p[i].getY() && p[(i-1)%len].getY()
insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
break;
case 2:
if((*(flag+len-1))!=1 && (*(flag+len-1))!=3)
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
if((*(flag+j+1))!=1 && (*(flag+j+1)!=1)!=3)
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
break;
case 3:
if((*(flag+j+1))!=1 && (*(flag+j+1))!=3)
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
break;
default: break;
}
}
else
{
switch(mark)
{
case 0: break;
case 1:
if((*(flag+j-1))!=1 && (*(flag+j-1))!=3)
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[j-1].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[j-1].getY()-1);
vertex[i]->insert(*e);
}
break;
case 2:
if((*(flag+(j+1)%len))!=1 && (*(flag+(j+1)%len))!=3)
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
if((*(flag+j-1))!=1 && (*(flag+j-1))!=3)
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[j-1].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[j-1].getY()-1);
vertex[i]->insert(*e);
}
break;
case 3:
if((*(flag+(j+1)%len))!=1 && (*(flag+(j+1)%len))!=3)
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
break;
default: break;
}
}
}
}
}
*/
/*
for(int i=ymin;i<=ymax;i++)
{
for(int j=0;jp[0].getY() && p[len-1].getY()>p[0].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
//非极值点 ymax-1
else if(p[1].getY()>p[0].getY() && p[len-1].getY()insert(*e);
}
//非极值点 ymax-1
else if(p[1].getY()
p[0].getY())
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
}
else
{
if(p[(j+1)%len].getY()>p[j].getY() && p[(j-1)%len].getY()>p[j].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
//非极值点 ymax-1
else if(p[(j+1)%len].getY()>p[j].getY() && p[(j-1)%len].getY()
insert(*e);
}
//非极值点 ymax-1
else if(p[(j+1)%len].getY()
p[j].getY())
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
}
}
}
*/
/*
for(int j=0;jp[0].getY() && p[len-1].getY()>p[0].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
//非极值点 ymax-1
else if(p[1].getY()>p[0].getY() && p[len-1].getY()insert(*e);
}
//非极值点 ymax-1
else if(p[1].getY()
p[0].getY())
{
e=new Edge(p[1].getX(),(p[1].getX()-p[j].getX())/(p[1].getY()-p[j].getY()),p[1].getY()-1);
vertex[(int)p[1].getY()]->insert(*e);
}
}
else
{
if(p[(j+1)%len].getY()>p[j].getY() && p[(j-1)%len].getY()>p[j].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
//非极值点 ymax-1
else if(p[(j+1)%len].getY()>p[j].getY() && p[(j-1)%len].getY()
insert(*e);
}
//非极值点 ymax-1
else if(p[(j+1)%len].getY()
p[j].getY())
{
e=new Edge(p[(j+1)%len].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[(int)p[(j+1)%len].getX()]->insert(*e);
}
}
}
*/
for(int i=ymin;i<=ymax;i++)
{
for(int j=0;jp[0].getY() && p[len-1].getY()>p[0].getY())
{
if(p[2].getY()>p[1].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
if(p[len-2].getY()>p[len-1].getY())
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY());
vertex[i]->insert(*e);
}
}
//非极值点 ymax-1
else if(p[1].getY()>p[0].getY() && p[len-1].getY()p[1].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
}
//非极值点 ymax-1
else if(p[1].getY()
p[0].getY())
{
if(p[len-2].getY()>p[len-1].getY())
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[len-1].getX()-p[j].getX())/(p[len-1].getY()-p[j].getY()),p[len-1].getY());
vertex[i]->insert(*e);
}
}
}
else
{
if(p[(j+1)%len].getY()>p[j].getY() && p[j-1].getY()>p[j].getY())
{
if(p[(j+2)%len].getY()>p[(j+1)%len].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
if(j==1)
{
if(p[len-1].getY()>p[j-1].getY())
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY());
vertex[i]->insert(*e);
}
}
else
{
if(p[j-2].getY()>p[j-1].getY())
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY());
vertex[i]->insert(*e);
}
}
}
//非极值点 ymax-1
else if(p[(j+1)%len].getY()>p[j].getY() && p[(j-1)%len].getY()
p[(j+1)%len].getY())
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j+1)%len].getX()-p[j].getX())/(p[(j+1)%len].getY()-p[j].getY()),p[(j+1)%len].getY());
vertex[i]->insert(*e);
}
}
//非极值点 ymax-1
else if(p[(j+1)%len].getY()
p[j].getY())
{
if(j==1)
{
if(p[len-1].getY()>p[j-1].getY())
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY());
vertex[i]->insert(*e);
}
}
else
{
if(p[j-2].getY()>p[j-1].getY())
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY()-1);
vertex[i]->insert(*e);
}
else
{
e=new Edge(p[j].getX(),(p[(j-1)%len].getX()-p[j].getX())/(p[(j-1)%len].getY()-p[j].getY()),p[(j-1)%len].getY());
vertex[i]->insert(*e);
}
}
}
}
}
}
}
};
代码有些凌乱, 但有些错误的我并没有删去,只是展示如何一步一步改进的,以及发现的错误,改了之后好像有些注释也为修改.
这个表结构成了话,其实其他的就简单了
这个函数就是扫描线算法啦。
void regionfill(HDC hdc,Point1 *p,int len,int ymin,int ymax)
{
EdgeTable ET(/*ymax-ymin+1*/1024);
//创建ET表
ET.createTableEdge(p,len,ymin,ymax);
//初始化AET表
EdgeTable AET(1024);
LList* l,*ptr;
for(int i=ymin;i<=ymax;i++)
{
//如果ET表中i桶非空,将该桶中全部记录合并到AET表中
if(!ET.isEmptyInLine(i))
{
Edge curr;
l=ET.getValue(i);
for(l->setStart();l->getValue(curr);l->next())
{
AET.setEdge(i,curr);
}
l->removeAll();
}
//在AET表中按从小到大排序
Edge curr;
Edge temp;
Edge min;
ptr=l=AET.getValue(i);
/*
AET.deleteAll(i);
int count=0;
for(l->setStart();l->getValue(curr);l->next())
{
min=curr;
for(ptr->setPos(count);ptr->getValue(curr);ptr->next())
{
if(min.x>curr.x)
min=curr;
}
count++;
AET.setEdge(i,min);
}
*/
//依次取出AET表中扫描线y桶中记录,两两配对填充
for(l->setStart();l->getValue(curr);l->next())
{
l->next();
l->getValue(temp);
for(int x0=int(curr.x);x0<=temp.x;x0++)
SetPixel(hdc,x0,i,255);
//如果AET表中某记录的ymax=扫描线y,则删除该记录
if(curr.ymax==i)
AET.delEdge(i,curr);
if(temp.ymax==i)
AET.delEdge(i,temp);
}
//在AET表中的每个记录 对x进行修改
ptr->removeAll();
ptr=AET.getValue(i);
for(ptr->setStart();ptr->getValue(curr);ptr->next())
{
curr.x+=curr.increment;
AET.setEdge(i+1,curr);
}
//填充完后 删除AET当前扫描线及l,ptr
AET.deleteAll(i);
l->removeAll();
ptr->removeAll();
}
}
最后,谈谈细节问题。就是扫描线与顶点相交,必须正确的进行交点个数的计算,否则会出错。这个我已经包含到EdgeTable类中了,上面给出了两种思路,但其中一种由于时间问题为全部完成,已被注释掉了,有兴趣可以修改哈,代码共享哈。由于顶点前后连接构成环,而我用的数组,所以对一些特殊情况要注意,当然也可以用循环链表。
对于求最大最小值,也可以建一个函数求解,这里为了简便,采用手动输入。还有指针一定要小心。可能上面我说要排序,但我没有排序,那是我插入记录就是按排序插入的。
因为一个大括号而多了100多的错误,因为一个变量折腾到凌晨一点……
最后的最后,我只想说,调bug是件考验毅力耐力的活!!!
附:代码下载:http://download.csdn.net/my