1.学习两种在计算机中画圆的方法:角度DDA算法及中点画圆算法的数学原理;
2.利用C++语言编写代码实现角度DDA画出的圆类,并基于此绘制出一个圆形;
2.利用C++语言编写代码实现中点画线算法画出的圆类,并基于此绘制出一个圆形。
Visual Studio 2022版
Windows 10/11
1.实现角度DDA画出的圆类,并基于此绘制出一个圆形
角度DDA算法是以圆的极坐标方程为基础,利用角度增量计算步长,并根据几何关系式计算出每一个步长的x、y方向的增量,并由此计算出下一个点亮的像素点坐标,利用循环结构绘制出所有点亮点的坐标,进而绘制出一个完整的圆弧。
圆的极坐标方程为:
x=x0+Rcosθy=y0+Rsinθ
其中,x0,y0为圆心坐标,R为圆的半径,θ为极坐标角度值;
对此式进行微分,可以得到x、y方向的角度增量微分值:
dx=-Rsinθdθdy=Rcosθdθ
其中,dθ为1R+1;
下一个点亮的像素点坐标可以表示为:
xn+1=xn+dxyn+1=yn+dy
将前两个式子代入第三个式子可得:
xn+1=xn-(yn-y0)dθyn+1=yn+(xn-x0)dθ
这样就可以根据前一个点亮像素x、y方向坐标增量递推计算出下一个点亮像素的x、y方向坐标增量,这就是角度DDA算法的基本数学原理。
在实现算法时,可以先计算dθ、dx、dy的初始值,利用圆心坐标加dx、dy计算出点亮像素点的坐标,并利用角度DDA算法的数学公式计算出下一个点亮像素点的dx、dy值,由此得到下一个点亮像素点的坐标,并利用循环进行迭代,最终点亮起始到结束弧段坐标的所有像素点,得到连续的圆弧。可以设置点亮像素的颜色,从而绘制出不同颜色的圆。
在利用类实现时,可以把所有利用角度DDA算法得到的圆弧抽象为一个类,每一个具体的圆弧为一个对象。在头文件中实现类的定义,成员变量为圆的圆心坐标、半径值、起始位置和终止位置的弧度值、线的颜色。源文件中实现类成员函数的定义,即角度DDA画圆函数的定义,在主函数中构建利用角度DDA算法得到的圆弧对象,并画出构建的对象。
2.实现中点画圆法画出的圆类,并基于此绘制出一个圆形
使用中点画圆法画圆可以利用圆的对称性,只需要得到从y轴向右旋转45度位置的八分之一圆弧的所有点坐标即可,再利用对称性得到其他七个部分,最终得到完整的圆,这样可以提高算法效率,也可以提高编程效率。所以为了满足圆的对称性,若圆心不在原点,需要先将圆心平移至原点,再进行接下来的计算。
中点画圆法的核心算法原理就是根据圆的方程计算出下一个增量位置的中点位于圆弧上部或者圆弧下部,若中点位于圆弧下部,则下一个点亮的像素为上部的像素,若中点位于圆弧上部,则下一个点亮的像素为下部的像素。而判别式可以通过圆的解析方程得出,通过对初始判别值进行迭代,可以算出接下来每一个位置的判别值,进而实现判别功能,通过判别可以求得下一个点亮像素的坐标,直到得到的x、y坐标相等为止,这样就可以绘制出八分之一圆弧。
对于判别方法,可以进行如下推导:
圆的解析方程为:
F(x,y)=x2+y2-R2
则可以产生如下判别情况:
(1)若F(x,y)=0,则点在圆上;
(2)若F(x,y)<0,则点在圆内;
(3)若F(x,y)>0,则点在圆外;
若判别结果为点在圆内,则点亮上部的像素,若判别结果在圆外,则点亮下部的像素。
对判别结果进行迭代,通过下一个像素点亮的位置构造新的判别式。通过将下一个点亮像素的坐标代入圆的解析方程,并进行展开,代入之前求出的判别式d的值,推导可以得到当d<0时,即选取上部坐标点亮,则下一个判别式为d+2xp+3;当d>0时,选取下部坐标点亮,即选取下部坐标点亮,则下一个判别式为2xp-yp+5。将初始点亮位置坐标代入判别式,即代入xp0=0,yp0=R,可得判别式初始值d0=1-R,若不取整可得d0=1.25-R,接着可以通过坐标值的迭代得到接下来各个判别式的值,从而计算出需要点亮像素点的坐标。可以设置点亮像素的颜色,从而绘制出不同颜色的圆。
若圆心不在原点,绘制完成后还需要将各点坐标值平移回相应位置。
在利用类实现时,可以把所有利用中点画圆算法得到的圆弧抽象为一个类,每一个具体的圆弧为一个对象。在头文件中实现类的定义,成员变量为圆的圆心坐标、半径值、起始位置和终止位置的弧度值、线的颜色。源文件中实现类成员函数的定义,即中点画圆算法的定义,在主函数中构建利用中点画圆算法得到的圆弧对象,并画出构建的对象。
1.实现角度DDA画圆类,并基于此绘制出一个圆形
(1)头文件内容如下:
#pragma once
class ARCDDACIRCLE
{
public:
//成员变量
int xc, yc, r;
double a1, a2;
int color;
//成员函数
void ARCDDA(ARCDDACIRCLE CIRCLE);
//操作结果,画出一个以xc、yc为圆心,r为半径,a1、a2为圆弧始末点的圆弧
};
(2)源文件内容如下:
//头文件
#include "ARCDDACIRCLE.h"
#include<graphics.h> //包含画图函数
#include <conio.h>//输入输出图形
#include<math.h> //数学函数库
using namespace std;
void ARCDDACIRCLE::ARCDDA(ARCDDACIRCLE CIRCLE)
{
int i, steps;//画出像素次数
double dx, dy;//每一步的坐标增量
double da, radin;//角增量和弧度范围
da = 1.0 / (CIRCLE.r + 1);//求角增量
radin = CIRCLE.a2 - CIRCLE.a1;//求弧度范围
steps = radin / da;//求画出像素的次数
dx = CIRCLE.r * cos(CIRCLE.a1);//x方向坐标增量
dy = CIRCLE.r * sin(CIRCLE.a2);//y方向坐标增量
for (i = 1; i <= steps; i++)
{
putpixel(CIRCLE.xc + dx, CIRCLE.yc + dy, CIRCLE.color);//绘制出相应位置像素
dx = dx - dy * da;//x方向坐标增量变化
dy = dy + dx * da;//y方向坐标增量变化
}//每一次循环x、y方向上增加相应增量值
}//角度DDA函数,输入参数为圆心坐标、半径、起始和结束弧度值、颜色
(3)主函数内容如下:
//头文件
#include "ARCDDACIRCLE.h"
#include<graphics.h> //包含画图函数
#include <conio.h>//输入输出图形
#include<math.h> //数学函数库
using namespace std;
void main() {
initgraph(600, 600);//初始化图形系统,创建图窗,大小为600×600像素
//初始化角度DDA画出的圆类的一个对象
ARCDDACIRCLE CIRCLE1;
CIRCLE1.xc = 300;//圆心横坐标300
CIRCLE1.yc = 300;//圆心纵坐标300
CIRCLE1.r = 200;//圆心半径200
CIRCLE1.a1 = -asin(1) * 2;//起始位置
CIRCLE1.a2 = asin(1) * 2;//结束位置
CIRCLE1.color = WHITE;//圆的颜色
//利用角度DDA画圆
CIRCLE1.ARCDDA(CIRCLE1);//绘制出圆
_getch();//相当于按任意键继续
closegraph();//关闭图窗
system("pause");//保留界面
}
2.实现中点画线算法画出的圆类,并基于此绘制出一个圆形
(1)头文件内容如下:
#pragma once
class MidCircle
{public:
int xc, yc, r,color;//圆的圆心坐标、半径、颜色
void Midcir(MidCircle Circle);//利用中点画圆算法绘出圆
};
(2)源文件内容如下:
#include "MidCircle.h"
#include<graphics.h> //包含画图函数
#include <conio.h>//输入输出图形
#include<math.h> //数学函数库
using namespace std;
void MidCircle::Midcir(MidCircle Circle) {
int x = 0, y = Circle.r;//初始点亮像素坐标
float d = 1.25 - Circle.r;//初始化判别式
while (x <= y) {
putpixel(Circle.xc + x, Circle.yc + y, Circle.color);
putpixel(Circle.xc + x, Circle.yc - y, Circle.color);
putpixel(Circle.xc - x, Circle.yc + y, Circle.color);
putpixel(Circle.xc - x, Circle.yc - y, Circle.color);
putpixel(Circle.xc + y, Circle.yc + x, Circle.color);
putpixel(Circle.xc + y, Circle.yc - x, Circle.color);
putpixel(Circle.xc - y, Circle.yc + x, Circle.color);
putpixel(Circle.xc - y, Circle.yc - x, Circle.color);//八分法画圆
//进行判别
if (d < 0)
d += 2 * x + 3;//若d>0,判别式如下
else {
d += 2 * (x - y) + 5;//若d<0,判别式如下,下一个点亮像素y值减一
y--;
}
x++;//x每次加一
}//循环终止条件为x、y坐标相等,即画完八分之一圆
}//这个函数可以根据中点画圆算法绘制出圆
(3)主函数内容如下:
#include "MidCircle.h"
#include<graphics.h> //包含画图函数
#include <conio.h>//输入输出图形
#include<math.h> //数学函数库
using namespace std;
void main() {
initgraph(600, 600);//初始化图形系统,创建图窗,大小为600×600像素
MidCircle Circle1;//创建用中点画线算法绘制出的圆类的Circle1对象
Circle1.xc = 300;//圆心横坐标300
Circle1.yc = 300;//圆心纵坐标300
Circle1.r = 200;//半径200
Circle1.color = WHITE;//画线颜色为白色
Circle1.Midcir(Circle1);//利用中点画线算法绘制出圆
_getch();//相当于按任意键继续
closegraph();//关闭图窗
system("pause");//保留界面
}
1.实现角度DDA画圆类,并基于此绘制出一个圆形
2.实现中点画线算法画出的圆类,并基于此绘制出一个圆形
本次实验通过C++编程实现了角度DDA和中点画圆算法的原理,更加深入地理解了这两种算法的原理,并且再一次回顾了C++中类的相应知识。
对于这两种算法,其都可以实现计算机屏幕上圆的绘制,但是两种算法的数学原理和算法效率却大有不同。
首先,在数学原理上,角度DDA算法利用圆的极坐标方程,用微小的角度值作为增量,通过极坐标的方法进行计算。已知圆心坐标、半径和弧度范围得到初始的点亮像素点坐标,并求得每一个点亮的点与下一个点坐标之间的增量,进行迭代相加,即点亮所有的像素点,所有点亮的像素点组成一个圆。而中点画圆算法则利用圆的解析方程,即F(x,y)=x2+y2-R2的形式,通过点和圆的位置关系,即若F(x,y)=0,则点在圆上;若F(x,y)<0,则点在圆内;若F(x,y)>0,则点在圆外。根据圆轮廓线附近两个点中点的坐标,对其与轮廓线的位置关系进行判断,从而判断出轮廓线更加接近上下哪一点,从而确定点亮的像素点。对于判别方法,即将前一个像素点的坐标代入判别式中,从而求得判别结果,判别结果有两种,判别结果小于0,说明中点在轮廓线下方,所以取上面的点作为点亮的像素点;判别结果大于0,说明中点在轮廓线上方,所以取下面的点作为点亮的像素点,同时继续利用这个点亮的像素点的坐标判断下一个点亮的像素点坐标,最终到达直线x=y在第一象限上的位置,即绘制出一个八分之一圆弧,再利用圆的对称性,通过x轴、y轴、直线y=x的对称,绘制出一个完整的圆。所以,可以说角度DDA算法和中点画圆算法分别是基于DDA算法和中点画线算法演化出来的。角度DDA算法根据微分方程将绘制直线时步长为1的坐标增量改变为角度增量,直接利用圆的极坐标方程画圆;而中点画圆算法则是利用与中点画线算法相同的原理,只不过将解析方程由直线改成了圆弧,利用点与解析方程的关系构建判别式,从而得到各点亮像素点的位置,同时利用圆的对称性,极大地减少了算法的工作量。
其次,在算法效率方面,两个算法的时间复杂度均为O(n),但是在循环执行次数上,角度DDA算法需要绘制一个完整的圆,其循环次数为弧度值除以角度增量取整后的值,基本绕了圆弧一周;而中点画圆算法只需要绘制出八分之一圆弧,其余圆弧直接利用对称得到,极大地减小了循环次数,增加了算法的运行效率。在运行两个程序时,分别对两个程序的运行效率进行了测试,发现利用角度DDA算法绘制出一个圆的时间为6秒左右,而利用中点画圆算法绘制出一个圆的时间不到1秒,因此可以印证中点画圆算法的效率较高。
对于使用类实现这两种算法的画圆,可以采用如下思路:将使用角度DDA算法绘制出的圆和使用中点画圆算法绘制出的圆作为两个类,成员变量有圆的一些参数,如圆心坐标、半径、圆的颜色等,成员函数即为分别利用两种算法画圆的函数,在源文件中实现函数的定义,并在主函数中创建这两个类的对象,即具体的圆,并利用函数画出具体的圆。使用类可以使程序的结构更加明晰,也应用了面向对象的程序设计思路,将使用两种算法画出的圆设置成为类可以有更强的兼容性,随时绘制出不同位置、不同半径的圆,而不用去函数中修改参数。
本次通过上机实验实际运行了采用两种画圆算法编写的程序,测试了运行效率和画圆效果,实现了在计算机屏幕上圆的绘制。
注意:本上机报告适用于中国地质大学(北京)测绘工程专业计算机图形学课程,其他学校也可以参考使用。这些代码只是起到启发作用,不能完全照搬,具体细节还需要自己斟酌修改。
如有问题欢迎在评论区留言,让我们共同交流,一起进步!