目录
1.基本原理
bresenham画线算法的基本原理是:若设p(x,y)是直线上的一点,与p点最近的网格为(xi,yi),那么,下一个与直线最近的像素只能是正右方的网格点S(xi+1,yi),或右上方的网格点T(xi+1,yi+1)。设直线与网格的交点为Q,令s=|QS|,t=|QT|,则有:
当s<t时,S比较靠近理想直线,应选为S。
当s>=t时,T比较靠近理想直线,应选T。
2.具体推导
设直线段由P1(x1,y1)到Pn(xn,yn),则直线方程可以表示为
y = kx + b
其中 k = (yn-y1)/(xn-x1) = dy/dx , b = y1-kx1
可知,S的坐标为(xi+1,yi),T的坐标为(xi+1,yi+1),因此
s = y-yi =k(xi+1)+b-yi
t = yi+1-y= yi+1-k(xi+1)-b
则有 s-t = 2k(xi+1)+2b-2yi-1
带入k= dy/dx
dx(s-t) = 2(xidy-yidx)+(2dy+2bdx-dx)
因为dx>0,所以以dx(s-t)的正负作为选择S或T的依据,故可令di = dx(s-t)
di = 2(xidy-yidx)+(2dy+2bdx-dx)
将每一个下标加1,则有
di+1 = 2(xi+1dy-yi+1dx)+(2dy+2bdx-dx)
两式相减
di+1 = di+2dy(xi+1-xi)+2dx(yi+1-yi)
因为xi+1-xi = 1,所以
di+1 = di+2dy+2dx(yi+1-yi)
这样,就得到一个递推公式,下一个di+1可以由前一个di递推得到。
若d>=0,即下一个应该选T,此刻yi+1-yi = 1,则
di+1 = di +2(dy-dx)
若d>=0,即下一个应该选S,此刻yi+1=yi 则
di+1 = di+2dy
于是
d1 = 2dy-dx
3.用代码实现操作
首先要用一个函数实现bresenham画图算法来画直线
void Bresenham(int x1,int y1,int x2,int y2){
int dx =abs(x2-x1);
int dy =abs(y2-y1);
if(dx==0 && dy==0) return;
int flag=0;
if(dx<dy){
flag = 1;
swap(&x1,&y1);//这是一个交换函数,如果dy>dx那么交换x,y的坐标
swap(&x2,&y2);//函数swap后续会详写
swap(&dx,&dy);
}
int tx =(x2-x1)>0?1:-1;//这是判断x的画图方向
int ty =(y2-y1)>0?1:-1;//这是判断y的画图方向
int curx =x1;//定义一个当前的x和y
int cury =y1;
int d1=2*dy;
int d2=2*(dy-dx);
int d=d1-dx;
while(curx!=x2){
if(d<0){
d+=d1;//用while函数计算点一直到点与目标点相同
}
else{
cury+=ty;//如果d>0,那么y++
d+=d2;
}
curx+=tx;
if(flag)//根据之前判断dx与dy的大小,画图时要将xy的坐标换回来
{ glPointSize(2);//定义线的大小
glBegin(GL_POINTS);
glVertex2f(cury/400.0,curx/400.0);//根据当前的点画线
glColor3f(1.0f, 0.0f, 0.0f);//定义颜色为红色
glEnd();
glFlush();
}
else
{ glPointSize(2);
glBegin(GL_POINTS);
glVertex2f(curx/400.0,cury/400.0);
glColor3f(1.0f, 0.0f, 0.0f);
glEnd();
glFlush();
}
}
}
在dy>dx的时候要自己设定一个函数swap来交换x和y的坐标点
void swap(int *x,int *y){
int temp = *x;
*x = *y;
*y = temp;
}
设定一个函数给出自己名字的点,根据这个点画图
void myDisplay(void){
glClearColor(0.0, 0.0, 0.0, 0.0);//背景颜色
glClear(GL_COLOR_BUFFER_BIT);//清空页面
Bresenham(-100,50,-150,-30);
Bresenham(-70,-50,80,-50);
Bresenham(-70,-50,-70,10);
Bresenham(45,0,10,20);//心
Bresenham(120,-20,125,-80);
Bresenham(-150,-150,100,-150);//一
Bresenham(-75,400,-125,330);
Bresenham(-50,400,-150,270);
Bresenham(-100,320,-100,120);
Bresenham(30,400,-50,270);
Bresenham(30,400,110,270);
Bresenham(-20,270,90,270);
Bresenham(-30,230,100,230);
Bresenham(35,270,35,120);
Bresenham(35,120,15,150);
Bresenham(-10,160,-30,120);
Bresenham(80,160,100,120);//徐
}
最后用主函数实现画直线写名字
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100,100);//窗口位置
glutInitWindowSize(400, 400);//窗口大小
glutCreateWindow("徐心一");//窗户名
glutDisplayFunc(&myDisplay);//调用函数
glutMainLoop();//一直循环刷帧
return 0;
}
下面是总体的效果