1、調用畫點函數,實現任意斜率直線的繪制 (運用DDA算法、中點Bresenam算法實現并比較算法精度與效率)
② 基本論述 DDA算法 又稱數值微分法,是計算機圖形學中一種基于直線的微分方程來生成直線的方法。 原理就是最直觀的根據斜率的偏移程度,決定是以x為步進方向還是以y為步進方向。然后在相應的步進方向上,每次增加一個像素,而另一個相關坐標變量則為Y k+1=Y k + m(以x為步進變量為例,m為斜率) 假定直線斜率k在0~1之間,當前象素點為(xp,yp),則下一個象素點有兩種可選擇點P1(xp+1,yp)或P2(xp+1,yp+1)。若P1與P2的中點(xp+1,yp+0.5)稱為M,Q為理想直線與x=xp+1垂線的交點。當M在Q的下方時,則取P2應為下一個象素點;當M在Q的上方時,則取P1為下一個象素點。這就是中點畫線法的基本原理 Bresenham算法 :過各行、各列像素中心構造一組虛擬網格線,按直線從起點到終點的順序計算直線各垂直網格線的交點,然后確定該列像素中與此交點最近的像素。該算法的優點在于可以采用增量計算,使得對于每一列,只要檢查一個誤差項的符號,就可以確定該列所求的像素。
②算法 DDA算法描述: m =直線的斜率 可通過計算由x方向的增量△x引起y的改變來生成直線: xi+1=xi+△x yi+1=yi+△y=yi+△x·m 也可通過計算由y方向的增量△y引起x的改變來生成直線: yi+1=yi+△y xi+1=xi+△x=xi+△y/m 以此類推。 中點Bresenam算法描述: 任意給定的兩點所繪制的線段斜率k可能有四種情況,分別是:0<k<1,k>=1,-1<k<0, k<=-1。下面對這四種情況分別進行分析。
(一) 當0<k<1時1.算法原理的推導
(1) 構造中點誤差項為:
di=F(xM,yM)=F(xi + 1,yi + 0.5)=yi + 0.5 –k(xi + 1) -b
(2) 中點誤差的初始值是:
d0=F(x0 + 1,y0 + 0.5)=y0 + 0.5 –k(x0 + 1) -b
=y0 – kx0 – b – k +0.5
其中,因為(x0,y0)在直線上,所以y0-kx0-b=0,則:d0=0.5-k
(3) 推導di+1
a. 當di>=0時;中點在直線上方,取下面的點(x i+1,yi)
di+1 = F(xi+2,yi+0.5)
=yi+0.5-k(xi+2)-b
=di-k
b. 當d<i0時;中點在直線下方,取下面的點(x i+1,yi+1)
di+1 = F(xi+2,yi+1.5)
=yi+1.5-k(xi+2)-b
=di+1 - k
(二) 當k>=1時1.算法原理的推導
(1) 構造中點誤差項
di=F(xM,yM)=F(xi + 0.5,yi +1)=yi + 1 –k(xi + 0.5) -b
(2) 中點誤差的初始值
d0=F(x0 + 0.5,y0 + 1)=y0 + 1 –k(x0 + 0.5) -b
=y0 – kx0 – b –0.5k+1
其中,因為(x0,y0)在直線上,所以y0-kx0-b=0,則:d0=1-0.5k
( 3 ) 推導di+1
a.當di>=0時;中點在直線左方,取右方的點(x i+1,yi+1)
di+1 = F(xi+1.5,yi+2)
=yi+2-k(xi+1.5)-b
=di+1-k
b.當d<i0時;中點在直線右方,取左方的點(x i+1,yi+1)
di+1 = F(xi+0.5,yi+2)
=yi+2-k(xi+0.5)-b
=di+1
(三)當-1<k<0時
1.算法原理的推導
(1) 構造中點誤差項
di=F(xM,yM)=F(xi + 1,yi -0.5)=yi - 0.5 –k(xi + 1) -b
(2) 中點誤差的初始值
d0=F(x0 + 1,y0 - 0.5)=y0 - 0.5 –k(x0 + 1) -b
=y0 – kx0 – b – k -0.5
其中,因為(x0,y0)在直線上,所以y0-kx0-b=0,則:d0=-0.5-k
( 3 ) 推導di+1
a.當di>=0時;中點在直線上方,取下方的點(x i+1,yi-1)
di+1 = F(xi+2.,yi-1.5)
=yi+1.5-k(xi+2)-b
=di-1-k
b.當di<0時;中點在直線下方,取下方的點(x i+1,yi)
di+1 = F(xi+2.,yi-0.5)
=yi-0.5-k(xi+2)-b
=di-k
(四) 當k<=-1時1.算法原理的推導
(1) 構造中點誤差項
di=F(xM,yM)=F(xi + 0.5,yi -1)=yi - 1 –k(xi + 0.5) -b
(2) 中點誤差的初始值
d0=F(x0 + 0.5,y0 - 1)=y0 - 1 –k(x0 + 0.5) -b
=y0 – kx0 – b –0.5k-1
其中,因為(x0,y0)在直線上,所以y0-kx0-b=0,則:d0=0.5-k
( 3 ) 推導di+1
a.當di>=0時;中點在直線右方,取左方的點(x i,yi-1)
di+1 = F(xi+0.5,yi-2)
=yi-2-k(xi+0.5)-b
=di-1
b.當d<i0時;中點在直線下方,取下方的點(x i+1,yi-1)
di+1 = F(xi+1.5,yi-2)
=yi-2-k(xi+1.5)-b
=di-1-k
③ 程序源代碼(是能直接運行的源代碼)
using namespace std
; //(DDA算法實現
)
void DDA_Line
( int x1,int y1,int x2,int y2
) { float increx, increy, x, y, length
; //定義變量int i
; if ( abs
( x2 - x1
) > abs
( y2 - y1
)) //判斷以哪個可以作為增量length
= abs
( x2 - x1
) ; else length
= abs
( y2 - y1
) ; increx
= ( x2 - x1
) / length
; //設置增量,分別為1和kincrey
= ( y2 - y1
) / length
; x
= x1, y
= y1
; //這是起點
for ( i
= 1
; i
<= length
; i++
) { putpixel
( int
( x + 0.5
) , int
( y + 0.5
) , RED
) ; //強制轉換為整型(因為像素點都是整個整個的)x +
= increx
; // x加增量y +
= increy
; // y加增量
}
} //
( 中點Bresenam算法實現
)
void Bresenham_line
( int x0,int y0,int x1,int y1
)
{ int x,y,dx,dy
; float k,e
; //定義變量dx
= x1-x0
; dy
= y1-y0
; k
= dy/dx
; //這是完成k值的初始化e
= -0.5
; x
= x0
; y
= y0
; for
( int i
= 0
; i
<= dx
; i++
) //i每次加一個,直到終點,y選擇性加一或者不加
{
putpixel
( x,y,BLUE
) ; //畫像素點x
= x+1
; e
= e+k
; if
( e
>= 0
) //這里e大于零就減一
{ y
= y+1
; e
= e-1
; } }
} void main
( ) //主函數測試調用算法
{ int x0,y0,x1,y1
; initgraph
( 640, 480
) ; // 定義640 x 480的畫面setbkcolor
( GREEN
) ; //背景cleardevice
( ) ; setcolor
( BLACK
) ; DDA_Line
( 100, 100, 640, 480
) ; Bresenham_line
( 100, 100, 640, 480
) ; getch
( ) ; // 按任意鍵繼續closegraph
( ) ; // 關閉圖形界面
}
關于二者效率和精度的研究: bresenham算法的特點是: 1,不必計算直線之斜率,因此不做除法; 2,不用浮點數,只用整數; 3,只做整數加減法和乘2運算,而乘2運算可以用硬件移位實現. DDA算法的特點: 浮點數運算 不易硬件實現 所以:Bresenham算法相比較于dda算法,速度很快,但是精度沒有dda高,適于用硬件實現.
3、圓或橢圓的繪制
①基本論述
中點畫圓法,考慮圓心在原點,半徑為R的圓在第一象限內的八分之一圓弧,從點(0, R)到點(R, R )順時針方向確定這段圓弧。
②算法 假定某點Pi(xi, yi)已經是該圓弧上最接近實際圓弧的點,那么Pi的下一個點只可能是正右方的P1或右下方的P2兩者之一 構造判別函數: F(x, y)= x2 + y2 – R2
當F(x, y)= 0,表示點在圓上,當F(x, y)> 0,表示點在圓外,當F(x, y)< 0,表示點在圓內。如果M是P1和P2的中點,則M的坐標是(xi + 1, yi – 0.5),當F(xi + 1, yi – 0.5)< 0時,M點在圓內,說明P1點離實際圓弧更近,應該取P1作為圓的下一個點。同理分析,當F(xi + 1, yi – 0.5)> 0時,P2離實際圓弧更近,應取P2作為下一個點。當F(xi + 1, yi – 0.5)= 0時,P1和P2都可以作為圓的下一個點,算法約定取P2作為下一個點。
現在將M點坐標(xi + 1, yi – 0.5)帶入判別函數F(x, y),得到判別式d: d = F(xi + 1, yi – 0.5)= (xi + 1)2 + (yi –0.5)2 – R2 若d < 0,則取P1為下一個點,此時P1的下一個點的判別式為: d’ = F(xi + 2, yi – 0.5)= (xi + 2)2 + (yi –0.5)2 – R2
展開后將d帶入可得到的遞推關系: d’ = d + 2xi + 3 若d > 0,則取P2為下一個點,此時P2的下一個點的判別式為: d’ = F(xi + 2, yi – 1.5)= (xi + 2)2 + (yi –1.5)2 – R
展開后將d帶入可得到判別式的遞推關系: d’ = d + 2(xi - yi) + 5 特別的,在第一個象限的第一個點(0, R)時,可以推倒出判別式d的初始值d0: d0 = F(1, R – 0.5) = 1 – (R – 0.5)2 –R2 = 1.25 - R
③程序源代碼(是能直接運行的源代碼)
//圓的繪制
#include<stdio.h>
#include<graphics.h> // 引用 EasyX 圖形庫
#include<conio.h>
#define x0 400
#define y0 400 //坐標軸中心(x0,y0)
void draw_circle(int x1, int y1, int r) //定義中點畫圓方法
{int d0, x = 0, y = r;//d0為判別式的值d0 = 1.25 - r; //判別式的初始值,1.25可以改為1(方便計算)while (x < y) //{if (d0 >= 0) {d0 = d0 + 2 * (x - y) + 5; //這是d<0時,判別式的遞推關系x += 1; //因為x,y是上一個點,所以要更新坐標y -= 1;//這里體現八段畫法:putpixel(((x + x1) + x0), (y0 - (y + y1)), RED); //像素點(x,y)著色putpixel(((-x + x1) + x0), (y0 - (y + y1)), RED); //像素點(-x,y)著色putpixel(((y + x1) + x0), (y0 - (x + y1)), RED); //像素點(y,x)著色putpixel(((-y + x1) + x0), (y0 - (x + y1)), RED); //像素點(-y,x)著色putpixel(((x + x1) + x0), (y0 - (-y + y1)), RED); //像素點(x,-y)著色putpixel(((-x + x1) + x0), (y0 - (-y + y1)), RED); //像素點(-x,-y)著色putpixel(((y + x1) + x0), (y0 - (-x + y1)), RED); //像素點(y,-y)著色putpixel(((-y + x1) + x0), (y0 - (-x + y1)), RED); //像素點(-y,-x)著色Sleep(50); //休眠函數,作用是延時}else { d0 = d0 + 2 * x + 3; //這是d》0時,判別式的遞推關系x += 1;y = y; //更新坐標點putpixel(((x + x1) + x0), (y0 - (y + y1)), RED); //像素點(x,y)putpixel(((-x + x1) + x0), (y0 - (y + y1)), RED); //像素點(-x,y)putpixel(((y + x1) + x0), (y0 - (x + y1)), RED); //像素點(y,x)putpixel(((-y + x1) + x0), (y0 - (x + y1)), RED); //像素點(-y,x)putpixel(((x + x1) + x0), (y0 - (-y + y1)), RED); //像素點(x,-y)putpixel(((-x + x1) + x0), (y0 - (-y + y1)), RED); //像素點(-x,-y)putpixel(((y + x1) + x0), (y0 - (-x + y1)), RED); //像素點(y,-y)putpixel(((-y + x1) + x0), (y0 - (-x + y1)), RED); //像素點(-y,-x)Sleep(50);}}
}
void main()
{int x1, y1, r;printf("輸入圓心坐標(x1,y1)和圓的半徑r:\n"); //用戶可以通過圓心坐標和圓的半徑來定義圓的形狀scanf("%d %d %d", &x1, &y1, &r); //輸入算法initgraph(x0 * 2, y0 * 2); //這是在初始化圖形窗口大小setbkcolor(GREEN); //背景為綠色cleardevice(); 這里插入圖片描述](https://img-blog.csdnimg.cn/20200730202245196.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RvbWF0b2FhYWE=,size_16,color_FFFFFF,t_70)setcolor(BLUE); //坐標軸為藍色line(x0, 0, x0, y0 * 2); //這是描繪坐標軸Xline(0, y0, x0 * 2, y0); //這是描繪坐標軸Ydraw_circle(x1, y1, r); //中點畫圓算法調用_getch(); //等待一個任意輸入結束closegraph(); //關閉圖形窗口}
② 程序運行截圖(提交你的測試數據及運行結果) ③
加油加油!
總結
以上是生活随笔 為你收集整理的DDA算法、中点Bresenam算法,圆或椭圆的绘制 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。