计算机图形学(二)输出图元_6_OpenGL曲线函数_2_中点画圆算法

来源:互联网 发布:mac去广告插件 编辑:程序博客网 时间:2024/05/06 15:25
中点画圆算法
       如同光栅画线算法,我们在每个步中以单位间隔取样并确定离指定圆最近的像素位置。对于给定半径r和屏幕中心(xc,yc),可以先使用算法计算圆心在坐标原点(0, 0)的圆的像素位置,然后通过将xc加到x且yc加到y。从而把计算出的每个位置(x,y)移动到其适当的屏幕位置。在第一象限中,圆弧段从x = 0到x = y,曲线的斜率从0变化到-1.0。因此,可以在该八分圆上的正x方向取单位步长,并使用决策参数来确定每一步两个可能的y位置中,哪一个更接近于圆的位置。然后,其他七个八分圆中的位置可由对称性得到。
    为了应用中点画圆算法,我们定义一个圆函数:

半径为r的圆边界上任何一点(x, y)满足方程fcirc(x,y) = 0。如果点在圆的内部,则圆函数为负值;如果点在圆的外部,则圆函数为正值。总之,任何点(x, y)的相对位置可由圆函数的符号来决定:



(3.30)式中的圆函数在每个取样步上对接近圆周的两个像素的中点进行测试。因此,在中点算法中圆函数是决策参数,并且可以类似画线算法为这个函数设置增量运算。
       图3.19给出了取样位置xk+1上两个候选像素间的中点。假设刚在(xk,yk)绘制了一个像素,下一步需要确定是像素位置(xk+1,yk)
还是(xk+1,yk-1)更接近于圆。我们的决策参数是圆函数方程(3.29 )
在这两个像素的中点求值而得到的:
 

假如pk < 0,那么这个中点位于圆内,扫描线yk上的像素接近于圆周边界。否则,中点位于圆外或在圆周边界上,我们选择扫描线yk-1的像素。

后续的决策参数可以使用增量运算得到。我们对取样位置xk+1= xk+2处的圆函数求值,可以得到下一个决策参数的循环表达式:


其中,yk+1是yk或是yk-1,取决于pk的符号。

     为了得到pk+1增量可能是2xk+1+1(如果pk为负)或是2xk+1+1-2yk+1。和2yk+1的求值也可以通过增量的方式进行,即




在起始位置(0, r)处,这两个项的值分别为0和2r 。2xk+1项的每个后续值可以通过对前一值加2或是对2yk+1的前一值减2而得到。

对圆函数在起始位置(x0,y0) = (0,r)处求值,就可以得到初始决策参数:



假如将半径r指定为整数,就可以对p0进行简单的取整:


因为所有的增量都是整数。

        假设以整数屏幕坐标指定圆参数,那么如同Bresenham画线算法中一样,中点方法使用整数加减来计算沿圆周的像素位置。我们可将中点画圆算法的步骤概括如下:

中点画圆算法的步骤
1.输入圆半径r和圆心(xc,yc),并得到圆周(圆心在原点)上的第一个点:

2.计算决策参数的初始值:


3.在每个xk位置,从k = 0开始,完成下列测试:假如pk < 0,圆心在(0, 0)的圆的下一个点为(xk+1,yk)并且


否则,圆的下一个点是(xk+1,yk-1),并且


其中2xk+1= 2xk+2且2yk+1= 2yk-2。

4.确定在其他七个八分圆中的对称点。

5.将每个计算出的像素位置移动到圆心在(xc,yc)的圆路径上,并画坐标值:


6.重复步骤3到步骤5,直至x >= y。


例3.2使用中点画圆算法画图

给定圆半径r = 10,我们将演示中点画圆算法,确定在第一象限从x = 0到x = y沿八分圆的像素位置。决策参数的初始值为


对于中心在坐标原点的圆,初始点(x0,y0) = (0,10),计算决策参数的初始增量项:

使用中点画圆算法计算的后继决策参数值和沿圆路径的位置为


    下列程序段给出了用于实现中点画圆算法的过程。半径值和圆心坐标传递给过程circleMidpoint。然后计算第一个八分圆上的一个像素位置并传递给过程circlePlotPoints。该过程通过反复调用setPixel函数在帧缓存中该像素及与其对称的所有像素位置来设定圆的颜色,setPixel由OpenGL画点函数实现。


#include <GL/glut.h>class screenPt{    private:Glint x,y;    public:    /*Default Constructor :initializes coordinate position to (0,0)     */    screenPt(){        x = y = 0;     }    void setCoords(GLint xCoordValue,GLint yCorrdValue){        x = xCoordValue;        y = yCorrdValue;    }    Glint getx() const{    return x; }GLint gety() const{      return y;   }void incrementx(){      x++;   } void incrementy(){      y--;   }};void setPixel(GLint xCoord,GLint yCoord){   glBegin(GL_POINTS);   glVertex2i(xCoord,yCoord);   glEnd();}void circleMidpoint(GLint xc,GLint yc,GLint radius){   screenPt circPt;   GLInt p = 1 - radius;//Initial value for midpoint parameter   circPt.setCoords(0,radius);//Set coords for top point of circle   void circlePlotPoints(GLint,GLint,screenPt);   /*Plot the initial point in each circle quadrant*/   circlePlotPoints(xc,yc,circPt);   /*Calculate next point and plot in each octant*/   while(circPt.getx() < circPt.gety()){         circPt.incrementx ();         if (p < 0)            p += 2 * circPt.getx() + 1;         else{            circPt.decrementy ();            p += 2 * (circPt.getx() - circPt.gety()) + 1;    }            circlePlotPoints(xc, xy, circPt);  }}void circlePlotPoints(GLint xc,GLint yc,screenPt circPt){   setPixel(xc + circPt.getx(), yc + circPt.gety());   setPixel(xc - circPt.getx(), yc + circPt.gety());   setPixel(xc + circPt.getx(), yc - circPt.gety());   setPixel(xc - circPt.getx(), yc - circPt.gety());   setPixel(xc + circPt.gety(), yc + circPt.getx());   setPixel(xc - circPt.gety(), yc + circPt.getx());   setPixel(xc + circPt.gety(), yc - circPt.getx());   setPixel(xc - circPt.gety(), yc - circPt.getx());}


0 0