您的当前位置:首页正文

实验二&实验三 基本图形

2023-08-06 来源:小奈知识网
实验二 基本图形(元)生成技术(一)

直线生成算法

一、实验目的

在一个图形系统中,基本图形(也称为图元、图素等)的生成技术是最基本的,任何复杂的图形都是由基本图形组成的,基本图形生成的质量直接影响该图形系统绘图的质量。所以,需要设计出精确的基本图形生成算法,以确保图形系统绘图的精确性。本次实验的目的就是验证直线生成的三种扫描算法,并要求对基本算法进行扩充和改进,包括:利用Visual C++实现三种直线生成算法,验证算法的正确性;

二、实验任务

1. 理解三种直线生成算法思想,写出实现程序; 2. 添加鼠标功能,实现交互式画直线程序;

3. 将10个像素作为步距单位,编出Bresenham算法的示例。

三、基本知识和实验步骤

任务一:实现DDA画线程序

实验步骤:

1. 建立一个DDALine的工程文件; 2. 添加ddaline()成员函数

方法:在工作区中选择CLASSVIEW类窗口,右击CDDAlineView类,选择“add member function…”,定义如下的成员函数:

void ddaline(CDC* pDC,int x0,int y0,int x1,int y1,COLORREF color);

3. 编写自定义的成员函数ddaline()程序

void CDDALineView::ddaline(CDC* pDC, int x0, int y0, int x1, int y1, COLORREF color) {

int length,i; float x,y,dx,dy; length=abs(x1-x0); if (abs(y1-y0)>length) length=abs(y1-y0); dx=(x1-x0)/length; dy=(y1-y0)/length; x=x0+0.5;y=y0+0.5; for (i=1;i<=length;i++) {

pDC->SetPixel((int)x,(int)y,color); x=x+dx;y=y+dy; } }

4.编写OnDraw()函数

void CDDALineView::OnDraw(CDC* pDC) {

CDDALineDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

// TODO: add draw code for native data here ddaline(pDC,100,100,400,100,RGB(255,0,0)); ddaline(pDC,400,100,400,400,RGB(0,255,0)); ddaline(pDC,400,400,100,400,RGB(0,0,255)); ddaline(pDC,100,400,100,100,RGB(255,255,0)); ddaline(pDC,100,100,400,400,RGB(255,0,255)); ddaline(pDC,100,400,400,100,RGB(0,255,255));} }

5.编译、调试和运行程序,查看程序结果。

任务二、放大10倍后,算法演示程序

先画出(100,100)到(600,400)大小为10的网格,然后从(100,100)以10为单位,计算出直线上各个像素位置。 步骤:

1. 建立DDA2Line工程;

2. 在OnDraw()函数中画出网格,并调用DDA2Line()函数 void CDDA2LineView::OnDraw(CDC* pDC) {

CDDA2LineDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

// TODO: add draw code for native data here //画网格 int gi,gj; //画横线

pDC->TextOut(90,90,\"(100,100)\"); pDC->MoveTo(100,100); for(gj=100;gj<=400;gj=gj+10) {

pDC->MoveTo(100,gj); pDC->LineTo(600,gj); }

//画竖线

pDC->MoveTo(100,100);

for (gi=100;gi<=600;gi=gi+10) {

pDC->MoveTo(gi,100); pDC->LineTo(gi,400); }

pDC->TextOut(590,410,\"(600,400)\");

//画出像素点

DDA2line(pDC,100,100,600,400,RGB(255,0,0)); }

3.添加DDA2Line()成员函数

方法:在工作区中选择CLASSVIEW类窗口,右击CDDAlineView类,选择“add member function…”,定义如下的成员函数:

void DDA2Line(CDC* pDC,int x0,int y0,int x1,int y1,COLORREF color);

4.编写DDA2Line()函数

void CDDA2LineView::DDA2line(CDC *pDC, int x0, int y0, int x1, int y1, COLORREF color) {

int length,i,tx,ty; float x,y,dx,dy; length=abs(x1-x0); if (abs(y1-y0)>length) length=abs(y1-y0);

dx=(float)(x1-x0)/length; dy=(float)(y1-y0)/length; //char tbuf[20]; //sprintf(tbuf,\"dx,dy=%f,%f\ //AfxMessageBox(tbuf); x=x0;y=y0;

for (i=0;i<=length;i=i+10) { tx=(int)((x+5)/10)*10; ty=(int)((y+5)/10)*10; pDC->SetPixel(tx,ty,color); pDC->Ellipse(tx-5,ty-5,tx+5,ty+5); x=x+dx*10;y=y+dy*10; } }

5.调试、运行程序

任务三、加入鼠标功能,实现交互式画直线

第一步:建立DDAMouseLine工程文件; 第二步:向视图类中添加自定义的成员变量

用鼠标右键单击视图类,选择“Add Member Variable…”,添加下面三个成员变量。 proctected :

CPoint m_p1; // 起点 CPoint m_p2; //终点

int m_ist; //区别,m_ist=0,表示直线起点, //m_ist=1,表示直线终点 第三步:向视图类中添加自定义的成员函数原型: public:

void DDAMouseLine(CDC *pDC, int x0, int y0, int x1, int y1, COLORREF color);

第四步:在视图类CPP文件的构造函数中初始化成员变量。

视图类的构造函数名与该视图类的名字相同。在视图类中选择构造函数,如:CDDAMouseLineView(),用鼠标左键双击,输入下面程序代码: CDDAMouseLineView::CDDAMouseLineView() { // TODO: add construction code here m_p1.x=0; m_p1.y=0; //起点 m_p2.x=0; m_p2.y=0; //终点

m_ist=0; //0,第1点;1,第2点; }

第五步:在视图类的OnDraw()函数中加入下列代码,实现视图绘图。 void CMouseSpringView::OnDraw(CDC* pDC) { CMouseSpringDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

// TODO: add draw code for native data here pDC->SelectStockObject(NULL_BRUSH); DDAMouseLine(pDC,m_p1.x,m_p1.y,m_p2.x,m_p2.y,RGB(255,0,0)); // 调用自定义的成员函数,用鼠标画直线 }

第六步:向视图类中添加鼠标OnLButtonDown()函数消息响应函数,并输入鼠标处理程序代码。

void CMouseSpringView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();

pDC->SelectStockObject(NULL_BRUSH); if (!m_ist) //是起点 {

m_p1=m_p2=point; //纪录第一次单击鼠标位置,定起点 m_ist++; } else { m_p2=point; //记录第二次单击鼠标的位置,定终点的点 m_ist--; // 为新绘图作准备 DDAMouseLine(pDC,m_p1.x,m_p1.y,m_p2.x,m_p2.y,RGB(255,0,0)); //绘制新直线 } ReleaseDC(pDC); //释放设备环境 CView::OnLButtonDown(nFlags, point); }

第七步:添加成员函数的程序代码。

void CDDAMouseLineView::DDAMouseLine(CDC *pDC, int x0, int y0, int x1, int y1, COLORREF color) {

int length,i; float x,y,dx,dy; length=abs(x1-x0); if (abs(y1-y0)>length) length=abs(y1-y0);

dx=(float)(x1-x0)/length; dy=(float)(y1-y0)/length; x=x0+0.5;y=y0+0.5; for (i=1;i<=length;i++) { pDC->SetPixel((int)x,(int)y,color); x=x+dx;y=y+dy; } //pDC->MoveTo(x0,y0); //pDC->LineTo(x1,y1); }

第八步:编译运行程序,验证运行结果。

程序改进,添加橡皮筋绘图技术,实现交互式画直线。

向视图类中添加鼠标OnMouseMove ()函数消息响应函数,并输入鼠标处理程序代码。 void CDDAMouseLineView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();

int nDrawmode=pDC->SetROP2(R2_NOT); //设置异或绘图模式,并保存原来绘图模式 pDC->SelectStockObject(NULL_BRUSH); if(m_ist==1)

{ CPoint prePnt,curPnt; prePnt=m_p2; //获得鼠标所在的前一位置 curPnt=point; //绘制橡皮筋线 DDAMouseLine(pDC,m_p1.x,m_p1.y,prePnt.x,prePnt.y,RGB(255,0,0)); //DrawCircle(pDC,m_bO,prePnt); //用异或模式重复画圆,擦出所画的圆 DDAMouseLine(pDC,m_p1.x,m_p1.y,curPnt.x,curPnt.y,RGB(255,0,0)); //DrawCircle(pDC,m_bO,curPnt); //用当前位置作为圆周上的点画圆 m_p2=point; } pDC->SetROP2(nDrawmode); //恢复原绘图模式 ReleaseDC(pDC); //释放设备环境 CView::OnMouseMove(nFlags, point); }

四、实验结果和分析

1. 查看实验结果,验证算法的正确性;

2. 对程序进行分析和比较,你还能提出哪些改进和扩充?例如:

(1)线刷子绘制直线和圆; (2)方形刷子绘制直线和圆; (3)虚线和点划线的绘制;

五、实验总结

总结从本次实验中学到了那些知识点或者有哪些感受?

实验三 基本图形(元)生成技术(二)

圆、椭圆生成算法

一、实验目的

编写圆和椭圆的扫描转换算法程序,验证算法的正确性。

二、实验任务

1. 编写中点画圆法的扫描转换程序,考虑原点在(x0,y0)处程序的改动; 2. 添加鼠标程序,实现交互式画圆; 3. 编写中点画椭圆法的扫描转换程序; 4. 添加鼠标程序,实现交互式画椭圆;

三、实验内容

任务一:中点画圆法的扫描转换算法

编写中点画圆法的扫描转换程序,考虑原点在(x0,y0)处程序的改动;

分析:考虑圆心不再原点,设圆心坐标为(x0,y0)。通过平移坐标原点到圆心,则第二个8分圆上一点p(x,y),其原始坐标为 x’=x+x0 y’=y+y0

即p’1(x0 +x, y+y0)

其它7个对称点分别是:p’2(x0+y,y+x0), p’3 (x0+y,y0-x),p’4 (x0+x,y0-y),p’5 (x0-x,y0-y),p’6 (x0-y,y0-x),p’7 (x0-y,y0+x),p’8 (x0-x,y0+y)

p’1(x0 +x, y+y0) Y’ Y

p(x,y) 程序实现步骤:

(1) 建立MidPointCircle工程文件; p’8 (x0-x,y0+y) R (2) 右击CMidPointCircleView类,建立成员函数 p’2(x0+y,y+x0) void MidpointCircle(CDC *pDC,int x0, int y0, int r, COLORREF color) p’7 (x0-y,y0+x) int CirPot(CDC *pDC,int x0, int y0, int x, int y, COLORREF color) (x0,y0) X’ (3) 编写成员函数代码,程序如下: p’6 (x0-y,y0-x) void CMidPointCircleView::MidpointCircle(CDC *pDC,int x0, int y0, int r, COLORREF color) p’3 (x0+y,y0-x) {

p’4 (x0+x,y0-y)) //自己补充 p’5 (x0-x,y0-y) }

O(0,0) X (x0+R,y0)

(4)编写OnDraw(CDC* pDC)函数,程序如下: void CMidPointCircleView::OnDraw(CDC* pDC) { CMidPointCircleDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here MidpointCircle(pDC,100, 100, 10, RGB(255,0,0)); MidpointCircle(pDC,500, 300, 60, RGB(255,255,0)); }

(6) 编译、运行程序,查看结果。

任务二:添加鼠标程序,实现交互式画圆

在任务1的基础上,完成下列步骤:

(1)向视图类中添加自定义的成员变量

用鼠标右键单击视图类,选择“Add Member Variable…”,添加下面三个成员变量。 proctected :

int m_r; // 半径

CPoint m_bO; // 圆心 CPoint m_bR; //圆上的点

int m_ist; //圆心与圆周上点的区别,m_ist=0,表示鼠标左击点为圆心, //m_ist=1,表示鼠标左击点为圆周上的点

(2)在视图类CPP文件的构造函数中初始化成员变量 CMidPointCircleMouseView::CMidPointCircleMouseView() { // TODO: add construction code here m_bO.x=0; m_bO.y=0; //圆心 m_bR.x=0; m_bR.y=0; //圆上的点 m_ist=0; //圆心与圆上的点区别 m_r=0; //圆的半径 }

(3)向视图类中添加自定义的成员函数原型: public:

int ComputeRadius(CPoint cenp,CPoint ardp);

添加成员函数的程序代码:

int CMouseSpringView::ComputeRadius(CPoint cenp, CPoint ardp) {

int dx=cenp.x-ardp.x; int dy=cenp.y-ardp.y;

//sqrt()函数的调用,在头文件中加入#include \"math.h\" return (int)sqrt(dx*dx+dy*dy); }

(4)向视图类中添加两个鼠标消息响应函数,并输入鼠标处理程序代码。

具体操作方法与鼠标示例1方法相同。一个是OnLButtonDown()函数,另一个是OnMouseMove()函数。程序如下:

void CMidPointCircleMouseView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();

pDC->SelectStockObject(NULL_BRUSH); if (!m_ist) //绘制圆 { m_bO=m_bR=point; //纪录第一次单击鼠标位置,定圆心 m_ist++; } else { m_bR=point; //记录第二次单击鼠标的位置,定圆周上的点 m_ist--; // 为新绘图作准备 m_r=ComputeRadius(m_bO,m_bR); MidpointCircle(pDC,m_bO.x,m_bO.y,m_r,RGB(255,0,0)); } ReleaseDC(pDC); //释放设备环境 CView::OnLButtonDown(nFlags, point); }

void CMidPointCircleMouseView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();

int nDrawmode=pDC->SetROP2(R2_NOT); //设置异或绘图模式,并保存原来绘图模式 pDC->SelectStockObject(NULL_BRUSH); if(m_ist==1) { CPoint prePnt,curPnt; prePnt=m_bR; //获得鼠标所在的前一位置 curPnt=point; //绘制橡皮筋线

m_r=ComputeRadius(m_bO,prePnt);

MidpointCircle(pDC,m_bO.x,m_bO.y,m_r,RGB(255,0,0));//用异或模式重复画圆,擦出所画的圆 // DrawCircle(pDC,m_bO,prePnt); m_r=ComputeRadius(m_bO,curPnt);

MidpointCircle(pDC,m_bO.x,m_bO.y,m_r,RGB(255,0,0)); //用当前位置作为圆周上的点画圆 m_bR=point; } pDC->SetROP2(nDrawmode); //恢复原绘图模式 ReleaseDC(pDC); //释放设备环境 CView::OnMouseMove(nFlags, point); }

任务三:编写中点画椭圆法的扫描转换程序

程序实现步骤:

(1) 建立MidPointEllise工程文件;

(2)右击CMidPointElliseView类,建立成员函数

void MidpointEllise(CDC *pDC, int x0, int y0, int a, int b, COLORREF color) (3) 编写成员函数代码,程序如下:

void CMidPointEllipseView::MidpointEllise(CDC *pDC, int x0, int y0, int a, int b, COLORREF color) {

//自己补充 }

(4) 编写OnDraw()函数

void CMidPointEllipseView::OnDraw(CDC* pDC) { CMidPointEllipseDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here

MidpointEllise(pDC, 300, 200, 50, 20, RGB(255,0,0)); }

(5)编译、运行程序。

任务四:添加鼠标程序,实现交互式画椭圆

程序实现步骤:

(1)~(3)同上,建立MidPointElliseMouse工程文件。 (4)添加成员变量 protected: int b; int a; int m_ist; CPoint CenterPoint; CPoint RightBottom;

CPoint LeftTop;

(5)在构造函数中赋初值

CMidPointElliseMouseView::CMidPointElliseMouseView() { // TODO: add construction code here LeftTop.x=0;LeftTop.y=0; // 左上角坐标初值

RightBottom.x=0;RightBottom.y=0; // 右下角坐标初值 CenterPoint.x=0;CenterPoint.y=0; // 中心点坐标初值 a=0; b=0; //长轴和短轴长度

m_ist=0; //0:表示第一点,1:表示第二点 }

(6) 添加OnLButtonDown()函数

void CMidPointElliseMouseView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();

pDC->SelectStockObject(NULL_BRUSH); if (!m_ist) //第一点,左上角 { LeftTop=RightBottom=point; //纪录第一次单击鼠标位置 m_ist++; } else { RightBottom=point; //记录第二次单击鼠标的位置 m_ist--; // 为新绘图作准备 CenterPoint.x=(LeftTop.x+RightBottom.x)/2; CenterPoint.y=(LeftTop.y+RightBottom.y)/2; a=(int)abs((RightBottom.x-LeftTop.x))/2; b=(int)abs((RightBottom.y-LeftTop.y))/2; MidpointEllise(pDC,CenterPoint.x,CenterPoint.y,a,b,RGB(255,0,0)); } ReleaseDC(pDC); //释放设备环境 CView::OnLButtonDown(nFlags, point); }

(7)添加OnMouseMove()函数

void CMidPointElliseMouseView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();

int nDrawmode=pDC->SetROP2(R2_NOT); //设置异或绘图模式,并保存原来绘图模式 pDC->SelectStockObject(NULL_BRUSH); if(m_ist==1)

{ CPoint prePnt,curPnt; prePnt=RightBottom; //获得鼠标所在的前一位置 curPnt=point; //绘制橡皮筋线

CenterPoint.x=(LeftTop.x+prePnt.x)/2; CenterPoint.y=(LeftTop.y+prePnt.y)/2; a=(int)abs((prePnt.x-LeftTop.x))/2; b=(int)abs((prePnt.y-LeftTop.y))/2; MidpointEllise(pDC,CenterPoint.x,CenterPoint.y,a,b,RGB(255,0,0)); //用异或模式重复画圆,擦出所画的圆 //用当前点作为右下角点,画椭圆

CenterPoint.x=(LeftTop.x+curPnt.x)/2; CenterPoint.y=(LeftTop.y+curPnt.y)/2; a=(int)abs((curPnt.x-LeftTop.x))/2; b=(int)abs((curPnt.y-LeftTop.y))/2; MidpointEllise(pDC,CenterPoint.x,CenterPoint.y,a,b,RGB(255,0,0)); //用当前位置作为圆周上的点画圆 RightBottom=point; } pDC->SetROP2(nDrawmode); //恢复原绘图模式 ReleaseDC(pDC); //释放设备环境 CView::OnMouseMove(nFlags, point); }

(8)编译、运行程序

四、实验结果、分析和总结

1.查看实验结果,验证算法的正确性;

2.对程序进行分析和比较,你还能提出哪些改进和扩充? 3.总结从本次实验中学到了那些知识点或者有哪些感受?

因篇幅问题不能全部显示,请点此查看更多更全内容