Bresenham's画线算法作图如下:
给定两个点起点P1(x1, y1), P2(x2, y2),如何画它们直连的直线呢,即是如何得到上图所示的蓝色的点。假设直线的斜率0<k>0,直线在第一象限,Bresenham算法的过程如下:
1.画起点(x1, y1).
2.准备画下一个点,X坐标加1,判断如果达到终点,则完成。否则找下一个点,由图可知要画的点要么为当前点的右邻接点,要么是当前点的右上邻接点。
2.1.如果线段ax+by+c=0与x=x1+1的交点y坐标大于(y+*y+1))/2则选右上那个点
2.2.否则选右下那个点。
3.画点
4.跳回第2步
5.结束
具体的算法如下,原理就是比较目标直线与x+1直线交点的纵坐标,哪个离交点近就去哪个
void Bresenhamline(int x0, int y0, int x1, int y1) { int x, y, dx, dy; float k, e; dx = x1 -x0;//x偏移量 dy = y1 -y0;//y偏移量 k = dy / dx;//斜率 e = -0.5; x = x0; y = y0; for (x= x0;x < x1; x++) { printf("%d,%d\n",x,y);//需要的折线上的点 e = e + k; if (e > 0)//y超过半格就去右上的点(否则就是右下的点) { y++; e = e -1; } } }
上述Bresenham算法在计算直线斜率与误差项时用到小数与除法。可以改用整数以避免除法。等式两边同时乘以2*dx, 将e统一乘以2*dx即变成了整数的Bresenhan算法了.
void DrawBresenhamline(int x0, int y0, int x1, int y1) { int dx = x1 - x0;//x偏移量 int dy = y1 - y0;//y偏移量 int dx2 = dx <<1;//x偏移量乘2 int dy2 = dy <<1;//y偏移量乘2 int e = -dx; //e = -0.5 * 2 * dx,把e 用2 * dx* e替换 int x = x0;//起点x坐标 int y = y0;//起点y坐标 for (x = x0; x < x1;x++) { printf ("%d,%d\n",x, y); e=e + dy2;//来自 2*e*dx= 2*e*dx + 2dy (原来是 e = e + k) if (e > 0)//e是整数且大于0时表示要取右上的点(否则是右下的点) { y++; e= e - dx2;//2*e*dx = 2*e*dx - 2*dx (原来是 e = e -1) } } }
最后,包含所有象限的代码如下:
void DrawBresenhamline(int x0, int y0, int x1, int y1) { int dx = x1 - x0;//x偏移量 int dy = y1 - y0;//y偏移量 int ux = dx >0 ?1:-1;//x伸展方向 int uy = dx >0 ?1:-1;//y伸展方向 int dx2 = dx <<1;//x偏移量乘2 int dy2 = dy <<1;//y偏移量乘2 if(abs(dx)>abs(dy)) {//以x为增量方向计算 int e = -dx; //e = -0.5 * 2 * dx,把e 用2 * dx* e替换 int x = x0;//起点x坐标 int y = y0;//起点y坐标 for (x = x0; x < x1;x+=ux) { printf ("%d,%d\n",x, y); e=e + dy2;//来自 2*e*dx= 2*e*dx + 2dy (原来是 e = e + k) if (e > 0)//e是整数且大于0时表示要取右上的点(否则是右下的点) { y += uy; e= e - dx2;//2*e*dx = 2*e*dx - 2*dx (原来是 e = e -1) } } } else {//以y为增量方向计算 int e = -dy; //e = -0.5 * 2 * dy,把e 用2 * dy* e替换 int x = x0;//起点x坐标 int y = y0;//起点y坐标 for (y = y0; y < y1;y += uy) { printf ("%d,%d\n",x, y); e=e + dx2;//来自 2*e*dy= 2*e*dy + 2dy (原来是 e = e + k) if (e > 0)//e是整数且大于0时表示要取右上的点(否则是右下的点) { x += ux; e= e - dy2;//2*e*dy = 2*e*dy - 2*dy (原来是 e = e -1) } } } }