此文根据布雷森汉姆直线算法的维基百科理解而来。
简介
布雷森汉姆直线算法(英语:Bresenham's line algorithm)是用来描绘由两点所决定的直线的算法,它会算出一条线段在n维位图上最接近的点。这个算法只会用到较为快速的整数加法、减法和位元移位,常用于绘制电脑画面中的直线。是计算机图形学中最先发展出来的算法。
推算方法
确定2点得一条直线,如下图所示,但是在屏幕上却不容易,因为屏幕是离散的LED组成,如何尽可能让屏幕上的LED灯亮的看起来很直,这就需要用到布雷森汉姆直线算法。
从左下角到右上角,且斜率小于1
先讨论最简单的,假设起点是,终点是
,且
,
。
当开始选择下个坐标时,无非是2个坐标,x轴+1,y轴看情况+1,即要么,要么
,那么如何判断y轴是否+1,这是我们这次研究的。
浮点型运算
我们先用浮点型的方式做出来,然后再转化成整型格式。
对于由(及
两点所组成之直线,公式如下:
因此,对于每一点的x,其y的值是
其中斜率k=
所以当x轴+1时,y轴的移动距离是,我们采取四舍五入的形式,当y轴移动的距离>0.5时认为y轴可以+1。但是这样的话会存在误差,且越来越大,所以我们还需要对误差进行处理,简单的说就是比如k=0.6,我们x+1后,由于y轴的位移是0.6,超过了0.5所以认为y轴也+1,但理论y还是0.6,所以这里相差了0.4,当x轴继续+1时,y轴理论增加0.6,但由于前面多+了0.4,所以得减去,也就是0.6-0.4=0.2,小于0.5,所以此时y轴不动,以此类推。
我们这里采用C语言伪代码进行演示一下:
void line(x0,x1,y0,y1)
{
dx=x1-x0;//x轴增加量
dy=y1-y0;//y轴增加量
k=dy/dx;//斜率
err=0;//误差
x=x0;
y=y0;//x,y是画点
for(x=x0;x<=x1;x++)
{
point(x,y);
err+=k;//x+1后y的移动距离会+k,同时也是误差
if(err>=0.5)//误差>=0.5,认为y轴该+1
{
y=y+1;
err=err-1;//实际y轴应该移动k,但是由于>=0.5了,所以直接+1,那么就有了k-1的误差
}
}
}
整数型运算:
但是由于STM32等MCU使用浮点型变量计算的话又慢还有误差,所以采取整型数据是最好的。所以我们将分母dx去掉就好了。
我们这里再总结下判断公式,x轴每次+1后,y轴是否+1,全看y轴的增加量,我们以起点为零点计算,那么y轴是否+1可以利用公式:
斜率k展开:
两边*dx:
最终得到的判断条件公式是:
我们将当做起始误差,即err=
所以我们就可以通过这个条件写一个程序,这里采用C语言编写
int main()
{
int x0=1,y0=1,x1=5,y1=3;//(1,1)->(5,3)
int dx=x1-x0;
int dy=y1-y0;
int dir=dy-dx;
int err=-dx/2;//误差基值
int x=x0,y=y0;
for(x=x0;x<=x1;x++)
{
printf("(%d,%d)\n",x,y);
err+=dy;
if(err>=0)
{
y+=1;
err-=dx;
}
}
return 0;
}
运行结果如图所示:
其他情况
其他情况无非是不在第一象限、斜率>1,这些通过对换2个坐标,将其转化为前面讨论的情况即可,比如从左下角到右上角,且斜率>1,那么将x0和y0对换一下,x1和y1对换一下。
小结
其实方法就是四舍五入,然后加上误差,这个算法运算量小且速度快,程序简单。
最终程序
#include "stdio.h"
#include "stdlib.h"
int main()
{
int x0=10,y0=1,x1=1,y1=5;
int dx=abs(x1-x0);
int dy=abs(y1-y0);
int err=0;
int y_inc=(y1-y0)>0 ?1:-1;//判断y是增加还是减少
int x_inc=(x1-x0)>0 ?1:-1;//判断x是增加还是减少
int x=x0,y=y0;//x,y初始化
if(dx>=dy)//x轴长
{
for(x=x0;x!=(x1+x_inc);x+=x_inc)
{
printf("(%d,%d)\n",x,y);
err+=dy;
if(err>=dx/2)
{
y+=y_inc;
err-=dx;
}
}
}
else//y轴长
{
for(y=y0;y!=(y1+y_inc);y+=y_inc)
{
printf("(%d,%d)\n",x,y);
err+=dx;
if(err>=dy/2)
{
x+=x_inc;
err-=dy;
}
}
}
return 0;
}
由(10,1)到(1,5) 的结果如下