您的位置:首页 > 其它

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);

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();
}


在以上算法基础上可以通过旋转得到任意椭圆。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法