综述
在上一篇文章我们介绍了利用类库来完成一个机器人绘制的过程,这里我们一起来看一下怎样直接利用直线和圆弧生成算法来进行图形的绘制。 P.S. 本篇文章针对《计算机图形学》张彩明 版来探讨学习。关于书中的详细算法不会再赘述。 P.P.S. 本篇文章算法扩展思路及代码实现为博主原创内容,如存在纰漏和错误,希望大家指正。
直线生成算法
1.DDA 算法
DDA 算法是最基本的一种直线生成算法了,代码实现简单,不过缺点是计算量比较大,画一个点要两次加法,两次取整运算。另外,DDA 算法还包括了除法运算。不仅算法复杂,而且硬件实现上有一定的难度。优点就是程序简单易懂,在这里实现如下
1 |
//画直线的DDA算法 |
解释一下这里的 gl_Point 方法,这个方法并不是直接调用类库的方法,而是我们自己来实现的画点的方法。
1 |
//画点 |
用来画点的话,我们必须要在 glBegin 方法传入 GL_POINTS 参数,然后利用类库中画点的方法来绘制点。
2.正负法
在教材中只讨论了斜率在 0-1 之间的情况,代码的实现也是仅仅只有 0-1 一种情况,对于斜率大于 1,斜率在-1 和 0 之间以及斜率小于-1 的情况没有加以讨论。 如果我们直接拿来教材中的代码来用,我们会发现只能绘制出 0-1 斜率的直线,对于其他的情况,均绘制错误。 所以我们需要分四种情况来讨论,直线方程 F(x,y)=ax+by+c=0,其中 a=ys-ye,b=xe-xs 设直线的斜率为 k,讨论分类如下 (1)k∈[0,1) 此时有 a<0,b>0 d=F(M)=F(x+1,y+0.5)=a(x+1)+b(y+0.5)+c 当 d>=0 时,Q 在 M 点下方,取右下方的点,d1=F(x+2,y+0.5)=a(x+2)+b(y+0.5)+c=d+a 当 d<0 时,Q 在 M 点上方,取右上方的点,d2=F(x+2,y+1.5)=a(x+2)+b(y+1.5)+c=d+a+b 此时 d 的初始值 d0=F(xs+1,ys+0.5)=a+0.5b **(2)k∈[1,+∞]** 此时有 a<0,b>0 d=F(M)=F(x+0.5,y+1)=a(x+0.5)+b(y+1)+c 当 d>=0 时,Q 在 M 点右侧,取右上方的点,d1=F(x+1.5,y+2)=a(x+1.5)+b(y+2)+c=a+b+d 当 d<0 时,Q 在 M 点左侧,取左上方的点,d2=F(x+0.5,y+2)=b+d 此时 d 的初始值 d0=F(xs+0.5,ys+1)=0.5a+b **(3)k∈[-1,0)** 此时有 a>0,b>0 d=F(M)=F(x+1,y-0.5)=a(x+1)+b(y-0.5)+c 当 d>=0 时,Q 在 M 点下方,取右下方的点,d1=F(x+2,y-1.5)=a(x+2)+b(y-1.5)+c=a-b+d 当 d<0 时,Q 在 M 点上方,取右上方的点,d2=F(x+2,y-0.5)=a(x+2)+b(y-0.5)+c=a+d 此时 d 的初始值 d0=F(xs+1,ys-0.5)=a-0.5b **(4)k∈[-∞,-1)** 此时有 a>0,b>0 d=F(M)=F(x+0.5,y-1)=a(x+0.5)+b(y-1)+c 当 d>=0 时,Q 在 M 点左方,取左下方的点,d1=F(x+0.5,y-2)=a(x+0.5)+b(y-2)+c=d-b 当 d<0 时,Q 在 M 点右方,取右下方的点,d2=F(x+1.5,y-2)=a(x+1.5)+b(y-2)+c=a-b+d 此时 d 的初始值 d0=F(xs+0.5,ys-1)=0.5a-b 注意:上面的 a 和 b 的符号,是在默认起点在终点的左侧来看待的 所以,如果我们传入参数时,第二个点在第一个点的左侧时,我们可能就不会得到正确的结果。所以当我们发现第二个点不在第一个点右侧时,就需要把二者的横纵坐标交换。 代码实现如下,此实现仅供参考,未经优化。
1 |
//画直线的正负法 |
在一开始我们用到了 swap 方法,是用来交换两个数字的,实现如下
1 |
//交换两个数字 |
以上便是正负法的实现,代码仅供参考。
3.Bresenham 算法
在教材中,同样是只针对斜率在 0-1 之间讨论。对于教材中的程序,我们也只能绘制斜率为 0-1 的直线,所以我们需要对另外三种情况进行扩充。 分类讨论如下 (1)k∈[0,1) 即教材中的讲解方法 (2)k∈[1,+∞] 需要把 x 和 y 互换即可 (3)k∈[-1,0) x 不变,y 换为-y (4)k∈[-∞,-1) x 换为-y,y 换为 x 程序实现如下
1 |
//画直线的Bresenham方法 |
以上便是 Bresenham 算法,经测试通过。
圆弧生成算法
1.正负法
教材中只讨论了圆弧在第一象限的情况,不过有趣的是,圆是具有对称性的,在绘制圆形时,我们如果把 x 换为-x,就可以绘制第二象限的图形,把 y 换为-y,就可以绘制第四象限的图形,代码也不需要改动很多。只需要在 gl_Point 上面下功夫即可。 另外,教材中圆弧生成算法中没有指定圆的中心点的坐标,我们可以把它当做参数来传递进来,然后传入 gl_Point 绘图函数即可,相当方便。 注:此方法不需要再繁琐地分类讨论。 在这里给出博主写出的两种方法,一种是如上所介绍的思路,利用对称性,另一种是分类讨论的思想。 (1)利用对称性实现
1 |
//正负法画圆 |
(2)分类讨论思想
1 |
//正负法画圆3 |
在上面的代码中,我们传入了 centerX 和 centerY 以及 area 参数。其中 centerX 和 centerY 是圆弧中心点的坐标,area 是所在的象限,传入的参数需要是 1,2,3,4 中的一个数字,如果传入其他数字则不会绘制出任何图形。 注:此方法只能一次性绘制一个四分之一圆弧,局限性比较大,如果要融入弧度,改动量比较大。
2.Bresenham 算法
和上面方法类似,我们的实现同样非常简单,即使教材中只讨论了八分之一圆,我们可以利用对称的思想来实现画圆。另外我们添加了圆弧中心点坐标已经所在的区块。 从(π/4,π/2)这个区块开始,编号为 1,角度为 45°,顺时针旋转(0,π/4)的编号为 2,以此类推,参数变量为 area 代码实现如下
1 |
//Bresenham画圆算法 |
此段代码亲测可用,仅供参考。
终极目标
上一节我们实现了用类库的方法和 sin,cos 方法来定位坐标绘制机器人,在这一节我们就利用上述的直线和圆弧生成算法,对上一篇中的机器人进行绘制。 在这里只贴出最核心的部分,那就是绘画的函数了,只是简单地传入坐标点然后调用刚才实现的一些方法,比较繁琐,但是比较简单,核心代码如下
1 |
//myDisplay函数用来画图 |
其他的部分不再赘述,都十分基础。
运行结果
直接贴图如下 嘿嘿,我们直接用生成算法绘制的图形是不是更好看一些呢?
总结
本节介绍了各种直线生成算法和圆弧生成算法,以及利用该算法重新绘制机器人,希望对大家有帮助!