Bresenham算法
2015-11-21 23:08
288 查看
Bresenham算法是一系列算法。
首先我们要了解一个事实,实际的描点坐标都是整数的。而公式计算浮点的是浮点数。
算法核心思想:已知当前点坐标及曲(直)线方程,递推下一个(x+1)点的y坐标(或者(y+1)点的x坐标)。抛弃使用三角函数计算坐标,提高数据重用率。
1.DDA画线算法
核心:直线方程 y=kx+b;
当前坐标和下一个坐标的关系。
xn=xn−1+1;
yn=round(yn−1+k);
2.Bresenham画线算法
核心:在DDA算法基础上,抛弃浮点运算,增加一个决策参数p(变化的)。
用公式计算的y和实际的描点yi,yi+1做差比较。
dlow=y−yi=m(xk+1)+b−yi
dup=(yi+1)−y=yi+1−m(xk+1)−b
p=Δx(dlow−dup)=2Δy∗xi−2Δx∗yi+c;
随着x递增1,如果p<0,则y值不变,反之y值加1。
∴p的递推:pi+1=pi+2Δy−2Δx∗{0,1};//0或者1取值取决于pi的符号。
p0=2Δy−Δx;
总结:opengl画线的话,完全可以用glVertex(GL_LINES);
3.Bresenham画圆算法
核心:同Bresenham画线算法思路一样,我们继续比较(xk+1,yk)和(xk+1,yk−1)哪一个实际描点更接近公式描点(x,y)
p=(x+1)2+(y−12)2−r2;//p即计算两个实际描点的中点是否在圆内。
//如果p<0则yk更接近公式计算的y
∴p的递推:pi+1=pi+2x+1+2y∗{0,1} //取值取决于pi的符号。
p0=1−r;
4.Bresenham画椭圆算法。
思路一样,找到一个决策参数p。
不过椭圆有两个决策参数p
rx,ry分别为x轴,y轴上的半径(此假设椭圆长半径与y轴重合)
第一个参数决定椭圆在第一象限上半段的点。p递推公式:
pi+1=pi+2r2yxi+1+r2y−2r2xyi+1∗{0,1};
p0=r2y−r2xry+14r2x
第二个参数决定椭圆在第一象限下半段的点。p递推公式:
pi+1=pi−2r2xyi+1+r2x+2r2yxi+1∗{0,1};
p0=r2y(x0+12)2+r2x(y0−1)2−r2xr2y
然后再根据椭圆对称性,画出其他三个象限的点。
在以上算法基础上可以通过旋转得到任意椭圆。
首先我们要了解一个事实,实际的描点坐标都是整数的。而公式计算浮点的是浮点数。
算法核心思想:已知当前点坐标及曲(直)线方程,递推下一个(x+1)点的y坐标(或者(y+1)点的x坐标)。抛弃使用三角函数计算坐标,提高数据重用率。
1.DDA画线算法
核心:直线方程 y=kx+b;
当前坐标和下一个坐标的关系。
xn=xn−1+1;
yn=round(yn−1+k);
void DDALine(int x0,int y0,int xEnd,int yEnd) { int dx = xEnd - x0; int dy = yEnd - y0; int step , k; float xi,yi; if(abs(dx)>abs(dy)) step = abs(dx); else step = abs(dy);//确定哪个是单位坐标。 xi = dx/step;//假如x坐标是单位坐标,则xi为1; yi = dy/< 248b5 span class="hljs-built_in">step; setPixel(x0,y0,0);//c //c++ MFC下:SetPixel(hdc,x0,y0,0);貌似有效率问题。 //c++ pDC->SetPixel(x0,y0,0); for(k=0;k<step;k++){ x0+=xi; y0+=yi; //下一个点 setPixel(round(x0),round(y0),0); } {
2.Bresenham画线算法
核心:在DDA算法基础上,抛弃浮点运算,增加一个决策参数p(变化的)。
用公式计算的y和实际的描点yi,yi+1做差比较。
dlow=y−yi=m(xk+1)+b−yi
dup=(yi+1)−y=yi+1−m(xk+1)−b
p=Δx(dlow−dup)=2Δy∗xi−2Δx∗yi+c;
随着x递增1,如果p<0,则y值不变,反之y值加1。
∴p的递推:pi+1=pi+2Δy−2Δx∗{0,1};//0或者1取值取决于pi的符号。
p0=2Δy−Δx;
void BresenhamLine(int x0,int y0,int xEnd,int yEnd) { int dx = abs(xEnd-x0); int dy = abs(yEnd-y0); if(dx<dy) { swap(dx,dy); swap(x0,y0); swap(xEnd,yEnd); }//斜率绝对值大于1,则以y为单位坐标 int p = 2*dy-dx;//决策值 int dd = 2*(dy-dx); int ddy = 2*dy; if(x0>xEnd){//斜率小于O;则从尾部画向头部。 swap(x0,xEnd); swap(y0,yEnd); } setPixel(x0,y0,0); while(x0<=xEnd){ x0++; if(p < 0){ p+=ddy;//如果p<0,y值不变。 }else{ y0++; p+=dd; } setPixel(x0,y0,0); } }
总结:opengl画线的话,完全可以用glVertex(GL_LINES);
3.Bresenham画圆算法
核心:同Bresenham画线算法思路一样,我们继续比较(xk+1,yk)和(xk+1,yk−1)哪一个实际描点更接近公式描点(x,y)
p=(x+1)2+(y−12)2−r2;//p即计算两个实际描点的中点是否在圆内。
//如果p<0则yk更接近公式计算的y
∴p的递推:pi+1=pi+2x+1+2y∗{0,1} //取值取决于pi的符号。
p0=1−r;
void Circle(int x, int y, int r) {//x,y为圆心坐标。r为半径。 int x1 = 0; int y1 = r; int p = 1 - r; while (x1 < y1) { if (p > 0) { p -= y1 + y1 - 2; y1--; } p += x1 + x1 + 3; x1++; //以上确定了圆心在原点的圆在第一象限上半段的点。 //可以利用平移及圆的对称性确定真正的点坐标。 plotC(x, y, x1, y1); } } void plotC(int x, int y, int x1, int y1) {//确定八个点 glBegin(GL_POINTS); glVertex2i(x + x1, y + y1); glVertex2i(x + x1, y - y1); glVertex2i(x - x1, y + y1); glVertex2i(x - x1, y - y1); glVertex2i(x + y1, y + x1); glVertex2i(x + y1, y - x1); glVertex2i(x - y1, y + x1); glVertex2i(x - y1, y - x1); glEnd(); glFlush();//c++下,用这个效率会高些。 } 总结:效率比用glOpen(GL_POLYGON)快。
4.Bresenham画椭圆算法。
思路一样,找到一个决策参数p。
不过椭圆有两个决策参数p
rx,ry分别为x轴,y轴上的半径(此假设椭圆长半径与y轴重合)
第一个参数决定椭圆在第一象限上半段的点。p递推公式:
pi+1=pi+2r2yxi+1+r2y−2r2xyi+1∗{0,1};
p0=r2y−r2xry+14r2x
第二个参数决定椭圆在第一象限下半段的点。p递推公式:
pi+1=pi−2r2xyi+1+r2x+2r2yxi+1∗{0,1};
p0=r2y(x0+12)2+r2x(y0−1)2−r2xr2y
然后再根据椭圆对称性,画出其他三个象限的点。
void Ellipse(int x, int y, int rx, int ry) {//x,y为椭圆中心,rx为x上的短轴,ry为y上的长轴。 int x1 = 0; int y1 = ry; int rx2 = rx*rx; int ry2 = ry*ry; int drx2 = 2 * rx2; int dry2 = 2 * ry2; int px = 0; int py = drx2*y1; int p = round(ry2 - rx2*ry + 0.25*rx2);//第一个决策参数 plotE(x, y, x1, y1); while (px < py) { x1++; px += dry2; if (p < 0) { p += ry2 + px; } else { y1--; py -= drx2; p += ry2 + px - py; } plotE(x, y, x1, y1); } p = round(ry2*(x1 + 0.5)*(x1 + 0.5) + rx2*(y1 - 1)*(y1 - 1) - rx2*ry2); //第二个决策参数的初始值依赖上半段的最后一个点的坐标。 while (y1 > 0) { y1--; py -= drx2; if (p > 0) { p += rx2 - py; } else { x1++; px += dry2; p += rx2 - py + px; } plotE(x, y, x1, y1); } } void plotE(int x, int y, int x1, int y1) { glBegin(GL_POINTS); glVertex2i(x + x1, y + y1); glVertex2i(x + x1, y - y1); glVertex2i(x - x1, y + y1); glVertex2i(x - x1, y - y1); glEnd(); glFlush(); }
在以上算法基础上可以通过旋转得到任意椭圆。
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析
- C#获取关键字附近文字算法实例