與方向無關的Bresenham算法

        寫這篇文章的原因是因爲發覺網絡上太少關於計算機圖形學算法的資料了,所以我希望從我這次完成計算機圖形學大作業的例子給一些也正在學的人一些小小的幫助,即使不是些很高深的問題,但我更覺得我需要做的是掃盲。當然我寫的也不是教程,只是針對一道題目而討論。

題目:圖元掃描轉換算法改進:實現改進的畫線算法(DDA或Bresenham),使得線段無論從哪個端點開始畫,算法求出的象素點都是相同的(即與方向無關)。 (要求:交算法說明;源程序及詳細註釋;程序輸出每一個象素點座標)另外一個題目可能沒有明確說到但是我們做的時候要按照的規定是:如果開始是起點A,終點B,你的改進算法是應該以B爲起點,A爲終點,但不能在程序中又把A變爲起點,B爲終點。

        我手頭上得到的一個Bresenham算法如下:

Bresenham_line(int x1 ,int y1,int x2 ,int y)               
      {   int dx,dy,s1,s2,temp,interchange=0,p,i;
           float x,y;
           
           dx = abs(x2 – x1);   dy = abs(y2 – y1);
           s1 = sign(x2 – x1);  s2 = sign(y2 – y1);   /*決定方向*/
           x = x1 + 0.5*s1;     y = y1 + 0.5*s2;
           if(dy > dx){                           /*決定m值*/
              temp = dx; dx = dy; dy = temp;      /*dx爲增長快的邊*/
              interchange = 1;}                   /*在2,3,6,7區間*/
           p = 2 * dy – dx;
           for( i = 1; i <= dx; i++) {
              setPixel(int(x), int(y));
              if( p>0 ){
                 if(interchange)
                     x = x + s1;                  /*把xi當成yI */
                 else
                     y = y +s2;
                 p = p – 2 * dx;
              }
              if(interchange)                /*當pi<=0,yi不變*/        
                 y = y + s2;                    /*把yi當成xi*/
              else
                 x = x + s1;
              p = p + 2 * dy;
           }/*for*/
       }/* Bresenham_line */  

(因爲以上是從PPT上直接copy下來的代碼,在編譯的時候會因爲很多符號問題而產生錯誤,如果有意想用者請將錯誤的符號先改正,因爲此算法只作原理參考,所以我就不再改正了,呵呵,我還是很懶的)
從上面的算法看,可自己驗證出,從(0,0)點畫直線到(3,5)點和從(3,5)點畫直線到(0,0)中間的像素點座標值是有區別的,所以上面的Bresenham算法是跟方向有關的。好的,如果明白這裏說的那麼我就可以正式開始說Bresenham算法的改進了。

因爲Bresenham畫線算法使用了最小的計算量,是最高效的單步畫線算法,所以值得去研究這算法的改進,使得其與畫線方向無關。

 

 

不過實際上當我們每一次畫線肯定會與方向相關,那如何做得像是與方向無關呢?我的解決辦法就是對稱直線生成算法。這種算法是基於這樣一個事實:直線以中點爲界,其兩邊是對稱的。因而可以利用這個對稱性,對Bresenham算法進行改進,使得每進行一次判斷就可以生成相對於直線中點的兩個對稱點。如此以來,直線就由兩端向中間生成。那就是說,當我們知道要畫線的任何兩點,無論它們那個是起點或者終點,本質上都只是以一個方向去判斷所有畫線點的生成,所以就不存在與畫線方向相關的問題了。
 
因爲OpenGL沒怎麼學過,沒有圖形化輸出,所以本程序只採用控制檯方式輸出所有將被描畫的像素點位置,以下是源程序代碼(C++):
#include<iostream.h>
#include<stdlib.h>
#include<math.h>
 
void setPixel(int m,int n)
{
         cout<<"x="<<m<<"; "<<"y="<<n<<endl;
}
 
void Bresenham_line(int x1,int y1,int x2,int y2) //x1爲起點x,y1爲起點y,x2爲終點x,y2爲終點y
{
         int dx,dy,half,s1,s2,temp,interchange=0,p,i; //halfx方向中點值
         double xa,ya,xb,yb;
           
    dx=abs(x2-x1);
         dy=abs(y2-y1);
        
         half=(dx+1)>>1; //移位,與除2效果相同
        
         s1=(x2-x1)/abs(x2-x1); /*決定方向*/
         s2=(y2-y1)/abs(y2-y1);
        
         xa=x1+0.5*s1; //根據不同方向(符號s1,s2)判斷生成像素點的位置
         xb=x2-0.5*s1;
         ya=y1+0.5*s2;
         yb=y2-0.5*s2;
        
         if(dy>dx){                    /*決定m*/
                   temp=dx; dx=dy; dy=temp; /*dx爲增長快的邊*/
                   interchange=1;}           /*2,3,6,7區間*/
         p=2*dy-dx;
         for(i=0;i<=half;i++) {
                   setPixel(int(xa),int(ya)); //輸出由起點向中點靠的各點
                   setPixel(int(xb),int(yb)); //輸出由終點向終點靠的各點
                   if(p>0){
                            if(interchange)
                            {
                                     xa=xa+s1; /*xi當成yi*/
                                     xb=xb-s1; //對稱輸出
                            }                                  
                            else
                            {
                                     ya=ya+s2;
                                     yb=yb-s2; //對稱輸出
                            }
                            p=p-2*dx;
                   }
                   if(interchange) /*pi<=0yi不變*/        
                   {
                            ya=ya+s2; /*yi當成xi*/
                            yb=yb-s2; //對稱輸出
                   }
                   else
                   {
                            xa=xa+s1;
                            xb=xb-s1; //對稱輸出
                   }
                   p=p+2*dy;
         }/*for*/
}/* Bresenham_line */     
 
void main()
{
         int x1,y1,x2,y2;
         cout<<"x1:";
         cin>>x1;     //輸入起點x
         cout<<"y1:";
         cin>>y1;     //輸入起點y
         cout<<"x2:";
         cin>>x2;     //輸入終點x
         cout<<"y2:";
         cin>>y2;     //輸入終點y
         Bresenham_line(x1,y1,x2,y2);
}
 
以下是輸出結果驗證:
(1)     分別輸入x1:0 y1:0 x2:3 y2:5 ,得到一下輸出:
點是從兩端點逐漸靠近中點輸出,中點值會輸出兩次,當然有必要的時候,如重畫會加深顏色的情況,可以通過一定的判斷語句改成一次輸出,但這裏的簡單實現就不用太在乎最終畫一次還是兩次,結果一樣,效率也基本沒有差別,考慮到儘量簡單化程序就不再加入太多條件控制語句了。
 
(2) 分別輸入x1:3  y1:5 x2:0 y2:0 ,得到一下輸出:
可見,只是連續輸出的兩個點的前後順序不同,而所有點的值都相等,所以可以證明該算法與方向無關!
 
發佈了33 篇原創文章 · 獲贊 4 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章