一种简单易行的扫描线填充算法

一种简单易行的扫描线填充算法

在提供的平台中,我们已经可以做出矩形、圆形、多边形三类图元,我们的任务就是对三类图元进行填充。很明显,不同于规则的矩形与圆形,对任意形状的多边形的填充是个难点,而我本此工作就负责了这一部分,也就是填充算法。对于一个已知顶点坐标的复杂多边形,该如何完成填充?下面我对自己算法的实现与优化过程进行详述。
1.1 有效交点的确定
对于一个填充问题,我最开始想到的就是扫描线填充。在本问题中,显而易见我可以简单的找到最左侧的点与最右侧的点,也就是可以知道扫描线的扫描范围,但是,该如何找扫描线与多边形的交点呢?一个常用的思路是,根据一条边的两个顶点确定一条直线,计算该直线与扫描线的交点,循环遍历每个边。
但是,一个新的问题来了,如图1.1所示,如果对多边形各边做一个循环求交点,那么会产生许多要舍弃的点,因为它们并非多边形与扫描线的交点,这时可能需要对每个点作判断,如果它不在多边形上,那么就舍弃,这无疑大大增加了运算量,且不利于编写代码。
在这里插入图片描述

为了解决这个问题,我需要想新的方法。这里,为了方便说明,我暂且定义一个词——有效交点:扫描线与多边形某边产生的位于该边两端点之间(不含端点)的交点。我自己想出了一种新的高效的判定规则:当
(x_1-x_i )(x_2-x_i )<0
时,可确定这条边与扫描线产生了有效交点。其中x_1与x_2分别表示该条边的两个端点的横坐标,x_i表示扫描线横坐标。
很明显,有效交点产生的充分必要条件是,两个点分居扫描线两侧。因此这个判定是极为简洁凝练的,且相当有效的。
完成对有效交点的选择之后,基本上就已经可以对任意凸多边形进行填充了,因为对于一个凸多边形而言,它与扫描线的有效交点不超过2,那么对于有2个交点的情况,直接连接这两个交点,就完成了一条扫描线。
那么问题来了,不超过2,会不会存在有效交点为1的情况呢?如果是一个有效交点,这条线又应该怎么连接?
1.2 奇数个有效交点的处理法
下面我们来看看,有效交点为1的情况。如图1.2所示,这是不是有效交点为1个呢?答案是:不是。因为根据定义,这应当被视为无有效交点的情况!所以我要说的并不是这种情况。

图1.2 扫描线过端点

我们看图1.3。在图1.3中,扫描线与上侧没有有效交点,但是与下侧有一个有效交点。因此,该扫描线与多边形的有效交点总数为1!那么,到底是什么导致了这种特殊情况的产生?对于这种特殊的情况又该如何处理?
图1.3的情况与图1.2的不同之处,在于穿过顶点的方式。首先,根据拓扑学原理,对于一个封闭图形,在沿着它的边界线转一圈的过程中,如果跨过一条直线,就一定会反向跨过一次。在图1.2中,多边形并没有跨过这条直线,表现在与扫描线接触的两个边位于扫描线的同一侧;而图1.3中,多边形上侧是跨过扫描线的,表现在与扫描线接触的两个边位于扫描线的异侧。这便是有效交点个数为1的特殊情况的产生原因。

图1.3 扫描线穿过角

那么,该如何解决这个问题?我的方法是,在求取交点的判定表达式下多加一个等于零的情况,对于等于零的边,它必然临接了另一条边,那么后面继续判定这两边是否位于扫描线同侧,若不是,则此交点也应当记录,虽然它不是有效交点。但是这样的描述过于简单,事实上这里需要作一连的判断,例如当这个点是第一条边的第一个点时,与它相连的边是最后一个边,此时要做的判定条件应当是
(x_0-x_i )(x_2-x_i )<0
并且最后一条边不需要再重复判断了,应当继续将边的总数降1。当这个点不是第一条边的第一个点时,那么与它相连的边是下一条边,此时要做的判定条件应当是
(x_1-x_i )(x_3-x_i )<0
这个过程比较繁杂,所述情形如图1.4所示,在扫描线为S1的情况下,第一条边的第一个点就是交点(针对边的遍历顺序为L1开始),此时判定条件应当为P6与P2分居S1两侧,且L6不需要再次判定;在扫描线为S2的情况下,此时判定条件应当为P1与P3分居S1两侧,且L2不需要再次判定。

图1.4

整个完整过程保证了,连接点时不会出现只有1个点的尴尬情况。这其实并不局限于凸多边形,事实上,在凹多边形中,它也一样能解决交点个数为任意奇数的情况。
1.3 凹多边形填充法
接下来我考虑,如何完成对凹多边形的填充。前面说的凸多边形,它与扫描线的交点不会超过2个,但是对于凹多边形,它与扫描线的交点可以很多,应该怎么处理呢?其实也很简单了,由于前面我们已经可以记录所有的有效交点了,包括非有效但需要记录的点,因此对于凹多边形,我只需要对点集按y坐标值大小做一个排序,再依此连接即可。

图1.5 凹多边形填充

至此,对于任意一个多边形,其填充过程都可以玩美完成了。但是,由于填充过程运算量大,因此我进一步考虑,如何采用多线程的方法,将整个填充过程变为独立的几个部分,同时进行以加快速度?
1.4 多线程并行填充
多线程这一部分内容是我和一个组员合作完成的,采用的方法其实也非常简单。由于本扫描线填充算法是按一个方向,从最大坐标扫描到最小坐标的,因此我们完全可以这个大的扫描区间细分为任意多个子区间,如图1.5所示。
为了便于说明,我们假定某个多边形的顶点中,最大横坐标为x_max,最小横坐标为x_min,那么原来的填充过程就是产生一系列的直线x=x_i作为扫描线求取有效交点,其中x_i∈[x_min,x_max]。现在我们想要实现多线程,线程数为n,应该如何操作?首先计算总区间长度L,
L=x_max-x_min
然后根据要分的线程个数,计算每个线程的长度l,
l=L/n
于是,很容易得到,第i个线程的作用区间即为
A_i=[ x_min+(i-1)l ,x_min+il ]
那么到这里,直接利用这个区间数值,调用填充处理函数即可完成多线程,甚至这里可以根据使用者的意愿产生线程数。

图1.6 多线程实现

最后把算法的程序框图做出来,如图1.7所示。
在这里插入图片描述
注:本文完全来自个人课程作业
本文只提供算法思想,源码是在一个画图平台上做的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值