画圆之前,首先需要了解如下:
当F(x, y)= 0,表示点在圆上,当F(x, y)> 0,表示点在圆外,当F(x, y)< 0,表示点在圆内。如果M是P1和P2的中点,则M的坐标是(xi + 1, yi – 0.5),当F(xi +1, yi – 0.5)< 0时,M点在圆内,说明P1点离实际圆弧更近,应该取P1作为圆的下一个点。同理分析,当F(xi + 1, yi – 0.5)> 0时,P2离实际圆弧更近,应取P2作为下一个点。当F(xi +1, yi – 0.5)= 0时,P1和P2都可以作为圆的下一个点,算法约定取P2作为下一个点。
现在将M点坐标(xi + 1, yi – 0.5)带入判别函数F(x, y),得到判别式d:
d = F(xi + 1, yi –0.5)= (xi + 1)2 +(yi – 0.5)2 – R2
若d < 0,则取P1为下一个点,此时P1的下一个点的判别式为
d’ = F(xi + 2, yi –0.5)= (xi + 2)2 +(yi – 0.5)2 – R2
展开后将d带入可得到判别式的递推关系
d’ = d + 2xi + 3
若d > 0,则取P2为下一个点,此时P2的下一个点的判别式为
d’ = d + 2(xi -yi) + 5
代码如下:
#include<gl/glut.h>
#include<iostream>
#include<cmath>
int xc,yc,r;
int winwidth=800,winheight=640;//窗口长宽
//声明一个类
class screenPt
{
public:
void setCoords(GLint xCoordValue,GLint yCoordValue){
x=xCoordValue;
y=yCoordValue;
}
screenPt();
GLint getx() const{
return x;
}
GLint gety() const{
return y;
}
void incrementx(){
x++;
}
void decrementy(){
y--;
}
private:
GLint x,y;
};
void circlePlotPoints(GLint,GLint,screenPt);
screenPt::screenPt()
{
x=y=0;
}
//画点
void setPixel(GLint xCoord,GLint yCoord){
glBegin(GL_POINTS);
glVertex2i(xCoord,yCoord);
glEnd();
}
/*中心画圆法*/
void circleMidpoint(GLint xc,GLint yc,GLint radius){
glClear(GL_COLOR_BUFFER_BIT);//清除窗口显示内容
setPixel(xc,yc);//画圆心
screenPt circpt;//创建一个对象
GLint p=1-radius;//
circpt.setCoords(0,radius);
circlePlotPoints(xc,yc,circpt);
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,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 init(){
glClearColor(1.0,1.0,1.0,1.0);//设置绘制窗口颜色为白色
glClear(GL_COLOR_BUFFER_BIT);//清除窗口显示内容
/*设置为投影类型模式和其他观察参数*/
glPointSize(3.0f);
glColor3f(1.0,0.0,0.0);//设置颜色为红
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,winwidth,winheight,0);
}
void DrawCircle(){
circleMidpoint(xc,yc,r);
glFlush();
}
//鼠标拖动
void dragmouse(int x,int y){
r=sqrt((y-yc)*(y-yc)+(x-xc)*(x-xc));
printf("%d %d %d\n",xc,yc,r);
DrawCircle();
glFlush();
}
//鼠标监听,画点
void mymouse(int button,int state,int x,int y){
if(button==GLUT_LEFT_BUTTON && state==GLUT_UP){
r=sqrt((y-yc)*(y-yc)+(x-xc)*(x-xc));
printf("%d %d %d\n",xc,yc,r);
DrawCircle();
glFlush();
}
if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN){
xc=x;
yc=y;
}
}
int main(int argc,char** argv){
glutInit(&argc,argv);//初始化
// scanf("%d%d%d",&xc,&yc,&r);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置绘制模式
glutInitWindowPosition(500,300);
glutInitWindowSize(winwidth,winheight);
glutCreateWindow("画圆");//创建窗口
init();
glutDisplayFunc(DrawCircle);
glutMouseFunc(mymouse);//鼠标监听回调函数
glutMotionFunc(dragmouse);//鼠标拖动
glutMainLoop();
}
运行结果:
第一个、二个为圆心坐标,第三个参数为半径。
为了清除的展示圆心位置,我在圆心处还多画了一个点。
上述的画圆心法也是使用OpenGL的鼠标事件监听来实现,非常简单而且方便地实现了画板又一个功能。