中点画圆算法
如同光栅画线算法,我们在每个步中以单位间隔取样并确定离指定圆最近的像素位置。对于给 定半径r和屏幕中心(xc, y c ),可以先使用算法计算圆心在坐标原点(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上两个候选像素间的中点。假设刚在( x k ,y k )绘制了一个像素,下一 步需要确定是像素位置 (x k +1,y k )
还是(xk+1,yk-1)更接近于圆。我们的决策参数是圆函数方程(3.29 ) 在这两个像素的中点求值而得到的:
假如pk < 0,那么这个中点位于圆内,扫描线yk上的像素接近于圆周边界。否则,中点位于圆外或 在圆周边界上,我们选择扫描线yk-1的像素。
后续的决策参数可以使用增量运算得到。我们对取样位置xk+1= xk+2处的圆函数求值,可以 得到下一个决策参数的循环表达式:
因为所有的增量都是整数。
1.输入圆半径r和圆心(xc, y c ),并得到圆周(圆心在原点)上的第一个点:
例3.2使用中点画圆算法画图
对于中心在坐标原点的圆,初始点(x0,y0) = (0,10),计算决策参数的初始增量项:
如同光栅画线算法,我们在每个步中以单位间隔取样并确定离指定圆最近的像素位置。对于给 定半径r和屏幕中心(xc, y c ),可以先使用算法计算圆心在坐标原点(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上两个候选像素间的中点。假设刚在( x k ,y k )绘制了一个像素,下一 步需要确定是像素位置 (x k +1,y k )
还是(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, y c ),并得到圆周(圆心在原点)上的第一个点:
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)的圆路径上,并画坐标值:
例3.2使用中点画圆算法画图
给定圆半径r = 10,我们将演示中点画圆算法,确定在第一象限从x = 0到x = y沿八分圆的像素位置。决策参数的初始值为
使用中点画圆算法计算的后继决策参数值和沿圆路径的位置为
#include "stdafx.h"
#include "GL/glut.h"
#include "stdlib.h"
#include "math.h"
#include "iostream"
using namespace std;
const GLdouble twoPi = 6.283185;
GLsizei winWidth = 400, winHeight = 300; // 屏幕显示宽高.
class screenPt {
public:
screenPt(){
x = y = 0;
}
GLint x, y;
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 init( )
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0, 200.0, 0.0, 150.0);
}
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;//中点参数初值
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.incrementy();
p += 2 * (circPt.getx() - circPt.gety()) + 1;
}
circlePlotPoints(xc, yc, 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());
}
void pieChart( )
{
screenPt circCtr, piePt;
GLint radius = winWidth / 4; // Circle radius.
circCtr.x = winWidth / 2; // Circle center position.
circCtr.y = winHeight / 2;
circleMidpoint(circCtr.x, circCtr.y, radius); // 调用中点画圆方法
}
void displayFcn( )
{
glClear(GL_COLOR_BUFFER_BIT); // Clear display window.
glColor3f(0.0, 0.0, 1.0); // Set circle color to blue.
pieChart();
glFlush();
}
void winReshpeFcn(GLint newWidth, GLint newHeight)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, GLdouble(newWidth), 0.0, GLdouble(newHeight));
glClear(GL_COLOR_BUFFER_BIT);
/* Reset display-window size parameters. */
winWidth = newWidth;
winHeight = newHeight;
}
int _tmain(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("Pie Chart");
init();
glutDisplayFunc(displayFcn);
glutReshapeFunc(winReshpeFcn);
glutMainLoop();
}