以前看到Bresenham畫線算法,直接拿來用,沒有去推導(dǎo)它,近日,參考一些資料,特整理其算法推導(dǎo)過程如下。各位大蝦如果知道其細(xì)節(jié),趕緊閃過,不用浪費(fèi)時間了。
基本上Bresenham畫線算法的思路如下:
// 假設(shè)該線段位于第一象限內(nèi)且斜率大于0小于1,設(shè)起點為(x1,y1),終點為(x2,y2).
// 根據(jù)對稱性,可推導(dǎo)至全象限內(nèi)的線段.
1.畫起點(x1,y1).
2.準(zhǔn)備畫下個點。x坐標(biāo)增1,判斷如果達(dá)到終點,則完成。否則,由圖中可知,下個要畫的點要么為當(dāng)前點的右鄰接點,要么是當(dāng)前點的右上鄰接點.
2.1.如果線段ax+by+c=0與x=x1+1的交點的y坐標(biāo)大于M點的y坐標(biāo)的話,下個點為U(x1+1,y1+1)
2.2.否則,下個點為B(x1+1,y1+1)
3.畫點(U或者B).
4.跳回第2步.
5.結(jié)束.

這里需要細(xì)化的是怎么判斷下個要畫的點為當(dāng)前點的右鄰接點還是當(dāng)前點的右上鄰接點.
設(shè)線段方程:ax+by+c=0(x1<x<x2,y1<y<y2)
令dx=x2-x1,dy=y2-y1
則:斜率-a/b = dy/dx.
從第一個點開始,我們有F(x,1,y1) = a*x1+b*y1+c=0
下面求線段ax+by+c=0與x=x1+1的交點:
由a*(x1+1)+b*y+c = 0, 求出交點坐標(biāo)y=(-c-a(x1+1))/b
所以交點與M的y坐標(biāo)差值Sub1 = (-c-a(x1+1))/b - (y1+0.5) = -a/b-0.5,即Sub1的處始值為-a/b-0.5。
則可得條件當(dāng) Sub1 = -a/b-0.5>0時候,即下個點為U.
反之,下個點為B.
代入a/b,則Sub1 = dy/dx-0.5.
因為是個循環(huán)中都要判斷Sub,所以得求出循環(huán)下的Sub表達(dá)式,我們可以求出Sub的差值的表達(dá)式.下面求x=x1+2時的Sub,即Sub2
1.如果下下個點是下個點的右上鄰接點,則
Sub2 = (-c-a(x1+2))/b - (y1+1.5) = -2a/b - 1.5
故Sub差值Dsub = Sub2 - Sub1 = -2a/b - 1.5 - (-a/b-0.5) = -a/b - 1.代入a/b得Dsub = dy/dx -1;
2.如果下下個點是下個點的右鄰接點,
Sub2 = (-c-a(x1+2))/b - (y1+0.5) = -2a/b - 0.5
故Sub差值Dsub = Sub2 - Sub1 = -2a/b - 0.5 - (-a/b-0.5) = -a/b. 代入a/b得Dsub = dy/dx;
于是,我們有了Sub的處始值Sub1 = -a/b-0.5 = dy/dx-0.5,又有了Sub的差值的表達(dá)式Dsub = dy/dx -1 (當(dāng)Sub1 > 0)或 dy/dx(當(dāng)Sub1 < 0).細(xì)化工作完成。
于是pcode可以細(xì)化如下:
// Pcode for Bresenham Line
// By SoRoMan
x=x1;
y=y1;
dx = x2-x1;
dy = y2-y1;
Sub = dy/dx-0.5; // 賦初值,下個要畫的點與中點的差值
DrawPixel(x, y); // 畫起點
while(x<x2)
{
?x++;
?if(Sub > 0) // 下個要畫的點為當(dāng)前點的右上鄰接點
?{
? Sub += dy/dx - 1; //下下個要畫的點與中點的差值
? y++; // 右上鄰接點y需增1
?}
?else// 下個要畫的點為當(dāng)前點的右鄰接點
?{
? Sub += dy/dx;?
?}
// 畫下個點
DrawPixel(x,y);
}
PS:一般優(yōu)化:
為避免小數(shù)轉(zhuǎn)整數(shù)以及除法運(yùn)算,由于Sub只是用來進(jìn)行正負(fù)判斷,所以可以令Sub = 2*dx*Sub = 2dy-dx,則
相應(yīng)的DSub = 2dy - 2dx或2dy.
思考1:如果Sub = 0時,會產(chǎn)生取兩個點都可以的問題。這個問題還沒深入。