Bresenham布雷森汉姆直线算法

 此文根据布雷森汉姆直线算法的维基百科理解而来。

简介

布雷森汉姆直线算法(英语:Bresenham's line algorithm)是用来描绘由两点所决定的直线的算法,它会算出一条线段在n维位图上最接近的点。这个算法只会用到较为快速的整数加法、减法和位元移位,常用于绘制电脑画面中的直线。是计算机图形学中最先发展出来的算法。

推算方法

确定2点得一条直线,如下图所示,但是在屏幕上却不容易,因为屏幕是离散的LED组成,如何尽可能让屏幕上的LED灯亮的看起来很直,这就需要用到布雷森汉姆直线算法。

从左下角到右上角,且斜率小于1

先讨论最简单的,假设起点是(x_{0},y_{0}),终点是(x_{1},y_{1}),且x_{1}>x_{0}y_{1}>y_{0}

当开始选择下个坐标时,无非是2个坐标,x轴+1,y轴看情况+1,即要么(x_{0}+1,y_{0}),要么(x_{0}+1,y_{0}+1),那么如何判断y轴是否+1,这是我们这次研究的。

浮点型运算

我们先用浮点型的方式做出来,然后再转化成整型格式。

对于由((x_{0},y_{0})(x_{1},y_{1})两点所组成之直线,公式如下:y-y_{0}=\frac{y_{1}-y_{0}}{x_{1}-x_{0}}(x-x_{0})

因此,对于每一点的x,其y的值是\frac{y_{1}-y_{0}}{x_{1}-x_{0}}(x-x_{0})+y_{0}

其中斜率k=\frac{y_{1}-y_{0}}{x_{1}-x_{0}}

所以当x轴+1时,y轴的移动距离是k(x-x_{0})=k*1=k,我们采取四舍五入的形式,当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可以利用公式:(x_{n}+1-x_{n})*k>=0.5

斜率k展开:(x_{n}+1-x_{n})(\frac{dy}{dx})>=0.5 \rightarrow 1*(\frac{dy}{dx})>=0.5

两边*dx:dy>=0.5dx\rightarrow dy-\frac{dx}{2}>=0

最终得到的判断条件公式是:dy-\frac{dx}{2}>=0

我们将-\frac{dx}{2}当做起始误差,即err=-\frac{dx}{2}

所以我们就可以通过这个条件写一个程序,这里采用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) 的结果如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值