Bresenham画线算法笔记

目录

一.DDA算法和中点画线算法的回顾

二.Bresenham画线算法


一.DDA算法和中点画线算法的回顾

1.DDA算法(Digtal Differential Analyzer)

假设两个端点坐标值是\left ( x_{1},y_{1} \right )\left ( x_{2},y_{2} \right ),规定x_{1}<x_{2},且y=mx+b

由于对直线方程求微分,有m = \frac{\Delta y}{\Delta x}= \frac{y_{i+1}-y_{i}}{x_{i+1}-x_{i}} (1)

整理(1)可得

y_{i+1}=y_{i}+m(x_{i+1}-x_{i})(2)

如果我们把x的步长变得无穷小,就可以近似地估计出下一个点的坐标,从而准确地画出一条直线。但是由于设备的精度是有限的,我们需要将x或者y的点的移动变成单位步长,也就是一次移动1,算法在最大位移方向上每次走一步。也就是说,如果直线当前(x_{i},y_{i})如图中所示(m<1),此时的最大位移方向显然是x,x每次增加一个单位,y增加m。下一个坐标应该如(x_{i+1},y_{i+1}),线段上实际点的坐标应当是y_{i}+m,在这里应当四舍五入,即int(y_{i+1}+0.5)来选取比较接近的点。类似地,如果m>1,最大位移方向显然是y方向,此时x的增量应当是1/m。

 综上所述,DDA画线算法如下:

void DDALine(int x1,int y1,int x2,int y2){
    double dx,dy,e,x,y;
    dx = x2-x1;
    dy = y2-y1;
    e = (fabs(dx)>fabs(dy)?fabs(dx):fabs(dy);
    dx/=e;
    dy/=e;
    x = x1;
    y = y1;
    for(int i=0;i<=e;i++){
        SetPixel((int)(x+0.5),(int)(y+0.5));
        x+=dx;
        y+=dy;
    }
}

 2.中点画线算法

假定:直线的斜率在0~1之间

实际上,对于直线扫描算法来说,已知了当前(x_{i},y_{i}),讨论下一个点的坐标,就相当于我们做一个选择题,选右上的点呢还是右边的点呢?(你妈和老婆掉水里了你先救谁?这当然要分情况讨论了。比如我妈会游泳,可以连着我老婆一起拉上来。)

所以说选点的情形是这样的:

 很显然,直线在x_{i+1}处有直线的坐标Q(x,y),我们只需要判断一下这个点(x,y)和中点的位置关系,它要是在中点上面,选上面的点,反之选下面的点。这就是中点画线法的基本思想,下面给出数学角度的讨论。

假设直线的起点和中点分别是(x_{0},y_{0})(x_{1},y_{1}),直线满足

F(x,y) = ax+by+c=0,其中a = y0-y1,b = x1-x0,c = x0y1-x1y0

从而对于直线上的点,F(x,y)=0,直线下方的点F(x,y)<0,直线上方的点F(x,y)>0

为了判断M和直线的位置关系,可以将M的坐标带入直线方程中,得到

d = F(M) = F(x_{i}+1,y_{i}+0.5)=a(x_{i}+1)+b(y_{i}+0.5)+c

根据d的值,当d<0时,M在Q的下方,应当取上面的点,如图所示:

进一步讨论,可以对每一个像素都计算判别式d得到下一个像素的位置,在d<0时,考虑下一个点的位置,此时x_{i}的坐标向右移动2个单位,而中点的位置也就是(x_{i}+2,y_{i}+1.5),如图中所示

 

 从而我们得到判别式d = F(x_{i}+2,y_{i}+1.5)=a(x_{i}+2)+b(y_{i}+1.5)+c=d+a+b

类似地,在d>=0时,应当取得下方的点,即正右的点,它y的坐标相同和左侧点相同,将M点坐标带入可得到判别式d的值:

d = F(x_{i}+2,y_{i}+0.5)=a(x_{i}+2)+b(y_{i}+0.5)=d+a

 初值的计算,此时第一个像素应当取左侧端点(x0,y0),则相应地我们可以得到中点,带入判别式就可以得到初值为:

d_{0}=F(x_{0}+1,y_{0}+0.5)=a(x_{0}+1)+b(y_{0}+0.5)+c=ax_{0}+b_{0}+c+a+0.5b=F(x_{0}+y_{0})+a+0.5b

综上所述,实际上中点画线算法就是根据中点相对直线的位置进行选点,我们选择了直线的隐式方程得到了判别式d,然后在当前点得到了下一个点的像素选择之后进行了递推,分成了d>=0和d<0两种不同的情况,得到了误差项的递推公式。在下面代码中,为了保证整数运算,将d用2d来代替。

void MidpointLine(int x0,int y0,int x1,int y1){
	int a,b,delta1,delta2,d,x,y;
	a = y0-y1;
	b = x1-x0;
	d = 2*a+b;//设定初值,乘了个2
	delta1 = 2*a;//方便整数运算 
	delta2 = 2*(a+b);
	x = x0;
	y = y0;
	SetPixel(x,y);
	while(x<x1){
		if(d<0){//d<0时,选择右上方的点,增量是a+b,这里乘2 
			x++;
			y++;
			d+=delta2
		}
		else{//右侧点,增量2a 
			x++;
			d+=delta1;
		}
		SetPixel(x,y) //一直到最右侧的端点再结束描像素 
	}
}

二.Bresenham画线算法

1967年,一个叫J.Bresenham的IBM秃头程序员提出了这个算法,这个算法只用整数运算而没有四舍五入,我们还是先看看这个秃头程序员到底想干啥。先看下面这个图:

 我们现在依然是做选择题,注意那个黄色点它不再是中点,而是直线此刻的与坐标线的交点了,我们不妨设它的值是yi,那么依然是这个直线,我们可以得到d1和d2的值。

d_{1} = y-y_{i}=m(x_{i}+1)+b-y_{i}

d_{2}=(y_{i}+1)-m(x_{i}+1)-b

d_{1}-d_{2}=2m(x_{i}+1)-2y_{i}+2b-1

可以通过这个差值来衡量接近程度,如果插值是正的,显然d2更小,我们选择上方点,反之取下方点,在这里又引入了新的判别量p,来减少m这个分式带来的计算不便。

 

 这就是斜率在0~1之间的Bresenham算法。

你问我为什么不打了?

累了886.

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值