多边形可以用多个点进行描述。假定点是按逆时针排序给出的。这样一来要填充由这些点所围区域,就需要扫描这一区域内点的点。 如果我们知道区域内的所有顶点,显然,我们可以直接对点着色。但这样的输入就显得不太方便了。倘若我们以数学武装起来,可以把不方便变为方便,把繁琐的事化简,那就是世界上再美妙不过的事了。 现在我们得到的输入只有按逆时针排序的顶点。我们把点连成线,再以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,int step,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(); } ![]() |