DDA算法和Bresenham算法

 DDA算法和Bresenham算法是計算機圖形學中繪製直線的兩種常用算法。本文具體介紹一下DDA算法和Bresenham算法實現的具體思路。DDA算法主要是根據直線公式y = kx + b來推導出來的,其關鍵之處在於如何設定單位步進,即一個方向的步進爲單位步進,另一個方向的步進必然是小於1。算法的具體思路如下:


1. 輸入直線的起點、終點;

2. 計算x方向的間距:△X和y方向的間距:△Y。

3. 確定單位步進,取MaxSteps = max(△X,△Y); 若△X>=△Y,則X方向的步進爲單位步進,X方向步進一個單位,Y方向步進△Y/MaxSteps;否則相反。

4. 設置第一個點的像素值

5. 令循環初始值爲1,循環次數爲MaxSteps,定義變量x,y,執行以下計算:
a. x增加一個單位步進,y增加一個單位步進
b. 設置位置爲(x,y)的像素值

    具體實現代碼如下:

  1. //@brief 浮點數轉整數的宏  
  2. #define  FloatToInteger(fNum)   ((fNum>0)?static_cast<int>(fNum+0.5):static_cast<int>(fNum-0.5))  
  3. /*! 
  4. *  @brief DDA畫線函數 
  5. * 
  6. *  @param pDC      [in]窗口DC 
  7. *  @param BeginPt  [in]直線起點 
  8. *  @param EndPt    [in]直線終點 
  9. *  @param LineCor  [in]直線顏色 
  10. *  @return 無 
  11. */  
  12. void CDrawMsg::DDA_DrawLine(CDC *pDC,CPoint &BeginPt,CPoint &EndPt,COLORREF LineCor)  
  13. {  
  14.     long YDis = (EndPt.y - BeginPt.y);  
  15.     long XDis = (EndPt.x-BeginPt.x);  
  16.     long MaxStep = max(abs(XDis),abs(YDis)); // 步進的步數  
  17.     float fXUnitLen = 1.0f;  // X方向的單位步進  
  18.     float fYUnitLen = 1.0f;  // Y方向的單位步進  
  19.     fYUnitLen = static_cast<float>(YDis)/static_cast<float>(MaxStep);  
  20.     fXUnitLen = static_cast<float>(XDis)/static_cast<float>(MaxStep);  
  21.     // 設置起點像素顏色  
  22.     pDC->SetPixel(BeginPt.x,BeginPt.y,LineCor);   
  23.     float x = static_cast<float>(BeginPt.x);  
  24.     float y = static_cast<float>(BeginPt.y);  
  25.     // 循環步進  
  26.     for (long i = 1;i<=MaxStep;i++)  
  27.     {  
  28.         x = x + fXUnitLen;  
  29.         y = y + fYUnitLen;  
  30.         pDC->SetPixel(FloatToInteger(x),FloatToInteger(y),LineCor);  
  31.     }  
  32. }  


      Bresenham算法是DDA算法畫線算法的一種改進算法。本質上它也是採取了步進的思想。不過它比DDA算法作了優化,避免了步進時浮點數運算,同時爲選取符合直線方程的點提供了一個好思路。首先通過直線的斜率確定了在x方向進行單位步進還是y方向進行單位步進:當斜率k的絕對值|k|<1時,在x方向進行單位步進;當斜率k的絕對值|k|>1時,在y方向進行單位步進。

下面以|k|<1時推導Bresenham算法的數學依據:


bresenham



    請看上圖,已知有一直線y = kx+b,|k|<1。我們通過斜率確定了x方向爲單位步進。當x = Xm時,y = Ym。那麼當x 執行一個單位步進時(即x = Xm+1時),y等於Ym還是等於Ym+1更符合這個直線方程呢?單憑肉眼我們很難得出結論,最好的辦法當然是比較Ym和Ym+1和真實的方程的y值的差是多少(即Yreal = k*(Xm+1)+b),看看哪一個更靠近真實的方程的y值。

我們設 
Dupper = Ym+1 - Yreal = Ym+1 - k*(Xm+1)+b); 表示Ym+1和方程真實值的差

Ddown = Yreal - Ym = k*(Xm+1)+b)- Ym; 表示Ym和方程真實值的差


那就是我們要比較Dupper和Ddown的大小。假設
Diff = Dupper - Ddown = (Ym+1 - k*(Xm+1)+b)) - (k*(Xm+1)+b)- Ym) 
令△X 爲線段x方向的間距,△Y 爲線段y方向的間距。
Pm = △X* Diff = 2*△X* Ym-2*△Y* Xm-2*△Y-△X*(2b-1);
那麼Pm+1 = Pm+2*△X*(Ym+1- Ym)-2*△Y;
其中Ym+1- Ym取0還是1,取決於Pm的符號。

根據等式Diff = Dupper - Ddown = (Ym+1 - k*(Xm+1)+b)) - (k*(Xm+1)+b)- Ym)以及k = △Y/△X,我們可以得出起始像素(x0,y0)的參數p0的值:
P0 =△X-2*△Y;

同理我們推出|k|>1的情況,Qm = 2*Xm*△Y-2*Ym*△X+(2b-2)*△X+△Y;
Qm+1 = Qm+2*(Xm+1-Xm)*△Y-2*△X;

其中Xm+1-Xm等於0還是1,取決於Qm的符號

其中第一個參數Q0 = △Y-2*△X;


    明白了數學原理,我們很快能確定算法步驟:


1. 輸入線段的起點和終點。

2. 判斷線段的斜率是否存在(即起點和終點的x座標是否相同),若相同,即斜率不存在,
只需計算y方向的單位步進(△Y+1次),x方向的座標保持不變即可繪製直線。

3. 計算線段的斜率k,分爲下面幾種情況處理
a. k等於0,即線段平行於x軸,即程序只需計算x方向的單位步進,y方向的值不變

b. |k|等於1,即線段的x方向的單位步進和y方向的單位步進一樣,皆爲1。直接循環△X次計算x和y座標。

4. 根據輸入的起點和終點的x、y座標值的大小決定x方向和y方向的單位步進是1還是-1

6. 畫出第一個點。

7. 若|k| <1,設m =0,計算P0,如果Pm>0,下一個要繪製的點爲(Xm+單位步進,Ym),
Pm+1 = Pm -2*△Y;
否則要繪製的點爲(Xm+單位步進,Ym+單位步進)
Pm+1 = Pm+2*△X-2*△Y;

8. 重複執行第七步△X-1次;

9. 若|k| >1,設m =0,計算Q0,如果Qm>0,下一個要繪製的點爲(Xm,Ym+單位步進),
Pm+1 = Pm -2*△X;
否則要繪製的點爲(Xm+單位步進,Ym+單位步進)
Pm+1 = Pm+2*△Y-2*△X;

10. 重複執行第9步△Y-1次;

參考文獻:

     1. 計算機圖形學,作者: (美國)Donald Hearn(美國)M.Pauline Baker 譯者: 蔡士傑,宋繼強,蔡敏

原文鏈接:http://blog.csdn.net/clever101/article/details/6076841

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章