实验目的和要求
搭建能够显示图形、文字的单文档绘图环境。
(1)在 OnDraw 函数内,使用 MFC 封装函数(MoveTo, LineTo, Rectangle, Ellipse 等)和点、线、矩形、椭圆/圆等基本图形,表示出指定实验区域的行政中心(或实验样点、河流(或类型边界)和行政区(或实验区)等图形。
(2)使用阔叶树种的叶片采集数据,表示出叶片的投影图形,为三维建模奠定基础。
提示:可通过平移坐标点,使数据显示在客户区的中央。
基本原理
(1)MFC(Microsoft Foundation Classes)库是 Microsoft 为利用 VC++ 开发 Windows 应用程序而提供的应用程序框架。在这个框架的支持下,对于不同的应用程序,编程的主要任务是填写各自特殊部分的代码。MFC 类库由 130 多个类组成,封装了两千多个 API 函数。
(2)MFC 程序的设计流程,如图 1:
图 1 程序的开发流程
开发环境
Visual C++ 6.0
实验步骤与结果
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)其他设置默认,完成创建。
(5)编译执行,查看效果。
4.2 绘制点
(1)增加成员函数 DrawPoints。
(2)编辑 DrawPoints 函数。代码如下:
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));
...
//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 循环绘制虚线的支路,使用 Polyline 函数绘制折线的边界,使用 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("支路")); //注记
}
//折线边界绘制:Polyline 函数
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)增加成员函数 DrawPolygons。
(2)编辑 DrawPolygons 函数。使用 Rectangle 函数绘制行政中心范围,使用 RoundRect 函数绘制操场,使用 Ellipse 函数与创建画笔笔刷绘制湖泊。代码如下:
void CExp_01View::DrawPolygons(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 函数中调用 DrawPolygons 函数。代码如下:
void CExp_01View::OnDraw(CDC* pDC) {
CExp_01Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
DrawPolygons(pDC); //调用 DrawPolygons 函数
DrawPoints(pDC); //调用 DrawPoints 函数
DrawLines(pDC); //调用 DrawLines 函数
}
(4)调试运行,结果如下图 9 所示:
图 7 绘制面要素
4.5 绘制简易叶片图形
(1)增加成员函数 DrawLeaf。
(2)编辑 DrawLeaf 函数。使用画笔画刷绘制树叶颜色,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 函数中调用 DrawLeaf 函数。代码如下:
void CExp_01View::OnDraw(CDC* pDC) {
CExp_01Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
DrawPolygons(pDC); //调用 DrawPolygons 函数
DrawPoints(pDC); //调用 DrawPoints 函数
DrawLines(pDC); //调用 DrawLines 函数
CRect rect;
GetClientRect(&rect);
CPoint center = rect.CenterPoint();
DrawLeaf(pDC, center);
}
(4)调试运行,结果如下图 10 所示:
图 8 简易树叶投影绘制
总结
本次实验通过创建单文本的 MFC AppWizard 项目,并使用 MoveTo、LineTo、Polygon 等函数绘制简易的区域平面图,包括行政中心等点要素,道路、河流等线要素,湖泊、操场、建筑范围等面要素。同时,在实验过程中,尝试以不同的方式实现相同的效果或以尝试其他方式以提高代码撰写的效率。最后,通过绘制简易的叶片投影,并将其始终置于客户区的中央,使用 Polygon 等函数简化描摹树叶叶片以二维图像凸显三维效果,表示正射投影的图形表示。
后期采集的叶片数据可能以 .tex 等文件格式存储三维叶片数据,可创建结构体、数组等将其读取与临时储存,通过正射投影、透视投影等投影方式将其转换为二维点集数据,以便于映射在二维的屏幕坐标中显示绘制。同时平移坐标点,使数据显示在客户区的中央。为三维与二维的建模转换的学习奠定良好的启发效果。
参考文献
- 侯俊杰。深入浅出 MFC(第 2 版)[M]. 武汉:华中科技大学出版社,2001.


