计算机图形学 Visual C++ MFC 图形开发初步
目录
1 实验目的和要求
搭建能够显示图形、文字的单文档绘图环境。
(1)在OnDraw函数内,使用MFC封装函数(MoveTo,LineTo,Rectangle,Ellipse等)和点、线、矩形、椭圆/圆等基本图形,表示出指定实验区域的行政中心(或实验样点、河流(或类型边界)和行政区(或实验区)等图形。
(2)使用阔叶树种的叶片采集数据,表示出叶片的投影图形,为三维建模奠定基础。
提示:可通过平移坐标点,使数据显示在客户区的中央。
2 基本原理
(1)MFC(Microsoft Foundation Classes)库是Microsoft为利用VC++开发Windows应用程序而提供的应用程序框架。在这个框架的支持下,对于不同的应用程序,编程的主要任务是填写各自特殊部分的代码。MFC类库由130多个类组成,封装了两千多个API函数。
(2)MFC程序的设计流程[1],如图1:

图1 程序的开发流程
3 主要仪器与设备
Visual C++ 6.0
4 实验步骤/数据处理与结果
4.1 创建MFC项目
(1)启动Visual C++6.0。
(2)从File菜单中选择New选项,出现New对话框。选中Projects分页,在Projects面板中左侧的项目类型列表框中单击选中MFC AppWizard[exe]项,创建一个MFC应用程序。
(3)单击OK按钮。出现MFC AppWizard-Step 1对话框。创建基于Single document(单文档)。
(4)其他设置默认,完成创建。如图2:


图2 创建MFC项目
(5)编译执行,查看效果。如图3:

图3 初始应用程序运行结果
4.2 绘制点
(1)增加成员函数DrawPoint。如图4,用鼠标右键单击类面板中的CExp_01View类节点,在弹出的快捷菜单中选择“Add Member Function…”,在出现的对话框中输入如图所示内容。

图4 增加成员函数DrawPoint
(2)编辑DrawPoint函数。代码如下:
void CExp_01View::DrawPoints(CDC *pDC) { //绘制邻近的点集,以突出显示 pDC->SetPixel(100,100,RGB(255,0,0)); pDC->SetPixel(101,100,RGB(255,0,0)); pDC->SetPixel(100,101,RGB(255,0,0)); pDC->SetPixel(101,101,RGB(255,0,0)); pDC->SetPixel(99,100,RGB(255,0,0)); pDC->SetPixel(100,99,RGB(255,0,0)); pDC->SetPixel(99,99,RGB(255,0,0)); }(3)在OnDraw函数中调用DrawPoints函数。代码如下:
void CExp_01View::OnDraw(CDC* pDC) { CExp_01Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here DrawPoints(pDC); //调用DrawPoints函数 }(4)修改DrawPoints函数,绘制行政中心。代码如下:
void CExp_01View::DrawPoints(CDC *pDC) { /* //方法尝试,查看效果 //Method_1:绘制邻近的点集,以突出显示 pDC->SetPixel(100,100,RGB(255,0,0)); pDC->SetPixel(101,100,RGB(255,0,0)); pDC->SetPixel(100,101,RGB(255,0,0)); pDC->SetPixel(101,101,RGB(255,0,0)); pDC->SetPixel(99,100,RGB(255,0,0)); pDC->SetPixel(100,99,RGB(255,0,0)); pDC->SetPixel(99,99,RGB(255,0,0)); //Method_2:使用for循环成实心方块,以突出显示 for(int i=0; i<10; ++i) { for (int j=0; j<10; ++j) { pDC->SetPixel(200+j, 150+i, RGB(255,0,0)); } } */ //绘制行政中心 for(int i=0; i<10; ++i) { for (int j=0; j<10; ++j) { //绘制4个实心方块,作行政中心 pDC->SetPixel(200+j, 150+i, RGB(255,0,0)); pDC->SetPixel(600+j, 150+i, RGB(0,255,0)); pDC->SetPixel(350+j, 300+i, RGB(0,0,255)); pDC->SetPixel(800+j, 400+i, RGB(200,100,100)); } } }(5)调试运行,结果如下图5所示:

图5 绘制行政中心
4.3 绘制线
(1)增加成员函数DrawLines。
(2)编辑DrawLines函数。使用LineTo函数和MoveTo函数绘制实线的主干道,使用SetPixel函数for循环绘制虚线的支路,使用Ployline函数绘制折线的边界,使用PolyBezierTo函数与创建画笔绘制蓝色河流。代码如下:
void CExp_01View::DrawLines(CDC *pDC) { //实线主干道绘制:LineTo函数和MoveTo函数 pDC->MoveTo(50,250); POINT p1;p1.x=700;p1.y=250; //使用POINT结构 pDC->LineTo(p1); CPoint p2;p2.x=1200;p2.y=20; //使用CPoint类 pDC->LineTo(p2); pDC->MoveTo(50,270); pDC->LineTo(720,270); pDC->LineTo(1240,20); pDC->TextOut(600,260,_T("主干道")); //注记 //虚线支路绘制:SetPixel函数for循环 for(int i=0;i<300;i=i+5) { pDC->SetPixel(500,270+i,RGB(255,0,0)); pDC->SetPixel(520,270+i,RGB(255,0,0)); pDC->TextOut(530,400,_T("支路")); //注记 } //折线边界绘制:Ployline函数 POINT p[10]; p[0].x=1100;p[0].y=400; p[1].x=1100;p[1].y=500; p[2].x=1200;p[2].y=500; p[3].x=1200;p[3].y=600; p[4].x=10;p[4].y=600; p[5].x=10;p[5].y=10; p[6].x=1250;p[6].y=10; p[7].x=1250;p[7].y=200; p[8].x=1100;p[8].y=200; p[9].x=1100;p[9].y=300; pDC->Polyline(p,10); pDC->TextOut(1100,350,_T("大门")); //注记 //曲线河流绘制:PolyBezierTo函数与创建画笔 HPEN hPen = CreatePen(PS_SOLID, 3, RGB(0,0,255)); //创建蓝色画笔 HPEN hOldPen = (HPEN)pDC->SelectObject(hPen); //把画笔选进DC POINT n[6]; pDC->MoveTo(200,100); n[0].x=200;n[0].y=200; n[1].x=300;n[1].y=200; n[2].x=400;n[2].y=300; n[3].x=500;n[3].y=400; n[4].x=300;n[4].y=400; n[5].x=100;n[5].y=450; pDC->PolyBezierTo(n,6); //六个点,两段连续 pDC->SelectObject(hOldPen); //还原旧笔 DeleteObject(hPen); //删除创建的画笔 pDC->TextOut(220,110,_T("河流")); //注记 }(3)在OnDraw函数中调用DrawLines函数。代码如下:
void CExp_01View::OnDraw(CDC* pDC) { CExp_01Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here DrawPoints(pDC); //调用DrawPoints函数 DrawLines(pDC); //调用DrawLines函数 }(4)调试运行,结果如下图7所示:

图6 绘制道路与边界
4.4 绘制面
(1)增加成员函数DrawPloygons。
(2)编辑DrawPloygons函数。使用Rectangle函数绘制行政中心范围,使用RoundRect函数绘制操场,使用RoundRect函数绘制操场,使用Ellipse函数与创建画笔笔刷绘制湖泊。代码如下:
void CExp_01View::DrawPloygons(CDC *pDC) { // 1.绘制行政中心范围:Rectangle函数 //Method_1:使用对角坐标 pDC->Rectangle(70,120,140,190); pDC->TextOut(70,200,_T("第一实验楼")); pDC->Rectangle(570,110,640,200); pDC->TextOut(570,210,_T("第一教学楼")); //Method_2:使用CRect类 CRect r,h; r.left=330;r.top=280; r.right=380;r.bottom=330; h.left=760;h.top=360; h.right=850;h.bottom=450; pDC->Rectangle(r); pDC->TextOut(320,340,_T("第二教学楼")); pDC->Rectangle(h); pDC->TextOut(770,460,_T("第二实验楼")); // 2.绘制操场:RoundRect函数 pDC->RoundRect(230,450,450,580,30,30); pDC->RoundRect(240,460,440,570,30,30); pDC->TextOut(330,510,_T("操场")); // 3.绘制湖泊:Ellipse函数与创建画笔笔刷 HPEN hPen = CreatePen(PS_SOLID, 3, RGB(0,0,200)); //创建蓝色画笔 HPEN hOldPen = (HPEN)pDC->SelectObject(hPen); //把画笔选进DC HBRUSH hBrush = CreateSolidBrush(RGB(50,50,200)); //创建填充笔刷 HBRUSH hOldBrush = (HBRUSH)pDC->SelectObject(hBrush); //把笔刷选进DC CRect l; l.left=130;l.top=30; l.right=250;l.bottom=100; pDC->Ellipse(l); pDC->SelectObject(hOldPen); //还原旧笔 DeleteObject(hPen); //删除创建的画笔 pDC->SelectObject(hOldBrush); //还原原刷 DeleteObject(hBrush); //删除创建的笔刷 pDC->TextOut(260,60,_T("湖泊")); //注记 }(3)在OnDraw函数中调用DrawPloygons函数。代码如下:
void CExp_01View::OnDraw(CDC* pDC) { CExp_01Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here DrawPloygons(pDC); //调用DrawPloygons函数 DrawPoints(pDC); //调用DrawPoints函数 DrawLines(pDC); //调用DrawLines函数 }(4)调试运行,结果如下图9所示:

图7 绘制面要素
4.5 绘制简易叶片图形
(1)增加成员函数DrawLeaf。
(2)编辑LoadData函数。使用画笔画刷绘制树叶颜色,Polygon函数描摹俯视树叶投影的轮廓,并通过MoveTo、LineTo绘制折线树叶叶脉凸显三维效果。同时,使用GetClientRect获取视图中心,保证调整窗口大小而叶片能始终显示在客户区的中央。代码如下:
void CExp_01View::DrawLeaf(CDC *pDC, const CPoint ¢er) { CPen leafPen(PS_SOLID, 2, RGB(34, 139, 34)); // 树叶轮廓颜色 CPen veinPen(PS_SOLID, 1, RGB(0, 100, 0)); // 叶脉颜色 CBrush leafBrush(RGB(124, 252, 0)); // 树叶填充颜色 CPen* pOldPen = pDC->SelectObject(&leafPen); CBrush* pOldBrush = pDC->SelectObject(&leafBrush); // 树叶轮廓点 CPoint pts[7]; pts[0] = CPoint(center.x, center.y - 40); // 叶尖 pts[1] = CPoint(center.x - 20, center.y - 20); pts[2] = CPoint(center.x - 30, center.y); pts[3] = CPoint(center.x, center.y + 30); // 叶底 pts[4] = CPoint(center.x + 30, center.y); pts[5] = CPoint(center.x + 20, center.y - 20); pts[6] = pts[0]; // 闭合 pDC->Polygon(pts, 7); // 绘制叶脉 pDC->SelectObject(&veinPen); pDC->MoveTo(center.x, center.y - 40); pDC->LineTo(center.x, center.y + 30); // 主脉 pDC->MoveTo(center.x, center.y - 10); pDC->LineTo(center.x - 20, center.y - 5); // 左侧脉 pDC->MoveTo(center.x, center.y - 10); pDC->LineTo(center.x + 20, center.y - 5); // 右侧脉 // 恢复原始画笔和画刷 pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush); }(3)在OnDraw函数中调用LoadData函数。代码如下:
void CExp_01View::OnDraw(CDC* pDC) { CExp_01Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here DrawPloygons(pDC); //调用DrawPloygons函数 DrawPoints(pDC); //调用DrawPoints函数 DrawLines(pDC); //调用DrawLines函数 CRect rect; GetClientRect(&rect); CPoint center = rect.CenterPoint(); DrawLeaf(pDC, center); }(4)调试运行,结果如下图10所示:


图8 简易树叶投影绘制
5 体会与总结
本次实验通过创建单文本的MFC AppWizard项目,并使用MoveTo 、LineTo、Ploygon等函数绘制简易的区域平面图,包括行政中心等点要素,道路、河流等线要素,湖泊、操场、建筑范围等面要素。同时,在实验过程中,尝试以不同的方式实现相同的效果或以尝试其他方式以提高代码撰写的效率。最后,通过绘制简易的叶片投影,并将其始终置于客户区的中央,使用Ploygon等函数简化描摹树叶叶片以二维图像凸显三维效果,表示正射投影的图形表示。
后期采集的叶片数据可能以.tex等文件格式存储三维叶片数据,可创建结构体、数组等将其读取与临时储存,通过正射投影、透视投影等投影方式将其转换为二维点集数据,以便于映射在二维的屏幕坐标中显示绘制。同时平移坐标点,使数据显示在客户区的中央。为三维与二维的建模转换的学习奠定良好的启发效果。
参考文献
- 侯俊杰. 深入浅出MFC(第2版)[M]. 武汉: 华中科技大学出版社, 2001: 80.00. ISBN 7-900614-93-1.