习题3.推广本章第一节给出的产生线段的整数Bresenham算法,去掉0<=m<=1和x1<x2的限制,使能完成对任意线段的扫描转换。
解答:Bresenham画线算法使用实际直线上点与可选像素点之间的距离差作为判别式。
假设要绘制的空间任一直线段的两个端点为(x1, y1)和(x2, y2),如果保证x1≤x2,则Bresenham画线算法的处理分为图3.1中所示的四种情况。
第(1)种情况,直线的斜率m处于0和1之间,当x增量为1像素时,y增量在0与1之间,此时下一个可选像素点为P1(x p+1, y p)或P2(x p+1,y p+1)。
此时在x = x p +1处直线上点的y值是y = m(x p + 1) + b,该点到P1(x p+1, y p)和P2(x p+1,y p+1)的距离分别为d1和d2:d1 = y – y p = m(x p + 1) + b - y pd2 = (y p + 1) – y = (y p + 1) – m(x p + 1) – b这两个距离的差为:d = d1– d2 = 2m(x p + 1) – 2y p + 2b – 1若d>0,则下一个像素点取P2(x p+1,y p+1);若d<0,则下一个像素点取P1(x p+1, y p);若d=0,则下一个像素点可取这两个像素点中任意一个。
为了简便d的符号的计算,可引入一个新的判别量p p:p p = Δxd = Δx(d1– d2) = 2Δy·x p– 2Δx·y p + c其中,Δx = x2– x1,Δy = y2– y1,c = 2·Δy +Δx(2b – 1)。
因为这里Δx>0,故p p与d 同号,可以作为判别量。
下面看如何从p p计算p p+1。
将上式中的p换成p+1,有:p p+1 = 2Δy·x p+1– 2Δx·y p+1 + c因为x p+1 = x p + 1,可知:p p+1– p p = 2Δy – 2Δx (y p+1– y p)当p<0时,取P1(x p+1, y p),此时y p+1 = y p,所以p p+1 = p p + 2Δy;否则,取P2(x p+1,y p+1),此时y p+1 = y p + 1,所以p p+1 = p p + 2(Δy –Δx)。
此时还需要看判别量p1的初始值,因为线段上第一个像素点可以取起点(x1, y1),所以有:p1 = 2Δy·x1 - 2Δx·y1 + 2·Δy +Δx(2b – 1)因为y1 = (Δy /Δx)x1 + b,可计算出:p1 = 2Δy –Δx其它三种情况都可以用此方法来完成处理。
第(2)种情况,直线的斜率m大于1,当y增量为1像素时,x增量在0与1之间,此时下一个可选像素点为P1(x p, y p+1)或P2(x p+1,y p+1)。
在y = y p +1处直线上点的x值是x = (y p + 1 – b)/m,该点到P1(x p+1, y p)和P2(x p+1,y p+1)的距离分别为d1和d2:d1 = x – x p = (y p + 1 – b)/m - x pd2 = (x p + 1) – x = (x p + 1) – (y p + 1 – b)/m则这两个距离的差为:d = d1– d2 = 2(y p + 1 – b)/m – 2x p– 1因为Δy>0,为简便运算引入新的判别量p p:p p = Δyd = Δy(d1– d2) = 2Δx·y p– 2Δy·x p + c其中,Δx = x2– x1,Δy = y2– y1,c = 2·Δx(1 – b) –Δy。
下面看如何从p p计算p p+1。
将上式中的p换成p+1,则:p p+1 = 2Δx·y p+1– 2Δy·x p+1 + c因为y p+1 = y p + 1,可知:p p+1– p p = 2Δx – 2Δy (x p+1– x p)当p<0时,取P1(x p, y p+1),此时x p+1 = x p,所以p p+1 = p p + 2Δx;否则,取P2(x p+1,y p+1),此时x p+1 = x p + 1,所以p p+1 = p p + 2(Δx –Δy)。
计算判别量p1的初始值时,因为线段上第一个像素点可以取起点(x1, y1),所以有:p1 = 2Δx·y1 - 2Δy·x1 + 2·Δx(1 – b) –Δy因为y1 = (Δy /Δx)x1 + b,可计算出:p1 = 2Δx –Δy第(3)种情况,直线的斜率m处于0和-1之间,当x增量为1像素时,y减量在0与1之间,此时下一个可选像素点为P1(x p+1, y p)或P2(x p+1,y p–1)。
在x = x p+1处直线上点的y 值是y = m(x p + 1) + b,该点到P1(x p+1, y p)和P2(x p+1,y p–1)的距离分别为d1和d2:d1 = y p– y = y p– m(x p + 1) – bd2 = y – (y p - 1) = m(x p + 1) + b – (y p - 1)则这两个距离的差为:d = d1– d2 = 2y p– 2m(x p + 1) – 2b + 1因为Δx>0,为简便运算引入新的判别量p p:p p = Δxd = Δx(d1– d2) = 2Δx·y p– 2Δy·x p + c其中,Δx = x2– x1,Δy = y2– y1,c =Δx(1 – 2b) – 2·Δy。
下面看如何从p p计算p p+1。
将上式中的p换成p+1,有:p p+1 = 2Δx·y p+1– 2Δy·x p+1 + c因为x p+1 = x p + 1,可知:p p+1– p p = 2Δx (y p+1– y p) – 2Δy当p<0时,取P1(x p+1, y p),此时y p+1 = y p,所以p p+1 = p p– 2Δy;否则,取P2(x p+1,y p–1),此时y p+1 = y p– 1,所以p p+1 = p p– 2(Δx + Δy)。
计算判别量p1的初始值时,因为线段上第一个像素点可以取起点(x1, y1),所以有:p1 = 2Δx·y1 - 2Δy·x1 + Δx(1 – 2b) – 2·Δy因为y1 = (Δy /Δx)x1 + b,可计算出:p1 = Δx – 2Δy第(4)种情况,直线的斜率m小于-1,当y减量为1像素时,x增量在0与1之间,此时下一个可选像素点为P1(x p, y p–1)或P2(x p+1,y p–1)。
在y = y p– 1处直线上点的x值是x = (y p–1 – b)/m,该点到P1(x p, y p–1)和P2(x p+1,y p–1)的距离分别为d1和d2:d1 = x – x p = (y p– 1 – b)/m - x pd2 = (x p + 1) – x = (x p + 1) – (y p– 1 – b)/m则这两个距离的差为:d = d1– d2 = 2(y p– 1 – b)/m – 2x p– 1因为这里-Δy>0,为简便运算引入新的判别量p p:p p = -Δyd = -Δy(d1– d2) = 2Δy·x p– 2Δx·y p + c其中,Δx = x2– x1,Δy = y2– y1,c = 2·Δx(1 + b) + Δy。
下面看如何从p p计算p p+1。
将上式中的p换成p+1,有:p p+1 = 2Δy·x p+1– 2Δx·y p+1 + c因为y p+1 = y p– 1,可知:p p+1– p p = 2Δx + 2Δy (x p+1– x p)当p<0时,取P1(x p, y p–1),此时x p+1 = x p,所以p p+1 = p p + 2Δx;否则,取P2(x p+1,y p–1),此时x p+1 = x p + 1,所以p p+1 = p p + 2(Δx + Δy)。
计算判别量p1的初始值时,因为线段上第一个像素点可以取起点(x1, y1),所以有:p1 = 2Δy·x1 - 2Δx·y1 + 2·Δx(1 + b) + Δy因为y1 = (Δy /Δx)x1 + b,可计算出:p1 = 2Δx + Δy现在我们可以写出Bresenham画线算法的实现函数,在CDraw类中添加如下的成员函数:public://Bresenham画线算法void BresenhamLine(CDC* pDC, int x1, int y1, int x2, int y2, COLORREF color);参数x1、y1、x2、y2为直线段的两个端点的坐标;参数pDC是设备环境对象指针;参数color为绘制直线段的颜色。
实现代码如下://Bresenham画线算法void CDraw::BresenhamLine(CDC *pDC, int x1, int y1, int x2, int y2, COLORREF color) {int x,y,dx,dy,p;//传入端点坐标x值相等if (x1 == x2){if (y1 < y2){for (int i=y1;i<=y2;i++)pDC->SetPixel(x1,i,color);}else{for (int i=y2;i<=y1;i++)pDC->SetPixel(x1,i,color);}return;//斜率判断,斜率绝对值大于1,则m为false,否则为true BOOL m = (fabs(y2-y1)<=fabs(x2-x1));//如果x1大于x2,交换坐标值if (x1 > x2){p=x1;x1=x2;x2=p;p=y1;y1=y2;y2=p;}x = x1;y = y1;dx = x2 - x1;dy = y2 - y1;//斜率绝对值小于等于1if (m){//第一种情况,y递增if (y1 <= y2){p = (dy<<1) - dx;while (x <= x2){pDC->SetPixel(x,y,color);if (p < 0){x++;p=p+ (dy<<1);}else{x++;y++;p=p+((dy-dx)<<1);}}}//第三种情况,y递减else{p = dx – (dy<<1);while (x <= x2){pDC->SetPixel(x,y,color);if (p < 0){x++;p=p-(dy<<1);}else{x++;y--;p=p-((dy+dx)<<1);}}}}//斜率绝对值大于1{//第二种情况,y递增if (y1 <= y2){p = (dx<<1) - dy;while (y <= y2){pDC->SetPixel(x,y,color);if (p < 0){y++;p=p+(dx<<1);}else{x++;y++;p=p+((dx-dy)<<1);}}}//第四种情况,y递减else{p = (dx<<1) + dy;while (y >= y2){pDC->SetPixel(x,y,color);if (p < 0){y--;p=p+(dx<<1);}else{x++;y--;p=p+((dx+dy)<<1);}}}}}此算法也需要注意四种情况的循环条件。