《计算机图形学课内实验》实验报告班级:计算机76班姓名:朱亚坤学号:2176413442日期:2019.10.22一、实验目的及要求中点椭圆算法①熟练掌握中点椭圆算法,能够用C/C++编写程序实现任意大小和位置的中点椭圆的绘画②要求以绘图区域中心为坐标系原点(0,0)③能够输入任意椭圆中心坐标(x,y)和参数rx和ry的值二、实验环境Windows 10 操作系统,Visual C++6.0 IDE,EasyX_2018 春分版三、实验内容用户在控制台输入椭圆中心坐标、长半轴长度和短半轴长度,利用中点椭圆算法,调用画点函数,在以绘图区域中心为坐标系原点(0,0)的图形界面,绘制出相应的中点椭圆。
四、数据结构和算法描述中点椭圆算法将第一象限的部分分成两部分绘制,即在斜率绝对值小于1的区域内在x 方向取单位步长,在斜率绝对值大于1的区域内在y方向取单位步长。
取,可定义椭圆函数为即作为决策参数。
从开始,在方向取单位步长直到区域1和区域2的界限处,然后转换为方向的单位步长,再绘制第一象限中剩余的曲线段。
每一步都要检测曲线斜率值。
在区域1和区域2的交界区,,且因此,移除区域1的条件是2ry2x³2rx2y (3-40)算法过程:1.输入、和椭圆中心,并得到椭圆上的第一个点:2.计算区域1中决策参数的初始值:3.在区域1中的每个位置,从开始,假如,沿中心在的椭圆的下一个点为,并且。
否则,沿椭圆的下一个点为,并且。
其中并且直到。
4.使用区域1中计算的最后点来计算区域2中参数的初始值:5.在区域2的每个位置处,从开始,假如,沿中心为的椭圆的下一个点为,并且否则,沿椭圆的下一个点,并且。
使用与区域1中相同的和增量进行计算,直到。
6.确定其他三个象限中的对称点。
7.将计算出的每个像素位置移动到中心在的椭圆轨迹上,并按坐标值绘制点:五、调试过程及实验结果问题一:电脑没有安装VS Studio怎么办解决方案:由于我通常使用Codeblocks,而MFC只能在VS下使用,所以我就选择了Qt,经网上推荐,我准备直接使用Qt creator进行该实验,但研究了几天,Qt里面的一些槽函数还有信号响应机制是在搞不懂,所以果断放弃;最后在网上找到说给C odeblocks可以配置一个叫做EGE的图形库,可以方便画图,但不知为何,我按照网上的指导配置好后无法使用,一直没有找到解决方案;最后又在网上找到一个简易的易上手的计算机图形学库EasyX,但是这个库也只能在VC环境下使用,可我实在不想下载安装占用空间巨大的VS Studio,最后就找到了老古董Visual C++6.0,并配置了E asyX,这才正式开始了实验。
问题二:坐标轴上的数字是颠倒的解决方案:经过查阅资料,EasyX中绘图界面里插入的文字方向是跟随坐标轴方向的,由于在插入文字前我已经改变了绘图界面的坐标原点和坐标轴方向,所以文字颠倒,因此我在输入文字前利用函数setaspectratio(1, 1)将坐标轴方向改回了原来的方向,在插入文字之后,又改成了我们所需要的Y轴正方向向上的样子。
问题三:绘图函数调用后,图形会立即绘制完成解决方案:在画点函数后添加函数sleep(20),这样每间隔20us才绘制一个点,可以让绘制过程看起来慢一点,更清晰。
问题四:在绘制完一个图形后清屏解决方案:为了可以连续作图,需要对之前绘制的图形进行擦除,可是EasyX自带的清屏函数会把我绘制的坐标轴等标识也擦除掉,所以我索性在每次要绘制新图形之前调用一次void coordinate()我自定义的坐标轴绘制函数,但这样每次绘图屏幕都会闪烁一下,看起来特别怪,又一时间想不到特别好的解决方法,所以又作了一个“绘制成功提示”的界面,在每次绘制完图形后的3秒后出现并关闭绘图界面,这样就避免了绘图界面闪烁的问题。
问题五:颜色选择函数的加入解决方案:我想让绘图变得更漂亮些、更易区分,所以准备添加颜色选择,在EasyX 中,颜色是由16进制的8位数字表示的,同时也有关键字代替,所以我想直接让用户输入颜色名称,即关键字来选择颜色,但是不知为何,他会将输入的关键字当做普通文本处理,发生报错,所以我又试着用switch函数用代表字母来选择相应的16进制数字,但是所选择的16进制数字不会被识别,最后发现我定义的函数的int类型,而在EasyX中,对于颜色有专门的数据类型COLORREF,于是将函数类型修改,问题解决。
问题六:实现连续做图解决方案:实现连续做图,首先想到的当然是用一个while循环,但是while循环的入口和出口,以及在循环体内部的循环条件以及各函数之间的调用搭配关系等都比较难以调控,再考虑到用户的使用方便程度,于是用了if+while+switch+switch多重嵌套到达了效果,还是挺不好整的,逻辑上捋了好久,试了好久才达到想要的效果。
实验结果:绘制中心为(50,10),x方向半轴长为100,y方向半轴长为30,颜色为紫色的椭圆绘制中心为(-10,-80),x方向半轴长为40,y方向半轴长为150,颜色为红色的椭圆六、总结总的来说,本次实验做的还是比较满意的,虽然并没有做出来特别漂亮的UI以及方便的菜单按钮等部件,但花费了很长时间,查阅了很多资料,学习使用了EasyX库,还是有很大收获的,同时也加深理解了这四种基本图元绘制的算法,掌握了其算法内涵,能够用代码将其实现,还是对计算机图形学的学习有很大帮助的。
建议本课程可以适当加长上机学识,并安排实验指导老师,带领同学们学习使用一下OpenGL,做一些小游戏或小项目,可以增加这个课程的趣味性,也可以拓展同学们的技能,同时也是对于以后想继续学习或从事计算机图形学这方面同学的一种引导。
七、附录(源程序清单)Head.h#include <graphics.h> // 这样引用EasyX 图形库#include <conio.h>#include <iostream>#include <windows.h>using namespace std;void coordinate(); //画坐标轴void suc(); //成功提示int Round(float x); //四舍五入COLORREF Color(char i); //选择颜色void Line_DDA(int x1, int y1, int x2, int y2, int color); //DDA直线算法void Line_Bresenham(int x1, int y1, int x2, int y2, int color); //Bresenham直线算法void Circle(int x, int y, int r, int color); //中点圆算法void ellipsePlotPoints(int xCenter, int yCenter, int x, int y, int color); //画椭圆的点void ellipse(int xCenter, int yCenter, int Rx, int Ry, int color); //中点椭圆函数Main.cpp (有省略)#include "head.h"void main(){cout<<"\t\tWelcome!"<<endl;cout<<"**************************************"<<endl;cout<<"press 's' to start, press 'Esc' to end"<<endl;cout<<"**************************************"<<endl;char K;K = getch();if(K == 's'){cout<<"\t-+-+-+-+-+-+-+-+-+-+-+-"<<endl;cout<<"\t press '1' :Bresenham"<<endl<<"\t press '2' :DDA"<<endl<<"\t press '3' :Circle"<<endl<<"\t press '4' :ellipse"<<endl;cout<<"\t-+-+-+-+-+-+-+-+-+-+-+-"<<endl;char t;int x1,y1,x2,y2,r,rx,ry;char color;COLORREF c;while(K != 27) //ESC键的ascll码是27{K = getch();switch(K){case'1': //Bresenham直线算法接收输入,省略break;case'2': //DDA直线算法接收输入,省略break;case'3': //中点圆算法接收输入,省略break;case'4': //中点椭圆算法接收输入{cout<<"Please input the center, x_radius, y_radius and color"<<endl;cout<<"'R':red 'Y':yellow 'B':blue 'P':purple 'G':green other:black"<<endl;cout<<"example: 10 10 40 50 B"<<endl;cin>>x1>>y1>>rx>>ry;cin>>color;c = Color(color);}break;case 27: //退出程序{initgraph(220, 60);setbkcolor(CY AN);cleardevice();setcolor(RED);char w[]="You have exited successfull!";outtextxy(10, 25, w);}break;}initgraph(640, 480);setbkcolor(WHITE);setlinestyle(PS_SOLID, 2);cleardevice();coordinate();t=K;switch(t){case'1': //调用Bresenham直线算法,省略break;case'2': //调用DDA直线算法,省略break;case'3': //调用中点圆算法,省略break;case'4': //调用中点椭圆算法{ellipse(x1,y1,rx,ry,c);suc();K = getch();closegraph();}break;case 27:{initgraph(220, 60);setbkcolor(CY AN);cleardevice();setcolor(RED);char w[]="You have exited successfull!";outtextxy(10, 25, w);}break;}}}else if(K == 27){initgraph(220, 60);setbkcolor(CY AN);cleardevice();setcolor(RED);char w[]="You have exited successfull!";outtextxy(10, 25, w);}_getch();closegraph(); // 关闭图形界面}void coordinate() //建立坐标系{setcolor(BLACK);setorigin(320, 240); //更改坐标系原点setaspectratio(1, -1); //更改坐标轴方向setlinestyle(PS_SOLID, 2);line(-320, 0, 320, 0);line(0, -240, 0, 240);line(320, 0, 315, 5);line(320, 0, 315, -5);line(0, 240, -5, 235);line(0, 240, 5, 235);setfillcolor(BLACK);fillcircle(0, 0, 3);int x[16];int j=0;for(int i = -320; i < 320; i += 40) //画坐标轴上的刻度点{ x[j]=i;fillcircle(x[j], 1, 1);fillcircle(1, x[j], 1);j++;}char a='0';char b[]="40";char c[]="-40";LOGFONT f;gettextstyle(&f); // 获取当前字体设置f.lfHeight = 25; // 设置字体高度为25settextstyle(&f); // 设置字体样式setaspectratio(1, 1);outtextxy(-10, 10, a);outtextxy(-20, -45, b);outtextxy(-45, 10, c);setaspectratio(1, -1);}void suc() //成功输出提示{Sleep(3500);initgraph(200, 60);setbkcolor(LIGHTGRAY);cleardevice();setcolor(LIGHTBLUE);char w1[]="Drawing successfull!";outtextxy(20, 15, w1);char w2[]="Press anykey to continue";outtextxy(15, 35, w2);cout<<"= * = * = * = * = * = * = * = * = * = * = * = * = * = * = * = * = * ="<<endl; }int Round(float x) //四舍五入函数{return (int)(x < 0 ? x - 0.5 : x + 0.5);}COLORREF Color(char i) //选择颜色函数{switch(i){case'R': return RED;break;case'Y': return YELLOW;break;case'B': return BLUE;break;case'P': return MAGENTA;break;case'G': return GREEN;break;default: return 0;break;}}void Line_DDA(int x1, int y1, int x2, int y2, int color) //DDA画直线,省略void Line_Bresenham(int x1, int y1, int x2, int y2, int color) //Bresenham画直线,省略void Circle(int x, int y, int r, int color) //中点画圆法,省略void ellipsePlotPoints(int xCenter, int yCenter, int x, int y, int color) //画椭圆的点{putpixel(xCenter + x, yCenter + y, color);putpixel(xCenter - x, yCenter + y, color);putpixel(xCenter + x, yCenter - y, color);putpixel(xCenter - x, yCenter - y, color);Sleep(20);}void ellipse(int xCenter, int yCenter, int Rx, int Ry, int color) //画中点椭圆{int Rx2 = Rx * Rx;int Ry2 = Ry * Ry;int twoRx2 = 2 * Rx2;int twoRy2 = 2 * Ry2;int p;int x = 0;int y = Ry;int px = 0;int py = twoRx2 * y;fillcircle(xCenter, yCenter, 1); //画椭圆中心ellipsePlotPoints(xCenter, yCenter, x, y, color);p = Round(Ry2 - (Rx2 * Ry) + (0.25 * Rx2)); //第一部分while (px < py) {x++;px += twoRy2;if (p < 0) {p += Ry2 + px;}else {y--;py -= twoRx2;p += Ry2 + px - py;}ellipsePlotPoints(xCenter, yCenter, x, y, color);}p=Round(Ry2*(x+0.5)*(x+0.5)+Rx2*(y-1)*(y-1)-Rx2*Ry2); //第二部分while (y > 0) {y--;py -= twoRx2;if (p > 0) {p += Rx2 - py;}else {x++;px += twoRx2;p += Rx2 - py + px;}ellipsePlotPoints(xCenter, yCenter, x, y, color);}}。