马赛克直线问题

这两天我去一家公司面试了,那是一家不错的游戏公司,我面试的职位是FlashAS3游戏客户端程序员,主管给我一道测试题,在公司做,两天内做完就算合格,这道题是这样子的:

使用任意一门编程语言,在窗口里画图,窗口是600*600的,画一个100*100的网格,然后在上边用鼠标点,第一个点是起点,第二个点是终点,画一条线,并且把这条线经过的所有网格涂色。(窗口和网格的大小和多少也可以修改成数量差不多的,比如网格改成200*200也行)效果类似于用放大镜看画图里的一条直线,如下图:


因为最近我在学Flash,所以直接选择了Flash CS 5.5,打开后新建项目,把舞台修改为600*600,名字为line.as,对应的绑定类就是Main.as了。

首先,通过搜索,很容易找到怎么画一条线,Flash用graphics画就行,其他语言也不难,用循环画线来打网格,这个很简单。

然后找一下两点画线的代码,只要用MouseDown事件配合一个布尔型变量控制点击是起点还是终点,就可以在窗口中画线。

棘手的问题来了,如何给网格涂色呢?

这个问题给我的初印象有点像在已有的舞台坐标系上,再做一个间距更大的坐标系。

我马上就想到了,通过两点计算出直线的方程,根据x计算出y,然后把这个x和y去除以6(600除以100等于6,就是单元格的宽度/高度),就计算出这个方格是舞台上第几个方格。(因为这是两点式,对于起点和终点的x值相等的直线可能用不了,那就单独考虑。出于方便和懒,我把起点和终点的y值相等的直线也单独考虑了,这样剩下的直线就都是斜着的了。)

让x递增(注意,这里的x已经不是舞台的x,而是我之前所说的“间距大的坐标系”的“坐标”,也就是之前的x除以6的结果,下同),计算出y,然后再乘以6得到这个方格的左上角的坐标,用graphics涂色,宽度和高度都是6

这个做法确实不错,但是新的问题马上出现了,那就是画一条斜率非常陡的直线的时候,也就是起点和终点的x相差很小,而y相差很大的时候,画出来的涂色的方格就是不连续的,一跳一跳的。

我当时也没有去想,直接把程序改成了让y递增,x通过方程计算,结果这回是画一条很缓和的直线,方格一跳一跳的。

然后我又没有细想,直接让他们来回计算两次,画两次,画第一次的时候把画过的方格的数据存入Array中,第二次遇到画过的就不画了。当时可能是程序有小问题吧,画的有毛病,后来知道其实这样是可行的。

想想方格会一跳一跳的原因,是因为如果直线太过于陡,x在一个整数之内会有多个y存在,而由于x是递增的1,所以只能画一次y,当时我的x和y都是int,所以这样一个直线画成了不连续的了。

于是我把x改成Number型,每次递增1/100(100是网格的数目),这样即使这条线从一头到另一头,也不会落下。

结果如果把起点定为舞台左上角,终点是舞台右下角,速度会非常慢(大约4秒才能画出来,够慢!),因为这样要执行100*100次循环递增x,计算y。我印象中在《Java程序员,上班那点事儿》这本书中扫过一眼,作者为了测试Java的性能,让Java和C语言(也有可能是C++)分别去做一个10000次的循环,然后计时,所以说这个速度问题应该不是Flash本身的问题。

后来实在是太晚了,我离开公司回家了,路上我想到了答案。

我们不需要每次递增1/100,只需要递增1/斜率就可以了,假如斜率是2,那么一个整数的x只对应2个y,假如斜率是3,那么一个整数的x只对应3个y,所以每次只要递增1/斜率就可以了。

再想想,既然斜率超过1,就是y增长得比x快,这里的斜率超过1,把x和y反过来,斜率就又小于1了。也就是如果斜率小于1,就让x递增1,y用方程计算,反之,就让y递增1,x用方程计算,如果斜率等于1,就两个都可以。

回家后看《计算机图形学(第3版)》(孙家广等编著,清华大学出版社),如果恰好也有这本书的观众可以翻翻,在第166页,有“直线的扫描转换”的三种方法,分别是“数值微分法”、“中点画线法”、“Bresenham画线算法”,我的思路就类似于数值微分法,书上也写了,直线斜率超过1的时候,应该把x和y的地位交换。

okay,我胜利了,成功取得了入职机会!

另外,在解决这个问题的过程中,我还想到了人工智能的寻路算法,如A*算法,或者是图论的迪杰斯特拉算法等,这个在当时主管直接告诉我这样做肯定不对,但是其实这种方法我觉得是有道理的,因为既然可以曲线找最短,也可以在一个无障碍的地图全是可达的、矩形网格地图中寻找最短路线。不过后来一想,这样的结果,也许涂色的方格并不是能完全和用graphics画的线能一致重合,具体各位看官可以自己尝试。

后来我的一个同事实现了一个不同的方法,他把网格当成矩形画到舞台上,直接改变矩形的颜色,然后用每个矩形和线段的两头去碰撞检测,去判断哪些矩形需要改变颜色,结果代码行数比我的少多了,我认为非常精巧。

后来我又在网上找到了这个《计算网格中直线经过的格子》(http://www.itamt.com/2011/04/line-touched-tile/)我没有细看,大家可以自己看看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jether

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值