【寒江雪】扫描线区域填充算法
来源:互联网 发布:linux nfs配置 编辑:程序博客网 时间:2024/06/05 08:53
多边形可以用多个点进行描述。假定点是按逆时针排序给出的。这样一来要填充由这些点所围区域,就需要扫描这一区域内点的点。
如果我们知道区域内的所有顶点,显然,我们可以直接对点着色。但这样的输入就显得不太方便了。倘若我们以数学武装起来,可以把不方便变为方便,把繁琐的事化简,那就是世界上再美妙不过的事了。
现在我们得到的输入只有按逆时针排序的顶点。我们把点连成线,再以y轴为扫描线,自底相上求交点,再把交点配对,对两点间的点进行着色。
算法步骤如下:
求交点
排序
配对
着色
但是求交和排序的操作效率比较低,就需要优化。
不过,讲道理,排序算法效率基本上就那样,就看怎么选择罢了。{书上介绍的是使用插入排序。如果允许边自相交则使用冒泡排序。本人比较懒,直接调用了标准库的sort}
再来看求交点的方法。因为输入时按逆时针排序,那么每个顶点的左边和右边的点都是与它相邻的顶点,并且连接可以得到直线。由于是以y为扫描线进行扫描,y的递增量为1,那就需要考虑下一个交点的横坐标在哪。
【考虑当前被扫描的点为(xi,yi),下一点交点则为(x(i+1),y(i+1)),这里y(i+1)=yi+1】
那么问题就转换为求x(i+1)了
考虑直线方程
ax+by+c=0
由此可得
x=-b/a*(y)-c/a (a!=0)
因此
xi = -b/a*(yi)-c/a
x(i+1)= -b/a*(y(i+1))-c/a=-b/a*(yi+1)-c/a=xi-b/a (a!=0)
-b/a即为斜率1/k
我们约定a=0时,x(i+1)=xi+0。这种情况,该条直线是一条水平线,两个点的y值相等,我们是不会考虑水平线的。
这样按y递增扫描的方式,就可以一步一步求出交点来了。不过!!这是某条水平线与直线的交点,要考虑到什么时候,对某条直线的扫描终止,也就是迭代终止的条件。
再来看输入的点,那些按逆时针排序的点,它的左边和右边,我们以左边举例,如果它左边的点的纵坐标比它的纵坐标大,我们只需要在y递增到这个值的时候就可以放弃对这条直线的扫描了。
因此,我们引入这样一张表
表中的每个元素都是一个链表,每个y值对应一个链表。从最低的y_min向上扫描
而链表中的元素是这样的结构
x,dx,y_max;
x代表当前点的横坐标,
dx,就是要计算下一点横坐标的增量(1/k或者是0)
y_max就是终止条件。假设当前扫描线纵坐标为yi,若yi+1<y_max,则计算下一点并加入yi+1
所对应的链表中。供下次扫描
给出点(1,1),(4,1),(2,3),(4,5),(1,5)
得出下表
从1开始往上叠加,一直到5,就可以计算出所有交点
再从1开始,对点进行配对,对区间进行填充
再求交点的时候,我们对以下特殊情况进行约定:
1. 扫描线单纯地与直线相交,只记一个交点。
特征就是有左右两边只有一个点的纵坐标比当前点的大
2. 扫描线与直线重合,只记一个交点。
特征就是,左右两边有一个点的纵坐标和当前点的一样大
3. 扫面线的点为底谷,记为两个点
特征就是左右两边的点的纵坐标比当前点的都高
4. 扫描线的点为顶峰,不记点
特征就是左右两边的点的纵坐标比当前点的都低
通用的描述方法就是,初始化表的时候,获取相邻点,当相邻点的纵坐标比当前点的大时,就将该点放入相应的表中。
至于水平线,要特别说明一下,扫描线填充算法对水平线是不在乎的,只需要有左右两端点,在填充的时候就可以给该水平线着色。
至于顶峰的点被忽略的问题,这个重要用一个数组标记一下,过后再对其进行着色就可以达到不遗失任何点的目的(本人就是这么干的,有强迫症)
还有排序算法的选择,经典的做法初始化表的时候先建立新边表,然后再扫描的过程,先把点从新边表按插入排序的方法插入活动边表,之后再扫描,更新出新的点并且还是按插入排序算法插入下一个链表中,这里用插入排序算法效率很高是因为,待插入链表中的数据是有序的,把一个元素插入一个有序的序列中,时间效率是O(n)的。但本人偷懒就直接使用sort,并使用自己写的比较函数来排序。
排序是二级排序,先按x由小到大递增,如果x相等,则按dx由小到大排
扫描线算法也可以用来绘制多边形,但是复杂度不如直接对点按顺序使用Bresenham直线算法好。
代码如下:
VOID ScanningLine_AreaFill(vector<Point>&point,COLORREF color,intstep,int pointSize)
{
inty_min = 0x3f3f3f3f;
inty_max = -1;
for (int i =0; i <point.size(); i++) {
y_min=min(point[i].y,y_min);
y_max=max(point[i].y,y_max);
}
intshift = 0;//默认为不用平移
if(y_min < 0) {
//往上平移y_min的距离
shift= abs(y_min);
y_max+= shift;
}
vector<AET_NODE>*AET =new vector<AET_NODE>[y_max+1];
vector<int>peek;
for (int i =0; i <point.size(); i++) {
floaty_left = (i == 0 ? point[point.size()- 1].y :point[i -1].y);
floatx_left = (i == 0 ? point[point.size()- 1].x :point[i -1].x);
floatdelta_left = GetDelta(point[i].x,point[i].y,x_left, y_left);//dy_left == 0 ? 0 : dx_left /dy_left;
floatx_right = (i == point.size()- 1 ?point[0].x :point[i + 1].x);
floaty_right = (i == point.size()- 1 ?point[0].y :point[i + 1].y);
floatdelta_right = GetDelta(point[i].x,point[i].y,x_right, y_right);
if(y_left > point[i].y)
AET[point[i].y +shift].push_back(AET_NODE{ (float)point[i].x ,delta_left , (int)y_left + shift });
if(y_right > point[i].y)
AET[point[i].y +shift].push_back(AET_NODE{ (float)point[i].x ,delta_right, (int)y_right + shift });
if(y_left <= point[i].y&&y_right<=point[i].y)
peek.push_back(i);
}
int y =y_min < 0 ? 0 : y_min;
while (y<= y_max) {
sort(AET[y].begin(),AET[y].end(),CompareAET_NODE);
for (int i =0; i < AET[y].size(); i++) {
if (y!= y_max && AET[y][i].y_max> y + 1) {
AET[y+ 1].push_back(AET_NODE{ AET[y][i].x +AET[y][i].dx,AET[y][i].dx,AET[y][i].y_max});
}
}
y++;
}
y= y_min < 0 ? 0 : y_min;
while (y<= y_max) {
for (int i =1; i < AET[y].size(); i+=2) {
int x_signal_min= AET[y][i - 1].x>= 0 ? 1 : -1;
intx_min = ((int)(abs(AET[y][i -1].x)+0.5f))*x_signal_min;
intx_signal_max = AET[y][i].x>= 0 ? 1 : -1;
intx_max = ((int)(abs(AET[y][i].x)+0.5f))*x_signal_max;
for (int j =x_min; j <= x_max; j+=step) {
DrawRectange(j,y - shift,pointSize,pointSize,color);
}
}
y+=step;
}
for (int i =0; i < peek.size(); i++) {
DrawRectange(point[peek[i]].x,point[peek[i]].y,pointSize,pointSize, color);
}
delete[]AET;
return VOID();
}
多边形绘制算法:
VOID Polygon(vector<Point>&point,COLORREF color,int step, int pointSize)
{
BresenhamInteger(point[0].x,point[0].y,point[point.size()- 1].x,point[point.size()- 1].y,color,step,pointSize);
for (int i =1; i <point.size(); i++) {
BresenhamInteger(point[i -1].x,point[i -1].y,point[i].x,point[i].y,color, step, pointSize);
}
return VOID();
}
- 【寒江雪】扫描线区域填充算法
- 扫描线区域填充算法
- 区域填充之扫描线算法
- 区域填充的扫描线算法
- 二维区域扫描线填充算法的实现
- 平面多边形区域的扫描线填充算法
- 区域填充之扫描线算法(续)
- 区域填充_扫描线种子填充_广度优先算法种子填充
- 算法系列之十二:多边形区域填充算法--扫描线种子填充算法
- 算法系列之十二:多边形区域填充算法--改进的扫描线填充算法
- 算法系列之十二:多边形区域填充算法--扫描线种子填充算法
- 算法系列之十二:多边形区域填充算法--改进的扫描线填充算法
- 算法系列之十二:多边形区域填充算法--扫描线种子填充算法 .
- 算法系列之十二:多边形区域填充算法--改进的扫描线填充算法 .
- 算法系列之十二:多边形区域填充算法--改进的扫描线填充算法
- 算法系列之十二:多边形区域填充算法--改进的扫描线填充算法
- 算法系列之十二:多边形区域填充算法--扫描线种子填充算法
- 多边形区域填充算法--扫描线种子填充算法
- CSS3初体验之奇技淫巧
- poj-3666
- HTTP中GET和POST的区别
- DLL编程和两种调用方法调用
- 微信小程序开发之常见问题 不在以下合法域名列表中 wx.request合法域名配置
- 【寒江雪】扫描线区域填充算法
- Android Fragment 初相识(二)
- DataGrid 用法示例
- 【寒江雪】直线裁切算法
- Java_类_多态
- 初识Unity 3D——基本脚本代码
- Linux Centos安装eclipse并创建桌面快捷方式
- Ubuntu16.04清理boot分区
- App性能优化