DDA算法和Bresenham算法

来源:互联网 发布:淘宝怎么看司法拍卖 编辑:程序博客网 时间:2024/05/16 10:24

DDA算法和Bresenham算法

本文结构如下:

  • 1、DDA算法
  • 2、Bresenham算法
  • 3、代码实现核心部分


1、DDA算法

DDA算法是计算机图形学中最简单的绘制直线算法。其主要思想是由直线公式y = kx + b推导出来的。
我们已知直线段两个端点P0(x0,y0)和P1(x1,y1),就能求出 k 和 b 。

在k,b均求出的条件下,只要知道一个x值,我们就能计算出一个y值。如果x的步进为1(x每次加1,即x = x +1),那么y的步进就为k+b;同样知道一个y值也能计算出x值,此时y的步进为1,x的步进为(1-b)/k。根据计算出的x值和y值,向下取整,得到坐标(x’,y’),并在(x’,y’)处绘制直线段上的一点。

为进一步简化计算,通常可令b取0,将起点看作(0,0)。设当前点为(xi, yi)则用DDA算法求解(xi+1,yi+1)的计算公式可以概括为:

  • xi+1 = xi + xStep (1)
  • yi+1 = yi + yStep (2)

我们一般通过计算 Δx 和 Δy 来确定xStep和yStep:

  • 如果 Δx > Δy ,说明x轴的最大差值大于y轴的最大差值,x轴方向为步进的主方向,xStep = 1,yStep = k;
  • 如果 Δy> Δx,说明y轴的最大差值大于x轴的最大差值,y轴方向为步进的主方向,yStep = 1,xStep = 1 / k。

根据这个公式,就能通过(xi,yi)迭代计算出(xi+1、yi+1),然后在坐标系中绘制计算出的(x,y)坐标点。


2、Bresenham算法

Bresenham算法也是一种计算机图形学中常见的绘制直线的算法,其本质思想也是步进的思想,但由于避免了浮点运算,相当于DDA算法的一种改进算法。

设直线的斜率为k,当|k| <=1时,x方向为主步进方向;当|k| >1时,y方向为主步进方向。现以|k| <1时为例,推导Bresenham算法的原理。

这里写图片描述

Bresenham算法直线绘制示意图
图片来自:http://st251256589.blog.163.com/blog/static/16487644920114112817666/

图中绘制了一条直线,蓝色点表示该直线上的点,红色点表示光栅下绘制的点。
假设当前点是(xi,yi)
- 如果int(yi+0.5) = yi,则在点(xi, round(yi))处绘制.
- 如果int(yi+0.5) = yi + 1,则在点(xi, round(yi)+1)处绘制。

上述逻辑可简述为:当x方向是主要步进方向时,以每一小格的中点为界,如果当前的yi在中点(图中红色短线)下方,则y取round(yi); 如果当前的yi在中点上方,则y取rund(yi)+1。

引用部分:现考虑这种方法的误差,因为直线的起始点在像素中心,所以误差项d的初值d0=0。x下标每增加1,d的值相应递增。直线的斜率值k,即d=d+k。一旦d≥1,就把它减去1,这样保证d在0、1之间。当d≥0.5时,最接近于当前像素的右上方像素(x+1,y+1)而当d<0.5时,更接近于右方像素(x+1,y)为方便计算,令e=d-0.5,e的初值为-0.5,增量为k。当e≥0时,取当前像素(xi,yi)的右上方像素(x+1,y+1),而当e<0时,更接近于右方像素(x+1,y)可以改用整数以避免除法。由于算法中只用到误差项的符号,因此可作如下替换:
e1 = 2e * Δx。

参考:
http://blog.csdn.net/xdg_blog/article/details/52848891
http://www.cnblogs.com/weiweishuo/archive/2013/03/11/2954443.html


3、代码实现

1、DDA算法

void CGView::DrawDDALine(int startx,int starty, int endx, int endy)  {       CDC* pDC = GetDC();    DrawStandLine(pDC);//绘制标准直线    int x0 = startx;     int y0 = starty ;    int x1 = endx;     int y1 = endy;     int color=RGB(255,0,0);    int x;     float dx,dy,y,k;    dx = x1-x0, dy = y1-y0;    k = dy / dx,y = y0;    int x2 = startx;     int y2 = endx;    for(x = x0;x <= x1;x++)    {   //(20,360)是坐标轴的起点        x2 = 20 + x * 30; //30是坐标轴的单位刻度        pDC->Ellipse( x2 - 5, y2 - 5, x2 + 5, y2 + 5 );        y = k * x + k;        y = (int)(y+0.5);        y2 = 360 - y * 30;    }}

2、Bresenham算法

void CGView::DrawBresenham(int startx,int starty, int endx, int endy){    CDC* pDC = GetDC();    DrawStandLine(pDC);//绘制标准直线    int x0, y0, x1, y1;    x0 = startx;     y0 =starty;    x1 = endx;     y1 = endy;    int x, y, dx, dy;      dx = x1-x0, dy = y1- y0;     x = x0;    y = y0;    double k = dy / dx;    //如果 >1,则y方向是主要步进方向,需要转换坐标系方向    if(abs(k)>1){       Swap(startx,starty,endx,endy);    }    int e = -dx; int x2 = 20, y2 = 360; //(20,360)是坐标轴的起点    for (int x=x0; x<=x1; x++)    {         pDC->Ellipse( x2 - 5, y2 - 5, x2 + 5, y2 + 5 );        x2 += 30;        e += 2 * dy;        if (e > 0)        {             y++;            y2 -= 30; //30是坐标轴的单位刻度            e -= 2*dx;        }    }}

两种算法的运行结果情况如下图所示,从图中可已看出,两种算法都能绘制出直线段,但是在细节稍有不同,当x=4时,DDA算法的y值为3,而Bresenham的算法y为2。在不过从效率上看,由于Bresenham避免了浮点运算,所以效率更高。

这里写图片描述

本文参考了网上的资料,包括图片、文字等。在文中均给出了链接。时间太久了,代码已经找不到出处了。如有疏漏,请指出。

这里写图片描述